diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000000..23c94155f3f --- /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 00000000000..e16795062d7 --- /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 00000000000..260cc275922 --- /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 00000000000..5b4ca79f518 --- /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 00000000000..722bace6df7 --- /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 00000000000..d43f05e9e20 --- /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 00000000000..a6b8d73fa73 --- /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 00000000000..ff5955a2fe7 --- /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/.eslintrc.json b/.eslintrc.json index 1c420f93df5..8761c1c1813 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -42,7 +42,15 @@ "jsdoc/no-types": "warn", "semi": "off", "@typescript-eslint/semi": "warn", - "@typescript-eslint/class-name-casing": "warn", + "@typescript-eslint/naming-convention": [ + "warn", + { + "selector": "class", + "format": [ + "PascalCase" + ] + } + ], "code-no-unused-expressions": [ "warn", { @@ -63,13 +71,18 @@ "browser": [ "common" ], - "electron-main": [ + "electron-sandbox": [ "common", - "node" + "browser" ], "electron-browser": [ "common", "browser", + "node", + "electron-sandbox" + ], + "electron-main": [ + "common", "node" ] } @@ -104,6 +117,14 @@ "**/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": [ @@ -149,13 +170,22 @@ "*" // 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-browser}/**", - "**/vs/base/parts/*/{common,browser,node,electron-browser}/**", + "**/vs/base/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/base/parts/*/{common,browser,node,electron-sandbox,electron-browser}/**", "*" // node modules ] }, @@ -185,6 +215,7 @@ "vs/nls", "**/vs/base/common/**", "**/vs/base/parts/*/common/**", + "**/vs/base/test/common/**", "**/vs/platform/*/common/**", "**/vs/platform/*/test/common/**" ] @@ -209,14 +240,24 @@ "*" // 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}/**", - "**/vs/base/parts/*/{common,browser,node,electron-browser}/**", - "**/vs/platform/*/{common,browser,node,electron-browser}/**", + "**/vs/base/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/base/parts/*/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/platform/*/{common,browser,node,electron-sandbox,electron-browser}/**", "*" // node modules ] }, @@ -420,18 +461,34 @@ "**/vs/**/{common,worker}/**" ] }, + { + "target": "**/vs/workbench/electron-sandbox/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/{common,browser,electron-sandbox}/**", + "**/vs/base/parts/*/{common,browser,electron-sandbox}/**", + "**/vs/platform/*/{common,browser,electron-sandbox}/**", + "**/vs/editor/{common,browser,electron-sandbox}/**", + "**/vs/editor/contrib/**", // editor/contrib is equivalent to /browser/ by convention + "**/vs/workbench/{common,browser,electron-sandbox}/**", + "**/vs/workbench/api/{common,browser,electron-sandbox}/**", + "**/vs/workbench/services/*/{common,browser,electron-sandbox}/**" + ] + }, { "target": "**/vs/workbench/electron-browser/**", "restrictions": [ "vs/nls", "vs/css!./**/*", - "**/vs/base/{common,browser,node,electron-browser}/**", - "**/vs/base/parts/*/{common,browser,node,electron-browser}/**", - "**/vs/platform/*/{common,browser,node,electron-browser}/**", - "**/vs/editor/{common,browser,node,electron-browser}/**", + "**/vs/base/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/base/parts/*/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/platform/*/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/editor/{common,browser,node,electron-sandbox,electron-browser}/**", "**/vs/editor/contrib/**", // editor/contrib is equivalent to /browser/ by convention - "**/vs/workbench/{common,browser,node,electron-browser,api}/**", - "**/vs/workbench/services/*/{common,browser,node,electron-browser}/**", + "**/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 ] }, @@ -443,7 +500,7 @@ "**/vs/base/**", "**/vs/platform/**", "**/vs/editor/**", - "**/vs/workbench/{common,browser,node,electron-browser}/**", + "**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**", "vs/workbench/contrib/files/common/editors/fileEditorInput", "**/vs/workbench/services/**", "**/vs/workbench/test/**", @@ -462,7 +519,9 @@ "**/vs/workbench/services/**/common/**", "**/vs/workbench/api/**/common/**", "vscode-textmate", - "vscode-oniguruma" + "vscode-oniguruma", + "iconv-lite-umd", + "semver-umd" ] }, { @@ -507,16 +566,30 @@ "*" // 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-browser}/**", - "**/vs/platform/**/{common,browser,node,electron-browser}/**", + "**/vs/base/**/{common,browser,worker,node,electron-sandbox,electron-browser}/**", + "**/vs/platform/**/{common,browser,node,electron-sandbox,electron-browser}/**", "**/vs/editor/**", - "**/vs/workbench/{common,browser,node,electron-browser,api}/**", - "**/vs/workbench/services/**/{common,browser,node,electron-browser}/**", + "**/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 ] }, @@ -529,7 +602,7 @@ "**/vs/base/**", "**/vs/platform/**", "**/vs/editor/**", - "**/vs/workbench/{common,browser,node,electron-browser}/**", + "**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**", "**/vs/workbench/services/**", "**/vs/workbench/contrib/**", "**/vs/workbench/test/**", @@ -623,20 +696,47 @@ "*" // 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-browser}/**", - "**/vs/platform/**/{common,browser,node,electron-browser}/**", + "**/vs/base/**/{common,browser,worker,node,electron-sandbox,electron-browser}/**", + "**/vs/platform/**/{common,browser,node,electron-sandbox,electron-browser}/**", "**/vs/editor/**", - "**/vs/workbench/{common,browser,node,electron-browser,api}/**", - "**/vs/workbench/services/**/{common,browser,node,electron-browser}/**", - "**/vs/workbench/contrib/**/{common,browser,node,electron-browser}/**", + "**/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": [ @@ -653,10 +753,10 @@ "restrictions": [ "vs/nls", "vs/css!./**/*", - "**/vs/base/**/{common,browser,node,electron-browser}/**", - "**/vs/base/parts/**/{common,browser,node,electron-browser}/**", - "**/vs/platform/**/{common,browser,node,electron-browser}/**", - "**/vs/code/**/{common,browser,node,electron-browser}/**", + "**/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 ] }, @@ -684,6 +784,66 @@ "*" // 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": "**/*" diff --git a/.github/classifier.json b/.github/classifier.json index 9607d5f137f..bb313eebafb 100644 --- a/.github/classifier.json +++ b/.github/classifier.json @@ -1,16 +1,181 @@ { - "$schema": "https://raw.githubusercontent.com/microsoft/vscode-github-triage-actions/master/classifier/apply/apply-labels/classifier-config.schema.json", + "$schema": "https://raw.githubusercontent.com/microsoft/vscode-github-triage-actions/master/classifier-deep/apply/apply-labels/deep-classifier-config.schema.json", "assignees": { - "JacksonKearl": { - "assign": true - } + "JacksonKearl": {"accuracy": 0.5} }, "labels": { - "search-editor": { - "applyLabel": true, - "assign": [ - "JacksonKearl" - ] + "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"]}, + "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/commands.json b/.github/commands.json index 6793e0036b4..1b2bc516842 100644 --- a/.github/commands.json +++ b/.github/commands.json @@ -133,6 +133,18 @@ "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", @@ -209,6 +221,19 @@ "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", @@ -259,7 +284,7 @@ ], "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/microsoft/vscode-go). Make sure to check their [contributing guidelines](https://github.com/microsoft/vscode-go/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!" + "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", @@ -272,7 +297,7 @@ ], "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!" + "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", @@ -326,6 +351,18 @@ "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", diff --git a/.github/workflows/author-verified.yml b/.github/workflows/author-verified.yml index 09c2eeb3f43..7114f351353 100644 --- a/.github/workflows/author-verified.yml +++ b/.github/workflows/author-verified.yml @@ -1,6 +1,7 @@ name: Author Verified on: repository_dispatch: + types: [trigger-author-verified] schedule: - cron: 20 14 * * * # 4:20pm Zurich issues: @@ -16,7 +17,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v15 + ref: v31 path: ./actions - name: Install Actions if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'author-verification-requested') @@ -31,6 +32,8 @@ jobs: 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/classifier-train.yml b/.github/workflows/classifier-train.yml deleted file mode 100644 index c728e374a78..00000000000 --- a/.github/workflows/classifier-train.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: "Classifier: Trainer" -on: - schedule: - - cron: 0 0 12 * * - -jobs: - main: - runs-on: ubuntu-latest - steps: - - name: Checkout Actions - uses: actions/checkout@v2 - with: - repository: 'microsoft/vscode-github-triage-actions' - ref: master - lfs: true - 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 - - name: "Run Classifier: Scraper" - uses: ./actions/classifier/train/fetch-issues - with: - token: ${{secrets.ISSUE_SCRAPER_TOKEN}} # My personal token, so as to not risk going over quota on main token - - 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 - - name: "Run Classifier: Generator" - run: python ./actions/classifier/train/generate-models/generate.py category - - name: "Run Classifier: Upload" - uses: ./actions/classifier/train/upload-models - with: - blobContainerName: classifier-models - blobStorageKey: ${{secrets.AZURE_BLOB_STORAGE_CONNECTION_STRING}} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000000..5e990067f1d --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,47 @@ +name: "Code Scanning" + +on: [push, pull_request] + +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 index 80a21503e64..7036a4937d4 100644 --- a/.github/workflows/commands.yml +++ b/.github/workflows/commands.yml @@ -13,11 +13,12 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v15 + ref: v31 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Commands uses: ./actions/commands with: + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} config-path: commands diff --git a/.github/workflows/deep-classifier-monitor.yml b/.github/workflows/deep-classifier-monitor.yml new file mode 100644 index 00000000000..7aa6d9de22f --- /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: v31 + 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/classifier-apply.yml b/.github/workflows/deep-classifier-runner.yml similarity index 60% rename from .github/workflows/classifier-apply.yml rename to .github/workflows/deep-classifier-runner.yml index 80b61b5d312..4ac237e5f83 100644 --- a/.github/workflows/classifier-apply.yml +++ b/.github/workflows/deep-classifier-runner.yml @@ -1,7 +1,9 @@ -name: "Classifier: Apply" +name: "Deep Classifier: Runner" on: schedule: - - cron: 0,30 * * * * + - cron: 0/30 * * * * + repository_dispatch: + types: [trigger-deep-classifier-runner] jobs: main: @@ -11,7 +13,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v15 + ref: v31 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions @@ -19,13 +21,16 @@ jobs: # Pulls in a bunch of other packages that arent needed for the rest of the actions run: npm install @azure/storage-blob@12 - name: "Run Classifier: Scraper" - uses: ./actions/classifier/apply/fetch-issues + uses: ./actions/classifier-deep/apply/fetch-sources with: # slightly overlapping to protect against issues slipping through the cracks if a run is delayed - from: 45 + from: 40 until: 5 - blobContainerName: classifier-models + 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: @@ -33,11 +38,13 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install --upgrade numpy scipy scikit-learn joblib nltk + pip install --upgrade numpy scipy scikit-learn joblib nltk simpletransformers torch torchvision - name: "Run Classifier: Generator" - run: python ./actions/classifier/apply/generate-labels/main.py + run: python ./actions/classifier-deep/apply/generate-labels/main.py - name: "Run Classifier: Labeler" - uses: ./actions/classifier/apply/apply-labels + uses: ./actions/classifier-deep/apply/apply-labels with: - config-path: classifier + configPath: classifier + allowLabels: "needs more info|new release" + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} diff --git a/.github/workflows/deep-classifier-scraper.yml b/.github/workflows/deep-classifier-scraper.yml new file mode 100644 index 00000000000..c93f2b5352f --- /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: v31 + 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 + - 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 index 315b282a004..1ecc532ce88 100644 --- a/.github/workflows/english-please.yml +++ b/.github/workflows/english-please.yml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v15 + ref: v31 path: ./actions - name: Install Actions if: contains(github.event.issue.labels.*.name, '*english-please') @@ -22,6 +22,7 @@ jobs: 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" diff --git a/.github/workflows/feature-request.yml b/.github/workflows/feature-request.yml index c322a407eac..cdd65c77202 100644 --- a/.github/workflows/feature-request.yml +++ b/.github/workflows/feature-request.yml @@ -1,6 +1,7 @@ name: Feature Request Manager on: repository_dispatch: + types: [trigger-feature-request-manager] issues: types: [milestoned] schedule: @@ -17,7 +18,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v15 + ref: v31 - name: Install Actions if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'feature-request') run: npm install --production --prefix ./actions @@ -25,6 +26,7 @@ jobs: 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 @@ -32,8 +34,8 @@ jobs: 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" + 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 diff --git a/.github/workflows/latest-release-monitor.yml b/.github/workflows/latest-release-monitor.yml new file mode 100644 index 00000000000..a00d3554147 --- /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: v31 + - name: Install Actions + run: npm install --production --prefix ./actions + - name: Install Storage Module + run: npm install @azure/storage-blob + - 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 index a808941b65d..d805f6a6428 100644 --- a/.github/workflows/locker.yml +++ b/.github/workflows/locker.yml @@ -3,6 +3,7 @@ on: schedule: - cron: 20 23 * * * # 4:20pm Redmond repository_dispatch: + types: [trigger-locker] jobs: main: @@ -13,12 +14,13 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v15 + ref: v31 - 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 index b8dbb60c6c4..d143dec9536 100644 --- a/.github/workflows/needs-more-info-closer.yml +++ b/.github/workflows/needs-more-info-closer.yml @@ -3,6 +3,7 @@ on: schedule: - cron: 20 11 * * * # 4:20am Redmond repository_dispatch: + types: [trigger-needs-more-info] jobs: main: @@ -13,15 +14,16 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v15 + ref: v31 - 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}} 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: 120 + pingDays: 100 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 index 816de682461..97c8ee18e8e 100644 --- a/.github/workflows/on-label.yml +++ b/.github/workflows/on-label.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v15 + ref: v31 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions @@ -27,6 +27,7 @@ jobs: 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 @@ -35,6 +36,7 @@ jobs: - name: Run Commands uses: ./actions/commands with: + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} config-path: commands @@ -44,6 +46,7 @@ jobs: 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 @@ -64,6 +67,7 @@ jobs: 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. @@ -73,6 +77,7 @@ jobs: 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" diff --git a/.github/workflows/on-open.yml b/.github/workflows/on-open.yml index 24eec136b31..e8c41404d99 100644 --- a/.github/workflows/on-open.yml +++ b/.github/workflows/on-open.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v15 + ref: v31 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions @@ -19,12 +19,14 @@ jobs: - 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 @@ -33,6 +35,7 @@ jobs: 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 @@ -40,15 +43,25 @@ jobs: - 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" diff --git a/.github/workflows/release-pipeline-labeler.yml b/.github/workflows/release-pipeline-labeler.yml new file mode 100644 index 00000000000..bc45221133f --- /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: v31 + path: ./actions + - name: Checkout Repo + if: github.event_name != 'issues' + uses: actions/checkout@v2 + with: + path: ./repo + fetch-depth: 0 + - name: Install Actions + run: npm install --production --prefix ./actions + - name: "Run Release Pipeline Labeler" + uses: ./actions/release-pipeline + with: + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + notYetReleasedLabel: unreleased + insidersReleasedLabel: insiders-released diff --git a/.github/workflows/rich-navigation.yml b/.github/workflows/rich-navigation.yml new file mode 100644 index 00000000000..d1ac9f09921 --- /dev/null +++ b/.github/workflows/rich-navigation.yml @@ -0,0 +1,23 @@ +name: "Rich Navigation Indexing" +on: + pull_request: + push: + branches: + - master + +jobs: + richnav: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Use Node.js + uses: actions/setup-node@v1 + - name: Install dependencies + run: yarn --frozen-lockfile + env: + CHILD_CONCURRENCY: 1 + - uses: microsoft/RichCodeNavIndexer@master + 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 index 07ff6492bfa..2dbf0e45871 100644 --- a/.github/workflows/test-plan-item-validator.yml +++ b/.github/workflows/test-plan-item-validator.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v15 + ref: v31 - 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 @@ -23,5 +23,6 @@ jobs: 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 e73dd4d9e8c..0fe46b6eadc 100644 --- a/.gitignore +++ b/.gitignore @@ -24,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/.vscode/launch.json b/.vscode/launch.json index 496b3cbf6e3..577b733df80 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -20,15 +20,13 @@ "port": 5870, "outFiles": [ "${workspaceFolder}/out/**/*.js" - ], - "presentation": { - "hidden": true - } + ] }, { "type": "pwa-chrome", "request": "attach", "name": "Attach to Shared Process", + "timeout": 30000, "port": 9222, "urlFilter": "*sharedProcess.html*", "presentation": { @@ -60,6 +58,7 @@ "type": "node", "request": "attach", "name": "Attach to Main Process", + "timeout": 30000, "port": 5875, "outFiles": [ "${workspaceFolder}/out/**/*.js" @@ -173,7 +172,25 @@ "${workspaceFolder}/out/**/*.js" ], "presentation": { - "group": "6_tests", + "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 } }, @@ -199,9 +216,10 @@ "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", @@ -214,16 +232,14 @@ "outFiles": [ "${workspaceFolder}/out/**/*.js" ], - "browserLaunchLocation": "workspace" + "browserLaunchLocation": "workspace", + "preLaunchTask": "Ensure Prelaunch Dependencies", }, { "type": "node", "request": "launch", "name": "VS Code (Web)", - "runtimeExecutable": "yarn", - "runtimeArgs": [ - "web" - ], + "program": "${workspaceFolder}/resources/serverless/code-web.js", "presentation": { "group": "0_vscode", "order": 2 @@ -259,6 +275,18 @@ "order": 3 } }, + { + "type": "pwa-msedge", + "request": "launch", + "name": "VS Code (Web, Edge)", + "url": "http://localhost:8080", + "pauseForSourceMap": false, + "preLaunchTask": "Run web", + "presentation": { + "group": "0_vscode", + "order": 3 + } + }, { "type": "node", "request": "launch", @@ -277,7 +305,7 @@ { "type": "node", "request": "launch", - "name": "HTML Unit Tests", + "name": "HTML Server Unit Tests", "program": "${workspaceFolder}/extensions/html-language-features/server/test/index.js", "stopOnEntry": false, "cwd": "${workspaceFolder}/extensions/html-language-features/server", @@ -289,13 +317,28 @@ "order": 10 } }, + { + "type": "node", + "request": "launch", + "name": "CSS Server Unit Tests", + "program": "${workspaceFolder}/extensions/css-language-features/server/test/index.js", + "stopOnEntry": false, + "cwd": "${workspaceFolder}/extensions/css-language-features/server", + "outFiles": [ + "${workspaceFolder}/extensions/css-language-features/server/out/**/*.js" + ], + "presentation": { + "group": "5_tests", + "order": 10 + } + }, { "type": "extensionHost", "request": "launch", "name": "Markdown Extension Tests", "runtimeExecutable": "${execPath}", "args": [ - "${workspaceFolder}/extensions/markdown-language-features/test-fixtures", + "${workspaceFolder}/extensions/markdown-language-features/test-workspace", "--extensionDevelopmentPath=${workspaceFolder}/extensions/markdown-language-features", "--extensionTestsPath=${workspaceFolder}/extensions/markdown-language-features/out/test" ], @@ -313,7 +356,7 @@ "name": "TypeScript Extension Tests", "runtimeExecutable": "${execPath}", "args": [ - "${workspaceFolder}/extensions/typescript-language-features/test-fixtures", + "${workspaceFolder}/extensions/typescript-language-features/test-workspace", "--extensionDevelopmentPath=${workspaceFolder}/extensions/typescript-language-features", "--extensionTestsPath=${workspaceFolder}/extensions/typescript-language-features/out/test" ], @@ -386,6 +429,7 @@ "compounds": [ { "name": "VS Code", + "stopAll": true, "configurations": [ "Launch VS Code", "Attach to Main Process", diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues new file mode 100644 index 00000000000..f36bb397581 --- /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:\"July 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 00000000000..979da52c04d --- /dev/null +++ b/.vscode/notebooks/inbox.github-issues @@ -0,0 +1,47 @@ +[ + { + "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 " + }, + { + "kind": 1, + "language": "markdown", + "value": "## Inbox tracking and Issue triage" + }, + { + "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." + }, + { + "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 00000000000..dc6d33365d7 --- /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:\"June 2020\"", + "editable": true + }, + { + "kind": 1, + "language": "github-issues", + "value": "## Milestone Work", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos $milestone assignee:@me is:open", + "editable": false + }, + { + "kind": 1, + "language": "github-issues", + "value": "## Bugs, Debt, Features...", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "#### My Bugs", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos assignee:@me is:open label:bug", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "#### Debt & Engineering", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos assignee:@me is:open label:debt OR $repos assignee:@me is:open label:engineering", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "#### Performance 🐌 🔜 🏎", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos assignee:@me is:open label:perf OR $repos assignee:@me is:open label:perf-startup OR $repos assignee:@me is:open label:perf-bloat OR $repos assignee:@me is:open label:freeze-slow-crash-leak", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "#### Feature Requests", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos assignee:@me is:open label:feature-request milestone:Backlog sort:reactions-+1-desc", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos assignee:@me is:open milestone:\"Backlog Candidates\"", + "editable": false + }, + { + "kind": 1, + "language": "markdown", + "value": "#### Not Actionable", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos assignee:@me is:open label:\"needs more info\"", + "editable": false + } +] \ No newline at end of file diff --git a/.vscode/notebooks/verification.github-issues b/.vscode/notebooks/verification.github-issues new file mode 100644 index 00000000000..6f32df8e077 --- /dev/null +++ b/.vscode/notebooks/verification.github-issues @@ -0,0 +1,55 @@ +[ + { + "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:\"June 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" + }, + { + "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/es6.code-search b/.vscode/searches/es6.code-search index 3a708a48798..9cf8cf0b264 100644 --- a/.vscode/searches/es6.code-search +++ b/.vscode/searches/es6.code-search @@ -2,7 +2,7 @@ # Flags: CaseSensitive WordMatch # ContextLines: 2 -16 results - 5 files +14 results - 4 files src/vs/base/browser/dom.ts: 81 }; @@ -34,24 +34,11 @@ src/vs/base/common/arrays.ts: 420 */ 421 export function first(array: ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T): T; - 560 - 561 /** - 562: * @deprecated ES6: use `Array.find` - 563 */ - 564 export function find(arr: ArrayLike, predicate: (value: T, index: number, arr: ArrayLike) => any): T | undefined { - -src/vs/base/common/map.ts: - 11 - 12 /** - 13: * @deprecated ES6: use `[...SetOrMap.values()]` - 14 */ - 15 export function values(set: Set): V[]; - - 22 - 23 /** - 24: * @deprecated ES6: use `[...map.keys()]` - 25 */ - 26 export function keys(map: Map): K[] { + 569 + 570 /** + 571: * @deprecated ES6: use `Array.find` + 572 */ + 573 export function find(arr: ArrayLike, predicate: (value: T, index: number, arr: ArrayLike) => any): T | undefined { src/vs/base/common/objects.ts: 115 @@ -79,8 +66,8 @@ src/vs/base/common/strings.ts: 170 */ 171 export function endsWith(haystack: string, needle: string): boolean { - 853 - 854 /** - 855: * @deprecated ES6 - 856 */ - 857 export function repeat(s: string, count: number): string { + 861 + 862 /** + 863: * @deprecated ES6 + 864 */ + 865 export function repeat(s: string, count: number): string { diff --git a/.vscode/settings.json b/.vscode/settings.json index 1ffd534543d..9239eae0d4d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -26,6 +26,7 @@ "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": [ @@ -72,7 +73,7 @@ }, "gulp.autoDetect": "off", "files.insertFinalNewline": true, - "[typescript]": { + "[typescript]": { "editor.defaultFormatter": "vscode.typescript-language-features" }, "typescript.tsc.autoDetect": "off" diff --git a/.vscode/tasks.json b/.vscode/tasks.json index f68880d6f68..89967210c75 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -3,12 +3,8 @@ "tasks": [ { "type": "npm", - "script": "watchd", - "label": "Build VS Code", - "group": { - "kind": "build", - "isDefault": true - }, + "script": "watch-clientd", + "label": "Build VS Code Core", "isBackground": true, "presentation": { "reveal": "never" @@ -33,14 +29,106 @@ }, { "type": "npm", - "script": "kill-watchd", - "label": "Kill Build VS Code", + "script": "watch-extensionsd", + "label": "Build VS Code Extensions", + "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" + } + } + }, + { + "label": "Build VS Code", + "dependsOn": [ + "Build VS Code Core", + "Build VS Code Extensions" + ], + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "type": "npm", + "script": "kill-watch-clientd", + "label": "Kill Build VS Code Core", "group": "build", "presentation": { "reveal": "never" }, "problemMatcher": "$tsc" }, + { + "type": "npm", + "script": "kill-watch-extensionsd", + "label": "Kill Build VS Code Extensions", + "group": "build", + "presentation": { + "reveal": "never" + }, + "problemMatcher": "$tsc" + }, + { + "label": "Kill Build VS Code", + "dependsOn": [ + "Kill Build VS Code Core", + "Kill Build VS Code Extensions" + ], + "group": "build" + }, + { + "type": "npm", + "script": "watch-webd", + "label": "Build Web Extensions", + "group": "build", + "isBackground": true, + "presentation": { + "reveal": "never" + }, + "problemMatcher": { + "owner": "typescript", + "applyTo": "closedDocuments", + "fileLocation": [ + "absolute" + ], + "pattern": { + "regexp": "Error: ([^(]+)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\): (.*)$", + "file": 1, + "location": 2, + "message": 3 + }, + "background": { + "beginsPattern": "Starting compilation", + "endsPattern": "Finished compilation" + } + } + }, + { + "type": "npm", + "script": "kill-watch-webd", + "label": "Kill Build Web Extensions", + "group": "build", + "presentation": { + "reveal": "never", + }, + "problemMatcher": "$tsc" + }, { "label": "Run tests", "type": "shell", @@ -75,7 +163,7 @@ }, { "type": "shell", - "command": "yarn web -- --no-launch", + "command": "yarn web --no-launch", "label": "Run web", "isBackground": true, "problemMatcher": { @@ -98,6 +186,14 @@ "source": "eslint", "base": "$eslint-stylish" } - } + }, + { + "type": "shell", + "command": "node build/lib/prelaunch.js", + "label": "Ensure Prelaunch Dependencies", + "presentation": { + "reveal": "silent" + } + }, ] } diff --git a/.yarnrc b/.yarnrc index d86b284e83e..135e10442a7 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "7.2.4" +target "7.3.2" runtime "electron" diff --git a/README.md b/README.md index 1d3bc28f9bb..c095a1d190b 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) @@ -56,6 +54,15 @@ Many of the core components and extensions to VS Code live in their own reposito VS Code includes a set of built-in extensions located in the [extensions](extensions) folder, including grammars and snippets for many languages. Extensions that provide rich language support (code completion, Go to Definition) for a language have the suffix `language-features`. For example, the `json` extension provides coloring for `JSON` and the `json-language-features` provides rich language support for `JSON`. +## Development Container + +This repository includes a Visual Studio Code Remote - Containers / Codespaces development container. + +- For [Remote - Containers](https://aka.ms/vscode-remote/download/containers), use the **Remote-Containers: Open Repository in Container...** command which creates a Docker volume for better disk I/O on macOS and Windows. +- For Codespaces, install the [Visual Studio Codespaces](https://aka.ms/vscs-ext-vscode) extension in VS Code, and use the **Codespaces: Create New Codespace** command. + +Docker / the Codespace should have at least **4 Cores and 6 GB of RAM (8 GB recommended)** to run full build. See the [development container README](.devcontainer/README.md) for more information. + ## Code of Conduct This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 93abc78486d..ac17c9eaa64 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -23,7 +23,7 @@ This project incorporates components from the projects listed below. The origina 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.1.8 (https://github.com/Ikuyadeu/vscode-R) +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) @@ -61,9 +61,9 @@ This project incorporates components from the projects listed below. The origina 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 (http://www.unicode.org/) +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.6.0 (https://github.com/emilast/vscode-logfile-highlighter) +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) diff --git a/build/.webignore b/build/.webignore new file mode 100644 index 00000000000..1035cb291cc --- /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/extract-telemetry.sh b/build/azure-pipelines/common/extract-telemetry.sh index 84bbd9c537c..6436e93c8c1 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/symbols.ts b/build/azure-pipelines/common/symbols.ts deleted file mode 100644 index 153be4f25b1..00000000000 --- a/build/azure-pipelines/common/symbols.ts +++ /dev/null @@ -1,228 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -import * as request from 'request'; -import { createReadStream, createWriteStream, unlink, mkdir } from 'fs'; -import * as github from 'github-releases'; -import { join } from 'path'; -import { tmpdir } from 'os'; -import { promisify } from 'util'; - -const BASE_URL = 'https://rink.hockeyapp.net/api/2/'; -const HOCKEY_APP_TOKEN_HEADER = 'X-HockeyAppToken'; - -export interface IVersions { - app_versions: IVersion[]; -} - -export interface IVersion { - id: number; - version: string; -} - -export interface IApplicationAccessor { - accessToken: string; - appId: string; -} - -export interface IVersionAccessor extends IApplicationAccessor { - id: string; -} - -enum Platform { - WIN_32 = 'win32-ia32', - WIN_64 = 'win32-x64', - LINUX_64 = 'linux-x64', - MAC_OS = 'darwin-x64' -} - -function symbolsZipName(platform: Platform, electronVersion: string, insiders: boolean): string { - return `${insiders ? 'insiders' : 'stable'}-symbols-v${electronVersion}-${platform}.zip`; -} - -const SEED = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; -async function tmpFile(name: string): Promise { - let res = ''; - for (let i = 0; i < 8; i++) { - res += SEED.charAt(Math.floor(Math.random() * SEED.length)); - } - - const tmpParent = join(tmpdir(), res); - - await promisify(mkdir)(tmpParent); - - return join(tmpParent, name); -} - -function getVersions(accessor: IApplicationAccessor): Promise { - return asyncRequest({ - url: `${BASE_URL}/apps/${accessor.appId}/app_versions`, - method: 'GET', - headers: { - [HOCKEY_APP_TOKEN_HEADER]: accessor.accessToken - } - }); -} - -function createVersion(accessor: IApplicationAccessor, version: string): Promise { - return asyncRequest({ - url: `${BASE_URL}/apps/${accessor.appId}/app_versions/new`, - method: 'POST', - headers: { - [HOCKEY_APP_TOKEN_HEADER]: accessor.accessToken - }, - formData: { - bundle_version: version - } - }); -} - -function updateVersion(accessor: IVersionAccessor, symbolsPath: string) { - return asyncRequest({ - url: `${BASE_URL}/apps/${accessor.appId}/app_versions/${accessor.id}`, - method: 'PUT', - headers: { - [HOCKEY_APP_TOKEN_HEADER]: accessor.accessToken - }, - formData: { - dsym: createReadStream(symbolsPath) - } - }); -} - -function asyncRequest(options: request.UrlOptions & request.CoreOptions): Promise { - return new Promise((resolve, reject) => { - request(options, (error, _response, body) => { - if (error) { - reject(error); - } else { - resolve(JSON.parse(body)); - } - }); - }); -} - -function downloadAsset(repository: any, assetName: string, targetPath: string, electronVersion: string) { - return new Promise((resolve, reject) => { - repository.getReleases({ tag_name: `v${electronVersion}` }, (err: any, releases: any) => { - if (err) { - reject(err); - } else { - const asset = releases[0].assets.filter((asset: any) => asset.name === assetName)[0]; - if (!asset) { - reject(new Error(`Asset with name ${assetName} not found`)); - } else { - repository.downloadAsset(asset, (err: any, reader: any) => { - if (err) { - reject(err); - } else { - const writer = createWriteStream(targetPath); - writer.on('error', reject); - writer.on('close', resolve); - reader.on('error', reject); - - reader.pipe(writer); - } - }); - } - } - }); - }); -} - -interface IOptions { - repository: string; - platform: Platform; - versions: { code: string; insiders: boolean; electron: string; }; - access: { hockeyAppToken: string; hockeyAppId: string; githubToken: string }; -} - -async function ensureVersionAndSymbols(options: IOptions) { - - // Check version does not exist - console.log(`HockeyApp: checking for existing version ${options.versions.code} (${options.platform})`); - const versions = await getVersions({ accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId }); - if (!Array.isArray(versions.app_versions)) { - throw new Error(`Unexpected response: ${JSON.stringify(versions)}`); - } - - if (versions.app_versions.some(v => v.version === options.versions.code)) { - console.log(`HockeyApp: Returning without uploading symbols because version ${options.versions.code} (${options.platform}) was already found`); - return; - } - - // Download symbols for platform and electron version - const symbolsName = symbolsZipName(options.platform, options.versions.electron, options.versions.insiders); - const symbolsPath = await tmpFile('symbols.zip'); - console.log(`HockeyApp: downloading symbols ${symbolsName} for electron ${options.versions.electron} (${options.platform}) into ${symbolsPath}`); - await downloadAsset(new (github as any)({ repo: options.repository, token: options.access.githubToken }), symbolsName, symbolsPath, options.versions.electron); - - // Create version - console.log(`HockeyApp: creating new version ${options.versions.code} (${options.platform})`); - const version = await createVersion({ accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId }, options.versions.code); - - // Upload symbols - console.log(`HockeyApp: uploading symbols for version ${options.versions.code} (${options.platform})`); - await updateVersion({ id: String(version.id), accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId }, symbolsPath); - - // Cleanup - await promisify(unlink)(symbolsPath); -} - -// Environment -const pakage = require('../../../package.json'); -const product = require('../../../product.json'); -const repository = product.electronRepository; -const electronVersion = require('../../lib/electron').getElectronVersion(); -const insiders = product.quality !== 'stable'; -let codeVersion = pakage.version; -if (insiders) { - codeVersion = `${codeVersion}-insider`; -} -const githubToken = process.argv[2]; -const hockeyAppToken = process.argv[3]; -const is64 = process.argv[4] === 'x64'; -const hockeyAppId = process.argv[5]; - -if (process.argv.length !== 6) { - throw new Error(`HockeyApp: Unexpected number of arguments. Got ${process.argv}`); -} - -let platform: Platform; -if (process.platform === 'darwin') { - platform = Platform.MAC_OS; -} else if (process.platform === 'win32') { - platform = is64 ? Platform.WIN_64 : Platform.WIN_32; -} else { - platform = Platform.LINUX_64; -} - -// Create version and upload symbols in HockeyApp -if (repository && codeVersion && electronVersion && (product.quality === 'stable' || product.quality === 'insider')) { - ensureVersionAndSymbols({ - repository, - platform, - versions: { - code: codeVersion, - insiders, - electron: electronVersion - }, - access: { - githubToken, - hockeyAppToken, - hockeyAppId - } - }).then(() => { - console.log('HockeyApp: done'); - }).catch(error => { - console.error(`HockeyApp: error ${error} (AppID: ${hockeyAppId})`); - - return process.exit(1); - }); -} else { - console.log(`HockeyApp: skipping due to unexpected context (repository: ${repository}, codeVersion: ${codeVersion}, electronVersion: ${electronVersion}, quality: ${product.quality})`); -} \ No newline at end of file diff --git a/build/azure-pipelines/darwin/entitlements.plist b/build/azure-pipelines/darwin/app-entitlements.plist similarity index 84% rename from build/azure-pipelines/darwin/entitlements.plist rename to build/azure-pipelines/darwin/app-entitlements.plist index be8b7163da7..90031d937be 100644 --- a/build/azure-pipelines/darwin/entitlements.plist +++ b/build/azure-pipelines/darwin/app-entitlements.plist @@ -6,8 +6,6 @@ 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/continuous-build-darwin.yml b/build/azure-pipelines/darwin/continuous-build-darwin.yml index 9f66907f0a1..5785de63367 100644 --- a/build/azure-pipelines/darwin/continuous-build-darwin.yml +++ b/build/azure-pipelines/darwin/continuous-build-darwin.yml @@ -29,10 +29,6 @@ steps: yarn electron x64 displayName: Download Electron -- script: | - yarn gulp hygiene - displayName: Run Hygiene Checks - - script: | yarn monaco-compile-check displayName: Run Monaco Editor Checks diff --git a/build/azure-pipelines/darwin/helper-entitlements.plist b/build/azure-pipelines/darwin/helper-entitlements.plist deleted file mode 100644 index 123d12a53e9..00000000000 --- a/build/azure-pipelines/darwin/helper-entitlements.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.security.cs.disable-library-validation - - - diff --git a/build/azure-pipelines/darwin/helper-gpu-entitlements.plist b/build/azure-pipelines/darwin/helper-gpu-entitlements.plist index 777b3abd95e..4efe1ce508f 100644 --- a/build/azure-pipelines/darwin/helper-gpu-entitlements.plist +++ b/build/azure-pipelines/darwin/helper-gpu-entitlements.plist @@ -4,7 +4,5 @@ com.apple.security.cs.allow-jit - com.apple.security.cs.disable-library-validation - diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 95d3d8ebca2..ea286ef1418 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -162,21 +162,13 @@ steps: - script: | set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin - APP_NAME="`ls $APP_ROOT | head -n 1`" - HELPER_APP_NAME="`echo $APP_NAME | sed -e 's/^Visual Studio //;s/\.app$//'`" - APP_FRAMEWORK_PATH="$APP_ROOT/$APP_NAME/Contents/Frameworks" 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 - codesign -s 99FM488X57 --deep --force --options runtime --entitlements build/azure-pipelines/darwin/entitlements.plist "$APP_ROOT"/*.app - codesign -s 99FM488X57 --force --options runtime --entitlements build/azure-pipelines/darwin/helper-entitlements.plist "$APP_FRAMEWORK_PATH/$HELPER_APP_NAME Helper.app" - codesign -s 99FM488X57 --force --options runtime --entitlements build/azure-pipelines/darwin/helper-gpu-entitlements.plist "$APP_FRAMEWORK_PATH/$HELPER_APP_NAME Helper (GPU).app" - codesign -s 99FM488X57 --force --options runtime --entitlements build/azure-pipelines/darwin/helper-plugin-entitlements.plist "$APP_FRAMEWORK_PATH/$HELPER_APP_NAME Helper (Plugin).app" - codesign -s 99FM488X57 --force --options runtime --entitlements build/azure-pipelines/darwin/helper-renderer-entitlements.plist "$APP_FRAMEWORK_PATH/$HELPER_APP_NAME Helper (Renderer).app" + DEBUG=electron-osx-sign* node build/darwin/sign.js displayName: Set Hardened Entitlements - script: | @@ -243,17 +235,24 @@ steps: 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 diff --git a/build/azure-pipelines/darwin/publish.sh b/build/azure-pipelines/darwin/publish.sh index fe3e9a59986..07734e194f8 100755 --- a/build/azure-pipelines/darwin/publish.sh +++ b/build/azure-pipelines/darwin/publish.sh @@ -17,8 +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" -# Skip hockey app because build failure. -# https://github.com/microsoft/vscode/issues/90491 diff --git a/build/azure-pipelines/exploration-build.yml b/build/azure-pipelines/exploration-build.yml index e0b1ef7d613..370c56fa6a1 100644 --- a/build/azure-pipelines/exploration-build.yml +++ b/build/azure-pipelines/exploration-build.yml @@ -31,10 +31,10 @@ steps: git config user.email "vscode@microsoft.com" git config user.name "VSCode" - git checkout origin/electron-8.0.x + git checkout origin/electron-x.y.z git merge origin/master # Push master branch into exploration branch - git push origin HEAD:electron-8.0.x + git push origin HEAD:electron-x.y.z displayName: Sync & Merge Exploration diff --git a/build/azure-pipelines/linux/product-build-linux-multiarch.yml b/build/azure-pipelines/linux/product-build-linux-multiarch.yml index 68ae4ee8b67..485f8dcfba7 100644 --- a/build/azure-pipelines/linux/product-build-linux-multiarch.yml +++ b/build/azure-pipelines/linux/product-build-linux-multiarch.yml @@ -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 f28c896ba83..5d7bccf467f 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -179,7 +179,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/publish.sh displayName: Publish diff --git a/build/azure-pipelines/linux/publish.sh b/build/azure-pipelines/linux/publish.sh index b168ab9cf7b..7e360be6cb4 100755 --- a/build/azure-pipelines/linux/publish.sh +++ b/build/azure-pipelines/linux/publish.sh @@ -27,11 +27,6 @@ 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" -# Skip hockey app because build failure. -# https://github.com/microsoft/vscode/issues/90491 - # Publish DEB PLATFORM_DEB="linux-deb-x64" DEB_ARCH="amd64" diff --git a/build/azure-pipelines/mixin.js b/build/azure-pipelines/mixin.js index efb7d4d1ca9..e133b2d2bb9 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 a98b5f4f77e..7b6d2bcbbde 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -36,6 +36,17 @@ jobs: steps: - template: win32/product-build-win32.yml +- job: WindowsARM64 + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WIN32_ARM64'], 'true')) + pool: + vmImage: VS2017-Win2016 + variables: + VSCODE_ARCH: arm64 + dependsOn: + - Compile + steps: + - template: win32/product-build-win32-arm64.yml + - job: Linux condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX'], 'true')) pool: diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index c3db41e80d5..db6524be03b 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -72,29 +72,6 @@ steps: vstsFeed: 'npm-vscode' condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true')) -- script: | - set -e - yarn generate-github-config - displayName: Generate GitHub config - env: - OSS_GITHUB_ID: "a5d3c261b032765a78de" - OSS_GITHUB_SECRET: $(oss-github-client-secret) - INSIDERS_GITHUB_ID: "31f02627809389d9f111" - INSIDERS_GITHUB_SECRET: $(insiders-github-client-secret) - STABLE_GITHUB_ID: "baa8a44b5e861d918709" - STABLE_GITHUB_SECRET: $(stable-github-client-secret) - EXPLORATION_GITHUB_ID: "94e8376d3a90429aeaea" - EXPLORATION_GITHUB_SECRET: $(exploration-github-client-secret) - VSO_GITHUB_ID: "3d4be8f37a0325b5817d" - VSO_GITHUB_SECRET: $(vso-github-client-secret) - VSO_PPE_GITHUB_ID: "eabf35024dc2e891a492" - VSO_PPE_GITHUB_SECRET: $(vso-ppe-github-client-secret) - VSO_DEV_GITHUB_ID: "84383ebd8a7c5f5efc5c" - VSO_DEV_GITHUB_SECRET: $(vso-dev-github-client-secret) - GITHUB_APP_ID: "Iv1.ae51e546bef24ff1" - GITHUB_APP_SECRET: $(github-app-client-secret) - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true')) - - script: | set -e yarn postinstall diff --git a/build/azure-pipelines/win32/continuous-build-win32.yml b/build/azure-pipelines/win32/continuous-build-win32.yml index b0a81aeb8c8..026a162f510 100644 --- a/build/azure-pipelines/win32/continuous-build-win32.yml +++ b/build/azure-pipelines/win32/continuous-build-win32.yml @@ -36,10 +36,6 @@ steps: yarn electron displayName: Download Electron -- script: | - yarn gulp hygiene - displayName: Run Hygiene Checks - - powershell: | yarn monaco-compile-check displayName: Run Monaco Editor Checks diff --git a/build/azure-pipelines/win32/product-build-win32-arm64.yml b/build/azure-pipelines/win32/product-build-win32-arm64.yml new file mode 100644 index 00000000000..01be34aa9a8 --- /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.13.0" + +- 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 bcd03489df6..fb4f3052578 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -218,7 +218,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 @@ -236,7 +236,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 c9ff9d09ec4..a225f9d5fdf 100644 --- a/build/azure-pipelines/win32/publish.ps1 +++ b/build/azure-pipelines/win32/publish.ps1 @@ -16,22 +16,21 @@ $ServerZip = "$Repo\.build\vscode-server-win32-$Arch.zip" $Build = "$Root\VSCode-win32-$Arch" # Create server archive -exec { xcopy $LegacyServer $Server /H /E /I } -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 } -# Skip hockey app because build failure. -# https://github.com/microsoft/vscode/issues/90491 -# 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/builtin/main.js b/build/builtin/main.js index b094a67cac5..7379de7a93d 100644 --- a/build/builtin/main.js +++ b/build/builtin/main.js @@ -10,11 +10,11 @@ const path = require('path'); let window = null; app.once('ready', () => { - window = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, webviewTag: true } }); + window = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, webviewTag: true, enableWebSQL: false, nativeWindowOpen: true } }); window.setMenuBarVisibility(false); window.loadURL(url.format({ pathname: path.join(__dirname, 'index.html'), protocol: 'file:', slashes: true })); // window.webContents.openDevTools(); window.once('closed', () => window = null); }); -app.on('window-all-closed', () => app.quit()); \ No newline at end of file +app.on('window-all-closed', () => app.quit()); diff --git a/build/darwin/sign.js b/build/darwin/sign.js new file mode 100644 index 00000000000..b8eb9fc7525 --- /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 00000000000..ee5d2eeb17b --- /dev/null +++ b/build/darwin/sign.ts @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as codesign from 'electron-osx-sign'; +import * as path from 'path'; +import * as util from '../lib/util'; +import * as product from '../../product.json'; + +async function main(): Promise { + const buildDir = process.env['AGENT_BUILDDIRECTORY']; + const tempDir = process.env['AGENT_TEMPDIRECTORY']; + + if (!buildDir) { + throw new Error('$AGENT_BUILDDIRECTORY not set'); + } + + if (!tempDir) { + throw new Error('$AGENT_TEMPDIRECTORY not set'); + } + + const baseDir = path.dirname(__dirname); + const appRoot = path.join(buildDir, 'VSCode-darwin'); + const appName = product.nameLong + '.app'; + const appFrameworkPath = path.join(appRoot, appName, 'Contents', 'Frameworks'); + const helperAppBaseName = product.nameShort; + const gpuHelperAppName = helperAppBaseName + ' Helper (GPU).app'; + const pluginHelperAppName = helperAppBaseName + ' Helper (Plugin).app'; + const rendererHelperAppName = helperAppBaseName + ' Helper (Renderer).app'; + + const defaultOpts: codesign.SignOptions = { + app: path.join(appRoot, appName), + platform: 'darwin', + entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'app-entitlements.plist'), + 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'app-entitlements.plist'), + hardenedRuntime: true, + 'pre-auto-entitlements': false, + 'pre-embed-provisioning-profile': false, + keychain: path.join(tempDir, 'buildagent.keychain'), + version: util.getElectronVersion(), + identity: '99FM488X57', + 'gatekeeper-assess': false + }; + + const appOpts = { + ...defaultOpts, + // TODO(deepak1556): Incorrectly declared type in electron-osx-sign + ignore: (filePath: string) => { + return filePath.includes(gpuHelperAppName) || + filePath.includes(pluginHelperAppName) || + filePath.includes(rendererHelperAppName); + } + }; + + const gpuHelperOpts: codesign.SignOptions = { + ...defaultOpts, + app: path.join(appFrameworkPath, gpuHelperAppName), + entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist'), + 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist'), + }; + + const pluginHelperOpts: codesign.SignOptions = { + ...defaultOpts, + app: path.join(appFrameworkPath, pluginHelperAppName), + entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-plugin-entitlements.plist'), + 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-plugin-entitlements.plist'), + }; + + const rendererHelperOpts: codesign.SignOptions = { + ...defaultOpts, + app: path.join(appFrameworkPath, rendererHelperAppName), + entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist'), + 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist'), + }; + + await codesign.signAsync(gpuHelperOpts); + await codesign.signAsync(pluginHelperOpts); + await codesign.signAsync(rendererHelperOpts); + await codesign.signAsync(appOpts as any); +} + +if (require.main === module) { + main().catch(err => { + console.error(err); + process.exit(1); + }); +} diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index 25cccf6d8e5..6ae72e4cf06 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -8,9 +8,11 @@ require('events').EventEmitter.defaultMaxListeners = 100; const gulp = require('gulp'); const path = require('path'); +const nodeUtil = require('util'); const tsb = require('gulp-tsb'); const es = require('event-stream'); const filter = require('gulp-filter'); +const webpack = require('webpack'); const util = require('./lib/util'); const task = require('./lib/task'); const watcher = require('./lib/watch'); @@ -21,6 +23,8 @@ const nlsDev = require('vscode-nls-dev'); const root = path.dirname(__dirname); const commit = util.getVersion(root); const plumber = require('gulp-plumber'); +const fancyLog = require('fancy-log'); +const ansiColors = require('ansi-colors'); const ext = require('./lib/extensions'); const extensionsPath = path.join(path.dirname(__dirname), 'extensions'); @@ -161,9 +165,84 @@ gulp.task(compileExtensionsBuildLegacyTask); const cleanExtensionsBuildTask = task.define('clean-extensions-build', util.rimraf('.build/extensions')); const compileExtensionsBuildTask = task.define('compile-extensions-build', task.series( cleanExtensionsBuildTask, - task.define('bundle-extensions-build', () => ext.packageLocalExtensionsStream().pipe(gulp.dest('.build'))), - task.define('bundle-marketplace-extensions-build', () => ext.packageMarketplaceExtensionsStream().pipe(gulp.dest('.build'))), + task.define('bundle-extensions-build', () => ext.packageLocalExtensionsStream(false).pipe(gulp.dest('.build'))), + task.define('bundle-marketplace-extensions-build', () => ext.packageMarketplaceExtensionsStream(false).pipe(gulp.dest('.build'))), )); gulp.task(compileExtensionsBuildTask); exports.compileExtensionsBuildTask = compileExtensionsBuildTask; + +const compileWebExtensionsTask = task.define('compile-web', () => buildWebExtensions(false)); +gulp.task(compileWebExtensionsTask); +exports.compileWebExtensionsTask = compileWebExtensionsTask; + +const watchWebExtensionsTask = task.define('watch-web', () => buildWebExtensions(true)); +gulp.task(watchWebExtensionsTask); +exports.watchWebExtensionsTask = watchWebExtensionsTask; + +async function buildWebExtensions(isWatch) { + + const webpackConfigLocations = await nodeUtil.promisify(glob)( + path.join(extensionsPath, '**', 'extension-browser.webpack.config.js'), + { ignore: ['**/node_modules'] } + ); + + const webpackConfigs = []; + + for (const webpackConfigPath of webpackConfigLocations) { + const configOrFnOrArray = require(webpackConfigPath); + function addConfig(configOrFn) { + if (typeof configOrFn === 'function') { + webpackConfigs.push(configOrFn({}, {})); + } else { + webpackConfigs.push(configOrFn); + } + } + addConfig(configOrFnOrArray); + } + function reporter(fullStats) { + if (Array.isArray(fullStats.children)) { + for (const stats of fullStats.children) { + const outputPath = stats.outputPath; + if (outputPath) { + const relativePath = path.relative(extensionsPath, outputPath).replace(/\\/g, '/'); + const match = relativePath.match(/[^\/]+(\/server|\/client)?/); + fancyLog(`Finished ${ansiColors.green('packaging web extension')} ${ansiColors.cyan(match[0])} with ${stats.errors.length} errors.`); + } + if (Array.isArray(stats.errors)) { + stats.errors.forEach(error => { + fancyLog.error(error); + }); + } + if (Array.isArray(stats.warnings)) { + stats.warnings.forEach(warning => { + fancyLog.warn(warning); + }); + } + } + } + } + return new Promise((resolve, reject) => { + if (isWatch) { + webpack(webpackConfigs).watch({}, (err, stats) => { + if (err) { + reject(); + } else { + reporter(stats.toJson()); + } + }); + } else { + webpack(webpackConfigs).run((err, stats) => { + if (err) { + fancyLog.error(err); + reject(); + } else { + reporter(stats.toJson()); + resolve(); + } + }); + } + }); +} + + diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js index 75c8413ae5d..fd1d8ceeed9 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -41,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', @@ -59,6 +59,7 @@ const indentationFilter = [ // 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,7 +85,7 @@ const indentationFilter = [ '!src/typings/**/*.d.ts', '!extensions/**/*.d.ts', '!**/*.{svg,exe,png,bmp,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns,plist}', - '!build/{lib,download}/**/*.js', + '!build/{lib,download,darwin}/**/*.js', '!build/**/*.sh', '!build/azure-pipelines/**/*.js', '!build/azure-pipelines/**/*.config', @@ -123,7 +124,7 @@ const copyrightFilter = [ '!extensions/html-language-features/server/src/modes/typescript/*', '!extensions/*/server/bin/*', '!src/vs/editor/test/node/classification/typescript-test.ts', - '!scripts/code-web.js' + '!resources/serverless/code-web.js' ]; const jsHygieneFilter = [ diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 25b41bd3a91..1bf7d36a9f6 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -37,16 +37,12 @@ const { compileBuildTask } = require('./gulpfile.compile'); const { compileExtensionsBuildTask } = require('./gulpfile.extensions'); const productionDependencies = deps.getProductionDependencies(path.dirname(__dirname)); -const baseModules = Object.keys(process.binding('natives')).filter(n => !/^_|\//.test(n)); -const nodeModules = ['electron', 'original-fs'] - .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.workbenchDesktop, buildfile.code ]); @@ -58,27 +54,31 @@ 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/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/platform/auth/common/auth.css', '!**/test/**' ]; @@ -89,7 +89,7 @@ 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', bundleInfo: undefined }) @@ -100,12 +100,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); @@ -154,6 +148,7 @@ 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', diff --git a/build/gulpfile.vscode.win32.js b/build/gulpfile.vscode.win32.js index b0956371451..2abc39976b4 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 => { @@ -145,6 +148,7 @@ 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 diff --git a/build/lib/builtInExtensions.js b/build/lib/builtInExtensions.js index ebcf8bc8ddb..64cb4deee29 100644 --- a/build/lib/builtInExtensions.js +++ b/build/lib/builtInExtensions.js @@ -100,7 +100,7 @@ function writeControlFile(control) { fs.writeFileSync(controlFilePath, JSON.stringify(control, null, 2)); } -function main() { +exports.getBuiltInExtensions = function getBuiltInExtensions() { log('Syncronizing built-in extensions...'); log(`You can manage built-in extensions with the ${ansiColors.cyan('--builtin')} flag`); @@ -116,14 +116,16 @@ function main() { writeControlFile(control); - es.merge(streams) - .on('error', err => { - console.error(err); - process.exit(1); - }) - .on('end', () => { - process.exit(0); - }); -} + return new Promise((resolve, reject) => { + es.merge(streams) + .on('error', reject) + .on('end', resolve); + }); +}; -main(); +if (require.main === module) { + exports.getBuiltInExtensions().then(() => process.exit(0)).catch(err => { + console.error(err); + process.exit(1); + }); +} diff --git a/build/lib/compilation.js b/build/lib/compilation.js index c4a3230424b..07cf3165541 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -18,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) { @@ -69,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 578fae31a19..11a0c7f2da7 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'); @@ -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 abf6baab419..bb71f14c12d 100644 --- a/build/lib/electron.js +++ b/build/lib/electron.js @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); -exports.config = exports.getElectronVersion = void 0; +exports.config = void 0; const fs = require("fs"); const path = require("path"); const vfs = require("vinyl-fs"); @@ -16,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 { @@ -33,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', @@ -70,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', @@ -100,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 86c7afcf312..e0beca78079 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', @@ -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', @@ -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/vscode-dts-literal-or-types.js b/build/lib/eslint/vscode-dts-literal-or-types.js index 02e6de876ba..e07dfc6de28 100644 --- a/build/lib/eslint/vscode-dts-literal-or-types.js +++ b/build/lib/eslint/vscode-dts-literal-or-types.js @@ -13,6 +13,10 @@ module.exports = new class ApiLiteralOrTypes { 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 index 01a3eb21523..fe4befd84e7 100644 --- a/build/lib/eslint/vscode-dts-literal-or-types.ts +++ b/build/lib/eslint/vscode-dts-literal-or-types.ts @@ -15,6 +15,9 @@ export = new class ApiLiteralOrTypes implements eslint.Rule.RuleModule { 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 32c2f56b47c..9cc40c4e1be 100644 --- a/build/lib/extensions.js +++ b/build/lib/extensions.js @@ -4,7 +4,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); -exports.packageMarketplaceExtensionsStream = exports.packageLocalExtensionsStream = exports.fromMarketplace = void 0; +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"); @@ -22,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(tmLanguageJsonFilter) + .pipe(jsonFilter) .pipe(buffer()) .pipe(es.mapSync((f) => { - f.contents = Buffer.from(JSON.stringify(JSON.parse(f.contents.toString('utf8')))); + 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(tmLanguageJsonFilter.restore); + .pipe(jsonFilter.restore); } -function fromLocalWebpack(extensionPath) { +function updateExtensionPackageJSON(input, update) { + const packageJsonFilter = filter('extensions/*/package.json', { restore: true }); + return input + .pipe(packageJsonFilter) + .pipe(buffer()) + .pipe(es.mapSync((f) => { + const data = JSON.parse(f.contents.toString('utf8')); + f.contents = Buffer.from(JSON.stringify(update(data))); + return f; + })) + .pipe(packageJsonFilter.restore); +} +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); @@ -64,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)))}...`); @@ -121,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); @@ -181,38 +193,134 @@ function fromMarketplace(extensionName, version, metadata) { exports.fromMarketplace = fromMarketplace; const excludedExtensions = [ 'vscode-api-tests', - 'vscode-web-playground', 'vscode-colorize-tests', 'vscode-test-resolver', 'ms-vscode.node-debug', 'ms-vscode.node-debug2', - 'vscode-notebook-tests' + 'vscode-notebook-tests', + 'vscode-custom-editor-tests', ]; -const builtInExtensions = JSON.parse(fs.readFileSync(path.join(__dirname, '../../product.json'), 'utf8')).builtInExtensions; -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 }) => (name === 'vscode-web-playground' ? forWeb : true)) // package vscode-web-playground only for web .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 c6f54bfb354..7e529f17cb8 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -21,6 +21,7 @@ import * as fancyLog from 'fancy-log'; import * as ansiColors from 'ansi-colors'; const buffer = require('gulp-buffer'); import json = require('gulp-json-editor'); +import * as jsoncParser from 'jsonc-parser'; const webpack = require('webpack'); const webpackGulp = require('webpack-stream'); const util = require('./util'); @@ -28,31 +29,67 @@ const root = path.dirname(path.dirname(__dirname)); const commit = util.getVersion(root); const sourceMappingURLBase = `https://ticino.blob.core.windows.net/sourcemaps/${commit}`; -function fromLocal(extensionPath: string): Stream { - const webpackFilename = path.join(extensionPath, 'extension.webpack.config.js'); - const input = fs.existsSync(webpackFilename) - ? fromLocalWebpack(extensionPath) - : fromLocalNormal(extensionPath); - - const tmLanguageJsonFilter = filter('**/*.tmLanguage.json', { restore: true }); - +function minifyExtensionResources(input: Stream): Stream { + const jsonFilter = filter(['**/*.json', '**/*.code-snippets'], { restore: true }); return input - .pipe(tmLanguageJsonFilter) + .pipe(jsonFilter) .pipe(buffer()) .pipe(es.mapSync((f: File) => { - f.contents = Buffer.from(JSON.stringify(JSON.parse(f.contents.toString('utf8')))); + const errors: jsoncParser.ParseError[] = []; + const value = jsoncParser.parse(f.contents.toString('utf8'), errors); + if (errors.length === 0) { + // file parsed OK => just stringify to drop whitespace and comments + f.contents = Buffer.from(JSON.stringify(value)); + } return f; })) - .pipe(tmLanguageJsonFilter.restore); + .pipe(jsonFilter.restore); } -function fromLocalWebpack(extensionPath: string): Stream { +function updateExtensionPackageJSON(input: Stream, update: (data: any) => any): Stream { + const packageJsonFilter = filter('extensions/*/package.json', { restore: true }); + return input + .pipe(packageJsonFilter) + .pipe(buffer()) + .pipe(es.mapSync((f: File) => { + const data = JSON.parse(f.contents.toString('utf8')); + f.contents = Buffer.from(JSON.stringify(update(data))); + return f; + })) + .pipe(packageJsonFilter.restore); +} + +function fromLocal(extensionPath: string, forWeb: boolean): Stream { + const webpackConfigFileName = forWeb ? 'extension-browser.webpack.config.js' : 'extension.webpack.config.js'; + + const isWebPacked = fs.existsSync(path.join(extensionPath, webpackConfigFileName)); + let input = isWebPacked + ? fromLocalWebpack(extensionPath, webpackConfigFileName) + : fromLocalNormal(extensionPath); + + if (isWebPacked) { + input = updateExtensionPackageJSON(input, (data: any) => { + delete data.scripts; + delete data.dependencies; + delete data.devDependencies; + if (data.main) { + data.main = data.main.replace('/out/', /dist/); + } + return data; + }); + } + + return input; +} + + +function fromLocalWebpack(extensionPath: string, webpackConfigFileName: string): Stream { const result = es.through(); const packagedDependencies: string[] = []; const packageJsonConfig = require(path.join(extensionPath, 'package.json')); if (packageJsonConfig.dependencies) { - const webpackRootConfig = require(path.join(extensionPath, 'extension.webpack.config.js')); + const webpackRootConfig = require(path.join(extensionPath, webpackConfigFileName)); for (const key in webpackRootConfig.externals) { if (key in packageJsonConfig.dependencies) { packagedDependencies.push(key); @@ -70,38 +107,13 @@ function fromLocalWebpack(extensionPath: string): Stream { contents: fs.createReadStream(filePath) as any })); - const filesStream = es.readArray(files); - // check for a webpack configuration files, then invoke webpack - // and merge its output with the files stream. also rewrite the package.json - // file to a new entry point + // and merge its output with the files stream. const webpackConfigLocations = (glob.sync( - path.join(extensionPath, '/**/extension.webpack.config.js'), + path.join(extensionPath, '**', webpackConfigFileName), { ignore: ['**/node_modules'] } )); - const packageJsonFilter = filter(f => { - if (path.basename(f.path) === 'package.json') { - // only modify package.json's next to the webpack file. - // to be safe, use existsSync instead of path comparison. - return fs.existsSync(path.join(path.dirname(f.path), 'extension.webpack.config.js')); - } - return false; - }, { restore: true }); - - const patchFilesStream = filesStream - .pipe(packageJsonFilter) - .pipe(buffer()) - .pipe(json((data: any) => { - if (data.main) { - // hardcoded entry point directory! - data.main = data.main.replace('/out/', /dist/); - } - return data; - })) - .pipe(packageJsonFilter.restore); - - const webpackStreams = webpackConfigLocations.map(webpackConfigPath => { const webpackDone = (err: any, stats: any) => { @@ -143,7 +155,7 @@ function fromLocalWebpack(extensionPath: string): Stream { })); }); - es.merge(...webpackStreams, patchFilesStream) + es.merge(...webpackStreams, es.readArray(files)) // .pipe(es.through(function (data) { // // debug // console.log('out', data.path, data.contents.length); @@ -212,15 +224,18 @@ export function fromMarketplace(extensionName: string, version: string, metadata .pipe(json({ __metadata: metadata })) .pipe(packageJsonFilter.restore); } - const excludedExtensions = [ 'vscode-api-tests', - 'vscode-web-playground', 'vscode-colorize-tests', 'vscode-test-resolver', 'ms-vscode.node-debug', 'ms-vscode.node-debug2', - 'vscode-notebook-tests' + 'vscode-notebook-tests', + 'vscode-custom-editor-tests', +]; + +const marketplaceWebExtensions = [ + 'ms-vscode.references-view' ]; interface IBuiltInExtension { @@ -230,34 +245,154 @@ interface IBuiltInExtension { metadata: any; } -const builtInExtensions: IBuiltInExtension[] = JSON.parse(fs.readFileSync(path.join(__dirname, '../../product.json'), 'utf8')).builtInExtensions; +const productJson = JSON.parse(fs.readFileSync(path.join(__dirname, '../../product.json'), 'utf8')); +const builtInExtensions: IBuiltInExtension[] = productJson.builtInExtensions || []; +const webBuiltInExtensions: IBuiltInExtension[] = productJson.webBuiltInExtensions || []; -export function packageLocalExtensionsStream(): NodeJS.ReadWriteStream { - const localExtensionDescriptions = (glob.sync('extensions/*/package.json')) - .map(manifestPath => { - const extensionPath = path.dirname(path.join(root, manifestPath)); - const extensionName = path.basename(extensionPath); - return { name: extensionName, path: extensionPath }; - }) - .filter(({ name }) => excludedExtensions.indexOf(name) === -1) - .filter(({ name }) => builtInExtensions.every(b => b.name !== name)); - - const nodeModules = gulp.src('extensions/node_modules/**', { base: '.' }); - const localExtensions = localExtensionDescriptions.map(extension => { - return fromLocal(extension.path) - .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); - }); - - return es.merge(nodeModules, ...localExtensions) - .pipe(util2.setExecutableBit(['**/*.sh'])); +type ExtensionKind = 'ui' | 'workspace' | 'web'; +interface IExtensionManifest { + main: string; + browser: string; + extensionKind?: ExtensionKind | ExtensionKind[]; +} +/** + * Loosely based on `getExtensionKind` from `src/vs/workbench/services/extensions/common/extensionsUtil.ts` + */ +function isWebExtension(manifest: IExtensionManifest): boolean { + if (typeof manifest.extensionKind !== 'undefined') { + const extensionKind = Array.isArray(manifest.extensionKind) ? manifest.extensionKind : [manifest.extensionKind]; + return (extensionKind.indexOf('web') >= 0); + } + return (!Boolean(manifest.main) || Boolean(manifest.browser)); } -export function packageMarketplaceExtensionsStream(): NodeJS.ReadWriteStream { - const extensions = builtInExtensions.map(extension => { - return fromMarketplace(extension.name, extension.version, extension.metadata) - .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); - }); +export function packageLocalExtensionsStream(forWeb: boolean): Stream { + const localExtensionsDescriptions = ( + (glob.sync('extensions/*/package.json')) + .map(manifestPath => { + const absoluteManifestPath = path.join(root, manifestPath); + const extensionPath = path.dirname(path.join(root, manifestPath)); + const extensionName = path.basename(extensionPath); + return { name: extensionName, path: extensionPath, manifestPath: absoluteManifestPath }; + }) + .filter(({ name }) => (name === 'vscode-web-playground' ? forWeb : true)) // package vscode-web-playground only for web + .filter(({ name }) => excludedExtensions.indexOf(name) === -1) + .filter(({ name }) => builtInExtensions.every(b => b.name !== name)) + .filter(({ manifestPath }) => (forWeb ? isWebExtension(require(manifestPath)) : true)) + ); + const localExtensionsStream = minifyExtensionResources( + es.merge( + ...localExtensionsDescriptions.map(extension => { + return fromLocal(extension.path, forWeb) + .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); + }) + ) + ); - return es.merge(extensions) - .pipe(util2.setExecutableBit(['**/*.sh'])); + let result: Stream; + if (forWeb) { + result = localExtensionsStream; + } else { + // also include shared node modules + result = es.merge(localExtensionsStream, gulp.src('extensions/node_modules/**', { base: '.' })); + } + + return ( + result + .pipe(util2.setExecutableBit(['**/*.sh'])) + ); +} + +export function packageMarketplaceExtensionsStream(forWeb: boolean): Stream { + const marketplaceExtensionsDescriptions = [ + ...builtInExtensions.filter(({ name }) => (forWeb ? marketplaceWebExtensions.indexOf(name) >= 0 : true)), + ...(forWeb ? webBuiltInExtensions : []) + ]; + const marketplaceExtensionsStream = minifyExtensionResources( + es.merge( + ...marketplaceExtensionsDescriptions + .map(extension => { + const input = fromMarketplace(extension.name, extension.version, extension.metadata) + .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); + return updateExtensionPackageJSON(input, (data: any) => { + delete data.scripts; + delete data.dependencies; + delete data.devDependencies; + return data; + }); + }) + ) + ); + + return ( + marketplaceExtensionsStream + .pipe(util2.setExecutableBit(['**/*.sh'])) + ); +} + +export interface IScannedBuiltinExtension { + extensionPath: string; + packageJSON: any; + packageNLS?: any; + readmePath?: string; + changelogPath?: string; +} + +export function scanBuiltinExtensions(extensionsRoot: string, exclude: string[] = []): IScannedBuiltinExtension[] { + const scannedExtensions: IScannedBuiltinExtension[] = []; + + try { + const extensionsFolders = fs.readdirSync(extensionsRoot); + for (const extensionFolder of extensionsFolders) { + if (exclude.indexOf(extensionFolder) >= 0) { + continue; + } + const packageJSONPath = path.join(extensionsRoot, extensionFolder, 'package.json'); + if (!fs.existsSync(packageJSONPath)) { + continue; + } + let packageJSON = JSON.parse(fs.readFileSync(packageJSONPath).toString('utf8')); + if (!isWebExtension(packageJSON)) { + continue; + } + const children = fs.readdirSync(path.join(extensionsRoot, extensionFolder)); + const packageNLSPath = children.filter(child => child === 'package.nls.json')[0]; + const packageNLS = packageNLSPath ? JSON.parse(fs.readFileSync(path.join(extensionsRoot, extensionFolder, packageNLSPath)).toString()) : undefined; + const readme = children.filter(child => /^readme(\.txt|\.md|)$/i.test(child))[0]; + const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0]; + + scannedExtensions.push({ + extensionPath: extensionFolder, + packageJSON, + packageNLS, + readmePath: readme ? path.join(extensionFolder, readme) : undefined, + changelogPath: changelog ? path.join(extensionFolder, changelog) : undefined, + }); + } + return scannedExtensions; + } catch (ex) { + return scannedExtensions; + } +} + +export function translatePackageJSON(packageJSON: string, packageNLSPath: string) { + const CharCode_PC = '%'.charCodeAt(0); + const packageNls = JSON.parse(fs.readFileSync(packageNLSPath).toString()); + const translate = (obj: any) => { + for (let key in obj) { + const val = obj[key]; + if (Array.isArray(val)) { + val.forEach(translate); + } else if (val && typeof val === 'object') { + translate(val); + } else if (typeof val === 'string' && val.charCodeAt(0) === CharCode_PC && val.charCodeAt(val.length - 1) === CharCode_PC) { + const translated = packageNls[val.substr(1, val.length - 2)]; + if (translated) { + obj[key] = translated; + } + } + } + }; + translate(packageJSON); + return packageJSON; } diff --git a/build/lib/i18n.js b/build/lib/i18n.js index 2e7415cd721..7371b0f022f 100644 --- a/build/lib/i18n.js +++ b/build/lib/i18n.js @@ -16,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); @@ -101,161 +101,158 @@ class TextModel { return this._lines; } } -let XLF = /** @class */ (() => { - class XLF { - constructor(project) { - this.project = project; - this.buffer = []; - this.files = Object.create(null); - this.numberOfMessages = 0; +class XLF { + constructor(project) { + this.project = project; + this.buffer = []; + this.files = Object.create(null); + this.numberOfMessages = 0; + } + toString() { + this.appendHeader(); + for (let file in this.files) { + this.appendNewLine(``, 2); + for (let item of this.files[file]) { + this.addStringItem(file, item); + } + this.appendNewLine('', 2); } - toString() { - this.appendHeader(); - for (let file in this.files) { - this.appendNewLine(``, 2); - for (let item of this.files[file]) { - this.addStringItem(file, item); + this.appendFooter(); + return this.buffer.join('\r\n'); + } + addFile(original, keys, messages) { + if (keys.length === 0) { + console.log('No keys in ' + original); + return; + } + if (keys.length !== messages.length) { + throw new Error(`Unmatching keys(${keys.length}) and messages(${messages.length}).`); + } + this.numberOfMessages += keys.length; + this.files[original] = []; + let existingKeys = new Set(); + for (let i = 0; i < keys.length; i++) { + let key = keys[i]; + let realKey; + let comment; + if (Is.string(key)) { + realKey = key; + comment = undefined; + } + else if (LocalizeInfo.is(key)) { + realKey = key.key; + if (key.comment && key.comment.length > 0) { + comment = key.comment.map(comment => encodeEntities(comment)).join('\r\n'); } - this.appendNewLine('', 2); } - this.appendFooter(); - return this.buffer.join('\r\n'); - } - addFile(original, keys, messages) { - if (keys.length === 0) { - console.log('No keys in ' + original); - return; + if (!realKey || existingKeys.has(realKey)) { + continue; } - if (keys.length !== messages.length) { - throw new Error(`Unmatching keys(${keys.length}) and messages(${messages.length}).`); - } - this.numberOfMessages += keys.length; - this.files[original] = []; - let existingKeys = new Set(); - for (let i = 0; i < keys.length; i++) { - let key = keys[i]; - let realKey; - let comment; - if (Is.string(key)) { - realKey = key; - comment = undefined; - } - else if (LocalizeInfo.is(key)) { - realKey = key.key; - if (key.comment && key.comment.length > 0) { - comment = key.comment.map(comment => encodeEntities(comment)).join('\r\n'); - } - } - if (!realKey || existingKeys.has(realKey)) { - continue; - } - existingKeys.add(realKey); - let message = encodeEntities(messages[i]); - this.files[original].push({ id: realKey, message: message, comment: comment }); - } - } - addStringItem(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); - if (item.comment) { - this.appendNewLine(`${item.comment}`, 6); - } - this.appendNewLine('', 4); - } - appendHeader() { - this.appendNewLine('', 0); - this.appendNewLine('', 0); - } - appendFooter() { - this.appendNewLine('', 0); - } - appendNewLine(content, indent) { - let line = new Line(indent); - line.append(content); - this.buffer.push(line.toString()); + existingKeys.add(realKey); + let message = encodeEntities(messages[i]); + this.files[original].push({ id: realKey, message: message, comment: comment }); } } - XLF.parsePseudo = function (xlfString) { - return new Promise((resolve) => { - let parser = new xml2js.Parser(); - let files = []; - parser.parseString(xlfString, function (_err, result) { - const fileNodes = result['xliff']['file']; - fileNodes.forEach(file => { - const originalFilePath = file.$.original; - const messages = {}; - const transUnits = file.body[0]['trans-unit']; - if (transUnits) { - transUnits.forEach((unit) => { - const key = unit.$.id; - const val = pseudify(unit.source[0]['_'].toString()); - if (key && val) { - messages[key] = decodeEntities(val); - } - }); - files.push({ messages: messages, originalFilePath: originalFilePath, language: 'ps' }); - } - }); - resolve(files); - }); - }); - }; - XLF.parse = function (xlfString) { - return new Promise((resolve, reject) => { - let parser = new xml2js.Parser(); - let files = []; - parser.parseString(xlfString, function (err, result) { - if (err) { - reject(new Error(`XLF parsing error: Failed to parse XLIFF string. ${err}`)); - } - const fileNodes = result['xliff']['file']; - if (!fileNodes) { - reject(new Error(`XLF parsing error: XLIFF file does not contain "xliff" or "file" node(s) required for parsing.`)); - } - fileNodes.forEach((file) => { - const originalFilePath = file.$.original; - if (!originalFilePath) { - reject(new Error(`XLF parsing error: XLIFF file node does not contain original attribute to determine the original location of the resource file.`)); - } - let language = file.$['target-language']; - if (!language) { - reject(new Error(`XLF parsing error: XLIFF file node does not contain target-language attribute to determine translated language.`)); - } - const messages = {}; - const transUnits = file.body[0]['trans-unit']; - if (transUnits) { - transUnits.forEach((unit) => { - const key = unit.$.id; - if (!unit.target) { - return; // No translation available - } - let val = unit.target[0]; - if (typeof val !== 'string') { - val = val._; - } - if (key && val) { - messages[key] = decodeEntities(val); - } - else { - reject(new Error(`XLF parsing error: XLIFF file ${originalFilePath} does not contain full localization data. ID or target translation for one of the trans-unit nodes is not present.`)); - } - }); - files.push({ messages: messages, originalFilePath: originalFilePath, language: language.toLowerCase() }); - } - }); - resolve(files); - }); - }); - }; - return XLF; -})(); + 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); + if (item.comment) { + this.appendNewLine(`${item.comment}`, 6); + } + this.appendNewLine('', 4); + } + appendHeader() { + this.appendNewLine('', 0); + this.appendNewLine('', 0); + } + appendFooter() { + this.appendNewLine('', 0); + } + appendNewLine(content, indent) { + let line = new Line(indent); + line.append(content); + this.buffer.push(line.toString()); + } +} exports.XLF = XLF; +XLF.parsePseudo = function (xlfString) { + return new Promise((resolve) => { + let parser = new xml2js.Parser(); + let files = []; + parser.parseString(xlfString, function (_err, result) { + const fileNodes = result['xliff']['file']; + fileNodes.forEach(file => { + const originalFilePath = file.$.original; + const messages = {}; + const transUnits = file.body[0]['trans-unit']; + if (transUnits) { + transUnits.forEach((unit) => { + const key = unit.$.id; + const val = pseudify(unit.source[0]['_'].toString()); + if (key && val) { + messages[key] = decodeEntities(val); + } + }); + files.push({ messages: messages, originalFilePath: originalFilePath, language: 'ps' }); + } + }); + resolve(files); + }); + }); +}; +XLF.parse = function (xlfString) { + return new Promise((resolve, reject) => { + let parser = new xml2js.Parser(); + let files = []; + parser.parseString(xlfString, function (err, result) { + if (err) { + reject(new Error(`XLF parsing error: Failed to parse XLIFF string. ${err}`)); + } + const fileNodes = result['xliff']['file']; + if (!fileNodes) { + reject(new Error(`XLF parsing error: XLIFF file does not contain "xliff" or "file" node(s) required for parsing.`)); + } + fileNodes.forEach((file) => { + const originalFilePath = file.$.original; + if (!originalFilePath) { + reject(new Error(`XLF parsing error: XLIFF file node does not contain original attribute to determine the original location of the resource file.`)); + } + let language = file.$['target-language']; + if (!language) { + reject(new Error(`XLF parsing error: XLIFF file node does not contain target-language attribute to determine translated language.`)); + } + const messages = {}; + const transUnits = file.body[0]['trans-unit']; + if (transUnits) { + transUnits.forEach((unit) => { + const key = unit.$.id; + if (!unit.target) { + return; // No translation available + } + let val = unit.target[0]; + if (typeof val !== 'string') { + val = val._; + } + if (key && val) { + messages[key] = decodeEntities(val); + } + else { + reject(new Error(`XLF parsing error: XLIFF file ${originalFilePath} does not contain full localization data. ID or target translation for one of the trans-unit nodes is not present.`)); + } + }); + files.push({ messages: messages, originalFilePath: originalFilePath, language: language.toLowerCase() }); + } + }); + resolve(files); + }); + }); +}; class Limiter { constructor(maxDegreeOfParalellism) { this.maxDegreeOfParalellism = maxDegreeOfParalellism; @@ -1144,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('='); @@ -1178,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 dd2b3cd1d60..9bba404c243 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -138,6 +138,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" @@ -214,6 +218,10 @@ "name": "vs/workbench/contrib/userDataSync", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/views", + "project": "vscode-workbench" + }, { "name": "vs/workbench/services/actions", "project": "vscode-workbench" @@ -338,6 +346,10 @@ "name": "vs/workbench/services/userDataSync", "project": "vscode-workbench" }, + { + "name": "vs/workbench/services/views", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/timeline", "project": "vscode-workbench" diff --git a/build/lib/i18n.ts b/build/lib/i18n.ts index b9fb3879872..89131b0129f 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; @@ -1308,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]; @@ -1339,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 index 663769dacea..f3cc2252b5e 100644 --- a/build/lib/layersChecker.js +++ b/build/lib/layersChecker.js @@ -130,6 +130,14 @@ const RULES = [ '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/**', diff --git a/build/lib/layersChecker.ts b/build/lib/layersChecker.ts index 0dcedb8d4e3..508d2b986e9 100644 --- a/build/lib/layersChecker.ts +++ b/build/lib/layersChecker.ts @@ -143,6 +143,15 @@ const RULES = [ ] }, + // Electron (sandbox) + { + target: '**/vs/**/electron-sandbox/**', + allowedTypes: CORE_TYPES, + disallowedDefinitions: [ + '@types/node' // no node.js + ] + }, + // Electron (renderer): skip { target: '**/vs/**/electron-browser/**', diff --git a/build/lib/optimize.js b/build/lib/optimize.js index 5098a093f9e..5403c105620 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -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; @@ -70,7 +70,7 @@ function loader(src, bundledFileHeader, bundleLoader) { })) .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 @@ -91,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) @@ -102,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 = [ @@ -120,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 @@ -128,7 +131,7 @@ function optimizeTask(opts) { if (err || !result) { 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) { diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index 973b843aa59..2822803f9f7 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -18,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'; @@ -32,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 }; @@ -48,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` @@ -84,7 +79,7 @@ function loader(src: string, bundledFileHeader: string, bundleLoader: boolean): ); } -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 @@ -108,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) }); }); @@ -122,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); })); } @@ -162,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 = [ @@ -178,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 @@ -187,7 +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)); } - toBundleStream(src, bundledFileHeader, result.files).pipe(bundlesStream); + toBundleStream(src, bundledFileHeader, result.files, fileContentMapper).pipe(bundlesStream); // Remove css inlined resources const filteredResources = resources.slice(); diff --git a/build/lib/preLaunch.js b/build/lib/preLaunch.js new file mode 100644 index 00000000000..1aecbe19048 --- /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 00000000000..bd084f5fec5 --- /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/util.js b/build/lib/util.js index d42670e67a5..e552a036f89 100644 --- a/build/lib/util.js +++ b/build/lib/util.js @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); -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; +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"); @@ -14,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(); @@ -137,7 +138,7 @@ function loadSourcemaps() { version: '3', names: [], mappings: '', - sources: [f.relative.replace(/\//g, '/')], + sources: [f.relative], sourcesContent: [contents] }; cb(undefined, f); @@ -255,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 45b6c9e1b82..035c7e95ea3 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] }; @@ -318,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/npm/postinstall.js b/build/npm/postinstall.js index 7a2320d8289..8f8b0019a77 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -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 => { @@ -72,3 +73,5 @@ yarnInstall('test/automation'); // node modules required for smoketest yarnInstall('test/smoke'); // node modules required for smoketest yarnInstall('test/integration/browser'); // node modules required for integration yarnInstallBuildDependencies(); // node modules for watching, specific to host node version, not electron + +cp.execSync('git config pull.rebase true'); diff --git a/build/package.json b/build/package.json index ae605126424..f6b1dbbcd21 100644 --- a/build/package.json +++ b/build/package.json @@ -33,19 +33,21 @@ "@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", "minimatch": "3.0.4", "minimist": "^1.2.3", "request": "^2.85.0", "terser": "4.3.8", - "typescript": "^3.9.1-rc", + "typescript": "^4.0.0-dev.20200729", "vsce": "1.48.0", - "vscode-telemetry-extractor": "^1.5.4", + "vscode-telemetry-extractor": "^1.6.0", "xml2js": "^0.4.17" }, "scripts": { diff --git a/build/polyfills/vscode-extension-telemetry.js b/build/polyfills/vscode-extension-telemetry.js new file mode 100644 index 00000000000..d038776c59c --- /dev/null +++ b/build/polyfills/vscode-extension-telemetry.js @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); + +let TelemetryReporter = (function () { + function TelemetryReporter(extensionId, extensionVersion, key) { + } + TelemetryReporter.prototype.updateUserOptIn = function (key) { + }; + TelemetryReporter.prototype.createAppInsightsClient = function (key) { + }; + TelemetryReporter.prototype.getCommonProperties = function () { + }; + TelemetryReporter.prototype.sendTelemetryEvent = function (eventName, properties, measurements) { + }; + TelemetryReporter.prototype.dispose = function () { + }; + TelemetryReporter.TELEMETRY_CONFIG_ID = 'telemetry'; + TelemetryReporter.TELEMETRY_CONFIG_ENABLED_ID = 'enableTelemetry'; + return TelemetryReporter; +}()); +exports.default = TelemetryReporter; diff --git a/build/polyfills/vscode-nls.js b/build/polyfills/vscode-nls.js new file mode 100644 index 00000000000..b89250102af --- /dev/null +++ b/build/polyfills/vscode-nls.js @@ -0,0 +1,79 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); + +function format(message, args) { + let result; + // if (isPseudo) { + // // FF3B and FF3D is the Unicode zenkaku representation for [ and ] + // message = '\uFF3B' + message.replace(/[aouei]/g, '$&$&') + '\uFF3D'; + // } + if (args.length === 0) { + result = message; + } + else { + result = message.replace(/\{(\d+)\}/g, function (match, rest) { + let index = rest[0]; + let arg = args[index]; + let replacement = match; + if (typeof arg === 'string') { + replacement = arg; + } + else if (typeof arg === 'number' || typeof arg === 'boolean' || arg === void 0 || arg === null) { + replacement = String(arg); + } + return replacement; + }); + } + return result; +} + +function localize(key, message) { + let args = []; + for (let _i = 2; _i < arguments.length; _i++) { + args[_i - 2] = arguments[_i]; + } + return format(message, args); +} + +function loadMessageBundle(file) { + return localize; +} + +let MessageFormat; +(function (MessageFormat) { + MessageFormat["file"] = "file"; + MessageFormat["bundle"] = "bundle"; + MessageFormat["both"] = "both"; +})(MessageFormat = exports.MessageFormat || (exports.MessageFormat = {})); +let BundleFormat; +(function (BundleFormat) { + // the nls.bundle format + BundleFormat["standalone"] = "standalone"; + BundleFormat["languagePack"] = "languagePack"; +})(BundleFormat = exports.BundleFormat || (exports.BundleFormat = {})); + +exports.loadMessageBundle = loadMessageBundle; +function config(opts) { + if (opts) { + if (isString(opts.locale)) { + options.locale = opts.locale.toLowerCase(); + options.language = options.locale; + resolvedLanguage = undefined; + resolvedBundles = Object.create(null); + } + if (opts.messageFormat !== undefined) { + options.messageFormat = opts.messageFormat; + } + if (opts.bundleFormat === BundleFormat.standalone && options.languagePackSupport === true) { + options.languagePackSupport = false; + } + } + isPseudo = options.locale === 'pseudo'; + return loadMessageBundle; +} +exports.config = config; diff --git a/build/tsconfig.json b/build/tsconfig.json index df15ccdd1be..f075fe3d4f5 100644 --- a/build/tsconfig.json +++ b/build/tsconfig.json @@ -14,7 +14,8 @@ "checkJs": true, "strict": true, "noUnusedLocals": true, - "noUnusedParameters": true + "noUnusedParameters": true, + "newLine": "lf" }, "include": [ "**/*.ts" diff --git a/build/win32/code.iss b/build/win32/code.iss index 15293a0c5cf..195e63cf12a 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 @@ -238,6 +243,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 @@ -1003,7 +1015,7 @@ begin Result := True; #if "user" == InstallTarget - if not WizardSilent() and IsAdminLoggedOn() then begin + if not WizardSilent() and IsAdmin() then begin if MsgBox('This User Installer is not meant to be run as an Administrator. If you would like to install VS Code for all users in this system, download the System Installer instead from https://code.visualstudio.com. Are you sure you want to continue?', mbError, MB_OKCANCEL) = IDCANCEL then begin Result := False; end; @@ -1011,7 +1023,7 @@ begin #endif #if "user" == InstallTarget - #if "ia32" == Arch + #if "ia32" == Arch || "arm64" == Arch #define IncompatibleArchRootKey "HKLM32" #else #define IncompatibleArchRootKey "HKLM64" @@ -1121,7 +1133,7 @@ begin end; end; -// http://stackoverflow.com/a/23838239/261019 +// https://stackoverflow.com/a/23838239/261019 procedure Explode(var Dest: TArrayOfString; Text: String; Separator: String); var i, p: Integer; diff --git a/build/win32/i18n/Default.hu.isl b/build/win32/i18n/Default.hu.isl new file mode 100644 index 00000000000..8c57d20a59a --- /dev/null +++ b/build/win32/i18n/Default.hu.isl @@ -0,0 +1,366 @@ +;Inno Setup version 6.0.3+ Hungarian messages +;Based on the translation of Kornél Pál, kornelpal@gmail.com +;István 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=Telepítõ +SetupWindowTitle=%1 - Telepítõ +UninstallAppTitle=Eltávolítķ +UninstallAppFullTitle=%1 Eltávolítķ + +; *** Misc. common +InformationTitle=Informáciķk +ConfirmTitle=Megerõsít +ErrorTitle=Hiba + +; *** SetupLdr messages +SetupLdrStartupMessage=%1 telepítve lesz. Szeretné folytatni? +LdrCannotCreateTemp=Átmeneti fájl létrehozása nem lehetséges. A telepítés megszakítva +LdrCannotExecTemp=Fájl futtatása nem lehetséges az átmeneti könyvtárban. A telepítés megszakítva +HelpTextNote= + +; *** Startup error messages +LastErrorMessage=%1.%n%nHiba %2: %3 +SetupFileMissing=A(z) %1 fájl hiányzik a telepítõ könyvtárábķl. Kérem hárítsa el a problémát, vagy szerezzen be egy másik példányt a programbķl! +SetupFileCorrupt=A telepítési fájlok sérültek. Kérem, szerezzen be új másolatot a programbķl! +SetupFileCorruptOrWrongVer=A telepítési fájlok sérültek, vagy inkompatibilisek a telepítõ ezen verziķjával. Hárítsa el a problémát, vagy szerezzen be egy másik példányt a programbķl! +InvalidParameter=A parancssorba átadott paraméter érvénytelen:%n%n%1 +SetupAlreadyRunning=A Telepítõ már fut. +WindowsVersionNotSupported=A program nem támogatja a Windows ezen verziķját. +WindowsServicePackRequired=A program futtatásához %1 Service Pack %2 vagy újabb szükséges. +NotOnThisPlatform=Ez a program nem futtathatķ %1 alatt. +OnlyOnThisPlatform=Ezt a programot %1 alatt kell futtatni. +OnlyOnTheseArchitectures=A program kizárķlag a következõ processzor architektúrákhoz tervezett Windows-on telepíthetõ:%n%n%1 +WinVersionTooLowError=A program futtatásához %1 %2 verziķja vagy késõbbi szükséges. +WinVersionTooHighError=Ez a program nem telepíthetõ %1 %2 vagy késõbbire. +AdminPrivilegesRequired=Csak rendszergazdai mķdban telepíthetõ ez a program. +PowerUserPrivilegesRequired=Csak rendszergazdaként vagy kiemelt felhasználķként telepíthetõ ez a program. +SetupAppRunningError=A telepítõ úgy észlelte %1 jelenleg fut.%n%nZárja be az összes példányt, majd kattintson az 'OK'-ra a folytatáshoz, vagy a 'Mégse'-re a kilépéshez. +UninstallAppRunningError=Az eltávolítķ úgy észlelte %1 jelenleg fut.%n%nZárja be az összes példányt, majd kattintson az 'OK'-ra a folytatáshoz, vagy a 'Mégse'-re a kilépéshez. + +; *** Startup questions +PrivilegesRequiredOverrideTitle=Telepítési mķd kiválasztása +PrivilegesRequiredOverrideInstruction=Válasszon telepítési mķdot +PrivilegesRequiredOverrideText1=%1 telepíthetõ az összes felhasználķnak (rendszergazdai jogok szükségesek), vagy csak magának. +PrivilegesRequiredOverrideText2=%1 csak magának telepíthetõ, vagy az összes felhasználķnak (rendszergazdai jogok szükségesek). +PrivilegesRequiredOverrideAllUsers=Telepítés &mindenkinek +PrivilegesRequiredOverrideAllUsersRecommended=Telepítés &mindenkinek (ajánlott) +PrivilegesRequiredOverrideCurrentUser=Telepítés csak &nekem +PrivilegesRequiredOverrideCurrentUserRecommended=Telepítés csak &nekem (ajánlott) + +; *** Misc. errors +ErrorCreatingDir=A Telepítõ nem tudta létrehozni a(z) "%1" könyvtárat +ErrorTooManyFilesInDir=Nem hozhatķ létre fájl a(z) "%1" könyvtárban, mert az már túl sok fájlt tartalmaz + +; *** Setup common messages +ExitSetupTitle=Kilépés a telepítõbõl +ExitSetupMessage=A telepítés még folyamatban van. Ha most kilép, a program nem kerül telepítésre.%n%nMásik alkalommal is futtathatķ a telepítés befejezéséhez%n%nKilép a telepítõbõl? +AboutSetupMenuItem=&Névjegy... +AboutSetupTitle=Telepítõ névjegye +AboutSetupMessage=%1 %2 verziķ%n%3%n%nAz %1 honlapja:%n%4 +AboutSetupNote= +TranslatorNote= + +; *** Buttons +ButtonBack=< &Vissza +ButtonNext=&Tovább > +ButtonInstall=&Telepít +ButtonOK=OK +ButtonCancel=Mégse +ButtonYes=&Igen +ButtonYesToAll=&Mindet +ButtonNo=&Nem +ButtonNoToAll=&Egyiket se +ButtonFinish=&Befejezés +ButtonBrowse=&Tallķzás... +ButtonWizardBrowse=T&allķzás... +ButtonNewFolder=Új &könyvtár + +; *** "Select Language" dialog messages +SelectLanguageTitle=Telepítõ nyelvi beállítás +SelectLanguageLabel=Válassza ki a telepítés alatt használt nyelvet. + +; *** Common wizard text +ClickNext=A folytatáshoz kattintson a 'Tovább'-ra, a kilépéshez a 'Mégse'-re. +BeveledLabel= +BrowseDialogTitle=Válasszon könyvtárt +BrowseDialogLabel=Válasszon egy könyvtárat az alábbi listábķl, majd kattintson az 'OK'-ra. +NewFolderName=Új könyvtár + +; *** "Welcome" wizard page +WelcomeLabel1=Üdvözli a(z) [name] Telepítõvarázslķja. +WelcomeLabel2=A(z) [name/ver] telepítésre kerül a számítķgépén.%n%nAjánlott minden, egyéb futķ alkalmazás bezárása a folytatás elõtt. + +; *** "Password" wizard page +WizardPassword=Jelszķ +PasswordLabel1=Ez a telepítés jelszķval védett. +PasswordLabel3=Kérem adja meg a jelszķt, majd kattintson a 'Tovább'-ra. A jelszavak kis- és nagy betû érzékenyek lehetnek. +PasswordEditLabel=&Jelszķ: +IncorrectPassword=Az ön által megadott jelszķ helytelen. Prķbálja újra. + +; *** "License Agreement" wizard page +WizardLicense=Licencszerzõdés +LicenseLabel=Olvassa el figyelmesen az informáciķkat folytatás elõtt. +LicenseLabel3=Kérem, olvassa el az alábbi licencszerzõdést. A telepítés folytatásához, el kell fogadnia a szerzõdést. +LicenseAccepted=&Elfogadom a szerzõdést +LicenseNotAccepted=&Nem fogadom el a szerzõdést + +; *** "Information" wizard pages +WizardInfoBefore=Informáciķk +InfoBeforeLabel=Olvassa el a következõ fontos informáciķkat a folytatás elõtt. +InfoBeforeClickLabel=Ha készen áll, kattintson a 'Tovább'-ra. +WizardInfoAfter=Informáciķk +InfoAfterLabel=Olvassa el a következõ fontos informáciķkat a folytatás elõtt. +InfoAfterClickLabel=Ha készen áll, kattintson a 'Tovább'-ra. + +; *** "User Information" wizard page +WizardUserInfo=Felhasználķ adatai +UserInfoDesc=Kérem, adja meg az adatait +UserInfoName=&Felhasználķnév: +UserInfoOrg=&Szervezet: +UserInfoSerial=&Sorozatszám: +UserInfoNameRequired=Meg kell adnia egy nevet. + +; *** "Select Destination Location" wizard page +WizardSelectDir=Válasszon célkönyvtárat +SelectDirDesc=Hova települjön a(z) [name]? +SelectDirLabel3=A(z) [name] az alábbi könyvtárba lesz telepítve. +SelectDirBrowseLabel=A folytatáshoz, kattintson a 'Tovább'-ra. Ha másik könyvtárat választana, kattintson a 'Tallķzás'-ra. +DiskSpaceGBLabel=At least [gb] GB szabad területre van szükség. +DiskSpaceMBLabel=Legalább [mb] MB szabad területre van szükség. +CannotInstallToNetworkDrive=A Telepítõ nem tud hálķzati meghajtķra telepíteni. +CannotInstallToUNCPath=A Telepítõ nem tud hálķzati UNC elérési útra telepíteni. +InvalidPath=Teljes útvonalat adjon meg, a meghajtķ betûjelével; például:%n%nC:\Alkalmazás%n%nvagy egy hálķzati útvonalat a következõ alakban:%n%n\\kiszolgálķ\megosztás +InvalidDrive=A kiválasztott meghajtķ vagy hálķzati megosztás nem létezik vagy nem elérhetõ. Válasszon egy másikat. +DiskSpaceWarningTitle=Nincs elég szabad terület +DiskSpaceWarning=A Telepítõnek legalább %1 KB szabad lemezterületre van szüksége, viszont a kiválasztott meghajtķn csupán %2 KB áll rendelkezésre.%n%nMindenképpen folytatja? +DirNameTooLong=A könyvtár neve vagy az útvonal túl hosszú. +InvalidDirName=A könyvtár neve érvénytelen. +BadDirName32=A könyvtárak nevei ezen karakterek egyikét sem tartalmazhatják:%n%n%1 +DirExistsTitle=A könyvtár már létezik +DirExists=A könyvtár:%n%n%1%n%nmár létezik. Mindenképp ide akar telepíteni? +DirDoesntExistTitle=A könyvtár nem létezik +DirDoesntExist=A könyvtár:%n%n%1%n%nnem létezik. Szeretné létrehozni? + +; *** "Select Components" wizard page +WizardSelectComponents=Összetevõk kiválasztása +SelectComponentsDesc=Mely összetevõk kerüljenek telepítésre? +SelectComponentsLabel2=Jelölje ki a telepítendõ összetevõket; törölje a telepíteni nem kívánt összetevõket. Kattintson a 'Tovább'-ra, ha készen áll a folytatásra. +FullInstallation=Teljes telepítés +; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) +CompactInstallation=Szokásos telepítés +CustomInstallation=Egyéni telepítés +NoUninstallWarningTitle=Létezõ összetevõ +NoUninstallWarning=A telepítõ úgy találta, hogy a következõ összetevõk már telepítve vannak a számítķgépre:%n%n%1%n%nEzen összetevõk kijelölésének törlése, nem távolítja el azokat a számítķgéprõl.%n%nMindenképpen folytatja? +ComponentSize1=%1 KB +ComponentSize2=%1 MB +ComponentsDiskSpaceMBLabel=A jelenlegi kijelölés legalább [gb] GB lemezterületet igényel. +ComponentsDiskSpaceMBLabel=A jelenlegi kijelölés legalább [mb] MB lemezterületet igényel. + +; *** "Select Additional Tasks" wizard page +WizardSelectTasks=További feladatok +SelectTasksDesc=Mely kiegészítõ feladatok kerüljenek végrehajtásra? +SelectTasksLabel2=Jelölje ki, mely kiegészítõ feladatokat hajtsa végre a Telepítõ a(z) [name] telepítése során, majd kattintson a 'Tovább'-ra. + +; *** "Select Start Menu Folder" wizard page +WizardSelectProgramGroup=Start Menü könyvtára +SelectStartMenuFolderDesc=Hova helyezze a Telepítõ a program parancsikonjait? +SelectStartMenuFolderLabel3=A Telepítõ a program parancsikonjait a Start menü következõ mappájában fogja létrehozni. +SelectStartMenuFolderBrowseLabel=A folytatáshoz kattintson a 'Tovább'-ra. Ha másik mappát választana, kattintson a 'Tallķzás'-ra. +MustEnterGroupName=Meg kell adnia egy mappanevet. +GroupNameTooLong=A könyvtár neve vagy az útvonal túl hosszú. +InvalidGroupName=A könyvtár neve érvénytelen. +BadGroupName=A könyvtárak nevei ezen karakterek egyikét sem tartalmazhatják:%n%n%1 +NoProgramGroupCheck2=&Ne hozzon létre mappát a Start menüben + +; *** "Ready to Install" wizard page +WizardReady=Készen állunk a telepítésre +ReadyLabel1=A Telepítõ készen áll, a(z) [name] számítķgépre telepítéshez. +ReadyLabel2a=Kattintson a 'Telepítés'-re a folytatáshoz, vagy a "Vissza"-ra a beállítások áttekintéséhez vagy megváltoztatásához. +ReadyLabel2b=Kattintson a 'Telepítés'-re a folytatáshoz. +ReadyMemoUserInfo=Felhasználķ adatai: +ReadyMemoDir=Telepítés célkönyvtára: +ReadyMemoType=Telepítés típusa: +ReadyMemoComponents=Választott összetevõk: +ReadyMemoGroup=Start menü mappája: +ReadyMemoTasks=Kiegészítõ feladatok: + +; *** "Preparing to Install" wizard page +WizardPreparing=Felkészülés a telepítésre +PreparingDesc=A Telepítõ felkészül a(z) [name] számítķgépre történõ telepítéshez. +PreviousInstallNotCompleted=gy korábbi program telepítése/eltávolítása nem fejezõdött be. Újra kell indítania a számítķgépét a másik telepítés befejezéséhez.%n%nA számítķgépe újraindítása után ismét futtassa a Telepítõt a(z) [name] telepítésének befejezéséhez. +CannotContinue=A telepítés nem folytathatķ. A kilépéshez kattintson a 'Mégse'-re +ApplicationsFound=A következõ alkalmazások olyan fájlokat használnak, amelyeket a Telepítõnek frissíteni kell. Ajánlott, hogy engedélyezze a Telepítõnek ezen alkalmazások automatikus bezárását. +ApplicationsFound2=A következõ alkalmazások olyan fájlokat használnak, amelyeket a Telepítõnek frissíteni kell. Ajánlott, hogy engedélyezze a Telepítõnek ezen alkalmazások automatikus bezárását. A telepítés befejezése után a Telepítõ megkísérli az alkalmazások újraindítását. +CloseApplications=&Alkalmazások automatikus bezárása +DontCloseApplications=&Ne zárja be az alkalmazásokat +ErrorCloseApplications=A Telepítõ nem tudott minden alkalmazást automatikusan bezárni. A folytatás elõtt ajánlott minden, a Telepítõ által frissítendõ fájlokat használķ alkalmazást bezárni. +PrepareToInstallNeedsRestart=A telepítõnek újra kell indítania a számítķgépet. Újraindítást követõen, futtassa újbķl a telepítõt, a [name] telepítésének befejezéséhez .%n%nÚjra szeretné indítani most a számítķgépet? + +; *** "Installing" wizard page +WizardInstalling=Telepítés +InstallingLabel=Kérem várjon, amíg a(z) [name] telepítése zajlik. + +; *** "Setup Completed" wizard page +FinishedHeadingLabel=A(z) [name] telepítésének befejezése +FinishedLabelNoIcons=A Telepítõ végzett a(z) [name] telepítésével. +FinishedLabel=A Telepítõ végzett a(z) [name] telepítésével. Az alkalmazást a létrehozott ikonok kiválasztásával indíthatja. +ClickFinish=Kattintson a 'Befejezés'-re a kilépéshez. +FinishedRestartLabel=A(z) [name] telepítésének befejezéséhez újra kell indítani a számítķgépet. Újraindítja most? +FinishedRestartMessage=A(z) [name] telepítésének befejezéséhez, a Telepítõnek újra kell indítani a számítķgépet.%n%nÚjraindítja most? +ShowReadmeCheck=Igen, szeretném elolvasni a FONTOS fájlt +YesRadio=&Igen, újraindítás most +NoRadio=&Nem, késõbb indítom újra +; used for example as 'Run MyProg.exe' +RunEntryExec=%1 futtatása +; used for example as 'View Readme.txt' +RunEntryShellExec=%1 megtekintése + +; *** "Setup Needs the Next Disk" stuff +ChangeDiskTitle=A Telepítõnek szüksége van a következõ lemezre +SelectDiskLabel2=Helyezze be a(z) %1. lemezt és kattintson az 'OK'-ra.%n%nHa a fájlok a lemez egy a megjelenítettõl különbözõ mappájában találhatķk, írja be a helyes útvonalat vagy kattintson a 'Tallķzás'-ra. +PathLabel=Ú&tvonal: +FileNotInDir2=A(z) "%1" fájl nem találhatķ a következõ helyen: "%2". Helyezze be a megfelelõ lemezt vagy válasszon egy másik mappát. +SelectDirectoryLabel=Adja meg a következõ lemez helyét. + +; *** Installation phase messages +SetupAborted=A telepítés nem fejezõdött be.%n%nHárítsa el a hibát és futtassa újbķl a Telepítõt. +AbortRetryIgnoreSelectAction=Válasszon mûveletet +AbortRetryIgnoreRetry=&Újra +AbortRetryIgnoreIgnore=&Hiba elvetése és folytatás +AbortRetryIgnoreCancel=Telepítés megszakítása + +; *** Installation status messages +StatusClosingApplications=Alkalmazások bezárása... +StatusCreateDirs=Könyvtárak létrehozása... +StatusExtractFiles=Fájlok kibontása... +StatusCreateIcons=Parancsikonok létrehozása... +StatusCreateIniEntries=INI bejegyzések létrehozása... +StatusCreateRegistryEntries=Rendszerleírķ bejegyzések létrehozása... +StatusRegisterFiles=Fájlok regisztrálása... +StatusSavingUninstall=Eltávolítķ informáciķk mentése... +StatusRunProgram=Telepítés befejezése... +StatusRestartingApplications=Alkalmazások újraindítása... +StatusRollback=Változtatások visszavonása... + +; *** Misc. errors +ErrorInternal2=Belsõ hiba: %1 +ErrorFunctionFailedNoCode=Sikertelen %1 +ErrorFunctionFailed=Sikertelen %1; kķd: %2 +ErrorFunctionFailedWithMessage=Sikertelen %1; kķd: %2.%n%3 +ErrorExecutingProgram=Nem hajthatķ végre a fájl:%n%1 + +; *** Registry errors +ErrorRegOpenKey=Nem nyithatķ meg a rendszerleírķ kulcs:%n%1\%2 +ErrorRegCreateKey=Nem hozhatķ létre a rendszerleírķ kulcs:%n%1\%2 +ErrorRegWriteKey=Nem mķdosíthatķ a rendszerleírķ kulcs:%n%1\%2 + +; *** INI errors +ErrorIniEntry=Bejegyzés létrehozása sikertelen a következõ INI fájlban: "%1". + +; *** File copying errors +FileAbortRetryIgnoreSkipNotRecommended=&Fájl kihagyása (nem ajánlott) +FileAbortRetryIgnoreIgnoreNotRecommended=&Hiba elvetése és folytatás (nem ajánlott) +SourceIsCorrupted=A forrásfájl megsérült +SourceDoesntExist=A(z) "%1" forrásfájl nem létezik +ExistingFileReadOnly2=A fájl csak olvashatķként van jelölve. +ExistingFileReadOnlyRetry=Csak &olvashatķ tulajdonság eltávolítása és újra prķbálkozás +ExistingFileReadOnlyKeepExisting=&Létezõ fájl megtartása +ErrorReadingExistingDest=Hiba lépett fel a fájl olvasása közben: +FileExists=A fájl már létezik.%n%nFelül kívánja írni? +ExistingFileNewer=A létezõ fájl újabb a telepítésre kerülõnél. Ajánlott a létezõ fájl megtartása.%n%nMeg kívánja tartani a létezõ fájlt? +ErrorChangingAttr=Hiba lépett fel a fájl attribútumának mķdosítása közben: +ErrorCreatingTemp=Hiba lépett fel a fájl telepítési könyvtárban történõ létrehozása közben: +ErrorReadingSource=Hiba lépett fel a forrásfájl olvasása közben: +ErrorCopying=Hiba lépett fel a fájl másolása közben: +ErrorReplacingExistingFile=Hiba lépett fel a létezõ fájl cseréje közben: +ErrorRestartReplace=A fájl cseréje az újraindítás után sikertelen volt: +ErrorRenamingTemp=Hiba lépett fel fájl telepítési könyvtárban történõ átnevezése közben: +ErrorRegisterServer=Nem lehet regisztrálni a DLL-t/OCX-et: %1 +ErrorRegSvr32Failed=Sikertelen RegSvr32. A visszaadott kķd: %1 +ErrorRegisterTypeLib=Nem lehet regisztrálni a típustárat: %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 felhasználķ +UninstallDisplayNameMarkCurrentUser=Jelenlegi felhasználķ + +; *** Post-installation errors +ErrorOpeningReadme=Hiba lépett fel a FONTOS fájl megnyitása közben. +ErrorRestartingComputer=A Telepítõ nem tudta újraindítani a számítķgépet. Indítsa újra kézileg. + +; *** Uninstaller messages +UninstallNotFound=A(z) "%1" fájl nem létezik. Nem távolíthatķ el. +UninstallOpenError=A(z) "%1" fájl nem nyithatķ meg. Nem távolíthatķ el. +UninstallUnsupportedVer=A(z) "%1" eltávolítási naplķfájl formátumát nem tudja felismerni az eltávolítķ jelen verziķja. Az eltávolítás nem folytathatķ +UninstallUnknownEntry=Egy ismeretlen bejegyzés (%1) találhatķ az eltávolítási naplķfájlban +ConfirmUninstall=Biztosan el kívánja távolítani a(z) %1 programot és minden összetevõjét? +UninstallOnlyOnWin64=Ezt a telepítést csak 64-bites Windowson lehet eltávolítani. +OnlyAdminCanUninstall=Ezt a telepítést csak adminisztráciķs jogokkal rendelkezõ felhasználķ távolíthatja el. +UninstallStatusLabel=Legyen türelemmel, amíg a(z) %1 számítķgépérõl történõ eltávolítása befejezõdik. +UninstalledAll=A(z) %1 sikeresen el lett távolítva a számítķgéprõl. +UninstalledMost=A(z) %1 eltávolítása befejezõdött.%n%nNéhány elemet nem lehetett eltávolítani. Törölje kézileg. +UninstalledAndNeedsRestart=A(z) %1 eltávolításának befejezéséhez újra kell indítania a számítķgépét.%n%nÚjraindítja most? +UninstallDataCorrupted=A(z) "%1" fájl sérült. Nem távolíthatķ el. + +; *** Uninstallation phase messages +ConfirmDeleteSharedFileTitle=Törli a megosztott fájlt? +ConfirmDeleteSharedFile2=A rendszer azt jelzi, hogy a következõ megosztott fájlra már nincs szüksége egyetlen programnak sem. Eltávolítja a megosztott fájlt?%n%nHa más programok még mindig használják a megosztott fájlt, akkor az eltávolítása után lehet, hogy nem fognak megfelelõen mûködni. Ha bizonytalan, válassza a Nemet. A fájl megtartása nem okoz problémát a rendszerben. +SharedFileNameLabel=Fájlnév: +SharedFileLocationLabel=Helye: +WizardUninstalling=Eltávolítás állapota +StatusUninstalling=%1 eltávolítása... + +; *** Shutdown block reasons +ShutdownBlockReasonInstallingApp=%1 telepítése. +ShutdownBlockReasonUninstallingApp=%1 eltávolítása. + +; 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=További parancsikonok: +CreateDesktopIcon=&Asztali ikon létrehozása +CreateQuickLaunchIcon=&Gyorsindítķ parancsikon létrehozása +ProgramOnTheWeb=%1 az interneten +UninstallProgram=Eltávolítás - %1 +LaunchProgram=Indítás %1 +AssocFileExtension=A(z) %1 &társítása a(z) %2 fájlkiterjesztéssel +AssocingFileExtension=A(z) %1 társítása a(z) %2 fájlkiterjesztéssel... +AutoStartProgramGroupDescription=Indítķpult: +AutoStartProgram=%1 automatikus indítása +AddonHostProgramNotFound=A(z) %1 nem találhatķ a kiválasztott könyvtárban.%n%nMindenképpen folytatja? diff --git a/build/win32/i18n/Default.isl b/build/win32/i18n/Default.isl deleted file mode 100644 index 370da6b37c7..00000000000 --- a/build/win32/i18n/Default.isl +++ /dev/null @@ -1,336 +0,0 @@ -; *** Inno Setup version 5.5.3+ English messages *** -; -; To download user-contributed translations of this file, go to: -; http://www.jrsoftware.org/files/istrans/ -; -; Note: When translating this text, do not add periods (.) to the end of -; messages that didn't have them already, because on those messages Inno -; Setup adds the periods automatically (appending a period would result in -; two periods being displayed). - -[LangOptions] -; The following three entries are very important. Be sure to read and -; understand the '[LangOptions] section' topic in the help file. -LanguageName=English -LanguageID=$0409 -LanguageCodePage=0 -; If the language you are translating to requires special font faces or -; sizes, uncomment any of the following entries and change them accordingly. -;DialogFontName= -;DialogFontSize=8 -;WelcomeFontName=Verdana -;WelcomeFontSize=12 -;TitleFontName=Arial -;TitleFontSize=29 -;CopyrightFontName=Arial -;CopyrightFontSize=8 - -[Messages] - -; *** Application titles -SetupAppTitle=Setup -SetupWindowTitle=Setup - %1 -UninstallAppTitle=Uninstall -UninstallAppFullTitle=%1 Uninstall - -; *** Misc. common -InformationTitle=Information -ConfirmTitle=Confirm -ErrorTitle=Error - -; *** SetupLdr messages -SetupLdrStartupMessage=This will install %1. Do you wish to continue? -LdrCannotCreateTemp=Unable to create a temporary file. Setup aborted -LdrCannotExecTemp=Unable to execute file in the temporary directory. Setup aborted - -; *** Startup error messages -LastErrorMessage=%1.%n%nError %2: %3 -SetupFileMissing=The file %1 is missing from the installation directory. Please correct the problem or obtain a new copy of the program. -SetupFileCorrupt=The setup files are corrupted. Please obtain a new copy of the program. -SetupFileCorruptOrWrongVer=The setup files are corrupted, or are incompatible with this version of Setup. Please correct the problem or obtain a new copy of the program. -InvalidParameter=An invalid parameter was passed on the command line:%n%n%1 -SetupAlreadyRunning=Setup is already running. -WindowsVersionNotSupported=This program does not support the version of Windows your computer is running. -WindowsServicePackRequired=This program requires %1 Service Pack %2 or later. -NotOnThisPlatform=This program will not run on %1. -OnlyOnThisPlatform=This program must be run on %1. -OnlyOnTheseArchitectures=This program can only be installed on versions of Windows designed for the following processor architectures:%n%n%1 -MissingWOW64APIs=The version of Windows you are running does not include functionality required by Setup to perform a 64-bit installation. To correct this problem, please install Service Pack %1. -WinVersionTooLowError=This program requires %1 version %2 or later. -WinVersionTooHighError=This program cannot be installed on %1 version %2 or later. -AdminPrivilegesRequired=You must be logged in as an administrator when installing this program. -PowerUserPrivilegesRequired=You must be logged in as an administrator or as a member of the Power Users group when installing this program. -SetupAppRunningError=Setup has detected that %1 is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit. -UninstallAppRunningError=Uninstall has detected that %1 is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit. - -; *** Misc. errors -ErrorCreatingDir=Setup was unable to create the directory "%1" -ErrorTooManyFilesInDir=Unable to create a file in the directory "%1" because it contains too many files - -; *** Setup common messages -ExitSetupTitle=Exit Setup -ExitSetupMessage=Setup is not complete. If you exit now, the program will not be installed.%n%nYou may run Setup again at another time to complete the installation.%n%nExit Setup? -AboutSetupMenuItem=&About Setup... -AboutSetupTitle=About Setup -AboutSetupMessage=%1 version %2%n%3%n%n%1 home page:%n%4 -AboutSetupNote= -TranslatorNote= - -; *** Buttons -ButtonBack=< &Back -ButtonNext=&Next > -ButtonInstall=&Install -ButtonOK=OK -ButtonCancel=Cancel -ButtonYes=&Yes -ButtonYesToAll=Yes to &All -ButtonNo=&No -ButtonNoToAll=N&o to All -ButtonFinish=&Finish -ButtonBrowse=&Browse... -ButtonWizardBrowse=B&rowse... -ButtonNewFolder=&Make New Folder - -; *** "Select Language" dialog messages -SelectLanguageTitle=Select Setup Language -SelectLanguageLabel=Select the language to use during the installation: - -; *** Common wizard text -ClickNext=Click Next to continue, or Cancel to exit Setup. -BeveledLabel= -BrowseDialogTitle=Browse For Folder -BrowseDialogLabel=Select a folder in the list below, then click OK. -NewFolderName=New Folder - -; *** "Welcome" wizard page -WelcomeLabel1=Welcome to the [name] Setup Wizard -WelcomeLabel2=This will install [name/ver] on your computer.%n%nIt is recommended that you close all other applications before continuing. - -; *** "Password" wizard page -WizardPassword=Password -PasswordLabel1=This installation is password protected. -PasswordLabel3=Please provide the password, then click Next to continue. Passwords are case-sensitive. -PasswordEditLabel=&Password: -IncorrectPassword=The password you entered is not correct. Please try again. - -; *** "License Agreement" wizard page -WizardLicense=License Agreement -LicenseLabel=Please read the following important information before continuing. -LicenseLabel3=Please read the following License Agreement. You must accept the terms of this agreement before continuing with the installation. -LicenseAccepted=I &accept the agreement -LicenseNotAccepted=I &do not accept the agreement - -; *** "Information" wizard pages -WizardInfoBefore=Information -InfoBeforeLabel=Please read the following important information before continuing. -InfoBeforeClickLabel=When you are ready to continue with Setup, click Next. -WizardInfoAfter=Information -InfoAfterLabel=Please read the following important information before continuing. -InfoAfterClickLabel=When you are ready to continue with Setup, click Next. - -; *** "User Information" wizard page -WizardUserInfo=User Information -UserInfoDesc=Please enter your information. -UserInfoName=&User Name: -UserInfoOrg=&Organization: -UserInfoSerial=&Serial Number: -UserInfoNameRequired=You must enter a name. - -; *** "Select Destination Location" wizard page -WizardSelectDir=Select Destination Location -SelectDirDesc=Where should [name] be installed? -SelectDirLabel3=Setup will install [name] into the following folder. -SelectDirBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse. -DiskSpaceMBLabel=At least [mb] MB of free disk space is required. -CannotInstallToNetworkDrive=Setup cannot install to a network drive. -CannotInstallToUNCPath=Setup cannot install to a UNC path. -InvalidPath=You must enter a full path with drive letter; for example:%n%nC:\APP%n%nor a UNC path in the form:%n%n\\server\share -InvalidDrive=The drive or UNC share you selected does not exist or is not accessible. Please select another. -DiskSpaceWarningTitle=Not Enough Disk Space -DiskSpaceWarning=Setup requires at least %1 KB of free space to install, but the selected drive only has %2 KB available.%n%nDo you want to continue anyway? -DirNameTooLong=The folder name or path is too long. -InvalidDirName=The folder name is not valid. -BadDirName32=Folder names cannot include any of the following characters:%n%n%1 -DirExistsTitle=Folder Exists -DirExists=The folder:%n%n%1%n%nalready exists. Would you like to install to that folder anyway? -DirDoesntExistTitle=Folder Does Not Exist -DirDoesntExist=The folder:%n%n%1%n%ndoes not exist. Would you like the folder to be created? - -; *** "Select Components" wizard page -WizardSelectComponents=Select Components -SelectComponentsDesc=Which components should be installed? -SelectComponentsLabel2=Select the components you want to install; clear the components you do not want to install. Click Next when you are ready to continue. -FullInstallation=Full installation -; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) -CompactInstallation=Compact installation -CustomInstallation=Custom installation -NoUninstallWarningTitle=Components Exist -NoUninstallWarning=Setup has detected that the following components are already installed on your computer:%n%n%1%n%nDeselecting these components will not uninstall them.%n%nWould you like to continue anyway? -ComponentSize1=%1 KB -ComponentSize2=%1 MB -ComponentsDiskSpaceMBLabel=Current selection requires at least [mb] MB of disk space. - -; *** "Select Additional Tasks" wizard page -WizardSelectTasks=Select Additional Tasks -SelectTasksDesc=Which additional tasks should be performed? -SelectTasksLabel2=Select the additional tasks you would like Setup to perform while installing [name], then click Next. - -; *** "Select Start Menu Folder" wizard page -WizardSelectProgramGroup=Select Start Menu Folder -SelectStartMenuFolderDesc=Where should Setup place the program's shortcuts? -SelectStartMenuFolderLabel3=Setup will create the program's shortcuts in the following Start Menu folder. -SelectStartMenuFolderBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse. -MustEnterGroupName=You must enter a folder name. -GroupNameTooLong=The folder name or path is too long. -InvalidGroupName=The folder name is not valid. -BadGroupName=The folder name cannot include any of the following characters:%n%n%1 -NoProgramGroupCheck2=&Don't create a Start Menu folder - -; *** "Ready to Install" wizard page -WizardReady=Ready to Install -ReadyLabel1=Setup is now ready to begin installing [name] on your computer. -ReadyLabel2a=Click Install to continue with the installation, or click Back if you want to review or change any settings. -ReadyLabel2b=Click Install to continue with the installation. -ReadyMemoUserInfo=User information: -ReadyMemoDir=Destination location: -ReadyMemoType=Setup type: -ReadyMemoComponents=Selected components: -ReadyMemoGroup=Start Menu folder: -ReadyMemoTasks=Additional tasks: - -; *** "Preparing to Install" wizard page -WizardPreparing=Preparing to Install -PreparingDesc=Setup is preparing to install [name] on your computer. -PreviousInstallNotCompleted=The installation/removal of a previous program was not completed. You will need to restart your computer to complete that installation.%n%nAfter restarting your computer, run Setup again to complete the installation of [name]. -CannotContinue=Setup cannot continue. Please click Cancel to exit. -ApplicationsFound=The following applications are using files that need to be updated by Setup. It is recommended that you allow Setup to automatically close these applications. -ApplicationsFound2=The following applications are using files that need to be updated by Setup. It is recommended that you allow Setup to automatically close these applications. After the installation has completed, Setup will attempt to restart the applications. -CloseApplications=&Automatically close the applications -DontCloseApplications=&Do not close the applications -ErrorCloseApplications=Setup was unable to automatically close all applications. It is recommended that you close all applications using files that need to be updated by Setup before continuing. - -; *** "Installing" wizard page -WizardInstalling=Installing -InstallingLabel=Please wait while Setup installs [name] on your computer. - -; *** "Setup Completed" wizard page -FinishedHeadingLabel=Completing the [name] Setup Wizard -FinishedLabelNoIcons=Setup has finished installing [name] on your computer. -FinishedLabel=Setup has finished installing [name] on your computer. The application may be launched by selecting the installed icons. -ClickFinish=Click Finish to exit Setup. -FinishedRestartLabel=To complete the installation of [name], Setup must restart your computer. Would you like to restart now? -FinishedRestartMessage=To complete the installation of [name], Setup must restart your computer.%n%nWould you like to restart now? -ShowReadmeCheck=Yes, I would like to view the README file -YesRadio=&Yes, restart the computer now -NoRadio=&No, I will restart the computer later -; used for example as 'Run MyProg.exe' -RunEntryExec=Run %1 -; used for example as 'View Readme.txt' -RunEntryShellExec=View %1 - -; *** "Setup Needs the Next Disk" stuff -ChangeDiskTitle=Setup Needs the Next Disk -SelectDiskLabel2=Please insert Disk %1 and click OK.%n%nIf the files on this disk can be found in a folder other than the one displayed below, enter the correct path or click Browse. -PathLabel=&Path: -FileNotInDir2=The file "%1" could not be located in "%2". Please insert the correct disk or select another folder. -SelectDirectoryLabel=Please specify the location of the next disk. - -; *** Installation phase messages -SetupAborted=Setup was not completed.%n%nPlease correct the problem and run Setup again. -EntryAbortRetryIgnore=Click Retry to try again, Ignore to proceed anyway, or Abort to cancel installation. - -; *** Installation status messages -StatusClosingApplications=Closing applications... -StatusCreateDirs=Creating directories... -StatusExtractFiles=Extracting files... -StatusCreateIcons=Creating shortcuts... -StatusCreateIniEntries=Creating INI entries... -StatusCreateRegistryEntries=Creating registry entries... -StatusRegisterFiles=Registering files... -StatusSavingUninstall=Saving uninstall information... -StatusRunProgram=Finishing installation... -StatusRestartingApplications=Restarting applications... -StatusRollback=Rolling back changes... - -; *** Misc. errors -ErrorInternal2=Internal error: %1 -ErrorFunctionFailedNoCode=%1 failed -ErrorFunctionFailed=%1 failed; code %2 -ErrorFunctionFailedWithMessage=%1 failed; code %2.%n%3 -ErrorExecutingProgram=Unable to execute file:%n%1 - -; *** Registry errors -ErrorRegOpenKey=Error opening registry key:%n%1\%2 -ErrorRegCreateKey=Error creating registry key:%n%1\%2 -ErrorRegWriteKey=Error writing to registry key:%n%1\%2 - -; *** INI errors -ErrorIniEntry=Error creating INI entry in file "%1". - -; *** File copying errors -FileAbortRetryIgnore=Click Retry to try again, Ignore to skip this file (not recommended), or Abort to cancel installation. -FileAbortRetryIgnore2=Click Retry to try again, Ignore to proceed anyway (not recommended), or Abort to cancel installation. -SourceIsCorrupted=The source file is corrupted -SourceDoesntExist=The source file "%1" does not exist -ExistingFileReadOnly=The existing file is marked as read-only.%n%nClick Retry to remove the read-only attribute and try again, Ignore to skip this file, or Abort to cancel installation. -ErrorReadingExistingDest=An error occurred while trying to read the existing file: -FileExists=The file already exists.%n%nWould you like Setup to overwrite it? -ExistingFileNewer=The existing file is newer than the one Setup is trying to install. It is recommended that you keep the existing file.%n%nDo you want to keep the existing file? -ErrorChangingAttr=An error occurred while trying to change the attributes of the existing file: -ErrorCreatingTemp=An error occurred while trying to create a file in the destination directory: -ErrorReadingSource=An error occurred while trying to read the source file: -ErrorCopying=An error occurred while trying to copy a file: -ErrorReplacingExistingFile=An error occurred while trying to replace the existing file: -ErrorRestartReplace=RestartReplace failed: -ErrorRenamingTemp=An error occurred while trying to rename a file in the destination directory: -ErrorRegisterServer=Unable to register the DLL/OCX: %1 -ErrorRegSvr32Failed=RegSvr32 failed with exit code %1 -ErrorRegisterTypeLib=Unable to register the type library: %1 - -; *** Post-installation errors -ErrorOpeningReadme=An error occurred while trying to open the README file. -ErrorRestartingComputer=Setup was unable to restart the computer. Please do this manually. - -; *** Uninstaller messages -UninstallNotFound=File "%1" does not exist. Cannot uninstall. -UninstallOpenError=File "%1" could not be opened. Cannot uninstall -UninstallUnsupportedVer=The uninstall log file "%1" is in a format not recognized by this version of the uninstaller. Cannot uninstall -UninstallUnknownEntry=An unknown entry (%1) was encountered in the uninstall log -ConfirmUninstall=Are you sure you want to completely remove %1? Extensions and settings will not be removed. -UninstallOnlyOnWin64=This installation can only be uninstalled on 64-bit Windows. -OnlyAdminCanUninstall=This installation can only be uninstalled by a user with administrative privileges. -UninstallStatusLabel=Please wait while %1 is removed from your computer. -UninstalledAll=%1 was successfully removed from your computer. -UninstalledMost=%1 uninstall complete.%n%nSome elements could not be removed. These can be removed manually. -UninstalledAndNeedsRestart=To complete the uninstallation of %1, your computer must be restarted.%n%nWould you like to restart now? -UninstallDataCorrupted="%1" file is corrupted. Cannot uninstall - -; *** Uninstallation phase messages -ConfirmDeleteSharedFileTitle=Remove Shared File? -ConfirmDeleteSharedFile2=The system indicates that the following shared file is no longer in use by any programs. Would you like for Uninstall to remove this shared file?%n%nIf any programs are still using this file and it is removed, those programs may not function properly. If you are unsure, choose No. Leaving the file on your system will not cause any harm. -SharedFileNameLabel=File name: -SharedFileLocationLabel=Location: -WizardUninstalling=Uninstall Status -StatusUninstalling=Uninstalling %1... - -; *** Shutdown block reasons -ShutdownBlockReasonInstallingApp=Installing %1. -ShutdownBlockReasonUninstallingApp=Uninstalling %1. - -; The custom messages below aren't used by Setup itself, but if you make -; use of them in your scripts, you'll want to translate them. - -[CustomMessages] - -NameAndVersion=%1 version %2 -AdditionalIcons=Additional icons: -CreateDesktopIcon=Create a &desktop icon -CreateQuickLaunchIcon=Create a &Quick Launch icon -ProgramOnTheWeb=%1 on the Web -UninstallProgram=Uninstall %1 -LaunchProgram=Launch %1 -AssocFileExtension=&Associate %1 with the %2 file extension -AssocingFileExtension=Associating %1 with the %2 file extension... -AutoStartProgramGroupDescription=Startup: -AutoStartProgram=Automatically start %1 -AddonHostProgramNotFound=%1 could not be located in the folder you selected.%n%nDo you want to continue anyway? diff --git a/build/win32/i18n/Default.ko.isl b/build/win32/i18n/Default.ko.isl index a7c38d12b9f..0f1b1a7ccf5 100644 --- a/build/win32/i18n/Default.ko.isl +++ b/build/win32/i18n/Default.ko.isl @@ -1,12 +1,16 @@ -; *** Inno Setup version 5.5.3+ Korean messages *** -; -; To download user-contributed translations of this file, go to: -; http://www.jrsoftware.org/files/istrans/ +; *** Inno Setup version 6.0.0+ Korean messages *** ; +; ĸÆ 6.0.3+ Translator: SungDong Kim (acroedit@gmail.com) +; ĸÆ 5.5.3+ Translator: Domddol (domddol@gmail.com) +; ĸÆ Translation date: MAR 04, 2014 +; ĸÆ Contributors: Hansoo KIM (iryna7@gmail.com), Woong-Jae An (a183393@hanmail.net) +; ĸÆ Storage: http://www.jrsoftware.org/files/istrans/ +; ĸÆ ĀĖ šøŋĒĀē ģõˇÎŋî ĮŅąšžî ¸ÂÃãšũ ąÔÄĸĀģ ÁØŧöĮÕ´Ī´Ų. ; Note: When translating this text, do not add periods (.) to the end of ; messages that didn't have them already, because on those messages Inno ; Setup adds the periods automatically (appending a period would result in ; two periods being displayed). + [LangOptions] ; The following three entries are very important. Be sure to read and ; understand the '[LangOptions] section' topic in the help file. @@ -23,50 +27,68 @@ LanguageCodePage=949 ;TitleFontSize=29 ;CopyrightFontName=Arial ;CopyrightFontSize=8 + [Messages] + ; *** Application titles SetupAppTitle=ŧŗÄĄ -SetupWindowTitle=ŧŗÄĄ - %1 +SetupWindowTitle=%1 ŧŗÄĄ UninstallAppTitle=ÁϰŠUninstallAppFullTitle=%1 ÁϰŠ+ ; *** Misc. common InformationTitle=Á¤ē¸ ConfirmTitle=ČŽĀÎ ErrorTitle=ŋŽų + ; *** SetupLdr messages -SetupLdrStartupMessage=ą×ˇ¯¸é %1ĀĖ(°Ą) ŧŗÄĄĩË´Ī´Ų. °čŧĶĮĪŊðÚŊĀ´Īąî? -LdrCannotCreateTemp=ĀĶŊà ÆÄĀĪĀģ ¸¸ĩé ŧö žøŊĀ´Ī´Ų. ŧŗÄĄ ĮÁˇÎą×ˇĨĀĖ Áß´ÜĩĮžúŊĀ´Ī´Ų. -LdrCannotExecTemp=ĀĶŊà ĩđˇēÅ͸ŽŋĄŧ­ ÆÄĀĪĀģ ŊĮĮāĮŌ ŧö žøŊĀ´Ī´Ų. ŧŗÄĄ ĮÁˇÎą×ˇĨĀĖ Áß´ÜĩĮžúŊĀ´Ī´Ų. +SetupLdrStartupMessage=%1Āģ(¸Ļ) ŧŗÄĄĮÕ´Ī´Ų, °čŧĶĮĪŊðÚŊĀ´Īąî? +LdrCannotCreateTemp=ĀĶŊà ÆÄĀĪĀģ ¸¸ĩé ŧö žøŊĀ´Ī´Ų, ŧŗÄĄ¸Ļ Áß´ÜĮÕ´Ī´Ų +LdrCannotExecTemp=ĀĶŊà Æú´õĀĮ ÆÄĀĪĀģ ŊĮĮāĮŌ ŧö žøŊĀ´Ī´Ų, ŧŗÄĄ¸Ļ Áß´ÜĮÕ´Ī´Ų +HelpTextNote= + ; *** Startup error messages LastErrorMessage=%1.%n%nŋŽų %2: %3 -SetupFileMissing=ÆÄĀĪ %1ĀĖ(°Ą) ŧŗÄĄ ĩđˇēÅ͸ŽŋĄŧ­ ´ŠļôĩĮžúŊĀ´Ī´Ų. šŽÁĻ¸Ļ ĮذáĮĪ°ÅŗĒ ĮÁˇÎą×ˇĨĀģ ģõˇÎ šŪŧŧŋä. -SetupFileCorrupt=ŧŗÄĄ ÆÄĀĪĀĖ ŧÕģķĩĮžúŊĀ´Ī´Ų. ĮÁˇÎą×ˇĨĀģ ģõˇÎ šŪŧŧŋä. -SetupFileCorruptOrWrongVer=ŧŗÄĄ ÆÄĀĪĀĖ ŧÕģķĩĮžú°ÅŗĒ ĀĖ šöĀüĀĮ ŧŗÄĄ ĮÁˇÎą×ˇĨ°ú ȪȝĩĮÁö žĘŊĀ´Ī´Ų. šŽÁĻ¸Ļ ĮذáĮĪ°ÅŗĒ ĮÁˇÎą×ˇĨĀģ ģõˇÎ šŪŧŧŋä. -InvalidParameter=¸íˇÉÁŲŋĄ Ā߸øĩČ ¸Å°ŗ ē¯ŧö°Ą Āü´ŪĩĘ:%n%n%1 -SetupAlreadyRunning=ŧŗÄĄ ĮÁˇÎą×ˇĨĀĖ ĀĖšĖ ŊĮĮā ÁßĀÔ´Ī´Ų. -WindowsVersionNotSupported=ĀĖ ĮÁˇÎą×ˇĨĀē ÄÄĮģÅÍŋĄŧ­ ŊĮĮā ÁßĀÎ šöĀüĀĮ Windows¸Ļ ÁöŋøĮĪÁö žĘŊĀ´Ī´Ų. -WindowsServicePackRequired=ĀĖ ĮÁˇÎą×ˇĨĀģ ŧŗÄĄĮΎÁ¸é %1 ŧ­ēņŊē ÆŅ %2 ĀĖģķĀĖ ĮĘŋäĮÕ´Ī´Ų. -NotOnThisPlatform=ĀĖ ĮÁˇÎą×ˇĨĀē %1ŋĄŧ­ ŊĮĮāĩĮÁö žĘŊĀ´Ī´Ų. +SetupFileMissing=%1 ÆÄĀĪĀĖ Á¸ĀįĮĪÁö žĘŊĀ´Ī´Ų, šŽÁĻ¸Ļ ĮذáĮØ ē¸°ÅŗĒ ģõˇÎŋî ŧŗÄĄ ĮÁˇÎą×ˇĨĀģ ą¸ĮĪŊÃąâ šŲļø´Ī´Ų. +SetupFileCorrupt=ŧŗÄĄ ÆÄĀĪĀĖ ŧÕģķĩĮžúŊĀ´Ī´Ų, ģõˇÎŋî ŧŗÄĄ ĮÁˇÎą×ˇĨĀģ ą¸ĮĪŊÃąâ šŲļø´Ī´Ų. +SetupFileCorruptOrWrongVer=ŧŗÄĄ ÆÄĀĪĀĮ ŧÕģķĀĖ°ÅŗĒ ĀĖ ŧŗÄĄ šöĀü°ú ȪȝĩĮÁö žĘŊĀ´Ī´Ų, šŽÁĻ¸Ļ ĮذáĮØ ē¸°ÅŗĒ ģõˇÎŋî ŧŗÄĄ ĮÁˇÎą×ˇĨĀģ ą¸ĮĪŊÃąâ šŲļø´Ī´Ų. +InvalidParameter=Ā߸øĩČ ¸Å°ŗ ē¯ŧöĀÔ´Ī´Ų:%n%n%1 +SetupAlreadyRunning=ŧŗÄĄ°Ą ĀĖšĖ ŊĮĮā ÁßĀÔ´Ī´Ų. +WindowsVersionNotSupported=ĀĖ ĮÁˇÎą×ˇĨĀē ąÍĮĪĀĮ Windows šöĀüĀģ ÁöŋøĮĪÁö žĘŊĀ´Ī´Ų. +WindowsServicePackRequired=ĀĖ ĮÁˇÎą×ˇĨĀģ ŊĮĮāĮΎÁ¸é %1 sp%2 ĀĖģķĀĖžîžß ĮÕ´Ī´Ų. +NotOnThisPlatform=ĀĖ ĮÁˇÎą×ˇĨĀē %1ŋĄŧ­ ĀÛĩŋĮĪÁö žĘŊĀ´Ī´Ų. OnlyOnThisPlatform=ĀĖ ĮÁˇÎą×ˇĨĀē %1ŋĄŧ­ ŊĮĮāĮØžß ĮÕ´Ī´Ų. -OnlyOnTheseArchitectures=ĀĖ ĮÁˇÎą×ˇĨĀē ĮÁˇÎŧŧŧ­ žÆÅ°ÅØÃŗ %n%n%1ŋ돷Πŧŗ°čĩČ Windows šöĀüŋĄŧ­¸¸ ŧŗÄĄĮŌ ŧö ĀÖŊĀ´Ī´Ų. -MissingWOW64APIs=ŊĮĮā ÁßĀÎ Windows šöĀüŋĄ´Â ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ 64ēņÆŽ¸Ļ ŧŗÄĄĮĪ´Â ĩĨ ĮĘŋäĮŅ ąâ´ÉĀĖ žøŊĀ´Ī´Ų. ĀĖ šŽÁĻ¸Ļ ĮذáĮΎÁ¸é ŧ­ēņŊē ÆŅ %1Āģ(¸Ļ) ŧŗÄĄĮĪŧŧŋä. -WinVersionTooLowError=ĀĖ ĮÁˇÎą×ˇĨĀģ ŧŗÄĄĮΎÁ¸é %1 šöĀü %2 ĀĖģķĀĖ ĮĘŋäĮÕ´Ī´Ų. -WinVersionTooHighError=ĀĖ ĮÁˇÎą×ˇĨĀē %1 šöĀü %2 ĀĖģķŋĄŧ­´Â ŧŗÄĄĮŌ ŧö žøŊĀ´Ī´Ų. -AdminPrivilegesRequired=ĀĖ ĮÁˇÎą×ˇĨĀģ ŧŗÄĄĮŌ ļ§´Â °ü¸ŽĀڡΠˇÎą×ĀÎĮØžß ĮÕ´Ī´Ų. -PowerUserPrivilegesRequired=ĀĖ ĮÁˇÎą×ˇĨĀģ ŧŗÄĄĮŌ ļ§´Â °ü¸ŽĀÚŗĒ °íąŪ ģįŋëĀÚ ą×ˇėĀĮ ą¸ŧēŋøĀ¸ˇÎ ˇÎą×ĀÎĮØžß ĮÕ´Ī´Ų. -SetupAppRunningError=ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ %1(ĀĖ)°Ą ĮöĀį ŊĮĮā ÁßĀĶĀģ °¨ÁöĮßŊĀ´Ī´Ų.%n%nĀĖ Į׸ņĀĮ ¸đĩį ĀÎŊēÅĪŊē¸Ļ ÁöąŨ ´Ũ°í °čŧĶĮΎÁ¸é [ČŽĀÎ]Āģ, ÁžˇáĮΎÁ¸é [ÃëŧŌ]¸Ļ ÅŦ¸¯ĮĪŧŧŋä. -UninstallAppRunningError=ÁϰŠĀÛž÷ŋĄŧ­ %1(ĀĖ)°Ą ĮöĀį ŊĮĮā ÁßĀĶĀģ °¨ÁöĮßŊĀ´Ī´Ų.%n%nĀĖ Į׸ņĀĮ ¸đĩį ĀÎŊēÅĪŊē¸Ļ ÁöąŨ ´Ũ°í °čŧĶĮΎÁ¸é [ČŽĀÎ]Āģ, ÁžˇáĮΎÁ¸é [ÃëŧŌ]¸Ļ ÅŦ¸¯ĮĪŧŧŋä. +OnlyOnTheseArchitectures=ĀĖ ĮÁˇÎą×ˇĨĀē žÆˇĄ Ãŗ¸Ž ą¸ÁļŋÍ ČŖČ¯ĩĮ´Â Windows šöĀüŋĄ¸¸ ŧŗÄĄĮŌ ŧö ĀÖŊĀ´Ī´Ų:%n%n%1 +WinVersionTooLowError=ĀĖ ĮÁˇÎą×ˇĨĀē %1 šöĀü %2 ĀĖģķĀĖ ĮĘŋäĮÕ´Ī´Ų. +WinVersionTooHighError=ĀĖ ĮÁˇÎą×ˇĨĀē %1 šöĀü %2 ĀĖģķŋĄŧ­ ŧŗÄĄĮŌ ŧö žøŊĀ´Ī´Ų. +AdminPrivilegesRequired=ĀĖ ĮÁˇÎą×ˇĨĀģ ŧŗÄĄĮΎÁ¸é °ü¸ŽĀڡΠˇÎą×ĀÎĮØžß ĮÕ´Ī´Ų. +PowerUserPrivilegesRequired=ĀĖ ĮÁˇÎą×ˇĨĀģ ŧŗÄĄĮΎÁ¸é °ü¸ŽĀÚ ļĮ´Â °íąŪ ģįŋëĀڡΠˇÎą×ĀÎĮØžß ĮÕ´Ī´Ų. +SetupAppRunningError=ĮöĀį %1ĀĖ(°Ą) ŊĮĮā ÁßĀÔ´Ī´Ų!%n%nÁöąŨ ą×°ÍĀĮ ¸đĩį ĀÎŊēÅĪŊē¸Ļ ´ŨžÆ ÁÖŊĘŊÃŋĀ. ą×ˇą ´ŲĀŊ °čŧĶĮΎÁ¸é "ČŽĀÎ"Āģ, ÁžˇáĮΎÁ¸é "ÃëŧŌ"¸Ļ ÅŦ¸¯ĮĪŊĘŊÃŋĀ. +UninstallAppRunningError=ĮöĀį %1ĀĖ(°Ą) ŊĮĮā ÁßĀÔ´Ī´Ų!%n%nÁöąŨ ą×°ÍĀĮ ¸đĩį ĀÎŊēÅĪŊē¸Ļ ´ŨžÆ ÁÖŊĘŊÃŋĀ. ą×ˇą ´ŲĀŊ °čŧĶĮΎÁ¸é "ČŽĀÎ"Āģ, ÁžˇáĮΎÁ¸é "ÃëŧŌ"¸Ļ ÅŦ¸¯ĮĪŊĘŊÃŋĀ. + +; *** Startup questions +PrivilegesRequiredOverrideTitle=ŧŗÄĄ ¸đĩå ŧąÅà +PrivilegesRequiredOverrideInstruction=ŧŗÄĄ ¸đĩå¸Ļ ŧąÅÃĮØ ÁÖŊĘŊÃŋĀ +PrivilegesRequiredOverrideText1=%1 Āē ¸đĩį ģįŋëĀÚ(°ü¸ŽĀÚ ąĮĮŅ ĮĘŋä) ļĮ´Â ĮöĀį ģįŋëĀÚŋ돷ΠŧŗÄĄĮÕ´Ī´Ų. +PrivilegesRequiredOverrideText2=%1 Āē ĮöĀį ģįŋëĀÚ ļĮ´Â ¸đĩį ģįŋëĀÚ(°ü¸ŽĀÚ ąĮĮŅ ĮĘŋä) ŋ돷ΠŧŗÄĄĮÕ´Ī´Ų. +PrivilegesRequiredOverrideAllUsers=¸đĩį ģįŋëĀÚŋ돷ΠŧŗÄĄ(&A) +PrivilegesRequiredOverrideAllUsersRecommended=¸đĩį ģįŋëĀÚŋ돷ΠŧŗÄĄ(&A) (ÃßÃĩ) +PrivilegesRequiredOverrideCurrentUser=ĮöĀį ģįŋëĀÚŋ돷ΠŧŗÄĄ(&M) +PrivilegesRequiredOverrideCurrentUserRecommended=ĮöĀį ģįŋëĀÚŋ돷ΠŧŗÄĄ(&M) (ÃßÃĩ) + ; *** Misc. errors -ErrorCreatingDir=ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ ĩđˇēÅÍ¸Ž "%1"Āģ(¸Ļ) ¸¸ĩé ŧö žøŊĀ´Ī´Ų. -ErrorTooManyFilesInDir=ĩđˇēÅÍ¸Ž "%1"ŋĄ ÆÄĀĪĀĖ ŗĘšĢ ¸šĀ¸šĮˇÎ ĀĖ ĩđˇēÅ͸ŽŋĄ ÆÄĀĪĀģ ¸¸ĩé ŧö žøŊĀ´Ī´Ų. +ErrorCreatingDir="%1" Æú´õ¸Ļ ¸¸ĩé ŧö žøŊĀ´Ī´Ų. +ErrorTooManyFilesInDir="%1" Æú´õŋĄ ÆÄĀĪĀĖ ŗĘšĢ ¸šąâ ļ§šŽŋĄ ÆÄĀĪĀģ ¸¸ĩé ŧö žøŊĀ´Ī´Ų. + ; *** Setup common messages -ExitSetupTitle=ŧŗÄĄ Ážˇá -ExitSetupMessage=ŧŗÄĄ°Ą ŋΎáĩĮÁö žĘžŌŊĀ´Ī´Ų. ÁöąŨ ÁžˇáĮΏé ĮÁˇÎą×ˇĨĀĖ ŧŗÄĄĩĮÁö žĘŊĀ´Ī´Ų.%n%nŗĒÁßŋĄ ŧŗÄĄ ĮÁˇÎą×ˇĨĀģ ´ŲŊà ŊĮĮāĮĪŋŠ ŧŗÄĄ¸Ļ ŗĄŗž ŧö ĀÖŊĀ´Ī´Ų.%n%nŧŗÄĄ ĮÁˇÎą×ˇĨĀģ ÁžˇáĮĪŊðÚŊĀ´Īąî? -AboutSetupMenuItem=ŧŗÄĄ ĮÁˇÎą×ˇĨ Á¤ē¸(&A)... -AboutSetupTitle=ŧŗÄĄ ĮÁˇÎą×ˇĨ Á¤ē¸ -AboutSetupMessage=%1 šöĀü %2%n%3%n%n%1 ȨÆäĀĖÁö:%n%4 +ExitSetupTitle=ŧŗÄĄ ŋΎá +ExitSetupMessage=ŧŗÄĄ°Ą ŋΎáĩĮÁö žĘžŌŊĀ´Ī´Ų, ŋŠąâŧ­ ŧŗÄĄ¸Ļ ÁžˇáĮΏé ĮÁˇÎą×ˇĨĀē ŧŗÄĄĩĮÁö žĘŊĀ´Ī´Ų.%n%nŧŗÄĄ¸Ļ ŋΎáĮΎÁ¸é ŗĒÁßŋĄ ´ŲŊà ŧŗÄĄ ĮÁˇÎą×ˇĨĀģ ŊĮĮāĮØžß ĮÕ´Ī´Ų.%n%ną×ˇĄĩĩ ŧŗÄĄ¸Ļ ÁžˇáĮĪŊðÚŊĀ´Īąî? +AboutSetupMenuItem=ŧŗÄĄ Á¤ē¸(&A)... +AboutSetupTitle=ŧŗÄĄ Á¤ē¸ +AboutSetupMessage=%1 šöĀü %2%n%3%n%n%1 Ȩ ÆäĀĖÁö:%n%4 AboutSetupNote= TranslatorNote= + ; *** Buttons ButtonBack=< ĩÚˇÎ(&B) ButtonNext=´ŲĀŊ(&N) > @@ -75,224 +97,271 @@ ButtonOK=ČŽ ButtonCancel=ÃëŧŌ ButtonYes=ŋš(&Y) ButtonYesToAll=¸đĩÎ ŋš(&A) -ButtonNo=žÆ´Īŋä(&N) -ButtonNoToAll=¸đĩÎ žÆ´Īŋä(&O) -ButtonFinish=¸ļħ(&F) +ButtonNo=žÆ´ĪŋĀ(&N) +ButtonNoToAll=¸đĩÎ žÆ´ĪŋĀ(&O) +ButtonFinish=Ážˇá(&F) ButtonBrowse=ÃŖžÆē¸ąâ(&B)... -ButtonWizardBrowse=ÃŖžÆē¸ąâ(&R) +ButtonWizardBrowse=ÃŖžÆē¸ąâ(&R)... ButtonNewFolder=ģõ Æú´õ ¸¸ĩéąâ(&M) + ; *** "Select Language" dialog messages SelectLanguageTitle=ŧŗÄĄ žđžî ŧąÅà -SelectLanguageLabel=ŧŗÄĄ ÁßŋĄ ģįŋëĮŌ žđžî¸Ļ ŧąÅÃĮĪŧŧŋä. +SelectLanguageLabel=ŧŗÄĄŋĄ ģįŋëĮŌ žđžî¸Ļ ŧąÅÃĮĪŊĘŊÃŋĀ. + ; *** Common wizard text -ClickNext=°čŧĶĮΎÁ¸é [´ŲĀŊ]Āģ ÅŦ¸¯Įΰí ŧŗÄĄ ĮÁˇÎą×ˇĨĀģ ÁžˇáĮΎÁ¸é [ÃëŧŌ]¸Ļ ÅŦ¸¯ĮĪŧŧŋä. +ClickNext=°čŧĶĮΎÁ¸é "´ŲĀŊ"Āģ ÅŦ¸¯Įΰí ŧŗÄĄ¸Ļ ÁžˇáĮΎÁ¸é "ÃëŧŌ"¸Ļ ÅŦ¸¯ĮÕ´Ī´Ų. BeveledLabel= BrowseDialogTitle=Æú´õ ÃŖžÆē¸ąâ -BrowseDialogLabel=žÆˇĄ ¸ņˇĪŋĄŧ­ Æú´õ¸Ļ ŧąÅÃĮŅ ´ŲĀŊ [ČŽĀÎ]Āģ ÅŦ¸¯ĮĪŧŧŋä. +BrowseDialogLabel=žÆˇĄ ¸ņˇĪŋĄŧ­ Æú´õ¸Ļ ŧąÅÃĮŅ ´ŲĀŊ "ČŽĀÎ"Āģ ÅŦ¸¯ĮÕ´Ī´Ų. NewFolderName=ģõ Æú´õ + ; *** "Welcome" wizard page WelcomeLabel1=[name] ŧŗÄĄ ¸ļšũģį ŊÃĀÛ -WelcomeLabel2=ĀĖ ¸ļšũģį´Â ÄÄĮģÅÍŋĄ [name/ver]Āģ(¸Ļ) ŧŗÄĄĮÕ´Ī´Ų.%n%n°čŧĶĮĪąâ ĀüŋĄ ´Ų¸Ĩ ¸đĩį ĀĀŋë ĮÁˇÎą×ˇĨĀģ ´Ũ´Â °ÍĀĖ ÁÁŊĀ´Ī´Ų. +WelcomeLabel2=ĀĖ ¸ļšũģį´Â ąÍĮĪĀĮ ÄÄĮģÅÍŋĄ [name/ver]Āģ(¸Ļ) ŧŗÄĄĮŌ °ÍĀÔ´Ī´Ų.%n%nŧŗÄĄĮĪąâ ĀüŋĄ ´Ų¸Ĩ ĀĀŋëĮÁˇÎą×ˇĨĩéĀģ ¸đĩÎ ´ŨŊÃąâ šŲļø´Ī´Ų. + ; *** "Password" wizard page -WizardPassword=žĪČŖ -PasswordLabel1=ĀĖ ŧŗÄĄ´Â žĪČŖˇÎ ē¸ČŖĩĮ°í ĀÖŊĀ´Ī´Ų. -PasswordLabel3=°čŧĶĮΎÁ¸é žĪČŖ¸Ļ ĀÔˇÂĮŅ ´ŲĀŊ [´ŲĀŊ]Āģ ÅŦ¸¯ĮĪŧŧŋä. žĪČŖ´Â ´ëŧŌšŽĀÚ¸Ļ ą¸ēĐĮÕ´Ī´Ų. -PasswordEditLabel=žĪČŖ(&P): -IncorrectPassword=ĀÔˇÂĮŅ žĪČŖ°Ą Ā߸øĩĮžúŊĀ´Ī´Ų. ´ŲŊà ŊÃĩĩĮĪŧŧŋä. +WizardPassword=ēņšĐ šøČŖ +PasswordLabel1=ĀĖ ŧŗÄĄ ¸ļšũģį´Â ēņšĐ šøČŖˇÎ ē¸ČŖĩĮžî ĀÖŊĀ´Ī´Ų. +PasswordLabel3=ēņšĐ šøČŖ¸Ļ ĀÔˇÂĮΰí "´ŲĀŊ"Āģ ÅŦ¸¯ĮĪŊĘŊÃŋĀ. ēņšĐ šøČŖ´Â ´ëŧŌšŽĀÚ¸Ļ ą¸ēĐĮØžß ĮÕ´Ī´Ų. +PasswordEditLabel=ēņšĐ šøČŖ(&P): +IncorrectPassword=ēņšĐ šøČŖ°Ą Á¤ČŽĮĪÁö žĘŊĀ´Ī´Ų, ´ŲŊà ĀÔˇÂĮĪŊĘŊÃŋĀ. + ; *** "License Agreement" wizard page WizardLicense=ģįŋëąĮ °čžā -LicenseLabel=°čŧĶĮĪąâ ĀüŋĄ ´ŲĀŊ ÁßŋäĮŅ Á¤ē¸¸Ļ ĀĐžî ē¸ŧŧŋä. -LicenseLabel3=´ŲĀŊ ģįŋëąĮ °čžāĀģ ĀĐžî ÁÖŧŧŋä. ŧŗÄĄ¸Ļ °čŧĶĮΎÁ¸é ¸ÕĀú ĀĖ °čžā Áļ°ĮŋĄ ĩŋĀĮĮØžß ĮÕ´Ī´Ų. -LicenseAccepted=°čžāŋĄ ĩŋĀĮĮÔ(&A) -LicenseNotAccepted=°čžāŋĄ ĩŋĀĮ žČ ĮÔ(&D) +LicenseLabel=°čŧĶĮĪąâ ĀüŋĄ ´ŲĀŊĀĮ Áßŋä Á¤ē¸¸Ļ ĀĐžîē¸ŊĘŊÃŋĀ. +LicenseLabel3=´ŲĀŊ ģįŋëąĮ °čžāĀģ ĀĐžîē¸ŊĘŊÃŋĀ, ŧŗÄĄ¸Ļ °čŧĶĮΎÁ¸é ĀĖ °čžāŋĄ ĩŋĀĮĮØžß ĮÕ´Ī´Ų. +LicenseAccepted=ĩŋĀĮĮÕ´Ī´Ų(&A) +LicenseNotAccepted=ĩŋĀĮĮĪÁö žĘŊĀ´Ī´Ų(&D) + ; *** "Information" wizard pages WizardInfoBefore=Á¤ē¸ -InfoBeforeLabel=°čŧĶĮĪąâ ĀüŋĄ ´ŲĀŊ ÁßŋäĮŅ Á¤ē¸¸Ļ ĀĐžî ē¸ŧŧŋä. -InfoBeforeClickLabel=ŧŗÄĄ¸Ļ °čŧĶ ÁøĮāĮŌ ÁØēņ°Ą ĩĮ¸é [´ŲĀŊ]Āģ ÅŦ¸¯ĮÕ´Ī´Ų. +InfoBeforeLabel=°čŧĶĮĪąâ ĀüŋĄ ´ŲĀŊĀĮ Áßŋä Á¤ē¸¸Ļ ĀĐžîē¸ŊĘŊÃŋĀ. +InfoBeforeClickLabel=ŧŗÄĄ¸Ļ °čŧĶĮΎÁ¸é "´ŲĀŊ"Āģ ÅŦ¸¯ĮĪŊĘŊÃŋĀ. WizardInfoAfter=Á¤ē¸ -InfoAfterLabel=°čŧĶĮĪąâ ĀüŋĄ ´ŲĀŊ ÁßŋäĮŅ Á¤ē¸¸Ļ ĀĐžî ē¸ŧŧŋä. -InfoAfterClickLabel=ŧŗÄĄ¸Ļ °čŧĶ ÁøĮāĮŌ ÁØēņ°Ą ĩĮ¸é [´ŲĀŊ]Āģ ÅŦ¸¯ĮÕ´Ī´Ų. +InfoAfterLabel=°čŧĶĮĪąâ ĀüŋĄ ´ŲĀŊĀĮ Áßŋä Á¤ē¸¸Ļ ĀĐžîē¸ŊĘŊÃŋĀ. +InfoAfterClickLabel=ŧŗÄĄ¸Ļ °čŧĶĮΎÁ¸é "´ŲĀŊ"Āģ ÅŦ¸¯ĮĪŊĘŊÃŋĀ. + ; *** "User Information" wizard page WizardUserInfo=ģįŋëĀÚ Á¤ē¸ -UserInfoDesc=Á¤ē¸¸Ļ ĀÔˇÂĮĪŧŧŋä. +UserInfoDesc=ģįŋëĀÚ Á¤ē¸¸Ļ ĀÔˇÂĮĪŊĘŊÃŋĀ. UserInfoName=ģįŋëĀÚ Āˏ§(&U): UserInfoOrg=ÁļÁ÷(&O): -UserInfoSerial=ĀĪˇÃ šøČŖ(&S): -UserInfoNameRequired=Āˏ§Āģ ĀÔˇÂĮØžß ĮÕ´Ī´Ų. +UserInfoSerial=ŊÃ¸Žžķ šøČŖ(&S): +UserInfoNameRequired=ģįŋëĀÚ Āˏ§Āģ ĀÔˇÂĮĪŊĘŊÃŋĀ. + ; *** "Select Destination Location" wizard page -WizardSelectDir=´ëģķ Ā§ÄĄ ŧąÅà -SelectDirDesc=[name]Āģ(¸Ļ) žîĩđŋĄ ŧŗÄĄĮĪŊðÚŊĀ´Īąî? -SelectDirLabel3=ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ [name]Āģ(¸Ļ) ´ŲĀŊ Æú´õŋĄ ŧŗÄĄĮÕ´Ī´Ų. -SelectDirBrowseLabel=°čŧĶĮΎÁ¸é [´ŲĀŊ]Āģ ÅŦ¸¯ĮĪŧŧŋä. ´Ų¸Ĩ Æú´õ¸Ļ ŧąÅÃĮΎÁ¸é [ÃŖžÆē¸ąâ]¸Ļ ÅŦ¸¯ĮĪŧŧŋä. -DiskSpaceMBLabel=Āûžîĩĩ [mb]MBĀĮ ŋŠĀ¯ ĩđŊēÅŠ °ø°ŖĀĖ ĮĘŋäĮÕ´Ī´Ų. -CannotInstallToNetworkDrive=ŧŗÄĄ ĮÁˇÎą×ˇĨĀē ŗ×ÆŽŋöÅŠ ĩåļķĀĖēęŋĄ ŧŗÄĄĮŌ ŧö žøŊĀ´Ī´Ų. -CannotInstallToUNCPath=ŧŗÄĄ ĮÁˇÎą×ˇĨĀē UNC °æˇÎŋĄ ŧŗÄĄĮŌ ŧö žøŊĀ´Ī´Ų. -InvalidPath=ĩåļķĀĖēę šŽĀÚŋÍ ĮÔ˛˛ ĀüÃŧ °æˇÎ¸Ļ ĀÔˇÂĮØžß ĮÕ´Ī´Ų. ŋš:%n%nC:\APP%n%nļĮ´Â ´ŲĀŊ ĮüÅÂĀĮ UNC °æˇÎ:%n%n\\server\share -InvalidDrive=ŧąÅÃĮŅ ĩåļķĀĖēęŗĒ UNC °øĀ¯°Ą žø°ÅŗĒ ĀĖ ĩÎ Į׸ņŋĄ ž×ŧŧŊēĮŌ ŧö žøŊĀ´Ī´Ų. ´Ų¸Ĩ ĩåļķĀĖēęŗĒ UNC °øĀ¯¸Ļ ŧąÅÃĮĪŧŧŋä. -DiskSpaceWarningTitle=ĩđŊēÅŠ °ø°Ŗ ēÎÁˇ -DiskSpaceWarning=ŧŗÄĄ ĮÁˇÎą×ˇĨĀģ ŧŗÄĄĮΎÁ¸é ŋŠĀ¯ ŧŗÄĄ °ø°ŖĀĖ Āûžîĩĩ %1KB°Ą ĮĘŋäĮĪÁö¸¸ ŧąÅÃĮŅ ĩåļķĀĖēęĀĮ °Ąŋë °ø°ŖĀē %2KBšÛŋĄ žøŊĀ´Ī´Ų.%n%ną×ˇĄĩĩ °čŧĶĮĪŊðÚŊĀ´Īąî? +WizardSelectDir=ŧŗÄĄ Ā§ÄĄ ŧąÅà +SelectDirDesc=[name]ĀĮ ŧŗÄĄ Ā§ÄĄ¸Ļ ŧąÅÃĮĪŊĘŊÃŋĀ. +SelectDirLabel3=´ŲĀŊ Æú´õŋĄ [name]Āģ(¸Ļ) ŧŗÄĄĮÕ´Ī´Ų. +SelectDirBrowseLabel=°čŧĶĮΎÁ¸é "´ŲĀŊ"Āģ, ´Ų¸Ĩ Æú´õ¸Ļ ŧąÅÃĮΎÁ¸é "ÃŖžÆē¸ąâ"¸Ļ ÅŦ¸¯ĮĪŊĘŊÃŋĀ. +DiskSpaceGBLabel=ĀĖ ĮÁˇÎą×ˇĨĀē ÃÖŧŌ [gb] GBĀĮ ĩđŊēÅŠ ŋŠĀ¯ °ø°ŖĀĖ ĮĘŋäĮÕ´Ī´Ų. +DiskSpaceMBLabel=ĀĖ ĮÁˇÎą×ˇĨĀē ÃÖŧŌ [mb] MBĀĮ ĩđŊēÅŠ ŋŠĀ¯ °ø°ŖĀĖ ĮĘŋäĮÕ´Ī´Ų. +CannotInstallToNetworkDrive=ŗ×ÆŽŋöÅŠ ĩåļķĀĖēęŋĄ ŧŗÄĄĮŌ ŧö žøŊĀ´Ī´Ų. +CannotInstallToUNCPath=UNC °æˇÎŋĄ ŧŗÄĄĮŌ ŧö žøŊĀ´Ī´Ų. +InvalidPath=ĩåļķĀĖēę šŽĀÚ¸Ļ Æ÷ĮÔĮŅ ĀüÃŧ °æˇÎ¸Ļ ĀÔˇÂĮĪŊĘŊÃŋĀ.%nĄØ ŋš: C:\APP %n%nļĮ´Â, UNC ĮüŊÄĀĮ °æˇÎ¸Ļ ĀÔˇÂĮĪŊĘŊÃŋĀ.%nĄØ ŋš: \\server\share +InvalidDrive=ŧąÅÃĮŅ ĩåļķĀĖēę ļĮ´Â UNC °øĀ¯°Ą Á¸ĀįĮĪÁö žĘ°ÅŗĒ ž×ŧŧŊēĮŌ ŧö žøŊĀ´Ī´Ų, ´Ų¸Ĩ °æˇÎ¸Ļ ŧąÅÃĮĪŊĘŊÃŋĀ. +DiskSpaceWarningTitle=ĩđŊēÅŠ °ø°ŖĀĖ ēÎÁˇĮÕ´Ī´Ų +DiskSpaceWarning=ŧŗÄĄ Ŋà ÃÖŧŌ %1 KB ĩđŊēÅŠ °ø°ŖĀĖ ĮĘŋäĮĪÁö¸¸, ŧąÅÃĮŅ ĩåļķĀĖēęĀĮ ŋŠĀ¯ °ø°ŖĀē %2 KB šÛŋĄ žøŊĀ´Ī´Ų.%n%ną×ˇĄĩĩ °čŧĶĮĪŊðÚŊĀ´Īąî? DirNameTooLong=Æú´õ Āˏ§ ļĮ´Â °æˇÎ°Ą ŗĘšĢ ąé´Ī´Ų. -InvalidDirName=Æú´õ Āˏ§ĀĖ Ā߸øĩĮžúŊĀ´Ī´Ų. -BadDirName32=Æú´õ Āˏ§ŋĄ´Â %n%n%1 šŽĀÚ¸Ļ ģįŋëĮŌ ŧö žøŊĀ´Ī´Ų. -DirExistsTitle=Æú´õ ĀÖĀŊ -DirExists=Æú´õ %n%n%1%n%nĀĖ(°Ą) ĀĖšĖ ĀÖŊĀ´Ī´Ų. ą×ˇĄĩĩ ĮØ´į Æú´õŋĄ ŧŗÄĄĮĪŊðÚŊĀ´Īąî? -DirDoesntExistTitle=Æú´õ žøĀŊ -DirDoesntExist=Æú´õ %n%n%1%n%nĀĖ(°Ą) žøŊĀ´Ī´Ų. Æú´õ¸Ļ ¸¸ĩåŊðÚŊĀ´Īąî? +InvalidDirName=Æú´õ Āˏ§ĀĖ Ā¯ČŋĮĪÁö žĘŊĀ´Ī´Ų. +BadDirName32=Æú´õ Āˏ§Āē ´ŲĀŊ šŽĀÚ¸Ļ Æ÷ĮÔĮŌ ŧö žøŊĀ´Ī´Ų:%n%n%1 +DirExistsTitle=Æú´õ°Ą Á¸ĀįĮÕ´Ī´Ų +DirExists=Æú´õ %n%n%1%n%nĀĖ(°Ą) ĀĖšĖ Á¸ĀįĮÕ´Ī´Ų, ĀĖ Æú´õŋĄ ŧŗÄĄĮĪŊðÚŊĀ´Īąî? +DirDoesntExistTitle=Æú´õ°Ą Á¸ĀįĮĪÁö žĘŊĀ´Ī´Ų +DirDoesntExist=Æú´õ %n%n%1%n%nĀĖ(°Ą) Á¸ĀįĮĪÁö žĘŊĀ´Ī´Ų, ģõˇÎ Æú´õ¸Ļ ¸¸ĩåŊðÚŊĀ´Īąî? + ; *** "Select Components" wizard page WizardSelectComponents=ą¸ŧē ŋäŧŌ ŧąÅà -SelectComponentsDesc=žîļ˛ ą¸ŧē ŋäŧŌ¸Ļ ŧŗÄĄĮĪŊðÚŊĀ´Īąî? -SelectComponentsLabel2=ŧŗÄĄĮŌ ą¸ŧē ŋäŧŌ´Â ŧąÅÃĮΰí ŧŗÄĄĮĪÁö žĘĀģ ą¸ŧē ŋäŧŌ´Â Áöŋėŧŧŋä. °čŧĶ ÁøĮāĮŌ ÁØēņ°Ą ĩĮ¸é [´ŲĀŊ]Āģ ÅŦ¸¯ĮĪŧŧŋä. -FullInstallation=ĀüÃŧ ŧŗÄĄ +SelectComponentsDesc=ŧŗÄĄĮŌ ą¸ŧē ŋäŧŌ¸Ļ ŧąÅÃĮĪŊĘŊÃŋĀ. +SelectComponentsLabel2=ĮĘŋäĮŅ ą¸ŧē ŋäŧŌ´Â ÃŧÅŠĮΰí ēŌĮĘŋäĮŅ ą¸ŧē ŋäŧŌ´Â ÃŧÅŠ ĮØÁĻĮÕ´Ī´Ų, °čŧĶĮΎÁ¸é "´ŲĀŊ"Āģ ÅŦ¸¯ĮĪŊĘŊÃŋĀ. +FullInstallation=¸đĩÎ ŧŗÄĄ ; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) -CompactInstallation=Compact ŧŗÄĄ +CompactInstallation=ÃÖŧŌ ŧŗÄĄ CustomInstallation=ģįŋëĀÚ ÁöÁ¤ ŧŗÄĄ -NoUninstallWarningTitle=ą¸ŧē ŋäŧŌ°Ą ĀÖĀŊ -NoUninstallWarning=ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ ą¸ŧē ŋäŧŌ %n%n%1%n%nĀĖ(°Ą) ÄÄĮģÅÍŋĄ ĀĖšĖ ŧŗÄĄĩĮžî ĀÖĀŊĀģ °¨ÁöĮßŊĀ´Ī´Ų. Āˎ¯ĮŅ ą¸ŧē ŋäŧŌ´Â ŧąÅà ÃëŧŌĮØĩĩ ÁϰÅĩĮÁö žĘŊĀ´Ī´Ų.%n%ną×ˇĄĩĩ °čŧĶĮĪŊðÚŊĀ´Īąî? -ComponentSize1=%1KB -ComponentSize2=%1MB -ComponentsDiskSpaceMBLabel=ĮöĀį ŧąÅÃĀģ §ĮØŧ­´Â Āûžîĩĩ [mb]MBĀĮ ĩđŊēÅŠ °ø°ŖĀĖ ĮĘŋäĮÕ´Ī´Ų. +NoUninstallWarningTitle=ą¸ŧē ŋäŧŌ°Ą Á¸ĀįĮÕ´Ī´Ų +NoUninstallWarning=´ŲĀŊ ą¸ŧē ŋäŧŌ°Ą ĀĖšĖ ŧŗÄĄĩĮžî ĀÖŊĀ´Ī´Ų:%n%n%1%n%n§ ą¸ŧē ŋäŧŌĀģ ŧąÅÃĮĪÁö žĘ¸é, ĮÁˇÎą×ˇĨ ÁϰÅŊà ĀĖ ą¸ŧē ŋäŧŌĩéĀē ÁϰÅĩĮÁö žĘĀģ °Ė´Ī´Ų.%n%ną×ˇĄĩĩ °čŧĶĮĪŊðÚŊĀ´Īąî? +ComponentSize1=%1 KB +ComponentSize2=%1 MB +ComponentsDiskSpaceGBLabel=ĮöĀį ŧąÅÃĀē ÃÖŧŌ [gb] GBĀĮ ĩđŊēÅŠ ŋŠĀ¯ °ø°ŖĀĖ ĮĘŋäĮÕ´Ī´Ų. +ComponentsDiskSpaceMBLabel=ĮöĀį ŧąÅÃĀē ÃÖŧŌ [mb] MBĀĮ ĩđŊēÅŠ ŋŠĀ¯ °ø°ŖĀĖ ĮĘŋäĮÕ´Ī´Ų. + ; *** "Select Additional Tasks" wizard page WizardSelectTasks=Ãß°Ą ĀÛž÷ ŧąÅà -SelectTasksDesc=žîļ˛ ĀÛž÷Āģ Ãß°ĄˇÎ ŧöĮāĮĪŊðÚŊĀ´Īąî? -SelectTasksLabel2=ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ [name]Āģ(¸Ļ) ŧŗÄĄĮĪ´Â ĩŋžČ ŧöĮāĮŌ Ãß°Ą ĀÛž÷Āģ ŧąÅÃĮŅ ČÄ [´ŲĀŊ]Āģ ÅŦ¸¯ĮĪŧŧŋä. +SelectTasksDesc=ŧöĮāĮŌ Ãß°Ą ĀÛž÷Āģ ŧąÅÃĮĪŊĘŊÃŋĀ. +SelectTasksLabel2=[name] ŧŗÄĄ °úÁ¤ŋĄ Æ÷ĮÔĮŌ Ãß°Ą ĀÛž÷Āģ ŧąÅÃĮŅ ČÄ, "´ŲĀŊ"Āģ ÅŦ¸¯ĮĪŊĘŊÃŋĀ. + ; *** "Select Start Menu Folder" wizard page WizardSelectProgramGroup=ŊÃĀÛ ¸Ū´ē Æú´õ ŧąÅà -SelectStartMenuFolderDesc=ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ ĮÁˇÎą×ˇĨĀĮ šŲˇÎ °Ąąâ¸Ļ žîĩđŋĄ ¸¸ĩéĩĩˇĪ ĮĪŊðÚŊĀ´Īąî? -SelectStartMenuFolderLabel3=ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ ĮÁˇÎą×ˇĨĀĮ šŲˇÎ °Ąąâ¸Ļ ´ŲĀŊ ŊÃĀÛ ¸Ū´ē Æú´õŋĄ ¸¸ĩė´Ī´Ų. -SelectStartMenuFolderBrowseLabel=°čŧĶĮΎÁ¸é [´ŲĀŊ]Āģ ÅŦ¸¯ĮĪŧŧŋä. ´Ų¸Ĩ Æú´õ¸Ļ ŧąÅÃĮΎÁ¸é [ÃŖžÆē¸ąâ]¸Ļ ÅŦ¸¯ĮĪŧŧŋä. -MustEnterGroupName=Æú´õ Āˏ§Āģ ĀÔˇÂĮØžß ĮÕ´Ī´Ų. +SelectStartMenuFolderDesc=žîĩđŋĄ ĮÁˇÎą×ˇĨ šŲˇÎ°Ąąâ¸Ļ Ā§ÄĄĮΰÚŊĀ´Īąî? +SelectStartMenuFolderLabel3=´ŲĀŊ ŊÃĀÛ ¸Ū´ē Æú´õŋĄ ĮÁˇÎą×ˇĨ šŲˇÎ°Ąąâ¸Ļ ¸¸ĩė´Ī´Ų. +SelectStartMenuFolderBrowseLabel=°čŧĶĮΎÁ¸é "´ŲĀŊ"Āģ ÅŦ¸¯Įΰí, ´Ų¸Ĩ Æú´õ¸Ļ ŧąÅÃĮΎÁ¸é "ÃŖžÆē¸ąâ"¸Ļ ÅŦ¸¯ĮĪŊĘŊÃŋĀ. +MustEnterGroupName=Æú´õ Āˏ§Āģ ĀÔˇÂĮĪŊĘŊÃŋĀ. GroupNameTooLong=Æú´õ Āˏ§ ļĮ´Â °æˇÎ°Ą ŗĘšĢ ąé´Ī´Ų. -InvalidGroupName=Æú´õ Āˏ§ĀĖ Ā߸øĩĮžúŊĀ´Ī´Ų. -BadGroupName=Æú´õ Āˏ§ŋĄ´Â %n%n%1 šŽĀÚ¸Ļ ģįŋëĮŌ ŧö žøŊĀ´Ī´Ų. +InvalidGroupName=Æú´õ Āˏ§ĀĖ Ā¯ČŋĮĪÁö žĘŊĀ´Ī´Ų. +BadGroupName=Æú´õ Āˏ§Āē ´ŲĀŊ šŽĀÚ¸Ļ Æ÷ĮÔĮŌ ŧö žøŊĀ´Ī´Ų:%n%n%1 NoProgramGroupCheck2=ŊÃĀÛ ¸Ū´ē Æú´õ¸Ļ ¸¸ĩéÁö žĘĀŊ(&D) + ; *** "Ready to Install" wizard page -WizardReady=ŧŗÄĄ ÁØēņĩĘ -ReadyLabel1=ĀĖÁĻ ŧŗÄĄ ĮÁˇÎą×ˇĨĀĖ ÄÄĮģÅÍŋĄ [name] ŧŗÄĄ¸Ļ ŊÃĀÛĮŌ ÁØēņ°Ą ĩĮžúŊĀ´Ī´Ų. -ReadyLabel2a=ŧŗÄĄ¸Ļ °čŧĶĮΎÁ¸é [ŧŗÄĄ]¸Ļ ÅŦ¸¯Įΰí, ŧŗÁ¤Āģ °ËÅäĮĪ°ÅŗĒ ē¯°æĮΎÁ¸é [ĩÚˇÎ]¸Ļ ÅŦ¸¯ĮĪŧŧŋä. -ReadyLabel2b=ŧŗÄĄ¸Ļ °čŧĶĮΎÁ¸é [ŧŗÄĄ]¸Ļ ÅŦ¸¯ĮĪŧŧŋä. +WizardReady=ŧŗÄĄ ÁØēņ ŋΎá +ReadyLabel1=ąÍĮĪĀĮ ÄÄĮģÅÍŋĄ [name]Āģ(¸Ļ) ŧŗÄĄĮŌ ÁØēņ°Ą ĩĮžúŊĀ´Ī´Ų. +ReadyLabel2a=ŧŗÄĄ¸Ļ °čŧĶĮΎÁ¸é "ŧŗÄĄ"¸Ļ, ŧŗÁ¤Āģ 睰æĮĪ°ÅŗĒ °ËÅäĮΎÁ¸é "ĩÚˇÎ"¸Ļ ÅŦ¸¯ĮĪŊĘŊÃŋĀ. +ReadyLabel2b=ŧŗÄĄ¸Ļ °čŧĶĮΎÁ¸é "ŧŗÄĄ"¸Ļ ÅŦ¸¯ĮĪŊĘŊÃŋĀ. ReadyMemoUserInfo=ģįŋëĀÚ Á¤ē¸: -ReadyMemoDir=´ëģķ Ā§ÄĄ: +ReadyMemoDir=ŧŗÄĄ Ā§ÄĄ: ReadyMemoType=ŧŗÄĄ Įü: ReadyMemoComponents=ŧąÅÃĮŅ ą¸ŧē ŋäŧŌ: ReadyMemoGroup=ŊÃĀÛ ¸Ū´ē Æú´õ: ReadyMemoTasks=Ãß°Ą ĀÛž÷: + ; *** "Preparing to Install" wizard page WizardPreparing=ŧŗÄĄ ÁØēņ Áß -PreparingDesc=ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ ÄÄĮģÅÍŋĄ [name] ŧŗÄĄ¸Ļ ÁØēņĮΰí ĀÖŊĀ´Ī´Ų. -PreviousInstallNotCompleted=ĀĖĀü ĮÁˇÎą×ˇĨĀĮ ŧŗÄĄ/ÁϰŠĀÛž÷ĀĖ ŋΎáĩĮÁö žĘžŌŊĀ´Ī´Ų. ĮØ´į ŧŗÄĄ¸Ļ ŋΎáĮΎÁ¸é ÄÄĮģÅÍ¸Ļ ´ŲŊà ŊÃĀÛĮØžß ĮÕ´Ī´Ų.%n%nÄÄĮģÅÍ¸Ļ ´ŲŊà ŊÃĀÛĮŅ ČÄ [name] ŧŗÄĄ¸Ļ ŋΎáĮΎÁ¸é ŧŗÄĄ ĮÁˇÎą×ˇĨĀģ ´ŲŊà ŊĮĮāĮĪŧŧŋä. -CannotContinue=ŧŗÄĄ ĮÁˇÎą×ˇĨĀģ °čŧĶĮŌ ŧö žøŊĀ´Ī´Ų. ÁžˇáĮΎÁ¸é [ÃëŧŌ]¸Ļ ÅŦ¸¯ĮĪŧŧŋä. -ApplicationsFound=ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ ž÷ĩĨĀĖÆŽĮØžß ĮĪ´Â ÆÄĀĪĀĖ ´ŲĀŊ ĀĀŋë ĮÁˇÎą×ˇĨŋĄ ģįŋëĩĮ°í ĀÖŊĀ´Ī´Ų. ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ Āˎ¯ĮŅ ĀĀŋë ĮÁˇÎą×ˇĨĀģ ĀÚĩŋˇÎ ´ŨĩĩˇĪ ĮãŋëĮĪ´Â °ÍĀĖ ÁÁŊĀ´Ī´Ų. -ApplicationsFound2=ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ ž÷ĩĨĀĖÆŽĮØžß ĮĪ´Â ÆÄĀĪĀĖ ´ŲĀŊ ĀĀŋë ĮÁˇÎą×ˇĨŋĄ ģįŋëĩĮ°í ĀÖŊĀ´Ī´Ų. ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ Āˎ¯ĮŅ ĀĀŋë ĮÁˇÎą×ˇĨĀģ ĀÚĩŋˇÎ ´ŨĩĩˇĪ ĮãŋëĮĪ´Â °ÍĀĖ ÁÁŊĀ´Ī´Ų. ŧŗÄĄ°Ą ŋΎáĩĮ¸é ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ ĀĀŋë ĮÁˇÎą×ˇĨĀģ ´ŲŊà ŊÃĀÛĮΎÁ°í ŊÃĩĩĮÕ´Ī´Ų. -CloseApplications=ĀĀŋë ĮÁˇÎą×ˇĨ ĀÚĩŋ ´Ũąâ(&A) -DontCloseApplications=ĀĀŋë ĮÁˇÎą×ˇĨĀģ ´ŨÁö žĘĀŊ(&D) -ErrorCloseApplications=ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ ĀĪēÎ ĀĀŋë ĮÁˇÎą×ˇĨĀģ ĀÚĩŋˇÎ ´ŨĀģ ŧö žøŊĀ´Ī´Ų. °čŧĶĮĪąâ ĀüŋĄ ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ ž÷ĩĨĀĖÆŽĮØžß ĮĪ´Â ÆÄĀĪĀģ ģįŋëĮĪ´Â ĀĀŋë ĮÁˇÎą×ˇĨĀģ ¸đĩÎ ´Ũ´Â °ÍĀĖ ÁÁŊĀ´Ī´Ų. +PreparingDesc=ąÍĮĪĀĮ ÄÄĮģÅÍŋĄ [name] ŧŗÄĄ¸Ļ ÁØēņĮĪ´Â ÁßĀÔ´Ī´Ų. +PreviousInstallNotCompleted=ĀĖĀü ĮÁˇÎą×ˇĨĀĮ ŧŗÄĄ/ÁϰŠĀÛž÷ĀĖ ŋΎáĩĮÁö žĘžŌŊĀ´Ī´Ų, ŋΎáĮΎÁ¸é ÄÄĮģÅÍ¸Ļ ´ŲŊà ŊÃĀÛĮØžß ĮÕ´Ī´Ų.%n%nÄÄĮģÅÍ¸Ļ ´ŲŊà ŊÃĀÛĮŅ ČÄ, ŧŗÄĄ ¸ļšũģį¸Ļ ´ŲŊà ŊĮĮāĮĪŋŠ [name] ŧŗÄĄ¸Ļ ŋΎáĮĪŊÃąâ šŲļø´Ī´Ų. +CannotContinue=ŧŗÄĄ¸Ļ °čŧĶĮŌ ŧö žøŊĀ´Ī´Ų, "ÃëŧŌ"¸Ļ ÅŦ¸¯ĮĪŋŠ ŧŗÄĄ¸Ļ ÁžˇáĮĪŊĘŊÃŋĀ. +ApplicationsFound=´ŲĀŊ ĀĀŋëĮÁˇÎą×ˇĨĀĖ ŧŗÄĄ ž÷ĩĨĀ˯ްĄ ĮĘŋäĮŅ ÆÄĀĪĀģ ģįŋëĮΰí ĀÖŊĀ´Ī´Ų, ŧŗÄĄ ¸ļšũģį°Ą Āˎą ĀĀŋëĮÁˇÎą×ˇĨĀģ ĀÚĩŋˇÎ ÁžˇáĮŌ ŧö ĀÖĩĩˇĪ ĮãŋëĮĪŊÃąâ šŲļø´Ī´Ų. +ApplicationsFound2=´ŲĀŊ ĀĀŋëĮÁˇÎą×ˇĨĀĖ ŧŗÄĄ ž÷ĩĨĀ˯ްĄ ĮĘŋäĮŅ ÆÄĀĪĀģ ģįŋëĮΰí ĀÖŊĀ´Ī´Ų, ŧŗÄĄ ¸ļšũģį°Ą Āˎą ĀĀŋëĮÁˇÎą×ˇĨĀģ ĀÚĩŋˇÎ ÁžˇáĮŌ ŧö ĀÖĩĩˇĪ ĮãŋëĮĪŊÃąâ šŲļø´Ī´Ų. ŧŗÄĄ°Ą ŋΎáĩĮ¸é, ŧŗÄĄ ¸ļšũģį´Â ĀĖ ĀĀŋëĮÁˇÎą×ˇĨĀĖ ´ŲŊà ŊÃĀÛĩĮĩĩˇĪ ŊÃĩĩĮŌ °Ė´Ī´Ų. +CloseApplications=ĀÚĩŋˇÎ ĀĀŋëĮÁˇÎą×ˇĨĀģ ÁžˇáĮÔ(&A) +DontCloseApplications=ĀĀŋëĮÁˇÎą×ˇĨĀģ ÁžˇáĮĪÁö žĘĀŊ(&D) +ErrorCloseApplications=ŧŗÄĄ ¸ļšũģį°Ą ĀĀŋëĮÁˇÎą×ˇĨĀģ ĀÚĩŋˇÎ ÁžˇáĮŌ ŧö žøŊĀ´Ī´Ų, °čŧĶĮĪąâ ĀüŋĄ ŧŗÄĄ ž÷ĩĨĀ˯ްĄ ĮĘŋäĮŅ ÆÄĀĪĀģ ģįŋëĮΰí ĀÖ´Â ĀĀŋëĮÁˇÎą×ˇĨĀģ ¸đĩÎ ÁžˇáĮĪŊÃąâ šŲļø´Ī´Ų. +PrepareToInstallNeedsRestart=ŧŗÄĄ ¸ļšũģį´Â ąÍĮĪĀĮ ÄÄĮģÅÍ¸Ļ ĀįŊÃĀÛĮØžß ĮÕ´Ī´Ų. [name] ŧŗÄĄ¸Ļ ŋΎáĮĪąâ §ĮØ ÄÄĮģÅÍ¸Ļ ´ŲŊà ŊÃĀÛĮŅ ČÄŋĄ ŧŗÄĄ ¸ļšũģį¸Ļ ´ŲŊà ŊĮĮāĮØ ÁÖŊĘŊÃŋĀ.%n%nÁöąŨ ´ŲŊà ŊÃĀÛĮĪŊðÚŊĀ´Īąî? + ; *** "Installing" wizard page WizardInstalling=ŧŗÄĄ Áß -InstallingLabel=ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ ÄÄĮģÅÍŋĄ [name]Āģ(¸Ļ) ŧŗÄĄĮĪ´Â ĩŋžČ ąâ´ŲˇÁ ÁÖŧŧŋä. +InstallingLabel=ąÍĮĪĀĮ ÄÄĮģÅÍŋĄ [name]Āģ(¸Ļ) ŧŗÄĄĮĪ´Â Áß... ĀáŊà ąâ´ŲˇÁ ÁÖŊĘŊÃŋĀ. + ; *** "Setup Completed" wizard page -FinishedHeadingLabel=[name] ŧŗÁ¤ ¸ļšũģį¸Ļ ŋΎáĮĪ´Â Áß -FinishedLabelNoIcons=ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ ÄÄĮģÅÍŋĄ [name]Āģ(¸Ļ) ŧŗÄĄĮßŊĀ´Ī´Ų. -FinishedLabel=ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ ÄÄĮģÅÍŋĄ [name]Āģ(¸Ļ) ŧŗÄĄĮßŊĀ´Ī´Ų. ŧŗÄĄĮŅ šŲˇÎ °Ąąâ¸Ļ ŧąÅÃĮĪŋŠ ĮØ´į ĀĀŋë ĮÁˇÎą×ˇĨĀģ ŊÃĀÛĮŌ ŧö ĀÖŊĀ´Ī´Ų. -ClickFinish=ŧŗÄĄ ĮÁˇÎą×ˇĨĀģ ÁžˇáĮΎÁ¸é [¸ļħ]Āģ ÅŦ¸¯ĮĪŧŧŋä. -FinishedRestartLabel=[name] ŧŗÄĄ¸Ļ ŋΎáĮΎÁ¸é ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ ÄÄĮģÅÍ¸Ļ ´ŲŊà ŊÃĀÛĮØžß ĮÕ´Ī´Ų. ÁöąŨ ´ŲŊà ŊÃĀÛĮĪŊðÚŊĀ´Īąî? -FinishedRestartMessage=[name] ŧŗÄĄ¸Ļ ŋΎáĮΎÁ¸é ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ ÄÄĮģÅÍ¸Ļ ´ŲŊà ŊÃĀÛĮØžß ĮÕ´Ī´Ų.%n%nÁöąŨ ´ŲŊà ŊÃĀÛĮĪŊðÚŊĀ´Īąî? -ShowReadmeCheck=ŋš, README ÆÄĀĪĀģ 珰ÚŊĀ´Ī´Ų. -YesRadio=ŋš, ÄÄĮģÅÍ¸Ļ ÁöąŨ ´ŲŊà ŊÃĀÛĮΰÚŊĀ´Ī´Ų(&Y). -NoRadio=žÆ´Īŋä, ÄÄĮģÅÍ¸Ļ ŗĒÁßŋĄ ´ŲŊà ŊÃĀÛĮΰÚŊĀ´Ī´Ų(&N). +FinishedHeadingLabel=[name] ŧŗÄĄ ¸ļšũģį ŋΎá +FinishedLabelNoIcons=ąÍĮĪĀĮ ÄÄĮģÅÍŋĄ [name]ĀĖ(°Ą) ŧŗÄĄĩĮžúŊĀ´Ī´Ų. +FinishedLabel=ąÍĮĪĀĮ ÄÄĮģÅÍŋĄ [name]ĀĖ(°Ą) ŧŗÄĄĩĮžúŊĀ´Ī´Ų, ĀĀŋëĮÁˇÎą×ˇĨĀē ŧŗÄĄĩČ žÆĀĖÄÜĀģ ŧąÅÃĮĪŋŠ ŊÃĀÛĮŌ ŧö ĀÖŊĀ´Ī´Ų. +ClickFinish=ŧŗÄĄ¸Ļ ŗĄŗģˇÁ¸é "Ážˇá"¸Ļ ÅŦ¸¯ĮĪŊĘŊÃŋĀ. +FinishedRestartLabel=[name] ŧŗÄĄ¸Ļ ŋΎáĮΎÁ¸é, ÄÄĮģÅÍ¸Ļ ´ŲŊà ŊÃĀÛĮØžß ĮÕ´Ī´Ų. ÁöąŨ ´ŲŊà ŊÃĀÛĮĪŊðÚŊĀ´Īąî? +FinishedRestartMessage=[name] ŧŗÄĄ¸Ļ ŋΎáĮΎÁ¸é, ÄÄĮģÅÍ¸Ļ ´ŲŊà ŊÃĀÛĮØžß ĮÕ´Ī´Ų.%n%nÁöąŨ ´ŲŊà ŊÃĀÛĮĪŊðÚŊĀ´Īąî? +ShowReadmeCheck=ŋš, README ÆÄĀĪĀģ ĮĨŊÃĮÕ´Ī´Ų +YesRadio=ŋš, ÁöąŨ ´ŲŊà ŊÃĀÛĮÕ´Ī´Ų(&Y) +NoRadio=žÆ´ĪŋĀ, ŗĒÁßŋĄ ´ŲŊà ŊÃĀÛĮÕ´Ī´Ų(&N) ; used for example as 'Run MyProg.exe' RunEntryExec=%1 ŊĮĮā ; used for example as 'View Readme.txt' -RunEntryShellExec=%1 ē¸ąâ +RunEntryShellExec=%1 ĮĨŊà + ; *** "Setup Needs the Next Disk" stuff -ChangeDiskTitle=ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ ´ŲĀŊ ĩđŊēÅŠ°Ą ĮĘŋäĮÔ -SelectDiskLabel2=ĩđŊēÅŠ %1Āģ(¸Ļ) ģđĀÔĮŅ ´ŲĀŊ [ČŽĀÎ]Āģ ÅŦ¸¯ĮĪŧŧŋä.%n%nĀĖ ĩđŊēÅŠĀĮ ÆÄĀĪĀĖ žÆˇĄ ĮĨŊÃĩČ Æú´õ°Ą žÆ´Ņ ´Ų¸Ĩ Æú´õŋĄ Ā֏¸é ŋÃšŲ¸Ĩ °æˇÎ¸Ļ ĀÔˇÂĮĪ°ÅŗĒ [ÃŖžÆē¸ąâ]¸Ļ ÅŦ¸¯ĮĪŧŧŋä. +ChangeDiskTitle=ĩđŊēÅŠ°Ą ĮĘŋäĮÕ´Ī´Ų +SelectDiskLabel2=ĩđŊēÅŠ %1Āģ(¸Ļ) ģđĀÔĮΰí "ČŽĀÎ"Āģ ÅŦ¸¯ĮĪŊĘŊÃŋĀ.%n%nĀĖ ĩđŊēÅŠ ģķĀĮ ÆÄĀĪĀĖ žÆˇĄ °æˇÎ°Ą žÆ´Ņ °÷ŋĄ ĀÖ´Â °æŋė, ŋÃšŲ¸Ĩ °æˇÎ¸Ļ ĀÔˇÂĮĪ°ÅŗĒ "ÃŖžÆē¸ąâ"¸Ļ ÅŦ¸¯ĮĪŊÃąâ šŲļø´Ī´Ų. PathLabel=°æˇÎ(&P): -FileNotInDir2="%2"ŋĄŧ­ ÆÄĀĪ "%1"Āģ(¸Ļ) ÃŖĀģ ŧö žøŊĀ´Ī´Ų. ŋÃšŲ¸Ĩ ĩđŊēÅŠ¸Ļ ģđĀÔĮĪ°ÅŗĒ ´Ų¸Ĩ Æú´õ¸Ļ ŧąÅÃĮĪŧŧŋä. -SelectDirectoryLabel=´ŲĀŊ ĩđŊēÅŠĀĮ Ā§ÄĄ¸Ļ ÁöÁ¤ĮĪŧŧŋä. +FileNotInDir2=%2ŋĄ ÆÄĀĪ %1Āģ(¸Ļ) Ā§ÄĄĮŌ ŧö žøŊĀ´Ī´Ų, ŋÃšŲ¸Ĩ ĩđŊēÅŠ¸Ļ ģđĀÔĮĪ°ÅŗĒ ´Ų¸Ĩ Æú´õ¸Ļ ŧąÅÃĮĪŊĘŊÃŋĀ. +SelectDirectoryLabel=´ŲĀŊ ĩđŊēÅŠĀĮ Ā§ÄĄ¸Ļ ÁöÁ¤ĮĪŊĘŊÃŋĀ. + ; *** Installation phase messages -SetupAborted=ŧŗÄĄ¸Ļ ŋΎáĮĪÁö ¸øĮßŊĀ´Ī´Ų.%n%nšŽÁĻ¸Ļ ĮذáĮŅ ´ŲĀŊ ŧŗÄĄ ĮÁˇÎą×ˇĨĀģ ´ŲŊà ŊĮĮāĮĪŧŧŋä. -EntryAbortRetryIgnore=´ŲŊà ŊÃĩĩĮΎÁ¸é [´ŲŊà ŊÃĩĩ]¸Ļ, ą×ˇĄĩĩ °čŧĶĮΎÁ¸é [šĢŊÃ]¸Ļ, ŧŗÄĄ¸Ļ ÃëŧŌĮΎÁ¸é [Áß´Ü]Āģ ÅŦ¸¯ĮĪŧŧŋä. +SetupAborted=ŧŗÄĄ°Ą ŋΎáĩĮÁö žĘžŌŊĀ´Ī´Ų.%n%nšŽÁĻ¸Ļ ĮذáĮŅ ČÄ, ´ŲŊà ŧŗÄĄ¸Ļ ŊÃĀÛĮĪŊĘŊÃŋĀ. +AbortRetryIgnoreSelectAction=ž×ŧĮĀģ ŧąÅÃĮØ ÁÖŊĘŊÃŋĀ. +AbortRetryIgnoreRetry=ĀįŊÃĩĩ(&T) +AbortRetryIgnoreIgnore=ŋŽų¸Ļ šĢŊÃĮΰí ÁøĮā(&I) +AbortRetryIgnoreCancel=ŧŗÄĄ ÃëŧŌ + ; *** Installation status messages -StatusClosingApplications=ĀĀŋë ĮÁˇÎą×ˇĨĀģ ´Ũ´Â Áß... -StatusCreateDirs=ĩđˇēÅÍ¸Ž¸Ļ ¸¸ĩå´Â Áß... +StatusClosingApplications=ĀĀŋëĮÁˇÎą×ˇĨĀģ ÁžˇáĮĪ´Â Áß... +StatusCreateDirs=Æú´õ¸Ļ ¸¸ĩå´Â Áß... StatusExtractFiles=ÆÄĀĪĀģ ÃßÃâĮĪ´Â Áß... -StatusCreateIcons=šŲˇÎ °Ąąâ¸Ļ ¸¸ĩå´Â Áß... +StatusCreateIcons=šŲˇÎ°Ąąâ¸Ļ ģũŧēĮĪ´Â Áß... StatusCreateIniEntries=INI Į׸ņĀģ ¸¸ĩå´Â Áß... StatusCreateRegistryEntries=ˇšÁöŊēÆŽ¸Ž Į׸ņĀģ ¸¸ĩå´Â Áß... StatusRegisterFiles=ÆÄĀĪĀģ ĩîˇĪĮĪ´Â Áß... StatusSavingUninstall=ÁϰŠÁ¤ē¸¸Ļ ĀúĀåĮĪ´Â Áß... StatusRunProgram=ŧŗÄĄ¸Ļ ŋΎáĮĪ´Â Áß... -StatusRestartingApplications=ĀĀŋë ĮÁˇÎą×ˇĨĀģ ´ŲŊà ŊÃĀÛĮĪ´Â Áß... -StatusRollback=睰æ ģįĮ×Āģ ˇŅšéĮĪ´Â Áß... +StatusRestartingApplications=ĀĀŋëĮÁˇÎą×ˇĨĀģ ´ŲŊà ŊÃĀÛĮĪ´Â Áß... +StatusRollback=睰æĀģ ÃëŧŌĮĪ´Â Áß... + ; *** Misc. errors ErrorInternal2=ŗģēÎ ŋŽų: %1 ErrorFunctionFailedNoCode=%1 ŊĮÆĐ -ErrorFunctionFailed=%1 ŊĮÆĐ, ÄÚĩå %2 -ErrorFunctionFailedWithMessage=%1 ŊĮÆĐ, ÄÚĩå %2.%n%3 -ErrorExecutingProgram=ÆÄĀĪĀģ ŊĮĮāĮŌ ŧö žøĀŊ:%n%1 +ErrorFunctionFailed=%1 ŊĮÆĐ; ÄÚĩå %2 +ErrorFunctionFailedWithMessage=%1 ŊĮÆĐ, ÄÚĩå: %2.%n%3 +ErrorExecutingProgram=ÆÄĀĪ ŊĮĮā ŋŽų:%n%1 + ; *** Registry errors -ErrorRegOpenKey=ˇšÁöŊēÆŽ¸Ž Ű¸Ļ ŋŠ´Â Áß ŋŽų šßģũ:%n%1\%2 -ErrorRegCreateKey=ˇšÁöŊēÆŽ¸Ž Ű¸Ļ ¸¸ĩå´Â Áß ŋŽų šßģũ:%n%1\%2 -ErrorRegWriteKey=ˇšÁöŊēÆŽ¸Ž ŰŋĄ ąâˇĪĮĪ´Â Áß ŋŽų šßģũ:%n%1\%2 +ErrorRegOpenKey=ˇšÁöŊēÆŽ¸Ž Ű ŋ­ąâ ŋŽų:%n%1\%2 +ErrorRegCreateKey=ˇšÁöŊēÆŽ¸Ž Ű ģũŧē ŋŽų:%n%1\%2 +ErrorRegWriteKey=ˇšÁöŊēÆŽ¸Ž Ű ž˛ąâ ŋŽų:%n%1\%2 + ; *** INI errors -ErrorIniEntry=ÆÄĀĪ "%1"ŋĄ INI Į׸ņĀģ ¸¸ĩå´Â ÁßŋĄ ŋŽų°Ą šßģũĮßŊĀ´Ī´Ų. +ErrorIniEntry=%1 ÆÄĀĪŋĄ INI Į׸ņ ¸¸ĩéąâ ŋŽųĀÔ´Ī´Ų. + ; *** File copying errors -FileAbortRetryIgnore=´ŲŊà ŊÃĩĩĮΎÁ¸é [´ŲŊà ŊÃĩĩ]¸Ļ, ĀĖ ÆÄĀĪĀģ °ĮŗĘļŲˇÁ¸é [šĢŊÃ](ąĮĀåĩĮÁö žĘĀŊ)¸Ļ, ŧŗÄĄ¸Ļ ÃëŧŌĮΎÁ¸é [Áß´Ü]Āģ ÅŦ¸¯ĮĪŧŧŋä. -FileAbortRetryIgnore2=´ŲŊà ŊÃĩĩĮΎÁ¸é [´ŲŊà ŊÃĩĩ]¸Ļ, ą×ˇĄĩĩ °čŧĶĮΎÁ¸é [šĢŊÃ](ąĮĀåĩĮÁö žĘĀŊ)¸Ļ, ŧŗÄĄ¸Ļ ÃëŧŌĮΎÁ¸é [Áß´Ü]Āģ ÅŦ¸¯ĮĪŧŧŋä. -SourceIsCorrupted=ŋøēģ ÆÄĀĪĀĖ ŧÕģķĩĮžúŊĀ´Ī´Ų. -SourceDoesntExist=ŋøēģ ÆÄĀĪ "%1"ĀĖ(°Ą) žøŊĀ´Ī´Ų. -ExistingFileReadOnly=ąâÁ¸ ÆÄĀĪĀĖ ĀĐąâ Āüŋ돷ΠĮĨŊÃĩĮžî ĀÖŊĀ´Ī´Ų.%n%nĀĐąâ Āüŋë Æ¯ŧēĀģ ÁϰÅĮĪ°í ´ŲŊà ŊÃĩĩĮΎÁ¸é [´ŲŊà ŊÃĩĩ]¸Ļ, ĀĖ ÆÄĀĪĀģ °ĮŗĘļŲˇÁ¸é [šĢŊÃ]¸Ļ, ŧŗÄĄ¸Ļ ÃëŧŌĮΎÁ¸é [Áß´Ü]Āģ ÅŦ¸¯ĮĪŧŧŋä. -ErrorReadingExistingDest=ąâÁ¸ ÆÄĀĪĀģ ĀĐ´Â Áß ŋŽų šßģũ: -FileExists=ĮØ´į ÆÄĀĪĀĖ ĀĖšĖ ĀÖŊĀ´Ī´Ų.%n%nŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ ĀĖ ÆÄĀĪĀģ ĩ¤žîž˛ĩĩˇĪ ĮĪŊðÚŊĀ´Īąî? -ExistingFileNewer=ąâÁ¸ ÆÄĀĪĀĖ ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ ŧŗÄĄĮΎÁ´Â ÆÄĀĪē¸´Ų ÃÖŊÅĀÔ´Ī´Ų. ąâÁ¸ ÆÄĀĪĀģ ÁöĮŌ °ÍĀģ ąĮĀåĮÕ´Ī´Ų.%n%nąâÁ¸ ÆÄĀĪĀģ ÁöĮĪŊðÚŊĀ´Īąî? -ErrorChangingAttr=ąâÁ¸ ÆÄĀĪĀĮ ƯŧēĀģ 睰æĮĪ´Â Áß ŋŽų šßģũ: -ErrorCreatingTemp=´ëģķ ĩđˇēÅ͸ŽŋĄ ÆÄĀĪĀģ ¸¸ĩå´Â Áß ŋŽų šßģũ: -ErrorReadingSource=ŋøēģ ÆÄĀĪĀģ ĀĐ´Â Áß ŋŽų šßģũ: -ErrorCopying=ÆÄĀĪĀģ ēšģįĮĪ´Â Áß ŋŽų šßģũ: -ErrorReplacingExistingFile=ąâÁ¸ ÆÄĀĪĀģ šŲ˛Ų´Â Áß ŋŽų šßģũ: +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=RestartReplace ŊĮÆĐ: -ErrorRenamingTemp=´ëģķ ĩđˇēÅ͸ŽŋĄ ĀÖ´Â ÆÄĀĪ Āˏ§Āģ šŲ˛Ų´Â Áß ŋŽų šßģũ: -ErrorRegisterServer=DLL/OCX¸Ļ ĩîˇĪĮŌ ŧö žøĀŊ: %1 -ErrorRegSvr32Failed=Ážˇá ÄÚĩå %1°ú(ŋÍ) ĮÔ˛˛ RegSvr32 ŊĮÆĐ -ErrorRegisterTypeLib=ĮüŊÄ ļķĀĖē掝¸Ž¸Ļ ĩîˇĪĮŌ ŧö žøĀŊ: %1 +ErrorRenamingTemp=´ëģķ Æú´õ ŗģĀĮ ÆÄĀĪ Āˏ§Āģ šŲ˛Ų´Â ĩŋžČ ŋŽų šßģũ: +ErrorRegisterServer=DLL/OCX ĩîˇĪ ŊĮÆĐ: %1 +ErrorRegSvr32Failed=RegSvr32°Ą ´ŲĀŊ Ážˇá ÄÚĩåˇÎ ŊĮÆĐ: %1 +ErrorRegisterTypeLib=´ŲĀŊ ĮüĀĮ ļķĀĖē掝¸Ž ĩîˇĪŋĄ ŊĮÆĐ: %1 + +; *** Uninstall display name markings +; used for example as 'My Program (32-bit)' +UninstallDisplayNameMark=%1 (%2) +; used for example as 'My Program (32-bit, All users)' +UninstallDisplayNameMarks=%1 (%2, %3) +UninstallDisplayNameMark32Bit=32ēņÆŽ +UninstallDisplayNameMark64Bit=64ēņÆŽ +UninstallDisplayNameMarkAllUsers=¸đĩį ģįŋëĀÚ +UninstallDisplayNameMarkCurrentUser=ĮöĀį ģįŋëĀÚ + ; *** Post-installation errors -ErrorOpeningReadme=README ÆÄĀĪĀģ ŋŠ´Â ÁßŋĄ ŋŽų°Ą šßģũĮßŊĀ´Ī´Ų. -ErrorRestartingComputer=ŧŗÄĄ ĮÁˇÎą×ˇĨŋĄŧ­ ÄÄĮģÅÍ¸Ļ ´ŲŊà ŊÃĀÛĮŌ ŧö žøŊĀ´Ī´Ų. ŧöĩŋˇÎ ÁøĮāĮĪŧŧŋä. +ErrorOpeningReadme=README ÆÄĀĪĀģ ŋŠ´Â Áß ŋŽų°Ą šßģũĮßŊĀ´Ī´Ų. +ErrorRestartingComputer=ÄÄĮģÅÍ¸Ļ ´ŲŊà ŊÃĀÛĮŌ ŧö žøŊĀ´Ī´Ų, ŧöĩŋˇÎ ´ŲŊà ŊÃĀÛĮĪŊĘŊÃŋĀ. + ; *** Uninstaller messages -UninstallNotFound=ÆÄĀĪ "%1"ĀĖ(°Ą) žøŊĀ´Ī´Ų. ÁϰÅĮŌ ŧö žøŊĀ´Ī´Ų. -UninstallOpenError=ÆÄĀĪ "%1"Āģ(¸Ļ) ŋ­ ŧö žøŊĀ´Ī´Ų. ÁϰÅĮŌ ŧö žøŊĀ´Ī´Ų. -UninstallUnsupportedVer=ÁϰŠˇÎą× ÆÄĀĪ "%1"ĀĖ(°Ą) ĀĖ šöĀüĀĮ ÁϰŠĮÁˇÎą×ˇĨŋĄŧ­ ĀÎŊÄĮĪÁö ¸øĮĪ´Â ĮüŊÄĀÔ´Ī´Ų. ÁϰÅĮŌ ŧö žøŊĀ´Ī´Ų. -UninstallUnknownEntry=ÁϰŠˇÎą×ŋĄŧ­ žË ŧö žø´Â Į׸ņ(%1)ĀĖ šß°ßĩĮžúŊĀ´Ī´Ų. -ConfirmUninstall=%1°ú(ŋÍ) ĮØ´į ą¸ŧē ŋäŧŌ¸Ļ ¸đĩÎ ŋĪĀüČ÷ ÁϰÅĮĪŊðÚŊĀ´Īąî? -UninstallOnlyOnWin64=ĀĖ ŧŗÄĄ´Â 64ēņÆŽ WindowsŋĄŧ­¸¸ ÁϰÅĮŌ ŧö ĀÖŊĀ´Ī´Ų. -OnlyAdminCanUninstall=ĀĖ ŧŗÄĄ´Â °ü¸ŽĀÚ ąĮĮŅĀĖ ĀÖ´Â ģįŋëĀÚ¸¸ ÁϰÅĮŌ ŧö ĀÖŊĀ´Ī´Ų. -UninstallStatusLabel=ÄÄĮģÅÍŋĄŧ­ %1Āģ(¸Ļ) ÁϰÅĮĪ´Â ĩŋžČ ąâ´ŲˇÁ ÁÖŧŧŋä. -UninstalledAll=ÄÄĮģÅÍŋĄŧ­ %1Āģ(¸Ļ) ÁϰÅĮßŊĀ´Ī´Ų. -UninstalledMost=%1 ÁϰŰĄ ŋΎáĩĮžúŊĀ´Ī´Ų.%n%nĀĪēÎ ŋäŧŌ´Â ÁϰÅĮŌ ŧö žøŊĀ´Ī´Ų. Āˎ¯ĮŅ Į׸ņĀē ŧöĩŋˇÎ ÁϰÅĮŌ ŧö ĀÖŊĀ´Ī´Ų. -UninstalledAndNeedsRestart=%1 ÁĻ°Å¸Ļ ŋΎáĮΎÁ¸é ÄÄĮģÅÍ¸Ļ ´ŲŊà ŊÃĀÛĮØžß ĮÕ´Ī´Ų.%n%nÁöąŨ ´ŲŊà ŊÃĀÛĮĪŊðÚŊĀ´Īąî? -UninstallDataCorrupted="%1" ÆÄĀĪĀĖ ŧÕģķĩĮžúŊĀ´Ī´Ų. ÁϰÅĮŌ ŧö žøŊĀ´Ī´Ų. +UninstallNotFound=ÆÄĀĪ %1ĀĖ(°Ą) Á¸ĀįĮĪÁö žĘąâ ļ§šŽŋĄ, ÁĻ°Å¸Ļ ŊĮĮāĮŌ ŧö žøŊĀ´Ī´Ų. +UninstallOpenError=ÆÄĀĪ %1Āģ(¸Ļ) ŋ­ ŧö žøąâ ļ§šŽŋĄ, ÁĻ°Å¸Ļ ŊĮĮāĮŌ ŧö žøŊĀ´Ī´Ų. +UninstallUnsupportedVer=ģčÁĻ ˇÎą× ÆÄĀĪ "%1"Āē(´Â) ĀĖ ģčÁĻ ¸ļšũģįˇÎ ĀÎŊÄĮŌ ŧö žø´Â ĮüŊÄĀĖąâ ļ§šŽŋĄ, ÁĻ°Å¸Ļ ŊĮĮāĮŌ ŧö žøŊĀ´Ī´Ų. +UninstallUnknownEntry=žË ŧö žø´Â Į׸ņ %1ĀĖ(°Ą) ģčÁĻ ˇÎą×ŋĄ Æ÷ĮÔĩĮžî ĀÖŊĀ´Ī´Ų. +ConfirmUninstall=Á¤¸ģ %1ŋÍ(°ú) ą× ą¸ŧē ŋäŧŌ¸Ļ ¸đĩÎ ÁϰÅĮĪŊðÚŊĀ´Īąî? +UninstallOnlyOnWin64=ĀĖ ĮÁˇÎą×ˇĨĀē 64ēņÆŽ WindowsŋĄŧ­¸¸ ÁϰÅĮŌ ŧö ĀÖŊĀ´Ī´Ų. +OnlyAdminCanUninstall=ĀĖ ĮÁˇÎą×ˇĨĀģ ÁϰÅĮΎÁ¸é °ü¸ŽĀÚ ąĮĮŅĀĖ ĮĘŋäĮÕ´Ī´Ų. +UninstallStatusLabel=ąÍĮĪĀĮ ÄÄĮģÅÍŋĄŧ­ %1Āģ(¸Ļ) ÁϰÅĮĪ´Â Áß... ĀáŊà ąâ´ŲˇÁ ÁÖŊĘŊÃŋĀ. +UninstalledAll=%1ĀĖ(°Ą) ŧē°øĀûˇÎ ÁϰÅĩĮžúŊĀ´Ī´Ų! +UninstalledMost=%1 ÁϰŰĄ ŋΎáĩĮžúŊĀ´Ī´Ų.%n%nĀĪēÎ ŋäŧŌ´Â ģčÁĻĮŌ ŧö žøĀ¸´Ī, ŧöĩŋˇÎ ÁϰÅĮĪŊÃąâ šŲļø´Ī´Ų. +UninstalledAndNeedsRestart=%1ĀĮ ÁĻ°Å¸Ļ ŋΎáĮΎÁ¸é, ÄÄĮģÅÍ¸Ļ ´ŲŊà ŊÃĀÛĮØžß ĮÕ´Ī´Ų.%n%nÁöąŨ ´ŲŊà ŊÃĀÛĮĪŊðÚŊĀ´Īąî? +UninstallDataCorrupted=ÆÄĀĪ "%1"ĀĖ(°Ą) ŧÕģķĩĮžúąâ ļ§šŽŋĄ, ÁĻ°Å¸Ļ ŊĮĮāĮŌ ŧö žøŊĀ´Ī´Ų. + ; *** Uninstallation phase messages ConfirmDeleteSharedFileTitle=°øĀ¯ ÆÄĀĪĀģ ÁϰÅĮĪŊðÚŊĀ´Īąî? -ConfirmDeleteSharedFile2=ŊÃŊēÅÛŋĄŧ­´Â ĀĖÁĻ ´ŲĀŊ °øĀ¯ ÆÄĀĪĀģ ģįŋëĮĪ´Â ĮÁˇÎą×ˇĨĀĖ žø´Â °ÍˇÎ ĮĨŊÃĩË´Ī´Ų. ÁϰŠĀÛž÷Āģ ÅëĮØ ĀĖ °øĀ¯ ÆÄĀĪĀģ ÁϰÅĮĪŊðÚŊĀ´Īąî?%n%nžÆÁ÷ ĀĖ ÆÄĀĪĀģ ģįŋëĮĪ´Â ĮÁˇÎą×ˇĨĀĖ ĀÖ´ÂĩĨ ĀĖ ÆÄĀĪĀģ ÁϰÅĮΏé ĮØ´į ĮÁˇÎą×ˇĨĀĖ ŋÃšŲ¸Ŗ°Ô ĀÛĩŋĮĪÁö žĘĀģ ŧö ĀÖŊĀ´Ī´Ų. Āß ¸đ¸Ŗ´Â °æŋė [žÆ´Īŋä]¸Ļ ŧąÅÃĮĪŧŧŋä. ŊÃŊēÅÛŋĄ ÆÄĀĪĀģ ą×´ëˇÎ ĩΞîĩĩ žÆšĢˇą šŽÁϰĄ šßģũĮĪÁö žĘŊĀ´Ī´Ų. +ConfirmDeleteSharedFile2=ŊÃŊēÅÛĀĮ žîļ˛ ĮÁˇÎą×ˇĨĩĩ ´ŲĀŊ °øĀ¯ ÆÄĀĪĀģ ģįŋëĮĪÁö žĘŊĀ´Ī´Ų, ĀĖ °øĀ¯ ÆÄĀĪĀģ ģčÁĻĮĪŊðÚŊĀ´Īąî?%n%nĀĖ ÆÄĀĪĀģ ´Ų¸Ĩ ĮÁˇÎą×ˇĨĀĖ °øĀ¯Įΰí ĀÖ´Â ģķÅÂŋĄŧ­ ĀĖ ÆÄĀĪĀģ ÁϰÅĮŌ °æŋė, ĮØ´į ĮÁˇÎą×ˇĨĀĖ ÁĻ´ëˇÎ ĀÛĩŋĮĪÁö žĘĀģ ŧö Ā֏´Ī, ČŽŊÅĀĖ žøĀ¸¸é "žÆ´ĪŋĀ"¸Ļ ŧąÅÃĮĪŧÅĩĩ ĩË´Ī´Ų. ŊÃŊēÅÛŋĄ ÆÄĀĪĀĖ ŗ˛žÆ ĀÖžîĩĩ šŽÁϰĄ ĩĮÁø žĘŊĀ´Ī´Ų. SharedFileNameLabel=ÆÄĀĪ Āˏ§: SharedFileLocationLabel=Ā§ÄĄ: WizardUninstalling=ÁϰŠģķÅ StatusUninstalling=%1Āģ(¸Ļ) ÁϰÅĮĪ´Â Áß... + ; *** Shutdown block reasons ShutdownBlockReasonInstallingApp=%1Āģ(¸Ļ) ŧŗÄĄĮĪ´Â ÁßĀÔ´Ī´Ų. ShutdownBlockReasonUninstallingApp=%1Āģ(¸Ļ) ÁϰÅĮĪ´Â ÁßĀÔ´Ī´Ų. + ; The custom messages below aren't used by Setup itself, but if you make ; use of them in your scripts, you'll want to translate them. + [CustomMessages] + NameAndVersion=%1 šöĀü %2 -AdditionalIcons=Ãß°Ą šŲˇÎ °Ąąâ: -CreateDesktopIcon=šŲÅÁ Č­¸é šŲˇÎ °Ąąâ ¸¸ĩéąâ(&D) -CreateQuickLaunchIcon=ēü¸Ĩ ŊĮĮā šŲˇÎ °Ąąâ ¸¸ĩéąâ(&Q) -ProgramOnTheWeb=%1 ĀĨ Á¤ē¸ +AdditionalIcons=žÆĀĖÄÜ Ãß°Ą: +CreateDesktopIcon=šŲÅÁ Č­¸éŋĄ šŲˇÎ°Ąąâ ¸¸ĩéąâ(&D) +CreateQuickLaunchIcon=ēü¸Ĩ ŊĮĮā žÆĀĖÄÜ ¸¸ĩéąâ(&Q) +ProgramOnTheWeb=%1 ĀĨÆäĀĖÁö UninstallProgram=%1 ÁϰŠ-LaunchProgram=%1 ŊÃĀÛ -AssocFileExtension=%1Āģ(¸Ļ) %2 ÆÄĀĪ ČŽĀå¸í°ú ŋŦ°á(&A) -AssocingFileExtension=%1Āģ(¸Ļ) %2 ÆÄĀĪ ČŽĀå¸í°ú ŋŦ°á Áß... +LaunchProgram=%1 ŊĮĮā +AssocFileExtension=ÆÄĀĪ ČŽĀåĀÚ %2Āģ(¸Ļ) %1ŋĄ ŋŦ°áĮÕ´Ī´Ų. +AssocingFileExtension=ÆÄĀĪ ČŽĀåĀÚ %2Āģ(¸Ļ) %1ŋĄ ŋŦ°áĮĪ´Â Áß... AutoStartProgramGroupDescription=ŊÃĀÛ: -AutoStartProgram=%1 ĀÚĩŋ ŊÃĀÛ -AddonHostProgramNotFound=ŧąÅÃĮŅ Æú´õŋĄŧ­ %1Āģ(¸Ļ) ÃŖĀģ ŧö žøŊĀ´Ī´Ų.%n%ną×ˇĄĩĩ °čŧĶĮĪŊðÚŊĀ´Īąî? \ No newline at end of file +AutoStartProgram=%1Āģ(¸Ļ) ĀÚĩŋˇÎ ŊÃĀÛ +AddonHostProgramNotFound=%1Āē(´Â) ŧąÅÃĮŅ Æú´õŋĄ Ā§ÄĄĮŌ ŧö žøŊĀ´Ī´Ų.%n%ną×ˇĄĩĩ °čŧĶĮĪŊðÚŊĀ´Īąî? diff --git a/build/win32/i18n/Default.zh-cn.isl b/build/win32/i18n/Default.zh-cn.isl index e384e83d30d..5c5df9a166f 100644 --- a/build/win32/i18n/Default.zh-cn.isl +++ b/build/win32/i18n/Default.zh-cn.isl @@ -1,16 +1,17 @@ -; *** Inno Setup version 5.5.3+ Simplified Chinese messages *** +īģŋ; *** Inno Setup version 6.0.3+ Chinese Simplified messages *** ; -; To download user-contributed translations of this file, go to: -; http://www.jrsoftware.org/files/istrans/ +; Maintained by Zhenghan Yang +; Email: 847320916@QQ.com +; Translation based on network resource +; The latest Translation is on https://github.com/kira-96/Inno-Setup-Chinese-Simplified-Translation ; -; Note: When translating this text, do not add periods (.) to the end of -; messages that didn't have them already, because on those messages Inno -; Setup adds the periods automatically (appending a period would result in -; two periods being displayed). + [LangOptions] ; The following three entries are very important. Be sure to read and ; understand the '[LangOptions] section' topic in the help file. -LanguageName=Simplified Chinese +LanguageName=įŽ€äŊ“中文 +; If Language Name display incorrect, uncomment next line +; LanguageName=<7B80><4F53><4E2D><6587> LanguageID=$0804 LanguageCodePage=936 ; If the language you are translating to requires special font faces or @@ -23,276 +24,342 @@ LanguageCodePage=936 ;TitleFontSize=29 ;CopyrightFontName=Arial ;CopyrightFontSize=8 + [Messages] -; *** Application titles -SetupAppTitle=°˛×°ŗĖĐō -SetupWindowTitle=°˛×°ŗĖĐō - %1 -UninstallAppTitle=ĐļÔØ -UninstallAppFullTitle=%1 ĐļÔØ + +; *** åē”ᔍፋåēæ ‡éĸ˜ +SetupAppTitle=åŽ‰čŖ… +SetupWindowTitle=åŽ‰čŖ… - %1 +UninstallAppTitle=卸čŊŊ +UninstallAppFullTitle=%1 卸čŊŊ + ; *** Misc. common -InformationTitle=ĐÅĪĸ -ConfirmTitle=ȡČĪ -ErrorTitle=´íÎķ +InformationTitle=äŋĄæ¯ +ConfirmTitle=įĄŽčŽ¤ +ErrorTitle=错蝝 + ; *** SetupLdr messages -SetupLdrStartupMessage=ÕâŊ̰˛×° %1ĄŖĘĮˇņŌĒŧĖĐø? -LdrCannotCreateTemp=ÎŪˇ¨´´Ŋ¨ÁŲĘąÎÄŧūĄŖ°˛×°ŗĖĐōŌŅÖĐÖš -LdrCannotExecTemp=ÎŪˇ¨ÔÚÁŲĘąÄŋÂŧÖĐÖ´ĐĐÎÄŧūĄŖ°˛×°ŗĖĐōŌŅÖĐÖš -; *** Startup error messages -LastErrorMessage=%1ĄŖ%n%n´íÎķ %2: %3 -SetupFileMissing=°˛×°ÄŋÂŧȹʧÎÄŧū %1ĄŖĮë¸üÕũ¸ÃÎĘĖâģōģņČĄ¸ÃÎĘĖâĩÄĐÂ¸ąąžĄŖ -SetupFileCorrupt=°˛×°ŗĖĐōÎÄŧūŧĐŌŅËđģĩĄŖĮëģņČĄ¸ÃŗĖĐōĩÄĐÂ¸ąąžĄŖ -SetupFileCorruptOrWrongVer=°˛×°ŗĖĐōÎÄŧūŧĐŌŅËđģĩģōĶë´Ë°˛×°ŗĖĐō°æąž˛ģŧæČŨĄŖĮë¸üÕũ¸ÃÎĘĖâģōģņČĄ¸ÃŗĖĐōĩÄĐÂ¸ąąžĄŖ -InvalidParameter=ÃüÁîĐĐ %n%n%1 ÉĪ´ĢĩŨÁËŌģ¸öÎŪЧ˛ÎĘũ -SetupAlreadyRunning=°˛×°ŗĖĐōŌŅÔÚÔËĐĐĄŖ -WindowsVersionNotSupported=´ËŗĖĐō˛ģÖ§ŗÖÄãŧÆËãģúÕũÔËĐĐĩÄ Windows °æąžĄŖ -WindowsServicePackRequired=´ËŗĖĐōĐčŌĒ %1 ˇūÎņ°ü %2 ģō¸ü¸ß°æąžĄŖ -NotOnThisPlatform=´ËŗĖĐōŊ̞ģÔÚ %1 ÉĪÔËĐĐĄŖ -OnlyOnThisPlatform=´ËŗĖĐōąØĐëÔÚ %1 ÉĪÔËĐĐĄŖ -OnlyOnTheseArchitectures=´ËŗĖĐōŊöŋɰ˛×°ÔÚÎĒŌÔĪ´ĻĀíÆ÷ĖåĪĩŊáššÉčŧÆĩÄ Windows °æąžÉĪ:%n%n%1 -MissingWOW64APIs=ÄãÕũÔËĐĐĩÄ Windows °æąž˛ģ°üēŦ°˛×°ŗĖĐōÖ´ĐĐ 64 Î찞װËųĐčĩÄšĻÄÜĄŖŌǏüÕũ´ËÎĘĖâŖŦĮ밲װˇūÎņ°ü %1ĄŖ -WinVersionTooLowError=´ËŗĖĐōĐčŌĒ %1 °æąž %2 ģō¸ü¸ß°æąžĄŖ -WinVersionTooHighError=´ËŗĖĐō˛ģÄܰ˛×°ÔÚ %1 °æąž %2 ģō¸ü¸ßĩÄ°æąžÉĪĄŖ -AdminPrivilegesRequired=ÔÚ°˛×°´ËŗĖĐōĘąąØĐë×÷ÎĒšÜĀíÔąĩĮÂŧĄŖ -PowerUserPrivilegesRequired=°˛×°´ËŗĖĐōĘąąØĐëŌÔšÜĀíÔąģō Power User ×éŗÉÔąÉíˇŨĩĮÂŧĄŖ -SetupAppRunningError=°˛×°ŗĖĐōŧė˛âĩŊ %1 ĩąĮ°ÕũÔÚÔËĐĐĄŖ%n%nĮëÁĸŧ´šØąÕËüĩÄËųĶĐĘĩĀũŖŦČģēķĩĨģ÷Ą°Čˇļ¨ĄąŌÔŧĖĐøŖŦģōĩĨģ÷Ą°ČĄĪûĄąŌÔÍËŗöĄŖ -UninstallAppRunningError=ĐļÔØŧė˛âĩŊ %1 ĩąĮ°ÕũÔÚÔËĐĐĄŖ%n%nĮëÁĸŧ´šØąÕËüĩÄËųĶĐĘĩĀũŖŦČģēķĩĨģ÷Ą°Čˇļ¨ĄąŌÔŧĖĐøģōĩĨģ÷Ą°ČĄĪûĄąŌÔÍËŗöĄŖ -; *** Misc. errors -ErrorCreatingDir=°˛×°ŗĖĐōÎŪˇ¨´´Ŋ¨ÄŋÂŧĄ°%1Ąą -ErrorTooManyFilesInDir=ÎŪˇ¨ÔÚÄŋÂŧĄ°%1ĄąÖĐ´´Ŋ¨ÎÄŧūŖŦŌōÎĒËü°üēŦĖĢļāÎÄŧū -; *** Setup common messages -ExitSetupTitle=ÍËŗö°˛×°ŗĖĐō -ExitSetupMessage=°˛×°ŗĖĐōδÍęŗÉĄŖČįšûÁĸŧ´ÍËŗöŖŦŊ̞ģģᰲװ¸ÃŗĖĐōĄŖ%n%nŋÉÔÚÆäËûĘąŧäÔŲ´ÎÔËĐа˛×°ŗĖĐōŌÔÍęŗÉ°˛×°ĄŖ%n%nĘĮˇņÍËŗö°˛×°ŗĖĐō? -AboutSetupMenuItem=šØĶÚ°˛×°ŗĖĐō(&A)... -AboutSetupTitle=šØĶÚ°˛×°ŗĖĐō -AboutSetupMessage=%1 °æąž %2%n%3%n%n%1 Ö÷Ōŗ:%n%4 +SetupLdrStartupMessage=įŽ°åœ¨å°†åŽ‰čŖ… %1ã€‚æ‚¨æƒŗčρįģ§įģ­å—īŧŸ +LdrCannotCreateTemp=不čƒŊ创åģē临æ—ļ文äģļã€‚åŽ‰čŖ…ä¸­æ–­ã€‚ +LdrCannotExecTemp=不čƒŊæ‰§čĄŒä¸´æ—ļį›ŽåŊ•ä¸­įš„æ–‡äģļã€‚åŽ‰čŖ…ä¸­æ–­ã€‚ +HelpTextNote= + +; *** å¯åŠ¨é”™č¯¯æļˆæ¯ +LastErrorMessage=%1.%n%n错蝝 %2: %3 +SetupFileMissing=åŽ‰čŖ…į›ŽåŊ•ä¸­įš„æ–‡äģļ %1 ä¸ĸå¤ąã€‚č¯ˇäŋŽæ­Ŗčŋ™ä¸Ē问éĸ˜æˆ–čŽˇå–ä¸€ä¸Ēæ–°įš„į¨‹åēå‰¯æœŦ。 +SetupFileCorrupt=åŽ‰čŖ…æ–‡äģļåˇ˛æŸåã€‚č¯ˇčŽˇå–ä¸€ä¸Ēæ–°įš„į¨‹åēå‰¯æœŦ。 +SetupFileCorruptOrWrongVer=åŽ‰čŖ…æ–‡äģļåˇ˛æŸåīŧŒæˆ–是与čŋ™ä¸ĒåŽ‰čŖ…į¨‹åēįš„į‰ˆæœŦ不å…ŧåŽšã€‚č¯ˇäŋŽæ­Ŗčŋ™ä¸Ē问éĸ˜æˆ–čŽˇå–æ–°įš„į¨‹åēå‰¯æœŦ。 +InvalidParameter=æ— æ•ˆįš„å‘Ŋäģ¤čĄŒå‚æ•°: %n%n%1 +SetupAlreadyRunning=åŽ‰čŖ…į¨‹åēæ­Ŗåœ¨čŋčĄŒã€‚ +WindowsVersionNotSupported=čŋ™ä¸Ēፋåēä¸æ”¯æŒč¯Ĩį‰ˆæœŦįš„čŽĄįŽ—æœēčŋčĄŒã€‚ +WindowsServicePackRequired=čŋ™ä¸ĒፋåēčĻæą‚%1æœåŠĄåŒ…%1或更éĢ˜ã€‚ +NotOnThisPlatform=čŋ™ä¸Ēፋåēå°†ä¸čƒŊčŋčĄŒäēŽ %1。 +OnlyOnThisPlatform=čŋ™ä¸Ēፋåēåŋ…éĄģčŋčĄŒäēŽ %1。 +OnlyOnTheseArchitectures=čŋ™ä¸ĒፋåēåĒčƒŊ在ä¸ēä¸‹åˆ—å¤„į†å™¨į쓿ž„čŽžčŽĄįš„ Windows į‰ˆæœŦ中čŋ›čĄŒåމ誅:%n%n%1 +WinVersionTooLowError=čŋ™ä¸Ēፋåēéœ€čρ %1 į‰ˆæœŦ %2 或更éĢ˜ã€‚ +WinVersionTooHighError=čŋ™ä¸Ēፋåēä¸čƒŊåŽ‰čŖ…äēŽ %1 į‰ˆæœŦ %2 或更éĢ˜ã€‚ +AdminPrivilegesRequired=åœ¨åŽ‰čŖ…čŋ™ä¸Ēፋåēæ—ļ您åŋ…éĄģäģĨįŽĄį†å‘˜čēĢäģŊį™ģåŊ•。 +PowerUserPrivilegesRequired=åœ¨åŽ‰čŖ…čŋ™ä¸Ēፋåēæ—ļ您åŋ…éĄģäģĨįŽĄį†å‘˜čēĢäģŊæˆ–æœ‰æƒé™įš„į”¨æˆˇįģ„čēĢäģŊį™ģåŊ•。 +SetupAppRunningError=åŽ‰čŖ…į¨‹åēå‘įŽ° %1 åŊ“å‰æ­Ŗåœ¨čŋčĄŒã€‚%n%nč¯ˇå…ˆå…ŗé—­æ‰€æœ‰čŋčĄŒįš„įĒ—åŖīŧŒį„ļ后单å‡ģâ€œįĄŽåŽšâ€įģ§įģ­īŧŒæˆ–按“取æļˆâ€é€€å‡ē。 +UninstallAppRunningError=卸čŊŊፋåēå‘įŽ° %1 åŊ“å‰æ­Ŗåœ¨čŋčĄŒã€‚%n%nč¯ˇå…ˆå…ŗé—­æ‰€æœ‰čŋčĄŒįš„įĒ—åŖīŧŒį„ļ后单å‡ģâ€œįĄŽåŽšâ€įģ§įģ­īŧŒæˆ–按“取æļˆâ€é€€å‡ē。 + +; *** 启动闎éĸ˜ +PrivilegesRequiredOverrideTitle=é€‰æ‹ŠåŽ‰čŖ…į¨‹åēæ¨Ąåŧ +PrivilegesRequiredOverrideInstruction=é€‰æ‹ŠåŽ‰čŖ…æ¨Ąåŧ +PrivilegesRequiredOverrideText1=%1 可äģĨä¸ēæ‰€æœ‰į”¨æˆˇåŽ‰čŖ…(需čĻįŽĄį†å‘˜æƒé™)īŧŒæˆ–äģ…ä¸ēæ‚¨åŽ‰čŖ…ã€‚ +PrivilegesRequiredOverrideText2=%1 åĒčƒŊä¸ēæ‚¨åŽ‰čŖ…īŧŒæˆ–ä¸ēæ‰€æœ‰į”¨æˆˇåŽ‰čŖ…(需čĻįŽĄį†å‘˜æƒé™)。 +PrivilegesRequiredOverrideAllUsers=ä¸ēæ‰€æœ‰į”¨æˆˇåŽ‰čŖ…(&A) +PrivilegesRequiredOverrideAllUsersRecommended=ä¸ēæ‰€æœ‰į”¨æˆˇåŽ‰čŖ…(åģē议选项)(&A) +PrivilegesRequiredOverrideCurrentUser=åĒä¸ēæˆ‘åŽ‰čŖ…(&M) +PrivilegesRequiredOverrideCurrentUserRecommended=åĒä¸ēæˆ‘åŽ‰čŖ…(åģē议选项)(&M) + +; *** å…ļåŽƒé”™č¯¯ +ErrorCreatingDir=åŽ‰čŖ…į¨‹åēä¸čƒŊ创åģēį›ŽåŊ•“%1”。 +ErrorTooManyFilesInDir=不čƒŊåœ¨į›ŽåŊ•“%1”中创åģēæ–‡äģļīŧŒå› ä¸ē里éĸįš„æ–‡äģļå¤Ē多 + +; *** åŽ‰čŖ…į¨‹åēå…Ŧå…ąæļˆæ¯ +ExitSetupTitle=退å‡ēåŽ‰čŖ…į¨‹åē +ExitSetupMessage=åŽ‰čŖ…į¨‹åēæœĒåŽŒæˆåŽ‰čŖ…ã€‚åĻ‚æžœæ‚¨įŽ°åœ¨é€€å‡ēīŧŒæ‚¨įš„ፋåēå°†ä¸čƒŊåŽ‰čŖ…ã€‚%n%n您可äģĨäģĨ后再čŋčĄŒåމ誅ፋåēåŽŒæˆåŽ‰čŖ…ã€‚%n%n退å‡ēåŽ‰čŖ…į¨‹åēå—īŧŸ +AboutSetupMenuItem=å…ŗäēŽåމ誅ፋåē(&A)... +AboutSetupTitle=å…ŗäēŽåމ誅ፋåē +AboutSetupMessage=%1 į‰ˆæœŦ %2%n%3%n%n%1 ä¸ģéĄĩ:%n%4 AboutSetupNote= TranslatorNote= -; *** Buttons -ButtonBack=< ÉĪŌģ˛Ŋ(&B) -ButtonNext=ĪÂŌģ˛Ŋ(&N) > -ButtonInstall=°˛×°(&I) -ButtonOK=Čˇļ¨ -ButtonCancel=ČĄĪû -ButtonYes=ĘĮ(&Y) -ButtonYesToAll=ŊĶĘÜČ̞ŋ(&A) -ButtonNo=ˇņ(&N) -ButtonNoToAll=ˇņļ¨Č̞ŋ(&O) -ButtonFinish=ÍęŗÉ(&F) -ButtonBrowse=ä¯ĀĀ(&B)... -ButtonWizardBrowse=ä¯ĀĀ(&R)... -ButtonNewFolder=ĐÂŊ¨ÎÄŧūŧĐ(&M) -; *** "Select Language" dialog messages -SelectLanguageTitle=ŅĄÔņ°˛×°ŗĖĐōĶīŅÔ -SelectLanguageLabel=ŅĄÔņ°˛×°ĘąŌĒĘšĶÃĩÄĶīŅÔ: -; *** Common wizard text -ClickNext=ĩĨģ÷Ą°ĪÂŌģ˛ŊĄąŌÔŧĖĐøŖŦģōĩĨģ÷Ą°ČĄĪûĄąŌÔÍËŗö°˛×°ŗĖĐōĄŖ + +; *** 按钎 +ButtonBack=< 上一æ­Ĩ(&B) +ButtonNext=下一æ­Ĩ(&N) > +ButtonInstall=åŽ‰čŖ…(&I) +ButtonOK=įĄŽåŽš +ButtonCancel=取æļˆ +ButtonYes=是(&Y) +ButtonYesToAll=全是(&A) +ButtonNo=åĻ(&N) +ButtonNoToAll=全åĻ(&O) +ButtonFinish=厌成(&F) +ButtonBrowse=æĩč§ˆ(&B)... +ButtonWizardBrowse=æĩč§ˆ(&R)... +ButtonNewFolder=新åģēæ–‡äģļ多(&M) + +; *** â€œé€‰æ‹Šč¯­č¨€â€å¯šč¯æĄ†æļˆæ¯ +SelectLanguageTitle=é€‰æ‹ŠåŽ‰čŖ…č¯­č¨€ +SelectLanguageLabel=é€‰æ‹ŠåŽ‰čŖ…æ—ļčρäŊŋį”¨įš„č¯­č¨€ã€‚ + +; *** å…Ŧå…ąå‘å¯ŧ文字 +ClickNext=单å‡ģ“下一æ­Ĩ”įģ§įģ­īŧŒæˆ–单å‡ģ“取æļˆâ€é€€å‡ēåŽ‰čŖ…į¨‹åēã€‚ BeveledLabel= -BrowseDialogTitle=ä¯ĀžéÕŌÎÄŧūŧĐ -BrowseDialogLabel=ÔÚŌÔĪÂÁĐąíÖĐŅĄÔņŌģ¸öÎÄŧūŧĐŖŦČģēķĩĨģ÷Ą°Čˇļ¨ĄąĄŖ -NewFolderName=ĐÂŊ¨ÎÄŧūŧĐ -; *** "Welcome" wizard page -WelcomeLabel1=ģļĶ­ĘšĶà [name] °˛×°Īōĩŧ -WelcomeLabel2=ÕâŊĢÔÚŧÆËãģúÉΰ˛×° [name/ver]ĄŖ%n%nŊ¨ŌéšØąÕËųĶĐÆäËûĶĻĶÃŗĖĐōÔŲŧĖĐøĄŖ -; *** "Password" wizard page -WizardPassword=ÃÜÂë -PasswordLabel1=´Ë°˛×°ĘÜÃÜÂëąŖģ¤ĄŖ -PasswordLabel3=ĮëĖᚊÃÜÂëŖŦČģēķĩĨģ÷Ą°ĪÂŌģ˛ŊĄąŌÔŧĖĐøĄŖÃÜÂëĮøˇÖ´ķĐĄĐ´ĄŖ -PasswordEditLabel=ÃÜÂë(&P): -IncorrectPassword=ĘäČëĩÄÃÜÂë˛ģÕũȡĄŖĮëÖØĘÔĄŖ -; *** "License Agreement" wizard page -WizardLicense=ĐíŋÉĐ­Ōé -LicenseLabel=ĮëÔÚŧĖĐø˛Ų×÷Į°ÔÄļÁŌÔĪÂÖØŌĒĐÅĪĸĄŖ -LicenseLabel3=ĮëÔÄļÁŌÔĪÂĐíŋÉĐ­ŌéĄŖąØĐëŊĶĘÜ´ËĐ­ŌéĖõŋî˛ÅŋÉŧĖĐø°˛×°ĄŖ -LicenseAccepted=ÎŌŊĶĘÜĐ­Ōé(&A) -LicenseNotAccepted=ÎŌ˛ģŊĶĘÜĐ­Ōé(&D) -; *** "Information" wizard pages -WizardInfoBefore=ĐÅĪĸ -InfoBeforeLabel=ĮëÔÚŧĖĐø˛Ų×÷Į°ÔÄļÁŌÔĪÂÖØŌĒĐÅĪĸĄŖ -InfoBeforeClickLabel=×ŧą¸ēÃŧĖĐø°˛×°ēķŖŦĩĨģ÷Ą°ĪÂŌģ˛ŊĄąĄŖ -WizardInfoAfter=ĐÅĪĸ -InfoAfterLabel=ĮëÔÚŧĖĐø˛Ų×÷Į°ÔÄļÁŌÔĪÂÖØŌĒĐÅĪĸĄŖ -InfoAfterClickLabel=×ŧą¸ēÃŧĖĐø°˛×°ēķŖŦĩĨģ÷Ą°ĪÂŌģ˛ŊĄąĄŖ -; *** "User Information" wizard page -WizardUserInfo=ĶÃģ§ĐÅĪĸ -UserInfoDesc=ĮëĘäČëÄãĩÄĐÅĪĸĄŖ -UserInfoName=ĶÃģ§Ãû(&U): -UserInfoOrg=×éÖ¯(&O): -UserInfoSerial=ĐōÁĐēÅ(&S): -UserInfoNameRequired=ąØĐëĘäČëÃûŗÆĄŖ -; *** "Select Destination Location" wizard page -WizardSelectDir=ŅĄÔņÄŋąęÎģÖà -SelectDirDesc=ĶĻŊĢ [name] °˛×°ĩŊÄÄĀī? -SelectDirLabel3=°˛×°ŗĖĐōģáŊĢ [name] °˛×°ĩŊŌÔĪÂÎÄŧūŧĐĄŖ -SelectDirBrowseLabel=ČôŌĒŧĖĐøŖŦĩĨģ÷Ą°ĪÂŌģ˛ŊĄąĄŖČįšûĪëŅĄÔņÆäËûÎÄŧūŧĐŖŦĩĨģ÷Ą°ä¯ĀĀĄąĄŖ -DiskSpaceMBLabel=ĐčŌĒÖÁÉŲ [mb] MB ŋÉĶôÅÅĖŋÕŧäĄŖ -CannotInstallToNetworkDrive=°˛×°ŗĖĐōÎŪˇ¨°˛×°ĩŊÍøÂįĮũ÷ĄŖ -CannotInstallToUNCPath=°˛×°ŗĖĐōÎŪˇ¨°˛×°ĩŊ UNC ¡žļĄŖ -InvalidPath=ąØĐëĘäČë´øĮũ÷ēÅĩÄÍęÕû¡žļ(ĀũČį:%n%nC:\APP%n%n)ģōŌÔΏņĘŊĩÄ UNC ¡žļ:%n%n\\server\share -InvalidDrive=ËųŅĄĮũ÷ģō UNC š˛Īí˛ģ´æÔÚģō˛ģŋɡÃÎĘĄŖĮëÁíÍâŅĄÔņĄŖ -DiskSpaceWarningTitle=´ÅÅĖŋÕŧä˛ģ×ã -DiskSpaceWarning=°˛×°ŗĖĐōĐčŌĒÖÁÉŲ %1 KB ŋÉĶÃŋÕŧäĀ´°˛×°ŖŦĩĢËųŅĄĮũ÷ŊöĶĐ %2 KB ŋÉĶÃŋÕŧäĄŖ%n%nĘĮˇņČÔŌĒŧĖĐø? -DirNameTooLong=ÎÄŧūŧĐÃûŗÆģō¡žļĖĢŗ¤ĄŖ -InvalidDirName=ÎÄŧūŧĐÃûŗÆÎŪЧĄŖ -BadDirName32=ÎÄŧūŧĐÃû˛ģÄܰüēŦŌÔĪÂČÎŌģ×Öˇû:%n%n%1 -DirExistsTitle=ÎÄŧūŧĐ´æÔÚ -DirExists=ÎÄŧūŧĐ:%n%n%1%n%nŌŅ´æÔÚĄŖĘĮˇņČÔŌǰ˛×°ĩŊ¸ÃÎÄŧūŧĐ? -DirDoesntExistTitle=ÎÄŧūŧĐ˛ģ´æÔÚ -DirDoesntExist=ÎÄŧūŧĐ:%n%n%1%n%n˛ģ´æÔÚĄŖĘĮˇņŌĒ´´Ŋ¨¸ÃÎÄŧūŧĐ? -; *** "Select Components" wizard page -WizardSelectComponents=ŅĄÔņ×éŧū -SelectComponentsDesc=Ķϰ˛×°ÄÄĐŠ×éŧū? -SelectComponentsLabel2=ŅĄÔņĪŖÍû°˛×°ĩÄ×éŧūŖģĮåŗũ˛ģĪŖÍû°˛×°ĩÄ×éŧūĄŖ×ŧą¸žÍĐ÷ēķĩĨģ÷Ą°ĪÂŌģ˛ŊĄąŌÔŧĖĐøĄŖ -FullInstallation=ÍęČ̰˛×° +BrowseDialogTitle=æĩč§ˆæ–‡äģļ多 +BrowseDialogLabel=åœ¨ä¸‹åˆ—åˆ—čĄ¨ä¸­é€‰æ‹Šä¸€ä¸Ē文äģļ多īŧŒį„ļ后单å‡ģâ€œįĄŽåŽšâ€ã€‚ +NewFolderName=新åģēæ–‡äģļ多 + +; *** “æŦĸčŋŽâ€å‘å¯ŧéĄĩ +WelcomeLabel1=æŦĸčŋŽäŊŋᔍ [name] åŽ‰čŖ…å‘å¯ŧ +WelcomeLabel2=įŽ°åœ¨å°†åŽ‰čŖ… [name/ver] åˆ°æ‚¨įš„į”ĩč„‘ä¸­ã€‚%n%næŽ¨čæ‚¨åœ¨įģ§įģ­åŽ‰čŖ…å‰å…ŗé—­æ‰€æœ‰å…ļ厃åē”ᔍፋåēã€‚ + +; *** â€œå¯†į â€å‘å¯ŧéĄĩ +WizardPassword=坆᠁ +PasswordLabel1=čŋ™ä¸ĒåŽ‰čŖ…į¨‹åēæœ‰å¯†į äŋæŠ¤ã€‚ +PasswordLabel3=č¯ˇčž“å…Ĩ坆᠁īŧŒį„ļ后单å‡ģ“下一æ­Ĩ”įģ§įģ­ã€‚坆᠁åŒē分大小写。 +PasswordEditLabel=坆᠁(&P): +IncorrectPassword=æ‚¨čž“å…Ĩįš„å¯†į ä¸æ­ŖįĄŽīŧŒč¯ˇé‡č¯•。 + +; *** â€œčŽ¸å¯åčŽŽâ€å‘å¯ŧéĄĩ +WizardLicense=čŽ¸å¯åčŽŽ +LicenseLabel=įģ§įģ­åŽ‰čŖ…å‰č¯ˇé˜…č¯ģ下列重čρäŋĄæ¯ã€‚ +LicenseLabel3=蝎äģ”įģ†é˜…č¯ģä¸‹åˆ—čŽ¸å¯åčŽŽã€‚æ‚¨åœ¨įģ§įģ­åŽ‰čŖ…å‰åŋ…éĄģ同意čŋ™äē›åčŽŽæĄæŦžã€‚ +LicenseAccepted=æˆ‘åŒæ„æ­¤åčŽŽ(&A) +LicenseNotAccepted=æˆ‘ä¸åŒæ„æ­¤åčŽŽ(&D) + +; *** “äŋĄæ¯â€å‘å¯ŧéĄĩ +WizardInfoBefore=äŋĄæ¯ +InfoBeforeLabel=蝎圍įģ§įģ­åŽ‰čŖ…å‰é˜…č¯ģ下列重čρäŋĄæ¯ã€‚ +InfoBeforeClickLabel=åĻ‚æžœæ‚¨æƒŗįģ§įģ­åމ誅īŧŒå•å‡ģ“下一æ­Ĩ”。 +WizardInfoAfter=äŋĄæ¯ +InfoAfterLabel=蝎圍įģ§įģ­åŽ‰čŖ…å‰é˜…č¯ģ下列重čρäŋĄæ¯ã€‚ +InfoAfterClickLabel=åĻ‚æžœæ‚¨æƒŗįģ§įģ­åމ誅īŧŒå•å‡ģ“下一æ­Ĩ”。 + +; *** â€œį”¨æˆˇäŋĄæ¯â€å‘å¯ŧéĄĩ +WizardUserInfo=į”¨æˆˇäŋĄæ¯ +UserInfoDesc=č¯ˇčž“å…Ĩæ‚¨įš„äŋĄæ¯ã€‚ +UserInfoName=į”¨æˆˇå(&U): +UserInfoOrg=įģ„įģ‡(&O): +UserInfoSerial=åēåˆ—åˇ(&S): +UserInfoNameRequired=您åŋ…éĄģ输å…Ĩ名字。 + +; *** â€œé€‰æ‹Šį›Žæ ‡į›ŽåŊ•”向å¯ŧéĸ +WizardSelectDir=é€‰æ‹Šį›Žæ ‡äŊįŊŽ +SelectDirDesc=æ‚¨æƒŗå°† [name] åŽ‰čŖ…åœ¨äģ€äšˆåœ°æ–šīŧŸ +SelectDirLabel3=åŽ‰čŖ…į¨‹åēå°†åމ誅 [name] 到下列文äģļ多中。 +SelectDirBrowseLabel=单å‡ģ“下一æ­Ĩ”įģ§įģ­ã€‚åĻ‚æžœæ‚¨æƒŗé€‰æ‹Šå…ļ厃文äģļ多īŧŒå•å‡ģ“æĩč§ˆâ€ã€‚ +DiskSpaceGBLabel=č‡ŗå°‘éœ€čĻæœ‰ [gb] GB įš„å¯į”¨įŖį›˜įŠē间。 +DiskSpaceMBLabel=č‡ŗå°‘éœ€čĻæœ‰ [mb] MB įš„å¯į”¨įŖį›˜įŠē间。 +CannotInstallToNetworkDrive=åŽ‰čŖ…į¨‹åēæ— æŗ•åŽ‰čŖ…åˆ°ä¸€ä¸ĒįŊ‘įģœéŠąåŠ¨å™¨ã€‚ +CannotInstallToUNCPath=åŽ‰čŖ…į¨‹åēæ— æŗ•åŽ‰čŖ…åˆ°ä¸€ä¸ĒUNCčˇ¯åž„ã€‚ +InvalidPath=您åŋ…éĄģ输å…Ĩ一ä¸Ēå¸ĻéŠąåŠ¨å™¨åˇæ ‡įš„åŽŒæ•´čˇ¯åž„īŧŒäž‹åĻ‚:%n%nC:\APP%n%n或下列åŊĸåŧįš„ UNC čˇ¯åž„:%n%n\\server\share +InvalidDrive=æ‚¨é€‰åŽšįš„éŠąåŠ¨å™¨æˆ– UNC å…ąäēĢ不存在或不čƒŊčŽŋé—Žã€‚č¯ˇé€‰é€‰æ‹Šå…ļ厃äŊįŊŽã€‚ +DiskSpaceWarningTitle=æ˛Ąæœ‰čļŗå¤Ÿįš„įŖį›˜įŠē间 +DiskSpaceWarning=åŽ‰čŖ…į¨‹åēč‡ŗå°‘需čρ %1 KB įš„å¯į”¨įŠē间才čƒŊåŽ‰čŖ…īŧŒäŊ†é€‰åŽšéŠąåŠ¨å™¨åĒ有 %2 KB įš„å¯į”¨įŠē间。%n%n您一厚čρįģ§įģ­å—īŧŸ +DirNameTooLong=文äģļå¤šåæˆ–čˇ¯åž„å¤Ēé•ŋ。 +InvalidDirName=文äģļå¤šåæ˜¯æ— æ•ˆįš„ã€‚ +BadDirName32=文äģļ多名不čƒŊ包åĢ下列äģģäŊ•å­—įŦĻ:%n%n%1 +DirExistsTitle=文äģļ多存在 +DirExists=文äģļ多:%n%n%1%n%n厞įģå­˜åœ¨ã€‚您一åޚčĻåŽ‰čŖ…åˆ°čŋ™ä¸Ē文äģļ多中吗īŧŸ +DirDoesntExistTitle=文äģļ多不存在 +DirDoesntExist=文äģļ多:%n%n%1%n%nä¸å­˜åœ¨ã€‚æ‚¨æƒŗčĻåˆ›åģēæ­¤į›ŽåŊ•吗īŧŸ + +; *** “选拊įģ„äģļ”向å¯ŧéĄĩ +WizardSelectComponents=选拊įģ„äģļ +SelectComponentsDesc=æ‚¨æƒŗåŽ‰čŖ…å“Ēäē›į¨‹åēįš„įģ„äģļīŧŸ +SelectComponentsLabel2=é€‰æ‹Šæ‚¨æƒŗčĻåŽ‰čŖ…įš„įģ„äģļīŧ›æ¸…é™¤æ‚¨ä¸æƒŗåŽ‰čŖ…įš„įģ„äģļ。į„ļ后单å‡ģ“下一æ­Ĩ”įģ§įģ­ã€‚ +FullInstallation=åŽŒå…¨åŽ‰čŖ… ; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) -CompactInstallation=ŧōŊⰞװ -CustomInstallation=×Ôļ¨Ō尞װ -NoUninstallWarningTitle=×éŧū´æÔÚ -NoUninstallWarning=°˛×°ŗĖĐōŧė˛âĩŊŧÆËãģúÉĪŌҰ˛×°ŌÔĪÂ×éŧū:%n%n%1%n%nČĄĪûŅĄÔņÕâĐŠ×éŧūŊ̞ģģáĐļÔØËüÃĮĄŖ%n%nĘĮˇņČÔŌĒŧĖĐø? +CompactInstallation=įŽ€æ´åŽ‰čŖ… +CustomInstallation=č‡ĒåŽšäš‰åŽ‰čŖ… +NoUninstallWarningTitle=įģ„äģļ存在 +NoUninstallWarning=åŽ‰čŖ…į¨‹åēäžĻæĩ‹åˆ°ä¸‹åˆ—įģ„äģļåˇ˛åœ¨æ‚¨įš„į”ĩč„‘ä¸­åŽ‰čŖ…ã€‚:%n%n%1%n%n取æļˆé€‰åޚčŋ™äē›įģ„äģļ将不čƒŊ卸čŊŊ厃äģŦ。%n%n您一厚čρįģ§įģ­å—īŧŸ ComponentSize1=%1 KB ComponentSize2=%1 MB -ComponentsDiskSpaceMBLabel=ĩąĮ°ŅĄÔņĐčŌĒÖÁÉŲ [mb] MB ´ÅÅĖŋÕŧäĄŖ -; *** "Select Additional Tasks" wizard page -WizardSelectTasks=ŅĄÔņÆäËûČÎÎņ -SelectTasksDesc=ĶĻÖ´ĐĐÄÄĐŠÆäËûČÎÎņ? -SelectTasksLabel2=ŅĄÔņ°˛×° [name] ĘąĪŖÍû°˛×°ŗĖĐōĀ´Ö´ĐĐĩÄÆäËûČÎÎņŖŦČģēķĩĨģ÷Ą°ĪÂŌģ˛ŊĄąĄŖ -; *** "Select Start Menu Folder" wizard page -WizardSelectProgramGroup=ŅĄÔņŋĒĘŧ˛ËĩĨÎÄŧūŧĐ -SelectStartMenuFolderDesc=°˛×°ŗĖĐōĶĻŊĢŗĖĐōĩÄŋėŊŨˇŊĘŊˇÅÖÃĩŊÄÄĀī? -SelectStartMenuFolderLabel3=°˛×°ŗĖĐōŊĢÔÚŌÔĪÂŋĒĘŧ˛ËĩĨÎÄŧūŧĐÖĐ´´Ŋ¨¸ÃŗĖĐōĩÄŋėŊŨˇŊĘŊĄŖ -SelectStartMenuFolderBrowseLabel=ČôŌĒŧĖĐøŖŦĩĨģ÷Ą°ĪÂŌģ˛ŊĄąĄŖČįšûĪëŅĄÔņÆäËûÎÄŧūŧĐŖŦĩĨģ÷Ą°ä¯ĀĀĄąĄŖ -MustEnterGroupName=ąØĐëĘäČëÎÄŧūŧĐÃûĄŖ -GroupNameTooLong=ÎÄŧūŧĐÃûŗÆģō¡žļĖĢŗ¤ĄŖ -InvalidGroupName=ÎÄŧūŧĐÃûŗÆÎŪЧĄŖ -BadGroupName=ÎÄŧūŧĐÃû˛ģÄÜąŖģ¤ŌÔĪÂČÎŌģ×Öˇû:%n%n%1 -NoProgramGroupCheck2=˛ģ´´Ŋ¨ŋĒĘŧ˛ËĩĨÎÄŧūŧĐ(&D) -; *** "Ready to Install" wizard page -WizardReady=°˛×°×ŧą¸žÍĐ÷ -ReadyLabel1=°˛×°ŗĖĐōĪÖŌŅ×ŧą¸ēÃÔÚŧÆËãģúÉΰ˛×° [name]ĄŖ -ReadyLabel2a=ĩĨģ÷Ą°°˛×°ĄąŌÔŧĖĐø°˛×°ŖŦČįĪë˛éŋ´ģō¸ü¸ÄČÎēÎÉčÖÃÔōĩĨģ÷"ˇĩģØ"ĄŖ -ReadyLabel2b=ĩĨģ÷Ą°°˛×°ĄąŌÔŧĖĐø°˛×°ĄŖ -ReadyMemoUserInfo=ĶÃģ§ĐÅĪĸ: -ReadyMemoDir=ÄŋąęÎģÖÃ: -ReadyMemoType=°˛×°ŗĖĐōĀāĐÍ: -ReadyMemoComponents=ËųŅĄ×éŧū: -ReadyMemoGroup=ŋĒĘŧ˛ËĩĨÎÄŧūŧĐ: -ReadyMemoTasks=ÆäËûČÎÎņ: -; *** "Preparing to Install" wizard page -WizardPreparing=ÕũÔÚ×ŧą¸°˛×° -PreparingDesc=°˛×°ŗĖĐōÕũ×ŧą¸ÔÚŧÆËãģúÉΰ˛×° [name]ĄŖ -PreviousInstallNotCompleted=ÉĪŌģ¸öŗĖĐōĩİ˛×°/ÉžŗũδÍęŗÉĄŖĐčÖØÆôŧÆËãģúŌÔÍęŗÉ¸Ã°˛×°ĄŖ%n%nÖØÆôŧÆËãģúēķŖŦÖØĐÂÔËĐа˛×°ŗĖĐōŌÔÍęŗÉ [name] ĩİ˛×°ĄŖ -CannotContinue=°˛×°ŗĖĐōÎŪˇ¨ŧĖĐøĄŖĮëĩĨģ÷"ČĄĪû"ŌÔÍËŗöĄŖ -ApplicationsFound=ŌÔĪÂĶĻĶÃŗĖĐōÕũÔÚĘšĶÃĐčŌĒͨšũ°˛×°ŗĖĐōŊøĐиüĐÂĩÄÎÄŧūĄŖŊ¨ŌéÔĘĐí°˛×°ŗĖĐō×Ôļ¯šØąÕÕâĐŠĶĻĶÃŗĖĐōĄŖ -ApplicationsFound2=ŌÔĪÂĶĻĶÃŗĖĐōÕũÔÚĘšĶÃĐčŌĒͨšũ°˛×°ŗĖĐōŊøĐиüĐÂĩÄÎÄŧūĄŖŊ¨ŌéÔĘĐí°˛×°ŗĖĐō×Ôļ¯šØąÕÕâĐŠĶĻĶÃŗĖĐōĄŖÍęŗÉ°˛×°ēķŖŦ°˛×°ŗĖĐōŊĢŗĸĘÔÖØÆôĶĻĶÃŗĖĐōĄŖ -CloseApplications=×Ôļ¯šØąÕĶĻĶÃŗĖĐō(&A) -DontCloseApplications=˛ģšØąÕĶĻĶÃŗĖĐō(&D) -ErrorCloseApplications=°˛×°ŗĖĐōÎŪˇ¨×Ôļ¯šØąÕËųĶĐĶĻĶÃŗĖĐōĄŖŊ¨ŌéÔÚŧĖĐø˛Ų×÷ÖŽĮ°ĪČšØąÕËųĶĐĘšĶÃĐčͨšũ°˛×°ŗĖĐōŊøĐиüĐÂĩÄÎÄŧūĩÄĶĻĶÃŗĖĐōĄŖ -; *** "Installing" wizard page -WizardInstalling=ÕũÔÚ°˛×° -InstallingLabel=°˛×°ŗĖĐōÕũÔÚŧÆËãģúÉΰ˛×° [name]ŖŦĮëÉÔĩČĄŖ -; *** "Setup Completed" wizard page -FinishedHeadingLabel=ÍęŗÉ [name] °˛×°Īōĩŧ -FinishedLabelNoIcons=°˛×°ŗĖĐōŌŅÔÚŧÆËãģúÉĪÍęŗÉ°˛×° [name]ĄŖ -FinishedLabel=°˛×°ŗĖĐōŌŅÔÚŧÆËãģúÉĪÍęŗÉ°˛×° [name]ĄŖÍ¨šũŅĄÔņ°˛×°ĩÄŋėŊŨˇŊĘŊŋÉŌÔÆôÃĶĻĶÃŗĖĐōĄŖ -ClickFinish=ĩĨģ÷Ą°ÍęŗÉĄąŌÔÍËŗö°˛×°ŗĖĐōĄŖ -FinishedRestartLabel=ŌĒÍęŗÉ [name] ĩİ˛×°ŖŦ°˛×°ŗĖĐōąØĐëÖØÆôŧÆËãģúĄŖĘĮˇņŌĒÁĸŧ´ÖØÆô? -FinishedRestartMessage=ŌĒÍęŗÉ [name] ĩİ˛×°ŖŦ°˛×°ŗĖĐōąØĐëÖØÆôŧÆËãģúĄŖ%n%nĘĮˇņŌĒÁĸŧ´ÖØÆô? -ShowReadmeCheck=ĘĮŖŦÎŌĪŖÍû˛éŋ´ README ÎÄŧū -YesRadio=ĘĮŖŦÁĸŧ´ÖØÆôŧÆËãģú(&Y) -NoRadio=ˇņŖŦÎŌŊĢÉÔēķÖØÆôŧÆËãģú(&N) -; used for example as 'Run MyProg.exe' -RunEntryExec=ÔËĐĐ %1 -; used for example as 'View Readme.txt' -RunEntryShellExec=˛éŋ´ %1 -; *** "Setup Needs the Next Disk" stuff -ChangeDiskTitle=°˛×°ŗĖĐōĐčŌĒĪÂŌģ¸ö´ÅÅĖ -SelectDiskLabel2=Įë˛åČë´ÅÅĖ %1 ˛ĸĩãģ÷Ą°Čˇļ¨ĄąĄŖ%n%nČįšû´Ë´ÅÅĖÉĪĩÄÎÄŧūŋÉÔÚŌÔĪÂÎÄŧūŧĐÍâĩÄÆäËûÎÄŧūŧĐÖĐÕŌĩŊŖŦĮëĘäČëÕũȡ¡žļģōĩĨģ÷Ą°ä¯ĀĀĄąĄŖ -PathLabel=¡žļ(&P): -FileNotInDir2=ÔÚĄ°%2ĄąÖĐÎŪˇ¨ļ¨ÎģÎÄŧūĄ°%1ĄąĄŖĮë˛åČëÕũȡĩÄ´ÅÅĖģōŅĄÔņÆäËûÎÄŧūŧĐĄŖ -SelectDirectoryLabel=ĮëÖ¸ļ¨ĪÂŌģ¸ö´ÅÅĖĩÄÎģÖÃĄŖ -; *** Installation phase messages -SetupAborted=°˛×°ŗĖĐōδÍęŗÉĄŖ%n%nĮë¸üÕũÎĘĖâ˛ĸÖØĐÂÔËĐа˛×°ŗĖĐōĄŖ -EntryAbortRetryIgnore=ĩĨģ÷Ą°ÖØĘÔĄąŌÔÔŲ´ÎŗĸĘÔŖŦĩĨģ÷Ą°ēöÂÔĄąŌÔŧĖĐøŖŦģōĩĨģ÷Ą°ÖĐÖšĄąŌÔČĄĪû°˛×°ĄŖ -; *** Installation status messages -StatusClosingApplications=ÕũÔÚšØąÕĶĻĶÃŗĖĐō... -StatusCreateDirs=ÕũÔÚ´´Ŋ¨ÄŋÂŧ... -StatusExtractFiles=ÕũÔÚŊâŅšËõÎÄŧū... -StatusCreateIcons=ÕũÔÚ´´Ŋ¨ŋėŊŨˇŊĘŊ... -StatusCreateIniEntries=ÕũÔÚ´´Ŋ¨ INI Īî... -StatusCreateRegistryEntries=ÕũÔÚ´´Ŋ¨×ĸ˛áąíĪî... -StatusRegisterFiles=ÕũÔÚ×ĸ˛áÎÄŧū... -StatusSavingUninstall=ÕũÔÚąŖ´æĐļÔØĐÅĪĸ... -StatusRunProgram=ÕũÔÚÍęŗÉ°˛×°... -StatusRestartingApplications=ÕũÔÚÖØÆôĶĻĶÃŗĖĐō... -StatusRollback=ÕũÔÚģØÍ˸ü¸Ä... -; *** Misc. errors -ErrorInternal2=ÄÚ˛ŋ´íÎķ: %1 -ErrorFunctionFailedNoCode=%1 ʧ°Ü -ErrorFunctionFailed=%1 ʧ°ÜŖģ´úÂë %2 -ErrorFunctionFailedWithMessage=%1 ʧ°ÜŖģ´úÂë %2ĄŖ%n%3 -ErrorExecutingProgram=ÎŪˇ¨Ö´ĐĐÎÄŧū:%n%1 -; *** Registry errors -ErrorRegOpenKey=´ōŋĒ×ĸ˛áąíĪîĘąŗö´í:%n%1\%2 -ErrorRegCreateKey=´´Ŋ¨×ĸ˛áąíĪîĘąŗö´í:%n%1\%2 -ErrorRegWriteKey=Đ´Čë×ĸ˛áąíĪîĘąŗö´í:%n%1\%2 -; *** INI errors -ErrorIniEntry=ÔÚÎÄŧūĄ°%1ĄąÖĐ´´Ŋ¨ INI ĪîĘąŗö´íĄŖ -; *** File copying errors -FileAbortRetryIgnore=ĩĨģ÷Ą°ÖØĘÔĄąŌÔÔŲ´Î˛Ų×÷ŖŦĩĨģ÷Ą°ēöÂÔĄąŌÔĖøšũ´ËÎÄŧū(˛ģŊ¨Ōé´Ë˛Ų×÷)ŖŦģōĩĨģ÷Ą°ÖĐÖšĄąŌÔČĄĪû°˛×°ĄŖ -FileAbortRetryIgnore2=ĩĨģ÷Ą°ÖØĘÔĄąŌÔÔŲ´Î˛Ų×÷ŖŦĩĨģ÷Ą°ēöÂÔĄąŌÔŧĖĐø(˛ģŊ¨Ōé´Ë˛Ų×÷)ŖŦģōĩĨģ÷Ą°ÖĐÖšĄąŌÔČĄĪû°˛×°ĄŖ -SourceIsCorrupted=Ô´ÎÄŧūŌŅËđģĩ -SourceDoesntExist=Ô´ÎÄŧūĄ°%1Ąą˛ģ´æÔÚ -ExistingFileReadOnly=ĪÖĶĐÎÄŧūąģąęŧĮÎĒÖģļÁ×´ĖŦĄŖ%n%nĩĨģ÷Ą°ÖØĘÔĄąŌÔÉžŗũÖģļÁĖØĐÔ˛ĸÖØĘÔŖŦĩĨģ÷Ą°ēöÂÔĄąŌÔĖøšũ´ËÎÄŧūŖŦģōĩĨģ÷Ą°ÖĐÖšĄąŌÔČĄĪû°˛×°ĄŖ -ErrorReadingExistingDest=ŗĸĘÔļÁČĄĪÖĶĐÎÄŧūĘąŗö´í: -FileExists=¸ÃÎÄŧūŌŅ´æÔÚĄŖ%n%nĘĮˇņŌǰ˛×°ŗĖĐō¸˛¸ĮËü? -ExistingFileNewer=ĪÖĶĐÎÄŧūąČ°˛×°ŗĖĐōÕũŗĸĘÔ°˛×°ĩÄÎÄŧū¸üĐÂĄŖŊ¨ŌéąŖÁôĪÖĶĐÎÄŧūĄŖ%n%nĘĮˇņŌĒąŖÁôĪÖĶĐÎÄŧū? -ErrorChangingAttr=ŗĸĘÔ¸ü¸ÄĪÖĶĐÎÄŧūĖØĐÔŗö´í: -ErrorCreatingTemp=ŗĸĘÔÔÚÄŋąęÄŋÂŧ´´Ŋ¨ÎÄŧūĘąŗö´í: -ErrorReadingSource=ŗĸĘÔļÁČĄÔ´ÎÄŧūĘąŗö´í: -ErrorCopying=ŗĸĘÔ¸´ÖÆÎÄŧūĘąŗö´í: -ErrorReplacingExistingFile=ŗĸĘÔĖæģģĪÖĶĐÎÄŧūĘąŗö´í: -ErrorRestartReplace=RestartReplace ʧ°Ü: -ErrorRenamingTemp=ŗĸĘÔÔÚÄŋąęÄŋÂŧÖØÃüÃûÎÄŧūĘąŗö´í: -ErrorRegisterServer=ÎŪˇ¨×ĸ˛á DLL/OCX: %1 -ErrorRegSvr32Failed=RegSvr32 ʧ°ÜŖŦÍËŗö´úÂëÎĒ %1 -ErrorRegisterTypeLib=ÎŪˇ¨×ĸ˛áĀāĐÍŋâ: %1 -; *** Post-installation errors -ErrorOpeningReadme=ŗĸĘÔ´ōŋĒ README ÎÄŧūĘąŗö´íĄŖ -ErrorRestartingComputer=°˛×°ŗĖĐōÎŪˇ¨ÖØÆôŧÆËãģúĄŖĮëĘÖļ¯Ö´Đд˲Ų×÷ĄŖ -; *** Uninstaller messages -UninstallNotFound=ÎÄŧūĄ°%1Ąą˛ģ´æÔÚĄŖÎŪˇ¨°˛×°ĄŖ -UninstallOpenError=ÎŪˇ¨´ōŋĒÎÄŧūĄ°%1ĄąĄŖÎŪˇ¨ĐļÔØ -UninstallUnsupportedVer=ĐļÔØČÕÖžĄ°%1ĄąĩĸņĘŊÎŪˇ¨ąģ´Ë°æąžĩÄĐļÔØŗĖĐōĘļąđĄŖÎŪˇ¨ĐļÔØ -UninstallUnknownEntry=ĐļÔØČÕÖžÖСĸĪÖδÖĒĖõÄŋ(%1) -ConfirmUninstall=ȡļ¨ŌĒŗšĩ×Éžŗũ %1 ēÍŧ°ÆäČ̞ŋ×éŧū? -UninstallOnlyOnWin64=ŊöŋÉÔÚ 64 Îģ Windows ÉĪĐļÔØ´Ë°˛×°ĄŖ -OnlyAdminCanUninstall=ŊöžßĶĐšÜĀíȨĪŪĩÄĶÃ짞ÅŋÉĐļÔØ´Ë°˛×°ĄŖ -UninstallStatusLabel=Õũ´ĶŧÆËãģúÉžŗũ %1ŖŦĮëÉÔĩČĄŖ -UninstalledAll=ŌŅŗÉšĻ´ĶŧÆËãģúÉĪÉžŗũ %1ĄŖ -UninstalledMost=%1 ĐļÔØÍęŗÉĄŖ%n%nÎŪˇ¨ÉžŗũŌģĐŠÔĒËØĄŖŋÉŊĢÆäĘÖļ¯ÉžŗũĄŖ -UninstalledAndNeedsRestart=ŌĒÍęŗÉ %1 ĩÄĐļÔØŖŦąØĐëÖØÆôŧÆËãģúĄŖ%n%nĘĮˇņŌĒÁĸŧ´ÖØÆô? -UninstallDataCorrupted=Ą°%1ĄąÎÄŧūŌŅËđģĩĄŖÎŪˇ¨ĐļÔØ -; *** Uninstallation phase messages -ConfirmDeleteSharedFileTitle=Éžŗũš˛ĪíÎÄŧū? -ConfirmDeleteSharedFile2=ĪĩÍŗąíĘžŌÔĪš˛ĪíÎÄŧū˛ģÔŲąģČÎēÎŗĖĐōĘšĶÃĄŖĘĮˇņŌĒĐļÔØÉžŗũ´Ëš˛ĪíÎÄŧū?%n%nČįšûÔÚĶĐŗĖĐōČÔÔÚĘšĶôËÎÄŧūļøËüąģÉžŗũŖŦÔōŗĖĐōŋÉÄܲģģáÕũŗŖÔËĐĐĄŖČįšû˛ģȎŦĮëŅĄÔņĄ°ˇņĄąĄŖŊĢÎÄŧūÁôץĪĩÍŗÉΞģģáÔėŗÉČÎēÎÎĘĖâĄŖ -SharedFileNameLabel=ÎÄŧūÃû: -SharedFileLocationLabel=ÎģÖÃ: -WizardUninstalling=ĐļÔØ×´ĖŦ -StatusUninstalling=ÕũÔÚĐļÔØ %1... +ComponentsDiskSpaceGBLabel=åŊ“å‰é€‰æ‹Šįš„įģ„äģļč‡ŗå°‘éœ€čρ [gb] GB įš„įŖį›˜įŠē间。 +ComponentsDiskSpaceMBLabel=åŊ“å‰é€‰æ‹Šįš„įģ„äģļč‡ŗå°‘éœ€čρ [mb] MB įš„įŖį›˜įŠē间。 + +; *** “选拊附加äģģåŠĄâ€å‘å¯ŧéĄĩ +WizardSelectTasks=选拊附加äģģåŠĄ +SelectTasksDesc=æ‚¨æƒŗčĻåŽ‰čŖ…į¨‹åēæ‰§čĄŒå“Ēäē›é™„加äģģåŠĄīŧŸ +SelectTasksLabel2=é€‰æ‹Šæ‚¨æƒŗčĻåŽ‰čŖ…į¨‹åēåœ¨åމ誅 [name] æ—￉§čĄŒįš„附加äģģåŠĄīŧŒį„ļ后单å‡ģ“下一æ­Ĩ”。 + +; *** “选拊åŧ€å§‹čœå•æ–‡äģļ多”向å¯ŧéĄĩ +WizardSelectProgramGroup=选拊åŧ€å§‹čœå•æ–‡äģļ多 +SelectStartMenuFolderDesc=æ‚¨æƒŗåœ¨å“Ē里攞įŊŽį¨‹åēįš„åŋĢæˇæ–šåŧīŧŸ +SelectStartMenuFolderLabel3=åŽ‰čŖ…į¨‹åēįŽ°åœ¨å°†åœ¨ä¸‹åˆ—åŧ€å§‹čœå•æ–‡äģļ多中创åģēፋåēįš„åŋĢæˇæ–šåŧã€‚ +SelectStartMenuFolderBrowseLabel=单å‡ģ“下一æ­Ĩ”įģ§įģ­ã€‚åĻ‚æžœæ‚¨æƒŗé€‰æ‹Šå…ļ厃文äģļ多īŧŒå•å‡ģ“æĩč§ˆâ€ã€‚ +MustEnterGroupName=您åŋ…éĄģ输å…Ĩ一ä¸Ē文äģļ多名。 +GroupNameTooLong=文äģļå¤šåæˆ–čˇ¯åž„å¤Ēé•ŋ。 +InvalidGroupName=文äģļå¤šåæ˜¯æ— æ•ˆįš„ã€‚ +BadGroupName=文äģļ多名不čƒŊ包åĢ下列äģģäŊ•å­—įŦĻ:%n%n%1 +NoProgramGroupCheck2=不创åģēåŧ€å§‹čœå•æ–‡äģļ多(&D) + +; *** â€œå‡†å¤‡åŽ‰čŖ…â€å‘å¯ŧéĄĩ +WizardReady=å‡†å¤‡åŽ‰čŖ… +ReadyLabel1=åŽ‰čŖ…į¨‹åēįŽ°åœ¨å‡†å¤‡åŧ€å§‹åމ誅 [name] åˆ°æ‚¨įš„į”ĩč„‘ä¸­ã€‚ +ReadyLabel2a=单å‡ģâ€œåŽ‰čŖ…â€įģ§įģ­æ­¤åމ誅ፋåēã€‚åĻ‚æžœæ‚¨æƒŗčĻå›žéĄžæˆ–æ”šå˜čŽžįŊŽīŧŒč¯ˇå•å‡ģ“上一æ­Ĩ”。 +ReadyLabel2b=单å‡ģâ€œåŽ‰čŖ…â€įģ§įģ­æ­¤åމ誅ፋåē? +ReadyMemoUserInfo=į”¨æˆˇäŋĄæ¯: +ReadyMemoDir=į›Žæ ‡äŊįŊŽ: +ReadyMemoType=åŽ‰čŖ…įąģ型: +ReadyMemoComponents=选厚įģ„äģļ: +ReadyMemoGroup=åŧ€å§‹čœå•æ–‡äģļ多: +ReadyMemoTasks=附加äģģåŠĄ: + +; *** â€œæ­Ŗåœ¨å‡†å¤‡åŽ‰čŖ…â€å‘å¯ŧéĄĩ +WizardPreparing=æ­Ŗåœ¨å‡†å¤‡åŽ‰čŖ… +PreparingDesc=åŽ‰čŖ…į¨‹åēæ­Ŗåœ¨å‡†å¤‡åމ誅 [name] åˆ°æ‚¨įš„į”ĩč„‘ä¸­ã€‚ +PreviousInstallNotCompleted=å…ˆå‰į¨‹åēįš„åމ誅/卸čŊŊæœĒ厌成。您需čĻé‡æ–°å¯åŠ¨æ‚¨įš„į”ĩč„‘æ‰čƒŊåŽŒæˆåŽ‰čŖ…ã€‚%n%n在重新启动į”ĩč„‘åŽīŧŒå†čŋčĄŒåŽ‰čŖ…åŽŒæˆ [name] įš„åŽ‰čŖ…ã€‚ +CannotContinue=åŽ‰čŖ…į¨‹åēä¸čƒŊįģ§įģ­ã€‚č¯ˇå•å‡ģ“取æļˆâ€é€€å‡ē。 +ApplicationsFound=下列åē”ᔍፋåēæ­Ŗåœ¨äŊŋį”¨įš„æ–‡äģļ需čĻæ›´æ–°čŽžįŊŽã€‚厃是åģēčŽŽæ‚¨å…čŽ¸åŽ‰čŖ…į¨‹åēč‡ĒåŠ¨å…ŗé—­čŋ™äē›åē”ᔍፋåēã€‚ +ApplicationsFound2=下列åē”ᔍፋåēæ­Ŗåœ¨äŊŋį”¨įš„æ–‡äģļ需čĻæ›´æ–°čŽžįŊŽã€‚厃是åģēčŽŽæ‚¨å…čŽ¸åŽ‰čŖ…į¨‹åēč‡ĒåŠ¨å…ŗé—­čŋ™äē›åē”ᔍፋåēã€‚åŽ‰čŖ…åŽŒæˆåŽīŧŒåމ誅ፋåēå°†å°č¯•重新启动åē”ᔍፋåēã€‚ +CloseApplications=č‡ĒåŠ¨å…ŗé—­č¯Ĩåē”ᔍፋåē(&A) +DontCloseApplications=不čρ兺闭č¯Ĩåē”ᔍፋåē(D) +ErrorCloseApplications=åŽ‰čŖ…į¨‹åēæ— æŗ•č‡ĒåŠ¨å…ŗé—­æ‰€æœ‰åē”ᔍፋåēã€‚在įģ§įģ­äš‹å‰īŧŒæˆ‘äģŦåģēčŽŽæ‚¨å…ŗé—­æ‰€æœ‰äŊŋį”¨éœ€čĻæ›´æ–°įš„åŽ‰čŖ…į¨‹åēæ–‡äģļ。 +PrepareToInstallNeedsRestart=åŽ‰čŖ…į¨‹åēåŋ…éĄģé‡æ–°å¯åŠ¨čŽĄįŽ—æœēã€‚é‡æ–°å¯åŠ¨čŽĄįŽ—æœē后īŧŒč¯ˇå†æŦĄčŋčĄŒåމ誅ፋåēäģĨ厌成 [name] įš„åŽ‰čŖ…ã€‚%n%n是åĻįĢ‹åŗé‡æ–°å¯åŠ¨īŧŸ + +; *** â€œæ­Ŗåœ¨åŽ‰čŖ…â€å‘å¯ŧéĄĩ +WizardInstalling=æ­Ŗåœ¨åŽ‰čŖ… +InstallingLabel=åŽ‰čŖ…į¨‹åēæ­Ŗåœ¨åމ誅 [name] åˆ°æ‚¨įš„į”ĩ脑中īŧŒč¯ˇį¨į­‰ã€‚ + +; *** â€œåŽ‰čŖ…åŽŒæˆâ€å‘å¯ŧéĄĩ +FinishedHeadingLabel=[name] åŽ‰čŖ…åŽŒæˆ +FinishedLabelNoIcons=åŽ‰čŖ…į¨‹åēåˇ˛åœ¨æ‚¨įš„į”ĩč„‘ä¸­åŽ‰čŖ…äē† [name]。 +FinishedLabel=åŽ‰čŖ…į¨‹åēåˇ˛åœ¨æ‚¨įš„į”ĩč„‘ä¸­åŽ‰čŖ…äē† [name]。此åē”ᔍፋåēå¯äģĨ通čŋ‡é€‰æ‹ŠåŽ‰čŖ…įš„åŋĢæˇæ–šåŧčŋčĄŒã€‚ +ClickFinish=单å‡ģ“厌成”退å‡ēåŽ‰čŖ…į¨‹åēã€‚ +FinishedRestartLabel=čĻåŽŒæˆ [name] įš„åŽ‰čŖ…īŧŒåމ誅ፋåēåŋ…éĄģé‡æ–°å¯åŠ¨æ‚¨įš„į”ĩč„‘ã€‚æ‚¨æƒŗįŽ°åœ¨é‡æ–°å¯åŠ¨å—īŧŸ +FinishedRestartMessage=čĻåŽŒæˆ [name] įš„åŽ‰čŖ…īŧŒåމ誅ፋåēåŋ…éĄģé‡æ–°å¯åŠ¨æ‚¨įš„į”ĩč„‘ã€‚%n%næ‚¨æƒŗįŽ°åœ¨é‡æ–°å¯åŠ¨å—īŧŸ +ShowReadmeCheck=是īŧŒæ‚¨æƒŗæŸĨ阅č‡Ēčŋ°æ–‡äģļ +YesRadio=是īŧŒįĢ‹åŗé‡æ–°å¯åŠ¨į”ĩ脑(&Y) +NoRadio=åĻīŧŒį¨åŽé‡æ–°å¯åЍį”ĩ脑(&N) +; ᔍäēŽčąĄâ€œčŋčĄŒ MyProg.exe” +RunEntryExec=čŋčĄŒ %1 +; ᔍäēŽčąĄâ€œæŸĨ阅 Readme.txt” +RunEntryShellExec=æŸĨ阅 %1 + +; *** â€œåŽ‰čŖ…į¨‹åēéœ€čρ䏋䏀åŧ įŖį›˜â€æį¤ē +ChangeDiskTitle=åŽ‰čŖ…į¨‹åēéœ€čρ䏋䏀åŧ įŖį›˜ +SelectDiskLabel2=č¯ˇæ’å…ĨįŖį›˜ %1 åšļ单å‡ģâ€œįĄŽåŽšâ€ã€‚%n%nåĻ‚æžœčŋ™ä¸ĒįŖį›˜ä¸­įš„æ–‡äģļ不čƒŊ在不同äēŽä¸‹åˆ—昞į¤ēįš„æ–‡äģļ多中扞到īŧŒčž“å…Ĩæ­ŖįĄŽįš„čˇ¯åž„æˆ–å•å‡ģ“æĩč§ˆâ€ã€‚ +PathLabel=čˇ¯åž„(&P): +FileNotInDir2=文äģļ“%1”不čƒŊ在“%2”厚äŊã€‚č¯ˇæ’å…Ĩæ­ŖįĄŽįš„įŖį›˜æˆ–é€‰æ‹Šå…ļ厃文äģļ多。 +SelectDirectoryLabel=č¯ˇæŒ‡åŽšä¸‹ä¸€åŧ įŖį›˜įš„äŊįŊŽã€‚ + +; *** åŽ‰čŖ…įŠļ态æļˆæ¯ +SetupAborted=åŽ‰čŖ…į¨‹åēæœĒåŽŒæˆåŽ‰čŖ…ã€‚%n%n蝎äŋŽæ­Ŗčŋ™ä¸Ē问éĸ˜åšļ重新čŋčĄŒåމ誅ፋåēã€‚ +AbortRetryIgnoreSelectAction=选项 +AbortRetryIgnoreRetry=é‡č¯•(&T) +AbortRetryIgnoreIgnore=åŋŊį•Ĩ错蝝åšļįģ§įģ­(&I) +AbortRetryIgnoreCancel=å…ŗé—­åŽ‰čŖ…į¨‹åē + +; *** åŽ‰čŖ…įŠļ态æļˆæ¯ +StatusClosingApplications=æ­Ŗåœ¨å…ŗé—­åē”ᔍፋåē... +StatusCreateDirs=æ­Ŗåœ¨åˆ›åģēį›ŽåŊ•... +StatusExtractFiles=æ­Ŗåœ¨č§ŖåŽ‹įŧŠæ–‡äģļ... +StatusCreateIcons=æ­Ŗåœ¨åˆ›åģēåŋĢæˇæ–šåŧ... +StatusCreateIniEntries=æ­Ŗåœ¨åˆ›åģē INI æĄį›Ž... +StatusCreateRegistryEntries=æ­Ŗåœ¨åˆ›åģēæŗ¨å†ŒčĄ¨æĄį›Ž... +StatusRegisterFiles=æ­Ŗåœ¨æŗ¨å†Œæ–‡äģļ... +StatusSavingUninstall=æ­Ŗåœ¨äŋå­˜å¸čŊŊäŋĄæ¯... +StatusRunProgram=æ­Ŗåœ¨åŽŒæˆåŽ‰čŖ…... +StatusRestartingApplications=æ­Ŗåœ¨é‡å¯åē”ᔍፋåē... +StatusRollback=æ­Ŗåœ¨æ’¤é”€æ›´æ”š... + +; *** å…ļåŽƒé”™č¯¯ +ErrorInternal2=å†…éƒ¨é”™č¯¯: %1 +ErrorFunctionFailedNoCode=%1 å¤ąč´Ĩ +ErrorFunctionFailed=%1 å¤ąč´Ĩīŧ›é”™č¯¯äģŖį  %2 +ErrorFunctionFailedWithMessage=%1 å¤ąč´Ĩīŧ›é”™č¯¯äģŖį  %2.%n%3 +ErrorExecutingProgram=不čƒŊæ‰§čĄŒæ–‡äģļ:%n%1 + +; *** æŗ¨å†ŒčĄ¨é”™č¯¯ +ErrorRegOpenKey=打åŧ€æŗ¨å†ŒčĄ¨éĄšæ—ļå‡ē错:%n%1\%2 +ErrorRegCreateKey=创åģēæŗ¨å†ŒčĄ¨éĄšæ—ļå‡ē错:%n%1\%2 +ErrorRegWriteKey=写å…Ĩæŗ¨å†ŒčĄ¨éĄšæ—ļå‡ē错:%n%1\%2 + +; *** INI 错蝝 +ErrorIniEntry=在文äģļ“%1”创åģē INI éĄšį›Žé”™č¯¯ã€‚ + +; *** 文äģļ复åˆļ错蝝 +FileAbortRetryIgnoreSkipNotRecommended=莺čŋ‡čŋ™ä¸Ē文äģļ (ä¸æŽ¨č)(&S) +FileAbortRetryIgnoreIgnoreNotRecommended=åŋŊį•Ĩ错蝝åšļįģ§įģ­ (ä¸æŽ¨č)(&I) +SourceIsCorrupted=æēæ–‡äģļåˇ˛æŸå +SourceDoesntExist=æēæ–‡äģļ“%1”不存在 +ExistingFileReadOnly2=æ— æŗ•æ›ŋæĸįŽ°æœ‰æ–‡äģļīŧŒå› ä¸ē厃是åĒč¯ģįš„ã€‚ +ExistingFileReadOnlyRetry=į§ģ除åĒč¯ģåąžæ€§åšļé‡č¯•(&R) +ExistingFileReadOnlyKeepExisting=äŋį•™įŽ°æœ‰æ–‡äģļ(&K) +ErrorReadingExistingDest=å°č¯•č¯ģå–įŽ°æœ‰æ–‡äģļæ—ļå‘į”Ÿä¸€ä¸Ē错蝝: +FileExists=文äģļ厞įģå­˜åœ¨ã€‚%n%næ‚¨æƒŗčĻåŽ‰čŖ…į¨‹åēčĻ†į›–åŽƒå—īŧŸ +ExistingFileNewer=įŽ°æœ‰įš„æ–‡äģļæ–°ä¸Žåމ誅ፋåēčĻåŽ‰čŖ…įš„æ–‡äģļã€‚æŽ¨čæ‚¨äŋį•™įŽ°æœ‰æ–‡äģļ。%n%næ‚¨æƒŗčρäŋį•™įŽ°æœ‰įš„æ–‡äģļ吗īŧŸ +ErrorChangingAttr=å°č¯•æ”šå˜ä¸‹åˆ—įŽ°æœ‰įš„æ–‡äģļįš„åąžæ€§æ—ļå‘į”Ÿä¸€ä¸Ē错蝝: +ErrorCreatingTemp=å°č¯•åœ¨į›Žæ ‡į›ŽåŊ•创åģēæ–‡äģļæ—ļå‘į”Ÿä¸€ä¸Ē错蝝: +ErrorReadingSource=å°č¯•č¯ģ取下列æēæ–‡äģļæ—ļå‘į”Ÿä¸€ä¸Ē错蝝: +ErrorCopying=å°č¯•å¤åˆļ下列文äģļæ—ļå‘į”Ÿä¸€ä¸Ē错蝝: +ErrorReplacingExistingFile=å°č¯•æ›ŋæĸįŽ°æœ‰įš„æ–‡äģļæ—ļå‘į”Ÿé”™č¯¯: +ErrorRestartReplace=重启į”ĩč„‘åŽæ›ŋæĸ文äģļå¤ąč´Ĩ: +ErrorRenamingTemp=å°č¯•é‡æ–°å‘Ŋ名äģĨä¸‹į›Žæ ‡į›ŽåŊ•ä¸­įš„ä¸€ä¸Ē文äģļæ—ļå‘į”Ÿé”™č¯¯: +ErrorRegisterServer=不čƒŊæŗ¨å†Œ DLL/OCX: %1 +ErrorRegSvr32Failed=RegSvr32 å¤ąč´Ĩīŧ›é€€å‡ēäģŖį  %1 +ErrorRegisterTypeLib=不čƒŊæŗ¨å†Œįąģ型åē“: %1 + +; *** 卸čŊŊ昞į¤ēåå­—æ ‡čŽ° +; used for example as 'My Program (32-bit)' +UninstallDisplayNameMark=%1 (%2) +; used for example as 'My Program (32-bit, All users)' +UninstallDisplayNameMarks=%1 (%2, %3) +UninstallDisplayNameMark32Bit=32äŊ +UninstallDisplayNameMark64Bit=64äŊ +UninstallDisplayNameMarkAllUsers=æ‰€æœ‰į”¨æˆˇ +UninstallDisplayNameMarkCurrentUser=åŊ“å‰į”¨æˆˇ + +; *** åŽ‰čŖ…åŽé”™č¯¯ +ErrorOpeningReadme=åŊ“å°č¯•æ‰“åŧ€č‡Ēčŋ°æ–‡äģļæ—ļå‘į”Ÿä¸€ä¸Ēé”™č¯¯ã€‚ +ErrorRestartingComputer=åŽ‰čŖ…į¨‹åēä¸čƒŊ重新启动į”ĩ脑īŧŒč¯ˇæ‰‹åŠ¨é‡å¯ã€‚ + +; *** 卸čŊŊæļˆæ¯ +UninstallNotFound=文äģļ“%1”不存在。不čƒŊ卸čŊŊ。 +UninstallOpenError=文äģļ“%1”不čƒŊ打åŧ€ã€‚不čƒŊ卸čŊŊ。 +UninstallUnsupportedVer=卸čŊŊæ—Ĩåŋ—æ–‡äģļ“%1”有æœĒčĸĢčŋ™ä¸Ēį‰ˆæœŦįš„å¸čŊŊ器æ‰ŋčŽ¤įš„æ ŧåŧã€‚不čƒŊ卸čŊŊ +UninstallUnknownEntry=在卸čŊŊæ—Ĩåŋ—中遇到一ä¸ĒæœĒįŸĨįš„æĄį›Ž (%1) +ConfirmUninstall=æ‚¨įĄŽčŽ¤æƒŗčĻåŽŒå…¨åˆ é™¤ %1 åŠåŽƒįš„æ‰€æœ‰įģ„äģļ吗īŧŸ +UninstallOnlyOnWin64=čŋ™ä¸ĒåŽ‰čŖ…į¨‹åēåĒčƒŊ在 64 äŊ Windows 中čŋ›čĄŒå¸čŊŊ。 +OnlyAdminCanUninstall=čŋ™ä¸ĒåŽ‰čŖ…įš„į¨‹åēåĒčƒŊæ˜¯æœ‰įŽĄį†å‘˜æƒé™įš„į”¨æˆˇæ‰čƒŊ卸čŊŊ。 +UninstallStatusLabel=æ­Ŗåœ¨äģŽæ‚¨įš„į”ĩč„‘ä¸­åˆ é™¤ %1īŧŒč¯ˇį­‰åž…。 +UninstalledAll=%1 厞éĄē刊地äģŽæ‚¨įš„į”ĩč„‘ä¸­åˆ é™¤ã€‚ +UninstalledMost=%1 卸čŊŊ厌成。%n%n有一äē›å†…厚不čƒŊčĸĢ删除。您可äģĨ手åˇĨ删除厃äģŦ。 +UninstalledAndNeedsRestart=čĻåŽŒæˆ %1 įš„å¸čŊŊīŧŒæ‚¨įš„į”ĩ脑åŋ…éĄģ重新启动。%n%næ‚¨įŽ°åœ¨æƒŗé‡æ–°å¯åŠ¨į”ĩ脑吗īŧŸ +UninstallDataCorrupted=“%1”文äģļčĸĢį ´åīŧŒä¸čƒŊ卸čŊŊ + +; *** 卸čŊŊįŠļ态æļˆæ¯ +ConfirmDeleteSharedFileTitle=åˆ é™¤å…ąäēĢæ–‡äģļ吗īŧŸ +ConfirmDeleteSharedFile2=įŗģįģŸä¸­åŒ…åĢįš„ä¸‹åˆ—å…ąäēĢæ–‡äģļ厞įģä¸čĸĢå…ļåŽƒį¨‹åēäŊŋį”¨ã€‚æ‚¨æƒŗčĻå¸čŊŊፋåēåˆ é™¤čŋ™äē›å…ąäēĢæ–‡äģļ吗īŧŸ%n%nåĻ‚æžœčŋ™ä盿–‡äģļčĸĢ删除īŧŒäŊ†čŋ˜æœ‰į¨‹åēæ­Ŗåœ¨äŊŋᔍčŋ™ä盿–‡äģļīŧŒčŋ™äē›į¨‹åēå¯čƒŊ不čƒŊæ­ŖįĄŽæ‰§čĄŒã€‚åĻ‚æžœæ‚¨ä¸čƒŊįĄŽåŽšīŧŒé€‰æ‹Šâ€œåĻ”。把čŋ™ä盿–‡äģļäŋį•™åœ¨įŗģįģŸä¸­äģĨ免åŧ•čĩˇé—Žéĸ˜ã€‚ +SharedFileNameLabel=文äģļ名: +SharedFileLocationLabel=äŊįŊŽ: +WizardUninstalling=卸čŊŊįŠļ态 +StatusUninstalling=æ­Ŗåœ¨å¸čŊŊ %1... + ; *** Shutdown block reasons -ShutdownBlockReasonInstallingApp=ÕũÔÚ°˛×° %1ĄŖ -ShutdownBlockReasonUninstallingApp=ÕũÔÚĐļÔØ %1ĄŖ +ShutdownBlockReasonInstallingApp=æ­Ŗåœ¨åŽ‰čŖ… %1. +ShutdownBlockReasonUninstallingApp=æ­Ŗåœ¨å¸čŊŊ %1. + ; The custom messages below aren't used by Setup itself, but if you make ; use of them in your scripts, you'll want to translate them. + [CustomMessages] -NameAndVersion=%1 °æąž %2 -AdditionalIcons=ÆäËûŋėŊŨˇŊĘŊ: -CreateDesktopIcon=´´Ŋ¨×ĀÃæŋėŊŨˇŊĘŊ(&D) -CreateQuickLaunchIcon=´´Ŋ¨ŋėËŲÆôļ¯ŋėŊŨˇŊĘŊ(&Q) -ProgramOnTheWeb=Web ÉĪĩÄ %1 -UninstallProgram=ĐļÔØ %1 -LaunchProgram=Æôļ¯ %1 -AssocFileExtension=ŊĢ %1 Ķë %2 ÎÄŧūĀŠÕšÃûšØÁĒ(&A) -AssocingFileExtension=ÕũŊĢ %1 Ķë %2 ÎÄŧūĀŠÕšÃûšØÁĒ... -AutoStartProgramGroupDescription=Æôļ¯: -AutoStartProgram=×Ôôļ¯ %1 -AddonHostProgramNotFound=ÎŪˇ¨ÔÚËųŅĄÎÄŧūŧĐÖĐļ¨Îģ %1ĄŖ%n%nĘĮˇņČÔŌĒŧĖĐø? \ No newline at end of file + +NameAndVersion=%1 į‰ˆæœŦ %2 +AdditionalIcons=附加åŋĢæˇæ–šåŧ: +CreateDesktopIcon=创åģēæĄŒéĸåŋĢæˇæ–šåŧ(&D) +CreateQuickLaunchIcon=创åģēåŋĢ速čŋčĄŒæ åŋĢæˇæ–šåŧ(&Q) +ProgramOnTheWeb=%1 įŊ‘įĢ™ +UninstallProgram=卸čŊŊ %1 +LaunchProgram=čŋčĄŒ %1 +AssocFileExtension=将 %2 文äģ￉Šåą•名与 %1 åģēį̋兺联(&A) +AssocingFileExtension=æ­Ŗåœ¨å°† %2 文äģ￉Šåą•名与 %1 åģēį̋兺联... +AutoStartProgramGroupDescription=启动įģ„: +AutoStartProgram=č‡Ē动启动 %1 +AddonHostProgramNotFound=%1æ— æŗ•æ‰žåˆ°æ‚¨æ‰€é€‰æ‹Šįš„æ–‡äģļ多。%n%næ‚¨æƒŗčρįģ§įģ­å—īŧŸ + diff --git a/build/win32/i18n/Default.zh-tw.isl b/build/win32/i18n/Default.zh-tw.isl index 4746349e191..fb748761f99 100644 --- a/build/win32/i18n/Default.zh-tw.isl +++ b/build/win32/i18n/Default.zh-tw.isl @@ -1,298 +1,359 @@ -; *** Inno Setup version 5.5.3+ Traditional Chinese messages *** +īģŋ; *** Inno Setup version 6.0.0+ Chinese Traditional messages *** ; -; To download user-contributed translations of this file, go to: -; http://www.jrsoftware.org/files/istrans/ +; Name: John Wu, mr.johnwu@gmail.com +; Base on 5.5.3+ translations by Samuel Lee, Email: 751555749@qq.com +; Translation based on network resource ; -; Note: When translating this text, do not add periods (.) to the end of -; messages that didn't have them already, because on those messages Inno -; Setup adds the periods automatically (appending a period would result in -; two periods being displayed). + [LangOptions] ; The following three entries are very important. Be sure to read and ; understand the '[LangOptions] section' topic in the help file. -LanguageName=Traditional Chinese +; If Language Name display incorrect, uncomment next line +LanguageName=<7e41><9ad4><4e2d><6587> LanguageID=$0404 -LanguageCodePage=950 +LanguageCodepage=950 ; If the language you are translating to requires special font faces or ; sizes, uncomment any of the following entries and change them accordingly. -;DialogFontName= -;DialogFontSize=8 -;WelcomeFontName=Verdana -;WelcomeFontSize=12 -;TitleFontName=Arial -;TitleFontSize=29 -;CopyrightFontName=Arial -;CopyrightFontSize=8 +DialogFontName=æ–°į´°æ˜ŽéĢ” +DialogFontSize=9 +TitleFontName=Arial +TitleFontSize=28 +WelcomeFontName=æ–°į´°æ˜ŽéĢ” +WelcomeFontSize=12 +CopyrightFontName=æ–°į´°æ˜ŽéĢ” +CopyrightFontSize=9 + [Messages] + ; *** Application titles -SetupAppTitle=Ļw¸Ëĩ{ĻĄ -SetupWindowTitle=Ļw¸Ëĩ{ĻĄ - %1 -UninstallAppTitle=¸Ņ°ŖĻw¸Ë -UninstallAppFullTitle=%1 ¸Ņ°ŖĻw¸Ë +SetupAppTitle=åŽ‰čŖį¨‹åŧ +SetupWindowTitle=%1 åŽ‰čŖį¨‹åŧ +UninstallAppTitle=č§Ŗé™¤åŽ‰čŖ +UninstallAppFullTitle=č§Ŗé™¤åŽ‰čŖ %1 + ; *** Misc. common -InformationTitle=¸ę°T -ConfirmTitle=ŊTģ{ -ErrorTitle=ŋųģ~ +InformationTitle=č¨Šæ¯ +ConfirmTitle=įĸēčĒ +ErrorTitle=錯čǤ + ; *** SetupLdr messages -SetupLdrStartupMessage=ŗoˇ|Ļw¸Ë %1ĄC­nÄ~ÄōļÜ? -LdrCannotCreateTemp=ĩLĒkĢØĨßŧČĻsĀÉĄCĻw¸Ë¤w¤¤¤î -LdrCannotExecTemp=ĩLĒk°õĻæŧČĻsĨØŋũ¤¤ĒēĀɎץCĻw¸Ë¤w¤¤¤î +SetupLdrStartupMessage=é€™å°‡æœƒåŽ‰čŖ %1ã€‚æ‚¨æƒŗčρįšŧįēŒå—Ž? +LdrCannotCreateTemp=į„Ąæŗ•åģēįĢ‹æšĢ存æĒ”æĄˆã€‚åŽ‰čŖį¨‹åŧå°‡æœƒįĩæŸã€‚ +LdrCannotExecTemp=į„Ąæŗ•åŸˇčĄŒæšĢ存æĒ”æĄˆã€‚åŽ‰čŖį¨‹åŧå°‡æœƒįĩæŸã€‚ +HelpTextNote= + ; *** Startup error messages -LastErrorMessage=%1ĄC%n%nŋųģ~ %2: %3 -SetupFileMissing=Ļw¸ËĨØŋũ¤¤¯Ę¤ÖĀÉŽ× %1ĄCŊĐ­×Ĩŋ°ŨÃDĄAŠÎ­Ģˇs¨úąoĩ{ĻĄĒēˇsŊÆĨģĄC -SetupFileCorrupt=Ļw¸Ëĩ{ĻĄĀɎפwˇlˇ´ĄCŊĐ­Ģˇs¨úąo¸Ķĩ{ĻĄĒēŊÆĨģĄC -SetupFileCorruptOrWrongVer=Ļw¸Ëĩ{ĻĄĀɎפwˇlˇ´ĄAŠÎ¤ŖŦÛŽeŠķģPĻšĒŠĒēĻw¸Ëĩ{ĻĄĄCŊĐ­×Ĩŋ°ŨÃDĄAŠÎ­Ģˇs¨úąoĩ{ĻĄĒēˇsŊÆĨģĄC -InvalidParameter=ĻbŠRĨOĻC¤WļĮģŧ¤FĩLŽÄĒē°ŅŧÆ:%n%n%1 -SetupAlreadyRunning=Ļw¸Ëĩ{ĻĄ¤wĻb°õĻæ¤¤ĄC -WindowsVersionNotSupported=Ļšĩ{ĻĄ¤Ŗ¤ä´Ššq¸ŖŠŌ°õĻæĒē Windows ĒŠĨģĄC -WindowsServicePackRequired=Ļšĩ{ĻĄģŨ­n %1 Service Pack %2 ŠÎ§ķˇsĒŠĨģĄC -NotOnThisPlatform=Ļšĩ{ĻĄ¤Ŗˇ|Ļb %1 ¤W°õĻæĄC -OnlyOnThisPlatform=Ļšĩ{ĻĄĨ˛ļˇĻb %1 ¤W°õĻæĄC -OnlyOnTheseArchitectures=Ļšĩ{ĻĄĨuĨiĻw¸ËĻbąMŦ°¤UĻCŗB˛zžšŦ[ēcŗ]­pĒē Windows ĒŠĨģ¤W:%n%n%1 -MissingWOW64APIs=ąz°õĻæĒē Windows ĒŠĨ줪§tĻw¸Ëĩ{ĻĄ°õĻæ 64 Ļ뤏Ļw¸ËŠŌģŨĒēĨ\¯āĄC­Y­n­×ĨŋĻš°ŨÃDĄAŊĐĻw¸Ë Service Pack %1ĄC -WinVersionTooLowError=Ļšĩ{ĻĄģŨ­n %1 ĒŠ %2 ŠÎ§ķˇsĒŠĨģĄC -WinVersionTooHighError=Ļšĩ{ĻĄĩLĒkĻw¸ËĻb %1 ĒŠ %2 ŠÎ§ķˇsĒŠĨģ¤WĄC -AdminPrivilegesRequired=Ļw¸ËĻšĩ{ĻĄŽÉĄAĨ˛ļˇĨH¨t˛ÎēŪ˛z­û¨­¤Āĩn¤JĄC -PowerUserPrivilegesRequired=ˇíązĻw¸ËĻšĩ{ĻĄŽÉĄAĨ˛ļˇĨH¨t˛ÎēŪ˛z­ûŠÎ Power Users ¸s˛ÕĒēύ­û¨­¤Āĩn¤JĄC -SetupAppRunningError=Ļw¸ËŽÉ°ģ´ú¨ė %1 ĨØĢeĨŋĻb°õĻæ¤¤ĄC%n%nŊĐĨß§YÃöŗŦ¨äŠŌĻŗ°õĻæ­ĶÅéĄC­Y­nÄ~ÄōĄAŊĐĢö¤@¤U [ŊTŠw]; ­Y­nĩ˛§ôĄAŊĐĢö¤@¤U [¨úŽø]ĄC -UninstallAppRunningError=¸Ņ°ŖĻw¸ËŽÉ°ģ´ú¨ė %1 ĨØĢeĨŋĻb°õĻæ¤¤ĄC%n%nŊĐĨß§YÃöŗŦ¨äŠŌĻŗ°õĻæ­ĶÅéĄC­Y­nÄ~ÄōĄAŊĐĢö¤@¤U [ŊTŠw]; ­Y­nĩ˛§ôĄAŊĐĢö¤@¤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¸Ëĩ{ĻĄĩLĒkĢØĨßĨØŋũ "%1" -ErrorTooManyFilesInDir=Ļ]Ŧ°ĨØŋũ "%1" Ĩ]§t¤ĶĻhĀɎץAŠŌĨHĩLĒkĻb¨ä¤¤ĢØĨßĀÉŽ× +ErrorCreatingDir=åŽ‰čŖį¨‹åŧį„Ąæŗ•åģēįĢ‹čŗ‡æ–™å¤žâ€œ%1”。 +ErrorTooManyFilesInDir=į„Ąæŗ•åœ¨čŗ‡æ–™å¤žâ€œ%1”內åģēįĢ‹æĒ”æĄˆīŧŒå› į‚ēčŗ‡æ–™å¤žå…§æœ‰å¤Ēå¤šįš„æĒ”æĄˆã€‚ + ; *** Setup common messages -ExitSetupTitle=ĩ˛§ôĻw¸Ë -ExitSetupMessage=Ļw¸ËĨŧ§šĻ¨ĄC­YĨß§Yĩ˛§ôĄAąN¤Ŗˇ|Ļw¸Ëĩ{ĻĄĄC%n%nązĨiĨHĩyĢáĻA°õĻæĻw¸Ëĩ{ĻĄ¨Ķ§šĻ¨Ļw¸ËĄC%n%n­nĩ˛§ôĻw¸ËļÜ? -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=ŊTŠw -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=­Y­nÄ~ÄōĄAŊĐĢö¤@¤U [¤U¤@¨B]; ­Y­nĩ˛§ôĻw¸ËĄAŊĐĢö¤@¤U [¨úŽø]ĄC +ClickNext=按 [下一æ­Ĩ] įšŧįēŒåŽ‰čŖīŧŒæˆ–按 [取æļˆ] įĩæŸåŽ‰čŖį¨‹åŧã€‚ BeveledLabel= -BrowseDialogTitle=ÂsÄũ¸ęŽÆ§¨ -BrowseDialogLabel=ŊĐąq¤UĻC˛Mŗæ¤¤ŋī¨ú¸ęŽÆ§¨ĄAĩMĢáĢö¤@¤U [ŊTŠw]ĄC -NewFolderName=ˇsŧW¸ęŽÆ§¨ +BrowseDialogTitle=į€čĻŊčŗ‡æ–™å¤ž +BrowseDialogLabel=在下éĸįš„čŗ‡æ–™å¤žåˆ—čĄ¨ä¸­é¸æ“‡ä¸€å€‹čŗ‡æ–™å¤žīŧŒį„ļ垌按 [įĸē厚]。 +NewFolderName=æ–°čŗ‡æ–™å¤ž + ; *** "Welcome" wizard page -WelcomeLabel1=ÅwĒī¨ĪĨÎ [name] Ļw¸ËēëÆF -WelcomeLabel2=ŗoˇ|ĻbązĒēšq¸Ŗ¤WĻw¸Ë [name/ver]ĄC%n%nĢØÄŗązĨũÃöŗŦŠŌĻŗ¨äĨLĀŗĨÎĩ{ĻĄĄAĩMĢáĻAÄ~ÄōĄC +WelcomeLabel1=æ­ĄčŋŽäŊŋᔍ [name] åŽ‰čŖį¨‹åŧ +WelcomeLabel2=é€™å€‹åŽ‰čŖį¨‹åŧå°‡æœƒåŽ‰čŖ [name/ver] åˆ°æ‚¨įš„é›ģč…Ļ。%n%n我們åŧˇįƒˆåģēč­°æ‚¨åœ¨åŽ‰čŖéŽį¨‹ä¸­é—œé–‰å…ļåŽƒįš„æ‡‰į”¨į¨‹åŧīŧŒäģĨéŋå…čˆ‡åŽ‰čŖį¨‹åŧį™ŧį”Ÿæ˛–įĒã€‚ + ; *** "Password" wizard page -WizardPassword=ąKŊX -PasswordLabel1=ĻšĻw¸Ë¨üąKŊXĢOÅ@ĄC -PasswordLabel3=ŊĐ´Ŗ¨ŅąKŊXĄAĩMĢáĢö¤@¤U [¤U¤@¨B] ĨHÄ~ÄōĄCąKŊX°Ī¤Ā¤j¤pŧgĄC -PasswordEditLabel=ąKŊX(&P): -IncorrectPassword=ŋé¤JĒēąKŊX¤ŖĨŋŊTĄCŊĐĻA¸Õ¤@ϏĄC +WizardPassword=密įĸŧ +PasswordLabel1=é€™å€‹åŽ‰čŖį¨‹åŧå…ˇæœ‰å¯†įĸŧäŋč­ˇã€‚ +PasswordLabel3=čĢ‹čŧ¸å…Ĩ密įĸŧīŧŒį„ļ垌按 [下一æ­Ĩ] įšŧįēŒã€‚密įĸŧ是區分大小å¯Ģįš„ã€‚ +PasswordEditLabel=密įĸŧ(&P): +IncorrectPassword=您čŧ¸å…Ĩįš„å¯†įĸŧä¸æ­ŖįĸēīŧŒčĢ‹é‡æ–°čŧ¸å…Ĩ。 + ; *** "License Agreement" wizard page -WizardLicense=ąÂÅvĻXŦų -LicenseLabel=ŊĐĨũž\ÅǤUĻC­Ģ­n¸ę°TĻAÄ~ÄōĄC -LicenseLabel3=ŊĐž\ÅǤUĻCąÂÅvĻXŦųĄCązĨ˛ļˇąĩ¨üĻšĻXŦųąø´ÚĄA¤~¯āÄ~ÄōĻw¸ËĄC -LicenseAccepted=§Úąĩ¨üĻXŦų(&A) -LicenseNotAccepted=§Ú¤Ŗąĩ¨üĻXŦų(&D) +WizardLicense=授æŦŠåˆį´„ +LicenseLabel=čĢ‹é–ąčŽ€äģĨ下授æŦŠåˆį´„。 +LicenseLabel3=čĢ‹é–ąčŽ€äģĨ下授æŦŠåˆį´„īŧŒæ‚¨åŋ…é ˆæŽĨå—åˆį´„įš„å„é …æĸæŦžæ‰čƒŊįšŧįēŒåŽ‰čŖã€‚ +LicenseAccepted=我同意(&A) +LicenseNotAccepted=我不同意(&D) + ; *** "Information" wizard pages -WizardInfoBefore=¸ę°T -InfoBeforeLabel=ŊĐĨũž\ÅǤUĻC­Ģ­n¸ę°TĻAÄ~ÄōĄC -InfoBeforeClickLabel=ˇíązˇĮŗÆĻn­nÄ~ÄōĻw¸ËŽÉĄAŊĐĢö¤@¤U [¤U¤@¨B]ĄC -WizardInfoAfter=¸ę°T -InfoAfterLabel=ŊĐĨũž\ÅǤUĻC­Ģ­n¸ę°TĻAÄ~ÄōĄC -InfoAfterClickLabel=ˇíązˇĮŗÆĻn­nÄ~ÄōĻw¸ËŽÉĄAŊĐĢö¤@¤U [¤U¤@¨B]ĄC +WizardInfoBefore=č¨Šæ¯ +InfoBeforeLabel=在įšŧįēŒåŽ‰čŖäš‹å‰čĢ‹é–ąčŽ€äģĨ下重čĻčŗ‡č¨Šã€‚ +InfoBeforeClickLabel=į•ļ您æē–å‚™åĨŊįšŧįēŒåŽ‰čŖīŧŒčĢ‹æŒ‰ [下一æ­Ĩ]。 +WizardInfoAfter=č¨Šæ¯ +InfoAfterLabel=在įšŧįēŒåŽ‰čŖäš‹å‰čĢ‹é–ąčŽ€äģĨ下重čĻčŗ‡č¨Šã€‚ +InfoAfterClickLabel=į•ļ您æē–å‚™åĨŊįšŧįēŒåŽ‰čŖīŧŒčĢ‹æŒ‰ [下一æ­Ĩ]。 + ; *** "User Information" wizard page -WizardUserInfo=¨ĪĨÎĒˏę°T -UserInfoDesc=ŊĐŋé¤JązĒē¸ę°TĄC -UserInfoName=¨ĪĨÎĒĖĻWēŲ(&U): -UserInfoOrg=˛Õ´(&O): -UserInfoSerial=§Į¸š(&S): -UserInfoNameRequired=Ĩ˛ļˇŋé¤JĻWēŲĄC +WizardUserInfo=äŊŋį”¨č€…čŗ‡č¨Š +UserInfoDesc=čĢ‹čŧ¸å…Ĩæ‚¨įš„čŗ‡æ–™ã€‚ +UserInfoName=äŊŋį”¨č€…åį¨ą(&U): +UserInfoOrg=įĩ„įš”(&O): +UserInfoSerial=åēč™Ÿ(&S): +UserInfoNameRequired=您åŋ…é ˆčŧ¸å…Ĩæ‚¨įš„åį¨ąã€‚ + ; *** "Select Destination Location" wizard page -WizardSelectDir=ŋī¨úĨØĒēĻaĻė¸m -SelectDirDesc=ĀŗąN [name] Ļw¸ËĻbĻķŗB? -SelectDirLabel3=Ļw¸Ëĩ{ĻĄˇ|ąN [name] Ļw¸ËĻb¤UĻC¸ęŽÆ§¨¤¤ĄC -SelectDirBrowseLabel=­Y­nÄ~ÄōĄAŊĐĢö¤@¤U [¤U¤@¨B]ĄC­YązˇQŋī¨ú¤ŖĻPĒē¸ęŽÆ§¨ĄAŊĐĢö¤@¤U [ÂsÄũ]ĄC -DiskSpaceMBLabel=ĻܤÖļˇĻŗ [mb] MB ĒēĨiĨÎēĪēĐĒÅļĄĄC -CannotInstallToNetworkDrive=Ļw¸Ëĩ{ĻĄĩLĒkĻw¸Ë¨ėēô¸ôēĪēĐž÷ĄC -CannotInstallToUNCPath=Ļw¸Ëĩ{ĻĄĩLĒkĻw¸Ë¨ė UNC ¸ôŽ|ĄC -InvalidPath=Ĩ˛ļˇŋé¤JĨ]§tēĪēĐž÷ĨN¸šĒē§šžã¸ôŽ|ĄA¨ŌĻp:%n%nC:\APP%n%nŠÎŋé¤J¤UĻCŽæĻĄĒē UNC ¸ôŽ|:%n%n\\ĻøĒAžš\Ļ@ĨÎ -InvalidDrive=ŋī¨úĒēēĪēĐž÷ŠÎ UNC Ļ@ĨΤŖĻsĻbŠÎĩLĒkĻs¨úĄCŊĐŋī¨ú¨äĨLēĪēĐž÷ŠÎ UNC Ļ@ĨÎĄC -DiskSpaceWarningTitle=ēĪēĐĒÅļĄ¤Ŗ¨Ŧ -DiskSpaceWarning=Ļw¸Ëĩ{ĻĄĻܤÖģŨ­n %1 KB ĒēĨiĨÎĒÅļĄ¤~¯āĻw¸ËĄAĻũŠŌŋīēĪēĐž÷ĒēĨiĨÎĒÅļĄĨuĻŗ %2 KBĄC%n%n¤´­nÄ~ÄōļÜ? -DirNameTooLong=¸ęŽÆ§¨ĻWēŲŠÎ¸ôŽ|šLĒøĄC -InvalidDirName=Ļš¸ęŽÆ§¨ĻWēŲĩLŽÄĄC -BadDirName32=¸ęŽÆ§¨ĻWēŲ¤ŖąoĨ]§t¤UĻCĨô¤@Ļr¤¸:%n%n%1 -DirExistsTitle=¸ęŽÆ§¨¤wĻsĻb -DirExists=¤wĻŗ¸ęŽÆ§¨ %n%n%1%n%nĄC¤´­nĻw¸Ë¨ė¸Ķ¸ęŽÆ§¨ļÜ? -DirDoesntExistTitle=¸ęŽÆ§¨¤ŖĻsĻb -DirDoesntExist=¸ęŽÆ§¨ %n%n%1%n%n ¤ŖĻsĻbĄC­nĢØĨß¸Ķ¸ęŽÆ§¨ļÜ? +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=ŋī¨úąz­nĻw¸ËĒ礏Ĩķ; ˛M°Ŗąz¤Ŗ­nĻw¸ËĒ礏ĨķĄCˇíązˇĮŗÆĻn­nÄ~ÄōŽÉĄ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=ĻÛ­qĻw¸Ë -NoUninstallWarningTitle=¤wĻŗĻš¤¸Ĩķ -NoUninstallWarning=Ļw¸Ëĩ{ĻĄ°ģ´ú¨ėązĒēšq¸Ŗ¤wĻw¸Ë¤F¤UĻC¤¸Ĩķ:%n%n%1%n%nąNŗo¨Į¤¸Ĩķ¨úŽøŋī¨ú¨Ã¤Ŗˇ|¨Ī¤¸Ĩķ¸Ņ°ŖĻw¸ËĄC%n%n¤´­nÄ~ÄōļÜ? +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=ŋī¨ú¨äĨL¤u§@ -SelectTasksDesc=ÁŲõĻæ­ū¨Į¨äĨL¤u§@? -SelectTasksLabel2=ŊĐŋī¨úĻw¸Ëĩ{ĻĄĻbĻw¸Ë [name] ŽÉĄAļˇÃBĨ~°õĻæĒē¨äĨL¤u§@ĄAĩMĢáĢö¤@¤U [¤U¤@¨B]ĄC +WizardSelectTasks=é¸æ“‡é™„åŠ įš„åˇĨäŊœ +SelectTasksDesc=選擇čĻåŸˇčĄŒįš„é™„åŠ åˇĨäŊœã€‚ +SelectTasksLabel2=é¸æ“‡åŽ‰čŖį¨‹åŧåœ¨åŽ‰čŖ [name] 時čĻåŸˇčĄŒįš„é™„åŠ åˇĨäŊœīŧŒį„ļ垌按 [下一æ­Ĩ]。 + ; *** "Select Start Menu Folder" wizard page -WizardSelectProgramGroup=ŋī¨ú [ļ}Šl] Ĩ\¯āĒí¸ęŽÆ§¨ -SelectStartMenuFolderDesc=Ļw¸Ëĩ{ĻĄĀŗąNĩ{ĻĄąļŽ|¸mŠķĻķŗB? -SelectStartMenuFolderLabel3=Ļw¸Ëĩ{ĻĄąNĻb¤UĻC [ļ}Šl] Ĩ\¯āĒí¸ęŽÆ§¨¤¤ĢØĨßĩ{ĻĄąļŽ|ĄC -SelectStartMenuFolderBrowseLabel=­Y­nÄ~ÄōĄAŊĐĢö¤@¤U [¤U¤@¨B]ĄC­YązˇQŋī¨ú¤ŖĻPĒē¸ęŽÆ§¨ĄAŊĐĢö¤@¤U [ÂsÄũ]ĄC -MustEnterGroupName=Ĩ˛ļˇŋé¤J¸ęŽÆ§¨ĻWēŲĄC -GroupNameTooLong=¸ęŽÆ§¨ĻWēŲŠÎ¸ôŽ|šLĒøĄC -InvalidGroupName=Ļš¸ęŽÆ§¨ĻWēŲĩLŽÄĄC -BadGroupName=¸ęŽÆ§¨ĻWēŲ¤ŖąoĨ]§t¤UĻCĨô¤@Ļ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=¤wĨiļ}ŠlĻw¸Ë -ReadyLabel1=Ļw¸Ëĩ{ĻĄ˛{Ļb¤wĨiļ}ŠląN [name] Ļw¸Ë¨ėązĒēšq¸Ŗ¤WĄC -ReadyLabel2a=­Y­nÄ~ÄōĻw¸ËĄAŊĐĢö¤@¤U [Ļw¸Ë]; ­Y­nĀËž\ŠÎÅܧķĨôĻķŗ]ŠwĄAŊĐĢö¤@¤U [¤W¤@¨B]ĄC -ReadyLabel2b=­Y­nÄ~ÄōĻw¸ËĄAŊĐĢö¤@¤U [Ļw¸Ë]ĄC -ReadyMemoUserInfo=¨ĪĨÎĒˏę°T: -ReadyMemoDir=ĨØĒēĻaĻė¸m: -ReadyMemoType=Ļw¸ËÃūĢŦ: -ReadyMemoComponents=ŋī¨úĒ礏Ĩķ: -ReadyMemoGroup=[ļ}Šl] Ĩ\¯āĒí¸ęŽÆ§¨: -ReadyMemoTasks=¨äĨL¤u§@: +WizardReady=æē–å‚™åŽ‰čŖ +ReadyLabel1=åŽ‰čŖį¨‹åŧå°‡é–‹å§‹åŽ‰čŖ [name] åˆ°æ‚¨įš„é›ģč…Ļ中。 +ReadyLabel2a=按下 [åŽ‰čŖ] įšŧįēŒåŽ‰čŖīŧŒæˆ–按 [上一æ­Ĩ] 重新æĒĸčĻ–æˆ–č¨­åŽšå„é¸é …įš„å…§åŽšã€‚ +ReadyLabel2b=按下 [åŽ‰čŖ] įšŧįēŒåŽ‰čŖã€‚ +ReadyMemoUserInfo=äŊŋį”¨č€…čŗ‡č¨Š +ReadyMemoDir=į›Žįš„čŗ‡æ–™å¤ž: +ReadyMemoType=åŽ‰čŖåž‹æ…‹: +ReadyMemoComponents=é¸æ“‡įš„å…ƒäģļ: +ReadyMemoGroup=「開始」功čƒŊčĄ¨čŗ‡æ–™å¤ž: +ReadyMemoTasks=附加åˇĨäŊœ: + ; *** "Preparing to Install" wizard page -WizardPreparing=ĨŋĻbˇĮŗÆĻw¸Ë -PreparingDesc=Ļw¸Ëĩ{ĻĄĨŋĻbˇĮŗÆąN [name] Ļw¸Ë¨ėązĒēšq¸Ŗ¤WĄC -PreviousInstallNotCompleted=¤W¤@­Ķĩ{ĻĄĒēĻw¸Ë/˛ž°ŖŠ|Ĩŧ§šĻ¨ĄCĨ˛ļˇ­ĢˇsąŌ°Ęšq¸ŖĄA¤~¯ā§šĻ¨¸ĶĻw¸ËĄC%n%nŊĐĻb­ĢˇsąŌ°Ęšq¸Ŗ¤§ĢáĄA­Ģˇs°õĻæĻw¸Ëĩ{ĻĄĄAĨH§šĻ¨ [name] ĒēĻw¸ËĄC -CannotContinue=Ļw¸Ëĩ{ĻĄĩLĒkÄ~ÄōĄCŊĐĢö¤@¤U [¨úŽø] ĨHĩ˛§ôĄC -ApplicationsFound=Ļw¸Ëĩ{ĻĄĨ˛ļˇ§ķˇs¤UĻCĀŗĨÎĩ{ĻĄĨŋĻb¨ĪĨÎĒē¤@¨ĮĀɎץCĢØÄŗąz¤šŗ\Ļw¸Ëĩ{ĻĄĻÛ°ĘÃöŗŦŗo¨ĮĀŗĨÎĩ{ĻĄĄC -ApplicationsFound2=Ļw¸Ëĩ{ĻĄĨ˛ļˇ§ķˇs¤UĻCĀŗĨÎĩ{ĻĄĨŋĻb¨ĪĨÎĒē¤@¨ĮĀɎץCĢØÄŗąz¤šŗ\Ļw¸Ëĩ{ĻĄĻÛ°ĘÃöŗŦŗo¨ĮĀŗĨÎĩ{ĻĄĄCˇíĻw¸Ë§šĻ¨¤§ĢáĄAĻw¸Ëĩ{ĻĄąNˇ|šÁ¸Õ­ĢˇsąŌ°Ęŗo¨ĮĀŗĨÎĩ{ĻĄĄC -CloseApplications=ĻÛ°ĘÃöŗŦĀŗĨÎĩ{ĻĄ(&A) -DontCloseApplications=¤Ŗ­nÃöŗŦĀŗĨÎĩ{ĻĄ(&D) -ErrorCloseApplications=Ļw¸Ëĩ{ĻĄĩLĒkĻÛ°ĘÃöŗŦŠŌĻŗĀŗĨÎĩ{ĻĄĄCĢØÄŗązÃöŗŦŠŌĻŗĨŋĻb¨ĪĨÎĻw¸Ëĩ{ĻĄĨ˛ļˇ§ķˇs¤§ĀÉŽ×ĒēĀŗĨÎĩ{ĻĄĄAĩMĢáĻAÄ~ÄōĄC +WizardPreparing=æē–å‚™åŽ‰čŖį¨‹åŧ +PreparingDesc=åŽ‰čŖį¨‹åŧæē–備將 [name] åŽ‰čŖåˆ°æ‚¨įš„é›ģč…Ļ上。 +PreviousInstallNotCompleted=å…ˆå‰įš„åŽ‰čŖ/ č§Ŗé™¤åŽ‰čŖå°šæœĒ厌成īŧŒæ‚¨åŋ…須重新啟動é›ģč…ĻäģĨåŽŒæˆčŠ˛åŽ‰čŖã€‚%n%n在重新啟動é›ģč…Ļ䚋垌īŧŒčĢ‹å†åŸˇčĄŒé€™å€‹į¨‹åŧäž†åŽ‰čŖ [name]。 +CannotContinue=åŽ‰čŖį¨‹åŧį„Ąæŗ•įšŧįēŒã€‚čĢ‹æŒ‰ [取æļˆ] é›ĸ開。 +ApplicationsFound=下éĸįš„æ‡‰į”¨į¨‹åŧæ­Ŗåœ¨äŊŋį”¨åŽ‰čŖį¨‹åŧæ‰€éœ€čĻæ›´æ–°įš„æ–‡æĒ”。åģēč­°æ‚¨å…č¨ąåŽ‰čŖį¨‹åŧč‡Ē動關閉這ä盿‡‰į”¨į¨‹åŧã€‚ +ApplicationsFound2=下éĸįš„æ‡‰į”¨į¨‹åŧæ­Ŗåœ¨äŊŋį”¨åŽ‰čŖį¨‹åŧæ‰€éœ€čĻæ›´æ–°įš„æ–‡æĒ”。åģēč­°æ‚¨å…č¨ąåŽ‰čŖį¨‹åŧč‡Ē動關閉這ä盿‡‰į”¨į¨‹åŧã€‚į•ļåŽ‰čŖéŽį¨‹įĩæŸåžŒīŧŒæœŦåŽ‰čŖį¨‹åŧå°‡æœƒå˜—čŠĻé‡æ–°é–‹å•ŸčŠ˛æ‡‰į”¨į¨‹åŧã€‚ +CloseApplications=é—œé–‰æ‡‰į”¨į¨‹åŧ(&A) +DontCloseApplications=不čĻé—œé–‰æ‡‰į”¨į¨‹åŧ (&D) +ErrorCloseApplications=åŽ‰čŖį¨‹åŧį„Ąæŗ•č‡Ēå‹•é—œé–‰æ‰€æœ‰æ‡‰į”¨į¨‹åŧã€‚åģēč­°æ‚¨åœ¨įšŧįēŒå‰å…ˆé—œé–‰æ‰€æœ‰æ‡‰į”¨į¨‹åŧäŊŋį”¨įš„æĒ”æĄˆã€‚ + ; *** "Installing" wizard page -WizardInstalling=Ļw¸Ë¤¤ -InstallingLabel=ŊĐĩy­ÔĄAĻw¸Ëĩ{ĻĄĨŋĻbąN [name] Ļw¸Ë¨ėązĒēšq¸Ŗ¤WĄC +WizardInstalling=æ­Ŗåœ¨åŽ‰čŖ +InstallingLabel=čĢ‹į¨å€™īŧŒåŽ‰čŖį¨‹åŧæ­Ŗåœ¨å°‡ [name] åŽ‰čŖåˆ°æ‚¨įš„é›ģč…Ļ上 + ; *** "Setup Completed" wizard page -FinishedHeadingLabel=ĨŋĻb§šĻ¨ [name] Ļw¸ËēëÆF -FinishedLabelNoIcons=Ļw¸Ëĩ{ĻĄ¤w§šĻ¨ązšq¸Ŗ¤W [name] ĒēĻw¸ËĄC -FinishedLabel=Ļw¸Ëĩ{ĻĄ¤w§šĻ¨ązšq¸Ŗ¤W [name] ĒēĻw¸ËĄCązĨiĨHŋī¨úŠŌĻw¸ËĒēąļŽ|¨ĶąŌ°ĘĀŗĨÎĩ{ĻĄĄC -ClickFinish=ŊĐĢö¤@¤U [§šĻ¨]ĄAĨHĩ˛§ôĻw¸ËĄC -FinishedRestartLabel=Ļw¸Ëĩ{ĻĄĨ˛ļˇ­ĢˇsąŌ°ĘązĒēšq¸ŖĄA¤~¯ā§šĻ¨ [name] ĒēĻw¸ËĄC­nĨß§Y­ĢˇsąŌ°ĘļÜ? -FinishedRestartMessage=Ļw¸Ëĩ{ĻĄĨ˛ļˇ­ĢˇsąŌ°ĘązĒēšq¸ŖĄA¤~¯ā§šĻ¨ [name] ĒēĻw¸ËĄC%n%n­nĨß§Y­ĢˇsąŌ°ĘļÜ? -ShowReadmeCheck=ŦOĄA§Ú­nĀËĩøÅǧÚĀÉŽ× -YesRadio=ŦOĄAĨß§Y­ĢˇsąŌ°Ęšq¸Ŗ(&Y) -NoRadio=§_ĄAĩy­ÔĻA­ĢˇsąŌ°Ęš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¸Ëĩ{ĻĄģŨ­n¤U¤@ąiēΤųĄC -SelectDiskLabel2=ŊĐ´Ą¤JēΤų %1ĄAĩMĢáĢö¤@¤U [ŊTŠw]ĄC%n%n­YĻšēΤų¤WĒēĀÉŽ×ĨiĨHĻb¤UĻCÅãĨܤ§¸ęŽÆ§¨ĨHĨ~Ēē¸ęŽÆ§¨¤¤§ä¨ėĄAŊĐŋé¤JĨŋŊTĒē¸ôŽ|ĄAŠÎĢö¤@¤U [ÂsÄũ]ĄC -PathLabel=¸ôŽ|(&P): -FileNotInDir2=Ļb "%2" ¤¤§ä¤Ŗ¨ėĀÉŽ× "%1"ĄCŊĐ´Ą¤JĨŋŊTĒēēΤųĄAŠÎŋī¨ú¨äĨL¸ęŽÆ§¨ĄC -SelectDirectoryLabel=ŊĐĢüŠw¤U¤@ąiēΤųĒēĻė¸mĄC +RunEntryShellExec=æĒĸčĻ– %1 + +; *** "Setup Needs the Next Disk" +ChangeDiskTitle=åŽ‰čŖį¨‹åŧéœ€čρ䏋䏀åŧĩ᪁ቇ +SelectDiskLabel2=čĢ‹æ’å…Ĩ᪁ቇ %1īŧŒį„ļ垌按 [įĸē厚]。%n%nåĻ‚æžœæĒ”æĄˆä¸åœ¨äģĨä¸‹æ‰€éĄ¯į¤ēįš„čŗ‡æ–™å¤žäš‹ä¸­īŧŒčĢ‹čŧ¸å…Ĩæ­Ŗįĸēįš„čŗ‡æ–™å¤žåį¨ąæˆ–æŒ‰ [į€čĻŊ] 選取。 +PathLabel=čˇ¯åž‘(&P): +FileNotInDir2=æĒ”æĄˆâ€œ%1â€į„Ąæŗ•åœ¨â€œ%2”扞到。čĢ‹æ’å…Ĩæ­Ŗįĸēįš„įŖį‰‡æˆ–é¸æ“‡å…ļåŽƒįš„čŗ‡æ–™å¤žã€‚ +SelectDirectoryLabel=čĢ‹æŒ‡åŽšä¸‹ä¸€åŧĩįŖį‰‡įš„äŊįŊŽã€‚ + ; *** Installation phase messages -SetupAborted=Ļw¸ËĨŧĻwύĄC%n%nŊĐ­×Ĩŋ°ŨÃDĄAĻA­Ģˇs°õĻæĻw¸Ëĩ{ĻĄĄC -EntryAbortRetryIgnore=­Y­nĻA¸Õ¤@ϏĄAŊĐĢö¤@¤U [­Ģ¸Õ]; ­Y­nÄ~ÄōĄAŊĐĢö¤@¤U [Šŋ˛¤]; ­Y­n¨úŽøĻw¸ËĄAŊĐĢö¤@¤U [¤¤¤î]ĄC +SetupAborted=åŽ‰čŖæ˛’æœ‰åŽŒæˆã€‚%n%nčĢ‹æ›´æ­Ŗå•éĄŒåžŒé‡æ–°åŽ‰čŖä¸€æŦĄã€‚ +AbortRetryIgnoreSelectAction=選取動äŊœ +AbortRetryIgnoreRetry=čĢ‹å†čŠĻ一æŦĄ (&T) +AbortRetryIgnoreIgnore=į•Ĩ過錯čǤä¸ĻįšŧįēŒ (&I) +AbortRetryIgnoreCancel=取æļˆåŽ‰čŖ + ; *** Installation status messages -StatusClosingApplications=ĨŋĻbÃöŗŦĀŗĨÎĩ{ĻĄ... -StatusCreateDirs=ĨŋĻbĢØĨßĨØŋũ... -StatusExtractFiles=ĨŋĻb¸ŅĀŖÁYĀÉŽ×... -StatusCreateIcons=ĨŋĻbĢØĨßąļŽ|... -StatusCreateIniEntries=ĨŋĻbĢØĨß INI ļĩĨØ... -StatusCreateRegistryEntries=ĨŋĻbĢØĨßĩnŋũļĩĨØ... -StatusRegisterFiles=ĨŋĻbĩnŋũĀÉŽ×... -StatusSavingUninstall=ĨŋĻbĀxĻs¸Ņ°ŖĻw¸Ë¸ę°T... -StatusRunProgram=ĨŋĻb§šĻ¨Ļw¸Ë... -StatusRestartingApplications=ĨŋĻb­ĢˇsąŌ°ĘĀŗĨÎĩ{ĻĄ... -StatusRollback=ĨŋĻb´_­ėÅܧķ... +StatusClosingApplications=æ­Ŗåœ¨é—œé–‰æ‡‰į”¨į¨‹åŧ... +StatusCreateDirs=æ­Ŗåœ¨åģēįĢ‹čŗ‡æ–™å¤ž... +StatusExtractFiles=æ­Ŗåœ¨č§ŖåŖ“į¸ŽæĒ”æĄˆ... +StatusCreateIcons=æ­Ŗåœ¨åģēį̋ፋåŧé›†åœ–į¤ē... +StatusCreateIniEntries=å¯Ģå…Ĩ INI æĒ”æĄˆįš„é …į›Ž... +StatusCreateRegistryEntries=æ­Ŗåœ¨æ›´æ–°įŗģįĩąį™ģ錄... +StatusRegisterFiles=æ­Ŗåœ¨į™ģ錄æĒ”æĄˆ... +StatusSavingUninstall=å„˛å­˜č§Ŗé™¤åŽ‰čŖčŗ‡č¨Š... +StatusRunProgram=æ­Ŗåœ¨åŽŒæˆåŽ‰čŖ... +StatusRestartingApplications=æ­Ŗåœ¨é‡æ–°é–‹å•Ÿæ‡‰į”¨į¨‹åŧ... +StatusRollback=æ­Ŗåœ¨åžŠåŽŸčŽŠæ›´... + ; *** Misc. errors -ErrorInternal2=¤ēŗĄŋųģ~: %1 -ErrorFunctionFailedNoCode=%1 ĨĸąŅ -ErrorFunctionFailed=%1 ĨĸąŅ; ĨNŊX %2 -ErrorFunctionFailedWithMessage=%1 ĨĸąŅ; ĨNŊX %2ĄC%n%3 -ErrorExecutingProgram=ĩLĒk°õĻæĀÉŽ×:%n%1 +ErrorInternal2=內部錯čǤ: %1 +ErrorFunctionFailedNoCode=%1 å¤ąæ•— +ErrorFunctionFailed=%1 å¤ąæ•—īŧ›äģŖįĸŧ %2 +ErrorFunctionFailedWithMessage=%1 å¤ąæ•—īŧ›äģŖįĸŧ %2.%n%3 +ErrorExecutingProgram=į„Ąæŗ•åŸˇčĄŒæĒ”æĄˆ:%n%1 + ; *** Registry errors -ErrorRegOpenKey=ļ}ąŌĩnŋũž÷ŊXŽÉĩoĨÍŋųģ~:%n%1\%2 -ErrorRegCreateKey=ĢØĨßĩnŋũž÷ŊXŽÉĩoĨÍŋųģ~:%n%1\%2 -ErrorRegWriteKey=ŧg¤Jĩnŋũž÷ŊXŽÉĩ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=­Y­nĻA¸Õ¤@ϏĄAŊĐĢö¤@¤U [­Ģ¸Õ]; ­Y­n˛¤šLĻšĀɎץAŊĐĢö¤@¤U [Šŋ˛¤] (¤ŖĢØÄŗ¨ĪĨÎ); ­Y­n¨úŽøĻw¸ËĄAŊĐĢö¤@¤U [¤¤¤î]ĄC -FileAbortRetryIgnore2=­Y­nĻA¸Õ¤@ϏĄAŊĐĢö¤@¤U [­Ģ¸Õ]; ­Y­nÄ~ÄōĄAŊĐĢö¤@¤U [Šŋ˛¤] (¤ŖĢØÄŗ¨ĪĨÎ); ­Y­n¨úŽøĻw¸ËĄAŊĐĢö¤@¤U [¤¤¤î]ĄC -SourceIsCorrupted=­ėŠlĩ{ĻĄĀɤwˇlˇ´ -SourceDoesntExist=­ėŠlĩ{ĻĄĀÉ "%1" ¤ŖĻsĻb -ExistingFileReadOnly=˛{ĻŗĀɎפwŧаOŦ°°ßÅĒĄC%n%n­Y­n˛ž°Ŗ°ßÅĒÄŨŠĘĄAĩMĢáĻA¸Õ¤@ϏĄAŊĐĢö¤@¤U [­Ģ¸Õ]; ­Y­n˛¤šLĻšĀɎץAŊĐĢö¤@¤U [Šŋ˛¤]; ­Y­n¨úŽøĻw¸ËĄAŊĐĢö¤@¤U [¤¤¤î]ĄC -ErrorReadingExistingDest=šÁ¸ÕÅǍú˛{ĻŗĀɎ׎ÉĩoĨÍŋųģ~: -FileExists=¤wĻŗĻšĀɎץC%n%n­nĨŅĻw¸Ëĩ{ĻĄĨ[ĨHÂĐŧgļÜ? -ExistingFileNewer=˛{ĻŗĀɎ׸ûĻw¸Ëĩ{ĻĄšÁ¸ÕĻw¸ËĒēĀɎסsĄCĢØÄŗązĢO¯d˛{ĻŗĀɎץC%n%n­nĢO¯d˛{ĻŗĒēĀÉŽ×ļÜ? -ErrorChangingAttr=šÁ¸ÕÅܧķ˛{ĻŗĀÉŽ×ĒēÄŨŠĘŽÉĩoĨÍŋųģ~: -ErrorCreatingTemp=šÁ¸ÕĻbĨØĒēĻaĨØŋũ¤¤ĢØĨßĀɎ׎ÉĩoĨÍŋųģ~: -ErrorReadingSource=šÁ¸ÕÅǍú­ėŠlĩ{ĻĄĀÉŽÉĩoĨÍŋųģ~: -ErrorCopying=šÁ¸ÕŊÆģsĀɎ׎ÉĩoĨÍŋųģ~: -ErrorReplacingExistingFile=šÁ¸Õ¨úĨN˛{ĻŗĀɎ׎ÉĩoĨÍŋųģ~: -ErrorRestartReplace=RestartReplace ĨĸąŅ: -ErrorRenamingTemp=šÁ¸Õ­ĢˇsŠRĻWĨØĒēĻaĨØŋũ¤¤ĒēĀɎ׎ÉĩoĨÍŋųģ~: -ErrorRegisterServer=ĩLĒkĩnŋũ DLL/OCX: %1 -ErrorRegSvr32Failed=RegSvr32 ĨĸąŅĄAĩ˛§ôĨNŊXŦ° %1 -ErrorRegisterTypeLib=ĩLĒkĩnŋũÃūĢŦĩ{ĻĄŽ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¸Ëĩ{ĻĄĩLĒk­ĢˇsąŌ°Ęšq¸ŖĄCŊФâ°Ę°õĻæĻš§@ˇ~ĄC +ErrorOpeningReadme=é–‹å•ŸčŽ€æˆ‘æĒ”æĄˆæ™‚į™ŧį”ŸéŒ¯čĒ¤ã€‚ +ErrorRestartingComputer=åŽ‰čŖį¨‹åŧį„Ąæŗ•重新啟動é›ģč…ĻīŧŒčĢ‹äģĨ手動斚åŧč‡ĒčĄŒé‡æ–°å•Ÿå‹•é›ģč…Ļ。 + ; *** Uninstaller messages -UninstallNotFound=¨SĻŗĀÉŽ× "%1"ĄCĩLĒk¸Ņ°ŖĻw¸ËĄC -UninstallOpenError=ĩLĒkļ}ąŌĀÉŽ× "%1"ĄCĩLĒk¸Ņ°ŖĻw¸Ë -UninstallUnsupportedVer=ĻšĒŠ¸Ņ°ŖĻw¸Ëĩ{ĻĄĩLĒkŋëÃҏҰŖĻw¸Ë°OŋũĀÉ "%1" ĒēŽæĻĄĄCĩLĒk¸Ņ°ŖĻw¸Ë -UninstallUnknownEntry=Ļb¸Ņ°ŖĻw¸Ë°Oŋũ¤¤§ä¨ė¤ŖŠúĒēļĩĨØ (%1) -ConfirmUninstall=ŊTŠw­n§šĨū˛ž°Ŗ %1 ¤Î¨äŠŌĻŗ¤¸ĨķļÜ? -UninstallOnlyOnWin64=ĨuĨiĻb 64 Ļ뤏 Windows ¤W¸Ņ°ŖĻw¸ËĻšĻw¸ËĄC -OnlyAdminCanUninstall=ĨuĻŗ¨ãŗÆ¨t˛ÎēŪ˛zÅv­­Ēē¨ĪĨÎĒĖĄA¤~¯ā¸Ņ°ŖĻw¸ËĻšĻw¸ËĄC -UninstallStatusLabel=ĨŋĻbąqązĒēšq¸Ŗ˛ž°Ŗ %1ĄAŊĐĩy­ÔĄC -UninstalledAll=¤wύĨ\ąqązĒēšq¸Ŗ˛ž°Ŗ %1ĄC -UninstalledMost=¸Ņ°ŖĻw¸Ë %1 ¤w§šĻ¨ĄC%n%nĻŗŗĄ¤ĀļĩĨØĩLĒk˛ž°ŖĄCązĨiĨH¤â°ĘĨ[ĨH˛ž°ŖĄC -UninstalledAndNeedsRestart=­Y­n§šĻ¨ %1 Ēē¸Ņ°ŖĻw¸ËĄAĨ˛ļˇ­ĢˇsąŌ°ĘązĒēšq¸ŖĄC%n%n­nĨß§Y­ĢˇsąŌ°ĘļÜ? -UninstallDataCorrupted="%1" ĀɎפwˇlˇ´ĄCĩLĒk¸Ņ°ŖĻ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˛ÎĢüĨX¤wĩLĨôĻķĩ{ĻĄĻb¨ĪĨΤUĻCĻ@ĨÎĀɎץCąz­n¸Ņ°ŖĻw¸ËĄAĨH˛ž°ŖĻšĻ@ĨÎĀÉŽ×ļÜ?%n%nĻpĻŗĨôĻķĩ{ĻĄ¤´Ļb¨ĪĨÎĻšĀÉŽ×ĻĶąN¸ĶĀɎײž°ŖĄAŗo¨Įĩ{ĻĄĨi¯āĩLĒkĨŋą`šB§@ĄC­Y¤ŖŊTŠwĄAŊĐŋīžÜ [§_]ĄCąNĀÉŽ×ĢO¯dĻb¨t˛Î¤W¨Ã¤Ŗˇ|ŗyύĨôĻķ¤Ŗ¨}ŧvÅTĄC -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=ĨŋĻbĻw¸Ë %1ĄC -ShutdownBlockReasonUninstallingApp=ĨŋĻb¸Ņ°ŖĻw¸Ë %1ĄC +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 °ÆĀÉĻWĒēÃöÁpĄK -AutoStartProgramGroupDescription=ąŌ°Ę: -AutoStartProgram=ĻÛ°ĘąŌ°Ę %1 -AddonHostProgramNotFound=Ļbŋī¨úĒē¸ęŽÆ§¨¤¤§ä¤Ŗ¨ė %1ĄC%n%n¤´­nÄ~ÄōļÜ? \ No newline at end of file + +NameAndVersion=%1 į‰ˆæœŦ %2 +AdditionalIcons=附加圖į¤ē: +CreateDesktopIcon=åģēįĢ‹æĄŒéĸ圖į¤ē(&D) +CreateQuickLaunchIcon=åģēįĢ‹åŋĢ速啟動圖į¤ē(&Q) +ProgramOnTheWeb=%1 įš„įļ˛įĢ™ +UninstallProgram=č§Ŗé™¤åŽ‰čŖ %1 +LaunchProgram=啟動 %1 +AssocFileExtension=將 %1 與æĒ”æĄˆå‰¯æĒ”名 %2 į”ĸį”Ÿé—œč¯(&A) +AssocingFileExtension=æ­Ŗåœ¨å°‡ %1 與æĒ”æĄˆå‰¯æĒ”名 %2 į”ĸį”Ÿé—œč¯... +AutoStartProgramGroupDescription=開啟: +AutoStartProgram=č‡Ē動開啟 %1 +AddonHostProgramNotFound=%1 į„Ąæŗ•åœ¨æ‚¨æ‰€é¸įš„čŗ‡æ–™å¤žä¸­æ‰žåˆ°ã€‚%n%n您是åĻ還čρįšŧįēŒīŧŸ diff --git a/build/win32/i18n/messages.en.isl b/build/win32/i18n/messages.en.isl index 12189c080d4..986eba00d3e 100644 --- a/build/win32/i18n/messages.en.isl +++ b/build/win32/i18n/messages.en.isl @@ -1,4 +1,11 @@ +[Messages] +FinishedLabel=Setup has finished installing [name] on your computer. The application may be launched by selecting the installed shortcuts. +ConfirmUninstall=Are you sure you want to completely remove %1 and all of its components? + [CustomMessages] +AdditionalIcons=Additional icons: +CreateDesktopIcon=Create a &desktop icon +CreateQuickLaunchIcon=Create a &Quick Launch icon AddContextMenuFiles=Add "Open with %1" action to Windows Explorer file context menu AddContextMenuFolders=Add "Open with %1" action to Windows Explorer directory context menu AssociateWithFiles=Register %1 as an editor for supported file types diff --git a/build/yarn.lock b/build/yarn.lock index 62326638698..3a8acba1bf7 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -394,6 +394,11 @@ acorn@4.X: resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" integrity sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c= +agent-base@5: + version "5.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" + integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== + ajv@^4.9.1: version "4.11.8" resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" @@ -557,6 +562,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +base64-js@^1.2.3: + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + bcrypt-pbkdf@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" @@ -569,6 +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" @@ -615,11 +630,29 @@ 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" @@ -717,6 +750,11 @@ commander@^2.8.1: resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== +compare-version@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/compare-version/-/compare-version-0.1.2.tgz#0162ec2d9351f5ddd59a9202cba935366a725080" + integrity sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA= + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -805,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== @@ -918,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" @@ -1370,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" @@ -1488,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" @@ -1537,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" @@ -1686,9 +1754,9 @@ lodash.unescape@4.0.1: integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw= lodash@^4.15.0, lodash@^4.17.10: - version "4.17.11" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" - integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== + version "4.17.19" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" + integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== macos-release@^2.2.0: version "2.3.0" @@ -1969,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" @@ -1992,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" @@ -2167,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" @@ -2458,10 +2535,10 @@ typescript@^3.0.1: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== -typescript@^3.9.1-rc: - version "3.9.1-rc" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.1-rc.tgz#81d5a5a0a597e224b6e2af8dffb46524b2eaf5f3" - integrity sha512-+cPv8L2Vd4KidCotqi2wjegBZ5n47CDRUu/QiLVu2YbeXAz78hIfcai9ziBiNI6JTGTVwUqXRug2UZxDcxhvFw== +typescript@^4.0.0-dev.20200729: + version "4.0.0-dev.20200729" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.0-dev.20200729.tgz#3e335af3ed54513bbfd9485799837b95bdfd15a0" + integrity sha512-jzPalday93NlFVuRkY7Vixd7I9dLKoefoB2dvIhoqWaAGc5WPbOQmCHOilEGPSXNd9gb+uy97RH6+EwM/cf1gQ== typical@^4.0.0: version "4.0.0" @@ -2593,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" @@ -2661,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 fcbac22e310..0da22bd9f57 100644 --- a/cglicenses.json +++ b/cglicenses.json @@ -200,6 +200,164 @@ }, { "name": "big-integer", - "prependLicenseText": ["Copyright released to public domain"] + "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 09478ca403e..cb9954628dd 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -60,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "0552e0d5de46ffa3b481d741f1db5c779e201565" + "commitHash": "5f93e889020d279d5a9cd1ecab080ab467312447" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "7.2.4" + "version": "7.3.2" }, { "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" }, { @@ -499,7 +533,7 @@ "git": { "name": "ripgrep", "repositoryUrl": "https://github.com/BurntSushi/ripgrep", - "commitHash": "8a7db1a918e969b85cd933d8ed9fa5285b281ba4" + "commitHash": "973de50c9ef451da2cfcdfa86f2b2711d8d6ff48" } }, "isOnlyProductionDependency": true, diff --git a/extensions/bat/test/colorize-results/test_bat.json b/extensions/bat/test/colorize-results/test_bat.json index 97155fafc8b..eb76b0e1d04 100644 --- a/extensions/bat/test/colorize-results/test_bat.json +++ b/extensions/bat/test/colorize-results/test_bat.json @@ -488,7 +488,7 @@ "t": "source.batchfile constant.character.escape.batchfile", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "constant.character: #569CD6" @@ -510,7 +510,7 @@ "t": "source.batchfile constant.character.escape.batchfile", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "constant.character: #569CD6" diff --git a/extensions/coffeescript/test/colorize-results/test_coffee.json b/extensions/coffeescript/test/colorize-results/test_coffee.json index 7c72176431d..9647307f073 100644 --- a/extensions/coffeescript/test/colorize-results/test_coffee.json +++ b/extensions/coffeescript/test/colorize-results/test_coffee.json @@ -1555,7 +1555,7 @@ "t": "source.coffee string.regexp.multiline.coffee keyword.control.anchor.regexp", "r": { "dark_plus": "keyword.control.anchor.regexp: #DCDCAA", - "light_plus": "keyword.control.anchor.regexp: #FF0000", + "light_plus": "keyword.control.anchor.regexp: #EE0000", "dark_vs": "keyword.control: #569CD6", "light_vs": "keyword.control: #0000FF", "hc_black": "keyword.control: #C586C0" @@ -1605,4 +1605,4 @@ "hc_black": "string.regexp: #D16969" } } -] +] \ No newline at end of file diff --git a/extensions/configuration-editing/.vscodeignore b/extensions/configuration-editing/.vscodeignore index f12c396fd04..de8e6dc5913 100644 --- a/extensions/configuration-editing/.vscodeignore +++ b/extensions/configuration-editing/.vscodeignore @@ -3,4 +3,5 @@ src/** tsconfig.json out/** extension.webpack.config.js +extension-browser.webpack.config.js yarn.lock diff --git a/src/vs/code/electron-browser/processExplorer/processExplorer.js b/extensions/configuration-editing/extension-browser.webpack.config.js similarity index 57% rename from src/vs/code/electron-browser/processExplorer/processExplorer.js rename to extensions/configuration-editing/extension-browser.webpack.config.js index d05816f9915..de941625373 100644 --- a/src/vs/code/electron-browser/processExplorer/processExplorer.js +++ b/extensions/configuration-editing/extension-browser.webpack.config.js @@ -4,10 +4,18 @@ *--------------------------------------------------------------------------------------------*/ //@ts-check + 'use strict'; -const bootstrapWindow = require('../../../../bootstrap-window'); +const withBrowserDefaults = require('../shared.webpack.config').browser; + +module.exports = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/configurationEditingMain.ts' + }, + output: { + filename: 'configurationEditingMain.js' + } +}); -bootstrapWindow.load(['vs/code/electron-browser/processExplorer/processExplorerMain'], function (processExplorer, configuration) { - processExplorer.startup(configuration.data); -}, { forceEnableDeveloperKeybindings: true }); \ No newline at end of file diff --git a/extensions/configuration-editing/extension.webpack.config.js b/extensions/configuration-editing/extension.webpack.config.js index b474e65cbb1..1b18dd86a99 100644 --- a/extensions/configuration-editing/extension.webpack.config.js +++ b/extensions/configuration-editing/extension.webpack.config.js @@ -12,7 +12,10 @@ const withDefaults = require('../shared.webpack.config'); module.exports = withDefaults({ context: __dirname, entry: { - extension: './src/extension.ts', + extension: './src/configurationEditingMain.ts', + }, + output: { + filename: 'configurationEditingMain.js' }, resolve: { mainFields: ['module', 'main'] diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index f929c260fd5..3cadb80fd33 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -12,7 +12,8 @@ "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" @@ -53,7 +54,7 @@ "url": "vscode://schemas/keybindings" }, { - "fileMatch": "vscode://defaultsettings/*/*.json", + "fileMatch": "vscode://defaultsettings/defaultSettings.json", "url": "vscode://schemas/settings/default" }, { @@ -116,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 015e32e8018..fc53bb896f1 100644 --- a/extensions/configuration-editing/schemas/attachContainer.schema.json +++ b/extensions/configuration-editing/schemas/attachContainer.schema.json @@ -42,8 +42,27 @@ "description": "An array of extensions that should be installed into the container.", "items": { "type": "string", - "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)$", - "errorMessage": "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'." + "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." + } + }, + "userEnvProbe": { + "type": "string", + "enum": [ + "none", + "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 5a69d57057b..999a50e7788 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.json @@ -17,8 +17,8 @@ "description": "An array of extensions that should be installed into the container.", "items": { "type": "string", - "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)$", - "errorMessage": "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'." + "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." } }, "settings": { @@ -68,9 +68,42 @@ "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", + "loginInteractiveShell", + "interactiveShell" + ], + "description": "User environment probe to run. The default is none." + }, + "codespaces": { + "type": "object", + "description": "Codespaces-specific configuration." } } }, diff --git a/extensions/configuration-editing/src/extension.ts b/extensions/configuration-editing/src/configurationEditingMain.ts similarity index 100% rename from extensions/configuration-editing/src/extension.ts rename to extensions/configuration-editing/src/configurationEditingMain.ts diff --git a/extensions/configuration-editing/src/settingsDocumentHelper.ts b/extensions/configuration-editing/src/settingsDocumentHelper.ts index 4a7f80c2a2a..dfd6100023f 100644 --- a/extensions/configuration-editing/src/settingsDocumentHelper.ts +++ b/extensions/configuration-editing/src/settingsDocumentHelper.ts @@ -223,7 +223,7 @@ 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); + 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/cpp/cgmanifest.json b/extensions/cpp/cgmanifest.json index 1690f2220d1..070312a63e4 100644 --- a/extensions/cpp/cgmanifest.json +++ b/extensions/cpp/cgmanifest.json @@ -19,7 +19,7 @@ "git": { "name": "textmate/c.tmbundle", "repositoryUrl": "https://github.com/textmate/c.tmbundle", - "commitHash": "9aa365882274ca52f01722f3dbb169b9539a20ee" + "commitHash": "60daf83b9d45329524f7847a75e9298b3aae5805" } }, "licenseDetail": [ @@ -42,4 +42,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/cpp/syntaxes/platform.tmLanguage.json b/extensions/cpp/syntaxes/platform.tmLanguage.json index 14fb45016c0..0147e6629a2 100644 --- a/extensions/cpp/syntaxes/platform.tmLanguage.json +++ b/extensions/cpp/syntaxes/platform.tmLanguage.json @@ -4,23 +4,131 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/textmate/c.tmbundle/commit/9aa365882274ca52f01722f3dbb169b9539a20ee", + "version": "https://github.com/textmate/c.tmbundle/commit/60daf83b9d45329524f7847a75e9298b3aae5805", "name": "Platform", "scopeName": "source.c.platform", - "comment": "This file was generated with clang-C using MacOSX10.12.sdk", + "comment": "This file was generated with clang-C using MacOSX10.15.sdk", "patterns": [ + { + "match": "\\bkAudioUnitSubType_3DMixer\\b", + "name": "invalid.deprecated.10.10.support.constant.c" + }, { "match": "\\bkCF(?:CalendarUnitWeek|Gregorian(?:AllUnits|Units(?:Days|Hours|M(?:inutes|onths)|Seconds|Years)))\\b", "name": "invalid.deprecated.10.10.support.constant.cf.c" }, + { + "match": "\\bLS(?:ApplicationParameters|LaunchFSRefSpec)\\b", + "name": "invalid.deprecated.10.10.support.type.c" + }, { "match": "\\bCFGregorian(?:Date|Units)\\b", "name": "invalid.deprecated.10.10.support.type.cf.c" }, { - "match": "\\bkCFURL(?:CustomIconKey|EffectiveIconKey|LabelColorKey)\\b", + "match": "\\bkLSItem(?:ContentType|Display(?:Kind|Name)|Extension(?:IsHidden)?|File(?:Creator|Type)|IsInvisible|QuarantineProperties|RoleHandlerDisplayName)\\b", + "name": "invalid.deprecated.10.10.support.variable.c" + }, + { + "match": "\\bk(?:AudioUnitProperty_(?:3DMixer(?:AttenuationCurve|Distance(?:Atten|Params)|RenderingFlags)|D(?:istanceAttenuationData|opplerShift)|ReverbPreset)|CT(?:Adobe(?:CNS1CharacterCollection|GB1CharacterCollection|Japan(?:1CharacterCollection|2CharacterCollection)|Korea1CharacterCollection)|CenterTextAlignment|Font(?:A(?:lertHeaderFontType|pplicationFontType)|ControlContentFontType|DefaultOrientation|EmphasizedSystem(?:DetailFontType|FontType)|HorizontalOrientation|LabelFontType|M(?:e(?:nu(?:Item(?:CmdKeyFontType|FontType|MarkFontType)|TitleFontType)|ssageFontType)|ini(?:EmphasizedSystemFontType|SystemFontType))|NoFontType|P(?:aletteFontType|ushButtonFontType)|S(?:mall(?:EmphasizedSystemFontType|SystemFontType|ToolbarFontType)|ystem(?:DetailFontType|FontType))|Tool(?:TipFontType|barFontType)|U(?:serF(?:ixedPitchFontType|ontType)|tilityWindowTitleFontType)|V(?:erticalOrientation|iewsFontType)|WindowTitleFontType)|IdentityMappingCharacterCollection|JustifiedTextAlignment|LeftTextAlignment|NaturalTextAlignment|RightTextAlignment)|LS(?:HandlerOptions(?:Default|IgnoreCreator)|ItemInfo(?:App(?:IsScriptable|Prefers(?:Classic|Native))|ExtensionIsHidden|Is(?:A(?:liasFile|pplication)|C(?:lassicApp|ontainer)|Invisible|NativeApp|P(?:ackage|lainFile)|Symlink|Volume))|Launch(?:InClassic|StartClassic)|Request(?:A(?:ll(?:Flags|Info)|ppTypeFlags)|BasicFlagsOnly|Extension(?:FlagsOnly)?|IconAndKind|TypeCreator)))\\b", + "name": "invalid.deprecated.10.11.support.constant.c" + }, + { + "match": "\\b(?:AUDistanceAttenuationData|LSItemInfoRecord)\\b", + "name": "invalid.deprecated.10.11.support.type.c" + }, + { + "match": "\\bk(?:C(?:F(?:FTPResource(?:Group|Link|Mod(?:Date|e)|Name|Owner|Size|Type)|Stream(?:NetworkServiceTypeVoIP|Property(?:FTP(?:AttemptPersistentConnection|F(?:etchResourceInfo|ileTransferOffset)|P(?:assword|roxy(?:Host|P(?:assword|ort)|User)?)|ResourceSize|Use(?:PassiveMode|rName))|HTTP(?:AttemptPersistentConnection|Final(?:Request|URL)|Proxy(?:Host|Port)?|Re(?:questBytesWrittenCount|sponseHeader)|S(?:Proxy(?:Host|Port)|houldAutoredirect)))))|GImagePropertyExifSubsecTimeOrginal|TCharacterShapeAttributeName)|IOSurfaceIsGlobal|LSSharedFileList(?:Favorite(?:Items|Volumes)|Item(?:BeforeFirst|Hidden|Last)|LoginItemHidden|Recent(?:ApplicationItems|DocumentItems|ItemsMaxAmount|ServerItems)|SessionLoginItems|Volumes(?:ComputerVisible|NetworkVisible))|SecUseNoAuthenticationUI)\\b", + "name": "invalid.deprecated.10.11.support.variable.c" + }, + { + "match": "\\bkLSLaunch(?:HasUntrustedContents|InhibitBGOnly|NoParams)\\b", + "name": "invalid.deprecated.10.12.support.constant.c" + }, + { + "match": "\\bOSSpinLock\\b", + "name": "invalid.deprecated.10.12.support.type.c" + }, + { + "match": "\\bkSC(?:EntNetPPTP|NetworkInterfaceTypePPTP|ValNetInterfaceSubTypePPTP)\\b", + "name": "invalid.deprecated.10.12.support.variable.c" + }, + { + "match": "\\bkCF(?:StreamSocketSecurityLevelSSLv(?:2|3)|URL(?:CustomIconKey|EffectiveIconKey|LabelColorKey))\\b", "name": "invalid.deprecated.10.12.support.variable.cf.c" }, + { + "match": "\\bk(?:C(?:FNetDiagnostic(?:Connection(?:Down|Indeterminate|Up)|Err|NoErr)|TFontManagerAutoActivationPromptUser)|SecAccessControlTouchID(?:Any|CurrentSet))\\b", + "name": "invalid.deprecated.10.13.support.constant.c" + }, + { + "match": "\\bCFNetDiagnosticStatus\\b", + "name": "invalid.deprecated.10.13.support.type.c" + }, + { + "match": "\\bkS(?:CPropNetInterfaceSupportsModemOnHold|SLSessionConfig_(?:3DES_fallback|RC4_fallback|TLSv1_(?:3DES_fallback|RC4_fallback)|default)|ecTrustCertificateTransparencyWhiteList)\\b", + "name": "invalid.deprecated.10.13.support.variable.c" + }, + { + "match": "\\bk(?:CVOpenGL(?:Buffer(?:Height|InternalFormat|MaximumMipmapLevel|PoolM(?:aximumBufferAgeKey|inimumBufferCountKey)|Target|Width)|TextureCacheChromaSamplingMode(?:Automatic|BestPerformance|HighestQuality|Key))|SecAttrAccessibleAlways(?:ThisDeviceOnly)?)\\b", + "name": "invalid.deprecated.10.14.support.variable.c" + }, + { + "match": "\\bk(?:DTLSProtocol1(?:2)?|S(?:SL(?:Aborted|C(?:l(?:ient(?:Cert(?:None|Re(?:jected|quested)|Sent)|Side)|osed)|onnected)|DatagramType|Handshake|Idle|Protocol(?:2|3(?:Only)?|All|Unknown)|S(?:e(?:rverSide|ssionOption(?:Allow(?:Renegotiation|ServerIdentityChange)|BreakOn(?:C(?:ertRequested|lient(?:Auth|Hello))|ServerAuth)|EnableSessionTickets|Fal(?:lback|seStart)|SendOneByteRecord))|treamType))|ecDataAccessEvent(?:Mask)?)|TLSProtocol(?:1(?:1|2|3|Only)?|MaxSupported))\\b", + "name": "invalid.deprecated.10.15.support.constant.c" + }, + { + "match": "\\bk(?:MIDIPropertyNameConfiguration|S(?:CPropNetPPP(?:AuthEAPPlugins|Plugins)|SLSessionConfig_(?:ATSv1(?:_noPFS)?|TLSv1_fallback|anonymous|legacy(?:_DHE)?|standard)))\\b", + "name": "invalid.deprecated.10.15.support.variable.c" + }, + { + "match": "\\bkCGColorSpace(?:DisplayP3_PQ_EOTF|ITUR_2020_PQ_EOTF)\\b", + "name": "invalid.deprecated.10.16.support.variable.quartz.c" + }, + { + "match": "\\bkMIDIProperty(?:FactoryPatchNameFile|UserPatchNameFile)\\b", + "name": "invalid.deprecated.10.2.support.variable.c" + }, + { + "match": "\\bkCFNetServiceFlagIsRegistrationDomain\\b", + "name": "invalid.deprecated.10.4.support.constant.c" + }, + { + "match": "\\bk(?:MDItemFS(?:Exists|Is(?:Readable|Writeable))|S(?:CPropUsersConsoleUser(?:GID|Name|UID)|KLanguageTypes))\\b", + "name": "invalid.deprecated.10.4.support.variable.c" + }, + { + "match": "\\bkMDItemSupportFileType\\b", + "name": "invalid.deprecated.10.5.support.variable.c" + }, + { + "match": "\\b(?:CM(?:2(?:Header|ProfileHandle)|4Header|A(?:daptationMatrixType|ppleProfileHeader)|B(?:itmap(?:C(?:allBack(?:ProcPtr|UPP)|olorSpace))?|ufferLocation)|C(?:MY(?:Color|KColor)|hromaticAdaptation|o(?:lor|ncat(?:CallBack(?:ProcPtr|UPP)|ProfileSet))|urveType)|D(?:at(?:aType|eTime(?:Type)?)|evice(?:I(?:D|nfoPtr)|Profile(?:ArrayPtr|I(?:D|nfo)|Scope)|State)|isplayIDType)|F(?:ixedXY(?:Color|ZColor)|loatBitmap)|GrayColor|H(?:LSColor|SVColor|andleLocation)|I(?:ntentCRDVMSize|terateDevice(?:InfoProcPtr|ProfileProcPtr))|L(?:ab(?:Color|ToLabProcPtr)|u(?:t(?:16Type|8Type)|vColor))|M(?:I(?:nfo|terate(?:ProcPtr|UPP))|akeAndModel(?:Type)?|easurementType|ulti(?:Funct(?:CLUTType|LutB2AType)|LocalizedUniCode(?:EntryRec|Type)|channel(?:5Color|6Color|7Color|8Color)))|Na(?:medColor(?:2(?:EntryType|Type)|Type)?|tiveDisplayInfo(?:Type)?)|P(?:S2CRDVMSizeType|a(?:rametricCurveType|thLocation)|rof(?:Loc|ile(?:Iterate(?:Data|ProcPtr|UPP)|Location|MD5(?:Ptr)?|Ref|SequenceDescType)))|RGBColor|S(?:15Fixed16ArrayType|creening(?:ChannelRec|Type)|ignatureType)|T(?:ag(?:ElemTable|Record)|ext(?:DescriptionType|Type))|U(?:16Fixed16ArrayType|Int(?:16ArrayType|32ArrayType|64ArrayType|8ArrayType)|crBgType|nicodeTextType)|Vi(?:deoCardGamma(?:Formula|T(?:able|ype))?|ewingConditionsType)|WorldRef|XYZType|YxyColor)|NCM(?:ConcatProfileS(?:et|pec)|DeviceProfileInfo))\\b", + "name": "invalid.deprecated.10.6.support.type.c" + }, + { + "match": "\\bkC(?:FStream(?:PropertySSLPeerCertificates|SSLAllows(?:AnyRoot|Expired(?:Certificates|Roots)))|VImageBufferTransferFunction_(?:EBU_3213|SMPTE_C))\\b", + "name": "invalid.deprecated.10.6.support.variable.c" + }, + { + "match": "\\b(?:AudioFileFDFTable(?:Extended)?|C(?:E_(?:A(?:ccessDescription|uthority(?:InfoAccess|KeyID))|BasicConstraints|C(?:RLDist(?:PointsSyntax|ributionPoint)|ertPolicies|rl(?:Dist(?:ReasonFlags|ributionPointNameType)|Reason))|D(?:ata(?:AndType)?|istributionPointName)|General(?:Name(?:s)?|Subtree(?:s)?)|I(?:nhibitAnyPolicy|ssuingDistributionPoint)|KeyUsage|N(?:ame(?:Constraints|RegistrationAuthorities)|etscapeCertType)|OtherName|Policy(?:Constraints|Information|Mapping(?:s)?|QualifierInfo)|QC_Statement(?:s)?|S(?:emanticsInformation|ubjectKeyID))|SSM_(?:A(?:C(?:CESS_CREDENTIALS(?:_PTR)?|L_(?:E(?:DIT(?:_PTR)?|NTRY_(?:IN(?:FO(?:_PTR)?|PUT(?:_PTR)?)|PROTOTYPE(?:_PTR)?))|OWNER_PROTOTYPE(?:_PTR)?|SUBJECT_CALLBACK|VALIDITY_PERIOD(?:_PTR)?))|PI_M(?:EMORY_FUNCS(?:_PTR)?|oduleEventHandler)|UTHORIZATIONGROUP(?:_PTR)?)|BASE_CERTS(?:_PTR)?|C(?:ALLBACK|ERT(?:GROUP(?:_PTR)?|_(?:BUNDLE(?:_(?:HEADER(?:_PTR)?|PTR))?|PAIR(?:_PTR)?))|HALLENGE_CALLBACK|ONTEXT(?:_(?:ATTRIBUTE(?:_PTR)?|PTR))?|R(?:L(?:GROUP(?:_PTR)?|_PAIR(?:_PTR)?)|YPTO_DATA(?:_PTR)?)|SP_OPERATIONAL_STATISTICS(?:_PTR)?)|D(?:AT(?:A_PTR|E(?:_PTR)?)|B(?:INFO(?:_PTR)?|_(?:ATTRIBUTE_(?:DATA(?:_PTR)?|INFO(?:_PTR)?)|INDEX_INFO(?:_PTR)?|PARSING_MODULE_INFO(?:_PTR)?|RECORD_(?:ATTRIBUTE_(?:DATA(?:_PTR)?|INFO(?:_PTR)?)|INDEX_INFO(?:_PTR)?)|SCHEMA_(?:ATTRIBUTE_INFO(?:_PTR)?|INDEX_INFO(?:_PTR)?)|UNIQUE_RECORD(?:_PTR)?))|L_DB_(?:HANDLE(?:_PTR)?|LIST(?:_PTR)?))|E(?:NCODED_C(?:ERT(?:_PTR)?|RL(?:_PTR)?)|VIDENCE(?:_PTR)?)|F(?:IELD(?:GROUP(?:_PTR)?|_PTR)?|UNC_NAME_ADDR(?:_PTR)?)|GUID(?:_PTR)?|K(?:E(?:A_DERIVE_PARAMS(?:_PTR)?|Y(?:HEADER(?:_PTR)?|_(?:PTR|SIZE(?:_PTR)?))?)|R(?:SUBSERVICE(?:_PTR)?|_(?:NAME|P(?:OLICY_(?:INFO(?:_PTR)?|LIST_ITEM(?:_PTR)?)|ROFILE(?:_PTR)?)|WRAPPEDPRODUCT_INFO(?:_PTR)?)))|LIST(?:_(?:ELEMENT|PTR))?|M(?:ANAGER_(?:EVENT_NOTIFICATION(?:_PTR)?|REGISTRATION_INFO(?:_PTR)?)|EMORY_FUNCS(?:_PTR)?|ODULE_FUNCS(?:_PTR)?)|N(?:AME_LIST(?:_PTR)?|ET_ADDRESS(?:_PTR)?)|OID_PTR|P(?:ARSED_C(?:ERT(?:_PTR)?|RL(?:_PTR)?)|KCS(?:1_OAEP_PARAMS(?:_PTR)?|5_PBKDF(?:1_PARAMS(?:_PTR)?|2_PARAMS(?:_PTR)?)))|QUERY(?:_(?:LIMITS(?:_PTR)?|PTR|SIZE_DATA(?:_PTR)?))?|R(?:ANGE(?:_PTR)?|ESOURCE_CONTROL_CONTEXT(?:_PTR)?)|S(?:AMPLE(?:GROUP(?:_PTR)?|_PTR)?|ELECTION_PREDICATE(?:_PTR)?|PI_(?:AC_FUNCS(?:_PTR)?|C(?:L_FUNCS(?:_PTR)?|SP_FUNCS(?:_PTR)?)|DL_FUNCS(?:_PTR)?|KR_FUNCS(?:_PTR)?|ModuleEventHandler|TP_FUNCS(?:_PTR)?)|TATE_FUNCS(?:_PTR)?|UBSERVICE_UID(?:_PTR)?)|T(?:P_(?:A(?:PPLE_EVIDENCE_INFO|UTHORITY_ID(?:_PTR)?)|C(?:ALLERAUTH_CONTEXT(?:_PTR)?|ERT(?:CHANGE_(?:INPUT(?:_PTR)?|OUTPUT(?:_PTR)?)|ISSUE_(?:INPUT(?:_PTR)?|OUTPUT(?:_PTR)?)|NOTARIZE_(?:INPUT(?:_PTR)?|OUTPUT(?:_PTR)?)|RECLAIM_(?:INPUT(?:_PTR)?|OUTPUT(?:_PTR)?)|VERIFY_(?:INPUT(?:_PTR)?|OUTPUT(?:_PTR)?))|ONFIRM_RESPONSE(?:_PTR)?|RLISSUE_(?:INPUT(?:_PTR)?|OUTPUT(?:_PTR)?))|POLICYINFO(?:_PTR)?|RE(?:QUEST_SET(?:_PTR)?|SULT_SET(?:_PTR)?)|VERIF(?:ICATION_RESULTS_CALLBACK|Y_CONTEXT(?:_(?:PTR|RESULT(?:_PTR)?))?))|UPLE(?:GROUP(?:_PTR)?|_PTR)?)|UPCALLS(?:_(?:CALLOC|FREE|MALLOC|PTR|REALLOC))?|VERSION(?:_PTR)?|WRAP_KEY(?:_PTR)?|X509(?:EXT_(?:BASICCONSTRAINTS(?:_PTR)?|P(?:AIR(?:_PTR)?|OLICY(?:INFO(?:_PTR)?|QUALIFIER(?:INFO(?:_PTR)?|S(?:_PTR)?)))|TAGandVALUE(?:_PTR)?)|_(?:ALGORITHM_IDENTIFIER_PTR|EXTENSION(?:S(?:_PTR)?|_PTR)?|NAME(?:_PTR)?|R(?:DN(?:_PTR)?|EVOKED_CERT_(?:ENTRY(?:_PTR)?|LIST(?:_PTR)?))|S(?:IGN(?:ATURE(?:_PTR)?|ED_C(?:ERTIFICATE(?:_PTR)?|RL(?:_PTR)?))|UBJECT_PUBLIC_KEY_INFO_PTR)|T(?:BS_CERT(?:IFICATE(?:_PTR)?|LIST(?:_PTR)?)|IME(?:_PTR)?|YPE_VALUE_PAIR(?:_PTR)?)|VALIDITY(?:_PTR)?))))|MDS_(?:DB_HANDLE|FUNCS(?:_PTR)?)|cssm_(?:ac(?:cess_credentials|l_(?:e(?:dit|ntry_(?:in(?:fo|put)|prototype))|owner_prototype|validity_period))|base_certs|c(?:ert(?:_(?:bundle(?:_header)?|pair)|group)|ontext(?:_attribute)?|r(?:l(?:_pair|group)|ypto_data))|d(?:b(?:_(?:attribute_(?:data|info)|index_info|parsing_module_info|record_(?:attribute_(?:data|info)|index_info)|schema_attribute_info|unique_record)|info)|l_db_list)|e(?:ncoded_c(?:ert|rl)|vidence)|field(?:group)?|k(?:e(?:a_derive_params|y(?:header)?)|r(?:_(?:p(?:olicy_(?:info|list_item)|rofile)|wrappedproductinfo)|subservice))|list_element|m(?:anager_(?:event_notification|registration_info)|odule_funcs)|net_address|pkcs(?:1_oaep_params|5_pbkdf(?:1_params|2_params))|query(?:_limits)?|resource_control_context|s(?:ample(?:group)?|election_predicate|pi_(?:ac_funcs|c(?:l_funcs|sp_funcs)|dl_funcs|kr_funcs|tp_funcs)|tate_funcs|ubservice_uid)|t(?:p_(?:authority_id|c(?:allerauth_context|ert(?:change_(?:input|output)|issue_(?:input|output)|notarize_(?:input|output)|reclaim_(?:input|output)|verify_(?:input|output))|onfirm_response|rlissue_(?:input|output))|policyinfo|request_set|verify_context(?:_result)?)|uplegroup)|upcalls|x509(?:_(?:extension(?:TagAndValue|s)?|name|r(?:dn|evoked_cert_(?:entry|list))|sign(?:ature|ed_c(?:ertificate|rl))|t(?:bs_cert(?:ificate|list)|ime|ype_value_pair))|ext_(?:basicConstraints|p(?:air|olicy(?:Info|Qualifier(?:Info|s))))))|mds_funcs|x509_validity)\\b", + "name": "invalid.deprecated.10.7.support.type.c" + }, + { + "match": "\\b(?:CSSMOID_(?:A(?:D(?:C_CERT_POLICY|_(?:CA_(?:ISSUERS|REPOSITORY)|OCSP|TIME_STAMPING))|NSI_(?:DH_(?:EPHEM(?:_SHA1)?|HYBRID(?:1(?:_SHA1)?|2(?:_SHA1)?|_ONEFLOW)|ONE_FLOW(?:_SHA1)?|PUB_NUMBER|STATIC(?:_SHA1)?)|MQV(?:1(?:_SHA1)?|2(?:_SHA1)?))|PPLE(?:ID_(?:CERT_POLICY|SHARING_CERT_POLICY)|_(?:ASC|CERT_POLICY|E(?:CDSA|KU_(?:CODE_SIGNING(?:_DEV)?|ICHAT_(?:ENCRYPTION|SIGNING)|P(?:ASSBOOK_SIGNING|ROFILE_SIGNING)|QA_PROFILE_SIGNING|RESOURCE_SIGNING|SYSTEM_IDENTITY)|XTENSION(?:_(?:A(?:AI_INTERMEDIATE|DC_(?:APPLE_SIGNING|DEV_SIGNING)|PPLE(?:ID_(?:INTERMEDIATE|SHARING)|_SIGNING))|CODE_SIGNING|DEVELOPER_AUTHENTICATION|ESCROW_SERVICE|I(?:NTERMEDIATE_MARKER|TMS_INTERMEDIATE)|MACAPPSTORE_RECEIPT|P(?:ASSBOOK_SIGNING|ROVISIONING_PROFILE_SIGNING)|S(?:ERVER_AUTHENTICATION|YSINT2_INTERMEDIATE)|WWDR_INTERMEDIATE))?)|FEE(?:D(?:EXP)?|_(?:MD5|SHA1))?|ISIGN|TP_(?:APPLEID_SHARING|C(?:ODE_SIGN(?:ING)?|SR_GEN)|E(?:AP|SCROW_SERVICE)|I(?:CHAT|P_SEC)|LOCAL_CERT_GEN|M(?:ACAPPSTORE_RECEIPT|OBILE_STORE)|P(?:A(?:CKAGE_SIGNING|SSBOOK_SIGNING)|CS_ESCROW_SERVICE|KINIT_(?:CLIENT|SERVER)|RO(?:FILE_SIGNING|VISIONING_PROFILE_SIGNING))|QA_PROFILE_SIGNING|RE(?:SOURCE_SIGN|VOCATION(?:_(?:CRL|OCSP))?)|S(?:MIME|SL|W_UPDATE_SIGNING)|T(?:EST_MOBILE_STORE|IMESTAMPING))|X509_BASIC))|liasedEntryName|uthority(?:InfoAccess|KeyIdentifier|RevocationList))|B(?:asicConstraints|iometricInfo|usinessCategory)|C(?:ACertificate|SSMKeyStruct|ert(?:Issuer|i(?:com(?:EllCurve)?|ficate(?:Policies|RevocationList)))|hallengePassword|lientAuth|o(?:llective(?:FacsimileTelephoneNumber|InternationalISDNNumber|Organization(?:Name|alUnitName)|P(?:hysicalDeliveryOfficeName|ost(?:OfficeBox|al(?:Address|Code)))|St(?:ateProvinceName|reetAddress)|Tele(?:phoneNumber|x(?:Number|TerminalIdentifier)))|mmonName|ntentType|unt(?:erSignature|ryName))|r(?:l(?:DistributionPoints|Number|Reason)|ossCertificatePair))|D(?:ES_CBC|H|NQualifier|OTMAC_CERT(?:_(?:E(?:MAIL_(?:ENCRYPT|SIGN)|XTENSION)|IDENTITY|POLICY|REQ(?:_(?:ARCHIVE_(?:FETCH|LIST|REMOVE|STORE)|EMAIL_(?:ENCRYPT|SIGN)|IDENTITY|SHARED_SERVICES|VALUE_(?:ASYNC|HOSTNAME|IS_PENDING|PASSWORD|RENEW|USERNAME)))?))?|SA(?:_(?:CMS|JDK))?|e(?:ltaCrlIndicator|s(?:cription|tinationIndicator))|istinguishedName|omainComponent)|E(?:CDSA_WithS(?:HA(?:1|2(?:24|56)|384|512)|pecified)|KU_IPSec|TSI_QCS_QC_(?:COMPLIANCE|LIMIT_VALUE|RETENTION|SSCD)|mail(?:Address|Protection)|nhancedSearchGuide|xtended(?:CertificateAttributes|KeyUsage(?:Any)?|UseCodeSigning))|FacsimileTelephoneNumber|G(?:enerationQualifier|ivenName)|Ho(?:ldInstructionCode|useIdentifier)|I(?:n(?:hibitAnyPolicy|itials|ternationalISDNNumber|validityDate)|ssu(?:erAltName|ingDistributionPoint(?:s)?))|K(?:ERBv5_PKINIT_(?:AUTH_DATA|DH_KEY_DATA|KP_(?:CLIENT_AUTH|KDC)|RKEY_DATA)|eyUsage|nowledgeInformation)|LocalityName|M(?:ACAPPSTORE_(?:CERT_POLICY|RECEIPT_CERT_POLICY)|D(?:2(?:WithRSA)?|4(?:WithRSA)?|5(?:WithRSA)?)|OBILE_STORE_SIGNING_POLICY|e(?:mber|ssageDigest)|icrosoftSGC)|N(?:ame(?:Constraints)?|etscape(?:Cert(?:Sequence|Type)|SGC))|O(?:AEP_(?:ID_PSPECIFIED|MGF1)|CSPSigning|ID_QCS_SYNTAX_V(?:1|2)|bjectClass|rganization(?:Name|alUnitName)|wner)|P(?:DA_(?:COUNTRY_(?:CITIZEN|RESIDENCE)|DATE_OF_BIRTH|GENDER|PLACE_OF_BIRTH)|K(?:CS(?:12_(?:c(?:ertBag|rlBag)|keyBag|pbe(?:WithSHAAnd(?:128BitRC(?:2CBC|4)|2Key3DESCBC|3Key3DESCBC|40BitRC4)|withSHAAnd40BitRC2CBC)|s(?:afeContentsBag|ecretBag|hroudedKeyBag))|3|5_(?:D(?:ES_EDE3_CBC|IGEST_ALG)|ENCRYPT_ALG|HMAC_SHA1|PB(?:ES2|KDF2|MAC1)|RC(?:2_CBC|5_CBC)|pbeWith(?:MD(?:2And(?:DES|RC2)|5And(?:DES|RC2))|SHA1And(?:DES|RC2)))|7_(?:D(?:ata(?:WithAttributes)?|igestedData)|En(?:crypted(?:Data|PrivateKeyInfo)|velopedData)|Signed(?:AndEnvelopedData|Data))|9_(?:C(?:ertTypes|rlTypes)|FriendlyName|Id_Ct_TSTInfo|LocalKeyId|SdsiCertificate|TimeStampToken|X509C(?:ertificate|rl)))|IX_OCSP(?:_(?:ARCHIVE_CUTOFF|BASIC|CRL|NO(?:CHECK|NCE)|RESPONSE|SERVICE_LOCATOR))?)|hysicalDeliveryOfficeName|o(?:licy(?:Constraints|Mappings)|st(?:OfficeBox|al(?:Address|Code)))|r(?:e(?:ferredDeliveryMethod|sentationAddress)|ivateKeyUsagePeriod|otocolInformation))|Q(?:C_Statements|T_(?:CPS|UNOTICE))|R(?:SA(?:WithOAEP)?|egisteredAddress|oleOccupant)|S(?:HA(?:1(?:With(?:DSA(?:_(?:CMS|JDK))?|RSA(?:_OIW)?))?|2(?:24(?:WithRSA)?|56(?:WithRSA)?)|384(?:WithRSA)?|512(?:WithRSA)?)|e(?:archGuide|eAlso|r(?:ialNumber|verAuth))|igningTime|t(?:ateProvinceName|reetAddress)|u(?:bject(?:AltName|DirectoryAttributes|EmailAddress|InfoAccess|KeyIdentifier|Picture|SignatureBitmap)|pportedApplicationContext|rname))|T(?:EST_MOBILE_STORE_SIGNING_POLICY|ele(?:phoneNumber|x(?:Number|TerminalIdentifier))|i(?:meStamping|tle))|U(?:n(?:ique(?:Identifier|Member)|structured(?:Address|Name))|se(?:Exemptions|r(?:Certificate|ID|Password)))|X(?:509V(?:1(?:C(?:RL(?:Issuer(?:Name(?:CStruct|LDAP)|Struct)|N(?:extUpdate|umberOfRevokedCertEntries)|Revoked(?:Certificates(?:CStruct|Struct)|Entry(?:CStruct|RevocationDate|S(?:erialNumber|truct)))|ThisUpdate)|ertificate(?:IssuerUniqueId|SubjectUniqueId))|IssuerName(?:CStruct|LDAP|Std)?|S(?:erialNumber|ignature(?:Algorithm(?:Parameters|TBS)?|CStruct|Struct)?|ubject(?:Name(?:CStruct|LDAP|Std)?|PublicKey(?:Algorithm(?:Parameters)?|CStruct)?))|V(?:alidityNot(?:After|Before)|ersion))|2CRL(?:AllExtensions(?:CStruct|Struct)|Extension(?:Critical|Id|Type)|NumberOfExtensions|RevokedEntry(?:AllExtensions(?:CStruct|Struct)|Extension(?:Critical|Id|Type|Value)|NumberOfExtensions|SingleExtension(?:CStruct|Struct))|Si(?:gnedCrl(?:CStruct|Struct)|ngleExtension(?:CStruct|Struct))|TbsCertList(?:CStruct|Struct)|Version)|3(?:Certificate(?:CStruct|Extension(?:C(?:Struct|ritical)|Id|Struct|Type|Value|s(?:CStruct|Struct))|NumberOfExtensions)?|SignedCertificate(?:CStruct)?))|9_62(?:_(?:C_TwoCurve|EllCurve|FieldType|P(?:rimeCurve|ubKeyType)|SigType))?|_121Address)|ecPublicKey|sec(?:p(?:1(?:12r(?:1|2)|28r(?:1|2)|60(?:k1|r(?:1|2))|92(?:k1|r1))|2(?:24(?:k1|r1)|56(?:k1|r1))|384r1|521r1)|t(?:1(?:13r(?:1|2)|31r(?:1|2)|63(?:k1|r(?:1|2))|93r(?:1|2))|2(?:3(?:3(?:k1|r1)|9k1)|83(?:k1|r1))|409(?:k1|r1)|571(?:k1|r1))))|k(?:MDItemLabel(?:I(?:D|con)|Kind|UUID)|SCPropNetSMBNetBIOSScope))\\b", + "name": "invalid.deprecated.10.7.support.variable.c" + }, + { + "match": "\\b(?:false32b|gestaltSystemVersion(?:BugFix|M(?:ajor|inor))?|kCT(?:FontTableOptionExcludeSynthetic|ParagraphStyleSpecifierLineSpacing)|true32b)\\b", + "name": "invalid.deprecated.10.8.support.constant.c" + }, + { + "match": "\\bWSMethodInvocation(?:CallBackProcPtr|DeserializationProcPtr|SerializationProcPtr)\\b", + "name": "invalid.deprecated.10.8.support.type.c" + }, + { + "match": "\\b(?:k(?:CTTypesetterOptionDisableBidiProcessing|FSOperation(?:Bytes(?:CompleteKey|RemainingKey)|Objects(?:CompleteKey|RemainingKey)|T(?:hroughputKey|otal(?:BytesKey|ObjectsKey|UserVisibleObjectsKey))|UserVisibleObjects(?:CompleteKey|RemainingKey))|LSSharedFileListVolumesIDiskVisible|WS(?:Debug(?:Incoming(?:Body|Headers)|Outgoing(?:Body|Headers))|Fault(?:Code|Extra|String)|HTTP(?:ExtraHeaders|FollowsRedirects|Message|Proxy|ResponseMessage|Version)|MethodInvocation(?:Result(?:ParameterName)?|TimeoutValue)|NetworkStreamFaultString|Record(?:NamespaceURI|ParameterOrder|Type)|S(?:OAP(?:1999Protocol|2001Protocol|BodyEncodingStyle|Me(?:ssageHeaders|thodNamespaceURI)|Style(?:Doc|RPC))|treamError(?:Domain|Error|Message))|XMLRPCProtocol))|pi)\\b", + "name": "invalid.deprecated.10.8.support.variable.c" + }, { "match": "\\bkCFURLUbiquitousItemPercent(?:DownloadedKey|UploadedKey)\\b", "name": "invalid.deprecated.10.8.support.variable.cf.c" @@ -29,6 +137,10 @@ "match": "\\bkCGWindowWorkspace\\b", "name": "invalid.deprecated.10.8.support.variable.quartz.c" }, + { + "match": "\\bk(?:AUVoiceIOProperty_VoiceProcessingQuality|Sec(?:AppleSharePasswordItemClass|TrustResultConfirm))\\b", + "name": "invalid.deprecated.10.9.support.constant.c" + }, { "match": "\\bkCFURL(?:BookmarkCreationPreferFileIDResolutionMask|HFSPathStyle|ImproperArgumentsError|PropertyKeyUnavailableError|Re(?:moteHostUnavailableError|source(?:AccessViolationError|NotFoundError))|TimeoutError|Unknown(?:Error|PropertyKeyError|SchemeError))\\b", "name": "invalid.deprecated.10.9.support.constant.cf.c" @@ -38,17 +150,57 @@ "name": "invalid.deprecated.10.9.support.constant.quartz.c" }, { - "match": "\\bCFURLError\\b", - "name": "invalid.deprecated.10.9.support.type.cf.c" + "match": "\\bSecTrustUserSetting\\b", + "name": "invalid.deprecated.10.9.support.type.c" }, { - "match": "\\bCGTextEncoding\\b", - "name": "invalid.deprecated.10.9.support.type.quartz.c" + "match": "\\b(?:XPC_ACTIVITY_REQUIRE_(?:BATTERY_LEVEL|HDD_SPINNING)|k(?:LSSharedFileListGlobalLoginItems|S(?:C(?:PropNetAirPort(?:A(?:llowNetCreation|uthPassword(?:Encryption)?)|JoinMode|P(?:owerEnabled|referredNetwork)|SavePasswords)|ValNetAirPort(?:AuthPasswordEncryptionKeychain|JoinMode(?:Automatic|Preferred|R(?:anked|ecent)|Strongest)))|ecPolicyAppleiChat)))\\b", + "name": "invalid.deprecated.10.9.support.variable.c" }, { "match": "\\bkCFURL(?:File(?:DirectoryContents|Exists|L(?:astModificationTime|ength)|OwnerID|POSIXMode)|HTTPStatus(?:Code|Line)|UbiquitousItemIsDownloadedKey)\\b", "name": "invalid.deprecated.10.9.support.variable.cf.c" }, + { + "match": "\\bk3DMixerParam_(?:GlobalReverbGain|M(?:axGain|inGain)|O(?:bstructionAttenuation|cclusionAttenuation)|ReverbBlend)\\b", + "name": "invalid.deprecated.tba.support.constant.c" + }, + { + "match": "\\bkQL(?:Preview(?:ContentIDScheme|OptionCursorKey|Property(?:Attachment(?:DataKey|sKey)|BaseBundlePathKey|CursorKey|DisplayNameKey|HeightKey|MIMETypeKey|PDFStyleKey|StringEncodingKey|WidthKey))|ThumbnailProperty(?:Ba(?:dgeImageKey|seBundlePathKey)|ExtensionKey))\\b", + "name": "invalid.deprecated.tba.support.variable.c" + }, + { + "match": "\\b(?:QOS_CLASS_(?:BACKGROUND|DEFAULT|U(?:NSPECIFIED|SER_IN(?:ITIATED|TERACTIVE)|TILITY))|k(?:C(?:GLCP(?:AbortOnGPURestartStatusBlacklisted|ContextPriorityRequest|GPURestartStatus|Support(?:GPURestart|SeparateAddressSpace))|TRuby(?:Alignment(?:Auto|Center|Distribute(?:Letter|Space)|End|Invalid|LineEdge|Start)|Overhang(?:Auto|End|Invalid|None|Start)|Position(?:After|Before|Count|In(?:line|terCharacter))))|FSEventStreamEventFlagItemIs(?:Hardlink|LastHardlink)|SecAccessControlUserPresence))\\b", + "name": "support.constant.10.10.c" + }, + { + "match": "\\bk(?:A(?:XValueType(?:AXError|C(?:FRange|G(?:Point|Rect|Size))|Illegal)|udioComponent(?:Flag_(?:CanLoadInProcess|IsV3AudioUnit|RequiresAsyncInstantiation)|Instantiation_Load(?:InProcess|OutOfProcess)))|SecAccessControlDevicePasscode)\\b", + "name": "support.constant.10.11.c" + }, + { + "match": "\\bkSec(?:AccessControl(?:A(?:nd|pplicationPassword)|Or|PrivateKeyUsage)|KeyOperationType(?:Decrypt|Encrypt|KeyExchange|Sign|Verify))\\b", + "name": "support.constant.10.12.c" + }, + { + "match": "\\bk(?:CGLRPRe(?:gistryID(?:High|Low)|movable)|FSEventStream(?:CreateFlagUseExtendedData|EventFlagItemCloned)|SecAccessControlBiometry(?:Any|CurrentSet))\\b", + "name": "support.constant.10.13.c" + }, + { + "match": "\\bk(?:3DMixerParam_(?:BusEnable|DryWetReverbBlend|GlobalReverbGainInDecibels|M(?:axGainInDecibels|inGainInDecibels)|O(?:bstructionAttenuationInDecibels|cclusionAttenuationInDecibels))|AudioQueueProperty_ChannelAssignments|JSTypeSymbol|SecAccessControlWatch)\\b", + "name": "support.constant.10.15.c" + }, + { + "match": "\\bk(?:AudioComponentFlag_SandboxSafe|CGL(?:PFASupportsAutomaticGraphicsSwitching|Renderer(?:ATIRadeonX4000ID|IntelHD5000ID)))\\b", + "name": "support.constant.10.8.c" + }, + { + "match": "\\bk(?:AXPriority(?:High|Low|Medium)|CGL(?:OGLPVersion_GL4_Core|R(?:PMajorGLVersion|endererGeForceID))|FSEventStream(?:CreateFlagMarkSelf|EventFlagOwnEvent))\\b", + "name": "support.constant.10.9.c" + }, + { + "match": "\\b(?:A(?:APNot(?:CreatedErr|FoundErr)|C(?:E(?:2Type|8Type)|L_(?:A(?:DD_(?:FILE|SUBDIRECTORY)|PPEND_DATA)|CHANGE_OWNER|DELETE(?:_CHILD)?|E(?:NTRY_(?:DIRECTORY_INHERIT|FILE_INHERIT|INHERITED|LIMIT_INHERIT|ONLY_INHERIT)|X(?:ECUTE|TENDED_(?:ALLOW|DENY)))|F(?:IRST_ENTRY|LAG_(?:DEFER_INHERIT|NO_INHERIT))|L(?:AST_ENTRY|IST_DIRECTORY)|NEXT_ENTRY|READ_(?:ATTRIBUTES|DATA|EXTATTRIBUTES|SECURITY)|S(?:EARCH|YNCHRONIZE)|TYPE_(?:A(?:CCESS|FS)|CODA|DEFAULT|EXTENDED|N(?:TFS|WFS))|UNDEFINED_TAG|WRITE_(?:ATTRIBUTES|DATA|EXTATTRIBUTES|SECURITY)))|IF(?:C(?:ID|Version1)|FID)|SD(?:Bad(?:ForkErr|HeaderErr)|EntryNotFoundErr)|VAudioSessionError(?:Code(?:BadParam|Cannot(?:InterruptOthers|Start(?:Playing|Recording))|ExpiredSession|I(?:n(?:compatibleCategory|sufficientPriority)|sBusy)|M(?:ediaServicesFailed|issingEntitlement)|None|ResourceNotAvailable|S(?:essionNotActive|iriIsRecording)|Unspecified)|InsufficientPriority)|nnotationID|ppl(?:eShareMediaType|icationSpecificID)|u(?:dioRecordingID|thorID))|C(?:DEFNFnd|E_CDNT_(?:FullName|NameRelativeToCrlIssuer)|S(?:SM(?:ERR_(?:A(?:C_(?:DEVICE_(?:FAILED|RESET)|FUNCTION_(?:FAILED|NOT_IMPLEMENTED)|IN(?:SUFFICIENT_CLIENT_IDENTIFICATION|TERNAL_ERROR|VALID_(?:BASE_ACLS|C(?:L_HANDLE|ONTEXT_HANDLE)|D(?:ATA|B_(?:HANDLE|LIST(?:_POINTER)?)|L_HANDLE)|ENCODING|INPUT_POINTER|OUTPUT_POINTER|P(?:ASSTHROUGH_ID|OINTER)|REQUEST(?:OR|_DESCRIPTOR)|T(?:P_HANDLE|UPLE_CREDENTIALS)|VALIDITY_PERIOD)|_DARK_WAKE)|M(?:DS_ERROR|EMORY_ERROR)|NO_USER_INTERACTION|OS_ACCESS_DENIED|SE(?:LF_CHECK_FAILED|RVICE_NOT_AVAILABLE)|USER_CANCELED)|PPLE(?:DL_(?:DISK_FULL|FILE_TOO_BIG|IN(?:COMPATIBLE_(?:DATABASE_BLOB|KEY_BLOB)|VALID_(?:DATABASE_BLOB|KEY_BLOB|OPEN_PARAMETERS))|QUOTA_EXCEEDED)|TP_(?:BAD_CERT_FROM_ISSUER|C(?:A_PIN_MISMATCH|ERT_NOT_FOUND_FROM_ISSUER|ODE_SIGN_DEVELOPMENT|RL_(?:BAD_URI|EXPIRED|INVALID_ANCHOR_CERT|NOT_(?:FOUND|TRUSTED|VALID_YET)|POLICY_FAIL|SERVER_DOWN)|S_(?:BAD_(?:CERT_CHAIN_LENGTH|PATH_LENGTH)|NO_(?:BASIC_CONSTRAINTS|EXTENDED_KEY_USAGE)))|EXT_KEYUSAGE_NOT_CRITICAL|HOSTNAME_MISMATCH|I(?:D(?:ENTIFIER_MISSING|P_FAIL)|N(?:COMPLETE_REVOCATION_CHECK|VALID_(?:AUTHORITY_ID|CA|E(?:MPTY_SUBJECT|XTENDED_KEY_USAGE)|ID_LINKAGE|KEY_USAGE|ROOT|SUBJECT_ID)))|MISSING_REQUIRED_EXTENSION|N(?:ETWORK_FAILURE|O_BASIC_CONSTRAINTS)|OCSP_(?:BAD_RE(?:QUEST|SPONSE)|INVALID_ANCHOR_CERT|NO(?:NCE_MISMATCH|T_TRUSTED|_SIGNER)|RESP_(?:INTERNAL_ERR|MALFORMED_REQ|SIG_REQUIRED|TRY_LATER|UNAUTHORIZED)|S(?:IG_ERROR|TATUS_UNRECOGNIZED)|UNAVAILABLE)|PATH_LEN_CONSTRAINT|RS_BAD_(?:CERT_CHAIN_LENGTH|EXTENDED_KEY_USAGE)|S(?:MIME_(?:BAD_(?:EXT_KEY_USE|KEY_USE)|EMAIL_ADDRS_NOT_FOUND|KEYUSAGE_NOT_CRITICAL|NO_EMAIL_ADDRS|SUBJ_ALT_NAME_NOT_CRIT)|SL_BAD_EXT_KEY_USE)|TRUST_SETTING_DENY|UNKNOWN_(?:C(?:ERT_EXTEN|R(?:ITICAL_EXTEN|L_EXTEN))|QUAL_CERT_STATEMENT))|_DOTMAC_(?:CSR_VERIFY_FAIL|FAILED_CONSISTENCY_CHECK|NO_REQ_PENDING|REQ_(?:IS_PENDING|QUEUED|REDIRECT|SERVER_(?:A(?:LREADY_EXIST|UTH)|ERR|NOT_AVAIL|PARAM|SERVICE_ERROR|UNIMPL)))))|C(?:L_(?:CRL_ALREADY_SIGNED|DEVICE_(?:FAILED|RESET)|FUNCTION_(?:FAILED|NOT_IMPLEMENTED)|IN(?:SUFFICIENT_CLIENT_IDENTIFICATION|TERNAL_ERROR|VALID_(?:BUNDLE_(?:INFO|POINTER)|C(?:ACHE_HANDLE|ERT(?:GROUP_POINTER|_POINTER)|ONTEXT_HANDLE|RL_(?:INDEX|POINTER))|DATA|FIELD_POINTER|INPUT_POINTER|NUMBER_OF_FIELDS|OUTPUT_POINTER|P(?:ASSTHROUGH_ID|OINTER)|RESULTS_HANDLE|SCOPE)|_DARK_WAKE)|M(?:DS_ERROR|EMORY_ERROR)|NO_(?:FIELD_VALUES|USER_INTERACTION)|OS_ACCESS_DENIED|S(?:COPE_NOT_SUPPORTED|E(?:LF_CHECK_FAILED|RVICE_NOT_AVAILABLE))|U(?:NKNOWN_(?:FORMAT|TAG)|SER_CANCELED)|VERIFICATION_FAILURE)|S(?:P(?:DL_APPLE_DL_CONVERSION_ERROR|_(?:A(?:CL_(?:ADD_FAILED|BASE_CERTS_NOT_SUPPORTED|CHA(?:LLENGE_CALLBACK_FAILED|NGE_FAILED)|DELETE_FAILED|ENTRY_TAG_NOT_FOUND|REPLACE_FAILED|SUBJECT_TYPE_NOT_SUPPORTED)|L(?:GID_MISMATCH|READY_LOGGED_IN)|PPLE_(?:ADD_APPLICATION_ACL_SUBJECT|INVALID_KEY_(?:END_DATE|START_DATE)|PUBLIC_KEY_INCOMPLETE|S(?:IGNATURE_MISMATCH|SLv2_ROLLBACK))|TTACH_HANDLE_BUSY)|BLOCK_SIZE_MISMATCH|CRYPTO_DATA_CALLBACK_FAILED|DEVICE_(?:ERROR|FAILED|MEMORY_ERROR|RESET|VERIFY_FAILED)|FUNCTION_(?:FAILED|NOT_IMPLEMENTED)|IN(?:PUT_LENGTH_ERROR|SUFFICIENT_CLIENT_IDENTIFICATION|TERNAL_ERROR|VALID_(?:A(?:C(?:CESS_CREDENTIALS|L_(?:BASE_CERTS|CHALLENGE_CALLBACK|E(?:DIT_MODE|NTRY_TAG)|SUBJECT_VALUE))|LGORITHM|TTR_(?:A(?:CCESS_CREDENTIALS|LG_PARAMS)|B(?:ASE|LOCK_SIZE)|DL_DB_HANDLE|E(?:FFECTIVE_BITS|ND_DATE)|I(?:NIT_VECTOR|TERATION_COUNT)|KEY(?:_(?:LENGTH|TYPE))?|LABEL|MODE|OUTPUT_SIZE|P(?:A(?:DDING|SSPHRASE)|RI(?:ME|VATE_KEY_FORMAT)|UBLIC_KEY_FORMAT)|R(?:ANDOM|OUNDS)|S(?:ALT|EED|TART_DATE|UBPRIME|YMMETRIC_KEY_FORMAT)|VERSION|WRAPPED_KEY_FORMAT))|C(?:ONTEXT(?:_HANDLE)?|RYPTO_DATA)|D(?:ATA(?:_COUNT)?|IGEST_ALGORITHM)|INPUT_(?:POINTER|VECTOR)|KEY(?:ATTR_MASK|USAGE_MASK|_(?:CLASS|FORMAT|LABEL|POINTER|REFERENCE))?|LOGIN_NAME|NEW_ACL_(?:ENTRY|OWNER)|OUTPUT_(?:POINTER|VECTOR)|P(?:ASSTHROUGH_ID|OINTER)|S(?:AMPLE_VALUE|IGNATURE))|_DARK_WAKE)|KEY_(?:BLOB_TYPE_INCORRECT|HEADER_INCONSISTENT|LABEL_ALREADY_EXISTS|USAGE_INCORRECT)|M(?:DS_ERROR|EMORY_ERROR|ISSING_ATTR_(?:A(?:CCESS_CREDENTIALS|LG_PARAMS)|B(?:ASE|LOCK_SIZE)|DL_DB_HANDLE|E(?:FFECTIVE_BITS|ND_DATE)|I(?:NIT_VECTOR|TERATION_COUNT)|KEY(?:_(?:LENGTH|TYPE))?|LABEL|MODE|OUTPUT_SIZE|P(?:A(?:DDING|SSPHRASE)|RI(?:ME|VATE_KEY_FORMAT)|UBLIC_KEY_FORMAT)|R(?:ANDOM|OUNDS)|S(?:ALT|EED|TART_DATE|UBPRIME|YMMETRIC_KEY_FORMAT)|VERSION|WRAPPED_KEY_FORMAT))|NO(?:T_LOGGED_IN|_USER_INTERACTION)|O(?:BJECT_(?:ACL_(?:NOT_SUPPORTED|REQUIRED)|MANIP_AUTH_DENIED|USE_AUTH_DENIED)|PERATION_AUTH_DENIED|S_ACCESS_DENIED|UTPUT_LENGTH_ERROR)|P(?:RIV(?:ATE_KEY_(?:ALREADY_EXISTS|NOT_FOUND)|ILEGE_NOT_(?:GRANTED|SUPPORTED))|UBLIC_KEY_INCONSISTENT)|QUERY_SIZE_UNKNOWN|S(?:AMPLE_VALUE_NOT_SUPPORTED|E(?:LF_CHECK_FAILED|RVICE_NOT_AVAILABLE)|TAGED_OPERATION_(?:IN_PROGRESS|NOT_STARTED))|U(?:NSUPPORTED_KEY(?:ATTR_MASK|USAGE_MASK|_(?:FORMAT|LABEL|SIZE))|SER_CANCELED)|VE(?:CTOR_OF_BUFS_UNSUPPORTED|RIFY_FAILED)))|SM_(?:A(?:DDIN_(?:AUTHENTICATE_FAILED|LOAD_FAILED|UNLOAD_FAILED)|TTRIBUTE_NOT_IN_CONTEXT)|BUFFER_TOO_SMALL|DEVICE_(?:FAILED|RESET)|E(?:MM_(?:AUTHENTICATE_FAILED|LOAD_FAILED|UNLOAD_FAILED)|VENT_NOTIFICATION_CALLBACK_NOT_FOUND)|FUNCTION_(?:FAILED|INTEGRITY_FAIL|NOT_IMPLEMENTED)|IN(?:COMPATIBLE_VERSION|SUFFICIENT_CLIENT_IDENTIFICATION|TERNAL_ERROR|VALID_(?:A(?:DDIN_(?:FUNCTION_TABLE|HANDLE)|TTRIBUTE)|CONTEXT_HANDLE|GUID|HANDLE_USAGE|INPUT_POINTER|KEY_HIERARCHY|OUTPUT_POINTER|P(?:OINTER|VC)|S(?:ERVICE_MASK|UBSERVICEID))|_DARK_WAKE)|LIB_REF_NOT_FOUND|M(?:DS_ERROR|EMORY_ERROR|ODULE_(?:MAN(?:AGER_(?:INITIALIZE_FAIL|NOT_FOUND)|IFEST_VERIFY_FAILED)|NOT_LOADED))|NO(?:T_INITIALIZED|_USER_INTERACTION)|OS_ACCESS_DENIED|P(?:RIVILEGE_NOT_GRANTED|VC_(?:ALREADY_CONFIGURED|REFERENT_NOT_FOUND))|S(?:COPE_NOT_SUPPORTED|E(?:LF_CHECK_FAILED|RVICE_NOT_AVAILABLE))|USER_CANCELED)))|DL_(?:ACL_(?:ADD_FAILED|BASE_CERTS_NOT_SUPPORTED|CHA(?:LLENGE_CALLBACK_FAILED|NGE_FAILED)|DELETE_FAILED|ENTRY_TAG_NOT_FOUND|REPLACE_FAILED|SUBJECT_TYPE_NOT_SUPPORTED)|D(?:ATA(?:BASE_CORRUPT|STORE_(?:ALREADY_EXISTS|DOESNOT_EXIST|IS_OPEN))|B_LOCKED|EVICE_(?:FAILED|RESET))|ENDOFDATA|F(?:IELD_SPECIFIED_MULTIPLE|UNCTION_(?:FAILED|NOT_IMPLEMENTED))|IN(?:COMPATIBLE_FIELD_FORMAT|SUFFICIENT_CLIENT_IDENTIFICATION|TERNAL_ERROR|VALID_(?:AC(?:CESS_(?:CREDENTIALS|REQUEST)|L_(?:BASE_CERTS|CHALLENGE_CALLBACK|E(?:DIT_MODE|NTRY_TAG)|SUBJECT_VALUE))|C(?:L_HANDLE|SP_HANDLE)|D(?:B_(?:HANDLE|L(?:IST_POINTER|OCATION)|NAME)|L_HANDLE)|FIELD_NAME|IN(?:DEX_INFO|PUT_POINTER)|MODIFY_MODE|NE(?:TWORK_ADDR|W_(?:ACL_(?:ENTRY|OWNER)|OWNER))|O(?:PEN_PARAMETERS|UTPUT_POINTER)|P(?:A(?:RSING_MODULE|SSTHROUGH_ID)|OINTER)|QUERY|RE(?:CORD(?:TYPE|_(?:INDEX|UID))|SULTS_HANDLE)|S(?:AMPLE_VALUE|ELECTION_TAG)|UNIQUE_INDEX_DATA|VALUE)|_DARK_WAKE)|M(?:DS_ERROR|EMORY_ERROR|ISSING_VALUE|ULTIPLE_VALUES_UNSUPPORTED)|NO_USER_INTERACTION|O(?:BJECT_(?:ACL_(?:NOT_SUPPORTED|REQUIRED)|MANIP_AUTH_DENIED|USE_AUTH_DENIED)|PERATION_AUTH_DENIED|S_ACCESS_DENIED)|RECORD_(?:MODIFIED|NOT_FOUND)|S(?:AMPLE_VALUE_NOT_SUPPORTED|E(?:LF_CHECK_FAILED|RVICE_NOT_AVAILABLE)|TALE_UNIQUE_RECORD)|U(?:NSUPPORTED_(?:FIELD_FORMAT|INDEX_INFO|LOCALITY|NUM_(?:ATTRIBUTES|INDEXES|RECORDTYPES|SELECTION_PREDS)|OPERATOR|QUERY(?:_LIMITS)?|RECORDTYPE)|SER_CANCELED))|TP_(?:AUTHENTICATION_FAILED|C(?:ERT(?:GROUP_INCOMPLETE|IFICATE_CANT_OPERATE|_(?:EXPIRED|NOT_VALID_YET|REVOKED|SUSPENDED))|RL_ALREADY_SIGNED)|DEVICE_(?:FAILED|RESET)|FUNCTION_(?:FAILED|NOT_IMPLEMENTED)|IN(?:SUFFICIENT_C(?:LIENT_IDENTIFICATION|REDENTIALS)|TERNAL_ERROR|VALID_(?:A(?:CTION(?:_DATA)?|NCHOR_CERT|UTHORITY)|C(?:ALL(?:BACK|ERAUTH_CONTEXT_POINTER)|ERT(?:GROUP(?:_POINTER)?|IFICATE|_(?:AUTHORITY|POINTER))|L_HANDLE|ONTEXT_HANDLE|RL(?:GROUP(?:_POINTER)?|_(?:AUTHORITY|ENCODING|POINTER|TYPE))?|SP_HANDLE)|D(?:ATA|B_(?:HANDLE|LIST(?:_POINTER)?)|L_HANDLE)|F(?:IELD_POINTER|ORM_TYPE)|I(?:D(?:ENTIFIER(?:_POINTER)?)?|N(?:DEX|PUT_POINTER))|KEYCACHE_HANDLE|N(?:AME|ETWORK_ADDR|UMBER_OF_FIELDS)|OUTPUT_POINTER|P(?:ASSTHROUGH_ID|O(?:INTER|LICY_IDENTIFIERS))|RE(?:ASON|QUEST_INPUTS|SPONSE_VECTOR)|S(?:IGNATURE|TOP_ON_POLICY)|T(?:IMESTRING|UPLE(?:GROUP(?:_POINTER)?)?))|_DARK_WAKE)|M(?:DS_ERROR|EMORY_ERROR)|NO(?:T_(?:SIGNER|TRUSTED)|_(?:DEFAULT_AUTHORITY|USER_INTERACTION))|OS_ACCESS_DENIED|RE(?:JECTED_FORM|QUEST_(?:LOST|REJECTED))|SE(?:LF_CHECK_FAILED|RVICE_NOT_AVAILABLE)|U(?:N(?:KNOWN_(?:FORMAT|TAG)|SUPPORTED_(?:ADDR_TYPE|SERVICE))|SER_CANCELED)|VERIF(?:ICATION_FAILURE|Y_ACTION_FAILED)))|_(?:A(?:C(?:L_(?:AUTHORIZATION_(?:ANY|CHANGE_(?:ACL|OWNER)|D(?:B(?:S_(?:CREATE|DELETE)|_(?:DELETE|INSERT|MODIFY|READ))|E(?:CRYPT|LETE|RIVE))|E(?:NCRYPT|XPORT_(?:CLEAR|WRAPPED))|GENKEY|I(?:MPORT_(?:CLEAR|WRAPPED)|NTEGRITY)|LOGIN|MAC|P(?:ARTITION_ID|REAUTH_(?:BASE|END))|SIGN|TAG_VENDOR_DEFINED_START)|CODE_SIGNATURE_(?:INVALID|OSX)|EDIT_MODE_(?:ADD|DELETE|REPLACE)|KEYCHAIN_PROMPT_(?:CURRENT_VERSION|INVALID(?:_ACT)?|REQUIRE_PASSPHRASE|UNSIGNED(?:_ACT)?)|MATCH_(?:BITS|GID|HONOR_ROOT|UID)|PR(?:EAUTH_TRACKING_(?:AUTHORIZED|BLOCKED|COUNT_MASK|UNKNOWN)|OCESS_SELECTOR_CURRENT_VERSION)|SUBJECT_TYPE_(?:A(?:NY|SYMMETRIC_KEY)|BIOMETRIC|CO(?:DE_SIGNATURE|MMENT)|EXT_PAM_NAME|HASHED_SUBJECT|KEYCHAIN_PROMPT|LOGIN_NAME|P(?:A(?:RTITION|SSWORD)|R(?:EAUTH(?:_SOURCE)?|O(?:CESS|MPTED_(?:BIOMETRIC|PASSWORD)|TECTED_(?:BIOMETRIC|PASSWORD)))|UBLIC_KEY)|SYMMETRIC_KEY|THRESHOLD))|_(?:BASE_(?:AC_ERROR|ERROR)|PRIVATE_ERROR))|DDR_(?:CUSTOM|N(?:AME|ONE)|SOCKADDR|URL)|LG(?:CLASS_(?:ASYMMETRIC|CUSTOM|D(?:ERIVEKEY|IGEST)|KEYGEN|MAC|NONE|RANDOMGEN|S(?:IGNATURE|YMMETRIC)|UNIQUEGEN)|ID_(?:3DES(?:_(?:1KEY(?:_EEE)?|2KEY(?:_E(?:DE|EE))?|3KEY(?:_E(?:DE|EE))?))?|A(?:ES|PPLE_YARROW|SC)|B(?:ATON|LOWFISH)|C(?:AST(?:3|5)?|DMF|RAB|USTOM|oncat(?:BaseAnd(?:Data|Key)|DataAndBase|KeyAndBase))|D(?:ES(?:Random|X)?|H|SA(?:_BSAFE)?)|E(?:C(?:AES|C|D(?:H(?:_X963_KDF)?|SA(?:_SPECIFIED)?)|ES|MQV|NRA)|NTROPY_DEFAULT|lGamal|xtractFromKey)|F(?:ASTHASH|E(?:AL|E(?:D(?:EXP)?|_(?:MD5|SHA1))?)|IPS186Random|ortezzaTimestamp)|G(?:OST|enericSecret)|HAVAL(?:3|4|5)?|I(?:BCHASH|DEA|ntelPlatformRandom)|JUNIPER|K(?:E(?:A|YCHAIN_KEY)|H(?:AFRE|UFU))|L(?:AST|OKI|UCIFER)|M(?:A(?:DRYGA|YFLY)|D(?:2(?:Random|WithRSA)?|4|5(?:HMAC|Random|WithRSA)?)|MB|QV)|N(?:HASH|ONE|RA)|OPENSSH1|P(?:BE_OPENSSL_MD5|H|KCS(?:12_(?:PBE_(?:ENCR|MAC)|SHA1_PBE)|5_PBKDF(?:1_(?:MD(?:2|5)|SHA1)|2)))|R(?:C(?:2|4|5)|DES|EDOC(?:3)?|IPEM(?:AC|D)|SA|UNNING_COUNTER)|S(?:AFER|E(?:AL|CURE_PASSPHRASE)|HA(?:1(?:HMAC(?:_LEGACY)?|With(?:DSA|EC(?:DSA|NRA)|RSA))?|2(?:24(?:With(?:ECDSA|RSA))?|56(?:With(?:ECDSA|RSA))?)|384(?:With(?:ECDSA|RSA))?|512(?:With(?:ECDSA|RSA))?|Random)|KIPJACK|SL3(?:KeyAndMacDerive|M(?:D5(?:_MAC)?|asterDerive)|PreMasterGen|SHA1(?:_MAC)?))|TIGER|UTC|VENDOR_DEFINED|Wrap(?:Lynks|SET_OAEP)|XORBaseAndData|_FIRST_UNUSED)|MODE_(?:BC|C(?:BC(?:128|64|C|P(?:D|adIV8)|_IV8)?|FB(?:16|32|8|PadIV8|_IV8)?|OUNTER|USTOM)|ECB(?:128|64|96|Pad)?|ISO_9796|LAST|NONE|O(?:AEP_HASH|FB(?:64|NLF|PadIV8|_IV8)?)|P(?:BC|CBC|FB|KCS1_EM(?:E_(?:OAEP|V15)|SA_V15)|RIVATE_(?:KEY|WRAP)|UBLIC_KEY)|RELAYX|SHUFFLE|VENDOR_DEFINED|WRAP|X9_31))|PPLE(?:CSP(?:DL_DB_(?:CHANGE_PASSWORD|GET_(?:HANDLE|SETTINGS)|IS_LOCKED|LOCK|SET_SETTINGS|UNLOCK)|_KEYDIGEST)|DL_OPEN_PARAMETERS_VERSION|FILEDL_(?:COMMIT|DELETE_FILE|MAKE_(?:BACKUP|COPY)|ROLLBACK|T(?:AKE_FILE_LOCK|OGGLE_AUTOCOMMIT))|SCPDL_CSP_GET_KEYHANDLE|X509CL_(?:OBTAIN_CSR|VERIFY_CSR)|_(?:PRIVATE_CSPDL_CODE_(?:1(?:0|1|2|3|4|5|6|7|8|9)|2(?:0|1|2|3|4|5|6|7)|8|9)|UNLOCK_TYPE_(?:KEY(?:BAG|_DIRECT)|WRAPPED_PRIVATE)))|SC_OPTIMIZE_(?:ASCII|DEFAULT|S(?:ECURITY|IZE)|TIME(?:_SIZE)?)|TT(?:ACH_READ_ONLY|RIBUTE_(?:A(?:CCESS_CREDENTIALS|L(?:ERT_TITLE|G_(?:ID|PARAMS))|SC_OPTIMIZATION)|B(?:ASE|LOCK_SIZE)|C(?:SP_HANDLE|USTOM)|D(?:ATA_(?:ACCESS_CREDENTIALS|C(?:RYPTO_DATA|SSM_DATA)|D(?:ATE|L_DB_HANDLE)|K(?:EY|R_PROFILE)|NONE|RANGE|STRING|UINT32|VERSION)|ESCRIPTION|L_DB_HANDLE)|E(?:FFECTIVE_BITS|ND_DATE)|FEE_(?:CURVE_TYPE|PRIME_TYPE)|I(?:NIT_VECTOR|TERATION_COUNT|V_SIZE)|K(?:EY(?:ATTR|USAGE|_(?:LENGTH(?:_RANGE)?|TYPE))?|RPROFILE_(?:LOCAL|REMOTE))|LABEL|MODE|NONE|OUTPUT_SIZE|P(?:A(?:DDING|RAM_KEY|SSPHRASE)|R(?:I(?:ME|VATE_KEY_FORMAT)|OMPT)|UBLIC_KEY(?:_FORMAT)?)|R(?:ANDOM|OUNDS(?:_RANGE)?|SA_BLINDING)|S(?:ALT|EED|TART_DATE|UBPRIME|YMMETRIC_KEY_FORMAT)|TYPE_MASK|VE(?:NDOR_DEFINED|R(?:IFY_PASSPHRASE|SION))|WRAPPED_KEY_FORMAT)))|BASE_ERROR|C(?:ERT(?:GROUP_(?:CERT_PAIR|DATA|ENCODED_CERT|PARSED_CERT)|_(?:ACL_ENTRY|BUNDLE_(?:CUSTOM|ENCODING_(?:BER|CUSTOM|DER|PGP|SEXPR|UNKNOWN)|LAST|P(?:FX|GP_KEYRING|KCS(?:12|7_SIGNED_(?:DATA|ENVELOPED_DATA)))|SPKI_SEQUENCE|UNKNOWN)|ENCODING_(?:BER|CUSTOM|DER|LAST|MULTIPLE|NDR|PGP|SEXPR|UNKNOWN)|Intel|LAST|MULTIPLE|P(?:ARSE_FORMAT_(?:C(?:OMPLEX|USTOM)|LAST|MULTIPLE|NONE|OID_NAMED|SEXPR|TUPLE)|GP)|S(?:DSIv1|PKI|TATUS_(?:EXPIRED|IS_(?:FROM_NET|IN_(?:ANCHORS|INPUT_CERTS)|ROOT)|NOT_VALID_YET|TRUST_SETTINGS_(?:DENY|FOUND_(?:ADMIN|SYSTEM|USER)|IGNORED_ERROR|TRUST)))|TUPLE|UNKNOWN|X(?:9_ATTRIBUTE|_509(?:_ATTRIBUTE|v(?:1|2|3)))))|L_(?:BASE_(?:CL_ERROR|ERROR)|CUSTOM_C(?:ERT_(?:BUNDLE_TYPE|ENCODING|PARSE_FORMAT|TYPE)|RL_PARSE_FORMAT)|PRIVATE_ERROR|TEMPLATE_(?:INTERMEDIATE_CERT|PKIX_CERTTEMPLATE))|ONTEXT_EVENT_(?:CREATE|DELETE|UPDATE)|RL(?:GROUP_(?:CRL_PAIR|DATA|ENCODED_CRL|PARSED_CRL)|_(?:ENCODING_(?:B(?:ER|LOOM)|CUSTOM|DER|MULTIPLE|SEXPR|UNKNOWN)|PARSE_FORMAT_(?:C(?:OMPLEX|USTOM)|LAST|MULTIPLE|NONE|OID_NAMED|SEXPR|TUPLE)|TYPE_(?:MULTIPLE|SPKI|UNKNOWN|X_509v(?:1|2))))|S(?:P_(?:BASE_(?:CSP_ERROR|ERROR)|H(?:ARDWARE|YBRID)|PRIVATE_ERROR|RDR_(?:EXISTS|HW|TOKENPRESENT)|S(?:OFTWARE|TORES_(?:CERTIFICATES|GENERIC|P(?:RIVATE_KEYS|UBLIC_KEYS)|SESSION_KEYS))|TOK_(?:CLOCK_EXISTS|LOGIN_REQUIRED|PR(?:IVATE_KEY_PASSWORD|OT_AUTHENTICATION)|RNG|SESSION_KEY_PASSWORD|USER_PIN_(?:EXPIRED|INITIALIZED)|WRITE_PROTECTED))|SM_(?:BASE_(?:CSSM_ERROR|ERROR)|PRIVATE_ERROR))|USTOM_COMMON_ERROR_EXTENT)|D(?:B_(?:A(?:CCESS_(?:PRIVILEGED|RE(?:AD|SET)|WRITE)|ND|TTRIBUTE_(?:FORMAT_(?:B(?:IG_NUM|LOB)|COMPLEX|MULTI_UINT32|REAL|S(?:INT32|TRING)|TIME_DATE|UINT32)|NAME_AS_(?:INTEGER|OID|STRING)))|C(?:ERT_USE_(?:OWNER|PRIVACY|REVOKED|S(?:IGNING|YSTEM)|TRUSTED)|ONTAINS(?:_(?:FINAL_SUBSTRING|INITIAL_SUBSTRING))?)|DATASTORES_UNKNOWN|EQUAL|FILESYSTEMSCAN_MODE|GREATER_THAN|INDEX_(?:NONUNIQUE|ON_(?:ATTRIBUTE|RECORD|UNKNOWN)|UNIQUE)|LESS_THAN|MODIFY_ATTRIBUTE_(?:ADD|DELETE|NONE|REPLACE)|NO(?:NE|T_EQUAL)|OR|RECORDTYPE_(?:APP_DEFINED_(?:END|START)|OPEN_GROUP_(?:END|START)|SCHEMA_(?:END|START))|TRANSACTIONAL_MODE)|L_(?:BASE_(?:DL_ERROR|ERROR)|CUSTOM|DB_(?:RECORD_(?:A(?:LL_KEYS|NY|PPLESHARE_PASSWORD)|C(?:ERT|RL)|EXTENDED_ATTRIBUTE|GENERIC(?:_PASSWORD)?|INTERNET_PASSWORD|METADATA|P(?:OLICY|RIVATE_KEY|UBLIC_KEY)|SYMMETRIC_KEY|U(?:NLOCK_REFERRAL|SER_TRUST)|X509_C(?:ERTIFICATE|RL))|SCHEMA_(?:ATTRIBUTES|IN(?:DEXES|FO)|PARSING_MODULE))|FFS|LDAP|MEMORY|ODBC|P(?:KCS11|RIVATE_ERROR)|REMOTEDIR|UNKNOWN))|E(?:LAPSED_TIME_(?:COMPLETE|UNKNOWN)|RR(?:CODE_(?:ACL_(?:ADD_FAILED|BASE_CERTS_NOT_SUPPORTED|CHA(?:LLENGE_CALLBACK_FAILED|NGE_FAILED)|DELETE_FAILED|ENTRY_TAG_NOT_FOUND|REPLACE_FAILED|SUBJECT_TYPE_NOT_SUPPORTED)|CRL_ALREADY_SIGNED|DEVICE_(?:FAILED|RESET)|FUNCTION_(?:FAILED|NOT_IMPLEMENTED)|IN(?:COMPATIBLE_VERSION|SUFFICIENT_CLIENT_IDENTIFICATION|TERNAL_ERROR|VALID_(?:AC(?:CESS_CREDENTIALS|L_(?:BASE_CERTS|CHALLENGE_CALLBACK|E(?:DIT_MODE|NTRY_TAG)|SUBJECT_VALUE)|_HANDLE)|C(?:ERT(?:GROUP_POINTER|_POINTER)|L_HANDLE|ONTEXT_HANDLE|R(?:L_POINTER|YPTO_DATA)|SP_HANDLE)|D(?:ATA|B_(?:HANDLE|LIST(?:_POINTER)?)|L_HANDLE)|FIELD_POINTER|GUID|INPUT_POINTER|KR_HANDLE|N(?:E(?:TWORK_ADDR|W_ACL_(?:ENTRY|OWNER))|UMBER_OF_FIELDS)|OUTPUT_POINTER|P(?:ASSTHROUGH_ID|OINTER)|SAMPLE_VALUE|TP_HANDLE)|_DARK_WAKE)|M(?:DS_ERROR|EMORY_ERROR|ODULE_MANIFEST_VERIFY_FAILED)|NO_USER_INTERACTION|O(?:BJECT_(?:ACL_(?:NOT_SUPPORTED|REQUIRED)|MANIP_AUTH_DENIED|USE_AUTH_DENIED)|PERATION_AUTH_DENIED|S_ACCESS_DENIED)|PRIVILEGE_NOT_GRANTED|S(?:AMPLE_VALUE_NOT_SUPPORTED|E(?:LF_CHECK_FAILED|RVICE_NOT_AVAILABLE))|U(?:NKNOWN_(?:FORMAT|TAG)|SER_CANCELED)|VERIFICATION_FAILURE)|ORCODE_(?:C(?:OMMON_EXTENT|USTOM_OFFSET)|MODULE_EXTENT))|STIMATED_TIME_UNKNOWN|VIDENCE_FORM_(?:APPLE_(?:CERT(?:GROUP|_INFO)|HEADER)|C(?:ERT(?:_ID)?|RL(?:_(?:ID|NEXTTIME|THISTIME))?)|POLICYINFO|TUPLEGROUP|UNSPECIFIC|VERIFIER_TIME))|F(?:ALSE|EE_(?:CURVE_TYPE_(?:ANSI_X9_62|DEFAULT|MONTGOMERY|WEIERSTRASS)|PRIME_TYPE_(?:DEFAULT|FEE|GENERAL|MERSENNE))|IELDVALUE_COMPLEX_DATA_TYPE)|HINT_(?:ADDRESS_(?:APP|SP)|NONE)|INVALID_HANDLE|K(?:EY(?:ATTR_(?:ALWAYS_SENSITIVE|EXTRACTABLE|MODIFIABLE|NEVER_EXTRACTABLE|P(?:ARTIAL|ERMANENT|RIVATE|UBLIC_KEY_ENCRYPT)|RETURN_(?:D(?:ATA|EFAULT)|NONE|REF)|SENSITIVE)|BLOB_(?:OTHER|R(?:AW(?:_FORMAT_(?:BSAFE|CCA|FIPS186|MSCAPI|NONE|O(?:CTET_STRING|PENSS(?:H(?:2)?|L)|THER)|P(?:GP|KCS(?:1|3|8))|SPKI|VENDOR_DEFINED|X509))?|EF(?:ERENCE|_FORMAT_(?:INTEGER|OTHER|S(?:PKI|TRING))))|WRAPPED(?:_FORMAT_(?:APPLE_CUSTOM|MSCAPI|NONE|O(?:PENSS(?:H1|L)|THER)|PKCS(?:7|8)))?)|CLASS_(?:OTHER|P(?:RIVATE_KEY|UBLIC_KEY)|SE(?:CRET_PART|SSION_KEY))|HEADER_VERSION|USE_(?:ANY|DE(?:CRYPT|RIVE)|ENCRYPT|SIGN(?:_RECOVER)?|UNWRAP|VERIFY(?:_RECOVER)?|WRAP)|_HIERARCHY_(?:EXPORT|INTEG|NONE))|R_(?:BASE_ERROR|PRIVATE_ERROR))|LIST_(?:ELEMENT_(?:DATUM|SUBLIST|WORDID)|TYPE_(?:CUSTOM|SEXPR|UNKNOWN))|M(?:DS_(?:BASE_ERROR|PRIVATE_ERROR)|ODULE_STRING_SIZE)|N(?:ET_PROTO_(?:C(?:MP(?:S)?|USTOM)|FTP(?:S)?|LDAP(?:NS|S)?|NONE|OCSP|UNSPECIFIED|X500DAP)|OTIFY_(?:FAULT|INSERT|REMOVE))|OK|P(?:ADDING_(?:A(?:LTERNATE|PPLE_SSLv2)|C(?:IPHERSTEALING|USTOM)|FF|NONE|ONE|PKCS(?:1|5|7)|RANDOM|SIGRAW|VENDOR_DEFINED|ZERO)|KCS(?:5_PBKDF2_PRF_HMAC_SHA1|_OAEP_(?:MGF(?:1_(?:MD5|SHA1)|_NONE)|PSOURCE_(?:NONE|Pspecified)))|RIVILEGE_SCOPE_(?:NONE|PROCESS|THREAD)|VC_(?:APP|NONE|SP))|QUERY_(?:RETURN_DATA|SIZELIMIT_NONE|TIMELIMIT_NONE)|S(?:AMPLE_TYPE_(?:ASYMMETRIC_KEY|BIOMETRIC|COMMENT|HASHED_PASSWORD|KEY(?:BAG_KEY|CHAIN_(?:CHANGE_LOCK|LOCK|PROMPT))|P(?:ASSWORD|R(?:EAUTH|O(?:CESS|MPTED_(?:BIOMETRIC|PASSWORD)|TECTED_(?:BIOMETRIC|PASSWORD))))|RETRY_ID|S(?:IGNED_(?:NONCE|SECRET)|YMMETRIC_KEY)|THRESHOLD)|ERVICE_(?:AC|C(?:L|S(?:P|SM))|DL|KR|TP))|T(?:P_(?:A(?:CTION_(?:ALLOW_EXPIRED(?:_ROOT)?|CRL_SUFFICIENT|DEFAULT|FETCH_C(?:ERT_FROM_NET|RL_FROM_NET)|IMPLICIT_ANCHORS|LEAF_IS_CA|REQUIRE_(?:CRL_(?:IF_PRESENT|PER_CERT)|REV_PER_CERT)|TRUST_SETTINGS)|UTHORITY_REQUEST_C(?:ERT(?:ISSUE|NOTARIZE|RE(?:SUME|VOKE)|SUSPEND|USERECOVER|VERIFY)|RLISSUE))|BASE_(?:ERROR|TP_ERROR)|C(?:ERT(?:CHANGE_(?:HOLD|NO(?:NE|T_AUTHORIZED)|OK(?:WITHNEWTIME)?|RE(?:ASON_(?:AFFILIATIONCHANGE|C(?:ACOMPROMISE|EASEOPERATION)|HOLDRELEASE|KEYCOMPROMISE|SU(?:PERCEDED|SPECTEDCOMPROMISE)|UNKNOWN)|JECTED|LEASE|VOKE)|STATUS_UNKNOWN|WRONGCA)|ISSUE_(?:NOT_AUTHORIZED|OK(?:WITH(?:CERTMODS|SERVICEMODS))?|REJECTED|STATUS_UNKNOWN|WILL_BE_REVOKED)|NOTARIZE_(?:NOT_AUTHORIZED|OK(?:WITH(?:OUTFIELDS|SERVICEMODS))?|REJECTED|STATUS_UNKNOWN)|RECLAIM_(?:NO(?:MATCH|T_AUTHORIZED)|OK|REJECTED|STATUS_UNKNOWN)|VERIFY_(?:EXPIRED|INVALID(?:_(?:AUTHORITY|BASIC_CONSTRAINTS|C(?:ERT(?:GROUP|_VALUE)|RL_DIST_PT)|NAME_TREE|POLICY(?:_IDS)?|SIGNATURE))?|NOT_VALID_YET|REVOKED|SUSPENDED|UNKNOWN(?:_CRITICAL_EXT)?|VALID)|_(?:DIR_UPDATE|NOTIFY_RENEW|PUBLISH))|ONFIRM_(?:ACCEPT|REJECT|STATUS_UNKNOWN)|RL(?:ISSUE_(?:INVALID_DOMAIN|NOT_(?:AUTHORIZED|CURRENT)|OK|REJECTED|STATUS_UNKNOWN|UNKNOWN_IDENTIFIER)|_DISTRIBUTE))|FORM_TYPE_(?:GENERIC|REGISTRATION)|KEY_ARCHIVE|PRIVATE_ERROR|STOP_ON_(?:FIRST_(?:FAIL|PASS)|NONE|POLICY))|RUE)|USEE_(?:AUTHENTICATION|DOMESTIC|FINANCIAL|INSURANCE|K(?:EYEXCH|R(?:ENT|LE))|LAST|MEDICAL|NONE|SSL|WEAK)|VALUE_NOT_AVAILABLE|WORDID_(?:A(?:CL|LPHA|SYMMETRIC_KEY)?|B(?:ER|I(?:NARY|OMETRIC))?|C(?:ANCELED|ERT|OMMENT|RL|USTOM)?|D(?:ATE|B(?:S_(?:CREATE|DELETE)|_(?:DELETE|EXEC_STORED_QUERY|INSERT|MODIFY|READ))|E(?:CRYPT|L(?:ETE|TA_CRL)|R(?:IVE)?)|ISPLAY|O|SA(?:_SHA1)?)?|E(?:LGAMAL|N(?:CRYPT|TRY)|XPORT_(?:CLEAR|WRAPPED))?|G(?:E(?:NKEY)?)?|HA(?:SH(?:ED_(?:PASSWORD|SUBJECT))?|VAL)|I(?:BCHASH|MPORT_(?:CLEAR|WRAPPED)|NTEL|SSUER(?:_INFO)?)|K(?:E(?:A|Y(?:BAG_KEY|CHAIN_(?:CHANGE_LOCK|LOCK|PROMPT)|HOLDER)?)|_OF_N)|L(?:E|OGIN(?:_NAME)?)?|M(?:AC|D(?:2(?:WITHRSA)?|4|5(?:WITHRSA)?))|N(?:AME|DR|HASH|OT_(?:AFTER|BEFORE)|U(?:LL|MERIC))?|O(?:BJECT_HASH|N(?:E_TIME|LINE)|WNER)|P(?:A(?:M_NAME|RTITION|SSWORD)|GP|IN|R(?:E(?:AUTH(?:_SOURCE)?|FIX)|IVATE_KEY|O(?:CESS|MPTED_(?:BIOMETRIC|PASSWORD)|PAGATE|TECTED_(?:BIOMETRIC|P(?:ASSWORD|IN))))|UBLIC_KEY(?:_FROM_CERT)?)?|Q|R(?:ANGE|EVAL|IPEM(?:AC|D(?:160)?)|SA(?:_(?:ISO9796|PKCS(?:1(?:_(?:MD5|S(?:HA1|IG)))?|_(?:MD5|SHA1))?|RAW))?)|S(?:DSIV1|E(?:QUENCE|T|XPR)|HA1(?:WITH(?:DSA|ECDSA|RSA))?|IGN(?:ATURE|ED_(?:NONCE|SECRET))?|PKI|UBJECT(?:_INFO)?|Y(?:MMETRIC_KEY|STEM))|T(?:AG|HRESHOLD|IME)|URI|VE(?:NDOR_(?:END|START)|RSION)|X(?:509(?:V(?:1|2|3)|_ATTRIBUTE)|9_ATTRIBUTE)|_(?:FIRST_UNUSED|NLU_|RESERVED_1|STAR_|UNK_))|X509_DATAFORMAT_(?:ENCODED|PA(?:IR|RSED))))|_MAX_PATH)|antDecompress|o(?:mm(?:entID|onID)|pyrightID))|D(?:RAWHook|T_(?:Authority(?:InfoAccess|KeyID)|BasicConstraints|C(?:ertPolicies|rl(?:DistributionPoints|Number|Reason))|DeltaCrl|ExtendedKeyUsage|I(?:nhibitAnyPolicy|ssu(?:erAltName|ingDistributionPoint))|KeyUsage|N(?:ameConstraints|etscapeCertType)|Other|Policy(?:Constraints|Mappings)|QC_Statements|Subject(?:AltName|KeyID)))|E(?:OLHook|QUALTO|V(?:HIDE|LEVEL|MOVE|NOP|SHOW))|F(?:ORMID|or(?:matVersionID|ward(?:BackwardLooping|Looping)))|G(?:NT_(?:D(?:NSName|irectoryName)|EdiPartyName|IPAddress|OtherName|R(?:FC822Name|egisteredID)|URI|X400Address)|REATERTHAN)|H(?:DActivity|ITTESTHook)|I(?:dleActivity|nstrumentID)|KAEISHandleCGI|L(?:A(?:TENCY_QOS_TIER_(?:0|1|2|3|4|5|UNSPECIFIED)|UNCH_DATA_(?:ARRAY|BOOL|DICTIONARY|ERRNO|FD|INTEGER|MACHPORT|OPAQUE|REAL|STRING))|ESSTHAN)|M(?:ACE(?:3Type|6Type)|IDIDataID|PLibrary_(?:DevelopmentRevision|M(?:ajorVersion|inorVersion)|Release)|arkerID)|N(?:X_(?:BigEndian|L(?:eftButton|ittleEndian)|OneButton|RightButton|UnknownByteOrder)|ameID|etActivity|o(?:Looping|neType))|O(?:S(?:A(?:ControlFlowError|Duplicate(?:Handler|P(?:arameter|roperty))|I(?:llegal(?:A(?:ccess|ssign)|Index|Range)|nconsistentDeclarations)|M(?:essageNotUnderstood|issingParameter)|ParameterMismatch|Syntax(?:Error|TypeError)|TokenTooLong|Undefined(?:Handler|Variable))|_LOG_TYPE_(?:DE(?:BUG|FAULT)|ERROR|FAULT|INFO))|verallAct)|S(?:SL_(?:DH(?:E_(?:DSS_(?:EXPORT_WITH_DES40_CBC_SHA|WITH_(?:3DES_EDE_CBC_SHA|DES_CBC_SHA))|RSA_(?:EXPORT_WITH_DES40_CBC_SHA|WITH_(?:3DES_EDE_CBC_SHA|DES_CBC_SHA)))|_(?:DSS_(?:EXPORT_WITH_DES40_CBC_SHA|WITH_(?:3DES_EDE_CBC_SHA|DES_CBC_SHA))|RSA_(?:EXPORT_WITH_DES40_CBC_SHA|WITH_(?:3DES_EDE_CBC_SHA|DES_CBC_SHA))|anon_(?:EXPORT_WITH_(?:DES40_CBC_SHA|RC4_40_MD5)|WITH_(?:3DES_EDE_CBC_SHA|DES_CBC_SHA|RC4_128_MD5))))|FORTEZZA_DMS_WITH_(?:FORTEZZA_CBC_SHA|NULL_SHA)|N(?:O_SUCH_CIPHERSUITE|ULL_WITH_NULL_NULL)|RSA_(?:EXPORT_WITH_(?:DES40_CBC_SHA|RC(?:2_CBC_40_MD5|4_40_MD5))|WITH_(?:3DES_EDE_CBC_(?:MD5|SHA)|DES_CBC_(?:MD5|SHA)|IDEA_CBC_(?:MD5|SHA)|NULL_(?:MD5|SHA)|RC(?:2_CBC_MD5|4_128_(?:MD5|SHA)))))|lpTypeErr|oundDataID)|T(?:ASK_(?:BACKGROUND_APPLICATION|CONTROL_APPLICATION|D(?:ARWINBG_APPLICATION|EFAULT_APPLICATION)|FOREGROUND_APPLICATION|GRAPHICS_SERVER|INSPECT_BASIC_COUNTS|NONUI_APPLICATION|RENICED|THROTTLE_APPLICATION|UNSPECIFIED)|HROUGHPUT_QOS_TIER_(?:0|1|2|3|4|5|UNSPECIFIED)|LS_(?:AES_(?:128_(?:CCM_(?:8_SHA256|SHA256)|GCM_SHA256)|256_GCM_SHA384)|CHACHA20_POLY1305_SHA256|DH(?:E_(?:DSS_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:256)?|GCM_SHA384)))|PSK_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:384)?|GCM_SHA384))|NULL_SHA(?:256|384)?|RC4_128_SHA)|RSA_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:256)?|GCM_SHA384))))|_(?:DSS_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:256)?|GCM_SHA384)))|RSA_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:256)?|GCM_SHA384)))|anon_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:256)?|GCM_SHA384))|RC4_128_MD5)))|E(?:CDH(?:E_(?:ECDSA_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:384)?|GCM_SHA384))|CHACHA20_POLY1305_SHA256|NULL_SHA|RC4_128_SHA)|PSK_WITH_AES_(?:128_CBC_SHA|256_CBC_SHA)|RSA_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:384)?|GCM_SHA384))|CHACHA20_POLY1305_SHA256|NULL_SHA|RC4_128_SHA))|_(?:ECDSA_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:384)?|GCM_SHA384))|NULL_SHA|RC4_128_SHA)|RSA_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:384)?|GCM_SHA384))|NULL_SHA|RC4_128_SHA)|anon_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_CBC_SHA|256_CBC_SHA)|NULL_SHA|RC4_128_SHA)))|MPTY_RENEGOTIATION_INFO_SCSV)|NULL_WITH_NULL_NULL|PSK_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:384)?|GCM_SHA384))|CHACHA20_POLY1305_SHA256|NULL_SHA(?:256|384)?|RC4_128_SHA)|RSA_(?:PSK_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:384)?|GCM_SHA384))|NULL_SHA(?:256|384)?|RC4_128_SHA)|WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:256)?|GCM_SHA384))|NULL_(?:MD5|SHA(?:256)?)|RC4_128_(?:MD5|SHA))))|extWidthHook)|U(?:NORDERED|srActivity)|W(?:DEFNFnd|IDTHHook)|XPC_ACTIVITY_STATE_(?:C(?:HECK_IN|ONTINUE)|D(?:EFER|ONE)|RUN|WAIT)|a(?:b(?:brevDate|ortErr)|c(?:tiv(?:Dev|Mask|ateEvt|eFlag(?:Bit)?)|uteUpr(?:A|I|O|U))|d(?:bAddrMask|d(?:Re(?:fFailed|sFailed)|Size(?:Bit)?))|eBuildSyntax(?:Bad(?:D(?:ata|esc)|EOF|Hex|Negative|Token)|CoercedList|MissingQuote|No(?:C(?:lose(?:Brac(?:e|ket)|Hex|Paren|String)|olon)|E(?:OF|rr)|Key)|OddHex|Uncoerced(?:DoubleAt|Hex))|fp(?:A(?:ccessDenied|lready(?:LoggedInErr|Mounted)|uthContinue)|B(?:ad(?:DirIDType|IDErr|UAM|VersNum)|itmapErr)|C(?:a(?:llNot(?:Allowed|Supported)|nt(?:Mo(?:untMoreSrvre|ve)|Rename)|talogChanged)|ontainsSharedErr)|D(?:enyConflict|i(?:ffVolErr|rNot(?:Empty|Found)|skFull))|EofError|F(?:ileBusy|latVol)|I(?:D(?:Exists|NotFound)|conTypeError|nside(?:SharedErr|TrashErr)|temNotFound)|LockErr|MiscErr|No(?:MoreLocks|Server)|Object(?:Exists|Locked|NotFound|TypeErr)|P(?:armErr|wd(?:ExpiredErr|NeedsChangeErr|PolicyErr|SameErr|TooShortErr))|Range(?:NotLocked|Overlap)|S(?:ame(?:NodeErr|ObjectErr)|e(?:rverGoingDown|ssClosed))|TooManyFilesOpen|UserNotAuth|VolLocked)|l(?:phaLock(?:Bit)?|tDBoxProc)|pp(?:1(?:Evt|Mask)|2(?:Evt|Mask)|3(?:Evt|Mask)|4(?:Evt|Mask)|IsDaemon|M(?:emFullErr|odeErr)|VersionTooOld|e(?:arance(?:Bad(?:BrushIndexErr|CursorIndexErr|TextColorIndexErr)|Process(?:NotRegisteredErr|RegisteredErr)|ThemeHasNoAccents)|ndDITL(?:Bottom|Right))|le(?:Logo|MenuFolderIconResource))|s(?:i(?:AliasName|ParentName|ServerName|VolumeName|ZoneName)|p(?:B(?:adVersNum|ufTooSmall)|No(?:Ack|MoreSess|Servers)|ParamErr|S(?:e(?:rverBusy|ssClosed)|izeErr)|TooMany))|t(?:AbsoluteCenter|Bottom(?:Left|Right)?|Center(?:Bottom|Left|Right|Top)|HorizontalCenter|Left|None|Right|Top(?:Left|Right)?|VerticalCenter|om(?:IndexInvalidErr|sNotOfSameTypeErr)|p(?:BadRsp|LenErr))|u(?:t(?:hFailErr|oKey(?:Mask)?)|xiliaryExportDataUnavailable))|b(?:A(?:ccessCntl|llowCDiDataHandler|ncestorModDateChanges)|DoNotDisplay|Ha(?:ndleAERecording|s(?:B(?:TreeMgr|lankAccessPrivileges)|C(?:atSearch|opyFile)|D(?:esktopMgr|irectIO)|ExtFSVol|F(?:ileIDs|olderLock)|MoveRename|OpenDeny|PersonalAccessPrivileges|ShortName|UserGroupList))|Is(?:AutoMounted|Case(?:Preserving|Sensitive)|Ejectable|On(?:ExternalBus|InternalBus)|Removable)|L(?:2PCanMapFileBlocks|anguageMask|imitFCBs|ocalWList)|No(?:BootBlks|DeskItems|LclSync|MiniFndr|RootTimes|S(?:witchTo|ysDir)|V(?:NEdit|olumeSizes))|ParentModDateChanges|S(?:cript(?:LanguageMask|Mask)|upports(?:2TBFiles|AsyncRequests|Ex(?:clusiveLocks|tendedFileSecurity)|FS(?:CatalogSearch|ExchangeObjects)|HFSPlusAPIs|Journaling|LongNames|MultiScriptNames|NamedForks|S(?:ubtreeIterators|ymbolicLinks)|TrashVolumeCache))|T(?:akeActiveEvent|rshOffLine)|a(?:d(?:ATPSkt|B(?:tSlpErr|uffNum)|C(?:allOrderErr|hannel|ksmErr|o(?:decCharacterizationErr|mponent(?:Instance|Selector|Type)|ntrollerHeight))|D(?:BtSlp|Cksum|ataRefIndex|e(?:lim|pthErr)|ictFormat|rag(?:FlavorErr|ItemErr|RefErr))|E(?:dit(?:Index|List|ionFileErr)|nding|xtResource)|F(?:CBErr|i(?:dErr|leFormat)|o(?:lderDescErr|rmat))|I(?:mage(?:Description|Err|RgnErr)|nputText)|LocNameErr|M(?:DBErr|ovErr)|P(?:asteboard(?:FlavorErr|I(?:ndexErr|temErr)|SyncErr)|ortNameErr|rofileError|ublicMovieAtom)|R(?:eqErr|outingSizeErr)|S(?:GChannel|crapRefErr|e(?:ctionErr|rviceMethodErr)|ubPartErr)|Tra(?:ckIndex|nslation(?:RefErr|SpecErr))|UnitErr)|se(?:DblQuote|SingQuote))|dNamErr|re(?:akRecd|veMark)|t(?:DupRecErr|Key(?:AttrErr|LenErr)|NoSpace|RecNotFnd|n(?:Ctrl|State(?:Bit)?))|uf(?:2SmallErr|TooSmall|fer(?:IsSmall|sTooSmall)))|c(?:A(?:DBAddress|EList|ccessory(?:Process|Suitcase)|ddress(?:Spec)?|lias(?:File|List|OrString)|p(?:pl(?:eTalkAddress|ication(?:File|Process)?)|ril)|rc|ugust)|B(?:o(?:dyColor|olean)|usAddress)|C(?:ell|har|l(?:assIdentifier|ipping(?:File|Window)|osure)|o(?:erc(?:e(?:KataHiragana|LowerCase|OneByteToTwoByte|Remove(?:Diacriticals|Hyphens|Punctuation|WhiteSpace)|SmallKana|UpperCase|Zenkakuhankaku)|ion)|l(?:orTable|umn)|n(?:stant|t(?:ainer(?:Window)?|entSpace|rolPanelFile))))|D(?:TPWindow|e(?:cember|pthErr|sk(?:AccessoryFile|top(?:Printer)?)|v(?:Err|Spec))|isk|ocument(?:File)?|rawingArea|ynamicLibrary)|E(?:n(?:tireContents|umeration)|thernetAddress|ventIdentifier)|F(?:TPItem|ebruary|i(?:le|reWireAddress|xed(?:Point|Rectangle)?)|o(?:lder|nt(?:File|Suitcase))|r(?:ame(?:Color|work)|iday))|Gr(?:aphic(?:Line|Object|Shape|Text)|oup(?:edGraphic)?)|H(?:TML|andle(?:Breakpoint|r))|I(?:PAddress|conFamily|n(?:foWindow|sertion(?:Loc|Point)|t(?:ern(?:alFinderObject|etAddress)|l(?:Text|WritingCode)))|tem)|J(?:anuary|u(?:ly|ne))|Key(?:Form|Identifier|stroke)|L(?:abel|i(?:n(?:e|kedList)|st(?:Element|Or(?:Record|String)|RecordOrString)?)|o(?:calTalkAddress|ng(?:DateTime|Fixed(?:Point|Rectangle)?|Integer|Point|Rectangle)))|M(?:a(?:chine(?:Loc)?|rch|tchErr|y)|enu(?:Item)?|issingValue|on(?:day|th))|N(?:o(?:MemErr|vember)|umber(?:DateTimeOrString|Or(?:DateTime|String))?)|O(?:bject(?:BeingExamined|Specifier)?|ctober|nline(?:Disk|LocalDisk|RemoteDisk)|penableObject|val)|P(?:ICT|a(?:ckage|ragraph)|ixel(?:Map)?|olygon|r(?:e(?:ferences(?:Window)?|position)|o(?:ce(?:dure|ss)|perty|tectErr)))|QD(?:Point|Rectangle)|R(?:GBColor|a(?:ngeErr|wData)|e(?:al|c(?:ord|tangle)|ference|sErr)|o(?:tation|undedRectangle|w)|unningAddress)|S(?:CSIAddress|aturday|cript(?:ingAddition)?|e(?:conds|lection|ptember)|h(?:ar(?:ableContainer|ing(?:Privileges|Window))|ortInteger)|mallReal|ound(?:File)?|pecialFolders|t(?:atusWindow|orage|ring(?:Class)?)|u(?:itcase|nday)|ymbol)|T(?:able|e(?:mpMemErr|xt(?:Color|Flow|Styles)?)|hu(?:mbColor|rsday)|okenRingAddress|rash|uesday|ype)|U(?:RL|SBAddress|ndefined|ser(?:Identifier)?)|Ve(?:ctor|rsion)|W(?:e(?:dnesday|ekday)|indow|ord|ritingCodeInfo)|Zone|a(?:l(?:Arabic(?:Civil|Lunar)|Coptic|Gregorian|J(?:apanese|ewish)|Persian|l(?:NotSupportedByNodeErr|erSecuritySession))|n(?:cel|not(?:BeLeafAtomErr|DeferErr|FindAtomErr|M(?:akeContiguousErr|oveAttachedController)|SetWidthOfAttachedController)|t(?:Create(?:PickerWindow|SingleForkFile)|DoThatInCurrentMode|EnableTrack|FindHandler|GetFlavorErr|LoadP(?:ackage|ick(?:MethodErr|er))|OpenHandler|PutPublicMovieAtom|Re(?:adUtilities|ceiveFromSynthesizerOSErr)|S(?:endToSynthesizerOSErr|tepErr)))|tChangedErr|utionIcon)|bNotFound|dev(?:GenErr|MemErr|ResErr|Unset)|e(?:dilla|nt(?:eredDot|ury))|frag(?:A(?:bortClosureErr|rchitectureErr)|C(?:F(?:M(?:InternalErr|StartupErr)|ragRsrcErr)|losureIDErr|on(?:nectionIDErr|t(?:ainerIDErr|extIDErr)))|DupRegistrationErr|ExecFileRefErr|F(?:i(?:leSizeErr|rst(?:ErrCode|ReservedCode))|ragment(?:CorruptErr|FormatErr|UsageErr))|I(?:mportToo(?:NewErr|OldErr)|nit(?:AtBootErr|FunctionErr|LoopErr|OrderErr))|L(?:astErrCode|ibConnErr)|MapFileErr|No(?:ApplicationErr|ClientMemErr|IDsErr|LibraryErr|P(?:ositionErr|rivateMemErr)|RegistrationErr|S(?:ectionErr|ymbolErr)|tClosureErr)|OutputLengthErr|R(?:eservedCode_(?:1|2|3)|srcForkErr)|StdFolderErr|UnresolvedErr)|h(?:a(?:nnel(?:Busy|NotBusy)|rCodeMask)|eckBoxProc|kCtrl)|ircumflex(?:Upr(?:A|E|I|O|U))?|kSumErr|l(?:earDev|k(?:RdErr|WrErr)|os(?:Err|eDev)|rBit)|m(?:1(?:0CLRData|1CLRData|2CLRData|3CLRData|4CLRData|5CLRData|6_8ColorPacking)|24_8ColorPacking|3(?:2_(?:16ColorPacking|32ColorPacking|8ColorPacking)|CLRData)|4(?:0_8ColorPacking|8_(?:16ColorPacking|8ColorPacking)|CLRData)|5(?:6_8ColorPacking|CLRData)|6(?:4_(?:16ColorPacking|8ColorPacking)|CLRData)|7CLRData|8(?:CLRData|_8ColorPacking)|9CLRData|A(?:RGB(?:32(?:PmulSpace|Space)|64(?:L(?:PmulSpace|Space)|PmulSpace|Space))|ToB(?:0Tag|1Tag|2Tag)|b(?:ortWriteAccess|s(?:oluteColorimetric|tractClass))|lpha(?:FirstPacking|LastPacking|PmulSpace|Space)|sciiData)|B(?:ToA(?:0Tag|1Tag|2Tag)|e(?:ginAccess|stMode)|inaryData|l(?:ackPointCompensation(?:Mask)?|ue(?:ColorantTag|TRCTag))|radfordChromaticAdaptation|ufferBasedProfile)|C(?:M(?:SReservedFlagsMask|Y(?:Data|K(?:32Space|64(?:LSpace|Space)|Data|Space)))|S(?:1(?:C(?:hromTag|ustTag)|NameTag|ProfileVersion|TRCTag)|2ProfileVersion)|a(?:librationDateTimeTag|meraDeviceClass|nt(?:Co(?:ncatenateError|pyModifiedV1Profile)|Delete(?:Element|Profile)|GamutCheckError|XYZ))|h(?:arTargetTag|romaticAdaptationTag)|lose(?:Access|Spool)|o(?:lorSpace(?:AlphaMask|Class|EncodingMask|P(?:ackingMask|remulAlphaMask)|ReservedMask|Space(?:AndAlphaMask|Mask))|pyrightTag)|reateNewAccess|urrent(?:DeviceInfoVersion|Profile(?:InfoVersion|LocationSize|MajorVersion)))|D(?:e(?:fault(?:DeviceID|ProfileID)|vice(?:AlreadyRegistered|DBNotFoundErr|InfoVersion1|M(?:fgDescTag|odelDescTag)|NotRegistered|Profile(?:InfoVersion(?:1|2)|sNotFound)|State(?:AppleRsvdBits|Busy|De(?:fault|viceRsvdBits)|ForceNotify|Offline)))|isplay(?:Class|DeviceClass|Use)|raftMode)|E(?:lementTagNotFound|mbedded(?:Mask|Profile|Use(?:Mask)?)|ndAccess|rrIncompatibleProfile)|F(?:atalProfileErr|lare(?:0|100))|G(?:amut(?:CheckingMask|Result(?:1Space|Space)|Tag)|eometry(?:0(?:45or450|dord0)|Unknown)|lossy(?:MatteMask)?|r(?:ay(?:16(?:LSpace|Space)|8Space|A(?:16(?:PmulSpace|Space)|32(?:L(?:PmulSpace|Space)|PmulSpace|Space)|PmulSpace|Space)|Data|Space|TRCTag)|een(?:ColorantTag|TRCTag)))|H(?:LS(?:32Space|Data|Space)|SV(?:32Space|Data|Space))|I(?:CC(?:ProfileVersion(?:2(?:1)?|4)|ReservedFlagsMask)|lluminant(?:A|D(?:5(?:0|5)|65|93)|EquiPower|F(?:2|8)|Unknown)|n(?:dexRangeErr|put(?:Class|Use)|ter(?:nalCFErr|polationMask)|valid(?:ColorSpace|DstMap|Profile(?:Comment|Location)?|S(?:earch|rcMap)))|terate(?:AllDeviceProfiles|Cu(?:rrentDeviceProfiles|stomDeviceProfiles)|DeviceProfilesMask|FactoryDeviceProfiles))|L(?:AB(?:24Space|32Space|48(?:LSpace|Space)|Space)|UV(?:32Space|Space)|abData|i(?:n(?:e(?:arChromaticAdaptation|sPer)|kClass)|ttleEndianPacking)|ong(?:10ColorPacking|8ColorPacking)|u(?:minanceTag|vData))|M(?:C(?:Eight(?:8Space|Space)|Five(?:8Space|Space)|H(?:5Data|6Data|7Data|8Data)|S(?:even(?:8Space|Space)|ix(?:8Space|Space)))|a(?:cintosh|gicNumber|keAndModelTag)|e(?:asurementTag|dia(?:BlackPointTag|WhitePointTag)|thod(?:Error|NotFound))|icrosoft)|N(?:a(?:med(?:Color(?:2Tag|Class|NotFound|Tag)|Data|Indexed(?:32(?:LSpace|Space)|Space))|tiveDisplayInfoTag)|o(?:C(?:olorPacking|urrentProfile)|GDevicesError|ProfileBase|Space|rmalMode)|umHeaderElements)|O(?:neBitDirectPacking|pen(?:Read(?:Access|Spool)|Write(?:Access|Spool))|riginalProfileLocationSize|utput(?:Class|Use))|P(?:S(?:2(?:C(?:RD(?:0Tag|1Tag|2Tag|3Tag|VMSizeTag)|SATag)|RenderingIntentTag)|7bit|8bit)|a(?:rametricType(?:0|1|2|3|4)|thBasedProfile)|erceptual|r(?:e(?:fsSynchError|view(?:0Tag|1Tag|2Tag))|interDeviceClass|o(?:file(?:Description(?:MLTag|Tag)|Error|IterateDataVersion(?:1|2|3|4)|MajorVersionMask|NotFound|SequenceDescTag|sIdentical)|of(?:DeviceClass|Use))|trDefaultScreens))|QualityMask|R(?:GB(?:16(?:LSpace|Space)|24Space|32Space|48(?:LSpace|Space)|565(?:LSpace|Space)|A(?:32(?:PmulSpace|Space)|64(?:L(?:PmulSpace|Space)|PmulSpace|Space)|PmulSpace|Space)|Data|Space)|angeOverFlow|e(?:ad(?:Access|Spool)|d(?:ColorantTag|TRCTag)|flective(?:TransparentMask)?|lativeColorimetric|servedSpace(?:1|2)|verseChannelPacking))|S(?:RGB(?:16ChannelEncoding|Data)|aturation|c(?:annerDeviceClass|reening(?:DescTag|Tag))|earchError|i(?:g(?:C(?:rdInfoType|urveType)|Dat(?:aType|eTimeType)|Lut(?:16Type|8Type)|M(?:akeAndModelType|easurementType|ulti(?:Funct(?:A2BType|B2AType)|LocalizedUniCodeType))|Na(?:medColor(?:2Type|Type)|tiveDisplayInfoType)|P(?:S2CRDVMSizeType|arametricCurveType|rofile(?:DescriptionType|SequenceDescType))|S(?:15Fixed16Type|creeningType|ignatureType)|TextType|U(?:1(?:6Fixed16Type|Fixed15Type)|Int(?:16Type|32Type|64Type|8Type)|crBgType|nicodeTextType)|Vi(?:deoCardGammaType|ewingConditionsType)|XYZType)|liconGraphics)|olaris|potFunction(?:Cross|D(?:efault|iamond)|Ellipse|Line|Round|Square|Unknown)|tdobs(?:19(?:31TwoDegrees|64TenDegrees)|Unknown))|T(?:aligent|echnology(?:AMDisplay|CRTDisplay|D(?:igitalCamera|yeSublimationPrinter)|Electro(?:photographicPrinter|staticPrinter)|F(?:ilm(?:Scanner|Writer)|lexography)|Gravure|InkJetPrinter|OffsetLithography|P(?:MDisplay|hoto(?:CD|ImageSetter|graphicPaperPrinter)|rojectionTelevision)|ReflectiveScanner|Silkscreen|T(?:ag|hermalWaxPrinter)|Video(?:Camera|Monitor)))|U(?:crBgTag|nsupportedDataType|seDefaultChromaticAdaptation)|V(?:i(?:deoCardGamma(?:FormulaType|Ta(?:bleType|g))|ewingConditions(?:DescTag|Tag))|onKriesChromaticAdaptation)|W(?:ord5(?:65ColorPacking|ColorPacking)|rite(?:Access|Spool))|XYZ(?:24Space|32Space|48(?:LSpace|Space)|Data|Space)|Y(?:CbCrData|XY(?:32Space|Space)|xyData)|apFontTableTag|dKey(?:Bit)?|p(?:Alias(?:NoFlags|OnlyThisFile)|IsMissing|ThreadSafe|WantsRegisterMessage))|o(?:dec(?:AbortErr|BadDataErr|C(?:ant(?:QueueErr|WhenErr)|onditionErr)|D(?:ataVersErr|isabledErr|roppedFrameErr)|E(?:rr|xtensionNotFoundErr)|ImageBufErr|N(?:eed(?:AccessKeyErr|ToFlushChainErr)|o(?:MemoryPleaseWaitErr|thingToBlitErr))|O(?:ffscreenFailed(?:Err|PleaseRetryErr)|penErr)|ParameterDialogConfirm|S(?:creenBufErr|izeErr|poolErr)|UnimpErr|WouldOffscreenErr)|l(?:lection(?:I(?:ndexRangeErr|tem(?:LockedErr|NotFoundErr))|VersionErr)|or(?:SyncNotInstalled|sRequestedErr))|mponent(?:AutoVersionIncludeFlags|D(?:ll(?:EntryNotFoundErr|LoadErr)|o(?:AutoVersion|ntRegister))|HasMultiplePlatforms|LoadResident|Not(?:Captured|ThreadSafeErr)|WantsUnregister)|n(?:nectionInvalid|straintReachedErr|t(?:ainer(?:AlreadyOpenWrn|NotFoundWrn)|rol(?:Err|HandleInvalidErr|InvalidDataVersionErr|Key(?:Bit)?|P(?:anelFolderIconResource|roperty(?:Invalid|NotFoundErr))|ler(?:BoundsNotExact|HasFixedHeight))))|pyDev|r(?:Err|eFoundationUnknownErr)|uld(?:Not(?:ParseSourceFileErr|ResolveDataRef|UseAnExistingSample)|ntGetRequiredComponent))|rash|trlItem|u(?:r(?:NumberPartsVersion|r(?:LeadingZ|NegSym|SymLead|TrailingZ|ent(?:CurLang|DefLang))|sorDev)|tDev))|d(?:BoxProc|InstErr|RemovErr|a(?:t(?:a(?:Already(?:Closed|OpenForWrite)|No(?:DataRef|tOpenFor(?:Read|Write))|VerErr)|e(?:StdMask|Time(?:Invalid|NotFound)))|y(?:Field|LdingZ|Mask|Of(?:Week(?:Field|Mask)|Year(?:Field|Mask))))|blDagger|c(?:eExtErr|m(?:B(?:ad(?:D(?:ataSizeErr|ictionaryErr)|F(?:eatureErr|i(?:eld(?:InfoErr|TypeErr)|ndMethodErr))|KeyErr|PropertyErr)|lockFullErr|ufferOverflowErr)|D(?:ictionary(?:BusyErr|NotOpenErr)|upRecordErr)|IterationCompleteErr|N(?:ecessaryFieldErr|o(?:AccessMethodErr|FieldErr|RecordErr|tDictionaryErr))|P(?:aramErr|ermissionErr|rotectedErr)|TooManyKeyErr))|dp(?:LenErr|SktErr)|e(?:activDev|bugging(?:Duplicate(?:OptionErr|SignatureErr)|ExecutionContextErr|Invalid(?:NameErr|OptionErr|SignatureErr)|No(?:CallbackErr|MatchErr))|fault(?:Component(?:Any(?:Flags(?:AnyManufacturer(?:AnySubType)?)?|Manufacturer|SubType)|Identical)|PhysicalEntryCount)|limPad|s(?:criptorFontTableTag|k(?:PatID|top(?:DamagedErr|IconResource))|tPortErr)|viceCantMeetRequest)|i(?:a(?:eresisUpr(?:E|I|Y)|log(?:Kind|NoTimeoutErr))|ffVolErr|giUnimpErr|r(?:FulErr|NFErr|ectXObjectAlreadyExists)|sk(?:Evt|Mask))|my|o(?:All|Color|F(?:ace|ont)|Size|Toggle|cumentProc|m(?:Cannot|Native|TranslateFirst|Wildcard)|tlessLwrI|ubleAcute)|r(?:agNotAcceptedErr|iver(?:Evt|HardwareGoneErr|Mask)|opFolderIconResource|vQType)|s(?:32BitMode|AddressErr|B(?:ad(?:L(?:aunch|ibrary)|Patch(?:Header)?|S(?:ANEOpcode|lotInt|tartupDisk))|u(?:fPtrTooLow|sError))|C(?:DEFNotFound|antHoldSystemHeap|hkErr|oreErr)|Di(?:rtyDisk|sassemblerInstalled)|ExtensionsDisabled|F(?:PErr|SErr|inderErr|orcedQuit)|G(?:ibblyMovedToDisabledFolder|reeting)|H(?:D20Installed|MenuFindErr)|I(?:OCoreErr|llInstErr|rqErr)|L(?:ine(?:AErr|FErr)|o(?:adErr|stConnectionToNetworkDisk))|M(?:B(?:ATA(?:PISysError|SysError)|ExternFlpySysError|FlpySysError|SysError|arNFnd)|DEFNotFound|ac(?:OSROMVersionTooOld|sBugInstalled)|emFullErr|i(?:scErr|xedModeFailure)|ustUseFCBAccessors)|N(?:eedToWriteBootBlocks|o(?:Exts(?:Disassembler|MacsBug)|FPU|P(?:a(?:ckErr|tch)|k(?:1|2|3|4|5|6|7))|t(?:EnoughRAMToBoot|The1)))|O(?:ldSystem|vflowErr)|P(?:CCardATASysError|arityErr|rivErr)|R(?:AMDiskTooBig|e(?:insert|moveDisk))|S(?:CSIWarn|hutDownOrRes(?:tart|ume)|tknHeap|witchOffOrRestart|ys(?:Err|tem(?:FileErr|RequiresPowerPC)))|TraceErr|UnBootableSystem|VM(?:BadBackingStore|DeferredFuncTableFull)|W(?:DEFNotFound|riteToSupervisorStackGuardPage)|ZeroDivErr|kFulErr)|tQType|u(?:mmyType|p(?:FNErr|licate(?:AtomTypeAndIDErr|F(?:lavorErr|olderDescErr)|HandlerErr|PasteboardFlavorErr|RoutingErr|ScrapFlavorErr))|ration(?:Day|Forever|Hour|Mi(?:crosecond|llisecond|nute)|NoWait|Second))|ym)|e(?:A(?:DB|ddressSpec|nalogAudio|ppleTalk|udio(?:Line(?:In|Out)|Out))|Bus|C(?:DROM|apsLockDown|learKey|o(?:mm(?:Slot|andDown)|n(?:duit|trolDown)))|D(?:VD|e(?:leteKey|viceType)|i(?:gitalAudio|splay)|ownArrowKey)|E(?:n(?:dKey|terKey)|scapeKey|thernet)|F(?:1(?:0Key|1Key|2Key|3Key|4Key|5Key|Key)|2Key|3Key|4Key|5Key|6Key|7Key|8Key|9Key|ireWire|loppy|orwardDelKey)|H(?:D|elpKey|omeKey)|I(?:P|RTalk|nfrared|rDA)|Key(?:Kind|board)|L(?:CD|e(?:ftArrowKey|nErr)|ocalTalk)|M(?:ac(?:IP|Video)|icrophone|o(?:d(?:em(?:P(?:ort|rinterPort))?|ifiers)|nitorOut|use)|ultiErr)|NuBus(?:Card)?|OptionDown|P(?:C(?:I(?:bus|card)|card)|DS(?:card|slot)|PP|age(?:DownKey|UpKey)|o(?:intingDevice|stScript)|r(?:inter(?:Port)?|otocol))|R(?:eturnKey|ightArrowKey)|S(?:CSI|VGA|cheme|erial|hiftDown|peakers|torageDevice|video)|T(?:abKey|okenRing|rack(?:ball|pad))|U(?:SB|pArrowKey)|Video(?:In|Monitor|Out)|WS(?:ArrayType|BooleanType|D(?:at(?:aType|eType)|ictionaryType|oubleType)|IntegerType|NullType|StringType|UnknownType)|dit(?:Text|i(?:ngNotAllowed|onMgrInitErr))|mptyPathErr|n(?:dOfDataReached|um(?:A(?:fterDate|l(?:iases|l(?:D(?:isks|ocuments)|LocalDisks|OpenFolders|RemoteDisks))|nyDate|rr(?:angement|ows))|B(?:e(?:foreDate|tweenDate)|ooleanValues)|Con(?:flicts|sid(?:erations|sAndIgnores))|Date|ExistingItems|Fo(?:lders|ntsPanel)|Ge(?:neralPanel|stalt)|I(?:conSize|nfoWindowPanel)|Justification|KeyForm|La(?:rgeIconSize|st(?:Month|Week|Year))|M(?:emoryPanel|i(?:niIconSize|scValues))|O(?:lderItems|nDate)|P(?:osition|r(?:efs(?:ButtonViewPanel|GeneralPanel|IconViewPanel|L(?:abelPanel|istViewPanel)|WindowPanel)|otection))|Quality|S(?:aveOptions|haringPanel|mallIconSize|ortDirection(?:Normal|Reverse)?|t(?:at(?:ionery|usNConfigPanel)|yle))|T(?:his(?:Month|Week|Year)|oday|ransferMode)|ViewBy|Where|Yesterday)|v(?:BadVers|NotPresent|VersTooBig))|ofErr|r(?:a(?:Field|Mask)|r(?:A(?:E(?:AccessorNotFound|B(?:ad(?:KeyForm|ListItem|TestKey)|u(?:fferTooSmall|ildSyntaxError))|C(?:ant(?:HandleClass|PutThatThere|SupplyType|Undo)|o(?:ercionFail|rruptData))|D(?:esc(?:IsNull|NotFound)|uplicateHandler)|E(?:mptyListContainer|vent(?:F(?:ailed|iltered)|Not(?:Handled|Permitted)|WouldRequireUserConsent))|HandlerNotFound|I(?:llegalIndex|mpossibleRange|n(?:Transaction|dexTooLarge))|LocalOnly|N(?:e(?:gativeCount|werVersion)|o(?:Such(?:Logical|Object|Transaction)|User(?:Interaction|Selection)|t(?:A(?:EDesc|S(?:ingleObject|pecialFunction)|n(?:E(?:lement|numMember)|ObjSpec)|ppleEvent)|Modifiable)))|P(?:aramMissed|r(?:ivilegeError|opertiesClash))|Re(?:adDenied|c(?:eive(?:EscapeCurrent|Terminate)|ordingIsAlreadyOn)|plyNot(?:Arrived|Valid))|Stream(?:AlreadyConverted|BadNesting)|T(?:argetAddressNotPermitted|imeout|ypeError)|Unknown(?:AddressType|ObjectType|SendMode)|ValueOutOfRange|W(?:aitCanceled|r(?:iteDenied|ong(?:DataType|NumberArgs))))|S(?:CantCo(?:mpareMoreThan32k|nsiderAndIgnore)|I(?:llegalFormalParameter|nconsistentNames)|NoResultReturned|ParameterNotForEvent|TerminologyNestingTooDeep)|borted|lreadyInImagingMode|ttention|uthorization(?:BadAddress|Canceled|Denied|ExternalizeNotAllowed|In(?:ter(?:actionNotAllowed|nal(?:izeNotAllowed)?)|valid(?:Flags|Pointer|Ref|Set|Tag))|Success|ToolE(?:nvironmentError|xecuteFailure)))|C(?:an(?:NotInsertWhileWalkProcInProgress|notUndo|tEmbed(?:IntoSelf|Root))|o(?:ntrol(?:DoesntSupportFocus|HiddenOrDisabled|IsNotEmbedder|sAlreadyExist)|r(?:eEndianData(?:DoesNotMatchFormat|Too(?:LongForFormat|ShortForFormat))|ruptWindowDescription)|uldntSetFocus)|pp(?:General|Last(?:SystemDefinedError|UserDefinedError)|bad_(?:alloc|cast|exception|typeid)|domain_error|i(?:nvalid_argument|os_base_failure)|l(?:ength_error|ogic_error)|o(?:ut_of_range|verflow_error)|r(?:ange_error|untime_error)|underflow_error))|D(?:SPQueueSize|ata(?:Browser(?:I(?:nvalidProperty(?:Data|Part)|temNot(?:Added|Found))|NotConfigured|PropertyNot(?:Found|Supported))|NotSupported|SizeMismatch))|E(?:mptyScrap|n(?:dOf(?:Body|Document)|gineNotFound))|F(?:S(?:AttributeNotFound|Bad(?:AllocFlags|Buffer|F(?:SRef|ork(?:Name|Ref))|I(?:nfoBitmap|te(?:mCount|ratorFlags))|PosMode|SearchParams)|Fork(?:Exists|NotFound)|IteratorNot(?:Found|Supported)|Missing(?:CatInfo|Name)|N(?:ameTooLong|o(?:MoreItems|t(?:AFolder|EnoughSpaceForOperation)))|OperationNotSupported|PropertyNotValid|QuotaExceeded|RefsDifferent|UnknownCall)|inder(?:AppFolderProtected|B(?:adPackageContents|oundsWrong)|C(?:an(?:notPutAway|t(?:DeleteImmediately|Move(?:Source|To(?:Ancestor|Destination))|Overwrite|UseTrashedItems))|orruptOpenFolderList)|FileSharingMustBeOn|I(?:ncestuousMove|sBusy|temAlreadyInDest)|L(?:astReserved|ockedItemsInTrash)|MustBeActive|NoInvisibleFiles|OnlyLockedItemsInTrash|Pro(?:gramLinkingMustBeOn|perty(?:DoesNotApply|NowWindowBased))|S(?:harePointsCantInherit|ysFolderProtected)|Un(?:knownUser|supportedInsidePackages)|VolumeNotFound|Window(?:MustBe(?:ButtonView|IconView|ListView)|NotOpen|WrongType))|loatingWindowsNotInitialized|wdReset)|HMIllegalContentForM(?:aximumState|inimumState)|I(?:A(?:AllocationErr|BufferTooSmall|Canceled|EndOfTextRun|InvalidDocument|No(?:Err|MoreItems)|ParamErr|TextExtractionErr|UnknownErr)|nvalid(?:PartCode|Range|Window(?:P(?:roperty|tr)|Ref))|te(?:m(?:AlreadyInTree|Not(?:Control|FoundInTree))|ratorReachedEnd))|K(?:B(?:Fail(?:Setting(?:ID|TranslationTable)|WritePreference)|IlligalParameters|PS2KeyboardNotAvailable)|C(?:AuthFailed|BufferTooSmall|CreateChainFailed|D(?:ata(?:Not(?:Available|Modifiable)|TooLarge)|uplicate(?:Callback|Item|Keychain))|I(?:n(?:teraction(?:NotAllowed|Required)|valid(?:Callback|ItemRef|Keychain|SearchRef))|temNotFound)|KeySizeNotAllowed|No(?:CertificateModule|DefaultKeychain|PolicyModule|S(?:torageModule|uch(?:Attr|Class|Keychain))|tAvailable)|ReadOnly(?:Attr)?|WrongKCVersion))|M(?:arginWilllNotFit|essageNotSupported)|N(?:eedsCompositedWindow|o(?:HiliteText|RootControl|nContiuousAttribute|t(?:InImagingMode|ValidTree)))|O(?:SA(?:AppNotHighLevelEventAware|BadS(?:elector|torageType)|C(?:ant(?:A(?:ccess|ssign)|C(?:oerce|reate)|GetTerminology|Launch|OpenComponent|StorePointers)|o(?:mponentMismatch|rrupt(?:Data|Terminology)))|D(?:ata(?:BlockTooLarge|Format(?:Obsolete|TooNew))|ivideByZero)|GeneralError|In(?:ternalTableOverflow|validID)|N(?:oSuchDialect|umericOverflow)|RecordingIsAlreadyOn|S(?:criptError|ourceNotAvailable|tackOverflow|ystemError)|TypeError)|ffset(?:I(?:nvalid|sOutsideOfView)|NotOnElementBounday)|pen(?:Denied|ing))|R(?:e(?:adOnlyText|fNum)|ootAlreadyExists)|S(?:SL(?:ATS(?:C(?:ertificate(?:HashAlgorithmViolation|TrustViolation)|iphersuiteViolation)|LeafCertificateHashAlgorithmViolation|Minimum(?:KeySizeViolation|VersionViolation)|Violation)|B(?:ad(?:C(?:ert(?:ificateStatusResponse)?|ipherSuite|onfiguration)|RecordMac)|ufferOverflow)|C(?:ert(?:Expired|NotYetValid|ificateRequired)|l(?:ient(?:CertRequested|HelloReceived)|osed(?:Abort|Graceful|NoNotify))|on(?:figurationFailed|nectionRefused)|rypto)|Dec(?:o(?:deError|mpressFail)|ryptionFail)|FatalAlert|H(?:andshakeFail|ostNameMismatch)|I(?:llegalParam|n(?:appropriateFallback|ternal))|M(?:issingExtension|oduleAttach)|N(?:e(?:gotiation|tworkTimeout)|oRootCert)|P(?:eer(?:A(?:ccessDenied|uthCompleted)|Bad(?:Cert|RecordMac)|Cert(?:Expired|Revoked|Unknown)|Dec(?:o(?:deError|mpressFail)|rypt(?:Error|ionFail))|ExportRestriction|HandshakeFail|In(?:sufficientSecurity|ternalError)|NoRenegotiation|ProtocolVersion|RecordOverflow|U(?:n(?:expectedMsg|knownCA|supportedCert)|serCancelled))|rotocol)|RecordOverflow|SessionNotFound|TransportReset|Un(?:expected(?:Message|Record)|known(?:PSKIdentity|RootCert)|recognizedName|supportedExtension)|W(?:eakPeerEphemeralDHKey|ouldBlock)|XCertChainInvalid)|e(?:c(?:A(?:CL(?:AddFailed|ChangeFailed|DeleteFailed|NotSimple|ReplaceFailed)|ddin(?:LoadFailed|UnloadFailed)|l(?:gorithmMismatch|locate|readyLoggedIn)|pple(?:AddAppACLSubject|InvalidKey(?:EndDate|StartDate)|PublicKeyIncomplete|S(?:SLv2Rollback|ignatureMismatch))|tt(?:achHandleBusy|ributeNotInContext)|uthFailed)|B(?:adReq|lockSizeMismatch|ufferTooSmall)|C(?:RL(?:AlreadySigned|BadURI|Expired|Not(?:Found|Trusted|ValidYet)|PolicyFailed|ServerDown)|S(?:AmbiguousBundleFormat|Bad(?:BundleFormat|CallbackValue|Di(?:ctionaryFormat|skImageFormat)|FrameworkVersion|LVArch|MainExecutable|NestedCode|ObjectFormat|Resource|TeamIdentifier)|C(?:MSTooLarge|ancelled)|D(?:B(?:Access|Denied)|SStoreSymlink|bCorrupt)|FileHardQuarantined|GuestInvalid|H(?:elperFailed|ost(?:Protocol(?:Contradiction|DedicationError|Invalid(?:Attribute|Hash)|NotProxy|RelativePath|StateError|Unrelated)|Reject))|In(?:foPlistFailed|ternalError|valid(?:A(?:ssociatedFileData|ttributeValues)|Entitlements|Flags|ObjectRef|Platform|RuntimeVersion|Symlink|TeamIdentifier))|MultipleGuests|No(?:Ma(?:inExecutable|tches)|SuchCode|t(?:A(?:Host|ppLike)|Supported))|O(?:bjectRequired|utdated)|Re(?:gularFile|q(?:Failed|Invalid|Unsupported)|source(?:DirectoryFailed|NotSupported|RulesInvalid|s(?:Invalid|Not(?:Found|Sealed)))|vokedNotarization)|S(?:ig(?:DB(?:Access|Denied)|nature(?:Failed|Invalid|NotVerifiable|Un(?:supported|trusted)))|taticCode(?:Changed|NotFound))|TooBig|Un(?:implemented|s(?:ealed(?:AppRoot|FrameworkRoot)|igned(?:NestedCode)?|upported(?:DigestAlgorithm|GuestAttributes)))|Vetoed|WeakResource(?:Envelope|Rules))|allbackFailed|ertificate(?:CannotOperate|Expired|N(?:ameNotAllowed|otValidYet)|PolicyNotAllowed|Revoked|Suspended|ValidityPeriodTooLong)|o(?:deSigning(?:Bad(?:CertChainLength|PathLengthConstraint)|Development|No(?:BasicConstraints|ExtendedKeyUsage))|nversionError|reFoundationUnknown)|reateChainFailed)|D(?:ata(?:Not(?:Available|Modifiable)|TooLarge|baseLocked|storeIsOpen)|e(?:code|vice(?:Error|Failed|Reset|VerifyFailed))|iskFull|skFull|uplicate(?:Callback|Item|Keychain))|E(?:MM(?:LoadFailed|UnloadFailed)|ndOfData|ventNotificationCallbackNotFound|xtendedKeyUsageNotCritical)|F(?:i(?:eldSpecifiedMultiple|leTooBig)|unction(?:Failed|IntegrityFail))|HostNameMismatch|I(?:DPFailure|O|n(?:DarkWake|comp(?:atible(?:DatabaseBlob|FieldFormat|KeyBlob|Version)|leteCertRevocationCheck)|putLengthError|sufficientC(?:lientID|redentials)|ter(?:action(?:NotAllowed|Required)|nal(?:Component|Error))|val(?:dCRLAuthority|id(?:A(?:CL|c(?:cess(?:Credentials|Request)|tion)|ddinFunctionTable|lgorithm(?:Parms)?|ttribute(?:AccessCredentials|B(?:ase|lockSize)|DLDBHandle|E(?:ffectiveBits|ndDate)|I(?:nitVector|terationCount)|Key(?:Length|Type)?|Label|Mode|OutputSize|P(?:a(?:dding|ssphrase)|ri(?:me|vateKeyFormat)|ublicKeyFormat)|R(?:andom|ounds)|S(?:alt|eed|tartDate|ubprime|ymmetricKeyFormat)|Version|WrappedKeyFormat)|uthority(?:KeyID)?)|B(?:aseACLs|undleInfo)|C(?:RL(?:Encoding|Group|Index|Type)?|allback|ert(?:Authority|ificate(?:Group|Ref))|ontext)|D(?:BL(?:ist|ocation)|ata(?:baseBlob)?|igestAlgorithm)|E(?:ncoding|xtendedKeyUsage)|FormType|GUID|Handle(?:Usage)?|I(?:D(?:Linkage)?|dentifier|n(?:dex(?:Info)?|putVector)|temRef)|Key(?:AttributeMask|Blob|Format|Hierarchy|Label|Ref|Usage(?:ForPolicy|Mask)|chain)|LoginName|ModifyMode|N(?:ame|e(?:tworkAddress|wOwner)|umberOfFields)|O(?:utputVector|wnerEdit)|P(?:VC|a(?:rsingModule|ss(?:throughID|wordRef))|o(?:inter|licyIdentifiers)|refsDomain)|Query|R(?:e(?:ason|cord|quest(?:Inputs|or)|sponseVector)|oot)|S(?:ampleValue|cope|e(?:archRef|rviceMask)|ignature|topOnPolicy|ub(?:ServiceID|ject(?:KeyID|Name)))|T(?:imeString|rustSetting(?:s)?|uple(?:Credendtials|Group)?)|Val(?:idityPeriod|ue))))|temNotFound)|Key(?:BlobTypeIncorrect|HeaderInconsistent|IsSensitive|SizeNotAllowed|UsageIncorrect)|LibraryReferenceNotFound|M(?:DSError|emoryError|issing(?:A(?:lgorithmParms|ttribute(?:AccessCredentials|B(?:ase|lockSize)|DLDBHandle|E(?:ffectiveBits|ndDate)|I(?:nitVector|terationCount)|Key(?:Length|Type)?|Label|Mode|OutputSize|P(?:a(?:dding|ssphrase)|ri(?:me|vateKeyFormat)|ublicKeyFormat)|R(?:andom|ounds)|S(?:alt|eed|tartDate|ubprime|ymmetricKeyFormat)|Version|WrappedKeyFormat))|Entitlement|RequiredExtension|Value)|o(?:bileMe(?:CSRVerifyFailure|FailedConsistencyCheck|NoRequestPending|Request(?:AlreadyPending|Queued|Redirected)|Server(?:AlreadyExists|Error|NotAvailable|ServiceErr))|dule(?:Man(?:ager(?:InitializeFailed|NotFound)|ifestVerifyFailed)|NotLoaded))|ultiple(?:ExecSegments|PrivKeys|ValuesUnsupported))|N(?:etworkFailure|o(?:AccessForItem|BasicConstraints(?:CA)?|CertificateModule|Default(?:Authority|Keychain)|FieldValues|PolicyModule|S(?:torageModule|uch(?:Attr|Class|Keychain))|TrustSettings|t(?:Available|Initialized|LoggedIn|Signer|Trusted)))|O(?:CSP(?:BadRe(?:quest|sponse)|No(?:Signer|tTrustedToAnchor)|Respon(?:der(?:InternalError|MalformedReq|SignatureRequired|TryLater|Unauthorized)|seNonceMismatch)|S(?:ignatureError|tatusUnrecognized)|Unavailable)|pWr|utputLengthError)|P(?:VC(?:AlreadyConfigured|ReferentNotFound)|a(?:ram|ssphraseRequired|thLengthConstraintExceeded)|kcs12VerifyFailure|olicyNotFound|rivilegeNot(?:Granted|Supported)|ublicKeyInconsistent)|Qu(?:erySizeUnknown|otaExceeded)|Re(?:adOnly(?:Attr)?|cordModified|jectedForm|quest(?:Descriptor|Lost|Rejected)|sourceSignBad(?:CertChainLength|ExtKeyUsage))|S(?:MIME(?:Bad(?:ExtendedKeyUsage|KeyUsage)|EmailAddressesNotFound|KeyUsageNotCritical|NoEmailAddress|SubjAltNameNotCritical)|SLBadExtendedKeyUsage|e(?:lfCheckFailed|rviceNotAvailable)|igningTimeMissing|tagedOperation(?:InProgress|NotStarted)|uccess)|T(?:agNotFound|imestamp(?:AddInfoNotAvailable|Bad(?:Alg|DataFormat|Request)|Invalid|Missing|NotTrusted|Re(?:jection|vocation(?:Notification|Warning))|S(?:erviceNotAvailable|ystemFailure)|TimeNotAvailable|Unaccepted(?:Extension|Policy)|Waiting)|rust(?:NotAvailable|SettingDeny))|U(?:n(?:implemented|known(?:C(?:RLExtension|ertExtension|riticalExtensionFlag)|Format|QualifiedCertStatement|Tag)|supported(?:AddressType|F(?:ieldFormat|ormat)|IndexInfo|Key(?:AttributeMask|Format|Label|Size|UsageMask)|Locality|Num(?:Attributes|Indexes|RecordTypes|SelectionPreds)|Operator|QueryLimits|Service|VectorOfBuffers))|serCanceled)|Verif(?:icationFailure|y(?:ActionFailed|Failed))|Wr(?:Perm|ongSecVersion))|ssion(?:AuthorizationDenied|In(?:ternal|valid(?:Attributes|Flags|Id))|Success|ValueNotSet))|tate)|T(?:askNotFound|opOf(?:Body|Document)|reeIsLocked)|U(?:n(?:known(?:AttributeTag|Control|Element)|recognizedWindowClass|supportedWindowAttributesForClass)|serWantsToDragWindow)|W(?:S(?:InternalError|ParseError|T(?:imeoutError|ransportError))|indow(?:Does(?:Not(?:FitOnscreen|HaveProxy)|ntSupportFocus)|NotFound|PropertyNotFound|RegionCodeInvalid|sAlreadyInitialized))))|url(?:A(?:FP|T)|EPPC|F(?:TP|ile)|Gopher|HTTP(?:S)?|IMAP|L(?:DAP|aunch)|M(?:ail(?:box)?|essage|ulti)|N(?:FS|NTP|ews)|POP|RTSP|SNews|Telnet|Unknown)|v(?:Type|e(?:nt(?:AlreadyPostedErr|ClassIn(?:correctErr|validErr)|DeferAccessibilityEventErr|H(?:andlerAlreadyInstalledErr|otKey(?:ExistsErr|InvalidErr))|InternalErr|KindIncorrectErr|Loop(?:QuitErr|TimedOutErr)|Not(?:HandledErr|InQueueErr)|Pa(?:rameterNotFoundErr|ssToNextTargetErr)|TargetBusyErr)|ryEvent)|tNotEnb)|x(?:UserBreak|cessCollsns|t(?:FSErr|en(?:dedBlock(?:Len)?|sionsFolderIconResource)|ra(?:ctErr|neousStrings))))|f(?:B(?:adPartsTable|estGuess|syErr)|D(?:esktop|isk)|E(?:mptyFormatString|xtra(?:Decimal|Exp|Percent|Separator))|Form(?:StrIsNAN|atO(?:K|verflow))|HasBundle|Invisible|LckdErr|Missing(?:Delimiter|Literal)|Negative|O(?:nDesk|utOfSynch)|Positive|SpuriousChars|Trash|VNumber|Zero|a(?:ceBit|talDateTime)|e(?:ature(?:FontTableTag|Unsupported)|tchReference)|i(?:Ligature|d(?:Exists|NotFound)|eldOrderNotIntl|le(?:BoundsErr|OffsetTooBigErr)|rst(?:DskErr|PickerError))|l(?:Ligature|avor(?:DataPromised|NotSaved|S(?:ender(?:Only|Translated)|ystemTranslated)|Type(?:HFS|PromiseHFS))|o(?:at(?:GrowProc|Proc|Side(?:GrowProc|Proc|Zoom(?:GrowProc|Proc))|Zoom(?:GrowProc|Proc))|ppyIconResource))|mt(?:1Err|2Err)|n(?:OpnErr|fErr)|o(?:nt(?:Bit|DecError|Not(?:Declared|OutlineErr)|Panel(?:FontSelectionQDStyleVersionErr|S(?:electionStyleErr|howErr))|SubErr|sFolderIconResource)|r(?:ceRead(?:Bit|Mask)|m(?:A(?:bsolutePosition|lias)|Creator|Name|PropertyID|R(?:ange|elativePosition)|Test|U(?:niqueID|serPropertyID)|Whose)))|raction|s(?:AtMark|CurPerm|D(?:SIntErr|ataTooBigErr)|From(?:LEOF|Mark|Start)|QType|R(?:d(?:AccessPerm|DenyPerm|Perm|Wr(?:Perm|ShPerm))|nErr|t(?:DirID|ParID))|SB(?:A(?:ccessDate(?:Bit)?|ttributeModDate(?:Bit)?)|Dr(?:BkDat(?:Bit)?|CrDat(?:Bit)?|FndrInfo(?:Bit)?|MdDat(?:Bit)?|NmFls(?:Bit)?|ParID(?:Bit)?|UsrWds(?:Bit)?)|F(?:l(?:Attrib(?:Bit)?|BkDat(?:Bit)?|CrDat(?:Bit)?|FndrInfo(?:Bit)?|LgLen(?:Bit)?|MdDat(?:Bit)?|P(?:arID(?:Bit)?|yLen(?:Bit)?)|R(?:LgLen(?:Bit)?|PyLen(?:Bit)?)|XFndrInfo(?:Bit)?)|ullName(?:Bit)?)|GroupID(?:Bit)?|N(?:egate(?:Bit)?|odeID(?:Bit)?)|P(?:artialName(?:Bit)?|ermissions(?:Bit)?)|Skip(?:HiddenItems(?:Bit)?|PackageContents(?:Bit)?)|UserID(?:Bit)?)|UnixPriv|Wr(?:AccessPerm|DenyPerm|Perm)|m(?:B(?:adF(?:FSNameErr|SD(?:LenErr|VersionErr))|usyFFSErr)|DuplicateFSIDErr|FFSNotFoundErr|NoAlternateStackErr|UnknownFSMMessageErr))|ullTrashIconResource)|g(?:WorldsNotSameDepthAndSizeErr|crOnMFMErr|e(?:n(?:CdevRangeBit|eric(?:ApplicationIconResource|CDROMIconResource|D(?:eskAccessoryIconResource|ocumentIconResource)|E(?:ditionFileIconResource|xtensionIconResource)|F(?:ileServerIconResource|olderIconResource)|HardDiskIconResource|MoverObjectIconResource|PreferencesIconResource|QueryDocumentIconResource|RAMDiskIconResource|S(?:tationeryIconResource|uitcaseIconResource)))|stalt(?:16Bit(?:AudioSupport|SoundIO)|20thAnniversary|32Bit(?:Addressing|Capable|QD(?:1(?:1|2|3))?|SysZone)|68(?:0(?:00|10|20|30(?:MMU)?|40(?:FPU|MMU)?)|8(?:51|8(?:1|2))|k)|8BitQD|A(?:DB(?:ISOKbdII|KbdII)|FPClient(?:3_(?:5|6(?:_(?:1|2|3))?|7(?:_2)?|8(?:_(?:1|3|4))?)|AttributeMask|CfgRsrc|MultiReq|SupportsIP|V(?:MUI|ersionMask))?|LM(?:Attr|Has(?:CFMSupport|RescanNotifiers|SF(?:Group|Location))|Present|Vers)|MU|T(?:A(?:Attr|Present)|SU(?:AscentDescentControlsFeature|B(?:atchBreakLinesFeature|iDiCursorPositionFeature|yCharacterClusterFeature)|D(?:ecimalTabFeature|irectAccess|ropShadowStyleFeature)|F(?:allbacks(?:Feature|ObjFeatures)|eatures)|GlyphBoundsFeature|Highlight(?:ColorControlFeature|InactiveTextFeature)|IgnoreLeadingFeature|L(?:ayoutC(?:acheClearFeature|reateAndCopyFeature)|ineControlFeature|owLevelOrigFeatures)|MemoryFeature|NearestCharLineBreakFeature|PositionToCursorFeature|StrikeThroughStyleFeature|T(?:abSupportFeature|extLocatorUsageFeature|rackingFeature)|U(?:nderlineOptionsStyleFeature|pdate(?:1|2|3|4|5|6|7))|Version)|alkVersion)|UXVersion|VLTree(?:Attr|PresentBit|Supports(?:HandleBasedTreeBit|TreeLockingBit))|WS(?:6150_6(?:0|6)|8(?:150_(?:110|80)|550)|9150_(?:120|80))|d(?:dressingModeAttr|minFeaturesFlagsAttr)|l(?:iasMgr(?:Attr|FollowsAliasesWhenResolving|Pre(?:fersPath|sent)|Re(?:quiresAccessors|solveAliasFileWithMountOptions)|Supports(?:AOCEKeychain|ExtendedCalls|FSCalls|RemoteAppletalk))|legroQD(?:Text)?|tivecRegistersSwappedCorrectlyBit)|ntiAliasedTextAvailable|pp(?:earance(?:Attr|CompatMode|Exists|Version)|le(?:Adjust(?:ADBKbd|ISOKbd|Keypad)|Events(?:Attr|Present)|Guide(?:IsDebug|Present)|Script(?:Attr|P(?:owerPCSupport|resent)|Version)|TalkVersion))|rbitorAttr|syncSCSI(?:INROM)?)|Bu(?:iltInSoundInput|sClkSpeed(?:MHz)?)|C(?:FM(?:99Present(?:Mask)?|Attr|Present(?:Mask)?)|PU(?:486|6(?:0(?:1|3(?:e(?:v)?)?|4(?:e(?:v)?)?)|80(?:00|10|20|30|40))|750(?:FX)?|970(?:FX|MP)?|Apollo|G4(?:74(?:47|50))?|Pentium(?:4|II|Pro)?|X86)|RM(?:Attr|P(?:ersistentFix|resent)|ToolRsrcCalls)|TBVersion|a(?:n(?:StartDragInFloatWindow|UseCGTextRendering)|r(?:bonVersion|dServicesPresent))|l(?:assic(?:II)?|oseView(?:Attr|DisplayMgrFriendly|Enabled))|o(?:l(?:lectionMgrVersion|or(?:Matching(?:Attr|LibLoaded|Version)|Picker(?:Version)?|Sync(?:1(?:0(?:4|5)?|1)|2(?:0|1(?:1|2|3)?|5|6(?:1)?)|30)))|mp(?:onent(?:Mgr|Platform)|ressionMgr)|n(?:nMgr(?:Attr|CMSearchFix|ErrorString|MultiAsyncIO|Present)|t(?:extualMenu(?:Attr|Has(?:AttributeAndModifierKeys|UnicodeSupport)|TrapAvailable|UnusedBit)|rol(?:M(?:gr(?:Attr|Present(?:Bit)?|Version)|sgPresentMask)|Strip(?:Attr|Exists|User(?:Font|HotKey)|Version(?:Fixed)?))))|untOfCPUs)|reatesAliasFontRsrc|urrentGraphicsVersion)|D(?:BAccessMgr(?:Attr|Present)|ITLExt(?:Attr|Present|SupportsIctb)|T(?:MgrSupportsFSM|P(?:Features|Info))|esktop(?:Pictures(?:Attr|Displayed|Installed)|SpeechRecognition)|i(?:alogM(?:gr(?:Attr|HasAquaAlert(?:Bit|Mask)|Present(?:Bit|Mask)?)|sgPresentMask)|ctionaryMgr(?:Attr|Present)|gitalSignatureVersion|s(?:kCacheSize|playMgr(?:Attr|C(?:an(?:Confirm|SwitchMirrored)|olorSyncAware)|GeneratesProfiles|Present|S(?:etDepthNotifies|leepNotifies)|Vers)))|ra(?:gMgr(?:Attr|FloatingWind|HasImageSupport|Present)|wSprocketVersion)|upSelectorErr)|E(?:MMU1|asyAccess(?:Attr|Locked|O(?:ff|n)|Sticky)|ditionMgr(?:Attr|Present|TranslationAware)|xt(?:ADBKbd|ISOADBKbd|ToolboxTable|en(?:ded(?:TimeMgr|WindowAttributes(?:Bit|Mask)?)|sionTableVersion)))|F(?:BC(?:CurrentVersion|IndexingState|Version|indexing(?:Critical|Safe))|PUType|S(?:A(?:llowsConcurrentAsyncIO|ttr)|IncompatibleDFA82|M(?:DoesDynamicLoad|Version)|NoMFSVols|Supports(?:2TBVols|4GBVols|DirectIO|ExclusiveLocks|H(?:FSPlusVols|ardLinkDetection))|UsesPOSIXPathsForConversion)|XfrMgr(?:A(?:sync|ttr)|ErrorString|MultiFile|Present)|i(?:le(?:AllocationZeroedBlocksBit|Mapping(?:Attr|MultipleFilesFix|Present))|nd(?:Folder(?:Attr|Present|RedirectionAttr)|er(?:Attr|CallsAEProcess|DropEvent|F(?:loppyRootComments|ullDragManagerSupport)|HasClippings|LargeAndNotSavedFlavorsOK|MagicPlacement|Supports4GBVolumes|U(?:nderstandsRedirectedDesktopFolder|ses(?:ExtensibleFolderManager|SpecialOpenFoldersFile))))|rstSlotNumber)|loppy(?:Attr|IsM(?:FMOnly|anualEject)|UsesDiskInPlace)|o(?:lder(?:DescSupport|Mgr(?:FollowsAliasesWhenResolving|Supports(?:Domains|ExtendedCalls|FSCalls)))|ntMgrAttr)|rontWindowMayBeHidden(?:Bit|Mask)|ullExtFSDispatching)|G(?:X(?:PrintingMgrVersion|Version)|raphics(?:Attr|Is(?:Debugging|Loaded|PowerPC)|Version))|H(?:a(?:rdware(?:Attr|Vendor(?:Apple|Code))|s(?:ASC|Color|D(?:eepGWorlds|irectPixMaps)|E(?:nhancedLtalk|xtendedDiskInit)|F(?:MTuner|SSpecCalls|ileSystemManager|loatingWindows(?:Bit|Mask)?)|G(?:PI(?:aTo(?:DCDa|RTxCa)|bToDCDb)|rayishTextOr)|H(?:FSPlusAPIs|WClosedCaptioning)|IRRemote|ParityCapability|ResourceOverrides|S(?:C(?:C|SI(?:96(?:1|2))?)|erialFader|ingleWindowMode(?:Bit|Mask)|o(?:ftPowerOff|und(?:Fader|InputDevice))|tereoDecoder|ystemIRFunction)|TVTuner|UniversalROM|V(?:IA(?:1|2)|idDecoderScaler)|Window(?:Buffering(?:Bit|Mask)?|Shadows(?:Bit|Mask))|ZoomedVideo))|elpMgr(?:Attr|Extensions|Present)|i(?:dePort(?:A|B)|ghLevelMatching))|I(?:NeedIRPowerOffConfirm|PCSupport|RDisabled|conUtilities(?:Attr|Has(?:32BitIcons|48PixelIcons|8BitDeepMasks|IconServices)|Present)|n(?:itHeapZerosOutHeapsBit|te(?:l|rnalDisplay)))|JapanAdjustADBKbd|K(?:BPS2(?:Keyboards|Set(?:IDToAny|TranslationTable))|eyboard(?:Type|sAttr))|L(?:aunch(?:C(?:anReturn|ontrol)|FullFileSpec)|ineLevelInput|o(?:cationErr|gical(?:PageSize|RAMSize)|wMemorySize))|M(?:B(?:Legacy|MultipleBays|SingleBay)|MUType|P(?:CallableAPIsAttr|DeviceManager|FileManager|TrapCalls)|ac(?:512KE|AndPad|C(?:entris6(?:10|50|60AV)|lassic|olorClassic)|II(?:c(?:i|x)|fx|si|v(?:i|m|x)|x)?|Kbd|LC(?:475|5(?:20|75|80)|II(?:I)?)?|OS(?:Compatibility(?:Box(?:Attr|HasSerial|Present|less))?|XQD(?:Text)?)|Plus(?:Kbd)?|Quadra(?:6(?:05|10|30|50|60AV)|700|8(?:00|40AV)|9(?:00|50))|SE(?:030)?|TV|XL|hine(?:Icon|Type))|e(?:diaBay|moryMap(?:Attr|Sparse)|nuMgr(?:A(?:quaLayout(?:Bit|Mask)|ttr)|CGImageMenuTitle(?:Bit|Mask)|M(?:oreThanFiveMenusDeep(?:Bit|Mask)|ultipleItemsWithCommandID(?:Bit|Mask))|Present(?:Bit|Mask)?|RetainsIconRef(?:Bit|Mask)|SendsMenuBoundsToDefProc(?:Bit|Mask))|ssageMgrVersion)|i(?:scAttr|xedMode(?:Attr|CFM68K(?:Has(?:State|Trap))?|PowerPC|Version))|u(?:lti(?:Channels|pleUsersState)|stUseFCBAccessors))|N(?:a(?:meRegistryVersion|tive(?:CPU(?:family|type)|ProcessMgrBit|T(?:imeMgr|ype1FontSupport)))|ew(?:HandleReturnsZeroedMemoryBit|PtrReturnsZeroedMemoryBit)|o(?:FPU|MMU|tification(?:MgrAttr|Present))|uBus(?:Connectors|Present|SlotCount))|O(?:CE(?:SFServerAvailable|T(?:B(?:Available|NativeGlueAvailable|Present)?|oolbox(?:Attr|Version)))|FA2available|S(?:Attr|L(?:CompliantFinder|InSystem)|Table|XFBCCurrentVersion)|pen(?:FirmwareInfo|Tpt(?:A(?:RAPPresent|ppleTalk(?:Loaded(?:Bit|Mask)|Present(?:Bit|Mask)))|IPXSPX(?:Loaded(?:Bit|Mask)|Present(?:Bit|Mask))|Loaded(?:Bit|Mask)|NetworkSetup(?:Legacy(?:Export|Import)|SupportsMultihoming|Version)?|P(?:PPPresent|resent(?:Bit|Mask))|RemoteAccess(?:ClientOnly|Loaded|MPServer|P(?:Server|resent)|Version)?|TCP(?:Loaded(?:Bit|Mask)|Present(?:Bit|Mask))|Versions)?)|riginal(?:ATSUVersion|QD(?:Text)?)|utlineFonts)|P(?:C(?:Card(?:FamilyPresent|HasPowerControl|SupportsCardBus)?|X(?:Attr|Has(?:8and16BitFAT|ProDOS)|NewUI|UseICMapping))|Mgr(?:CPUIdle|DispatchExists|Exists|S(?:CC|ound|upportsAVPowerStateAtSleepWake))|PC(?:DragLibPresent|QuickTimeLibPresent|Supports(?:Incoming(?:AppleTalk|TCP_IP)?|Out(?:Going|going(?:AppleTalk|TCP_IP))|RealTime|TCP_IP)|Toolbox(?:Attr|Present))|S2Keyboard|ar(?:ity(?:Attr|Enabled)|tialRsrcs)|erforma(?:250|4(?:50|6x|7x)|5(?:300|50|80)|6(?:00|3(?:00|60)|400))|hysicalRAMSize(?:InMegabytes)?|layAndRecord|o(?:pup(?:Attr|Present)|rt(?:ADisabled|BDisabled|able(?:2001(?:ANSIKbd|ISOKbd|JISKbd)|SlotPresent|USB(?:ANSIKbd|ISOKbd|JISKbd))?)|wer(?:Book(?:1(?:00|4(?:0(?:0)?|5)|50|6(?:0|5(?:c)?)|70|80(?:c)?|90)|2400|3400|5(?:00PPCUpgrade|20(?:c)?|300|40(?:c)?)|Duo2(?:10|30(?:0)?|50|70c|80(?:c)?)|G3(?:Series(?:2)?)?)|M(?:ac(?:4400(?:_160)?|5(?:2(?:00|60)|400|500)|6(?:100_6(?:0|6)|200|400|500)|7(?:100_(?:66|80)|200|300|500|600)|8(?:100_(?:1(?:00|10|20)|80)|500|600)|9(?:500|600)|Centris6(?:10|50)|G3|LC(?:475|575|630)|NewWorld|Performa(?:47x|57x|63x)|Quadra(?:6(?:10|30|50)|700|800|9(?:00|50)))|gr(?:Attr|Vers))|PC(?:A(?:SArchitecture|ware)|Has(?:64BitSupport|D(?:CB(?:AInstruction|TStreams)|ataStreams)|GraphicsInstructions|S(?:TFIWXInstruction|quareRootInstructions)|VectorInstructions)|IgnoresDCBST|ProcessorFeatures)?))|r(?:o(?:F16(?:ANSIKbd|ISOKbd|JISKbd)|c(?:ClkSpeed(?:MHz)?|essor(?:CacheLineSize|Type)))|tbl(?:ADBKbd|ISOKbd))|wrB(?:k(?:99JISKbd|E(?:K(?:DomKbd|ISOKbd|JISKbd)|xt(?:ADBKbd|ISOKbd|JISKbd))|Sub(?:DomKbd|ISOKbd|JISKbd))|ook(?:ADBKbd|ISOADBKbd)))|Q(?:D(?:3D(?:Present|V(?:ersion|iewer(?:Present)?))?|HasLongRowBytes|Text(?:Features|Version))|TVR(?:C(?:ubicPanosPresent|ylinderPanosPresent)|Mgr(?:Attr|Present|Vers)|ObjMoviesPresent)|u(?:adra(?:6(?:05|10|30|50|60AV)|700|8(?:00|40AV)|9(?:00|50))|ick(?:Time(?:Conferencing(?:Info)?|Features|Streaming(?:Features|Version)|ThreadSafe(?:FeaturesAttr|Graphics(?:Export|Import)|ICM|Movie(?:Export|Import|Playback|Toolbox))|Version)?|draw(?:Features|Version))))|R(?:BVAddr|M(?:F(?:akeAppleMenuItemsRolledIn|orceSysHeapRolledIn)|SupportsFSCalls|TypeIndexOrderingReverse)|OM(?:Size|Version)|e(?:al(?:TempMemory|timeMgr(?:Attr|Present))|sourceMgr(?:Attr|BugFixesAttrs)|visedTimeMgr))|S(?:C(?:C(?:ReadAddr|WriteAddr)|SI(?:PollSIH|SlotBoot)?)|DP(?:FindVersion|PromptVersion|StandardDirectoryVersion)|E(?:30SlotPresent|SlotPresent)|FServer|MP(?:MailerVersion|SPSendLetterVersion)|a(?:feOFAttr|nityCheckResourceFiles)|bitFontSupport|cr(?:apMgr(?:Attr|TranslationAware)|eenCapture(?:Dir|Main)|ipt(?:Count|MgrVersion|ingSupport)|ollingThrottle)|e(?:rialA(?:rbitrationExists|ttr)|tDragImageUpdates)|h(?:eetsAreWindowModal(?:Bit|Mask)|utdown(?:Attributes|HassdOnBootVolUnmount))|lot(?:Attr|MgrExists)|ndPlayDoubleBuffer|o(?:ftwareVendor(?:Apple|Code|Licensee)|und(?:Attr|IOMgrPresent))|p(?:e(?:cificMatchSupport|ech(?:Attr|HasPPCGlue|MgrPresent|Recognition(?:Attr|Version)))|litOS(?:A(?:ttr|ware)|BootDriveIsNetworkVolume|EnablerVolumeIsDifferentFromBootVolume|MachineNameS(?:etToNetworkNameTemp|tartupDiskIsNonPersistent)))|quareMenuBar|t(?:andard(?:File(?:58|Attr|Has(?:ColorIcons|DynamicVolumeAllocation)|TranslationAware|UseGenericIcons)|TimeMgr)|d(?:ADBKbd|ISOADBKbd|NBP(?:Attr|Present|SupportsAutoPosition))|ereo(?:Capability|Input|Mixing))|upports(?:ApplicationURL|FSpResourceFileAlreadyOpenBit|Mirroring)|ys(?:Architecture|DebuggerSupport|ZoneGrowable|temUpdateVersion))|T(?:E(?:1|2|3|4|5|6|Attr|Has(?:GetHiliteRgn|WhiteBackground)|Supports(?:InlineInput|TextObjects))|SM(?:DisplayMgrAwareBit|TE(?:1(?:5(?:2)?)?|Attr|Present|Version)?|doesTSMTEBit|gr(?:15|2(?:0|2|3)|Attr|Version))|VAttr|e(?:le(?:Mgr(?:A(?:ttr|utoAnswer)|IndHandset|NewTELNewSupport|P(?:owerPCSupport|resent)|S(?:ilenceDetect|oundStreams))|phoneSpeechRecognition)|mpMem(?:Support|Tracked)|rmMgr(?:Attr|ErrorString|Present)|xtEditVersion)|h(?:irdParty(?:ANSIKbd|ISOKbd|JISKbd)|read(?:Mgr(?:Attr|Present)|sLibraryPresent))|imeMgrVersion|oolboxTable|ranslation(?:Attr|GetPathAPIAvail|Mgr(?:Exists|HintOrder)|PPCAvail))|U(?:DFSupport|SB(?:A(?:ndy(?:ANSIKbd|ISOKbd|JISKbd)|ttr)|Cosmo(?:ANSIKbd|ISOKbd|JISKbd)|HasIsoch|Pr(?:esent|interSharing(?:Attr(?:Booted|Mask|Running)?|Version(?:Mask)?)|oF16(?:ANSIKbd|ISOKbd|JISKbd))|Version)|n(?:defSelectorErr|known(?:Err|ThirdPartyKbd))|serVisibleMachineName)|V(?:IA(?:1Addr|2Addr)|M(?:Attr|BackingStoreFileRefNum|FilemappingOn|Has(?:LockMemoryForOutput|PagingControl)|Info(?:NoneType|Si(?:mpleType|ze(?:StorageType|Type))|Type)|Present|ZerosPagesBit)|alueImplementedVers|ersion)|W(?:SII(?:CanPrintWithoutPrGeneralBit|Support)|indow(?:LiveResize(?:Bit|Mask)|M(?:gr(?:Attr|Present(?:Bit|Mask)?)|inimizeToDock(?:Bit|Mask)))|orldScriptII(?:Attr|Version))|X86(?:AdditionalFeatures|Features|Has(?:APIC|C(?:ID|LFSH|MOV|X(?:16|8))|D(?:E|S(?:CPL)?)|EST|F(?:PU|XSR)|HTT|M(?:C(?:A|E)|MX|ONITOR|SR|TRR)|P(?:A(?:E|T)|GE|S(?:E(?:36)?|N))|S(?:EP|MX|S(?:E(?:2|3)?)?|upplementalSSE3)|T(?:M(?:2)?|SC)|VM(?:E|X)|xTPR)|ResACPI|Serviced20)))|fpErr|ra(?:bTimeComplete|veUpr(?:E|I|O|U))|uestNotAllowedErr)|h(?:AxisOnly|Menu(?:Cmd|FindErr)|a(?:chek|ndlerNotFoundErr|rdwareConfigErr)|i(?:Archive(?:EncodingCompleteErr|HIObjectIgnoresArchivingErr|KeyNotAvailableErr|TypeMismatchErr)|Object(?:C(?:annotSubclassSingletonErr|lass(?:ExistsErr|Has(?:InstancesErr|SubclassesErr)|IsAbstractErr))|Delegate(?:AlreadyExistsErr|NotFoundErr))|erMenu|ghLevelEventMask|tDev)|m(?:BalloonAborted|CloseViewActive|Help(?:Disabled|ManagerNotInited)|NoBalloonUp|OperationUnsupported|S(?:ameAsLastBalloon|kippedBalloon)|UnknownHelpType|WrongVersion)|our(?:Field|Mask)|r(?:HTMLRenderingLibNotInstalledErr|LeadingZ|MiscellaneousExceptionErr|U(?:RLNotHandledErr|nableToResizeHandleErr))|wParamErr)|i(?:IOAbort(?:Err)?|MemFullErr|c(?:Config(?:InappropriateErr|NotFoundErr)|InternalErr|No(?:MoreWritersErr|Perm|URLErr|thingToOverrideErr)|P(?:ermErr|r(?:ef(?:DataErr|NotFoundErr)|ofileNotFoundErr))|Read(?:OnlyPerm|WritePerm)|T(?:ooManyProfilesErr|runcatedErr)|onItem)|llegal(?:C(?:hannelOSErr|ontrollerOSErr)|InstrumentOSErr|Knob(?:OSErr|ValueOSErr)|NoteChannelOSErr|PartOSErr|ScrapFlavor(?:FlagsErr|SizeErr|TypeErr)|VoiceAllocationOSErr)|n(?:Co(?:llapseBox|ntent)|D(?:esk|rag)|G(?:oAway|row)|MenuBar|NoWindow|ProxyIcon|S(?:tructure|ysWindow)|ToolbarButton|Zoom(?:In|Out)|compatibleVoice|it(?:Dev|IWMErr)|putOutOfBounds|sufficientStackErr|t(?:Arabic|DrawHook|E(?:OLHook|uropean)|HitTestHook|InlineInputTSMTEP(?:ostUpdateHook|reUpdateHook)|Japanese|NWidthHook|OutputMask|Roman|TextWidthHook|W(?:estern|idthHook)|er(?:nal(?:ComponentErr|QuickTimeError|ScrapErr)|ruptsMaskedErr)|lCurrency)|valid(?:Atom(?:ContainerErr|Err|TypeErr)|C(?:hunk(?:Cache|Num)|omponentID)|D(?:ataRef(?:Container)?|uration)|EditState|FolderTypeErr|H(?:andler|otSpotIDErr)|I(?:conRefErr|mageIndexErr|ndexErr)|M(?:edia|ovie)|Node(?:FormatErr|IDErr)|PickerType|Rect|S(?:ample(?:Desc(?:Index|ription)|Num|Table)|prite(?:I(?:DErr|ndexErr)|PropertyErr|WorldPropertyErr))|T(?:ime|ra(?:ck|nslationPathErr))|ViewStateErr))|o(?:Dir(?:Flg|Mask)|Err|QType)|t(?:emDisable|lc(?:D(?:isableKeyScriptSync(?:Mask)?|ualCaret)|S(?:howIcon|ysDirection)))|u(?:Current(?:CurLang|DefLang|Script)|NumberPartsTable|S(?:cript(?:CurLang|DefLang)|ystem(?:CurLang|DefLang|Script))|UnTokenTable|W(?:hiteSpaceList|ord(?:SelectTable|WrapTable))))|k(?:1(?:6(?:B(?:E5(?:55PixelFormat|65PixelFormat)|itCardErr)|LE5(?:55(?:1PixelFormat|PixelFormat)|65PixelFormat))|IndexedGrayPixelFormat|MonochromePixelFormat)|2(?:4(?:BGRPixelFormat|RGBPixelFormat)|Indexed(?:GrayPixelFormat|PixelFormat)|vuyPixelFormat)|3(?:2(?:A(?:BGRPixelFormat|RGBPixelFormat)|B(?:GRAPixelFormat|itHeap)|RGBAPixelFormat)|DMixer(?:AttenuationCurve_(?:Exponential|Inverse|Linear|Power)|Param_(?:Azimuth|Distance|Elevation|Gain|P(?:laybackRate|ost(?:AveragePower|PeakHoldLevel)|re(?:AveragePower|PeakHoldLevel)))|RenderingFlags_(?:ConstantReverbBlend|D(?:istance(?:Attenuation|Diffusion|Filter)|opplerShift)|InterAuralDelay|LinearDistanceAttenuation)))|4Indexed(?:GrayPixelFormat|PixelFormat)|68kInterruptLevelMask|8Indexed(?:GrayPixelFormat|PixelFormat)|A(?:E(?:A(?:ND|bout|ctivate|fter|l(?:iasSelection|l(?:Caps)?|waysInteract)|n(?:swer|y)|pp(?:earanceChanged|lication(?:Class|Died))|rrow(?:At(?:End|Start)|BothEnds)|sk|utoDown)|B(?:e(?:fore|gin(?:Transaction|ning|sWith))|old)|C(?:a(?:n(?:Interact|SwitchLayer)|se(?:ConsiderMask|IgnoreMask|SensEquals)?)|entered|hangeView|l(?:eanUp|o(?:ne|se))|o(?:mmandClass|n(?:densed|tains)|py|reSuite|untElements)|reate(?:Element|Publisher)|ut)|D(?:ata(?:Array|baseSuite)|e(?:activate|bug(?:POSTHeader|ReplyHeader|XML(?:DebugAll|Re(?:quest|sponse)))|faultTimeout|lete|sc(?:Array|ListFactor(?:None|Type(?:AndSize)?)))|i(?:acritic(?:ConsiderMask|IgnoreMask)?|rectCall|skEvent)|o(?:Not(?:AutomaticallyAddAnnotationsToEvent|IgnoreHandler|PromptForUserConsent)|ObjectsExist|Script|nt(?:DisposeOnResume|Execute|Reco(?:nnect|rd))|wn)|rag|uplicateSelection)|E(?:ditGraphic|ject|mpty(?:Trash)?|nd(?:Transaction|sWith)?|quals|rase|xpan(?:ded|sion(?:ConsiderMask|IgnoreMask)?))|F(?:a(?:lse|st)|etchURL|i(?:nder(?:Events|Suite)|rst)|ormulaProtect|ullyJustified)|G(?:e(?:stalt|t(?:ClassInfo|Data(?:Size)?|EventInfo|InfoSelection|PrivilegeSelection|SuiteInfo|URL))|r(?:eaterThan(?:Equals)?|ow))|H(?:TTPProxy(?:HostAttr|PortAttr)|andle(?:Array|SimpleRanges)|i(?:Quality|dden|gh(?:Level|Priority))|yphens(?:ConsiderMask|IgnoreMask)?)|I(?:Do(?:M(?:arking|inimum)|Whose)|S(?:Action(?:Path)?|C(?:lient(?:Address|IP)|ontentType)|F(?:romUser|ullRequest)|GetURL|HTTPSearchArgs|Method|P(?:assword|ostArgs)|Referrer|S(?:criptName|erver(?:Name|Port))|User(?:Agent|Name)|WebStarSuite)|gnore(?:App(?:EventHandler|PhacHandler)|Sys(?:EventHandler|PhacHandler))|mageGraphic|n(?:fo|goreBuiltInEventHandler|ter(?:actWith(?:All|Local|Self)|ceptOpen|netSuite))|sUniform|talic)|K(?:ataHiragana|ey(?:Class|D(?:escArray|own)))|L(?:ast|e(?:ftJustified|ssThan(?:Equals)?)|o(?:calProcess|gOut|wercase))|M(?:a(?:in|keObjectsVisible)|enu(?:Class|Select)|i(?:ddle|scStandards)|o(?:difiable|use(?:Class|Down(?:InBack)?)|ve(?:d)?))|N(?:OT|avigationKey|e(?:verInteract|xt)|o(?:Arrow|Dispatch|Reply|nmodifiable|rmalPriority|tify(?:Recording|St(?:artRecording|opRecording)))?|ullEvent)|O(?:R|SAXSizeResource|pen(?:Application|Contents|Documents|Selection)?|utline)|P(?:a(?:ckedArray|geSetup|s(?:sSubDescs|te))|lain|r(?:evious|int(?:Documents|Selection|Window)?|o(?:cessNonReplyEvents|mise))|u(?:nctuation(?:ConsiderMask|IgnoreMask)?|tAway(?:Selection)?))|Q(?:D(?:Ad(?:M(?:ax|in)|d(?:Over|Pin))|B(?:ic|lend)|Copy|Not(?:Bic|Copy|Or|Xor)|Or|Su(?:b(?:Over|Pin)|pplementalSuite)|Xor)|u(?:eueReply|i(?:ckdrawSuite|t(?:A(?:ll|pplication)|PreserveState|Reason))))|R(?:PCClass|awKey|e(?:allyLogOut|buildDesktopDB|do|gular|moteProcess|openApplication|place|quiredSuite|s(?:ized|olveNestedLists|tart|ume)|ve(?:alSelection|rt))|ightJustified)|S(?:OAPScheme|a(?:meProcess|ve)|cr(?:apEvent|iptingSizeResource)|e(?:lect|t(?:Data|Position))|h(?:a(?:dow|r(?:edScriptHandler|ing))|ow(?:Clipboard|Preferences|RestartDialog|ShutdownDialog)|utDown)|leep|mall(?:Caps|Kana|SystemFontChanged)|o(?:cks(?:4Protocol|5Protocol|HostAttr|P(?:asswordAttr|ortAttr|roxyAttr)|UserAttr)|rt)|pe(?:cialClassProperties|ech(?:D(?:etected|one)|Suite))|t(?:artRecording|op(?:Recording|pedMoving)|rikethrough)|u(?:bscript|perscript|spend)|y(?:nc|stemFontChanged))|T(?:ableSuite|e(?:rminologyExtension|xtSuite)|hemeSwitch|r(?:ansactionTerminated|ue))|U(?:T(?:Apostrophe|ChangesState|DirectParamIsReference|Enum(?:ListIsExclusive|erated|sAreTypes)|Feminine|HasReturningParam|Masculine|NotDirectParamIsTarget|Optional|P(?:aramIs(?:Reference|Target)|lural|ropertyIsReference)|Re(?:adWrite|plyIsReference)|TightBindingFunction|listOfItems)|n(?:d(?:erline|o)|knownSource)|p(?:date)?|se(?:HTTPProxyAttr|RelativeIterators|S(?:ocksAttr|tandardDispatch)|rTerminology))|Vi(?:ewsFontChanged|rtualKey)|W(?:a(?:itReply|keUpEvent|ntReceipt)|h(?:iteSpace(?:ConsiderMask|IgnoreMask)?|oleWordEquals)|indowClass)|XMLRPCScheme|Yes|Z(?:enkakuHankaku|oom(?:In|Out)?))|FP(?:ExtendedFlagsAlternateAddressMask|ServerIcon|Tag(?:Length(?:DDP|IP(?:Port)?)|Type(?:D(?:DP|NS)|IP(?:Port)?)))|H(?:Intern(?:alErr|etConfigPrefErr)|TOCType(?:Developer|User))|LM(?:D(?:eferSwitchErr|uplicateModuleErr)|GroupNotFoundErr|In(?:stallationErr|ternalErr)|Location(?:NotFoundErr|sFolderType)|Module(?:CommunicationErr|sFolderType)|NoSuchModuleErr|PreferencesFolderType|RebootFlagsLevelErr)|NKRCurrentVersion|RM(?:M(?:ountVol|ultVols)|NoUI|Search(?:More|RelFirst)?|TryFileIDFirst)|S(?:A(?:dd|nd|ppleScriptSuite)|C(?:o(?:m(?:es(?:After|Before)|ment(?:Event)?)|n(?:catenate|siderReplies(?:ConsiderMask|IgnoreMask)?|tains))|urrentApplication)|D(?:efault(?:M(?:ax(?:HeapSize|StackSize)|in(?:HeapSize|StackSize))|Preferred(?:HeapSize|StackSize))|ivide)|E(?:ndsWith|qual|rrorEventCode|xcluding)|GreaterThan(?:OrEqual)?|HasOpenHandler|I(?:mporting|nitializeEventCode)|L(?:aunchEvent|essThan(?:OrEqual)?)|M(?:agic(?:EndTellEvent|TellEvent)|inimumVersion|ultiply)|N(?:egate|ot(?:Equal)?|um(?:berOfSourceStyles|ericStrings(?:ConsiderMask|IgnoreMask)?))|Or|P(?:ower|repositionalSubroutine)|Quotient|Remainder|S(?:criptEditorSuite|elect(?:CopySourceAttributes|Get(?:AppTerminology(?:Obsolete)?|Handler(?:Names|Obsolete)?|Property(?:Names|Obsolete)?|S(?:ourceStyle(?:Names|s)|ysTerminology))|Init|Set(?:Handler(?:Obsolete)?|Property(?:Obsolete)?|Source(?:Attributes|Styles)))|ourceStyle(?:ApplicationKeyword|C(?:lass|omment)|Dynamic(?:Class|E(?:numValue|ventName)|P(?:arameterName|roperty))|E(?:numValue|ventName)|L(?:anguageKeyword|iteral)|NormalText|ObjectSpecifier|P(?:arameterName|roperty)|String|U(?:ncompiledText|serSymbol))|t(?:art(?:LogEvent|sWith)|opLogEvent)|ub(?:routineEvent|tract))|TypeNamesSuite|UseEventCode)|TS(?:BoldQDStretch|CubicCurveType|DeletedGlyphcode|F(?:ileReferenceFilterSelector|lat(?:DataUstl(?:CurrentVersion|Version(?:0|1|2))|tenedFontSpecifierRawNameData)|ont(?:AutoActivation(?:Ask|D(?:efault|isabled)|Enabled)|Cont(?:ainerRefUnspecified|ext(?:Global|Local|Unspecified))|F(?:amilyRefUnspecified|ilter(?:CurrentVersion|Selector(?:Font(?:ApplierFunction|Family(?:ApplierFunction)?)|Generation|Unspecified))|ormatUnspecified)|Notify(?:Action(?:DirectoriesChanged|FontsChanged)|Option(?:Default|ReceiveWhileSuspended))|RefUnspecified))|G(?:enerationUnspecified|lyphInfo(?:AppleReserved|ByteSizeMask|HasImposedWidth|Is(?:Attachment|LTHanger|RBHanger|WhiteSpace)|TerminatorGlyph))|I(?:nvalid(?:Font(?:Access|ContainerAccess|FamilyAccess|TableAccess)|GlyphAccess)|t(?:alicQDSkew|eration(?:Completed|ScopeModified)))|Line(?:Appl(?:eReserved|yAntiAliasing)|BreakToNearestCharacter|Disable(?:A(?:ll(?:BaselineAdjustments|GlyphMorphing|Justification|KerningAdjustments|LayoutOperations|TrackingAdjustments)|utoAdjustDisplayPos)|NegativeJustification)|F(?:illOutToWidth|ractDisable)|HasNo(?:Hangers|OpticalAlignment)|I(?:gnoreFontLeading|mposeNoAngleForEnds|sDisplayOnly)|KeepSpacesOutOfMargin|LastNoJustification|No(?:AntiAliasing|LayoutOptions|SpecialJustification)|TabAdjustEnabled|Use(?:DeviceMetrics|QDRendering))|NoTracking|O(?:ptionFlags(?:ActivateDisabled|ComposeFontPostScriptName|D(?:efault(?:Scope)?|oNotNotify)|I(?:ncludeDisabledMask|terat(?:eByPrecedenceMask|ionScopeMask))|ProcessSubdirectories|Re(?:cordPersistently|strictedScope)|U(?:nRestrictedScope|se(?:DataFork(?:AsResourceFork)?|ResourceFork)))|therCurveType)|Qu(?:adCurveType|eryActivateFontMessage)|RadiansFactor|Style(?:Appl(?:eReserved|y(?:AntiAliasing|Hints))|No(?:AntiAliasing|Hinting|Options))|U(?:A(?:fterWithStreamShiftTag|scentTag)|B(?:a(?:ckgroundC(?:allback|olor)|dStreamErr|selineClassTag)|eforeWithStreamShiftTag|usyObjectErr|y(?:C(?:haracter(?:Cluster)?|luster)|TypographicCluster|Word))|C(?:GContextTag|enterTab|learAll|o(?:lorTag|ordinateOverflowErr)|rossStreamShiftTag)|D(?:ataStreamUnicodeStyledText|e(?:c(?:imalTab|ompositionFactorTag)|faultFontFallbacks|scentTag)|irectData(?:AdvanceDeltaFixedArray|BaselineDeltaFixedArray|DeviceDeltaSInt16Array|LayoutRecordATSLayoutRecord(?:Current|Version1)|Style(?:IndexUInt16Array|SettingATSUStyleSettingRefArray)))|F(?:lattenOptionNoOptionsMask|o(?:nt(?:MatrixTag|Tag|s(?:Matched|NotMatched))|rceHangingTag)|rom(?:FollowingLayout|PreviousLayout|TextBeginning))|GlyphSelectorTag|HangingInhibitFactorTag|I(?:mposeWidthTag|nvalid(?:Attribute(?:SizeErr|TagErr|ValueErr)|Ca(?:cheErr|llInsideCallbackErr)|Font(?:Err|FallbacksErr|ID)|StyleErr|Text(?:LayoutErr|RangeErr)))|KerningInhibitFactorTag|L(?:a(?:ng(?:RegionTag|uageTag)|st(?:Err|ResortOnlyFallback)|youtOperation(?:AppleReserved|BaselineAdjustment|CallbackStatus(?:Continue|Handled)|Justification|KerningAdjustment|Morph|None|OverrideTag|PostLayoutAdjustment|TrackingAdjustment))|e(?:adingTag|ftT(?:ab|oRightBaseDirection))|ine(?:AscentTag|B(?:aselineValuesTag|reakInWord)|D(?:e(?:cimalTabCharacterTag|scentTag)|irectionTag)|F(?:lushFactorTag|ontFallbacksTag)|HighlightCGColorTag|JustificationFactorTag|La(?:ng(?:RegionTag|uageTag)|youtOptionsTag)|RotationTag|T(?:extLocatorTag|runcationTag)|WidthTag)|owLevelErr)|Max(?:ATSUITagValue|LineTag|StyleTag)|N(?:o(?:C(?:aretAngleTag|orrespondingFontErr)|Font(?:CmapAvailableErr|NameErr|ScalerAvailableErr)|LigatureSplitTag|OpticalAlignmentTag|S(?:elector|pecialJustificationTag|tyleRunsAssignedErr)|tSetErr)|umberTabTypes)|OutputBufferTooSmallErr|PriorityJustOverrideTag|Q(?:D(?:BoldfaceTag|CondensedTag|ExtendedTag|ItalicTag|UnderlineTag)|uickDrawTextErr)|R(?:GBAlphaColorTag|ightT(?:ab|oLeftBaseDirection))|S(?:equentialFallbacks(?:Exclusive|Preferred)|izeTag|t(?:rongly(?:Horizontal|Vertical)|yle(?:Contain(?:edBy|s)|D(?:oubleLineCount|ropShadow(?:BlurOptionTag|ColorOptionTag|OffsetOptionTag|Tag))|Equals|RenderingOptionsTag|S(?:ingleLineCount|trikeThrough(?:Co(?:lorOptionTag|untOptionTag)|Tag))|TextLocatorTag|Un(?:derlineCo(?:lorOptionTag|untOptionTag)|equal)))|uppressCrossKerningTag)|T(?:oTextEnd|r(?:ackingTag|unc(?:FeatNoSquishing|ate(?:End|Middle|None|S(?:pecificationMask|tart)))))|U(?:n(?:FlattenOptionNoOptionsMask|supportedStreamFormatErr)|se(?:GrafPortPenLoc|LineControlWidth))|VerticalCharacterTag|se(?:CaretOrigins|DeviceOrigins|FractionalOrigins|GlyphAdvance|LineHeight|OriginFlags)))|U(?:Gr(?:aphErr_(?:CannotDoInCurrentContext|Invalid(?:AudioUnit|Connection)|NodeNotFound|OutputNodeErr)|oupParameterID_(?:All(?:NotesOff|SoundOff)|ChannelPressure|DataEntry(?:_LSB)?|Expression(?:_LSB)?|Foot(?:_LSB)?|KeyPressure(?:_(?:FirstKey|LastKey))?|ModWheel(?:_LSB)?|P(?:an(?:_LSB)?|itchBend)|ResetAllControllers|S(?:ostenuto|ustain)|Volume(?:_LSB)?))|LowShelfParam_(?:CutoffFrequency|Gain)|MIDISynthProperty_EnablePreload|N(?:BandEQ(?:FilterType_(?:2ndOrderButterworth(?:HighPass|LowPass)|Band(?:Pass|Stop)|HighShelf|LowShelf|Parametric|Resonant(?:High(?:Pass|Shelf)|Low(?:Pass|Shelf)))|P(?:aram_(?:B(?:andwidth|ypassBand)|F(?:ilterType|requency)|G(?:ain|lobalGain))|roperty_(?:BiquadCoefficients|MaxNumberOfBands|NumberOfBands)))|et(?:ReceiveP(?:aram_(?:NumParameters|Status)|roperty_(?:Hostname|Password))|S(?:end(?:NumPresetFormats|P(?:aram_(?:NumParameters|Status)|r(?:esetFormat_(?:AAC_(?:128kbpspc|32kbpspc|4(?:0kbpspc|8kbpspc)|64kbpspc|80kbpspc|96kbpspc|LD_(?:32kbpspc|4(?:0kbpspc|8kbpspc)|64kbpspc))|IMA4|Lossless(?:16|24)|PCM(?:Float32|Int(?:16|24))|ULaw)|operty_(?:Disconnect|P(?:assword|ortNum)|ServiceName|TransmissionFormat(?:Index)?))))|tatus_(?:Connect(?:ed|ing)|Listening|NotConnected|Overflow|Underflow)))|odeInteraction_(?:Connection|InputCallback))|Parameter(?:Listener_AnyParameter|MIDIMapping_(?:Any(?:ChannelFlag|NoteFlag)|Bipolar(?:_On)?|SubRange|Toggle))|Sampler(?:P(?:aram_(?:CoarseTuning|FineTuning|Gain|Pan)|roperty_(?:BankAndPreset|Load(?:AudioFiles|Instrument|PresetFromBank)))|_Default(?:BankLSB|MelodicBankMSB|PercussionBankMSB))|VoiceIO(?:Err_UnexpectedNumberOfInputChannels|Property_(?:BypassVoiceProcessing|MuteOutput|VoiceProcessingEnableAGC)))|VL(?:I(?:nOrder|s(?:Le(?:af|ftBranch)|RightBranch|Tree))|NullNode|P(?:ostOrder|reOrder))|X(?:CopyMultipleAttributeOptionStopOnError|Error(?:A(?:PIDisabled|ctionUnsupported|ttributeUnsupported)|CannotComplete|Failure|I(?:llegalArgument|nvalidUIElement(?:Observer)?)|No(?:Value|t(?:EnoughPrecision|Implemented|ification(?:AlreadyRegistered|NotRegistered|Unsupported)))|ParameterizedAttributeUnsupported|Success)|MenuItemModifier(?:Control|No(?:Command|ne)|Option|Shift)|UnderlineStyle(?:Double|None|Single|Thick))|bbrevSquaredLigaturesO(?:ffSelector|nSelector)|c(?:c(?:essException|ountKCItemAttr)|tivateAnd(?:HandleClick|IgnoreClick))|dd(?:KCEvent(?:Mask)?|ressKCItemAttr)|l(?:ert(?:Caution(?:Alert|BadgeIcon|Icon)|Default(?:CancelText|O(?:KText|therText))|Flags(?:AlertIsMovable|Use(?:Co(?:mpositing|ntrolHierarchy)|Theme(?:Background|Controls)))|Note(?:Alert|Icon)|PlainAlert|St(?:dAlert(?:CancelButton|HelpButton|O(?:KButton|therButton))|op(?:Alert|Icon))|VariantCode|WindowClass)|i(?:asBadgeIcon|gn(?:AbsoluteCenter|Bottom(?:Left|Right)?|Center(?:Bottom|Left|Right|Top)|HorizontalCenter|Left|None|Right|Top(?:Left|Right)?|VerticalCenter))|l(?:CapsSelector|LowerCaseSelector|PPDDomains|Typ(?:eFeaturesO(?:ffSelector|nSelector)|ographicFeaturesType)|WindowClasses)|readySavedStateErr|t(?:HalfWidthTextSelector|P(?:lainWindowClass|roportionalTextSelector)|ernate(?:HorizKanaO(?:ffSelector|nSelector)|KanaType|VertKanaO(?:ffSelector|nSelector)))|ways(?:Authenticate|SendSubject))|n(?:dConnections|notationType|y(?:AuthType|Component(?:FlagsMask|Manufacturer|SubType|Type)|P(?:ort|rotocol)|TransactionID))|pp(?:PackageAliasType|earance(?:EventClass|Folder(?:Icon|Type)|Part(?:DownButton|Indicator|LeftButton|Meta(?:Disabled|Inactive|None)|Page(?:DownArea|LeftArea|RightArea|UpArea)|RightButton|UpButton)|Region(?:C(?:loseBox|o(?:llapseBox|ntent))|Drag|Grow|Structure|T(?:itle(?:Bar|ProxyIcon|Text)|oolbarButton)|ZoomBox))|l(?:e(?:ExtrasFolder(?:Icon|Type)|JapaneseDictionarySignature|Lo(?:go(?:CharCode|Icon|Unicode)|sslessFormatFlag_(?:16BitSourceData|2(?:0BitSourceData|4BitSourceData)|32BitSourceData))|M(?:anufacturer|enu(?:Folder(?:AliasType|Icon(?:Resource)?|Type)|Icon))|S(?:cript(?:BadgeIcon|Subtype)|hare(?:AuthenticationFolderType|PasswordKCItemClass|SupportFolderType))|Talk(?:Icon|ZoneIcon)|shareAutomountServerAliasesFolderType)|ication(?:AliasType|CPAliasType|DAAliasType|SupportFolder(?:Icon|Type)|ThreadID|WindowKind|sFolder(?:Icon|Type))))|s(?:sistantsFolder(?:Icon|Type)|teriskToMultiplyO(?:ffSelector|nSelector)|ync(?:Eject(?:Complete|InProgress)|Mount(?:Complete|InProgress)|Unmount(?:Complete|InProgress)))|t(?:SpecifiedOrigin|temptDupCardEntryErr)|u(?:dio(?:A(?:ggregateDevice(?:ClassID|Property(?:ActiveSubDeviceList|C(?:lockDevice|omposition)|FullSubDeviceList|MasterSubDevice))|lertSoundsFolderType)|B(?:alanceFadeType_(?:EqualPower|MaxUnityGain)|o(?:o(?:leanControl(?:ClassID|PropertyValue)|tChimeVolumeControlClassID)|x(?:ClassID|Property(?:Acqui(?:red|sitionFailed)|BoxUID|ClockDeviceList|DeviceList|Has(?:Audio|MIDI|Video)|IsProtected|TransportType))))|C(?:hannel(?:Bit_(?:Center(?:Surround|Top(?:Front|Middle|Rear))?|L(?:FEScreen|eft(?:Center|Surround(?:Direct)?|Top(?:Front|Middle|Rear))?)|Right(?:Center|Surround(?:Direct)?|Top(?:Front|Middle|Rear))?|Top(?:Back(?:Center|Left|Right)|CenterSurround)|VerticalHeight(?:Center|Left|Right))|Coordinates_(?:Azimuth|BackFront|D(?:istance|ownUp)|Elevation|LeftRight)|Flags_(?:AllOff|Meters|RectangularCoordinates|SphericalCoordinates)|La(?:bel_(?:Ambisonic_(?:W|X|Y|Z)|B(?:eginReserved|inaural(?:Left|Right))|C(?:enter(?:Surround(?:Direct)?|Top(?:Front|Middle|Rear))?|lickTrack)|Di(?:alogCentricMix|screte(?:_(?:0|1(?:0|1|2|3|4|5)?|2|3|4|5|6(?:5535)?|7|8|9))?)|EndReserved|ForeignLanguage|H(?:OA_ACN(?:_(?:0|1(?:0|1|2|3|4|5)?|2|3|4|5|6(?:5024)?|7|8|9))?|aptic|ea(?:dphones(?:Left|Right)|ringImpaired))|L(?:FE(?:2|Screen)|eft(?:Center|Surround(?:Direct)?|To(?:p(?:Front|Middle|Rear)|tal)|Wide)?)|M(?:S_(?:Mid|Side)|ono)|Narration|R(?:earSurround(?:Left|Right)|ight(?:Center|Surround(?:Direct)?|To(?:p(?:Front|Middle|Rear)|tal)|Wide)?)|Top(?:Back(?:Center|Left|Right)|CenterSurround)|U(?:n(?:known|used)|seCoordinates)|VerticalHeight(?:Center|Left|Right)|XY_(?:X|Y))|youtTag_(?:A(?:AC_(?:3_0|4_0|5_(?:0|1)|6_(?:0|1)|7_(?:0|1(?:_(?:B|C))?)|Octagonal|Quadraphonic)|C3_(?:1_0_1|2_1_1|3_(?:0(?:_1)?|1(?:_1)?))|mbisonic_B_Format|tmos_(?:5_1_2|7_1_4|9_1_6)|udioUnit_(?:4|5(?:_(?:0|1))?|6(?:_(?:0|1))?|7_(?:0(?:_Front)?|1(?:_Front)?)|8))|B(?:eginReserved|inaural)|Cube|D(?:TS_(?:3_1|4_1|6_(?:0_(?:A|B|C)|1_(?:A|B|C|D))|7_(?:0|1)|8_(?:0_(?:A|B)|1_(?:A|B)))|VD_(?:0|1(?:0|1|2|3|4|5|6|7|8|9)?|2(?:0)?|3|4|5|6|7|8|9)|iscreteInOrder)|E(?:AC(?:3_(?:6_1_(?:A|B|C)|7_1_(?:A|B|C|D|E|F|G|H))|_(?:6_0_A|7_0_A))|magic_Default_7_1|ndReserved)|H(?:OA_ACN_(?:N3D|SN3D)|exagonal)|ITU_(?:1_0|2_(?:0|1|2)|3_(?:0|1|2(?:_1)?|4_1))|M(?:PEG_(?:1_0|2_0|3_0_(?:A|B)|4_0_(?:A|B)|5_(?:0_(?:A|B|C|D)|1_(?:A|B|C|D))|6_1_A|7_1_(?:A|B|C))|atrixStereo|idSide|ono)|Octagonal|Pentagonal|Quadraphonic|S(?:MPTE_DTV|tereo(?:Headphones)?)|TMH_10_2_(?:full|std)|U(?:nknown|seChannel(?:Bitmap|Descriptions))|WAVE_(?:2_1|3_0|4_0_(?:A|B)|5_(?:0_(?:A|B)|1_(?:A|B))|6_1|7_1)|XY)))|l(?:ipLightControlClassID|ock(?:Device(?:ClassID|Property(?:AvailableNominalSampleRates|C(?:lockDomain|ontrolList)|Device(?:Is(?:Alive|Running)|UID)|Latency|NominalSampleRate|TransportType))|Source(?:Control(?:ClassID|PropertyItemKind)|ItemKindInternal)))|o(?:dec(?:AppendInput(?:BufferListSelect|DataSelect)|B(?:ad(?:DataError|PropertySizeError)|itRate(?:ControlMode_(?:Constant|LongTermAverage|Variable(?:Constrained)?)|Format(?:_(?:ABR|CBR|VBR))?))|D(?:elayMode_(?:Compatibility|Minimum|Optimal)|oesSampleRateConversion)|ExtendFrequencies|GetProperty(?:InfoSelect|Select)|I(?:llegalOperationError|n(?:itializeSelect|putFormatsForOutputFormat))|No(?:Error|tEnoughBufferSpaceError)|Output(?:FormatsForInputFormat|Precedence(?:BitRate|None|SampleRate)?)|Pr(?:imeMethod_(?:No(?:ne|rmal)|Pre)|o(?:duceOutput(?:BufferListSelect|DataSelect|Packet(?:AtEOF|Failure|NeedsMoreInputData|Success(?:HasMore)?))|perty(?:A(?:djustLocalQuality|pplicable(?:BitRateRange|InputSampleRates|OutputSampleRates)|vailable(?:BitRate(?:Range|s)|Input(?:ChannelLayout(?:Tags|s)|SampleRates)|NumberChannels|Output(?:ChannelLayout(?:Tags|s)|SampleRates)))|BitRate(?:ControlMode|ForVBR)|Current(?:Input(?:ChannelLayout|Format|SampleRate)|Output(?:ChannelLayout|Format|SampleRate)|TargetBitRate)|D(?:elayMode|oesSampleRateConversion|ynamicRangeControlMode)|EmploysDependentPackets|Format(?:CFString|Info|List)|HasVariablePacketByteSizes|I(?:nput(?:BufferSize|ChannelLayout|FormatsForOutputFormat)|sInitialized)|M(?:a(?:gicCookie|nufacturerCFString|ximumPacketByteSize)|inimum(?:DelayMode|Number(?:InputPackets|OutputPackets)))|NameCFString|Output(?:ChannelLayout|FormatsForInputFormat)|P(?:a(?:cket(?:FrameSize|SizeLimitForVBR)|ddedZeros)|r(?:ime(?:Info|Method)|ogramTargetLevel(?:Constant)?))|QualitySetting|Re(?:commendedBitRateRange|quiresPacketDescription)|S(?:ettings|oundQualityForVBR|upported(?:InputFormats|OutputFormats))|UsedInputBufferSize|ZeroFramesPadded)))|Quality_(?:High|Low|M(?:ax|edium|in))|ResetSelect|S(?:etPropertySelect|tateError)|U(?:n(?:initializeSelect|knownPropertyError|s(?:pecifiedError|upportedFormatError))|seRecommendedSampleRate))|mponent(?:Err_Instance(?:Invalidated|TimedOut)|Flag_Unsearchable|ValidationResult_(?:Failed|Passed|TimedOut|Un(?:authorizedError_(?:Init|Open)|known))|sFolderType)|n(?:trol(?:ClassID|Property(?:Element|Scope|Variant))|verter(?:A(?:pplicableEncode(?:BitRates|SampleRates)|vailableEncode(?:BitRates|ChannelLayoutTags|SampleRates))|C(?:hannelMap|o(?:decQuality|mpressionMagicCookie)|urrent(?:InputStreamDescription|OutputStreamDescription))|DecompressionMagicCookie|E(?:ncode(?:AdjustableSampleRate|BitRate)|rr_(?:BadPropertySizeError|FormatNotSupported|In(?:putSampleRateOutOfRange|valid(?:InputSize|OutputSize))|O(?:perationNotSupported|utputSampleRateOutOfRange)|PropertyNotSupported|RequiresPacketDescriptionsError|UnspecifiedError))|InputChannelLayout|OutputChannelLayout|Pr(?:ime(?:Info|Method)|operty(?:BitDepthHint|Calculate(?:InputBufferSize|OutputBufferSize)|Dither(?:BitDepth|ing)|FormatList|InputCodecParameters|M(?:aximum(?:Input(?:BufferSize|PacketSize)|OutputPacketSize)|inimum(?:InputBufferSize|OutputBufferSize))|OutputCodecParameters|Settings))|Quality_(?:High|Low|M(?:ax|edium|in))|SampleRateConverter(?:Algorithm|Complexity(?:_(?:Linear|M(?:astering|inimumPhase)|Normal))?|InitialPhase|Quality)))))|D(?:ata(?:DestinationControlClassID|SourceControlClassID)|e(?:coderComponentType|vice(?:ClassID|P(?:ermissionsError|ro(?:cessorOverload|perty(?:A(?:ctualSampleRate|vailableNominalSampleRates)|Buffer(?:FrameSize(?:Range)?|Size(?:Range)?)|C(?:hannel(?:CategoryName(?:CFString)?|N(?:ame(?:CFString)?|ominalLineLevel(?:NameForID(?:CFString)?|s)?|umberName(?:CFString)?))|l(?:ipLight|ock(?:D(?:evice|omain)|Source(?:KindForID|NameForID(?:CFString)?|s)?))|onfigurationApplication)|D(?:ataSource(?:KindForID|NameForID(?:CFString)?|s)?|evice(?:CanBeDefault(?:Device|SystemDevice)|HasChanged|Is(?:Alive|Running(?:Somewhere)?)|Manufacturer(?:CFString)?|Name(?:CFString)?|UID)|riverShouldOwniSub)|H(?:ighPassFilterSetting(?:NameForID(?:CFString)?|s)?|ogMode)|I(?:O(?:CycleUsage|ProcStreamUsage|StoppedAbnormally)|con|sHidden)|JackIsConnected|L(?:atency|istenback)|M(?:odelUID|ute)|NominalSampleRate|P(?:ha(?:ntomPower|seInvert)|l(?:ayThru(?:Destination(?:NameForID(?:CFString)?|s)?|S(?:olo|tereoPan(?:Channels)?)|Volume(?:Decibels(?:ToScalar(?:TransferFunction)?)?|RangeDecibels|Scalar(?:ToDecibels)?))?|ugIn)|referredChannel(?:Layout|sForStereo))|Re(?:gisterBufferList|latedDevices)|S(?:afetyOffset|cope(?:Input|Output|PlayThrough)|olo|t(?:ereoPan(?:Channels)?|ream(?:Configuration|Format(?:Match|Supported|s)?|s))|u(?:b(?:Mute|Volume(?:Decibels(?:ToScalar(?:TransferFunction)?)?|RangeDecibels|Scalar(?:ToDecibels)?))|pportsMixing))|T(?:alkback|ransportType)|UsesVariableBufferFrameSizes|Volume(?:Decibels(?:ToScalar(?:TransferFunction)?)?|RangeDecibels|Scalar(?:ToDecibels)?))))|StartTime(?:DontConsult(?:DeviceFlag|HALFlag)|IsInputFlag)|TransportType(?:A(?:VB|ggregate|irPlay|utoAggregate)|B(?:luetooth(?:LE)?|uiltIn)|DisplayPort|FireWire|HDMI|PCI|Thunderbolt|U(?:SB|nknown)|Virtual)|Un(?:known|supportedFormatError)))|igidesignFolderType)|En(?:coderComponentType|dPoint(?:ClassID|Device(?:ClassID|Property(?:Composition|EndPointList|IsPrivate))))|F(?:ile(?:3GP(?:2Type|Type)|A(?:AC_ADTSType|C3Type|IF(?:CType|FType)|MRType)|BadPropertySizeError|C(?:AFType|loseSelect|o(?:mponent_(?:Available(?:FormatIDs|StreamDescriptionsForFormat)|Can(?:Read|Write)|ExtensionsForType|F(?:astDispatchTable|ileTypeName)|HFSTypeCodesForType|MIMETypesForType|UTIsForType)|untUserDataSelect)|reate(?:Select|URLSelect))|D(?:ataIsThisFormatSelect|oesNotAllow64BitDataSizeError)|E(?:ndOfFileError|xtensionIsThisFormatSelect)|F(?:LACType|ile(?:DataIsThisFormatSelect|IsThisFormatSelect|NotFoundError)|lags_(?:DontPageAlignAudioData|EraseFile))|G(?:et(?:GlobalInfoS(?:elect|izeSelect)|Property(?:InfoSelect|Select)|UserDataS(?:elect|izeSelect))|lobalInfo_(?:A(?:ll(?:Extensions|HFSTypeCodes|MIMETypes|UTIs)|vailable(?:FormatIDs|StreamDescriptionsForFormat))|ExtensionsForType|FileTypeName|HFSTypeCodesForType|MIMETypesForType|ReadableTypes|TypesFor(?:Extension|HFSTypeCode|MIMEType|UTI)|UTIsForType|WritableTypes))|In(?:itialize(?:Select|WithCallbacksSelect)|valid(?:ChunkError|FileError|Packet(?:DependencyError|OffsetError)))|L(?:ATMInLOASType|oopDirection_(?:Backward|Forward(?:AndBackward)?|NoLooping))|M(?:4(?:AType|BType)|P(?:1Type|2Type|3Type|EG4Type)|arkerType_Generic)|N(?:extType|otOp(?:enError|timizedError))|Op(?:e(?:n(?:Select|URLSelect|WithCallbacksSelect)|rationNotSupportedError)|timizeSelect)|P(?:ermissionsError|ositionError|roperty(?:A(?:lbumArtwork|udio(?:Data(?:ByteCount|PacketCount)|TrackCount))|B(?:itRate|yteToPacket)|Ch(?:annelLayout|unkIDs)|D(?:ata(?:Format(?:Name)?|Offset)|eferSizeUpdates)|EstimatedDuration|F(?:ileFormat|ormatList|rameToPacket)|I(?:D3Tag|nfoDictionary|sOptimized)|Ma(?:gicCookieData|rkerList|ximumPacketSize)|NextIndependentPacket|P(?:acket(?:RangeByteCountUpperBound|SizeUpperBound|T(?:ableInfo|o(?:Byte|DependencyInfo|Frame|RollDistance)))|reviousIndependentPacket)|Re(?:gionList|s(?:erveDuration|trictsRandomAccess))|SourceBitDepth|UseAudioTrack))|R(?:F64Type|e(?:ad(?:BytesSelect|P(?:acket(?:DataSelect|sSelect)|ermission)|WritePermission)|gionFlag_(?:LoopEnable|Play(?:Backward|Forward))|moveUserDataSelect))|S(?:et(?:PropertySelect|UserDataSelect)|oundDesigner2Type|tream(?:Error_(?:BadPropertySize|D(?:ataUnavailable|iscontinuityCantRecover)|I(?:llegalOperation|nvalid(?:File|PacketOffset))|NotOptimized|Uns(?:pecifiedError|upported(?:DataFormat|FileType|Property))|ValueUnknown)|P(?:arseFlag_Discontinuity|roperty(?:Flag_(?:CacheProperty|PropertyIsCached)|_(?:A(?:udioData(?:ByteCount|PacketCount)|verageBytesPerPacket)|B(?:itRate|yteToPacket)|ChannelLayout|Data(?:Format|Offset)|F(?:ileFormat|ormatList|rameToPacket)|InfoDictionary|Ma(?:gicCookieData|ximumPacketSize)|NextIndependentPacket|P(?:acket(?:SizeUpperBound|T(?:ableInfo|o(?:Byte|DependencyInfo|Frame|RollDistance)))|reviousIndependentPacket)|Re(?:adyToProducePackets|strictsRandomAccess))))|SeekFlag_OffsetIsEstimated))|Uns(?:pecifiedError|upported(?:DataFormatError|FileTypeError|PropertyError))|W(?:AVEType|rite(?:BytesSelect|P(?:acketsSelect|ermission))))|ormat(?:60958AC3|A(?:C3|ES3|Law|MR(?:_WB)?|pple(?:IMA4|Lossless)|udible)|Bad(?:PropertySizeError|SpecifierSizeError)|DVIIntelIMA|EnhancedAC3|F(?:LAC|lag(?:Is(?:AlignedHigh|BigEndian|Float|Non(?:Interleaved|Mixable)|Packed|SignedInteger)|s(?:A(?:reAllClear|udioUnitCanonical)|Canonical|Native(?:Endian|FloatPacked))))|LinearPCM|M(?:ACE(?:3|6)|IDIStream|PEG(?:4(?:AAC(?:_(?:ELD(?:_(?:SBR|V2))?|HE(?:_V2)?|LD|Spatial))?|CELP|HVXC|TwinVQ)|D_USAC|Layer(?:1|2|3))|icrosoftGSM)|Opus|P(?:arameterValueStream|roperty_(?:A(?:SBDFrom(?:ESDS|MPEGPacket)|reChannelLayoutsEquivalent|vailableEncode(?:BitRates|ChannelLayoutTags|NumberChannels|SampleRates))|B(?:alanceFade|itmapForLayoutTag)|Channel(?:Layout(?:F(?:or(?:Bitmap|Tag)|romESDS)|Hash|Name|SimpleName)|Map|Name|ShortName)|Decode(?:FormatIDs|rs)|Encode(?:FormatIDs|rs)|F(?:irstPlayableFormatFromList|ormat(?:EmploysDependentPackets|I(?:nfo|s(?:E(?:ncrypted|xternallyFramed)|VBR))|List|Name))|ID3Tag(?:Size|ToDictionary)|MatrixMixMap|NumberOfChannelsForLayout|OutputFormatList|PanningMatrix|Tag(?:ForChannelLayout|sForNumberOfChannels)|ValidateChannelLayout))|Q(?:Design(?:2)?|UALCOMM)|TimeCode|U(?:Law|n(?:knownFormatError|s(?:pecifiedError|upported(?:DataFormatError|PropertyError))))|iLBC))|H(?:ardware(?:Bad(?:DeviceError|ObjectError|PropertySizeError|StreamError)|IllegalOperationError|No(?:Error|tRunningError)|P(?:owerHint(?:FavorSavingPower|None)|roperty(?:Bo(?:otChimeVolume(?:Decibels(?:ToScalar(?:TransferFunction)?)?|RangeDecibels|Scalar(?:ToDecibels)?)|xList)|ClockDeviceList|De(?:fault(?:InputDevice|OutputDevice|SystemOutputDevice)|vice(?:ForUID|s))|HogModeIsAllowed|IsInitingOrExiting|MixStereoToMono|P(?:lugIn(?:ForBundleID|List)|owerHint|rocessIs(?:Audible|Master))|RunLoop|S(?:erviceRestarted|leepingIsAllowed)|Trans(?:late(?:BundleIDTo(?:PlugIn|TransportManager)|UIDTo(?:Box|ClockDevice|Device))|portManagerList)|U(?:nloadingIsAllowed|ser(?:IDChanged|SessionIsActiveOrHeadless))))|Service(?:DeviceProperty_VirtualMaster(?:Balance|Volume)|Property_ServiceRestarted)|Un(?:knownPropertyError|s(?:pecifiedError|upportedOperationError)))|ighPassFilterControlClassID)|ISubOwnerControlClassID|JackControlClassID|L(?:FE(?:MuteControlClassID|VolumeControlClassID)|evelControl(?:ClassID|Property(?:Convert(?:DecibelsToScalar|ScalarToDecibels)|Decibel(?:Range|Value|sToScalarTransferFunction)|ScalarValue)|TranferFunction(?:1(?:0Over1|1Over1|2Over1|Over(?:2|3))|2Over1|3Over(?:1|2|4)|4Over1|5Over1|6Over1|7Over1|8Over1|9Over1|Linear))|i(?:neLevelControlClassID|stenbackControlClassID))|MuteControlClassID|O(?:bject(?:ClassID(?:Wildcard)?|Property(?:BaseClass|C(?:lass|ontrolList|reator)|Element(?:CategoryName|Master|N(?:ame|umberName)|Wildcard)|FirmwareVersion|Identify|Listener(?:Added|Removed)|M(?:anufacturer|odelName)|Name|Owne(?:dObjects|r)|S(?:cope(?:Global|Input|Output|PlayThrough|Wildcard)|e(?:lectorWildcard|rialNumber)))|SystemObject|Unknown)|fflineUnit(?:Property_(?:InputSize|OutputSize)|RenderAction_(?:Complete|Preflight|Render))|utputUnit(?:Property_(?:C(?:hannelMap|urrentDevice)|EnableIO|HasIO|IsRunning|S(?:etInputCallback|tartTime(?:stampsAtZero)?))|Range|St(?:artSelect|opSelect)))|P(?:ha(?:ntomPowerControlClassID|seInvertControlClassID)|lugIn(?:C(?:lassID|reateAggregateDevice)|DestroyAggregateDevice|Property(?:B(?:oxList|undleID)|ClockDeviceList|DeviceList|TranslateUIDTo(?:Box|ClockDevice|Device))|sFolderType)|r(?:esetsFolderType|opertyWildcard(?:Channel|PropertyID|Section)))|Queue(?:DeviceProperty_(?:NumberChannels|SampleRate)|Err_(?:Buffer(?:E(?:mpty|nqueuedTwice)|InQueue)|C(?:annotStart(?:Yet)?|odecNotFound)|DisposalPending|EnqueueDuringReset|Invalid(?:Buffer|CodecAccess|Device|OfflineMode|P(?:arameter|roperty(?:Size|Value)?)|QueueType|RunState|Tap(?:Context|Type))|P(?:ermissions|rimeTimedOut)|QueueInvalidated|RecordUnderrun|TooManyTaps)|P(?:aram_(?:P(?:an|itch|layRate)|Volume(?:RampTime)?)|ro(?:cessingTap_(?:EndOfStream|P(?:ostEffects|reEffects)|S(?:iphon|tartOfStream))|perty_(?:C(?:hannelLayout|onverterError|urrent(?:Device|LevelMeter(?:DB)?))|DecodeBufferSizeFrames|Enable(?:LevelMetering|TimePitch)|IsRunning|Ma(?:gicCookie|ximumOutputPacketSize)|StreamDescription|TimePitch(?:Algorithm|Bypass))))|TimePitchAlgorithm_(?:Spectral|TimeDomain|Varispeed))|S(?:e(?:lectorControl(?:ClassID|ItemKindSpacer|Property(?:AvailableItems|CurrentItem|Item(?:Kind|Name)))|rvices(?:Bad(?:PropertySizeError|SpecifierSizeError)|NoError|Property(?:CompletePlaybackIfAppDies|IsUISound)|SystemSound(?:ClientTimedOutError|ExceededMaximumDurationError|UnspecifiedError)|UnsupportedPropertyError)|ttingsFlags_(?:ExpertParameter|InvisibleParameter|MetaParameter|UserInterfaceParameter))|liderControl(?:ClassID|Property(?:Range|Value))|o(?:loControlClassID|und(?:BanksFolderType|sFolderType))|t(?:ereoPanControl(?:ClassID|Property(?:PanningChannels|Value))|ream(?:ClassID|Property(?:Available(?:PhysicalFormats|VirtualFormats)|Direction|IsActive|Latency|OwningDevice|PhysicalFormat(?:Match|Supported|s)?|StartingChannel|TerminalType|VirtualFormat)|TerminalType(?:Di(?:gitalAudioInterface|splayPort)|H(?:DMI|ead(?:phones|setMicrophone))|L(?:FESpeaker|ine)|Microphone|Receiver(?:Microphone|Speaker)|Speaker|TTY|Unknown)|Unknown))|u(?:bDevice(?:ClassID|DriftCompensation(?:HighQuality|LowQuality|M(?:axQuality|ediumQuality|inQuality))|Property(?:DriftCompensation(?:Quality)?|ExtraLatency))|pportFolderType)|ystemObjectClassID)|T(?:alkbackControlClassID|imeStamp(?:HostTimeValid|NothingValid|RateScalarValid|S(?:MPTETimeValid|ample(?:HostTimeValid|TimeValid))|WordClockTimeValid)|oolboxErr(?:_(?:CannotDoInCurrentContext|EndOfTrack|I(?:llegalTrackDestination|nvalid(?:EventType|PlayerState|SequenceType))|NoSequence|StartOfTrack|Track(?:IndexError|NotFound))|or_NoTrackDestination)|ransportManager(?:C(?:lassID|reateEndPointDevice)|DestroyEndPointDevice|Property(?:EndPointList|Trans(?:lateUIDToEndPoint|portType))))|Unit(?:Add(?:PropertyListenerSelect|RenderNotifySelect)|C(?:lumpID_System|omplexRenderSelect)|E(?:rr_(?:CannotDoInCurrentContext|ExtensionNotFound|F(?:ailedInitialization|ileNotSpecified|ormatNotSupported)|I(?:llegalInstrument|n(?:itialized|strumentTypeNotFound|valid(?:Element|File(?:Path)?|OfflineRender|P(?:arameter(?:Value)?|roperty(?:Value)?)|Scope)))|M(?:IDIOutputBufferFull|issingKey)|NoConnection|PropertyNot(?:InUse|Writable)|RenderTimeout|TooManyFramesToProcess|Un(?:authorized|initialized|knownFileType))|vent_(?:BeginParameterChangeGesture|EndParameterChangeGesture|P(?:arameterValueChange|ropertyChange)))|GetP(?:arameterSelect|roperty(?:InfoSelect|Select))|InitializeSelect|M(?:anufacturer_Apple|igrateProperty_(?:FromPlugin|OldAutomation))|OfflineProperty_(?:InputSize|OutputSize|Preflight(?:Name|Requirements)|StartOffset)|P(?:arameter(?:Flag_(?:C(?:FNameRelease|anRamp)|Display(?:Cube(?:Root|d)|Exponential|Logarithmic|Mask|Square(?:Root|d))|ExpertMode|G(?:lobal|roup)|Has(?:C(?:FNameString|lump)|Name)|I(?:nput|s(?:ElementMeta|GlobalMeta|HighResolution|Readable|Writable))|MeterReadOnly|NonRealTime|O(?:mitFromPresets|utput)|PlotHistory|ValuesHaveStrings)|Name_Full|Unit_(?:AbsoluteCents|B(?:PM|eats|oolean)|C(?:ents|ustomUnit)|De(?:cibels|grees)|EqualPowerCrossfade|Generic|Hertz|Indexed|LinearGain|M(?:IDI(?:Controller|NoteNumber)|eters|i(?:lliseconds|xerFaderCurve1))|Octaves|P(?:an|ercent|hase)|R(?:at(?:e|io)|elativeSemiTones)|S(?:ampleFrames|econds)))|ro(?:cess(?:MultipleSelect|Select)|perty_(?:A(?:UHostIdentifier|ddParameterMIDIMapping|llParameterMIDIMappings|udioChannelLayout)|B(?:usCount|ypassEffect)|C(?:PULoad|lassInfo(?:FromDocument)?|o(?:coaUI|ntextName)|urrentP(?:layTime|reset))|De(?:ferredRenderer(?:ExtraLatency|PullSize|WaitFrames)|pendentParameters)|Element(?:Count|Name)|F(?:a(?:ctoryPresets|stDispatch)|requencyResponse)|GetUIComponentList|Ho(?:stCallbacks|tMapParameterMIDIMapping)|I(?:conLocation|n(?:PlaceProcessing|put(?:AnchorTimeStamp|SamplesInOutput)))|La(?:stRenderError|tency)|M(?:IDI(?:ControlMapping|OutputCallback(?:Info)?)|a(?:keConnection|trix(?:Dimensions|Levels)|ximumFramesPerSlice)|eter(?:Clipping|ingMode))|NickName|OfflineRender|P(?:a(?:nnerMode|rameter(?:ClumpName|HistoryInfo|I(?:DName|nfo)|List|StringFromValue|Value(?:FromString|Name|Strings)|sForOverview))|resent(?:Preset|ationLatency))|Re(?:moveParameterMIDIMapping|nderQuality|questViewController|verbRoomType)|S(?:RCAlgorithm|ampleRate(?:ConverterComplexity)?|chedule(?:AudioSlice|StartTimeStamp|dFile(?:BufferSizeFrames|IDs|NumberBuffers|Prime|Region))|et(?:ExternalBuffer|RenderCallback)|houldAllocateBuffer|p(?:atial(?:Mixer(?:AttenuationCurve|DistanceParams|RenderingFlags)|izationAlgorithm)|e(?:akerConfiguration|echChannel))|treamFormat|upport(?:ed(?:ChannelLayoutTags|NumChannels)|sMPE))|TailTime|UsesInternalReverb|Voice)))|R(?:ange|e(?:move(?:PropertyListener(?:Select|WithUserDataSelect)|RenderNotifySelect)|nder(?:Action_(?:DoNotCheckRenderArgs|OutputIsSilence|P(?:ostRender(?:Error)?|reRender))|Select)|setSelect))|S(?:RCAlgorithm_(?:MediumQuality|Polyphase)|ampleRateConverterComplexity_(?:Linear|Mastering|Normal)|c(?:heduleParametersSelect|ope_(?:G(?:lobal|roup)|Input|Layer(?:Item)?|Note|Output|Part))|etP(?:arameterSelect|ropertySelect)|ubType_(?:A(?:U(?:Converter|Filter|iPodTimeOther)|udioFilePlayer)|BandPassFilter|D(?:LSSynth|e(?:f(?:aultOutput|erredRenderer)|lay)|istortion|ynamicsProcessor)|G(?:enericOutput|raphicEQ)|H(?:ALOutput|RTFPanner|igh(?:PassFilter|ShelfFilter))|Low(?:PassFilter|ShelfFilter)|M(?:IDISynth|atrix(?:Mixer|Reverb)|erger|ulti(?:BandCompressor|ChannelMixer|Splitter))|N(?:BandEQ|e(?:t(?:Receive|Send)|wTimePitch))|P(?:arametricEQ|eakLimiter|itch)|R(?:everb2|o(?:gerBeep|undTripAAC))|S(?:ample(?:Delay|r)|cheduledSoundPlayer|oundFieldPanner|p(?:atialMixer|eechSynthesis|hericalHeadPanner|litter)|tereoMixer|ystemOutput)|TimePitch|V(?:arispeed|ectorPanner|oiceProcessingIO)))|Type_(?:Effect|FormatConverter|Generator|M(?:IDIProcessor|ixer|usic(?:Device|Effect))|O(?:fflineEffect|utput)|Panner)|UninitializeSelect|yCodecComponentType)|V(?:STFolderType|olumeControlClassID)|_(?:BadFilePathError|File(?:NotFoundError|PermissionError)|MemFullError|ParamError|TooManyFilesOpenError|UnimplementedError))|t(?:h(?:TypeKCItemAttr|orizationFlag(?:CanNotPreAuthorize|De(?:faults|stroyRights)|ExtendRights|InteractionAllowed|NoData|P(?:artialRights|reAuthorize)))|o(?:GenerateReturnID|matorWorkflowsFolderType|saveInformationFolderType)))|vailBoundsChangedFor(?:D(?:isplay|ock)|MenuBar))|B(?:LibTag2|SLN(?:C(?:ontrolPointFormat(?:NoMap|WithMap)|urrentVersion)|DistanceFormat(?:NoMap|WithMap)|HangingBaseline|Ideographic(?:CenterBaseline|HighBaseline|LowBaseline)|LastBaseline|MathBaseline|N(?:oBaseline(?:Override)?|umBaselineClasses)|RomanBaseline|Tag)|T(?:B(?:adCloseMask|igKeysMask)|HeaderNode|IndexNode|LeafNode|MapNode|VariableIndexKeysMask)|a(?:ck(?:spaceCharCode|wardArrowIcon)|d(?:A(?:dapterErr|rg(?:LengthErr|sErr)|ttributeErr)|BaseErr|C(?:ISErr|ustomIFIDErr)|DeviceErr|EDCErr|HandleErr|IRQErr|LinkErr|OffsetErr|PageErr|S(?:izeErr|ocketErr|peedErr)|T(?:upleDataErr|ypeErr)|V(?:ccErr|ppErr)|WindowErr)|ndpassParam_(?:Bandwidth|CenterFrequency))|ellCharCode|ig5_(?:BasicVariant|DOSVariant|ETenVariant|StandardVariant)|lessed(?:BusErrorBait|Folder)|o(?:otTimeStartupItemsFolderType|xAnnotationSelector)|ridgeSoftwareRunningCantSleep|u(?:llet(?:CharCode|Unicode)|rningIcon|syErr|ttonDialogItem)|y(?:CommentView|DateView|IconView|KindView|LabelView|NameView|S(?:izeView|mallIcon)|VersionView|tePacketTranslationFlag_IsEstimate))|C(?:A(?:Clock(?:Message_(?:Armed|Disarmed|PropertyChanged|St(?:art(?:TimeSet|ed)|opped)|WrongSMPTEFormat)|Property_(?:InternalTimebase|M(?:IDIClockDestinations|TC(?:Destinations|FreewheelTime)|eterTrack)|Name|S(?:MPTE(?:Format|Offset)|endMIDISPP|ync(?:Mode|Source))|T(?:empoMap|imebaseSource))|SyncMode_(?:Internal|M(?:IDIClockTransport|TCTransport))|Time(?:Format_(?:AbsoluteSeconds|Beats|HostTime|S(?:MPTE(?:Seconds|Time)|amples|econds))|base_(?:Audio(?:Device|OutputUnit)|HostTime))|_(?:CannotSetTimeError|Invalid(?:P(?:layRateError|ropertySizeError)|S(?:MPTE(?:FormatError|OffsetError)|ync(?:ModeError|SourceError))|Time(?:FormatError|base(?:Error|SourceError))|UnitError)|UnknownPropertyError))|F(?:LinearPCMFormatFlagIs(?:Float|LittleEndian)|MarkerType_(?:Edit(?:Destination(?:Begin|End)|Source(?:Begin|End))|Generic|Index|KeySignature|Program(?:End|Start)|Re(?:gion(?:End|S(?:tart|yncPoint))|leaseLoop(?:End|Start))|S(?:avedPlayPosition|election(?:End|Start)|ustainLoop(?:End|Start))|T(?:empo|imeSignature|rack(?:End|Start)))|RegionFlag_(?:LoopEnable|Play(?:Backward|Forward))|_(?:AudioDataChunkID|ChannelLayoutChunkID|EditCommentsChunkID|F(?:il(?:e(?:Type|Version_Initial)|lerChunkID)|ormatListID)|In(?:foStringsChunkID|strumentChunkID)|M(?:IDIChunkID|a(?:gicCookieID|rkerChunkID))|OverviewChunkID|P(?:acketTableChunkID|eakChunkID)|RegionChunkID|S(?:MPTE_TimeType(?:2(?:398|4|5|997(?:Drop)?)|30(?:Drop)?|5(?:0|994(?:Drop)?)|60(?:Drop)?|None)|tr(?:eamDescriptionChunkID|ingsChunkID))|U(?:MIDChunkID|UIDChunkID)|iXMLChunkID)))|CRegister(?:CBit|NBit|VBit|XBit|ZBit)|F(?:Error(?:HTTP(?:AuthenticationTypeUnsupported|Bad(?:Credentials|ProxyCredentials|URL)|ConnectionLost|P(?:arseFailure|roxyConnectionFailure)|RedirectionLoopDetected|SProxyConnectionFailure)|PACFile(?:Auth|Error))|FTPErrorUnexpectedStatusCode|H(?:TTPCookieCannotParseCookieFile|ost(?:Addresses|Error(?:HostNotFound|Unknown)|Names|Reachability))|M68kRTA|NetService(?:Error(?:BadArgument|C(?:ancel|ollision)|DNSServiceFailure|In(?:Progress|valid)|NotFound|Timeout|Unknown)|Flag(?:IsD(?:efault|omain)|MoreComing|NoAutoRename|Remove)|MonitorTXT|sError(?:BadArgument|C(?:ancel|ollision)|In(?:Progress|valid)|NotFound|Timeout|Unknown))|S(?:OCKS(?:4Error(?:Id(?:Conflict|entdFailed)|RequestFailed|UnknownStatusCode)|5Error(?:Bad(?:Credentials|ResponseAddr|State)|NoAcceptableMethod|UnsupportedNegotiationMethod)|ErrorUn(?:knownClientVersion|supportedServerVersion))|treamError(?:HTTP(?:Authentication(?:Bad(?:Password|UserName)|TypeUnsupported)|BadURL|ParseFailure|RedirectionLoop|SProxyFailureUnexpectedResponseToCONNECTMethod)|SOCKS(?:4(?:Id(?:Conflict|entdFailed)|RequestFailed|SubDomainResponse)|5(?:Bad(?:ResponseAddr|State)|SubDomain(?:Method|Response|UserPass))|SubDomain(?:None|VersionCode)|UnknownClientVersion)))|URLError(?:AppTransportSecurityRequiresSecureConnection|Ba(?:ckgroundSession(?:InUseByAnotherProcess|WasDisconnected)|d(?:ServerResponse|URL))|C(?:a(?:llIsActive|n(?:celled|not(?:C(?:loseFile|onnectToHost|reateFile)|Decode(?:ContentData|RawData)|FindHost|LoadFromNetwork|MoveFile|OpenFile|ParseResponse|RemoveFile|WriteToFile)))|lientCertificateRe(?:jected|quired))|D(?:NSLookupFailed|ata(?:LengthExceedsMaximum|NotAllowed)|ownloadDecodingFailed(?:MidStream|ToComplete))|File(?:DoesNotExist|IsDirectory|OutsideSafeArea)|HTTPTooManyRedirects|InternationalRoamingOff|N(?:etworkConnectionLost|o(?:PermissionsToReadFile|tConnectedToInternet))|Re(?:directToNonExistentLocation|questBodyStreamExhausted|sourceUnavailable)|Se(?:cureConnectionFailed|rverCertificate(?:Has(?:BadDate|UnknownRoot)|NotYetValid|Untrusted))|TimedOut|U(?:n(?:known|supportedURL)|ser(?:AuthenticationRequired|CancelledAuthentication))|ZeroByteResource))|G(?:Image(?:AnimationStatus_(?:AllocationFailure|CorruptInputImage|IncompleteInputImage|ParameterError|UnsupportedFormat)|Metadata(?:Error(?:BadArgument|ConflictingArguments|PrefixConflict|Un(?:known|supportedFormat))|Type(?:A(?:lternate(?:Array|Text)|rray(?:Ordered|Unordered))|Default|Invalid|Str(?:ing|ucture)))|PropertyOrientation(?:Down(?:Mirrored)?|Left(?:Mirrored)?|Right(?:Mirrored)?|Up(?:Mirrored)?)|Status(?:Complete|In(?:complete|validData)|ReadingHeader|Un(?:expectedEOF|knownType)))|L(?:Bad(?:A(?:ddress|lloc|ttribute)|Co(?:deModule|n(?:nection|text))|D(?:isplay|rawable)|Enumeration|FullScreen|Match|OffScreen|P(?:ixelFormat|roperty)|RendererInfo|State|Value|Window)|C(?:E(?:CrashOnRemovedFunctions|DisplayListOptimization|MPEngine|Rasterization|S(?:tateValidation|urfaceBackingSize|wap(?:Limit|Rectangle)))|P(?:C(?:lientStorage|ontextPriorityRequest(?:High|Low|Normal)|urrentRendererID)|DispatchTableSize|GPU(?:FragmentProcessing|RestartStatus(?:Blacklisted|Caused|None)|VertexProcessing)|HasDrawable|MPSwapsInFlight|ReclaimResources|S(?:urface(?:BackingSize|O(?:pacity|rder)|SurfaceVolatile|Texture)|wap(?:Interval|Rectangle))))|GO(?:ClearFormatCache|FormatCacheSize|Re(?:setLibrary|tainRenderers)|Use(?:BuildCache|ErrorHandler))|NoError|OGLPVersion_(?:3_2_Core|GL3_Core|Legacy)|PFA(?:A(?:cc(?:elerated(?:Compute)?|umSize)|l(?:l(?:Renderers|owOfflineRenderers)|phaSize)|ux(?:Buffers|DepthStencil))|Backing(?:Store|Volatile)|C(?:losestPolicy|o(?:lor(?:Float|Size)|mpliant))|D(?:epthSize|isplayMask|oubleBuffer)|FullScreen|M(?:PSafe|aximumPolicy|inimumPolicy|ulti(?:Screen|sample))|NoRecovery|O(?:ffScreen|penGLProfile)|PBuffer|R(?:e(?:motePBuffer|ndererID)|obust)|S(?:ample(?:Alpha|Buffers|s)|ingleRenderer|te(?:ncilSize|reo)|upersample)|TripleBuffer|VirtualScreenCount|Window)|R(?:P(?:Acc(?:elerated(?:Compute)?|umModes)|B(?:ackingStore|ufferModes)|Co(?:lorModes|mpliant)|D(?:epthModes|isplayMask)|FullScreen|GPU(?:FragProcCapable|VertProcCapable)|M(?:PSafe|ax(?:AuxBuffers|Sample(?:Buffers|s))|ultiScreen)|O(?:ffScreen|nline)|R(?:enderer(?:Count|ID)|obust)|S(?:ample(?:Alpha|Modes)|tencilModes)|TextureMemory(?:Megabytes)?|VideoMemory(?:Megabytes)?|Window)|enderer(?:A(?:TIRa(?:deon(?:8500ID|9700ID|ID|X(?:1000ID|2000ID|3000ID))|ge(?:128ID|ProID))|ppleSWID)|Ge(?:Force(?:2MXID|3ID|8xxxID|FXID)|neric(?:FloatID|ID))|Intel(?:900ID|HD(?:4000ID|ID)|X3100ID)|Mesa3DFXID|VTBladeXP2ID))))|JK(?:ItalicRoman(?:O(?:ffSelector|nSelector)|Selector)|RomanSpacingType|SymbolAlt(?:F(?:iveSelector|ourSelector)|OneSelector|T(?:hreeSelector|woSelector)|ernativesType)|VerticalRoman(?:CenteredSelector|HBaselineSelector|PlacementType))|M(?:ActivateTextService|CopyTextServiceInputModeList|DeactivateTextService|F(?:ixTextService|loatBitmapFlags(?:Alpha(?:Premul)?|None|RangeClipped))|Get(?:InputModePaletteMenu|ScriptLangSupport|TextService(?:Menu|Property))|H(?:elpItem(?:AppleGuide|NoHelp|OtherHelp|RemoveHelp)|idePaletteWindows)|In(?:itiateTextService|putModePaletteItemHit)|MenuItemSelected|NothingSelected|S(?:Attr(?:Apple(?:CodesigningHashAgility(?:V2)?|ExpirationTime)|None|S(?:igningTime|mime(?:Capabilities|EncryptionKeyPrefs|MSEncryptionKeyPrefs)))|Certificate(?:Chain(?:WithRoot(?:OrFail)?)?|None|SignerOnly)|Signer(?:Invalid(?:Cert|Index|Signature)|NeedsDetachedContent|Unsigned|Valid)|etTextService(?:Cursor|Property)|howHelpSelected)|Te(?:rminateTextService|xtService(?:Event(?:Ref)?|MenuSelect))|UCTextServiceEvent)|S(?:Accept(?:AllComponentsMode|ThreadSafeComponentsOnlyMode)|DiskSpaceRecoveryOptionNoUI|Identity(?:AuthorityNotAccessibleErr|C(?:lass(?:Group|User)|ommitCompleted)|D(?:eletedErr|uplicate(?:FullNameErr|PosixNameErr))|Flag(?:Hidden|None)|Invalid(?:FullNameErr|PosixNameErr)|PermissionErr|Query(?:Event(?:ErrorOccurred|Results(?:Added|Changed|Removed)|SearchPhaseFinished)|GenerateUpdateEvents|IncludeHiddenIdentities|String(?:BeginsWith|Equals))|UnknownAuthorityErr)|SM_APPLEDL_MASK_MODE|tackBased)|T(?:CharacterCollection(?:Adobe(?:CNS1|GB1|Japan(?:1|2)|Korea1)|IdentityMapping)|F(?:ont(?:BoldTrait|C(?:la(?:rendonSerifsClass|ss(?:ClarendonSerifs|FreeformSerifs|M(?:ask(?:Shift|Trait)|odernSerifs)|O(?:ldStyleSerifs|rnamentals)|S(?:ansSerif|cripts|labSerifs|ymbolic)|TransitionalSerifs|Unknown))|o(?:l(?:lectionCopy(?:DefaultOptions|StandardSort|Unique)|orGlyphsTrait)|mpositeTrait|ndensedTrait))|DescriptorMatching(?:D(?:id(?:Begin|F(?:ailWithError|inish(?:Downloading)?)|Match)|ownloading)|Stalled|WillBegin(?:Downloading|Querying))|ExpandedTrait|F(?:ormat(?:Bitmap|OpenType(?:PostScript|TrueType)|PostScript|TrueType|Unrecognized)|reeformSerifsClass)|ItalicTrait|M(?:anager(?:AutoActivation(?:D(?:efault|isabled)|Enabled)|Error(?:A(?:lreadyRegistered|ssetNotFound)|CancelledByUser|DuplicatedName|ExceededResourceLimit|FileNotFound|In(?:Use|sufficient(?:Info|Permissions)|validF(?:ilePath|ontData))|MissingEntitlement|NotRegistered|RegistrationFailed|SystemRequired|UnrecognizedFormat)|Scope(?:None|P(?:ersistent|rocess)|Session|User))|o(?:dernSerifsClass|noSpaceTrait))|O(?:ldStyleSerifsClass|ptions(?:Default|Pre(?:ferSystemFont|ventAutoActivation))|r(?:ientation(?:Default|Horizontal|Vertical)|namentalsClass))|Priority(?:Computer|Dynamic|Network|Process|System|User)|S(?:ansSerifClass|criptsClass|labSerifsClass|ymbolicClass)|T(?:able(?:A(?:cnt|nkr|var)|B(?:ASE|dat|hed|loc|sln)|C(?:B(?:DT|LC)|FF(?:2)?|OLR|PAL|idg|map|v(?:ar|t))|DSIG|EB(?:DT|LC|SC)|F(?:dsc|eat|mtx|ond|pgm|var)|G(?:DEF|POS|SUB|asp|lyf|var)|H(?:VAR|dmx|ead|hea|mtx|sty)|J(?:STF|ust)|Ker(?:n|x)|L(?:TSH|car|oca|tag)|M(?:ATH|ERG|VAR|axp|eta|or(?:t|x))|Name|O(?:S2|p(?:bd|tionNoOptions))|P(?:CLT|ost|r(?:ep|op))|S(?:TAT|VG|bi(?:t|x))|Trak|V(?:DMX|ORG|VAR|hea|mtx)|Xref|Zapf)|ra(?:it(?:Bold|C(?:lassMask|o(?:lorGlyphs|mposite|ndensed))|Expanded|Italic|MonoSpace|UIOptimized|Vertical)|nsitionalSerifsClass))|U(?:I(?:Font(?:A(?:lertHeader|pplication)|ControlContent|EmphasizedSystem(?:Detail)?|Label|M(?:e(?:nu(?:Item(?:CmdKey|Mark)?|Title)|ssage)|ini(?:EmphasizedSystem|System))|None|P(?:alette|ushButton)|S(?:mall(?:EmphasizedSystem|System|Toolbar)|ystem(?:Detail)?)|Tool(?:Tip|bar)|U(?:ser(?:FixedPitch)?|tilityWindowTitle)|Views|WindowTitle)|OptimizedTrait)|nknownClass)|VerticalTrait)|rameP(?:athFill(?:EvenOdd|WindingNumber)|rogression(?:LeftToRight|RightToLeft|TopToBottom)))|Line(?:B(?:ounds(?:ExcludeTypographic(?:Leading|Shifts)|IncludeLanguageExtents|Use(?:GlyphPathBounds|HangingPunctuation|OpticalBounds))|reakBy(?:C(?:harWrapping|lipping)|Truncating(?:Head|Middle|Tail)|WordWrapping))|Truncation(?:End|Middle|Start))|ParagraphStyleSpecifier(?:Alignment|BaseWritingDirection|Count|DefaultTabInterval|FirstLineHeadIndent|HeadIndent|Line(?:B(?:oundsOptions|reakMode)|HeightMultiple|SpacingAdjustment)|M(?:aximumLine(?:Height|Spacing)|inimumLine(?:Height|Spacing))|ParagraphSpacing(?:Before)?|Ta(?:bStops|ilIndent))|Run(?:Delegate(?:CurrentVersion|Version1)|Status(?:HasNonIdentityMatrix|No(?:Status|nMonotonic)|RightToLeft))|TextAlignment(?:Center|Justified|Left|Natural|Right)|Underline(?:Pattern(?:D(?:ash(?:Dot(?:Dot)?)?|ot)|Solid)|Style(?:Double|None|Single|Thick))|WritingDirection(?:Embedding|LeftToRight|Natural|Override|RightToLeft))|UPSPPDDomain|V(?:AttachmentMode_Should(?:NotPropagate|Propagate)|Pixel(?:Buffer(?:Lock_ReadOnly|PoolFlushExcessBuffers)|FormatType_(?:1(?:28RGBAFloat|4Bayer_(?:BGGR|G(?:BRG|RBG)|RGGB)|6(?:BE5(?:55|65)|Gray|LE5(?:55(?:1)?|65))|IndexedGray_WhiteIsZero|Monochrome)|2(?:4(?:BGR|RGB)|Indexed(?:Gray_WhiteIsZero)?)|3(?:0RGB(?:LEPackedWideGamut)?|2(?:A(?:BGR|RGB|lphaGray)|BGRA|RGBA))|4(?:2(?:0YpCbCr(?:10BiPlanar(?:FullRange|VideoRange)|8(?:BiPlanar(?:FullRange|VideoRange)|Planar(?:FullRange)?|VideoRange_8A_TriPlanar))|2YpCbCr(?:1(?:0(?:BiPlanar(?:FullRange|VideoRange))?|6)|8(?:FullRange|_yuvs)?|_4A_8BiPlanar))|44(?:4(?:AYpCbCr(?:16|8)|YpCbCrA8(?:R)?)|YpCbCr(?:10(?:BiPlanar(?:FullRange|VideoRange))?|8))|8RGB|Indexed(?:Gray_WhiteIsZero)?)|64(?:ARGB|RGBAHalf)|8Indexed(?:Gray_WhiteIsZero)?|ARGB2101010LEPacked|D(?:epthFloat(?:16|32)|isparityFloat(?:16|32))|OneComponent(?:16Half|32Float|8)|TwoComponent(?:16Half|32Float|8)))|Return(?:AllocationFailed|DisplayLink(?:AlreadyRunning|CallbacksNotSet|NotRunning)|Error|First|Invalid(?:Argument|Display|P(?:ixel(?:BufferAttributes|Format)|oolAttributes)|Size)|Last|P(?:ixelBufferNot(?:MetalCompatible|OpenGLCompatible)|oolAllocationFailed)|Retry|Success|Unsupported|WouldExceedAllocationThreshold)|SMPTETime(?:Running|Type(?:2(?:4|5|997(?:Drop)?)|30(?:Drop)?|5994|60)|Valid)|Time(?:IsIndefinite|Stamp(?:BottomField|HostTimeValid|IsInterlaced|RateScalarValid|SMPTETimeValid|TopField|Video(?:HostTimeValid|RefreshPeriodValid|TimeValid))))|a(?:chedDataFolderType|l(?:ibratorNamePrefix|lingConvention(?:Mask|Phase|Width))|n(?:onicalCompositionO(?:ffSelector|nSelector)|t(?:ConfigureCardErr|ReportProcessorTemperatureErr))|r(?:bonLibraryFolderType|d(?:BusCardErr|PowerOffErr))|seSensitive(?:Layout(?:O(?:ffSelector|nSelector)|Type)|SpacingO(?:ffSelector|nSelector))|utionIcon)|e(?:nterOn(?:MainScreen|Screen)|rt(?:Search(?:Any|Decrypt(?:Allowed|Disallowed|Ignored|Mask)|Encrypt(?:Allowed|Disallowed|Ignored|Mask)|PrivKeyRequired|S(?:hift|igning(?:Allowed|Disallowed|Ignored|Mask))|Unwrap(?:Allowed|Disallowed|Ignored|Mask)|Verify(?:Allowed|Disallowed|Ignored|Mask)|Wrap(?:Allowed|Disallowed|Ignored|Mask))|Usage(?:AllAdd|DecryptA(?:dd|skAndAdd)|EncryptA(?:dd|skAndAdd)|KeyExchA(?:dd|skAndAdd)|RootA(?:dd|skAndAdd)|S(?:SLA(?:dd|skAndAdd)|hift|igningA(?:dd|skAndAdd))|VerifyA(?:dd|skAndAdd))|ificateKCItemClass))|h(?:aracter(?:AlternativesType|PaletteInputMethodClass|ShapeType)|e(?:ck(?:BoxDialogItem|CharCode|Unicode)|wableItemsFolderType))|ircleAnnotationSelector|l(?:ass(?:KCItemAttr|ic(?:D(?:esktopFolderType|omain)|PreferencesFolderType))|ea(?:nUpAEUT|rCharCode)|i(?:entRequestDenied|p(?:boardIcon|ping(?:Creator|PictureType(?:Icon)?|SoundType(?:Icon)?|TextType(?:Icon)?|UnknownType(?:Icon)?))))|o(?:l(?:l(?:ate(?:AttributesNotFoundErr|BufferTooSmall|Invalid(?:C(?:har|ollationRef)|Options)|MissingUnicodeTableErr|PatternNotFoundErr|UnicodeConvertFailedErr)|ection(?:AllAttributes|D(?:efaultAttributes|ontWant(?:Attributes|Data|I(?:d|ndex)|Size|Tag))|Lock(?:Bit|Mask)|NoAttributes|Persistence(?:Bit|Mask)|Reserved(?:0(?:Bit|Mask)|1(?:0(?:Bit|Mask)|1(?:Bit|Mask)|2(?:Bit|Mask)|3(?:Bit|Mask)|Bit|Mask)|2(?:Bit|Mask)|3(?:Bit|Mask)|4(?:Bit|Mask)|5(?:Bit|Mask)|6(?:Bit|Mask)|7(?:Bit|Mask)|8(?:Bit|Mask)|9(?:Bit|Mask))|User(?:0(?:Bit|Mask)|1(?:0(?:Bit|Mask)|1(?:Bit|Mask)|2(?:Bit|Mask)|3(?:Bit|Mask)|4(?:Bit|Mask)|5(?:Bit|Mask)|Bit|Mask)|2(?:Bit|Mask)|3(?:Bit|Mask)|4(?:Bit|Mask)|5(?:Bit|Mask)|6(?:Bit|Mask)|7(?:Bit|Mask)|8(?:Bit|Mask)|9(?:Bit|Mask)|Attributes)))|or(?:Picker(?:AppIsColorSyncAware|Ca(?:llColorProcLive|n(?:AnimatePalette|ModifyPalette))|D(?:etachedFromChoices|ialogIsMo(?:dal|veable))|In(?:ApplicationDialog|PickerDialog|SystemDialog)|sFolderType)|Sync(?:1(?:0BitInteger|6Bit(?:Float|Integer)|BitGamut)|32Bit(?:Float|Integer|NamedColorIndex)|8BitInteger|Alpha(?:First|InfoMask|Last|None(?:Skip(?:First|Last))?|Premultiplied(?:First|Last))|ByteOrder(?:16(?:Big|Little)|32(?:Big|Little)|Default|Mask)|CMMFolderType|Folder(?:Icon|Type)|ProfilesFolderType|ScriptingFolderType))?)|m(?:m(?:and(?:CharCode|Unicode)|entKCItemAttr|on(?:LigaturesO(?:ffSelector|nSelector)|NameKCItemAttr))|p(?:atibilityCompositionO(?:ffSelector|nSelector)|o(?:nent(?:AliasResourceType|C(?:anDoSelect|loseSelect)|DebugOption|ExecuteWiredActionSelect|Get(?:MPWorkFunctionSelect|PublicResourceSelect)|OpenSelect|Re(?:gisterSelect|sourceType)|TargetSelect|UnregisterSelect|VersionSelect|sFolderType)|sitionsFolderType)|uterIcon))|n(?:figurationLockedErr|n(?:Suite|ect(?:ToIcon|ion(?:AudioStreaming|BlueGammaScale|C(?:h(?:anged|eckEnable)|o(?:lor(?:DepthsSupported|Mode(?:sSupported)?)|ntroller(?:ColorDepth|D(?:epthsSupported|itherControl))))|Display(?:Flags|Parameter(?:Count|s))|Enable(?:Audio)?|Fl(?:ags|ushParameters)|G(?:ammaScale|reenGammaScale)|HandleDisplayPortEvent|Ignore|Overscan|P(?:anelTimingDisable|o(?:stWake|wer)|robe)|RedGammaScale|S(?:tartOfFrameTime|upports(?:AppleSense|HLDDCSense|LLDDCSense)|ync(?:Enable|Flags))|V(?:BLMultiplier|ideoBest))))|t(?:ainer(?:AliasType|CDROMAliasType|F(?:loppyAliasType|olderAliasType)|HardDiskAliasType|ServerAliasType|TrashAliasType)|extual(?:Alternates(?:O(?:ffSelector|nSelector)|Type)|LigaturesO(?:ffSelector|nSelector)|MenuItemsFolder(?:Icon|Type)|SwashAlternatesO(?:ffSelector|nSelector))|rol(?:A(?:dd(?:FontSizeMask|ToMetaFontMask)|utoToggles)|B(?:e(?:havior(?:CommandMenu|MultiValueMenu|OffsetContents|Pushbutton|S(?:ingleValueMenu|ticky)|Toggles)|velButton(?:Align(?:Bottom(?:Left|Right)?|Center|Left|Right|SysDirection|T(?:ext(?:Center|Flush(?:Left|Right)|SysDirection)|op(?:Left|Right)?))|C(?:enterPopupGlyphTag|ontentTag)|Graphic(?:AlignTag|OffsetTag)|IsMultiValueMenuTag|KindTag|La(?:rgeBevel(?:Proc|Variant)?|stMenuTag)|Menu(?:DelayTag|HandleTag|On(?:Bottom|Right(?:Variant)?)|RefTag|ValueTag)|NormalBevel(?:Proc|Variant)?|OwnedMenuRefTag|Place(?:AboveGraphic|BelowGraphic|Normally|SysDirection|To(?:LeftOfGraphic|RightOfGraphic))|S(?:caleIconTag|mallBevel(?:Proc|Variant)?)|T(?:ext(?:AlignTag|OffsetTag|PlaceTag)|ransformTag)))|oundsChange(?:PositionChanged|SizeChanged)|uttonPart)|C(?:h(?:asingArrows(?:AnimatingTag|Proc)|eckBox(?:AutoToggleProc|CheckedValue|MixedValue|P(?:art|roc)|UncheckedValue))|l(?:ickableMetaPart|ock(?:A(?:MPMPart|bsoluteTimeTag|nimatingTag)|DateProc|F(?:lag(?:DisplayOnly|Live|Standard)|ontStyleTag)|HourDayPart|Is(?:DisplayOnly|Live)|LongDateTag|M(?:inuteMonthPart|onthYearProc)|NoFlags|Part|SecondYearPart|T(?:ime(?:Proc|SecondsProc)|ype(?:HourMinute(?:Second)?|Month(?:DayYear|Year)))))|o(?:l(?:lectionTag(?:Bounds|Command|ID(?:ID|Signature)|M(?:aximum|inimum)|RefCon|Title|UnicodeTitle|V(?:a(?:lue|rCode)|i(?:ewSize|sibility)))|orTableResourceType)|ntent(?:AlertIconRes|C(?:GImageRef|Icon(?:Handle|Res))|I(?:CON(?:Res)?|con(?:Ref|Suite(?:Handle|Res)))|MetaPart|Pict(?:Handle|Res)|T(?:ag|extOnly))))|D(?:ataBrowser(?:DraggedPart|EditText(?:KeyFilterTag|ValidationProcTag)|IncludesFrameAndFocusTag|KeyFilterTag|Part)|efProc(?:ResourceType|Type)|i(?:alogItem|s(?:abledPart|closure(?:Button(?:Closed|Disclosed)|TrianglePoint(?:Default|Left|Right))))|ownButtonPart)|E(?:dit(?:Text(?:C(?:FStringTag|harCount)|FixedTextTag|In(?:line(?:InputProc|P(?:ostUpdateProcTag|reUpdateProcTag))|sert(?:CFStringRefTag|TextBufferTag))|Key(?:FilterTag|ScriptBehaviorTag)|LockedTag|P(?:a(?:rt|ssword(?:CFStringTag|Proc|Tag))|roc)|S(?:electionTag|ingleLineTag|pellCheck(?:AsYouTypeTag|ingTag)|tyleTag)|T(?:EHandleTag|extTag)|ValidationProcTag)|UnicodeTextP(?:asswordProc|ostUpdateProcTag|roc))|ntireControl)|Fo(?:cus(?:N(?:extPart|oPart)|PrevPart)|nt(?:BigSystemFont|MiniSystemFont|S(?:mall(?:BoldSystemFont|SystemFont)|tyleTag)|ViewSystemFont))|G(?:etsFocusOnClick|roupBox(?:CheckBoxProc|F(?:ontStyleTag|rameRectTag)|Menu(?:HandleTag|RefTag)|PopupButtonProc|Secondary(?:CheckBoxProc|PopupButtonProc|TextTitleProc)|T(?:extTitleProc|itleRectTag)))|Ha(?:ndlesTracking|s(?:RadioBehavior|SpecialBackground))|I(?:con(?:AlignmentTag|ContentTag|NoTrackProc|P(?:art|roc)|Re(?:f(?:NoTrackProc|Proc)|sourceIDTag)|Suite(?:NoTrackProc|Proc)|TransformTag)|dlesWithTimer|mageWell(?:ContentTag|IsDragDestinationTag|P(?:art|roc)|TransformTag)|n(?:activePart|dicatorPart|vertsUpDownValueMeaning))|K(?:ey(?:Filter(?:BlockKey|PassKey|Tag)|ScriptBehavior(?:AllowAnyScript|PrefersRoman|RequiresRoman))|ind(?:BevelButton|C(?:h(?:asingArrows|eck(?:Box|GroupBox))|lock)|D(?:ataBrowser|isclosure(?:Button|Triangle))|Edit(?:Text|UnicodeText)|GroupBox|HI(?:ComboBox|GrowBoxView|ImageView|MenuView|S(?:crollView|earchField|tandardMenuView)|TextView)|I(?:con|mageWell)|Li(?:stBox|ttleArrows)|P(?:icture|lacard|opup(?:Arrow|Button|GroupBox)|rogressBar|ush(?:Button|IconButton))|R(?:adio(?:Button|Group)|elevanceBar|oundButton)|S(?:croll(?:Bar|ingTextBox)|eparator|ignatureApple|lider|taticText)|Ta(?:bs|g)|UserPane|WindowHeader))|L(?:abelPart|i(?:st(?:Box(?:AutoSizeProc|DoubleClick(?:Part|Tag)|FontStyleTag|KeyFilterTag|L(?:DEFTag|istHandleTag)|P(?:art|roc))|DescResType)|ttleArrows(?:IncrementValueTag|Proc)))|MenuPart|No(?:Content|Part|Variant)|OpaqueMetaPart|P(?:a(?:ge(?:DownPart|UpPart)|nel(?:DisabledFolder(?:Icon|Type)|Folder(?:AliasType|Icon(?:Resource)?|Type)))|icture(?:HandleTag|NoTrackProc|P(?:art|roc))|lacardProc|opup(?:Arrow(?:EastProc|NorthProc|Orientation(?:East|North|South|West)|S(?:ize(?:Normal|Small)|mall(?:EastProc|NorthProc|SouthProc|WestProc)|outhProc)|WestProc)|Button(?:CheckCurrentTag|ExtraHeightTag|Menu(?:HandleTag|IDTag|RefTag)|OwnedMenuRefTag|Proc)|FixedWidthVariant|Use(?:AddResMenuVariant|WFontVariant)|VariableWidthVariant)|ro(?:gressBar(?:AnimatingTag|IndeterminateTag|Proc)|pertyPersistent)|ushBut(?:LeftIconProc|RightIconProc|ton(?:AnimatingTag|C(?:ancelTag|ontentTag)|DefaultTag|I(?:con(?:AlignmentTag|On(?:Left|Right))|sTexturedTag)|Proc)))|R(?:adio(?:Button(?:AutoToggleProc|CheckedValue|MixedValue|P(?:art|roc)|UncheckedValue)|GroupP(?:art|roc))|elevanceBarProc|oundButton(?:ContentTag|LargeSize|NormalSize|SizeTag))|S(?:croll(?:Bar(?:LiveProc|Proc|ShowsArrowsTag)|TextBox(?:A(?:nimatingTag|utoScroll(?:AmountTag|Proc))|ContentsTag|DelayBe(?:foreAutoScrollTag|tweenAutoScrollTag)|Proc))|e(?:archField(?:CancelPart|MenuPart)|paratorLineProc)|ize(?:Auto|Large|Mini|Normal|Small|Tag)|lider(?:DoesNotPoint|HasTickMarks|LiveFeedback|NonDirectional|P(?:oints(?:DownOrRight|UpOrLeft)|roc)|ReverseDirection)|t(?:aticText(?:CFStringTag|IsMultilineTag|Proc|StyleTag|T(?:ext(?:HeightTag|Tag)|runcTag))|r(?:ipModulesFolder(?:Icon|Type)|uctureMetaPart))|upports(?:C(?:alcBestRect|lickActivation|ontextualMenus)|D(?:ataAccess|ragAndDrop)|Embedding|F(?:lattening|ocus)|G(?:etRegion|hosting)|LiveFeedback|SetCursor))|T(?:ab(?:ContentRectTag|Direction(?:East|North|South|West)|EnabledFlagTag|FontStyleTag|I(?:mageContentTag|nfo(?:Tag|Version(?:One|Zero)))|L(?:arge(?:EastProc|NorthProc|Proc|SouthProc|WestProc)|istResType)|S(?:ize(?:Large|Mini|Small)|mall(?:EastProc|NorthProc|Proc|SouthProc|WestProc)))|emplateResourceType|hemeText(?:FontTag|HorizontalFlushTag|InfoTag|TruncationTag|VerticalFlushTag)|riangle(?:AutoToggleProc|L(?:astValueTag|eftFacing(?:AutoToggleProc|Proc))|P(?:art|roc)))|U(?:nicode|pButtonPart|se(?:AllMask|BackColorMask|F(?:aceMask|o(?:ntMask|reColorMask))|JustMask|ModeMask|SizeMask|ThemeFontIDMask|r(?:ItemDrawProcTag|Pane(?:ActivateProcTag|BackgroundProcTag|DrawProcTag|FocusProcTag|HitTestProcTag|IdleProcTag|KeyDownProcTag|Proc|TrackingProcTag))|sOwningWindowsFontVariant))|W(?:ants(?:Activate|Idle)|indow(?:Header(?:IsListHeaderTag|Proc)|ListViewHeaderProc))))|verterPrimeMethod_(?:No(?:ne|rmal)|Pre))|operativeThread|re(?:E(?:ndian(?:AppleEventManagerDomain|ResourceManagerDomain)|ventClass)|ServicesFolderType))|reat(?:e(?:Folder(?:AtBoot(?:Bit)?)?|IfNeeded)|ionDateKCItemAttr|orKCItemAttr)|u(?:r(?:rent(?:MixedModeStateRecord|Process|ThreadID|User(?:Folder(?:Location|Type)|RemoteFolder(?:Location|Type)))|sive(?:ConnectionType|Selector))|stom(?:BadgeResource(?:ID|Type|Version)|Icon(?:KCItemAttr|Resource))))|D(?:0Dispatched(?:CStackBased|PascalStackBased)|1DispatchedPascalStackBased|CM(?:A(?:llowListing|nyFieldT(?:ag|ype))|BasicDictionaryClass|Can(?:AddDictionaryFieldMask|CreateDictionaryMask|HaveMultipleIndexMask|ModifyDictionaryMask|StreamDictionaryMask|Use(?:FileDictionaryMask|MemoryDictionaryMask|TransactionMask))|DictionaryHeader(?:Signature|Version)|Fi(?:ndMethod(?:B(?:ackwardTrie|eginningMatch)|ContainsMatch|E(?:ndingMatch|xactMatch)|ForwardTrie)|xedSizeFieldMask)|HiddenFieldMask|I(?:dentifyFieldMask|ndexedFieldMask)|Japanese(?:AccentT(?:ag|ype)|FukugouInfoT(?:ag|ype)|H(?:inshiT(?:ag|ype)|yokiT(?:ag|ype))|OnKunReadingT(?:ag|ype)|PhoneticT(?:ag|ype)|WeightT(?:ag|ype)|YomiT(?:ag|ype))|ProhibitListing|Re(?:ad(?:OnlyDictionary|WriteDictionary)|quiredFieldMask)|SpecificDictionaryClass|UserDictionaryClass)|M(?:CantBlock|D(?:isplay(?:AlreadyInstalledErr|NotFoundErr)|riverNotDisplayMgrAwareErr)|FoundErr|GenErr|M(?:ainDisplayCannotMoveErr|irroring(?:Blocked|NotOn|OnAlready))|No(?:DeviceTableclothErr|tFoundErr)|SWNotInitializedErr|WrongNumberOfDisplays)|OSJapanese(?:PalmVariant|StandardVariant)|R(?:AudioFileNotSupportedErr|B(?:adLayoutErr|lock(?:Size(?:Audio|DVDData|Mode(?:1Data|2(?:Data|Form(?:1Data|2Data))))|Type(?:Audio|DVDData|Mode(?:1Data|2(?:Data|Form(?:1Data|2Data)))))|urn(?:MediaWriteFailureErr|NotAllowedErr|PowerCalibrationErr|UnderrunErr))|CDText(?:Encoding(?:ASCII|ISOLatin1Modified)|GenreCode(?:A(?:dultContemporary|lternativeRock)|C(?:hildrens|lassical|o(?:ntemporaryChristian|untry))|Dance|E(?:asyListening|rotic)|Folk|Gospel|HipHop|Jazz|Latin|Musical|NewAge|Oper(?:a|etta)|Pop|R(?:ap|eggae|hythmAndBlues|ock)|S(?:ound(?:Effects|track)|pokenWord)|Unknown|WorldMusic))|D(?:ata(?:Form(?:Audio|DVDData|Mode(?:1Data|2(?:Data|Form(?:1Data|2Data))))|ProductionErr)|evice(?:AccessErr|Bu(?:rnStrategyNotAvailableErr|syErr)|C(?:antWrite(?:CDTextErr|I(?:SRCErr|ndexPointsErr)|SCMSErr)|ommunicationErr)|InvalidErr|Not(?:ReadyErr|SupportedErr)|PreGapLengthNotValidErr)|oubleLayerL0(?:AlreadySpecifiedErr|DataZoneBlocksParamErr))|F(?:i(?:le(?:Fork(?:Data|Resource|Size(?:Actual|Estimate))|LocationConflictErr|M(?:essage(?:ForkSize|P(?:ostBurn|r(?:eBurn|oduceData))|Release|VerificationStarting)|odifiedDuringBurnErr)|system(?:Mask(?:Default|HFSPlus|ISO9660|Joliet|UDF)|sNotSupportedErr))|rstErr)|lag(?:NoMoreData|SubchannelDataRequested)|unctionNotSupportedErr)|In(?:ternalErr|validIndexPointsErr)|LinkType(?:FinderAlias|HardLink|SymbolicLink)|Media(?:BusyErr|InvalidErr|Not(?:BlankErr|ErasableErr|PresentErr|SupportedErr|WritableErr))|S(?:essionFormat(?:Audio|CD(?:I|XA)|DVDData|Mode1Data)|peedTestAlreadyRunningErr)|T(?:ooMany(?:NameConflictsErr|TracksForDVDErr)|rack(?:M(?:essage(?:EstimateLength|P(?:ostBurn|r(?:eBurn|oduce(?:Data|PreGap)))|Verif(?:ication(?:Done|Starting)|y(?:Data|PreGap)))|ode(?:1Data|2(?:Data|Form(?:1Data|2Data))|Audio|DVDData))|ReusedErr))|UserCanceledErr|VerificationFailedErr)|Sp(?:Con(?:firmSwitchWarning|text(?:AlreadyReservedErr|Not(?:FoundErr|ReservedErr)))|FrameRateNotReadyErr|In(?:ternalErr|valid(?:AttributesErr|ContextErr))|NotInitializedErr|S(?:tereoContextErr|ystemSWTooOldErr))|TP(?:AbortJobErr|HoldJobErr|StopQueueErr|T(?:hirdPartySupported|ryAgainErr))|ata(?:A(?:ccessKCEvent(?:Mask)?|lignmentException)|Br(?:eakpointException|owser(?:A(?:lwaysExtendSelection|ttribute(?:AutoHideScrollBars|ColumnViewResizeWindow|ListView(?:AlternatingRowColors|DrawColumnDividers)|None|ReserveGrowBoxSpace))|C(?:heckboxT(?:riState|ype)|lientPropertyFlags(?:Mask|Offset)|mdTogglesSelection|o(?:lumnView(?:PreviewProperty)?|nt(?:ainer(?:AliasIDProperty|Clos(?:ed|ing)|Is(?:ClosableProperty|Open(?:ableProperty)?|SortableProperty)|Opened|Sort(?:ed|ing))|entHit))|ustomType)|D(?:ateTime(?:DateOnly|Relative|SecondsToo|T(?:imeOnly|ype))|efaultPropertyFlags|oNotTruncateText|ragSelect)|Edit(?:Msg(?:C(?:lear|opy|ut)|Paste|Redo|SelectAll|Undo)|St(?:arted|opped))|I(?:con(?:AndTextType|Type)|tem(?:A(?:dded|nyState)|D(?:eselected|oubleClicked)|Is(?:ActiveProperty|ContainerProperty|DragTarget|EditableProperty|Select(?:ableProperty|ed))|No(?:Property|State)|ParentContainerProperty|Removed|Sel(?:ected|fIdentityProperty)|s(?:A(?:dd|ssign)|Remove|Toggle)))|L(?:atestC(?:allbacks|ustomCallbacks)|istView(?:AppendColumn|DefaultColumnFlags|LatestHeaderDesc|MovableColumn|NoGapForIconInHeaderButton|S(?:electionColumn|ortableColumn)|TypeSelectColumn)?)|Metric(?:CellContentInset|Disclosure(?:Column(?:EdgeInset|PerDepthGap)|TriangleAndContentGap)|IconAndTextGap|Last)|N(?:everEmptySelectionSet|o(?:DisjointSelection|Item|View|thingHit))|Order(?:Decreasing|Increasing|Undefined)|P(?:opupMenu(?:Buttonless|Type)|ro(?:gressBarType|perty(?:C(?:heckboxPart|ontentPart)|DisclosurePart|EnclosingPart|Flags(?:Mask|Offset)|I(?:conPart|s(?:Editable|Mutable))|ModificationFlags|ProgressBarPart|RelevanceRankPart|SliderPart|TextPart)))|Re(?:l(?:ativeDateTime|evanceRankType)|setSelection|veal(?:AndCenterInView|Only|WithoutSelecting))|S(?:elect(?:OnlyOne|ion(?:Anchor(?:Down|Left|Right|Up)|SetChanged))|lider(?:DownwardThumb|PlainThumb|Type|UpwardThumb)|topTracking)|T(?:a(?:bleView(?:FillHilite|LastColumn|MinimalHilite|SelectionColumn)|rgetChanged)|extType|runcateText(?:At(?:End|Start)|Middle))|U(?:niversalPropertyFlags(?:Mask)?|ser(?:StateChanged|ToggledContainer))|ViewSpecific(?:Flags(?:Mask|Offset)|PropertyFlags))))|e(?:c(?:o(?:mposeDiacriticsSelector|rativeBordersSelector)|ryptKCItemAttr)|epestColorScreen|fault(?:C(?:JKRomanSelector|MMSignature|hangedKCEvent(?:Mask)?|olorPicker(?:Height|Width))|LowerCaseSelector|UpperCaseSelector)|l(?:ayParam_(?:DelayTime|Feedback|LopassCutoff|WetDryMix)|ete(?:AliasIcon|CharCode|KCEvent(?:Mask)?))|s(?:criptionKCItemAttr|ign(?:ComplexityType|Level(?:1Selector|2Selector|3Selector|4Selector|5Selector))|ktop(?:FolderType|Icon(?:Resource)?|P(?:icturesFolderType|rinterAliasType)))|v(?:eloper(?:ApplicationsFolderType|DocsFolderType|FolderType|HelpFolderType)|ice(?:InitiatedWake|ToPCS)))|i(?:a(?:criticsType|gonalFractionsSelector|l(?:ectBundleResType|og(?:F(?:lags(?:HandleMovableModal|Use(?:Co(?:mpositing|ntrolHierarchy)|Theme(?:Background|Controls)))|ont(?:Add(?:FontSizeMask|ToMetaFontMask)|NoFontStyle|Use(?:AllMask|BackColorMask|F(?:aceMask|o(?:nt(?:Mask|NameMask)|reColorMask))|JustMask|ModeMask|SizeMask|ThemeFontIDMask)))|WindowKind))|mond(?:AnnotationSelector|CharCode|Unicode))|ctionar(?:iesFolderType|yFileType)|giHub(?:Blank(?:CD|DVD)|EventClass|MusicCD|PictureCD|VideoDVD)|ngbatsSelector|phthongLigaturesO(?:ffSelector|nSelector)|rectoryServices(?:FolderType|PlugInsFolderType)|s(?:p(?:atched(?:ParameterPhase|SelectorSize(?:Phase|Width))|lay(?:ExtensionsFolderType|Mode(?:A(?:cceleratorBackedFlag|lwaysShowFlag)|BuiltInFlag|DefaultFlag|InterlacedFlag|N(?:ativeFlag|everShowFlag|ot(?:GraphicsQualityFlag|PresetFlag|ResizeFlag))|RequiresPanFlag|S(?:afe(?:Flag|tyFlags)|imulscanFlag|tretchedFlag)|TelevisionFlag|Valid(?:F(?:lag|or(?:AirPlayFlag|HiResFlag|MirroringFlag))|ateAgainstDisplay))|ProductIDGeneric|SubPixel(?:Configuration(?:Delta|Quad|Stripe(?:Offset)?|Undefined)|Layout(?:BGR|QuadGB(?:L|R)|RGB|Undefined)|Shape(?:Elliptical|Oval|R(?:ectangular|ound)|Square|Undefined))|TextSelector|VendorIDUnknown))|tortionParam_(?:CubicTerm|De(?:c(?:ay|imation(?:Mix)?)|lay(?:Mix)?)|FinalMix|LinearTerm|PolynomialMix|R(?:ingMod(?:Balance|Freq(?:1|2)|Mix)|ounding)|S(?:oftClipGain|quaredTerm)))|therAlgorithm_(?:NoiseShaping|TPDF))|o(?:FolderActionEvent|NotActivateAnd(?:HandleClick|IgnoreClick)|cument(?:Window(?:Class|VariantCode)|ationFolderType|sFolder(?:Icon|Type))|main(?:LibraryFolderType|TopLevelFolderType)|nt(?:CreateFolder|FindAppBySignature|PassSelector)|wn(?:ArrowCharCode|loadsFolderType)|ze(?:Demand|Request|ToFullWakeUp|WakeUp))|r(?:a(?:g(?:Action(?:Al(?:ias|l)|Copy|Delete|Generic|Move|Nothing|Private)|Behavior(?:None|ZoomBackAnimation)|D(?:ark(?:Translucency|erTranslucency)|oNotScaleImage)|FlavorType(?:HFS|PromiseHFS)|HasLeftSenderWindow|InsideSender(?:Application|Window)|OpaqueTranslucency|P(?:romisedFlavor(?:FindFile)?|seudo(?:CreatorVolumeOrDirectory|FileType(?:Directory|Volume)))|Region(?:AndImage|Begin|Draw|End|Hide|Idle)|Standard(?:DropLocation(?:Trash|Unknown)|Translucency)|Tracking(?:Enter(?:Control|Handler|Window)|In(?:Control|Window)|Leave(?:Control|Handler|Window)))|w(?:Control(?:EntireControl|IndicatorOnly)|erWindowClass))|op(?:BoxFolderType|Folder(?:AliasType|Icon(?:Resource)?)|IconVariant))|uration(?:Forever|Immediate|Mi(?:crosecond|llisecond))|ynamic(?:RangeControlMode_(?:Heavy|Light|None)|sProcessorParam_(?:AttackTime|CompressionAmount|Expansion(?:Ratio|Threshold)|HeadRoom|InputAmplitude|MasterGain|OutputAmplitude|ReleaseTime|Threshold)))|E(?:A(?:CCESErr|DDR(?:INUSEErr|NOTAVAILErr)|GAINErr|LREADYErr)|B(?:AD(?:FErr|MSGErr)|USYErr)|C(?:ANCELErr|ONN(?:ABORTEDErr|RE(?:FUSEDErr|SETErr)))|DE(?:ADLKErr|STADDRREQErr)|EXISTErr|FAULTErr|HOST(?:DOWNErr|UNREACHErr)|I(?:N(?:PROGRESSErr|TRErr|VALErr)|OErr|SCONNErr)|M(?:SGSIZEErr|ailKCItemAttr)|N(?:ET(?:DOWNErr|RESETErr|UNREACHErr)|O(?:BUFSErr|D(?:ATAErr|EVErr)|ENTErr|M(?:EMErr|SGErr)|PROTOOPTErr|RSRCErr|S(?:RErr|TRErr)|T(?:CONNErr|SOCKErr|TYErr))|XIOErr)|OPNOTSUPPErr|P(?:ERMErr|IPEErr|ROTO(?:Err|NOSUPPORTErr|TYPEErr))|RANGEErr|S(?:HUTDOWNErr|OCKTNOSUPPORTErr|RCHErr)|T(?:IME(?:DOUTErr|Err)|OOMANYREFSErr)|UC_(?:CN_(?:BasicVariant|DOSVariant)|KR_(?:BasicVariant|DOSVariant))|WOULDBLOCKErr|dit(?:TextDialogItem|orsFolderType)|jectMediaIcon|n(?:crypt(?:KCItemAttr|Password)|d(?:CharCode|DateKCItemAttr|Of(?:Sentence|Word))|gravedTextSelector|ter(?:CharCode|Idle|Run|Standby))|scapeCharCode|ve(?:nt(?:A(?:ccessible(?:Get(?:All(?:A(?:ctionNames|ttributeNames)|ParameterizedAttributeNames)|ChildAtPoint|FocusedChild|NamedA(?:ctionDescription|ttribute))|IsNamedAttributeSettable|PerformNamedAction|SetNamedAttribute)|pp(?:A(?:ctiv(?:ated|eWindowChanged)|vailableWindowBoundsChanged)|Deactivated|F(?:ocus(?:Drawer|MenuBar|Next(?:DocumentWindow|FloatingWindow)|Toolbar)|rontSwitched)|GetDockTileMenu|Hidden|IsEventInInstantMouser|Launch(?:Notification|ed)|Quit|S(?:hown|ystemUIModeChanged)|Terminated|UpdateDockTile|earanceScrollBarVariantChanged|leEvent)|ttribute(?:Monitored|None|UserEvent))|C(?:l(?:ass(?:A(?:ccessibility|pp(?:earance|l(?:eEvent|ication)))|C(?:lockView|o(?:mmand|ntrol))|D(?:ataBrowser|elegate)|EPPC|Font|Gesture|HI(?:ComboBox|Object)|Ink|Keyboard|M(?:enu|ouse)|S(?:crollable|e(?:archField|rvice)|ystem)|T(?:SMDocumentAccess|ablet|ext(?:Field|Input)|oolbar(?:Item(?:View)?)?)|Volume|Window)|ockDateOrTimeChanged)|o(?:m(?:boBoxListItemSelected|mand(?:Process|UpdateStatus))|ntrol(?:A(?:ctivate|ddedSubControl|pplyTextColor)|BoundsChanged|C(?:lick|ontextualMenuClick)|D(?:eactivate|ispose|ra(?:g(?:Enter|Leave|Receive|Within)|w))|EnabledStateChanged|FocusPartChanged|G(?:et(?:A(?:ctionProcPart|utoToggleValue)|ClickActivation|Data|F(?:ocusPart|rameMetrics)|IndicatorDragConstraint|NextFocusCandidate|OptimalBounds|Part(?:Bounds|Region)|S(?:crollToHereStartPoint|izeConstraints|ubviewForMouseEvent))|hostingFinished)|Hi(?:liteChanged|t(?:Test)?)|In(?:dicatorMoved|itialize|terceptSubviewClick|validateForSizeChange)|LayoutInfoChanged|O(?:ptimalBoundsChanged|wningWindowChanged)|RemovingSubControl|S(?:et(?:Cursor|Data|FocusPart)|imulateHit)|T(?:itleChanged|rack(?:ingAreaE(?:ntered|xited))?)|V(?:alueFieldChanged|isibilityChanged))))|D(?:ataBrowserDrawCustomItem|elegate(?:Get(?:GroupClasses|TargetClasses)|I(?:nstalled|sGroup)|Removed))|Font(?:PanelClosed|Selection)|Ge(?:sture(?:Ended|Magnify|Rotate|S(?:tarted|wipe))|tSelectedText)|H(?:IObject(?:C(?:onstruct|reatedFromArchive)|Destruct|Encode|GetInitParameters|I(?:nitialize|sEqual)|PrintDebugInfo)|ighLevelEvent|otKey(?:Exclusive|NoOptions|Pressed|Released))|Ink(?:Gesture|Point|Text)|KeyModifier(?:Fn(?:Bit|Mask)|NumLock(?:Bit|Mask))|L(?:eaveInQueue|oopIdleTimer(?:Idling|St(?:arted|opped)))|M(?:enu(?:B(?:ar(?:Hidden|Shown)|e(?:comeScrollable|ginTracking))|C(?:alculateSize|easeToBeScrollable|hangeTrackingMode|losed|reateFrameView)|D(?:ispose|rawItem(?:Content)?)|En(?:ableItems|dTracking)|GetFrameBounds|M(?:atchKey|easureItem(?:Height|Width))|Opening|Populate|TargetItem)|ouse(?:Button(?:Primary|Secondary|Tertiary)|D(?:own|ragged)|E(?:ntered|xited)|Moved|Scroll|Up|Wheel(?:Axis(?:X|Y)|Moved)))|OffsetToPos|P(?:aram(?:A(?:EEvent(?:Class|ID)|TSUFont(?:ID|Size)|ccessib(?:ilityEventQueued|le(?:A(?:ction(?:Description|Name(?:s)?)|ttribute(?:Name(?:s)?|Parameter|Settable|Value))|Child|Object))|fterDelegates|ppleEvent(?:Reply)?|ttributes|vailableBounds)|B(?:eforeDelegates|ounds)|C(?:G(?:ContextRef|ImageRef)|TFontDescriptor|andidateText|lick(?:Activation|Count)|o(?:mboBoxListSelectedItemIndex|ntrol(?:Action|C(?:lickActivationResult|urrent(?:OwningWindow|Part))|D(?:ata(?:Buffer(?:Size)?|Tag)|raw(?:Depth|Engraved|InColor))|F(?:eatures|ocusEverything|rameMetrics)|Hit|I(?:n(?:dicator(?:DragConstraint|Offset|Region)|valRgn)|sGhosting)|Message|O(?:ptimalB(?:aselineOffset|ounds)|riginalOwningWindow)|P(?:ar(?:am|t(?:AutoRepeats|Bounds)?)|re(?:fersShape|viousPart))|Re(?:f|gion|sult)|Sub(?:Control|view)|Value|WouldAcceptDrop))|urrent(?:Bounds|Dock(?:Device|Rect)|MenuTrackingMode|Window))|D(?:ataBrowser(?:Item(?:ID|State)|PropertyID)|e(?:codingForEditor|legate(?:Group(?:Classes|Parameters)|Target(?:Classes)?)|vice(?:Color|Depth))|i(?:ctionary|mensions|rect(?:Object|ionInverted)|splay(?:ChangeFlags|Device))|ragRef)|E(?:nable(?:MenuForKeyEvent|d)|ventRef)|F(?:MFont(?:Family|S(?:ize|tyle))|ontColor)|G(?:Device|rafPort)|HI(?:Archive|Command|ObjectInstance|ViewTrackingArea)|I(?:mageSize|n(?:dex|it(?:Collection|Parameters)|k(?:Gesture(?:Bounds|Hotspot|Kind)|KeyboardShortcut|TextRef))|sInInstantMouser)|Key(?:Code|M(?:acCharCodes|odifiers)|Unicodes|boardType)|L(?:aunch(?:Err|RefCon)|ineSize)|M(?:a(?:gnificationAmount|ximumSize)|enu(?:Co(?:mmand(?:KeyBounds)?|ntext(?:Height)?)|D(?:i(?:rection|smissed)|rawState)|EventOptions|F(?:irstOpen|rameView)|I(?:conBounds|sPopup|tem(?:Bounds|Height|Index|Type|Width))|MarkBounds|PopupItem|Ref|T(?:extB(?:aseline|ounds)|ype)|Virtual(?:Bottom|Top))|inimumSize|o(?:dal(?:ClickResult|Window)|use(?:Button|Chord|Delta|Location|TrackingRef|Wheel(?:Axis|Delta|Smooth(?:HorizontalDelta|VerticalDelta))))|utableArray)|Ne(?:w(?:MenuTrackingMode|ScrollBarVariant)|xtControl)|Origin(?:alBounds)?|P(?:a(?:rentMenu(?:Item)?|steboardRef)|ost(?:Options|Target)|r(?:evious(?:Bounds|Dock(?:Device|Rect)|Window)|ocessID))|R(?:e(?:ason|placementText|sult)|gnHandle|otationAmount)|S(?:crapRef|ervice(?:CopyTypes|MessageName|PasteTypes|UserData)|hape|tartControl|wipeDirection|ystemUI(?:Mode|Options))|T(?:SM(?:DocAccess(?:BaselineDelta|CharacterCount|EffectiveRange|L(?:ineBounds|ockCount)|Re(?:ply(?:ATS(?:Font|UGlyphSelector)|C(?:T(?:FontRef|GlyphInfoRef)|haracter(?:Range|sPtr))|FontSize)|questedCharacterAttributes)|Send(?:C(?:haracter(?:Index|Range|sPtr)|omponentInstance)|RefCon))|Send(?:ComponentInstance|RefCon))|ablet(?:EventType|P(?:oint(?:Rec|erRec)|roximityRec))|ext(?:Input(?:GlyphInfoArray|Reply(?:A(?:TSFont|ttributedString)|CTFontRef|F(?:MFont|ont)|GlyphInfoArray|L(?:eadingEdge|ine(?:Ascent|Height))|MacEncoding|Point(?:Size)?|RegionClass|S(?:LRec|howHide)|Text(?:Angle|Offset)?)|Send(?:AttributedString|C(?:lauseRng|omponentInstance|urrentPoint)|DraggingMode|FixLen|GlyphInfoArray|HiliteRng|KeyboardEvent|LeadingEdge|MouseEvent|PinRng|Re(?:fCon|placeRange)|S(?:LRec|howHide)|Text(?:Offset|Service(?:Encoding|MacEncoding))?|UpdateRng))|Length|Selection)|oolbar(?:Display(?:Mode|Size)|Item(?:ConfigData|Identifier)?)?|ransactionID)|U(?:nconfirmed(?:Range|Text)|serData)|View(?:AttributesDictionary|Size)|Window(?:ContentBounds|D(?:efPart|ragHiliteFlag)|Features|GrowRect|Mo(?:d(?:ality|ifiedFlag)|useLocation)|P(?:artCode|roxy(?:GWorldPtr|ImageRgn|OutlineRgn))|Re(?:f|gionCode)|StateChangedFlags|T(?:itle(?:FullWidth|TextWidth)|ransition(?:Action|Effect))))|osToOffset|r(?:iority(?:High|Low|Standard)|ocessCommand))|QueueOptionsNone|R(?:awKey(?:Down|ModifiersChanged|Repeat|Up)|emoveFromQueue)|S(?:crollable(?:GetInfo|InfoChanged|ScrollTo)|e(?:archField(?:CancelClicked|SearchClicked)|rvice(?:Copy|GetTypes|P(?:aste|erform)))|howHideBottomWindow|ystem(?:Display(?:Reconfigured|sA(?:sleep|wake))|TimeDateChanged|UserSession(?:Activated|Deactivated)))|T(?:SMDocumentAccess(?:Get(?:Characters(?:Ptr(?:ForLargestBuffer)?)?|F(?:irstRectForRange|ont)|GlyphInfo|Length|SelectedRange)|LockDocument|UnlockDocument)|a(?:bletP(?:oint(?:er)?|roximity)|rget(?:DontPropagate|SendToAllHandlers))|ext(?:Accepted|DidChange|Input(?:FilterText|GetSelectedText|IsMouseEventInInlineInputArea|OffsetToPos|PosToOffset|ShowHideBottomWindow|U(?:nicode(?:ForKeyEvent|Text)|pdateActiveInputArea))|ShouldChangeInRange)|oolbar(?:BeginMultiChange|CreateItem(?:FromDrag|WithIdentifier)|Display(?:ModeChanged|SizeChanged)|EndMultiChange|Get(?:AllowedIdentifiers|DefaultIdentifiers|SelectableIdentifiers)|Item(?:A(?:cceptDrop|dded)|C(?:ommandIDChanged|reateCustomView)|EnabledStateChanged|GetPersistentData|HelpTextChanged|ImageChanged|LabelChanged|PerformAction|Removed|SelectedStateChanged|View(?:ConfigFor(?:Mode|Size)|E(?:nterConfigMode|xitConfigMode))|WouldAcceptDrop)|LayoutChanged))|U(?:nicodeForKeyEvent|pdateActiveInputArea)|Volume(?:Mounted|Unmounted)|Window(?:A(?:ctivated|ttributesChanged)|BoundsChang(?:ed|ing)|C(?:lose(?:All|d)?|o(?:l(?:laps(?:e(?:All|d)?|ing)|orSpaceChanged)|n(?:strain|textualMenuSelect))|ursorChange)|D(?:e(?:activated|f(?:D(?:ispose|ra(?:gHilite|w(?:Frame|GrowBox|Part)))|Get(?:GrowImageRegion|Region)|HitTest|Init|M(?:easureTitle|odified)|S(?:etupProxyDragImage|tateChanged)))|ispose|ra(?:g(?:Completed|Hilite|Started)|w(?:Frame|GrowBox|Part|er(?:Clos(?:ed|ing)|Open(?:ed|ing)))))|Expand(?:All|ed|ing)?|F(?:ocus(?:Acquired|Content|Drawer|Lost|Re(?:linquish|stored)|Toolbar)|ullScreenE(?:nter(?:Completed|Started)|xit(?:Completed|Started)))|Get(?:Click(?:Activation|Modality)|DockTileMenu|FullScreenContentSize|GrowImageRegion|IdealS(?:ize|tandardState)|M(?:aximumSize|inimumSize)|Region)|H(?:andle(?:Activate|Deactivate)|i(?:d(?:den|ing)|tTest))|Init|M(?:easureTitle|odified)|P(?:a(?:int|thSelect)|roxy(?:BeginDrag|EndDrag))|Res(?:ize(?:Completed|Started)|tore(?:FromDock|dAfterRelaunch))|S(?:etupProxyDragImage|h(?:eet(?:Clos(?:ed|ing)|Open(?:ed|ing))|ow(?:ing|n))|tateChanged)|T(?:itleChanged|oolbarSwitchMode|ransition(?:Completed|Started))|UpdateDockTile|Zoom(?:All|ed)?))|ryKCEventMask)|x(?:actMatchThread|cludedMemoryException|itIdle|p(?:ertCharactersSelector|o(?:nentsO(?:ffSelector|nSelector)|rtedFolderAliasType))|t(?:AudioFile(?:Error_(?:AsyncWrite(?:BufferOverflow|TooLarge)|Invalid(?:ChannelMap|DataFormat|OperationOrder|Property(?:Size)?|Seek)|MaxPacketSizeUnknown|NonPCMClientFormat)|Property_(?:Audio(?:Converter|File)|C(?:lient(?:ChannelLayout|DataFormat|MaxPacketSize)|o(?:decManufacturer|nverterConfig))|File(?:ChannelLayout|DataFormat|LengthFrames|MaxPacketSize)|IOBuffer(?:SizeBytes)?|PacketTable))|en(?:dedFlag(?:Has(?:CustomBadge|RoutingInfo)|ObjectIsBusy|sAreInvalid)|sion(?:DisabledFolderType|Folder(?:AliasType|Type)|s(?:DisabledFolderIcon|FolderIcon(?:Resource)?))))))|F(?:A(?:AttachCommand|EditCommand|FileParam|IndexParam|RemoveCommand|S(?:erverApp|uiteCode))|BC(?:a(?:ccess(?:Canceled|orStoreFailed)|ddDocFailed|llocFailed|nalysisNotAvailable)|bad(?:IndexFile(?:Version)?|Param|SearchSession)|com(?:mitFailed|pactionFailed)|deletionFailed|f(?:ileNotIndexed|lushFailed)|i(?:llegalSessionChange|ndex(?:CreationFailed|DiskIOFailed|FileDestroyed|Not(?:Available|Found)|ing(?:Canceled|Failed)))|m(?:ergingFailed|oveFailed)|no(?:IndexesFound|S(?:earchSession|uchHit))|s(?:earchFailed|omeFilesNotIndexed|ummarizationCanceled)|tokenizationFailed|v(?:TwinExceptionErr|alidationFailed))|M(?:CurrentFilterFormat|Font(?:C(?:allbackFilterSelector|ontainer(?:AccessErr|FilterSelector))|DirectoryFilterSelector|F(?:amilyCallbackFilterSelector|ileRefFilterSelector)|T(?:ableAccessErr|echnologyFilterSelector))|GenerationFilterSelector|I(?:nvalidFont(?:Err|FamilyErr)|teration(?:Completed|ScopeModifiedErr))|PostScriptFontTechnology|TrueTypeFontTechnology)|N(?:DirectoryModifiedMessage|No(?:ImplicitAllSubscription|tifyInBackground)|S(?:Bad(?:FlattenedSizeErr|ProfileVersionErr|ReferenceVersionErr)|DuplicateReferenceErr|In(?:sufficientDataErr|valid(?:ProfileErr|ReferenceErr))|MismatchErr|NameNotFoundErr))|PUNotNeeded|S(?:Al(?:iasInfo(?:F(?:SInfo|inderInfo)|I(?:Ds|sDirectory)|None|TargetCreateDate|Volume(?:CreateDate|Flags))|lo(?:c(?:AllOrNothingMask|ContiguousMask|DefaultFlags|NoRoundUpMask|ReservedMask)|wConcurrentAsyncIO(?:Bit|Mask)))|CatInfo(?:A(?:ccessDate|llDates|ttrMod)|BackupDate|C(?:ontentMod|reateDate)|DataSizes|F(?:SFileSecurityRef|inder(?:Info|XInfo))|GettableInfo|No(?:de(?:Flags|ID)|ne)|P(?:arentDirID|ermissions)|R(?:eserved|srcSizes)|S(?:et(?:Ownership|tableInfo)|haringFlags)|TextEncoding|User(?:Access|Privs)|V(?:alence|olume))|E(?:jectVolumeForceEject|ventStream(?:CreateFlag(?:FileEvents|IgnoreSelf|No(?:Defer|ne)|UseCFTypes|WatchRoot)|Event(?:Flag(?:EventIdsWrapped|HistoryDone|Item(?:C(?:hangeOwner|reated)|FinderInfoMod|I(?:nodeMetaMod|s(?:Dir|File|Symlink))|Modified|Re(?:moved|named)|XattrMod)|KernelDropped|M(?:ount|ustScanSubDirs)|None|RootChanged|U(?:nmount|serDropped))|IdSinceNow)))|F(?:ileOperation(?:D(?:efaultOptions|oNotMoveAcrossVolumes)|Overwrite|Skip(?:Preflight|SourcePermissionErrors))|orceRead(?:Bit|Mask))|I(?:nvalidVolumeRefNum|terate(?:Delete|Flat|Reserved|Subtree))|KMountVersion|MountServer(?:M(?:arkDoNotDisplay|ount(?:OnMountDir|WithoutNotification))|SuppressConnectionUI)|N(?:ewLine(?:Bit|CharMask|Mask)|o(?:Cache(?:Bit|Mask)|de(?:CopyProtect(?:Bit|Mask)|DataOpen(?:Bit|Mask)|ForkOpen(?:Bit|Mask)|HardLink(?:Bit|Mask)|I(?:nShared(?:Bit|Mask)|s(?:Directory(?:Bit|Mask)|Mounted(?:Bit|Mask)|SharePoint(?:Bit|Mask)))|Locked(?:Bit|Mask)|ResOpen(?:Bit|Mask))))|OperationStage(?:Complete|Preflighting|Running|Undefined)|P(?:athMakeRefD(?:efaultOptions|oNotFollowLeafSymlink)|leaseCache(?:Bit|Mask))|R(?:dVerify(?:Bit|Mask)|eplaceObject(?:D(?:efaultOptions|oNotCheckObjectWriteAccess)|PreservePermissionInfo|Replace(?:Metadata|PermissionInfo)|SaveOriginalAsABackup))|UnmountVolumeForceUnmount|Vol(?:Flag(?:DefaultVolume(?:Bit|Mask)|FilesOpen(?:Bit|Mask)|HardwareLocked(?:Bit|Mask)|JournalingActive(?:Bit|Mask)|SoftwareLocked(?:Bit|Mask))|Info(?:B(?:ackupDate|locks)|C(?:heckedDate|reateDate)|D(?:ataClump|irCount|riveInfo)|F(?:SInfo|i(?:leCount|nderInfo)|lags)|GettableInfo|ModDate|N(?:ext(?:Alloc|ID)|one)|RsrcClump|S(?:ettableInfo|izes))))|TPServerIcon|avorite(?:ItemsIcon|sFolder(?:Icon|Type))|e(?:male|tchReference)|i(?:l(?:eSystemSupportFolderType|lScreen)|nd(?:ByContent(?:FolderType|IndexesFolderType|PluginsFolderType)|SupportFolderType|erIcon)|rst(?:FailKCStopOn|IOKitNotificationType|MagicBusyFiletype|PassKCStopOn)|tToScreen)|l(?:avorType(?:Clipping(?:Filename|Name)|DragToTrashOnly|FinderNoTrackingBehavior|UnicodeClipping(?:Filename|Name))|euronsSelector|o(?:ating(?:PointException|Window(?:Class|Definition))|ppyIconResource))|o(?:lder(?:Action(?:Code|sFolderType)|C(?:losedEvent|reated(?:AdminPrivs(?:Bit)?|Invisible(?:Bit)?|NameLocked(?:Bit)?))|I(?:n(?:LocalOrRemoteUserFolder|RemoteUserFolderIfAvailable(?:Bit)?|UserFolder(?:Bit)?)|tems(?:AddedEvent|RemovedEvent))|M(?:anager(?:FolderInMacOS9FolderIfMacOSXIsInstalled(?:Bit|Mask)|LastDomain|N(?:ewlyCreatedFolder(?:IsLocalizedBit|ShouldHaveDotLocalizedCreatedWithinMask)|otCreatedOnRemoteVolumes(?:Bit|Mask)))|ustStayOnSameVolume(?:Bit)?)|NeverMatchedInIdentifyFolder(?:Bit)?|OpenedEvent|TrackedByAlias(?:Bit)?|WindowMovedEvent)|nt(?:A(?:lbanianLanguage|mharic(?:Language|Script)|r(?:abic(?:Language|Script)|menian(?:Language|Script))|ssameseLanguage|ymaraLanguage|zerbaijan(?:ArLanguage|iLanguage))|B(?:asqueLanguage|engali(?:Language|Script)|u(?:lgarianLanguage|rmese(?:Language|Script))|yelorussianLanguage)|C(?:atalanLanguage|h(?:ewaLanguage|ineseScript)|o(?:llectionsFolderType|pyrightName)|roatianLanguage|ustom(?:16BitScript|8(?:16BitScript|BitScript)|Platform)|yrillicScript|zechLanguage)|D(?:anishLanguage|e(?:s(?:criptionName|igner(?:Name|URLName))|vanagariScript)|utchLanguage|zongkhaLanguage)|E(?:astEuropeanRomanScript|nglishLanguage|s(?:perantoLanguage|tonianLanguage)|thiopicScript|xtendedArabicScript)|F(?:a(?:eroeseLanguage|milyName|rsiLanguage)|innishLanguage|lemishLanguage|renchLanguage|ullName)|G(?:allaLanguage|e(?:ezScript|orgian(?:Language|Script)|rmanLanguage)|reek(?:Language|Script)|u(?:araniLanguage|jarati(?:Language|Script)|rmukhiScript))|H(?:ebrew(?:Language|Script)|indiLanguage|ungarianLanguage)|I(?:SO10646_1993Semantics|celandicLanguage|ndonesianLanguage|rishLanguage|talianLanguage)|Ja(?:panese(?:Language|Script)|vaneseRomLanguage)|K(?:a(?:nnada(?:Language|Script)|shmiriLanguage|zakhLanguage)|hmer(?:Language|Script)|irghizLanguage|orean(?:Language|Script)|urdishLanguage)|L(?:a(?:o(?:Language|tianScript)|ppishLanguage|stReservedName|t(?:inLanguage|vianLanguage))|ettishLanguage|i(?:cense(?:DescriptionName|InfoURLName)|thuanianLanguage))|M(?:a(?:c(?:CompatibleFullName|edonianLanguage|intoshPlatform)|l(?:a(?:gasyLanguage|y(?:ArabicLanguage|RomanLanguage|alam(?:Language|Script)))|teseLanguage)|nufacturerName|rathiLanguage)|icrosoft(?:Platform|S(?:tandardScript|ymbolScript)|UCS4Script)|o(?:ldavianLanguage|ngolian(?:CyrLanguage|Language|Script)))|N(?:epaliLanguage|o(?:Language(?:Code)?|Name(?:Code)?|Platform(?:Code)?|Script(?:Code)?|rwegianLanguage))|Or(?:iya(?:Language|Script)|omoLanguage)|P(?:ashtoLanguage|ersianLanguage|o(?:lishLanguage|rtugueseLanguage|st(?:ScriptCIDName|scriptName))|referred(?:FamilyName|SubfamilyName)|unjabiLanguage)|QuechuaLanguage|R(?:SymbolScript|eservedPlatform|oman(?:Script|ianLanguage)|u(?:andaLanguage|ndiLanguage|ssian(?:Language)?))|S(?:a(?:amiskLanguage|mpleTextName|nskritLanguage)|e(?:lection(?:ATSUIType|CoreTextType|QD(?:StyleVersionZero|Type))|rbianLanguage)|i(?:mp(?:ChineseLanguage|leChineseScript)|n(?:dhi(?:Language|Script)|halese(?:Language|Script)))|l(?:avicScript|ov(?:akLanguage|enianLanguage))|omaliLanguage|panishLanguage|tyleName|u(?:itcaseIcon|ndaneseRomLanguage)|w(?:ahiliLanguage|edishLanguage))|T(?:a(?:galogLanguage|jikiLanguage|mil(?:Language|Script)|tarLanguage)|elugu(?:Language|Script)|hai(?:Language|Script)|i(?:betan(?:Language|Script)|grinyaLanguage)|rad(?:ChineseLanguage|emarkName|itionalChineseScript)|urk(?:ishLanguage|menLanguage))|U(?:ighurLanguage|krainianLanguage|ni(?:code(?:DefaultSemantics|Platform|V(?:1_1Semantics|2_0(?:BMPOnlySemantics|FullCoverageSemantics)|4_0VariationSequenceSemantics)|_FullRepertoire)|nterpretedScript|queName)|rduLanguage|zbekLanguage)|V(?:e(?:ndorURLName|rsionName)|ietnamese(?:Language|Script))|WelshLanguage|YiddishLanguage|sFolder(?:Icon(?:Resource)?|Type))|r(?:kInfoFlags(?:FileLocked(?:Bit|Mask)|LargeFile(?:Bit|Mask)|Modified(?:Bit|Mask)|OwnClump(?:Bit|Mask)|Resource(?:Bit|Mask)|SharedWrite(?:Bit|Mask)|Write(?:Bit|Locked(?:Bit|Mask)|Mask))|m(?:FeedCharCode|InterrobangO(?:ffSelector|nSelector))|wardArrowIcon)|urByteCode)|ra(?:ctionsType|gment(?:IsPrepared|NeedsPreparing)|me(?:buffer(?:DisableAltivecAccess|Supports(?:CopybackCache|GammaCorrection|WritethruCache))|worksFolderType))|u(?:ll(?:TrashIcon(?:Resource)?|Width(?:CJKRomanSelector|IdeographsSelector|KanaSelector))|nctionKeyCharCode))|G(?:SSSelect(?:Ge(?:nericToRealID|t(?:DefaultScriptingComponent|ScriptingComponent(?:FromStored)?))|OutOfRange|RealToGenericID|SetDefaultScriptingComponent)|UARD_EXC_(?:DE(?:ALLOC_GAP|STROY)|I(?:MMOVABLE|N(?:CORRECT_GUARD|VALID_(?:ARGUMENT|NAME|RIGHT|VALUE)))|KERN_(?:FAILURE|NO_SPACE|RESOURCE)|MOD_REFS|R(?:CV_(?:GUARDED_DESC|INVALID_NAME)|IGHT_EXISTS)|S(?:E(?:ND_INVALID_(?:R(?:EPLY|IGHT)|VOUCHER)|T_CONTEXT)|TRICT_REPLY)|UNGUARDED)|e(?:n(?:EditorsFolderType|er(?:alFailureErr|ic(?:ApplicationIcon(?:Resource)?|C(?:DROMIcon(?:Resource)?|o(?:mponent(?:Icon|Version)|ntrol(?:PanelIcon|StripModuleIcon)))|D(?:eskAccessoryIcon(?:Resource)?|ocumentIcon(?:Resource)?)|E(?:ditionFileIcon(?:Resource)?|xtensionIcon(?:Resource)?)|F(?:ileServerIcon(?:Resource)?|loppyIcon|o(?:lderIcon(?:Resource)?|nt(?:Icon|ScalerIcon)))|HardDiskIcon(?:Resource)?|IDiskIcon|KCItemAttr|MoverObjectIcon(?:Resource)?|NetworkIcon|P(?:CCardIcon|asswordKCItemClass|referencesIcon(?:Resource)?)|QueryDocumentIcon(?:Resource)?|R(?:AMDiskIcon(?:Resource)?|emovableMediaIcon)|S(?:haredLibaryIcon|tationeryIcon(?:Resource)?|uitcaseIcon(?:Resource)?)|URLIcon|W(?:ORMIcon|indowIcon))))|t(?:AE(?:TE|UT)|DebugOption|Power(?:Info|Level)|SelectedText|WakeOnNetInfo))|lyphCollection(?:Adobe(?:CNS1|GB1|Japan(?:1|2)|Korea1)|GID|Unspecified)|r(?:aphicEQParam_NumberOfBands|idIcon|oup(?:I(?:D2Name|con)|Name2ID))|uestUserIcon)|H(?:ALOutputParam_Volume|FS(?:A(?:llocationFileID|ttribute(?:DataFileID|sFileID)|utoCandidate(?:Bit|Mask))|B(?:adBlockFileID|inaryCompare|o(?:gusExtentFileID|otVolumeInconsistent(?:Bit|Mask)))|C(?:a(?:seFolding|talog(?:FileID|KeyM(?:aximumLength|inimumLength)|NodeIDsReused(?:Bit|Mask)))|ontentProtection(?:Bit|Mask))|DoNotFastDevPin(?:Bit|Mask)|Extent(?:Density|KeyMaximumLength|sFileID)|F(?:astDev(?:Candidate(?:Bit|Mask)|Pinned(?:Bit|Mask))|i(?:le(?:Locked(?:Bit|Mask)|Record|ThreadRecord)|rstUserCatalogNodeID)|older(?:Record|ThreadRecord))|Has(?:Attributes(?:Bit|Mask)|ChildLink(?:Bit|Mask)|DateAdded(?:Bit|Mask)|FolderCount(?:Bit|Mask)|LinkChain(?:Bit|Mask)|Security(?:Bit|Mask))|JMountVersion|M(?:DBAttributesMask|ax(?:AttrNameLen|FileNameChars|VolumeNameChars))|Plus(?:Attr(?:Extents|ForkData|InlineData|MinNodeSize)|C(?:atalog(?:KeyM(?:aximumLength|inimumLength)|MinNodeSize)|reator)|Extent(?:Density|KeyMaximumLength|MinNodeSize)|F(?:ile(?:Record|ThreadRecord)|older(?:Record|ThreadRecord))|M(?:axFileNameChars|ountVersion)|SigWord|Version)|R(?:epairCatalogFileID|oot(?:FolderID|ParentID))|S(?:igWord|tartupFileID)|ThreadExists(?:Bit|Mask)|UnusedNode(?:Fix(?:Bit|Mask)|sFixDate)|Volume(?:HardwareLock(?:Bit|Mask)|Inconsistent(?:Bit|Mask)|Journaled(?:Bit|Mask)|NoCacheRequired(?:Bit|Mask)|S(?:oftwareLock(?:Bit|Mask)|paredBlocks(?:Bit|Mask))|Unmounted(?:Bit|Mask))|X(?:SigWord|Version))|I(?:ArchiveDecod(?:eSuperclassForUnregisteredObjects|ingForEditor)|C(?:lassOptionSingleton|o(?:m(?:boBox(?:Auto(?:CompletionAttribute|DisclosureAttribute|S(?:izeListAttribute|ortAttribute))|DisclosurePart|EditTextPart|List(?:Pixel(?:HeightTag|WidthTag)|Tag)|N(?:oAttributes|umVisibleItemsTag)|StandardAttributes)|mand(?:A(?:bout|ppHelp|rrangeInFront)|BringAllToFront|C(?:ancel|h(?:angeSpelling|eckSpelling(?:AsYouType)?)|l(?:ear|ose(?:All|File)?)|opy|u(?:stomizeToolbar|t)|ycleToolbarMode(?:Larger|Smaller))|From(?:Control|Menu|Window)|Hide(?:Others|Toolbar)?|IgnoreSpelling|LearnWord|M(?:aximize(?:All|Window)|inimize(?:All|Window))|New|O(?:K|pen|ther)|P(?:a(?:geSetup|ste)|r(?:eferences|int))|Quit(?:And(?:DiscardWindows|KeepWindows))?|R(?:e(?:do|vert)|otate(?:FloatingWindows(?:Backward|Forward)|Windows(?:Backward|Forward)))|S(?:ave(?:As)?|elect(?:All|Window)|how(?:All|CharacterPalette|HideFontPanel|SpellingPanel|Toolbar)|tartDictation)|Toggle(?:AllToolbars|FullScreen|Toolbar)|Undo|WindowList(?:Separator|Terminator)|ZoomWindow))|ordSpace(?:72DPIGlobal|ScreenPixel|View|Window)))|D(?:B(?:a(?:d(?:Log(?:PhysValuesErr|icalM(?:aximumErr|inimumErr))|ParameterErr)|seError)|ufferTooSmallErr)|DeviceNotReady|EndOfDescriptorErr|In(?:compatibleReportErr|v(?:alid(?:PreparsedDataErr|R(?:angePageErr|eport(?:LengthErr|TypeErr)))|erted(?:LogicalRangeErr|PhysicalRangeErr|UsageRangeErr)))|N(?:ot(?:EnoughMemoryErr|ValueArrayErr)|ull(?:PointerErr|StateErr))|Report(?:CountZeroErr|IDZeroErr|SizeZeroErr)|Success|U(?:nmatched(?:DesignatorRangeErr|StringRangeErr|UsageRangeErr)|sage(?:NotFoundErr|PageZeroErr))|V(?:alueOutOfRangeErr|ersionIncompatibleErr)|elegate(?:A(?:fter|ll)|Before))|HotKeyModeAll(?:Disabled(?:ExceptUniversalAccess)?|Enabled)|ImageView(?:AutoTransform(?:None|OnD(?:eactivate|isable))|ImageTag)|Layout(?:Bind(?:Bottom|Left|M(?:ax|in)|None|Right|Top)|InfoVersionZero|Position(?:Bottom|Center|Left|M(?:ax|in)|None|Right|Top)|ScaleAbsolute)|M(?:enu(?:AppendItem|CenterDirection|DismissedBy(?:A(?:ctivationChange|ppSwitch)|CancelMenuTracking|FocusChange|KeyEvent|Mouse(?:Down|Up)|Selection|Timeout|UserCancel)|LeftDirection|RightDirection)|odalClick(?:A(?:llowEvent|nnounce)|IsModal|RaiseWindow))|S(?:crollView(?:Options(?:AllowGrow|DisableSmoothScrolling|FillGrowArea|HorizScroll|VertScroll)|Page(?:Down|Left|Right|Up)|ScrollTo(?:Bottom|Left|Right|Top)|ValidOptions)|e(?:archField(?:Attributes(?:Cancel|SearchIcon)|NoAttributes)|gment(?:Behavior(?:Momentary|Radio|Sticky|Toggles)|NoAttributes|SendCmdToUserFocus|edViewKind))|hape(?:Enumerate(?:Init|Rect|Terminate)|ParseFrom(?:Bottom(?:Right)?|Left|Right|Top(?:Left)?)))|T(?:heme(?:F(?:ocusRing(?:Above|Below|Only)|rame(?:ListBox|TextField(?:Round(?:Mini|Small)?|Square)))|Gro(?:upBoxKind(?:Primary(?:Opaque)?|Secondary(?:Opaque)?)|wBox(?:KindNo(?:ne|rmal)|Size(?:Normal|Small)))|HeaderKind(?:List|Window)|Menu(?:DrawInfoVersion(?:One|Zero)|TitleDrawCondensed)|Orientation(?:Inverted|Normal)|S(?:egment(?:Adornment(?:Focus|LeadingSeparator|None|TrailingSeparator)|Kind(?:Inset|Normal|Textured)|Position(?:First|Last|Middle|Only)|Size(?:Mini|Normal|Small))|plitterAdornment(?:Metal|None))|T(?:ab(?:Adornment(?:Focus|LeadingSeparator|None|TrailingSeparator)|KindNormal|P(?:aneAdornmentNormal|osition(?:First|Last|Middle|Only))|Size(?:Mini|Normal|Small))|ext(?:BoxOption(?:DontClip|Engraved|None|StronglyVertical)|HorizontalFlush(?:Center|Default|Left|Right)|InfoVersion(?:One|Zero)|Truncation(?:Default|End|Middle|None)|VerticalFlush(?:Bottom|Center|Default|Top))))|oolbar(?:AutoSavesConfig|CommandPressAction|Display(?:Mode(?:Default|Icon(?:AndLabel|Only)|LabelOnly)|Size(?:Default|Normal|Small))|I(?:sConfigurable|tem(?:A(?:llowDuplicates|nchoredLeft)|CantBeRemoved|Disabled|IsSeparator|LabelDisabled|MutableAttrs|NoAttributes|Se(?:lected|ndCmdToUserFocus)|ValidAttrs))|NoAttributes|V(?:alidAttrs|iewDrawBackgroundTag))|ransform(?:Disabled|None|Selected))|View(?:A(?:llowsSubviews|ttribute(?:IsFieldEditor|SendCommandToUserFocus)|utoToggles)|C(?:lickableMetaPart|ontent(?:AlertIconType|CGImageRef|I(?:con(?:Ref|SuiteRef|TypeAndCreator)|mage(?:File|Resource))|MetaPart|N(?:SImage|one)|TextOnly))|D(?:isabledPart|oesNot(?:Draw|UseSpecialParts))|EntireView|F(?:eature(?:A(?:llowsSubviews|utoToggles)|DoesNot(?:Draw|UseSpecialParts)|GetsFocusOnClick|I(?:dlesWithTimer|gnoresClicks|nvertsUpDownValueMeaning|sOpaque)|Supports(?:Ghosting|LiveFeedback|RadioBehavior))|ocus(?:N(?:extPart|oPart)|OnAnyControl|PrevPart|Traditionally|WithoutWrapping))|GetsFocusOnClick|I(?:dlesWithTimer|gnoresClicks|n(?:activePart|dicatorPart|vertsUpDownValueMeaning)|sOpaque)|KindSignatureApple|NoPart|O(?:ffscreenImageUseWindowBackingResolution|paqueMetaPart)|S(?:endCommandToUserFocus|tructureMetaPart|upports(?:Ghosting|LiveFeedback|RadioBehavior))|ValidFeaturesForPanther|ZOrder(?:Above|Below))|Window(?:B(?:ackingLocation(?:Default|MainMemory|VideoMemory)|ehavior(?:Stationary|Transient)|it(?:A(?:syncDrag|uto(?:Calibration|ViewDragTracking))|C(?:anBeVisibleWithoutLogin|loseBox|o(?:llapseBox|mpositing))|DoesNot(?:Cycle|Hide)|F(?:rameworkScaled|ullScreen(?:Auxiliary|Primary))|Hi(?:deOn(?:FullScreen|Suspend)|ghResolutionCapable)|I(?:gnoreClicks|nWindowMenu)|LiveResize|No(?:Activates|Constrain|Shadow|T(?:exturedContentSeparator|itleBar)|Updates)|OpaqueForEvents|R(?:esizable|oundBottomBarCorners)|S(?:ideTitlebar|tandardHandler)|T(?:extured(?:SquareCorners)?|oolbarButton)|UnifiedTitleAndToolbar|ZoomBox))|CanJoinAllSpaces|D(?:epth(?:32Bit|64Bit|Float|Invalid)|ragPart)|ExposeHidden|IgnoreObscuringWindows|M(?:enu(?:Creator|WindowTag)|oveToActiveSpace)|S(?:caleMode(?:FrameworkScaled|Magnified|Unscaled)|haring(?:None|Read(?:Only|Write)))|Title(?:BarPart|ProxyIconPart)|VisibleInAllSpaces))|M(?:AbsoluteCenterAligned|Bottom(?:LeftCorner|RightCorner|Side)|C(?:FString(?:Content|LocalizedContent)|ontent(?:NotProvided(?:DontPropagate)?|Provided))|D(?:efaultSide|isposeContent)|H(?:elpMenuID|ideTag(?:Fade|Immediately))|I(?:llegalContentForMinimumState|nside(?:Bottom(?:CenterAligned|LeftCorner|RightCorner)|LeftCenterAligned|RightCenterAligned|Top(?:CenterAligned|LeftCorner|RightCorner)))|Left(?:BottomCorner|Side|TopCorner)|M(?:aximumContentIndex|inimumContentIndex)|NoContent|Outside(?:Bottom(?:CenterAligned|LeftAligned|RightAligned|ScriptAligned)|Left(?:BottomAligned|CenterAligned|TopAligned)|Right(?:BottomAligned|CenterAligned|TopAligned)|Top(?:CenterAligned|LeftAligned|RightAligned|ScriptAligned))|PascalStrContent|Right(?:BottomCorner|Side|TopCorner)|S(?:tr(?:ResContent|ingResContent)|upplyContent)|T(?:EHandleContent|extResContent|op(?:LeftCorner|RightCorner|Side)))|TTPServerIcon|a(?:lfWidth(?:CJKRomanSelector|IdeographsSelector|TextSelector)|n(?:dle(?:IsResource(?:Bit|Mask)|Locked(?:Bit|Mask)|Purgeable(?:Bit|Mask))|jaToHangul(?:Alt(?:OneSelector|T(?:hreeSelector|woSelector))|Selector))|rd(?:LinkFileType|wareCursor(?:DescriptorM(?:ajorVersion|inorVersion)|InfoM(?:ajorVersion|inorVersion)))|s(?:B(?:eenInited|undle)|CustomIcon|NoINITs))|e(?:brew(?:FigureSpaceVariant|StandardVariant)|lp(?:CharCode|DialogItem|Folder(?:Icon|Type)|Icon(?:Resource)?|TagEventHandlerTag|WindowClass))|i(?:deDiacriticsSelector|erarchicalFontMenuOption|gh(?:LevelEvent|ShelfParam_(?:CutOffFrequency|Gain))|nt(?:Advanced|Basic|Hidden)|passParam_(?:CutoffFrequency|Resonance)|raganaToKatakanaSelector|storicalLigaturesO(?:ffSelector|nSelector))|o(?:joCharactersSelector|meCharCode|rizontalConstraint)|uge(?:1BitMask|32BitData|4BitData|8Bit(?:Data|Mask))|yphen(?:To(?:EnDashO(?:ffSelector|nSelector)|MinusO(?:ffSelector|nSelector))|sToEmDashO(?:ffSelector|nSelector)))|I(?:BCarbonRuntime(?:CantFind(?:NibFile|Object)|ObjectNotOfRequestedType)|C(?:Attr(?:Locked(?:Bit|Mask)|NoChange|Volatile(?:Bit|Mask))|C(?:omponent(?:InterfaceVersion(?:0|1|2|3|4)?|Version)|reator)|EditPreferenceEvent(?:Class)?|File(?:SpecHeaderSize|Type)|Map(?:Binary(?:Bit|Mask)|DataFork(?:Bit|Mask)|FixedLength|Not(?:Incoming(?:Bit|Mask)|Outgoing(?:Bit|Mask))|Post(?:Bit|Mask)|ResourceFork(?:Bit|Mask))|N(?:ilProfileID|oUserInteraction(?:Bit|Mask)|umVersion)|Services(?:TCP(?:Bit|Mask)|UDP(?:Bit|Mask)))|MJaTypingMethod(?:Kana|Property|Roman)|O(?:A(?:nalogS(?:etupExpected|ignalLevel_(?:07(?:00_0(?:000|300)|14_0286)|1000_0400))|sync(?:C(?:allout(?:Count|FuncIndex|RefconIndex)|ompletionNotificationType)|Reserved(?:Count|Index)))|B(?:itsPerColorComponent(?:1(?:0|2|6)|6|8|NotSupported)|uiltinPanelPowerAttribute)|C(?:LUTPixels|SyncDisable|apturedAttribute|lamshellStateAttribute|o(?:lorimetry(?:AdobeRGB|BT(?:2(?:020|100)|601|709)|DCIP3|N(?:ativeRGB|otSupported)|WGRGB|sRGB|xvYCC)|nnect(?:MethodVarOutputSize|ion(?:BuiltIn|StereoSync))|pyback(?:Cache|InnerCache))|ursorControlAttribute)|D(?:PEvent(?:AutomatedTestRequest|ContentProtection|ForceRetrain|Idle|MCCS|RemoteControlCommandPending|S(?:inkSpecific|tart))|SCBlockPredEnable|e(?:f(?:ault(?:Cache|MemoryType)|erCLUTSetAttribute)|tailedTimingValid)|i(?:gitalSignal|splay(?:ColorMode|Dither(?:All|D(?:efault|isable)|FrameRateControl|RGBShift|Spatial|Temporal|YCbCr4(?:22Shift|44Shift))|ModeID(?:BootProgrammable|ReservedBase)|NeedsCEAUnderscan|PowerState(?:MinUsable|O(?:ff|n))|RGBColorComponentBits(?:1(?:0|2|4|6)|6|8|Unknown)|YCbCr4(?:22ColorComponentBits(?:1(?:0|2|4|6)|6|8|Unknown)|44ColorComponentBits(?:1(?:0|2|4|6)|6|8|Unknown))))|riverPowerAttribute|ynamicRange(?:Dolby(?:NormalMode|TunnelMode)|HDR10|NotSupported|SDR|TraditionalGammaHDR))|F(?:B(?:AVSignalType(?:D(?:P|VI)|HDMI|Unknown|VGA)|B(?:itRate(?:HBR(?:2)?|RBR)|lueGammaScaleAttribute)|C(?:hangedInterruptType|onnectInterruptType)|Display(?:Port(?:InterruptType|LinkChangeInterruptType|TrainingAttribute)|State(?:_(?:AlreadyActive|Mask|PipelineBlack|RestoredProfile))?)|FrameInterruptType|GreenGammaScaleAttribute|H(?:BLInterruptType|D(?:CPLimit_(?:AllowAll|NoHDCP(?:1x|20Type(?:0|1)))|RMetaDataAttribute))|Li(?:mitHDCP(?:Attribute|StateAttribute)|nk(?:Downspread(?:Max|None)|PreEmphasisLevel(?:0|1|2|3)|Scrambler(?:Alternate|Normal)|VoltageLevel(?:0|1|2|3)))|MCCSInterruptType|NS_(?:D(?:i(?:m|splayState(?:Mask|Shift))|oze)|Generation(?:Mask|Shift)|MessageMask|Rendezvous|Sleep|UnDim|Wake)|O(?:fflineInterruptType|nlineInterruptType)|RedGammaScaleAttribute|S(?:erverConnectType|haredConnectType|top|ystemAperture)|UserRequestProbe|V(?:BLInterruptType|ariableRefreshRate)|WakeInterruptType)|ixedCLUTPixels)|GDiagnose(?:ConnectType|GTraceType)|H(?:SyncDisable|ardwareCursorAttribute|ibernatePreview(?:Active|Updates))|In(?:hibitCache|ter(?:estCallout(?:Count|FuncIndex|RefconIndex|ServiceIndex)|lacedCEATiming))|KitNotication(?:MsgSizeMask|Type(?:Mask|SizeAdjShift))|M(?:a(?:p(?:Anywhere|C(?:ache(?:Mask|Shift)|opyback(?:Cache|InnerCache))|DefaultCache|InhibitCache|Overwrite|P(?:osted(?:CombinedReordered|Reordered|Write)|refault)|Re(?:a(?:dOnly|lTimeCache)|ference)|Static|U(?:nique|serOptionsMask)|Write(?:CombineCache|ThruCache))|tchingCallout(?:Count|FuncIndex|RefconIndex)|xPixelBits)|irror(?:Attribute|Default(?:Attribute)?|Forced|HWClipped|Is(?:Mirrored|Primary))|ono(?:DirectPixels|InverseDirectPixels))|N(?:TSCTiming|oSeparateSyncControl)|P(?:ALTiming|ixelEncoding(?:NotSupported|RGB444|YCbCr4(?:2(?:0|2)|44))|o(?:sted(?:CombinedReordered|Reordered|Write)|wer(?:Attribute|StateAttribute)))|R(?:GB(?:DirectPixels|Signed(?:DirectPixels|FloatingPointPixels))|ange(?:BitsPerColorComponent(?:1(?:0|2|6)|6|8|NotSupported)|Colorimetry(?:AdobeRGB|BT(?:2(?:020|100)|601|709)|DCIP3|N(?:ativeRGB|otSupported)|WGRGB|sRGB|xvYCC)|DynamicRange(?:Dolby(?:NormalMode|TunnelMode)|HDR10|NotSupported|SDR|TraditionalGammaHDR)|PixelEncoding(?:NotSupported|RGB444|YCbCr4(?:2(?:0|2)|44))|Supports(?:CompositeSync|InterlacedCEATiming(?:WithConfirm)?|S(?:eparateSyncs|ignal_(?:07(?:00_0(?:000|300)|14_0286)|1000_0400)|yncOnGreen)|VSyncSerration))|e(?:alTimeCache|gistryIterate(?:Parents|Recursively)))|S(?:cal(?:e(?:Can(?:BorderInsetOnly|DownSamplePixels|Rotate|S(?:caleInterlaced|upportInset)|UpSamplePixels)|Invert(?:X|Y)|Rotate(?:0|180|270|90|Flags)|S(?:tretch(?:Only|ToFit)|wapAxes))|ingInfoValid)|ervice(?:InteractionAllowed|M(?:atchedNotificationType|essageNotificationType)|PublishNotificationType|TerminatedNotificationType)|urface(?:Co(?:mponent(?:Name(?:Alpha|Blue|Chroma(?:Blue|Red)|Green|Luma|Red|Unknown)|Range(?:FullRange|Unknown|VideoRange|WideRange)|Type(?:Float|SignedInteger|Un(?:known|signedInteger)))|pyback(?:Cache|InnerCache))|DefaultCache|InhibitCache|Lock(?:AvoidSync|ReadOnly)|Map(?:C(?:acheShift|opyback(?:Cache|InnerCache))|DefaultCache|InhibitCache|Write(?:CombineCache|ThruCache))|Purgeable(?:Empty|KeepCurrent|NonVolatile|Volatile)|Subsampling(?:4(?:11|2(?:0|2))|None|Unknown)|Write(?:CombineCache|ThruCache))|y(?:nc(?:On(?:Blue|Green|Red)|PositivePolarity)|stemPowerAttribute))|T(?:iming(?:ID(?:Apple(?:NTSC_(?:FF(?:conv)?|ST(?:conv)?)|PAL_(?:FF(?:conv)?|ST(?:conv)?)|_(?:0x0_0hz_Offline|1(?:024x768_75hz|152x870_75hz)|5(?:12x384_60hz|60x384_60hz)|640x(?:4(?:00_67hz|80_67hz)|8(?:18_75hz|70_75hz))|832x624_75hz|FixedRateLCD))|FilmRate_48hz|GTF_640x480_120hz|Invalid|S(?:MPTE240M_60hz|ony_1(?:600x1024_76hz|920x1(?:080_(?:60hz|72hz)|200_76hz)))|VESA_(?:1(?:024x768_(?:60hz|7(?:0hz|5hz)|85hz)|152x864_75hz|280x(?:1024_(?:60hz|75hz|85hz)|960_(?:60hz|75hz|85hz))|360x768_60hz|600x1200_(?:6(?:0hz|5hz)|7(?:0hz|5hz)|8(?:0hz|5hz))|792x1344_(?:60hz|75hz)|856x1392_(?:60hz|75hz)|920x1440_(?:60hz|75hz))|640x480_(?:60hz|7(?:2hz|5hz)|85hz)|8(?:00x600_(?:56hz|60hz|7(?:2hz|5hz)|85hz)|48x480_60hz)))|RangeV(?:1|2))|riStateSyncs)|V(?:RAMSaveAttribute|SyncDisable)|W(?:SAA_(?:Accelerated|D(?:efer(?:End|Start)|riverOpen)|From_Accelerated|Hibernate|NonConsoleDevice|Reserved|S(?:leep|tateMask)|T(?:o_Accelerated|ransactional)|Unaccelerated)|indowServerActiveAttribute|rite(?:CombineCache|ThruCache)))|PFileServerIcon|S(?:OLatin(?:1(?:MusicCDVariant|StandardVariant)|Arabic(?:ExplicitOrderVariant|ImplicitOrderVariant|VisualOrderVariant)|Hebrew(?:ExplicitOrderVariant|ImplicitOrderVariant|VisualOrderVariant))|SDownloadsFolderType|p(?:BufferToSmallErr|Device(?:ActiveErr|InactiveErr)|Element(?:InListErr|NotInListErr)|InternalErr|ListBusyErr|System(?:ActiveErr|InactiveErr|ListErr)))|con(?:DialogItem|FamilyType|Services(?:1(?:024PixelDataARGB|28PixelDataARGB|6PixelDataARGB)|256PixelDataARGB|32PixelDataARGB|48PixelDataARGB|512PixelDataARGB|CatalogInfoMask|No(?:BadgeFlag|rmalUsageFlag)|UpdateIfNeededFlag))|d(?:eographic(?:Alt(?:F(?:iveSelector|ourSelector)|OneSelector|T(?:hreeSelector|woSelector)|ernativesType)|SpacingType)|leKCEvent(?:Mask)?)|ll(?:egal(?:ClockValueErr|InstructionException)|uminatedCapsSelector)|mmediate|n(?:DeferredTaskMask|NestedInterruptMask|SecondaryIntHandlerMask|UseErr|VBLTaskMask|dexFilesFolderType|equalityLigaturesO(?:ffSelector|nSelector)|feriorsSelector|itialCaps(?:AndSmallCapsSelector|Selector)|kInputMethodClass|putM(?:anagersFolderType|ethodsFolderType)|s(?:ertHierarchicalMenu|t(?:aller(?:LogsFolderType|ReceiptsFolderType)|ru(?:ctionBreakpointException|mentType_(?:A(?:UPreset|udiofile)|DLSPreset|EXS24|SF2Preset))))|te(?:gerException|rn(?:ation(?:ResourcesIcon|al(?:ResourcesIcon|SymbolsSelector))|et(?:EventClass|Folder(?:Icon|Type)|Location(?:A(?:FP|pple(?:ShareIcon|Talk(?:ZoneIcon)?))|Creator|F(?:TP(?:Icon)?|ile(?:Icon)?)|Generic(?:Icon)?|HTTP(?:Icon)?|Mail(?:Icon)?|N(?:NTP|SL(?:NeighborhoodIcon)?|ewsIcon))|P(?:asswordKCItemClass|lugInFolder(?:Icon|Type))|S(?:earchSitesFolder(?:Icon|Type)|itesFolderType))))|v(?:alid(?:CSClientErr|DeviceNumber|Font(?:Family)?|Generation|RegEntryErr)|ert(?:Highlighting|ed(?:BoxAnnotationSelector|CircleAnnotationSelector|RoundedBoxAnnotationSelector)|ingEncod(?:edPixel|ing(?:Shift)?))|isibleKCItemAttr))|s(?:Alias|Invisible|OnDesk|S(?:hared|tationery)|suer(?:KCItemAttr|URLKCItemAttr))|t(?:alicCJKRomanType|em(?:DisableBit|List)))|J(?:I(?:Journal(?:InFSMask|NeedInitMask|OnOtherDeviceMask)|S(?:19(?:78CharactersSelector|83CharactersSelector|90CharactersSelector)|2004CharactersSelector))|S(?:ClassAttributeNo(?:AutomaticPrototype|ne)|PropertyAttribute(?:Dont(?:Delete|Enum)|None|ReadOnly)|Type(?:Boolean|Nu(?:ll|mber)|Object|String|Undefined|dArrayType(?:ArrayBuffer|Float(?:32Array|64Array)|Int(?:16Array|32Array|8Array)|None|Uint(?:16Array|32Array|8(?:Array|ClampedArray)))))|UST(?:CurrentVersion|KashidaPriority|LetterPriority|NullPriority|Override(?:Limits|Priority|Unlimited)|Priority(?:Count|Mask)|S(?:pacePriority|tandardFormat)|Tag|Unlimited|noGlyphcode|pc(?:ConditionalAddAction|D(?:ecompositionAction|uctilityAction)|Glyph(?:RepeatAddAction|StretchAction)|UnconditionalAddAction))|apanese(?:BasicVariant|PostScript(?:PrintVariant|ScrnVariant)|St(?:andardVariant|dNoVerticalsVariant)|VertAtKuPlusTenVariant))|K(?:C(?:AuthType(?:D(?:PA|efault)|HTTPDigest|MSN|NTLM|RPA)|ProtocolType(?:A(?:FP|ppleTalk)|FTP(?:Account)?|HTTP|I(?:MAP|RC)|LDAP|NNTP|POP3|S(?:MTP|OCKS)|Telnet))|ER(?:N(?:C(?:rossStream(?:ResetNote)?|urrentVersion)|FormatMask|IndexArray|Line(?:EndKerning|Start)|No(?:CrossKerning|StakeNote|t(?:Applied|esRequested))|OrderedList|ResetCrossStream|S(?:impleArray|tateTable)|Tag|UnusedBits|V(?:ariation|ertical))|X(?:Action(?:OffsetMask|Type(?:AnchorPoints|Co(?:ntrolPoints|ordinates)|Mask))|C(?:ontrolPoint|rossStream(?:ResetNote)?|urrentVersion)|Descending|FormatMask|IndexArray|Line(?:EndKerning|Start)|No(?:CrossKerning|StakeNote|t(?:Applied|esRequested))|OrderedList|ResetCrossStream|S(?:impleArray|tateTable)|Tag|Unused(?:Bits|Flags)|V(?:a(?:luesAreLong|riation)|ertical)))|L(?:GroupIdentifier|I(?:con|dentifier)|K(?:CHR(?:Data|Kind|uchrKind)|ind)|L(?:anguageCode|ocalizedName)|Name|USKeyboard|uchr(?:Data|Kind))|a(?:na(?:SpacingType|ToRomanizationSelector)|takanaToHiraganaSelector)|e(?:epArrangedIcon|rnelExtensionsFolderType|y(?:board(?:ANSI|I(?:SO|nputMethodClass)|JIS|Layout(?:Icon|sFolderType)|Unknown)|chain(?:FolderType|ListChangedKCEvent))))|L(?:A(?:AllMorphemes|DefaultEdge|EndOfSourceTextMask|FreeEdge|IncompleteEdge|MorphemesArrayVersion|Speech(?:BagyouGodan|Chimei(?:Setsubigo)?|Do(?:kuritsugo|ushi)|Fu(?:kushi|tsuuMeishi)|G(?:agyouGodan|odanDoushi)|IchidanDoushi|J(?:inmei(?:Mei|Se(?:i|tsubigo))?|o(?:doushi|shi))|K(?:a(?:gyouGodan|henDoushi|ndoushi|tsuyou(?:Gokan|Katei|M(?:ask|eirei|izen)|Ren(?:tai|you)|Syuushi))|ei(?:douMeishi|you(?:doushi|shi))|igou|oyuuMeishi|uten)|M(?:agyouGodan|e(?:diumClassMask|ishi)|uhinshi)|NagyouGodan|R(?:agyouGodan|entaishi|oughClassMask)|S(?:a(?:gyouGodan|hen(?:Doushi|Meishi))|e(?:iku|t(?:su(?:bi(?:Chimei|go)|zokushi)|tougo))|oshikimei(?:Setsubigo)?|trictClassMask|uu(?:jiSet(?:subigo|tougo)|shi))|T(?:a(?:gyouGodan|nkanji)|outen)|WagyouGodan|ZahenDoushi))|CAR(?:C(?:tlPointFormat|urrentVersion)|LinearFormat|Tag)|S(?:A(?:ccept(?:AllowLoginUI|Default)|pp(?:DoesNot(?:ClaimTypeErr|SupportSchemeWarning)|InTrashErr|licationNotFoundErr)|ttributeNot(?:FoundErr|SettableErr))|CannotSetInfoErr|Data(?:Err|TooOldErr|UnavailableErr)|ExecutableIncorrectFormat|GarbageCollectionUnsupportedErr|Incompatible(?:ApplicationVersionErr|SystemVersionErr)|Launch(?:A(?:nd(?:DisplayErrors|Hide(?:Others)?|Print)|sync)|D(?:efaults|ont(?:AddToRecents|Switch))|InProgressErr|NewInstance)|MultipleSessionsNotSupportedErr|No(?:32BitEnvironmentErr|ClassicEnvironmentErr|ExecutableErr|LaunchPermissionErr|R(?:egistrationInfoErr|osettaEnvironmentErr)|t(?:AnApplicationErr|InitializedErr|RegisteredErr))|Roles(?:All|Editor|None|Shell|Viewer)|S(?:erverCommunicationErr|haredFileList(?:DoNotMountVolumes|NoUserInteraction))|Unknown(?:Creator|Err|Type(?:Err)?))|TAGCurrentVersion|a(?:belKCItemAttr|nguageTagType|rge(?:1BitMask|32BitData|4Bit(?:Data|IconSize)|8Bit(?:Data|IconSize|Mask)|IconSize)|st(?:DomainConstant|FeatureType|IOKitNotificationType|MagicBusyFiletype)|unch(?:ToGetTerminology|erItemsFolderType))|e(?:ft(?:ArrowCharCode|ToRight)|tterCaseType)|i(?:braryAssistantsFolderType|gaturesType|miterParam_(?:AttackTime|DecayTime|PreGain)|n(?:e(?:F(?:eedCharCode|inalSwashesO(?:ffSelector|nSelector))|InitialSwashesO(?:ffSelector|nSelector)|arPCMFormatFlag(?:Is(?:AlignedHigh|BigEndian|Float|Non(?:Interleaved|Mixable)|Packed|SignedInteger)|s(?:AreAllClear|SampleFraction(?:Mask|Shift))))|guisticRearrangement(?:O(?:ffSelector|nSelector)|Type))|stDef(?:ProcPtr|Standard(?:IconType|TextType)|UserProcType))|o(?:c(?:al(?:Domain|PPDDomain|e(?:A(?:llPartsMask|ndVariantNameMask)|Language(?:Mask|VariantMask)|NameMask|OperationVariantNameMask|Region(?:Mask|VariantMask)|Script(?:Mask|VariantMask)|s(?:BufferTooSmallErr|DefaultDisplayStatus|Folder(?:Icon|Type)|TableFormatErr)))|k(?:KCEvent(?:Mask)?|ed(?:BadgeIcon|Icon)))|g(?:osO(?:ffSelector|nSelector)|sFolderType)|w(?:PassParam_(?:CutoffFrequency|Resonance)|erCase(?:NumbersSelector|PetiteCapsSelector|SmallCapsSelector|Type))))|M(?:68kISA|D(?:Label(?:LocalDomain|UserDomain)|Query(?:AllowFSTranslation|ReverseSortOrderFlag|Synchronous|WantsUpdates))|IDI(?:DriversFolderType|I(?:DNotUnique|nvalid(?:Client|Port|UniqueID))|M(?:essageSendErr|sg(?:IOError|Object(?:Added|Removed)|PropertyChanged|Se(?:rialPortOwnerChanged|tupChanged)|ThruConnectionsChanged))|No(?:C(?:onnection|urrentSetup)|tPermitted)|Object(?:NotFound|Type_(?:De(?:stination|vice)|E(?:ntity|xternal(?:De(?:stination|vice)|Entity|Source))|Other|Source))|Se(?:rverStartErr|tupFormatErr)|Unknown(?:E(?:ndpoint|rror)|Property)|Wrong(?:EndpointType|PropertyType|Thread))|OR(?:T(?:C(?:o(?:ntextualType|ver(?:Descending|IgnoreVertical|TypeMask|Vertical))|urr(?:Insert(?:Before|Count(?:Mask|Shift)|KashidaLike)|JustTableCount(?:Mask|Shift)|entVersion))|DoInsertionsBefore|I(?:nsertion(?:Type|sCountMask)|sSplitVowelPiece)|Lig(?:FormOffset(?:Mask|Shift)|LastAction|StoreLigature|atureType)|Mark(?:Insert(?:Before|Count(?:Mask|Shift)|KashidaLike)|JustTableCount(?:Mask|Shift))|RearrangementType|SwashType|Tag|ra(?:CDx(?:A(?:B)?|BA)?|D(?:Cx(?:A(?:B)?|BA)?|x(?:A(?:B)?|BA)?)|NoAction|x(?:A(?:B)?|BA)))|X(?:C(?:over(?:Descending|IgnoreVertical|LogicalOrder|TypeMask|Vertical)|urrentVersion)|Tag))|P(?:A(?:ddressSpaceInfoVersion|llocate(?:1(?:024ByteAligned|6ByteAligned)|32ByteAligned|4096ByteAligned|8ByteAligned|AltiVecAligned|ClearMask|DefaultAligned|GloballyMask|InterlockAligned|MaxAlignment|No(?:CreateMask|GrowthMask)|ResidentMask|VM(?:PageAligned|XAligned))|nyRemoteContext|syncInterruptRemoteContext)|BlueBlockingErr|Cr(?:eateTask(?:NotDebuggableMask|SuspendedMask|TakesAllExceptionsMask|ValidOptionsMask)|iticalRegionInfoVersion)|DeletedErr|E(?:G4Object_(?:AAC_(?:L(?:C|TP)|Main|S(?:BR|SR|calable))|CELP|HVXC|TwinVQ)|ventInfoVersion)|HighLevelDebugger|I(?:n(?:sufficientResourcesErr|terruptRemoteContext|validIDErr)|terationEndErr)|LowLevelDebugger|M(?:axAllocSize|idLevelDebugger)|N(?:anokernelNeedsMemoryErr|o(?:ID|tificationInfoVersion))|OwningProcessRemoteContext|Pr(?:eserveTimerIDMask|ivilegedErr|ocess(?:CreatedErr|TerminatedErr))|QueueInfoVersion|SemaphoreInfoVersion|T(?:ask(?:AbortedErr|Blocked(?:Err)?|CreatedErr|InfoVersion|Propagate(?:Mask)?|R(?:e(?:ady|sume(?:Branch(?:Mask)?|Mask|Step(?:Mask)?))|unning)|St(?:ate(?:32BitMemoryException|FPU|Machine|Registers|TaskInfo|Vectors)|oppedErr))|ime(?:IsD(?:eltaMask|urationMask)|outErr)))|a(?:c(?:Arabic(?:AlBayanVariant|StandardVariant|T(?:huluthVariant|rueTypeVariant))|C(?:roatian(?:CurrencySignVariant|DefaultVariant|EuroSignVariant)|yrillic(?:CurrSign(?:StdVariant|UkrVariant)|DefaultVariant|EuroSignVariant))|Farsi(?:StandardVariant|TrueTypeVariant)|Greek(?:DefaultVariant|EuroSignVariant|NoEuroSignVariant)|He(?:brew(?:FigureSpaceVariant|StandardVariant)|lpVersion)|Icelandic(?:St(?:andardVariant|d(?:CurrSignVariant|DefaultVariant|EuroSignVariant))|T(?:T(?:CurrSignVariant|DefaultVariant|EuroSignVariant)|rueTypeVariant))|Japanese(?:BasicVariant|PostScript(?:PrintVariant|ScrnVariant)|St(?:andardVariant|dNoVerticalsVariant)|VertAtKuPlusTenVariant)|MemoryMaximumMemoryManagerBlockSize|OSReadMe(?:FolderIcon|sFolderType)|Roman(?:CurrencySignVariant|DefaultVariant|EuroSignVariant|Latin1(?:CroatianVariant|DefaultVariant|IcelandicVariant|RomanianVariant|StandardVariant|TurkishVariant)|StandardVariant|ian(?:CurrencySignVariant|DefaultVariant|EuroSignVariant))|VT100(?:CurrencySignVariant|DefaultVariant|EuroSignVariant)|hineNameStrID)|gic(?:BusyCreationDate|TemporaryItemsFolderType)|le|nagedItemsFolderType|t(?:h(?:SymbolsSelector|ematical(?:ExtrasType|GreekO(?:ffSelector|nSelector)))|rixMixerParam_(?:Enable|P(?:ost(?:AveragePower(?:Linear)?|PeakHoldLevel(?:Linear)?)|re(?:AveragePower(?:Linear)?|PeakHoldLevel(?:Linear)?))|Volume))|x(?:AsyncArgs|InputLengthOfAppleJapaneseEngine|K(?:anjiLengthInAppleJapaneseDictionary|eyLength)|YomiLengthInAppleJapaneseDictionary|imumBlocksIn4GB))|enu(?:A(?:ppleLogo(?:FilledGlyph|OutlineGlyph)|ttr(?:AutoDisable|CondenseSeparators|DoNot(?:CacheImage|UseUserCommandKeys)|ExcludesMarkColumn|Hidden|UsePencilGlyph))|BlankGlyph|C(?:GImageRefType|a(?:lcItemMsg|psLockGlyph)|heckmarkGlyph|learGlyph|o(?:lorIconType|mmandGlyph|nt(?:ext(?:Co(?:mmandIDSearch|ntextualMenu)|DontUpdate(?:Enabled|Icon|Key|Text)|Inspection|KeyMatching|Menu(?:Bar(?:Tracking)?|Enabling)|P(?:opUp(?:Tracking)?|ullDown)|Submenu|ualMenuGlyph)|rol(?:Glyph|ISOGlyph|Modifier))))|D(?:e(?:f(?:ClassID|ProcPtr)|lete(?:LeftGlyph|RightGlyph))|i(?:amondGlyph|sposeMsg)|own(?:ArrowGlyph|wardArrowDashedGlyph)|raw(?:ItemsMsg|Msg))|E(?:isuGlyph|jectGlyph|nterGlyph|scapeGlyph|vent(?:DontCheckSubmenus|IncludeDisabledItems|QueryOnly))|F(?:1(?:0Glyph|1Glyph|2Glyph|3Glyph|4Glyph|5Glyph|6Glyph|7Glyph|8Glyph|9Glyph|Glyph)|2Glyph|3Glyph|4Glyph|5Glyph|6Glyph|7Glyph|8Glyph|9Glyph|indItemMsg)|H(?:elpGlyph|iliteItemMsg)|I(?:con(?:Re(?:fType|sourceType)|SuiteType|Type)|nitMsg|tem(?:Attr(?:Auto(?:Disable|Repeat)|CustomDraw|D(?:isabled|ynamic)|Hidden|I(?:conDisabled|gnoreMeta|ncludeInCmdKeyMatching)|NotPreviousAlternate|S(?:e(?:ctionHeader|parator)|ubmenuParentChoosable)|U(?:pdateSingleItem|seVirtualKey))|Data(?:A(?:llDataVersion(?:One|T(?:hree|wo))|ttribute(?:dText|s))|C(?:FString|md(?:Key(?:Glyph|Modifiers)?|VirtualKey)|ommandID)|Enabled|Font(?:ID)?|I(?:con(?:Enabled|Handle|ID)|ndent)|Mark|Properties|Refcon|S(?:tyle|ubmenu(?:Handle|ID))|Text(?:Encoding)?)))|KanaGlyph|Left(?:Arrow(?:DashedGlyph|Glyph)|DoubleQuotesJapaneseGlyph)|N(?:o(?:CommandModifier|Icon|Modifiers|nmarkingReturnGlyph|rthwestArrowGlyph)|ullGlyph)|Option(?:Glyph|Modifier)|P(?:a(?:ge(?:DownGlyph|UpGlyph)|ragraphKoreanGlyph)|encilGlyph|o(?:pUpMsg|werGlyph)|ropertyPersistent)|R(?:eturn(?:Glyph|R2LGlyph)|ight(?:Arrow(?:DashedGlyph|Glyph)|DoubleQuotesJapaneseGlyph))|S(?:h(?:ift(?:Glyph|Modifier)|rinkIconType)|izeMsg|mallIconType|outheastArrowGlyph|paceGlyph|tdMenu(?:BarProc|Proc)|ystemIconSelectorType)|T(?:ab(?:LeftGlyph|RightGlyph)|hemeSavvyMsg|ra(?:ckingMode(?:Keyboard|Mouse)|demarkJapaneseGlyph))|UpArrow(?:DashedGlyph|Glyph))|i(?:crosecondScale|llisecondScale|ni(?:1BitMask|4BitData|8BitData))|o(?:d(?:DateKCItemAttr|al(?:DialogVariantCode|WindowClass)|em(?:OutOfMemory|PreferencesMissing|Script(?:Missing|sFolderType)))|nospaced(?:NumbersSelector|TextSelector)|u(?:nted(?:BadgeIcon|Folder(?:AliasType|Icon(?:Resource)?))|se(?:Params(?:ClickAndHold|DragInitiation|ProxyIcon|Sticky)|Tracking(?:ClientEvent|KeyModifiersChanged|Mouse(?:D(?:own|ragged)|E(?:ntered|xited)|Moved|Pressed|Released|Up)|ScrollWheel|TimedOut|UserCancelled)|UpOutOfSlop))|v(?:able(?:Alert(?:VariantCode|WindowClass)|Modal(?:DialogVariantCode|WindowClass))|ieDocumentsFolderType))|u(?:lti(?:ChannelMixerParam_(?:Enable|P(?:an|ost(?:AveragePower|PeakHoldLevel)|re(?:AveragePower|PeakHoldLevel))|Volume)|band(?:CompressorParam_(?:AttackTime|C(?:ompressionAmount(?:1|2|3|4)|rossover(?:1|2|3))|EQ(?:1|2|3|4)|Headroom(?:1|2|3|4)|InputAmplitude(?:1|2|3|4)|OutputAmplitude(?:1|2|3|4)|P(?:ostgain|regain)|ReleaseTime|Threshold(?:1|2|3|4))|Filter_(?:Bandwidth(?:1|2|3)|Center(?:Freq(?:1|2|3)|Gain(?:1|2|3))|High(?:F(?:ilterType|requency)|Gain)|Low(?:F(?:ilterType|requency)|Gain)))|processingFolderType)|sic(?:D(?:evice(?:MIDIEventSelect|P(?:aram_(?:ReverbVolume|Tuning|Volume)|r(?:epareInstrumentSelect|operty_(?:BankName|DualSchedulingMode|GroupOutputBus|Instrument(?:Count|N(?:ame|umber))|MIDIXMLNames|PartGroup|S(?:oundBank(?:Data|FS(?:Ref|Spec)|URL)|treamFromDisk|upportsStartStopNote)|UsesInternalReverb)))|R(?:ange|eleaseInstrumentSelect)|S(?:ampleFrameMask_(?:IsScheduled|SampleOffset)|t(?:artNoteSelect|opNoteSelect)|ysExSelect))|ocumentsFolderType)|EventType_(?:AUPreset|Extended(?:Control|Note|Tempo)|M(?:IDI(?:ChannelMessage|NoteMessage|RawData)|eta)|NULL|Parameter|User)|NoteEvent_U(?:nused|seGroupInstrument)|Sequence(?:File(?:Flags_(?:Default|EraseFile)|_(?:AnyType|MIDIType|iMelodyType))|LoadSMF_(?:ChannelsToTracks|PreserveTracks)|Type_(?:Beats|S(?:amples|econds))))))|N(?:LCCharactersSelector|S(?:L(?:68kContextNotSupported|B(?:ad(?:ClientInfoPtr|DataTypeErr|NetConnection|ProtocolTypeErr|ReferenceErr|ServiceTypeErr|URLSyntax)|ufferTooSmallForData)|CannotContinueLookup|ErrNullPtrError|In(?:itializationFailed|sufficient(?:OTVer|SysVer)|validPluginSpec)|N(?:o(?:C(?:arbonLib|ontextAvailable)|ElementsInList|PluginsFo(?:rSearch|und)|SupportForService|tI(?:mplementedYet|nitialized))|ull(?:ListPtr|NeighborhoodPtr))|PluginLoadFailed|RequestBufferAlreadyInList|S(?:chedulerError|earchAlreadyInProgress|omePluginsFailedToLoad)|UILibraryNotAvailable)|p(?:A(?:dd(?:PlayerFailedErr|ressInUseErr)|lready(?:AdvertisingErr|InitializedErr))|C(?:antBlockErr|onnectFailedErr|reateGroupFailedErr)|F(?:eatureNotImplementedErr|reeQExhaustedErr)|GameTerminatedErr|HostFailedErr|In(?:itializationFailedErr|valid(?:AddressErr|DefinitionErr|G(?:ameRefErr|roupIDErr)|P(?:arameterErr|layerIDErr|rotocol(?:ListErr|RefErr))))|JoinFailedErr|Me(?:mAllocationErr|ssageTooBigErr)|N(?:ameRequiredErr|o(?:GroupsErr|HostVolunteersErr|PlayersErr|tAdvertisingErr))|OT(?:NotPresentErr|VersionTooOldErr)|P(?:ipeFullErr|rotocolNotAvailableErr)|RemovePlayerFailedErr|SendFailedErr|T(?:imeoutErr|opologyNotSupportedErr)))|a(?:meLocked|nosecondScale|v(?:CustomControlMessageFailedErr|Invalid(?:CustomControlMessageErr|SystemConfigErr)|MissingKindStringErr|WrongDialog(?:ClassErr|StateErr)))|e(?:gativeKCItemAttr|twork(?:Domain|PPDDomain)|ut(?:er|ralScript)|verAuthenticate|w(?:DebugHeap|S(?:izeParameter|tyleHeap|uspend)|TimePitchParam_(?:EnablePeakLocking|Overlap|Pitch|Rate))|xt(?:Body|WindowGroup))|o(?:A(?:lternatesSelector|nnotationSelector)|ByteCode|C(?:JK(?:ItalicRomanSelector|SymbolAlternativesSelector)|ard(?:BusCISErr|E(?:nablersFoundErr|rr)|SevicesSocketsErr)|lientTableErr|o(?:mpatibleNameErr|nstraint))|En(?:ablerForCardErr|dingProsody)|F(?:ilesIcon|olderIcon|ractionsSelector)|I(?:OWindowRequestedErr|deographicAlternativesSelector)|More(?:I(?:nterruptSlotsErr|temsErr)|TimerClientsErr)|OrnamentsSelector|Process|RubyKanaSelector|S(?:peechInterrupt|tyl(?:eOptionsSelector|isticAlternatesSelector)|uchPowerSource)|T(?:hreadID|imeOut|rans(?:form|literationSelector))|UserAuthentication|WriteIcon|n(?:BreakingSpaceCharCode|FinalSwashesO(?:ffSelector|nSelector)|eKCStopOn)|rmalPositionSelector|t(?:Paged|ReadyErr|ZVCapableErr|eIcon))|u(?:llCharCode|m(?:AUNBandEQFilterTypes|ber(?:C(?:aseType|tlCTabEntries)|OfResponseFrequencies|SpacingType))))|O(?:CRInputMethodClass|PBD(?:C(?:ontrolPointFormat|urrentVersion)|DistanceFormat|Tag)|S(?:A(?:C(?:anGetSource|omponentType)|DontUsePhac|Error(?:A(?:pp|rgs)|BriefMessage|ExpectedType|Message|Number|OffendingObject|PartialResult|Range)|FileType|GenericScriptingComponentSubtype|Mode(?:A(?:lwaysInteract|ugmentContext)|C(?:an(?:Interact|tSwitchLayer)|ompileIntoContext)|D(?:isp(?:atchToDirectObject|layForHumans)|o(?:Record|nt(?:Define|GetDataForArguments|Reconnect|StoreParent)))|FullyQualifyDescriptors|N(?:everInteract|ull)|PreventGetSource)|N(?:oDispatch|ull(?:Mode|Script))|RecordedText|S(?:cript(?:BestType|Is(?:Modified|Type(?:CompiledScript|Script(?:Context|Value)))|ResourceType)|elect(?:AvailableDialect(?:CodeList|s)|Co(?:erce(?:FromDesc|ToDesc)|mp(?:ile(?:Execute)?|onentSpecificStart)|py(?:DisplayString|ID|S(?:cript|ourceString)))|D(?:isp(?:lay|ose)|o(?:Event|Script))|Execute(?:Event)?|Get(?:ActiveProc|C(?:reateProc|urrentDialect)|DialectInfo|ResumeDispatchProc|S(?:criptInfo|endProc|ource))|Load(?:Execute)?|MakeContext|S(?:cript(?:Error|ingComponentName)|et(?:ActiveProc|C(?:reateProc|urrentDialect)|DefaultTarget|ResumeDispatchProc|S(?:criptInfo|endProc))|t(?:artRecording|o(?:pRecording|re))))|u(?:ite|pports(?:AE(?:Coercion|Sending)|Co(?:mpiling|nvenience)|Dialects|EventHandling|GetSource|Recording)))|UseStandardDispatch|sync(?:CompleteMessageID|Ref(?:64(?:Count|Size)|Count|Size)))|IZ(?:CodeInSharedLibraries|DontOpenResourceFile|OpenWithReadPermission|dontAcceptRemoteEvents)|NotificationMessageID)|T(?:A(?:ccessErr|ddressBusyErr)|B(?:ad(?:AddressErr|ConfigurationErr|DataErr|FlagErr|NameErr|OptionErr|QLenErr|ReferenceErr|S(?:equenceErr|yncErr))|ufferOverflowErr)|C(?:anceledErr|lientNotInittedErr|onfigurationChangedErr)|DuplicateFoundErr|FlowErr|IndOutErr|LookErr|No(?:AddressErr|D(?:ataErr|isconnectErr)|Error|ReleaseErr|StructureTypeErr|UDErrErr|t(?:FoundErr|SupportedErr))|Out(?:OfMemoryErr|StateErr)|P(?:ort(?:HasDiedErr|LostConnection|WasEjectedErr)|ro(?:tocolErr|viderMismatchErr))|QFullErr|Res(?:AddressErr|QLenErr)|S(?:tateChangeErr|ysErrorErr)|UserRequestedErr)|ff(?:linePreflight_(?:NotRequired|Optional|Required)|set2Pos)|ld68kRTA|n(?:AppropriateDisk|SystemDisk|eByteCode)|p(?:aque(?:A(?:ddressSpaceID|nyID|reaID)|C(?:o(?:herenceID|nsoleID)|puID|riticalRegionID)|EventID|NotificationID|ProcessID|QueueID|SemaphoreID|T(?:askID|imerID))|en(?:D(?:oc(?:EditorsFolderType|FolderType|LibrariesFolderType|ShellPlugInsFolderType)|ropIconVariant)|FolderIcon(?:Resource)?|IconVariant)|tionUnicode)|r(?:Connections|dinalsSelector|namentSetsType)|therPluginFormat_(?:AU|Undefined|k(?:MAS|VST))|ut(?:OfResourceErr|putTextInUnicodeEncoding(?:Bit|Mask))|verla(?:ppingCharactersType|yWindowClass)|wne(?:dFolderIcon(?:Resource)?|r(?:I(?:D2Name|con)|Name2ID)))|P(?:CSTo(?:Device|PCS)|EF(?:AbsoluteExport|Co(?:deS(?:ection|ymbol)|nstantSection)|D(?:ataSymbol|ebugSection)|Ex(?:ceptionSection|ecDataSection|pSym(?:ClassShift|MaxNameOffset|NameOffsetMask))|FirstSectionHeaderOffset|Gl(?:obalShare|ueSymbol)|Hash(?:LengthShift|MaxLength|Slot(?:FirstKeyMask|Max(?:KeyIndex|SymbolCount)|SymCountShift)|ValueMask)|I(?:mpSym(?:ClassShift|MaxNameOffset|NameOffsetMask)|nitLibBeforeMask)|LoaderSection|P(?:ackedDataSection|kData(?:Block|Count5Mask|MaxCount5|OpcodeShift|Repeat(?:Block|Zero)?|VCount(?:EndMask|Mask|Shift)|Zero)|ro(?:cessShare|tectedShare))|Re(?:exportedImport|loc(?:B(?:asicOpcodeRange|ySect(?:C|D(?:WithSkip)?))|I(?:mportRun|ncrPosition(?:MaxOffset)?)|Lg(?:By(?:Import(?:MaxIndex)?|SectionSubopcode)|Repeat(?:Max(?:ChunkCount|RepeatCount))?|Set(?:OrBySection(?:MaxIndex)?|Sect(?:CSubopcode|DSubopcode)))|RunMaxRunLength|S(?:etPos(?:MaxOffset|ition)|m(?:By(?:Import|Section)|IndexMaxIndex|Repeat(?:Max(?:ChunkCount|RepeatCount))?|SetSect(?:C|D)))|TVector(?:12|8)|UndefinedOpcode|VTable8|WithSkipMax(?:RelocCount|SkipCount)))|T(?:OCSymbol|VectorSymbol|ag(?:1|2)|racebackSection)|Un(?:definedSymbol|packedDataSection)|Version|WeakImport(?:LibMask|SymMask))|M(?:AllocationFailure|Border(?:Double(?:Hairline|Thickline)|Single(?:Hairline|Thickline))|C(?:MYKColorSpaceModel|VMSymbolNotFound|ancel|loseFailed|overPage(?:After|Before|None)|reateMessageFailed)|D(?:ataFormatXML(?:Compressed|Default|Minimal)|e(?:leteSubTicketFailed|stination(?:F(?:ax|ile)|Invalid|Pr(?:eview|inter|ocessPDF))|vNColorSpaceModel)|o(?:cumentNotFound|ntSwitchPDEError)|uplex(?:No(?:Tumble|ne)|Tumble))|EditRequestFailed|F(?:eatureNotInstalled|ileOrDirOperationFailed|ontN(?:ameTooLong|otFound))|G(?:eneral(?:CGError|Error)|rayColorSpaceModel)|HideInlineItems|I(?:O(?:AttrNotAvailable|MSymbolNotFound)|n(?:ternalError|valid(?:Allocator|C(?:VMContext|alibrationTarget|onnection)|FileType|I(?:OMContext|ndex|tem)|Job(?:ID|Template)|Key|LookupSpec|Object|P(?:BMRef|DEContext|MContext|a(?:geFormat|per|rameter)|r(?:eset|int(?:Se(?:ssion|ttings)|er(?:Address|Info)?)))|Reply|S(?:tate|ubTicket)|T(?:icket|ype)|Value))|temIsLocked)|Job(?:Busy|Canceled|GetTicket(?:BadFormatError|ReadError)|ManagerAborted|NotFound|Stream(?:EndError|OpenFailed|ReadFailed))|Key(?:Not(?:Found|Unique)|OrValueNotFound)|La(?:ndscape|stErrorCodeToMakeMaintenanceOfThisListEasier|yout(?:BottomTop(?:LeftRight|RightLeft)|LeftRight(?:BottomTop|TopBottom)|RightLeft(?:BottomTop|TopBottom)|TopBottom(?:LeftRight|RightLeft)))|MessagingError|No(?:Default(?:Item|Printer|Settings)|Error|PrinterJobID|S(?:electedPrinters|uchEntry)|tImplemented)|O(?:bjectInUse|penFailed|utOfScope)|P(?:MSymbolNotFound|a(?:geToPaperMapping(?:None|ScaleToFit)|perType(?:Coated|Glossy|P(?:lain|remium)|T(?:Shirt|ransparency)|Unknown))|ermissionError|lugin(?:NotFound|RegisterationFailed)|ortrait|r(?:BrowserNoUI|int(?:AllPages|er(?:Idle|Processing|Stopped))))|Qu(?:ality(?:Best|Draft|Highest|InkSaver|Lowest|Normal|Photo)|eue(?:AlreadyExists|JobFailed|NotFound))|R(?:GBColorSpaceModel|e(?:ad(?:Failed|GotZeroData)|verse(?:Landscape|Portrait)))|S(?:caling(?:CenterOn(?:ImgArea|Paper)|Pin(?:Bottom(?:Left|Right)|Top(?:Left|Right)))|erver(?:A(?:lreadyRunning|ttributeRestricted)|CommunicationFailed|NotFound|Suspended)|how(?:DefaultInlineItems|Inline(?:Copies|Orientation|Pa(?:geRange(?:WithSelection)?|perSize)|Scale)|PageAttributesPDE)|implexTumble|t(?:atusFailed|ringConversionFailure)|ubTicketNotFound|yncRequestFailed)|T(?:emplateIsLocked|icket(?:IsLocked|TypeNotFound))|U(?:n(?:ableToFindProcess|expectedImagingError|known(?:ColorSpaceModel|DataType|Message)|locked|supportedConnection)|pdateTicketFailed|serOrGroupNotFound)|Val(?:idateTicketFailed|ueOutOfRange)|WriteFailed|XMLParseError)|OSIXError(?:Base|E(?:2BIG|A(?:CCES|DDR(?:INUSE|NOTAVAIL)|FNOSUPPORT|GAIN|LREADY|UTH)|B(?:AD(?:ARCH|EXEC|F|M(?:ACHO|SG)|RPC)|USY)|C(?:ANCELED|HILD|ONN(?:ABORTED|RE(?:FUSED|SET)))|D(?:E(?:ADLK|STADDRREQ|VERR)|OM|QUOT)|EXIST|F(?:AULT|BIG|TYPE)|HOST(?:DOWN|UNREACH)|I(?:DRM|LSEQ|N(?:PROGRESS|TR|VAL)|O|S(?:CONN|DIR))|LOOP|M(?:FILE|LINK|SGSIZE|ULTIHOP)|N(?:AMETOOLONG|E(?:EDAUTH|T(?:DOWN|RESET|UNREACH))|FILE|O(?:ATTR|BUFS|D(?:ATA|EV)|E(?:NT|XEC)|L(?:CK|INK)|M(?:EM|SG)|PROTOOPT|S(?:PC|R|TR|YS)|T(?:BLK|CONN|DIR|EMPTY|S(?:OCK|UP)|TY))|XIO)|O(?:PNOTSUPP|VERFLOW)|P(?:ERM|FNOSUPPORT|IPE|RO(?:C(?:LIM|UNAVAIL)|G(?:MISMATCH|UNAVAIL)|TO(?:NOSUPPORT|TYPE)?)|WROFF)|R(?:ANGE|EMOTE|OFS|PCMISMATCH)|S(?:H(?:LIBVERS|UTDOWN)|OCKTNOSUPPORT|PIPE|RCH|TALE)|T(?:IME(?:DOUT)?|OOMANYREFS|XTBSY)|USERS|XDEV))|ROP(?:A(?:LDirectionClass|NDirectionClass)|BNDirectionClass|C(?:SDirectionClass|anHang(?:LTMask|RBMask)|urrentVersion)|DirectionMask|E(?:NDirectionClass|SDirectionClass|TDirectionClass)|IsFloaterMask|L(?:DirectionClass|R(?:EDirectionClass|ODirectionClass))|N(?:SMDirectionClass|umDirectionClasses)|ONDirectionClass|P(?:DFDirectionClass|SDirectionClass|airOffset(?:Mask|S(?:hift|ign)))|R(?:DirectionClass|L(?:EDirectionClass|ODirectionClass)|ightConnectMask)|S(?:DirectionClass|ENDirectionClass)|Tag|UseRLPairMask|WSDirectionClass|ZeroReserved)|a(?:ckageAliasType|ge(?:DownCharCode|InMemory|OnDisk|UpCharCode)|nn(?:erParam_(?:Azimuth|CoordScale|Distance|Elevation|Gain|RefDistance)|ingMode_(?:SoundField|VectorBasedPanning))|r(?:amet(?:erEvent_(?:Immediate|Ramped)|ricEQParam_(?:CenterFreq|Gain|Q))|enthesisAnnotationSelector|tiallyConnectedSelector)|s(?:calStackBased|s(?:CallToChainErr|Selector|word(?:ChangedKCEvent(?:Mask)?)?)|teboard(?:ClientIsOwner|Flavor(?:No(?:Flags|tSaved)|Promised|RequestOnly|S(?:ender(?:Only|Translated)|ystemTranslated))|Modified|StandardLocation(?:Trash|Unknown)))|thKCItemAttr)|e(?:ncil(?:LeftUnicode|Unicode)|riod(?:AnnotationSelector|sToEllipsisO(?:ffSelector|nSelector)))|i(?:CharactersSelector|ctureD(?:ialogItem|ocumentsFolderType))|l(?:ain(?:DialogVariantCode|WindowClass)|otIconRefNo(?:Image|Mask|rmalFlags))|o(?:licyKCStopOn|rtKCItemAttr|s(?:2Offset|tCardEventErr)|wer(?:Handler(?:ExistsForDeviceErr|NotFoundFor(?:DeviceErr|ProcErr))|Mgt(?:MessageNotHandled|RequestDenied)|PC(?:ISA|RTA)))|r(?:e(?:MacOS91(?:A(?:ppl(?:eExtrasFolderType|icationsFolderType)|ssistantsFolderType|utomountedServersFolderType)|In(?:stallerLogsFolderType|ternetFolderType)|MacOSReadMesFolderType|StationeryFolderType|UtilitiesFolderType)|emptiveThread|f(?:erence(?:PanesFolderType|sFolder(?:AliasType|Icon(?:Resource)?|Type))|lightThenPause)|v(?:entOverlapO(?:ffSelector|nSelector)|ious(?:Body|WindowGroup)))|i(?:nt(?:Monitor(?:DocsFolder(?:AliasType|Type)|FolderIcon(?:Resource)?)|er(?:D(?:escriptionFolder(?:Icon|Type)|riverFolder(?:Icon|Type))|sFolderType)|ingPlugInsFolderType)|v(?:ateF(?:olderIcon(?:Resource)?|rameworksFolderType)|ilegeViolationException))|o(?:c(?:DescriptorIs(?:Absolute|Index|ProcPtr|Relative)|ess(?:DictionaryIncludeAllInformationMask|TransformTo(?:BackgroundApplication|ForegroundApplication|UIElementApplication)|orTempRoutineRequiresMPLib2))|gramTargetLevel_(?:Minus(?:2(?:0dB|3dB)|31dB)|None)|portional(?:CJKRomanSelector|IdeographsSelector|KanaSelector|NumbersSelector|TextSelector)|t(?:ected(?:ApplicationFolderIcon|SystemFolderIcon)|ocolKCItemAttr)))|ublic(?:Folder(?:Icon|Type)|KeyHashKCItemAttr|ThemeFontCount))|Q(?:D(?:C(?:orruptPICTDataErr|ursor(?:AlreadyRegistered|NotRegistered))|No(?:ColorHWCursorSupport|Palette))|LPreviewPDF(?:PagesWithThumbnailsOn(?:LeftStyle|RightStyle)|StandardStyle)|TSSUnknownErr|u(?:arterWidth(?:NumbersSelector|TextSelector)|estionMarkIcon|i(?:ck(?:LookFolderType|Time(?:ComponentsFolderType|ExtensionsFolderType))|t(?:AtNormalTimeMask|Before(?:FBAsQuitMask|NormalTimeMask|ShellQuitsMask|TerminatorAppQuitsMask)|N(?:everMask|otQuitDuring(?:InstallMask|LogoutMask))|OptionsMask))))|R(?:A(?:ATalkInactive|C(?:allBackFailed|on(?:figurationDBInitErr|nectionCanceled))|DuplicateIPAddr|ExtAuthenticationFailed|In(?:compatiblePrefs|itOpenTransportFailed|stallationDamaged|ternalError|valid(?:P(?:a(?:rameter|ssword)|ort(?:State)?)|SerialProtocol))|MissingResources|N(?:CPRejectedbyPeer|ot(?:Connected|Enabled|PrimaryInterface|Supported))|OutOfMemory|P(?:PP(?:AuthenticationFailed|NegotiationFailed|P(?:eerDisconnected|rotocolRejected)|UserDisconnected)|eerNotResponding|ort(?:Busy|SetupFailed))|RemoteAccessNotReady|StartupFailed|TCPIP(?:Inactive|NotConfigured)|U(?:nknown(?:PortState|User)|ser(?:InteractionRequired|LoginDisabled|Pwd(?:ChangeRequired|EntryRequired))))|a(?:dioButtonDialogItem|ndomParam_(?:Bound(?:A|B)|Curve)|reLigaturesO(?:ffSelector|nSelector))|dPermKCStatus|e(?:ad(?:ExtensionTermsMask|FailureErr|OnlyMemoryException|Reference|yThreadState)|busPicturesO(?:ffSelector|nSelector)|cent(?:ApplicationsFolder(?:Icon|Type)|DocumentsFolder(?:Icon|Type)|ItemsIcon|ServersFolder(?:Icon|Type))|d(?:irectedRelativeFolder|rawHighlighting)|gister(?:A(?:0|1|2|3|4|5|6)|Based|D(?:0|1|2|3|4|5|6|7)|Parameter(?:Mask|Phase|Size(?:Phase|Width)|W(?:hich(?:Phase|Width)|idth))|ResultLocation(?:Phase|Width))|lativeFolder|nderQuality_(?:High|Low|M(?:ax|edium|in))|quiredLigaturesO(?:ffSelector|nSelector)|s(?:FileNotOpened|o(?:lveAlias(?:FileNoUI|TryFileIDFirst)|urceControlDialogItem)|ultSize(?:Mask|Phase|Width))|turn(?:CharCode|Next(?:Group|U(?:G|ser)))|verb(?:2Param_(?:D(?:ecayTimeAt(?:0Hz|Nyquist)|ryWetMix)|Gain|M(?:axDelayTime|inDelayTime)|RandomizeReflections)|Param_(?:DryWetMix|Filter(?:Bandwidth|Enable|Frequency|Gain|Type)|Large(?:Brightness|De(?:lay(?:Range)?|nsity)|Size)|Modulation(?:Depth|Rate)|PreDelay|Small(?:Brightness|De(?:layRange|nsity)|LargeMix|Size))|RoomType_(?:Cathedral|Large(?:Chamber|Hall(?:2)?|Room(?:2)?)|Medium(?:Chamber|Hall(?:2|3)?|Room)|Plate|SmallRoom)))|ight(?:ArrowCharCode|ContainerArrowIcon|ToLeft)|o(?:gerBeepParam_(?:InGateThreshold(?:Time)?|OutGateThreshold(?:Time)?|Roger(?:Gain|Type)|Sensitivity)|lloverIconVariant|man(?:NumeralAnnotationSelector|izationTo(?:HiraganaSelector|KatakanaSelector))|otFolder|u(?:nd(?:TripAACParam_(?:BitRate|CompressedFormatSampleRate|EncodingStrategy|Format|Quality|RateOrQuality)|WindowDefinition|edBoxAnnotationSelector)|tin(?:e(?:DescriptorVersion|Is(?:DispatchedDefaultRoutine|NotDispatchedDefaultRoutine))|gResource(?:ID|Type))))|srcChain(?:AboveA(?:llMaps|pplicationMap)|Below(?:ApplicationMap|SystemMap))|u(?:byKana(?:O(?:ffSelector|nSelector)|Selector|Type)|nningThreadState))|S(?:C(?:BondStatus(?:LinkInvalid|No(?:Partner|tInActiveGroup)|OK|Unknown)|Network(?:Connection(?:Connect(?:ed|ing)|Disconnect(?:ed|ing)|Invalid|PPP(?:Authenticating|Connect(?:ed|ingLink)|Di(?:alOnTraffic|sconnect(?:ed|ingLink))|HoldingLinkOff|Initializing|Negotiating(?:Link|Network)|Suspended|Terminating|WaitingFor(?:CallBack|Redial)))|Flags(?:Connection(?:Automatic|Required)|I(?:nterventionRequired|s(?:Direct|LocalAddress))|Reachable|TransientConnection)|ReachabilityFlags(?:Connection(?:Automatic|On(?:Demand|Traffic)|Required)|I(?:nterventionRequired|s(?:Direct|LocalAddress|WWAN))|Reachable|TransientConnection))|PreferencesNotification(?:Apply|Commit)|Status(?:AccessError|Connection(?:Ignore|NoService)|Failed|InvalidArgument|KeyExists|Locked|MaxLink|N(?:eedLock|o(?:ConfigFile|Key|Link|PrefsSession|StoreSe(?:rver|ssion)|tifierActive))|OK|PrefsBusy|ReachabilityUnknown|Stale))|FNTLookup(?:S(?:egment(?:Array|Single)|i(?:mpleArray|ngleTable))|TrimmedArray|Vector)|K(?:DocumentState(?:AddPending|DeletePending|Indexed|NotIndexed)|Index(?:Inverted(?:Vector)?|Unknown|Vector)|Search(?:BooleanRanked|Option(?:Default|FindSimilar|NoRelevanceScores|SpaceMeansOR)|PrefixRanked|R(?:anked|equiredRanked)))|MPTETime(?:Running|Type(?:2(?:398|4|5|997(?:Drop)?)|30(?:Drop)?|5(?:0|994(?:Drop)?)|60(?:Drop)?)|Unknown|Valid)|O(?:AP(?:1999Schema|2001Schema)|CKS5NoAcceptableMethod)|R(?:A(?:lready(?:Finished|Listening|Released)|utoFinishingParam)|B(?:ad(?:Parameter|Selector)|lock(?:Background|Modally)|ufferTooSmall)|C(?:a(?:llBackParam|n(?:celOnSoundOut|ned22kHzSpeechSource|t(?:Add|GetProperty|ReadLanguageObject|Set(?:DuringRecognition|Property))))|leanupOnClientExit|omponentNotFound)|Default(?:Re(?:cognitionSystemID|jectionLevel)|SpeechSource)|E(?:nabled|xpansionTooDeep)|F(?:eedback(?:AndListeningModes|NotAvail)|oregroundOnly)|Has(?:FeedbackHasListenModes|NoSubItems)|I(?:dleRecognizer|nternalError)|Key(?:Expected|Word)|L(?:MObjType|anguageModel(?:Format|T(?:ooBig|ype))|i(?:stenKey(?:Combo|Mode|Name)|veDesktopSpeechSource))|M(?:odelMismatch|ustCancelSearch)|No(?:ClientLanguageModel|Feedback(?:HasListenModes|NoListenModes)|PendingUtterances|t(?:A(?:RecSystem|SpeechObject|vailable)|FinishedWithRejection|ImplementedYet|ListeningState|if(?:icationParam|yRecognition(?:Beginning|Done))))|O(?:ptional|therRecAlreadyModal|utOfMemory)|P(?:a(?:ramOutOfRange|th(?:Format|Type))|endingSearch|hrase(?:Format|Type))|Re(?:adAudio(?:FSSpec|URL)|cognition(?:Canceled|Done)|fCon|ject(?:able|edWord|ionLevel)|peatable)|S(?:earch(?:InProgress|StatusParam|WaitForAllClients)|ndInSourceDisconnected|oundInVolume|pe(?:edVsAccuracyParam|lling)|ubItemNotFound)|T(?:EXTFormat|ooManyElements)|Use(?:PushToTalk|ToggleListen)|W(?:ants(?:AutoFBGestures|ResultTextDrawn)|ord(?:NotFound|Type)))|S(?:LCiphersuiteGroup(?:ATS(?:Compatibility)?|Compatibility|Default|Legacy)|p(?:CantInstallErr|InternalErr|ParallelUpVectorErr|ScaleToZeroErr|VersionErr))|T(?:Class(?:DeletedGlyph|EndOf(?:Line|Text)|OutOfBounds)|KCrossStreamReset|LigActionMask|MarkEnd|NoAdvance|RearrVerbMask|SetMark|XHasLigAction)|c(?:heduledAudioSliceFlag_(?:BeganToRender(?:Late)?|Complete|Interrupt(?:AtLoop)?|Loop)|ientificInferiorsSelector|r(?:ap(?:ClearNamedScrap|Flavor(?:Mask(?:None|SenderOnly|Translated)|SizeUnknown|Type(?:Movie|Picture|Sound|Text(?:Style)?|U(?:TF16External|nicode(?:Style)?)))|GetNamedScrap|ReservedFlavorType)|eenSaversFolderType|ipt(?:CodeKCItemAttr|ingAdditionsFolder(?:Icon|Type)|sFolder(?:Icon|Type))|oll(?:Bars(?:AlwaysActive|SyncWithFocus)|Window(?:EraseToPortBackground|Invalidate|NoOptions))))|e(?:c(?:3DES192|A(?:ES(?:1(?:28|92)|256)|ccountItemAttr|dd(?:Event(?:Mask)?|ressItemAttr)|lias|uthenticationType(?:Any|D(?:PA|efault)|HT(?:MLForm|TP(?:Basic|Digest))|ItemAttr|MSN|NTLM|RPA))|C(?:S(?:BasicValidateOnly|C(?:alculateCMSDigest|heck(?:AllArchitectures|GatekeeperArchitectures|NestedCode|TrustedAnchors)|on(?:siderExpiration|tentInformation))|D(?:e(?:dicatedHost|faultFlags)|oNotValidate(?:Executable|Resources)|ynamicInformation)|EnforceRevocationChecks|FullReport|GenerateGuestHash|InternalInformation|NoNetworkAccess|QuickCheck|Re(?:portProgress|quirementInformation|strict(?:S(?:idebandData|ymlinks)|ToAppLike))|S(?:i(?:gningInformation|ngleThreaded)|kipResourceDirectory|trictValidate)|Use(?:AllArchitectures|SoftwareSigningCert)|ValidatePEH)|ert(?:EncodingItemAttr|TypeItemAttr|ificate(?:Encoding|ItemClass|Type))|o(?:deS(?:ignature(?:Adhoc|Enforcement|Force(?:Expiration|Hard|Kill)|H(?:ashSHA(?:1|256(?:Truncated)?|384|512)|ost)|LibraryValidation|NoHash|R(?:estrict|untime))|tatus(?:Debugged|Hard|Kill|Platform|Valid))|mmentItemAttr)|r(?:e(?:at(?:ionDateItemAttr|orItemAttr)|dentialType(?:Default|NoUI|WithUI))|l(?:Encoding|Type))|ustomIconItemAttr)|De(?:fault(?:ChangedEvent(?:Mask)?|KeySize)|leteEvent(?:Mask)?|s(?:criptionItemAttr|ignatedRequirementType))|EveryEventMask|Format(?:BSAFE|NetscapeCertSequence|OpenSSL|P(?:EMSequence|KCS(?:12|7))|RawKey|SSH(?:v2)?|Unknown|Wrapped(?:LSH|OpenSSL|PKCS8|SSH)|X509Cert)|G(?:eneric(?:ItemAttr|PasswordItemClass)|uestRequirementType)|Ho(?:norRoot|stRequirementType)|I(?:n(?:ternetPasswordItemClass|v(?:alidRequirementType|isibleItemAttr))|ssuerItemAttr|tem(?:PemArmour|Type(?:Aggregate|Certificate|P(?:rivateKey|ublicKey)|SessionKey|Unknown)))|Key(?:A(?:l(?:ias|waysSensitive)|pplicationTag)|De(?:crypt|rive)|E(?:ffectiveKeySize|n(?:crypt|dDate)|xtractable)|ImportOnlyOne|Key(?:C(?:lass|reator)|SizeInBits|Type)|Label|Modifiable|N(?:everExtractable|oAccessControl)|P(?:ermanent|ri(?:ntName|vate))|S(?:e(?:curePassphrase|nsitive)|ign(?:Recover)?|tartDate)|U(?:nwrap|sage(?:All|C(?:RLSign|ontentCommitment|ritical)|D(?:ataEncipherment|ecipherOnly|igitalSignature)|EncipherOnly|Key(?:Agreement|CertSign|Encipherment)|NonRepudiation|Unspecified))|Verify(?:Recover)?|Wrap|chain(?:ListChanged(?:Event|Mask)|Prompt(?:Invalid(?:Act)?|RequirePassphase|Unsigned(?:Act)?)))|L(?:abelItemAttr|ibraryRequirementType|ockEvent(?:Mask)?)|M(?:atchBits|odDateItemAttr)|N(?:egativeItemAttr|oGuest)|OptionReserved|P(?:a(?:dding(?:None|OAEP|PKCS1(?:MD(?:2|5)|SHA(?:1|2(?:24|56)|384|512))?|SigRaw)|sswordChangedEvent(?:Mask)?|thItemAttr)|luginRequirementType|ortItemAttr|r(?:eferencesDomain(?:Common|Dynamic|System|User)|ivateKeyItemClass|otocol(?:ItemAttr|Type(?:A(?:FP|ny|ppleTalk)|C(?:IFS|VSpserver)|DAAP|EPPC|FTP(?:Account|Proxy|S)?|HTTP(?:Proxy|S(?:Proxy)?)?|I(?:MAP(?:S)?|PP|RC(?:S)?)|LDAP(?:S)?|NNTP(?:S)?|POP3(?:S)?|RTSP(?:Proxy)?|S(?:M(?:B|TP)|OCKS|SH|VN)|Telnet(?:S)?)))|ublicKey(?:HashItemAttr|ItemClass))|R(?:SAM(?:ax|in)|e(?:adPermStatus|quirementTypeCount|vocation(?:CRLMethod|NetworkAccessDisabled|OCSPMethod|PreferCRL|RequirePositiveResponse|UseAnyAvailableMethod)))|S(?:criptCodeItemAttr|e(?:curityDomainItemAttr|r(?:ialNumberItemAttr|v(?:erItemAttr|iceItemAttr)))|ignatureItemAttr|ubject(?:ItemAttr|KeyIdentifierItemAttr)|ymmetricKeyItemClass)|T(?:r(?:ansform(?:Error(?:A(?:bort(?:InProgress|ed)|ttributeNotFound)|Invalid(?:Algorithm|Connection|Input(?:Dictionary)?|Length|Operation|Type)|M(?:issingParameter|oreThanOneOutput)|N(?:ameAlreadyRegistered|otInitializedCorrectly)|UnsupportedAttribute)|Invalid(?:Argument|Override)|MetaAttribute(?:CanCycle|Deferred|Externalize|Has(?:InboundConnection|OutboundConnections)|Name|Re(?:f|quire(?:d|sOutboundConnection))|Stream|Value)|OperationNotSupportedOnGroup|TransformIs(?:Executing|NotRegistered))|ust(?:Option(?:AllowExpired(?:Root)?|FetchIssuerFromNet|ImplicitAnchors|LeafIsCA|RequireRevPerCert|UseTrustSettings)|Result(?:Deny|FatalTrustFailure|Invalid|OtherError|Proceed|RecoverableTrustFailure|Unspecified)|Settings(?:ChangedEvent(?:Mask)?|Domain(?:Admin|System|User)|KeyUse(?:Any|EnDecrypt(?:Data|Key)|KeyExchange|Sign(?:Cert|Revocation|ature))|Result(?:Deny|Invalid|Trust(?:AsRoot|Root)|Unspecified))))|ypeItemAttr)|U(?:nlock(?:Event(?:Mask)?|StateStatus)|pdateEvent(?:Mask)?|seOnly(?:GID|UID))|VolumeItemAttr|WritePermStatus|ondScale|p(?:192r1|256r1|384r1|521r1)|urityDomainKCItemAttr)|lector(?:All(?:1BitData|32BitData|4BitData|8BitData|AvailableData|HugeData|LargeData|MiniData|SmallData)|Huge(?:1Bit|32Bit|4Bit|8Bit(?:Mask)?)|Large(?:1Bit|32Bit|4Bit|8Bit(?:Mask)?)|Mini(?:1Bit|4Bit|8Bit)|Small(?:1Bit|32Bit|4Bit|8Bit(?:Mask)?)|sAre(?:Indexable|NotIndexable))|quenceTrackProperty_(?:AutomatedParameters|LoopInfo|MuteStatus|OffsetTime|SoloStatus|T(?:imeResolution|rackLength))|r(?:ialNumberKCItemAttr|v(?:erKCItemAttr|ice(?:KCItemAttr|sFolderType)))|t(?:CLUT(?:ByValue|Immediately|WithLuminance)|DebugOption|FrontProcess(?:CausedByUser|FrontWindowOnly)|PowerLevel))|h(?:a(?:dowDialogVariantCode|r(?:ed(?:BadgeIcon|Folder(?:AliasType|Icon(?:Resource)?)|LibrariesFolder(?:Icon|Type)|UserDataFolderType)|ingPrivs(?:NotApplicableIcon|Read(?:OnlyIcon|WriteIcon)|UnknownIcon|WritableIcon)))|eet(?:AlertWindowClass|WindowClass)|ift(?:JIS_(?:BasicVariant|DOSVariant|MusicCDVariant)|Unicode)|o(?:rtcutIcon|w(?:DiacriticsSelector|HideInputWindow))|utdown(?:FolderType|Items(?:DisabledFolder(?:Icon|Type)|FolderIcon)))|i(?:deFloaterVariantCode|gn(?:KCItemAttr|atureKCItemAttr)|mpl(?:eWindowClass|ifiedCharactersSelector))|l(?:ash(?:ToDivideO(?:ffSelector|nSelector)|edZeroO(?:ffSelector|nSelector))|eep(?:De(?:mand|ny)|Now|Re(?:quest|voke)|Unlock|WakeUp))|ma(?:ll(?:1BitMask|32BitData|4Bit(?:Data|IconSize)|8Bit(?:Data|IconSize|Mask)|CapsSelector|IconSize)|rt(?:QuotesO(?:ffSelector|nSelector)|SwashType))|o(?:rt(?:AscendingIcon|DescendingIcon)|und(?:FileIcon|SetsFolderType))|p(?:a(?:ceCharCode|tial(?:Mixer(?:AttenuationCurve_(?:Exponential|Inverse|Linear|Power)|Param_(?:Azimuth|Distance|E(?:levation|nable)|G(?:ain|lobalReverbGain)|M(?:axGain|inGain)|O(?:bstructionAttenuation|cclusionAttenuation)|PlaybackRate|ReverbBlend)|RenderingFlags_(?:DistanceAttenuation|InterAuralDelay))|izationAlgorithm_(?:EqualPowerPanning|HRTF(?:HQ)?|S(?:oundField|phericalHead|tereoPassThrough)|VectorBasedPanning)))|e(?:ak(?:ableItemsFolder(?:Type)?|erConfiguration_(?:5_(?:0|1)|HeadPhones|Quad|Stereo))|cial(?:Case(?:CaretHook|DrawHook|EOLHook|GNEFilterProc|Hi(?:ghHook|tTestHook)|MBarHook|NWidthHook|ProtocolHandler|S(?:elector(?:Mask|Phase|Width)|ocketListener)|T(?:E(?:DoText|FindWord|Recalc)|extWidthHook)|WidthHook)?|Folder)|ech(?:FolderType|GenerateTune|InputMethodClass|Relative(?:Duration|Pitch)|ShowSyllables))|otlight(?:ImportersFolderType|MetadataCacheFolderType|SavedSearchesFolderType))|quaredLigaturesO(?:ffSelector|nSelector)|t(?:a(?:ck(?:DispatchedPascalStackBased|OverflowException|Parameter(?:Mask|Phase|Width))|ndardWindowDefinition|rt(?:DateKCItemAttr|up(?:Folder(?:AliasType|IconResource|Type)|Items(?:DisabledFolder(?:Icon|Type)|FolderIcon)))|ti(?:cTextDialogItem|oneryFolderType))|d(?:AlertDoNot(?:AnimateOn(?:Cancel|Default|Other)|CloseOnHelp|DisposeSheet)|C(?:FStringAlertVersion(?:One|Two)|ancelItemIndex)|OkItemIndex)|ereoMixerParam_(?:P(?:an|ost(?:AveragePower|PeakHoldLevel)|re(?:AveragePower|PeakHoldLevel))|Volume)|illIdle|o(?:p(?:Icon|pedThreadState)|red(?:BasicWindowDescriptionID|Window(?:PascalTitleID|SystemTag|TitleCFStringID)))|yl(?:eOptionsType|isticAlt(?:E(?:ight(?:O(?:ffSelector|nSelector)|eenO(?:ffSelector|nSelector))|levenO(?:ffSelector|nSelector))|F(?:i(?:fteenO(?:ffSelector|nSelector)|veO(?:ffSelector|nSelector))|our(?:O(?:ffSelector|nSelector)|teenO(?:ffSelector|nSelector)))|Nine(?:O(?:ffSelector|nSelector)|teenO(?:ffSelector|nSelector))|OneO(?:ffSelector|nSelector)|S(?:even(?:O(?:ffSelector|nSelector)|teenO(?:ffSelector|nSelector))|ix(?:O(?:ffSelector|nSelector)|teenO(?:ffSelector|nSelector)))|T(?:enO(?:ffSelector|nSelector)|h(?:irteenO(?:ffSelector|nSelector)|reeO(?:ffSelector|nSelector))|w(?:e(?:lveO(?:ffSelector|nSelector)|ntyO(?:ffSelector|nSelector))|oO(?:ffSelector|nSelector)))|ernativesType)))|u(?:b(?:jectKCItemAttr|stituteVerticalFormsO(?:ffSelector|nSelector))|p(?:eriorsSelector|ports(?:FileTranslation|ScrapTranslation))|spend(?:Demand|Re(?:quest|voke)|Wake(?:ToDoze|Up)))|washAlternatesO(?:ffSelector|nSelector)|y(?:m(?:Link(?:Creator|FileType)|bolLigaturesO(?:ffSelector|nSelector))|s(?:SWTooOld|tem(?:ControlPanelFolderType|D(?:esktopFolderType|omain)|E(?:ventKCEventMask|xtensionDisabledFolder(?:Icon|Type))|Folder(?:AliasType|Icon(?:Resource)?|Type)|IconsCreator|KCEvent|P(?:PDDomain|r(?:eferencesFolderType|ocess))|ResFile|S(?:ound(?:ID_(?:FlashScreen|UserPreferredAlert|Vibrate)|sFolderType)|uitcaseIcon)|TrashFolderType))))|T(?:EC(?:A(?:dd(?:F(?:allbackInterrupt(?:Bit|Mask)|orceASCIIChanges(?:Bit|Mask))|TextRunHeuristics(?:Bit|Mask))|rrayFullErr|vailable(?:EncodingsResType|SniffersResType))|B(?:adTextRunErr|ufferBelowMinimumSizeErr)|C(?:hinesePluginSignature|o(?:nversionInfoResType|rruptConverterErr))|Di(?:rectionErr|sable(?:Fallbacks(?:Bit|Mask)|LooseMappings(?:Bit|Mask)))|FallbackTextLengthFix(?:Bit|Mask)|GlobalsUnavailableErr|I(?:n(?:completeElementErr|foCurrentFormat|ternetName(?:DefaultUsageMask|StrictUsageMask|TolerantUsageMask|sResType))|temUnavailableErr)|JapanesePluginSignature|K(?:eepInfoFix(?:Bit|Mask)|oreanPluginSignature)|M(?:ailEncodingsResType|issingTableErr)|N(?:eedFlushStatus|oConversionPathErr)|OutputBufferFullStatus|P(?:artialCharErr|lugin(?:Creator|DispatchTable(?:CurrentVersion|Version1(?:_(?:1|2))?)|ManyToOne|OneTo(?:Many|One)|SniffObj|Type)|referredEncodingFix(?:Bit|Mask))|ResourceID|S(?:ignature|ubTextEncodingsResType)|T(?:able(?:ChecksumErr|FormatErr)|ext(?:RunBitClearFix(?:Bit|Mask)|ToUnicodeScanFix(?:Bit|Mask)))|U(?:n(?:icodePluginSignature|mappableElementErr)|sedFallbacksStatus)|WebEncodingsResType|_MIBEnumDontCare)|MTaskActive|RAK(?:CurrentVersion|Tag|UniformFormat)|SM(?:15Version|2(?:0Version|2Version|3Version|4Version)|Doc(?:Access(?:EffectiveRangeAttribute(?:Bit)?|FontSizeAttribute(?:Bit)?)|ument(?:EnabledInputSourcesPropertyTag|Input(?:ModePropertyTag|SourceOverridePropertyTag)|Property(?:SupportGlyphInfo|UnicodeInputWindow)|RefconPropertyTag|Support(?:DocumentAccessPropertyTag|GlyphInfoPropertyTag)|T(?:SMTEPropertyTag|extServicePropertyTag)|U(?:nicode(?:InputWindowPropertyTag|PropertyTag)|seFloatingWindowPropertyTag)|WindowLevelPropertyTag))|Hilite(?:BlockFillText|C(?:aretPosition|onvertedText)|NoHilite|OutlineText|RawText|Selected(?:ConvertedText|RawText|Text))|InsideOf(?:ActiveInputArea|Body)|OutsideOfBody|TEDocumentInterfaceType|Version)|XN(?:A(?:IFFFile|TSUI(?:Font(?:FeaturesAttribute|VariationsAttribute)|IsNotInstalledErr|Style(?:Continuous(?:Bit|Mask)|Size)?)|l(?:ign(?:CenterAction|LeftAction|RightAction)|lCountMask|readyInitializedErr|waysWrapAtViewEdge(?:Bit|Mask))|ttributeTagInvalidForRunErr|uto(?:Indent(?:O(?:ff|n)|StateTag)|Scroll(?:BehaviorTag|InsertionIntoView|Never|WhenInsertionVisible)|Wrap))|Ba(?:ckgroundTypeRGB|dDefaultFileTypeWarning)|C(?:annot(?:AddFrameErr|SetAutoIndentErr|TurnTSMOffWhenUsingUnicodeErr)|enter(?:Tab)?|hange(?:Font(?:Action|ColorAction|SizeAction)|StyleAction)|lear(?:Action|Th(?:eseFontFeatures|isControl))|o(?:lorContinuous(?:Bit|Mask)|pyNotAllowedInEchoModeErr)|utAction)|D(?:ataTypeNotAllowedErr|e(?:crementTypeSize|stinationRectKey)|isable(?:DragAndDrop(?:Bit|Mask|Tag)?|LayoutAndDraw(?:Tag)?|dFunctionalityErr)|o(?:FontSubstitution(?:Bit|Mask)?|NotInstallDragProcs(?:Bit|Mask)|nt(?:CareTypeS(?:ize|tyle)|Draw(?:CaretWhenInactive(?:Bit|Mask)?|SelectionWhenInactive(?:Bit|Mask)?)))|r(?:aw(?:CaretWhenInactive(?:Tag)?|GrowIcon(?:Bit|Mask)|Item(?:AllMask|Scrollbars(?:Bit|Mask)|Text(?:AndSelection(?:Bit|Mask)|Bit|Mask))|SelectionWhenInactive(?:Tag)?)|opAction))|En(?:able(?:DragAndDrop|LayoutAndDraw)|d(?:IterationErr|Offset)|tireWord(?:Bit|Mask))|F(?:l(?:attenMoviesTag|ush(?:Default|Left|Right))|o(?:nt(?:Continuous(?:Bit|Mask)|FeatureAction|SizeAttributeSize|VariationAction)|rceFullJust)|ullJust)|Horizontal(?:ScrollBarRectKey)?|I(?:OPrivilegesTag|gnoreCase(?:Bit|Mask)|llegalToCrossDataBoundariesErr|mageWithQD(?:Bit|Mask)|n(?:crementTypeSize|lineStateTag|valid(?:FrameIDErr|RunIndex)))|JustificationTag|L(?:eftT(?:ab|oRight)|in(?:eDirectionTag|k(?:NotPressed|Tracking|WasPressed)))|M(?:a(?:cOSEncoding|rginsTag)|o(?:nostyledText(?:Bit|Mask)|veAction)|ultiple(?:FrameType|StylesPerTextDocumentResType))|No(?:A(?:ppleEventHandlers(?:Bit|Mask)|utoWrap)|FontVariations|MatchErr|Selection(?:Bit|Mask)|TSMEver(?:Bit|Mask)|UserIOTag)|O(?:perationNotAllowedErr|utsideOf(?:FrameErr|LineErr))|Pa(?:geFrameType|steAction)|QDFont(?:ColorAttribute(?:Size)?|FamilyIDAttribute(?:Size)?|NameAttribute(?:Size)?|S(?:izeAttribute(?:Size)?|tyleAttribute(?:Size)?))|R(?:e(?:ad(?:Only(?:Bit|Mask)?|Write)|fConTag|startAppleEventHandlers(?:Bit|Mask))|i(?:chTextFormatData|ghtT(?:ab|oLeft))|un(?:Count(?:Bit|Mask)|IndexOutofBoundsErr))|S(?:aveStylesAsSTYLResource(?:Bit|Mask)|crollUnitsIn(?:Lines|Pixels|ViewRects)|election(?:O(?:ff|n)|StateTag)|how(?:End|Start|Window(?:Bit|Mask))|i(?:ngle(?:L(?:evelUndoTag|ineOnly(?:Bit|Mask))|StylePerTextDocumentResType)|zeContinuous(?:Bit|Mask))|omeOrAllTagsInvalidForRunErr|t(?:artOffset|yleContinuous(?:Bit|Mask))|upport(?:EditCommand(?:Processing|Updating)|FontCommand(?:Processing|Updating)|SpellCheckCommand(?:Processing|Updating))|ystemDefaultEncoding)|T(?:abSettingsTag|ext(?:Data|E(?:ditStyleFrameType|ncodingAttribute(?:Size)?)|File|InputCount(?:Bit|Mask)|RectKey|ensionFile)|ypingAction)|U(?:RLAttribute|n(?:doLastAction|icode(?:Encoding|Text(?:Data|File)))|se(?:Bottomline|C(?:arbonEvents|urrentSelection)|EncodingWordRules(?:Bit|Mask)|Inline|QDforImaging(?:Bit|Mask)|ScriptDefaultValue|rCanceledOperationErr))|V(?:ertical(?:ScrollBarRectKey)?|i(?:ewRectKey|sibilityTag))|W(?:ant(?:HScrollBar(?:Bit|Mask)|VScrollBar(?:Bit|Mask))|illDefaultTo(?:ATSUI(?:Bit|Mask)|CarbonEvent(?:Bit|Mask))|ordWrapStateTag))|a(?:bCharCode|llCapsSelector|sk(?:CreationException|TerminationException))|e(?:mporary(?:FolderType|ItemsIn(?:CacheDataFolderType|UserDomainFolderType))|xt(?:Center|Encoding(?:ANSEL|B(?:aseName|ig5(?:_(?:E|HKSCS_1999))?)|CNS_11643_92_P(?:1|2|3)|D(?:OS(?:Arabic|BalticRim|C(?:anadianFrench|hinese(?:Simplif|Trad)|yrillic)|Greek(?:1|2)?|Hebrew|Icelandic|Japanese|Korean|Latin(?:1|2|US)|Nordic|Portuguese|Russian|T(?:hai|urkish))|efault(?:Format|Variant))|E(?:BCDIC_(?:CP037|LatinCore|US)|UC_(?:CN|JP|KR|TW))|F(?:ormatName|ullName)|GB(?:K_95|_(?:18030_200(?:0|5)|2312_80))|HZ_GB_2312|ISO(?:10646_1993|Latin(?:1(?:0)?|2|3|4|5|6|7|8|9|Arabic|Cyrillic|Greek|Hebrew)|_2022_(?:CN(?:_EXT)?|JP(?:_(?:1|2|3))?|KR))|JIS_(?:C6226_78|X02(?:0(?:1_76|8_(?:83|90))|1(?:2_90|3_MenKuTen)))|K(?:OI8_(?:R|U)|SC_5601_(?:87|92_Johab))|M(?:ac(?:Ar(?:abic|menian)|B(?:engali|urmese)|C(?:e(?:ltic|ntralEurRoman)|hinese(?:Simp|Trad)|roatian|yrillic)|D(?:evanagari|ingbats)|E(?:astEurRoman|thiopic|xtArabic)|Farsi|G(?:aelic|e(?:ez|orgian)|reek|u(?:jarati|rmukhi))|H(?:FS|ebrew)|I(?:celandic|nuit)|Japanese|K(?:annada|eyboardGlyphs|hmer|orean)|Laotian|M(?:alayalam|ongolian)|Oriya|R(?:Symbol|oman(?:Latin1|ian)?)|S(?:i(?:mpChinese|nhalese)|ymbol)|T(?:amil|elugu|hai|ibetan|radChinese|urkish)|U(?:krainian|ni(?:code|nterp))|V(?:T100|ietnamese))|ultiRun)|NextStep(?:Japanese|Latin)|ShiftJIS(?:_X0213(?:_00)?)?|U(?:S_ASCII|n(?:icode(?:Default|V(?:1(?:0_0|1_0|2_1|_1)|2_(?:0|1)|3_(?:0|1|2)|4_0|5_(?:0|1)|6_(?:0|1|3)|7_0|8_0|9_0))|known))|V(?:ISCII|ariantName)|Windows(?:A(?:NSI|rabic)|BalticRim|Cyrillic|Greek|Hebrew|KoreanJohab|Latin(?:1|2|5)|Vietnamese)|sFolder(?:Icon|Type))|Flush(?:Default|Left|Right)|LanguageDontCare|MalformedInputErr|RegionDontCare|S(?:criptDontCare|ervice(?:Class|DocumentInterfaceType|InputModePropertyTag|JaTypingMethodPropertyTag)?|pacingType)|ToSpeech(?:SynthType|Voice(?:BundleType|FileType|Type))|Un(?:definedElementErr|supportedEncodingErr)))|h(?:eme(?:A(?:ctive(?:Alert(?:BackgroundBrush|TextColor)|BevelButtonTextColor|D(?:ialog(?:BackgroundBrush|TextColor)|ocumentWindowTitleTextColor)|M(?:enuItemTextColor|o(?:delessDialog(?:BackgroundBrush|TextColor)|vableModalWindowTitleTextColor))|P(?:lacardTextColor|opup(?:ArrowBrush|ButtonTextColor|LabelTextColor|WindowTitleColor)|ushButtonTextColor)|RootMenuTextColor|ScrollBarDelimiterBrush|UtilityWindow(?:BackgroundBrush|TitleTextColor)|WindowHeaderTextColor)|dornment(?:Arrow(?:Do(?:ubleArrow|wnArrow)|LeftArrow|RightArrow|UpArrow)|D(?:efault|rawIndicatorOnly)|Focus|Header(?:Button(?:LeftNeighborSelected|NoS(?:hadow|ortArrow)|RightNeighborSelected|S(?:hadowOnly|ortUp))|MenuButton)|No(?:Shadow|ne)|RightToLeft|ShadowOnly)|l(?:ert(?:HeaderFont|Window)|iasArrowCursor)|pp(?:earanceFileNameTag|l(?:eGuideCoachmarkBrush|icationFont))|rrow(?:3pt|5pt|7pt|9pt|Button(?:Mini|Small)?|Cursor|Down|Left|Right|Up))|B(?:ackground(?:ListViewWindowHeader|Metal|Placard|SecondaryGroupBox|TabPane|WindowHeader)|evelButton(?:Inset|Large|Medium|Small)?|ottom(?:InsideArrowPressed|OutsideArrowPressed|TrackPressed)|rush(?:A(?:ctiveAreaFill|l(?:ertBackground(?:Active|Inactive)|ternatePrimaryHighlightColor)|ppleGuideCoachmark)|B(?:evel(?:Active(?:Dark|Light)|Inactive(?:Dark|Light))|lack|utton(?:Active(?:Dark(?:Highlight|Shadow)|Light(?:Highlight|Shadow))|F(?:ace(?:Active|Inactive|Pressed)|rame(?:Active|Inactive))|Inactive(?:Dark(?:Highlight|Shadow)|Light(?:Highlight|Shadow))|Pressed(?:Dark(?:Highlight|Shadow)|Light(?:Highlight|Shadow))))|ChasingArrows|D(?:ialogBackground(?:Active|Inactive)|ocumentWindowBackground|ra(?:gHilite|werBackground))|F(?:inderWindowBackground|ocusHighlight)|IconLabelBackground(?:Selected)?|ListView(?:Background|ColumnDivider|EvenRowBackground|OddRowBackground|S(?:eparator|ortColumnBackground))|M(?:enuBackground(?:Selected)?|o(?:delessDialogBackground(?:Active|Inactive)|vableModalBackground))|NotificationWindowBackground|P(?:assiveAreaFill|opupArrow(?:Active|Inactive|Pressed)|rimaryHighlightColor)|S(?:crollBarDelimiter(?:Active|Inactive)|econdaryHighlightColor|heetBackground(?:Opaque|Transparent)?|taticAreaFill)|ToolbarBackground|UtilityWindowBackground(?:Active|Inactive)|White)|utton(?:Mixed|O(?:ff|n)))|C(?:h(?:asingArrowsBrush|eckBox(?:C(?:heckMark|lassicX)|Mini|Small)?)|losedHandCursor|o(?:mboBox(?:Mini|Small)?|nt(?:extualMenuArrowCursor|rolSoundsMask)|pyArrowCursor|unting(?:DownHandCursor|Up(?:AndDownHandCursor|HandCursor)))|rossCursor|u(?:rrentPortFont|stomThemesFileType))|D(?:ataFileType|blClickCollapseTag|e(?:faultAdornment|sktopP(?:attern(?:NameTag|Tag)|icture(?:Ali(?:asTag|gnmentTag)|NameTag)))|i(?:alogWindow|s(?:abled(?:MenuItemTextColor|RootMenuTextColor)|closure(?:Button|Down|Left|Right|Triangle)))|ocumentWindow(?:BackgroundBrush)?|ra(?:g(?:HiliteBrush|Sound(?:Dragging|Grow(?:UtilWindow|Window)|Move(?:Alert|Dialog|Icon|UtilWindow|Window)|None|S(?:crollBar(?:Arrow(?:Decreasing|Increasing)|Ghost|Thumb)|lider(?:Ghost|Thumb))))|w(?:IndicatorOnly|erWindow)))|E(?:mphasizedSystemFont|xamplePictureIDTag)|F(?:inder(?:SoundsMask|WindowBackgroundBrush)|ocus(?:Adornment|HighlightBrush))|Grow(?:Down|Left|Right|Up)|HighlightColor(?:NameTag|Tag)|I(?:BeamCursor|conLabel(?:BackgroundBrush|TextColor)|n(?:active(?:Alert(?:BackgroundBrush|TextColor)|BevelButtonTextColor|D(?:ialog(?:BackgroundBrush|TextColor)|ocumentWindowTitleTextColor)|Mo(?:delessDialog(?:BackgroundBrush|TextColor)|vableModalWindowTitleTextColor)|P(?:lacardTextColor|opup(?:ArrowBrush|ButtonTextColor|LabelTextColor|WindowTitleColor)|ushButtonTextColor)|ScrollBarDelimiterBrush|UtilityWindow(?:BackgroundBrush|TitleTextColor)|WindowHeaderTextColor)|cDecButton(?:Mini|Small)?|determinateBar(?:Large|M(?:edium|ini))?))|L(?:a(?:belFont|rge(?:BevelButton|IndeterminateBar|ProgressBar|RoundButton|TabHeight(?:Max)?))|eft(?:InsideArrowPressed|OutsideArrowPressed|TrackPressed)|ist(?:HeaderButton|View(?:BackgroundBrush|S(?:eparatorBrush|ortColumnBackgroundBrush)|TextColor)))|M(?:e(?:dium(?:BevelButton|IndeterminateBar|ProgressBar|S(?:crollBar|lider))|nu(?:Active|Bar(?:Inactive|Normal|Selected)|Disabled|Item(?:A(?:lignRight|t(?:Bottom|Top))|CmdKeyFont|Font|H(?:asIcon|ier(?:Background|archical))|MarkFont|NoBackground|P(?:lain|opUpBackground)|Scroll(?:DownArrow|UpArrow))|S(?:elected|oundsMask|quareMenuBar)|T(?:itleFont|ype(?:Hierarchical|Inactive|P(?:opUp|ullDown))))|tric(?:B(?:estListHeaderHeight|uttonRounded(?:Height|RecessedHeight))|C(?:heckBox(?:GlyphHeight|Height|Width)|omboBox(?:Large(?:BottomShadowOffset|DisclosureWidth|RightShadowOffset)|Mini(?:BottomShadowOffset|DisclosureWidth|RightShadowOffset)|Small(?:BottomShadowOffset|DisclosureWidth|RightShadowOffset)))|Disclosure(?:Button(?:Height|Size|Width)|Triangle(?:Height|Width))|EditText(?:FrameOutset|Whitespace)|FocusRectOutset|HSlider(?:Height|Tick(?:Height|Offset))|ImageWellThickness|L(?:arge(?:ProgressBarThickness|RoundButtonSize|Tab(?:CapsWidth|Height))|i(?:st(?:BoxFrameOutset|HeaderHeight)|ttleArrows(?:Height|Mini(?:Height|Width)|Small(?:Height|Width)|Width)))|M(?:enu(?:ExcludedMarkColumnWidth|I(?:conTrailingEdgeMargin|ndentWidth)|Mark(?:ColumnWidth|Indent)|Text(?:LeadingEdgeMargin|TrailingEdgeMargin))|ini(?:CheckBox(?:Height|Width)|DisclosureButton(?:Height|Width)|HSlider(?:Height|MinThumbWidth|Tick(?:Height|Offset))|P(?:opupButtonHeight|u(?:llDownHeight|shButtonHeight))|RadioButton(?:Height|Width)|Tab(?:CapsWidth|FrameOverlap|Height|Overlap)|VSlider(?:MinThumbHeight|Tick(?:Offset|Width)|Width)))|NormalProgressBarThickness|P(?:aneSplitterHeight|opupButtonHeight|r(?:imaryGroupBoxContentInset|ogressBar(?:ShadowOutset|Thickness))|u(?:llDownHeight|shButtonHeight))|R(?:adioButton(?:GlyphHeight|Height|Width)|e(?:levanceIndicatorHeight|sizeControlHeight)|ound(?:ButtonSize|TextField(?:Content(?:Height|Inset(?:Bottom|Left|Right|Top|WithIcon(?:Left|Right)))|MiniContent(?:Height|Inset(?:Bottom|Left|Right|Top|WithIcon(?:Left|Right)))|SmallContent(?:Height|Inset(?:Bottom|Left|Right|Top|WithIcon(?:Left|Right))))))|S(?:crollBar(?:MinThumb(?:Height|Width)|Overlap|Width)|e(?:condaryGroupBoxContentInset|paratorSize)|liderMinThumb(?:Height|Width)|mall(?:CheckBox(?:Height|Width)|DisclosureButton(?:Height|Width)|HSlider(?:Height|MinThumbWidth|Tick(?:Height|Offset))|P(?:aneSplitterHeight|opupButtonHeight|rogressBar(?:ShadowOutset|Thickness)|u(?:llDownHeight|shButtonHeight))|R(?:adioButton(?:Height|Width)|esizeControlHeight)|ScrollBar(?:MinThumb(?:Height|Width)|Width)|Tab(?:CapsWidth|FrameOverlap|Height|Overlap)|VSlider(?:MinThumbHeight|Tick(?:Offset|Width)|Width)))|T(?:ab(?:FrameOverlap|IndentOrStyle|Overlap)|extured(?:PushButtonHeight|SmallPushButtonHeight)|itleBarControlsHeight)|VSlider(?:Tick(?:Offset|Width)|Width)))|ini(?:CheckBox|IndeterminateBar|ProgressBar|RadioButton|S(?:crollBar|lider|ystemFont))|ovable(?:AlertWindow|DialogWindow))|N(?:ameTag|o(?:Adornment|Sounds|rmal(?:CheckBox|RadioButton)|tAllowedCursor))|OpenHandCursor|P(?:l(?:a(?:inDialogWindow|tinumFileType)|usCursor)|o(?:intingHandCursor|ofCursor|pup(?:Button(?:Mini|Normal|Small)?|Tab(?:CenterOn(?:Offset|Window)|NormalPosition)|Window))|r(?:essed(?:BevelButtonTextColor|P(?:lacardTextColor|opup(?:ArrowBrush|ButtonTextColor)|ushButtonTextColor))|ogressBar(?:Large|M(?:edium|ini))?)|ushButton(?:Font|Inset(?:Small)?|Mini|Normal|Small|Textured(?:Small)?)?)|R(?:adioButton(?:Mini|Small)?|e(?:levanceBar|size(?:DownCursor|Left(?:Cursor|RightCursor)|RightCursor|Up(?:Cursor|DownCursor)))|ight(?:InsideArrowPressed|OutsideArrowPressed|T(?:oLeftAdornment|rackPressed))|ound(?:Button(?:Help|Large)?|edBevelButton))|S(?:avvyMenuResponse|crollBar(?:Arrow(?:StyleTag|s(?:LowerRight|Single))|M(?:edium|ini)|Small|Thumb(?:Normal|Proportional|StyleTag))?|elected(?:MenuItemTextColor|RootMenuTextColor)|h(?:adowDialogWindow|eetWindow)|lider(?:M(?:edium|ini)|Small)?|m(?:all(?:BevelButton|CheckBox|EmphasizedSystemFont|RadioButton|S(?:crollBar|lider|ystemFont(?:Tag)?)|TabHeight(?:Max)?)|oothFont(?:EnabledTag|MinSizeTag))|ound(?:Alert(?:Close|Open)|B(?:alloon(?:Close|Open)|evel(?:E(?:nter|xit)|Press|Release)|utton(?:E(?:nter|xit)|Press|Release))|C(?:ancelButton(?:E(?:nter|xit)|Press|Release)|heckbox(?:E(?:nter|xit)|Press|Release)|opyDone)|D(?:efaultButton(?:E(?:nter|xit)|Press|Release)|i(?:alog(?:Close|Open)|s(?:closure(?:E(?:nter|xit)|Press|Release)|k(?:Eject|Insert)))|ragTarget(?:Drop|Hilite|Unhilite))|EmptyTrash|FinderDragO(?:ffIcon|nIcon)|L(?:aunchApp|ittleArrow(?:Dn(?:Press|Release)|E(?:nter|xit)|Up(?:Press|Release)))|M(?:askTag|enu(?:Close|Item(?:Hilite|Release)|Open))|N(?:ewItem|one)|Popup(?:E(?:nter|xit)|Press|Release|Window(?:Close|Open))|R(?:adio(?:E(?:nter|xit)|Press|Release)|e(?:ceiveDrop|solveAlias))|S(?:croll(?:Arrow(?:E(?:nter|xit)|Press|Release)|EndOfTrack|TrackPress)|electItem|lider(?:EndOfTrack|TrackPress))|T(?:ab(?:E(?:nter|xit)|Pressed|Release)|rack(?:FileType|NameTag))|UtilWin(?:C(?:lose(?:E(?:nter|xit)|Press|Release)|ollapse(?:E(?:nter|xit)|Press|Release))|DragBoundary|Zoom(?:E(?:nter|xit)|Press|Release)|dow(?:Activate|C(?:lose|ollapse(?:Down|Up))|Open|Zoom(?:In|Out)))|Window(?:Activate|C(?:lose(?:E(?:nter|xit)|Press|Release)?|ollapse(?:Down|E(?:nter|xit)|Press|Release|Up))|DragBoundary|Open|Zoom(?:E(?:nter|xit)|In|Out|Press|Release))|sEnabledTag)|p(?:ecifiedFont|inningCursor)|tate(?:Active|Disabled|Inactive|Pressed(?:Down|Up)?|Rollover|Unavailable(?:Inactive)?)|ystemFont(?:Detail(?:Emphasized)?|Tag)?)|T(?:ab(?:East|Front(?:Inactive|Unavailable)?|No(?:nFront(?:Inactive|Pressed|Unavailable)?|rth)|PaneOverlap|South|West)|extColor(?:Alert(?:Active|Inactive)|B(?:evelButton(?:Active|Inactive|Pressed|Sticky(?:Active|Inactive))|lack)|D(?:ialog(?:Active|Inactive)|ocumentWindowTitle(?:Active|Inactive))|IconLabel(?:Selected)?|ListView|M(?:enuItem(?:Active|Disabled|Selected)|o(?:delessDialog(?:Active|Inactive)|vableModalWindowTitle(?:Active|Inactive)))|Notification|P(?:lacard(?:Active|Inactive|Pressed)|opup(?:Button(?:Active|Inactive|Pressed)|Label(?:Active|Inactive)|WindowTitle(?:Active|Inactive))|ushButton(?:Active|Inactive|Pressed))|RootMenu(?:Active|Disabled|Selected)|SystemDetail|Tab(?:Front(?:Active|Inactive)|NonFront(?:Active|Inactive|Pressed))|UtilityWindowTitle(?:Active|Inactive)|W(?:hite|indowHeader(?:Active|Inactive)))|humb(?:Downward|P(?:lain|ressed)|Upward)|o(?:olbarFont|p(?:InsideArrowPressed|OutsideArrowPressed|TrackPressed))|rack(?:Active|Disabled|H(?:asFocus|ideTrack|orizontal)|Inactive|No(?:ScrollBarArrows|thingToScroll)|RightToLeft|ShowThumb|ThumbRgnIsNotGhost))|U(?:serDefinedTag|tility(?:SideWindow|Window(?:TitleFont)?))|V(?:ariant(?:BaseTintTag|NameTag)|iewsFont(?:SizeTag|Tag)?)|W(?:atchCursor|i(?:dget(?:C(?:loseBox|ollapseBox)|DirtyCloseBox|ToolbarButton|ZoomBox)|ndow(?:Has(?:C(?:loseBox|ollapseBox)|Dirty|FullZoom|Grow|HorizontalZoom|T(?:itleText|oolbarButton)|VerticalZoom)|IsCollapsed|SoundsMask|TitleFont)))|sFolderType)|i(?:nkCStackBased|rdWidth(?:NumbersSelector|TextSelector))|umbnail(?:32BitData|8BitMask))|i(?:ckScale|le(?:IconVariant|dOnScreen)|mePitchParam_(?:EffectBlend|Pitch|Rate)|tlingCapsSelector)|oo(?:ManyIOWindowsErr|lbar(?:A(?:dvancedIcon|pplicationsFolderIcon)|CustomizeIcon|D(?:e(?:leteIcon|sktopFolderIcon)|o(?:cumentsFolderIcon|wnloadsFolderIcon))|FavoritesIcon|HomeIcon|InfoIcon|L(?:abelsIcon|ibraryFolderIcon)|M(?:ovieFolderIcon|usicFolderIcon)|P(?:icturesFolderIcon|ublicFolderIcon)|SitesFolderIcon|UtilitiesFolderIcon|WindowClass))|r(?:a(?:c(?:eException|kMouseLocationOption(?:DontConsumeMouseUp|IncludeScrollWheel))|ditional(?:Alt(?:F(?:iveSelector|ourSelector)|OneSelector|T(?:hreeSelector|woSelector))|CharactersSelector|NamesCharactersSelector)|ns(?:codingCompositionO(?:ffSelector|nSelector)|form(?:Disabled|Label(?:1|2|3|4|5|6|7)|None|O(?:ffline|pen)|Selected(?:Disabled|O(?:ffline|pen))?)|l(?:at(?:e(?:Get(?:FileTranslationList|ScrapTranslationList(?:ConsideringData)?|TranslatedFilename)|Identify(?:File|Scrap)|Translate(?:File|Scrap))|ion(?:DataTranslation|FileTranslation|ScrapProgressDialogID)|orCanGenerateFilename)|iterationType)|parentEncod(?:edPixel|ing(?:Shift)?))|pException|sh(?:FolderType|Icon(?:Resource)?))|ueType(?:F(?:latFontIcon|ontIcon)|MultiFlatFontIcon)|yAuthenticate)|wo(?:ByteCode|WayEncryptPassword)|yp(?:eKCItemAttr|ographicExtrasType))|U(?:AZoomFocusType(?:InsertionPoint|Other)|C(?:BidiCat(?:ArabicNumber|B(?:lockSeparator|oundaryNeutral)|CommonNumberSeparator|EuroNumber(?:Separator|Terminator)?|FirstStrongIsolate|LeftRight(?:Embedding|Isolate|Override)?|No(?:nSpacingMark|tApplicable)|OtherNeutral|PopDirectional(?:Format|Isolate)|RightLeft(?:Arabic|Embedding|Isolate|Override)?|SegmentSeparator|Whitespace)|C(?:harPropType(?:BidiCategory|CombiningClass|DecimalDigitValue|GenlCategory)|ollate(?:C(?:aseInsensitiveMask|omposeInsensitiveMask)|Di(?:acritInsensitiveMask|gits(?:AsNumberMask|OverrideMask))|PunctuationSignificantMask|StandardOptions|Type(?:HFSExtended|Mask|S(?:hiftBits|ourceMask))|WidthInsensitiveMask))|GenlCat(?:Letter(?:Lowercase|Modifier|Other|Titlecase|Uppercase)|Mark(?:Enclosing|NonSpacing|SpacingCombining)|Number(?:DecimalDigit|Letter|Other)|Other(?:Control|Format|NotAssigned|PrivateUse|Surrogate)|Punct(?:C(?:lose|onnector)|Dash|FinalQuote|InitialQuote|O(?:pen|ther))|S(?:eparator(?:Line|Paragraph|Space)|ymbol(?:Currency|M(?:ath|odifier)|Other)))|HighSurrogateRange(?:End|Start)|Key(?:Action(?:AutoKey|D(?:isplay|own)|Up)|Layout(?:FeatureInfoFormat|HeaderFormat)|ModifiersToTableNumFormat|Output(?:GetIndexMask|S(?:equenceIndexMask|tateIndexMask)|TestForIndexMask)|S(?:equenceDataIndexFormat|tate(?:Entry(?:RangeFormat|TerminalFormat)|RecordsIndexFormat|TerminatorsFormat))|T(?:oCharTableIndexFormat|ranslateNoDeadKeys(?:Bit|Mask)))|LowSurrogateRange(?:End|Start)|OutputBufferTooSmall|T(?:S(?:Direction(?:Next|Previous)|NoKeysAddedToObjectErr|Options(?:DataIsOrderedMask|NoneMask|ReleaseStringMask)|SearchListErr)|extBreak(?:C(?:harMask|lusterMask)|GoBackwardsMask|IterateMask|L(?:eadingEdgeMask|ineMask|ocatorMissingType)|ParagraphMask|WordMask)|oken(?:NotFound|izer(?:IterationFinished|UnknownLang))|ypeSelectMaxListSize))|I(?:Mode(?:All(?:Hidden|Suppressed)|Content(?:Hidden|Suppressed)|Normal)|Option(?:A(?:nimateMenuBar|utoShowMenuBar)|Disable(?:AppleMenu|ForceQuit|Hide|MenuBarTransparency|ProcessSwitch|SessionTerminate)))|RL(?:68kNotSupportedError|A(?:bort(?:Initiated(?:Event|Mask)|ingState)|ccessNotAvailableError|ll(?:BufferEventsMask|EventsMask|NonBufferEventsMask)|uthenticationError)|BinHexFileFlag|Co(?:mpleted(?:Event(?:Mask)?|State)|nnectingState)|D(?:ataAvailable(?:Event(?:Mask)?|State)|e(?:binhexOnlyFlag|stinationExistsError)|i(?:rectoryListingFlag|splay(?:AuthFlag|ProgressFlag))|o(?:Not(?:DeleteOnErrorFlag|TryAnonymousFlag)|wnloading(?:Event|Mask|State)))|E(?:rrorOccurred(?:Event(?:Mask)?|State)|x(?:pand(?:AndVerifyFlag|FileFlag)|tensionFailureError))|FileEmptyError|I(?:n(?:itiat(?:edEvent(?:Mask)?|ingState)|valid(?:C(?:allError|onfigurationError)|URL(?:Error|ReferenceError)))|sDirectoryHintFlag)|LookingUpHostState|N(?:oAutoRedirectFlag|ullState)|P(?:er(?:centEvent(?:Mask)?|iodicEvent(?:Mask)?)|ro(?:gressAlreadyDisplayedError|perty(?:BufferTooSmallError|ChangedEvent(?:Mask)?|NotYetKnownError)))|Re(?:placeExistingFlag|s(?:ervedFlag|ourceFound(?:Event(?:Mask)?|State)|umeDownloadFlag))|S(?:erverBusyError|ystemEvent(?:Mask)?)|TransactionComplete(?:Event(?:Mask)?|State)|U(?:n(?:knownPropertyError|s(?:ettablePropertyError|upportedSchemeError))|pload(?:Flag|ing(?:Event|Mask|State))))|SB(?:A(?:bortedError|lreadyOpenErr)|B(?:adDispatchTable|itstufErr|uf(?:OvrRunErr|UnderRunErr))|C(?:RCErr|ompletionError)|D(?:ataToggleErr|evice(?:Busy|Disconnected|Err|NotSuspended|PowerProblem|Suspended))|EndpointStallErr|FlagsError|In(?:correctTypeErr|ternal(?:Err|Reserved(?:1(?:0)?|2|3|4|5|6|7|8|9))|validBuffer)|LinkErr|No(?:BandwidthError|De(?:lay|viceErr)|Err|Tran|t(?:Found|Handled|RespondingErr|Sent(?:1Err|2Err)))|O(?:utOfMemoryErr|verRunErr)|P(?:B(?:LengthError|VersionError)|IDCheckErr|ending|ipe(?:IdleError|StalledError)|ortDisabled)|Queue(?:Aborted|Full)|R(?:es(?:1Err|2Err)|qErr)|T(?:imedOut|ooMany(?:PipesErr|TransactionsErr))|Un(?:derRunErr|known(?:DeviceErr|InterfaceErr|Notification|PipeErr|RequestErr))|WrongPIDErr)|TC(?:DefaultOptions|OverflowErr|UnderflowErr)|YVY422PixelFormat|n(?:connectedSelector|icode(?:16BitFormat|32BitFormat|ByteOrderMark|C(?:anonical(?:CompVariant|DecompVariant)|ollationClass)|D(?:e(?:compositionType|faultDirection(?:Mask)?)|irectionality(?:Bits|Mask)|ocument(?:InterfaceType)?)|F(?:allback(?:Custom(?:First|Only)|Default(?:First|Only)|InterruptSafeMask|Sequencing(?:Bits|Mask))|orceASCIIRange(?:Bit|Mask))|HFSPlus(?:CompVariant|DecompVariant)|Keep(?:Info(?:Bit|Mask)|SameEncoding(?:Bit|Mask))|L(?:eftToRight(?:Mask)?|ooseMappings(?:Bit|Mask))|Ma(?:pLineFeedToReturn(?:Bit|Mask)|tch(?:Other(?:Base(?:Bit|Mask)|Format(?:Bit|Mask)|Variant(?:Bit|Mask))|Unicode(?:Base(?:Bit|Mask)|Format(?:Bit|Mask)|Variant(?:Bit|Mask)))|xDecomposedVariant)|No(?:Co(?:mp(?:atibilityVariant|osedVariant)|rporateVariant)|HalfwidthChars(?:Bit|Mask)|Subset|rmalizationForm(?:C|D)|t(?:AChar|FromInputMethod))|ObjectReplacement|R(?:eplacementChar|ightToLeft(?:Mask)?)|S(?:CSUFormat|tringUnterminated(?:Bit|Mask)|wappedByteOrderMark)|Text(?:BreakClass|Run(?:Bit|Heuristics(?:Bit|Mask)|Mask))|U(?:TF(?:16(?:BEFormat|Format|LEFormat)|32(?:BEFormat|Format|LEFormat)|7Format|8Format)|se(?:ExternalEncodingForm(?:Bit|Mask)|Fallbacks(?:Bit|Mask)|HFSPlusMapping|LatestMapping))|VerticalForm(?:Bit|Mask))|known(?:Exception|FSObjectIcon|Language|Script)|lock(?:KCEvent(?:Mask)?|StateKCStatus|edIcon)|mappedMemoryException|resolvablePageFaultException|supported(?:CardErr|FunctionErr|ModeErr|VsErr)|wrapKCItemAttr)|p(?:ArrowCharCode|date(?:A(?:E(?:TE|UT)|ctiveInputArea)|KCEvent(?:Mask)?)|per(?:AndLowerCaseSelector|Case(?:NumbersSelector|PetiteCapsSelector|SmallCapsSelector|Type)))|se(?:AtoB|B(?:estGuess|to(?:A|B))|CurrentISA|NativeISA|Pr(?:emadeThread|ofileIntent)|WidePositioning|r(?:D(?:ialogItem|omain)|FolderIcon|I(?:DiskIcon|con)|NameAndPasswordFlag|P(?:PDDomain|referredAlert)|SpecificTmpFolderType|sFolder(?:Icon|Type)))|tilit(?:iesFolder(?:Icon|Type)|yWindowClass))|V(?:CBFlags(?:H(?:FSPlusAPIs(?:Bit|Mask)|ardwareGone(?:Bit|Mask))|IdleFlush(?:Bit|Mask)|VolumeDirty(?:Bit|Mask))|K_(?:ANSI_(?:0|1|2|3|4|5|6|7|8|9|A|B(?:ackslash)?|C(?:omma)?|D|E(?:qual)?|F|G(?:rave)?|H|I|J|K(?:eypad(?:0|1|2|3|4|5|6|7|8|9|Clear|D(?:ecimal|ivide)|E(?:nter|quals)|M(?:inus|ultiply)|Plus))?|L(?:eftBracket)?|M(?:inus)?|N|O|P(?:eriod)?|Q(?:uote)?|R(?:ightBracket)?|S(?:emicolon|lash)?|T|U|V|W|X|Y|Z)|C(?:apsLock|o(?:mmand|ntrol))|D(?:elete|ownArrow)|E(?:nd|scape)|F(?:1(?:0|1|2|3|4|5|6|7|8|9)?|2(?:0)?|3|4|5|6|7|8|9|orwardDelete|unction)|H(?:elp|ome)|ISO_Section|JIS_(?:Eisu|K(?:ana|eypadComma)|Underscore|Yen)|LeftArrow|Mute|Option|Page(?:Down|Up)|R(?:eturn|ight(?:Arrow|Co(?:mmand|ntrol)|Option|Shift))|S(?:hift|pace)|Tab|UpArrow|Volume(?:Down|Up))|LibTag2|arispeedParam_Playback(?:Cents|Rate)|er(?:ifyKCItemAttr|tical(?:Constraint|FractionsSelector|PositionType|SubstitutionType|TabCharCode))|o(?:icesFolder(?:Icon|Type)|lume(?:KCItemAttr|RootFolderType|SettingsFolderType)))|W(?:akeToDoze|hereToEmptyTrashFolderType|i(?:d(?:ePosOffsetBit|getsFolderType)|ndow(?:A(?:ctivationScope(?:All|Independent|None)|lertP(?:osition(?:MainScreen|On(?:MainScreen|ParentWindow(?:Screen)?)|ParentWindow(?:Screen)?)|roc)|syncDragAttribute)|BoundsChange(?:OriginChanged|SizeChanged|User(?:Drag|Resize)|Zoom)|C(?:a(?:n(?:BeVisibleWithoutLoginAttribute|Collapse|DrawInCurrentPort|G(?:etWindowRegion|row)|MeasureTitle|SetupProxyDragImage|Zoom)|scade(?:On(?:MainScreen|ParentWindow(?:Screen)?)|StartAtParentWindowScreen))|enter(?:MainScreen|On(?:MainScreen|ParentWindow(?:Screen)?)|ParentWindow(?:Screen)?)|loseBox(?:Attribute|Rgn)|o(?:llapseBox(?:Attribute|Rgn)|mpositingAttribute|n(?:strain(?:AllowPartial|CalcOnly|M(?:ayResize|ove(?:Minimum|RegardlessOfFit))|StandardOptions|Use(?:SpecifiedBounds|TransitionWindow))|tentRgn)))|D(?:ef(?:HIView|ObjectClass|Proc(?:ID|Ptr|Type)|SupportsColorGrafPort|aultPosition|initionVersion(?:One|Two))|ialogDefProcResID|o(?:cument(?:DefProcResID|Proc)|esNotCycleAttribute)|ra(?:gRgn|wer(?:Clos(?:ed|ing)|Open(?:ing)?)))|Edge(?:Bottom|Default|Left|Right|Top)|F(?:adeTransitionEffect|loat(?:FullZoom(?:GrowProc|Proc)|GrowProc|HorizZoom(?:GrowProc|Proc)|Proc|Side(?:FullZoom(?:GrowProc|Proc)|GrowProc|HorizZoom(?:GrowProc|Proc)|Proc|VertZoom(?:GrowProc|Proc))|VertZoom(?:GrowProc|Proc))|rameworkScaledAttribute|ullZoom(?:Attribute|DocumentProc|GrowDocumentProc))|G(?:enieTransitionEffect|lobalPortRgn|ro(?:up(?:Attr(?:FixedLevel|HideOnCollapse|LayerTogether|MoveTogether|PositionFixed|S(?:elect(?:AsLayer|able)|haredActivation)|ZOrderFixed)|Contents(?:Re(?:curse|turnWindows)|Visible)|Level(?:Active|Inactive|Promoted))|w(?:DocumentProc|Rgn)))|H(?:as(?:RoundBottomBarCornersAttribute|TitleBar)|i(?:de(?:On(?:FullScreenAttribute|SuspendAttribute)|TransitionAction)|ghResolutionCapableAttribute)|oriz(?:Zoom(?:DocumentProc|GrowDocumentProc)|ontalZoomAttribute))|I(?:gnoreClicksAttribute|nWindowMenuAttribute|s(?:Alert|CollapsedState|Modal|Opaque))|L(?:atentVisible(?:AppHidden|Collapsed(?:Group|Owner)|F(?:loater|ullScreen)|Suspend)|iveResizeAttribute)|M(?:e(?:nuIncludeRotate|tal(?:Attribute|NoContentSeparatorAttribute))|o(?:dal(?:DialogProc|ity(?:AppModal|None|SystemModal|WindowModal))|v(?:able(?:AlertProc|Modal(?:DialogProc|GrowProc))|eTransitionAction))|sg(?:C(?:alculateShape|leanUp)|Dra(?:gHilite|w(?:Grow(?:Box|Outline)|InCurrentPort)?)|Get(?:Features|GrowImageRegion|Region)|HitTest|Initialize|M(?:easureTitle|odified)|S(?:etupProxyDragImage|tateChanged)))|No(?:A(?:ctivatesAttribute|ttributes)|ConstrainAttribute|Position|ShadowAttribute|TitleBarAttribute|UpdatesAttribute)|O(?:paque(?:ForEventsAttribute|Rgn)|verlayProc)|P(?:aintProcOptionsNone|lainDialogProc|ropertyPersistent)|Resiz(?:ableAttribute|eTransitionAction)|S(?:h(?:adowDialogProc|eet(?:Alert(?:DefProcResID|Proc)|DefProcResID|Proc|TransitionEffect)|owTransitionAction)|i(?:deTitlebarAttribute|mple(?:DefProcResID|FrameProc|Proc))|lideTransitionEffect|t(?:a(?:gger(?:MainScreen|ParentWindow(?:Screen)?)|ndard(?:DocumentAttributes|FloatingAttributes|HandlerAttribute)|teTitleChanged)|ructureRgn)|upports(?:DragHilite|GetGrowImageRegion|ModifiedBit))|T(?:exturedSquareCornersAttribute|itle(?:BarRgn|ProxyIconRgn|TextRgn)|oolbarButton(?:Attribute|Rgn))|U(?:nifiedTitleAndToolbarAttribute|pdateRgn|tility(?:DefProcResID|SideTitleDefProcResID))|Vert(?:Zoom(?:DocumentProc|GrowDocumentProc)|icalZoomAttribute)|WantsDisposeAtProcessDeath|Zoom(?:BoxRgn|TransitionEffect)|sLatin1(?:PalmVariant|StandardVariant)))|or(?:d(?:FinalSwashesO(?:ffSelector|nSelector)|InitialSwashesO(?:ffSelector|nSelector))|kgroupFolderIcon)|r(?:PermKCStatus|apKCItemAttr|ite(?:FailureErr|ProtectedErr|Reference)))|X(?:86(?:ISA|RTA)|Lib(?:Tag1|Version))|Y(?:UV(?:211PixelFormat|411PixelFormat|SPixelFormat|UPixelFormat)|V(?:U9PixelFormat|YU422PixelFormat))|Zoom(?:Accelerate|Decelerate|NoAcceleration)|administratorUser|e(?:rnel(?:A(?:lreadyFreeErr|sync(?:ReceiveLimitErr|SendLimitErr)|ttributeErr)|CanceledErr|DeletePermissionErr|Ex(?:ceptionErr|ecut(?:ePermissionErr|ionLevelErr))|I(?:DErr|n(?:UseErr|completeErr))|O(?:bjectExistsErr|ptionsErr)|PrivilegeErr|Re(?:adPermissionErr|turnValueErr)|T(?:erminatedErr|imeoutErr)|Un(?:recoverableErr|supportedErr)|WritePermissionErr)|y(?:32BitIcon|4BitIcon|8Bit(?:Icon|Mask)|A(?:E(?:A(?:djustMarksProc|ngle|rcAngle|ttaching)|B(?:aseAddr|estType|gnd(?:Color|Pattern)|ounds|ufferSize)|C(?:ellList|la(?:ssID|useOffsets)|o(?:lor(?:Table)?|mp(?:Operator|areProc)|ntainer|untProc)|ur(?:rentPoint|ve(?:Height|Width)))|D(?:a(?:shStyle|ta)|e(?:f(?:aultType|initionRect)|s(?:cType|iredClass|tination))|o(?:AntiAlias|Dithered|Rotate|Scale|Translate)|ragging)|E(?:ditionFileLoc|lements|ndPoint|rrorObject|vent(?:Class|ID))|F(?:i(?:l(?:e(?:Type)?|l(?:Color|Pattern))|xLength)|lip(?:Horizontal|Vertical)|o(?:nt|rmula))|G(?:etErrDescProc|raphicObjects)|H(?:iliteRange|omograph(?:Accent|DicInfo|Weight))|I(?:D|mageQuality|n(?:dex|sertHere))|Key(?:Data|Form(?:s)?|word)|L(?:A(?:Homograph|Morpheme(?:Bundle|Path)?)|aunchedAs(?:LogInItem|ServiceItem)|e(?:ftSide|vel)|ineArrow|ogical(?:Operator|Terms))|M(?:ark(?:Proc|TokenProc)|o(?:rpheme(?:PartOfSpeechCode|TextRange)|veView))|N(?:ame|e(?:wElementLoc|xtBody)|oAutoRouting)|O(?:bject(?:1|2|Class)?|ff(?:Styles|set)|nStyles)|P(?:MTable|OSTHeaderData|aram(?:Flags|eters)|en(?:Color|Pattern|Width)|i(?:nRange|x(?:MapMinus|elDepth))|o(?:int(?:List|Size)?|sition)|ro(?:p(?:Data|Flags|ID|ert(?:ies|y))|tection))|R(?:angeSt(?:art|op)|e(?:corderCount|gionClass|nderAs|pl(?:acing|yHeaderData)|questedType|sult(?:Info)?)|o(?:t(?:Point|ation)|wList))|S(?:aveOptions|c(?:ale|riptTag)|e(?:archText|rverInstance)|howWhere|t(?:art(?:Angle|Point)|yles)|uiteID)|T(?:SM(?:DocumentRefcon|EventRe(?:cord|f)|GlyphInfoArray|ScriptTag|Text(?:F(?:MFont|ont)|PointSize))|arget|e(?:st|xt(?:Color|Font|Line(?:Ascent|Height)|PointSize|S(?:ervice(?:Encoding|MacEncoding)|tyles))?)|he(?:Data|Text)|r(?:ans(?:ferMode|lation)|yAsStructGraf))|U(?:niformStyles|pdate(?:On|Range)|s(?:erTerm|ing))|Version|W(?:hoseRangeSt(?:art|op)|indow|ritingCode)|XMLRe(?:plyData|questData))|S(?:Arg|P(?:ositionalArgs|reposition(?:A(?:bo(?:ut|ve)|gainst|partFrom|round|sideFrom|t)|B(?:e(?:low|neath|side|tween)|y)|F(?:or|rom)|Given|Has|In(?:steadOf|to)?|O(?:n(?:to)?|utOf|ver)|Since|T(?:hr(?:ough|u)|o)|Un(?:der|til)|With(?:out)?))|Returning|SubroutineName|UserRecordFields)|c(?:ceptTimeoutAttr|tualSenderAuditToken)|dd(?:itionalHTTPHeaders|ressAttr)|ll|pp(?:HandledCoercion|leEventAttributesAttr))|C(?:loseAllWindows|o(?:deMask|ntextualMenu(?:Attributes|CommandID|Modifiers|Name|Submenu)))|D(?:CM(?:Field(?:Attributes|DefaultData|FindMethods|Name|T(?:ag|ype))|MaxRecordSize)|i(?:rectObject|s(?:ableAuthenticationAttr|poseTokenProc))|own(?:Mask)?|riveNumber)|E(?:rror(?:Code|Number|String)|v(?:ent(?:ClassAttr|IDAttr|SourceAttr)|tDev))|GlobalPositionList|HighLevel(?:Class|ID)|I(?:CEditPreferenceDestination|conAndMask|nteractLevelAttr)|Key(?:Code|board)?|Local(?:PositionList|Where)|M(?:enuI(?:D|tem)|i(?:ni(?:1BitMask|4BitIcon|8BitIcon)|s(?:cellaneous|sedKeywordAttr))|odifiers)|NewBounds|O(?:SA(?:Dialect(?:Code|LangCode|Name|ScriptCode)|Source(?:End|Start))|ldFinderItems|ptionalKeywordAttr|riginal(?:AddressAttr|Bounds))|Pr(?:eDispatch|ocessSerialNumber)|R(?:PCMethod(?:Name|Param(?:Order)?)|e(?:directedDocumentList|ply(?:PortAttr|RequestedAttr)|turnIDAttr))|S(?:OAP(?:Action|MethodNameSpace(?:URI)?|S(?:MD(?:Namespace(?:URI)?|Type)|chemaVersion|tructureMetaData))|R(?:Recognizer|Speech(?:Result|Status))|cszResource|e(?:lect(?:Proc|ion)|nder(?:A(?:ppl(?:escriptEntitlementsAttr|ication(?:IdentifierEntitlementAttr|Sandboxed))|uditTokenAttr)|E(?:GIDAttr|UIDAttr)|GIDAttr|PIDAttr|UIDAttr))|mall(?:32BitIcon|4BitIcon|8Bit(?:Icon|Mask)|IconAndMask)|ubjectAttr)|T(?:imeoutAttr|ransactionIDAttr)|U(?:p(?:Mask)?|ser(?:NameAttr|PasswordAttr))|W(?:he(?:n|re)|indow)|XMLDebuggingAttr))|fullPrivileges|i(?:Movie(?:FolderType|PlugInsFolderType|SoundEffectsFolderType)|o(?:AC(?:Access(?:BlankAccess(?:Bit|Mask)|Everyone(?:Read(?:Bit|Mask)|Search(?:Bit|Mask)|Write(?:Bit|Mask))|Group(?:Read(?:Bit|Mask)|Search(?:Bit|Mask)|Write(?:Bit|Mask))|Owner(?:Bit|Mask|Read(?:Bit|Mask)|Search(?:Bit|Mask)|Write(?:Bit|Mask))|User(?:Read(?:Bit|Mask)|Search(?:Bit|Mask)|Write(?:Bit|Mask)))|UserNo(?:MakeChanges(?:Bit|Mask)|SeeF(?:iles(?:Bit|Mask)|older(?:Bit|Mask))|tOwner(?:Bit|Mask)))|F(?:CB(?:FileLocked(?:Bit|Mask)|LargeFile(?:Bit|Mask)|Modified(?:Bit|Mask)|OwnClump(?:Bit|Mask)|Resource(?:Bit|Mask)|SharedWrite(?:Bit|Mask)|Write(?:Bit|Locked(?:Bit|Mask)|Mask))|lAttrib(?:CopyProt(?:Bit|Mask)|D(?:ataOpen(?:Bit|Mask)|ir(?:Bit|Mask))|FileOpen(?:Bit|Mask)|InShared(?:Bit|Mask)|Locked(?:Bit|Mask)|Mounted(?:Bit|Mask)|ResOpen(?:Bit|Mask)|SharePoint(?:Bit|Mask)))|VAtrb(?:DefaultVolume(?:Bit|Mask)|FilesOpen(?:Bit|Mask)|HardwareLocked(?:Bit|Mask)|SoftwareLocked(?:Bit|Mask))))|no(?:Group|User)|ownerPrivileges)|l(?:CloseMsg|D(?:o(?:HAutoscroll(?:Bit)?|VAutoscroll(?:Bit)?)|raw(?:Msg|ingModeOff(?:Bit)?))|ExtendDrag(?:Bit)?|HiliteMsg|InitMsg|No(?:Disjoint(?:Bit)?|Extend(?:Bit)?|NilHilite(?:Bit)?|Rect(?:Bit)?)|OnlyOne(?:Bit)?|UseSense(?:Bit)?|a(?:Dictionary(?:NotOpenedErr|TooManyErr|UnknownErr)|En(?:gineNotFoundErr|vironment(?:BusyErr|ExistErr|NotFoundErr))|FailAnalysisErr|InvalidPathErr|NoMoreMorphemeErr|Property(?:Err|IsReadOnlyErr|NotFoundErr|UnknownErr|ValueErr)|T(?:extOverFlowErr|ooSmallBufferErr)|ng(?:A(?:fri(?:caans|kaans)|lbanian|mharic|r(?:abic|menian)|ssamese|ymara|zerbaijan(?:Ar|Roman|i))|B(?:asque|e(?:lorussian|ngali)|reton|u(?:lgarian|rmese)|yelorussian)|C(?:atalan|h(?:ewa|inese)|roatian|zech)|D(?:anish|utch|zongkha)|E(?:nglish|s(?:peranto|tonian))|F(?:a(?:eroese|r(?:oese|si))|innish|lemish|rench)|G(?:al(?:ician|la)|e(?:orgian|rman)|ree(?:k(?:Ancient|Poly)?|nlandic)|u(?:arani|jarati))|H(?:ebrew|indi|ungarian)|I(?:celandic|n(?:donesian|uktitut)|rish(?:Gaelic(?:Script)?)?|talian)|Ja(?:panese|vaneseRom)|K(?:a(?:nnada|shmiri|zakh)|hmer|i(?:nyarwanda|rghiz)|orean|urdish)|L(?:a(?:o|pp(?:ish|onian)|t(?:in|vian))|ettish|ithuanian)|M(?:a(?:cedonian|l(?:a(?:gasy|y(?:Arabic|Roman|alam))|t(?:a|ese))|nxGaelic|rathi)|o(?:ldavian|ngolian(?:Cyr)?))|N(?:epali|orwegian|y(?:anja|norsk))|Or(?:iya|omo)|P(?:ashto|ersian|o(?:lish|rtug(?:ese|uese))|unjabi)|Quechua|R(?:omanian|u(?:anda|ndi|ssian))|S(?:a(?:amisk|mi|nskrit)|cottishGaelic|erbian|i(?:mpChinese|n(?:dhi|halese))|lov(?:ak|enian)|omali|panish|undaneseRom|w(?:ahili|edish))|T(?:a(?:galog|jiki|mil|tar)|elugu|hai|i(?:betan|grinya)|ongan|radChinese|urk(?:ish|men))|U(?:ighur|krainian|nspecified|rdu|zbek)|Vietnamese|Welsh|Y(?:iddish|ugoslavian))|pProtErr|rge(?:1BitMask|4BitData|8BitData)|stDskErr|unch(?:Allow24Bit|Continue|DontSwitch|InhibitDaemon|NoFileFlags|UseMinimum))|eft(?:OverChars|SingGuillemet)|imitReachedErr|o(?:c(?:alOnlyErr|kPortBits(?:Bad(?:PortErr|SurfaceErr)|SurfaceLostErr|W(?:indow(?:ClippedErr|MovedErr|ResizedErr)|rongGDeviceErr)))|ng(?:Da(?:te(?:Found)?|y)|Month|Week|Year)))|m(?:BarNFnd|CalcItemMsg|D(?:ownMask|rawMsg)|FulErr|PopUpMsg|SizeMsg|UpMask|a(?:c(?:Dev|ron)|p(?:C(?:hanged(?:Bit)?|ompact(?:Bit)?)|Read(?:Err|Only(?:Bit)?))|trixErr|x(?:Country|DateField|SizeToGrowTooSmall))|ct(?:AllItems|LastIDIndic)|dy|e(?:diaTypesDontMatch|m(?:A(?:ZErr|drErr)|BCErr|F(?:ragErr|ullErr)|LockedErr|P(?:CErr|urErr)|ROZ(?:Err(?:or)?|Warn)|SCErr|WZErr)|nu(?:I(?:nvalidErr|temNotFoundErr)|NotFoundErr|Pr(?:gErr|operty(?:Invalid(?:Err)?|NotFoundErr))|UsesSystemDefErr))|i(?:di(?:DupIDErr|InvalidCmdErr|ManagerAbsentOSErr|N(?:ameLenErr|o(?:C(?:lientErr|onErr)|PortErr))|TooMany(?:ConsErr|PortsErr)|VConnect(?:Err|Made|Rmvd)|WriteErr)|n(?:Country|LeadingZ|i(?:1BitMask|4BitData|8BitData)|ute(?:Field|Mask))|ssingRequiredParameterErr)|mInternalError|ntLdingZ|o(?:de(?:32BitCompatible|C(?:anBackground|ontrolPanel)|D(?:eskAccessory|isplayManagerAware|oesActivateOnFGSwitch)|Get(?:AppDiedMsg|FrontClicks)|HighLevelEventAware|L(?:aunchDontSwitch|iteral|ocalAndRemoteHLEvents)|MultiLaunch|N(?:eedSuspendResume|ormal)|OnlyBackground|Phonemes|Reserved|StationeryAware|T(?:ext|une)|UseTextEditServices)|nth(?:Field|Mask)|u(?:ntedFolderIconResource|se(?:Down|MovedMessage|Up))|v(?:ableDBoxProc|ieT(?:extNotFoundErr|oolboxUninitialized)))|pWorkFlag(?:CopyWorkBlock|Do(?:Completion|Work|ntBlock)|Get(?:IsRunning|ProcessorCount))|ultiplePublisherWrn|yd)|n(?:WIDTHHook|ame(?:FontTableTag|TypeErr)|bp(?:BuffOvr|ConfDiff|Duplicate|N(?:ISErr|o(?:Confirm|tFound)))|e(?:edClearScrapErr|gZcbFreeErr|twork(?:E(?:rr|vt)|Mask)|wLine(?:Bit|CharMask|Mask))|il(?:HandleErr|ScrapFlavorDataErr)|mTyp(?:Err|e)|o(?:AdrMkErr|BridgeErr|C(?:a(?:che(?:Bit|Mask)|lls)|o(?:decErr|nstraint))|D(?:MAErr|ata(?:Area|Handler)|e(?:fault(?:DataRef|UserErr)|viceForChannel)|riveErr|taMkErr)|ExportProcAvailableErr|G(?:lobalsErr|rowDocProc)|H(?:ardware(?:Err)?|elpForItem)|I(?:conDataAvailableErr|nformErr)|M(?:MUErr|PPErr|a(?:c(?:DskErr|hineNameErr)|rk|skFoundErr)|e(?:diaHandler|m(?:ForPictPlaybackErr|oryNodeFailedInitialize))|o(?:re(?:FolderDescErr|KeyColorsErr|RealTime)|vieFound))|NybErr|OutstandingHLE|P(?:a(?:steboardPromiseKeeperErr|thMappingErr)|ortErr|refAppErr)|Re(?:cordOfApp|lErr|quest|sponseErr)|S(?:crap(?:Err|PromiseKeeperErr)|e(?:curitySession|ndResp|ssionErr)|ou(?:ndTrackInMovieErr|rceTreeFoundErr)|u(?:chIconErr|itableDisplaysErr)|ynthFound)|T(?:humbnailFoundErr|oolboxNameErr|ranslationPathErr|ypeErr)|User(?:InteractionAllowed|NameErr|Re(?:cErr|fErr))|VideoTrackInMovieErr|n(?:DragOriginatorErr|GlyphID|MatchingEditState)|t(?:A(?:FileErr|QTVRMovieErr|RemountErr|llowedToSaveMovieErr|ppropriateForClassic)|BTree|E(?:nough(?:BufferSpace|D(?:ataErr|iskSpaceToGrab)|Hardware(?:Err)?|Memory(?:Err|ToGrab))|xact(?:MatrixErr|SizeErr))|HeldErr|I(?:mplementedMusicOSErr|nitErr)|L(?:eafAtomErr|o(?:ckedErr|ggedInErr))|OpenErr|PasteboardOwnerErr|RegisteredSectionErr|ThePublisherWrn|e(?:ChannelNotAllocatedOSErr|Icon)))|r(?:CallNotSupported|DataTruncatedErr|ExitedIteratorScope|I(?:nvalid(?:EntryIterationOp|NodeErr)|terationDone)|LockedErr|N(?:ameErr|ot(?:CreatedErr|EnoughMemoryErr|FoundErr|ModifiedErr|SlotDeviceErr))|OverrunErr|P(?:ath(?:BufferTooSmall|NotFound)|ower(?:Err|SwitchAbortErr)|ropertyAlreadyExists)|ResultCodeBase|T(?:ransactionAborted|ypeMismatchErr))|s(?:DrvErr|StackErr|vErr)|u(?:l(?:Dev|lEvent)|mberFor(?:matting(?:Bad(?:CurrencyPositionErr|FormatErr|NumberFormattingObjectErr|OptionsErr|TokenErr)|DelimiterMissingErr|EmptyFormatErr|LiteralMissingErr|NotA(?:DigitErr|NumberErr)|OverflowInDestinationErr|SpuriousCharErr|UnOrd(?:eredCurrencyRangeErr|redCurrencyRangeErr))|tmattingNotADigitErr)))|o(?:ffLinErr|gonek|k|p(?:WrErr|en(?:Err|FolderIconResource)|tionKey(?:Bit)?)|s(?:2FontTableTag|Evt(?:MessageMask)?|Mask)|ver(?:Dot|layDITL)|wnedFolderIconResource)|p(?:A(?:RADialIn|S(?:Da(?:teString|y(?:s)?)|Hours|It|M(?:e|inutes|onth)|P(?:arent|i|rint(?:Depth|Length))|Quote|Re(?:quiredImportItems|sult|turn)|S(?:econds|pace)|T(?:ab|ime(?:String)?|opLevelScript)|Week(?:day|s)|Year)|T(?:Machine|Type|Zone)|boutMacintosh|pp(?:Partition|l(?:eMenuItemsFolder|icationFile))|r(?:cAngle|ePrivilegesInherited))|B(?:ackground(?:Color|Pattern)|estType|ounds|uttonViewArrangement|y(?:CreationDateArrangement|KindArrangement|LabelArrangement|ModificationDateArrangement|NameArrangement|SizeArrangement))|C(?:a(?:llBackNumber|n(?:C(?:hangePassword|onnect)|DoProgramLinking)|pacity)|l(?:ass|ipboard)|o(?:lor(?:Table)?|m(?:ment|pletelyExpanded)|n(?:duit|t(?:ainer(?:Window)?|ent(?:Space|s)|rolPanelsFolder))|rnerCurve(?:Height|Width))|reationDate(?:Old)?)|D(?:CM(?:AccessMethod|C(?:lass|opyright)|L(?:isting|ocale)|Maintenance|Permission)|NS(?:Form)?|ashStyle|e(?:f(?:ault(?:ButtonViewIconSize|IconViewIconSize|ListViewIconSize|Type)|initionRect)|layBeforeSpringing|s(?:cription|k(?:AccessoryFile|top))|vice(?:Address|Type))|isk|ottedDecimal)|E(?:jectable|n(?:abled|dPoint|tireContents)|x(?:p(?:and(?:able|ed)|orted)|tensionsFolder))|F(?:TPKind|i(?:l(?:e(?:Creator|Share(?:On|StartingUp)|Type(?:Old)?)?|l(?:Color|Pattern))|nderPreferences)|o(?:lder(?:Old)?|nt(?:sFolder(?:PreAllegro)?)?|rmula)|reeSpace)|G(?:r(?:aphicObjects|id(?:Icons)?|oup(?:Privileges)?)|uestPrivileges)|H(?:as(?:CloseBox|ScriptingTerminology|TitleBar)|ost)|I(?:D|con(?:Bitmap|Size|ViewArrangement)|n(?:dex|fo(?:Panel|Window)|herits|sertionLoc|ternetLocation)|s(?:Collapsed|F(?:loating|rontProcess)|Locked(?:Old)?|Mod(?:al|ified)|Owner|P(?:opup|ulledOpen)|Resizable|S(?:criptable|elected|ta(?:rtup|tioneryPad))|Zoom(?:able|ed(?:Full)?))|temNumber)|Justification|K(?:e(?:epArranged(?:By)?|y(?:Kind|strokeKey))|ind)|L(?:a(?:bel(?:1|2|3|4|5|6|7|Index)|ngCode|rge(?:Button|stFreeBlock))|ength|i(?:neArrow|stViewIconSize)|ocal(?:AndRemoteEvents)?)|M(?:akeChanges|enuID|inAppPartition|o(?:difi(?:cationDate(?:Old)?|ers)|unted))|N(?:ame|e(?:twork|wElementLoc)|o(?:Arrangement|de))|O(?:bject|riginalItem|wner(?:Privileges)?)|P(?:a(?:rtitionSpaceUsed|th)|en(?:Color|Pattern|Width)|hysicalSize|ixelDepth|o(?:int(?:List|Size)|rt|sition)|r(?:e(?:ferences(?:Folder|Window)|viousView)|o(?:ductVersion|gramLinkingOn|perties|t(?:ection|ocol))))|R(?:e(?:st|verse)|otation)|S(?:CSI(?:Bus|LUN)|c(?:ale|heme|ript(?:Code|Tag)?)|e(?:eF(?:iles|olders)|lect(?:ed|ion))|h(?:ar(?:ableContainer|ing(?:Protection|Window)?)|o(?:rtCuts|uldCallBack|w(?:C(?:omment|reationDate)|D(?:ate|iskInfo)|FolderSize|Kind|Label|ModificationDate|Size|Version))|utdownFolder)|ize|mall(?:Button|Icon)|napToGridArrangement|o(?:cket|rtDirection|und)|pringOpenFolders|ta(?:ggerIcons|rt(?:Angle|Point|ingUp|up(?:Disk|ItemsFolder)))|uggestedAppPartition|ystemFolder)|T(?:e(?:mporaryFolder|xt(?:Color|Encoding|Font|ItemDelimiters|PointSize|Styles))|ra(?:ns(?:ferMode|lation)|sh))|U(?:RL|niformStyles|pdateOn|se(?:RelativeDate|ShortMenus|WideGrid|r(?:Name|Password|Selection)))|V(?:ersion|i(?:ew(?:Font(?:Size)?|Preferences)?|sible))|W(?:arnOnEmpty|indow)|a(?:ramErr|steDev|th(?:NotVerifiedErr|TooLongErr))|er(?:Thousand|mErr)|i(?:c(?:Item|ker(?:CantLive|ResourceError)|t(?:Info(?:IDErr|Ver(?:bErr|sionErr))|ureDataErr))|xMapTooDeepErr)|l(?:a(?:inDBox|tform(?:68k|AIXppc|I(?:A32NativeEntryPoint|RIXmips|nterpreted)|Linux(?:intel|ppc)|MacOSx86|NeXT(?:68k|Intel|ppc|sparc)|PowerPC(?:64NativeEntryPoint|NativeEntryPoint)?|SunOS(?:intel|sparc)|Win32|X86_64NativeEntryPoint))|easeCache(?:Bit|Mask))|m(?:BusyErr|Field|Mask|Re(?:cv(?:EndErr|StartErr)|plyTOErr)|Send(?:EndErr|StartErr))|o(?:pup(?:FixedWidth|MenuProc|Title(?:Bold|C(?:enterJust|ondense)|Extend|Italic|LeftJust|NoStyle|Outline|RightJust|Shadow|Underline)|Use(?:AddResMenu|WFont)|VariableWidth)|rt(?:ClosedErr|InUse|N(?:ameExistsErr|ot(?:Cf|Pwr)))|sErr)|r(?:InitErr|WrErr|eferencesFolderIconResource|i(?:nt(?:MonitorFolderIconResource|erStatusOpCodeNotSupportedErr)|vateFolderIconResource)|o(?:c(?:NotFound|essStateIncorrectErr)|gressProcAborted|pertyNotSupportedByNodeErr|tocolErr))|ushButProc)|q(?:Err|fcbNot(?:CreatedErr|FoundErr)|t(?:ActionNotHandledErr|NetworkAlreadyAllocatedErr|ParamErr|XML(?:ApplicationErr|ParseErr)|ml(?:Dll(?:EntryNotFoundErr|LoadErr)|Uninitialized)|s(?:AddressBusyErr|Bad(?:DataErr|S(?:electorErr|tateErr))|ConnectionFailedErr|T(?:imeoutErr|ooMuchDataErr)|Un(?:knownValueErr|supported(?:DataTypeErr|FeatureErr|RateErr)))|vr(?:LibraryLoadErr|Uninitialized))|ueueFull)|r(?:AliasType|ad(?:Ctrl|ioButProc)|c(?:DB(?:AsyncNotSupp|B(?:ad(?:AsyncPB|DDEV|Sess(?:ID|Num)|Type)|reak)|E(?:rror|xec)|N(?:oHandler|ull)|PackNotInited|Value|WrongVersion)|vrErr)|dVerify(?:Bit|Mask)?|e(?:ad(?:Err|QErr|Reference)|c(?:NotFnd|ordDataTooBigErr)|gisterComponent(?:A(?:fterExisting|liasesOnly)|Global|NoDuplicates)|q(?:Aborted|Failed|uiredFlagsDontMatch)|s(?:1Field|2Field|3Field|AttrErr|C(?:hanged(?:Bit)?|trl)|FNotFound|Locked(?:Bit)?|NotFound|P(?:r(?:eload(?:Bit)?|o(?:blem|tected(?:Bit)?))|urgeable(?:Bit)?)|Sys(?:Heap(?:Bit)?|RefBit)|ourceInMemory|umeFlag)|tryComponentRegistrationErr)|fNumErr|gn(?:OverflowErr|TooBigErr(?:or)?)|i(?:ght(?:ControlKey(?:Bit)?|OptionKey(?:Bit)?|S(?:hiftKey(?:Bit)?|ingGuillemet))|ngMark)|mvRe(?:fFailed|sFailed)|o(?:man(?:AppFond|Flags|SysFond)|utingNotFoundErr))|s(?:IQType|am(?:eFileErr|plesAlreadyInMediaErr)|c(?:TypeNotFoundErr|r(?:ap(?:Flavor(?:FlagsMismatchErr|NotFoundErr|SizeMismatchErr)|PromiseNotKeptErr)|ipt(?:CurLang|DefLang)|ollBarProc))|dm(?:InitErr|JTInitErr|P(?:RAMInitErr|riInitErr)|SRTInitErr)|e(?:NoDB|c(?:LeadingZ|ond(?:Field|Mask)|tNFErr)|ekErr|lectorNotSupportedByNodeErr|pNot(?:Consistent|IntlSep)|qGrabInfoNotAvailable|ss(?:ClosedErr|TableErr|ion(?:Has(?:GraphicAccess|TTY)|IsR(?:emote|oot)|KeepCurrentBootstrap))|ttingNotSupportedByNodeErr)|h(?:aredFolderIconResource|iftKey(?:Bit)?|ortDate|utDownAlert)|i(?:Bad(?:DeviceName|RefNum|SoundInDevice)|DeviceBusyErr|HardDriveTooSlow|In(?:it(?:S(?:DTblErr|PTblErr)|VBLQsErr)|putDeviceErr|valid(?:Compression|Sample(?:Rate|Size)))|No(?:BufferSpecified|SoundInHardware)|Unknown(?:InfoType|Quality)|VBRCompressionNotSupported|ze(?:Bit|of_sfnt(?:CMap(?:E(?:ncoding|xtendedSubHeader)|Header|SubHeader)|D(?:escriptorHeader|irectory)|Instance|Name(?:Header|Record)|Variation(?:Axis|Header))))|ktClosedErr|l(?:eepQType|otNumErr|pQType)|m(?:A(?:llScripts|mharic|r(?:abic|menian))|B(?:LFieldBad|ad(?:BoardId|RefId|Script|Verb|s(?:List|PtrErr))|engali|lkMoveErr|u(?:rmese|sErrTO)|yteLanesErr)|C(?:PUErr|RCFail|entralEuroRoman|h(?:ar(?:1byte|2byte|Ascii|B(?:idirect|opomofo)|ContextualLR|E(?:uro|xtAscii)|FIS(?:G(?:ana|reek)|Ideo|Kana|Russian)|GanaKana|H(?:angul|iragana|orizontal)|Ideographic|Jamo|Katakana|L(?:eft|ower)|NonContextualLR|Punct|Right|TwoByte(?:Greek|Russian)|Upper|Vertical)|inese)|kStatusErr|odeRevErr|urrentScript|yrillic)|D(?:evanagari|is(?:DrvrNamErr|abledSlot|posePErr))|E(?:astEurRoman|mptySlot|thiopic|xtArabic)|F(?:HBl(?:kDispErr|ockRdErr)|ISClass(?:Lvl(?:1|2)|User)|irstByte|o(?:nd(?:End|Start)|rmatErr))|G(?:e(?:ez|orgian|t(?:DrvrNamErr|PRErr))|reek|u(?:jarati|rmukhi))|Hebrew|I(?:deographic(?:Level(?:1|2)|User)|nit(?:StatVErr|TblVErr))|Ja(?:mo(?:Bog(?:Jaeum|Moeum)|Jaeum|Moeum)|panese)|K(?:CHRCache|an(?:a(?:HardOK|S(?:mall|oftOK))|nada)|ey(?:DisableKybd(?:Switch|s)|EnableKybds|ForceKeyScript(?:Bit|Mask)|Next(?:InputMethod|Kybd|Script)|Roman|S(?:cript|etDir(?:LeftRight|RightLeft)|wap(?:InputMethod|Kybd|Script)|ysScript)|Toggle(?:Direction|Inline))|hmer|lingon|orean)|La(?:o(?:tian)?|stByte)|M(?:a(?:layalam|sk(?:A(?:ll|scii(?:1|2)?)|Bopomofo2|Gana2|Hangul2|Jamo2|Kana(?:1|2)|Native))|iddleByte|ongolian)|N(?:ewPErr|ilsBlockErr|o(?:Board(?:Id|SRsrc)|Dir|GoodOpens|JmpTbl|MoresRsrcs|sInfoArray|tInstalled)|umberPartsTable)|O(?:ffsetErr|riya)|P(?:RAMInitErr|riInitErr|unct(?:Blank|Graphic|N(?:ormal|umber)|Repeat|Symbol))|R(?:Symbol|e(?:cNotFnd|gionCode|s(?:erved(?:Err|Slot)|rvErr)|visionErr)|oman|ussian)|S(?:DMInitErr|RT(?:InitErr|OvrFlErr)|elOOBErr|i(?:mpChinese|n(?:dhi|gleByte|halese))|l(?:avic|otOOBErr)|ys(?:Script|temScript))|T(?:amil|elugu|hai|ibetan|ra(?:dChinese|ns(?:Ascii(?:1|2)?|Bopomofo2|Case|Gana2|Hangul(?:2|Format)|Jamo2|Kana(?:1|2)|Lower|Native|Pre(?:DoubleByting|LowerCasing)|RuleBaseFormat|System|Upper)))|U(?:n(?:ExBusErr|TokenTable|i(?:codeScript|nterp))|prHalfCharSet)|Vietnamese|W(?:hiteSpaceList|ord(?:SelectTable|WrapTable))|all(?:1BitMask|4BitData|8BitData|DateBit)|c(?:ClassMask|DoubleMask|OrientationMask|R(?:eserved|ightMask)|TypeMask|UpperMask)|f(?:D(?:isableKeyScriptSync(?:Mask)?|ualCaret)|NameTagEnab|ShowIcon|UseAssocFontInfo)|s(?:GetDrvrErr|PointerNil|f(?:AutoInit|B0Digits|Context|Forms|IntellCP|Ligatures|N(?:atCase|oForceFont)|Reverse|S(?:ingByte|ynchUnstyledTE)|UnivExt)))|o(?:C(?:haracterMode|ommandDelimiter|urrent(?:A5|Voice))|Error(?:CallBack|s)|InputMode|NumberMode|OutputTo(?:AudioDevice|ExtAudioFile|FileWithCFURL)|P(?:honeme(?:CallBack|Options|Symbols)|itch(?:Base|Mod))|R(?:ate|e(?:centSync|fCon|set))|S(?:oundOutput|peechDoneCallBack|tatus|yn(?:cCallBack|th(?:Extension|Type)))|TextDoneCallBack|Vo(?:ice(?:Description|File)|lume)|WordCallBack|rts(?:After|Before|Equal)|u(?:ndSupportNotAvailableErr|rceNotFoundErr))|pdAdjErr|rcCopy|t(?:a(?:leEditState|rtupFolderIconResource|t(?:Text|usErr))|opIcon|r(?:UserBreak|eamingNodeNotReadyErr|ingOverflow))|u(?:p(?:Day|Month|Week|Year)|spendResumeMessage)|v(?:All(?:1BitData|4BitData|8BitData|AvailableData|LargeData|MiniData|SmallData)|Disabled|Large(?:1Bit|4Bit|8Bit)|Mini(?:1Bit|4Bit|8Bit)|Small(?:1Bit|4Bit|8Bit)|TempDisable)|y(?:nth(?:NotReady|OpenFailed|esizer(?:NotRespondingOSErr|OSErr))|stem(?:CurLang|DefLang|FolderIconResource)))|t(?:aDst(?:DocNeedsResourceFork|IsAppTranslation)|e(?:Bit(?:Clear|Set|Test)|C(?:aret|enter)|Draw|F(?:AutoScroll|I(?:dleWithEventLoopTimer|nlineInput(?:AutoScroll)?)|OutlineHilite|TextBuffering|Use(?:InlineInput|TextServices|WhiteBackground)|ind|lush(?:Default|Left|Right)|orceLeft|rom(?:Find|Recal))|Highlight|Just(?:Center|Left|Right)|ScrapSizeErr|Word(?:Drag|Select)|l(?:A(?:PattNotSupp|lreadyOpen|utoAnsNotOn)|Bad(?:APattErr|BearerType|C(?:AErr|odeResource)|D(?:N(?:DType|Err|Type)|isplayMode)|F(?:eatureID|unction|wdType)|H(?:TypeErr|andErr)|In(?:dex|t(?:Ext|ercomID))|LevelErr|P(?:a(?:geID|rkID)|ickupGroupID|roc(?:Err|ID))|Rate|S(?:WErr|ampleRate|elect|tateErr)|TermErr|VTypeErr)|C(?:A(?:Not(?:Acceptable|Deflectable|Rejectable)|Unavail)|BErr|onf(?:Err|LimitE(?:rr|xceeded)|NoLimit|Rej))|D(?:N(?:DTypeNotSupp|TypeNotSupp)|e(?:tAlreadyOn|viceNotFound)|isplayModeNotSupp)|F(?:eat(?:Active|Not(?:Avail|Su(?:b|pp)))|wdTypeNotSupp)|GenericError|HTypeNotSupp|In(?:dexNotSupp|itFailed|tExtNotSupp)|No(?:C(?:allbackRef|ommFolder)|Err|MemErr|OpenErr|SuchTool|Tools|tEnoughdspBW)|PBErr|St(?:ateNotSupp|illNeeded)|T(?:ermNotOpen|ransfer(?:Err|Rej))|UnknownErr|V(?:TypeNotSupp|alidateFailed))|xt(?:MenuProc|Parser(?:Bad(?:Par(?:amErr|serObjectErr)|T(?:ext(?:EncodingErr|LanguageErr)|okenValueErr))|No(?:MoreT(?:extErr|okensErr)|SuchTokenFoundErr)|ObjectNotFoundErr|ParamErr)))|h(?:eme(?:Bad(?:CursorIndexErr|TextColorErr)|HasNoAccentsErr|InvalidBrushErr|MonitorDepthNotSupportedErr|NoAppropriateBrushErr|Process(?:NotRegisteredErr|RegisteredErr)|ScriptFontNotFoundErr)|read(?:BadAppContextErr|NotFoundErr|ProtocolErr|TooManyReqsErr))|i(?:lde|me(?:Cycle(?:12|24|Zero)|NotIn(?:Media|Track|ViewErr)))|k0BadErr|ls_(?:ciphersuite_(?:AES_(?:128_GCM_SHA256|256_GCM_SHA384)|CHACHA20_POLY1305_SHA256|ECDHE_(?:ECDSA_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:384)?|GCM_SHA384))|CHACHA20_POLY1305_SHA256)|RSA_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:384)?|GCM_SHA384))|CHACHA20_POLY1305_SHA256))|RSA_WITH_(?:3DES_EDE_CBC_SHA|AES_(?:128_(?:CBC_SHA(?:256)?|GCM_SHA256)|256_(?:CBC_SHA(?:256)?|GCM_SHA384)))|group_(?:ats(?:_compatibility)?|compatibility|default|legacy))|protocol_version_(?:DTLSv1(?:0|2)|TLSv1(?:0|1|2|3)))|m(?:foErr|wdoErr)|o(?:g(?:Char(?:12HourBit|ZCycleBit)|Delta12HourBit|gle(?:B(?:ad(?:Char|Delta|Field|Num)|it)|Err(?:3|4|5)|O(?:K|utOfRange)|Un(?:defined|known)))|k(?:DecPoint|E(?:Minus|Plus|scape)|Le(?:ad(?:Placer|er)|ftQuote)|M(?:axSymbols|inusSign)|NonLeader|P(?:ercent|lusSign)|R(?:eserved|ightQuote)|Separator|Thousands|ZeroLead|en(?:1Quote|2(?:Equal|Quote)|A(?:l(?:pha|t(?:Num|Real))|mpersand|sterisk|tSign)|Ba(?:ckSlash|r)|C(?:a(?:pPi|r(?:at|et))|enterDot|o(?:lon(?:Equal)?|mma))|D(?:ivide|ollar)|E(?:llipsis|mpty|qual|rr|scape|xclam(?:Equal)?)|Fraction|Great(?:Equal(?:1|2))?|Hash|In(?:finity|t(?:egral|l(?:Currency)?))|L(?:e(?:ft(?:1Quote|2Quote|Bracket|C(?:omment|urly)|Enclose|Lit|Paren|SingGuillemet)|ss(?:Equal(?:1|2)|Great)?)|iteral)|Mi(?:cro|nus)|N(?:ewLine|il|o(?:BreakSpace|tEqual)|umeric)|O(?:K|verflow)|P(?:er(?:Thousand|cent|iod)|i|lus(?:Minus)?)|Question|R(?:e(?:alNum|serve(?:1|2))|ight(?:1Quote|2Quote|Bracket|C(?:omment|urly)|Enclose|Lit|Paren|SingGuillemet)|oot)|S(?:emicolon|igma|lash)|Tild(?:a|e)|Un(?:derline|known)|White))|oMany(?:Reqs|S(?:eps|kts)))|ra(?:ck(?:IDNotFound|NotInMovie)|shIconResource)|s(?:N(?:extSelectMode|ormalSelectMode)|PreviousSelectMode|m(?:AlreadyRegisteredErr|C(?:ant(?:ChangeForcedClassStateErr|OpenComponentErr)|omponent(?:AlreadyOpenErr|NoErr|Property(?:NotFoundErr|UnsupportedErr)))|D(?:efaultIsNotInputMethodErr|oc(?:NotActiveErr|Property(?:BufferTooSmallErr|NotFoundErr)|umentOpenErr))|In(?:putM(?:ethod(?:IsOldErr|NotFoundErr)|odeChangeFailedErr)|valid(?:Context|DocIDErr))|N(?:everRegisteredErr|o(?:Handler|MoreTokens|OpenTSErr|Stem|tAnAppErr))|ScriptHasNoIMErr|T(?:S(?:HasNoMenuErr|MDocBusyErr|NotOpenErr)|extServiceNotFoundErr)|U(?:n(?:knownErr|sup(?:ScriptLanguageErr|portedTypeErr))|seInputWindowErr)))|t(?:Disabled|Label(?:1|2|3|4|5|6|7)|None|O(?:ffline|pen)|Selected(?:Disabled|O(?:ffline|pen))?)|uneP(?:arseOSErr|layerFullOSErr)|woSideErr|ype(?:128BitFloatingPoint|32BitIcon|4BitIcon|8Bit(?:Icon|Mask)|A(?:E(?:Homograph(?:Accent|DicInfo|Weight)|List|Morpheme(?:PartOfSpeechCode|TextRange)|Record|T(?:E|ext)|UT)|SStorage|TS(?:FontRef|U(?:FontID|Size))|bsoluteOrdinal|lias|pp(?:Parameters|l(?:Signature|e(?:Event|Script)|ication(?:BundleID|URL)))|rc|uditToken)|B(?:est|oo(?:kmarkData|lean)|yte(?:Count|Offset))|C(?:F(?:A(?:bsoluteTime|rrayRef|ttributedStringRef)|BooleanRef|DictionaryRef|Index|Mutable(?:A(?:rrayRef|ttributedStringRef)|DictionaryRef|StringRef)|NumberRef|Range|StringRef|TypeRef)|G(?:ContextRef|Display(?:ChangeFlags|ID)|Float(?:72DPIGlobal|ScreenPixel)?|ImageRef)|String|T(?:Font(?:DescriptorRef|Ref)|GlyphInfoRef)|e(?:ll|ntimeters)|har|l(?:assInfo|ickActivationResult)|o(?:l(?:lection|orTable|umn)|mp(?:Descriptor|onentInstance)|n(?:ceptualTime|trol(?:ActionUPP|FrameMetrics|PartCode|Ref)))|u(?:bic(?:Centimeter|Feet|Inches|Meters|Yards)|rrentContainer))|D(?:CMFi(?:eldAttributes|ndMethod)|a(?:shStyle|ta)|e(?:cimalStruct|grees(?:C|F|K))|ra(?:gRef|wingArea))|E(?:PS|lemInfo|n(?:codedString|umerat(?:ed|ion))|vent(?:HotKeyID|Info|Re(?:cord|f)|Target(?:Options|Ref)))|F(?:MFont(?:Family|S(?:ize|tyle))|S(?:Ref|VolumeRefNum)|alse|eet|i(?:leURL|nderWindow|xed(?:Point|Rectangle)?)|ontColor)|G(?:DHandle|IF|WorldPtr|allons|lyph(?:InfoArray|Selector)|r(?:a(?:fPtr|ms|phic(?:Line|Text))|oupedGraphic))|HI(?:Command|Menu|ObjectRef|Point(?:72DPIGlobal|ScreenPixel)?|Rect(?:72DPIGlobal|ScreenPixel)?|S(?:hapeRef|ize(?:72DPIGlobal|ScreenPixel)?)|Toolbar(?:Display(?:Mode|Size)|ItemRef|Ref)|ViewTrackingAreaRef|Window)|I(?:EEE(?:32BitFloatingPoint|64BitFloatingPoint)|SO8601DateTime|con(?:AndMask|Family)|n(?:ches|d(?:exDescriptor|icatorDragConstraint)|sertionLoc|tl(?:Text|WritingCode)))|JPEG|K(?:e(?:rnelProcessID|yword)|ilo(?:grams|meters))|L(?:A(?:Homograph|Morpheme(?:Bundle|Path)?)|iters|o(?:gicalDescriptor|ng(?:DateTime|Fixed(?:Point|Rectangle)?|Point|Rectangle)|wLevelEventRecord))|M(?:ach(?:Port|ineLoc)|e(?:nu(?:Command|Direction|EventOptions|ItemIndex|Ref|TrackingMode)|ters)|iles|o(?:dalClickResult|use(?:Button|TrackingRef|WheelAxis)))|Null|O(?:S(?:A(?:DialectInfo|ErrorRange|GenericStorage)|LTokenList|Status)|bject(?:BeingExamined|Specifier)|ffsetArray|unces|val)|P(?:String|a(?:ramInfo|steboardRef)|i(?:ct|x(?:MapMinus|elMap))|o(?:lygon|unds)|ro(?:cessSerialNumber|p(?:Info|erty))|tr)|Q(?:D(?:Point|R(?:e(?:ctangle|gion)|gnHandle))|uarts)|R(?:GB(?:16|96|Color)|angeDescriptor|e(?:ctangle|fCon|lative(?:Descriptor|Time)|plyPortAttr)|o(?:tation|undedRectangle|w))|S(?:Int(?:16|32|64)|R(?:Recognizer|SpeechResult)|c(?:r(?:ap(?:Ref|Styles)|ipt)|szResource)|ec(?:IdentityRef|tionH)|ignedByte(?:Count|Offset)|mall(?:32BitIcon|4BitIcon|8Bit(?:Icon|Mask)|IconAndMask)|ound|quare(?:Feet|Kilometers|M(?:eters|iles)|Yards)|tyled(?:Text|UnicodeText)|uiteInfo)|T(?:IFF|able(?:tP(?:oint(?:Rec|erRec)|roximityRec))?|ext(?:Range(?:Array)?|Styles)?|hemeMenu(?:ItemType|State|Type)|oken|rue|ype)|U(?:Int(?:16|32|64)|TF(?:16ExternalRepresentation|8Text)|nicodeText|serRecordFields)|V(?:ersion|oidPtr)|W(?:hose(?:Descriptor|Range)|i(?:ldCard|ndow(?:DefPartCode|Modality|PartCode|Re(?:f|gionCode)|Transition(?:Action|Effect))))|Yards))|u(?:n(?:doDev|i(?:code(?:BufErr|C(?:h(?:arErr|ecksumErr)|ontextualErr)|DirectionErr|ElementErr|FallbacksErr|No(?:TableErr|tFoundErr)|PartConvertErr|T(?:ableFormatErr|extEncodingDataErr)|VariantErr)|mpErr|t(?:EmptyErr|TblFullErr))|known(?:FormatErr|InsertModeErr)|resolvedComponentDLLErr|supported(?:AuxiliaryImportData|ForPlatformErr|OSErr|ProcessorErr))|p(?:d(?:PixMemErr|ate(?:Dev|Evt|Mask))|p(?:C(?:allComponent(?:C(?:anDoProcInfo|loseProcInfo)|Get(?:MPWorkFunctionProcInfo|PublicResourceProcInfo)|OpenProcInfo|RegisterProcInfo|TargetProcInfo|UnregisterProcInfo|VersionProcInfo)|omponent(?:FunctionImplementedProcInfo|SetTargetProcInfo))|GetComponentVersionProcInfo))|rlDataH(?:FTP(?:Bad(?:NameListErr|PasswordErr|UserErr)|DataConnectionErr|FilenameErr|N(?:eedPasswordErr|o(?:DirectoryErr|NetDriverErr|PasswordErr))|P(?:ermissionsErr|rotocolErr)|QuotaErr|S(?:erver(?:DisconnectedErr|Err)|hutdownErr)|URLErr)|HTTP(?:NoNetDriverErr|ProtocolErr|RedirectErr|URLErr))|se(?:A(?:Talk|sync)|ExtClk|Free|MIDI|r(?:Break|CanceledErr|DataItemNotFound|Item|Kind|RejectErr)))|v(?:AxisOnly|LckdErr|Typ(?:Err|e)|a(?:lid(?:DateFields|InstancesExist)|riationFontTableTag)|er(?:A(?:frikaans|lternateGr|r(?:abi(?:a|c)|menia(?:n)?)|ustr(?:alia|ia(?:German)?))|B(?:e(?:l(?:arus|giumLux(?:Point)?)|ngali)|hutan|r(?:azil|eton|it(?:ain|tany))|ulgaria|yeloRussian)|C(?:a(?:nada(?:Comma|Point)|talonia)|hina|roatia|yprus|zech)|Denmark|E(?:astAsiaGeneric|ngCanada|rr|s(?:peranto|tonia))|F(?:a(?:eroeIsl|r(?:EastGeneric|oeIsl))|inland|lemish(?:Point)?|r(?:Belgium(?:Lux)?|Canada|Swiss|ance|enchUniversal))|G(?:e(?:nericFE|orgia(?:n)?|rman(?:Reformed|y))|r(?:Swiss|ee(?:ce(?:Alt|Poly)?|kAncient|nland))|ujarati)|Hungary|I(?:celand|n(?:dia(?:Hindi|Urdu)?|ternational)|r(?:an|eland(?:English)?|ishGaelicScript)|srael|tal(?:ianSwiss|y))|Japan|Korea|L(?:a(?:pland|tvia)|ithuania)|M(?:a(?:cedonia(?:n)?|gyar|lta|nxGaelic|rathi)|ultilingual)|N(?:e(?:pal|therlands(?:Comma)?)|orway|unavut|ynorsk)|P(?:akistan(?:Urdu)?|o(?:land|rtugal)|unjabi)|R(?:omania|u(?:mania|ssia))|S(?:ami|c(?:ottishGaelic|riptGeneric)|erbia(?:n)?|ingapore|lov(?:ak|enia(?:n)?)|p(?:LatinAmerica|ain)|weden)|T(?:aiwan|hailand|ibet(?:an)?|onga|urk(?:ey|ishModified))|U(?:S|kra(?:ine|nia)|nspecified|zbek)|Vietnam|W(?:ales|elsh)|Yugo(?:Croatian|slavia)|variant(?:Denmark|Norway|Portugal))|ideoOutputInUseErr|m(?:AddressNotInFileViewErr|B(?:adDriver|usyBackingFileErr)|FileViewAccessErr|Invalid(?:BackingFileIDErr|FileViewIDErr|OwningProcessErr)|KernelMMUInitErr|M(?:appingPrivilegesErr|emLckdErr|orePhysicalThanVirtualErr)|No(?:More(?:BackingFilesErr|FileViewsErr)|VectorErr)|OffErr)|o(?:iceNotFound|l(?:GoneErr|Mount(?:Changed(?:Bit|Mask)|ExtendedFlags(?:Bit|Mask)|FSReservedMask|Interact(?:Bit|Mask)|NoLoginMsgFlag(?:Bit|Mask)|SysReservedMask)|O(?:ffLinErr|nLinErr)|VMBusyErr)))|w(?:C(?:alcRgns|ontentColor)|D(?:ispose|raw(?:GIcon)?)|FrameColor|Grow|Hi(?:liteColor|t)|In(?:Co(?:llapseBox|ntent)|Drag|G(?:oAway|row)|ProxyIcon|Structure|ToolbarButton|Zoom(?:In|Out))|N(?:ew|oHit)|PrErr|T(?:extColor|itleBarColor)|ack(?:Bad(?:FileErr|MetaDataErr)|ForkNotFoundErr)|eekOfYear(?:Field|Mask)|fFileNotFound|indow(?:A(?:ppModalStateAlreadyExistsErr|ttribute(?:ImmutableErr|sConflictErr))|GroupInvalidErr|ManagerInternalErr|NoAppModalStateErr|WrongStateErr)|r(?:PermErr|Underrun|gVolTypErr|it(?:Err|eReference|ingPastEnd)|ongApplicationPlatform))|y(?:dm|ear(?:Field|Mask)|md)|z(?:eroCycle|oom(?:DocProc|NoGrow)))\\b", + "name": "support.constant.c" + }, { "match": "\\bkCFNumberFormatter(?:Currency(?:AccountingStyle|ISOCodeStyle|PluralStyle)|OrdinalStyle)\\b", "name": "support.constant.cf.10.11.c" @@ -57,6 +209,14 @@ "match": "\\bkCFISO8601DateFormatWith(?:ColonSeparatorInTime(?:Zone)?|Da(?:shSeparatorInDate|y)|Full(?:Date|Time)|InternetDateTime|Month|SpaceBetweenDateAndTime|Time(?:Zone)?|WeekOfYear|Year)\\b", "name": "support.constant.cf.10.12.c" }, + { + "match": "\\bkCFISO8601DateFormatWithFractionalSeconds\\b", + "name": "support.constant.cf.10.13.c" + }, + { + "match": "\\bkCFURLEnumeratorGenerateRelativePathURLs\\b", + "name": "support.constant.cf.10.15.c" + }, { "match": "\\bkCFFileSecurityClear(?:AccessControlList|Group(?:UUID)?|Mode|Owner(?:UUID)?)\\b", "name": "support.constant.cf.10.8.c" @@ -65,6 +225,10 @@ "match": "\\b(?:CF(?:ByteOrder(?:BigEndian|LittleEndian|Unknown)|NotificationSuspensionBehavior(?:Coalesce|D(?:eliverImmediately|rop)|Hold))|kCF(?:B(?:ookmarkResolutionWithout(?:MountingMask|UIMask)|undleExecutableArchitecture(?:I386|PPC(?:64)?|X86_64))|C(?:alendar(?:ComponentsWrap|Unit(?:Day|Era|Hour|M(?:inute|onth)|Quarter|Second|Week(?:Of(?:Month|Year)|day(?:Ordinal)?)|Year(?:ForWeekOfYear)?))|haracterSet(?:AlphaNumeric|C(?:apitalizedLetter|ontrol)|Dec(?:imalDigit|omposable)|Illegal|L(?:etter|owercaseLetter)|N(?:ewline|onBase)|Punctuation|Symbol|UppercaseLetter|Whitespace(?:AndNewline)?)|ompare(?:Anchored|Backwards|CaseInsensitive|DiacriticInsensitive|EqualTo|ForcedOrdering|GreaterThan|L(?:essThan|ocalized)|N(?:onliteral|umerically)|WidthInsensitive))|Dat(?:aSearch(?:Anchored|Backwards)|eFormatter(?:FullStyle|LongStyle|MediumStyle|NoStyle|ShortStyle))|FileDescriptor(?:ReadCallBack|WriteCallBack)|LocaleLanguageDirection(?:BottomToTop|LeftToRight|RightToLeft|TopToBottom|Unknown)|MessagePort(?:BecameInvalidError|IsInvalid|ReceiveTimeout|S(?:endTimeout|uccess)|TransportError)|N(?:otification(?:DeliverImmediately|PostToAllSessions)|umber(?:C(?:FIndexType|GFloatType|harType)|DoubleType|F(?:loat(?:32Type|64Type|Type)|ormatter(?:CurrencyStyle|DecimalStyle|NoStyle|P(?:a(?:d(?:After(?:Prefix|Suffix)|Before(?:Prefix|Suffix))|rseIntegersOnly)|ercentStyle)|Round(?:Ceiling|Down|Floor|Half(?:Down|Even|Up)|Up)|S(?:cientificStyle|pellOutStyle)))|IntType|Long(?:LongType|Type)|MaxType|NSIntegerType|S(?:Int(?:16Type|32Type|64Type|8Type)|hortType)))|PropertyList(?:BinaryFormat_v1_0|Immutable|MutableContainers(?:AndLeaves)?|OpenStepFormat|Read(?:CorruptError|StreamError|UnknownVersionError)|WriteStreamError|XMLFormat_v1_0)|RunLoop(?:A(?:fterWaiting|llActivities)|Before(?:Sources|Timers|Waiting)|E(?:ntry|xit)|Run(?:Finished|HandledSource|Stopped|TimedOut))|S(?:ocket(?:A(?:cceptCallBack|utomaticallyReenable(?:AcceptCallBack|DataCallBack|ReadCallBack|WriteCallBack))|C(?:loseOnInvalidate|onnectCallBack)|DataCallBack|Error|LeaveErrors|NoCallBack|ReadCallBack|Success|Timeout|WriteCallBack)|tr(?:eam(?:E(?:rrorDomain(?:Custom|MacOSStatus|POSIX)|vent(?:CanAcceptBytes|E(?:ndEncountered|rrorOccurred)|HasBytesAvailable|None|OpenCompleted))|Status(?:AtEnd|Closed|Error|NotOpen|Open(?:ing)?|Reading|Writing))|ing(?:Encoding(?:A(?:NSEL|SCII)|Big5(?:_(?:E|HKSCS_1999))?|CNS_11643_92_P(?:1|2|3)|DOS(?:Arabic|BalticRim|C(?:anadianFrench|hinese(?:Simplif|Trad)|yrillic)|Greek(?:1|2)?|Hebrew|Icelandic|Japanese|Korean|Latin(?:1|2|US)|Nordic|Portuguese|Russian|T(?:hai|urkish))|E(?:BCDIC_(?:CP037|US)|UC_(?:CN|JP|KR|TW))|GB(?:K_95|_(?:18030_2000|2312_80))|HZ_GB_2312|ISO(?:Latin(?:1(?:0)?|2|3|4|5|6|7|8|9|Arabic|Cyrillic|Greek|Hebrew|Thai)|_2022_(?:CN(?:_EXT)?|JP(?:_(?:1|2|3))?|KR))|JIS_(?:C6226_78|X02(?:0(?:1_76|8_(?:83|90))|12_90))|K(?:OI8_(?:R|U)|SC_5601_(?:87|92_Johab))|Mac(?:Ar(?:abic|menian)|B(?:engali|urmese)|C(?:e(?:ltic|ntralEurRoman)|hinese(?:Simp|Trad)|roatian|yrillic)|D(?:evanagari|ingbats)|E(?:thiopic|xtArabic)|Farsi|G(?:aelic|eorgian|reek|u(?:jarati|rmukhi))|H(?:FS|ebrew)|I(?:celandic|nuit)|Japanese|K(?:annada|hmer|orean)|Laotian|M(?:alayalam|ongolian)|Oriya|Roman(?:Latin1|ian)?|S(?:inhalese|ymbol)|T(?:amil|elugu|hai|ibetan|urkish)|Ukrainian|V(?:T100|ietnamese))|N(?:extStep(?:Japanese|Latin)|onLossyASCII)|ShiftJIS(?:_X0213(?:_(?:00|MenKuTen))?)?|U(?:TF(?:16(?:BE|LE)?|32(?:BE|LE)?|7(?:_IMAP)?|8)|nicode)|VISCII|Windows(?:Arabic|BalticRim|Cyrillic|Greek|Hebrew|KoreanJohab|Latin(?:1|2|5)|Vietnamese))|NormalizationForm(?:C|D|K(?:C|D))|Tokenizer(?:AttributeLa(?:nguage|tinTranscription)|Token(?:Has(?:DerivedSubTokensMask|HasNumbersMask|NonLettersMask|SubTokensMask)|IsCJWordMask|No(?:ne|rmal))|Unit(?:LineBreak|Paragraph|Sentence|Word(?:Boundary)?)))))|TimeZoneNameStyle(?:DaylightSaving|Generic|S(?:hort(?:DaylightSaving|Generic|Standard)|tandard))|U(?:RL(?:Bookmark(?:Creation(?:MinimalBookmarkMask|S(?:ecurityScopeAllowOnlyReadAccess|uitableForBookmarkFile)|WithSecurityScope)|ResolutionWith(?:SecurityScope|out(?:MountingMask|UIMask)))|Component(?:Fragment|Host|NetLocation|P(?:a(?:rameterString|ssword|th)|ort)|Query|ResourceSpecifier|Scheme|User(?:Info)?)|Enumerator(?:D(?:e(?:faultBehavior|scendRecursively)|irectoryPostOrderSuccess)|E(?:nd|rror)|GenerateFileReferenceURLs|IncludeDirectoriesP(?:ostOrder|reOrder)|S(?:kip(?:Invisibles|PackageContents)|uccess))|POSIXPathStyle|WindowsPathStyle)|serNotification(?:AlternateResponse|Ca(?:ncelResponse|utionAlertLevel)|DefaultResponse|No(?:DefaultButtonFlag|teAlertLevel)|OtherResponse|PlainAlertLevel|StopAlertLevel|UseRadioButtonsFlag))|XML(?:E(?:ntityType(?:Character|Par(?:ameter|sed(?:External|Internal))|Unparsed)|rror(?:E(?:lementlessDocument|ncodingConversionFailure)|Malformed(?:C(?:DSect|haracterReference|loseTag|omment)|D(?:TD|ocument)|Name|P(?:arsedCharacterData|rocessingInstruction)|StartTag)|NoData|Un(?:expectedEOF|knownEncoding)))|Node(?:CurrentVersion|Type(?:Attribute(?:ListDeclaration)?|C(?:DATASection|omment)|Document(?:Fragment|Type)?|E(?:lement(?:TypeDeclaration)?|ntity(?:Reference)?)|Notation|ProcessingInstruction|Text|Whitespace))|Parser(?:A(?:ddImpliedAttributes|llOptions)|NoOptions|Re(?:placePhysicalEntities|solveExternalEntities)|Skip(?:MetaData|Whitespace)|ValidateDocument)|StatusParse(?:InProgress|NotBegun|Successful))))\\b", "name": "support.constant.cf.c" }, + { + "match": "\\bDISPATCH_WALLTIME_NOW\\b", + "name": "support.constant.clib.10.14.c" + }, { "match": "\\b(?:FILESEC_(?:ACL(?:_(?:ALLOCSIZE|RAW))?|GR(?:OUP|PUUID)|MODE|OWNER|UUID)|P_(?:ALL|P(?:GID|ID)))\\b", "name": "support.constant.clib.c" @@ -86,23 +250,39 @@ "name": "support.constant.os.c" }, { - "match": "\\bkCGImageByteOrder(?:16(?:Big|Little)|32(?:Big|Little)|Mask)\\b", - "name": "support.constant.quartz.10.12.c" + "match": "\\bkCGImagePixelFormat(?:Mask|Packed|RGB(?:101010|5(?:55|65)|CIF10))\\b", + "name": "support.constant.quartz.10.14.c" }, { - "match": "\\b(?:CG(?:GlyphM(?:ax|in)|PDFDataFormat(?:JPEG(?:2000|Encoded)|Raw)|RectM(?:ax(?:XEdge|YEdge)|in(?:XEdge|YEdge)))|kCG(?:A(?:nnotatedSessionEventTap|ssistiveTechHighWindowLevelKey)|B(?:a(?:ck(?:ingStore(?:Buffered|Nonretained|Retained)|stopMenuLevelKey)|seWindowLevelKey)|itmap(?:AlphaInfoMask|ByteOrder(?:16(?:Big|Little)|32(?:Big|Little)|Default|Mask)|Float(?:Components|InfoMask))|lendMode(?:C(?:lear|o(?:lor(?:Burn|Dodge)?|py))|D(?:arken|estination(?:Atop|In|O(?:ut|ver))|ifference)|Exclusion|H(?:ardLight|ue)|L(?:ighten|uminosity)|Multiply|Normal|Overlay|Plus(?:Darker|Lighter)|S(?:aturation|creen|o(?:ftLight|urce(?:Atop|In|Out)))|XOR))|C(?:aptureNo(?:Fill|Options)|o(?:lor(?:ConversionTransform(?:ApplySpace|FromSpace|ToSpace)|SpaceModel(?:CMYK|DeviceN|Indexed|Lab|Monochrome|Pattern|RGB|Unknown))|nfigure(?:For(?:AppOnly|Session)|Permanently))|ursorWindowLevelKey)|D(?:esktop(?:IconWindowLevelKey|WindowLevelKey)|isplay(?:AddFlag|BeginConfigurationFlag|D(?:esktopShapeChangedFlag|isabledFlag)|EnabledFlag|M(?:irrorFlag|ovedFlag)|RemoveFlag|S(?:etM(?:ainFlag|odeFlag)|tream(?:FrameStatus(?:Frame(?:Blank|Complete|Idle)|Stopped)|Update(?:DirtyRects|MovedRects|Re(?:ducedDirtyRects|freshedRects))))|UnMirrorFlag)|ockWindowLevelKey|raggingWindowLevelKey)|E(?:rror(?:CannotComplete|Failure|I(?:llegalArgument|nvalid(?:Con(?:nection|text)|Operation))|No(?:neAvailable|tImplemented)|RangeCheck|Success|TypeCheck)|vent(?:F(?:ilterMaskPermit(?:Local(?:KeyboardEvents|MouseEvents)|SystemDefinedEvents)|lag(?:Mask(?:Al(?:phaShift|ternate)|Co(?:mmand|ntrol)|Help|N(?:onCoalesced|umericPad)|S(?:econdaryFn|hift))|sChanged))|Key(?:Down|Up)|LeftMouse(?:D(?:own|ragged)|Up)|Mouse(?:Moved|Subtype(?:Default|TabletP(?:oint|roximity)))|Null|OtherMouse(?:D(?:own|ragged)|Up)|RightMouse(?:D(?:own|ragged)|Up)|S(?:crollWheel|ource(?:GroupID|State(?:CombinedSessionState|HIDSystemState|ID|Private)|U(?:nixProcessID|ser(?:Data|ID)))|uppressionState(?:RemoteMouseDrag|SuppressionInterval))|Ta(?:bletP(?:ointer|roximity)|p(?:DisabledBy(?:Timeout|UserInput)|Option(?:Default|ListenOnly))|rget(?:ProcessSerialNumber|UnixProcessID))))|F(?:loatingWindowLevelKey|ontPostScriptFormatType(?:1|3|42))|G(?:esturePhase(?:Began|C(?:ancelled|hanged)|Ended|MayBegin|None)|radientDraws(?:AfterEndLocation|BeforeStartLocation))|H(?:IDEventTap|e(?:adInsertEventTap|lpWindowLevelKey))|I(?:mageAlpha(?:First|Last|None(?:Skip(?:First|Last))?|Only|Premultiplied(?:First|Last))|nterpolation(?:Default|High|Low|Medium|None))|KeyboardEvent(?:Autorepeat|Key(?:boardType|code))|Line(?:Cap(?:Butt|Round|Square)|Join(?:Bevel|Miter|Round))|M(?:a(?:inMenuWindowLevelKey|ximumWindowLevelKey)|inimumWindowLevelKey|o(?:dalPanelWindowLevelKey|mentumScrollPhase(?:Begin|Continue|End|None)|use(?:Button(?:Center|Left|Right)|Event(?:ButtonNumber|ClickState|Delta(?:X|Y)|InstantMouser|Number|Pressure|Subtype|WindowUnderMousePointer(?:ThatCanHandleThisEvent)?))))|N(?:ormalWindowLevelKey|umberOf(?:EventSuppressionStates|WindowLevelKeys))|OverlayWindowLevelKey|P(?:DF(?:ArtBox|BleedBox|CropBox|MediaBox|ObjectType(?:Array|Boolean|Dictionary|Integer|N(?:ame|ull)|Real|Str(?:eam|ing))|TrimBox)|at(?:h(?:E(?:OFill(?:Stroke)?|lement(?:Add(?:CurveToPoint|LineToPoint|QuadCurveToPoint)|CloseSubpath|MoveToPoint))|Fill(?:Stroke)?|Stroke)|ternTiling(?:ConstantSpacing(?:MinimalDistortion)?|NoDistortion))|opUpMenuWindowLevelKey)|RenderingIntent(?:AbsoluteColorimetric|Default|Perceptual|RelativeColorimetric|Saturation)|S(?:cr(?:een(?:SaverWindowLevelKey|UpdateOperation(?:Move|Re(?:ducedDirtyRectangleCount|fresh)))|oll(?:EventUnit(?:Line|Pixel)|Phase(?:Began|C(?:ancelled|hanged)|Ended|MayBegin)|WheelEvent(?:DeltaAxis(?:1|2|3)|FixedPtDeltaAxis(?:1|2|3)|I(?:nstantMouser|sContinuous)|MomentumPhase|PointDeltaAxis(?:1|2|3)|Scroll(?:Count|Phase))))|essionEventTap|tatusWindowLevelKey)|T(?:a(?:blet(?:Event(?:DeviceID|Point(?:Buttons|Pressure|X|Y|Z)|Rotation|T(?:angentialPressure|ilt(?:X|Y))|Vendor(?:1|2|3))|ProximityEvent(?:CapabilityMask|DeviceID|EnterProximity|Pointer(?:ID|Type)|SystemTabletID|TabletID|Vendor(?:ID|Pointer(?:SerialNumber|Type)|UniqueID)))|ilAppendEventTap)|ext(?:Clip|Fill(?:Clip|Stroke(?:Clip)?)?|Invisible|Stroke(?:Clip)?)|ornOffMenuWindowLevelKey)|UtilityWindowLevelKey|Window(?:Image(?:B(?:estResolution|oundsIgnoreFraming)|Default|NominalResolution|OnlyShadows|ShouldBeOpaque)|List(?:ExcludeDesktopElements|Option(?:All|IncludingWindow|OnScreen(?:AboveWindow|BelowWindow|Only)))|Sharing(?:None|Read(?:Only|Write)))))\\b", + "match": "\\bCGPDFTagType(?:A(?:nnotation|rt)|B(?:ibliography|lockQuote)|C(?:aption|ode)|D(?:iv|ocument)|F(?:igure|orm(?:ula)?)|Header(?:1|2|3|4|5|6)?|Index|L(?:abel|i(?:nk|st(?:Body|Item)?))|No(?:nStructure|te)|P(?:ar(?:agraph|t)|rivate)|Quote|R(?:eference|uby(?:AnnotationText|BaseText|Punctuation)?)|S(?:ection|pan)|T(?:OC(?:I)?|able(?:Body|DataCell|Footer|Header(?:Cell)?|Row)?)|Warichu(?:Punctiation|Text)?)\\b", + "name": "support.constant.quartz.10.15.c" + }, + { + "match": "\\b(?:CG(?:GlyphM(?:ax|in)|PDFDataFormat(?:JPEG(?:2000|Encoded)|Raw)|RectM(?:ax(?:XEdge|YEdge)|in(?:XEdge|YEdge)))|kCG(?:A(?:nnotatedSessionEventTap|ssistiveTechHighWindowLevelKey)|B(?:a(?:ck(?:ingStore(?:Buffered|Nonretained|Retained)|stopMenuLevelKey)|seWindowLevelKey)|itmap(?:AlphaInfoMask|ByteOrder(?:16(?:Big|Little)|32(?:Big|Little)|Default|Mask)|Float(?:Components|InfoMask))|lendMode(?:C(?:lear|o(?:lor(?:Burn|Dodge)?|py))|D(?:arken|estination(?:Atop|In|O(?:ut|ver))|ifference)|Exclusion|H(?:ardLight|ue)|L(?:ighten|uminosity)|Multiply|Normal|Overlay|Plus(?:Darker|Lighter)|S(?:aturation|creen|o(?:ftLight|urce(?:Atop|In|Out)))|XOR))|C(?:aptureNo(?:Fill|Options)|o(?:lor(?:ConversionTransform(?:ApplySpace|FromSpace|ToSpace)|SpaceModel(?:CMYK|DeviceN|Indexed|Lab|Monochrome|Pattern|RGB|Unknown|XYZ))|nfigure(?:For(?:AppOnly|Session)|Permanently))|ursorWindowLevelKey)|D(?:esktop(?:IconWindowLevelKey|WindowLevelKey)|isplay(?:AddFlag|BeginConfigurationFlag|D(?:esktopShapeChangedFlag|isabledFlag)|EnabledFlag|M(?:irrorFlag|ovedFlag)|RemoveFlag|S(?:etM(?:ainFlag|odeFlag)|tream(?:FrameStatus(?:Frame(?:Blank|Complete|Idle)|Stopped)|Update(?:DirtyRects|MovedRects|Re(?:ducedDirtyRects|freshedRects))))|UnMirrorFlag)|ockWindowLevelKey|raggingWindowLevelKey)|E(?:rror(?:CannotComplete|Failure|I(?:llegalArgument|nvalid(?:Con(?:nection|text)|Operation))|No(?:neAvailable|tImplemented)|RangeCheck|Success|TypeCheck)|vent(?:F(?:ilterMaskPermit(?:Local(?:KeyboardEvents|MouseEvents)|SystemDefinedEvents)|lag(?:Mask(?:Al(?:phaShift|ternate)|Co(?:mmand|ntrol)|Help|N(?:onCoalesced|umericPad)|S(?:econdaryFn|hift))|sChanged))|Key(?:Down|Up)|LeftMouse(?:D(?:own|ragged)|Up)|Mouse(?:Moved|Subtype(?:Default|TabletP(?:oint|roximity)))|Null|OtherMouse(?:D(?:own|ragged)|Up)|RightMouse(?:D(?:own|ragged)|Up)|S(?:crollWheel|ource(?:GroupID|State(?:CombinedSessionState|HIDSystemState|ID|Private)|U(?:nixProcessID|ser(?:Data|ID)))|uppressionState(?:RemoteMouseDrag|SuppressionInterval))|Ta(?:bletP(?:ointer|roximity)|p(?:DisabledBy(?:Timeout|UserInput)|Option(?:Default|ListenOnly))|rget(?:ProcessSerialNumber|UnixProcessID))|UnacceleratedPointerMovement(?:X|Y)))|F(?:loatingWindowLevelKey|ontPostScriptFormatType(?:1|3|42))|G(?:esturePhase(?:Began|C(?:ancelled|hanged)|Ended|MayBegin|None)|radientDraws(?:AfterEndLocation|BeforeStartLocation))|H(?:IDEventTap|e(?:adInsertEventTap|lpWindowLevelKey))|I(?:mage(?:Alpha(?:First|Last|None(?:Skip(?:First|Last))?|Only|Premultiplied(?:First|Last))|ByteOrder(?:16(?:Big|Little)|32(?:Big|Little)|Default|Mask))|nterpolation(?:Default|High|Low|Medium|None))|KeyboardEvent(?:Autorepeat|Key(?:boardType|code))|Line(?:Cap(?:Butt|Round|Square)|Join(?:Bevel|Miter|Round))|M(?:a(?:inMenuWindowLevelKey|ximumWindowLevelKey)|inimumWindowLevelKey|o(?:dalPanelWindowLevelKey|mentumScrollPhase(?:Begin|Continue|End|None)|use(?:Button(?:Center|Left|Right)|Event(?:ButtonNumber|ClickState|Delta(?:X|Y)|InstantMouser|Number|Pressure|Subtype|WindowUnderMousePointer(?:ThatCanHandleThisEvent)?))))|N(?:ormalWindowLevelKey|umberOf(?:EventSuppressionStates|WindowLevelKeys))|OverlayWindowLevelKey|P(?:DF(?:A(?:llows(?:Co(?:mmenting|ntent(?:Accessibility|Copying))|Document(?:Assembly|Changes)|FormFieldEntry|HighQualityPrinting|LowQualityPrinting)|rtBox)|BleedBox|CropBox|MediaBox|ObjectType(?:Array|Boolean|Dictionary|Integer|N(?:ame|ull)|Real|Str(?:eam|ing))|TrimBox)|at(?:h(?:E(?:OFill(?:Stroke)?|lement(?:Add(?:CurveToPoint|LineToPoint|QuadCurveToPoint)|CloseSubpath|MoveToPoint))|Fill(?:Stroke)?|Stroke)|ternTiling(?:ConstantSpacing(?:MinimalDistortion)?|NoDistortion))|opUpMenuWindowLevelKey)|RenderingIntent(?:AbsoluteColorimetric|Default|Perceptual|RelativeColorimetric|Saturation)|S(?:cr(?:een(?:SaverWindowLevelKey|UpdateOperation(?:Move|Re(?:ducedDirtyRectangleCount|fresh)))|oll(?:EventUnit(?:Line|Pixel)|Phase(?:Began|C(?:ancelled|hanged)|Ended|MayBegin)|WheelEvent(?:DeltaAxis(?:1|2|3)|FixedPtDeltaAxis(?:1|2|3)|I(?:nstantMouser|sContinuous)|MomentumPhase|PointDeltaAxis(?:1|2|3)|Scroll(?:Count|Phase))))|essionEventTap|tatusWindowLevelKey)|T(?:a(?:blet(?:Event(?:DeviceID|Point(?:Buttons|Pressure|X|Y|Z)|Rotation|T(?:angentialPressure|ilt(?:X|Y))|Vendor(?:1|2|3))|ProximityEvent(?:CapabilityMask|DeviceID|EnterProximity|Pointer(?:ID|Type)|SystemTabletID|TabletID|Vendor(?:ID|Pointer(?:SerialNumber|Type)|UniqueID)))|ilAppendEventTap)|ext(?:Clip|Fill(?:Clip|Stroke(?:Clip)?)?|Invisible|Stroke(?:Clip)?)|ornOffMenuWindowLevelKey)|UtilityWindowLevelKey|Window(?:Image(?:B(?:estResolution|oundsIgnoreFraming)|Default|NominalResolution|OnlyShadows|ShouldBeOpaque)|List(?:ExcludeDesktopElements|Option(?:All|IncludingWindow|OnScreen(?:AboveWindow|BelowWindow|Only)))|Sharing(?:None|Read(?:Only|Write)))))\\b", "name": "support.constant.quartz.c" }, + { + "match": "\\bcl_device_id\\b", + "name": "support.type.10.10.c" + }, + { + "match": "\\b(?:JSTypedArrayType|SecKey(?:Algorithm|KeyExchangeParameter)|os_unfair_lock(?:_t)?)\\b", + "name": "support.type.10.12.c" + }, + { + "match": "\\b(?:A(?:E(?:A(?:ddressDesc|rray(?:Data(?:Pointer)?|Type))|BuildError(?:Code)?|Coerc(?:e(?:Desc(?:ProcPtr|UPP)|Ptr(?:ProcPtr|UPP))|ionHandlerUPP)|D(?:ataStorage(?:Type)?|esc(?:List|Ptr)?|isposeExternal(?:ProcPtr|UPP))|Event(?:Class|Handler(?:ProcPtr|UPP)|ID|Source)|Filter(?:ProcPtr|UPP)|I(?:dle(?:ProcPtr|UPP)|nteractAllowed)|Key(?:Desc|word)|Re(?:cord|moteProcessResolver(?:C(?:allback|ontext)|Ref)?|turnID)|S(?:end(?:Mode|Priority)|treamRef)|TransactionID)|FP(?:AlternateAddress|ServerSignature|TagData|VolMountInfo(?:Ptr)?|XVolMountInfo(?:Ptr)?)|HTOCType|IFFLoop|LMX(?:GlyphEntry|Header)|TS(?:Cu(?:bic(?:C(?:losePath(?:ProcPtr|UPP)|urveTo(?:ProcPtr|UPP))|LineTo(?:ProcPtr|UPP)|MoveTo(?:ProcPtr|UPP))|rveType)|F(?:SSpec|latData(?:Font(?:NameDataHeader|Spec(?:RawNameData(?:Header)?|iferType))|L(?:ayoutControlsDataHeader|ineInfo(?:Data|Header))|MainHeaderBlock|Style(?:List(?:FeatureData|Header|StyleDataHeader|VariationData)|RunDataHeader)|TextLayout(?:DataHeader|Header))|ont(?:A(?:pplierFunction|utoActivationSetting)|Cont(?:ainerRef|ext)|F(?:amily(?:ApplierFunction|Iterator(?:_)?|Ref)|ilter(?:Selector)?|ormat)|Iterator(?:_)?|Metrics|Notif(?:ication(?:InfoRef(?:_)?|Ref(?:_)?)|y(?:Action|Option))|Query(?:Callback|MessageID|SourceContext)|Ref|Size))|G(?:eneration|lyph(?:I(?:dealMetrics|nfoFlags)|Ref|ScreenMetrics|Vector))|Just(?:PriorityWidthDeltaOverrides|WidthDeltaEntryOverride)|L(?:ayoutRecord|ineLayoutOptions)|NotificationCallback|OptionFlags|Point|Quadratic(?:C(?:losePath(?:ProcPtr|UPP)|urve(?:ProcPtr|UPP))|Line(?:ProcPtr|UPP)|NewPath(?:ProcPtr|UPP))|StyleRenderingOptions|Trapezoid|U(?:Attribute(?:Info|Tag|ValuePtr)|Background(?:Color|Data(?:Type)?)|C(?:aret|ur(?:sorMovementType|vePath(?:s)?))|Direct(?:DataSelector|LayoutOperationOverride(?:ProcPtr|UPP))|F(?:latten(?:StyleRunOptions|edDataStreamFormat)|ont(?:F(?:allback(?:Method|s)|eature(?:Selector|Type))|ID|Variation(?:Axis|Value)))|Glyph(?:Info(?:Array)?|Selector)|HighlightMethod|L(?:ayoutOperation(?:CallbackStatus|OverrideSpecifier|Selector)|ine(?:Ref|Truncation))|RGBAlphaColor|Style(?:Comparison|LineCountType|RunInfo|SettingRef)?|T(?:ab(?:Type)?|ext(?:Layout|Measurement))|Un(?:FlattenStyleRunOptions|highlightData)|VerticalCharacterType))|U(?:3DMixer(?:AttenuationCurve|RenderingFlags)|ChannelInfo|D(?:ependentParameter|istanceAttenuationData)|EventListener(?:Block|Proc|Ref)|Graph|Host(?:Identifier|VersionIdentifier)|InputSamplesInOutputCallback(?:Struct)?|ListenerBase|MIDIOutputCallback(?:Struct)?|N(?:ode(?:Connection|Interaction|RenderCallback)?|umVersion)|P(?:arameter(?:EventType|Listener(?:Block|Proc|Ref)|MIDIMapping(?:Flags)?)|reset(?:Event)?)|Re(?:nderCallback(?:Struct)?|verbRoomType)|S(?:ampler(?:BankPresetData|InstrumentData)|cheduledAudioSliceFlags|patial(?:Mixer(?:AttenuationCurve|RenderingFlags)|izationAlgorithm)))|V(?:Audio(?:Integer|SessionErrorCode|UInteger)|L(?:CompareItems(?:ProcPtr|UPP)|DisposeItem(?:ProcPtr|UPP)|ItemSize(?:ProcPtr|UPP)|NodeType|Order|Tree(?:Ptr|Struct)|VisitStage|Walk(?:ProcPtr|UPP)))|X(?:CopyMultipleAttributeOptions|Error|MenuItemModifiers|Observer(?:Callback(?:WithInfo)?|Ref)|Priority|U(?:IElementRef|nderlineStyle)|Value(?:Ref|Type))|l(?:ert(?:Std(?:AlertParam(?:Ptr|Rec)|CFStringAlertParam(?:Ptr|Rec))|T(?:Hndl|Ptr|emplate|ype))|ias(?:Handle|InfoType|Ptr|Record))|n(?:chorPoint(?:Table)?|krTable)|pp(?:Parameters(?:Ptr)?|earance(?:PartCode|RegionCode)|l(?:eEvent(?:Ptr)?|icationSpecificChunk(?:Ptr)?))|reaID|sscEntry|u(?:dio(?:B(?:alanceFade(?:Type)?|uffer(?:List)?|ytePacketTranslation(?:Flags)?)|C(?:hannel(?:Bitmap|CoordinateIndex|Description|Flags|La(?:bel|yout(?:Tag)?))|lass(?:Description|ID)|o(?:dec(?:AppendInput(?:BufferListProc|DataProc)|GetProperty(?:InfoProc|Proc)|InitializeProc|MagicCookieInfo|Pr(?:imeInfo|o(?:duceOutput(?:BufferListProc|PacketsProc)|pertyID))|ResetProc|SetPropertyProc|UninitializeProc)?|mponent(?:Description|F(?:actoryFunction|lags)|Instan(?:ce|tiationOptions)|Method|PlugInInterface|ValidationResult)?|nverter(?:ComplexInputDataProc|InputDataProc|Pr(?:imeInfo|opertyID)|Ref)))|Device(?:I(?:D|O(?:Block|Proc(?:ID)?))|Property(?:ID|ListenerProc))|F(?:ile(?:Component(?:C(?:loseProc|ountUserDataProc|reateURLProc)|ExtensionIsThisFormatProc|FileDataIsThisFormatProc|Get(?:GlobalInfo(?:Proc|SizeProc)|Property(?:InfoProc|Proc)|UserData(?:Proc|SizeProc))|InitializeWithCallbacksProc|Op(?:en(?:URLProc|WithCallbacksProc)|timizeProc)|PropertyID|Re(?:ad(?:BytesProc|Packet(?:DataProc|sProc))|moveUserDataProc)|Set(?:PropertyProc|UserDataProc)|Write(?:BytesProc|PacketsProc))?|F(?:DFTable(?:Extended)?|lags)|ID|Marker(?:List)?|P(?:acketTableInfo|ermissions|ropertyID)|Region(?:Flags|List)?|Stream(?:ID|P(?:arseFlags|roperty(?:Flags|ID))|SeekFlags|_P(?:acketsProc|ropertyListenerProc))|Type(?:AndFormatID|ID)|_(?:GetSizeProc|ReadProc|S(?:MPTE_Time|etSizeProc)|WriteProc))|ormat(?:Flags|I(?:D|nfo)|ListItem|PropertyID)|ramePacketTranslation)|Hardware(?:IOProcStreamUsage|P(?:owerHint|roperty(?:ID|ListenerProc)))|IndependentPacketTranslation|LevelControlTransferFunction|O(?:bject(?:ID|Property(?:Address|Element|Listener(?:Block|Proc)|S(?:cope|elector)))|utputUnitSt(?:art(?:AtTimeParams|Proc)|opProc))|Pa(?:cket(?:DependencyInfoTranslation|R(?:angeByteCountTranslation|ollDistanceTranslation))|nning(?:Info|Mode))|Queue(?:Buffer(?:Ref)?|ChannelAssignment|InputCallback(?:Block)?|LevelMeterState|OutputCallback(?:Block)?|P(?:arameter(?:Event|ID|Value)|ro(?:cessingTap(?:Callback|Flags|Ref)|perty(?:ID|ListenerProc)))|Ref|TimelineRef)|RecordingChunk(?:Ptr)?|S(?:ampleType|e(?:rvices(?:PropertyID|SystemSoundCompletionProc)|ttingsFlags)|tream(?:BasicDescription|ID|P(?:acketDescription|ropertyListenerProc)|RangedDescription))|TimeStamp(?:Flags)?|Unit(?:Add(?:PropertyListenerProc|RenderNotifyProc)|Co(?:coaViewInfo|mplexRenderProc|nnection)|E(?:lement|vent(?:Type)?|xternalBuffer)|FrequencyResponseBin|GetP(?:arameterProc|roperty(?:InfoProc|Proc))|InitializeProc|M(?:IDIControlMapping|eterClipping)|NodeConnection|OtherPluginDesc|P(?:arameter(?:Event|HistoryInfo|I(?:D(?:Name)?|nfo)|NameInfo|Options|StringFromValue|Unit|Value(?:FromString|Name|Translation)?)?|r(?:esetMAS_Setting(?:Data|s)|o(?:cess(?:MultipleProc|Proc)|perty(?:ID|ListenerProc)?)))|Re(?:move(?:PropertyListener(?:Proc|WithUserDataProc)|RenderNotifyProc)|nder(?:ActionFlags|Proc)|setProc)|S(?:ampleType|c(?:heduleParametersProc|ope)|etP(?:arameterProc|ropertyProc))|UninitializeProc)?|Value(?:Range|Translation))|thorization(?:AsyncCallback|E(?:nvironment|xternalForm)|Flags|Item(?:Set)?|OpaqueRef|R(?:ef|ights)|String)))|B(?:T(?:HeaderRec|NodeDescriptor|reeKey(?:Limits)?)|asicWindowDescription|i(?:gEndian(?:Fixed|Long|OSType|Short|U(?:Int32|nsigned(?:Fixed|Long|Short)))|tMap(?:Handle|Ptr)?)|sln(?:Baseline(?:Class|Record)|Format(?:0Part|1Part|2Part|3Part|Union)|Table(?:Format|Ptr)?))|C(?:A(?:BarBeatTime|Clock(?:Beats|ListenerProc|Message|PropertyID|Ref|S(?:MPTEFormat|amples|econds|yncMode)|T(?:empo|ime(?:Format|base)?))|F(?:Audio(?:Description|FormatListItem)|ChunkHeader|DataChunk|F(?:ileHeader|ormatFlags)|In(?:foStrings|strumentChunk)|Marker(?:Chunk)?|Overview(?:Chunk|Sample)|P(?:acketTableHeader|eakChunk|ositionPeak)|Region(?:Chunk|Flags)?|String(?:ID|s)|UMIDChunk|_(?:SMPTE_Time|UUID_ChunkHeader))|MeterTrackEntry|T(?:empoMapEntry|ransform3D))|C(?:Tab(?:Handle|Ptr)|_(?:LONG(?:64)?|MD(?:2(?:_CTX|state_st)|4(?:_CTX|state_st)|5(?:_CTX|state_st))|SHA(?:1(?:_CTX|state_st)|256(?:_CTX|state_st)|512(?:_CTX|state_st))))|E_(?:CrlNumber|D(?:ataType|eltaCrl)|ExtendedKeyUsage|GeneralNameType)|F(?:H(?:TTP(?:AuthenticationRef|MessageRef)|ost(?:ClientC(?:allBack|ontext)|InfoType|Ref))|Net(?:Diagnostic(?:Ref|StatusValues)|Service(?:Browser(?:ClientCallBack|Flags|Ref)|ClientC(?:allBack|ontext)|Monitor(?:ClientCallBack|Ref|Type)|Re(?:f|gisterFlags)|sError)|workErrors)|ProxyAutoConfigurationResultCallback|StreamErrorHTTP(?:Authentication)?)|G(?:Image(?:AnimationStatus|Destination(?:Ref)?|Metadata(?:Errors|Ref|T(?:ag(?:Ref)?|ype))?|PropertyOrientation|Source(?:AnimationBlock|Ref|Status)?)|L(?:C(?:PContextPriorityRequest|ontext(?:Enable|Obj|Parameter))|Error|G(?:PURestartStatus|lobalOption)|OpenGLProfile|P(?:BufferObj|ixelFormat(?:Attribute|Obj))|Renderer(?:InfoObj|Property)|ShareGroup(?:Obj|Rec))|MutableImageMetadataRef|rafP(?:ort|tr))|M(?:2(?:Header|Profile(?:Ptr)?)|4Header|AdaptationMatrixType|B(?:itmap|ufferLocation)|C(?:MY(?:Color|KColor)|oncatProfileSet|urveType)|D(?:at(?:aType|eTime(?:Type)?)|evice(?:Class|Info|Profile(?:Array|Info)|Scope))|F(?:ixedXY(?:Color|ZColor)|l(?:atten(?:ProcPtr|UPP)|oatBitmap(?:Flags)?))|GrayColor|H(?:LSColor|SVColor|andleLocation)|IntentCRDVMSize|L(?:abColor|u(?:t(?:16Type|8Type)|vColor))|M(?:CreateTransformPropertyProc|Info|akeAndModel(?:Type)?|easurementType|ulti(?:Funct(?:CLUTType|Lut(?:A2BType|Type))|LocalizedUniCode(?:EntryRec|Type)|channel(?:5Color|6Color|7Color|8Color)))|Na(?:medColor(?:2(?:EntryType|Type)|Type)?|tiveDisplayInfo(?:Type)?)|P(?:S2CRDVMSizeType|a(?:rametricCurveType|thLocation)|rofile(?:IterateData|Location|SequenceDescType))|RGBColor|S(?:15Fixed16ArrayType|CertificateChainMode|DecoderRef|EncoderRef|Signe(?:dAttributes|rStatus)|creening(?:ChannelRec|Type)|ignatureType)|T(?:ag(?:ElemTable|Record)|ext(?:DescriptionType|Type))|U(?:16Fixed16ArrayType|Int(?:16ArrayType|32ArrayType|64ArrayType|8ArrayType)|crBgType|nicodeTextType)|Vi(?:deoCardGamma(?:Formula|T(?:able|ype))?|ewingConditionsType)|XYZ(?:Co(?:lor|mponent)|Type)|YxyColor)|QDProcs(?:Ptr)?|S(?:ComponentsThreadMode|DiskSpaceRecovery(?:Callback|Options)|Identity(?:AuthorityRef|Cl(?:ass|ientContext)|Flags|Query(?:ClientContext|Event|Flags|Re(?:ceiveEventCallback|f)|StringComparisonMethod)|Ref|StatusUpdatedCallback)|SM_(?:A(?:C(?:L_(?:AUTHORIZATION_TAG|EDIT_MODE|HANDLE|KEYCHAIN_PROMPT_SELECTOR|PR(?:EAUTH_TRACKING_STATE|OCESS_SUBJECT_SELECTOR)|SUBJECT_TYPE)|_HANDLE)|LGORITHMS|PPLE(?:CSPDL_DB_(?:CHANGE_PASSWORD_PARAMETERS(?:_PTR)?|IS_LOCKED_PARAMETERS(?:_PTR)?|SETTINGS_PARAMETERS(?:_PTR)?)|DL_OPEN_PARAMETERS(?:_PTR)?|_(?:CL_CSR_REQUEST|TP_(?:ACTION_(?:DATA|FLAGS)|C(?:ERT_REQUEST|RL_OPT(?:IONS|_FLAGS))|NAME_OID|S(?:MIME_OPTIONS|SL_OPTIONS))))|TT(?:ACH_FLAGS|RIBUTE_TYPE))|B(?:ER_TAG|ITMASK|OOL)|C(?:ALLOC|C_HANDLE|ERT(?:GROUP_TYPE(?:_PTR)?|_(?:BUNDLE_(?:ENCODING|TYPE)|ENCODING(?:_PTR)?|PARSE_FORMAT(?:_PTR)?|TYPE(?:_PTR)?))|L_(?:HANDLE|TEMPLATE_TYPE)|ONTEXT_(?:EVENT|TYPE)|RL(?:GROUP_TYPE(?:_PTR)?|_(?:ENCODING(?:_PTR)?|PARSE_FORMAT(?:_PTR)?|TYPE(?:_PTR)?))|SP(?:TYPE|_(?:FLAGS|HANDLE|READER_FLAGS)))|D(?:B_(?:A(?:CCESS_TYPE(?:_PTR)?|TTRIBUTE_(?:FORMAT(?:_PTR)?|NAME_FORMAT(?:_PTR)?))|CONJUNCTIVE(?:_PTR)?|HANDLE|INDEX(?:ED_DATA_LOCATION|_TYPE)|MODIFY_MODE|OPERATOR(?:_PTR)?|RE(?:CORDTYPE|TRIEVAL_MODES))|L(?:TYPE(?:_PTR)?|_(?:CUSTOM_ATTRIBUTES|FFS_ATTRIBUTES|HANDLE|LDAP_ATTRIBUTES|ODBC_ATTRIBUTES|PKCS11_ATTRIBUTE(?:_PTR)?)))|E(?:NCRYPT_MODE|VIDENCE_FORM)|FREE|H(?:ANDLE(?:_PTR)?|EADERVERSION)|INTPTR|K(?:EY(?:ATTR_FLAGS|BLOB_(?:FORMAT|TYPE)|CLASS|USE|_(?:HIERARCHY|TYPE))|R(?:SP_HANDLE|_POLICY_(?:FLAGS|TYPE)))|L(?:IST_(?:ELEMENT_(?:PTR|TYPE(?:_PTR)?)|TYPE(?:_PTR)?)|ONG_HANDLE(?:_PTR)?)|M(?:A(?:LLOC|NAGER_EVENT_TYPES)|ODULE_(?:EVENT(?:_PTR)?|HANDLE(?:_PTR)?))|NET_(?:ADDRESS_TYPE|PROTOCOL)|P(?:ADDING|KCS(?:5_PBKDF2_PRF|_OAEP_(?:MGF|PSOURCE))|R(?:IVILEGE(?:_SCOPE)?|OC_ADDR(?:_PTR)?)|VC_MODE)|QUERY_FLAGS|RE(?:ALLOC|TURN)|S(?:AMPLE_TYPE|C_FLAGS|ERVICE_(?:MASK|TYPE)|IZE|TRING)|T(?:IMESTRING|P_(?:A(?:CTION|PPLE_(?:CERT_STATUS|EVIDENCE_HEADER)|UTHORITY_REQUEST_TYPE(?:_PTR)?)|C(?:ERT(?:CHANGE_(?:ACTION|REASON|STATUS)|ISSUE_STATUS|NOTARIZE_STATUS|RECLAIM_STATUS|VERIFY_STATUS)|ONFIRM_STATUS(?:_PTR)?|RLISSUE_STATUS)|FORM_TYPE|HANDLE|S(?:ERVICES|TOP_ON)))|USEE_TAG|WORDID_TYPE|X509(?:EXT_DATA_FORMAT|_OPTION))|pecArray)|T(?:CharacterCollection|F(?:ont(?:Collection(?:CopyOptions|Ref|SortDescriptorsCallback)|Descriptor(?:MatchingState|Ref)|Format|Manager(?:AutoActivationSetting|Error|Scope)|O(?:ptions|rientation)|Priority|Ref|S(?:tylisticClass|ymbolicTraits)|Table(?:Options|Tag)|UIFontType)|rame(?:P(?:athFillRule|rogression)|Ref|setterRef))|GlyphInfoRef|Line(?:B(?:oundsOptions|reakMode)|Ref|TruncationType)|MutableFontCollectionRef|ParagraphStyle(?:Ref|S(?:etting|pecifier))|Ru(?:by(?:A(?:lignment|nnotationRef)|Overhang|Position)|n(?:Delegate(?:Callbacks|DeallocateCallback|Get(?:AscentCallback|DescentCallback|WidthCallback)|Ref)|Ref|Status))|T(?:ext(?:Alignment|TabRef)|ypesetterRef)|UnderlineStyle(?:Modifiers)?|WritingDirection|ab(?:Handle|Ptr))|V(?:AttachmentMode|BufferRef|DisplayLink(?:Output(?:Callback|Handler)|Ref)|FillExtendedPixelsCallBack(?:Data)?|ImageBufferRef|Op(?:enGL(?:Buffer(?:PoolRef|Ref)|Texture(?:CacheRef|Ref))|tionFlags)|P(?:ixelBuffer(?:LockFlags|Pool(?:FlushFlags|Ref)|Re(?:f|lease(?:BytesCallback|PlanarBytesCallback)))|lanar(?:ComponentInfo|PixelBufferInfo(?:_YCbCr(?:BiPlanar|Planar))?))|Return|SMPTETime(?:Flags|Type)?|Time(?:Flags|Stamp(?:Flags)?)?)|a(?:l(?:ibrat(?:e(?:Event(?:ProcPtr|UPP)|ProcPtr|UPP)|orInfo)|lingConventionType)|nCalibrate(?:ProcPtr|UPP)|retHook(?:ProcPtr|UPP)|tPositionRec)|ell|h(?:ar(?:ByteTable|s(?:Handle|Ptr)?)|unkHeader)|lickActivationResult|o(?:l(?:l(?:atorRef|ection(?:Exception(?:ProcPtr|UPP)|Flatten(?:ProcPtr|UPP)|Tag)?)|or(?:C(?:hangedUPP|omplement(?:ProcPtr|UPP))|S(?:earch(?:ProcPtr|UPP)|pec(?:Ptr)?|ync(?:AlphaInfo|CMM(?:Ref)?|Data(?:Depth|Layout)|M(?:D5|utableProfileRef)|Profile(?:Ref)?|Transform(?:Ref)?))|Table))|m(?:m(?:ent(?:Type|sChunk(?:Ptr)?)?|onChunk(?:Ptr)?)|ponent(?:AliasResource|Description|FunctionUPP|Instance(?:Record)?|MPWorkFunction(?:HeaderRecord(?:Ptr)?|ProcPtr|UPP)|P(?:arameters|latformInfo(?:Array)?)|R(?:e(?:cord|s(?:ource(?:Extension|Handle|Ptr)?|ult))|outine(?:ProcPtr|UPP)))?)|n(?:st(?:ATSUAttributeValuePtr|FS(?:EventStreamRef|SpecPtr)|HFSUniStr255Param|ScriptCodeRunPtr|Text(?:EncodingRunPtr|Ptr|ToUnicodeInfo)|Uni(?:CharArrayPtr|code(?:MappingPtr|ToTextInfo)))|t(?:ainerChunk|extualMenuInterfaceStruct|rol(?:Action(?:ProcPtr|UPP)|B(?:evel(?:Button(?:Behavior|Menu(?:Behavior|Placement))|Thickness)|utton(?:ContentInfo(?:Ptr)?|GraphicAlignment|Text(?:Alignment|Placement)))|C(?:lock(?:Flags|Type)|ontentType)|DisclosureTriangleOrientation|EditText(?:Selection(?:Ptr|Rec)|Validation(?:ProcPtr|UPP))|Fo(?:cusPart|ntStyle(?:Ptr|Rec))|Handle|I(?:D|mageContentInfo(?:Ptr)?)|K(?:ey(?:Filter(?:ProcPtr|Result|UPP)|ScriptBehavior)|ind)|P(?:artCode|opupArrow(?:Orientation|Size)|ushButtonIconAlignment)|R(?:ef|oundButtonSize)|S(?:ize|liderOrientation)|T(?:ab(?:Direction|Entry|InfoRec(?:V1)?|Size)|emplate(?:Handle|Ptr)?)|UserPane(?:Activate(?:ProcPtr|UPP)|Draw(?:ProcPtr|UPP)|Focus(?:ProcPtr|UPP)|HitTest(?:ProcPtr|UPP)|Idle(?:ProcPtr|UPP)|KeyDown(?:ProcPtr|UPP)|Tracking(?:ProcPtr|UPP))|Variant)))|reEndianFlipProc|untUserDataFDF)|tlCTab|ustomBadgeResource(?:Handle|Ptr)?)|D(?:A(?:ApprovalSessionRef|DiskRef|SessionRef)|C(?:M(?:AccessMethod(?:Feature|I(?:D|terator))|Dictionary(?:Header|I(?:D|terator)|Ref|StreamRef)|F(?:i(?:eld(?:Attributes|T(?:ag|ype))|ndMethod)|oundRecordIterator)|Object(?:I(?:D|terator)|Ref)|ProgressFilter(?:ProcPtr|UPP)|UniqueID)|SDictionaryRef)|ER(?:Byte|Item|Size)|I(?:TLMethod|nfo)|R(?:AudioTrackRef|BurnRef|CDTextBlockRef|DeviceRef|EraseRef|F(?:SObjectRef|ile(?:Fork(?:Index|Size(?:Info|Query))|Message|Pro(?:c|ductionInfo)|Ref|system(?:Mask|TrackRef))|olderRef)|LinkType|NotificationC(?:allback|enterRef)|RefCon(?:Callbacks|Re(?:leaseCallback|tainCallback))|T(?:rack(?:CallbackProc|Message|ProductionInfo|Ref)|ypeRef))|XInfo|at(?:a(?:Array|Browser(?:A(?:cce(?:ptDrag(?:ProcPtr|UPP)|ssibilityItemInfo(?:V(?:0|1))?)|ddDragItem(?:ProcPtr|UPP))|C(?:allbacks|ustomCallbacks)|Dra(?:gFlags|wItem(?:ProcPtr|UPP))|Edit(?:Command|Item(?:ProcPtr|UPP))|GetContextualMenu(?:ProcPtr|UPP)|HitTest(?:ProcPtr|UPP)|Item(?:AcceptDrag(?:ProcPtr|UPP)|Compare(?:ProcPtr|UPP)|D(?:ata(?:ProcPtr|Ref|UPP)|ragRgn(?:ProcPtr|UPP))|HelpContent(?:ProcPtr|UPP)|ID|Notification(?:ProcPtr|UPP|WithItem(?:ProcPtr|UPP))?|ProcPtr|ReceiveDrag(?:ProcPtr|UPP)|State|UPP)|ListView(?:ColumnDesc|HeaderDesc|PropertyFlags)|Metric|P(?:ostProcessDrag(?:ProcPtr|UPP)|roperty(?:Desc|Flags|ID|Part|Type))|Re(?:ceiveDrag(?:ProcPtr|UPP)|vealOptions)|S(?:e(?:lect(?:ContextualMenu(?:ProcPtr|UPP)|ion(?:AnchorDirection|Flags))|tOption)|ortOrder)|T(?:ableView(?:Column(?:Desc|I(?:D|ndex))|HiliteStyle|PropertyFlags|RowIndex)|racking(?:ProcPtr|Result|UPP))|ViewStyle)|Handle|Ptr)|e(?:Cache(?:Ptr|Record)|Delta|Form|Orders|TimeRec))|e(?:bug(?:AssertOutputHandler(?:ProcPtr|UPP)|ComponentCallback(?:ProcPtr|UPP)|ger(?:DisposeThread(?:ProcPtr|TPP|UPP)|NewThread(?:ProcPtr|TPP|UPP)|ThreadScheduler(?:ProcPtr|TPP|UPP)))|ferredTask(?:P(?:rocPtr|tr)|UPP)?|lim(?:Type|iterInfo)|s(?:cType|kHook(?:ProcPtr|UPP)))|ialog(?:Item(?:Index(?:ZeroBased)?|Type)|P(?:lacementSpec|tr)|Ref|T(?:Hndl|Ptr|emplate))|o(?:Get(?:FileTranslationListProcPtr|ScrapTranslationListProcPtr|TranslatedFilenameProcPtr)|Identify(?:FileProcPtr|ScrapProcPtr)|Translate(?:FileProcPtr|ScrapProcPtr)|cOpenMethod)|ra(?:g(?:A(?:ctions|ttributes)|Behaviors|Constraint|Drawing(?:ProcPtr|UPP)|GrayRgn(?:ProcPtr|UPP)|I(?:mageFlags|nput(?:ProcPtr|UPP)|temRef)|Re(?:ceiveHandler(?:ProcPtr|UPP)|f(?:erence)?|gionMessage)|SendData(?:ProcPtr|UPP)|Tracking(?:Handler(?:ProcPtr|UPP)|Message))|wHook(?:ProcPtr|UPP)))|E(?:OLHook(?:ProcPtr|UPP)|ditUnicodePostUpdate(?:ProcPtr|UPP)|v(?:Cmd|QEl(?:Ptr)?|ent(?:Attributes|C(?:lass(?:ID)?|omparator(?:ProcPtr|UPP))|H(?:andler(?:CallRef|ProcPtr|Ref|UPP)|otKey(?:ID|Ref))|Kind|Loop(?:IdleTimer(?:Message|ProcPtr|UPP)|Ref|Timer(?:ProcPtr|Ref|UPP))|M(?:ask|o(?:difiers|use(?:Button|WheelAxis)))|P(?:aram(?:Name|Type)|riority)|QueueRef|Re(?:cord|f)|T(?:argetRef|ime(?:out|rInterval)?|ype(?:Spec)?)))|x(?:ception(?:Handler(?:ProcPtr|TPP|UPP)?|Info(?:rmation(?:PowerPC)?)?|Kind)|t(?:AudioFile(?:PropertyID|Ref)|Com(?:monChunk(?:Ptr)?|ponentResource(?:Handle|Ptr)?)|ended(?:AudioFormatInfo|ControlEvent|F(?:ileInfo|olderInfo)|NoteOnEvent|TempoEvent))))|F(?:CFontDescriptorRef|I(?:LE|nfo)|KEY(?:ProcPtr|UPP)|M(?:F(?:ilter(?:Selector)?|ont(?:CallbackFilter(?:ProcPtr|UPP)|DirectoryFilter|Family(?:CallbackFilter(?:ProcPtr|UPP)|I(?:nstance(?:Iterator)?|terator))?|Iterator|S(?:ize|tyle))?)|Generation|Input)|N(?:Message|Subscription(?:ProcPtr|Ref|UPP))|P(?:RegIntel|UInformation(?:Intel64|PowerPC)?)|S(?:Al(?:ias(?:FilterProcPtr|Info(?:Bitmap|Ptr)?)|locationFlags)|Catalog(?:BulkParam(?:Ptr)?|Info(?:Bitmap|Ptr)?)|E(?:jectStatus|ventStream(?:C(?:allback|ontext|reateFlags)|Event(?:Flags|Id)|Ref))|F(?:ile(?:Operation(?:ClientContext|Ref|Sta(?:ge|tusProcPtr))|SecurityRef)|ork(?:CBInfoParam(?:Ptr)?|I(?:OParam(?:Ptr)?|nfo(?:Flags|Ptr)?)))|I(?:ORefNum|terator(?:Flags)?)|MountStatus|P(?:athFileOperationStatusProcPtr|ermissionInfo)|R(?:angeLockParam(?:Ptr)?|ef(?:ForkIOParam(?:Ptr)?|P(?:aram(?:Ptr)?|tr))?)|S(?:earchParams(?:Ptr)?|pec(?:ArrayPtr|Handle|Ptr)?)|UnmountStatus|Volume(?:Eject(?:ProcPtr|UPP)|Info(?:Bitmap|P(?:aram(?:Ptr)?|tr))?|Mount(?:ProcPtr|UPP)|Operation|RefNum|Unmount(?:ProcPtr|UPP)))|Vector|XInfo|amRec|ile(?:Info|T(?:ranslation(?:List(?:Handle|Ptr)?|Spec(?:Array(?:Handle|Ptr))?)|ype(?:Spec)?))|lavor(?:Flags|Type)|ndr(?:DirInfo|Extended(?:DirInfo|FileInfo)|FileInfo|OpaqueInfo)|o(?:lder(?:Class|Desc(?:Flags|Ptr)?|Info|Location|ManagerNotification(?:ProcPtr|UPP)|Routing(?:Ptr)?|Type)|nt(?:Assoc|Info|LanguageCode|NameCode|PlatformCode|Rec(?:Hdl|Ptr)?|S(?:criptCode|electionQDStyle(?:Ptr)?)|Variation)|rmat(?:Class|ResultType|Status|VersionChunk(?:Ptr)?)))|G(?:D(?:Handle|Ptr|evice)|L(?:b(?:itfield|oolean|yte)|c(?:har(?:ARB)?|lamp(?:d|f))|double|enum|f(?:ixed|loat)|ha(?:lf(?:ARB)?|ndleARB)|int(?:64(?:EXT)?|ptr(?:ARB)?)?|s(?:hort|izei(?:ptr(?:ARB)?)?|ync)|u(?:byte|int(?:64(?:EXT)?)?|short)|void)|NEFilterUPP|World(?:Flags|Ptr)|e(?:nericID|t(?:GrowImageRegionRec|MissingComponentResource(?:ProcPtr|UPP)|NextEventFilter(?:ProcPtr|UPP)|Property(?:FDF|InfoFDF)|ScrapData(?:ProcPtr|UPP)?|UserData(?:FDF|SizeFDF)|VolParmsInfoBuffer|WindowRegion(?:Ptr|Rec(?:Ptr)?)))|lyph(?:Collection|ID)|raf(?:P(?:ort|tr)|Verb))|H(?:FS(?:Catalog(?:F(?:ile|older)|Key|NodeID|Thread)|Extent(?:Descriptor|Key|Record)|Flavor|MasterDirectoryBlock|Plus(?:Attr(?:Data|Extents|ForkData|InlineData|Key|Record)|BSDInfo|Catalog(?:F(?:ile|older)|Key|Thread)|Extent(?:Descriptor|Key|Record)|ForkData|VolumeHeader)|UniStr255)|I(?:A(?:rchiveRef|xis(?:Position|Scale))|Binding(?:Kind)?|Co(?:mmand(?:Extended)?|ntentBorderMetrics|ordinateSpace)|DelegatePosition|ImageViewAutoTransformOptions|LayoutInfo|M(?:odalClickResult|utableShapeRef)|Object(?:ClassRef|Ref)|Po(?:int|sition(?:Kind|ing))|Rect|S(?:c(?:al(?:eKind|ing)|roll(?:BarTrackInfo|ViewAction))|egmentBehavior|hape(?:EnumerateProcPtr|Ref)|i(?:deBinding|ze))|T(?:heme(?:Animation(?:FrameInfo|TimeInfo)|B(?:ackgroundDrawInfo(?:Ptr)?|uttonDrawInfo(?:Ptr)?)|ChasingArrowsDrawInfo(?:Ptr)?|F(?:ocusRing|rame(?:DrawInfo(?:Ptr)?|Kind))|Gr(?:abberDrawInfo(?:Ptr)?|o(?:upBox(?:DrawInfo(?:Ptr)?|Kind)|wBox(?:DrawInfo(?:Ptr)?|Kind|Size)))|Header(?:DrawInfo(?:Ptr)?|Kind)|Menu(?:BarDrawInfo(?:Ptr)?|DrawInfo(?:Ptr|VersionZero(?:Ptr)?)?|ItemDrawInfo(?:Ptr)?|TitleDrawInfo(?:Ptr)?)|Orientation|P(?:lacardDrawInfo(?:Ptr)?|opupArrowDrawInfo(?:Ptr)?)|S(?:crollBarDelimitersDrawInfo(?:Ptr)?|e(?:gment(?:Adornment|DrawInfo(?:Ptr)?|Kind|Position|Size)|paratorDrawInfo(?:Ptr)?)|plitter(?:Adornment|DrawInfo(?:Ptr)?))|T(?:ab(?:Adornment|DrawInfo(?:VersionZero)?|Kind|P(?:ane(?:Adornment|DrawInfo(?:VersionZero)?)|osition)|Size)|ext(?:BoxOptions|HorizontalFlush|Info|Truncation|VerticalFlush)|ickMarkDrawInfo(?:Ptr)?|rackDrawInfo)|Window(?:DrawInfo(?:Ptr)?|WidgetDrawInfo(?:Ptr)?))|oolbar(?:Display(?:Mode|Size)|ItemRef|Ref)|ypeAndCreator)|View(?:Content(?:Info(?:Ptr)?|Type)|F(?:eatures|rameMetrics)|I(?:D|mageContent(?:Info|Type))|Kind|PartCode|Ref|TrackingArea(?:ID|Ref)|ZOrderOp)|Window(?:Availability|BackingLocation|Depth|Ref|S(?:caleMode|haringType)))|M(?:Cont(?:ent(?:ProvidedType|Request|Type)|rolContent(?:ProcPtr|UPP))|HelpContent(?:Ptr|Rec)?|Menu(?:ItemContent(?:ProcPtr|UPP)|TitleContent(?:ProcPtr|UPP))|StringResType|TagDisplaySide|WindowContent(?:ProcPtr|UPP)|enuBar(?:Header|Menu))|i(?:ghHook(?:ProcPtr|UPP)|liteMenuItemData(?:Ptr)?|tTestHook(?:ProcPtr|UPP))|o(?:mograph(?:Accent|DicInfoRec|Weight)|stCallback(?:Info|_Get(?:BeatAndTempo|MusicalTimeLocation|TransportState(?:2)?))))|I(?:BNibRef|C(?:A(?:ppSpec(?:Handle|List(?:Handle|Ptr)?|Ptr)?|ttr)|CharTable(?:Handle|Ptr)?|F(?:i(?:leSpec(?:Handle|Ptr)?|xedLength)|ontRecord(?:Handle|Ptr)?)|Instance|MapEntry(?:Flags|Handle|Ptr)?|P(?:erm|rofileID(?:Ptr)?)|Service(?:Entry(?:Flags|Handle|Ptr)?|s(?:Handle|Ptr)?))|O(?:A(?:ddressRange|lignment|ppleTimingID|syncC(?:allback(?:0|1|2)?|ompletionContent))|ByteCount(?:32|64)?|C(?:acheMode|o(?:lor(?:Component|Entry)|mpletion(?:ProcPtr|UPP)))|D(?:e(?:tailedTimingInformation(?:V(?:1|2))?|viceNumber)|isplay(?:ModeI(?:D|nformation)|ProductID|ScalerInformation|TimingRange(?:V(?:1|2))?|VendorID))|F(?:B(?:D(?:PLinkConfig|isplayModeDescription)|HDRMetaData(?:V1)?)|ixed(?:1616|Point32)?|ramebufferInformation)|G(?:Bounds|Point|Size)|HardwareCursor(?:Descriptor|Info)|I(?:ndex|temCount)|LogicalAddress|N(?:amedValue|otificationPort(?:Ref)?)|OptionBits|P(?:hysical(?:Address(?:32|64)?|Length(?:32|64)?|Range)|ixel(?:Aperture|Encoding|Information))|Return|S(?:e(?:lect|rvice(?:InterestC(?:allback|ontent(?:64)?)|MatchingCallback))|urface(?:Component(?:Name|Range|Type)|ID|LockOptions|PurgeabilityState|Ref|Subsampling))|TimingInformation|V(?:ersion|irtual(?:Address|Range)))|SAType|con(?:A(?:ction(?:ProcPtr|UPP)|lignmentType)|Family(?:Element|Handle|Ptr|Resource)|Getter(?:ProcPtr|UPP)|Ref|Se(?:lectorValue|rvicesUsageFlags)|TransformType)|n(?:d(?:exToUCString(?:ProcPtr|UPP)|icatorDragConstraint(?:Ptr)?)|strumentChunk(?:Ptr)?|t(?:erfaceTypeList|l(?:0(?:Hndl|Ptr|Rec)|1(?:Hndl|Ptr|Rec)|Text)))|t(?:emReference|l(?:1ExtRec|4(?:Handle|Ptr|Rec)|5Record|b(?:ExtRecord|Record)|cRecord)))|J(?:S(?:C(?:har|lass(?:Attributes|Definition|Ref)|ontext(?:GroupRef|Ref))|GlobalContextRef|Object(?:C(?:allAs(?:ConstructorCallback|FunctionCallback)|onvertToTypeCallback)|FinalizeCallback|GetProperty(?:Callback|NamesCallback)|InitializeCallback|Ref)|Property(?:Attributes|NameA(?:ccumulatorRef|rrayRef))|St(?:atic(?:Function|Value)|ringRef)|Type(?:dArrayBytesDeallocator)?|ValueRef)|apanesePartOfSpeech|ournalInfoBlock|ust(?:DirectionTable|P(?:C(?:Action(?:Subrecord|Type)?|ConditionalAddAction|D(?:ecompositionAction|uctilityAction)|GlyphRepeatAddAction|UnconditionalAddAction)|ostcompTable)|Table|WidthDelta(?:Entry|Group)|ificationFlags))|K(?:C(?:A(?:ttr(?:Type|ibute(?:List)?)|uthType)|C(?:allback(?:Info|ProcPtr|UPP)|ert(?:AddOptions|SearchOptions))|Event(?:Mask)?|Item(?:Attr|Class|Ref)|P(?:rotocolType|ublicKeyHash)|Ref|S(?:earchRef|tatus)|VerifyStopOn)|e(?:r(?:n(?:ArrayOffset|Entry|FormatSpecificHeader|IndexArrayHeader|Kerning(?:Pair|Value)|O(?:ffsetTable(?:Ptr)?|rderedList(?:Entry(?:Ptr)?|Header))|Pair|S(?:impleArrayHeader|tate(?:Entry|Header)|ubtable(?:Header(?:Ptr)?|Info))|Table(?:Format|Header(?:Handle|Ptr)?)?|Version0(?:Header|SubtableHeader))|x(?:A(?:nchorPointAction|rrayOffset)|Co(?:ntrolPoint(?:Action|Entry|Header)|ordinateAction)|FormatSpecificHeader|IndexArrayHeader|KerningPair|OrderedList(?:Entry(?:Ptr)?|Header)|S(?:impleArrayHeader|tate(?:Entry|Header)|ubtable(?:Coverage|Header(?:Ptr)?))|TableHeader(?:Handle|Ptr)?))|y(?:Map(?:ByteArray)?|boardLayout(?:Identifier|Kind|PropertyTag|Ref))))|L(?:A(?:ContextRef|EnvironmentRef|Homograph|Morpheme(?:Bundle|Path|Rec|sArray(?:Ptr)?)?|Property(?:Key|Type))|H(?:Element|Handle|Ptr|Table)|LCStyleInfo|S(?:A(?:cceptanceFlags|pplicationParameters)|HandlerOptions|ItemInfo(?:Flags|Record)|Launch(?:F(?:SRefSpec|lags)|URLSpec)|R(?:equestedInfo|olesMask)|SharedFileList(?:ChangedProcPtr|ItemRef|Re(?:f|solutionFlags)))|aunch(?:Flags|P(?:BPtr|aramBlockRec))|carCaret(?:ClassEntry|Table(?:Ptr)?)|ist(?:Bounds|ClickLoop(?:ProcPtr|UPP)|Def(?:ProcPtr|Spec(?:Ptr)?|Type|UPP)|Handle|Ptr|Re(?:c|f)|Search(?:ProcPtr|UPP))|o(?:cal(?:DateTime(?:Handle|Ptr)?|e(?:AndVariant|NameMask|Operation(?:Class|Variant)|PartMask|Ref))|ngDate(?:Cvt|Field|Rec|Time))|tag(?:StringRange|Table))|M(?:BarHook(?:ProcPtr|UPP)|C(?:Entry(?:Ptr)?|Table(?:Handle|Ptr)?)|D(?:EF(?:Draw(?:Data(?:Ptr)?|ItemsData(?:Ptr)?)|FindItemData(?:Ptr)?|HiliteItemData(?:Ptr)?)|ItemRef|Label(?:Domain|Ref)|Query(?:BatchingParams|Create(?:ResultFunction|ValueFunction)|OptionFlags|Ref|Sort(?:ComparatorFunction|OptionFlags))|S_HANDLE)|IDI(?:C(?:hannelMessage|lientRef|ompletionProc)|D(?:ataChunk(?:Ptr)?|eviceRef)|En(?:dpointRef|tityRef)|IOErrorNotification|MetaEvent|Not(?:eMessage|if(?:ication(?:MessageID)?|y(?:Block|Proc)))|Object(?:AddRemoveNotification|PropertyChangeNotification|Ref|Type)|P(?:acket(?:List)?|ortRef)|R(?:awData|ead(?:Block|Proc))|SysexSendRequest|TimeStamp|UniqueID)|P(?:A(?:ddressSpaceI(?:D|nfo)|reaID)|C(?:o(?:herenceID|nsoleID)|puID|riticalRegionI(?:D|nfo))|DebuggerLevel|E(?:G4ObjectID|vent(?:Flags|I(?:D|nfo))|xceptionKind)|IsFullyInitializedProc|NotificationI(?:D|nfo)|OpaqueID(?:Class)?|P(?:ageSizeClass|rocessID)|QueueI(?:D|nfo)|Remote(?:Context|Procedure)|Semaphore(?:Count|I(?:D|nfo))|T(?:ask(?:I(?:D|nfo(?:Version2)?)|Options|StateKind|Weight)|imerID))|a(?:c(?:Polygon|hine(?:Information(?:Intel64|PowerPC)?|Location))|gicCookieInfo|rker(?:Chunk(?:Ptr)?|IdType)?)|e(?:asureWindowTitleRec(?:Ptr)?|mory(?:ExceptionInformation|ReferenceKind)|nu(?:Attributes|Bar(?:Def(?:ProcPtr|UPP)|H(?:andle|eader)|Menu)|C(?:Rsrc(?:Handle|Ptr)?|ommand)|Def(?:Spec(?:Ptr)?|Type|UPP)|EventOptions|H(?:andle|ook(?:ProcPtr|UPP))|I(?:D|tem(?:Attributes|D(?:ata(?:Flags|Ptr|Rec)|rawing(?:ProcPtr|UPP))|I(?:D|ndex)))|Ref|T(?:itleDrawing(?:ProcPtr|UPP)|racking(?:Data(?:Ptr)?|Mode))))|ixe(?:dModeStateRecord|rDistanceParams)|o(?:dalFilter(?:ProcPtr|UPP|YD(?:ProcPtr|UPP))|r(?:pheme(?:PartOfSpeech|TextRange)|t(?:C(?:hain|ontextualSubtable)|FeatureEntry|InsertionSubtable|Ligature(?:ActionEntry|Subtable)|RearrangementSubtable|S(?:pecificSubtable|ubtable(?:MaskFlags)?|washSubtable)|Table)|x(?:C(?:hain|ontextualSubtable)|InsertionSubtable|LigatureSubtable|RearrangementSubtable|S(?:pecificSubtable|ubtable)|Table))|useTrackingResult)|usic(?:Device(?:Component|GroupID|InstrumentID|MIDIEventProc|NoteParams|S(?:t(?:artNoteProc|dNoteParams|opNoteProc)|ysExProc))|Event(?:Iterator|Type|UserData)|Player|Sequence(?:File(?:Flags|TypeID)|LoadFlags|Type|UserCallback)?|T(?:imeStamp|rack(?:LoopInfo)?)))|N(?:C(?:M(?:ConcatProfileS(?:et|pec)|DeviceProfileInfo)|olor(?:Changed(?:ProcPtr|UPP)|PickerInfo))|DR_record_t|Itl4(?:Handle|Ptr|Rec)|M(?:ProcPtr|Rec(?:Ptr)?|UPP)|PMColor(?:Ptr)?|WidthHook(?:ProcPtr|UPP)|X(?:ByteOrder|Coord|Event(?:Data|Ext(?:ension)?|Ptr|System(?:Device(?:List)?|Info(?:Data|Type)))?|KeyMapping|Mouse(?:Button|Scaling)|Point|S(?:ize|wapped(?:Double|Float))|TabletP(?:ointData(?:Ptr)?|roximityData(?:Ptr)?))|a(?:meTable|noseconds)|ote(?:InstanceID|ParamsControlValue)|u(?:llSt(?:Handle|Ptr|Rec)|m(?:FormatString(?:Rec)?|berParts(?:Ptr)?)))|O(?:S(?:A(?:Active(?:ProcPtr|UPP)|CreateAppleEvent(?:ProcPtr|UPP)|Error|ID|Send(?:ProcPtr|UPP)|syncReference(?:64)?|tomic_int64_aligned64_t)|FifoQueueHead|L(?:A(?:ccessor(?:ProcPtr|UPP)|djustMarks(?:ProcPtr|UPP))|Co(?:mpare(?:ProcPtr|UPP)|unt(?:ProcPtr|UPP))|DisposeToken(?:ProcPtr|UPP)|Get(?:ErrDesc(?:ProcPtr|UPP)|MarkToken(?:ProcPtr|UPP))|Mark(?:ProcPtr|UPP))|NotificationHeader(?:64)?|QueueHead)|ff(?:Pair|set(?:Array(?:Handle|Ptr)?|Table))|p(?:aque(?:A(?:E(?:DataStorageType|StreamRef)|TSU(?:FontFallbacks|Style|TextLayout)|UGraph|reaID|udio(?:Co(?:mponent|nverter)|FileStreamID|Queue(?:ProcessingTap|Timeline)?))|C(?:AClock|M(?:ProfileRef|WorldRef)|o(?:ll(?:atorRef|ection)|ntrolRef))|D(?:CM(?:FoundRecordIterator|Object(?:I(?:D|terator)|Ref))|ialogPtr|ragRef)|E(?:vent(?:H(?:andler(?:CallRef|Ref)|otKeyRef)|LoopRef|QueueRef|Ref|TargetRef)|xtAudioFile)|F(?:CFontDescriptorRef|NSubscriptionRef|S(?:Iterator|VolumeOperation))|GrafPtr|HI(?:ArchiveRef|Object(?:ClassRef|Ref)|ViewTrackingAreaRef)|I(?:BNibRef|CInstance|conRef)|JS(?:C(?:lass|ontext(?:Group)?)|PropertyNameA(?:ccumulator|rray)|String|Value)|KeyboardLayoutRef|L(?:A(?:ContextRef|EnvironmentRef)|SSharedFileList(?:ItemRef|Ref)|ocaleRef)|M(?:P(?:A(?:ddressSpaceID|reaID)|C(?:o(?:herenceID|nsoleID)|puID|riticalRegionID)|EventID|NotificationID|OpaqueID|ProcessID|QueueID|SemaphoreID|T(?:askID|imerID))|enuRef|usic(?:EventIterator|Player|Sequence|Track))|P(?:M(?:P(?:a(?:geFormat|per)|r(?:eset|int(?:Se(?:ssion|ttings)|er)))|Server)|asteboardRef|icker|olicySearchRef)|RgnHandle|S(?:RSpeechObject|crapRef|ec(?:AccessRef|CertificateRef|Identity(?:Ref|SearchRef)|KeyRef|TransformImplementation))|T(?:EC(?:ObjectRef|SnifferObjectRef)|SMDocumentID|XNObject|ext(?:BreakLocatorRef|ToUnicodeInfo)|hemeDrawingState|oolboxObjectClassRef|ranslationRef)|U(?:CTypeSelectRef|RLReference|nicodeToText(?:Info|RunInfo))|W(?:S(?:MethodInvocationRef|ProtocolHandlerRef)|indow(?:GroupRef|Ptr)))|bd(?:SideValues|Table(?:Format)?)|enCPicParams))|P(?:EF(?:ContainerHeader|ExportedSymbol(?:HashSlot|Key)?|Imported(?:Library|Symbol)|Loader(?:InfoHeader|RelocationHeader)|RelocChunk|S(?:ectionHeader|plitHashWord))|M(?:BorderType|ColorSpaceModel|D(?:ataFormat|estinationType|uplexMode)|La(?:nguageInfo|youtDirection)|O(?:bject|rientation)|P(?:PDDomain|a(?:ge(?:Format|ToPaperMappingType)|per(?:Margins|Type)?)|r(?:eset|int(?:DialogOptionFlags|Se(?:ssion|ttings)|er(?:State)?)))|QualityMode|Re(?:ct|solution)|S(?:calingAlignment|erver))|a(?:r(?:am(?:BlockRec|eterEvent)|mBlkPtr)|steboard(?:FlavorFlags|ItemID|PromiseKeeperProcPtr|Ref|S(?:tandardLocation|yncFlags))|t(?:Handle|Ptr|tern))|h(?:oneme(?:Descriptor|Info)|ysicalKeyboardLayoutType)|i(?:c(?:Handle|Ptr|ker(?:MenuItemInfo)?|ture)|x(?:Map(?:Handle|Ptr)?|Pat(?:Handle|Ptr)?))|lotIconRefFlags|oly(?:Handle|Ptr|gon)|r(?:interStatusOpcode|o(?:c(?:InfoType|ess(?:ApplicationTransformState|Info(?:ExtendedRec(?:Ptr)?|Rec(?:Ptr)?)))|gressTrackInfo|miseHFSFlavor|p(?:CharProperties|LookupS(?:egment|ingle)|Table|erty(?:Creator|Tag)))))|Q(?:D(?:Arc(?:ProcPtr|UPP)|Bits(?:ProcPtr|UPP)|Comment(?:ProcPtr|UPP)|Err|GetPic(?:ProcPtr|UPP)|JShieldCursor(?:ProcPtr|UPP)|Line(?:ProcPtr|UPP)|O(?:pcode(?:ProcPtr|UPP)|val(?:ProcPtr|UPP))|P(?:oly(?:ProcPtr|UPP)|rinterStatus(?:ProcPtr|UPP)|utPic(?:ProcPtr|UPP))|R(?:Rect(?:ProcPtr|UPP)|e(?:ct(?:ProcPtr|UPP)|gionParseDirection)|gn(?:ProcPtr|UPP))|StdGlyphs(?:ProcPtr|UPP)|T(?:ext(?:ProcPtr|UPP)|xMeas(?:ProcPtr|UPP)))|Elem(?:Ptr)?|Hdr(?:Ptr)?|L(?:Preview(?:PDFStyle|RequestRef)|ThumbnailRe(?:f|questRef))|Types)|R(?:DFlagsType|GBColor|OTA(?:GlyphEntry|Header)|TAType|e(?:ad(?:BytesFDF|Packet(?:DataFDF|sFDF))|drawBackground(?:ProcPtr|UPP)|gi(?:onToRects(?:ProcPtr|UPP)|ster(?:Information(?:Intel64|PowerPC)?|edComponent(?:InstanceRecord(?:Ptr)?|Record(?:Ptr)?)))|s(?:Attributes|Err(?:ProcPtr|UPP)|File(?:Attributes|RefNum)|ID|ource(?:Count|EndianFilterPtr|Index|Spec)))|gnHandle|outin(?:e(?:Descriptor(?:Handle|Ptr)?|FlagsType|Record(?:Handle|Ptr)?)|g(?:Flags|Resource(?:Entry|Handle|Ptr)))|srcChainLocation|uleBasedTrslRecord)|S(?:C(?:Bond(?:InterfaceRef|StatusRef)|DynamicStore(?:C(?:allBack|ontext)|Ref)|Network(?:Connection(?:C(?:allBack|ontext)|Flags|PPPStatus|Ref|Status)|InterfaceRef|ProtocolRef|Reachability(?:C(?:allBack|ontext)|Flags|Ref)|Se(?:rviceRef|tRef))|Preferences(?:C(?:allBack|ontext)|Notification|Ref)|VLANInterfaceRef)|FNTLookup(?:ArrayHeader|BinarySearchHeader|FormatSpecificHeader|Kind|Offset|S(?:egment(?:Header)?|ingle(?:Header)?)|T(?:able(?:Format|Handle|Ptr)?|rimmedArrayHeader)|V(?:alue|ectorHeader))|Int|K(?:Document(?:I(?:D|ndexState)|Ref)|Index(?:DocumentIteratorRef|Ref|Type)|S(?:earch(?:GroupRef|Options|Re(?:f|sults(?:FilterCallBack|Ref))|Type)|ummaryRef))|MPTETime(?:Flags|Type)?|R(?:CallBack(?:P(?:aram|rocPtr)|Struct|UPP)|Language(?:Model|Object)|P(?:ath|hrase)|Re(?:cogni(?:tion(?:Result|System)|zer)|jectionLevel)|Spee(?:ch(?:Object|Source)|dSetting)|Word)|SL(?:Authenticate|C(?:ipher(?:Suite|suiteGroup)|lientCertificateState|on(?:nection(?:Ref|Type)|text(?:Ref)?))|Protocol(?:Side)?|ReadFunc|Session(?:Option|State)|WriteFunc)|T(?:Class(?:Table)?|E(?:lement|ntry(?:Index|One|Two|Zero))|H(?:andle|eader)|Ptr|X(?:Class(?:Table)?|Entry(?:Index|One|Two|Zero)|Header|StateIndex))|c(?:hedule(?:dAudio(?:FileRegion(?:CompletionProc)?|Slice(?:CompletionProc)?)|rInfoRec(?:Ptr)?)|r(?:ap(?:Flavor(?:Flags|Info|Type)|PromiseKeeper(?:ProcPtr|UPP)|Ref|T(?:ranslationList(?:Handle|Ptr)?|ype(?:Spec)?))|ipt(?:CodeRun(?:Ptr)?|Language(?:Record|Support(?:Handle|Ptr)?)|TokenType|ingComponentSelector)|oll(?:BarTrackInfo|WindowOptions)|pST(?:Element|Table)))|e(?:c(?:A(?:CLRef|FPServerSignature|ccess(?:Control(?:CreateFlags|Ref)|OwnerType|Ref)|sn1(?:AlgId|Item|Oid|PubKeyInfo|Template(?:Chooser(?:Ptr)?|_struct)?)|uthenticationType)|C(?:S(?:DigestAlgorithm|Flags)|ertificateRef|ode(?:Ref|S(?:ignatureFlags|tatus))|redentialType)|External(?:Format|ItemType)|G(?:roupTransformRef|uestRef)|I(?:dentity(?:Ref|SearchRef)|tem(?:Attr|Class|ImportExport(?:Flags|KeyParameters)))|Key(?:GeneratePairBlock|ImportExport(?:Flags|Parameters)|OperationType|Ref|Sizes|Usage|chain(?:Attr(?:Type|ibute(?:Info|List|Ptr)?)|Callback(?:Info)?|Event(?:Mask)?|ItemRef|PromptSelector|Ref|S(?:e(?:archRef|ttings)|tatus)))|MessageBlock|P(?:a(?:dding|sswordRef)|olicy(?:Ref|SearchRef)|r(?:eferencesDomain|otocolType)|ublicKeyHash)|R(?:andomRef|equirement(?:Ref|Type))|StaticCodeRef|T(?:askRef|r(?:ansform(?:A(?:ctionBlock|ttribute(?:ActionBlock|Ref))|CreateFP|DataBlock|I(?:mplementationRef|nstanceBlock)|MetaAttributeType|Ref|StringOrAttributeRef)|ust(?:Callback|OptionFlags|Re(?:f|sultType)|Settings(?:Domain|KeyUsage|Result)|WithErrorCallback|edApplicationRef)))|uritySessionId)|lectorFunction(?:ProcPtr|UPP)|ssion(?:AttributeBits|CreationFlags)|t(?:PropertyFDF|UserDataFDF|upWindowProxyDragImageRec))|izeResourceRec(?:Handle|Ptr)?|l(?:eepQ(?:ProcPtr|Rec(?:Ptr)?|UPP)|iderTrackInfo)|ound(?:DataChunk(?:Ptr)?|ProcPtr|UPP)|peech(?:Channel(?:Record)?|Done(?:ProcPtr|UPP)|Error(?:CFProcPtr|Info|ProcPtr|UPP)|Phoneme(?:ProcPtr|UPP)|S(?:tatusInfo|ync(?:ProcPtr|UPP))|TextDone(?:ProcPtr|UPP)|VersionInfo|Word(?:CFProcPtr|ProcPtr|UPP)|XtndData)|t(?:Scrp(?:Handle|Ptr|Rec)|a(?:geList|ndard(?:DropLocation|IconListCellData(?:Ptr|Rec)))|ring(?:2DateStatus|ToDateStatus)|yle(?:Run|Table))|ys(?:PPtr|tem(?:SoundID|UI(?:Mode|Options))))|T(?:E(?:C(?:BufferContextRec|Conver(?:sionInfo|terContextRec)|Encoding(?:Pair(?:Rec|s(?:Handle|Ptr|Rec)?)|sList(?:Handle|Ptr|Rec))|In(?:fo(?:Handle|Ptr)?|ternetName(?:Rec|UsageMask|s(?:Handle|Ptr|Rec)))|Locale(?:ListToEncodingList(?:Ptr|Rec)|ToEncodingsList(?:Handle|Ptr|Rec))|ObjectRef|Plugin(?:C(?:lear(?:ContextInfoPtr|SnifferContextInfoPtr)|onvertTextEncodingPtr)|Disp(?:atchTable|oseEncoding(?:ConverterPtr|SnifferPtr))|FlushConversionPtr|Get(?:Count(?:Available(?:SniffersPtr|TextEncoding(?:PairsPtr|sPtr))|DestinationTextEncodingsPtr|MailEncodingsPtr|SubTextEncodingsPtr|WebEncodingsPtr)|PluginDispatchTablePtr|TextEncoding(?:FromInternetNamePtr|InternetNamePtr))|NewEncoding(?:ConverterPtr|SnifferPtr)|S(?:ig(?:nature)?|niffTextEncodingPtr|tateRec)|Version)|S(?:niffer(?:ContextRec|ObjectRef)|ubTextEncoding(?:Rec|s(?:Handle|Ptr|Rec)))|lickLoop(?:ProcPtr|UPP))|DoText(?:ProcPtr|UPP)|FindWord(?:ProcPtr|UPP)|Handle|IntHook|Ptr|Rec(?:alc(?:ProcPtr|UPP))?|Style(?:Handle|Ptr|Rec|Table))|ISInputSourceRef|MTask(?:Ptr)?|S(?:Code|M(?:Doc(?:AccessAttributes|ument(?:I(?:D|nterfaceType)|PropertyTag))|GlyphInfo(?:Array)?)|criptingSizeResource)|X(?:N(?:A(?:TSUI(?:Features|Variations)|ction(?:Key(?:Mapper(?:ProcPtr|UPP))?|NameMapper(?:ProcPtr|UPP))|ttributeData|utoScrollBehavior)|Background(?:Data|Type)?|C(?:arbonEventInfo|o(?:mmandEventSupportOptions|nt(?:extualMenuSetup(?:ProcPtr|UPP)|inuousFlags|rol(?:Data|Tag))|untOptions))|D(?:ataType|rawItems)|Errors|F(?:eatureBits|i(?:leType|nd(?:ProcPtr|UPP))|rame(?:ID|Options|Type))|HyperLinkState|LongRect|Ma(?:rgins|tch(?:Options|TextRecord))|O(?:bject(?:Refcon)?|ffset)|PermanentTextEncodingType|RectKey|Scroll(?:Bar(?:Orientation|State)|Info(?:ProcPtr|UPP)|Unit)|T(?:ab(?:Type)?|ype(?:Attributes|RunAttribute(?:Sizes|s)))|VersionValue)|TNTag)|a(?:ble(?:DirectoryRecord|tP(?:oint(?:Rec|erRec)|roximityRec))|sk(?:Proc|Storage(?:Index|Value)))|ext(?:BreakLocatorRef|Chunk(?:Ptr)?|Encoding(?:Base|Format|NameSelector|R(?:ec|un(?:Ptr)?)|Variant)?|Ptr|Range(?:Array(?:Handle|Ptr)?|Handle|Ptr)?|S(?:ervice(?:Class|Info(?:Ptr)?|List(?:Handle|Ptr)?|Property(?:Tag|Value))|tyle(?:Handle|Ptr)?)|ToUnicodeInfo|WidthHook(?:ProcPtr|UPP))|h(?:eme(?:ArrowOrientation|B(?:ackgroundKind|rush|utton(?:Adornment|Draw(?:Info(?:Ptr)?|ProcPtr|UPP)|Kind|Value))|C(?:heckBoxStyle|ursor)|Dra(?:gSoundKind|w(?:State|ingState))|Erase(?:ProcPtr|UPP)|FontID|GrowDirection|Iterator(?:ProcPtr|UPP)|Me(?:nu(?:BarState|ItemType|State|Type)|tric)|PopupArrowSize|S(?:crollBar(?:ArrowStyle|ThumbStyle)|oundKind)|T(?:ab(?:Direction|Style|TitleDraw(?:ProcPtr|UPP))|extColor|humbDirection|itleBarWidget|rack(?:Attributes|DrawInfo|EnableState|Kind|PressState))|Window(?:Attributes|Metrics(?:Ptr)?|Type))|read(?:Entry(?:ProcPtr|TPP|UPP)|ID|Options|S(?:cheduler(?:ProcPtr|TPP|UPP)|t(?:ate|yle)|witch(?:ProcPtr|TPP|UPP))|T(?:askRef|ermination(?:ProcPtr|TPP|UPP))))|imer(?:ProcPtr|UPP)|o(?:ggle(?:PB|Results)|ken(?:Block(?:Ptr)?|Re(?:c(?:Ptr)?|sults))|olboxObjectClassRef)|r(?:a(?:k(?:Table(?:Data|Entry)?|Value)|ns(?:itionWindowOptions|lation(?:Attributes|Flags|Ref(?:Num)?)))|ipleInt|uncCode)|ype(?:SelectRecord|sBlock(?:Ptr)?))|U(?:AZoomChangeFocusType|C(?:C(?:harProperty(?:Type|Value)|ollat(?:eOptions|ionValue))|Key(?:CharSeq|LayoutFeatureInfo|ModifiersToTableNum|Output|S(?:equenceDataIndex|tate(?:Entry(?:Range|Terminal)|Record(?:sIndex)?|Terminators))|ToCharTableIndex|board(?:Layout|TypeHeader))|T(?:SWalkDirection|extBreak(?:Options|Type)|ypeSelect(?:CompareResult|Options|Ref)))|Int|NDServerRef|RL(?:CallbackInfo|Event(?:Mask)?|Notify(?:ProcPtr|UPP)|OpenFlags|Reference|S(?:tate|ystemEvent(?:ProcPtr|UPP)))|TCDateTime(?:Handle|Ptr)?|n(?:i(?:CharArray(?:Handle|Offset|Ptr)|code(?:Map(?:Version|ping(?:Ptr)?)|ToText(?:Fallback(?:ProcPtr|UPP)|Info|RunInfo)))|tokenTable(?:Handle|Ptr)?)|ser(?:EventUPP|Item(?:ProcPtr|UPP)))|V(?:DGam(?:RecPtr|maRecord)|ector(?:128(?:Intel)?|Information(?:Intel64|PowerPC)?)|o(?:ice(?:Description|FileInfo|Spec(?:Ptr)?)|l(?:MountInfo(?:Header|Ptr)|ume(?:MountInfoHeader(?:Ptr)?|Type))))|W(?:CTab(?:Handle|Ptr)|S(?:ClientContext(?:CopyDescriptionCallBackProcPtr|Re(?:leaseCallBackProcPtr|tainCallBackProcPtr))?|MethodInvocationRef|ProtocolHandler(?:DeserializationProcPtr|Ref|SerializationProcPtr)|TypeID|tateData(?:Handle|Ptr)?)|i(?:d(?:eChar(?:Arr)?|thHook(?:ProcPtr|UPP))|n(?:CTab|dow(?:A(?:ctivationScope|ttributes)|C(?:lass|onstrainOptions)|D(?:ef(?:PartCode|Spec(?:Ptr)?|Type|UPP)|rawerState)|Group(?:Attributes|ContentOptions|Ref)|LatentVisibility|Modality|P(?:a(?:int(?:Proc(?:Options|Ptr)|UPP)|rtCode)|ositionMethod|tr)|Re(?:f|gionCode)|T(?:itleDrawing(?:ProcPtr|UPP)|ransition(?:Action|Effect)))))|ordBreak(?:ProcPtr|UPP)|rit(?:e(?:BytesFDF|PacketsFDF)|ingCode))|XLib(?:ContainerHeader|ExportedSymbol(?:HashSlot|Key)?)|ZoomAcceleration|a(?:cl_(?:entry_(?:id_t|t)|flag(?:_t|set_t)|perm(?:_t|set_(?:mask_t|t))|t(?:ag_t|ype_t)?)|ddr64_t|larm_(?:port_t|t(?:ype_t)?)|rcade_register_t|u(?:_(?:as(?:flgs_t|id_t)|c(?:lass_t|tlmode_t)|e(?:mod_t|v(?:class_map(?:_t)?|ent_t)|xpire_after(?:_t)?)|fstat_t|id_t|mask(?:_t)?|qctrl(?:_t)?|s(?:ession(?:_t)?|tat_t)|t(?:id(?:_(?:addr(?:_t)?|t))?|oken))|dit(?:_(?:fstat|stat|token_t)|info(?:_(?:addr(?:_t)?|t))?|pinfo(?:_(?:addr(?:_t)?|t))?)))|b(?:oo(?:lean_t|tstrap_t)|uild_(?:tool_version|version_command))|c(?:cntTokenRec(?:Handle|Ptr|ord)|lock_(?:attr_t|ctrl_(?:port_t|t)|flavor_t|id_t|re(?:ply_t|s_t)|serv_(?:port_t|t))|msghdr|oalition_t|pu_(?:subtype_t|t(?:hreadtype_t|ype_t))|ssm_(?:a(?:cl_(?:keychain_prompt_selector|process_subject_selector)|pple(?:cspdl_db_(?:change_password_parameters|is_locked_parameters|settings_parameters)|dl_open_parameters(?:_mask)?)|uthorizationgroup)|csp_operational_statistics|d(?:at(?:a|e)|b_schema_index_info|l_(?:db_handle|pkcs11_attributes))|func_name_addr|guid|k(?:ey_size|r_name)|list(?:_element)?|memory_funcs|name_list|parsed_c(?:ert|rl)|query_size_data|range|tp_result_set|version))|d(?:ata_in_code_entry|ec(?:form|imal)|y(?:l(?:d_(?:info_command|kernel_(?:image_info(?:_(?:array_t|t))?|process_info(?:_t)?))|i(?:b(?:_(?:command|module(?:_64)?|reference|table_of_contents))?|nker_command))|symtab_command))|e(?:mulation_vector_t|n(?:cryption_info_command(?:_64)?|try_point_command)|vsio(?:Keymapping|MouseScaling)|x(?:ception_(?:behavior_(?:array_t|t)|data_t(?:ype_t)?|flavor_array_t|handler_(?:array_t|t)|mask_(?:array_t|t)|port_(?:arra(?:ry_t|y_t)|t)|type_t)|tension_data_format))|f(?:e(?:nv_t|xcept_t)|pos_t|vm(?:file_command|lib(?:_command)?))|gpu_energy_data(?:_t)?|h(?:ash_info_bucket(?:_(?:array_t|t))?|ost_(?:basic_info(?:_(?:data_t|t))?|c(?:an_has_debugger_info(?:_(?:data_t|t))?|pu_load_info(?:_(?:data_t|t))?)|flavor_t|info(?:64_t|_(?:data_t|t))|load_info(?:_(?:data_t|t))?|name_(?:port_t|t)|p(?:r(?:eferred_user_arch(?:_(?:data_t|t))?|i(?:ority_info(?:_(?:data_t|t))?|v_t))|urgable_info_(?:data_t|t))|s(?:ched_info(?:_(?:data_t|t))?|ecurity_t)|t))|i(?:386_(?:exception_state_t|float_state_t|thread_state_t)|dent_command|maxdiv_t|nteger_t|o_(?:async_ref(?:64_t|_t)|buf_ptr_t|connect_t|enumerator_t|iterator_t|master_t|name_t|object_t|registry_entry_t|s(?:calar_inband(?:64_t|_t)|ervice_t|t(?:at_(?:entry|info(?:_t)?)|r(?:ing_(?:inband_t|t)|uct_inband_t)))|user_(?:reference_t|scalar_t))|pc_(?:info_(?:name(?:_(?:array_t|t))?|space(?:_(?:basic(?:_t)?|t))?|tree_name(?:_(?:array_t|t))?)|space_(?:inspect_t|port_t|t)|voucher_(?:attr_(?:control_t|manager_t)|t)))|k(?:auth_(?:ac(?:e(?:_(?:rights_t|t))?|l(?:_t)?)|cache_sizes|filesec(?:_t)?|identity_extlookup)|ern(?:_return_t|el_(?:boot_info_t|resource_sizes(?:_(?:data_t|t))?|version_t))|mod_(?:args_t|control_flavor_t|info(?:_(?:32_v1(?:_t)?|64_v1(?:_t)?|array_t|t))?|reference(?:_t)?|st(?:art_func_t|op_func_t)|t)|object_description_t)|l(?:a(?:belstr_t|unch_data_(?:dict_iterator_t|t(?:ype_t)?))|edger_(?:a(?:mount_t|rray_t)|item_t|port_(?:array_t|t)|t)|in(?:ger|ke(?:dit_data_command|r_option_command))|o(?:ad_command|ck(?:_set_(?:port_t|t)|group_info(?:_(?:array_t|t))?)))|m(?:a(?:ch_(?:core_(?:details|fileheader)|dead_name_notification_t|e(?:rror_(?:fn_t|t)|xception_(?:code_t|data_t(?:ype_t)?|subcode_t))|header(?:_64)?|m(?:emory_info(?:_(?:array_t|t))?|sg_(?:audit_trailer_t|b(?:ase_t|its_t|ody_t)|co(?:ntext_trailer_t|py_options_t)|descriptor_t(?:ype_t)?|empty_(?:rcv_t|send_t|t)|format_0_trailer_t|guard(?:_flags_t|ed_port_descriptor(?:32_t|64_t|_t))|header_t|id_t|ma(?:c_trailer_t|x_trailer_t)|o(?:ol_(?:descriptor(?:32_t|64_t|_t)|ports_descriptor(?:32_t|64_t|_t))|ption(?:_t|s_t))|p(?:ort_descriptor_t|riority_t)|return_t|s(?:e(?:curity_trailer_t|qno_trailer_t)|ize_t)|t(?:imeout_t|railer_(?:info_t|size_t|t(?:ype_t)?)|ype_(?:descriptor_t|n(?:ame_t|umber_t)|size_t))))|no_senders_notification_t|port_(?:array_t|context_t|de(?:l(?:eted_notification_t|ta_t)|stroyed_notification_t)|flavor_t|guard_exception_codes|info_(?:ext(?:_t)?|t)|limits(?:_t)?|ms(?:count_t|gcount_t)|name_(?:array_t|t)|options(?:_(?:ptr_t|t))?|qos(?:_t)?|right(?:_t|s_t)|s(?:eqno_t|rights_t|tatus(?:_t)?)|type_(?:array_t|t)|urefs_t)|send_(?:once_notification_t|possible_notification_t)|t(?:ask_basic_info(?:_(?:data_t|t))?|imespec(?:_t)?)|v(?:m_(?:address_t|info_region(?:_t)?|offset_t|read_entry(?:_t)?|size_t)|oucher_(?:attr_(?:co(?:mmand_t|nt(?:ent_(?:size_t|t)|rol_(?:flags_t|t)))|importance_refs|key_(?:array_t|t)|manager_t|r(?:aw_recipe_(?:array_(?:size_t|t)|size_t|t)|ecipe_(?:command_(?:array_t|t)|data(?:_t)?|size_t|t))|value_(?:flags_t|handle_(?:array_(?:size_t|t)|t)|reference_t))|name_(?:array_t|t)|selector_t|t))|zone_(?:info_(?:array_t|data|t)|name(?:_(?:array_t|t))?))|trix_(?:double(?:2x(?:2|3|4)|3x(?:2|3|4)|4x(?:2|3|4))|float(?:2x(?:2|3|4)|3x(?:2|3|4)|4x(?:2|3|4))))|context_t|em(?:_entry_name_port_t|ory_object_(?:a(?:rray_t|ttr_info(?:_(?:data_t|t))?)|behave_info(?:_(?:data_t|t))?|c(?:luster_size_t|o(?:ntrol_t|py_strategy_t))|default_t|f(?:ault_info_t|lavor_t)|info_(?:data_t|t)|name_t|offset_t|perf_info(?:_(?:data_t|t))?|return_t|size_t|t))|ig_(?:impl_routine_t|r(?:eply_error_t|outine_(?:arg_descriptor_t|descriptor(?:_t)?|t))|s(?:erver_routine_t|tub_routine_t|ubsystem(?:_t)?|ymtab(?:_t)?))|sg(?:_labels_t|hdr))|n(?:atural_t|ot(?:e_command|ify_port_t)|space_path_t|tsid_t)|os_(?:block_t|function_t|log_(?:s|t(?:ype_t)?)|trace_payload_(?:object_t|t)|unfair_lock_s)|p(?:a(?:cked_(?:char(?:16|2|32|4|64|8)|double(?:2|4|8)|float(?:16|2|4|8)|int(?:16|2|4|8)|long(?:2|4|8)|short(?:16|2|32|4|8)|u(?:char(?:16|2|32|4|64|8)|int(?:16|2|4|8)|long(?:2|4|8)|short(?:16|2|32|4|8)))|ge_address_array_t)|icker|o(?:inter_t|licy_(?:base(?:_(?:data_t|t)|s)|fifo_(?:base(?:_(?:data_t|t))?|info(?:_(?:data_t|t))?|limit(?:_(?:data_t|t))?)|info(?:_(?:data_t|t)|s)|limit(?:_(?:data_t|t)|s)|rr_(?:base(?:_(?:data_t|t))?|info(?:_(?:data_t|t))?|limit(?:_(?:data_t|t))?)|t(?:imeshare_(?:base(?:_(?:data_t|t))?|info(?:_(?:data_t|t))?|limit(?:_(?:data_t|t))?))?))|pnum_t|r(?:eb(?:ind_cksum_command|ound_dylib_command)|ocessor_(?:array_t|basic_info(?:_(?:data_t|t))?|cpu_load_info(?:_(?:data_t|t))?|flavor_t|info_(?:array_t|data_t|t)|port_(?:array_t|t)|set_(?:array_t|basic_info(?:_(?:data_t|t))?|control_(?:port_t|t)|flavor_t|info_(?:data_t|t)|load_info(?:_(?:data_t|t))?|name_(?:array_t|port_(?:array_t|t)|t)|port_t|t)|t)))|qos_class_t|r(?:e(?:g(?:64_t|isterSelectorType)|lop)|outine(?:_(?:arg_(?:descriptor(?:_t)?|offset|size|type)|descriptor(?:_t)?)|s_command(?:_64)?)|p(?:ath_command|c_(?:routine_(?:arg_descriptor(?:_t)?|descriptor(?:_t)?)|s(?:ignature|ubsystem(?:_t)?))))|s(?:a(?:_endpoints(?:_t)?|e_(?:associd_t|connid_t))|e(?:c(?:_(?:certificate(?:_t)?|identity(?:_t)?|object(?:_t)?|protocol_(?:challenge_(?:complete_t|t)|key_update_(?:complete_t|t)|metadata(?:_t)?|options(?:_t)?|pre_shared_key_selection_(?:complete_t|t)|verify_(?:complete_t|t))|trust(?:_t)?)|tion(?:_64)?|urity_token_t)|gment_command(?:_64)?|maphore_(?:port_t|t))|f(?:_hdtr|nt(?:CMap(?:E(?:ncoding|xtendedSubHeader)|Header|SubHeader)|D(?:escriptorHeader|irectory(?:Entry)?)|F(?:eature(?:Header|Name)|ont(?:Descriptor|FeatureSetting|RunFeature))|Instance|Name(?:Header|Record)|Variation(?:Axis|Header)))|i(?:md_(?:bool|char(?:1(?:6)?|2|3(?:2)?|4|64|8)|double(?:1|2|3|4|8)|float(?:1(?:6)?|2|3|4|8)|int(?:1(?:6)?|2|3|4|8)|long(?:1|2|3|4|8)|packed_(?:char(?:16|2|32|4|64|8)|double(?:2|4|8)|float(?:16|2|4|8)|int(?:16|2|4|8)|long(?:2|4|8)|short(?:16|2|32|4|8)|u(?:char(?:16|2|32|4|64|8)|int(?:16|2|4|8)|long(?:2|4|8)|short(?:16|2|32|4|8)))|short(?:1(?:6)?|2|3(?:2)?|4|8)|u(?:char(?:1(?:6)?|2|3(?:2)?|4|64|8)|int(?:1(?:6)?|2|3|4|8)|long(?:1|2|3|4|8)|short(?:1(?:6)?|2|3(?:2)?|4|8)))|nt(?:16|32|64|8))|leep_type_t|o(?:_np_extensions|ck(?:addr(?:_storage)?|proto)|urce_version_command)|u(?:b_(?:client_command|framework_command|library_command|umbrella_command)|id_cred_(?:path_t|t|uid_t))|y(?:m(?:seg_command|tab_(?:command|name_t))|nc_policy_t))|t(?:ask_(?:a(?:bsolutetime_info(?:_(?:data_t|t))?|ffinity_tag_info(?:_(?:data_t|t))?|rray_t)|basic_info(?:_(?:32(?:_(?:data_t|t))?|64(?:_(?:data_t|t))?|data_t|t))?|category_policy(?:_(?:data_t|t))?|dyld_info(?:_(?:data_t|t))?|e(?:vents_info(?:_(?:data_t|t))?|x(?:c_guard_behavior_t|tmod_info(?:_(?:data_t|t))?))|fla(?:gs_info(?:_(?:data_t|t))?|vor_t)|in(?:fo_(?:data_t|t)|spect_(?:basic_counts(?:_(?:data_t|t))?|flavor(?:_t)?|info_t|t))|kernelmemory_info(?:_(?:data_t|t))?|latency_qos(?:_t)?|name_t|p(?:o(?:licy_(?:flavor_t|t)|rt_(?:array_t|t)|wer_info(?:_(?:data_t|t|v2(?:_(?:data_t|t))?))?)|urgable_info_t)|qos_policy(?:_t)?|role(?:_t)?|s(?:pecial_port_t|uspension_token_t)|t(?:hr(?:ead_times_info(?:_(?:data_t|t))?|oughput_qos(?:_t)?)|race_memory_info(?:_(?:data_t|t))?)?|vm_info(?:_(?:data_t|t))?|wait_state_info(?:_(?:data_t|t))?|zone_info_(?:array_t|data|t))|hread_(?:a(?:ct_(?:array_t|port_(?:array_t|t)|t)|ffinity_policy(?:_(?:data_t|t))?|rray_t)|ba(?:ckground_policy(?:_(?:data_t|t))?|sic_info(?:_(?:data_t|t))?)|command|extended_(?:info(?:_(?:data_t|t))?|policy(?:_(?:data_t|t))?)|flavor_t|i(?:dentifier_info(?:_(?:data_t|t))?|n(?:fo_(?:data_t|t)|spect_t))|latency_qos_(?:policy(?:_(?:data_t|t))?|t)|p(?:o(?:licy_(?:flavor_t|t)|rt_(?:array_t|t))|recedence_policy(?:_(?:data_t|t))?)|sta(?:ndard_policy(?:_(?:data_t|t))?|te_(?:data_t|flavor_(?:array_t|t)|t))|t(?:hroughput_qos_(?:policy(?:_(?:data_t|t))?|t)|ime_constraint_policy(?:_(?:data_t|t))?)?)|ime_value(?:_t)?|l(?:s_(?:ciphersuite_(?:group_t|t)|protocol_version_t)|v_descriptor)|oken_t|wolevel_hint(?:s_command)?)|u(?:ext_object_t|int(?:16|32|64|8)|pl_t|ser_subsystem_t|uid_(?:command|string_t))|v(?:e(?:ctor_(?:char(?:16|2|3(?:2)?|4|8)|double(?:2|3|4|8)|float(?:16|2|3|4|8)|int(?:16|2|3|4|8)|long(?:1|2|3|4|8)|short(?:16|2|3(?:2)?|4|8)|u(?:char(?:16|2|3(?:2)?|4|8)|int(?:16|2|3|4|8)|long(?:1|2|3|4|8)|short(?:16|2|3(?:2)?|4|8)))|rsion_min_command)|fs_path_t|irtual_memory_guard_exception_codes|m(?:32_object_id_t|_(?:address_t|behavior_t|extmod_statistics(?:_(?:data_t|t))?|in(?:fo_(?:object(?:_(?:array_t|t))?|region(?:_(?:64(?:_t)?|t))?)|herit_t)|ma(?:chine_attribute_(?:t|val_t)|p_(?:address_t|offset_t|size_t|t))|named_entry_t|o(?:bject_(?:id_t|offset_t|size_t)|ffset_t)|p(?:age_info_(?:basic(?:_(?:data_t|t))?|data_t|flavor_t|t)|rot_t|urg(?:able_t|eable_(?:info(?:_t)?|stat(?:_t)?)))|re(?:ad_entry(?:_t)?|gion_(?:basic_info(?:_(?:64(?:_t)?|data_(?:64_t|t)|t))?|extended_info(?:_(?:data_t|t))?|flavor_t|info_(?:64_t|data_t|t)|recurse_info_(?:64_t|t)|submap_(?:info(?:_(?:64(?:_t)?|data_(?:64_t|t)|t))?|short_info_(?:64(?:_t)?|data_64_t))|top_info(?:_(?:data_t|t))?))|s(?:ize_t|tatistics(?:64(?:_(?:data_t|t))?|_(?:data_t|t))?|ync_t)|task_entry_t))|o(?:idPtr|ucher_mach_msg_state_(?:s|t)))|x(?:86_(?:avx(?:512_state(?:32_t|64_t|_t)?|_state(?:32_t|64_t|_t)?)|debug_state(?:32_t|64_t|_t)?|exception_state(?:32_t|64_t|_t)?|float_state(?:32_t|64_t|_t)?|pagein_state_t|state_hdr(?:_t)?|thread_(?:full_state64_t|state(?:32_t|64_t|_t)?))|pc_(?:activity_(?:handler_t|state_t|t)|connection_(?:handler_t|t)|endpoint_t|finalizer_t|handler_t|object_t|type_t))|zone_(?:btrecord(?:_(?:array_t|t))?|info(?:_(?:array_t|t))?|name(?:_(?:array_t|t))?))\\b", + "name": "support.type.c" + }, { "match": "\\b(?:CF(?:A(?:bsoluteTime|llocator(?:AllocateCallBack|Co(?:ntext|pyDescriptionCallBack)|DeallocateCallBack|PreferredSizeCallBack|Re(?:allocateCallBack|f|leaseCallBack|tainCallBack))|rray(?:ApplierFunction|C(?:allBacks|opyDescriptionCallBack)|EqualCallBack|Re(?:f|leaseCallBack|tainCallBack))|ttributedStringRef)|B(?:ag(?:ApplierFunction|C(?:allBacks|opyDescriptionCallBack)|EqualCallBack|HashCallBack|Re(?:f|leaseCallBack|tainCallBack))|i(?:naryHeap(?:ApplierFunction|C(?:allBacks|ompareContext)|Ref)|t(?:VectorRef)?)|ooleanRef|undleRef(?:Num)?|yteOrder)|C(?:alendar(?:Identifier|Ref|Unit)|haracterSet(?:PredefinedSet|Ref)|ompar(?:atorFunction|isonResult))|D(?:at(?:a(?:Ref|SearchFlags)|e(?:Formatter(?:Key|Ref|Style)|Ref))|ictionary(?:ApplierFunction|CopyDescriptionCallBack|EqualCallBack|HashCallBack|KeyCallBacks|Re(?:f|leaseCallBack|tainCallBack)|ValueCallBacks))|Error(?:Domain|Ref)|File(?:Descriptor(?:C(?:allBack|ontext)|NativeDescriptor|Ref)|Security(?:ClearOptions|Ref))|GregorianUnitFlags|HashCode|I(?:SO8601DateFormatOptions|ndex)|Locale(?:Identifier|Key|LanguageDirection|Ref)|M(?:achPort(?:C(?:allBack|ontext)|InvalidationCallBack|Ref)|essagePort(?:C(?:allBack|ontext)|InvalidationCallBack|Ref)|utable(?:A(?:rrayRef|ttributedStringRef)|B(?:agRef|itVectorRef)|CharacterSetRef|D(?:ataRef|ictionaryRef)|S(?:etRef|tringRef)))|N(?:otification(?:C(?:allback|enterRef)|Name|SuspensionBehavior)|u(?:llRef|mber(?:Formatter(?:Key|OptionFlags|PadPosition|R(?:ef|oundingMode)|Style)|Ref|Type)))|OptionFlags|P(?:lugIn(?:DynamicRegisterFunction|FactoryFunction|Instance(?:DeallocateInstanceDataFunction|GetInterfaceFunction|Ref)|Ref|UnloadFunction)|ropertyList(?:Format|MutabilityOptions|Ref))|R(?:ange|eadStream(?:ClientCallBack|Ref)|unLoop(?:Activity|Mode|Observer(?:C(?:allBack|ontext)|Ref)|R(?:ef|unResult)|Source(?:Context(?:1)?|Ref)|Timer(?:C(?:allBack|ontext)|Ref)))|S(?:et(?:ApplierFunction|C(?:allBacks|opyDescriptionCallBack)|EqualCallBack|HashCallBack|Re(?:f|leaseCallBack|tainCallBack))|ocket(?:C(?:allBack(?:Type)?|ontext)|Error|NativeHandle|Ref|Signature)|tr(?:eam(?:ClientContext|E(?:rror(?:Domain)?|ventType)|PropertyKey|Status)|ing(?:BuiltInEncodings|CompareFlags|Encoding(?:s)?|InlineBuffer|NormalizationForm|Ref|Tokenizer(?:Ref|TokenType)))|wappedFloat(?:32|64))|T(?:ime(?:Interval|Zone(?:NameStyle|Ref))|ree(?:ApplierFunction|Co(?:ntext|pyDescriptionCallBack)|Re(?:f|leaseCallBack|tainCallBack))|ype(?:ID|Ref))|U(?:RL(?:Bookmark(?:CreationOptions|FileCreationOptions|ResolutionOptions)|ComponentType|E(?:numerator(?:Options|Re(?:f|sult))|rror)|PathStyle|Ref)|UID(?:Bytes|Ref)|serNotification(?:CallBack|Ref))|WriteStream(?:ClientCallBack|Ref)|XML(?:Attribute(?:DeclarationInfo|ListDeclarationInfo)|Document(?:Info|TypeInfo)|E(?:lement(?:Info|TypeDeclarationInfo)|ntity(?:Info|ReferenceInfo|TypeCode)|xternalID)|No(?:de(?:Ref|TypeCode)|tationInfo)|P(?:arser(?:AddChildCallBack|C(?:allBacks|o(?:ntext|pyDescriptionCallBack)|reateXMLStructureCallBack)|EndXMLStructureCallBack|HandleErrorCallBack|Options|Re(?:f|leaseCallBack|solveExternalEntityCallBack|tainCallBack)|StatusCode)|rocessingInstructionInfo)|TreeRef))|FSRef)\\b", "name": "support.type.cf.c" }, { - "match": "\\b(?:FILE|accessx_descriptor|blk(?:cnt_t|size_t)|c(?:addr_t|lock(?:_t|id_t)|t_rune_t)|d(?:addr_t|ev_t|i(?:spatch_time_t|v_t)|ouble_t)|errno_t|f(?:bootstraptransfer(?:_t)?|c(?:hecklv(?:_t)?|odeblobs(?:_t)?)|d_(?:mask|set)|i(?:lesec_(?:property_t|t)|xpt_t)|lo(?:at_t|ck(?:timeout)?)|pos_t|s(?:blkcnt_t|filcnt_t|i(?:d(?:_t)?|gnatures(?:_t)?)|obj_id(?:_t)?|searchblock|tore(?:_t)?))|g(?:id_t|uid_t)|i(?:d(?:_t|type_t)|n(?:_(?:addr_t|port_t)|o(?:64_t|_t)|t(?:16_t|32_t|64_t|8_t|_(?:fast(?:16_t|32_t|64_t|8_t)|least(?:16_t|32_t|64_t|8_t))|max_t|ptr_t)))|jmp_buf|key_t|l(?:conv|div_t|ldiv_t|og2phys)|m(?:ach_port_t|ode_t)|nlink_t|off_t|p(?:id_t|roc_rlimit_control_wakeupmon|trdiff_t)|q(?:addr_t|uad_t)|r(?:advisory|egister_t|lim(?:_t|it)|size_t|u(?:ne_t|sage(?:_info_(?:current|t|v(?:0|1|2|3)))?))|s(?:e(?:archstate|gsz_t)|i(?:g(?:_(?:atomic_t|t)|action|event|info_t|jmp_buf|s(?:et_t|tack)|vec)|ze_t)|size_t|tack_t|useconds_t|wblk_t|yscall_arg_t)|t(?:ime(?:_t|spec|val)|m)|u(?:_(?:char|int(?:16_t|32_t|64_t|8_t)?|long|quad_t|short)|context_t|i(?:d_t|nt(?:16_t|32_t|64_t|8_t|_(?:fast(?:16_t|32_t|64_t|8_t)|least(?:16_t|32_t|64_t|8_t))|max_t|ptr_t)?)|s(?:e(?:conds_t|r_(?:addr_t|long_t|off_t|s(?:ize_t|size_t)|time_t|ulong_t))|hort)|uid_t)|va_list|wint_t)\\b", + "match": "\\b(?:accessx_descriptor|blk(?:cnt_t|size_t)|c(?:addr_t|lock(?:_t|i(?:d_t|nfo))|t_rune_t)|d(?:addr_t|ev_t|i(?:spatch_time_t|v_t)|ouble_t)|e(?:rrno_t|xception)|f(?:bootstraptransfer(?:_t)?|c(?:hecklv(?:_t)?|odeblobs(?:_t)?)|d_(?:mask|set)|i(?:lesec_(?:property_t|t)|xpt_t)|lo(?:at_t|ck(?:timeout)?)|punchhole(?:_t)?|s(?:blkcnt_t|filcnt_t|i(?:d(?:_t)?|gnatures(?:_t)?)|obj_id(?:_t)?|pecread(?:_t)?|searchblock|tore(?:_t)?)|trimactivefile(?:_t)?)|g(?:id_t|uid_t)|i(?:d(?:_t|type_t)|n(?:_(?:addr_t|port_t)|o(?:64_t|_t)|t(?:16_t|32_t|64_t|8_t|_(?:fast(?:16_t|32_t|64_t|8_t)|least(?:16_t|32_t|64_t|8_t))|max_t|ptr_t))|ovec|timerval)|jmp_buf|key_t|l(?:conv|div_t|ldiv_t|og2phys)|m(?:a(?:ch_port_t|x_align_t)|ode_t)|nlink_t|off_t|p(?:id_t|roc_rlimit_control_wakeupmon|trdiff_t)|q(?:addr_t|uad_t)|r(?:advisory|egister_t|lim(?:_t|it)|size_t|u(?:ne_t|sage(?:_info_(?:current|t|v(?:0|1|2|3|4)))?))|s(?:a_family_t|e(?:archstate|gsz_t)|i(?:g(?:_(?:atomic_t|t)|action|event|info_t|jmp_buf|s(?:et_t|tack)|vec)|md_(?:double(?:2x(?:2|3|4)|3x(?:2|3|4)|4x(?:2|3|4))|float(?:2x(?:2|3|4)|3x(?:2|3|4)|4x(?:2|3|4))|quat(?:d|f))|ze_t)|ocklen_t|size_t|tack_t|useconds_t|wblk_t|yscall_arg_t)|t(?:ime(?:_t|spec|val(?:64)?|zone)|m)|u(?:_(?:char|int(?:16_t|32_t|64_t|8_t)?|long|quad_t|short)|context_t|i(?:d_t|nt(?:16_t|32_t|64_t|8_t|_(?:fast(?:16_t|32_t|64_t|8_t)|least(?:16_t|32_t|64_t|8_t))|max_t|ptr_t)?)|s(?:e(?:conds_t|r_(?:addr_t|long_t|off_t|s(?:ize_t|size_t)|time_t|ulong_t))|hort)|uid_t)|va_list|w(?:char_t|int_t))\\b", "name": "support.type.clib.c" }, { - "match": "\\bdispatch_(?:autorelease_frequency_t|block_(?:flags_t|t)|data_(?:applier_t|s|t)|f(?:d_t|unction_t)|group_(?:s|t)|io_(?:close_flags_t|handler_t|interval_flags_t|s|t(?:ype_t)?)|o(?:bject_(?:s|t)|nce_t)|q(?:os_class_t|ueue_(?:attr_(?:s|t)|priority_t|s|t))|s(?:emaphore_(?:s|t)|ource_(?:m(?:ach_send_flags_t|emorypressure_flags_t)|proc_flags_t|s|t(?:imer_flags_t|ype_(?:s|t))?|vnode_flags_t)))\\b", + "match": "\\bdispatch_(?:autorelease_frequency_t|block_(?:flags_t|t)|channel_s|data_(?:s|t)|f(?:d_t|unction_t)|group_(?:s|t)|io_(?:close_flags_t|handler_t|interval_flags_t|s|t(?:ype_t)?)|mach_(?:msg_s|s)|o(?:bject_(?:s|t)|nce_t)|q(?:os_class_t|ueue_(?:attr_(?:s|t)|concurrent_t|global_t|main_t|priority_t|s(?:erial_t)?|t))|s(?:emaphore_(?:s|t)|ource_(?:m(?:ach_(?:recv_flags_t|send_flags_t)|emorypressure_flags_t)|proc_flags_t|s|t(?:imer_flags_t|ype_(?:s|t))?|vnode_flags_t))|workloop_t)\\b", "name": "support.type.dispatch.c" }, { @@ -110,16 +290,52 @@ "name": "support.type.mac-classic.c" }, { - "match": "\\b(?:pthread_(?:attr_t|cond(?:_t|attr_t)|key_t|mutex(?:_t|attr_t)|o(?:nce_t|verride_(?:s|t))|rwlock(?:_t|attr_t)|t)|sched_param)\\b", + "match": "\\bpthread_(?:attr_t|cond(?:_t|attr_t)|key_t|mutex(?:_t|attr_t)|once_t|rwlock(?:_t|attr_t)|t)\\b", "name": "support.type.pthread.c" }, { - "match": "\\bCGImageByteOrderInfo\\b", - "name": "support.type.quartz.10.12.c" + "match": "\\b(?:CG(?:AffineTransform|B(?:itmap(?:ContextReleaseDataCallback|Info)|lendMode|uttonCount)|C(?:aptureOptions|harCode|o(?:lor(?:ConversionInfo(?:Ref|TransformType)?|Re(?:f|nderingIntent)|Space(?:Model|Ref)?)?|n(?:figureOption|text(?:Ref)?)))|D(?:ata(?:Consumer(?:Callbacks|PutBytesCallback|Re(?:f|leaseInfoCallback))?|Provider(?:DirectCallbacks|GetByte(?:PointerCallback|s(?:AtPositionCallback|Callback))|Re(?:f|lease(?:BytePointerCallback|DataCallback|InfoCallback)|windCallback)|S(?:equentialCallbacks|kipForwardCallback))?)|eviceColor|i(?:rectDisplayID|splay(?:BlendFraction|C(?:hangeSummaryFlags|o(?:nfigRef|unt))|Err|Fade(?:Interval|ReservationToken)|Mode(?:Ref)?|Re(?:configurationCallBack|servationInterval)|Stream(?:Frame(?:AvailableHandler|Status)|Ref|Update(?:Re(?:ctType|f))?)?)))|E(?:rror|vent(?:Err|F(?:i(?:eld|lterMask)|lags)|M(?:ask|ouseSubtype)|Ref|S(?:ource(?:KeyboardType|Ref|StateID)|uppressionState)|T(?:ap(?:CallBack|Information|Location|Options|P(?:lacement|roxy))|imestamp|ype)))|F(?:loat|ont(?:Index|PostScriptFormat|Ref)?|unction(?:Callbacks|EvaluateCallback|Re(?:f|leaseInfoCallback))?)|G(?:ammaValue|esturePhase|lyph(?:DeprecatedEnum)?|radient(?:DrawingOptions|Ref)?)|I(?:mage(?:AlphaInfo|ByteOrderInfo|PixelFormatInfo|Ref)?|nterpolationQuality)|KeyCode|L(?:ayer(?:Ref)?|ine(?:Cap|Join))|M(?:o(?:mentumScrollPhase|useButton)|utablePathRef)|OpenGLDisplayMask|P(?:DF(?:A(?:ccessPermissions|rray(?:Ref)?)|Bo(?:olean|x)|ContentStream(?:Ref)?|D(?:ataFormat|ictionary(?:ApplierFunction|Ref)?|ocument(?:Ref)?)|Integer|O(?:bject(?:Ref|Type)?|perator(?:Callback|Table(?:Ref)?))|Page(?:Ref)?|Real|S(?:canner(?:Ref)?|tr(?:eam(?:Ref)?|ing(?:Ref)?))|Tag(?:Property|Type))|SConverter(?:Begin(?:DocumentCallback|PageCallback)|Callbacks|End(?:DocumentCallback|PageCallback)|MessageCallback|ProgressCallback|Re(?:f|leaseInfoCallback))?|at(?:h(?:Appl(?:ierFunction|yBlock)|DrawingMode|Element(?:Type)?|Ref)?|tern(?:Callbacks|DrawPatternCallback|Re(?:f|leaseInfoCallback)|Tiling)?)|oint)|Re(?:ct(?:Count|Edge)?|freshRate)|S(?:cr(?:een(?:RefreshCallback|Update(?:Move(?:Callback|Delta)|Operation))|oll(?:EventUnit|Phase))|hading(?:Ref)?|ize)|Text(?:DrawingMode|Encoding)|Vector|W(?:heelCount|indow(?:BackingType|I(?:D|mageOption)|L(?:evel(?:Key)?|istOption)|SharingType)))|IOSurfaceRef)\\b", + "name": "support.type.quartz.c" }, { - "match": "\\b(?:CG(?:AffineTransform|B(?:itmap(?:ContextReleaseDataCallback|Info)|lendMode|uttonCount)|C(?:aptureOptions|harCode|o(?:lor(?:ConversionInfo(?:Ref|TransformType)?|Re(?:f|nderingIntent)|Space(?:Model|Ref)?)?|n(?:figureOption|text(?:Ref)?)))|D(?:ata(?:Consumer(?:Callbacks|PutBytesCallback|Re(?:f|leaseInfoCallback))?|Provider(?:DirectCallbacks|GetByte(?:PointerCallback|s(?:AtPositionCallback|Callback))|Re(?:f|lease(?:BytePointerCallback|DataCallback|InfoCallback)|windCallback)|S(?:equentialCallbacks|kipForwardCallback))?)|eviceColor|i(?:rectDisplayID|splay(?:BlendFraction|C(?:hangeSummaryFlags|o(?:nfigRef|unt))|Err|Fade(?:Interval|ReservationToken)|Mode(?:Ref)?|Re(?:configurationCallBack|servationInterval)|Stream(?:Frame(?:AvailableHandler|Status)|Ref|Update(?:Re(?:ctType|f))?)?)))|E(?:rror|vent(?:Err|F(?:i(?:eld|lterMask)|lags)|M(?:ask|ouseSubtype)|Ref|S(?:ource(?:KeyboardType|Ref|StateID)|uppressionState)|T(?:ap(?:CallBack|Information|Location|Options|P(?:lacement|roxy))|imestamp|ype)))|F(?:loat|ont(?:Index|PostScriptFormat|Ref)?|unction(?:Callbacks|EvaluateCallback|Re(?:f|leaseInfoCallback))?)|G(?:ammaValue|esturePhase|lyph(?:DeprecatedEnum)?|radient(?:DrawingOptions|Ref)?)|I(?:mage(?:AlphaInfo|ByteOrderInfo|Ref)?|nterpolationQuality)|KeyCode|L(?:ayer(?:Ref)?|ine(?:Cap|Join))|M(?:o(?:mentumScrollPhase|useButton)|utablePathRef)|OpenGLDisplayMask|P(?:DF(?:Array(?:Ref)?|Bo(?:olean|x)|ContentStream(?:Ref)?|D(?:ataFormat|ictionary(?:ApplierFunction|Ref)?|ocument(?:Ref)?)|Integer|O(?:bject(?:Ref|Type)?|perator(?:Callback|Table(?:Ref)?))|Page(?:Ref)?|Real|S(?:canner(?:Ref)?|tr(?:eam(?:Ref)?|ing(?:Ref)?)))|SConverter(?:Begin(?:DocumentCallback|PageCallback)|Callbacks|End(?:DocumentCallback|PageCallback)|MessageCallback|ProgressCallback|Re(?:f|leaseInfoCallback))?|at(?:h(?:ApplierFunction|DrawingMode|Element(?:Type)?|Ref)?|tern(?:Callbacks|DrawPatternCallback|Re(?:f|leaseInfoCallback)|Tiling)?)|oint)|Re(?:ct(?:Count|Edge)?|freshRate)|S(?:cr(?:een(?:RefreshCallback|Update(?:Move(?:Callback|Delta)|Operation))|oll(?:EventUnit|Phase))|hading(?:Ref)?|ize)|Text(?:DrawingMode|Encoding)|Vector|W(?:heelCount|indow(?:BackingType|I(?:D|mageOption)|L(?:evel(?:Key)?|istOption)|SharingType)))|IOSurfaceRef)\\b", - "name": "support.type.quartz.c" + "match": "\\b(?:k(?:C(?:FHTTPVersion2_0|GImage(?:Destination(?:EmbedThumbnail|ImageMaxPixelSize)|MetadataShouldExcludeGPS|Property(?:8BIMVersion|APNG(?:DelayTime|LoopCount|UnclampedDelayTime)|GPSHPositioningError|MakerAppleDictionary))|T(?:FontOpenTypeFeature(?:Tag|Value)|RubyAnnotationAttributeName)|V(?:ImageBufferAlphaChannelIsOpaque|PixelFormatCo(?:mponentRange(?:_(?:FullRange|VideoRange|WideRange))?|ntains(?:RGB|YCbCr))))|Sec(?:AttrAccess(?:Control|ibleWhenPasscodeSetThisDeviceOnly)|UseOperationPrompt)|UTType(?:3DContent|A(?:VIMovie|pple(?:ProtectedMPEG4Video|Script)|ssemblyLanguageSource|udioInterchangeFileFormat)|B(?:inaryPropertyList|ookmark|zip2Archive)|C(?:alendarEvent|ommaSeparatedText)|DelimitedText|E(?:lectronicPublication|mailMessage)|Font|GNUZipArchive|InternetLocation|J(?:SON|ava(?:Archive|Class|Script))|Log|M(?:3UPlaylist|IDIAudio|PEG2(?:TransportStream|Video))|OSAScript(?:Bundle)?|P(?:HPScript|KCS12|erlScript|l(?:aylist|uginBundle)|r(?:esentation|opertyList)|ythonScript)|QuickLookGenerator|R(?:awImage|ubyScript)|S(?:c(?:alableVectorGraphics|ript)|hellScript|p(?:otlightImporter|readsheet)|ystemPreferencesPane)|T(?:abSeparatedText|oDoItem)|U(?:RLBookmarkData|TF8TabSeparatedText|nixExecutable)|W(?:aveformAudio|indowsExecutable)|X(?:509Certificate|MLPropertyList|PCService)|ZipArchive))|matrix_identity_(?:double(?:2x2|3x3|4x4)|float(?:2x2|3x3|4x4)))\\b", + "name": "support.variable.10.10.c" + }, + { + "match": "\\bk(?:A(?:XListItem(?:IndexTextAttribute|LevelTextAttribute|PrefixTextAttribute)|udioComponent(?:InstanceInvalidationNotification|RegistrationsChangedNotification))|C(?:FStreamPropertySocketExtendedBackgroundIdleMode|GImage(?:Property(?:ExifSubsecTimeOriginal|PNGCompressionFilter|TIFFTile(?:Length|Width))|SourceSubsampleFactor)|V(?:ImageBuffer(?:ColorPrimaries_(?:DCI_P3|ITU_R_2020|P3_D65)|TransferFunction_ITU_R_2020|YCbCrMatrix_(?:DCI_P3|ITU_R_2020|P3_D65))|MetalTextureCacheMaximumTextureAgeKey|PixelBuffer(?:MetalCompatibilityKey|OpenGLTextureCacheCompatibilityKey)))|MDItemHTMLContent|Sec(?:A(?:CLAuthorization(?:Integrity|PartitionID)|ttrSyncViewHint)|PolicyApplePayIssuerEncryption|TrustCertificateTransparency|UseAuthentication(?:Context|UI(?:Allow|Fail|Skip)?))|UTTypeSwiftSource)\\b", + "name": "support.variable.10.11.c" + }, + { + "match": "\\bk(?:C(?:FStreamNetworkServiceTypeCallSignaling|GImage(?:DestinationOptimizeColorForSharing|PropertyDNG(?:AsShot(?:Neutral|WhiteXY)|B(?:aseline(?:Exposure|Noise|Sharpness)|lackLevel)|C(?:a(?:librationIlluminant(?:1|2)|meraCalibration(?:1|2|Signature))|olorMatrix(?:1|2))|FixVignetteRadial|NoiseProfile|Pr(?:ivateData|ofileCalibrationSignature)|W(?:arp(?:Fisheye|Rectilinear)|hiteLevel)))|T(?:BackgroundColorAttributeName|FontDownloadedAttribute|HorizontalInVerticalFormsAttributeName|RubyAnnotationS(?:caleToFitAttributeName|izeFactorAttributeName)|TrackingAttributeName)|VImageBufferTransferFunction_SMPTE_ST_428_1)|IOSurfacePixelSizeCastingAllowed|Sec(?:Attr(?:AccessGroupToken|KeyTypeECSECPrimeRandom|TokenID(?:SecureEnclave)?)|Key(?:Algorithm(?:EC(?:D(?:HKeyExchange(?:Cofactor(?:X963SHA(?:1|2(?:24|56)|384|512))?|Standard(?:X963SHA(?:1|2(?:24|56)|384|512))?)|SASignature(?:DigestX962(?:SHA(?:1|2(?:24|56)|384|512))?|MessageX962SHA(?:1|2(?:24|56)|384|512)|RFC4754))|IESEncryption(?:CofactorX963SHA(?:1AESGCM|2(?:24AESGCM|56AESGCM)|384AESGCM|512AESGCM)|StandardX963SHA(?:1AESGCM|2(?:24AESGCM|56AESGCM)|384AESGCM|512AESGCM)))|RSA(?:Encryption(?:OAEPSHA(?:1(?:AESGCM)?|2(?:24(?:AESGCM)?|56(?:AESGCM)?)|384(?:AESGCM)?|512(?:AESGCM)?)|PKCS1|Raw)|Signature(?:DigestPKCS1v15(?:Raw|SHA(?:1|2(?:24|56)|384|512))|MessagePKCS1v15SHA(?:1|2(?:24|56)|384|512)|Raw)))|KeyExchangeParameter(?:RequestedSize|SharedInfo)))|UTTypeLivePhoto)\\b", + "name": "support.variable.10.12.c" + }, + { + "match": "\\bk(?:C(?:GImage(?:AuxiliaryData(?:Info(?:Data(?:Description)?|Metadata)|TypeD(?:epth|isparity))|Metadata(?:NamespaceIPTCExtension|PrefixIPTCExtension)|Property(?:AuxiliaryData(?:Type)?|BytesPerRow|FileContentsDictionary|Height|I(?:PTCExt(?:A(?:boutCvTerm(?:CvId|Id|Name|RefinedAbout)?|ddlModelInfo|rtwork(?:C(?:ircaDateCreated|o(?:nt(?:entDescription|ributionDescription)|pyright(?:Notice|Owner(?:ID|Name)))|reator(?:ID)?)|DateCreated|Licensor(?:ID|Name)|OrObject|PhysicalDescription|S(?:ource(?:Inv(?:URL|entoryNo))?|tylePeriod)|Title)|udio(?:Bitrate(?:Mode)?|ChannelCount))|C(?:ircaDateCreated|o(?:nt(?:ainerFormat(?:Identifier|Name)?|r(?:ibutor(?:Identifier|Name|Role)?|olledVocabularyTerm))|pyrightYear)|reator(?:Identifier|Name|Role)?)|D(?:ataOnScreen(?:Region(?:D|H|Text|Unit|W|X|Y)?)?|igital(?:ImageGUID|Source(?:FileType|Type))|opesheet(?:Link(?:Link(?:Qualifier)?)?)?)|E(?:mb(?:dEncRightsExpr|eddedEncodedRightsExpr(?:LangID|Type)?)|pisode(?:Identifier|N(?:ame|umber))?|vent|xternalMetadataLink)|FeedIdentifier|Genre(?:Cv(?:Id|Term(?:Id|Name|RefinedAbout)))?|Headline|IPTCLastEdited|L(?:inkedEnc(?:RightsExpr|odedRightsExpr(?:LangID|Type)?)|ocation(?:C(?:ity|ountry(?:Code|Name)|reated)|GPS(?:Altitude|L(?:atitude|ongitude))|Identifier|Location(?:Id|Name)|ProvinceState|S(?:hown|ublocation)|WorldRegion))|M(?:axAvail(?:Height|Width)|odelAge)|OrganisationInImage(?:Code|Name)|P(?:erson(?:Heard(?:Identifier|Name)?|InImage(?:C(?:haracteristic|vTerm(?:CvId|Id|Name|RefinedAbout))|Description|Id|Name|WDetails)?)|roductInImage(?:Description|GTIN|Name)?|ublicationEvent(?:Date|Identifier|Name)?)|R(?:ating(?:R(?:atingRegion|egion(?:C(?:ity|ountry(?:Code|Name))|GPS(?:Altitude|L(?:atitude|ongitude))|Identifier|Location(?:Id|Name)|ProvinceState|Sublocation|WorldRegion))|S(?:caleM(?:axValue|inValue)|ourceLink)|Value(?:LogoLink)?)?|e(?:gistry(?:EntryRole|I(?:D|temID)|OrganisationID)|leaseReady))|S(?:e(?:ason(?:Identifier|N(?:ame|umber))?|ries(?:Identifier|Name)?)|hownEvent(?:Identifier|Name)?|t(?:orylineIdentifier|reamReady|ylePeriod)|upplyChainSource(?:Identifier|Name)?)|T(?:emporalCoverage(?:From|To)?|ranscript(?:Link(?:Link(?:Qualifier)?)?)?)|Vi(?:deo(?:Bitrate(?:Mode)?|DisplayAspectRatio|EncodingProfile|S(?:hotType(?:Identifier|Name)?|treamsCount))|sualColor)|WorkflowTag(?:Cv(?:Id|Term(?:Id|Name|RefinedAbout)))?)|mage(?:Count|s))|NamedColorSpace|P(?:ixelFormat|rimaryImage)|ThumbnailImages|Width))|T(?:BaselineOffsetAttributeName|FontVariationAxisHiddenKey)|V(?:ImageBuffer(?:ContentLightLevelInfoKey|MasteringDisplayColorVolumeKey|TransferFunction_(?:ITU_R_2100_HLG|SMPTE_ST_2084_PQ|sRGB))|MetalTextureUsage))|IOSurface(?:Plane(?:BitsPerElement|Component(?:Bit(?:Depths|Offsets)|Names|Ranges|Types))|Subsampling)|Sec(?:A(?:CLAuthorizationChange(?:ACL|Owner)|ttrPersist(?:antReference|entReference))|KeyAlgorithm(?:ECIESEncryption(?:CofactorVariableIVX963SHA(?:2(?:24AESGCM|56AESGCM)|384AESGCM|512AESGCM)|StandardVariableIVX963SHA(?:2(?:24AESGCM|56AESGCM)|384AESGCM|512AESGCM))|RSASignature(?:DigestPSSSHA(?:1|2(?:24|56)|384|512)|MessagePSSSHA(?:1|2(?:24|56)|384|512)))))\\b", + "name": "support.variable.10.13.c" + }, + { + "match": "\\bkC(?:GImage(?:AuxiliaryDataTypePortraitEffectsMatte|Property(?:DNG(?:A(?:ctiveArea|n(?:alogBalance|tiAliasStrength)|sShot(?:ICCProfile|Pr(?:eProfileMatrix|ofileName)))|B(?:a(?:selineExposureOffset|yerGreenSplit)|estQualityScale|lackLevel(?:Delta(?:H|V)|RepeatDim))|C(?:FA(?:Layout|PlaneColor)|hromaBlurRadius|olorimetricReference|urrent(?:ICCProfile|PreProfileMatrix))|Default(?:BlackRender|Crop(?:Origin|Size)|Scale|UserCrop)|ExtraCameraProfiles|ForwardMatrix(?:1|2)|Linear(?:ResponseLimit|izationTable)|Ma(?:kerNoteSafety|skedAreas)|N(?:ewRawImageDigest|oiseReductionApplied)|O(?:pcodeList(?:1|2|3)|riginal(?:BestQualityFinalSize|Default(?:CropSize|FinalSize)|RawFile(?:D(?:ata|igest)|Name)))|Pr(?:eview(?:Application(?:Name|Version)|ColorSpace|DateTime|Settings(?:Digest|Name))|ofile(?:Copyright|EmbedPolicy|HueSatMap(?:D(?:ata(?:1|2)|ims)|Encoding)|LookTable(?:D(?:ata|ims)|Encoding)|Name|ToneCurve))|R(?:aw(?:DataUniqueID|ImageDigest|ToPreviewGain)|eductionMatrix(?:1|2)|owInterleaveFactor)|S(?:hadowScale|ubTileBlockSize))|PNG(?:Comment|Disclaimer|Source|Warning)))|TTypesetterOptionAllowUnboundedLayout|V(?:ImageBufferTransferFunction_Linear|PixelFormatContainsGrayscale))\\b", + "name": "support.variable.10.14.c" + }, + { + "match": "\\bk(?:C(?:FStreamProperty(?:Allow(?:ConstrainedNetworkAccess|ExpensiveNetworkAccess)|ConnectionIsExpensive)|GImage(?:A(?:nimation(?:DelayTime|LoopCount|StartIndex)|uxiliaryDataTypeSemanticSegmentation(?:HairMatte|SkinMatte|TeethMatte))|Property(?:APNG(?:CanvasPixel(?:Height|Width)|FrameInfoArray)|Exif(?:CompositeImage|OffsetTime(?:Digitized|Original)?|Source(?:ExposureTimesOfCompositeImage|ImageNumberOfCompositeImage))|GIF(?:CanvasPixel(?:Height|Width)|FrameInfoArray)|HEICS(?:CanvasPixel(?:Height|Width)|D(?:elayTime|ictionary)|FrameInfoArray|LoopCount|UnclampedDelayTime)))|TFontFeature(?:SampleTextKey|TooltipTextKey)|V(?:ImageBufferAlphaChannelMode(?:Key|_(?:PremultipliedAlpha|StraightAlpha))|MetalTextureStorageMode))|MIDIPropertyNameConfigurationDictionary|Sec(?:PropertyType(?:Array|Number)|UseDataProtectionKeychain))\\b", + "name": "support.variable.10.15.c" + }, + { + "match": "\\bkColorSyncExtendedRange\\b", + "name": "support.variable.10.16.c" + }, + { + "match": "\\bk(?:C(?:FStream(?:NetworkServiceType(?:AVStreaming|Responsive(?:AV|Data))|Property(?:ConnectionIsCellular|NoCellular))|GImage(?:Destination(?:DateTime|Me(?:rgeMetadata|tadata)|Orientation)|Metadata(?:EnumerateRecursively|Namespace(?:DublinCore|Exif(?:Aux)?|IPTCCore|Photoshop|TIFF|XMP(?:Basic|Rights))|Prefix(?:DublinCore|Exif(?:Aux)?|IPTCCore|Photoshop|TIFF|XMP(?:Basic|Rights))|ShouldExcludeXMP))|T(?:Baseline(?:Class(?:AttributeName|Hanging|Ideographic(?:Centered|High|Low)|Math|Roman)|InfoAttributeName|OriginalFont|Reference(?:Font|InfoAttributeName))|FontD(?:escriptorMatching(?:CurrentAssetSize|Descriptors|Error|Percentage|Result|SourceDescriptor|Total(?:AssetSize|DownloadedSize))|ownloadableAttribute)|WritingDirectionAttributeName)|VImageBufferColorPrimaries_P22)|Sec(?:O(?:AEP(?:EncodingParametersAttributeName|M(?:GF1DigestAlgorithmAttributeName|essageLengthAttributeName))|IDSRVName)|P(?:addingOAEPKey|olicyAppleTimeStamping|rivateKeyAttrs|ublicKeyAttrs)))\\b", + "name": "support.variable.10.8.c" + }, + { + "match": "\\b(?:XPC_ACTIVITY_(?:ALLOW_BATTERY|CHECK_IN|DELAY|GRACE_PERIOD|INTERVAL(?:_(?:1(?:5_MIN|_(?:DAY|HOUR|MIN))|30_MIN|4_HOURS|5_MIN|7_DAYS|8_HOURS))?|PRIORITY(?:_(?:MAINTENANCE|UTILITY))?|RE(?:PEATING|QUIRE_SCREEN_SLEEP))|k(?:AX(?:MarkedMisspelledTextAttribute|TrustedCheckOptionPrompt)|C(?:FStreamPropertySSLContext|GImage(?:Metadata(?:NamespaceExifEX|PrefixExifEX)|Property(?:Exif(?:ISOSpeed(?:Latitude(?:yyy|zzz))?|RecommendedExposureIndex|S(?:ensitivityType|tandardOutputSensitivity))|OpenEXR(?:AspectRatio|Dictionary))|SourceShouldCacheImmediately)|TLanguageAttributeName)|S(?:ec(?:Attr(?:Access(?:Group|ible(?:AfterFirstUnlock(?:ThisDeviceOnly)?|WhenUnlocked(?:ThisDeviceOnly)?)?)|KeyTypeEC|Synchronizable(?:Any)?)|Policy(?:Apple(?:PassbookSigning|Revocation)|RevocationFlags|TeamIdentifier)|Trust(?:E(?:valuationDate|xtendedValidation)|OrganizationName|Re(?:sultValue|vocation(?:Checked|ValidUntilDate))))|peech(?:AudioOutputFormatProperty|Output(?:ChannelMapProperty|ToFileDescriptorProperty)|SynthExtensionProperty)))|vm_kernel_page_(?:mask|s(?:hift|ize)))\\b", + "name": "support.variable.10.9.c" + }, + { + "match": "\\b(?:CATransform3DIdentity|KERNEL_(?:AUDIT_TOKEN|SECURITY_TOKEN)|NDR_record|UUID_NULL|bootstrap_port|gGuid(?:Apple(?:CSP(?:DL)?|DotMac(?:DL|TP)|FileDL|LDAPDL|SdCSPDL|X509(?:CL|TP))|Cssm)|k(?:A(?:ERemoteProcess(?:NameKey|ProcessIDKey|U(?:RLKey|serIDKey))|X(?:A(?:ttachmentTextAttribute|utocorrectedTextAttribute)|BackgroundColorTextAttribute|Fo(?:nt(?:FamilyKey|NameKey|SizeKey|TextAttribute)|reg(?:oundColorTextAttribute|roundColorTextAttribute))|LinkTextAttribute|MisspelledTextAttribute|NaturalLanguageTextAttribute|ReplacementStringTextAttribute|S(?:hadowTextAttribute|trikethrough(?:ColorTextAttribute|TextAttribute)|uperscriptTextAttribute)|Underline(?:ColorTextAttribute|TextAttribute)|V(?:alue(?:AXErrorType|C(?:FRangeType|G(?:PointType|RectType|SizeType))|IllegalType)|isibleNameKey))|u(?:dioStreamAnyRate|thorizationExternalFormLength))|C(?:F(?:DNSServiceFailureKey|ErrorDomain(?:C(?:FNetwork|GImageMetadata)|SystemConfiguration|WinSock)|FTPStatusCodeKey|GetAddrInfoFailureKey|HTTP(?:Authentication(?:AccountDomain|Password|Scheme(?:Basic|Digest|Kerberos|N(?:TLM|egotiate(?:2)?)|XMobileMeAuthToken)|Username)|Version1_(?:0|1))|NetworkProxies(?:Exc(?:eptionsList|ludeSimpleHostnames)|FTP(?:Enable|P(?:assive|ort|roxy))|Gopher(?:Enable|P(?:ort|roxy))|HTTP(?:Enable|P(?:ort|roxy)|S(?:Enable|P(?:ort|roxy)))|ProxyAuto(?:Config(?:Enable|JavaScript|URLString)|DiscoveryEnable)|RTSP(?:Enable|P(?:ort|roxy))|SOCKS(?:Enable|P(?:ort|roxy)))|Proxy(?:AutoConfiguration(?:HTTPResponseKey|JavaScriptKey|URLKey)|HostNameKey|P(?:asswordKey|ortNumberKey)|Type(?:AutoConfiguration(?:JavaScript|URL)|FTP|HTTP(?:S)?|Key|None|SOCKS)|UsernameKey)|S(?:OCKS(?:NegotiationMethodKey|StatusCodeKey|VersionKey)|tream(?:ErrorDomain(?:FTP|HTTP|Mach|Net(?:DB|Services)|SystemConfiguration|WinSock)|NetworkServiceType(?:Background|V(?:ideo|oice))?|Property(?:ProxyLocalBypass|S(?:SL(?:PeerTrust|Settings)|ocketRemote(?:Host|NetService)))|SSL(?:Certificates|IsServer|Level|PeerName|ValidatesCertificateChain)))|URLErrorFailingURL(?:ErrorKey|StringErrorKey))|GImage(?:Destination(?:BackgroundColor|LossyCompressionQuality)|Property(?:8BIM(?:Dictionary|LayerNames)|C(?:IFF(?:C(?:ameraSerialNumber|ontinuousDrive)|D(?:escription|ictionary)|F(?:irmware|lashExposureComp|ocusMode)|Image(?:FileName|Name|SerialNumber)|LensM(?:axMM|inMM|odel)|Me(?:asuredEV|teringMode)|OwnerName|Re(?:cordID|lease(?:Method|Timing))|S(?:elfTimingTime|hootingMode)|WhiteBalanceIndex)|olorModel(?:CMYK|Gray|Lab|RGB)?)|D(?:NG(?:BackwardVersion|CameraSerialNumber|Dictionary|L(?:ensInfo|ocalizedCameraModel)|UniqueCameraModel|Version)|PI(?:Height|Width)|epth)|Exif(?:A(?:pertureValue|ux(?:Dictionary|F(?:irmware|lashCompensation)|ImageNumber|Lens(?:I(?:D|nfo)|Model|SerialNumber)|OwnerName|SerialNumber))|B(?:odySerialNumber|rightnessValue)|C(?:FAPattern|ameraOwnerName|o(?:lorSpace|mp(?:onentsConfiguration|ressedBitsPerPixel)|ntrast)|ustomRendered)|D(?:ateTime(?:Digitized|Original)|eviceSettingDescription|i(?:ctionary|gitalZoomRatio))|Exposure(?:BiasValue|Index|Mode|Program|Time)|F(?:Number|ileSource|lash(?:Energy|PixVersion)?|ocal(?:Len(?:In35mmFilm|gth)|Plane(?:ResolutionUnit|XResolution|YResolution)))|Ga(?:inControl|mma)|I(?:SOSpeedRatings|mageUniqueID)|L(?:ens(?:M(?:ake|odel)|S(?:erialNumber|pecification))|ightSource)|M(?:a(?:kerNote|xApertureValue)|eteringMode)|OECF|Pixel(?:XDimension|YDimension)|RelatedSoundFile|S(?:aturation|cene(?:CaptureType|Type)|ensingMethod|h(?:arpness|utterSpeedValue)|p(?:atialFrequencyResponse|ectralSensitivity)|ub(?:ject(?:Area|Dist(?:Range|ance)|Location)|secTime(?:Digitized)?))|UserComment|Version|WhiteBalance)|FileSize|G(?:IF(?:D(?:elayTime|ictionary)|HasGlobalColorMap|ImageColorMap|LoopCount|UnclampedDelayTime)|PS(?:A(?:ltitude(?:Ref)?|reaInformation)|D(?:OP|ateStamp|est(?:Bearing(?:Ref)?|Distance(?:Ref)?|L(?:atitude(?:Ref)?|ongitude(?:Ref)?))|i(?:ctionary|fferental))|ImgDirection(?:Ref)?|L(?:atitude(?:Ref)?|ongitude(?:Ref)?)|M(?:apDatum|easureMode)|ProcessingMethod|S(?:atellites|peed(?:Ref)?|tatus)|T(?:imeStamp|rack(?:Ref)?)|Version))|HasAlpha|I(?:PTC(?:ActionAdvised|Byline(?:Title)?|C(?:a(?:ptionAbstract|tegory)|ity|o(?:nt(?:act(?:Info(?:Address|C(?:ity|ountry)|Emails|P(?:hones|ostalCode)|StateProvince|WebURLs))?|entLocation(?:Code|Name))|pyrightNotice|untryPrimaryLocation(?:Code|Name))|re(?:atorContactInfo|dit))|D(?:ateCreated|i(?:ctionary|gitalCreation(?:Date|Time)))|E(?:dit(?:Status|orialUpdate)|xpiration(?:Date|Time))|FixtureIdentifier|Headline|Image(?:Orientation|Type)|Keywords|LanguageIdentifier|O(?:bject(?:AttributeReference|Cycle|Name|TypeReference)|rigina(?:lTransmissionReference|tingProgram))|Pro(?:gramVersion|vinceState)|R(?:e(?:ference(?:Date|Number|Service)|lease(?:Date|Time))|ightsUsageTerms)|S(?:cene|ource|pecialInstructions|tarRating|u(?:b(?:Location|jectReference)|pplementalCategory))|TimeCreated|Urgency|WriterEditor)|s(?:Float|Indexed))|JFIF(?:D(?:ensityUnit|ictionary)|IsProgressive|Version|XDensity|YDensity)|Maker(?:Canon(?:AspectRatioInfo|C(?:ameraSerialNumber|ontinuousDrive)|Dictionary|F(?:irmware|lashExposureComp)|ImageSerialNumber|LensModel|OwnerName)|FujiDictionary|MinoltaDictionary|Nikon(?:C(?:ameraSerialNumber|olorMode)|Di(?:ctionary|gitalZoom)|F(?:lash(?:ExposureComp|Setting)|ocus(?:Distance|Mode))|I(?:SOSe(?:lection|tting)|mageAdjustment)|Lens(?:Adapter|Info|Type)|Quality|Sh(?:arpenMode|ootingMode|utterCount)|WhiteBalanceMode)|OlympusDictionary|PentaxDictionary)|Orientation|P(?:NG(?:Author|C(?:hromaticities|opyright|reationTime)|D(?:escription|ictionary)|Gamma|InterlaceType|ModificationTime|Software|Title|XPixelsPerMeter|YPixelsPerMeter|sRGBIntent)|ixel(?:Height|Width)|rofileName)|RawDictionary|TIFF(?:Artist|Co(?:mpression|pyright)|D(?:ateTime|ictionary|ocumentName)|HostComputer|ImageDescription|M(?:ake|odel)|Orientation|P(?:hotometricInterpretation|rimaryChromaticities)|ResolutionUnit|Software|TransferFunction|WhitePoint|XResolution|YResolution))|Source(?:CreateThumbnail(?:FromImage(?:Always|IfAbsent)|WithTransform)|Should(?:AllowFloat|Cache)|T(?:humbnailMaxPixelSize|ypeIdentifierHint)))|M(?:M(?:ApplyTransformProcName|CreateTransformPropertyProcName|Initialize(?:LinkProfileProcName|TransformProcName))|SEncoderDigestAlgorithmSHA(?:1|256))|SIdentity(?:ErrorDomain|GeneratePosixName)|T(?:F(?:o(?:nt(?:AttributeName|BaselineAdjustAttribute|C(?:ascadeListAttribute|haracterSetAttribute|o(?:llection(?:DisallowAutoActivationOption|IncludeDisabledFontsOption|RemoveDuplicatesOption)|pyrightNameKey))|D(?:es(?:criptionNameKey|igner(?:NameKey|URLNameKey))|isplayNameAttribute)|EnabledAttribute|F(?:amilyName(?:Attribute|Key)|eature(?:Se(?:lector(?:DefaultKey|IdentifierKey|NameKey|SettingKey)|ttingsAttribute)|Type(?:ExclusiveKey|IdentifierKey|NameKey|SelectorsKey)|sAttribute)|ixedAdvanceAttribute|ormatAttribute|ullNameKey)|L(?:anguagesAttribute|icense(?:NameKey|URLNameKey))|Ma(?:cintoshEncodingsAttribute|n(?:ager(?:BundleIdentifier|Error(?:Domain|Font(?:AssetNameKey|DescriptorsKey|URLsKey))|RegisteredFontsChangedNotification)|ufacturerNameKey)|trixAttribute)|NameAttribute|OrientationAttribute|P(?:ostScript(?:CIDNameKey|NameKey)|riorityAttribute)|Registration(?:ScopeAttribute|UserInfoAttribute)|S(?:ampleTextNameKey|izeAttribute|lantTrait|tyleName(?:Attribute|Key)|ubFamilyNameKey|ymbolicTrait)|Tra(?:demarkNameKey|itsAttribute)|U(?:RLAttribute|niqueNameKey)|V(?:ariationA(?:ttribute|xis(?:DefaultValueKey|IdentifierKey|M(?:aximumValueKey|inimumValueKey)|NameKey))|e(?:ndorURLNameKey|rsionNameKey))|W(?:eightTrait|idthTrait))|regroundColor(?:AttributeName|FromContextAttributeName))|rame(?:ClippingPathsAttributeName|P(?:ath(?:ClippingPathAttributeName|FillRuleAttributeName|WidthAttributeName)|rogressionAttributeName)))|GlyphInfoAttributeName|KernAttributeName|LigatureAttributeName|ParagraphStyleAttributeName|RunDelegateAttributeName|S(?:troke(?:ColorAttributeName|WidthAttributeName)|uperscriptAttributeName)|T(?:abColumnTerminatorsAttributeName|ypesetterOptionForcedEmbeddingLevel)|Underline(?:ColorAttributeName|StyleAttributeName)|VerticalFormsAttributeName)|V(?:Buffer(?:MovieTimeKey|NonPropagatedAttachmentsKey|PropagatedAttachmentsKey|Time(?:ScaleKey|ValueKey))|I(?:mageBuffer(?:C(?:GColorSpaceKey|hroma(?:Location(?:BottomFieldKey|TopFieldKey|_(?:Bottom(?:Left)?|Center|DV420|Left|Top(?:Left)?))|Subsampling(?:Key|_4(?:11|2(?:0|2))))|leanAperture(?:H(?:eightKey|orizontalOffsetKey)|Key|VerticalOffsetKey|WidthKey)|olorPrimaries(?:Key|_(?:EBU_3213|ITU_R_709_2|SMPTE_C)))|Display(?:DimensionsKey|HeightKey|WidthKey)|Field(?:CountKey|Detail(?:Key|SpatialFirstLine(?:Early|Late)|Temporal(?:BottomFirst|TopFirst)))|GammaLevelKey|ICCProfileKey|P(?:ixelAspectRatio(?:HorizontalSpacingKey|Key|VerticalSpacingKey)|referredCleanApertureKey)|TransferFunction(?:Key|_(?:ITU_R_709_2|SMPTE_240M_1995|UseGamma))|YCbCrMatrix(?:Key|_(?:ITU_R_(?:601_4|709_2)|SMPTE_240M_1995)))|ndefiniteTime)|Pixel(?:Buffer(?:BytesPerRowAlignmentKey|CG(?:BitmapContextCompatibilityKey|ImageCompatibilityKey)|ExtendedPixels(?:BottomKey|LeftKey|RightKey|TopKey)|HeightKey|IOSurface(?:CoreAnimationCompatibilityKey|OpenGL(?:ES(?:FBOCompatibilityKey|TextureCompatibilityKey)|FBOCompatibilityKey|TextureCompatibilityKey)|PropertiesKey)|MemoryAllocatorKey|OpenGL(?:CompatibilityKey|ES(?:CompatibilityKey|TextureCacheCompatibilityKey))|P(?:ixelFormatTypeKey|laneAlignmentKey|ool(?:AllocationThresholdKey|FreeBufferNotification|M(?:aximumBufferAgeKey|inimumBufferCountKey)))|WidthKey)|Format(?:B(?:itsPerBlock|l(?:ackBlock|ock(?:H(?:eight|orizontalAlignment)|VerticalAlignment|Width)))|C(?:G(?:Bitmap(?:ContextCompatibility|Info)|ImageCompatibility)|o(?:decType|n(?:stant|tainsAlpha)))|F(?:illExtendedPixelsCallback|ourCC)|HorizontalSubsampling|Name|OpenGL(?:Compatibility|ESCompatibility|Format|InternalFormat|Type)|Planes|QDCompatibility|VerticalSubsampling))|ZeroTime)|olorSync(?:A(?:CESCGLinearProfile|dobeRGB1998Profile)|B(?:estQuality|lackPointCompensation)|C(?:ameraDeviceClass|onver(?:sion(?:1DLut|3DLut|BPC|ChannelID|GridPoints|InpChan|Matrix|NDLut|OutChan|ParamCurve(?:0|1|2|3|4))|tQuality)|ustomProfiles)|D(?:CIP3Profile|evice(?:Class|De(?:faultProfileID|scription(?:s)?)|HostScope|ID|ModeDescription(?:s)?|Profile(?:I(?:D|s(?:Current|Default|Factory))|URL|sNotification)|RegisteredNotification|U(?:nregisteredNotification|serScope))|isplay(?:Device(?:Class|ProfilesNotification)|P3Profile)|raftQuality)|F(?:actoryProfiles|ixedPointRange)|Generic(?:CMYKProfile|Gray(?:Gamma22Profile|Profile)|LabProfile|RGBProfile|XYZProfile)|ITUR(?:2020Profile|709Profile)|NormalQuality|Pr(?:eferredCMM|interDeviceClass|ofile(?:C(?:lass|o(?:lorSpace|mputerDomain))|Description|H(?:eader|ostScope)|MD5Digest|PCS|RepositoryChangeNotification|U(?:RL|ser(?:Domain|Scope)))?)|R(?:OMMRGBProfile|e(?:gistrationUpdateWindowServer|nderingIntent(?:Absolute|Perceptual|Relative|Saturation|UseProfileHeader)?))|S(?:RGBProfile|cannerDeviceClass|ig(?:A(?:ToB(?:0Tag|1Tag|2Tag)|bstractClass)|B(?:ToA(?:0Tag|1Tag|2Tag)|lue(?:ColorantTag|TRCTag))|C(?:mykData|o(?:lorSpaceClass|pyrightTag))|D(?:eviceM(?:fgDescTag|odelDescTag)|isplayClass)|G(?:amutTag|r(?:ay(?:Data|TRCTag)|een(?:ColorantTag|TRCTag)))|InputClass|L(?:abData|inkClass)|Media(?:BlackPointTag|WhitePointTag)|NamedColor(?:2Tag|Class)|OutputClass|Pr(?:eview(?:0Tag|1Tag|2Tag)|ofile(?:DescriptionTag|SequenceDescTag))|R(?:ed(?:ColorantTag|TRCTag)|gbData)|TechnologyTag|ViewingCond(?:DescTag|itionsTag)|XYZData))|Transform(?:C(?:odeFragment(?:MD5|Type)|reator)|D(?:eviceTo(?:Device|PCS)|stSpace)|FullConversionData|GamutCheck|Info|P(?:CSTo(?:Device|PCS)|arametricConversionData)|S(?:implifiedConversionData|rcSpace)|Tag)))|D(?:ADiskDescription(?:Bus(?:NameKey|PathKey)|Device(?:GUIDKey|InternalKey|ModelKey|P(?:athKey|rotocolKey)|RevisionKey|TDMLockedKey|UnitKey|VendorKey)|Media(?:B(?:SD(?:M(?:ajorKey|inorKey)|NameKey|UnitKey)|lockSizeKey)|ContentKey|E(?:jectableKey|ncrypt(?:edKey|ionDetailKey))|IconKey|KindKey|LeafKey|NameKey|PathKey|RemovableKey|SizeKey|TypeKey|UUIDKey|W(?:holeKey|ritableKey))|Volume(?:KindKey|MountableKey|N(?:ameKey|etworkKey)|PathKey|TypeKey|UUIDKey))|R(?:A(?:bstractFile|ccessDate|llFilesystems|pplicationIdentifier|ttributeModificationDate|udio(?:FourChannelKey|PreEmphasisKey))|B(?:ackupDate|ibliographicFile|lock(?:Size(?:Key)?|TypeKey)|u(?:fferZone1DataKey|rn(?:AppendableKey|CompletionAction(?:Eject|Key|Mount)|DoubleLayerL0DataZoneBlocksKey|FailureAction(?:Eject|Key|None)|Key|OverwriteDiscKey|RequestedSpeedKey|St(?:atusChangedNotification|rategy(?:BDDAO|CD(?:SAO|TAO)|DVDDAO|IsRequiredKey|Key))|TestingKey|UnderrunProtectionKey|VerifyDiscKey)))|C(?:DText(?:ArrangerKey|C(?:FStringEncodingKey|haracterCodeKey|losedKey|o(?:mposerKey|pyrightAssertedFor(?:NamesKey|SpecialMessagesKey|TitlesKey)))|DiscIdentKey|Genre(?:CodeKey|Key)|Key|LanguageKey|MCNISRCKey|PerformerKey|S(?:izeKey|ongwriterKey|pecialMessageKey)|T(?:OC(?:2Key|Key)|itleKey))|o(?:ntentModificationDate|pyrightFile)|reationDate)|D(?:VD(?:CopyrightInfoKey|TimestampKey)|ata(?:FormKey|Preparer)|e(?:faultDate|vice(?:AppearedNotification|BurnSpeed(?:BD1x|CD1x|DVD1x|HDDVD1x|Max|sKey)|C(?:an(?:TestWrite(?:CDKey|DVDKey)|UnderrunProtect(?:CDKey|DVDKey)|Write(?:BD(?:Key|R(?:EKey|Key))|CD(?:Key|R(?:Key|WKey|awKey)|SAOKey|T(?:AOKey|extKey))|DVD(?:DAOKey|Key|PlusR(?:DoubleLayerKey|Key|W(?:DoubleLayerKey|Key))|R(?:AMKey|DualLayerKey|Key|W(?:DualLayerKey|Key)))|HDDVD(?:Key|R(?:AMKey|DualLayerKey|Key|W(?:DualLayerKey|Key)))|I(?:SRCKey|ndexPointsKey)|Key))|urrentWriteSpeedKey)|DisappearedNotification|FirmwareRevisionKey|I(?:ORegistryEntryPathKey|s(?:BusyKey|TrayOpenKey))|LoadingMechanismCan(?:EjectKey|InjectKey|OpenKey)|M(?:aximumWriteSpeedKey|edia(?:B(?:SDNameKey|locks(?:FreeKey|OverwritableKey|UsedKey))|Class(?:BD|CD|DVD|HDDVD|Key|Unknown)|DoubleLayerL0DataZoneBlocksKey|I(?:nfoKey|s(?:AppendableKey|BlankKey|ErasableKey|OverwritableKey|ReservedKey))|S(?:essionCountKey|tate(?:InTransition|Key|MediaPresent|None))|T(?:rackCountKey|ype(?:BDR(?:E|OM)?|CDR(?:OM|W)?|DVD(?:PlusR(?:DoubleLayer|W(?:DoubleLayer)?)?|R(?:AM|DualLayer|OM|W(?:DualLayer)?)?)|HDDVDR(?:AM|DualLayer|OM|W(?:DualLayer)?)?|Key|Unknown))))|P(?:hysicalInterconnect(?:ATAPI|Fi(?:breChannel|reWire)|Key|Location(?:External|Internal|Key|Unknown)|SCSI|USB)|roductNameKey)|S(?:tatusChangedNotification|upportLevel(?:AppleS(?:hipping|upported)|Key|None|Unsupported|VendorSupported))|Track(?:InfoKey|RefsKey)|VendorNameKey|Write(?:BufferSizeKey|CapabilitiesKey))))|E(?:ffectiveDate|r(?:ase(?:StatusChangedNotification|Type(?:Complete|Key|Quick))|rorStatus(?:AdditionalSenseStringKey|Error(?:InfoStringKey|Key|StringKey)|Key|Sense(?:CodeStringKey|Key)))|xpirationDate)|FreeBlocksKey|HFSPlus(?:CatalogNodeID|TextEncodingHint)?|I(?:SO(?:9660(?:Level(?:One|Two)|VersionNumber)?|Level|MacExtensions|RockRidgeExtensions)|n(?:dexPointsKey|visible))|Joliet|M(?:a(?:c(?:ExtendedFinderFlags|Fi(?:le(?:Creator|Type)|nder(?:Flags|HideExtension))|IconLocation|ScrollPosition|Window(?:Bounds|View))|xBurnSpeedKey)|ediaCatalogNumberKey)|NextWritableAddressKey|P(?:osix(?:FileMode|GID|UID)|reGap(?:IsRequiredKey|LengthKey)|ublisher)|Re(?:cordingDate|fConCFTypeCallbacks)|S(?:CMSCopyright(?:Free|Protected(?:Copy|Original))|e(?:rialCopyManagementStateKey|ssion(?:FormatKey|NumberKey))|tatus(?:Current(?:S(?:essionKey|peedKey)|TrackKey)|EraseTypeKey|P(?:ercentCompleteKey|rogress(?:Current(?:KPS|XFactor)|InfoKey))|State(?:Done|Erasing|F(?:ailed|inishing)|Key|None|Preparing|Session(?:Close|Open)|Track(?:Close|Open|Write)|Verifying)|Total(?:SessionsKey|TracksKey))|u(?:bchannelDataForm(?:Key|None|Pack|Raw)|ppressMacSpecificFiles)|y(?:nchronousBehaviorKey|stemIdentifier))|Track(?:I(?:SRCKey|sEmptyKey)|LengthKey|ModeKey|NumberKey|Packet(?:SizeKey|Type(?:Fixed|Key|Variable))|StartAddressKey|Type(?:Closed|In(?:complete|visible)|Key|Reserved))|UDF(?:ApplicationIdentifierSuffix|ExtendedFilePermissions|InterchangeLevel|Max(?:InterchangeLevel|VolumeSequenceNumber)|PrimaryVolumeDescriptorNumber|RealTimeFile|V(?:ersion1(?:02|50)|olumeSe(?:quenceNumber|t(?:I(?:dentifier|mplementationUse)|Timestamp)))|WriteVersion)?|V(?:erificationType(?:Checksum|Key|None|ProduceAgain|ReceiveData)|olume(?:C(?:heckedDate|reationDate)|E(?:ffectiveDate|xpirationDate)|ModificationDate|Set))))|F(?:CFont(?:CGColorAttribute|Fa(?:ceAttribute|milyAttribute)|NameAttribute|SizeAttribute|VisibleNameAttribute)|ontPanel(?:A(?:TSUFontIDKey|ttribute(?:SizesKey|TagsKey|ValuesKey|sKey))|BackgroundColorAttributeName|Feature(?:SelectorsKey|TypesKey)|MouseTrackingState|Variation(?:AxesKey|ValuesKey)))|HI(?:Delegate(?:AfterKey|BeforeKey)|Object(?:CustomData(?:C(?:DEFProcIDKey|lassIDKey)|DelegateGroupParametersKey|Parameter(?:NamesKey|TypesKey|ValuesKey)|SuperClassIDKey)|InitParam(?:Description|Event(?:Name|Type)|UserName))|T(?:extViewClassID|oolboxVersionNumber)|View(?:MenuContentID|Window(?:C(?:loseBoxID|o(?:llapseBoxID|ntentID))|GrowBoxID|T(?:itleID|oolbar(?:ButtonID|ID))|ZoomBoxID)))|IO(?:MasterPortDefault|Surface(?:AllocSize|BytesPer(?:Element|Row)|CacheMode|Element(?:Height|Width)|Height|Offset|P(?:ixelFormat|lane(?:B(?:ase|ytesPer(?:Element|Row))|Element(?:Height|Width)|Height|Info|Offset|Size|Width))|Width))|JSClassDefinitionEmpty|LSQuarantine(?:Agent(?:BundleIdentifierKey|NameKey)|DataURLKey|OriginURLKey|T(?:imeStampKey|ype(?:CalendarEventAttachment|EmailAttachment|InstantMessageAttachment|Key|Other(?:Attachment|Download)|WebDownload)))|M(?:D(?:Attribute(?:AllValues|DisplayValues|MultiValued|Name|ReadOnlyValues|Type)|ExporterAvaliable|Item(?:A(?:cquisitionM(?:ake|odel)|l(?:bum|titude)|p(?:erture|pl(?:eLoop(?:Descriptors|s(?:KeyFilterType|LoopMode|RootKey))|icationCategories))|ttributeChangeDate|u(?:di(?:ences|o(?:BitRate|ChannelCount|EncodingApplication|SampleRate|TrackNumber))|thor(?:Addresses|EmailAddresses|s)))|BitsPerSample|C(?:FBundleIdentifier|ameraOwner|ity|o(?:decs|lorSpace|m(?:ment|poser)|nt(?:actKeywords|ent(?:CreationDate|ModificationDate|Type(?:Tree)?)|ributors)|pyright|untry|verage)|reator)|D(?:ateAdded|e(?:liveryType|scription)|i(?:rector|splayName)|ownloadedDate|u(?:eDate|rationSeconds))|E(?:XIF(?:GPSVersion|Version)|ditors|mailAddresses|ncodingApplications|x(?:ecutable(?:Architectures|Platform)|posure(?:Mode|Program|TimeS(?:econds|tring))))|F(?:Number|S(?:C(?:ontentChangeDate|reationDate)|HasCustomIcon|I(?:nvisible|s(?:ExtensionHidden|Stationery))|Label|N(?:ame|odeCount)|Owner(?:GroupID|UserID)|Size)|inderComment|lashOnOff|o(?:calLength(?:35mm)?|nts))|G(?:PS(?:AreaInformation|D(?:OP|ateStamp|est(?:Bearing|Distance|L(?:atitude|ongitude))|ifferental)|M(?:apDatum|easureMode)|ProcessingMethod|Status|Track)|enre)|H(?:asAlphaChannel|eadline)|I(?:SOSpeed|dentifier|mageDirection|n(?:formation|st(?:antMessageAddresses|ructions))|s(?:ApplicationManaged|GeneralMIDISequence|LikelyJunk))|K(?:ey(?:Signature|words)|ind)|L(?:a(?:nguages|stUsedDate|titude|yerNames)|ensModel|ongitude|yricist)|M(?:axAperture|e(?:diaTypes|teringMode)|usical(?:Genre|Instrument(?:Category|Name)))|N(?:amedLocation|umberOfPages)|Or(?:ganizations|i(?:entation|ginal(?:Format|Source)))|P(?:a(?:ge(?:Height|Width)|rticipants|th)|erformers|honeNumbers|ixel(?:Count|Height|Width)|ro(?:ducer|fileName|jects)|ublishers)|R(?:e(?:c(?:ipient(?:Addresses|EmailAddresses|s)|ording(?:Date|Year))|dEyeOnOff|solution(?:HeightDPI|WidthDPI))|ights)|S(?:ecurityMethod|peed|t(?:a(?:rRating|teOrProvince)|reamable)|ubject)|T(?:e(?:mpo|xtContent)|heme|i(?:me(?:Signature|stamp)|tle)|otalBitRate)|URL|V(?:ersion|ideoBitRate)|Wh(?:ereFroms|iteBalance))|Label(?:AddedNotification|BundleURL|C(?:hangedNotification|ontentChangeDate)|DisplayName|I(?:con(?:Data|UUID)|sMutuallyExclusiveSetMember)|Kind(?:IsMutuallyExclusiveSetKey|VisibilityKey)?|RemovedNotification|SetsFinderColor|UUID|Visibility)|P(?:rivateVisibility|ublicVisibility)|Query(?:Did(?:FinishNotification|UpdateNotification)|ProgressNotification|ResultContentRelevance|Scope(?:AllIndexed|Computer(?:Indexed)?|Home|Network(?:Indexed)?)|Update(?:AddedItems|ChangedItems|RemovedItems)))|IDI(?:ObjectType_ExternalMask|Property(?:AdvanceScheduleTimeMuSec|C(?:anRoute|onnectionUniqueID)|D(?:eviceID|isplayName|river(?:DeviceEditorApp|Owner|Version))|I(?:mage|s(?:Broadcast|DrumMachine|E(?:ffectUnit|mbeddedEntity)|Mixer|Sampler))|M(?:a(?:nufacturer|x(?:ReceiveChannels|SysExSpeed|TransmitChannels))|odel)|Name|Offline|P(?:anDisruptsStereo|rivate)|Receive(?:Channels|s(?:BankSelect(?:LSB|MSB)|Clock|MTC|Notes|ProgramChanges))|S(?:ingleRealtimeEntity|upports(?:GeneralMIDI|MMC|ShowControl))|Transmit(?:Channels|s(?:BankSelect(?:LSB|MSB)|Clock|MTC|Notes|ProgramChanges))|UniqueID)))|QL(?:PreviewPropertyTextEncodingNameKey|ThumbnailOption(?:IconModeKey|ScaleFactorKey))|S(?:C(?:BondStatusDevice(?:AggregationStatus|Collecting|Distributing)|Comp(?:AnyRegex|Global|HostNames|Interface|Network|S(?:ervice|ystem)|Users)|DynamicStore(?:Domain(?:File|P(?:lugin|refs)|S(?:etup|tate))|Prop(?:Net(?:Interfaces|Primary(?:Interface|Service)|ServiceIDs)|Setup(?:CurrentSet|LastUpdated))|UseSessionKeys)|Ent(?:Net(?:6to4|AirPort|D(?:HCP|NS)|Ethernet|FireWire|I(?:P(?:Sec|v(?:4|6))|nterface)|L(?:2TP|ink)|Modem|P(?:PP(?:Serial|oE)?|roxies)|SMB)|UsersConsoleUser)|Network(?:Interface(?:IPv4|Type(?:6to4|B(?:luetooth|ond)|Ethernet|FireWire|I(?:EEE80211|P(?:Sec|v4)|rDA)|L2TP|Modem|PPP|Serial|VLAN|WWAN))|ProtocolType(?:DNS|IPv(?:4|6)|Proxies|SMB))|Pr(?:ef(?:CurrentSet|NetworkServices|S(?:ets|ystem))|op(?:InterfaceName|MACAddress|Net(?:6to4Relay|DNS(?:DomainName|Options|S(?:e(?:arch(?:Domains|Order)|rver(?:Addresses|Port|Timeout))|ortList|upplementalMatch(?:Domains|Orders)))|EthernetM(?:TU|edia(?:Options|SubType))|I(?:P(?:Sec(?:AuthenticationMethod|ConnectTime|Local(?:Certificate|Identifier(?:Type)?)|RemoteAddress|S(?:haredSecret(?:Encryption)?|tatus)|XAuth(?:Enabled|Name|Password(?:Encryption)?))|v(?:4(?:Addresses|BroadcastAddresses|ConfigMethod|D(?:HCPClientID|estAddresses)|Router|SubnetMasks)|6(?:Addresses|ConfigMethod|DestAddresses|Flags|PrefixLength|Router)))|nterface(?:DeviceName|Hardware|SubType|Type|s))|L(?:2TP(?:IPSecSharedSecret(?:Encryption)?|Transport)|ink(?:Active|Detaching)|ocalHostName)|Modem(?:AccessPointName|Connect(?:Speed|ion(?:Personality|Script))|D(?:ataCompression|evice(?:ContextID|Model|Vendor)|ialMode)|ErrorCorrection|Hold(?:CallWaitingAudibleAlert|DisconnectOnAnswer|Enabled|Reminder(?:Time)?)|Note|PulseDial|Spe(?:aker|ed))|OverridePrimary|P(?:PP(?:A(?:CSPEnabled|uth(?:Name|P(?:assword(?:Encryption)?|ro(?:mpt|tocol))))|C(?:CP(?:Enabled|MPPE(?:128Enabled|40Enabled))|o(?:mm(?:AlternateRemoteAddress|ConnectDelay|DisplayTerminalWindow|Re(?:dial(?:Count|Enabled|Interval)|moteAddress)|TerminalScript|UseTerminalScript)|nnectTime))|D(?:eviceLastCause|i(?:alOnDemand|sconnect(?:On(?:FastUserSwitch|Idle(?:Timer)?|Logout|Sleep)|Time)))|I(?:PCP(?:CompressionVJ|UsePeerDNS)|dleReminder(?:Timer)?)|L(?:CP(?:Compression(?:ACField|PField)|Echo(?:Enabled|Failure|Interval)|M(?:RU|TU)|ReceiveACCM|TransmitACCM)|astCause|ogfile)|OverridePrimary|RetryConnectTime|S(?:essionTimer|tatus)|UseSessionTimer|VerboseLogging)|roxies(?:Exc(?:eptionsList|ludeSimpleHostnames)|FTP(?:Enable|P(?:assive|ort|roxy))|Gopher(?:Enable|P(?:ort|roxy))|HTTP(?:Enable|P(?:ort|roxy)|S(?:Enable|P(?:ort|roxy)))|ProxyAuto(?:Config(?:Enable|JavaScript|URLString)|DiscoveryEnable)|RTSP(?:Enable|P(?:ort|roxy))|SOCKS(?:Enable|P(?:ort|roxy))))|S(?:MB(?:NetBIOSN(?:ame|odeType)|W(?:INSAddresses|orkgroup))|erviceOrder))|SystemComputerName(?:Encoding)?|UserDefinedName|Version))|Resv(?:Inactive|Link)|ValNet(?:I(?:P(?:Sec(?:AuthenticationMethod(?:Certificate|Hybrid|SharedSecret)|LocalIdentifierTypeKeyID|SharedSecretEncryptionKeychain|XAuthPasswordEncryption(?:Keychain|Prompt))|v(?:4ConfigMethod(?:Automatic|BOOTP|DHCP|INFORM|LinkLocal|Manual|PPP)|6ConfigMethod(?:6to4|Automatic|LinkLocal|Manual|RouterAdvertisement)))|nterface(?:SubType(?:L2TP|PPP(?:Serial|oE))|Type(?:6to4|Ethernet|FireWire|IPSec|PPP)))|L2TP(?:IPSecSharedSecretEncryptionKeychain|TransportIP(?:Sec)?)|ModemDialMode(?:IgnoreDialTone|Manual|WaitForDialTone)|PPPAuthP(?:asswordEncryption(?:Keychain|Token)|ro(?:mpt(?:After|Before)|tocol(?:CHAP|EAP|MSCHAP(?:1|2)|PAP)))|SMBNetBIOSNodeType(?:Broadcast|Hybrid|Mixed|Peer)))|K(?:EndTermChars|M(?:aximumTerms|inTermLength)|ProximityIndexing|S(?:t(?:artTermChars|opWords)|ubstitutions)|TermChars)|ec(?:A(?:CLAuthorization(?:Any|De(?:crypt|lete|rive)|E(?:ncrypt|xport(?:Clear|Wrapped))|GenKey|Import(?:Clear|Wrapped)|Keychain(?:Create|Delete|Item(?:Delete|Insert|Modify|Read))|Login|MAC|Sign)|ttr(?:A(?:cc(?:ess|ount)|pplication(?:Label|Tag)|uthenticationType(?:D(?:PA|efault)|HT(?:MLForm|TP(?:Basic|Digest))|MSN|NTLM|RPA)?)|C(?:an(?:De(?:crypt|rive)|Encrypt|Sign|Unwrap|Verify|Wrap)|ertificate(?:Encoding|Type)|omment|reat(?:ionDate|or))|Description|EffectiveKeySize|Generic|Is(?:Extractable|Invisible|Negative|Permanent|Sensitive|suer)|Key(?:Class(?:P(?:rivate|ublic)|Symmetric)?|SizeInBits|Type(?:3DES|AES|CAST|D(?:ES|SA)|ECDSA|R(?:C(?:2|4)|SA))?)|Label|ModificationDate|P(?:RF(?:HmacAlgSHA(?:1|2(?:24|56)|384|512))?|ath|ort|rotocol(?:A(?:FP|ppleTalk)|DAAP|EPPC|FTP(?:Account|Proxy|S)?|HTTP(?:Proxy|S(?:Proxy)?)?|I(?:MAP(?:S)?|PP|RC(?:S)?)|LDAP(?:S)?|NNTP(?:S)?|POP3(?:S)?|RTSP(?:Proxy)?|S(?:M(?:B|TP)|OCKS|SH)|Telnet(?:S)?)?|ublicKeyHash)|Rounds|S(?:alt|e(?:curityDomain|r(?:ialNumber|v(?:er|ice)))|ubject(?:KeyID)?)|Type))|Base(?:32Encoding|64Encoding)|C(?:FError(?:Architecture|GuestAttributes|InfoPlist|Pat(?:h|tern)|Re(?:quirementSyntax|source(?:A(?:dded|ltered)|Missing|S(?:eal|ideband))))|lass(?:Certificate|GenericPassword|I(?:dentity|nternetPassword)|Key)?|o(?:de(?:Attribute(?:Architecture|BundleVersion|Subarchitecture|UniversalFileOffset)|Info(?:C(?:MS|dHashes|ertificates|hangedFiles)|D(?:esignatedRequirement|igestAlgorithm(?:s)?)|Entitlements(?:Dict)?|F(?:lags|ormat)|I(?:dentifier|mplicitDesignatedRequirement)|MainExecutable|P(?:List|latformIdentifier)|R(?:equirement(?:Data|s)|untimeVersion)|S(?:ource|tatus)|T(?:eamIdentifier|ime(?:stamp)?|rust)|Unique))|mpressionRatio))|D(?:ecodeTypeAttribute|igest(?:HMAC(?:KeyAttribute|MD5|SHA(?:1|2))|LengthAttribute|MD(?:2|4|5)|SHA(?:1|2)|TypeAttribute))|Enc(?:ode(?:LineLengthAttribute|TypeAttribute)|rypt(?:Key|ionMode))|GuestAttribute(?:A(?:rchitecture|udit)|Canonical|DynamicCode(?:InfoPlist)?|Hash|MachPort|Pid|Subarchitecture)|I(?:VKey|dentityDomain(?:Default|KerberosKDC)|mport(?:Export(?:Access|Keychain|Passphrase)|Item(?:CertChain|Identity|KeyID|Label|Trust))|nputIs(?:AttributeName|Digest|PlainText|Raw))|KeyAttributeName|LineLength(?:64|76)|M(?:atch(?:CaseInsensitive|DiacriticInsensitive|EmailAddressIfPresent|I(?:ssuers|temList)|Limit(?:All|One)?|Policy|S(?:earchList|ubject(?:Contains|EndsWith|StartsWith|WholeString))|TrustedOnly|ValidOnDate|WidthInsensitive)|ode(?:C(?:BCKey|FBKey)|ECBKey|NoneKey|OFBKey))|OID(?:A(?:DC_CERT_POLICY|PPLE_(?:CERT_POLICY|E(?:KU_(?:CODE_SIGNING(?:_DEV)?|ICHAT_(?:ENCRYPTION|SIGNING)|RESOURCE_SIGNING|SYSTEM_IDENTITY)|XTENSION(?:_(?:A(?:AI_INTERMEDIATE|DC_(?:APPLE_SIGNING|DEV_SIGNING)|PPLE(?:ID_INTERMEDIATE|_SIGNING))|CODE_SIGNING|I(?:NTERMEDIATE_MARKER|TMS_INTERMEDIATE)|WWDR_INTERMEDIATE))?))|uthority(?:InfoAccess|KeyIdentifier))|B(?:asicConstraints|iometricInfo)|C(?:SSMKeyStruct|ert(?:Issuer|ificatePolicies)|lientAuth|o(?:llectiveSt(?:ateProvinceName|reetAddress)|mmonName|untryName)|rl(?:DistributionPoints|Number|Reason))|D(?:OTMAC_CERT_(?:E(?:MAIL_(?:ENCRYPT|SIGN)|XTENSION)|IDENTITY|POLICY)|e(?:ltaCrlIndicator|scription))|E(?:KU_IPSec|mail(?:Address|Protection)|xtended(?:KeyUsage(?:Any)?|UseCodeSigning))|GivenName|HoldInstructionCode|I(?:nvalidityDate|ssu(?:erAltName|ingDistributionPoint(?:s)?))|K(?:ERBv5_PKINIT_KP_(?:CLIENT_AUTH|KDC)|eyUsage)|LocalityName|M(?:S_NTPrincipalName|icrosoftSGC)|N(?:ameConstraints|etscape(?:Cert(?:Sequence|Type)|SGC))|O(?:CSPSigning|rganization(?:Name|alUnitName))|P(?:olicy(?:Constraints|Mappings)|rivateKeyUsagePeriod)|QC_Statements|S(?:er(?:ialNumber|verAuth)|t(?:ateProvinceName|reetAddress)|u(?:bject(?:AltName|DirectoryAttributes|EmailAddress|InfoAccess|KeyIdentifier|Picture|SignatureBitmap)|rname))|Ti(?:meStamping|tle)|UseExemptions|X509V(?:1(?:Certificate(?:IssuerUniqueId|SubjectUniqueId)|IssuerName(?:CStruct|LDAP|Std)?|S(?:erialNumber|ignature(?:Algorithm(?:Parameters|TBS)?|CStruct|Struct)?|ubject(?:Name(?:CStruct|LDAP|Std)?|PublicKey(?:Algorithm(?:Parameters)?|CStruct)?))|V(?:alidityNot(?:After|Before)|ersion))|3(?:Certificate(?:CStruct|Extension(?:C(?:Struct|ritical)|Id|Struct|Type|Value|s(?:CStruct|Struct))|NumberOfExtensions)?|SignedCertificate(?:CStruct)?)))|P(?:adding(?:Key|NoneKey|PKCS(?:1Key|5Key|7Key))|olicy(?:Apple(?:CodeSigning|EAP|I(?:DValidation|Psec)|PKINIT(?:Client|Server)|S(?:MIME|SL)|X509Basic)|Client|KU_(?:CRLSign|D(?:ataEncipherment|ecipherOnly|igitalSignature)|EncipherOnly|Key(?:Agreement|CertSign|Encipherment)|NonRepudiation)|MacAppStoreReceipt|Name|Oid)|roperty(?:Key(?:L(?:abel|ocalizedLabel)|Type|Value)|Type(?:Dat(?:a|e)|Error|S(?:ection|tring|uccess)|Title|URL|Warning)))|R(?:andomDefault|eturn(?:Attributes|Data|PersistentRef|Ref))|S(?:haredPassword|ignatureAttributeName)|Transform(?:A(?:bort(?:AttributeName|OriginatorKey)|ction(?:Attribute(?:Notification|Validation)|CanExecute|ExternalizeExtraData|Finalize|InternalizeExtraData|ProcessData|StartingExecution))|DebugAttributeName|ErrorDomain|InputAttributeName|OutputAttributeName|PreviousErrorKey|TransformName)|Use(?:ItemList|Keychain)|Value(?:Data|PersistentRef|Ref)|ZLibEncoding)|peech(?:Audio(?:GraphProperty|UnitProperty)|C(?:haracterModeProperty|ommand(?:DelimiterProperty|Prefix|Suffix)|urrentVoiceProperty)|Dictionary(?:Abbreviations|Entry(?:Phonemes|Spelling)|LocaleIdentifier|ModificationDate|Pronunciations)|Error(?:C(?:FCallBack|allback(?:CharacterOffset|SpokenString)|ount)|Newest(?:CharacterOffset)?|Oldest(?:CharacterOffset)?|sProperty)|InputModeProperty|Mode(?:Literal|Normal|Phoneme|T(?:ext|une))|N(?:o(?:EndingProsody|SpeechInterrupt)|umberModeProperty)|OutputTo(?:AudioDeviceProperty|ExtAudioFileProperty|FileURLProperty)|P(?:honeme(?:CallBack|Info(?:Example|Hilite(?:End|Start)|Opcode|Symbol)|OptionsProperty|SymbolsProperty)|itch(?:BaseProperty|ModProperty)|reflightThenPause)|R(?:ateProperty|e(?:centSyncProperty|fConProperty|setProperty))|S(?:peechDoneCallBack|tatus(?:NumberOfCharactersLeft|Output(?:Busy|Paused)|P(?:honemeCode|roperty))|yn(?:cCallBack|thesizerInfo(?:Identifier|Manufacturer|Property|Version)))|TextDoneCallBack|Vo(?:ice(?:Creator|ID)|lumeProperty)|WordCFCallBack))|T(?:IS(?:Category(?:InkInputSource|KeyboardInputSource|PaletteInputSource)|Notify(?:EnabledKeyboardInputSourcesChanged|SelectedKeyboardInputSourceChanged)|Property(?:BundleID|I(?:con(?:ImageURL|Ref)|nput(?:ModeID|Source(?:Category|I(?:D|s(?:ASCIICapable|Enable(?:Capable|d)|Select(?:Capable|ed)))|Languages|Type)))|LocalizedName|UnicodeKeyLayoutData)|Type(?:CharacterPalette|Ink|Keyboard(?:InputM(?:ethod(?:ModeEnabled|WithoutModes)|ode)|Layout|Viewer)))|XN(?:Action(?:Align(?:Center|Left|Right)|C(?:hange(?:Color|Font(?:Feature|Variation)?|GlyphVariation|S(?:ize|tyle)|TextPosition)|lear|ountOf(?:AllChanges|StyleChanges|TextChanges)|ut)|Drop|Move|Paste|Typing|UndoLast)|D(?:ataOption(?:CharacterEncodingKey|DocumentTypeKey)|ocumentAttribute(?:AuthorKey|C(?:o(?:m(?:mentKey|panyNameKey)|pyrightKey)|reationTimeKey)|EditorKey|KeywordsKey|ModificationTimeKey|SubjectKey|TitleKey))|MLTEDocumentType|PlainTextDocumentType|QuickTimeDocumentType|RTFDocumentType))|UT(?:ExportedTypeDeclarationsKey|ImportedTypeDeclarationsKey|T(?:agClass(?:FilenameExtension|MIMEType|NSPboardType|OSType)|ype(?:A(?:lias(?:File|Record)|ppl(?:e(?:ICNS|ProtectedMPEG4Audio)|ication(?:Bundle|File)?)|rchive|udio(?:visualContent)?)|B(?:MP|undle)|C(?:Header|PlusPlus(?:Header|Source)|Source|o(?:mpositeContent|n(?:formsToKey|t(?:act|ent))))|D(?:ata(?:base)?|escriptionKey|i(?:rectory|skImage))|Executable|F(?:ileURL|latRTFD|older|ramework)|GIF|HTML|I(?:CO|conFileKey|dentifierKey|mage|nkText|tem)|J(?:PEG(?:2000)?|avaSource)|M(?:P(?:3|EG(?:4(?:Audio)?)?)|essage|o(?:untPoint|vie))|ObjectiveC(?:PlusPlusSource|Source)|P(?:DF|ICT|NG|ackage|lainText)|QuickTime(?:Image|Movie)|R(?:TF(?:D)?|e(?:ferenceURLKey|solvable))|S(?:ourceCode|ymLink)|T(?:IFF|XNTextAndMultimediaData|agSpecificationKey|ext)|U(?:RL|TF(?:16(?:ExternalPlainText|PlainText)|8PlainText))|V(?:Card|ersionKey|ideo|olume)|WebArchive|XML))))|mach_task_self_|oid(?:A(?:d(?:CAIssuer|OCSP)|n(?:sip(?:384r1|521r1)|y(?:ExtendedKeyUsage|Policy))|uthority(?:InfoAccess|KeyIdentifier))|BasicConstraints|C(?:ertificatePolicies|o(?:mmonName|untryName)|rlDistributionPoints)|Description|E(?:cP(?:rime(?:192v1|256v1)|ubKey)|mailAddress|ntrustVersInfo|xtendedKeyUsage(?:C(?:lientAuth|odeSigning)|EmailProtection|IPSec|MicrosoftSGC|NetscapeSGC|OCSPSigning|ServerAuth|TimeStamping)?)|F(?:ee|riendlyName)|Google(?:EmbeddedSignedCertificateTimestamp|OCSPSignedCertificateTimestamp)|I(?:nhibitAnyPolicy|ssuerAltName)|KeyUsage|Local(?:KeyId|ityName)|M(?:SNTPrincipalName|d(?:2(?:Rsa)?|4(?:Rsa)?|5(?:Fee|Rsa)?))|N(?:ameConstraints|etscapeCertType)|Organization(?:Name|alUnitName)|P(?:olicy(?:Constraints|Mappings)|rivateKeyUsagePeriod)|Qt(?:Cps|UNotice)|Rsa|S(?:ha(?:1(?:Dsa(?:CommonOIW|OIW)?|Ecdsa|Fee|Rsa(?:OIW)?)?|2(?:24(?:Ecdsa|Rsa)?|56(?:Ecdsa|Rsa)?)|384(?:Ecdsa|Rsa)?|512(?:Ecdsa|Rsa)?)|tateOrProvinceName|ubject(?:AltName|InfoAccess|KeyIdentifier)))|v(?:m_page_(?:mask|s(?:hift|ize))|printf_stderr_func))\\b", + "name": "support.variable.c" }, { "match": "\\bkCF(?:Islamic(?:TabularCalendar|UmmAlQuraCalendar)|URL(?:AddedToDirectoryDateKey|DocumentIdentifierKey|GenerationIdentifierKey|QuarantinePropertiesKey))\\b", @@ -133,6 +349,10 @@ "match": "\\bkCFURL(?:CanonicalPathKey|Volume(?:Is(?:EncryptedKey|RootFileSystemKey)|Supports(?:CompressionKey|ExclusiveRenamingKey|FileCloningKey|SwapRenamingKey)))\\b", "name": "support.variable.cf.10.12.c" }, + { + "match": "\\bkCF(?:ErrorLocalizedFailureKey|URLVolume(?:AvailableCapacityFor(?:ImportantUsageKey|OpportunisticUsageKey)|Supports(?:AccessPermissionsKey|ImmutableFilesKey)))\\b", + "name": "support.variable.cf.10.13.c" + }, { "match": "\\bkCFURL(?:IsExcludedFromBackupKey|PathKey)\\b", "name": "support.variable.cf.10.8.c" @@ -142,7 +362,7 @@ "name": "support.variable.cf.10.9.c" }, { - "match": "\\bkCF(?:A(?:bsoluteTimeIntervalSince19(?:04|70)|llocator(?:Default|Malloc(?:Zone)?|Null|SystemDefault|UseContext))|B(?:oolean(?:False|True)|u(?:ddhistCalendar|ndle(?:DevelopmentRegionKey|ExecutableKey|I(?:dentifierKey|nfoDictionaryVersionKey)|LocalizationsKey|NameKey|VersionKey)))|C(?:hineseCalendar|o(?:pyString(?:BagCallBacks|DictionaryKeyCallBacks|SetCallBacks)|reFoundationVersionNumber))|DateFormatter(?:AMSymbol|Calendar(?:Name)?|D(?:efault(?:Date|Format)|oesRelativeDateFormattingKey)|EraSymbols|GregorianStartDate|IsLenient|LongEraSymbols|MonthSymbols|PMSymbol|QuarterSymbols|S(?:hort(?:MonthSymbols|QuarterSymbols|Standalone(?:MonthSymbols|QuarterSymbols|WeekdaySymbols)|WeekdaySymbols)|tandalone(?:MonthSymbols|QuarterSymbols|WeekdaySymbols))|T(?:imeZone|woDigitStartDate)|VeryShort(?:MonthSymbols|Standalone(?:MonthSymbols|WeekdaySymbols)|WeekdaySymbols)|WeekdaySymbols)|Error(?:D(?:escriptionKey|omain(?:Cocoa|Mach|OSStatus|POSIX))|FilePathKey|Localized(?:DescriptionKey|FailureReasonKey|RecoverySuggestionKey)|U(?:RLKey|nderlyingErrorKey))|GregorianCalendar|HebrewCalendar|I(?:SO8601Calendar|ndianCalendar|slamicC(?:alendar|ivilCalendar))|JapaneseCalendar|Locale(?:AlternateQuotation(?:BeginDelimiterKey|EndDelimiterKey)|C(?:alendar(?:Identifier)?|o(?:llat(?:ionIdentifier|orIdentifier)|untryCode)|urren(?:cy(?:Code|Symbol)|tLocaleDidChangeNotification))|DecimalSeparator|ExemplarCharacterSet|GroupingSeparator|Identifier|LanguageCode|MeasurementSystem|Quotation(?:BeginDelimiterKey|EndDelimiterKey)|ScriptCode|UsesMetricSystem|VariantCode)|N(?:otFound|u(?:ll|mber(?:Formatter(?:AlwaysShowDecimalSeparator|Currency(?:Code|DecimalSeparator|GroupingSeparator|Symbol)|De(?:cimalSeparator|faultFormat)|ExponentSymbol|FormatWidth|GroupingS(?:eparator|ize)|I(?:n(?:finitySymbol|ternationalCurrencySymbol)|sLenient)|M(?:ax(?:FractionDigits|IntegerDigits|SignificantDigits)|in(?:FractionDigits|IntegerDigits|SignificantDigits|usSign)|ultiplier)|N(?:aNSymbol|egative(?:Prefix|Suffix))|P(?:adding(?:Character|Position)|er(?:MillSymbol|centSymbol)|lusSign|ositive(?:Prefix|Suffix))|Rounding(?:Increment|Mode)|SecondaryGroupingSize|Use(?:GroupingSeparator|SignificantDigits)|ZeroSymbol)|N(?:aN|egativeInfinity)|PositiveInfinity)))|P(?:ersianCalendar|lugIn(?:DynamicRegist(?:erFunctionKey|rationKey)|FactoriesKey|TypesKey|UnloadFunctionKey)|references(?:Any(?:Application|Host|User)|Current(?:Application|Host|User)))|R(?:epublicOfChinaCalendar|unLoop(?:CommonModes|DefaultMode))|S(?:ocket(?:CommandKey|ErrorKey|NameKey|Re(?:gisterCommand|sultKey|trieveCommand)|ValueKey)|tr(?:eamProperty(?:AppendToFile|DataWritten|FileCurrentOffset|Socket(?:NativeHandle|Remote(?:HostName|PortNumber)))|ing(?:BinaryHeapCallBacks|Transform(?:FullwidthHalfwidth|HiraganaKatakana|Latin(?:Arabic|Cyrillic|Greek|H(?:angul|ebrew|iragana)|Katakana|Thai)|MandarinLatin|Strip(?:CombiningMarks|Diacritics)|To(?:Latin|UnicodeName|XMLHex)))))|T(?:imeZoneSystemTimeZoneDidChangeNotification|ype(?:ArrayCallBacks|BagCallBacks|Dictionary(?:KeyCallBacks|ValueCallBacks)|SetCallBacks))|U(?:RL(?:AttributeModificationDateKey|C(?:ontent(?:AccessDateKey|ModificationDateKey)|reationDateKey)|File(?:AllocatedSizeKey|Protection(?:Complete(?:Un(?:lessOpen|tilFirstUserAuthentication))?|Key|None)|Resource(?:IdentifierKey|Type(?:BlockSpecial|CharacterSpecial|Directory|Key|NamedPipe|Regular|S(?:ocket|ymbolicLink)|Unknown))|S(?:ecurityKey|izeKey))|HasHiddenExtensionKey|Is(?:AliasFileKey|DirectoryKey|ExecutableKey|HiddenKey|MountTriggerKey|PackageKey|Re(?:adableKey|gularFileKey)|Sy(?:mbolicLinkKey|stemImmutableKey)|U(?:biquitousItemKey|serImmutableKey)|VolumeKey|WritableKey)|KeysOfUnsetValuesKey|L(?:abelNumberKey|inkCountKey|ocalized(?:LabelKey|NameKey|TypeDescriptionKey))|NameKey|P(?:arentDirectoryURLKey|referredIOBlockSizeKey)|T(?:otalFile(?:AllocatedSizeKey|SizeKey)|ypeIdentifierKey)|UbiquitousItem(?:HasUnresolvedConflictsKey|Is(?:DownloadingKey|Upload(?:edKey|ingKey)))|Volume(?:AvailableCapacityKey|CreationDateKey|I(?:dentifierKey|s(?:AutomountedKey|BrowsableKey|EjectableKey|InternalKey|JournalingKey|LocalKey|Re(?:adOnlyKey|movableKey)))|Localized(?:FormatDescriptionKey|NameKey)|MaximumFileSizeKey|NameKey|ResourceCountKey|Supports(?:AdvisoryFileLockingKey|Case(?:PreservedNamesKey|SensitiveNamesKey)|ExtendedSecurityKey|HardLinksKey|JournalingKey|PersistentIDsKey|R(?:enamingKey|ootDirectoryDatesKey)|S(?:parseFilesKey|ymbolicLinksKey)|VolumeSizesKey|ZeroRunsKey)|TotalCapacityKey|U(?:RL(?:ForRemountingKey|Key)|UIDStringKey)))|serNotification(?:Al(?:ert(?:HeaderKey|MessageKey)|ternateButtonTitleKey)|CheckBoxTitlesKey|DefaultButtonTitleKey|IconURLKey|LocalizationURLKey|OtherButtonTitleKey|P(?:opUp(?:SelectionKey|TitlesKey)|rogressIndicatorValueKey)|SoundURLKey|TextField(?:TitlesKey|ValuesKey)))|XMLTreeError(?:Description|L(?:ineNumber|ocation)|StatusCode))\\b", + "match": "\\bkCF(?:A(?:bsoluteTimeIntervalSince19(?:04|70)|llocator(?:Default|Malloc(?:Zone)?|Null|SystemDefault|UseContext))|B(?:oolean(?:False|True)|u(?:ddhistCalendar|ndle(?:DevelopmentRegionKey|ExecutableKey|I(?:dentifierKey|nfoDictionaryVersionKey)|LocalizationsKey|NameKey|VersionKey)))|C(?:hineseCalendar|o(?:pyString(?:BagCallBacks|DictionaryKeyCallBacks|SetCallBacks)|reFoundationVersionNumber))|DateFormatter(?:AMSymbol|Calendar(?:Name)?|D(?:efault(?:Date|Format)|oesRelativeDateFormattingKey)|EraSymbols|GregorianStartDate|IsLenient|LongEraSymbols|MonthSymbols|PMSymbol|QuarterSymbols|S(?:hort(?:MonthSymbols|QuarterSymbols|Standalone(?:MonthSymbols|QuarterSymbols|WeekdaySymbols)|WeekdaySymbols)|tandalone(?:MonthSymbols|QuarterSymbols|WeekdaySymbols))|T(?:imeZone|woDigitStartDate)|VeryShort(?:MonthSymbols|Standalone(?:MonthSymbols|WeekdaySymbols)|WeekdaySymbols)|WeekdaySymbols)|Error(?:D(?:escriptionKey|omain(?:Cocoa|Mach|OSStatus|POSIX))|FilePathKey|Localized(?:DescriptionKey|FailureReasonKey|RecoverySuggestionKey)|U(?:RLKey|nderlyingErrorKey))|GregorianCalendar|HebrewCalendar|I(?:SO8601Calendar|ndianCalendar|slamicC(?:alendar|ivilCalendar))|JapaneseCalendar|Locale(?:AlternateQuotation(?:BeginDelimiterKey|EndDelimiterKey)|C(?:alendar(?:Identifier)?|o(?:llat(?:ionIdentifier|orIdentifier)|untryCode)|urren(?:cy(?:Code|Symbol)|tLocaleDidChangeNotification))|DecimalSeparator|ExemplarCharacterSet|GroupingSeparator|Identifier|LanguageCode|MeasurementSystem|Quotation(?:BeginDelimiterKey|EndDelimiterKey)|ScriptCode|UsesMetricSystem|VariantCode)|N(?:otFound|u(?:ll|mber(?:Formatter(?:AlwaysShowDecimalSeparator|Currency(?:Code|DecimalSeparator|GroupingSeparator|Symbol)|De(?:cimalSeparator|faultFormat)|ExponentSymbol|FormatWidth|GroupingS(?:eparator|ize)|I(?:n(?:finitySymbol|ternationalCurrencySymbol)|sLenient)|M(?:ax(?:FractionDigits|IntegerDigits|SignificantDigits)|in(?:FractionDigits|IntegerDigits|SignificantDigits|usSign)|ultiplier)|N(?:aNSymbol|egative(?:Prefix|Suffix))|P(?:adding(?:Character|Position)|er(?:MillSymbol|centSymbol)|lusSign|ositive(?:Prefix|Suffix))|Rounding(?:Increment|Mode)|SecondaryGroupingSize|Use(?:GroupingSeparator|SignificantDigits)|ZeroSymbol)|N(?:aN|egativeInfinity)|PositiveInfinity)))|P(?:ersianCalendar|lugIn(?:DynamicRegist(?:erFunctionKey|rationKey)|FactoriesKey|TypesKey|UnloadFunctionKey)|references(?:Any(?:Application|Host|User)|Current(?:Application|Host|User)))|R(?:epublicOfChinaCalendar|unLoop(?:CommonModes|DefaultMode))|S(?:ocket(?:CommandKey|ErrorKey|NameKey|Re(?:gisterCommand|sultKey|trieveCommand)|ValueKey)|tr(?:eam(?:ErrorDomainS(?:OCKS|SL)|Property(?:AppendToFile|DataWritten|FileCurrentOffset|S(?:OCKS(?:P(?:assword|roxy(?:Host|Port)?)|User|Version)|houldCloseNativeSocket|ocket(?:NativeHandle|Remote(?:HostName|PortNumber)|SecurityLevel)))|SocketS(?:OCKSVersion(?:4|5)|ecurityLevel(?:N(?:egotiatedSSL|one)|TLSv1)))|ing(?:BinaryHeapCallBacks|Transform(?:FullwidthHalfwidth|HiraganaKatakana|Latin(?:Arabic|Cyrillic|Greek|H(?:angul|ebrew|iragana)|Katakana|Thai)|MandarinLatin|Strip(?:CombiningMarks|Diacritics)|To(?:Latin|UnicodeName|XMLHex)))))|T(?:imeZoneSystemTimeZoneDidChangeNotification|ype(?:ArrayCallBacks|BagCallBacks|Dictionary(?:KeyCallBacks|ValueCallBacks)|SetCallBacks))|U(?:RL(?:AttributeModificationDateKey|C(?:ontent(?:AccessDateKey|ModificationDateKey)|reationDateKey)|File(?:AllocatedSizeKey|Protection(?:Complete(?:Un(?:lessOpen|tilFirstUserAuthentication))?|Key|None)|Resource(?:IdentifierKey|Type(?:BlockSpecial|CharacterSpecial|Directory|Key|NamedPipe|Regular|S(?:ocket|ymbolicLink)|Unknown))|S(?:ecurityKey|izeKey))|HasHiddenExtensionKey|Is(?:AliasFileKey|DirectoryKey|ExecutableKey|HiddenKey|MountTriggerKey|PackageKey|Re(?:adableKey|gularFileKey)|Sy(?:mbolicLinkKey|stemImmutableKey)|U(?:biquitousItemKey|serImmutableKey)|VolumeKey|WritableKey)|KeysOfUnsetValuesKey|L(?:abelNumberKey|inkCountKey|ocalized(?:LabelKey|NameKey|TypeDescriptionKey))|NameKey|P(?:arentDirectoryURLKey|referredIOBlockSizeKey)|T(?:otalFile(?:AllocatedSizeKey|SizeKey)|ypeIdentifierKey)|UbiquitousItem(?:HasUnresolvedConflictsKey|Is(?:DownloadingKey|Upload(?:edKey|ingKey)))|Volume(?:AvailableCapacityKey|CreationDateKey|I(?:dentifierKey|s(?:AutomountedKey|BrowsableKey|EjectableKey|InternalKey|JournalingKey|LocalKey|Re(?:adOnlyKey|movableKey)))|Localized(?:FormatDescriptionKey|NameKey)|MaximumFileSizeKey|NameKey|ResourceCountKey|Supports(?:AdvisoryFileLockingKey|Case(?:PreservedNamesKey|SensitiveNamesKey)|ExtendedSecurityKey|HardLinksKey|JournalingKey|PersistentIDsKey|R(?:enamingKey|ootDirectoryDatesKey)|S(?:parseFilesKey|ymbolicLinksKey)|VolumeSizesKey|ZeroRunsKey)|TotalCapacityKey|U(?:RL(?:ForRemountingKey|Key)|UIDStringKey)))|serNotification(?:Al(?:ert(?:HeaderKey|MessageKey|TopMostKey)|ternateButtonTitleKey)|CheckBoxTitlesKey|DefaultButtonTitleKey|IconURLKey|KeyboardTypesKey|LocalizationURLKey|OtherButtonTitleKey|P(?:opUp(?:SelectionKey|TitlesKey)|rogressIndicatorValueKey)|SoundURLKey|TextField(?:TitlesKey|ValuesKey)))|XMLTreeError(?:Description|L(?:ineNumber|ocation)|StatusCode))\\b", "name": "support.variable.cf.c" }, { @@ -157,6 +377,22 @@ "match": "\\bkCGColor(?:ConversionBlackPointCompensation|Space(?:Extended(?:Gray|Linear(?:Gray|SRGB)|SRGB)|Linear(?:Gray|SRGB)))\\b", "name": "support.variable.quartz.10.12.c" }, + { + "match": "\\bkCG(?:Color(?:ConversionTRCSize|SpaceGenericLab)|PDF(?:ContextAccessPermissions|Outline(?:Children|Destination(?:Rect)?|Title)))\\b", + "name": "support.variable.quartz.10.13.c" + }, + { + "match": "\\bkCGColorSpace(?:DisplayP3_HLG|ExtendedLinear(?:DisplayP3|ITUR_2020)|ITUR_2020_HLG)\\b", + "name": "support.variable.quartz.10.14.c" + }, + { + "match": "\\bkCGPDFTagProperty(?:A(?:ctualText|lternativeText)|LanguageText|TitleText)\\b", + "name": "support.variable.quartz.10.15.c" + }, + { + "match": "\\bkCGColorSpace(?:DisplayP3_PQ|ITUR_2020_PQ)\\b", + "name": "support.variable.quartz.10.16.c" + }, { "match": "\\bkCGDisplayS(?:howDuplicateLowResolutionModes|tream(?:ColorSpace|DestinationRect|MinimumFrameTime|PreserveAspectRatio|QueueDepth|S(?:howCursor|ourceRect)|YCbCrMatrix(?:_(?:ITU_R_(?:601_4|709_2)|SMPTE_240M_1995))?))\\b", "name": "support.variable.quartz.10.8.c" @@ -169,6 +405,17 @@ "repository": { "functions": { "patterns": [ + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.10.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:AudioFileReadPackets|LS(?:C(?:anRefAcceptItem|opy(?:ApplicationForMIMEType|DisplayNameForRef|Item(?:Attribute(?:s)?|InfoForRef)|KindStringFor(?:MIMEType|Ref|TypeInfo)))|FindApplicationForInfo|GetApplicationFor(?:I(?:nfo|tem)|URL)|Open(?:Application|F(?:SRef|romRefSpec)|ItemsWithRole|URLsWithRole)|RegisterFSRef|S(?:et(?:ExtensionHiddenForRef|ItemAttribute)|haredFileListI(?:nsertItemFSRef|temResolve)))|launch_(?:data_(?:a(?:lloc|rray_(?:get_(?:count|index)|set_index))|copy|dict_(?:get_count|i(?:nsert|terate)|lookup|remove)|free|get_(?:bool|errno|fd|integer|machport|opaque(?:_size)?|real|string|type)|new_(?:bool|fd|integer|machport|opaque|real|string)|set_(?:bool|fd|integer|machport|opaque|real|string))|get_fd|msg))\\b)" + }, { "captures": { "1": { @@ -180,6 +427,17 @@ }, "match": "(\\s*)(\\bCF(?:AbsoluteTime(?:AddGregorianUnits|Get(?:D(?:ayOf(?:Week|Year)|ifferenceAsGregorianUnits)|GregorianDate|WeekOfYear))|GregorianDate(?:GetAbsoluteTime|IsValid)|PropertyList(?:Create(?:From(?:Stream|XMLData)|XMLData)|WriteToStream))\\b)" }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.11.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:AudioHardwareService(?:AddPropertyListener|GetPropertyData(?:Size)?|HasProperty|IsPropertySettable|RemovePropertyListener|SetPropertyData)|CF(?:FTPCreateParsedResourceListing|ReadStreamCreate(?:For(?:HTTPRequest|StreamedHTTPRequest)|WithFTPURL)|WriteStreamCreateWithFTPURL)|LS(?:Copy(?:DisplayNameForURL|ItemInfoForURL|KindStringForURL)|Get(?:ExtensionInfo|HandlerOptionsForContentType)|S(?:et(?:ExtensionHiddenForURL|HandlerOptionsForContentType)|haredFileList(?:AddObserver|C(?:opy(?:Property|Snapshot)|reate)|Get(?:SeedValue|TypeID)|I(?:nsertItemURL|tem(?:Copy(?:DisplayName|IconRef|Property|ResolvedURL)|Get(?:ID|TypeID)|Move|Remove|SetProperty))|Remove(?:AllItems|Observer)|Set(?:Authorization|Property))))|SSLSetEncryptionCertificate)\\b)" + }, { "captures": { "1": { @@ -202,6 +460,17 @@ }, "match": "(\\s*)(\\bCGDisplayModeCopyPixelEncoding\\b)" }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.12.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:OS(?:Atomic(?:A(?:dd(?:32(?:Barrier)?|64(?:Barrier)?)|nd32(?:Barrier|Orig(?:Barrier)?)?)|CompareAndSwap(?:32(?:Barrier)?|64(?:Barrier)?|Int(?:Barrier)?|Long(?:Barrier)?|Ptr(?:Barrier)?)|Decrement(?:32(?:Barrier)?|64(?:Barrier)?)|Increment(?:32(?:Barrier)?|64(?:Barrier)?)|Or32(?:Barrier|Orig(?:Barrier)?)?|TestAnd(?:Clear(?:Barrier)?|Set(?:Barrier)?)|Xor32(?:Barrier|Orig(?:Barrier)?)?)|MemoryBarrier|SpinLock(?:Lock|Try|Unlock))|SecCertificateCopyNormalized(?:IssuerContent|SubjectContent)|os_log_is_(?:debug_enabled|enabled))\\b)" + }, { "captures": { "1": { @@ -211,7 +480,106 @@ "name": "invalid.deprecated.10.12.support.function.clib.c" } }, - "match": "(\\s*)(\\bsyscall\\b)" + "match": "(\\s*)(\\b(?:arc4random_addrandom|syscall)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.13.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:C(?:FNetDiagnostic(?:C(?:opyNetworkStatusPassively|reateWith(?:Streams|URL))|DiagnoseProblemInteractively|SetName)|MSDecoderSetSearchKeychain)|DisposeSRCallBackUPP|GetIconRefFromFileInfo|InvokeSRCallBackUPP|NewSRCallBackUPP|Re(?:adIconFromFSRef|gisterIconRefFromFSRef)|S(?:R(?:Add(?:LanguageObject|Text)|C(?:ancelRecognition|hangeLanguageObject|loseRecognitionSystem|o(?:ntinueRecognition|untItems))|Draw(?:RecognizedText|Text)|EmptyLanguageObject|Get(?:IndexedItem|LanguageModel|Property|Reference)|Idle|New(?:Language(?:Model|ObjectFrom(?:DataFile|Handle))|P(?:ath|hrase)|Recognizer|Word)|OpenRecognitionSystem|P(?:rocess(?:Begin|End)|utLanguageObjectInto(?:DataFile|Handle))|Re(?:leaseObject|move(?:IndexedItem|LanguageObject))|S(?:et(?:IndexedItem|LanguageModel|Property)|pe(?:ak(?:AndDrawText|Text)|echBusy)|t(?:artListening|op(?:Listening|Speech))))|ec(?:CertificateCopySerialNumber|Keychain(?:CopyAccess|SetAccess)|TrustSetKeychains))|UnregisterIconRef|os_trace_(?:debug_enabled|info_enabled|type_enabled))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.13.support.function.quartz.c" + } + }, + "match": "(\\s*)(\\bCGColorSpaceC(?:opyICCProfile|reateWithICCProfile)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.14.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:CVOpenGL(?:Buffer(?:Attach|Create|Get(?:Attributes|TypeID)|Pool(?:Create(?:OpenGLBuffer)?|Get(?:Attributes|OpenGLBufferAttributes|TypeID)|Re(?:lease|tain))|Re(?:lease|tain))|Texture(?:Cache(?:Create(?:TextureFromImage)?|Flush|GetTypeID|Re(?:lease|tain))|Get(?:CleanTexCoords|Name|T(?:arget|ypeID))|IsFlipped|Re(?:lease|tain)))|DR(?:AudioTrackCreate|F(?:SObjectGetRealFSRef|ileCreateReal|olderCreateReal))|SecCertificateCopyPublicKey)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.15.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:AcquireIconRef|C(?:C_MD(?:2(?:_(?:Final|Init|Update))?|4(?:_(?:Final|Init|Update))?|5(?:_(?:Final|Init|Update))?)|TFont(?:CreateWithQuickdrawInstance|Manager(?:RegisterFontsForURLs|UnregisterFontsForURLs))|ompositeIconRef)|Get(?:CustomIconsEnabled|IconRef(?:From(?:Component|Folder|IconFamilyPtr|TypeInfo)|Owners)?)|Is(?:DataAvailableInIconRef|IconRefComposite|ValidIconRef)|LSCopy(?:AllHandlersForURLScheme|DefaultHandlerForURLScheme)|OverrideIconRef|Re(?:gisterIconRefFromIconFamily|leaseIconRef|moveIconRefOverride)|S(?:SL(?:AddDistinguishedName|C(?:lose|o(?:ntextGetTypeID|py(?:ALPNProtocols|CertificateAuthorities|DistinguishedNames|PeerTrust|RequestedPeerName(?:Length)?))|reateContext)|Get(?:BufferedReadSize|C(?:lientCertificateState|onnection)|D(?:atagramWriteSize|iffieHellmanParams)|EnabledCiphers|MaxDatagramRecordSize|N(?:egotiated(?:Cipher|ProtocolVersion)|umber(?:EnabledCiphers|SupportedCiphers))|P(?:eer(?:DomainName(?:Length)?|ID)|rotocolVersionM(?:ax|in))|S(?:ession(?:Option|State)|upportedCiphers))|Handshake|Re(?:Handshake|ad)|Set(?:ALPNProtocols|C(?:ertificate(?:Authorities)?|lientSideAuthenticate|onnection)|D(?:atagramHelloCookie|iffieHellmanParams)|E(?:nabledCiphers|rror)|IOFuncs|MaxDatagramRecordSize|OCSPResponse|P(?:eer(?:DomainName|ID)|rotocolVersionM(?:ax|in))|Session(?:Config|Option|TicketsEnabled))|Write)|e(?:cTrust(?:Evaluate(?:Async)?|edApplication(?:C(?:opyData|reateFromPath)|SetData))|tCustomIconsEnabled))|UpdateIconRef|sec_protocol_(?:metadata_get_negotiated_(?:ciphersuite|protocol_version)|options_(?:add_tls_ciphersuite(?:_group)?|set_tls_(?:diffie_hellman_parameters|m(?:ax_version|in_version)))))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.15.support.function.cf.c" + } + }, + "match": "(\\s*)(\\bCF(?:Bundle(?:CloseBundleResourceMap|OpenBundleResource(?:Files|Map))|URLCopyParameterString)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.15.support.function.quartz.c" + } + }, + "match": "(\\s*)(\\bCGColorSpaceIsHDR\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.3.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:DisposeGetScrapDataUPP|InvokeGetScrapDataUPP|NewGetScrapDataUPP|ReleaseFolder)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.4.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:AH(?:GotoMainTOC|RegisterHelpBook)|CFNetServiceRe(?:gister|solve)|Dispose(?:CaretHookUPP|DrawHookUPP|EOLHookUPP|Hi(?:ghHookUPP|tTestHookUPP)|NWidthHookUPP|T(?:E(?:ClickLoopUPP|DoTextUPP|FindWordUPP|RecalcUPP)|XNActionKeyMapperUPP|extWidthHookUPP)|URL(?:NotifyUPP|SystemEventUPP)|WidthHookUPP)|In(?:s(?:Time|XTime|tall(?:TimeTask|XTimeTask))|voke(?:CaretHookUPP|DrawHookUPP|EOLHookUPP|Hi(?:ghHookUPP|tTestHookUPP)|NWidthHookUPP|T(?:E(?:ClickLoopUPP|DoTextUPP|FindWordUPP|RecalcUPP)|XNActionKeyMapperUPP|extWidthHookUPP)|URL(?:NotifyUPP|SystemEventUPP)|WidthHookUPP))|LM(?:Get(?:ApFontID|SysFontSize)|Set(?:ApFontID|SysFontFam))|New(?:CaretHookUPP|DrawHookUPP|EOLHookUPP|Hi(?:ghHookUPP|tTestHookUPP)|NWidthHookUPP|T(?:E(?:ClickLoopUPP|DoTextUPP|FindWordUPP|RecalcUPP)|XNActionKeyMapperUPP|extWidthHookUPP)|URL(?:NotifyUPP|SystemEventUPP)|WidthHookUPP)|P(?:L(?:pos|str(?:c(?:at|hr|mp|py)|len|nc(?:at|mp|py)|pbrk|rchr|s(?:pn|tr)))|rimeTime(?:Task)?)|R(?:emoveTimeTask|mvTime)|SKSearch(?:Group(?:C(?:opyIndexes|reate)|GetTypeID)|Results(?:C(?:opyMatchingTerms|reateWith(?:Documents|Query))|Get(?:Count|InfoInRange|TypeID))))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.5.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:A(?:S(?:GetSourceStyles|SetSourceStyles)|UGraph(?:CountNodeConnections|Get(?:ConnectionInfo|N(?:ode(?:Connections|Info)|umberOfConnections))|NewNode)|udio(?:ConverterFillBuffer|Device(?:AddIOProc|Re(?:ad|moveIOProc))|FileComponent(?:DataIsThisFormat|FileIsThisFormat)))|Dispose(?:AVL(?:CompareItemsUPP|DisposeItemUPP|ItemSizeUPP|WalkUPP)|Drag(?:DrawingUPP|ReceiveHandlerUPP|SendDataUPP|TrackingHandlerUPP)|List(?:ClickLoopUPP|DefUPP|SearchUPP)|Menu(?:ItemDrawingUPP|TitleDrawingUPP)|S(?:crapPromiseKeeperUPP|leepQUPP)|Theme(?:ButtonDrawUPP|EraseUPP|IteratorUPP|TabTitleDrawUPP)|Window(?:PaintUPP|TitleDrawingUPP))|Get(?:NameFromSoundBank|ScriptManagerVariable)|Invoke(?:AVL(?:CompareItemsUPP|DisposeItemUPP|ItemSizeUPP|WalkUPP)|Drag(?:DrawingUPP|ReceiveHandlerUPP|SendDataUPP|TrackingHandlerUPP)|List(?:ClickLoopUPP|DefUPP|SearchUPP)|Menu(?:ItemDrawingUPP|TitleDrawingUPP)|S(?:crapPromiseKeeperUPP|leepQUPP)|Theme(?:ButtonDrawUPP|EraseUPP|IteratorUPP|TabTitleDrawUPP)|Window(?:PaintUPP|TitleDrawingUPP))|Music(?:Device(?:PrepareInstrument|ReleaseInstrument)|Sequence(?:LoadSMF(?:DataWithFlags|WithFlags)|Save(?:MIDIFile|SMFData)))|New(?:AVL(?:CompareItemsUPP|DisposeItemUPP|ItemSizeUPP|WalkUPP)|Drag(?:DrawingUPP|ReceiveHandlerUPP|SendDataUPP|TrackingHandlerUPP)|List(?:ClickLoopUPP|DefUPP|SearchUPP)|Menu(?:ItemDrawingUPP|TitleDrawingUPP)|S(?:crapPromiseKeeperUPP|leepQUPP)|Theme(?:ButtonDrawUPP|EraseUPP|IteratorUPP|TabTitleDrawUPP)|Window(?:PaintUPP|TitleDrawingUPP))|S(?:CNetworkInterfaceRefreshConfiguration|etScriptManagerVariable))\\b)" }, { "captures": { @@ -235,6 +603,17 @@ }, "match": "(\\s*)(\\bCG(?:ContextDrawPDFDocument|PDFDocumentGet(?:ArtBox|BleedBox|CropBox|MediaBox|RotationAngle|TrimBox))\\b)" }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.6.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:Audio(?:Device(?:AddPropertyListener|GetProperty(?:Info)?|RemovePropertyListener|SetProperty)|File(?:C(?:omponent(?:Create|Initialize|OpenFile)|reate)|Initialize|Open)|Hardware(?:AddPropertyListener|GetProperty(?:Info)?|RemovePropertyListener|SetProperty)|Stream(?:AddPropertyListener|GetProperty(?:Info)?|RemovePropertyListener|SetProperty))|Button|DisposeKCCallbackUPP|ExtAudioFile(?:CreateNew|Open)|FlushEvents|I(?:nvokeKCCallbackUPP|sCmdChar)|KC(?:Add(?:AppleSharePassword|Callback|GenericPassword|I(?:nternetPassword(?:WithPath)?|tem))|C(?:hangeSettings|o(?:pyItem|untKeychains)|reateKeychain)|DeleteItem|Find(?:AppleSharePassword|FirstItem|GenericPassword|InternetPassword(?:WithPath)?|NextItem)|Get(?:Attribute|D(?:ata|efaultKeychain)|IndKeychain|Keychain(?:ManagerVersion|Name)?|Status)|IsInteractionAllowed|Lock|Make(?:AliasFromKCRef|KCRefFrom(?:Alias|FSRef))|NewItem|Re(?:lease(?:Item|Keychain|Search)|moveCallback)|Set(?:Attribute|D(?:ata|efaultKeychain)|InteractionAllowed)|U(?:nlock|pdateItem))|Munger|New(?:KCCallbackUPP|MusicTrackFrom)|S(?:CNetworkCheckReachabilityBy(?:Address|Name)|ecHost(?:CreateGuest|RemoveGuest|Se(?:lect(?:Guest|edGuest)|t(?:GuestStatus|HostingPort))))|UC(?:CreateTextBreakLocator|DisposeTextBreakLocator|FindTextBreak)|kc(?:add(?:applesharepassword|genericpassword|internetpassword(?:withpath)?)|createkeychain|find(?:applesharepassword|genericpassword|internetpassword(?:withpath)?)|getkeychainname|unlock))\\b)" + }, { "captures": { "1": { @@ -246,6 +625,17 @@ }, "match": "(\\s*)(\\bCG(?:ConfigureDisplayMode|Display(?:AvailableModes|BestModeForParameters(?:AndRefreshRate)?|CurrentMode|SwitchToMode)|EnableEventStateCombining|FontCreateWithPlatformFont|InhibitLocalEvents|Post(?:KeyboardEvent|MouseEvent|ScrollWheelEvent)|SetLocalEvents(?:FilterDuringSuppressionState|SuppressionInterval))\\b)" }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.7.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:Au(?:dioHardware(?:AddRunLoopSource|RemoveRunLoopSource)|thorization(?:CopyPrivilegedReference|ExecuteWithPrivileges))|C(?:MSEncode(?:rSetEncapsulatedContentType)?|SSM_(?:AC_(?:AuthCompute|PassThrough)|C(?:L_(?:C(?:ert(?:Abort(?:Cache|Query)|C(?:ache|reateTemplate)|DescribeFormat|G(?:et(?:All(?:Fields|TemplateFields)|First(?:CachedFieldValue|FieldValue)|KeyInfo|Next(?:CachedFieldValue|FieldValue))|roup(?:FromVerifiedBundle|ToSignedBundle))|Sign|Verify(?:WithKey)?)|rl(?:A(?:bort(?:Cache|Query)|ddCert)|C(?:ache|reateTemplate)|DescribeFormat|Get(?:All(?:CachedRecordFields|Fields)|First(?:CachedFieldValue|FieldValue)|Next(?:CachedFieldValue|FieldValue))|RemoveCert|S(?:etFields|ign)|Verify(?:WithKey)?))|FreeField(?:Value|s)|IsCertInC(?:achedCrl|rl)|PassThrough)|SP_(?:C(?:hangeLogin(?:Acl|Owner)|reate(?:AsymmetricContext|D(?:eriveKeyContext|igestContext)|KeyGenContext|MacContext|PassThroughContext|RandomGenContext|S(?:ignatureContext|ymmetricContext)))|Get(?:Login(?:Acl|Owner)|OperationalStatistics)|Log(?:in|out)|ObtainPrivateKeyFromPublicKey|PassThrough)|hangeKey(?:Acl|Owner))|D(?:L_(?:Authenticate|C(?:hangeDb(?:Acl|Owner)|reateRelation)|D(?:ata(?:AbortQuery|Delete|Get(?:F(?:irst|romUniqueRecordId)|Next)|Insert|Modify)|b(?:C(?:lose|reate)|Delete|Open)|estroyRelation)|Free(?:NameList|UniqueRecord)|GetDb(?:Acl|Name(?:FromHandle|s)|Owner)|PassThrough)|e(?:cryptData(?:Final|Init(?:P)?|P|Update)?|leteContext(?:Attributes)?|riveKey)|igestData(?:Clone|Final|Init|Update)?)|EncryptData(?:Final|Init(?:P)?|P|Update)?|Free(?:Context|Key)|Ge(?:nerate(?:AlgorithmParams|Key(?:P(?:air(?:P)?)?)?|Mac(?:Final|Init|Update)?|Random)|t(?:APIMemoryFunctions|Context(?:Attribute)?|Key(?:Acl|Owner)|ModuleGUIDFromHandle|Privilege|SubserviceUIDFromHandle|TimeValue))|In(?:it|troduce)|ListAttachedModuleManagers|Module(?:Attach|Detach|Load|Unload)|Query(?:KeySizeInBits|Size)|Retrieve(?:Counter|UniqueId)|S(?:et(?:Context|Privilege)|ignData(?:Final|Init|Update)?)|T(?:P_(?:ApplyCrlToDb|C(?:ert(?:CreateTemplate|G(?:etAllTemplateFields|roup(?:Construct|Prune|ToTupleGroup|Verify))|Re(?:claim(?:Abort|Key)|moveFromCrlTemplate|voke)|Sign)|onfirmCredResult|rl(?:CreateTemplate|Sign|Verify))|Form(?:Request|Submit)|PassThrough|Re(?:ceiveConfirmation|trieveCredResult)|SubmitCredRequest|TupleGroupToCertGroup)|erminate)|U(?:n(?:introduce|wrapKey(?:P)?)|pdateContextAttributes)|Verify(?:D(?:ata(?:Final|Init|Update)?|evice)|Mac(?:Final|Init|Update)?)|WrapKey(?:P)?)|reateThreadPool)|Dispose(?:Debugger(?:DisposeThreadUPP|NewThreadUPP|ThreadSchedulerUPP)|Thread(?:EntryUPP|S(?:chedulerUPP|witchUPP)|TerminationUPP)?)|Get(?:CurrentThread|DefaultThreadStackSize|Thread(?:CurrentTaskRef|State(?:GivenTaskRef)?))|I(?:C(?:Add(?:MapEntry|Profile)|Begin|C(?:ount(?:MapEntries|Pr(?:ef|ofiles))|reateGURLEvent)|Delete(?:MapEntry|Pr(?:ef|ofile))|E(?:ditPreferences|nd)|FindPrefHandle|Get(?:C(?:onfigName|urrentProfile)|DefaultPref|Ind(?:MapEntry|Pr(?:ef|ofile))|MapEntry|P(?:erm|r(?:ef(?:Handle)?|ofileName))|Seed|Version)|LaunchURL|Map(?:Entries(?:Filename|TypeCreator)|Filename|TypeCreator)|ParseURL|S(?:e(?:ndGURLEvent|t(?:CurrentProfile|MapEntry|Pr(?:ef(?:Handle)?|ofileName)))|t(?:art|op)))|nvoke(?:Debugger(?:DisposeThreadUPP|NewThreadUPP|ThreadSchedulerUPP)|Thread(?:EntryUPP|S(?:chedulerUPP|witchUPP)|TerminationUPP))|sMetric)|M(?:DS_(?:In(?:itialize|stall)|Terminate|Uninstall)|P(?:A(?:llocate(?:Aligned|TaskStorageIndex)?|rmTimer)|BlockC(?:lear|opy)|C(?:a(?:ncelTimer|useNotification)|reate(?:CriticalRegion|Event|Notification|Queue|Semaphore|T(?:ask|imer))|urrentTaskID)|D(?:e(?:allocateTaskStorageIndex|l(?:ayUntil|ete(?:CriticalRegion|Event|Notification|Queue|Semaphore|Timer)))|isposeTaskException)|E(?:nterCriticalRegion|x(?:it(?:CriticalRegion)?|tractTaskState))|Free|Get(?:AllocatedBlockSize|Next(?:CpuID|TaskID)|TaskStorageValue)|ModifyNotification(?:Parameters)?|NotifyQueue|Processors(?:Scheduled)?|Re(?:gisterDebugger|moteCall(?:CFM)?)|S(?:et(?:E(?:vent|xceptionHandler)|QueueReserve|T(?:ask(?:St(?:ate|orageValue)|Weight)|imerNotify))|ignalSemaphore)|T(?:askIsPreemptive|erminateTask|hrowException)|UnregisterDebugger|Wait(?:ForEvent|On(?:Queue|Semaphore))|Yield)|usicTrackNewExtendedControlEvent)|New(?:Debugger(?:DisposeThreadUPP|NewThreadUPP|ThreadSchedulerUPP)|Thread(?:EntryUPP|S(?:chedulerUPP|witchUPP)|TerminationUPP)?)|Se(?:c(?:A(?:CL(?:C(?:opySimpleContents|reateFromSimpleContents)|GetAuthorizations|Set(?:Authorizations|SimpleContents))|ccess(?:C(?:opySelectedACLList|reateFromOwnerAndACL)|GetOwnerAndACL))|Certificate(?:C(?:opyPreference|reateFromData)|Get(?:AlgorithmID|CLHandle|Data|Issuer|Subject|Type)|SetPreference)|Identity(?:CopyPreference|Se(?:arch(?:C(?:opyNext|reate)|GetTypeID)|tPreference))|Key(?:CreatePair|Ge(?:nerate|tC(?:S(?:PHandle|SMKey)|redentials))|chain(?:Get(?:CSPHandle|DLDBHandle)|Item(?:Export|Get(?:DLDBHandle|UniqueRecordID)|Import)|Search(?:C(?:opyNext|reateFromAttributes)|GetTypeID)))|Policy(?:Get(?:OID|TPHandle|Value)|Se(?:arch(?:C(?:opyNext|reate)|GetTypeID)|tValue))|Trust(?:Get(?:CssmResult(?:Code)?|Result|TPHandle)|SetParameters))|t(?:DebuggerNotificationProcs|Thread(?:ReadyGivenTaskRef|S(?:cheduler|tate(?:EndCritical)?|witcher)|Terminator)))|Thread(?:BeginCritical|CurrentStackSpace|EndCritical)|YieldTo(?:AnyThread|Thread))\\b)" + }, { "captures": { "1": { @@ -257,6 +647,17 @@ }, "match": "(\\s*)(\\bCFURLEnumeratorGetSourceDidChange\\b)" }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.8.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:A(?:TS(?:CreateFontQueryRunLoopSource|Font(?:A(?:ctivateFrom(?:FileReference|Memory)|pplyFunction)|Deactivate|F(?:amily(?:ApplyFunction|FindFrom(?:Name|QuickDrawName)|Get(?:Encoding|Generation|Name|QuickDrawName)|Iterator(?:Create|Next|Re(?:lease|set)))|indFrom(?:Container|Name|PostScriptName))|Get(?:AutoActivationSettingForApplication|Container(?:FromFileReference)?|F(?:ileReference|ontFamilyResource)|G(?:eneration|lobalAutoActivationSetting)|HorizontalMetrics|Name|PostScriptName|Table(?:Directory)?|VerticalMetrics)|I(?:sEnabled|terator(?:Create|Next|Re(?:lease|set)))|Notif(?:ication(?:Subscribe|Unsubscribe)|y)|Set(?:AutoActivationSettingForApplication|Enabled|GlobalAutoActivationSetting))|GetGeneration)|bsolute(?:DeltaTo(?:Duration|Nanoseconds)|To(?:Duration|Nanoseconds))|dd(?:A(?:bsoluteToAbsolute|tomic(?:16|8)?)|CollectionItem(?:Hdl)?|DurationToAbsolute|FolderDescriptor|NanosecondsToAbsolute|Resource))|B(?:atteryCount|it(?:And(?:Atomic(?:16|8)?)?|Clr|Not|Or(?:Atomic(?:16|8)?)?|S(?:et|hift)|Tst|Xor(?:Atomic(?:16|8)?)?))|C(?:S(?:Copy(?:MachineName|UserName)|GetComponentsThreadMode|SetComponentsThreadMode)|a(?:llComponent(?:C(?:anDo|lose)|Dispatch|Function(?:WithStorage(?:ProcInfo)?)?|Get(?:MPWorkFunction|PublicResource)|Open|Register|Target|Unregister|Version)|ptureComponent)|hangedResource|lo(?:neCollection|se(?:Component(?:ResFile)?|ResFile))|o(?:llectionTagExists|mpareAndSwap|pyCollection|reEndian(?:FlipData|GetFlipper|InstallFlipper)|unt(?:1(?:Resources|Types)|Co(?:llection(?:Items|Owners|Tags)|mponent(?:Instances|s))|Resources|T(?:aggedCollectionItems|ypes)))|ur(?:ResFile|rentProcessorSpeed))|D(?:e(?:bugAssert|crementAtomic(?:16|8)?|l(?:ay|e(?:gateComponentCall|teGestaltValue))|queue|t(?:achResource(?:File)?|ermineIfPathIsEnclosedByFolder))|ispose(?:Co(?:llection(?:ExceptionUPP|FlattenUPP)?|mponent(?:FunctionUPP|MPWorkFunctionUPP|RoutineUPP))|De(?:bug(?:AssertOutputHandlerUPP|Component(?:CallbackUPP)?)|ferredTaskUPP)|ExceptionHandlerUPP|F(?:NSubscriptionUPP|SVolume(?:EjectUPP|MountUPP|UnmountUPP)|olderManagerNotificationUPP)|GetMissingComponentResourceUPP|Handle|IOCompletionUPP|Ptr|ResErrUPP|S(?:electorFunctionUPP|peech(?:DoneUPP|ErrorUPP|PhonemeUPP|SyncUPP|TextDoneUPP|WordUPP))|TimerUPP)|urationTo(?:Absolute|Nanoseconds))|E(?:mpty(?:Collection|Handle)|nqueue)|F(?:N(?:GetDirectoryForSubscription|Notify(?:All|ByPath)?|Subscribe(?:ByPath)?|Unsubscribe)|S(?:AllocateFork|C(?:a(?:ncelVolumeOperation|talogSearch)|lose(?:Fork|Iterator)|o(?:mpareFSRefs|py(?:AliasInfo|D(?:ADiskForVolume|iskIDForVolume)|Object(?:Async|Sync)|URLForVolume))|reate(?:DirectoryUnicode|F(?:ile(?:AndOpenForkUnicode|Unicode)|ork)|Res(?:File|ourceF(?:ile|ork))|StringFromHFSUniStr|VolumeOperation))|D(?:e(?:lete(?:Fork|Object)|termineIfRefIsEnclosedByFolder)|isposeVolumeOperation)|E(?:jectVolume(?:Async|Sync)|xchangeObjects)|F(?:i(?:le(?:Operation(?:C(?:ancel|opyStatus|reate)|GetTypeID|ScheduleWithRunLoop|UnscheduleFromRunLoop)|Security(?:C(?:opyAccessControlList|reate(?:WithFSPermissionInfo)?)|Get(?:Group(?:UUID)?|Mode|Owner(?:UUID)?|TypeID)|RefCreateCopy|Set(?:AccessControlList|Group(?:UUID)?|Mode|Owner(?:UUID)?)))|ndFolder)|lush(?:Fork|Volume)|ollowFinderAlias)|Get(?:Async(?:EjectStatus|MountStatus|UnmountStatus)|CatalogInfo(?:Bulk)?|DataForkName|Fork(?:CBInfo|Position|Size)|HFSUniStrFromString|ResourceForkName|TemporaryDirectoryForReplaceObject|Volume(?:ForD(?:ADisk|iskID)|Info|MountInfo(?:Size)?|Parms))|I(?:s(?:AliasFile|FSRefValid)|terateForks)|LockRange|M(?:a(?:keFSRefUnicode|tchAliasBulk)|o(?:unt(?:LocalVolume(?:Async|Sync)|ServerVolume(?:Async|Sync))|veObject(?:Async|Sync|ToTrash(?:Async|Sync))?))|NewAlias(?:FromPath|Minimal(?:Unicode)?|Unicode)?|Open(?:Fork|Iterator|OrphanResFile|Res(?:File|ourceFile))|Path(?:CopyObject(?:Async|Sync)|FileOperationCopyStatus|GetTemporaryDirectoryForReplaceObject|M(?:akeRef(?:WithOptions)?|oveObject(?:Async|Sync|ToTrash(?:Async|Sync)))|ReplaceObject)|Re(?:adFork|fMakePath|nameUnicode|placeObject|so(?:lve(?:Alias(?:File(?:WithMountFlags)?|WithMountFlags)?|NodeID)|urceFileAlreadyOpen))|Set(?:CatalogInfo|Fork(?:Position|Size)|VolumeInfo)|U(?:n(?:l(?:inkObject|ockRange)|mountVolume(?:Async|Sync))|pdateAlias)|VolumeMount|WriteFork)|i(?:nd(?:Folder|NextComponent)|x(?:2(?:Frac|Long|X)|ATan2|Div|Mul|R(?:atio|ound)))|latten(?:Collection(?:ToHdl)?|PartialCollection)|rac(?:2(?:Fix|X)|Cos|Div|Mul|S(?:in|qrt)))|Ge(?:stalt|t(?:1(?:Ind(?:Resource|Type)|NamedResource|Resource)|Alias(?:Size(?:FromPtr)?|UserType(?:FromPtr)?)|C(?:PUSpeed|o(?:llection(?:DefaultAttributes|ExceptionProc|Item(?:Hdl|Info)?|RetainCount)|mponent(?:In(?:dString|fo|stance(?:Error|Storage))|ListModSeed|Public(?:IndString|Resource(?:List)?)|Re(?:fcon|source)|TypeModSeed)))|Debug(?:ComponentInfo|OptionInfo)|Folder(?:NameUnicode|Types)|HandleSize|Ind(?:Resource|Type|exedCollection(?:Item(?:Hdl|Info)?|Tag))|Ma(?:cOSStatus(?:CommentString|ErrorString)|xResourceSize)|N(?:amedResource|e(?:wCollection|xt(?:FOND|ResourceFile)))|PtrSize|Res(?:Attrs|FileAttrs|Info|ource(?:SizeOnDisk)?)|SpeechInfo|T(?:aggedCollectionItem(?:Info)?|opResourceFile)))|H(?:ClrRBit|GetState|Lock(?:Hi)?|Set(?:RBit|State)|Unlock|and(?:AndHand|ToHand)|omeResFile)|I(?:dentifyFolder|n(?:crementAtomic(?:16|8)?|s(?:ertResourceFile|tall(?:DebugAssertOutputHandler|ExceptionHandler))|v(?:alidateFolderDescriptorCache|oke(?:Co(?:llection(?:ExceptionUPP|FlattenUPP)|mponent(?:MPWorkFunctionUPP|RoutineUPP))|De(?:bug(?:AssertOutputHandlerUPP|ComponentCallbackUPP)|ferredTaskUPP)|ExceptionHandlerUPP|F(?:NSubscriptionUPP|SVolume(?:EjectUPP|MountUPP|UnmountUPP)|olderManagerNotificationUPP)|GetMissingComponentResourceUPP|IOCompletionUPP|ResErrUPP|S(?:electorFunctionUPP|peech(?:DoneUPP|ErrorUPP|PhonemeUPP|SyncUPP|TextDoneUPP|WordUPP))|TimerUPP)))|s(?:H(?:andleValid|eapValid)|PointerValid))|L(?:M(?:Get(?:BootDrive|IntlSpec|MemErr|Res(?:Err|Load)|SysMap|TmpResLoad)|Set(?:BootDrive|IntlSpec|MemErr|Res(?:Err|Load)|Sys(?:FontSize|Map)|TmpResLoad))|o(?:adResource|cale(?:CountNames|Get(?:IndName|Name)|Operation(?:CountLocales|GetLocales))|ng2Fix))|M(?:PSetTaskType|aximumProcessorSpeed|emError|i(?:croseconds|nimumProcessorSpeed))|N(?:anosecondsTo(?:Absolute|Duration)|ew(?:Co(?:llection(?:ExceptionUPP|FlattenUPP)?|mponent(?:FunctionUPP|MPWorkFunctionUPP|RoutineUPP))|De(?:bug(?:AssertOutputHandlerUPP|Component(?:CallbackUPP)?|Option)|ferredTaskUPP)|E(?:mptyHandle|xceptionHandlerUPP)|F(?:NSubscriptionUPP|SVolume(?:EjectUPP|MountUPP|UnmountUPP)|olderManagerNotificationUPP)|Ge(?:staltValue|tMissingComponentResourceUPP)|Handle(?:Clear)?|IOCompletionUPP|Ptr(?:Clear)?|ResErrUPP|S(?:electorFunctionUPP|peech(?:DoneUPP|ErrorUPP|PhonemeUPP|SyncUPP|TextDoneUPP|WordUPP))|TimerUPP))|Open(?:A(?:Component(?:ResFile)?|DefaultComponent)|Component(?:ResFile)?|DefaultComponent)|P(?:B(?:AllocateFork(?:Async|Sync)|C(?:atalogSearch(?:Async|Sync)|lose(?:Fork(?:Async|Sync)|Iterator(?:Async|Sync))|ompareFSRefs(?:Async|Sync)|reate(?:DirectoryUnicode(?:Async|Sync)|F(?:ile(?:AndOpenForkUnicode(?:Async|Sync)|Unicode(?:Async|Sync))|ork(?:Async|Sync))))|Delete(?:Fork(?:Async|Sync)|Object(?:Async|Sync))|ExchangeObjects(?:Async|Sync)|F(?:S(?:CopyFile(?:Async|Sync)|ResolveNodeID(?:Async|Sync))|lush(?:Fork(?:Async|Sync)|Volume(?:Async|Sync)))|Get(?:CatalogInfo(?:Async|Bulk(?:Async|Sync)|Sync)|Fork(?:CBInfo(?:Async|Sync)|Position(?:Async|Sync)|Size(?:Async|Sync))|VolumeInfo(?:Async|Sync))|IterateForks(?:Async|Sync)|M(?:akeFSRefUnicode(?:Async|Sync)|oveObject(?:Async|Sync))|Open(?:Fork(?:Async|Sync)|Iterator(?:Async|Sync))|Re(?:adFork(?:Async|Sync)|nameUnicode(?:Async|Sync))|Set(?:CatalogInfo(?:Async|Sync)|Fork(?:Position(?:Async|Sync)|Size(?:Async|Sync))|VolumeInfo(?:Async|Sync))|UnlinkObject(?:Async|Sync)|WriteFork(?:Async|Sync)|X(?:LockRange(?:Async|Sync)|UnlockRange(?:Async|Sync)))|tr(?:AndHand|To(?:Hand|XHand))|urgeCollection(?:Tag)?)|Re(?:a(?:d(?:Location|PartialResource)|llocateHandle)|coverHandle|gisterComponent(?:FileRef(?:Entries)?|Resource(?:File)?)?|lease(?:Collection|Resource)|move(?:CollectionItem|FolderDescriptor|IndexedCollectionItem|Resource)|place(?:GestaltValue|IndexedCollectionItem(?:Hdl)?)|s(?:Error|olveComponentAlias)|tainCollection)|S(?:64Compare|SL(?:GetProtocolVersion|SetProtocolVersion)|e(?:cTranformCustomGetAttribute|t(?:AliasUserType(?:WithPtr)?|Co(?:llection(?:DefaultAttributes|ExceptionProc|ItemInfo)|mponent(?:Instance(?:Error|Storage)|Refcon))|De(?:bugOptionValue|faultComponent)|GestaltValue|HandleSize|IndexedCollectionItemInfo|PtrSize|Res(?:Attrs|FileAttrs|Info|Load|Purge|ourceSize)|SpeechInfo))|leepQ(?:Install|Remove)|peak(?:Buffer|String|Text)|ub(?:AbsoluteFromAbsolute|DurationFromAbsolute|NanosecondsFromAbsolute)|ysError)|T(?:askLevel|e(?:mpNewHandle|stAnd(?:Clear|Set)|xtToPhonemes)|ickCount)|U(?:64Compare|n(?:captureComponent|flattenCollection(?:FromHdl)?|ique(?:1ID|ID)|registerComponent|signedFixedMulDiv)|p(?:Time|date(?:ResFile|SystemActivity))|se(?:Dictionary|ResFile))|W(?:S(?:Get(?:CFTypeIDFromWSTypeID|WSTypeIDFromCFType)|Method(?:Invocation(?:Add(?:DeserializationOverride|SerializationOverride)|C(?:opy(?:P(?:arameters|roperty)|Serialization)|reate(?:FromSerialization)?)|GetTypeID|Invoke|S(?:cheduleWithRunLoop|et(?:CallBack|P(?:arameters|roperty)))|UnscheduleFromRunLoop)|ResultIsFault)|ProtocolHandler(?:C(?:opy(?:FaultDocument|Property|Re(?:plyD(?:ictionary|ocument)|questD(?:ictionary|ocument)))|reate)|GetTypeID|Set(?:DeserializationOverride|Property|SerializationOverride)))|ide(?:Add|BitShift|Compare|Divide|Multiply|Negate|S(?:hift|quareRoot|ubtract)|WideDivide)|rite(?:PartialResource|Resource))|X2F(?:ix|rac)|annuity|compound|d(?:ec2(?:f|l|num|s(?:tr)?)|tox80)|getaudit|num(?:2dec|tostring)|r(?:andomx|elation)|s(?:etaudit|tr2dec)|x80tod)\\b)" + }, { "captures": { "1": { @@ -290,6 +691,17 @@ }, "match": "(\\s*)(\\bCG(?:Re(?:gisterScreenRefreshCallback|leaseScreenRefreshRects)|Screen(?:RegisterMoveCallback|UnregisterMoveCallback)|UnregisterScreenRefreshCallback|W(?:aitForScreen(?:RefreshRects|UpdateRects)|indowServerCFMachPort))\\b)" }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.10.9.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:AX(?:APIEnabled|MakeProcessTrusted|UIElementPostKeyboardEvent)|CopyProcessName|ExitToShell|Get(?:CurrentProcess|FrontProcess|NextProcess|Process(?:BundleLocation|ForPID|Information|PID))|IsProcessVisible|KillProcess|LaunchApplication|ProcessInformationCopyDictionary|S(?:SL(?:Copy(?:PeerCertificates|TrustedRoots)|DisposeContext|Get(?:Allows(?:AnyRoot|Expired(?:Certs|Roots))|EnableCertVerify|ProtocolVersionEnabled|RsaBlinding)|NewContext|Set(?:Allows(?:AnyRoot|Expired(?:Certs|Roots))|EnableCertVerify|ProtocolVersionEnabled|RsaBlinding|TrustedRoots))|ameProcess|e(?:c(?:ChooseIdentity(?:AsSheet)?|DisplayCertificate(?:Group)?|EditTrust(?:AsSheet)?|Policy(?:CreateWithOID|SetProperties))|tFrontProcess(?:WithOptions)?)|howHideProcess)|WakeUpProcess)\\b)" + }, { "captures": { "1": { @@ -334,6 +746,116 @@ }, "match": "(\\s*)(\\bCG(?:C(?:ontextS(?:electFont|how(?:Glyphs(?:AtPoint|WithAdvances)?|Text(?:AtPoint)?))|ursorIs(?:DrawnInFramebuffer|Visible))|Display(?:FadeOperationInProgress|I(?:OServicePort|sCaptured)))\\b)" }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "invalid.deprecated.tba.support.function.c" + } + }, + "match": "(\\s*)(\\b(?:AUGraph(?:Add(?:Node|RenderNotify)|C(?:l(?:earConnections|ose)|o(?:nnectNodeInput|untNodeInteractions))|DisconnectNodeInput|Get(?:CPULoad|In(?:dNode|teractionInfo)|MaxCPULoad|N(?:ode(?:Count|In(?:foSubGraph|teractions))|umberOfInteractions))|I(?:nitialize|s(?:Initialized|NodeSubGraph|Open|Running))|N(?:ewNodeSubGraph|odeInfo)|Open|Remove(?:Node|RenderNotify)|S(?:etNodeInputCallback|t(?:art|op))|U(?:ninitialize|pdate))|DisposeAUGraph|NewAUGraph|QL(?:PreviewRequest(?:C(?:opy(?:ContentUTI|Options|URL)|reate(?:Context|PDFContext))|FlushContext|Get(?:DocumentObject|GeneratorBundle|TypeID)|IsCancelled|Set(?:D(?:ataRepresentation|ocumentObject)|URLRepresentation))|Thumbnail(?:C(?:ancel|opy(?:DocumentURL|Image|Options)|reate)|DispatchAsync|Get(?:ContentRect|MaximumSize|TypeID)|IsCancelled|Request(?:C(?:opy(?:ContentUTI|Options|URL)|reateContext)|FlushContext|Get(?:DocumentObject|GeneratorBundle|MaximumSize|TypeID)|IsCancelled|Set(?:DocumentObject|Image(?:AtURL|WithData)?|ThumbnailWith(?:DataRepresentation|URLRepresentation))))))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.10.10.c" + } + }, + "match": "(\\s*)(\\b(?:C(?:GLGetDeviceFromGLRenderer|MS(?:DecoderCopySignerTimestampWithPolicy|EncoderCopySignerTimestampWithPolicy)|TRubyAnnotation(?:Create(?:Copy)?|Get(?:Alignment|Overhang|SizeFactor|T(?:extForPosition|ypeID))))|JSGlobalContext(?:CopyName|SetName)|LSCopy(?:ApplicationURLsForBundleIdentifier|DefaultApplicationURLFor(?:ContentType|URL))|SecAccessControl(?:CreateWithFlags|GetTypeID)|UTType(?:CopyAllTagsWithClass|IsD(?:eclared|ynamic))|launch_activate_socket|os_re(?:lease|tain)|qos_class_(?:main|self)|simd_inverse)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.10.11.c" + } + }, + "match": "(\\s*)(\\b(?:Audio(?:ComponentInstantiate|ServicesPlay(?:AlertSoundWithCompletion|SystemSoundWithCompletion))|C(?:MSEncoderSetSignerAlgorithm|T(?:LineEnumerateCaretOffsets|RunGetBaseAdvancesAndOrigins))|IORegistryEntryCopy(?:FromPath|Path)|JSValueIs(?:Array|Date)|MIDI(?:ClientCreateWithBlock|DestinationCreateWithBlock|InputPortCreateWithBlock)|connectx|disconnectx|xpc_(?:array_get_(?:array|dictionary)|dictionary_get_(?:array|dictionary)))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.10.12.c" + } + }, + "match": "(\\s*)(\\b(?:CTRubyAnnotationCreateWithAttributes|IOSurface(?:AllowsPixelSizeCasting|SetPurgeable)|JS(?:Object(?:Get(?:ArrayBufferByte(?:Length|sPtr)|TypedArray(?:B(?:uffer|yte(?:Length|Offset|sPtr))|Length))|Make(?:ArrayBufferWithBytesNoCopy|TypedArray(?:With(?:ArrayBuffer(?:AndOffset)?|BytesNoCopy))?))|ValueGetTypedArrayType)|MDItemsCopyAttributes|Sec(?:CertificateCopyNormalized(?:IssuerSequence|SubjectSequence)|Key(?:C(?:opy(?:Attributes|ExternalRepresentation|KeyExchangeResult|PublicKey)|reate(?:DecryptedData|EncryptedData|RandomKey|Signature|WithData))|IsAlgorithmSupported|VerifySignature))|os_(?:log_(?:create|type_enabled)|unfair_lock_(?:assert_(?:not_owner|owner)|lock|trylock|unlock))|xpc_connection_activate)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.10.13.c" + } + }, + "match": "(\\s*)(\\b(?:AudioUnitExtension(?:CopyComponentList|SetComponentList)|C(?:GImage(?:DestinationAddAuxiliaryDataInfo|SourceCopyAuxiliaryDataInfoAtIndex)|TFontManagerCreateFontDescriptorsFromData|V(?:ColorPrimariesGet(?:IntegerCodePointForString|StringForIntegerCodePoint)|TransferFunctionGet(?:IntegerCodePointForString|StringForIntegerCodePoint)|YCbCrMatrixGet(?:IntegerCodePointForString|StringForIntegerCodePoint)))|IOSurfaceGet(?:Bit(?:DepthOfComponentOfPlane|OffsetOfComponentOfPlane)|N(?:ameOfComponentOfPlane|umberOfComponentsOfPlane)|RangeOfComponentOfPlane|Subsampling|TypeOfComponentOfPlane)|SecCertificateCopySerialNumberData)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.10.14.c" + } + }, + "match": "(\\s*)(\\b(?:AEDeterminePermissionToAutomateTarget|C(?:GImageSourceGetPrimaryImageIndex|TFramesetterCreateWithTypesetter)|Sec(?:CertificateCopyKey|Trust(?:EvaluateWithError|SetSignedCertificateTimestamps))|sec_(?:certificate_c(?:opy_ref|reate)|identity_c(?:opy_(?:certificates_ref|ref)|reate(?:_with_certificates)?)|protocol_(?:metadata_(?:access_(?:distinguished_names|ocsp_response|peer_certificate_chain|supported_signature_algorithms)|c(?:hallenge_parameters_are_equal|opy_peer_public_key|reate_secret(?:_with_context)?)|get_(?:early_data_accepted|negotiated_(?:protocol|tls_ciphersuite)|server_name)|peers_are_equal)|options_(?:add_(?:pre_shared_key|tls_application_protocol)|set_(?:challenge_block|key_update_block|local_identity|peer_authentication_required|tls_(?:false_start_enabled|is_fallback_attempt|ocsp_enabled|re(?:negotiation_enabled|sumption_enabled)|s(?:ct_enabled|erver_name)|tickets_enabled)|verify_block)))|trust_c(?:opy_ref|reate)))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.10.15.c" + } + }, + "match": "(\\s*)(\\b(?:C(?:GAnimateImage(?:AtURLWithBlock|DataWithBlock)|T(?:FontManager(?:RegisterFont(?:Descriptors|URLs)|UnregisterFont(?:Descriptors|URLs))|GlyphInfoGetGlyph))|JS(?:Object(?:DeletePropertyForKey|GetPropertyForKey|HasPropertyForKey|MakeDeferredPromise|SetPropertyForKey)|Value(?:IsSymbol|MakeSymbol))|SecTrustEvaluateAsyncWithError|aligned_alloc|sec_(?:identity_access_certificates|protocol_(?:metadata_(?:access_pre_shared_keys|get_negotiated_tls_protocol_version)|options_(?:a(?:ppend_tls_ciphersuite(?:_group)?|re_equal)|get_default_m(?:ax_(?:dtls_protocol_version|tls_protocol_version)|in_(?:dtls_protocol_version|tls_protocol_version))|set_(?:m(?:ax_tls_protocol_version|in_tls_protocol_version)|pre_shared_key_selection_block|tls_pre_shared_key_identity_hint))))|xpc_type_get_name)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.10.8.c" + } + }, + "match": "(\\s*)(\\b(?:A(?:ECompareDesc|udioQueueProcessingTapGetQueueTime)|C(?:GImage(?:Destination(?:AddImageAndMetadata|CopyImageSource)|Metadata(?:C(?:opy(?:StringValueWithPath|Tag(?:MatchingImageProperty|WithPath|s))|reate(?:FromXMPData|Mutable(?:Copy)?|XMPData))|EnumerateTagsUsingBlock|Re(?:gisterNamespaceForPrefix|moveTagWithPath)|Set(?:TagWithPath|Value(?:MatchingImageProperty|WithPath))|Tag(?:C(?:opy(?:Name(?:space)?|Prefix|Qualifiers|Value)|reate)|GetType(?:ID)?))|SourceCopyMetadataAtIndex)|MS(?:DecoderCopySigner(?:SigningTime|Timestamp(?:Certificates)?)|EncoderCopySignerTimestamp)|T(?:Font(?:CopyDefaultCascadeListForLanguages|GetOpticalBoundsForGlyphs|Manager(?:RegisterGraphicsFont|UnregisterGraphicsFont))|LineGetBoundsWithOptions)|VImageBufferCreateColorSpaceFromAttachments|opyInstrumentInfoFromSoundBank))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.10.9.c" + } + }, + "match": "(\\s*)(\\b(?:A(?:XIsProcessTrustedWithOptions|udioHardware(?:CreateAggregateDevice|DestroyAggregateDevice))|C(?:GImageSourceRemoveCacheAtIndex|TFont(?:CreateForStringWithLanguage|Descriptor(?:CreateCopyWith(?:Family|SymbolicTraits)|MatchFontDescriptorsWithProgressHandler)))|FSEventStreamSetExclusionPaths|Sec(?:PolicyCreate(?:Revocation|WithProperties)|Trust(?:Copy(?:Exceptions|Result)|GetNetworkFetchAllowed|Set(?:Exceptions|NetworkFetchAllowed|OCSPResponse)))|xpc_activity_(?:copy_criteria|get_state|register|s(?:et_(?:criteria|state)|hould_defer)|unregister))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.c" + } + }, + "match": "(\\s*)(\\b(?:A(?:E(?:Build(?:AppleEvent|Desc|Parameters)|C(?:allObjectAccessor|heckIsRecord|o(?:erce(?:Desc|Ptr)|untItems)|reate(?:AppleEvent|Desc(?:FromExternalPtr)?|List|RemoteProcessResolver))|D(?:e(?:codeMessage|lete(?:Item|Param))|ispose(?:Desc|RemoteProcessResolver|Token)|uplicateDesc)|FlattenDesc|Get(?:A(?:rray|ttribute(?:Desc|Ptr))|CoercionHandler|DescData(?:Range|Size)?|EventHandler|InteractionAllowed|Nth(?:Desc|Ptr)|ObjectAccessor|Param(?:Desc|Ptr)|RegisteredMachPort|SpecialHandler|TheCurrentEvent)|In(?:itializeDesc|stall(?:CoercionHandler|EventHandler|ObjectAccessor|SpecialHandler)|teractWithUser)|ManagerInfo|ObjectInit|P(?:r(?:intDescToHandle|ocess(?:AppleEvent|Event|Message))|ut(?:A(?:rray|ttribute(?:Desc|Ptr))|Desc|P(?:aram(?:Desc|Ptr)|tr)))|Re(?:mo(?:teProcessResolver(?:GetProcesses|ScheduleWithRunLoop)|ve(?:CoercionHandler|EventHandler|ObjectAccessor|SpecialHandler))|placeDescData|s(?:etTimer|olve|umeTheCurrentEvent))|S(?:e(?:nd(?:Message)?|t(?:InteractionAllowed|ObjectCallbacks|TheCurrentEvent))|izeOf(?:Attribute|FlattenedDesc|NthItem|Param)|tream(?:C(?:lose(?:Desc|List|Record)?|reateEvent)|Op(?:en(?:Desc|Event|KeyDesc|List|Record)?|tionalParam)|SetRecordType|Write(?:AEDesc|D(?:ata|esc)|Key(?:Desc)?))|uspendTheCurrentEvent)|UnflattenDesc)|H(?:GotoPage|LookupAnchor|RegisterHelpBookWithURL|Search)|S(?:CopySourceAttributes|GetSourceStyleNames|Init|SetSourceAttributes)|U(?:EventListener(?:AddEventType|Create(?:WithDispatchQueue)?|Notify|RemoveEventType)|Listener(?:AddParameter|Create(?:WithDispatchQueue)?|Dispose|RemoveParameter)|Parameter(?:FormatValue|ListenerNotify|Set|Value(?:FromLinear|ToLinear)))|X(?:IsProcessTrusted|Observer(?:AddNotification|Create(?:WithInfoCallback)?|Get(?:RunLoopSource|TypeID)|RemoveNotification)|UIElement(?:C(?:opy(?:A(?:ction(?:Description|Names)|ttribute(?:Names|Value(?:s)?))|ElementAtPosition|MultipleAttributeValues|ParameterizedAttribute(?:Names|Value))|reate(?:Application|SystemWide))|Get(?:AttributeValueCount|Pid|TypeID)|IsAttributeSettable|PerformAction|Set(?:AttributeValue|MessagingTimeout))|Value(?:Create|Get(?:Type(?:ID)?|Value)))|cquireFirstMatchingEventInQueue|ddEventTypesToHandler|u(?:dio(?:C(?:hannelLayoutTag_GetNumberOfChannels|o(?:dec(?:AppendInput(?:BufferList|Data)|GetProperty(?:Info)?|Initialize|ProduceOutput(?:BufferList|Packets)|Reset|SetProperty|Uninitialize)|mponent(?:Co(?:py(?:ConfigurationInfo|Name)|unt)|FindNext|Get(?:Description|Version)|Instance(?:CanDo|Dispose|GetComponent|New)|Register|Validate)|nverter(?:Convert(?:Buffer|ComplexBuffer)|Dispose|FillComplexBuffer|GetProperty(?:Info)?|New(?:Specific)?|Reset|SetProperty)))|Device(?:CreateIOProcID(?:WithBlock)?|DestroyIOProcID|Get(?:CurrentTime|NearestStartTime)|St(?:art(?:AtTime)?|op)|TranslateTime)|F(?:ile(?:C(?:lose|o(?:mponent(?:C(?:loseFile|ountUserData|reateURL)|ExtensionIsThisFormat|FileDataIsThisFormat|Get(?:GlobalInfo(?:Size)?|Property(?:Info)?|UserData(?:Size)?)|InitializeWithCallbacks|Op(?:en(?:URL|WithCallbacks)|timize)|Re(?:ad(?:Bytes|Packet(?:Data|s))|moveUserData)|Set(?:Property|UserData)|Write(?:Bytes|Packets))|untUserData)|reateWithURL)|Get(?:GlobalInfo(?:Size)?|Property(?:Info)?|UserData(?:Size)?)|InitializeWithCallbacks|Op(?:en(?:URL|WithCallbacks)|timize)|Re(?:ad(?:Bytes|PacketData)|moveUserData)|S(?:et(?:Property|UserData)|tream(?:Close|GetProperty(?:Info)?|Open|ParseBytes|Se(?:ek|tProperty)))|Write(?:Bytes|Packets))|ormatGetProperty(?:Info)?)|HardwareUnload|O(?:bject(?:AddPropertyListener(?:Block)?|GetPropertyData(?:Size)?|HasProperty|IsPropertySettable|RemovePropertyListener(?:Block)?|S(?:etPropertyData|how))|utputUnitSt(?:art|op))|Queue(?:A(?:ddPropertyListener|llocateBuffer(?:WithPacketDescriptions)?)|CreateTimeline|D(?:evice(?:Get(?:CurrentTime|NearestStartTime)|TranslateTime)|ispose(?:Timeline)?)|EnqueueBuffer(?:WithParameters)?|F(?:lush|reeBuffer)|Get(?:CurrentTime|P(?:arameter|roperty(?:Size)?))|New(?:Input(?:WithDispatchQueue)?|Output(?:WithDispatchQueue)?)|OfflineRender|P(?:ause|r(?:ime|ocessingTap(?:Dispose|GetSourceAudio|New)))|Re(?:movePropertyListener|set)|S(?:et(?:OfflineRenderFormat|P(?:arameter|roperty))|t(?:art|op)))|Services(?:AddSystemSoundCompletion|CreateSystemSoundID|DisposeSystemSoundID|GetProperty(?:Info)?|Play(?:AlertSound|SystemSound)|RemoveSystemSoundCompletion|SetProperty)|Unit(?:Add(?:PropertyListener|RenderNotify)|GetP(?:arameter|roperty(?:Info)?)|Initialize|Process(?:Multiple)?|Re(?:move(?:PropertyListenerWithUserData|RenderNotify)|nder|set)|S(?:cheduleParameters|etP(?:arameter|roperty))|Uninitialize))|thorization(?:C(?:opy(?:Info|Rights(?:Async)?)|reate(?:FromExternalForm)?)|Free(?:ItemSet)?|MakeExternalForm|Right(?:Get|Remove|Set))))|C(?:A(?:C(?:lock(?:A(?:ddListener|rm)|B(?:arBeatTimeToBeats|eatsToBarBeatTime)|Dis(?:arm|pose)|Get(?:CurrentT(?:empo|ime)|P(?:layRate|roperty(?:Info)?)|StartTime)|New|ParseMIDI|RemoveListener|S(?:MPTETimeToSeconds|e(?:condsToSMPTETime|t(?:CurrentT(?:empo|ime)|P(?:layRate|roperty)))|t(?:art|op))|TranslateTime)|urrentMediaTime)|Show(?:File)?|Transform3D(?:Concat|EqualToTransform|GetAffineTransform|I(?:nvert|s(?:Affine|Identity))|Make(?:AffineTransform|Rotation|Scale|Translation)|Rotate|Scale|Translate))|C_SHA(?:1(?:_(?:Final|Init|Update))?|2(?:24(?:_(?:Final|Init|Update))?|56(?:_(?:Final|Init|Update))?)|384(?:_(?:Final|Init|Update))?|512(?:_(?:Final|Init|Update))?)|F(?:H(?:TTP(?:Authentication(?:AppliesToRequest|C(?:opy(?:Domains|Method|Realm)|reateFromResponse)|GetTypeID|IsValid|Requires(?:AccountDomain|OrderedRequests|UserNameAndPassword))|Message(?:A(?:ddAuthentication|pp(?:endBytes|lyCredential(?:Dictionary|s)))|C(?:opy(?:AllHeaderFields|Body|HeaderFieldValue|Re(?:quest(?:Method|URL)|sponseStatusLine)|SerializedMessage|Version)|reate(?:Copy|Empty|Re(?:quest|sponse)))|Get(?:ResponseStatusCode|TypeID)|Is(?:HeaderComplete|Request)|Set(?:Body|HeaderFieldValue)))|ost(?:C(?:ancelInfoResolution|reate(?:Copy|With(?:Address|Name)))|Get(?:Addressing|Names|Reachability|TypeID)|S(?:cheduleWithRunLoop|etClient|tartInfoResolution)|UnscheduleFromRunLoop))|Net(?:Service(?:Browser(?:Create|GetTypeID|Invalidate|S(?:cheduleWithRunLoop|earchFor(?:Domains|Services)|topSearch)|UnscheduleFromRunLoop)|C(?:ancel|reate(?:Copy|DictionaryWithTXTData|TXTDataWithDictionary)?)|Get(?:Addressing|Domain|Name|PortNumber|T(?:XTData|argetHost|ype(?:ID)?))|Monitor(?:Create|GetTypeID|Invalidate|S(?:cheduleWithRunLoop|t(?:art|op))|UnscheduleFromRunLoop)|Re(?:gisterWithOptions|solveWithTimeout)|S(?:cheduleWithRunLoop|et(?:Client|TXTData))|UnscheduleFromRunLoop)|work(?:Copy(?:ProxiesFor(?:AutoConfigurationScript|URL)|SystemProxySettings)|ExecuteProxyAutoConfiguration(?:Script|URL)))|S(?:ocketStreamSOCKSGetError(?:Subdomain)?|treamCreatePairWithSocketTo(?:CFHost|NetService)))|G(?:Display(?:CreateUUIDFromDisplayID|GetDisplayIDFromUUID)|Image(?:Destination(?:AddImage(?:FromSource)?|C(?:opyTypeIdentifiers|reateWith(?:Data(?:Consumer)?|URL))|Finalize|GetTypeID|SetProperties)|MetadataGetTypeID|Source(?:C(?:opy(?:Properties(?:AtIndex)?|TypeIdentifiers)|reate(?:I(?:mageAtIndex|ncremental)|ThumbnailAtIndex|With(?:Data(?:Provider)?|URL)))|Get(?:Count|Status(?:AtIndex)?|Type(?:ID)?)|UpdateData(?:Provider)?))|L(?:C(?:hoosePixelFormat|learDrawable|opyContext|reate(?:Context|PBuffer))|D(?:es(?:cribe(?:P(?:Buffer|ixelFormat)|Renderer)|troy(?:Context|P(?:Buffer|ixelFormat)|RendererInfo))|isable)|E(?:nable|rrorString)|FlushDrawable|Get(?:C(?:ontextRetainCount|urrentContext)|GlobalOption|O(?:ffScreen|ption)|P(?:Buffer(?:RetainCount)?|arameter|ixelFormat(?:RetainCount)?)|ShareGroup|V(?:ersion|irtualScreen))|IsEnabled|LockContext|QueryRendererInfo|Re(?:lease(?:Context|P(?:Buffer|ixelFormat))|tain(?:Context|P(?:Buffer|ixelFormat)))|Set(?:CurrentContext|FullScreen(?:OnDisplay)?|GlobalOption|O(?:ffScreen|ption)|P(?:Buffer|arameter)|VirtualScreen)|TexImage(?:IOSurface2D|PBuffer)|U(?:nlockContext|pdateContext)))|M(?:CalibrateDisplay|Plugin(?:ExamineContext|HandleSelection|PostMenuCleanup)|S(?:Decoder(?:C(?:opy(?:AllCerts|Content|DetachedContent|EncapsulatedContentType|Signer(?:Cert|EmailAddress|Status))|reate)|FinalizeMessage|Get(?:NumSigners|TypeID)|IsContentEncrypted|SetDetachedContent|UpdateMessage)|Encode(?:Content|r(?:Add(?:Recipients|S(?:igne(?:dAttributes|rs)|upportingCerts))|C(?:opy(?:Enc(?:apsulatedContentType|odedContent)|Recipients|S(?:igners|upportingCerts))|reate)|Get(?:CertificateChainMode|HasDetachedContent|TypeID)|Set(?:CertificateChainMode|EncapsulatedContentTypeOID|HasDetachedContent)|UpdateContent))))|S(?:Backup(?:IsItemExcluded|SetItemExcluded)|DiskSpace(?:CancelRecovery|GetRecoveryEstimate|StartRecovery)|Get(?:DefaultIdentityAuthority|LocalIdentityAuthority|ManagedIdentityAuthority)|Identity(?:A(?:dd(?:Alias|Member)|uth(?:enticateUsingPassword|ority(?:CopyLocalizedName|GetTypeID)))|C(?:ommit(?:Asynchronously)?|reate(?:Copy|GroupMembershipQuery|PersistentReference)?)|Delete|Get(?:A(?:liases|uthority)|C(?:ertificate|lass)|EmailAddress|FullName|Image(?:Data(?:Type)?|URL)|Posix(?:ID|Name)|TypeID|UUID)|Is(?:Committing|Enabled|Hidden|MemberOfGroup)|Query(?:C(?:opyResults|reate(?:For(?:CurrentUser|Name|P(?:ersistentReference|osixID)|UUID))?)|Execute(?:Asynchronously)?|GetTypeID|Stop)|Remove(?:Alias|Client|Member)|Set(?:Certificate|EmailAddress|FullName|I(?:mage(?:Data|URL)|sEnabled)|Password)))|T(?:F(?:ont(?:C(?:o(?:llection(?:C(?:opy(?:ExclusionDescriptors|FontAttribute(?:s)?|QueryDescriptors)|reate(?:CopyWithFontDescriptors|FromAvailableFonts|M(?:atchingFontDescriptors(?:ForFamily|SortedWithCallback|WithOptions)?|utableCopy)|WithFontDescriptors))|GetTypeID|Set(?:ExclusionDescriptors|QueryDescriptors))|py(?:A(?:ttribute|vailableTables)|CharacterSet|DisplayName|F(?:amilyName|eature(?:Settings|s)|ontDescriptor|ullName)|GraphicsFont|LocalizedName|Name|PostScriptName|SupportedLanguages|T(?:able|raits)|Variation(?:Axes)?))|reate(?:CopyWith(?:Attributes|Family|SymbolicTraits)|ForString|PathForGlyph|UIFontForLanguage|With(?:FontDescriptor(?:AndOptions)?|GraphicsFont|Name(?:AndOptions)?|PlatformFont)))|D(?:escriptor(?:C(?:opy(?:Attribute(?:s)?|LocalizedAttribute)|reate(?:CopyWith(?:Attributes|Feature|Variation)|MatchingFontDescriptor(?:s)?|With(?:Attributes|NameAndSize)))|GetTypeID)|rawGlyphs)|Get(?:A(?:dvancesForGlyphs|scent)|Bounding(?:Box|RectsForGlyphs)|CapHeight|Descent|Glyph(?:Count|WithName|sForCharacters)|L(?:eading|igatureCaretPositions)|Matrix|PlatformFont|S(?:ize|lantAngle|tringEncoding|ymbolicTraits)|TypeID|Un(?:derline(?:Position|Thickness)|itsPerEm)|VerticalTranslationsForGlyphs|XHeight)|Manager(?:C(?:o(?:mpareFontFamilyNames|py(?:Available(?:Font(?:FamilyNames|URLs)|PostScriptNames)|RegisteredFontDescriptors))|reateFont(?:Descriptor(?:FromData|sFromURL)|RequestRunLoopSource))|EnableFontDescriptors|Get(?:AutoActivationSetting|ScopeForURL)|IsSupportedFont|Re(?:gisterFonts(?:ForURL|WithAssetNames)|questFonts)|SetAutoActivationSetting|UnregisterFontsForURL))|rame(?:Draw|Get(?:FrameAttributes|Line(?:Origins|s)|Path|StringRange|TypeID|VisibleStringRange)|setter(?:Create(?:Frame|WithAttributedString)|GetType(?:ID|setter)|SuggestFrameSizeWithConstraints)))|G(?:etCoreTextVersion|lyphInfo(?:CreateWith(?:CharacterIdentifier|Glyph(?:Name)?)|Get(?:Character(?:Collection|Identifier)|GlyphName|TypeID)))|Line(?:Create(?:JustifiedLine|TruncatedLine|WithAttributedString)|Draw|Get(?:Glyph(?:Count|Runs)|ImageBounds|OffsetForStringIndex|PenOffsetForFlush|String(?:IndexForPosition|Range)|T(?:railingWhitespaceWidth|yp(?:eID|ographicBounds))))|ParagraphStyle(?:Create(?:Copy)?|Get(?:TypeID|ValueForSpecifier))|Run(?:D(?:elegate(?:Create|Get(?:RefCon|TypeID))|raw)|Get(?:A(?:dvances(?:Ptr)?|ttributes)|Glyph(?:Count|s(?:Ptr)?)|ImageBounds|Positions(?:Ptr)?|St(?:atus|ring(?:Indices(?:Ptr)?|Range))|T(?:extMatrix|yp(?:eID|ographicBounds))))|T(?:extTab(?:Create|Get(?:Alignment|Location|Options|TypeID))|ypesetter(?:Create(?:Line(?:WithOffset)?|WithAttributedString(?:AndOptions)?)|GetTypeID|Suggest(?:ClusterBreak(?:WithOffset)?|LineBreak(?:WithOffset)?))))|V(?:Buffer(?:GetAttachment(?:s)?|PropagateAttachments|Re(?:lease|moveA(?:llAttachments|ttachment)|tain)|SetAttachment(?:s)?)|DisplayLink(?:CreateWith(?:ActiveCGDisplays|CGDisplay(?:s)?|OpenGLDisplayMask)|Get(?:ActualOutputVideoRefreshPeriod|Current(?:CGDisplay|Time)|NominalOutputVideoRefreshPeriod|OutputVideoLatency|TypeID)|IsRunning|Re(?:lease|tain)|S(?:et(?:CurrentCGDisplay(?:FromOpenGLContext)?|Output(?:Callback|Handler))|t(?:art|op))|TranslateTime)|Get(?:CurrentHostTime|HostClock(?:Frequency|MinimumTimeDelta))|ImageBuffer(?:Get(?:C(?:leanRect|olorSpace)|DisplaySize|EncodedSize)|IsFlipped)|Pixel(?:Buffer(?:Create(?:ResolvedAttributesDictionary|With(?:Bytes|IOSurface|PlanarBytes))?|FillExtendedPixels|Get(?:B(?:aseAddress(?:OfPlane)?|ytesPerRow(?:OfPlane)?)|DataSize|ExtendedPixels|Height(?:OfPlane)?|IOSurface|P(?:ixelFormatType|laneCount)|TypeID|Width(?:OfPlane)?)|IsPlanar|LockBaseAddress|Pool(?:Create(?:PixelBuffer(?:WithAuxAttributes)?)?|Flush|Get(?:Attributes|PixelBufferAttributes|TypeID)|Re(?:lease|tain))|Re(?:lease|tain)|UnlockBaseAddress)|FormatDescription(?:ArrayCreateWithAllPixelFormatTypes|CreateWithPixelFormatType|RegisterDescriptionWithPixelFormatType)))|allNextEventHandler|h(?:ange(?:TextToUnicodeInfo|UnicodeToTextInfo)|eckEventQueueForUserCancel)|o(?:lorSync(?:C(?:MM(?:C(?:opy(?:CMMIdentifier|LocalizedName)|reate)|Get(?:Bundle|TypeID))|reateCodeFragment)|Device(?:CopyDeviceInfo|SetCustomProfiles)|Iterate(?:DeviceProfiles|Installed(?:CMMs|Profiles))|Profile(?:C(?:o(?:ntainsTag|py(?:D(?:ata|escriptionString)|Header|Tag(?:Signatures)?))|reate(?:D(?:eviceProfile|isplayTransferTablesFromVCGT)|Link|Mutable(?:Copy)?|With(?:DisplayID|Name|URL))?)|EstimateGamma(?:WithDisplayID)?|Get(?:DisplayTransferFormulaFromVCGT|MD5|TypeID|URL)|Install|RemoveTag|Set(?:Header|Tag)|Uninstall|Verify)|RegisterDevice|Transform(?:C(?:o(?:nvert|pyProperty)|reate)|GetTypeID|SetProperty)|UnregisterDevice)|n(?:tinueSpeech|vertFrom(?:PStringToUnicode|TextToUnicode|UnicodeTo(?:PString|ScriptCodeRun|Text(?:Run)?)))|py(?:Event(?:As|CGEvent)?|NameFromSoundBank|PhonemesFromText|S(?:peechProperty|ymbolicHotKeys)|ThemeIdentifier)|unt(?:UnicodeMappings|Voices))|reate(?:CompDescriptor|Event(?:WithCGEvent)?|LogicalDescriptor|O(?:bjSpecifier|ffsetDescriptor)|RangeDescriptor|Text(?:Encoding|ToUnicodeInfo(?:ByEncoding)?)|UnicodeToText(?:Info(?:ByEncoding)?|RunInfo(?:By(?:Encoding|ScriptCode))?)))|D(?:A(?:A(?:ddCallbackToSession|pprovalSession(?:Create|GetTypeID|ScheduleWithRunLoop|UnscheduleFromRunLoop))|CallbackCreate|Disk(?:C(?:opy(?:Description|IOMedia|WholeDisk)|reateFrom(?:BSDName|IOMedia|VolumePath))|Get(?:BSDName|TypeID))|GetCallbackFromSession|RemoveCallbackFromSession(?:WithKey)?|Session(?:Create|GetTypeID|S(?:cheduleWithRunLoop|etDispatchQueue)|UnscheduleFromRunLoop))|CS(?:CopyTextDefinition|GetTermRangeInString)|R(?:AudioTrackCreateWithURL|Burn(?:Abort|C(?:opyStatus|reate)|Get(?:Device|Properties|TypeID)|SetProperties|WriteLayout)|C(?:DTextBlock(?:Create(?:ArrayFromPackList)?|Flatten|Get(?:Properties|T(?:rackDictionaries|ypeID)|Value)|Set(?:Properties|TrackDictionaries|Value))|opy(?:DeviceArray|LocalizedStringFor(?:AdditionalSense|DiscRecordingError|SenseCode|Value)))|Device(?:Acquire(?:ExclusiveAccess|MediaReservation)|C(?:loseTray|opy(?:DeviceFor(?:BSDName|IORegistryEntryPath)|Info|Status))|EjectMedia|GetTypeID|IsValid|KPSForXFactor|OpenTray|Release(?:ExclusiveAccess|MediaReservation)|XFactorForKPS)|Erase(?:C(?:opyStatus|reate)|Get(?:Device|Properties|TypeID)|S(?:etProperties|tart))|F(?:SObject(?:Copy(?:BaseName|FilesystemPropert(?:ies|y)|MangledName(?:s)?|RealURL|SpecificName(?:s)?)|Get(?:FilesystemMask|Parent)|IsVirtual|Set(?:BaseName|Filesystem(?:Mask|Propert(?:ies|y))|SpecificName(?:s)?))|ile(?:Create(?:RealWithURL|Virtual(?:Link|With(?:Callback|Data)))|GetTypeID|systemTrack(?:Create|EstimateOverhead))|older(?:AddChild|C(?:o(?:nvertRealToVirtual|pyChildren|untChildren)|reate(?:RealWithURL|Virtual))|GetTypeID|RemoveChild))|Get(?:RefCon|Version)|NotificationCenter(?:AddObserver|Create(?:RunLoopSource)?|GetTypeID|RemoveObserver)|SetRefCon|Track(?:Create|EstimateLength|Get(?:Properties|TypeID)|S(?:etProperties|peedTest)))|ebugPrint(?:Event|MainEventQueue)|is(?:ableSecureEventInput|pose(?:AE(?:Coerce(?:DescUPP|PtrUPP)|DisposeExternalUPP|EventHandlerUPP|FilterUPP|IdleUPP)|C(?:a(?:librate(?:EventUPP|UPP)|nCalibrateUPP)|ontrol(?:ActionUPP|EditTextValidationUPP|KeyFilterUPP|UserPane(?:ActivateUPP|DrawUPP|FocusUPP|HitTestUPP|IdleUPP|KeyDownUPP|TrackingUPP)))|D(?:ataBrowser(?:A(?:cceptDragUPP|ddDragItemUPP)|DrawItemUPP|EditItemUPP|GetContextualMenuUPP|HitTestUPP|Item(?:AcceptDragUPP|CompareUPP|D(?:ataUPP|ragRgnUPP)|HelpContentUPP|Notification(?:UPP|WithItemUPP)|ReceiveDragUPP|UPP)|PostProcessDragUPP|ReceiveDragUPP|SelectContextualMenuUPP|TrackingUPP)|ragInputUPP)|E(?:ditUnicodePostUpdateUPP|vent(?:ComparatorUPP|HandlerUPP|Loop(?:IdleTimerUPP|TimerUPP)))|HM(?:ControlContentUPP|Menu(?:ItemContentUPP|TitleContentUPP)|WindowContentUPP)|I(?:con(?:ActionUPP|GetterUPP)|ndexToUCStringUPP)|M(?:odalFilter(?:UPP|YDUPP)|usic(?:EventIterator|Player|Sequence))|N(?:ColorChangedUPP|MUPP)|OS(?:A(?:ActiveUPP|CreateAppleEventUPP|SendUPP)|L(?:A(?:ccessorUPP|djustMarksUPP)|Co(?:mpareUPP|untUPP)|DisposeTokenUPP|Get(?:ErrDescUPP|MarkTokenUPP)|MarkUPP))|SpeechChannel|T(?:XN(?:ActionNameMapperUPP|ContextualMenuSetupUPP|FindUPP|ScrollInfoUPP)|extToUnicodeInfo)|U(?:nicodeToText(?:FallbackUPP|Info|RunInfo)|serItemUPP))))|E(?:nableSecureEventInput|xtAudioFile(?:CreateWithURL|Dispose|GetProperty(?:Info)?|OpenURL|Read|Se(?:ek|tProperty)|Tell|Wr(?:apAudioFileID|ite(?:Async)?)))|F(?:C(?:Add(?:Collection|FontDescriptorToCollection)|Copy(?:CollectionNames|FontDescriptorsInCollection)|FontDescriptorCreateWith(?:FontAttributes|Name)|Remove(?:Collection|FontDescriptorFromCollection))|P(?:IsFontPanelVisible|ShowHideFontPanel)|SEvent(?:Stream(?:C(?:opy(?:Description|PathsBeingWatched)|reate(?:RelativeToDevice)?)|Flush(?:Async|Sync)|Get(?:DeviceBeingWatched|LatestEventId)|Invalidate|Re(?:lease|tain)|S(?:cheduleWithRunLoop|etDispatchQueue|how|t(?:art|op))|UnscheduleFromRunLoop)|s(?:CopyUUIDForDevice|Get(?:CurrentEventId|LastEventIdForDeviceBeforeTime)|PurgeEventsForDeviceUpToEventId))|indSpecificEventInQueue|lush(?:Event(?:Queue|sMatchingListFromQueue)|SpecificEventsFromQueue))|Get(?:A(?:pplication(?:EventTarget|TextEncoding)|udioUnitParameterDisplayType)|C(?:FRunLoopFromEventLoop|olor|urrent(?:ButtonState|Event(?:ButtonState|KeyModifiers|Loop|Queue|Time)?|KeyModifiers))|Event(?:Class|DispatcherTarget|Kind|MonitorTarget|Parameter|RetainCount|Time)|I(?:con(?:FamilyData|RefVariant)|ndVoice)|Keys|M(?:ainEvent(?:Loop|Queue)|enuTrackingData)|NumEventsInQueue|S(?:criptInfoFromTextEncoding|peech(?:Pitch|Rate)|y(?:mbolicHotKeyMode|stemUIMode))|T(?:extEncoding(?:Base|F(?:ormat|romScriptInfo)|Name|Variant)|hemeMe(?:nu(?:ItemExtra|SeparatorHeight|TitleExtra)|tric))|Voice(?:Description|Info))|HI(?:DictionaryWindowShow|GetMousePosition|MouseTrackingGetParameters|Object(?:AddDelegate|C(?:opy(?:ClassID|Delegates)|reate(?:FromBundle)?)|DynamicCast|FromEventTarget|GetEvent(?:HandlerObject|Target)|Is(?:ArchivingIgnored|OfClass)|PrintDebugInfo|Re(?:gisterSubclass|moveDelegate)|UnregisterClass)|PointConvert|RectConvert|S(?:earchWindowShow|hape(?:C(?:ontainsPoint|reate(?:Copy|Difference|Empty|Intersection|Mutable(?:Copy|WithRect)?|Union|With(?:QDRgn|Rect)|Xor))|Difference|Enumerate|Get(?:AsQDRgn|Bounds|TypeID)|I(?:n(?:set|tersect(?:sRect)?)|s(?:Empty|Rectangular))|Offset|ReplacePathInCGContext|Set(?:Empty|WithShape)|Union(?:WithRect)?|Xor)|izeConvert)|Theme(?:ApplyBackground|B(?:eginFocus|rushCreateCGColor)|Draw(?:B(?:ackground|utton)|ChasingArrows|F(?:ocusRect|rame)|G(?:enericWell|r(?:abber|o(?:upBox|wBox)))|Header|Menu(?:Ba(?:ckground|rBackground)|Item|Separator|Title)|P(?:aneSplitter|lacard|opupArrow)|S(?:crollBarDelimiters|e(?:gment|parator))|T(?:ab(?:Pane)?|extBox|i(?:ckMark|tleBarWidget)|rack(?:TickMarks)?)|WindowFrame)|EndFocus|Get(?:Button(?:BackgroundBounds|ContentBounds|Shape)|GrowBoxBounds|MenuBackgroundShape|ScrollBarTrackRect|T(?:ab(?:DrawShape|Pane(?:ContentShape|DrawShape)|Shape)|ext(?:ColorForThemeBrush|Dimensions)|rack(?:Bounds|DragRect|LiveValue|Part(?:Bounds|s)|Thumb(?:PositionFrom(?:Bounds|Offset)|Shape)))|UIFontType|Window(?:RegionHit|Shape))|HitTest(?:ScrollBarArrows|Track)|Set(?:Fill|Stroke|TextFill)))|I(?:O(?:BSDNameMatching|C(?:atalogue(?:GetData|ModuleLoaded|Reset|SendData|Terminate)|onnect(?:Add(?:Client|Ref)|Call(?:Async(?:Method|S(?:calarMethod|tructMethod))|Method|S(?:calarMethod|tructMethod))|GetService|MapMemory(?:64)?|Release|Set(?:CFPropert(?:ies|y)|NotificationPort)|Trap(?:0|1|2|3|4|5|6)|UnmapMemory(?:64)?)|reateReceivePort)|DispatchCalloutFromMessage|Iterator(?:IsValid|Next|Reset)|Kit(?:GetBusyState|WaitQuiet)|MasterPort|NotificationPort(?:Create|Destroy|Get(?:MachPort|RunLoopSource)|Set(?:DispatchQueue|ImportanceReceiver))|O(?:bject(?:Co(?:nformsTo|py(?:BundleIdentifierForClass|Class|SuperclassForClass))|Get(?:Class|KernelRetainCount|RetainCount|UserRetainCount)|IsEqualTo|Re(?:lease|tain))|penFirmwarePathMatching)|Registry(?:CreateIterator|Entry(?:Create(?:CFPropert(?:ies|y)|Iterator)|FromPath|Get(?:Child(?:Entry|Iterator)|LocationInPlane|Name(?:InPlane)?|P(?:a(?:rent(?:Entry|Iterator)|th)|roperty)|RegistryEntryID)|I(?:DMatching|nPlane)|Se(?:archCFProperty|tCFPropert(?:ies|y)))|GetRootEntry|IteratorE(?:nterEntry|xitEntry))|S(?:ervice(?:A(?:dd(?:InterestNotification|MatchingNotification|Notification)|uthorize)|Close|Get(?:BusyState|MatchingService(?:s)?)|Match(?:PropertyTable|ing)|NameMatching|O(?:FPathToBSDName|pen(?:AsFileDescriptor)?)|RequestProbe|WaitQuiet)|urface(?:AlignProperty|C(?:opy(?:AllValues|Value)|reate(?:MachPort|XPCObject)?)|DecrementUseCount|Get(?:AllocSize|B(?:aseAddress(?:OfPlane)?|ytesPer(?:Element(?:OfPlane)?|Row(?:OfPlane)?))|Element(?:Height(?:OfPlane)?|Width(?:OfPlane)?)|Height(?:OfPlane)?|ID|P(?:ixelFormat|laneCount|roperty(?:Alignment|Maximum))|Seed|TypeID|UseCount|Width(?:OfPlane)?)|I(?:ncrementUseCount|sInUse)|Lo(?:ck|okup(?:From(?:MachPort|XPCObject))?)|Remove(?:AllValues|Value)|SetValue(?:s)?|Unlock)))|conRef(?:ContainsCGPoint|IntersectsCGRect|To(?:HIShape|IconFamily))|n(?:stallEvent(?:Handler|LoopTimer)|voke(?:AE(?:Coerce(?:DescUPP|PtrUPP)|DisposeExternalUPP|EventHandlerUPP|FilterUPP|IdleUPP)|C(?:a(?:librate(?:EventUPP|UPP)|nCalibrateUPP)|ontrol(?:ActionUPP|EditTextValidationUPP|KeyFilterUPP|UserPane(?:ActivateUPP|DrawUPP|FocusUPP|HitTestUPP|IdleUPP|KeyDownUPP|TrackingUPP)))|D(?:ataBrowser(?:A(?:cceptDragUPP|ddDragItemUPP)|DrawItemUPP|EditItemUPP|GetContextualMenuUPP|HitTestUPP|Item(?:AcceptDragUPP|CompareUPP|D(?:ataUPP|ragRgnUPP)|HelpContentUPP|Notification(?:UPP|WithItemUPP)|ReceiveDragUPP|UPP)|PostProcessDragUPP|ReceiveDragUPP|SelectContextualMenuUPP|TrackingUPP)|ragInputUPP)|E(?:ditUnicodePostUpdateUPP|vent(?:ComparatorUPP|HandlerUPP|Loop(?:IdleTimerUPP|TimerUPP)))|HM(?:ControlContentUPP|Menu(?:ItemContentUPP|TitleContentUPP)|WindowContentUPP)|I(?:con(?:ActionUPP|GetterUPP)|ndexToUCStringUPP)|ModalFilter(?:UPP|YDUPP)|N(?:ColorChangedUPP|MUPP)|OS(?:A(?:ActiveUPP|CreateAppleEventUPP|SendUPP)|L(?:A(?:ccessorUPP|djustMarksUPP)|Co(?:mpareUPP|untUPP)|DisposeTokenUPP|Get(?:ErrDescUPP|MarkTokenUPP)|MarkUPP))|TXN(?:ActionNameMapperUPP|ContextualMenuSetupUPP|FindUPP|ScrollInfoUPP)|U(?:nicodeToTextFallbackUPP|serItemUPP)))|s(?:EventInQueue|IconRefMaskEmpty|SecureEventInputEnabled|UserCancelEventRef))|JS(?:C(?:heckScriptSyntax|lass(?:Create|Re(?:lease|tain))|ontextG(?:etG(?:lobal(?:Context|Object)|roup)|roup(?:Create|Re(?:lease|tain))))|EvaluateScript|G(?:arbageCollect|lobalContext(?:Create(?:InGroup)?|Re(?:lease|tain)))|Object(?:C(?:allAs(?:Constructor|Function)|opyPropertyNames)|DeleteProperty|GetPr(?:ivate|o(?:perty(?:AtIndex)?|totype))|HasProperty|Is(?:Constructor|Function)|Make(?:Array|Constructor|Date|Error|Function(?:WithCallback)?|RegExp)?|SetPr(?:ivate|o(?:perty(?:AtIndex)?|totype)))|PropertyNameA(?:ccumulatorAddName|rray(?:Get(?:Count|NameAtIndex)|Re(?:lease|tain)))|String(?:C(?:opyCFString|reateWith(?:C(?:FString|haracters)|UTF8CString))|Get(?:CharactersPtr|Length|MaximumUTF8CStringSize|UTF8CString)|IsEqual(?:ToUTF8CString)?|Re(?:lease|tain))|Value(?:CreateJSONString|GetType|Is(?:Boolean|Equal|InstanceOfConstructor|Nu(?:ll|mber)|Object(?:OfClass)?|Stri(?:ctEqual|ng)|Undefined)|Make(?:Boolean|FromJSONString|Nu(?:ll|mber)|String|Undefined)|Protect|To(?:Boolean|Number|Object|StringCopy)|Unprotect))|KBGetLayoutType|L(?:MGetK(?:bd(?:Last|Type)|ey(?:RepThresh|Thresh))|S(?:C(?:anURLAcceptURL|opy(?:A(?:llRoleHandlersForContentType|pplicationURLsForURL)|DefaultRoleHandlerForContentType))|Open(?:CFURLRef|FromURLSpec)|RegisterURL|SetDefault(?:HandlerForURLScheme|RoleHandlerForContentType))|o(?:cale(?:Operation(?:CountNames|Get(?:IndName|Name))|Ref(?:FromL(?:angOrRegionCode|ocaleString)|GetPartString)|StringToLangAndRegionCodes)|ngDoubleTo(?:SInt64|UInt64)))|M(?:D(?:CopyLabel(?:Kinds|WithUUID|s(?:MatchingExpression|WithKind))|Item(?:C(?:opy(?:Attribute(?:List|Names|s)?|Labels)|reate(?:WithURL)?)|GetTypeID|RemoveLabel|SetLabel|sCreateWithURLs)|Label(?:C(?:opyAttribute(?:Name)?|reate)|Delete|GetTypeID|SetAttributes)|Query(?:C(?:opy(?:QueryString|SortingAttributes|Value(?:ListAttributes|sOfAttribute))|reate(?:ForItems|Subset)?)|DisableUpdates|E(?:nableUpdates|xecute)|Get(?:AttributeValueOfResultAtIndex|BatchingParameters|CountOfResultsWithAttributeValue|IndexOfResult|Result(?:AtIndex|Count)|SortOptionFlagsForAttribute|TypeID)|IsGatheringComplete|S(?:et(?:BatchingParameters|Create(?:ResultFunction|ValueFunction)|DispatchQueue|MaxCount|S(?:earchScope|ort(?:Comparator(?:Block)?|O(?:ptionFlagsForAttribute|rder))))|top))|SchemaCopy(?:A(?:llAttributes|ttributesForContentType)|Display(?:DescriptionForAttribute|NameForAttribute)|MetaAttributesForAttribute))|IDI(?:Client(?:Create|Dispose)|De(?:stinationCreate|viceGet(?:Entity|NumberOfEntities))|En(?:dpoint(?:Dispose|GetEntity)|tityGet(?:De(?:stination|vice)|NumberOf(?:Destinations|Sources)|Source))|FlushOutput|Get(?:De(?:stination|vice)|ExternalDevice|NumberOf(?:De(?:stinations|vices)|ExternalDevices|Sources)|Source)|InputPortCreate|O(?:bject(?:FindByUniqueID|Get(?:D(?:ataProperty|ictionaryProperty)|IntegerProperty|Properties|StringProperty)|RemoveProperty|Set(?:D(?:ataProperty|ictionaryProperty)|IntegerProperty|StringProperty))|utputPortCreate)|P(?:acket(?:List(?:Add|Init)|Next)|ort(?:ConnectSource|Dis(?:connectSource|pose)))|Re(?:ceived|start)|S(?:end(?:Sysex)?|ourceCreate))|akeVoiceSpec|usic(?:Device(?:MIDIEvent|S(?:t(?:artNote|opNote)|ysEx))|EventIterator(?:DeleteEvent|GetEventInfo|Has(?:CurrentEvent|NextEvent|PreviousEvent)|NextEvent|PreviousEvent|Se(?:ek|tEvent(?:Info|Time)))|Player(?:Get(?:BeatsForHostTime|HostTimeForBeats|PlayRateScalar|Sequence|Time)|IsPlaying|Preroll|S(?:et(?:PlayRateScalar|Sequence|Time)|t(?:art|op)))|Sequence(?:B(?:arBeatTimeToBeats|eatsToBarBeatTime)|DisposeTrack|File(?:Create(?:Data)?|Load(?:Data)?)|Get(?:AUGraph|BeatsForSeconds|In(?:dTrack|foDictionary)|S(?:MPTEResolution|e(?:condsForBeats|quenceType))|T(?:empoTrack|rack(?:Count|Index)))|NewTrack|Reverse|Set(?:AUGraph|MIDIEndpoint|S(?:MPTEResolution|equenceType)|UserCallback))|Track(?:C(?:lear|opyInsert|ut)|Get(?:Dest(?:MIDIEndpoint|Node)|Property|Sequence)|M(?:erge|oveEvents)|New(?:AUPresetEvent|Extended(?:NoteEvent|TempoEvent)|M(?:IDI(?:ChannelEvent|NoteEvent|RawDataEvent)|etaEvent)|ParameterEvent|UserEvent)|Set(?:Dest(?:MIDIEndpoint|Node)|Property))))|N(?:PickColor|X(?:Convert(?:Host(?:DoubleToSwapped|FloatToSwapped)|Swapped(?:DoubleToHost|FloatToHost))|HostByteOrder|Swap(?:Big(?:DoubleToHost|FloatToHost|IntToHost|Long(?:LongToHost|ToHost)|ShortToHost)|Double|Float|Host(?:DoubleTo(?:Big|Little)|FloatTo(?:Big|Little)|IntTo(?:Big|Little)|Long(?:LongTo(?:Big|Little)|To(?:Big|Little))|ShortTo(?:Big|Little))|Int|L(?:ittle(?:DoubleToHost|FloatToHost|IntToHost|Long(?:LongToHost|ToHost)|ShortToHost)|ong(?:Long)?)|Short))|e(?:arestMacTextEncodings|w(?:AE(?:Coerce(?:DescUPP|PtrUPP)|DisposeExternalUPP|EventHandlerUPP|FilterUPP|IdleUPP)|C(?:a(?:librate(?:EventUPP|UPP)|nCalibrateUPP)|ontrol(?:ActionUPP|EditTextValidationUPP|KeyFilterUPP|UserPane(?:ActivateUPP|DrawUPP|FocusUPP|HitTestUPP|IdleUPP|KeyDownUPP|TrackingUPP)))|D(?:ataBrowser(?:A(?:cceptDragUPP|ddDragItemUPP)|DrawItemUPP|EditItemUPP|GetContextualMenuUPP|HitTestUPP|Item(?:AcceptDragUPP|CompareUPP|D(?:ataUPP|ragRgnUPP)|HelpContentUPP|Notification(?:UPP|WithItemUPP)|ReceiveDragUPP|UPP)|PostProcessDragUPP|ReceiveDragUPP|SelectContextualMenuUPP|TrackingUPP)|ragInputUPP)|E(?:ditUnicodePostUpdateUPP|vent(?:ComparatorUPP|HandlerUPP|Loop(?:IdleTimerUPP|TimerUPP)))|HM(?:ControlContentUPP|Menu(?:ItemContentUPP|TitleContentUPP)|WindowContentUPP)|I(?:con(?:ActionUPP|GetterUPP)|ndexToUCStringUPP)|M(?:odalFilter(?:UPP|YDUPP)|usic(?:EventIterator|Player|Sequence))|N(?:ColorChangedUPP|MUPP)|OS(?:A(?:ActiveUPP|CreateAppleEventUPP|SendUPP)|L(?:A(?:ccessorUPP|djustMarksUPP)|Co(?:mpareUPP|untUPP)|DisposeTokenUPP|Get(?:ErrDescUPP|MarkTokenUPP)|MarkUPP))|SpeechChannel|TXN(?:ActionNameMapperUPP|ContextualMenuSetupUPP|FindUPP|ScrollInfoUPP)|U(?:nicodeToTextFallbackUPP|serItemUPP))|xtAudioFileRegion)|um(?:AudioFileMarkersToNumBytes|BytesToNumAudioFileMarkers))|OS(?:A(?:A(?:ddStorageType|vailableDialect(?:CodeList|s))|Co(?:erce(?:FromDesc|ToDesc)|mpile(?:Execute)?|py(?:DisplayString|ID|S(?:cript(?:ingDefinition(?:FromURL)?)?|ourceString)))|D(?:isp(?:lay|ose)|o(?:Event|Script(?:File)?))|Execute(?:Event)?|Ge(?:nericToRealID|t(?:ActiveProc|C(?:reateProc|urrentDialect)|D(?:efaultScriptingComponent|ialectInfo)|Handler(?:Names)?|Property(?:Names)?|ResumeDispatchProc|S(?:cript(?:DataFromURL|Info|ingComponent(?:FromStored)?)|endProc|ource|torageType|ysTerminology)))|Load(?:Execute(?:File)?|File|ScriptData)?|MakeContext|Re(?:alToGenericID|moveStorageType)|S(?:cript(?:Error|ingComponentName)|et(?:ActiveProc|C(?:reateProc|urrentDialect)|Default(?:ScriptingComponent|Target)|Handler|Property|ResumeDispatchProc|S(?:criptInfo|endProc))|t(?:artRecording|o(?:pRecording|re(?:File)?)))|tomic(?:Dequeue|Enqueue|Fifo(?:Dequeue|Enqueue)))|GetNotificationFromMessage)|P(?:M(?:C(?:GImageCreateWithEPSDataProvider|opy(?:AvailablePPDs|LocalizedPPD|P(?:PDData|ageFormat|rintSettings))|reate(?:GenericPrinter|P(?:ageFormat(?:WithPMPaper)?|rintSettings)|Session))|Get(?:AdjustedPa(?:geRect|perRect)|Co(?:llate|pies)|Duplex|FirstPage|LastPage|Orientation|Page(?:Format(?:ExtendedData|Paper)|Range)|Scale|UnadjustedPa(?:geRect|perRect))|P(?:a(?:geFormat(?:Create(?:DataRepresentation|WithDataRepresentation)|GetPrinterID)|per(?:Create(?:Custom|LocalizedName)|Get(?:Height|ID|Margins|P(?:PDPaperName|rinterID)|Width)|IsCustom))|r(?:eset(?:C(?:opyName|reatePrintSettings)|GetAttributes)|int(?:Settings(?:C(?:opy(?:AsDictionary|Keys)|reate(?:DataRepresentation|WithDataRepresentation))|Get(?:JobName|Value)|Set(?:JobName|Value)|ToOptions(?:WithPrinterAndPageFormat)?)|er(?:C(?:opy(?:De(?:scriptionURL|viceURI)|HostName|Presets|State)|reateFromPrinterID)|Get(?:CommInfo|Driver(?:Creator|ReleaseInfo)|I(?:D|ndexedPrinterResolution)|L(?:anguageInfo|ocation)|M(?:akeAndModelName|imeTypes)|Name|OutputResolution|P(?:aperList|rinterResolutionCount)|State)|Is(?:Default|Favorite|PostScript(?:Capable|Printer)|Remote)|PrintWith(?:File|Provider)|Se(?:ndCommand|t(?:Default|OutputResolution))|WritePostScriptToURL))))|Re(?:lease|tain)|Se(?:rver(?:CreatePrinterList|LaunchPrinterBrowser)|ssion(?:Begin(?:CGDocumentNoDialog|PageNoDialog)|C(?:opy(?:Destination(?:Format|Location)|OutputFormatList)|reateP(?:ageFormatList|rinterList))|DefaultP(?:ageFormat|rintSettings)|E(?:nd(?:DocumentNoDialog|PageNoDialog)|rror)|Get(?:C(?:GGraphicsContext|urrentPrinter)|D(?:ataFromSession|estinationType))|Set(?:CurrentPMPrinter|D(?:ataInSession|estination)|Error)|ValidateP(?:ageFormat|rintSettings))|t(?:Co(?:llate|pies)|Duplex|FirstPage|LastPage|Orientation|Page(?:FormatExtendedData|Range)|Scale))|Workflow(?:CopyItems|SubmitPDFWith(?:Options|Settings)))|a(?:steboard(?:C(?:lear|opy(?:ItemFlavor(?:Data|s)|Name|PasteLocation)|reate)|Get(?:Item(?:Count|FlavorFlags|Identifier)|TypeID)|PutItemFlavor|ResolvePromises|S(?:etP(?:asteLocation|romiseKeeper)|ynchronize))|useSpeechAt)|lotIconRefInContext|o(?:pSymbolicHotKeyMode|stEventToQueue)|rocessHICommand|ushSymbolicHotKeyMode)|Q(?:LThumbnailImageCreate|u(?:eryUnicodeMappings|itEventLoop))|R(?:e(?:ceiveNextEvent|gisterEventHotKey|leaseEvent|moveEvent(?:FromQueue|Handler|LoopTimer|Parameter|TypesFromHandler)|s(?:et(?:TextToUnicodeInfo|UnicodeToText(?:Info|RunInfo))|olveDefaultTextEncoding)|tainEvent|vertTextEncodingToScriptInfo)|unCurrentEventLoop)|S(?:32Set|64(?:A(?:dd|nd)|Bitwise(?:And|Eor|Not|Or)|Div(?:ide)?|Eor|M(?:ax|in|od|ultiply)|N(?:egate|ot)|Or|S(?:et(?:U)?|hift(?:Left|Right)|ubtract))|C(?:Bond(?:Interface(?:C(?:opy(?:A(?:ll|vailableMemberInterfaces)|Status)|reate)|Get(?:MemberInterfaces|Options)|Remove|Set(?:LocalizedDisplayName|MemberInterfaces|Options))|StatusGet(?:InterfaceStatus|MemberInterfaces|TypeID))|CopyLastError|DynamicStore(?:Add(?:TemporaryValue|Value)|C(?:opy(?:Co(?:mputerName|nsoleUser)|KeyList|Loca(?:lHostName|tion)|Multiple|NotifiedKeys|Proxies|Value)|reate(?:RunLoopSource|WithOptions)?)|GetTypeID|KeyCreate(?:Co(?:mputerName|nsoleUser)|HostNames|Location|Network(?:GlobalEntity|Interface(?:Entity)?|ServiceEntity)|Proxies)?|NotifyValue|RemoveValue|Set(?:DispatchQueue|Multiple|NotificationKeys|Value))|Error(?:String)?|Network(?:Connection(?:C(?:opy(?:ExtendedStatus|S(?:erviceID|tatistics)|User(?:Options|Preferences))|reateWithServiceID)|Get(?:Status|TypeID)|S(?:cheduleWithRunLoop|etDispatchQueue|t(?:art|op))|UnscheduleFromRunLoop)|Interface(?:C(?:opy(?:All|M(?:TU|edia(?:Options|SubType(?:Options|s))))|reateWithInterface)|ForceConfigurationRefresh|Get(?:BSDName|Configuration|ExtendedConfiguration|HardwareAddressString|Interface(?:Type)?|LocalizedDisplayName|Supported(?:InterfaceTypes|ProtocolTypes)|TypeID)|Set(?:Configuration|ExtendedConfiguration|M(?:TU|ediaOptions)))|Protocol(?:Get(?:Configuration|Enabled|ProtocolType|TypeID)|Set(?:Configuration|Enabled))|Reachability(?:CreateWith(?:Address(?:Pair)?|Name)|Get(?:Flags|TypeID)|S(?:cheduleWithRunLoop|et(?:Callback|DispatchQueue))|UnscheduleFromRunLoop)|Se(?:rvice(?:AddProtocolType|C(?:opy(?:All|Protocol(?:s)?)?|reate)|EstablishDefaultConfiguration|Get(?:Enabled|Interface|Name|ServiceID|TypeID)|Remove(?:ProtocolType)?|Set(?:Enabled|Name))|t(?:AddService|C(?:o(?:ntainsInterface|py(?:All|Current|Services)?)|reate)|Get(?:Name|Se(?:rviceOrder|tID)|TypeID)|Remove(?:Service)?|Set(?:Current|Name|ServiceOrder))))|Preferences(?:A(?:ddValue|pplyChanges)|C(?:o(?:mmitChanges|pyKeyList)|reate(?:WithAuthorization)?)|Get(?:Signature|TypeID|Value)|Lock|Path(?:CreateUniqueChild|Get(?:Link|Value)|RemoveValue|Set(?:Link|Value))|RemoveValue|S(?:cheduleWithRunLoop|et(?:C(?:allback|omputerName)|DispatchQueue|LocalHostName|Value)|ynchronize)|Un(?:lock|scheduleFromRunLoop))|VLANInterface(?:C(?:opyA(?:ll|vailablePhysicalInterfaces)|reate)|Get(?:Options|PhysicalInterface|Tag)|Remove|Set(?:LocalizedDisplayName|Options|PhysicalInterfaceAndTag)))|Int64To(?:LongDouble|UInt64|Wide)|K(?:Document(?:C(?:opyURL|reate(?:WithURL)?)|Get(?:Name|Parent|SchemeName|TypeID))|Index(?:AddDocument(?:WithText)?|C(?:lose|o(?:mpact|py(?:Document(?:ForDocumentID|IDArrayForTermID|Properties|RefsForDocumentIDs|URLsForDocumentIDs)|InfoForDocumentIDs|Term(?:IDArrayForDocumentID|StringForTermID)))|reateWith(?:MutableData|URL))|DocumentIterator(?:C(?:opyNext|reate)|GetTypeID)|Flush|Get(?:AnalysisProperties|Document(?:Count|ID|State|Term(?:Count|Frequency))|IndexType|Maximum(?:BytesBeforeFlush|DocumentID|TermID)|T(?:erm(?:DocumentCount|IDForTermString)|ypeID))|MoveDocument|OpenWith(?:Data|MutableData|URL)|Re(?:moveDocument|nameDocument)|Set(?:DocumentProperties|MaximumBytesBeforeFlush))|LoadDefaultExtractorPlugIns|S(?:earch(?:C(?:ancel|reate)|FindMatches|GetTypeID)|ummary(?:C(?:opy(?:Paragraph(?:AtIndex|SummaryString)|Sentence(?:AtIndex|SummaryString))|reateWithString)|Get(?:Paragraph(?:Count|SummaryInfo)|Sentence(?:Count|SummaryInfo)|TypeID))))|e(?:c(?:A(?:CL(?:C(?:opy(?:Authorizations|Contents)|reateWithSimpleContents)|GetTypeID|Remove|SetContents|UpdateAuthorizations)|ccess(?:C(?:opy(?:ACLList|MatchingACLList|OwnerAndACL)|reate(?:WithOwnerAndACL)?)|GetTypeID)|ddSharedWebCredential)|C(?:ertificate(?:AddToKeychain|C(?:opy(?:CommonName|Data|EmailAddresses|LongDescription|Preferred|S(?:hortDescription|ubjectSummary)|Values)|reateWithData)|GetTypeID|SetPreferred)|o(?:de(?:C(?:heckValidity(?:WithErrors)?|opy(?:DesignatedRequirement|GuestWithAttributes|Host|Path|S(?:elf|igningInformation|taticCode)))|GetTypeID|MapMemory)|pyErrorMessageString)|reateSharedWebCredentialPassword)|D(?:ec(?:odeTransformCreate|ryptTransform(?:Create|GetTypeID))|igestTransform(?:Create|GetTypeID))|Enc(?:odeTransformCreate|ryptTransform(?:Create|GetTypeID))|GroupTransformGetTypeID|I(?:dentity(?:C(?:opy(?:Certificate|Pr(?:eferred|ivateKey)|SystemIdentity)|reateWithCertificate)|GetTypeID|Set(?:Preferred|SystemIdentity))|tem(?:Add|CopyMatching|Delete|Export|Import|Update))|Key(?:CreateFromData|DeriveFromPassword|Ge(?:nerate(?:Pair(?:Async)?|Symmetric)|t(?:BlockSize|TypeID))|UnwrapSymmetric|WrapSymmetric|chain(?:A(?:dd(?:Callback|GenericPassword|InternetPassword)|ttributeInfoForItemID)|C(?:opy(?:D(?:efault|omain(?:Default|SearchList))|Se(?:archList|ttings))|reate)|Delete|F(?:ind(?:GenericPassword|InternetPassword)|reeAttributeInfo)|Get(?:P(?:ath|referenceDomain)|Status|TypeID|UserInteractionAllowed|Version)|Item(?:C(?:opy(?:A(?:ccess|ttributesAndData)|Content|FromPersistentReference|Keychain)|reate(?:Copy|FromContent|PersistentReference))|Delete|Free(?:AttributesAndData|Content)|GetTypeID|Modify(?:AttributesAndData|Content)|SetAccess)|Lock(?:All)?|Open|RemoveCallback|Set(?:D(?:efault|omain(?:Default|SearchList))|PreferenceDomain|Se(?:archList|ttings)|UserInteractionAllowed)|Unlock))|P(?:KCS12Import|olicy(?:C(?:opyProperties|reate(?:BasicX509|SSL))|GetTypeID))|R(?:andomCopyBytes|equ(?:estSharedWebCredential|irement(?:C(?:opy(?:Data|String)|reateWith(?:Data|String(?:AndErrors)?))|GetTypeID)))|S(?:ignTransformCreate|taticCode(?:C(?:heckValidity(?:WithErrors)?|reateWithPath(?:AndAttributes)?)|GetTypeID))|T(?:ask(?:C(?:opy(?:SigningIdentifier|Value(?:ForEntitlement|sForEntitlements))|reate(?:FromSelf|WithAuditToken))|Get(?:CodeSignStatus|TypeID))|r(?:ansform(?:C(?:o(?:nnectTransforms|pyExternalRepresentation)|reate(?:FromExternalRepresentation|GroupTransform|ReadTransformWithReadStream)?|ustom(?:GetAttribute|SetAttribute))|Execute(?:Async)?|FindByName|Get(?:Attribute|TypeID)|NoData|PushbackAttribute|Register|Set(?:Attribute(?:Action)?|DataAction|TransformAction))|ust(?:C(?:opy(?:AnchorCertificates|CustomAnchorCertificates|P(?:olicies|roperties|ublicKey))|reateWithCertificates)|Get(?:Certificate(?:AtIndex|Count)|T(?:rustResult|ypeID)|VerifyTime)|Set(?:AnchorCertificates(?:Only)?|Options|Policies|VerifyDate|tings(?:C(?:opy(?:Certificates|ModificationDate|TrustSettings)|reateExternalRepresentation)|ImportExternalRepresentation|RemoveTrustSettings|SetTrustSettings))|edApplicationGetTypeID)))|VerifyTransformCreate)|ndEventToEventTarget(?:WithOptions)?|ssion(?:Create|GetInfo)|t(?:AudioUnitParameterDisplayType|Event(?:LoopTimerNextFireTime|Parameter|Time)|F(?:allbackUnicodeToText(?:Run)?|ontInfoForSelection)|IconFamilyData|S(?:peech(?:P(?:itch|roperty)|Rate)|ystemUIMode)))|pe(?:akCFString|ech(?:Busy(?:SystemWide)?|ManagerVersion|Synthesis(?:RegisterModuleURL|UnregisterModuleURL)))|topSpeech(?:At)?)|T(?:EC(?:C(?:lear(?:ConverterContextInfo|SnifferContextInfo)|o(?:nvertText(?:ToMultipleEncodings)?|pyTextEncodingInternetNameAndMIB|unt(?:Available(?:Sniffers|TextEncodings)|D(?:estinationTextEncodings|irectTextEncodingConversions)|MailTextEncodings|SubTextEncodings|WebTextEncodings))|reate(?:Converter(?:FromPath)?|OneToManyConverter|Sniffer))|Dispose(?:Converter|Sniffer)|Flush(?:MultipleEncodings|Text)|Get(?:Available(?:Sniffers|TextEncodings)|D(?:estinationTextEncodings|irectTextEncodingConversions)|EncodingList|Info|MailTextEncodings|SubTextEncodings|TextEncoding(?:FromInternetName(?:OrMIB)?|InternetName)|WebTextEncodings)|S(?:etBasicOptions|niffTextEncoding))|IS(?:C(?:opy(?:Current(?:ASCIICapableKeyboard(?:InputSource|LayoutInputSource)|Keyboard(?:InputSource|LayoutInputSource))|Input(?:MethodKeyboardLayoutOverride|SourceForLanguage))|reate(?:ASCIICapableInputSourceList|InputSourceList))|D(?:eselectInputSource|isableInputSource)|EnableInputSource|GetInputSourceProperty|InputSourceGetTypeID|RegisterInputSource|Se(?:lectInputSource|tInputMethodKeyboardLayoutOverride))|SM(?:Get(?:ActiveDocument|DocumentProperty)|RemoveDocumentProperty|SetDocumentProperty)|r(?:ans(?:formProcessType|lation(?:C(?:opy(?:DestinationType|SourceType)|reate(?:WithSourceArray)?)|GetT(?:ranslationFlags|ypeID)|PerformFor(?:Data|File|URL)))|uncateFor(?:TextToUnicode|UnicodeToText)))|U(?:32SetU|64(?:A(?:dd|nd)|Bitwise(?:And|Eor|Not|Or)|Div(?:ide)?|Eor|M(?:ax|od|ultiply)|Not|Or|S(?:et(?:U)?|hift(?:Left|Right)|ubtract))|AZoom(?:ChangeFocus|Enabled)|C(?:C(?:o(?:mpare(?:CollationKeys|Text(?:Default|NoLocale)?)|nvert(?:CFAbsoluteTimeTo(?:LongDateTime|Seconds|UTCDateTime)|LongDateTimeToCFAbsoluteTime|SecondsToCFAbsoluteTime|UTCDateTimeToCFAbsoluteTime))|reateCollator)|DisposeCollator|Get(?:C(?:harProperty|ollationKey)|UnicodeScalarValueForSurrogatePair)|IsSurrogate(?:HighCharacter|LowCharacter)|KeyTranslate|TypeSelect(?:AddKeyToSelector|C(?:ompare|reateSelector)|F(?:indItem|lushSelectorData)|ReleaseSelector|W(?:alkList|ouldResetBuffer)))|Int64To(?:LongDouble|SInt64|UnsignedWide)|T(?:CreateStringForOSType|GetOSTypeFromString|Type(?:C(?:o(?:nformsTo|py(?:De(?:clar(?:ation|ingBundleURL)|scription)|PreferredTagWithClass))|reate(?:AllIdentifiersForTag|PreferredIdentifierForTag))|Equal))|n(?:registerEventHotKey|signedWideToUInt64)|pgradeScriptInfoToTextEncoding|seSpeechDictionary)|WideToSInt64|a(?:c(?:cept|l_(?:add_(?:flag_np|perm)|c(?:alc_mask|lear_(?:flags_np|perms)|opy_(?:e(?:ntry|xt(?:_native)?)|int(?:_native)?)|reate_entry(?:_np)?)|d(?:elete_(?:def_file|entry|flag_np|perm)|up)|fr(?:ee|om_text)|get_(?:entry|f(?:d(?:_np)?|ile|lag(?:_np|set_np))|link_np|perm(?:_np|set(?:_mask_np)?)|qualifier|tag_type)|init|maximal_permset_mask_np|s(?:et_(?:f(?:d(?:_np)?|ile|lagset_np)|link_np|permset(?:_mask_np)?|qualifier|tag_type)|ize)|to_text|valid(?:_(?:f(?:d_np|ile_np)|link_np))?)|t_(?:get_state|set_state))|udit(?:_session_(?:join|port|self)|ctl|on)?)|bind|c(?:a(?:bs(?:f|l)?|cos(?:f|h(?:f|l)?|l)?|lloc|rg(?:f|l)?|sin(?:f|h(?:f|l)?|l)?|tan(?:f|h(?:f|l)?|l)?)|cos(?:f|h(?:f|l)?|l)?|exp(?:f|l)?|imag(?:f|l)?|lo(?:ck_(?:get_res|s(?:et_(?:attributes|res|time)|leep(?:_trap)?))|g(?:f|l)?)|on(?:j(?:f|l)?|nect)|p(?:ow(?:f|l)?|roj(?:f|l)?)|real(?:f|l)?|s(?:in(?:f|h(?:f|l)?|l)?|qrt(?:f|l)?|sm(?:AlgToOid|OidToAlg|Perror))|t(?:an(?:f|h(?:f|l)?|l)?|ermid))|d(?:e(?:bug_control_port_for_pid|c2numl)|igittoint)|etap_trace_thread|f(?:e(?:clearexcept|get(?:e(?:nv|xceptflag)|round)|holdexcept|raiseexcept|set(?:e(?:nv|xceptflag)|round)|testexcept|updateenv)|ree)|g(?:et(?:au(?:dit_addr|id)|peername|sock(?:name|opt))|l(?:A(?:c(?:cum|tive(?:StencilFaceEXT|Texture(?:ARB)?))|lphaFunc|r(?:eTexturesResident|rayElement)|ttach(?:ObjectARB|Shader))|B(?:egin(?:ConditionalRenderNV|Query(?:ARB)?|TransformFeedbackEXT)?|i(?:nd(?:AttribLocation(?:ARB)?|Buffer(?:ARB|BaseEXT|OffsetEXT|RangeEXT)?|Fra(?:gDataLocationEXT|mebuffer(?:EXT)?)|ProgramARB|Renderbuffer(?:EXT)?|Texture|VertexArrayAPPLE)|tmap)|l(?:end(?:Color(?:EXT)?|Equation(?:EXT|Separate(?:ATI|EXT)?)?|Func(?:Separate(?:EXT)?)?)|itFramebuffer(?:EXT)?)|uffer(?:Data(?:ARB)?|ParameteriAPPLE|SubData(?:ARB)?))|C(?:allList(?:s)?|heckFramebufferStatus(?:EXT)?|l(?:ampColorARB|ear(?:Accum|Color(?:I(?:iEXT|uiEXT))?|Depth|Index|Stencil)?|i(?:ent(?:ActiveTexture(?:ARB)?|WaitSync)|pPlane))|o(?:lor(?:3(?:b(?:v)?|d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?|u(?:b(?:v)?|i(?:v)?|s(?:v)?))|4(?:b(?:v)?|d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?|u(?:b(?:v)?|i(?:v)?|s(?:v)?))|Ma(?:sk(?:IndexedEXT)?|terial)|Pointer|SubTable|Table(?:Parameter(?:fv|iv))?)|mp(?:ileShader(?:ARB)?|ressedTex(?:Image(?:1D(?:ARB)?|2D(?:ARB)?|3D(?:ARB)?)|SubImage(?:1D(?:ARB)?|2D(?:ARB)?|3D(?:ARB)?)))|nvolution(?:Filter(?:1D|2D)|Parameter(?:f(?:v)?|i(?:v)?))|py(?:Co(?:lor(?:SubTable|Table)|nvolutionFilter(?:1D|2D))|Pixels|Tex(?:Image(?:1D|2D)|SubImage(?:1D|2D|3D))))|reate(?:Program(?:ObjectARB)?|Shader(?:ObjectARB)?)|ullFace)|D(?:e(?:lete(?:Buffers(?:ARB)?|F(?:encesAPPLE|ramebuffers(?:EXT)?)|Lists|ObjectARB|Program(?:sARB)?|Queries(?:ARB)?|Renderbuffers(?:EXT)?|S(?:hader|ync)|Textures|VertexArraysAPPLE)|pth(?:BoundsEXT|Func|Mask|Range)|tach(?:ObjectARB|Shader))|isable(?:ClientState|IndexedEXT|VertexAttribA(?:PPLE|rray(?:ARB)?))?|raw(?:Arrays(?:InstancedARB)?|Buffer(?:s(?:ARB)?)?|Element(?:ArrayAPPLE|s(?:BaseVertex|Instanced(?:ARB|BaseVertex))?)|Pixels|RangeElement(?:ArrayAPPLE|s(?:BaseVertex|EXT)?)))|E(?:dgeFlag(?:Pointer|v)?|lementPointerAPPLE|n(?:able(?:ClientState|IndexedEXT|VertexAttribA(?:PPLE|rray(?:ARB)?))?|d(?:ConditionalRenderNV|List|Query(?:ARB)?|TransformFeedbackEXT)?)|val(?:Coord(?:1(?:d(?:v)?|f(?:v)?)|2(?:d(?:v)?|f(?:v)?))|Mesh(?:1|2)|Point(?:1|2)))|F(?:e(?:edbackBuffer|nceSync)|inish(?:FenceAPPLE|ObjectAPPLE|RenderAPPLE)?|lush(?:MappedBufferRangeAPPLE|RenderAPPLE|VertexArrayRangeAPPLE)?|og(?:Coord(?:Pointer(?:EXT)?|d(?:EXT|v(?:EXT)?)?|f(?:EXT|v(?:EXT)?)?)|f(?:v)?|i(?:v)?)|r(?:amebuffer(?:Renderbuffer(?:EXT)?|Texture(?:1D(?:EXT)?|2D(?:EXT)?|3D(?:EXT)?|EXT|FaceEXT|Layer(?:EXT)?))|ontFace|ustum))|Ge(?:n(?:Buffers(?:ARB)?|F(?:encesAPPLE|ramebuffers(?:EXT)?)|Lists|ProgramsARB|Queries(?:ARB)?|Renderbuffers(?:EXT)?|Textures|VertexArraysAPPLE|erateMipmap(?:EXT)?)|t(?:A(?:ctive(?:Attrib(?:ARB)?|Uniform(?:ARB)?)|tt(?:ached(?:ObjectsARB|Shaders)|ribLocation(?:ARB)?))|B(?:oolean(?:IndexedvEXT|v)|uffer(?:P(?:arameteriv(?:ARB)?|ointerv(?:ARB)?)|SubData(?:ARB)?))|C(?:lipPlane|o(?:lorTable(?:Parameter(?:fv|iv))?|mpressedTexImage(?:ARB)?|nvolution(?:Filter|Parameter(?:fv|iv))))|Doublev|Error|F(?:loatv|ra(?:gDataLocationEXT|mebufferAttachmentParameteriv(?:EXT)?))|H(?:andleARB|istogram(?:Parameter(?:fv|iv))?)|In(?:foLogARB|teger(?:64v|IndexedvEXT|v))|Light(?:fv|iv)|M(?:a(?:p(?:dv|fv|iv)|terial(?:fv|iv))|inmax(?:Parameter(?:fv|iv))?)|Object(?:LabelEXT|Parameter(?:fvARB|ivA(?:PPLE|RB)))|P(?:ixelMap(?:fv|u(?:iv|sv))|o(?:interv|lygonStipple)|rogram(?:EnvParameter(?:dvARB|fvARB)|InfoLog|LocalParameter(?:dvARB|fvARB)|StringARB|iv(?:ARB)?))|Query(?:Object(?:i(?:64vEXT|v(?:ARB)?)|ui(?:64vEXT|v(?:ARB)?))|iv(?:ARB)?)|RenderbufferParameteriv(?:EXT)?|S(?:eparableFilter|hader(?:InfoLog|Source(?:ARB)?|iv)|tring|ynciv)|T(?:ex(?:Env(?:fv|iv)|Gen(?:dv|fv|iv)|Image|LevelParameter(?:fv|iv)|Parameter(?:I(?:ivEXT|uivEXT)|PointervAPPLE|fv|iv))|ransformFeedbackVaryingEXT)|Uniform(?:BufferSizeEXT|Location(?:ARB)?|OffsetEXT|fv(?:ARB)?|iv(?:ARB)?|uivEXT)|VertexAttrib(?:I(?:ivEXT|uivEXT)|Pointerv(?:ARB)?|dv(?:ARB)?|fv(?:ARB)?|iv(?:ARB)?)))|Hi(?:nt|stogram)|I(?:n(?:dex(?:Mask|Pointer|d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?|ub(?:v)?)|itNames|sertEventMarkerEXT|terleavedArrays)|s(?:Buffer(?:ARB)?|Enabled(?:IndexedEXT)?|F(?:enceAPPLE|ramebuffer(?:EXT)?)|List|Program(?:ARB)?|Query(?:ARB)?|Renderbuffer(?:EXT)?|S(?:hader|ync)|Texture|VertexA(?:rrayAPPLE|ttribEnabledAPPLE)))|L(?:abelObjectEXT|i(?:ght(?:Model(?:f(?:v)?|i(?:v)?)|f(?:v)?|i(?:v)?)|n(?:e(?:Stipple|Width)|kProgram(?:ARB)?)|stBase)|o(?:ad(?:Identity|Matrix(?:d|f)|Name|TransposeMatrix(?:d(?:ARB)?|f(?:ARB)?))|gicOp))|M(?:a(?:p(?:1(?:d|f)|2(?:d|f)|Buffer(?:ARB)?|Grid(?:1(?:d|f)|2(?:d|f))|VertexAttrib(?:1(?:dAPPLE|fAPPLE)|2(?:dAPPLE|fAPPLE)))|t(?:erial(?:f(?:v)?|i(?:v)?)|rixMode))|inmax|ult(?:Matrix(?:d|f)|TransposeMatrix(?:d(?:ARB)?|f(?:ARB)?)|i(?:Draw(?:Arrays(?:EXT)?|Element(?:ArrayAPPLE|s(?:BaseVertex|EXT)?)|RangeElementArrayAPPLE)|TexCoord(?:1(?:d(?:ARB|v(?:ARB)?)?|f(?:ARB|v(?:ARB)?)?|i(?:ARB|v(?:ARB)?)?|s(?:ARB|v(?:ARB)?)?)|2(?:d(?:ARB|v(?:ARB)?)?|f(?:ARB|v(?:ARB)?)?|i(?:ARB|v(?:ARB)?)?|s(?:ARB|v(?:ARB)?)?)|3(?:d(?:ARB|v(?:ARB)?)?|f(?:ARB|v(?:ARB)?)?|i(?:ARB|v(?:ARB)?)?|s(?:ARB|v(?:ARB)?)?)|4(?:d(?:ARB|v(?:ARB)?)?|f(?:ARB|v(?:ARB)?)?|i(?:ARB|v(?:ARB)?)?|s(?:ARB|v(?:ARB)?)?)))))|N(?:ewList|ormal(?:3(?:b(?:v)?|d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|Pointer))|O(?:bject(?:PurgeableAPPLE|UnpurgeableAPPLE)|rtho)|P(?:assThrough|ixel(?:Map(?:fv|u(?:iv|sv))|Store(?:f|i)|Transfer(?:f|i)|Zoom)|o(?:int(?:Parameter(?:f(?:ARB|v(?:ARB)?)?|i(?:NV|v(?:NV)?)?)|Size(?:PointerAPPLE)?)|lygon(?:Mode|Offset|Stipple)|p(?:Attrib|ClientAttrib|GroupMarkerEXT|Matrix|Name))|r(?:ioritizeTextures|o(?:gram(?:EnvParameter(?:4(?:d(?:ARB|vARB)|f(?:ARB|vARB))|s4fvEXT)|LocalParameter(?:4(?:d(?:ARB|vARB)|f(?:ARB|vARB))|s4fvEXT)|ParameteriEXT|StringARB)|vokingVertex(?:EXT)?))|ush(?:Attrib|ClientAttrib|GroupMarkerEXT|Matrix|Name))|R(?:asterPos(?:2(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|3(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|4(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?))|e(?:ad(?:Buffer|Pixels)|ct(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|nder(?:Mode|bufferStorage(?:EXT|Multisample(?:EXT)?)?)|set(?:Histogram|Minmax))|otate(?:d|f))|S(?:ampleCoverage(?:ARB)?|c(?:ale(?:d|f)|issor)|e(?:condaryColor(?:3(?:b(?:EXT|v(?:EXT)?)?|d(?:EXT|v(?:EXT)?)?|f(?:EXT|v(?:EXT)?)?|i(?:EXT|v(?:EXT)?)?|s(?:EXT|v(?:EXT)?)?|u(?:b(?:EXT|v(?:EXT)?)?|i(?:EXT|v(?:EXT)?)?|s(?:EXT|v(?:EXT)?)?))|Pointer(?:EXT)?)|lectBuffer|parableFilter2D|tFenceAPPLE)|hade(?:Model|rSource(?:ARB)?)|tencil(?:Func(?:Separate(?:ATI)?)?|Mask(?:Separate)?|Op(?:Separate(?:ATI)?)?)|wapAPPLE)|T(?:e(?:st(?:FenceAPPLE|ObjectAPPLE)|x(?:Coord(?:1(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|2(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|3(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|4(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|Pointer)|Env(?:f(?:v)?|i(?:v)?)|Gen(?:d(?:v)?|f(?:v)?|i(?:v)?)|Image(?:1D|2D|3D)|Parameter(?:I(?:ivEXT|uivEXT)|f(?:v)?|i(?:v)?)|SubImage(?:1D|2D|3D)|ture(?:BarrierNV|RangeAPPLE)))|rans(?:formFeedbackVaryingsEXT|late(?:d|f)))|U(?:n(?:iform(?:1(?:f(?:ARB|v(?:ARB)?)?|i(?:ARB|v(?:ARB)?)?|ui(?:EXT|vEXT))|2(?:f(?:ARB|v(?:ARB)?)?|i(?:ARB|v(?:ARB)?)?|ui(?:EXT|vEXT))|3(?:f(?:ARB|v(?:ARB)?)?|i(?:ARB|v(?:ARB)?)?|ui(?:EXT|vEXT))|4(?:f(?:ARB|v(?:ARB)?)?|i(?:ARB|v(?:ARB)?)?|ui(?:EXT|vEXT))|BufferEXT|Matrix(?:2(?:fv(?:ARB)?|x(?:3fv|4fv))|3(?:fv(?:ARB)?|x(?:2fv|4fv))|4(?:fv(?:ARB)?|x(?:2fv|3fv))))|mapBuffer(?:ARB)?)|seProgram(?:ObjectARB)?)|V(?:alidateProgram(?:ARB)?|ertex(?:2(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|3(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|4(?:d(?:v)?|f(?:v)?|i(?:v)?|s(?:v)?)|A(?:rray(?:ParameteriAPPLE|RangeAPPLE)|ttrib(?:1(?:d(?:ARB|v(?:ARB)?)?|f(?:ARB|v(?:ARB)?)?|s(?:ARB|v(?:ARB)?)?)|2(?:d(?:ARB|v(?:ARB)?)?|f(?:ARB|v(?:ARB)?)?|s(?:ARB|v(?:ARB)?)?)|3(?:d(?:ARB|v(?:ARB)?)?|f(?:ARB|v(?:ARB)?)?|s(?:ARB|v(?:ARB)?)?)|4(?:N(?:bv(?:ARB)?|iv(?:ARB)?|sv(?:ARB)?|u(?:b(?:ARB|v(?:ARB)?)?|iv(?:ARB)?|sv(?:ARB)?))|bv(?:ARB)?|d(?:ARB|v(?:ARB)?)?|f(?:ARB|v(?:ARB)?)?|iv(?:ARB)?|s(?:ARB|v(?:ARB)?)?|u(?:bv(?:ARB)?|iv(?:ARB)?|sv(?:ARB)?))|DivisorARB|I(?:1(?:i(?:EXT|vEXT)|ui(?:EXT|vEXT))|2(?:i(?:EXT|vEXT)|ui(?:EXT|vEXT))|3(?:i(?:EXT|vEXT)|ui(?:EXT|vEXT))|4(?:bvEXT|i(?:EXT|vEXT)|svEXT|u(?:bvEXT|i(?:EXT|vEXT)|svEXT))|PointerEXT)|Pointer(?:ARB)?))|BlendARB|Point(?:SizefAPPLE|er))|iewport)|W(?:aitSync|eight(?:PointerARB|bvARB|dvARB|fvARB|ivARB|svARB|u(?:bvARB|ivARB|svARB))|indowPos(?:2(?:d(?:ARB|v(?:ARB)?)?|f(?:ARB|v(?:ARB)?)?|i(?:ARB|v(?:ARB)?)?|s(?:ARB|v(?:ARB)?)?)|3(?:d(?:ARB|v(?:ARB)?)?|f(?:ARB|v(?:ARB)?)?|i(?:ARB|v(?:ARB)?)?|s(?:ARB|v(?:ARB)?)?)))))|host_(?:c(?:heck_multiuser_mode|reate_mach_voucher(?:_trap)?)|default_memory_manager|get_(?:UNDServer|atm_diagnostic_flag|boot_info|clock_(?:control|service)|exception_ports|io_master|multiuser_config_flags|special_port)|info|kernel_version|lockgroup_info|p(?:age_size|r(?:iv_statistics|ocessor(?:_(?:info|set(?:_priv|s))|s)))|re(?:boot|gister_(?:mach_voucher_attr_manager|well_known_mach_voucher_attr_manager)|quest_notification)|s(?:e(?:curity_(?:create_task_token|set_task_token)|t_(?:UNDServer|atm_diagnostic_flag|exception_ports|multiuser_config_flags|special_port))|tatistics(?:64)?|wap_exception_ports)|virtual_physical_table_info)|i(?:max(?:abs|div)|s(?:a(?:l(?:num|pha)|scii)|blank|cntrl|digit|graph|hexnumber|ideogram|lower|number|p(?:honogram|rint|unct)|rune|sp(?:ace|ecial)|upper|xdigit))|k(?:ext_request|mod_(?:c(?:ontrol|reate)|destroy|get_info))|l(?:dtox80|isten|ock_(?:acquire|handoff(?:_accept)?|make_stable|release|set_(?:create|destroy)|try))|m(?:a(?:c(?:h_(?:error(?:_(?:string|type))?|generate_activity_id|host_self|m(?:ake_memory_entry(?:_64)?|emory_(?:info|object_memory_entry(?:_64)?)|sg(?:_(?:destroy|overwrite|receive|se(?:nd|rver(?:_(?:importance|once))?)))?)|port(?:_(?:allocate(?:_(?:full|name|qos))?|construct|d(?:e(?:allocate|str(?:oy|uct))|nrequest_info)|extract_(?:member|right)|g(?:et_(?:attributes|context|refs|s(?:et_status|rights))|uard(?:_with_flags)?)|insert_(?:member|right)|k(?:ernel_object|object(?:_description)?)|mo(?:d_refs|ve_member)|names|peek|re(?:name|quest_notification)|s(?:et_(?:attributes|context|mscount|seqno)|pace_(?:basic_info|info)|wap_guard)|type|unguard)|s_(?:lookup|register))|thread_self|v(?:m_(?:region_info(?:_64)?|wire)|oucher_(?:deallocate|extract_attr_recipe_trap))|zone_info(?:_for_zone)?)|x_(?:backing_store_(?:recovery|suspend)|swapo(?:ff|n)|triggers))|dvise|lloc|trix_(?:multiply|scale))|i(?:g_(?:allocate|dealloc(?:_reply_port|ate)|get_reply_port|put_reply_port|reply_setup|strncpy(?:_zerofill)?)|n(?:core|herit))|lock(?:all)?|map|protect|sync|un(?:lock(?:all)?|map))|num2decl|p(?:anic(?:_init)?|fctlinput|id_for_task|osix_m(?:advise|emalign)|rocessor_(?:assign|control|exit|get_assignment|info|s(?:et_(?:create|de(?:fault|stroy)|info|max_priority|policy_(?:control|disable|enable)|sta(?:ck_usage|tistics)|t(?:asks|hreads))|tart)))|re(?:alloc|cv(?:from|msg)?|lationl)|s(?:afe_gets|e(?:c_re(?:lease|tain)|maphore_(?:create|destroy|signal(?:_(?:all|thread))?|timedwait(?:_signal)?|wait(?:_signal)?)|nd(?:file|msg|to)?|t(?:au(?:dit_addr|id)|sockopt))|h(?:m_(?:open|unlink)|utdown)|imd_(?:a(?:bs|ct|dd|l(?:l|most_equal_elements(?:_relative)?)|n(?:gle|y)|xis)|b(?:ezier|itselect)|c(?:har(?:_sat)?|lamp|onjugate|ross)|d(?:eterminant|i(?:agonal_matrix|stance(?:_squared)?)|o(?:t|uble))|equal|f(?:ast_(?:distance|length|normalize|project|r(?:ecip|sqrt))|loat|ract)|i(?:mag|n(?:circle|sphere|t(?:_sat)?|verse))|l(?:ength(?:_squared)?|inear_combination|ong(?:_sat)?)|m(?:a(?:ke_(?:char(?:16(?:_undef)?|2(?:_undef)?|3(?:2(?:_undef)?|_undef)?|4(?:_undef)?|64(?:_undef)?|8(?:_undef)?)|double(?:2(?:_undef)?|3(?:_undef)?|4(?:_undef)?|8(?:_undef)?)|float(?:16(?:_undef)?|2(?:_undef)?|3(?:_undef)?|4(?:_undef)?|8(?:_undef)?)|int(?:16(?:_undef)?|2(?:_undef)?|3(?:_undef)?|4(?:_undef)?|8(?:_undef)?)|long(?:2(?:_undef)?|3(?:_undef)?|4(?:_undef)?|8(?:_undef)?)|short(?:16(?:_undef)?|2(?:_undef)?|3(?:2(?:_undef)?|_undef)?|4(?:_undef)?|8(?:_undef)?)|u(?:char(?:16(?:_undef)?|2(?:_undef)?|3(?:2(?:_undef)?|_undef)?|4(?:_undef)?|64(?:_undef)?|8(?:_undef)?)|int(?:16(?:_undef)?|2(?:_undef)?|3(?:_undef)?|4(?:_undef)?|8(?:_undef)?)|long(?:2(?:_undef)?|3(?:_undef)?|4(?:_undef)?|8(?:_undef)?)|short(?:16(?:_undef)?|2(?:_undef)?|3(?:2(?:_undef)?|_undef)?|4(?:_undef)?|8(?:_undef)?)))|trix(?:3x3|4x4|_from_rows)?|x)|i(?:n|x)|ul)|n(?:egate|orm(?:_(?:inf|one)|alize))|orient|pr(?:ecise_(?:distance|length|normalize|project|r(?:ecip|sqrt))|oject)|quaternion|r(?:e(?:al|cip|duce_(?:add|m(?:ax|in))|f(?:lect|ract))|sqrt)|s(?:elect|hort(?:_sat)?|ign|lerp(?:_longest)?|moothstep|pline|tep|ub)|transpose|u(?:char(?:_sat)?|int(?:_sat)?|long(?:_sat)?|short(?:_sat)?))|lot_name|ock(?:atmark|et(?:pair)?)|trto(?:imax|umax)|wtch(?:_pri)?)|t(?:ask_(?:assign(?:_default)?|create(?:_suid_cred)?|for_pid|ge(?:nerate_corpse|t_(?:assignment|dyld_image_infos|e(?:mulation_vector|xc(?:_guard_behavior|eption_ports))|mach_voucher|s(?:pecial_port|tate)))|in(?:fo|spect)|map_corpse_info(?:_64)?|name_for_pid|p(?:olicy(?:_(?:get|set))?|urgable_info)|re(?:gister_dyld_(?:get_process_state|image_infos|s(?:et_dyld_state|hared_cache_image_info))|sume(?:2)?)|s(?:ample|e(?:lf_trap|t_(?:e(?:mulation(?:_vector)?|xc(?:_guard_behavior|eption_ports))|info|mach_voucher|p(?:hys_footprint_limit|o(?:licy|rt_space))|ras_pc|s(?:pecial_port|tate)))|uspend(?:2)?|wap_(?:exception_ports|mach_voucher))|t(?:erminate|hreads)|unregister_dyld_image_infos|wire|zone_info)|hread_(?:a(?:bort(?:_safely)?|ssign(?:_default)?)|create(?:_running)?|depress_abort|get_(?:assignment|exception_ports|mach_voucher|s(?:pecial_port|tate))|info|policy(?:_(?:get|set))?|resume|s(?:ample|et_(?:exception_ports|mach_voucher|policy|s(?:pecial_port|tate))|uspend|w(?:ap_(?:exception_ports|mach_voucher)|itch))|terminate|wire)|o(?:ascii|lower|upper))|uuid_(?:c(?:lear|o(?:mpare|py))|generate(?:_(?:early_random|random|time))?|is_null|parse|unparse(?:_(?:lower|upper))?)|v(?:AEBuild(?:AppleEvent|Desc|Parameters)|alloc|ector(?:16|2|3(?:2)?|4|8)|m_(?:allocate(?:_cpm)?|behavior_set|copy|deallocate|inherit|m(?:a(?:chine_attribute|p(?:_(?:64|exec_lockdown|page_query)|ped_pages_info)?)|sync)|p(?:rotect|urgable_control)|re(?:ad(?:_(?:list|overwrite))?|gion(?:_(?:64|recurse(?:_64)?))?|map)|w(?:ire|rite))|oucher_mach_msg_(?:adopt|clear|revert|set))|wcsto(?:imax|umax)|x(?:80told|pc_(?:array_(?:app(?:end_value|ly)|create(?:_connection)?|dup_fd|get_(?:bool|count|d(?:at(?:a|e)|ouble)|int64|string|u(?:int64|uid)|value)|set_(?:bool|connection|d(?:at(?:a|e)|ouble)|fd|int64|string|u(?:int64|uid)|value))|bool_(?:create|get_value)|co(?:nnection_(?:c(?:ancel|reate(?:_(?:from_endpoint|mach_service))?)|get_(?:asid|context|e(?:gid|uid)|name|pid)|resume|s(?:e(?:nd_(?:barrier|message(?:_with_reply(?:_sync)?)?)|t_(?:context|event_handler|finalizer_f|target_queue))|uspend))|py(?:_description)?)|d(?:at(?:a_(?:create(?:_with_dispatch_data)?|get_(?:bytes(?:_ptr)?|length))|e_(?:create(?:_from_current)?|get_value))|ebugger_api_misuse_info|ictionary_(?:apply|create(?:_(?:connection|reply))?|dup_fd|get_(?:bool|count|d(?:at(?:a|e)|ouble)|int64|remote_connection|string|u(?:int64|uid)|value)|set_(?:bool|connection|d(?:at(?:a|e)|ouble)|fd|int64|string|u(?:int64|uid)|value))|ouble_(?:create|get_value))|e(?:ndpoint_create|qual)|fd_(?:create|dup)|get_type|hash|int64_(?:create|get_value)|main|null_create|re(?:lease|tain)|s(?:et_event_stream_handler|hmem_(?:create|map)|tring_(?:create(?:_with_format(?:_and_arguments)?)?|get_(?:length|string_ptr)))|transaction_(?:begin|end)|u(?:int64_(?:create|get_value)|uid_(?:create|get_bytes)))))\\b)" + }, { "captures": { "1": { @@ -376,7 +898,7 @@ "name": "support.function.cf.c" } }, - "match": "(\\s*)(\\bCF(?:A(?:bsoluteTimeGetCurrent|llocator(?:Allocate|Create|Deallocate|Get(?:Context|Default|PreferredSizeForSize|TypeID)|Reallocate|SetDefault)|rray(?:App(?:end(?:Array|Value)|lyFunction)|BSearchValues|C(?:ontainsValue|reate(?:Copy|Mutable(?:Copy)?)?)|ExchangeValuesAtIndices|Get(?:Count(?:OfValue)?|FirstIndexOfValue|LastIndexOfValue|TypeID|Value(?:AtIndex|s))|InsertValueAtIndex|Re(?:move(?:AllValues|ValueAtIndex)|placeValues)|S(?:etValueAtIndex|ortValues))|ttributedString(?:BeginEditing|Create(?:Copy|Mutable(?:Copy)?|WithSubstring)?|EndEditing|Get(?:Attribute(?:AndLongestEffectiveRange|s(?:AndLongestEffectiveRange)?)?|Length|MutableString|String|TypeID)|Re(?:moveAttribute|place(?:AttributedString|String))|SetAttribute(?:s)?))|B(?:ag(?:A(?:ddValue|pplyFunction)|C(?:ontainsValue|reate(?:Copy|Mutable(?:Copy)?)?)|Get(?:Count(?:OfValue)?|TypeID|Value(?:IfPresent|s)?)|Re(?:move(?:AllValues|Value)|placeValue)|SetValue)|i(?:naryHeap(?:A(?:ddValue|pplyFunction)|C(?:ontainsValue|reate(?:Copy)?)|Get(?:Count(?:OfValue)?|Minimum(?:IfPresent)?|TypeID|Values)|Remove(?:AllValues|MinimumValue))|tVector(?:C(?:ontainsBit|reate(?:Copy|Mutable(?:Copy)?)?)|FlipBit(?:AtIndex|s)|Get(?:Bit(?:AtIndex|s)|Count(?:OfBit)?|FirstIndexOfBit|LastIndexOfBit|TypeID)|Set(?:AllBits|Bit(?:AtIndex|s)|Count)))|ooleanGet(?:TypeID|Value)|undle(?:C(?:loseBundleResourceMap|opy(?:AuxiliaryExecutableURL|Bu(?:iltInPlugInsURL|ndle(?:Localizations|URL))|Executable(?:Architectures(?:ForURL)?|URL)|InfoDictionary(?:ForURL|InDirectory)|Localiz(?:ationsFor(?:Preferences|URL)|edString)|Pr(?:eferredLocalizationsFromArray|ivateFrameworksURL)|Resource(?:URL(?:ForLocalization|InDirectory|sOfType(?:ForLocalization|InDirectory)?)?|sDirectoryURL)|S(?:hared(?:FrameworksURL|SupportURL)|upportFilesDirectoryURL))|reate(?:BundlesFromDirectory)?)|Get(?:AllBundles|BundleWithIdentifier|D(?:ataPointer(?:ForName|sForNames)|evelopmentRegion)|FunctionPointer(?:ForName|sForNames)|I(?:dentifier|nfoDictionary)|LocalInfoDictionary|MainBundle|P(?:ackageInfo(?:InDirectory)?|lugIn)|TypeID|V(?:alueForInfoDictionaryKey|ersionNumber))|IsExecutableLoaded|LoadExecutable(?:AndReturnError)?|OpenBundleResource(?:Files|Map)|PreflightExecutable|UnloadExecutable)|yteOrderGetCurrent)|C(?:alendar(?:AddComponents|C(?:o(?:mposeAbsoluteTime|py(?:Current|Locale|TimeZone))|reateWithIdentifier)|DecomposeAbsoluteTime|Get(?:ComponentDifference|FirstWeekday|Identifier|M(?:aximumRangeOfUnit|inimum(?:DaysInFirstWeek|RangeOfUnit))|OrdinalityOfUnit|RangeOfUnit|T(?:imeRangeOfUnit|ypeID))|Set(?:FirstWeekday|Locale|MinimumDaysInFirstWeek|TimeZone))|haracterSet(?:AddCharactersIn(?:Range|String)|Create(?:BitmapRepresentation|Copy|InvertedSet|Mutable(?:Copy)?|With(?:BitmapRepresentation|CharactersIn(?:Range|String)))|Get(?:Predefined|TypeID)|HasMemberInPlane|I(?:n(?:tersect|vert)|s(?:CharacterMember|LongCharacterMember|SupersetOfSet))|RemoveCharactersIn(?:Range|String)|Union)|o(?:nvert(?:Double(?:HostToSwapped|SwappedToHost)|Float(?:32(?:HostToSwapped|SwappedToHost)|64(?:HostToSwapped|SwappedToHost)|HostToSwapped|SwappedToHost))|py(?:Description|HomeDirectoryURL|TypeIDDescription)))|D(?:at(?:a(?:AppendBytes|Create(?:Copy|Mutable(?:Copy)?|WithBytesNoCopy)?|DeleteBytes|Find|Get(?:Byte(?:Ptr|s)|Length|MutableBytePtr|TypeID)|IncreaseLength|ReplaceBytes|SetLength)|e(?:C(?:ompare|reate)|Formatter(?:C(?:opyProperty|reate(?:DateF(?:ormatFromTemplate|romString)|StringWith(?:AbsoluteTime|Date))?)|Get(?:AbsoluteTimeFromString|DateStyle|Format|Locale|T(?:imeStyle|ypeID))|Set(?:Format|Property))|Get(?:AbsoluteTime|T(?:imeIntervalSinceDate|ypeID))))|ictionary(?:A(?:ddValue|pplyFunction)|C(?:ontains(?:Key|Value)|reate(?:Copy|Mutable(?:Copy)?)?)|Get(?:Count(?:Of(?:Key|Value))?|KeysAndValues|TypeID|Value(?:IfPresent)?)|Re(?:move(?:AllValues|Value)|placeValue)|SetValue))|E(?:qual|rror(?:C(?:opy(?:Description|FailureReason|RecoverySuggestion|UserInfo)|reate(?:WithUserInfoKeysAndValues)?)|Get(?:Code|Domain|TypeID)))|File(?:Descriptor(?:Create(?:RunLoopSource)?|DisableCallBacks|EnableCallBacks|Get(?:Context|NativeDescriptor|TypeID)|I(?:nvalidate|sValid))|Security(?:C(?:opy(?:AccessControlList|GroupUUID|OwnerUUID)|reate(?:Copy)?)|Get(?:Group|Mode|Owner|TypeID)|Set(?:AccessControlList|Group(?:UUID)?|Mode|Owner(?:UUID)?)))|Get(?:Allocator|RetainCount|TypeID)|Hash|Locale(?:C(?:opy(?:AvailableLocaleIdentifiers|C(?:ommonISOCurrencyCodes|urrent)|DisplayNameForPropertyValue|ISO(?:C(?:ountryCodes|urrencyCodes)|LanguageCodes)|PreferredLanguages)|reate(?:C(?:anonicalL(?:anguageIdentifierFromString|ocaleIdentifierFromS(?:criptManagerCodes|tring))|o(?:mponentsFromLocaleIdentifier|py))|LocaleIdentifierFrom(?:Components|WindowsLocaleCode))?)|Get(?:Identifier|Language(?:CharacterDirection|LineDirection)|System|TypeID|Value|WindowsLocaleCodeFromLocaleIdentifier))|M(?:a(?:chPort(?:Create(?:RunLoopSource|WithPort)?|Get(?:Context|InvalidationCallBack|Port|TypeID)|I(?:nvalidate|sValid)|SetInvalidationCallBack)|keCollectable)|essagePort(?:Create(?:Local|R(?:emote|unLoopSource))|Get(?:Context|InvalidationCallBack|Name|TypeID)|I(?:nvalidate|s(?:Remote|Valid))|Se(?:ndRequest|t(?:DispatchQueue|InvalidationCallBack|Name))))|N(?:otificationCenter(?:AddObserver|Get(?:D(?:arwinNotifyCenter|istributedCenter)|LocalCenter|TypeID)|PostNotification(?:WithOptions)?|Remove(?:EveryObserver|Observer))|u(?:llGetTypeID|mber(?:C(?:ompare|reate)|Formatter(?:C(?:opyProperty|reate(?:NumberFromString|StringWith(?:Number|Value))?)|Get(?:DecimalInfoForCurrencyCode|Format|Locale|Style|TypeID|ValueFromString)|Set(?:Format|Property))|Get(?:ByteSize|Type(?:ID)?|Value)|IsFloatType)))|P(?:lugIn(?:AddInstanceForFactory|Create|FindFactoriesForPlugInType(?:InPlugIn)?|Get(?:Bundle|TypeID)|I(?:nstance(?:Create(?:WithInstanceDataSize)?|Get(?:FactoryName|In(?:stanceData|terfaceFunctionTable)|TypeID))|sLoadOnDemand)|Re(?:gister(?:FactoryFunction(?:ByName)?|PlugInType)|moveInstanceForFactory)|SetLoadOnDemand|Unregister(?:Factory|PlugInType))|r(?:eferences(?:A(?:ddSuitePreferencesToApp|pp(?:Synchronize|ValueIsForced))|Copy(?:AppValue|KeyList|Multiple|Value)|GetApp(?:BooleanValue|IntegerValue)|RemoveSuitePreferencesFromApp|S(?:et(?:AppValue|Multiple|Value)|ynchronize))|opertyList(?:Create(?:D(?:ata|eepCopy)|With(?:Data|Stream))|IsValid|Write)))|R(?:angeMake|e(?:adStream(?:C(?:lose|opy(?:Error|Property)|reateWith(?:BytesNoCopy|File))|Get(?:Buffer|Error|Status|TypeID)|HasBytesAvailable|Open|Read|S(?:cheduleWithRunLoop|et(?:Client|Property))|UnscheduleFromRunLoop)|lease|tain)|unLoop(?:Add(?:CommonMode|Observer|Source|Timer)|Co(?:ntains(?:Observer|Source|Timer)|py(?:AllModes|CurrentMode))|Get(?:Current|Main|NextTimerFireDate|TypeID)|IsWaiting|Observer(?:Create(?:WithHandler)?|DoesRepeat|Get(?:Activities|Context|Order|TypeID)|I(?:nvalidate|sValid))|PerformBlock|R(?:emove(?:Observer|Source|Timer)|un(?:InMode)?)|S(?:ource(?:Create|Get(?:Context|Order|TypeID)|I(?:nvalidate|sValid)|Signal)|top)|Timer(?:Create(?:WithHandler)?|DoesRepeat|Get(?:Context|Interval|NextFireDate|Order|TypeID)|I(?:nvalidate|sValid)|SetNextFireDate)|WakeUp))|S(?:et(?:A(?:ddValue|pplyFunction)|C(?:ontainsValue|reate(?:Copy|Mutable(?:Copy)?)?)|Get(?:Count(?:OfValue)?|TypeID|Value(?:IfPresent|s)?)|Re(?:move(?:AllValues|Value)|placeValue)|SetValue)|how(?:Str)?|ocket(?:C(?:o(?:nnectToAddress|py(?:Address|PeerAddress|Registered(?:SocketSignature|Value)))|reate(?:ConnectedToSocketSignature|RunLoopSource|With(?:Native|SocketSignature))?)|DisableCallBacks|EnableCallBacks|Get(?:Context|DefaultNameRegistryPortNumber|Native|SocketFlags|TypeID)|I(?:nvalidate|sValid)|Register(?:SocketSignature|Value)|Se(?:ndData|t(?:Address|DefaultNameRegistryPortNumber|SocketFlags))|Unregister)|tr(?:eamCreate(?:BoundPair|PairWith(?:PeerSocketSignature|Socket(?:ToHost)?))|ing(?:Append(?:C(?:String|haracters)|Format(?:AndArguments)?|PascalString)?|C(?:apitalize|o(?:mpare(?:WithOptions(?:AndLocale)?)?|nvert(?:EncodingTo(?:IANACharSetName|NSStringEncoding|WindowsCodepage)|IANACharSetNameToEncoding|NSStringEncodingToEncoding|WindowsCodepageToEncoding))|reate(?:Array(?:BySeparatingStrings|WithFindResults)|ByCombiningStrings|Copy|ExternalRepresentation|FromExternalRepresentation|Mutable(?:Copy|WithExternalCharactersNoCopy)?|With(?:Bytes(?:NoCopy)?|C(?:String(?:NoCopy)?|haracters(?:NoCopy)?)|F(?:ileSystemRepresentation|ormat(?:AndArguments)?)|PascalString(?:NoCopy)?|Substring)))|Delete|F(?:ind(?:AndReplace|CharacterFromSet|WithOptions(?:AndLocale)?)?|old)|Get(?:Bytes|C(?:String(?:Ptr)?|haracter(?:AtIndex|FromInlineBuffer|s(?:Ptr)?))|DoubleValue|F(?:astestEncoding|ileSystemRepresentation)|HyphenationLocationBeforeIndex|IntValue|L(?:ength|i(?:neBounds|stOfAvailableEncodings)|ongCharacterForSurrogatePair)|M(?:aximumSize(?:ForEncoding|OfFileSystemRepresentation)|ostCompatibleMacStringEncoding)|NameOfEncoding|Pa(?:ragraphBounds|scalString(?:Ptr)?)|RangeOfComposedCharactersAtIndex|S(?:mallestEncoding|urrogatePairForLongCharacter|ystemEncoding)|TypeID)|Has(?:Prefix|Suffix)|I(?:n(?:itInlineBuffer|sert)|s(?:EncodingAvailable|HyphenationAvailableForLocale|Surrogate(?:HighCharacter|LowCharacter)))|Lowercase|Normalize|Pad|Replace(?:All)?|SetExternalCharactersNoCopy|T(?:okenizer(?:AdvanceToNextToken|C(?:opy(?:BestStringLanguage|CurrentTokenAttribute)|reate)|G(?:et(?:Current(?:SubTokens|TokenRange)|TypeID)|oToTokenAtIndex)|SetString)|r(?:ansform|im(?:Whitespace)?))|Uppercase))|wapInt(?:16(?:BigToHost|HostTo(?:Big|Little)|LittleToHost)?|32(?:BigToHost|HostTo(?:Big|Little)|LittleToHost)?|64(?:BigToHost|HostTo(?:Big|Little)|LittleToHost)?))|T(?:imeZone(?:C(?:opy(?:Abbreviation(?:Dictionary)?|Default|KnownNames|LocalizedName|System)|reate(?:With(?:Name|TimeIntervalFromGMT))?)|Get(?:Da(?:ta|ylightSavingTimeOffset)|N(?:ame|extDaylightSavingTimeTransition)|SecondsFromGMT|TypeID)|IsDaylightSavingTime|ResetSystem|Set(?:AbbreviationDictionary|Default))|ree(?:App(?:endChild|lyFunctionToChildren)|Create|FindRoot|Get(?:C(?:hild(?:AtIndex|Count|ren)|ontext)|FirstChild|NextSibling|Parent|TypeID)|InsertSibling|PrependChild|Remove(?:AllChildren)?|S(?:etContext|ortChildren)))|U(?:RL(?:C(?:anBeDecomposed|learResourcePropertyCache(?:ForKey)?|opy(?:AbsoluteURL|F(?:ileSystemPath|ragment)|HostName|LastPathComponent|NetLocation|Pa(?:rameterString|ssword|th(?:Extension)?)|QueryString|Resource(?:Propert(?:iesForKeys|yForKey)|Specifier)|S(?:cheme|trictPath)|UserName)|reate(?:AbsoluteURLWithBytes|B(?:ookmarkData(?:From(?:AliasRecord|File))?|yResolvingBookmarkData)|Copy(?:AppendingPath(?:Component|Extension)|Deleting(?:LastPathComponent|PathExtension))|Data|F(?:ile(?:PathURL|ReferenceURL)|romFileSystemRepresentation(?:RelativeToBase)?)|ResourcePropert(?:iesForKeysFromBookmarkData|yForKeyFromBookmarkData)|StringByReplacingPercentEscapes|With(?:Bytes|FileSystemPath(?:RelativeToBase)?|String)))|Enumerator(?:CreateFor(?:DirectoryURL|MountedVolumes)|Get(?:DescendentLevel|NextURL|TypeID)|SkipDescendents)|Get(?:B(?:aseURL|yte(?:RangeForComponent|s))|FileSystemRepresentation|PortNumber|String|TypeID)|HasDirectoryPath|ResourceIsReachable|S(?:et(?:ResourcePropert(?:iesForKeys|yForKey)|TemporaryResourcePropertyForKey)|t(?:artAccessingSecurityScopedResource|opAccessingSecurityScopedResource))|WriteBookmarkDataToFile)|UID(?:Create(?:From(?:String|UUIDBytes)|String|WithBytes)?|Get(?:ConstantUUIDWithBytes|TypeID|UUIDBytes))|serNotification(?:C(?:ancel|heckBoxChecked|reate(?:RunLoopSource)?)|Display(?:Alert|Notice)|Get(?:Response(?:Dictionary|Value)|TypeID)|PopUpSelection|ReceiveResponse|SecureTextField|Update))|WriteStream(?:C(?:anAcceptBytes|lose|opy(?:Error|Property)|reateWith(?:AllocatedBuffers|Buffer|File))|Get(?:Error|Status|TypeID)|Open|S(?:cheduleWithRunLoop|et(?:Client|Property))|UnscheduleFromRunLoop|Write)|XMLCreateStringBy(?:EscapingEntities|UnescapingEntities))\\b)" + "match": "(\\s*)(\\bCF(?:A(?:bsoluteTimeGetCurrent|llocator(?:Allocate|Create|Deallocate|Get(?:Context|Default|PreferredSizeForSize|TypeID)|Reallocate|SetDefault)|rray(?:App(?:end(?:Array|Value)|lyFunction)|BSearchValues|C(?:ontainsValue|reate(?:Copy|Mutable(?:Copy)?)?)|ExchangeValuesAtIndices|Get(?:Count(?:OfValue)?|FirstIndexOfValue|LastIndexOfValue|TypeID|Value(?:AtIndex|s))|InsertValueAtIndex|Re(?:move(?:AllValues|ValueAtIndex)|placeValues)|S(?:etValueAtIndex|ortValues))|ttributedString(?:BeginEditing|Create(?:Copy|Mutable(?:Copy)?|WithSubstring)?|EndEditing|Get(?:Attribute(?:AndLongestEffectiveRange|s(?:AndLongestEffectiveRange)?)?|Length|MutableString|String|TypeID)|Re(?:moveAttribute|place(?:AttributedString|String))|SetAttribute(?:s)?))|B(?:ag(?:A(?:ddValue|pplyFunction)|C(?:ontainsValue|reate(?:Copy|Mutable(?:Copy)?)?)|Get(?:Count(?:OfValue)?|TypeID|Value(?:IfPresent|s)?)|Re(?:move(?:AllValues|Value)|placeValue)|SetValue)|i(?:naryHeap(?:A(?:ddValue|pplyFunction)|C(?:ontainsValue|reate(?:Copy)?)|Get(?:Count(?:OfValue)?|Minimum(?:IfPresent)?|TypeID|Values)|Remove(?:AllValues|MinimumValue))|tVector(?:C(?:ontainsBit|reate(?:Copy|Mutable(?:Copy)?)?)|FlipBit(?:AtIndex|s)|Get(?:Bit(?:AtIndex|s)|Count(?:OfBit)?|FirstIndexOfBit|LastIndexOfBit|TypeID)|Set(?:AllBits|Bit(?:AtIndex|s)|Count)))|ooleanGet(?:TypeID|Value)|undle(?:C(?:opy(?:AuxiliaryExecutableURL|Bu(?:iltInPlugInsURL|ndle(?:Localizations|URL))|Executable(?:Architectures(?:ForURL)?|URL)|InfoDictionary(?:ForURL|InDirectory)|Localiz(?:ationsFor(?:Preferences|URL)|edString)|Pr(?:eferredLocalizationsFromArray|ivateFrameworksURL)|Resource(?:URL(?:ForLocalization|InDirectory|sOfType(?:ForLocalization|InDirectory)?)?|sDirectoryURL)|S(?:hared(?:FrameworksURL|SupportURL)|upportFilesDirectoryURL))|reate(?:BundlesFromDirectory)?)|Get(?:AllBundles|BundleWithIdentifier|D(?:ataPointer(?:ForName|sForNames)|evelopmentRegion)|FunctionPointer(?:ForName|sForNames)|I(?:dentifier|nfoDictionary)|LocalInfoDictionary|MainBundle|P(?:ackageInfo(?:InDirectory)?|lugIn)|TypeID|V(?:alueForInfoDictionaryKey|ersionNumber))|IsExecutableLoaded|LoadExecutable(?:AndReturnError)?|PreflightExecutable|UnloadExecutable)|yteOrderGetCurrent)|C(?:alendar(?:AddComponents|C(?:o(?:mposeAbsoluteTime|py(?:Current|Locale|TimeZone))|reateWithIdentifier)|DecomposeAbsoluteTime|Get(?:ComponentDifference|FirstWeekday|Identifier|M(?:aximumRangeOfUnit|inimum(?:DaysInFirstWeek|RangeOfUnit))|OrdinalityOfUnit|RangeOfUnit|T(?:imeRangeOfUnit|ypeID))|Set(?:FirstWeekday|Locale|MinimumDaysInFirstWeek|TimeZone))|haracterSet(?:AddCharactersIn(?:Range|String)|Create(?:BitmapRepresentation|Copy|InvertedSet|Mutable(?:Copy)?|With(?:BitmapRepresentation|CharactersIn(?:Range|String)))|Get(?:Predefined|TypeID)|HasMemberInPlane|I(?:n(?:tersect|vert)|s(?:CharacterMember|LongCharacterMember|SupersetOfSet))|RemoveCharactersIn(?:Range|String)|Union)|o(?:nvert(?:Double(?:HostToSwapped|SwappedToHost)|Float(?:32(?:HostToSwapped|SwappedToHost)|64(?:HostToSwapped|SwappedToHost)|HostToSwapped|SwappedToHost))|py(?:Description|HomeDirectoryURL|TypeIDDescription)))|D(?:at(?:a(?:AppendBytes|Create(?:Copy|Mutable(?:Copy)?|WithBytesNoCopy)?|DeleteBytes|Find|Get(?:Byte(?:Ptr|s)|Length|MutableBytePtr|TypeID)|IncreaseLength|ReplaceBytes|SetLength)|e(?:C(?:ompare|reate)|Formatter(?:C(?:opyProperty|reate(?:DateF(?:ormatFromTemplate|romString)|StringWith(?:AbsoluteTime|Date))?)|Get(?:AbsoluteTimeFromString|DateStyle|Format|Locale|T(?:imeStyle|ypeID))|Set(?:Format|Property))|Get(?:AbsoluteTime|T(?:imeIntervalSinceDate|ypeID))))|ictionary(?:A(?:ddValue|pplyFunction)|C(?:ontains(?:Key|Value)|reate(?:Copy|Mutable(?:Copy)?)?)|Get(?:Count(?:Of(?:Key|Value))?|KeysAndValues|TypeID|Value(?:IfPresent)?)|Re(?:move(?:AllValues|Value)|placeValue)|SetValue))|E(?:qual|rror(?:C(?:opy(?:Description|FailureReason|RecoverySuggestion|UserInfo)|reate(?:WithUserInfoKeysAndValues)?)|Get(?:Code|Domain|TypeID)))|File(?:Descriptor(?:Create(?:RunLoopSource)?|DisableCallBacks|EnableCallBacks|Get(?:Context|NativeDescriptor|TypeID)|I(?:nvalidate|sValid))|Security(?:C(?:opy(?:AccessControlList|GroupUUID|OwnerUUID)|reate(?:Copy)?)|Get(?:Group|Mode|Owner|TypeID)|Set(?:AccessControlList|Group(?:UUID)?|Mode|Owner(?:UUID)?)))|Get(?:Allocator|RetainCount|TypeID)|Hash|Locale(?:C(?:opy(?:AvailableLocaleIdentifiers|C(?:ommonISOCurrencyCodes|urrent)|DisplayNameForPropertyValue|ISO(?:C(?:ountryCodes|urrencyCodes)|LanguageCodes)|PreferredLanguages)|reate(?:C(?:anonicalL(?:anguageIdentifierFromString|ocaleIdentifierFromS(?:criptManagerCodes|tring))|o(?:mponentsFromLocaleIdentifier|py))|LocaleIdentifierFrom(?:Components|WindowsLocaleCode))?)|Get(?:Identifier|Language(?:CharacterDirection|LineDirection)|System|TypeID|Value|WindowsLocaleCodeFromLocaleIdentifier))|M(?:a(?:chPort(?:Create(?:RunLoopSource|WithPort)?|Get(?:Context|InvalidationCallBack|Port|TypeID)|I(?:nvalidate|sValid)|SetInvalidationCallBack)|keCollectable)|essagePort(?:Create(?:Local|R(?:emote|unLoopSource))|Get(?:Context|InvalidationCallBack|Name|TypeID)|I(?:nvalidate|s(?:Remote|Valid))|Se(?:ndRequest|t(?:DispatchQueue|InvalidationCallBack|Name))))|N(?:otificationCenter(?:AddObserver|Get(?:D(?:arwinNotifyCenter|istributedCenter)|LocalCenter|TypeID)|PostNotification(?:WithOptions)?|Remove(?:EveryObserver|Observer))|u(?:llGetTypeID|mber(?:C(?:ompare|reate)|Formatter(?:C(?:opyProperty|reate(?:NumberFromString|StringWith(?:Number|Value))?)|Get(?:DecimalInfoForCurrencyCode|Format|Locale|Style|TypeID|ValueFromString)|Set(?:Format|Property))|Get(?:ByteSize|Type(?:ID)?|Value)|IsFloatType)))|P(?:lugIn(?:AddInstanceForFactory|Create|FindFactoriesForPlugInType(?:InPlugIn)?|Get(?:Bundle|TypeID)|I(?:nstance(?:Create(?:WithInstanceDataSize)?|Get(?:FactoryName|In(?:stanceData|terfaceFunctionTable)|TypeID))|sLoadOnDemand)|Re(?:gister(?:FactoryFunction(?:ByName)?|PlugInType)|moveInstanceForFactory)|SetLoadOnDemand|Unregister(?:Factory|PlugInType))|r(?:eferences(?:A(?:ddSuitePreferencesToApp|pp(?:Synchronize|ValueIsForced))|Copy(?:AppValue|KeyList|Multiple|Value)|GetApp(?:BooleanValue|IntegerValue)|RemoveSuitePreferencesFromApp|S(?:et(?:AppValue|Multiple|Value)|ynchronize))|opertyList(?:Create(?:D(?:ata|eepCopy)|With(?:Data|Stream))|IsValid|Write)))|R(?:angeMake|e(?:adStream(?:C(?:lose|opy(?:Error|Property)|reateWith(?:BytesNoCopy|File))|Get(?:Buffer|Error|Status|TypeID)|HasBytesAvailable|Open|Read|S(?:cheduleWithRunLoop|et(?:Client|Property))|UnscheduleFromRunLoop)|lease|tain)|unLoop(?:Add(?:CommonMode|Observer|Source|Timer)|Co(?:ntains(?:Observer|Source|Timer)|py(?:AllModes|CurrentMode))|Get(?:Current|Main|NextTimerFireDate|TypeID)|IsWaiting|Observer(?:Create(?:WithHandler)?|DoesRepeat|Get(?:Activities|Context|Order|TypeID)|I(?:nvalidate|sValid))|PerformBlock|R(?:emove(?:Observer|Source|Timer)|un(?:InMode)?)|S(?:ource(?:Create|Get(?:Context|Order|TypeID)|I(?:nvalidate|sValid)|Signal)|top)|Timer(?:Create(?:WithHandler)?|DoesRepeat|Get(?:Context|Interval|NextFireDate|Order|TypeID)|I(?:nvalidate|sValid)|SetNextFireDate)|WakeUp))|S(?:et(?:A(?:ddValue|pplyFunction)|C(?:ontainsValue|reate(?:Copy|Mutable(?:Copy)?)?)|Get(?:Count(?:OfValue)?|TypeID|Value(?:IfPresent|s)?)|Re(?:move(?:AllValues|Value)|placeValue)|SetValue)|how(?:Str)?|ocket(?:C(?:o(?:nnectToAddress|py(?:Address|PeerAddress|Registered(?:SocketSignature|Value)))|reate(?:ConnectedToSocketSignature|RunLoopSource|With(?:Native|SocketSignature))?)|DisableCallBacks|EnableCallBacks|Get(?:Context|DefaultNameRegistryPortNumber|Native|SocketFlags|TypeID)|I(?:nvalidate|sValid)|Register(?:SocketSignature|Value)|Se(?:ndData|t(?:Address|DefaultNameRegistryPortNumber|SocketFlags))|Unregister)|tr(?:eamCreate(?:BoundPair|PairWith(?:PeerSocketSignature|Socket(?:ToHost)?))|ing(?:Append(?:C(?:String|haracters)|Format(?:AndArguments)?|PascalString)?|C(?:apitalize|o(?:mpare(?:WithOptions(?:AndLocale)?)?|nvert(?:EncodingTo(?:IANACharSetName|NSStringEncoding|WindowsCodepage)|IANACharSetNameToEncoding|NSStringEncodingToEncoding|WindowsCodepageToEncoding))|reate(?:Array(?:BySeparatingStrings|WithFindResults)|ByCombiningStrings|Copy|ExternalRepresentation|FromExternalRepresentation|Mutable(?:Copy|WithExternalCharactersNoCopy)?|With(?:Bytes(?:NoCopy)?|C(?:String(?:NoCopy)?|haracters(?:NoCopy)?)|F(?:ileSystemRepresentation|ormat(?:AndArguments)?)|PascalString(?:NoCopy)?|Substring)))|Delete|F(?:ind(?:AndReplace|CharacterFromSet|WithOptions(?:AndLocale)?)?|old)|Get(?:Bytes|C(?:String(?:Ptr)?|haracter(?:AtIndex|FromInlineBuffer|s(?:Ptr)?))|DoubleValue|F(?:astestEncoding|ileSystemRepresentation)|HyphenationLocationBeforeIndex|IntValue|L(?:ength|i(?:neBounds|stOfAvailableEncodings)|ongCharacterForSurrogatePair)|M(?:aximumSize(?:ForEncoding|OfFileSystemRepresentation)|ostCompatibleMacStringEncoding)|NameOfEncoding|Pa(?:ragraphBounds|scalString(?:Ptr)?)|RangeOfComposedCharactersAtIndex|S(?:mallestEncoding|urrogatePairForLongCharacter|ystemEncoding)|TypeID)|Has(?:Prefix|Suffix)|I(?:n(?:itInlineBuffer|sert)|s(?:EncodingAvailable|HyphenationAvailableForLocale|Surrogate(?:HighCharacter|LowCharacter)))|Lowercase|Normalize|Pad|Replace(?:All)?|SetExternalCharactersNoCopy|T(?:okenizer(?:AdvanceToNextToken|C(?:opy(?:BestStringLanguage|CurrentTokenAttribute)|reate)|G(?:et(?:Current(?:SubTokens|TokenRange)|TypeID)|oToTokenAtIndex)|SetString)|r(?:ansform|im(?:Whitespace)?))|Uppercase))|wapInt(?:16(?:BigToHost|HostTo(?:Big|Little)|LittleToHost)?|32(?:BigToHost|HostTo(?:Big|Little)|LittleToHost)?|64(?:BigToHost|HostTo(?:Big|Little)|LittleToHost)?))|T(?:imeZone(?:C(?:opy(?:Abbreviation(?:Dictionary)?|Default|KnownNames|LocalizedName|System)|reate(?:With(?:Name|TimeIntervalFromGMT))?)|Get(?:Da(?:ta|ylightSavingTimeOffset)|N(?:ame|extDaylightSavingTimeTransition)|SecondsFromGMT|TypeID)|IsDaylightSavingTime|ResetSystem|Set(?:AbbreviationDictionary|Default))|ree(?:App(?:endChild|lyFunctionToChildren)|Create|FindRoot|Get(?:C(?:hild(?:AtIndex|Count|ren)|ontext)|FirstChild|NextSibling|Parent|TypeID)|InsertSibling|PrependChild|Remove(?:AllChildren)?|S(?:etContext|ortChildren)))|U(?:RL(?:C(?:anBeDecomposed|learResourcePropertyCache(?:ForKey)?|opy(?:AbsoluteURL|F(?:ileSystemPath|ragment)|HostName|LastPathComponent|NetLocation|Pa(?:ssword|th(?:Extension)?)|QueryString|Resource(?:Propert(?:iesForKeys|yForKey)|Specifier)|S(?:cheme|trictPath)|UserName)|reate(?:AbsoluteURLWithBytes|B(?:ookmarkData(?:From(?:AliasRecord|File))?|yResolvingBookmarkData)|Copy(?:AppendingPath(?:Component|Extension)|Deleting(?:LastPathComponent|PathExtension))|Data|F(?:ile(?:PathURL|ReferenceURL)|romFileSystemRepresentation(?:RelativeToBase)?)|ResourcePropert(?:iesForKeysFromBookmarkData|yForKeyFromBookmarkData)|StringByReplacingPercentEscapes|With(?:Bytes|FileSystemPath(?:RelativeToBase)?|String)))|Enumerator(?:CreateFor(?:DirectoryURL|MountedVolumes)|Get(?:DescendentLevel|NextURL|TypeID)|SkipDescendents)|Get(?:B(?:aseURL|yte(?:RangeForComponent|s))|FileSystemRepresentation|PortNumber|String|TypeID)|HasDirectoryPath|ResourceIsReachable|S(?:et(?:ResourcePropert(?:iesForKeys|yForKey)|TemporaryResourcePropertyForKey)|t(?:artAccessingSecurityScopedResource|opAccessingSecurityScopedResource))|WriteBookmarkDataToFile)|UID(?:Create(?:From(?:String|UUIDBytes)|String|WithBytes)?|Get(?:ConstantUUIDWithBytes|TypeID|UUIDBytes))|serNotification(?:C(?:ancel|heckBoxChecked|reate(?:RunLoopSource)?)|Display(?:Alert|Notice)|Get(?:Response(?:Dictionary|Value)|TypeID)|PopUpSelection|ReceiveResponse|SecureTextField|Update))|WriteStream(?:C(?:anAcceptBytes|lose|opy(?:Error|Property)|reateWith(?:AllocatedBuffers|Buffer|File))|Get(?:Error|Status|TypeID)|Open|S(?:cheduleWithRunLoop|et(?:Client|Property))|UnscheduleFromRunLoop|Write)|XMLCreateStringBy(?:EscapingEntities|UnescapingEntities))\\b)" }, { "captures": { @@ -400,6 +922,28 @@ }, "match": "(\\s*)(\\b(?:clock_(?:get(?:res|time(?:_nsec_np)?)|settime)|mk(?:ostemp(?:s)?|pathat_np)|rename(?:atx_np|x_np)|timingsafe_bcmp)\\b)" }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.clib.10.13.c" + } + }, + "match": "(\\s*)(\\b(?:fmemopen|mk(?:dtempat_np|ostempsat_np|stempsat_np)|open_memstream|ptsname_r|setattrlistat)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.clib.10.15.c" + } + }, + "match": "(\\s*)(\\b(?:rpmatch|timespec_get)\\b)" + }, { "captures": { "1": { @@ -420,7 +964,7 @@ "name": "support.function.clib.10.9.c" } }, - "match": "(\\s*)(\\bf(?:fsll|lsll)\\b)" + "match": "(\\s*)(\\b(?:f(?:fsll|lsll)|memset_s)\\b)" }, { "captures": { @@ -431,7 +975,7 @@ "name": "support.function.clib.c" } }, - "match": "(\\s*)(\\b(?:a(?:64l|b(?:ort|s)|c(?:c(?:ess(?:x_np)?|t)|os(?:f|h(?:f|l)?|l)?)|dd_profil|l(?:arm|loca)|rc4random(?:_(?:addrandom|buf|stir|uniform))?|s(?:ctime(?:_r)?|in(?:f|h(?:f|l)?|l)?|printf)|t(?:an(?:2(?:f|l)?|f|h(?:f|l)?|l)?|exit(?:_b)?|o(?:f|i|l(?:l)?)))|b(?:c(?:mp|opy)|rk|s(?:d_signal|earch(?:_b)?)|zero)|c(?:alloc|brt(?:f|l)?|eil(?:f|l)?|get(?:c(?:ap|lose)|ent|first|match|n(?:ext|um)|s(?:et|tr)|ustr)|h(?:dir|own|root)|l(?:earerr|o(?:ck|se))|o(?:nfstr|pysign(?:f|l)?|s(?:f|h(?:f|l)?|l)?)|r(?:eat|ypt)|t(?:ermid(?:_r)?|ime(?:_r)?))|d(?:evname(?:_r)?|i(?:fftime|gittoint|spatch_(?:time|walltime)|v)|printf|rand48|up(?:2)?)|e(?:cvt|n(?:crypt|dusershell)|r(?:and48|f(?:c(?:f|l)?|f|l)?)|x(?:changedata|ec(?:l(?:e|p)?|v(?:P|e|p)?)|it|p(?:2(?:f|l)?|f|l|m1(?:f|l)?)?))|f(?:abs(?:f|l)?|c(?:h(?:dir|own)|lose|ntl|vt)|d(?:im(?:f|l)?|open)|e(?:of|rror)|f(?:l(?:agstostr|ush)|s(?:ctl|l)?)|get(?:attrlist|c|ln|pos|s)|ile(?:no|sec_(?:dup|free|get_property|init|query_property|set_property|unset_property))|l(?:o(?:ck(?:file)?|or(?:f|l)?)|s(?:l)?)|m(?:a(?:f|l|x(?:f|l)?)?|in(?:f|l)?|od(?:f|l)?|tcheck)|o(?:pen|rk)|p(?:athconf|rintf|u(?:rge|t(?:c|s)))|re(?:ad|e|open|xp(?:f|l)?)|s(?:c(?:anf|tl)|e(?:ek(?:o)?|t(?:attrlist|pos))|ync)|t(?:ell(?:o)?|r(?:uncate|ylockfile))|un(?:lockfile|open)|write)|g(?:cvt|et(?:attrlist|bsize|c(?:_unlocked|har(?:_unlocked)?|wd)?|d(?:ate|elim|irentriesattr|omainname|tablesize)|e(?:gid|nv|uid)|g(?:id|roup(?:list|s))|host(?:id|name)|iopolicy_np|l(?:ine|o(?:adavg|gin(?:_r)?))|mode|opt|p(?:a(?:gesize|ss)|eereid|g(?:id|rp)|id|pid|r(?:iority|ogname))|r(?:limit|usage)|s(?:groups_np|id|ubopt)?|u(?:id|sershell)|w(?:d|groups_np)?)|mtime(?:_r)?|rantpt)|h(?:eapsort(?:_b)?|ypot(?:f|l)?)|i(?:logb(?:f|l)?|n(?:dex|it(?:groups|state))|ruserok(?:_sa)?|s(?:a(?:l(?:num|pha)|scii|tty)|blank|cntrl|digit|graph|hexnumber|ideogram|lower|number|p(?:honogram|rint|unct)|rune|s(?:etugid|p(?:ace|ecial))|upper|xdigit))|j(?:0|1|n|rand48)|kill(?:pg)?|l(?:64a|abs|c(?:hown|ong48)|d(?:exp(?:f|l)?|iv)|gamma(?:f|l)?|ink|l(?:abs|div|r(?:int(?:f|l)?|ound(?:f|l)?))|o(?:c(?:al(?:econv|time(?:_r)?)|kf)|g(?:1(?:0(?:f|l)?|p(?:f|l)?)|2(?:f|l)?|b(?:f|l)?|f|l)?|ngjmp(?:error)?)|r(?:and48|int(?:f|l)?|ound(?:f|l)?)|seek)|m(?:a(?:jor|kedev|lloc)|b(?:len|stowcs|towc)|e(?:m(?:c(?:cpy|hr|mp|py)|m(?:em|ove)|set(?:_pattern(?:16|4|8))?)|rgesort(?:_b)?)|inor|k(?:dtemp|nod|stemp(?:_dprotected_np|s)?|t(?:emp|ime))|odf(?:f|l)?|rand48)|n(?:an(?:f|l|osleep)?|e(?:arbyint(?:f|l)?|xt(?:after(?:f|l)?|toward(?:f|l)?))|fssvc|ice|rand48)|open(?:_dprotected_np|x_np)?|p(?:a(?:thconf|use)|close|error|ipe|o(?:pen|six(?:2time|_(?:memalign|openpt))|w(?:f|l)?)|r(?:ead|intf|ofil)|s(?:elect|ignal|ort(?:_(?:b|r))?)|t(?:hread_(?:getugid_np|kill|s(?:etugid_np|igmask))|sname)|ut(?:c(?:_unlocked|har(?:_unlocked)?)?|env|s|w)|write)|qsort(?:_(?:b|r))?|r(?:a(?:dixsort|ise|nd(?:_r|om)?)|cmd(?:_af)?|e(?:a(?:d(?:link)?|l(?:loc(?:f)?|path))|boot|m(?:ainder(?:f|l)?|ove|quo(?:f|l)?)|name|voke|wind)|in(?:dex|t(?:f|l)?)|mdir|ound(?:f|l)?|resvport(?:_af)?|userok)|s(?:brk|ca(?:lb(?:ln(?:f|l)?|n(?:f|l)?)?|nf)|e(?:archfs|ed48|lect|t(?:attrlist|buf(?:fer)?|domainname|e(?:gid|nv|uid)|g(?:id|roups)|host(?:id|name)|iopolicy_np|jmp|key|l(?:inebuf|o(?:cale|gin))|mode|p(?:g(?:id|rp)|r(?:iority|ogname))|r(?:e(?:gid|uid)|gid|limit|uid)|s(?:groups_np|id|tate)|u(?:id|sershell)|vbuf|wgroups_np))|i(?:g(?:a(?:ction|ddset|ltstack)|block|delset|emptyset|fillset|hold|i(?:gnore|nterrupt|smember)|longjmp|nal|p(?:ause|ending|rocmask)|relse|s(?:et(?:jmp|mask)?|uspend)|vec|wait)|n(?:f|h(?:f|l)?|l)?)|leep|nprintf|printf|qrt(?:f|l)?|ra(?:dixsort|nd(?:48|dev|om(?:dev)?)?)|scanf|t(?:p(?:cpy|ncpy)|r(?:c(?:a(?:se(?:cmp|str)|t)|hr|mp|oll|py|spn)|dup|error(?:_r)?|ftime|l(?:c(?:at|py)|en)|mode|n(?:c(?:a(?:secmp|t)|mp|py)|dup|len|str)|p(?:brk|time)|rchr|s(?:ep|ignal|pn|tr)|to(?:d|f(?:flags)?|k(?:_r)?|l(?:d|l)?|q|u(?:l(?:l)?|q))|xfrm))|wa(?:b|pon)|y(?:mlink|nc|s(?:conf|tem)))|t(?:an(?:f|h(?:f|l)?|l)?|c(?:getpgrp|setpgrp)|empnam|gamma(?:f|l)?|ime(?:2posix|gm|local)?|mp(?:file|nam)|o(?:ascii|lower|upper)|runc(?:ate|f|l)?|ty(?:name(?:_r)?|slot)|zset(?:wall)?)|u(?:alarm|n(?:delete|getc|l(?:ink|ockpt)|setenv|whiteout)|sleep)|v(?:a(?:lloc|sprintf)|dprintf|f(?:ork|printf|scanf)|printf|s(?:canf|nprintf|printf|scanf))|w(?:ait(?:3|4|id|pid)?|c(?:stombs|tomb)|rite)|y(?:0|1|n)|zopen)\\b)" + "match": "(\\s*)(\\b(?:a(?:64l|b(?:ort|s)|c(?:c(?:ess(?:x_np)?|t)|os(?:f|h(?:f|l)?|l)?)|d(?:d_profil|jtime)|l(?:arm|loca)|rc4random(?:_(?:buf|stir|uniform))?|s(?:ctime(?:_r)?|in(?:f|h(?:f|l)?|l)?|printf)|t(?:an(?:2(?:f|l)?|f|h(?:f|l)?|l)?|exit(?:_b)?|o(?:f|i|l(?:l)?)))|b(?:c(?:mp|opy)|rk|s(?:d_signal|earch(?:_b)?)|zero)|c(?:brt(?:f|l)?|eil(?:f|l)?|get(?:c(?:ap|lose)|ent|first|match|n(?:ext|um)|s(?:et|tr)|ustr)|h(?:dir|own|root)|l(?:earerr|o(?:ck|se))|o(?:nfstr|pysign(?:f|l)?|s(?:f|h(?:f|l)?|l)?)|r(?:eat|ypt)|t(?:ermid_r|ime(?:_r)?))|d(?:evname(?:_r)?|i(?:fftime|spatch_(?:time|walltime)|v)|printf|rand48|up(?:2)?)|e(?:cvt|n(?:crypt|dusershell)|r(?:and48|f(?:c(?:f|l)?|f|l)?)|x(?:changedata|ec(?:l(?:e|p)?|v(?:P|e|p)?)|it|p(?:2(?:f|l)?|f|l|m1(?:f|l)?)?))|f(?:abs(?:f|l)?|c(?:h(?:dir|own)|lose|ntl|vt)|d(?:im(?:f|l)?|open)|e(?:of|rror)|f(?:l(?:agstostr|ush)|s(?:ctl|l)?)|get(?:attrlist|c|ln|pos|s)|ile(?:no|sec_(?:dup|free|get_property|init|query_property|set_property|unset_property))|l(?:o(?:ck(?:file)?|or(?:f|l)?)|s(?:l)?)|m(?:a(?:f|l|x(?:f|l)?)?|in(?:f|l)?|od(?:f|l)?|tcheck)|o(?:pen|rk)|p(?:athconf|rintf|u(?:rge|t(?:c|s)))|re(?:ad|open|xp(?:f|l)?)|s(?:c(?:anf|tl)|e(?:ek(?:o)?|t(?:attrlist|pos))|ync)|t(?:ell(?:o)?|r(?:uncate|ylockfile))|u(?:n(?:lockfile|open)|times)|write)|g(?:cvt|et(?:attrlist|bsize|c(?:_unlocked|har(?:_unlocked)?|wd)?|d(?:ate|elim|irentriesattr|omainname|tablesize)|e(?:gid|nv|uid)|g(?:id|roup(?:list|s))|host(?:id|name)|i(?:opolicy_np|timer)|l(?:ine|o(?:adavg|gin(?:_r)?))|mode|opt|p(?:a(?:gesize|ss)|eereid|g(?:id|rp)|id|pid|r(?:iority|ogname))|r(?:limit|usage)|s(?:groups_np|id|ubopt)?|timeofday|u(?:id|sershell)|w(?:d|groups_np)?)|mtime(?:_r)?|rantpt)|h(?:eapsort(?:_b)?|ypot(?:f|l)?)|i(?:logb(?:f|l)?|n(?:dex|it(?:groups|state))|ruserok(?:_sa)?|s(?:atty|setugid))|j(?:0|1|n|rand48)|kill(?:pg)?|l(?:64a|abs|c(?:hown|ong48)|d(?:exp(?:f|l)?|iv)|gamma(?:f|l)?|ink|l(?:abs|div|r(?:int(?:f|l)?|ound(?:f|l)?))|o(?:c(?:al(?:econv|time(?:_r)?)|kf)|g(?:1(?:0(?:f|l)?|p(?:f|l)?)|2(?:f|l)?|b(?:f|l)?|f|l)?|ngjmp(?:error)?)|r(?:and48|int(?:f|l)?|ound(?:f|l)?)|seek|utimes)|m(?:b(?:len|stowcs|towc)|e(?:m(?:c(?:cpy|hr|mp|py)|m(?:em|ove)|set(?:_pattern(?:16|4|8))?)|rgesort(?:_b)?)|k(?:dtemp|nod|stemp(?:_dprotected_np|s)?|t(?:emp|ime))|odf(?:f|l)?|rand48)|n(?:an(?:f|l|osleep)?|e(?:arbyint(?:f|l)?|xt(?:after(?:f|l)?|toward(?:f|l)?))|fssvc|ice|rand48)|open(?:_dprotected_np|x_np)?|p(?:a(?:thconf|use)|close|error|ipe|o(?:pen|six(?:2time|_openpt)|w(?:f|l)?)|r(?:ead|intf|ofil)|s(?:elect|ignal|ort(?:_(?:b|r))?)|t(?:hread_(?:getugid_np|kill|s(?:etugid_np|igmask))|sname)|ut(?:c(?:_unlocked|har(?:_unlocked)?)?|env|s|w)|write)|qsort(?:_(?:b|r))?|r(?:a(?:dixsort|ise|nd(?:_r|om)?)|cmd(?:_af)?|e(?:a(?:d(?:link)?|l(?:locf|path))|boot|m(?:ainder(?:f|l)?|ove|quo(?:f|l)?)|name|voke|wind)|in(?:dex|t(?:f|l)?)|mdir|ound(?:f|l)?|resvport(?:_af)?|userok)|s(?:brk|ca(?:lb(?:ln(?:f|l)?|n(?:f|l)?)?|nf)|e(?:archfs|ed48|lect|t(?:attrlist|buf(?:fer)?|domainname|e(?:gid|nv|uid)|g(?:id|roups)|host(?:id|name)|i(?:opolicy_np|timer)|jmp|key|l(?:inebuf|o(?:cale|gin))|mode|p(?:g(?:id|rp)|r(?:iority|ogname))|r(?:e(?:gid|uid)|gid|limit|uid)|s(?:groups_np|id|tate)|timeofday|u(?:id|sershell)|vbuf|wgroups_np))|i(?:g(?:a(?:ction|ddset|ltstack)|block|delset|emptyset|fillset|hold|i(?:gnore|nterrupt|smember)|longjmp|nal|p(?:ause|ending|rocmask)|relse|s(?:et(?:jmp|mask)?|uspend)|vec|wait)|md_muladd|n(?:f|h(?:f|l)?|l)?)|leep|nprintf|printf|qrt(?:f|l)?|ra(?:dixsort|nd(?:48|dev|om(?:dev)?)?)|scanf|t(?:p(?:cpy|ncpy)|r(?:c(?:a(?:se(?:cmp|str)|t)|hr|mp|oll|py|spn)|dup|error(?:_r)?|ftime|l(?:c(?:at|py)|en)|mode|n(?:c(?:a(?:secmp|t)|mp|py)|dup|len|str)|p(?:brk|time)|rchr|s(?:ep|ignal|pn|tr)|to(?:d|f(?:flags)?|k(?:_r)?|l(?:d|l)?|q|u(?:l(?:l)?|q))|xfrm))|wa(?:b|pon)|y(?:mlink|nc|s(?:conf|tem)))|t(?:an(?:f|h(?:f|l)?|l)?|c(?:getpgrp|setpgrp)|empnam|gamma(?:f|l)?|ime(?:2posix|gm|local)?|mp(?:file|nam)|runc(?:ate|f|l)?|ty(?:name(?:_r)?|slot)|zset(?:wall)?)|u(?:alarm|n(?:delete|getc|l(?:ink|ockpt)|setenv|whiteout)|sleep|times)|v(?:a(?:lloc|sprintf)|dprintf|f(?:ork|printf|scanf)|printf|s(?:canf|nprintf|printf|scanf))|w(?:ait(?:3|4|id|pid)?|c(?:stombs|tomb)|rite)|y(?:0|1|n)|zopen)\\b)" }, { "captures": { @@ -455,6 +999,17 @@ }, "match": "(\\s*)(\\bdispatch_(?:a(?:ctivate|ssert_queue(?:_(?:barrier|not))?)|queue_(?:attr_make_(?:initially_inactive|with_autorelease_frequency)|create_with_target))\\b)" }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.dispatch.10.14.c" + } + }, + "match": "(\\s*)(\\bdispatch_(?:async_and_wait(?:_f)?|barrier_async_and_wait(?:_f)?|set_qos_class_floor|workloop_(?:create(?:_inactive)?|set_autorelease_frequency))\\b)" + }, { "captures": { "1": { @@ -466,17 +1021,6 @@ }, "match": "(\\s*)(\\bdispatch_(?:a(?:fter(?:_f)?|pply(?:_f)?|sync(?:_f)?)|barrier_(?:async(?:_f)?|sync(?:_f)?)|cancel|data_(?:apply|c(?:opy_region|reate(?:_(?:concat|map|subrange))?)|get_size)|g(?:et_(?:context|global_queue|main_queue|specific)|roup_(?:async(?:_f)?|create|enter|leave|notify(?:_f)?|wait))|io_(?:barrier|c(?:lose|reate(?:_with_(?:io|path))?)|get_descriptor|read|set_(?:high_water|interval|low_water)|write)|main|notify|once(?:_f)?|queue_(?:create|get_(?:label|specific)|set_specific)|re(?:ad|lease|sume|tain)|s(?:e(?:maphore_(?:create|signal|wait)|t_(?:context|finalizer_f|target_queue))|ource_(?:c(?:ancel|reate)|get_(?:data|handle|mask)|merge_data|set_(?:cancel_handler(?:_f)?|event_handler(?:_f)?|registration_handler(?:_f)?|timer)|testcancel)|uspend|ync(?:_f)?)|testcancel|w(?:ait|rite))\\b)" }, - { - "captures": { - "1": { - "name": "punctuation.whitespace.support.function.leading" - }, - "2": { - "name": "support.function.mac-classic.c" - } - }, - "match": "(\\s*)(\\bStrLength\\b)" - }, { "captures": { "1": { @@ -488,28 +1032,6 @@ }, "match": "(\\s*)(\\b(?:OS(?:HostByteOrder|ReadSwapInt(?:16|32|64)|WriteSwapInt(?:16|32|64))|gethostuuid)\\b)" }, - { - "captures": { - "1": { - "name": "punctuation.whitespace.support.function.leading" - }, - "2": { - "name": "support.function.pthread.10.10.c" - } - }, - "match": "(\\s*)(\\bpthread_(?:attr_(?:get_qos_class_np|set_qos_class_np)|get_qos_class_np|override_qos_class_(?:end_np|start_np)|set_qos_class_self_np)\\b)" - }, - { - "captures": { - "1": { - "name": "punctuation.whitespace.support.function.leading" - }, - "2": { - "name": "support.function.pthread.c" - } - }, - "match": "(\\s*)(\\b(?:pthread_(?:at(?:fork|tr_(?:destroy|get(?:detachstate|guardsize|inheritsched|s(?:c(?:hedp(?:aram|olicy)|ope)|tack(?:addr|size)?))|init|set(?:detachstate|guardsize|inheritsched|s(?:c(?:hedp(?:aram|olicy)|ope)|tack(?:addr|size)?))))|c(?:ancel|ond(?:_(?:broadcast|destroy|init|signal(?:_thread_np)?|timedwait(?:_relative_np)?|wait)|attr_(?:destroy|getpshared|init|setpshared))|reate(?:_suspended_np)?)|detach|e(?:qual|xit)|from_mach_thread_np|get(?:_stack(?:addr_np|size_np)|concurrency|name_np|s(?:chedparam|pecific))|is_threaded_np|join|k(?:ey_(?:create|delete)|ill)|m(?:a(?:ch_thread_np|in_np)|utex(?:_(?:destroy|getprioceiling|init|lock|setprioceiling|trylock|unlock)|attr_(?:destroy|get(?:p(?:r(?:ioceiling|otocol)|shared)|type)|init|set(?:p(?:r(?:ioceiling|otocol)|shared)|type))))|once|rwlock(?:_(?:destroy|init|rdlock|try(?:rdlock|wrlock)|unlock|wrlock)|attr_(?:destroy|getpshared|init|setpshared))|s(?:e(?:lf|t(?:c(?:ancel(?:state|type)|oncurrency)|name_np|s(?:chedparam|pecific)))|igmask)|t(?:estcancel|hreadid_np)|yield_np)|sched_(?:get_priority_m(?:ax|in)|yield))\\b)" - }, { "captures": { "1": { @@ -530,7 +1052,40 @@ "name": "support.function.quartz.10.12.c" } }, - "match": "(\\s*)(\\bCGColor(?:ConversionInfoCreate(?:FromList)?|Space(?:CopyICCData|IsWideGamutRGB|SupportsOutput))\\b)" + "match": "(\\s*)(\\bCGColor(?:ConversionInfoCreate(?:FromList)?|Space(?:C(?:opy(?:ICCData|PropertyList)|reateWith(?:ICCData|PropertyList))|IsWideGamutRGB|SupportsOutput))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.quartz.10.13.c" + } + }, + "match": "(\\s*)(\\bCG(?:Color(?:ConversionInfoCreateFromListWithArguments|SpaceGetName)|DataProviderGetInfo|EventCreateScrollWheelEvent2|P(?:DF(?:ContextSetOutline|DocumentGet(?:AccessPermissions|Outline))|athApplyWithBlock))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.quartz.10.14.c" + } + }, + "match": "(\\s*)(\\bCG(?:ColorConversionInfoCreateWithOptions|ImageGet(?:ByteOrderInfo|PixelFormatInfo)|PDF(?:ArrayApplyBlock|DictionaryApplyBlock))\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.quartz.10.15.c" + } + }, + "match": "(\\s*)(\\bCG(?:ColorCreate(?:GenericGrayGamma2_2|SRGB)|PDF(?:Context(?:BeginTag|EndTag)|TagTypeGetName))\\b)" }, { "captures": { @@ -563,7 +1118,7 @@ "name": "support.function.quartz.c" } }, - "match": "(\\s*)(\\bCG(?:A(?:cquireDisplayFadeReservation|ffineTransform(?:Concat|EqualToTransform|I(?:nvert|sIdentity)|Make(?:Rotation|Scale|Translation)?|Rotate|Scale|Translate)|ssociateMouseAndMouseCursorPosition)|B(?:eginDisplayConfiguration|itmapContext(?:Create(?:Image|WithData)?|Get(?:AlphaInfo|B(?:it(?:mapInfo|sPer(?:Component|Pixel))|ytesPerRow)|ColorSpace|Data|Height|Width)))|C(?:a(?:ncelDisplayConfiguration|ptureAllDisplays(?:WithOptions)?)|o(?:lor(?:C(?:onversionInfoGetTypeID|reate(?:Copy(?:WithAlpha)?|Generic(?:CMYK|Gray|RGB)|WithPattern)?)|EqualToColor|Get(?:Alpha|Co(?:lorSpace|mponents|nstantColor)|NumberOfComponents|Pattern|TypeID)|Re(?:lease|tain)|Space(?:C(?:opy(?:ICCProfile|Name)|reate(?:Calibrated(?:Gray|RGB)|Device(?:CMYK|Gray|RGB)|I(?:CCBased|ndexed)|Lab|Pattern|With(?:ICCProfile|Name|PlatformColorSpace)))|Get(?:BaseColorSpace|ColorTable(?:Count)?|Model|NumberOfComponents|TypeID)|Re(?:lease|tain)))|mpleteDisplayConfiguration|n(?:figureDisplay(?:FadeEffect|MirrorOfDisplay|Origin|StereoOperation|WithDisplayMode)|text(?:Add(?:Arc(?:ToPoint)?|CurveToPoint|EllipseInRect|Line(?:ToPoint|s)|Path|QuadCurveToPoint|Rect(?:s)?)|Begin(?:Pa(?:ge|th)|TransparencyLayer(?:WithRect)?)|C(?:l(?:earRect|ip(?:To(?:Mask|Rect(?:s)?))?|osePath)|o(?:n(?:catCTM|vert(?:PointTo(?:DeviceSpace|UserSpace)|RectTo(?:DeviceSpace|UserSpace)|SizeTo(?:DeviceSpace|UserSpace)))|pyPath))|Draw(?:Image|L(?:ayer(?:AtPoint|InRect)|inearGradient)|P(?:DFPage|ath)|RadialGradient|Shading|TiledImage)|E(?:O(?:Clip|FillPath)|nd(?:Page|TransparencyLayer))|F(?:ill(?:EllipseInRect|Path|Rect(?:s)?)|lush)|Get(?:C(?:TM|lipBoundingBox)|InterpolationQuality|Path(?:BoundingBox|CurrentPoint)|T(?:ext(?:Matrix|Position)|ypeID)|UserSpaceToDeviceSpaceTransform)|IsPathEmpty|MoveToPoint|PathContainsPoint|R(?:e(?:lease|placePathWithStrokedPath|storeGState|tain)|otateCTM)|S(?:aveGState|caleCTM|et(?:Al(?:lows(?:Antialiasing|FontS(?:moothing|ubpixel(?:Positioning|Quantization)))|pha)|BlendMode|C(?:MYK(?:FillColor|StrokeColor)|haracterSpacing)|F(?:ill(?:Color(?:Space|WithColor)?|Pattern)|latness|ont(?:Size)?)|Gray(?:FillColor|StrokeColor)|InterpolationQuality|Line(?:Cap|Dash|Join|Width)|MiterLimit|PatternPhase|R(?:GB(?:FillColor|StrokeColor)|enderingIntent)|S(?:h(?:adow(?:WithColor)?|ould(?:Antialias|S(?:moothFonts|ubpixel(?:PositionFonts|QuantizeFonts))))|troke(?:Color(?:Space|WithColor)?|Pattern))|Text(?:DrawingMode|Matrix|Position))|howGlyphsAtPositions|troke(?:EllipseInRect|LineSegments|Path|Rect(?:WithWidth)?)|ynchronize)|TranslateCTM))))|D(?:ata(?:Consumer(?:Create(?:With(?:CFData|URL))?|GetTypeID|Re(?:lease|tain))|Provider(?:C(?:opyData|reate(?:Direct|Sequential|With(?:CFData|Data|Filename|URL)))|GetTypeID|Re(?:lease|tain)))|isplay(?:Bounds|C(?:apture(?:WithOptions)?|opy(?:AllDisplayModes|ColorSpace|DisplayMode)|reateImage(?:ForRect)?)|Fade|G(?:ammaTableCapacity|etDrawingContext)|HideCursor|I(?:DToOpenGLDisplayMask|s(?:A(?:ctive|lwaysInMirrorSet|sleep)|Builtin|In(?:HWMirrorSet|MirrorSet)|Main|Online|Stereo))|M(?:irrorsDisplay|o(?:de(?:Get(?:Height|IO(?:DisplayModeID|Flags)|RefreshRate|TypeID|Width)|IsUsableForDesktopGUI|Re(?:lease|tain)|lNumber)|veCursorToPoint))|P(?:ixels(?:High|Wide)|rimaryDisplay)|R(?:e(?:gisterReconfigurationCallback|lease|moveReconfigurationCallback|storeColorSyncSettings)|otation)|S(?:creenSize|e(?:rialNumber|t(?:DisplayMode|StereoOperation))|howCursor)|U(?:nitNumber|sesOpenGLAcceleration)|VendorNumber))|Event(?:Create(?:Copy|Data|FromData|KeyboardEvent|MouseEvent|S(?:crollWheelEvent|ourceFromEvent))?|Get(?:DoubleValueField|Flags|IntegerValueField|Location|T(?:imestamp|ype(?:ID)?)|UnflippedLocation)|Keyboard(?:GetUnicodeString|SetUnicodeString)|Post(?:ToPSN)?|S(?:et(?:DoubleValueField|Flags|IntegerValueField|Location|Source|T(?:imestamp|ype))|ource(?:ButtonState|C(?:ounterForEventType|reate)|FlagsState|Get(?:KeyboardType|LocalEvents(?:FilterDuringSuppressionState|SuppressionInterval)|PixelsPerLine|SourceStateID|TypeID|UserData)|KeyState|Se(?:condsSinceLastEventType|t(?:KeyboardType|LocalEvents(?:FilterDuringSuppressionState|SuppressionInterval)|PixelsPerLine|UserData))))|Tap(?:Create(?:ForPSN)?|Enable|IsEnabled|PostEvent))|F(?:ont(?:C(?:anCreatePostScriptSubset|opy(?:FullName|GlyphNameForGlyph|PostScriptName|Table(?:ForTag|Tags)|Variation(?:Axes|s))|reate(?:CopyWithVariations|PostScript(?:Encoding|Subset)|With(?:DataProvider|FontName)))|Get(?:Ascent|CapHeight|Descent|FontBBox|Glyph(?:Advances|BBoxes|WithGlyphName)|ItalicAngle|Leading|NumberOfGlyphs|StemV|TypeID|UnitsPerEm|XHeight)|Re(?:lease|tain))|unction(?:Create|GetTypeID|Re(?:lease|tain)))|G(?:et(?:ActiveDisplayList|Display(?:TransferBy(?:Formula|Table)|sWith(?:OpenGLDisplayMask|Point|Rect))|EventTapList|LastMouseDelta|OnlineDisplayList)|radient(?:CreateWithColor(?:Components|s)|GetTypeID|Re(?:lease|tain)))|Image(?:Create(?:Copy(?:WithColorSpace)?|With(?:ImageInRect|JPEGDataProvider|Mask(?:ingColors)?|PNGDataProvider))?|Get(?:AlphaInfo|B(?:it(?:mapInfo|sPer(?:Component|Pixel))|ytesPerRow)|ColorSpace|D(?:ataProvider|ecode)|Height|RenderingIntent|ShouldInterpolate|TypeID|Width)|IsMask|MaskCreate|Re(?:lease|tain))|Layer(?:CreateWithContext|Get(?:Context|Size|TypeID)|Re(?:lease|tain))|MainDisplayID|OpenGLDisplayMaskToDisplayID|P(?:DF(?:ArrayGet(?:Array|Boolean|Count|Dictionary|Integer|N(?:ame|u(?:ll|mber))|Object|Str(?:eam|ing))|Conte(?:ntStream(?:CreateWith(?:Page|Stream)|Get(?:Resource|Streams)|Re(?:lease|tain))|xt(?:AddD(?:estinationAtPoint|ocumentMetadata)|BeginPage|C(?:lose|reate(?:WithURL)?)|EndPage|Set(?:DestinationForRect|URLForRect)))|D(?:ictionary(?:ApplyFunction|Get(?:Array|Boolean|Count|Dictionary|Integer|N(?:ame|umber)|Object|Str(?:eam|ing)))|ocument(?:Allows(?:Copying|Printing)|CreateWith(?:Provider|URL)|Get(?:Catalog|I(?:D|nfo)|NumberOfPages|Page|TypeID|Version)|Is(?:Encrypted|Unlocked)|Re(?:lease|tain)|UnlockWithPassword))|O(?:bjectGet(?:Type|Value)|peratorTable(?:Create|Re(?:lease|tain)|SetCallback))|Page(?:Get(?:BoxRect|D(?:ictionary|ocument|rawingTransform)|PageNumber|RotationAngle|TypeID)|Re(?:lease|tain))|S(?:canner(?:Create|GetContentStream|Pop(?:Array|Boolean|Dictionary|Integer|N(?:ame|umber)|Object|Str(?:eam|ing))|Re(?:lease|tain)|Scan)|tr(?:eam(?:CopyData|GetDictionary)|ing(?:Copy(?:Date|TextString)|Get(?:BytePtr|Length)))))|SConverter(?:Abort|C(?:onvert|reate)|GetTypeID|IsConverting)|at(?:h(?:A(?:dd(?:Arc(?:ToPoint)?|CurveToPoint|EllipseInRect|Line(?:ToPoint|s)|Path|QuadCurveToPoint|Re(?:ct(?:s)?|lativeArc))|pply)|C(?:loseSubpath|ontainsPoint|reate(?:Copy(?:By(?:DashingPath|StrokingPath|TransformingPath))?|Mutable(?:Copy(?:ByTransformingPath)?)?|With(?:EllipseInRect|Rect)))|EqualToPath|Get(?:BoundingBox|CurrentPoint|PathBoundingBox|TypeID)|Is(?:Empty|Rect)|MoveToPoint|Re(?:lease|tain))|tern(?:Create|GetTypeID|Re(?:lease|tain)))|oint(?:ApplyAffineTransform|CreateDictionaryRepresentation|EqualToPoint|Make(?:WithDictionaryRepresentation)?))|Re(?:ct(?:ApplyAffineTransform|C(?:ontains(?:Point|Rect)|reateDictionaryRepresentation)|Divide|EqualToRect|Get(?:Height|M(?:ax(?:X|Y)|i(?:d(?:X|Y)|n(?:X|Y)))|Width)|I(?:n(?:set|te(?:gral|rsect(?:ion|sRect)))|s(?:Empty|Infinite|Null))|Make(?:WithDictionaryRepresentation)?|Offset|Standardize|Union)|lease(?:AllDisplays|DisplayFadeReservation)|storePermanentDisplayConfiguration)|S(?:e(?:ssionCopyCurrentDictionary|tDisplayTransferBy(?:ByteTable|Formula|Table))|h(?:ading(?:Create(?:Axial|Radial)|GetTypeID|Re(?:lease|tain))|ieldingWindow(?:ID|Level))|ize(?:ApplyAffineTransform|CreateDictionaryRepresentation|EqualToSize|Make(?:WithDictionaryRepresentation)?))|VectorMake|W(?:arpMouseCursorPosition|indowL(?:evelForKey|istC(?:opyWindowInfo|reate(?:DescriptionFromArray|Image(?:FromArray)?)?))))\\b)" + "match": "(\\s*)(\\bCG(?:A(?:cquireDisplayFadeReservation|ffineTransform(?:Concat|EqualToTransform|I(?:nvert|sIdentity)|Make(?:Rotation|Scale|Translation)?|Rotate|Scale|Translate)|ssociateMouseAndMouseCursorPosition)|B(?:eginDisplayConfiguration|itmapContext(?:Create(?:Image|WithData)?|Get(?:AlphaInfo|B(?:it(?:mapInfo|sPer(?:Component|Pixel))|ytesPerRow)|ColorSpace|Data|Height|Width)))|C(?:a(?:ncelDisplayConfiguration|ptureAllDisplays(?:WithOptions)?)|o(?:lor(?:C(?:onversionInfoGetTypeID|reate(?:Copy(?:WithAlpha)?|Generic(?:CMYK|Gray|RGB)|WithPattern)?)|EqualToColor|Get(?:Alpha|Co(?:lorSpace|mponents|nstantColor)|NumberOfComponents|Pattern|TypeID)|Re(?:lease|tain)|Space(?:C(?:opyName|reate(?:Calibrated(?:Gray|RGB)|Device(?:CMYK|Gray|RGB)|I(?:CCBased|ndexed)|Lab|Pattern|With(?:Name|PlatformColorSpace)))|Get(?:BaseColorSpace|ColorTable(?:Count)?|Model|NumberOfComponents|TypeID)|Re(?:lease|tain)))|mpleteDisplayConfiguration|n(?:figureDisplay(?:FadeEffect|MirrorOfDisplay|Origin|StereoOperation|WithDisplayMode)|text(?:Add(?:Arc(?:ToPoint)?|CurveToPoint|EllipseInRect|Line(?:ToPoint|s)|Path|QuadCurveToPoint|Rect(?:s)?)|Begin(?:Pa(?:ge|th)|TransparencyLayer(?:WithRect)?)|C(?:l(?:earRect|ip(?:To(?:Mask|Rect(?:s)?))?|osePath)|o(?:n(?:catCTM|vert(?:PointTo(?:DeviceSpace|UserSpace)|RectTo(?:DeviceSpace|UserSpace)|SizeTo(?:DeviceSpace|UserSpace)))|pyPath))|Draw(?:Image|L(?:ayer(?:AtPoint|InRect)|inearGradient)|P(?:DFPage|ath)|RadialGradient|Shading|TiledImage)|E(?:O(?:Clip|FillPath)|nd(?:Page|TransparencyLayer))|F(?:ill(?:EllipseInRect|Path|Rect(?:s)?)|lush)|Get(?:C(?:TM|lipBoundingBox)|InterpolationQuality|Path(?:BoundingBox|CurrentPoint)|T(?:ext(?:Matrix|Position)|ypeID)|UserSpaceToDeviceSpaceTransform)|IsPathEmpty|MoveToPoint|PathContainsPoint|R(?:e(?:lease|placePathWithStrokedPath|s(?:etClip|toreGState)|tain)|otateCTM)|S(?:aveGState|caleCTM|et(?:Al(?:lows(?:Antialiasing|FontS(?:moothing|ubpixel(?:Positioning|Quantization)))|pha)|BlendMode|C(?:MYK(?:FillColor|StrokeColor)|haracterSpacing)|F(?:ill(?:Color(?:Space|WithColor)?|Pattern)|latness|ont(?:Size)?)|Gray(?:FillColor|StrokeColor)|InterpolationQuality|Line(?:Cap|Dash|Join|Width)|MiterLimit|PatternPhase|R(?:GB(?:FillColor|StrokeColor)|enderingIntent)|S(?:h(?:adow(?:WithColor)?|ould(?:Antialias|S(?:moothFonts|ubpixel(?:PositionFonts|QuantizeFonts))))|troke(?:Color(?:Space|WithColor)?|Pattern))|Text(?:DrawingMode|Matrix|Position))|howGlyphsAtPositions|troke(?:EllipseInRect|LineSegments|Path|Rect(?:WithWidth)?)|ynchronize)|TranslateCTM))))|D(?:ata(?:Consumer(?:Create(?:With(?:CFData|URL))?|GetTypeID|Re(?:lease|tain))|Provider(?:C(?:opyData|reate(?:Direct|Sequential|With(?:CFData|Data|Filename|URL)))|GetTypeID|Re(?:lease|tain)))|isplay(?:Bounds|C(?:apture(?:WithOptions)?|opy(?:AllDisplayModes|ColorSpace|DisplayMode)|reateImage(?:ForRect)?)|Fade|G(?:ammaTableCapacity|etDrawingContext)|HideCursor|I(?:DToOpenGLDisplayMask|s(?:A(?:ctive|lwaysInMirrorSet|sleep)|Builtin|In(?:HWMirrorSet|MirrorSet)|Main|Online|Stereo))|M(?:irrorsDisplay|o(?:de(?:Get(?:Height|IO(?:DisplayModeID|Flags)|RefreshRate|TypeID|Width)|IsUsableForDesktopGUI|Re(?:lease|tain)|lNumber)|veCursorToPoint))|P(?:ixels(?:High|Wide)|rimaryDisplay)|R(?:e(?:gisterReconfigurationCallback|lease|moveReconfigurationCallback|storeColorSyncSettings)|otation)|S(?:creenSize|e(?:rialNumber|t(?:DisplayMode|StereoOperation))|howCursor)|U(?:nitNumber|sesOpenGLAcceleration)|VendorNumber))|Event(?:Create(?:Copy|Data|FromData|KeyboardEvent|MouseEvent|S(?:crollWheelEvent|ourceFromEvent))?|Get(?:DoubleValueField|Flags|IntegerValueField|Location|T(?:imestamp|ype(?:ID)?)|UnflippedLocation)|Keyboard(?:GetUnicodeString|SetUnicodeString)|Post(?:ToPSN)?|S(?:et(?:DoubleValueField|Flags|IntegerValueField|Location|Source|T(?:imestamp|ype))|ource(?:ButtonState|C(?:ounterForEventType|reate)|FlagsState|Get(?:KeyboardType|LocalEvents(?:FilterDuringSuppressionState|SuppressionInterval)|PixelsPerLine|SourceStateID|TypeID|UserData)|KeyState|Se(?:condsSinceLastEventType|t(?:KeyboardType|LocalEvents(?:FilterDuringSuppressionState|SuppressionInterval)|PixelsPerLine|UserData))))|Tap(?:Create(?:ForPSN)?|Enable|IsEnabled|PostEvent))|F(?:ont(?:C(?:anCreatePostScriptSubset|opy(?:FullName|GlyphNameForGlyph|PostScriptName|Table(?:ForTag|Tags)|Variation(?:Axes|s))|reate(?:CopyWithVariations|PostScript(?:Encoding|Subset)|With(?:DataProvider|FontName)))|Get(?:Ascent|CapHeight|Descent|FontBBox|Glyph(?:Advances|BBoxes|WithGlyphName)|ItalicAngle|Leading|NumberOfGlyphs|StemV|TypeID|UnitsPerEm|XHeight)|Re(?:lease|tain))|unction(?:Create|GetTypeID|Re(?:lease|tain)))|G(?:et(?:ActiveDisplayList|Display(?:TransferBy(?:Formula|Table)|sWith(?:OpenGLDisplayMask|Point|Rect))|EventTapList|LastMouseDelta|OnlineDisplayList)|radient(?:CreateWithColor(?:Components|s)|GetTypeID|Re(?:lease|tain)))|Image(?:Create(?:Copy(?:WithColorSpace)?|With(?:ImageInRect|JPEGDataProvider|Mask(?:ingColors)?|PNGDataProvider))?|Get(?:AlphaInfo|B(?:it(?:mapInfo|sPer(?:Component|Pixel))|ytesPerRow)|ColorSpace|D(?:ataProvider|ecode)|Height|RenderingIntent|ShouldInterpolate|TypeID|Width)|IsMask|MaskCreate|Re(?:lease|tain))|Layer(?:CreateWithContext|Get(?:Context|Size|TypeID)|Re(?:lease|tain))|MainDisplayID|OpenGLDisplayMaskToDisplayID|P(?:DF(?:ArrayGet(?:Array|Boolean|Count|Dictionary|Integer|N(?:ame|u(?:ll|mber))|Object|Str(?:eam|ing))|Conte(?:ntStream(?:CreateWith(?:Page|Stream)|Get(?:Resource|Streams)|Re(?:lease|tain))|xt(?:AddD(?:estinationAtPoint|ocumentMetadata)|BeginPage|C(?:lose|reate(?:WithURL)?)|EndPage|Set(?:DestinationForRect|URLForRect)))|D(?:ictionary(?:ApplyFunction|Get(?:Array|Boolean|Count|Dictionary|Integer|N(?:ame|umber)|Object|Str(?:eam|ing)))|ocument(?:Allows(?:Copying|Printing)|CreateWith(?:Provider|URL)|Get(?:Catalog|I(?:D|nfo)|NumberOfPages|Page|TypeID|Version)|Is(?:Encrypted|Unlocked)|Re(?:lease|tain)|UnlockWithPassword))|O(?:bjectGet(?:Type|Value)|peratorTable(?:Create|Re(?:lease|tain)|SetCallback))|Page(?:Get(?:BoxRect|D(?:ictionary|ocument|rawingTransform)|PageNumber|RotationAngle|TypeID)|Re(?:lease|tain))|S(?:canner(?:Create|GetContentStream|Pop(?:Array|Boolean|Dictionary|Integer|N(?:ame|umber)|Object|Str(?:eam|ing))|Re(?:lease|tain)|Scan)|tr(?:eam(?:CopyData|GetDictionary)|ing(?:Copy(?:Date|TextString)|Get(?:BytePtr|Length)))))|SConverter(?:Abort|C(?:onvert|reate)|GetTypeID|IsConverting)|at(?:h(?:A(?:dd(?:Arc(?:ToPoint)?|CurveToPoint|EllipseInRect|Line(?:ToPoint|s)|Path|QuadCurveToPoint|Re(?:ct(?:s)?|lativeArc))|pply)|C(?:loseSubpath|ontainsPoint|reate(?:Copy(?:By(?:DashingPath|StrokingPath|TransformingPath))?|Mutable(?:Copy(?:ByTransformingPath)?)?|With(?:EllipseInRect|Rect)))|EqualToPath|Get(?:BoundingBox|CurrentPoint|PathBoundingBox|TypeID)|Is(?:Empty|Rect)|MoveToPoint|Re(?:lease|tain))|tern(?:Create|GetTypeID|Re(?:lease|tain)))|oint(?:ApplyAffineTransform|CreateDictionaryRepresentation|EqualToPoint|Make(?:WithDictionaryRepresentation)?))|Re(?:ct(?:ApplyAffineTransform|C(?:ontains(?:Point|Rect)|reateDictionaryRepresentation)|Divide|EqualToRect|Get(?:Height|M(?:ax(?:X|Y)|i(?:d(?:X|Y)|n(?:X|Y)))|Width)|I(?:n(?:set|te(?:gral|rsect(?:ion|sRect)))|s(?:Empty|Infinite|Null))|Make(?:WithDictionaryRepresentation)?|Offset|Standardize|Union)|lease(?:AllDisplays|DisplayFadeReservation)|storePermanentDisplayConfiguration)|S(?:e(?:ssionCopyCurrentDictionary|tDisplayTransferBy(?:ByteTable|Formula|Table))|h(?:ading(?:Create(?:Axial|Radial)|GetTypeID|Re(?:lease|tain))|ieldingWindow(?:ID|Level))|ize(?:ApplyAffineTransform|CreateDictionaryRepresentation|EqualToSize|Make(?:WithDictionaryRepresentation)?))|VectorMake|W(?:arpMouseCursorPosition|indowL(?:evelForKey|istC(?:opyWindowInfo|reate(?:DescriptionFromArray|Image(?:FromArray)?)?))))\\b)" } ] } diff --git a/extensions/cpp/test/colorize-results/test-78769_cpp.json b/extensions/cpp/test/colorize-results/test-78769_cpp.json index eb114a4f007..82846880603 100644 --- a/extensions/cpp/test/colorize-results/test-78769_cpp.json +++ b/extensions/cpp/test/colorize-results/test-78769_cpp.json @@ -191,7 +191,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -257,7 +257,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -389,7 +389,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -433,7 +433,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -532,7 +532,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -587,7 +587,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -719,7 +719,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -763,7 +763,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -862,7 +862,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -906,7 +906,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -1027,7 +1027,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -1071,7 +1071,7 @@ "t": "source.cpp meta.preprocessor.macro.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "meta.preprocessor: #569CD6", "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6" @@ -1187,4 +1187,4 @@ "hc_black": "meta.preprocessor: #569CD6" } } -] +] \ No newline at end of file diff --git a/extensions/cpp/test/colorize-results/test_cpp.json b/extensions/cpp/test/colorize-results/test_cpp.json index 108895f8f65..10f6041bb00 100644 --- a/extensions/cpp/test/colorize-results/test_cpp.json +++ b/extensions/cpp/test/colorize-results/test_cpp.json @@ -2314,7 +2314,7 @@ "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp constant.character.escape.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" @@ -2430,4 +2430,4 @@ "hc_black": "default: #FFFFFF" } } -] +] \ No newline at end of file diff --git a/extensions/css-language-features/.vscodeignore b/extensions/css-language-features/.vscodeignore index fa38a471362..a08d9b8dec7 100644 --- a/extensions/css-language-features/.vscodeignore +++ b/extensions/css-language-features/.vscodeignore @@ -16,4 +16,6 @@ server/.npmignore yarn.lock server/extension.webpack.config.js extension.webpack.config.js -CONTRIBUTING.md \ No newline at end of file +server/extension-browser.webpack.config.js +extension-browser.webpack.config.js +CONTRIBUTING.md diff --git a/extensions/css-language-features/client/src/browser/cssClientMain.ts b/extensions/css-language-features/client/src/browser/cssClientMain.ts new file mode 100644 index 00000000000..8b1d7205fcd --- /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 } 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 = context.asAbsolutePath('server/dist/browser/cssServerMain.js'); + try { + const worker = new Worker(serverMain); + const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => { + return new LanguageClient(id, name, clientOptions, worker); + }; + + startClient(context, newLanguageClient, { TextDecoder }); + + } catch (e) { + console.log(e); + } +} diff --git a/extensions/css-language-features/client/src/cssMain.ts b/extensions/css-language-features/client/src/cssClient.ts similarity index 76% rename from extensions/css-language-features/client/src/cssMain.ts rename to extensions/css-language-features/client/src/cssClient.ts index beabfc68cad..4bfa95c8439 100644 --- a/extensions/css-language-features/client/src/cssMain.ts +++ b/extensions/css-language-features/client/src/cssClient.ts @@ -3,38 +3,31 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as path from 'path'; -import { commands, CompletionItem, CompletionItemKind, ExtensionContext, languages, Position, Range, SnippetString, TextEdit, window, workspace, TextDocument, CompletionContext, CancellationToken, ProviderResult, CompletionList } from 'vscode'; -import { Disposable, LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, ProvideCompletionItemsSignature } from 'vscode-languageclient'; +import { commands, CompletionItem, CompletionItemKind, ExtensionContext, languages, Position, Range, SnippetString, TextEdit, window, TextDocument, CompletionContext, CancellationToken, ProviderResult, CompletionList } from 'vscode'; +import { Disposable, LanguageClientOptions, ProvideCompletionItemsSignature, NotificationType, CommonLanguageClient } from 'vscode-languageclient'; import * as nls from 'vscode-nls'; -import { getCustomDataPathsFromAllExtensions, getCustomDataPathsInAllWorkspaces } from './customData'; +import { getCustomDataSource } from './customData'; +import { RequestService, serveFileSystemRequests } from './requests'; + +namespace CustomDataChangedNotification { + export const type: NotificationType = new NotificationType('css/customDataChanged'); +} const localize = nls.loadMessageBundle(); -// this method is called when vs code is activated -export function activate(context: ExtensionContext) { +export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => CommonLanguageClient; - let serverMain = readJSONFile(context.asAbsolutePath('./server/package.json')).main; - let serverModule = context.asAbsolutePath(path.join('server', serverMain)); +export interface Runtime { + TextDecoder: { new(encoding?: string): { decode(buffer: ArrayBuffer): string; } }; + fs?: RequestService; +} - // The debug options for the server - let debugOptions = { execArgv: ['--nolazy', '--inspect=6044'] }; +export function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime) { - // If the extension is launch in debug mode the debug server options are use - // Otherwise the run options are used - let serverOptions: ServerOptions = { - run: { module: serverModule, transport: TransportKind.ipc }, - debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions } - }; + const customDataSource = getCustomDataSource(context.subscriptions); let documentSelector = ['css', 'scss', 'less']; - let dataPaths = [ - ...getCustomDataPathsInAllWorkspaces(workspace.workspaceFolders), - ...getCustomDataPathsFromAllExtensions() - ]; - // Options to control the language client let clientOptions: LanguageClientOptions = { documentSelector, @@ -42,7 +35,7 @@ export function activate(context: ExtensionContext) { configurationSection: ['css', 'scss', 'less'] }, initializationOptions: { - dataPaths + handledSchemas: ['file'] }, middleware: { provideCompletionItem(document: TextDocument, position: Position, context: CompletionContext, token: CancellationToken, next: ProvideCompletionItemsSignature): ProviderResult { @@ -82,8 +75,17 @@ export function activate(context: ExtensionContext) { }; // Create the language client and start the client. - let client = new LanguageClient('css', localize('cssserver.name', 'CSS Language Server'), serverOptions, clientOptions); + let client = newLanguageClient('css', localize('cssserver.name', 'CSS Language Server'), clientOptions); client.registerProposedFeatures(); + client.onReady().then(() => { + + client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris); + customDataSource.onDidChange(() => { + client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris); + }); + + serveFileSystemRequests(client, runtime); + }); let disposable = client.start(); // Push the disposable to the context's subscriptions so that the @@ -118,7 +120,7 @@ export function activate(context: ExtensionContext) { const regionCompletionRegExpr = /^(\s*)(\/(\*\s*(#\w*)?)?)?$/; return languages.registerCompletionItemProvider(documentSelector, { - provideCompletionItems(doc, pos) { + provideCompletionItems(doc: TextDocument, pos: Position) { let lineUntilPos = doc.getText(new Range(new Position(pos.line, 0), pos)); let match = lineUntilPos.match(regionCompletionRegExpr); if (match) { @@ -162,13 +164,3 @@ export function activate(context: ExtensionContext) { } } } - -function readJSONFile(location: string) { - try { - return JSON.parse(fs.readFileSync(location).toString()); - } catch (e) { - console.log(`Problems reading ${location}: ${e}`); - return {}; - } -} - diff --git a/extensions/css-language-features/client/src/customData.ts b/extensions/css-language-features/client/src/customData.ts index 92d9030a343..24b56f12b06 100644 --- a/extensions/css-language-features/client/src/customData.ts +++ b/extensions/css-language-features/client/src/customData.ts @@ -3,54 +3,86 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'path'; -import { workspace, WorkspaceFolder, extensions } from 'vscode'; +import { workspace, extensions, Uri, EventEmitter, Disposable } from 'vscode'; +import { resolvePath, joinPath } from './requests'; -interface ExperimentalConfig { - customData?: string[]; - experimental?: { - customData?: string[]; +export function getCustomDataSource(toDispose: Disposable[]) { + let pathsInWorkspace = getCustomDataPathsInAllWorkspaces(); + let pathsInExtensions = getCustomDataPathsFromAllExtensions(); + + const onChange = new EventEmitter(); + + toDispose.push(extensions.onDidChange(_ => { + const newPathsInExtensions = getCustomDataPathsFromAllExtensions(); + if (newPathsInExtensions.length !== pathsInExtensions.length || !newPathsInExtensions.every((val, idx) => val === pathsInExtensions[idx])) { + pathsInExtensions = newPathsInExtensions; + onChange.fire(); + } + })); + toDispose.push(workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('css.customData')) { + pathsInWorkspace = getCustomDataPathsInAllWorkspaces(); + onChange.fire(); + } + })); + + return { + get uris() { + return pathsInWorkspace.concat(pathsInExtensions); + }, + get onDidChange() { + return onChange.event; + } }; } -export function getCustomDataPathsInAllWorkspaces(workspaceFolders: readonly WorkspaceFolder[] | undefined): string[] { + +function getCustomDataPathsInAllWorkspaces(): string[] { + const workspaceFolders = workspace.workspaceFolders; + const dataPaths: string[] = []; if (!workspaceFolders) { return dataPaths; } - workspaceFolders.forEach(wf => { - const allCssConfig = workspace.getConfiguration(undefined, wf.uri); - const wfCSSConfig = allCssConfig.inspect('css'); - if (wfCSSConfig && wfCSSConfig.workspaceFolderValue && wfCSSConfig.workspaceFolderValue.customData) { - const customData = wfCSSConfig.workspaceFolderValue.customData; - if (Array.isArray(customData)) { - customData.forEach(t => { - if (typeof t === 'string') { - dataPaths.push(path.resolve(wf.uri.fsPath, t)); - } - }); + const collect = (paths: string[] | undefined, rootFolder: Uri) => { + if (Array.isArray(paths)) { + for (const path of paths) { + if (typeof path === 'string') { + dataPaths.push(resolvePath(rootFolder, path).toString()); + } } } - }); + }; + for (let i = 0; i < workspaceFolders.length; i++) { + const folderUri = workspaceFolders[i].uri; + const allCssConfig = workspace.getConfiguration('css', folderUri); + const customDataInspect = allCssConfig.inspect('customData'); + if (customDataInspect) { + collect(customDataInspect.workspaceFolderValue, folderUri); + if (i === 0) { + if (workspace.workspaceFile) { + collect(customDataInspect.workspaceValue, workspace.workspaceFile); + } + collect(customDataInspect.globalValue, folderUri); + } + } + + } return dataPaths; } -export function getCustomDataPathsFromAllExtensions(): string[] { +function getCustomDataPathsFromAllExtensions(): string[] { const dataPaths: string[] = []; - for (const extension of extensions.all) { - const contributes = extension.packageJSON && extension.packageJSON.contributes; - - if (contributes && contributes.css && contributes.css.customData && Array.isArray(contributes.css.customData)) { - const relativePaths: string[] = contributes.css.customData; - relativePaths.forEach(rp => { - dataPaths.push(path.resolve(extension.extensionPath, rp)); - }); + const customData = extension.packageJSON?.contributes?.css?.customData; + if (Array.isArray(customData)) { + for (const rp of customData) { + dataPaths.push(joinPath(extension.extensionUri, rp).toString()); + } } } - return dataPaths; } diff --git a/extensions/css-language-features/client/src/node/cssClientMain.ts b/extensions/css-language-features/client/src/node/cssClientMain.ts new file mode 100644 index 00000000000..cc675b494c2 --- /dev/null +++ b/extensions/css-language-features/client/src/node/cssClientMain.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { getNodeFSRequestService } from './nodeFs'; +import { ExtensionContext, extensions } from 'vscode'; +import { startClient, LanguageClientConstructor } from '../cssClient'; +import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient } from 'vscode-languageclient/node'; +import { TextDecoder } from 'util'; + +// this method is called when vs code is activated +export function activate(context: ExtensionContext) { + + const clientMain = extensions.getExtension('vscode.css-language-features')?.packageJSON?.main || ''; + + const serverMain = `./server/${clientMain.indexOf('/dist/') !== -1 ? 'dist' : 'out'}/node/cssServerMain`; + const serverModule = context.asAbsolutePath(serverMain); + + // The debug options for the server + const debugOptions = { execArgv: ['--nolazy', '--inspect=6044'] }; + + // If the extension is launch in debug mode the debug server options are use + // Otherwise the run options are used + const serverOptions: ServerOptions = { + run: { module: serverModule, transport: TransportKind.ipc }, + debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions } + }; + + const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => { + return new LanguageClient(id, name, serverOptions, clientOptions); + }; + + startClient(context, newLanguageClient, { fs: getNodeFSRequestService(), TextDecoder }); +} diff --git a/extensions/css-language-features/client/src/node/nodeFs.ts b/extensions/css-language-features/client/src/node/nodeFs.ts new file mode 100644 index 00000000000..c13ef2e1c08 --- /dev/null +++ b/extensions/css-language-features/client/src/node/nodeFs.ts @@ -0,0 +1,85 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as fs from 'fs'; +import { Uri } from 'vscode'; +import { getScheme, RequestService, FileType } from '../requests'; + +export function getNodeFSRequestService(): RequestService { + function ensureFileUri(location: string) { + if (getScheme(location) !== 'file') { + throw new Error('fileRequestService can only handle file URLs'); + } + } + return { + getContent(location: string, encoding?: string) { + ensureFileUri(location); + return new Promise((c, e) => { + const uri = Uri.parse(location); + fs.readFile(uri.fsPath, encoding, (err, buf) => { + if (err) { + return e(err); + } + c(buf.toString()); + + }); + }); + }, + stat(location: string) { + ensureFileUri(location); + return new Promise((c, e) => { + const uri = Uri.parse(location); + fs.stat(uri.fsPath, (err, stats) => { + if (err) { + if (err.code === 'ENOENT') { + return c({ type: FileType.Unknown, ctime: -1, mtime: -1, size: -1 }); + } else { + return e(err); + } + } + + let type = FileType.Unknown; + if (stats.isFile()) { + type = FileType.File; + } else if (stats.isDirectory()) { + type = FileType.Directory; + } else if (stats.isSymbolicLink()) { + type = FileType.SymbolicLink; + } + + c({ + type, + ctime: stats.ctime.getTime(), + mtime: stats.mtime.getTime(), + size: stats.size + }); + }); + }); + }, + readDirectory(location: string) { + ensureFileUri(location); + return new Promise((c, e) => { + const path = Uri.parse(location).fsPath; + + fs.readdir(path, { withFileTypes: true }, (err, children) => { + if (err) { + return e(err); + } + c(children.map(stat => { + if (stat.isSymbolicLink()) { + return [stat.name, FileType.SymbolicLink]; + } else if (stat.isDirectory()) { + return [stat.name, FileType.Directory]; + } else if (stat.isFile()) { + return [stat.name, FileType.File]; + } else { + return [stat.name, FileType.Unknown]; + } + })); + }); + }); + } + }; +} diff --git a/extensions/css-language-features/client/src/requests.ts b/extensions/css-language-features/client/src/requests.ts new file mode 100644 index 00000000000..1b1e70b2d88 --- /dev/null +++ b/extensions/css-language-features/client/src/requests.ts @@ -0,0 +1,148 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Uri, workspace } from 'vscode'; +import { RequestType, CommonLanguageClient } from 'vscode-languageclient'; +import { Runtime } from './cssClient'; + +export namespace FsContentRequest { + export const type: RequestType<{ uri: string; encoding?: string; }, string, any, any> = new RequestType('fs/content'); +} +export namespace FsStatRequest { + export const type: RequestType = new RequestType('fs/stat'); +} + +export namespace FsReadDirRequest { + export const type: RequestType = new RequestType('fs/readDir'); +} + +export function serveFileSystemRequests(client: CommonLanguageClient, runtime: Runtime) { + client.onRequest(FsContentRequest.type, (param: { uri: string; encoding?: string; }) => { + const uri = Uri.parse(param.uri); + if (uri.scheme === 'file' && runtime.fs) { + return runtime.fs.getContent(param.uri); + } + return workspace.fs.readFile(uri).then(buffer => { + return new runtime.TextDecoder(param.encoding).decode(buffer); + }); + }); + client.onRequest(FsReadDirRequest.type, (uriString: string) => { + const uri = Uri.parse(uriString); + if (uri.scheme === 'file' && runtime.fs) { + return runtime.fs.readDirectory(uriString); + } + return workspace.fs.readDirectory(uri); + }); + client.onRequest(FsStatRequest.type, (uriString: string) => { + const uri = Uri.parse(uriString); + if (uri.scheme === 'file' && runtime.fs) { + return runtime.fs.stat(uriString); + } + return workspace.fs.stat(uri); + }); +} + +export enum FileType { + /** + * The file type is unknown. + */ + Unknown = 0, + /** + * A regular file. + */ + File = 1, + /** + * A directory. + */ + Directory = 2, + /** + * A symbolic link to a file. + */ + SymbolicLink = 64 +} +export interface FileStat { + /** + * The type of the file, e.g. is a regular file, a directory, or symbolic link + * to a file. + */ + type: FileType; + /** + * The creation timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC. + */ + ctime: number; + /** + * The modification timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC. + */ + mtime: number; + /** + * The size in bytes. + */ + size: number; +} + +export interface RequestService { + getContent(uri: string, encoding?: string): Promise; + + stat(uri: string): Promise; + readDirectory(uri: string): Promise<[string, FileType][]>; +} + +export function getScheme(uri: string) { + return uri.substr(0, uri.indexOf(':')); +} + +export function dirname(uri: string) { + const lastIndexOfSlash = uri.lastIndexOf('/'); + return lastIndexOfSlash !== -1 ? uri.substr(0, lastIndexOfSlash) : ''; +} + +export function basename(uri: string) { + const lastIndexOfSlash = uri.lastIndexOf('/'); + return uri.substr(lastIndexOfSlash + 1); +} + +const Slash = '/'.charCodeAt(0); +const Dot = '.'.charCodeAt(0); + +export function isAbsolutePath(path: string) { + return path.charCodeAt(0) === Slash; +} + +export function resolvePath(uri: Uri, path: string): Uri { + if (isAbsolutePath(path)) { + return uri.with({ path: normalizePath(path.split('/')) }); + } + return joinPath(uri, path); +} + +export function normalizePath(parts: string[]): string { + const newParts: string[] = []; + for (const part of parts) { + if (part.length === 0 || part.length === 1 && part.charCodeAt(0) === Dot) { + // ignore + } else if (part.length === 2 && part.charCodeAt(0) === Dot && part.charCodeAt(1) === Dot) { + newParts.pop(); + } else { + newParts.push(part); + } + } + if (parts.length > 1 && parts[parts.length - 1].length === 0) { + newParts.push(''); + } + let res = newParts.join('/'); + if (parts[0].length === 0) { + res = '/' + res; + } + return res; +} + + +export function joinPath(uri: Uri, ...paths: string[]): Uri { + const parts = uri.path.split('/'); + for (let path of paths) { + parts.push(...path.split('/')); + } + return uri.with({ path: normalizePath(parts) }); +} diff --git a/extensions/css-language-features/extension-browser.webpack.config.js b/extensions/css-language-features/extension-browser.webpack.config.js new file mode 100644 index 00000000000..cb2e13c7ed3 --- /dev/null +++ b/extensions/css-language-features/extension-browser.webpack.config.js @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withBrowserDefaults = require('../shared.webpack.config').browser; +const path = require('path'); + +module.exports = withBrowserDefaults({ + context: path.join(__dirname, 'client'), + entry: { + extension: './src/browser/cssClientMain.ts' + }, + output: { + filename: 'cssClientMain.js', + path: path.join(__dirname, 'client', 'dist', 'browser') + } +}); diff --git a/extensions/css-language-features/extension.webpack.config.js b/extensions/css-language-features/extension.webpack.config.js index dec7ad5afb4..a931210ab32 100644 --- a/extensions/css-language-features/extension.webpack.config.js +++ b/extensions/css-language-features/extension.webpack.config.js @@ -13,10 +13,10 @@ const path = require('path'); module.exports = withDefaults({ context: path.join(__dirname, 'client'), entry: { - extension: './src/cssMain.ts', + extension: './src/node/cssClientMain.ts', }, output: { - filename: 'cssMain.js', - path: path.join(__dirname, 'client', 'dist') + filename: 'cssClientMain.js', + path: path.join(__dirname, 'client', 'dist', 'node') } }); diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index 4c737601679..0b632903b52 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", @@ -806,7 +807,7 @@ ] }, "dependencies": { - "vscode-languageclient": "^6.1.3", + "vscode-languageclient": "7.0.0-next.5.1", "vscode-nls": "^4.1.2" }, "devDependencies": { diff --git a/extensions/css-language-features/server/extension-browser.webpack.config.js b/extensions/css-language-features/server/extension-browser.webpack.config.js new file mode 100644 index 00000000000..38816259ddf --- /dev/null +++ b/extensions/css-language-features/server/extension-browser.webpack.config.js @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withBrowserDefaults = require('../../shared.webpack.config').browser; +const path = require('path'); + +module.exports = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/browser/cssServerMain.ts', + }, + output: { + filename: 'cssServerMain.js', + path: path.join(__dirname, 'dist', 'browser'), + libraryTarget: 'var' + } +}); diff --git a/extensions/css-language-features/server/extension.webpack.config.js b/extensions/css-language-features/server/extension.webpack.config.js index 68b850b3773..531035f636c 100644 --- a/extensions/css-language-features/server/extension.webpack.config.js +++ b/extensions/css-language-features/server/extension.webpack.config.js @@ -13,10 +13,10 @@ const path = require('path'); module.exports = withDefaults({ context: path.join(__dirname), entry: { - extension: './src/cssServerMain.ts', + extension: './src/node/cssServerMain.ts', }, output: { filename: 'cssServerMain.js', - path: path.join(__dirname, 'dist') + path: path.join(__dirname, 'dist', 'node'), } }); diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index c142ff1e620..ccbdba90a29 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -7,10 +7,12 @@ "engines": { "node": "*" }, - "main": "./out/cssServerMain", + "main": "./out/node/cssServerMain", + "browser": "./dist/browser/cssServerMain", "dependencies": { - "vscode-css-languageservice": "^4.1.2", - "vscode-languageserver": "^6.1.1" + "vscode-css-languageservice": "^4.3.1", + "vscode-languageserver": "7.0.0-next.3", + "vscode-uri": "^2.1.2" }, "devDependencies": { "@types/mocha": "7.0.2", @@ -27,6 +29,6 @@ "install-service-local": "npm install ../../../../vscode-css-languageservice -f", "install-server-next": "yarn add vscode-languageserver@next", "install-server-local": "npm install ../../../../vscode-languageserver-node/server -f", - "test": "../../../node_modules/.bin/mocha" + "test": "node ./test/index.js" } } diff --git a/extensions/css-language-features/server/src/browser/cssServerMain.ts b/extensions/css-language-features/server/src/browser/cssServerMain.ts new file mode 100644 index 00000000000..13284fadcd9 --- /dev/null +++ b/extensions/css-language-features/server/src/browser/cssServerMain.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createConnection, BrowserMessageReader, BrowserMessageWriter } from 'vscode-languageserver/browser'; +import { startServer } from '../cssServer'; + +declare let self: any; + +const messageReader = new BrowserMessageReader(self); +const messageWriter = new BrowserMessageWriter(self); + +const connection = createConnection(messageReader, messageWriter); + +startServer(connection, {}); diff --git a/extensions/css-language-features/server/src/cssServer.ts b/extensions/css-language-features/server/src/cssServer.ts new file mode 100644 index 00000000000..c2666a46c50 --- /dev/null +++ b/extensions/css-language-features/server/src/cssServer.ts @@ -0,0 +1,373 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { + Connection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder, TextDocumentSyncKind, NotificationType +} from 'vscode-languageserver'; +import { URI } from 'vscode-uri'; +import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet, TextDocument, Position } from 'vscode-css-languageservice'; +import { getLanguageModelCache } from './languageModelCache'; +import { formatError, runSafeAsync } from './utils/runner'; +import { getDocumentContext } from './utils/documentContext'; +import { fetchDataProviders } from './customData'; +import { RequestService, getRequestService } from './requests'; + +namespace CustomDataChangedNotification { + export const type: NotificationType = new NotificationType('css/customDataChanged'); +} + +export interface Settings { + css: LanguageSettings; + less: LanguageSettings; + scss: LanguageSettings; +} + +export interface RuntimeEnvironment { + file?: RequestService; + http?: RequestService +} + +export function startServer(connection: Connection, runtime: RuntimeEnvironment) { + + // Create a text document manager. + const documents = new TextDocuments(TextDocument); + // Make the text document manager listen on the connection + // for open, change and close text document events + documents.listen(connection); + + const stylesheets = getLanguageModelCache(10, 60, document => getLanguageService(document).parseStylesheet(document)); + documents.onDidClose(e => { + stylesheets.onDocumentRemoved(e.document); + }); + connection.onShutdown(() => { + stylesheets.dispose(); + }); + + let scopedSettingsSupport = false; + let foldingRangeLimit = Number.MAX_VALUE; + let workspaceFolders: WorkspaceFolder[]; + + let dataProvidersReady: Promise = Promise.resolve(); + + const languageServices: { [id: string]: LanguageService } = {}; + + const notReady = () => Promise.reject('Not Ready'); + let requestService: RequestService = { getContent: notReady, stat: notReady, readDirectory: notReady }; + + // After the server has started the client sends an initialize request. The server receives + // in the passed params the rootPath of the workspace plus the client capabilities. + connection.onInitialize((params: InitializeParams): InitializeResult => { + workspaceFolders = (params).workspaceFolders; + if (!Array.isArray(workspaceFolders)) { + workspaceFolders = []; + if (params.rootPath) { + workspaceFolders.push({ name: '', uri: URI.file(params.rootPath).toString() }); + } + } + + requestService = getRequestService(params.initializationOptions.handledSchemas || ['file'], connection, runtime); + + function getClientCapability(name: string, def: T) { + const keys = name.split('.'); + let c: any = params.capabilities; + for (let i = 0; c && i < keys.length; i++) { + if (!c.hasOwnProperty(keys[i])) { + return def; + } + c = c[keys[i]]; + } + return c; + } + const snippetSupport = !!getClientCapability('textDocument.completion.completionItem.snippetSupport', false); + scopedSettingsSupport = !!getClientCapability('workspace.configuration', false); + foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); + + languageServices.css = getCSSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities }); + languageServices.scss = getSCSSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities }); + languageServices.less = getLESSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities }); + + const capabilities: ServerCapabilities = { + textDocumentSync: TextDocumentSyncKind.Incremental, + completionProvider: snippetSupport ? { resolveProvider: false, triggerCharacters: ['/', '-'] } : undefined, + hoverProvider: true, + documentSymbolProvider: true, + referencesProvider: true, + definitionProvider: true, + documentHighlightProvider: true, + documentLinkProvider: { + resolveProvider: false + }, + codeActionProvider: true, + renameProvider: true, + colorProvider: {}, + foldingRangeProvider: true, + selectionRangeProvider: true + }; + return { capabilities }; + }); + + function getLanguageService(document: TextDocument) { + let service = languageServices[document.languageId]; + if (!service) { + connection.console.log('Document type is ' + document.languageId + ', using css instead.'); + service = languageServices['css']; + } + return service; + } + + let documentSettings: { [key: string]: Thenable } = {}; + // remove document settings on close + documents.onDidClose(e => { + delete documentSettings[e.document.uri]; + }); + function getDocumentSettings(textDocument: TextDocument): Thenable { + if (scopedSettingsSupport) { + let promise = documentSettings[textDocument.uri]; + if (!promise) { + const configRequestParam = { items: [{ scopeUri: textDocument.uri, section: textDocument.languageId }] }; + promise = connection.sendRequest(ConfigurationRequest.type, configRequestParam).then(s => s[0]); + documentSettings[textDocument.uri] = promise; + } + return promise; + } + return Promise.resolve(undefined); + } + + // The settings have changed. Is send on server activation as well. + connection.onDidChangeConfiguration(change => { + updateConfiguration(change.settings); + }); + + function updateConfiguration(settings: Settings) { + for (const languageId in languageServices) { + languageServices[languageId].configure((settings as any)[languageId]); + } + // reset all document settings + documentSettings = {}; + // Revalidate any open text documents + documents.all().forEach(triggerValidation); + } + + const pendingValidationRequests: { [uri: string]: NodeJS.Timer } = {}; + const validationDelayMs = 500; + + // The content of a text document has changed. This event is emitted + // when the text document first opened or when its content has changed. + documents.onDidChangeContent(change => { + triggerValidation(change.document); + }); + + // a document has closed: clear all diagnostics + documents.onDidClose(event => { + cleanPendingValidation(event.document); + connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }); + }); + + function cleanPendingValidation(textDocument: TextDocument): void { + const request = pendingValidationRequests[textDocument.uri]; + if (request) { + clearTimeout(request); + delete pendingValidationRequests[textDocument.uri]; + } + } + + function triggerValidation(textDocument: TextDocument): void { + cleanPendingValidation(textDocument); + pendingValidationRequests[textDocument.uri] = setTimeout(() => { + delete pendingValidationRequests[textDocument.uri]; + validateTextDocument(textDocument); + }, validationDelayMs); + } + + function validateTextDocument(textDocument: TextDocument): void { + const settingsPromise = getDocumentSettings(textDocument); + Promise.all([settingsPromise, dataProvidersReady]).then(async ([settings]) => { + const stylesheet = stylesheets.get(textDocument); + const diagnostics = getLanguageService(textDocument).doValidation(textDocument, stylesheet, settings); + // Send the computed diagnostics to VSCode. + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); + }, e => { + connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e)); + }); + } + + + function updateDataProviders(dataPaths: string[]) { + dataProvidersReady = fetchDataProviders(dataPaths, requestService).then(customDataProviders => { + for (const lang in languageServices) { + languageServices[lang].setDataProviders(true, customDataProviders); + } + }); + } + + connection.onCompletion((textDocumentPosition, token) => { + return runSafeAsync(async () => { + const document = documents.get(textDocumentPosition.textDocument.uri); + if (document) { + await dataProvidersReady; + const styleSheet = stylesheets.get(document); + const documentContext = getDocumentContext(document.uri, workspaceFolders); + return getLanguageService(document).doComplete2(document, textDocumentPosition.position, styleSheet, documentContext); + } + return null; + }, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`, token); + }); + + connection.onHover((textDocumentPosition, token) => { + return runSafeAsync(async () => { + const document = documents.get(textDocumentPosition.textDocument.uri); + if (document) { + await dataProvidersReady; + const styleSheet = stylesheets.get(document); + return getLanguageService(document).doHover(document, textDocumentPosition.position, styleSheet); + } + return null; + }, null, `Error while computing hover for ${textDocumentPosition.textDocument.uri}`, token); + }); + + connection.onDocumentSymbol((documentSymbolParams, token) => { + return runSafeAsync(async () => { + const document = documents.get(documentSymbolParams.textDocument.uri); + if (document) { + await dataProvidersReady; + const stylesheet = stylesheets.get(document); + return getLanguageService(document).findDocumentSymbols(document, stylesheet); + } + return []; + }, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token); + }); + + connection.onDefinition((documentDefinitionParams, token) => { + return runSafeAsync(async () => { + const document = documents.get(documentDefinitionParams.textDocument.uri); + if (document) { + await dataProvidersReady; + const stylesheet = stylesheets.get(document); + return getLanguageService(document).findDefinition(document, documentDefinitionParams.position, stylesheet); + } + return null; + }, null, `Error while computing definitions for ${documentDefinitionParams.textDocument.uri}`, token); + }); + + connection.onDocumentHighlight((documentHighlightParams, token) => { + return runSafeAsync(async () => { + const document = documents.get(documentHighlightParams.textDocument.uri); + if (document) { + await dataProvidersReady; + const stylesheet = stylesheets.get(document); + return getLanguageService(document).findDocumentHighlights(document, documentHighlightParams.position, stylesheet); + } + return []; + }, [], `Error while computing document highlights for ${documentHighlightParams.textDocument.uri}`, token); + }); + + + connection.onDocumentLinks(async (documentLinkParams, token) => { + return runSafeAsync(async () => { + const document = documents.get(documentLinkParams.textDocument.uri); + if (document) { + await dataProvidersReady; + const documentContext = getDocumentContext(document.uri, workspaceFolders); + const stylesheet = stylesheets.get(document); + return getLanguageService(document).findDocumentLinks2(document, stylesheet, documentContext); + } + return []; + }, [], `Error while computing document links for ${documentLinkParams.textDocument.uri}`, token); + }); + + + connection.onReferences((referenceParams, token) => { + return runSafeAsync(async () => { + const document = documents.get(referenceParams.textDocument.uri); + if (document) { + await dataProvidersReady; + const stylesheet = stylesheets.get(document); + return getLanguageService(document).findReferences(document, referenceParams.position, stylesheet); + } + return []; + }, [], `Error while computing references for ${referenceParams.textDocument.uri}`, token); + }); + + connection.onCodeAction((codeActionParams, token) => { + return runSafeAsync(async () => { + const document = documents.get(codeActionParams.textDocument.uri); + if (document) { + await dataProvidersReady; + const stylesheet = stylesheets.get(document); + return getLanguageService(document).doCodeActions(document, codeActionParams.range, codeActionParams.context, stylesheet); + } + return []; + }, [], `Error while computing code actions for ${codeActionParams.textDocument.uri}`, token); + }); + + connection.onDocumentColor((params, token) => { + return runSafeAsync(async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + await dataProvidersReady; + const stylesheet = stylesheets.get(document); + return getLanguageService(document).findDocumentColors(document, stylesheet); + } + return []; + }, [], `Error while computing document colors for ${params.textDocument.uri}`, token); + }); + + connection.onColorPresentation((params, token) => { + return runSafeAsync(async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + await dataProvidersReady; + const stylesheet = stylesheets.get(document); + return getLanguageService(document).getColorPresentations(document, stylesheet, params.color, params.range); + } + return []; + }, [], `Error while computing color presentations for ${params.textDocument.uri}`, token); + }); + + connection.onRenameRequest((renameParameters, token) => { + return runSafeAsync(async () => { + const document = documents.get(renameParameters.textDocument.uri); + if (document) { + await dataProvidersReady; + const stylesheet = stylesheets.get(document); + return getLanguageService(document).doRename(document, renameParameters.position, renameParameters.newName, stylesheet); + } + return null; + }, null, `Error while computing renames for ${renameParameters.textDocument.uri}`, token); + }); + + connection.onFoldingRanges((params, token) => { + return runSafeAsync(async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + await dataProvidersReady; + return getLanguageService(document).getFoldingRanges(document, { rangeLimit: foldingRangeLimit }); + } + return null; + }, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token); + }); + + connection.onSelectionRanges((params, token) => { + return runSafeAsync(async () => { + const document = documents.get(params.textDocument.uri); + const positions: Position[] = params.positions; + + if (document) { + await dataProvidersReady; + const stylesheet = stylesheets.get(document); + return getLanguageService(document).getSelectionRanges(document, positions, stylesheet); + } + return []; + }, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token); + }); + + connection.onNotification(CustomDataChangedNotification.type, updateDataProviders); + + // Listen on the connection + connection.listen(); + +} + + diff --git a/extensions/css-language-features/server/src/cssServerMain.ts b/extensions/css-language-features/server/src/cssServerMain.ts deleted file mode 100644 index 6a9db0b77e7..00000000000 --- a/extensions/css-language-features/server/src/cssServerMain.ts +++ /dev/null @@ -1,390 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { - createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder, TextDocumentSyncKind -} from 'vscode-languageserver'; -import { URI } from 'vscode-uri'; -import { stat as fsStat } from 'fs'; -import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet, FileSystemProvider, FileType, TextDocument, CompletionList, Position } from 'vscode-css-languageservice'; -import { getLanguageModelCache } from './languageModelCache'; -import { getPathCompletionParticipant } from './pathCompletion'; -import { formatError, runSafe, runSafeAsync } from './utils/runner'; -import { getDocumentContext } from './utils/documentContext'; -import { getDataProviders } from './customData'; - -export interface Settings { - css: LanguageSettings; - less: LanguageSettings; - scss: LanguageSettings; -} - -// Create a connection for the server. -const connection: IConnection = createConnection(); - -console.log = connection.console.log.bind(connection.console); -console.error = connection.console.error.bind(connection.console); - -process.on('unhandledRejection', (e: any) => { - connection.console.error(formatError(`Unhandled exception`, e)); -}); - -// Create a text document manager. -const documents = new TextDocuments(TextDocument); -// Make the text document manager listen on the connection -// for open, change and close text document events -documents.listen(connection); - -const stylesheets = getLanguageModelCache(10, 60, document => getLanguageService(document).parseStylesheet(document)); -documents.onDidClose(e => { - stylesheets.onDocumentRemoved(e.document); -}); -connection.onShutdown(() => { - stylesheets.dispose(); -}); - -let scopedSettingsSupport = false; -let foldingRangeLimit = Number.MAX_VALUE; -let workspaceFolders: WorkspaceFolder[]; - -const languageServices: { [id: string]: LanguageService } = {}; - -const fileSystemProvider: FileSystemProvider = { - stat(documentUri: string) { - const filePath = URI.parse(documentUri).fsPath; - - return new Promise((c, e) => { - fsStat(filePath, (err, stats) => { - if (err) { - if (err.code === 'ENOENT') { - return c({ - type: FileType.Unknown, - ctime: -1, - mtime: -1, - size: -1 - }); - } else { - return e(err); - } - } - - let type = FileType.Unknown; - if (stats.isFile()) { - type = FileType.File; - } else if (stats.isDirectory()) { - type = FileType.Directory; - } else if (stats.isSymbolicLink()) { - type = FileType.SymbolicLink; - } - - c({ - type, - ctime: stats.ctime.getTime(), - mtime: stats.mtime.getTime(), - size: stats.size - }); - }); - }); - } -}; - -// After the server has started the client sends an initialize request. The server receives -// in the passed params the rootPath of the workspace plus the client capabilities. -connection.onInitialize((params: InitializeParams): InitializeResult => { - workspaceFolders = (params).workspaceFolders; - if (!Array.isArray(workspaceFolders)) { - workspaceFolders = []; - if (params.rootPath) { - workspaceFolders.push({ name: '', uri: URI.file(params.rootPath).toString() }); - } - } - - const dataPaths: string[] = params.initializationOptions.dataPaths || []; - const customDataProviders = getDataProviders(dataPaths); - - function getClientCapability(name: string, def: T) { - const keys = name.split('.'); - let c: any = params.capabilities; - for (let i = 0; c && i < keys.length; i++) { - if (!c.hasOwnProperty(keys[i])) { - return def; - } - c = c[keys[i]]; - } - return c; - } - const snippetSupport = !!getClientCapability('textDocument.completion.completionItem.snippetSupport', false); - scopedSettingsSupport = !!getClientCapability('workspace.configuration', false); - foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); - - languageServices.css = getCSSLanguageService({ customDataProviders, fileSystemProvider, clientCapabilities: params.capabilities }); - languageServices.scss = getSCSSLanguageService({ customDataProviders, fileSystemProvider, clientCapabilities: params.capabilities }); - languageServices.less = getLESSLanguageService({ customDataProviders, fileSystemProvider, clientCapabilities: params.capabilities }); - - const capabilities: ServerCapabilities = { - textDocumentSync: TextDocumentSyncKind.Incremental, - completionProvider: snippetSupport ? { resolveProvider: false, triggerCharacters: ['/', '-'] } : undefined, - hoverProvider: true, - documentSymbolProvider: true, - referencesProvider: true, - definitionProvider: true, - documentHighlightProvider: true, - documentLinkProvider: { - resolveProvider: false - }, - codeActionProvider: true, - renameProvider: true, - colorProvider: {}, - foldingRangeProvider: true, - selectionRangeProvider: true - }; - return { capabilities }; -}); - -function getLanguageService(document: TextDocument) { - let service = languageServices[document.languageId]; - if (!service) { - connection.console.log('Document type is ' + document.languageId + ', using css instead.'); - service = languageServices['css']; - } - return service; -} - -let documentSettings: { [key: string]: Thenable } = {}; -// remove document settings on close -documents.onDidClose(e => { - delete documentSettings[e.document.uri]; -}); -function getDocumentSettings(textDocument: TextDocument): Thenable { - if (scopedSettingsSupport) { - let promise = documentSettings[textDocument.uri]; - if (!promise) { - const configRequestParam = { items: [{ scopeUri: textDocument.uri, section: textDocument.languageId }] }; - promise = connection.sendRequest(ConfigurationRequest.type, configRequestParam).then(s => s[0]); - documentSettings[textDocument.uri] = promise; - } - return promise; - } - return Promise.resolve(undefined); -} - -// The settings have changed. Is send on server activation as well. -connection.onDidChangeConfiguration(change => { - updateConfiguration(change.settings); -}); - -function updateConfiguration(settings: Settings) { - for (const languageId in languageServices) { - languageServices[languageId].configure((settings as any)[languageId]); - } - // reset all document settings - documentSettings = {}; - // Revalidate any open text documents - documents.all().forEach(triggerValidation); -} - -const pendingValidationRequests: { [uri: string]: NodeJS.Timer } = {}; -const validationDelayMs = 500; - -// The content of a text document has changed. This event is emitted -// when the text document first opened or when its content has changed. -documents.onDidChangeContent(change => { - triggerValidation(change.document); -}); - -// a document has closed: clear all diagnostics -documents.onDidClose(event => { - cleanPendingValidation(event.document); - connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }); -}); - -function cleanPendingValidation(textDocument: TextDocument): void { - const request = pendingValidationRequests[textDocument.uri]; - if (request) { - clearTimeout(request); - delete pendingValidationRequests[textDocument.uri]; - } -} - -function triggerValidation(textDocument: TextDocument): void { - cleanPendingValidation(textDocument); - pendingValidationRequests[textDocument.uri] = setTimeout(() => { - delete pendingValidationRequests[textDocument.uri]; - validateTextDocument(textDocument); - }, validationDelayMs); -} - -function validateTextDocument(textDocument: TextDocument): void { - const settingsPromise = getDocumentSettings(textDocument); - settingsPromise.then(settings => { - const stylesheet = stylesheets.get(textDocument); - const diagnostics = getLanguageService(textDocument).doValidation(textDocument, stylesheet, settings); - // Send the computed diagnostics to VSCode. - connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); - }, e => { - connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e)); - }); -} - -connection.onCompletion((textDocumentPosition, token) => { - return runSafe(() => { - const document = documents.get(textDocumentPosition.textDocument.uri); - if (!document) { - return null; - } - const cssLS = getLanguageService(document); - const pathCompletionList: CompletionList = { - isIncomplete: false, - items: [] - }; - cssLS.setCompletionParticipants([getPathCompletionParticipant(document, workspaceFolders, pathCompletionList)]); - const result = cssLS.doComplete(document, textDocumentPosition.position, stylesheets.get(document)); - return { - isIncomplete: pathCompletionList.isIncomplete, - items: [...pathCompletionList.items, ...result.items] - }; - }, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`, token); -}); - -connection.onHover((textDocumentPosition, token) => { - return runSafe(() => { - const document = documents.get(textDocumentPosition.textDocument.uri); - if (document) { - const styleSheet = stylesheets.get(document); - return getLanguageService(document).doHover(document, textDocumentPosition.position, styleSheet); - } - return null; - }, null, `Error while computing hover for ${textDocumentPosition.textDocument.uri}`, token); -}); - -connection.onDocumentSymbol((documentSymbolParams, token) => { - return runSafe(() => { - const document = documents.get(documentSymbolParams.textDocument.uri); - if (document) { - const stylesheet = stylesheets.get(document); - return getLanguageService(document).findDocumentSymbols(document, stylesheet); - } - return []; - }, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token); -}); - -connection.onDefinition((documentDefinitionParams, token) => { - return runSafe(() => { - const document = documents.get(documentDefinitionParams.textDocument.uri); - if (document) { - - const stylesheet = stylesheets.get(document); - return getLanguageService(document).findDefinition(document, documentDefinitionParams.position, stylesheet); - } - return null; - }, null, `Error while computing definitions for ${documentDefinitionParams.textDocument.uri}`, token); -}); - -connection.onDocumentHighlight((documentHighlightParams, token) => { - return runSafe(() => { - const document = documents.get(documentHighlightParams.textDocument.uri); - if (document) { - const stylesheet = stylesheets.get(document); - return getLanguageService(document).findDocumentHighlights(document, documentHighlightParams.position, stylesheet); - } - return []; - }, [], `Error while computing document highlights for ${documentHighlightParams.textDocument.uri}`, token); -}); - - -connection.onDocumentLinks(async (documentLinkParams, token) => { - return runSafeAsync(async () => { - const document = documents.get(documentLinkParams.textDocument.uri); - if (document) { - const documentContext = getDocumentContext(document.uri, workspaceFolders); - const stylesheet = stylesheets.get(document); - return await getLanguageService(document).findDocumentLinks2(document, stylesheet, documentContext); - } - return []; - }, [], `Error while computing document links for ${documentLinkParams.textDocument.uri}`, token); -}); - - -connection.onReferences((referenceParams, token) => { - return runSafe(() => { - const document = documents.get(referenceParams.textDocument.uri); - if (document) { - const stylesheet = stylesheets.get(document); - return getLanguageService(document).findReferences(document, referenceParams.position, stylesheet); - } - return []; - }, [], `Error while computing references for ${referenceParams.textDocument.uri}`, token); -}); - -connection.onCodeAction((codeActionParams, token) => { - return runSafe(() => { - const document = documents.get(codeActionParams.textDocument.uri); - if (document) { - const stylesheet = stylesheets.get(document); - return getLanguageService(document).doCodeActions(document, codeActionParams.range, codeActionParams.context, stylesheet); - } - return []; - }, [], `Error while computing code actions for ${codeActionParams.textDocument.uri}`, token); -}); - -connection.onDocumentColor((params, token) => { - return runSafe(() => { - const document = documents.get(params.textDocument.uri); - if (document) { - const stylesheet = stylesheets.get(document); - return getLanguageService(document).findDocumentColors(document, stylesheet); - } - return []; - }, [], `Error while computing document colors for ${params.textDocument.uri}`, token); -}); - -connection.onColorPresentation((params, token) => { - return runSafe(() => { - const document = documents.get(params.textDocument.uri); - if (document) { - const stylesheet = stylesheets.get(document); - return getLanguageService(document).getColorPresentations(document, stylesheet, params.color, params.range); - } - return []; - }, [], `Error while computing color presentations for ${params.textDocument.uri}`, token); -}); - -connection.onRenameRequest((renameParameters, token) => { - return runSafe(() => { - const document = documents.get(renameParameters.textDocument.uri); - if (document) { - const stylesheet = stylesheets.get(document); - return getLanguageService(document).doRename(document, renameParameters.position, renameParameters.newName, stylesheet); - } - return null; - }, null, `Error while computing renames for ${renameParameters.textDocument.uri}`, token); -}); - -connection.onFoldingRanges((params, token) => { - return runSafe(() => { - const document = documents.get(params.textDocument.uri); - if (document) { - return getLanguageService(document).getFoldingRanges(document, { rangeLimit: foldingRangeLimit }); - } - return null; - }, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token); -}); - -connection.onSelectionRanges((params, token) => { - return runSafe(() => { - const document = documents.get(params.textDocument.uri); - const positions: Position[] = params.positions; - - if (document) { - const stylesheet = stylesheets.get(document); - return getLanguageService(document).getSelectionRanges(document, positions, stylesheet); - } - return []; - }, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token); -}); - - -// Listen on the connection -connection.listen(); diff --git a/extensions/css-language-features/server/src/customData.ts b/extensions/css-language-features/server/src/customData.ts index f173d884a2b..ccfc706452c 100644 --- a/extensions/css-language-features/server/src/customData.ts +++ b/extensions/css-language-features/server/src/customData.ts @@ -3,48 +3,36 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CSSDataV1, ICSSDataProvider } from 'vscode-css-languageservice'; -import * as fs from 'fs'; +import { ICSSDataProvider, newCSSDataProvider } from 'vscode-css-languageservice'; +import { RequestService } from './requests'; -export function getDataProviders(dataPaths: string[]): ICSSDataProvider[] { - const providers = dataPaths.map(p => { - if (fs.existsSync(p)) { - const data = parseCSSData(fs.readFileSync(p, 'utf-8')); - return { - provideProperties: () => data.properties || [], - provideAtDirectives: () => data.atDirectives || [], - providePseudoClasses: () => data.pseudoClasses || [], - providePseudoElements: () => data.pseudoElements || [] - }; - } else { - return { - provideProperties: () => [], - provideAtDirectives: () => [], - providePseudoClasses: () => [], - providePseudoElements: () => [] - }; +export function fetchDataProviders(dataPaths: string[], requestService: RequestService): Promise { + const providers = dataPaths.map(async p => { + try { + const content = await requestService.getContent(p); + return parseCSSData(content); + } catch (e) { + return newCSSDataProvider({ version: 1 }); } }); - return providers; + return Promise.all(providers); } -function parseCSSData(source: string): CSSDataV1 { +function parseCSSData(source: string): ICSSDataProvider { let rawData: any; try { rawData = JSON.parse(source); } catch (err) { - return { - version: 1 - }; + return newCSSDataProvider({ version: 1 }); } - return { - version: 1, + return newCSSDataProvider({ + version: rawData.version || 1, properties: rawData.properties || [], atDirectives: rawData.atDirectives || [], pseudoClasses: rawData.pseudoClasses || [], pseudoElements: rawData.pseudoElements || [] - }; + }); } diff --git a/extensions/css-language-features/server/src/node/cssServerMain.ts b/extensions/css-language-features/server/src/node/cssServerMain.ts new file mode 100644 index 00000000000..9e145398ff1 --- /dev/null +++ b/extensions/css-language-features/server/src/node/cssServerMain.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createConnection, Connection } from 'vscode-languageserver/node'; +import { formatError } from '../utils/runner'; +import { startServer } from '../cssServer'; +import { getNodeFSRequestService } from './nodeFs'; + +// Create a connection for the server. +const connection: Connection = createConnection(); + +console.log = connection.console.log.bind(connection.console); +console.error = connection.console.error.bind(connection.console); + +process.on('unhandledRejection', (e: any) => { + connection.console.error(formatError(`Unhandled exception`, e)); +}); + +startServer(connection, { file: getNodeFSRequestService() }); diff --git a/extensions/css-language-features/server/src/node/nodeFs.ts b/extensions/css-language-features/server/src/node/nodeFs.ts new file mode 100644 index 00000000000..c7b1301296d --- /dev/null +++ b/extensions/css-language-features/server/src/node/nodeFs.ts @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { RequestService, getScheme } from '../requests'; +import { URI as Uri } from 'vscode-uri'; + +import * as fs from 'fs'; +import { FileType } from 'vscode-css-languageservice'; + +export function getNodeFSRequestService(): RequestService { + function ensureFileUri(location: string) { + if (getScheme(location) !== 'file') { + throw new Error('fileRequestService can only handle file URLs'); + } + } + return { + getContent(location: string, encoding?: string) { + ensureFileUri(location); + return new Promise((c, e) => { + const uri = Uri.parse(location); + fs.readFile(uri.fsPath, encoding, (err, buf) => { + if (err) { + return e(err); + } + c(buf.toString()); + + }); + }); + }, + stat(location: string) { + ensureFileUri(location); + return new Promise((c, e) => { + const uri = Uri.parse(location); + fs.stat(uri.fsPath, (err, stats) => { + if (err) { + if (err.code === 'ENOENT') { + return c({ type: FileType.Unknown, ctime: -1, mtime: -1, size: -1 }); + } else { + return e(err); + } + } + + let type = FileType.Unknown; + if (stats.isFile()) { + type = FileType.File; + } else if (stats.isDirectory()) { + type = FileType.Directory; + } else if (stats.isSymbolicLink()) { + type = FileType.SymbolicLink; + } + + c({ + type, + ctime: stats.ctime.getTime(), + mtime: stats.mtime.getTime(), + size: stats.size + }); + }); + }); + }, + readDirectory(location: string) { + ensureFileUri(location); + return new Promise((c, e) => { + const path = Uri.parse(location).fsPath; + + fs.readdir(path, { withFileTypes: true }, (err, children) => { + if (err) { + return e(err); + } + c(children.map(stat => { + if (stat.isSymbolicLink()) { + return [stat.name, FileType.SymbolicLink]; + } else if (stat.isDirectory()) { + return [stat.name, FileType.Directory]; + } else if (stat.isFile()) { + return [stat.name, FileType.File]; + } else { + return [stat.name, FileType.Unknown]; + } + })); + }); + }); + } + }; +} diff --git a/extensions/css-language-features/server/src/pathCompletion.ts b/extensions/css-language-features/server/src/pathCompletion.ts deleted file mode 100644 index 6862f28e034..00000000000 --- a/extensions/css-language-features/server/src/pathCompletion.ts +++ /dev/null @@ -1,213 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as path from 'path'; -import * as fs from 'fs'; -import { URI } from 'vscode-uri'; - -import { TextDocument, CompletionList, CompletionItemKind, CompletionItem, TextEdit, Range, Position } from 'vscode-languageserver-types'; -import { WorkspaceFolder } from 'vscode-languageserver'; -import { ICompletionParticipant } from 'vscode-css-languageservice'; - -import { startsWith, endsWith } from './utils/strings'; - -export function getPathCompletionParticipant( - document: TextDocument, - workspaceFolders: WorkspaceFolder[], - result: CompletionList -): ICompletionParticipant { - return { - onCssURILiteralValue: ({ position, range, uriValue }) => { - const fullValue = stripQuotes(uriValue); - if (!shouldDoPathCompletion(uriValue, workspaceFolders)) { - if (fullValue === '.' || fullValue === '..') { - result.isIncomplete = true; - } - return; - } - - let suggestions = providePathSuggestions(uriValue, position, range, document, workspaceFolders); - result.items = [...suggestions, ...result.items]; - }, - onCssImportPath: ({ position, range, pathValue }) => { - const fullValue = stripQuotes(pathValue); - if (!shouldDoPathCompletion(pathValue, workspaceFolders)) { - if (fullValue === '.' || fullValue === '..') { - result.isIncomplete = true; - } - return; - } - - let suggestions = providePathSuggestions(pathValue, position, range, document, workspaceFolders); - - if (document.languageId === 'scss') { - suggestions.forEach(s => { - if (startsWith(s.label, '_') && endsWith(s.label, '.scss')) { - if (s.textEdit) { - s.textEdit.newText = s.label.slice(1, -5); - } else { - s.label = s.label.slice(1, -5); - } - } - }); - } - - result.items = [...suggestions, ...result.items]; - } - }; -} - -function providePathSuggestions(pathValue: string, position: Position, range: Range, document: TextDocument, workspaceFolders: WorkspaceFolder[]) { - const fullValue = stripQuotes(pathValue); - const isValueQuoted = startsWith(pathValue, `'`) || startsWith(pathValue, `"`); - const valueBeforeCursor = isValueQuoted - ? fullValue.slice(0, position.character - (range.start.character + 1)) - : fullValue.slice(0, position.character - range.start.character); - const workspaceRoot = resolveWorkspaceRoot(document, workspaceFolders); - const currentDocFsPath = URI.parse(document.uri).fsPath; - - const paths = providePaths(valueBeforeCursor, currentDocFsPath, workspaceRoot) - .filter(p => { - // Exclude current doc's path - return path.resolve(currentDocFsPath, '../', p) !== currentDocFsPath; - }) - .filter(p => { - // Exclude paths that start with `.` - return p[0] !== '.'; - }); - - const fullValueRange = isValueQuoted ? shiftRange(range, 1, -1) : range; - const replaceRange = pathToReplaceRange(valueBeforeCursor, fullValue, fullValueRange); - - const suggestions = paths.map(p => pathToSuggestion(p, replaceRange)); - return suggestions; -} - -function shouldDoPathCompletion(pathValue: string, workspaceFolders: WorkspaceFolder[]): boolean { - const fullValue = stripQuotes(pathValue); - if (fullValue === '.' || fullValue === '..') { - return false; - } - - if (!workspaceFolders || workspaceFolders.length === 0) { - return false; - } - - return true; -} - -function stripQuotes(fullValue: string) { - if (startsWith(fullValue, `'`) || startsWith(fullValue, `"`)) { - return fullValue.slice(1, -1); - } else { - return fullValue; - } -} - -/** - * Get a list of path suggestions. Folder suggestions are suffixed with a slash. - */ -function providePaths(valueBeforeCursor: string, activeDocFsPath: string, root?: string): string[] { - const lastIndexOfSlash = valueBeforeCursor.lastIndexOf('/'); - const valueBeforeLastSlash = valueBeforeCursor.slice(0, lastIndexOfSlash + 1); - - const startsWithSlash = startsWith(valueBeforeCursor, '/'); - let parentDir: string; - if (startsWithSlash) { - if (!root) { - return []; - } - parentDir = path.resolve(root, '.' + valueBeforeLastSlash); - } else { - parentDir = path.resolve(activeDocFsPath, '..', valueBeforeLastSlash); - } - - try { - return fs.readdirSync(parentDir).map(f => { - return isDir(path.resolve(parentDir, f)) - ? f + '/' - : f; - }); - } catch (e) { - return []; - } -} - -const isDir = (p: string) => { - try { - return fs.statSync(p).isDirectory(); - } catch (e) { - return false; - } -}; - -function pathToReplaceRange(valueBeforeCursor: string, fullValue: string, fullValueRange: Range) { - let replaceRange: Range; - const lastIndexOfSlash = valueBeforeCursor.lastIndexOf('/'); - if (lastIndexOfSlash === -1) { - replaceRange = fullValueRange; - } else { - // For cases where cursor is in the middle of attribute value, like ', { + test('HTML JavaScript Completions', async () => { + await testCompletionFor('', { items: [ { label: 'location', resultText: '' }, ] }); - testCompletionFor('', { + await testCompletionFor('', { items: [ { label: 'getJSON', resultText: '' }, ] }); + await testCompletionFor('', { + items: [ + { label: 'a', resultText: '' }, + ] + }, 'test://test/test2.html'); }); }); @@ -96,8 +105,8 @@ suite('HTML Path Completion', () => { const indexHtmlUri = URI.file(path.resolve(fixtureRoot, 'index.html')).toString(); const aboutHtmlUri = URI.file(path.resolve(fixtureRoot, 'about/about.html')).toString(); - test('Basics - Correct label/kind/result/command', () => { - testCompletionFor('', '\n\n\n \n\n\n'); - assertFormat('', '\n\n\n \n\n\n'); - assertFormat('', '\n\n\n \n\n\n'); - assertFormat('\n ', '\n\n\n \n\n\n'); - assertFormat('\n ', '\n\n\n \n\n\n'); - assertFormat('\n ||', '\n '); + test('HTML & Scripts', async () => { + await assertFormat('', '\n\n\n \n\n\n'); + await assertFormat('', '\n\n\n \n\n\n'); + await assertFormat('', '\n\n\n \n\n\n'); + await assertFormat('\n ', '\n\n\n \n\n\n'); + await assertFormat('\n ', '\n\n\n \n\n\n'); + await assertFormat('\n ||', '\n '); }); - test('HTLM & Scripts - Fixtures', function () { + test('HTLM & Scripts - Fixtures', async () => { assertFormatWithFixture('19813.html', '19813.html'); assertFormatWithFixture('19813.html', '19813-4spaces.html', undefined, FormattingOptions.create(4, true)); assertFormatWithFixture('19813.html', '19813-tab.html', undefined, FormattingOptions.create(1, false)); assertFormatWithFixture('21634.html', '21634.html'); }); - test('Script end tag', function (): any { - assertFormat('\n\n ', '\n\n\n \n\n\n'); + test('Script end tag', async () => { + await assertFormat('\n\n ', '\n\n\n \n\n\n'); }); - test('HTML & Multiple Scripts', function (): any { - assertFormat('\n', '\n\n\n \n \n\n\n'); + test('HTML & Multiple Scripts', async () => { + await assertFormat('\n', '\n\n\n \n \n\n\n'); }); - test('HTML & Styles', function (): any { - assertFormat('\n', '\n\n\n \n\n\n'); + test('HTML & Styles', async () => { + await assertFormat('\n', '\n\n\n \n\n\n'); }); - test('EndWithNewline', function (): any { + test('EndWithNewline', async () => { let options = { html: { format: { @@ -91,26 +92,26 @@ suite('HTML Embedded Formatting', () => { } } }; - assertFormat('

Hello

', '\n\n\n

Hello

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

Hello

|', '\n

Hello

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

Hello

', '\n\n\n

Hello

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

Hello

|', '\n

Hello

\n', options); + await assertFormat('', '\n\n\n \n\n\n\n', options); }); - test('Inside script', function (): any { - assertFormat('\n ', '\n '); - assertFormat('\n ', '\n '); + test('Inside script', async () => { + await assertFormat('\n ', '\n '); + await assertFormat('\n ', '\n '); }); - test('Range after new line', function (): any { - assertFormat('\n |\n|', '\n \n'); + test('Range after new line', async () => { + await assertFormat('\n |\n|', '\n \n'); }); - test('bug 36574', function (): any { - assertFormat('', ''); + test('bug 36574', async () => { + await assertFormat('', ''); }); - test('bug 48049', function (): any { - assertFormat( + test('bug 48049', async () => { + await assertFormat( [ '', '', @@ -158,7 +159,7 @@ suite('HTML Embedded Formatting', () => { ].join('\n') ); }); - test('#58435', () => { + test('#58435', async () => { let options = { html: { format: { @@ -189,7 +190,7 @@ suite('HTML Embedded Formatting', () => { '', ].join('\n'); - assertFormat(content, expected, options); + await assertFormat(content, expected, options); }); }); /* diff --git a/extensions/html-language-features/server/src/test/selectionRanges.test.ts b/extensions/html-language-features/server/src/test/selectionRanges.test.ts index dbc33d93a9b..9f9937bcaf7 100644 --- a/extensions/html-language-features/server/src/test/selectionRanges.test.ts +++ b/extensions/html-language-features/server/src/test/selectionRanges.test.ts @@ -7,8 +7,9 @@ import 'mocha'; import * as assert from 'assert'; import { getLanguageModes, ClientCapabilities, TextDocument, SelectionRange} from '../modes/languageModes'; import { getSelectionRanges } from '../modes/selectionRanges'; +import { getNodeFSRequestService } from '../node/nodeFs'; -function assertRanges(content: string, expected: (number | string)[][]): void { +async function assertRanges(content: string, expected: (number | string)[][]): Promise { let message = `${content} gives selection range:\n`; const offset = content.indexOf('|'); @@ -18,10 +19,10 @@ function assertRanges(content: string, expected: (number | string)[][]): void { settings: {}, folders: [{ name: 'foo', uri: 'test://foo' }] }; - let languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST); + const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST, getNodeFSRequestService()); const document = TextDocument.create('test://foo.html', 'html', 1, content); - const actualRanges = getSelectionRanges(languageModes, document, [document.positionAt(offset)]); + const actualRanges = await getSelectionRanges(languageModes, document, [document.positionAt(offset)]); assert.equal(actualRanges.length, 1); const offsetPairs: [number, string][] = []; let curr: SelectionRange | undefined = actualRanges[0]; @@ -35,8 +36,8 @@ function assertRanges(content: string, expected: (number | string)[][]): void { } suite('HTML SelectionRange', () => { - test('Embedded JavaScript', () => { - assertRanges('', [ + test('Embedded JavaScript', async () => { + await assertRanges('', [ [48, '1'], [48, '1+2'], [47, '(1+2)'], @@ -51,8 +52,8 @@ suite('HTML SelectionRange', () => { ]); }); - test('Embedded CSS', () => { - assertRanges('', [ + test('Embedded CSS', async () => { + await assertRanges('', [ [34, 'none'], [25, 'display: none'], [24, ' display: none; '], @@ -65,8 +66,8 @@ suite('HTML SelectionRange', () => { ]); }); - test('Embedded style', () => { - assertRanges('
', [ + test('Embedded style', async () => { + await assertRanges('
', [ [19, 'red'], [12, 'color: red'], [11, '"color: red"'], diff --git a/extensions/html-language-features/server/src/test/semanticTokens.test.ts b/extensions/html-language-features/server/src/test/semanticTokens.test.ts index ea3777e84e9..6d815748e5e 100644 --- a/extensions/html-language-features/server/src/test/semanticTokens.test.ts +++ b/extensions/html-language-features/server/src/test/semanticTokens.test.ts @@ -7,6 +7,7 @@ import 'mocha'; import * as assert from 'assert'; import { TextDocument, getLanguageModes, ClientCapabilities, Range, Position } from '../modes/languageModes'; import { newSemanticTokenProvider } from '../modes/semanticTokens'; +import { getNodeFSRequestService } from '../node/nodeFs'; interface ExpectedToken { startLine: number; @@ -15,17 +16,17 @@ interface ExpectedToken { tokenClassifiction: string; } -function assertTokens(lines: string[], expected: ExpectedToken[], ranges?: Range[], message?: string): void { +async function assertTokens(lines: string[], expected: ExpectedToken[], ranges?: Range[], message?: string): Promise { const document = TextDocument.create('test://foo/bar.html', 'html', 1, lines.join('\n')); const workspace = { settings: {}, folders: [{ name: 'foo', uri: 'test://foo' }] }; - const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST); + const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST, getNodeFSRequestService()); const semanticTokensProvider = newSemanticTokenProvider(languageModes); const legend = semanticTokensProvider.legend; - const actual = semanticTokensProvider.getSemanticTokens(document, ranges); + const actual = await semanticTokensProvider.getSemanticTokens(document, ranges); let actualRanges = []; let lastLine = 0; @@ -48,7 +49,7 @@ function t(startLine: number, character: number, length: number, tokenClassifict suite('HTML Semantic Tokens', () => { - test('Variables', () => { + test('Variables', async () => { const input = [ /*0*/'', /*1*/'', @@ -63,7 +64,7 @@ suite('HTML Semantic Tokens', () => { /*10*/'', /*11*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 6, 1, 'variable.declaration'), t(3, 13, 2, 'variable.declaration'), t(3, 19, 1, 'variable'), t(5, 15, 1, 'variable.declaration.readonly'), t(5, 20, 2, 'variable'), t(5, 26, 1, 'variable'), t(5, 30, 1, 'variable.readonly'), t(6, 11, 1, 'variable.declaration'), @@ -71,7 +72,7 @@ suite('HTML Semantic Tokens', () => { ]); }); - test('Functions', () => { + test('Functions', async () => { const input = [ /*0*/'', /*1*/'', @@ -84,14 +85,14 @@ suite('HTML Semantic Tokens', () => { /*8*/'', /*9*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 11, 3, 'function.declaration'), t(3, 15, 2, 'parameter.declaration'), t(4, 11, 3, 'function'), t(4, 15, 4, 'interface'), t(4, 20, 3, 'member'), t(4, 24, 2, 'parameter'), t(6, 6, 6, 'variable'), t(6, 13, 8, 'property'), t(6, 24, 5, 'member'), t(6, 35, 7, 'member'), t(6, 43, 1, 'parameter.declaration'), t(6, 48, 3, 'function'), t(6, 52, 1, 'parameter') ]); }); - test('Members', () => { + test('Members', async () => { const input = [ /*0*/'', /*1*/'', @@ -110,7 +111,7 @@ suite('HTML Semantic Tokens', () => { ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 8, 1, 'class.declaration'), t(4, 11, 1, 'property.declaration.static'), t(5, 4, 1, 'property.declaration'), @@ -120,7 +121,7 @@ suite('HTML Semantic Tokens', () => { ]); }); - test('Interfaces', () => { + test('Interfaces', async () => { const input = [ /*0*/'', /*1*/'', @@ -132,14 +133,14 @@ suite('HTML Semantic Tokens', () => { /*7*/'', /*8*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 12, 8, 'interface.declaration'), t(3, 23, 1, 'property.declaration'), t(3, 34, 1, 'property.declaration'), t(4, 8, 1, 'variable.declaration.readonly'), t(4, 30, 8, 'interface'), t(5, 8, 3, 'variable.declaration.readonly'), t(5, 15, 1, 'parameter.declaration'), t(5, 18, 8, 'interface'), t(5, 31, 1, 'parameter'), t(5, 33, 1, 'property'), t(5, 37, 1, 'parameter'), t(5, 39, 1, 'property') ]); }); - test('Readonly', () => { + test('Readonly', async () => { const input = [ /*0*/'', /*1*/'', @@ -152,7 +153,7 @@ suite('HTML Semantic Tokens', () => { /*8*/'', /*9*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 8, 1, 'variable.declaration.readonly'), t(4, 8, 1, 'class.declaration'), t(4, 28, 1, 'property.declaration.static.readonly'), t(4, 42, 3, 'property.declaration.static'), t(4, 47, 3, 'interface'), t(5, 13, 1, 'enum.declaration'), t(5, 17, 1, 'property.declaration.readonly'), t(5, 24, 1, 'property.declaration.readonly'), t(5, 28, 1, 'property.readonly'), @@ -161,7 +162,7 @@ suite('HTML Semantic Tokens', () => { }); - test('Type aliases and type parameters', () => { + test('Type aliases and type parameters', async () => { const input = [ /*0*/'', /*1*/'', @@ -174,14 +175,14 @@ suite('HTML Semantic Tokens', () => { /*8*/'', /*9*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 7, 5, 'type.declaration'), t(3, 15, 3, 'interface') /* to investiagte */, t(4, 11, 1, 'function.declaration'), t(4, 13, 1, 'typeParameter.declaration'), t(4, 23, 5, 'type'), t(4, 30, 1, 'parameter.declaration'), t(4, 33, 1, 'typeParameter'), t(4, 47, 1, 'typeParameter'), t(5, 12, 1, 'typeParameter'), t(5, 29, 3, 'interface'), t(5, 41, 5, 'type'), ]); }); - test('TS and JS', () => { + test('TS and JS', async () => { const input = [ /*0*/'', /*1*/'', @@ -194,13 +195,13 @@ suite('HTML Semantic Tokens', () => { /*8*/'', /*9*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 11, 1, 'function.declaration'), t(3, 13, 1, 'typeParameter.declaration'), t(3, 16, 2, 'parameter.declaration'), t(3, 20, 1, 'typeParameter'), t(3, 24, 1, 'typeParameter'), t(3, 39, 2, 'parameter'), t(6, 2, 6, 'variable'), t(6, 9, 5, 'member') ]); }); - test('Ranges', () => { + test('Ranges', async () => { const input = [ /*0*/'', /*1*/'', @@ -213,11 +214,11 @@ suite('HTML Semantic Tokens', () => { /*8*/'', /*9*/'', ]; - assertTokens(input, [ + await assertTokens(input, [ t(3, 2, 6, 'variable'), t(3, 9, 5, 'member') ], [Range.create(Position.create(2, 0), Position.create(4, 0))]); - assertTokens(input, [ + await assertTokens(input, [ t(6, 2, 6, 'variable'), ], [Range.create(Position.create(6, 2), Position.create(6, 8))]); }); diff --git a/extensions/html-language-features/server/src/utils/documentContext.ts b/extensions/html-language-features/server/src/utils/documentContext.ts index 0f728860de8..f8bc67f8b10 100644 --- a/extensions/html-language-features/server/src/utils/documentContext.ts +++ b/extensions/html-language-features/server/src/utils/documentContext.ts @@ -3,9 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DocumentContext, WorkspaceFolder } from '../modes/languageModes'; +import { DocumentContext } from 'vscode-css-languageservice'; import { endsWith, startsWith } from '../utils/strings'; -import * as url from 'url'; +import { WorkspaceFolder } from 'vscode-languageserver'; +import { resolvePath } from '../requests'; export function getDocumentContext(documentUri: string, workspaceFolders: WorkspaceFolder[]): DocumentContext { function getRootFolder(): string | undefined { @@ -22,20 +23,15 @@ export function getDocumentContext(documentUri: string, workspaceFolders: Worksp } return { - resolveReference: (ref, base = documentUri) => { + resolveReference: (ref: string, base = documentUri) => { if (ref[0] === '/') { // resolve absolute path against the current workspace folder - if (startsWith(base, 'file://')) { - let folderUri = getRootFolder(); - if (folderUri) { - return folderUri + ref.substr(1); - } + let folderUri = getRootFolder(); + if (folderUri) { + return folderUri + ref.substr(1); } } - try { - return url.resolve(base, ref); - } catch { - return ''; - } + base = base.substr(0, base.lastIndexOf('/') + 1); + return resolvePath(base, ref); }, }; } diff --git a/extensions/html-language-features/server/src/utils/runner.ts b/extensions/html-language-features/server/src/utils/runner.ts index 98a7a96f9aa..6bf8a88d0c6 100644 --- a/extensions/html-language-features/server/src/utils/runner.ts +++ b/extensions/html-language-features/server/src/utils/runner.ts @@ -17,7 +17,7 @@ export function formatError(message: string, err: any): string { return message; } -export function runSafeAsync(func: () => Thenable, errorVal: T, errorMessage: string, token: CancellationToken): Thenable> { +export function runSafe(func: () => Thenable, errorVal: T, errorMessage: string, token: CancellationToken): Thenable> { return new Promise>((resolve) => { setImmediate(() => { if (token.isCancellationRequested) { @@ -38,29 +38,7 @@ export function runSafeAsync(func: () => Thenable, errorVal: T, errorMessa }); } -export function runSafe(func: () => T, errorVal: T, errorMessage: string, token: CancellationToken): Thenable> { - return new Promise>((resolve) => { - setImmediate(() => { - if (token.isCancellationRequested) { - resolve(cancelValue()); - } else { - try { - let result = func(); - if (token.isCancellationRequested) { - resolve(cancelValue()); - return; - } else { - resolve(result); - } - } catch (e) { - console.error(formatError(errorMessage, e)); - resolve(errorVal); - } - } - }); - }); -} function cancelValue() { return new ResponseError(ErrorCodes.RequestCancelled, 'Request cancelled'); diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index dab9e8cf78d..dc001b1ada8 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -418,15 +418,10 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^3.0.0" -lodash@^4.16.4: - version "4.17.10" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" - integrity sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg== - -lodash@^4.17.15: - version "4.17.15" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" - integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +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== log-symbols@3.0.0: version "3.0.0" @@ -726,65 +721,65 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -vscode-css-languageservice@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.1.2.tgz#533bfeb79b38e8add07230dc67001cceb80253e8" - integrity sha512-clIjSS940NPBvtfubZokKT/YDNfE5ST9VDwsuwdCbQSkJAVZPAbmIgfmgrz/f/o8PawYQU/ooUBEuRIvIYq3ag== +vscode-css-languageservice@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.3.1.tgz#a78755b28b8a0cbb1681121f0fa372860f34ef6b" + integrity sha512-Vdz2cyoTP2tLWikhFdouK8dAQ3gVhLPxsFkIscM30Quh6rd/YejTeZEYC/W+b0iKumHYebDeo1GUFbf0ptySRw== dependencies: vscode-languageserver-textdocument "^1.0.1" - vscode-languageserver-types "^3.15.1" + vscode-languageserver-types "3.16.0-next.2" vscode-nls "^4.1.2" - vscode-uri "^2.1.1" + vscode-uri "^2.1.2" -vscode-html-languageservice@^3.1.0-next.0: - version "3.1.0-next.0" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.1.0-next.0.tgz#cd3efee3729ede804e82a8a1ac44bd6ea326d237" - integrity sha512-DaByLVmFQA0iLoYi6PzwlE6GJFZxvBUan0Wz30qaXwRqwrTG55Xq8n7Y5e6JHB9zixYyo1jhsQdQq/+rT0tv5w== +vscode-html-languageservice@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.1.0.tgz#265b53bda595e6947b16b0fb8c604e1e58685393" + integrity sha512-QAyRHI98bbEIBCqTzZVA0VblGU40na0txggongw5ZgTj9UVsVk5XbLT16O9OTcbqBGSqn0oWmFDNjK/XGIDcqg== dependencies: vscode-languageserver-textdocument "^1.0.1" - vscode-languageserver-types "^3.15.1" + vscode-languageserver-types "3.16.0-next.2" vscode-nls "^4.1.2" - vscode-uri "^2.1.1" + vscode-uri "^2.1.2" -vscode-jsonrpc@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz#9bab9c330d89f43fc8c1e8702b5c36e058a01794" - integrity sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A== +vscode-jsonrpc@6.0.0-next.2: + version "6.0.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.2.tgz#3d73f86d812304cb91b9fb1efee40ec60b09ed7f" + integrity sha512-dKQXRYNUY6BHALQJBJlyZyv9oWlYpbJ2vVoQNNVNPLAYQ3hzNp4zy+iSo7zGx1BPXByArJQDWTKLQh8dz3dnNw== -vscode-languageserver-protocol@^3.15.3: - version "3.15.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz#3fa9a0702d742cf7883cb6182a6212fcd0a1d8bb" - integrity sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw== +vscode-languageserver-protocol@3.16.0-next.4: + version "3.16.0-next.4" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.4.tgz#8f8b1b831d4dfd9b26aa1ba3d2a32c427a91c99f" + integrity sha512-6GmPUp2MhJy2H1CTWp2B40Pa9BeC9glrXWmQWVG6A/0V9UbcAjVC9m56znm2GL32iyLDIprTBe8gBvvvcjbpaQ== dependencies: - vscode-jsonrpc "^5.0.1" - vscode-languageserver-types "3.15.1" + 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.15.1, 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-types@3.16.0-next.2: + version "3.16.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz#940bd15c992295a65eae8ab6b8568a1e8daa3083" + integrity sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q== -vscode-languageserver@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-6.1.1.tgz#d76afc68172c27d4327ee74332b468fbc740d762" - integrity sha512-DueEpkUAkD5XTR4MLYNr6bQIp/UFR0/IPApgXU3YfCBCB08u2sm9hRCs6DxYZELkk++STPjpcjksR2H8qI3cDQ== +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.3" + 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.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== which-module@^2.0.0: version "2.0.0" diff --git a/extensions/html-language-features/yarn.lock b/extensions/html-language-features/yarn.lock index fd8ffd55140..0d4b9507c21 100644 --- a/extensions/html-language-features/yarn.lock +++ b/extensions/html-language-features/yarn.lock @@ -45,31 +45,31 @@ vscode-extension-telemetry@0.1.1: dependencies: applicationinsights "1.0.8" -vscode-jsonrpc@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz#9bab9c330d89f43fc8c1e8702b5c36e058a01794" - integrity sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A== +vscode-jsonrpc@6.0.0-next.2: + version "6.0.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.2.tgz#3d73f86d812304cb91b9fb1efee40ec60b09ed7f" + integrity sha512-dKQXRYNUY6BHALQJBJlyZyv9oWlYpbJ2vVoQNNVNPLAYQ3hzNp4zy+iSo7zGx1BPXByArJQDWTKLQh8dz3dnNw== -vscode-languageclient@^6.1.3: - version "6.1.3" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-6.1.3.tgz#c979c5bb5855714a0307e998c18ca827c1b3953a" - integrity sha512-YciJxk08iU5LmWu7j5dUt9/1OLjokKET6rME3cI4BRpiF6HZlusm2ZwPt0MYJ0lV5y43sZsQHhyon2xBg4ZJVA== +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.3" + vscode-languageserver-protocol "3.16.0-next.4" -vscode-languageserver-protocol@^3.15.3: - version "3.15.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz#3fa9a0702d742cf7883cb6182a6212fcd0a1d8bb" - integrity sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw== +vscode-languageserver-protocol@3.16.0-next.4: + version "3.16.0-next.4" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.4.tgz#8f8b1b831d4dfd9b26aa1ba3d2a32c427a91c99f" + integrity sha512-6GmPUp2MhJy2H1CTWp2B40Pa9BeC9glrXWmQWVG6A/0V9UbcAjVC9m56znm2GL32iyLDIprTBe8gBvvvcjbpaQ== dependencies: - vscode-jsonrpc "^5.0.1" - vscode-languageserver-types "3.15.1" + vscode-jsonrpc "6.0.0-next.2" + vscode-languageserver-types "3.16.0-next.2" -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-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.2: version "4.1.2" diff --git a/extensions/html/test/colorize-results/test-embedding_html.json b/extensions/html/test/colorize-results/test-embedding_html.json index 1931950a02c..cd3af42bf2c 100644 --- a/extensions/html/test/colorize-results/test-embedding_html.json +++ b/extensions/html/test/colorize-results/test-embedding_html.json @@ -34,13 +34,13 @@ }, { "c": "console", - "t": "text.html.derivative meta.embedded.block.html source.js meta.function-call.js support.class.console.js", + "t": "text.html.derivative meta.embedded.block.html source.js meta.function-call.js variable.other.object.js", "r": { - "dark_plus": "support.class: #4EC9B0", - "light_plus": "support.class: #267F99", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "support.class: #4EC9B0" + "hc_black": "variable: #9CDCFE" } }, { @@ -56,13 +56,13 @@ }, { "c": "log", - "t": "text.html.derivative meta.embedded.block.html source.js meta.function-call.js support.function.console.js", + "t": "text.html.derivative meta.embedded.block.html source.js meta.function-call.js entity.name.function.js", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { diff --git a/extensions/image-preview/.vscodeignore b/extensions/image-preview/.vscodeignore index 30d948fbc66..bcb886a094d 100644 --- a/extensions/image-preview/.vscodeignore +++ b/extensions/image-preview/.vscodeignore @@ -4,6 +4,7 @@ tsconfig.json out/test/** out/** extension.webpack.config.js +extension-browser.webpack.config.js cgmanifest.json yarn.lock preview-src/** diff --git a/extensions/image-preview/extension-browser.webpack.config.js b/extensions/image-preview/extension-browser.webpack.config.js new file mode 100644 index 00000000000..9a1bb4d3c8e --- /dev/null +++ b/extensions/image-preview/extension-browser.webpack.config.js @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withBrowserDefaults = require('../shared.webpack.config').browser; + +module.exports = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/extension.ts' + }, +}); diff --git a/extensions/image-preview/media/main.js b/extensions/image-preview/media/main.js index 394511aa8ef..d344b99fbaf 100644 --- a/extensions/image-preview/media/main.js +++ b/extensions/image-preview/media/main.js @@ -279,6 +279,9 @@ image.classList.add('scale-to-fit'); image.addEventListener('load', () => { + if (hasLoadedImage) { + return; + } hasLoadedImage = true; vscode.postMessage({ @@ -297,7 +300,11 @@ } }); - image.addEventListener('error', () => { + image.addEventListener('error', e => { + if (hasLoadedImage) { + return; + } + hasLoadedImage = true; document.body.classList.add('error'); document.body.classList.remove('loading'); diff --git a/extensions/image-preview/package.json b/extensions/image-preview/package.json index 48c7ae314a9..064ada94858 100644 --- a/extensions/image-preview/package.json +++ b/extensions/image-preview/package.json @@ -2,7 +2,11 @@ "name": "image-preview", "displayName": "%displayName%", "description": "%description%", - "extensionKind": ["ui", "workspace"], + "extensionKind": [ + "ui", + "workspace", + "web" + ], "version": "1.0.0", "publisher": "vscode", "icon": "icon.png", @@ -13,6 +17,7 @@ "vscode": "^1.39.0" }, "main": "./out/extension", + "browser": "./dist/browser/extension.js", "categories": [ "Other" ], @@ -65,7 +70,9 @@ "compile": "gulp compile-extension:image-preview", "watch": "npm run build-preview && gulp watch-extension:image-preview", "vscode:prepublish": "npm run build-ext", - "build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:image-preview ./tsconfig.json" + "build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:image-preview ./tsconfig.json", + "compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none", + "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { "vscode-extension-telemetry": "0.1.1", diff --git a/extensions/image-preview/src/extension.ts b/extensions/image-preview/src/extension.ts index e6b394fa29b..552b32d39b6 100644 --- a/extensions/image-preview/src/extension.ts +++ b/extensions/image-preview/src/extension.ts @@ -10,8 +10,6 @@ import { BinarySizeStatusBarEntry } from './binarySizeStatusBarEntry'; import { ZoomStatusBarEntry } from './zoomStatusBarEntry'; export function activate(context: vscode.ExtensionContext) { - const extensionRoot = vscode.Uri.file(context.extensionPath); - const sizeStatusBarEntry = new SizeStatusBarEntry(); context.subscriptions.push(sizeStatusBarEntry); @@ -21,9 +19,9 @@ export function activate(context: vscode.ExtensionContext) { const zoomStatusBarEntry = new ZoomStatusBarEntry(); context.subscriptions.push(zoomStatusBarEntry); - const previewManager = new PreviewManager(extensionRoot, sizeStatusBarEntry, binarySizeStatusBarEntry, zoomStatusBarEntry); + const previewManager = new PreviewManager(context.extensionUri, sizeStatusBarEntry, binarySizeStatusBarEntry, zoomStatusBarEntry); - context.subscriptions.push(vscode.window.registerCustomEditorProvider2(PreviewManager.viewType, previewManager, { + context.subscriptions.push(vscode.window.registerCustomEditorProvider(PreviewManager.viewType, previewManager, { supportsMultipleEditorsPerDocument: true, })); diff --git a/extensions/image-preview/src/preview.ts b/extensions/image-preview/src/preview.ts index 25d3ac86c22..f4b589e0fb8 100644 --- a/extensions/image-preview/src/preview.ts +++ b/extensions/image-preview/src/preview.ts @@ -179,7 +179,7 @@ class Preview extends Disposable { private async render() { if (this._previewState !== PreviewState.Disposed) { - this.webviewEditor.webview.html = await this.getWebiewContents(); + this.webviewEditor.webview.html = await this.getWebviewContents(); } } @@ -203,7 +203,7 @@ class Preview extends Disposable { } } - private async getWebiewContents(): Promise { + private async getWebviewContents(): Promise { const version = Date.now().toString(); const settings = { isMac: process.platform === 'darwin', @@ -249,9 +249,9 @@ class Preview extends Disposable { // Avoid adding cache busting if there is already a query string if (resource.query) { - return webviewEditor.webview.asWebviewUri(resource).toString(true); + return webviewEditor.webview.asWebviewUri(resource).toString(); } - return webviewEditor.webview.asWebviewUri(resource).with({ query: `version=${version}` }).toString(true); + return webviewEditor.webview.asWebviewUri(resource).with({ query: `version=${version}` }).toString(); } private extensionResource(path: string) { diff --git a/extensions/ini/package.json b/extensions/ini/package.json index cc5ca5da606..24d86072749 100644 --- a/extensions/ini/package.json +++ b/extensions/ini/package.json @@ -18,8 +18,8 @@ }, { "id": "properties", - "extensions": [ ".properties", ".cfg", ".conf", ".directory" ], - "filenames": [ ".gitattributes", ".gitconfig", "gitconfig", ".gitmodules", ".editorconfig" ], + "extensions": [ ".properties", ".cfg", ".conf", ".directory", ".gitattributes", ".gitconfig", ".gitmodules", ".editorconfig" ], + "filenames": [ "gitconfig" ], "filenamePatterns": [ "**/.config/git/config", "**/.git/config" ], "aliases": [ "Properties", "properties" ], "configuration": "./properties.language-configuration.json" diff --git a/extensions/javascript/snippets/javascript.code-snippets b/extensions/javascript/snippets/javascript.code-snippets index 5da4ebe0c18..fc892b57e92 100644 --- a/extensions/javascript/snippets/javascript.code-snippets +++ b/extensions/javascript/snippets/javascript.code-snippets @@ -173,8 +173,7 @@ "Log to the console": { "prefix": "log", "body": [ - "console.log($1);", - "$0" + "console.log($1);" ], "description": "Log to the console" }, @@ -182,7 +181,6 @@ "prefix": "warn", "body": [ "console.warn($1);", - "$0" ], "description": "Log warning to the console" }, @@ -190,7 +188,6 @@ "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 40c0a9b33c1..60f6ce87547 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/84422d92e164c379ed817ef8e1d6c35b61de233e", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/fa4e0d3a918db0eab8e5c5be952f3bd649968456", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -3532,23 +3532,6 @@ } } }, - { - "match": "(?x)(? { + 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/jsonMain.ts b/extensions/json-language-features/client/src/jsonClient.ts similarity index 81% rename from extensions/json-language-features/client/src/jsonMain.ts rename to extensions/json-language-features/client/src/jsonClient.ts index 3faf0d19630..a2ff8c1b78a 100644 --- a/extensions/json-language-features/client/src/jsonMain.ts +++ b/extensions/json-language-features/client/src/jsonClient.ts @@ -2,11 +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 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(); @@ -16,13 +12,13 @@ import { ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString, } from 'vscode'; import { - LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, + LanguageClientOptions, RequestType, NotificationType, DidChangeConfigurationNotification, HandleDiagnosticsSignature, ResponseError, DocumentRangeFormattingParams, - DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, ProvideHoverSignature + DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, ProvideHoverSignature, CommonLanguageClient } from 'vscode-languageclient'; -import TelemetryReporter from 'vscode-extension-telemetry'; import { hash } from './utils/hash'; +import { RequestService, joinPath } from './requests'; namespace VSCodeContentRequest { export const type: RequestType = new RequestType('vscode/content'); @@ -53,12 +49,6 @@ namespace ResultLimitReachedNotification { export const type: NotificationType = new NotificationType('json/resultLimitReached'); } -interface IPackageInfo { - name: string; - version: string; - aiKey: string; -} - interface Settings { json?: { schemas?: JSONSchemaSettings[]; @@ -83,29 +73,27 @@ namespace SettingIds { export const maxItemsComputed = 'json.maxItemsComputed'; } -let telemetryReporter: TelemetryReporter | undefined; +export interface TelemetryReporter { + sendTelemetryEvent(eventName: string, properties?: { + [key: string]: string; + }, measurements?: { + [key: string]: number; + }): void; +} -export function activate(context: ExtensionContext) { +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 packageInfo = getPackageInfo(context); - telemetryReporter = packageInfo && new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey); - - const serverMain = readJSONFile(context.asAbsolutePath('./server/package.json')).main; - const serverModule = context.asAbsolutePath(path.join('server', serverMain)); - - // The debug options for the server - const 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 - const serverOptions: ServerOptions = { - run: { module: serverModule, transport: TransportKind.ipc }, - debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions } - }; const documentSelector = ['json', 'jsonc']; @@ -119,6 +107,7 @@ export function activate(context: ExtensionContext) { toDispose.push(schemaResolutionErrorStatusBarItem); const fileSchemaErrors = new Map(); + let schemaDownloadEnabled = true; // Options to control the language client const clientOptions: LanguageClientOptions = { @@ -139,7 +128,7 @@ export function activate(context: ExtensionContext) { didChangeConfiguration: () => client.sendNotification(DidChangeConfigurationNotification.type, { settings: getSettings() }) }, handleDiagnostics: (uri: Uri, diagnostics: Diagnostic[], next: HandleDiagnosticsSignature) => { - const schemaErrorIndex = diagnostics.findIndex(candidate => candidate.code === /* SchemaResolveError */ 0x300); + const schemaErrorIndex = diagnostics.findIndex(isSchemaResolveError); if (schemaErrorIndex === -1) { fileSchemaErrors.delete(uri.toString()); @@ -149,6 +138,10 @@ export function activate(context: ExtensionContext) { 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(); } @@ -197,51 +190,39 @@ export function activate(context: ExtensionContext) { }; // Create the language client and start the client. - const client = new LanguageClient('json', localize('jsonserver.name', 'JSON Language Server'), serverOptions, clientOptions); + 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 } = {}; - let schemaDownloadEnabled = true; // handle content request client.onRequest(VSCodeContentRequest.type, (uriPath: string) => { const uri = Uri.parse(uriPath); if (uri.scheme === 'untitled') { - return Promise.reject(new Error(localize('untitled.schema', 'Unable to load {0}', uri.toString()))); + return Promise.reject(new ResponseError(3, localize('untitled.schema', 'Unable to load {0}', uri.toString()))); } if (uri.scheme !== 'http' && uri.scheme !== 'https') { - if (schemaDownloadEnabled) { - return workspace.openTextDocument(uri).then(doc => { - schemaDocuments[uri.toString()] = true; - return doc.getText(); - }, error => { - return Promise.reject(error); - }); - } else { - return Promise.reject(localize('schemaDownloadDisabled', 'Downloading schemas is disabled through setting \'{0}\'', SettingIds.enableSchemaDownload)); - } - } else { - if (telemetryReporter && uri.authority === 'schema.management.azure.com') { + 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" } } */ - telemetryReporter.sendTelemetryEvent('json.schema', { schemaURL: uriPath }); + runtime.telemetry.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)); - }); + return runtime.http.getContent(uriPath); + } else { + return Promise.reject(new ResponseError(1, localize('schemaDownloadDisabled', 'Downloading schemas is disabled through setting \'{0}\'', SettingIds.enableSchemaDownload))); } }); @@ -282,7 +263,7 @@ export function activate(context: ExtensionContext) { 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); + const schemaErrorIndex = diagnostics.findIndex(isSchemaResolveError); if (schemaErrorIndex !== -1) { // Show schema resolution errors in status bar only; ref: #51032 const schemaResolveDiagnostic = diagnostics[schemaErrorIndex]; @@ -337,7 +318,7 @@ export function activate(context: ExtensionContext) { return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then( client.protocol2CodeConverter.asTextEdits, (error) => { - client.logFailedRequest(DocumentRangeFormattingRequest.type, error); + client.handleFailedRequest(DocumentRangeFormattingRequest.type, error, []); return Promise.resolve([]); } ); @@ -372,12 +353,6 @@ export function activate(context: ExtensionContext) { } - - -export function deactivate(): Promise { - return telemetryReporter ? telemetryReporter.dispose() : Promise.resolve(null); -} - function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[] { const associations: ISchemaAssociation[] = []; extensions.all.forEach(extension => { @@ -390,9 +365,10 @@ function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[] if (typeof fileMatch === 'string') { fileMatch = [fileMatch]; } - if (Array.isArray(fileMatch) && url) { - if (url[0] === '.' && url[1] === '/') { - url = Uri.file(path.join(extension.extensionPath, url)).toString(); + 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] === '%') { @@ -404,7 +380,7 @@ function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[] } return fm; }); - associations.push({ fileMatch, uri: url }); + associations.push({ fileMatch, uri }); } }); } @@ -506,39 +482,18 @@ function getSettings(): Settings { return settings; } -function getSchemaId(schema: JSONSchemaSettings, folderUri?: Uri) { +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 = folderUri.with({ path: path.posix.join(folderUri.path, url) }).toString(); + url = joinPath(folderUri, url).toString(); } return url; } -function getPackageInfo(context: ExtensionContext): IPackageInfo | undefined { - const 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 {}; - } -} - function isThenable(obj: ProviderResult): obj is Thenable { return obj && (obj)['then']; } @@ -548,3 +503,7 @@ function updateMarkdownString(h: MarkdownString): MarkdownString { n.isTrusted = h.isTrusted; return n; } + +function isSchemaResolveError(d: Diagnostic) { + return d.code === /* SchemaResolveError */ 0x300; +} diff --git a/extensions/json-language-features/client/src/node/jsonClientMain.ts b/extensions/json-language-features/client/src/node/jsonClientMain.ts new file mode 100644 index 00000000000..6e9aed7f105 --- /dev/null +++ b/extensions/json-language-features/client/src/node/jsonClientMain.ts @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ExtensionContext } from 'vscode'; +import { startClient, LanguageClientConstructor } from '../jsonClient'; +import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient } from 'vscode-languageclient/node'; + +import * as fs from 'fs'; +import { xhr, XHRResponse, getErrorStatusDescription } from 'request-light'; + +import TelemetryReporter from 'vscode-extension-telemetry'; +import { RequestService } from '../requests'; + +let telemetry: TelemetryReporter | undefined; + +// this method is called when vs code is activated +export function activate(context: ExtensionContext) { + + const clientPackageJSON = getPackageInfo(context); + telemetry = new TelemetryReporter(clientPackageJSON.name, clientPackageJSON.version, clientPackageJSON.aiKey); + + const serverMain = `./server/${clientPackageJSON.main.indexOf('/dist/') !== -1 ? 'dist' : 'out'}/node/jsonServerMain`; + const serverModule = context.asAbsolutePath(serverMain); + + // The debug options for the server + const debugOptions = { execArgv: ['--nolazy', '--inspect=6044'] }; + + // If the extension is launch in debug mode the debug server options are use + // Otherwise the run options are used + const serverOptions: ServerOptions = { + run: { module: serverModule, transport: TransportKind.ipc }, + debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions } + }; + + const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => { + return new LanguageClient(id, name, serverOptions, clientOptions); + }; + + startClient(context, newLanguageClient, { http: getHTTPRequestService(), telemetry }); +} + +export function deactivate(): Promise { + return telemetry ? telemetry.dispose() : Promise.resolve(null); +} + +interface IPackageInfo { + name: string; + version: string; + aiKey: string; + main: string; +} + +function getPackageInfo(context: ExtensionContext): IPackageInfo { + const location = context.asAbsolutePath('./package.json'); + try { + return JSON.parse(fs.readFileSync(location).toString()); + } catch (e) { + console.log(`Problems reading ${location}: ${e}`); + return { name: '', version: '', aiKey: '', main: '' }; + } +} + +function getHTTPRequestService(): RequestService { + return { + getContent(uri: string, _encoding?: string) { + const headers = { 'Accept-Encoding': 'gzip, deflate' }; + return xhr({ url: uri, followRedirects: 5, headers }).then(response => { + return response.responseText; + }, (error: XHRResponse) => { + return Promise.reject(error.responseText || getErrorStatusDescription(error.status) || error.toString()); + }); + } + }; +} diff --git a/extensions/json-language-features/client/src/requests.ts b/extensions/json-language-features/client/src/requests.ts new file mode 100644 index 00000000000..be05fae6aef --- /dev/null +++ b/extensions/json-language-features/client/src/requests.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Uri } from 'vscode'; + +export interface RequestService { + getContent(uri: string, encoding?: string): Thenable; +} + +export function getScheme(uri: string) { + return uri.substr(0, uri.indexOf(':')); +} + +export function dirname(uri: string) { + const lastIndexOfSlash = uri.lastIndexOf('/'); + return lastIndexOfSlash !== -1 ? uri.substr(0, lastIndexOfSlash) : ''; +} + +export function basename(uri: string) { + const lastIndexOfSlash = uri.lastIndexOf('/'); + return uri.substr(lastIndexOfSlash + 1); +} + +const Slash = '/'.charCodeAt(0); +const Dot = '.'.charCodeAt(0); + +export function isAbsolutePath(path: string) { + return path.charCodeAt(0) === Slash; +} + +export function resolvePath(uri: Uri, path: string): Uri { + if (isAbsolutePath(path)) { + return uri.with({ path: normalizePath(path.split('/')) }); + } + return joinPath(uri, path); +} + +export function normalizePath(parts: string[]): string { + const newParts: string[] = []; + for (const part of parts) { + if (part.length === 0 || part.length === 1 && part.charCodeAt(0) === Dot) { + // ignore + } else if (part.length === 2 && part.charCodeAt(0) === Dot && part.charCodeAt(1) === Dot) { + newParts.pop(); + } else { + newParts.push(part); + } + } + if (parts.length > 1 && parts[parts.length - 1].length === 0) { + newParts.push(''); + } + let res = newParts.join('/'); + if (parts[0].length === 0) { + res = '/' + res; + } + return res; +} + + +export function joinPath(uri: Uri, ...paths: string[]): Uri { + const parts = uri.path.split('/'); + for (let path of paths) { + parts.push(...path.split('/')); + } + return uri.with({ path: normalizePath(parts) }); +} diff --git a/extensions/json-language-features/extension-browser.webpack.config.js b/extensions/json-language-features/extension-browser.webpack.config.js new file mode 100644 index 00000000000..900ef6a3b12 --- /dev/null +++ b/extensions/json-language-features/extension-browser.webpack.config.js @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withBrowserDefaults = require('../shared.webpack.config').browser; +const path = require('path'); + +module.exports = withBrowserDefaults({ + target: 'webworker', + context: path.join(__dirname, 'client'), + entry: { + extension: './src/browser/jsonClientMain.ts' + }, + output: { + filename: 'jsonClientMain.js', + path: path.join(__dirname, 'client', 'dist', 'browser') + } +}); diff --git a/extensions/json-language-features/extension.webpack.config.js b/extensions/json-language-features/extension.webpack.config.js index 39e5e567a7a..13a05952995 100644 --- a/extensions/json-language-features/extension.webpack.config.js +++ b/extensions/json-language-features/extension.webpack.config.js @@ -14,11 +14,11 @@ const webpack = require('webpack'); const config = withDefaults({ context: path.join(__dirname, 'client'), entry: { - extension: './src/jsonMain.ts', + extension: './src/node/jsonClientMain.ts' }, output: { - filename: 'jsonMain.js', - path: path.join(__dirname, 'client', 'dist') + filename: 'jsonClientMain.js', + path: path.join(__dirname, 'client', 'dist', 'node') } }); diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index 2868c0c01ac..db003f05121 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -14,7 +14,8 @@ "onLanguage:json", "onLanguage:jsonc" ], - "main": "./client/out/jsonMain", + "main": "./client/out/node/jsonClientMain", + "browser": "./client/dist/browser/jsonClientMain", "enableProposedApi": true, "scripts": { "compile": "gulp compile-extension:json-language-features-client compile-extension:json-language-features-server", @@ -128,7 +129,7 @@ "dependencies": { "request-light": "^0.3.0", "vscode-extension-telemetry": "0.1.1", - "vscode-languageclient": "^6.1.3", + "vscode-languageclient": "7.0.0-next.5.1", "vscode-nls": "^4.1.2" }, "devDependencies": { diff --git a/extensions/json-language-features/package.nls.json b/extensions/json-language-features/package.nls.json index abf7d17b925..59729a0ee99 100644 --- a/extensions/json-language-features/package.nls.json +++ b/extensions/json-language-features/package.nls.json @@ -3,7 +3,7 @@ "description": "Provides rich language support for JSON files.", "json.schemas.desc": "Associate schemas to JSON files in the current project", "json.schemas.url.desc": "A URL to a schema or a relative path to a schema in the current directory", - "json.schemas.fileMatch.desc": "An array of file patterns to match against when resolving JSON files to schemas. `*` can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there at least one matching pattern and the last matching pattern is not an exclusion pattern.", + "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", diff --git a/extensions/json-language-features/server/README.md b/extensions/json-language-features/server/README.md index f68b03fcceb..d04ff913e92 100644 --- a/extensions/json-language-features/server/README.md +++ b/extensions/json-language-features/server/README.md @@ -62,14 +62,14 @@ The server supports the following settings: - json - `format` - `enable`: Whether the server should register the formatting support. This option is only applicable if the client supports *dynamicRegistration* for *rangeFormatting* and `initializationOptions.provideFormatter` is not defined. - - `schema`: Configures association of file names to schema URL or schemas and/or associations of schema URL to schema content. - - `fileMatch`: an array of file names or paths (separated by `/`). `*` can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there 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 foldig ranges and otline symbols to be computed (for performance reasons) + - `schemas`: Configures association of file names to schema URL or schemas and/or associations of schema URL to schema content. + - `fileMatch`: an array of file names or paths (separated by `/`). `*` can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there is at least one matching pattern and the last matching pattern is not an exclusion pattern. + - `url`: The URL of the schema, optional when also a schema is provided. + - `schema`: The schema content. + - `resultLimit`: The max number folding ranges and outline symbols to be computed (for performance reasons) ```json - { + { "http": { "proxy": "", "proxyStrictSSL": true @@ -86,7 +86,7 @@ The server supports the following settings: ], "url": "http://json.schemastore.org/foo", "schema": { - "type": "array" + "type": "array" } } ] @@ -160,7 +160,7 @@ Notification: ### Item Limit If the setting `resultLimit` is set, the JSON language server will limit the number of folding ranges and document symbols computed. -When the limit is reached, a notification `json/resultLimitReached` is sent that can be shown that camn be shown to the user. +When the limit is reached, a notification `json/resultLimitReached` is sent that can be shown that can be shown to the user. Notification: - method: 'json/resultLimitReached' @@ -180,7 +180,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 00000000000..b1ae74c7b60 --- /dev/null +++ b/extensions/json-language-features/server/extension-browser.webpack.config.js @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withBrowserDefaults = require('../../shared.webpack.config').browser; +const path = require('path'); + +module.exports = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/browser/jsonServerMain.ts', + }, + output: { + filename: 'jsonServerMain.js', + path: path.join(__dirname, 'dist', 'browser'), + libraryTarget: 'var' + } +}); diff --git a/extensions/json-language-features/server/extension.webpack.config.js b/extensions/json-language-features/server/extension.webpack.config.js index 2c90a0b0751..50a7c3d545f 100644 --- a/extensions/json-language-features/server/extension.webpack.config.js +++ b/extensions/json-language-features/server/extension.webpack.config.js @@ -14,11 +14,11 @@ const webpack = require('webpack'); const config = withDefaults({ context: path.join(__dirname), entry: { - extension: './src/jsonServerMain.ts', + extension: './src/node/jsonServerMain.ts', }, output: { filename: 'jsonServerMain.js', - path: path.join(__dirname, 'dist') + path: path.join(__dirname, 'dist', 'node'), } }); diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index b48b844c03e..e3e507c7b6c 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -10,13 +10,13 @@ "bin": { "vscode-json-languageserver": "./bin/vscode-json-languageserver" }, - "main": "./out/jsonServerMain", + "main": "./out/node/jsonServerMain", "dependencies": { "jsonc-parser": "^2.2.1", "request-light": "^0.3.0", - "vscode-json-languageservice": "^3.6.0", - "vscode-languageserver": "^6.1.1", - "vscode-uri": "^2.1.1" + "vscode-json-languageservice": "^3.8.0", + "vscode-languageserver": "7.0.0-next.3", + "vscode-uri": "^2.1.2" }, "devDependencies": { "@types/mocha": "2.2.33", diff --git a/extensions/json-language-features/server/src/browser/jsonServerMain.ts b/extensions/json-language-features/server/src/browser/jsonServerMain.ts new file mode 100644 index 00000000000..5394412877c --- /dev/null +++ b/extensions/json-language-features/server/src/browser/jsonServerMain.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createConnection, BrowserMessageReader, BrowserMessageWriter } from 'vscode-languageserver/browser'; +import { startServer } from '../jsonServer'; + +declare let self: any; + +const messageReader = new BrowserMessageReader(self); +const messageWriter = new BrowserMessageWriter(self); + +const connection = createConnection(messageReader, messageWriter); + +startServer(connection, {}); diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts new file mode 100644 index 00000000000..78151f746e7 --- /dev/null +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -0,0 +1,504 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; + +interface ISchemaAssociations { + [pattern: string]: string[]; +} + +interface ISchemaAssociation { + fileMatch: string[]; + uri: string; +} + +namespace SchemaAssociationNotification { + export const type: NotificationType = new NotificationType('json/schemaAssociations'); +} + +namespace VSCodeContentRequest { + export const type: RequestType = new RequestType('vscode/content'); +} + +namespace SchemaContentChangeNotification { + export const type: NotificationType = new NotificationType('json/schemaContent'); +} + +namespace ResultLimitReachedNotification { + export const type: NotificationType = new NotificationType('json/resultLimitReached'); +} + +namespace ForceValidateRequest { + export const type: RequestType = new RequestType('json/validate'); +} + + +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 | ISchemaAssociation[] | 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 cc8c01acfa5..00000000000 --- a/extensions/json-language-features/server/src/jsonServerMain.ts +++ /dev/null @@ -1,532 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { - createConnection, IConnection, - TextDocuments, InitializeParams, InitializeResult, NotificationType, RequestType, - DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind, TextEdit -} from 'vscode-languageserver'; - -import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription } from 'request-light'; -import * as fs from 'fs'; -import { URI } from 'vscode-uri'; -import * as URL from 'url'; -import { posix } from 'path'; -import { setTimeout, clearTimeout } from 'timers'; -import { formatError, runSafe, runSafeAsync } from './utils/runner'; -import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, SchemaRequestService, Diagnostic, Range, Position } from 'vscode-json-languageservice'; -import { getLanguageModelCache } from './languageModelCache'; - -interface ISchemaAssociations { - [pattern: string]: string[]; -} - -interface ISchemaAssociation { - fileMatch: string[]; - uri: string; -} - -namespace SchemaAssociationNotification { - export const type: NotificationType = new NotificationType('json/schemaAssociations'); -} - -namespace VSCodeContentRequest { - export const type: RequestType = new RequestType('vscode/content'); -} - -namespace SchemaContentChangeNotification { - export const type: NotificationType = new NotificationType('json/schemaContent'); -} - -namespace ResultLimitReachedNotification { - export const type: NotificationType = new NotificationType('json/resultLimitReached'); -} - -namespace ForceValidateRequest { - export const type: RequestType = new RequestType('json/validate'); -} - -// Create a connection for the server -const connection: IConnection = createConnection(); - -process.on('unhandledRejection', (e: any) => { - console.error(formatError(`Unhandled exception`, e)); -}); -process.on('uncaughtException', (e: any) => { - console.error(formatError(`Unhandled exception`, e)); -}); - - -console.log = connection.console.log.bind(connection.console); -console.error = connection.console.error.bind(connection.console); - -const workspaceContext = { - resolveRelativePath: (relativePath: string, resource: string) => { - return URL.resolve(resource, relativePath); - } -}; - -const fileRequestService: SchemaRequestService = (uri: string) => { - const fsPath = URI.parse(uri).fsPath; - return new Promise((c, e) => { - fs.readFile(fsPath, 'UTF-8', (err, result) => { - err ? e(err.message || err.toString()) : c(result.toString()); - }); - }); -}; - -const httpRequestService: SchemaRequestService = (uri: string) => { - const headers = { 'Accept-Encoding': 'gzip, deflate' }; - return xhr({ url: uri, followRedirects: 5, headers }).then(response => { - return response.responseText; - }, (error: XHRResponse) => { - return Promise.reject(error.responseText || getErrorStatusDescription(error.status) || error.toString()); - }); -}; - -function getSchemaRequestService(handledSchemas: string[] = ['https', 'http', 'file']) { - const builtInHandlers: { [protocol: string]: SchemaRequestService } = {}; - for (let protocol of handledSchemas) { - if (protocol === 'file') { - builtInHandlers[protocol] = fileRequestService; - } else if (protocol === 'http' || protocol === 'https') { - builtInHandlers[protocol] = httpRequestService; - } - } - return (uri: string): Thenable => { - const protocol = uri.substr(0, uri.indexOf(':')); - - const builtInHandler = builtInHandlers[protocol]; - if (builtInHandler) { - return builtInHandler(uri); - } - return connection.sendRequest(VSCodeContentRequest.type, uri).then(responseText => { - return responseText; - }, error => { - return Promise.reject(error.message); - }); - }; -} - -// create the JSON language service -let languageService = getLanguageService({ - workspaceContext, - contributions: [], - clientCapabilities: ClientCapabilities.LATEST -}); - -// Create a text document manager. -const documents = new TextDocuments(TextDocument); - -// Make the text document manager listen on the connection -// for open, change and close text document events -documents.listen(connection); - -let clientSnippetSupport = false; -let dynamicFormatterRegistration = false; -let hierarchicalDocumentSymbolSupport = false; - -let foldingRangeLimitDefault = Number.MAX_VALUE; -let foldingRangeLimit = Number.MAX_VALUE; -let resultLimit = Number.MAX_VALUE; -let formatterMaxNumberOfEdits = Number.MAX_VALUE; - -// After the server has started the client sends an initialize request. The server receives -// in the passed params the rootPath of the workspace plus the client capabilities. -connection.onInitialize((params: InitializeParams): InitializeResult => { - - const handledProtocols = params.initializationOptions?.handledSchemaProtocols; - - languageService = getLanguageService({ - schemaRequestService: getSchemaRequestService(handledProtocols), - workspaceContext, - contributions: [], - clientCapabilities: params.capabilities - }); - - function getClientCapability(name: string, def: T) { - const keys = name.split('.'); - let c: any = params.capabilities; - for (let i = 0; c && i < keys.length; i++) { - if (!c.hasOwnProperty(keys[i])) { - return def; - } - c = c[keys[i]]; - } - return c; - } - - clientSnippetSupport = getClientCapability('textDocument.completion.completionItem.snippetSupport', false); - dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof params.initializationOptions?.provideFormatter !== 'boolean'); - foldingRangeLimitDefault = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); - hierarchicalDocumentSymbolSupport = getClientCapability('textDocument.documentSymbol.hierarchicalDocumentSymbolSupport', false); - formatterMaxNumberOfEdits = params.initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE; - const capabilities: ServerCapabilities = { - textDocumentSync: TextDocumentSyncKind.Incremental, - completionProvider: clientSnippetSupport ? { - resolveProvider: 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; -} - -namespace LimitExceededWarnings { - const pendingWarnings: { [uri: string]: { features: { [name: string]: string }; timeout?: NodeJS.Timeout; } } = {}; - - export function cancel(uri: string) { - const warning = pendingWarnings[uri]; - if (warning && warning.timeout) { - clearTimeout(warning.timeout); - delete pendingWarnings[uri]; - } - } - - export function onResultLimitExceeded(uri: string, resultLimit: number, name: string) { - return () => { - let warning = pendingWarnings[uri]; - if (warning) { - if (!warning.timeout) { - // already shown - return; - } - warning.features[name] = name; - warning.timeout.refresh(); - } else { - warning = { features: { [name]: name } }; - warning.timeout = setTimeout(() => { - connection.sendNotification(ResultLimitReachedNotification.type, `${posix.basename(uri)}: For performance reasons, ${Object.keys(warning.features).join(' and ')} have been limited to ${resultLimit} items.`); - warning.timeout = undefined; - }, 2000); - pendingWarnings[uri] = warning; - } - }; - } -} - -let jsonConfigurationSettings: JSONSchemaSettings[] | undefined = undefined; -let schemaAssociations: ISchemaAssociations | ISchemaAssociation[] | 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) { - 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.onCompletionResolve((completionItem, token) => { - return runSafeAsync(() => { - return languageService.doResolve(completionItem); - }, completionItem, `Error while resolving completion proposal`, token); -}); - -connection.onHover((textDocumentPositionParams, token) => { - return runSafeAsync(async () => { - const document = documents.get(textDocumentPositionParams.textDocument.uri); - if (document) { - const jsonDocument = getJSONDocument(document); - return languageService.doHover(document, textDocumentPositionParams.position, jsonDocument); - } - return null; - }, null, `Error while computing hover for ${textDocumentPositionParams.textDocument.uri}`, token); -}); - -connection.onDocumentSymbol((documentSymbolParams, token) => { - return runSafe(() => { - const document = documents.get(documentSymbolParams.textDocument.uri); - if (document) { - const jsonDocument = getJSONDocument(document); - const onResultLimitExceeded = LimitExceededWarnings.onResultLimitExceeded(document.uri, resultLimit, 'document symbols'); - if (hierarchicalDocumentSymbolSupport) { - return languageService.findDocumentSymbols2(document, jsonDocument, { resultLimit, onResultLimitExceeded }); - } else { - return languageService.findDocumentSymbols(document, jsonDocument, { resultLimit, onResultLimitExceeded }); - } - } - return []; - }, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token); -}); - -connection.onDocumentRangeFormatting((formatParams, token) => { - return runSafe(() => { - const document = documents.get(formatParams.textDocument.uri); - if (document) { - const edits = languageService.format(document, formatParams.range, formatParams.options); - if (edits.length > formatterMaxNumberOfEdits) { - const newText = TextDocument.applyEdits(document, edits); - return [TextEdit.replace(Range.create(Position.create(0, 0), document.positionAt(document.getText().length)), 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/node/jsonServerMain.ts b/extensions/json-language-features/server/src/node/jsonServerMain.ts new file mode 100644 index 00000000000..94192b4b1ce --- /dev/null +++ b/extensions/json-language-features/server/src/node/jsonServerMain.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createConnection, Connection } from 'vscode-languageserver/node'; +import { formatError } from '../utils/runner'; +import { startServer } from '../jsonServer'; +import { RequestService } from '../requests'; + +import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription } from 'request-light'; +import { URI as Uri } from 'vscode-uri'; +import * as fs from 'fs'; + +// Create a connection for the server. +const connection: Connection = createConnection(); + +console.log = connection.console.log.bind(connection.console); +console.error = connection.console.error.bind(connection.console); + +process.on('unhandledRejection', (e: any) => { + connection.console.error(formatError(`Unhandled exception`, e)); +}); + +function getHTTPRequestService(): RequestService { + return { + getContent(uri: string, _encoding?: string) { + const headers = { 'Accept-Encoding': 'gzip, deflate' }; + return xhr({ url: uri, followRedirects: 5, headers }).then(response => { + return response.responseText; + }, (error: XHRResponse) => { + return Promise.reject(error.responseText || getErrorStatusDescription(error.status) || error.toString()); + }); + } + }; +} + +function getFileRequestService(): RequestService { + return { + getContent(location: string, encoding?: string) { + return new Promise((c, e) => { + const uri = Uri.parse(location); + fs.readFile(uri.fsPath, encoding, (err, buf) => { + if (err) { + return e(err); + } + c(buf.toString()); + }); + }); + } + }; +} + + +startServer(connection, { file: getFileRequestService(), http: getHTTPRequestService(), configureHttpRequests }); diff --git a/extensions/json-language-features/server/src/requests.ts b/extensions/json-language-features/server/src/requests.ts new file mode 100644 index 00000000000..a87cf92483f --- /dev/null +++ b/extensions/json-language-features/server/src/requests.ts @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from 'vscode-uri'; + +export interface RequestService { + getContent(uri: string, encoding?: string): Promise; +} + +export function getScheme(uri: string) { + return uri.substr(0, uri.indexOf(':')); +} + +export function dirname(uri: string) { + const lastIndexOfSlash = uri.lastIndexOf('/'); + return lastIndexOfSlash !== -1 ? uri.substr(0, lastIndexOfSlash) : ''; +} + +export function basename(uri: string) { + const lastIndexOfSlash = uri.lastIndexOf('/'); + return uri.substr(lastIndexOfSlash + 1); +} + + +const Slash = '/'.charCodeAt(0); +const Dot = '.'.charCodeAt(0); + +export function extname(uri: string) { + for (let i = uri.length - 1; i >= 0; i--) { + const ch = uri.charCodeAt(i); + if (ch === Dot) { + if (i > 0 && uri.charCodeAt(i - 1) !== Slash) { + return uri.substr(i); + } else { + break; + } + } else if (ch === Slash) { + break; + } + } + return ''; +} + +export function isAbsolutePath(path: string) { + return path.charCodeAt(0) === Slash; +} + +export function resolvePath(uriString: string, path: string): string { + if (isAbsolutePath(path)) { + const uri = URI.parse(uriString); + const parts = path.split('/'); + return uri.with({ path: normalizePath(parts) }).toString(); + } + return joinPath(uriString, path); +} + +export function normalizePath(parts: string[]): string { + const newParts: string[] = []; + for (const part of parts) { + if (part.length === 0 || part.length === 1 && part.charCodeAt(0) === Dot) { + // ignore + } else if (part.length === 2 && part.charCodeAt(0) === Dot && part.charCodeAt(1) === Dot) { + newParts.pop(); + } else { + newParts.push(part); + } + } + if (parts.length > 1 && parts[parts.length - 1].length === 0) { + newParts.push(''); + } + let res = newParts.join('/'); + if (parts[0].length === 0) { + res = '/' + res; + } + return res; +} + +export function joinPath(uriString: string, ...paths: string[]): string { + const uri = URI.parse(uriString); + const parts = uri.path.split('/'); + for (let path of paths) { + parts.push(...path.split('/')); + } + return uri.with({ path: normalizePath(parts) }).toString(); +} diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index 4d15a61184f..281f8276eb3 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -80,46 +80,51 @@ request-light@^0.3.0: https-proxy-agent "^2.2.4" vscode-nls "^4.1.1" -vscode-json-languageservice@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.6.0.tgz#133a1e2c3a3dffe38564a1ba948516805c3c1869" - integrity sha512-dXzFywypUZ9T0tjr4fREZiknXDz6vAGx1zsxbQY1+9DOpjMfbz0VLP873KmcbuvL4K3nseKTxc4TKHu8kLXRMw== +vscode-json-languageservice@^3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.8.0.tgz#c7e7283f993e3db39fa5501407b023ada6fd3ae3" + integrity sha512-sYz5JElJMIlPoqhrRfG3VKnDjnPinLdblIiEVsJgTz1kj2hWD2q5BSbo+evH/5/jKDXDLfA8kb0lHC4vd5g5zg== dependencies: 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.1" + vscode-uri "^2.1.2" -vscode-jsonrpc@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz#9bab9c330d89f43fc8c1e8702b5c36e058a01794" - integrity sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A== +vscode-jsonrpc@6.0.0-next.2: + version "6.0.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.2.tgz#3d73f86d812304cb91b9fb1efee40ec60b09ed7f" + integrity sha512-dKQXRYNUY6BHALQJBJlyZyv9oWlYpbJ2vVoQNNVNPLAYQ3hzNp4zy+iSo7zGx1BPXByArJQDWTKLQh8dz3dnNw== -vscode-languageserver-protocol@^3.15.3: - version "3.15.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz#3fa9a0702d742cf7883cb6182a6212fcd0a1d8bb" - integrity sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw== +vscode-languageserver-protocol@3.16.0-next.4: + version "3.16.0-next.4" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.4.tgz#8f8b1b831d4dfd9b26aa1ba3d2a32c427a91c99f" + integrity sha512-6GmPUp2MhJy2H1CTWp2B40Pa9BeC9glrXWmQWVG6A/0V9UbcAjVC9m56znm2GL32iyLDIprTBe8gBvvvcjbpaQ== dependencies: - vscode-jsonrpc "^5.0.1" - vscode-languageserver-types "3.15.1" + 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.15.1, vscode-languageserver-types@^3.15.1: +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@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-6.1.1.tgz#d76afc68172c27d4327ee74332b468fbc740d762" - integrity sha512-DueEpkUAkD5XTR4MLYNr6bQIp/UFR0/IPApgXU3YfCBCB08u2sm9hRCs6DxYZELkk++STPjpcjksR2H8qI3cDQ== +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.3" + vscode-languageserver-protocol "3.16.0-next.4" vscode-nls@^4.1.1: version "4.1.1" @@ -131,7 +136,7 @@ vscode-nls@^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 93d78c8c001..29972b7df03 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -120,31 +120,31 @@ vscode-extension-telemetry@0.1.1: dependencies: applicationinsights "1.0.8" -vscode-jsonrpc@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz#9bab9c330d89f43fc8c1e8702b5c36e058a01794" - integrity sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A== +vscode-jsonrpc@6.0.0-next.2: + version "6.0.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.2.tgz#3d73f86d812304cb91b9fb1efee40ec60b09ed7f" + integrity sha512-dKQXRYNUY6BHALQJBJlyZyv9oWlYpbJ2vVoQNNVNPLAYQ3hzNp4zy+iSo7zGx1BPXByArJQDWTKLQh8dz3dnNw== -vscode-languageclient@^6.1.3: - version "6.1.3" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-6.1.3.tgz#c979c5bb5855714a0307e998c18ca827c1b3953a" - integrity sha512-YciJxk08iU5LmWu7j5dUt9/1OLjokKET6rME3cI4BRpiF6HZlusm2ZwPt0MYJ0lV5y43sZsQHhyon2xBg4ZJVA== +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.3" + vscode-languageserver-protocol "3.16.0-next.4" -vscode-languageserver-protocol@^3.15.3: - version "3.15.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz#3fa9a0702d742cf7883cb6182a6212fcd0a1d8bb" - integrity sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw== +vscode-languageserver-protocol@3.16.0-next.4: + version "3.16.0-next.4" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.4.tgz#8f8b1b831d4dfd9b26aa1ba3d2a32c427a91c99f" + integrity sha512-6GmPUp2MhJy2H1CTWp2B40Pa9BeC9glrXWmQWVG6A/0V9UbcAjVC9m56znm2GL32iyLDIprTBe8gBvvvcjbpaQ== dependencies: - vscode-jsonrpc "^5.0.1" - vscode-languageserver-types "3.15.1" + vscode-jsonrpc "6.0.0-next.2" + vscode-languageserver-types "3.16.0-next.2" -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-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" diff --git a/extensions/json/package.json b/extensions/json/package.json index 901584e1d90..73902a8ec49 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -26,6 +26,7 @@ ".webmanifest", ".js.map", ".css.map", + ".ts.map", ".har", ".jslintrc", ".jsonld" @@ -47,14 +48,14 @@ "JSON with Comments" ], "extensions": [ - ".hintrc", - ".babelrc", ".jsonc", ".eslintrc", ".eslintrc.json", ".jsfmtrc", ".jshintrc", - ".swcrc" + ".swcrc", + ".hintrc", + ".babelrc" ], "configuration": "./language-configuration.json" } diff --git a/extensions/json/test/colorize-results/test_json.json b/extensions/json/test/colorize-results/test_json.json index fc3dec21721..6f94bec76e6 100644 --- a/extensions/json/test/colorize-results/test_json.json +++ b/extensions/json/test/colorize-results/test_json.json @@ -389,7 +389,7 @@ "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json string.quoted.double.json constant.character.escape.json", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" @@ -1165,4 +1165,4 @@ "hc_black": "default: #FFFFFF" } } -] +] \ No newline at end of file diff --git a/extensions/log/cgmanifest.json b/extensions/log/cgmanifest.json index d84272fbe32..0fe21e5c2c4 100644 --- a/extensions/log/cgmanifest.json +++ b/extensions/log/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "vscode-logfile-highlighter", "repositoryUrl": "https://github.com/emilast/vscode-logfile-highlighter", - "commitHash": "fbe9797ea7889ee55def6da8dee86ba625ca9759" + "commitHash": "5dcab1c304110b605041824cde3810c6ef305477" } }, "license": "MIT", - "version": "2.6.0" + "version": "2.8.0" } ], "version": 1 diff --git a/extensions/log/syntaxes/log.tmLanguage.json b/extensions/log/syntaxes/log.tmLanguage.json index 0c511935ad3..4882a761e6f 100644 --- a/extensions/log/syntaxes/log.tmLanguage.json +++ b/extensions/log/syntaxes/log.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/emilast/vscode-logfile-highlighter/commit/fbe9797ea7889ee55def6da8dee86ba625ca9759", + "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,7 +29,7 @@ "name": "markup.changed log.debug" }, { - "match": "\\bD/", + "match": "(?<=^[\\s\\d\\p]*)\\bD\\b", "name": "markup.changed log.debug" }, { @@ -41,7 +41,7 @@ "name": "markup.inserted log.info" }, { - "match": "\\bI/", + "match": "(?<=^[\\s\\d\\p]*)\\bI\\b", "name": "markup.inserted log.info" }, { @@ -53,7 +53,7 @@ "name": "markup.deleted log.warning" }, { - "match": "\\bW/", + "match": "(?<=^[\\s\\d\\p]*)\\bW\\b", "name": "markup.deleted log.warning" }, { @@ -65,7 +65,7 @@ "name": "string.regexp, strong log.error" }, { - "match": "\\bE/", + "match": "(?<=^[\\s\\d\\p]*)\\bE\\b", "name": "string.regexp, strong log.error" }, { @@ -88,6 +88,10 @@ "match": "[0-9a-fA-F]{8}[-]?([0-9a-fA-F]{4}[-]?){3}[0-9a-fA-F]{12}", "name": "constant.language log.constant" }, + { + "match": "([0-9a-fA-F]+[:-])+[0-9a-fA-F]+", + "name": "constant.language log.constant" + }, { "match": "\\b([0-9]+|true|false|null)\\b", "name": "constant.language log.constant" diff --git a/extensions/make/cgmanifest.json b/extensions/make/cgmanifest.json index 544b0bc5bd3..9a311cfe811 100644 --- a/extensions/make/cgmanifest.json +++ b/extensions/make/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "fadeevab/make.tmbundle", "repositoryUrl": "https://github.com/fadeevab/make.tmbundle", - "commitHash": "fd57c0552dbe5e4d0c1e6765daea4b296d9bfc59" + "commitHash": "e36e02becd20730259b0115d9ca5c419f65023a9" } }, "licenseDetail": [ diff --git a/extensions/make/syntaxes/make.tmLanguage.json b/extensions/make/syntaxes/make.tmLanguage.json index 2503fce46fc..890148cb19f 100644 --- a/extensions/make/syntaxes/make.tmLanguage.json +++ b/extensions/make/syntaxes/make.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/fadeevab/make.tmbundle/commit/fd57c0552dbe5e4d0c1e6765daea4b296d9bfc59", + "version": "https://github.com/fadeevab/make.tmbundle/commit/e36e02becd20730259b0115d9ca5c419f65023a9", "name": "Makefile", "scopeName": "source.makefile", "patterns": [ @@ -18,13 +18,13 @@ "include": "#variable-assignment" }, { - "include": "#target" + "include": "#directives" }, { "include": "#recipe" }, { - "include": "#directives" + "include": "#target" } ], "repository": { diff --git a/extensions/make/test/colorize-fixtures/makefile b/extensions/make/test/colorize-fixtures/makefile index 45a7c80465a..7720e43ae4d 100644 --- a/extensions/make/test/colorize-fixtures/makefile +++ b/extensions/make/test/colorize-fixtures/makefile @@ -84,3 +84,5 @@ var-$(nested-var)=val # but not so much. var-$(shell printf 2) := val2 $(info Should be 'val2' here: $(var-2)) + +export a ?= b:c diff --git a/extensions/make/test/colorize-results/makefile.json b/extensions/make/test/colorize-results/makefile.json index 616de8fb8e0..eacaa05384f 100644 --- a/extensions/make/test/colorize-results/makefile.json +++ b/extensions/make/test/colorize-results/makefile.json @@ -136,7 +136,7 @@ "t": "source.makefile meta.scope.target.makefile meta.scope.prerequisites.makefile constant.character.escape.continuation.makefile", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "constant.character: #569CD6" @@ -279,7 +279,7 @@ "t": "source.makefile meta.scope.target.makefile meta.scope.prerequisites.makefile constant.character.escape.continuation.makefile", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "constant.character: #569CD6" @@ -323,7 +323,7 @@ "t": "source.makefile meta.scope.target.makefile meta.scope.prerequisites.makefile comment.line.number-sign.makefile constant.character.escape.continuation.makefile", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "constant.character: #569CD6" @@ -378,7 +378,7 @@ "t": "source.makefile comment.line.number-sign.makefile constant.character.escape.continuation.makefile", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "constant.character: #569CD6" @@ -554,7 +554,7 @@ "t": "source.makefile meta.scope.target.makefile meta.scope.prerequisites.makefile constant.character.escape.continuation.makefile", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "constant.character: #569CD6" @@ -884,7 +884,7 @@ "t": "source.makefile meta.scope.recipe.makefile constant.character.escape.continuation.makefile", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "constant.character: #569CD6" @@ -3029,7 +3029,7 @@ "t": "source.makefile constant.character.escape.continuation.makefile", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "constant.character: #569CD6" @@ -3353,5 +3353,71 @@ "light_vs": "string: #A31515", "hc_black": "string: #CE9178" } + }, + { + "c": "export", + "t": "source.makefile keyword.control.export.makefile", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "a", + "t": "source.makefile variable.other.makefile", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "?=", + "t": "source.makefile punctuation.separator.key-value.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " b:c", + "t": "source.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } } ] \ No newline at end of file diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index af6042b5594..e8feaa75fa6 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/7cf9aa7bb76c55428063383610edc0a631230d58", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/4be9cb335581f3559166c319607dac9100103083", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -1715,6 +1715,72 @@ } ] }, + "fenced_code_block_erlang": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(erlang)((\\s+|:|\\{)[^`~]*)?$)", + "name": "markup.fenced_code.block.markdown", + "end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$", + "beginCaptures": { + "3": { + "name": "punctuation.definition.markdown" + }, + "4": { + "name": "fenced_code.block.language.markdown" + }, + "5": { + "name": "fenced_code.block.language.attributes.markdown" + } + }, + "endCaptures": { + "3": { + "name": "punctuation.definition.markdown" + } + }, + "patterns": [ + { + "begin": "(^|\\G)(\\s*)(.*)", + "while": "(^|\\G)(?!\\s*([`~]{3,})\\s*$)", + "contentName": "meta.embedded.block.erlang", + "patterns": [ + { + "include": "source.erlang" + } + ] + } + ] + }, + "fenced_code_block_elixir": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(elixir)((\\s+|:|\\{)[^`~]*)?$)", + "name": "markup.fenced_code.block.markdown", + "end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$", + "beginCaptures": { + "3": { + "name": "punctuation.definition.markdown" + }, + "4": { + "name": "fenced_code.block.language.markdown" + }, + "5": { + "name": "fenced_code.block.language.attributes.markdown" + } + }, + "endCaptures": { + "3": { + "name": "punctuation.definition.markdown" + } + }, + "patterns": [ + { + "begin": "(^|\\G)(\\s*)(.*)", + "while": "(^|\\G)(?!\\s*([`~]{3,})\\s*$)", + "contentName": "meta.embedded.block.elixir", + "patterns": [ + { + "include": "source.elixir" + } + ] + } + ] + }, "fenced_code_block": { "patterns": [ { @@ -1867,6 +1933,12 @@ { "include": "#fenced_code_block_log" }, + { + "include": "#fenced_code_block_erlang" + }, + { + "include": "#fenced_code_block_elixir" + }, { "include": "#fenced_code_block_unknown" } @@ -1891,12 +1963,12 @@ "name": "markup.fenced_code.block.markdown" }, "heading": { - "match": "(?:^|\\G)[ ]{0,3}((#{1,6})\\s+(?=[\\S[^#]]).*?\\s*(#{1,6})?)$\\n?", + "match": "(?:^|\\G)[ ]{0,3}(#{1,6}\\s+(.*?)(\\s+#{1,6})?\\s*)$", "captures": { "1": { "patterns": [ { - "match": "(#{6})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{6})\\s+(.*?)(?:\\s+(#+))?\\s*$", "name": "heading.6.markdown", "captures": { "1": { @@ -1911,7 +1983,7 @@ } }, { - "match": "(#{5})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{5})\\s+(.*?)(?:\\s+(#+))?\\s*$", "name": "heading.5.markdown", "captures": { "1": { @@ -1926,7 +1998,7 @@ } }, { - "match": "(#{4})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{4})\\s+(.*?)(?:\\s+(#+))?\\s*$", "name": "heading.4.markdown", "captures": { "1": { @@ -1941,7 +2013,7 @@ } }, { - "match": "(#{3})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{3})\\s+(.*?)(?:\\s+(#+))?\\s*$", "name": "heading.3.markdown", "captures": { "1": { @@ -1956,7 +2028,7 @@ } }, { - "match": "(#{2})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{2})\\s+(.*?)(?:\\s+(#+))?\\s*$", "name": "heading.2.markdown", "captures": { "1": { @@ -1971,7 +2043,7 @@ } }, { - "match": "(#{1})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{1})\\s+(.*?)(?:\\s+(#+))?\\s*$", "name": "heading.1.markdown", "captures": { "1": { diff --git a/extensions/markdown-basics/test/colorize-results/test_md.json b/extensions/markdown-basics/test/colorize-results/test_md.json index 6963f66e797..1fee1287417 100644 --- a/extensions/markdown-basics/test/colorize-results/test_md.json +++ b/extensions/markdown-basics/test/colorize-results/test_md.json @@ -33,7 +33,18 @@ } }, { - "c": " #", + "c": " ", + "t": "text.html.markdown markup.heading.markdown heading.1.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "#", "t": "text.html.markdown markup.heading.markdown heading.1.markdown punctuation.definition.heading.markdown", "r": { "dark_plus": "markup.heading: #569CD6", @@ -77,7 +88,18 @@ } }, { - "c": " ##", + "c": " ", + "t": "text.html.markdown markup.heading.markdown heading.2.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "##", "t": "text.html.markdown markup.heading.markdown heading.2.markdown punctuation.definition.heading.markdown", "r": { "dark_plus": "markup.heading: #569CD6", @@ -2189,7 +2211,18 @@ } }, { - "c": " ##", + "c": " ", + "t": "text.html.markdown markup.heading.markdown heading.2.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "##", "t": "text.html.markdown markup.heading.markdown heading.2.markdown punctuation.definition.heading.markdown", "r": { "dark_plus": "markup.heading: #569CD6", @@ -2343,7 +2376,18 @@ } }, { - "c": " ##", + "c": " ", + "t": "text.html.markdown markup.heading.markdown heading.2.markdown", + "r": { + "dark_plus": "markup.heading: #569CD6", + "light_plus": "markup.heading: #800000", + "dark_vs": "markup.heading: #569CD6", + "light_vs": "markup.heading: #800000", + "hc_black": "markup.heading: #6796E6" + } + }, + { + "c": "##", "t": "text.html.markdown markup.heading.markdown heading.2.markdown punctuation.definition.heading.markdown", "r": { "dark_plus": "markup.heading: #569CD6", diff --git a/extensions/markdown-language-features/.vscodeignore b/extensions/markdown-language-features/.vscodeignore index 30d948fbc66..9f1e0620775 100644 --- a/extensions/markdown-language-features/.vscodeignore +++ b/extensions/markdown-language-features/.vscodeignore @@ -1,9 +1,11 @@ test/** +test-workspace/** src/** tsconfig.json out/test/** out/** extension.webpack.config.js +extension-browser.webpack.config.js cgmanifest.json yarn.lock preview-src/** diff --git a/extensions/markdown-language-features/extension-browser.webpack.config.js b/extensions/markdown-language-features/extension-browser.webpack.config.js new file mode 100644 index 00000000000..7fcc53a728b --- /dev/null +++ b/extensions/markdown-language-features/extension-browser.webpack.config.js @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withBrowserDefaults = require('../shared.webpack.config').browser; + +module.exports = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/extension.ts' + } +}); diff --git a/extensions/markdown-language-features/media/index.js b/extensions/markdown-language-features/media/index.js index 0762949b41c..0433f33c89a 100644 --- a/extensions/markdown-language-features/media/index.js +++ b/extensions/markdown-language-features/media/index.js @@ -1,2 +1 @@ -!function(e){var t={};function n(o){if(t[o])return t[o].exports;var 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=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&&in?{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(),...s.getData("data-state")};d.setState(m);const p=r.createPosterForVsCode(d);window.cspAlerter.setPoster(p),window.styleLoadingMonitor.setPoster(p),window.onload=()=>{h()},i.onceDocumentLoaded(()=>{const t=m.scrollProgress;"number"!=typeof t||f.fragment?f.scrollPreviewWithEditor&&e(()=>{if(f.fragment){m.fragment=void 0,d.setState(m);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 g=(()=>{const e=a(e=>{u=!0,c.scrollToRevealSourceLine(e)},50);return t=>{isNaN(t)||(m.line=t,e(t))}})();let h=a(()=>{const e=[];let t=document.getElementsByTagName("img");if(t){let n;for(n=0;n{u=!0,y(),h()},!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":g(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)||p.postMessage("didClick",{line:Math.floor(n)})});const v=["http:","https:","mailto:","vscode:","vscode-insiders:"];function y(){m.scrollProgress=window.scrollY/document.body.clientHeight,d.setState(m)}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(v.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(y(),u)u=!1;else{const e=c.getEditorLineNumberForPageOffset(window.scrollY);"number"!=typeof e||isNaN(e)||p.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;n1)for(var n=1;nnew 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))}]); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvc2V0dGluZ3MudHMiLCJ3ZWJwYWNrOi8vLyh3ZWJwYWNrKS9idWlsZGluL2dsb2JhbC5qcyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zY3JvbGwtc3luYy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9pbmRleC50cyIsIndlYnBhY2s6Ly8vLi9ub2RlX21vZHVsZXMvdGltZXJzLWJyb3dzZXJpZnkvbWFpbi5qcyIsIndlYnBhY2s6Ly8vLi9ub2RlX21vZHVsZXMvc2V0aW1tZWRpYXRlL3NldEltbWVkaWF0ZS5qcyIsIndlYnBhY2s6Ly8vLi9ub2RlX21vZHVsZXMvcHJvY2Vzcy9icm93c2VyLmpzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2FjdGl2ZUxpbmVNYXJrZXIudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvZXZlbnRzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL21lc3NhZ2luZy50cyIsIndlYnBhY2s6Ly8vLi9ub2RlX21vZHVsZXMvbG9kYXNoLnRocm90dGxlL2luZGV4LmpzIl0sIm5hbWVzIjpbImluc3RhbGxlZE1vZHVsZXMiLCJfX3dlYnBhY2tfcmVxdWlyZV9fIiwibW9kdWxlSWQiLCJleHBvcnRzIiwibW9kdWxlIiwiaSIsImwiLCJtb2R1bGVzIiwiY2FsbCIsIm0iLCJjIiwiZCIsIm5hbWUiLCJnZXR0ZXIiLCJvIiwiT2JqZWN0IiwiZGVmaW5lUHJvcGVydHkiLCJlbnVtZXJhYmxlIiwiZ2V0IiwiciIsIlN5bWJvbCIsInRvU3RyaW5nVGFnIiwidmFsdWUiLCJ0IiwibW9kZSIsIl9fZXNNb2R1bGUiLCJucyIsImNyZWF0ZSIsImtleSIsImJpbmQiLCJuIiwib2JqZWN0IiwicHJvcGVydHkiLCJwcm90b3R5cGUiLCJoYXNPd25Qcm9wZXJ0eSIsInAiLCJzIiwiY2FjaGVkU2V0dGluZ3MiLCJ1bmRlZmluZWQiLCJnZXREYXRhIiwiZWxlbWVudCIsImRvY3VtZW50IiwiZ2V0RWxlbWVudEJ5SWQiLCJkYXRhIiwiZ2V0QXR0cmlidXRlIiwiSlNPTiIsInBhcnNlIiwiRXJyb3IiLCJnZXRTZXR0aW5ncyIsImciLCJ0aGlzIiwiRnVuY3Rpb24iLCJlIiwid2luZG93Iiwic2V0dGluZ3NfMSIsImNvZGVMaW5lQ2xhc3MiLCJjbGFtcExpbmUiLCJsaW5lIiwibWluIiwibWF4IiwibGluZUNvdW50IiwiTWF0aCIsImdldENvZGVMaW5lRWxlbWVudHMiLCJlbGVtZW50cyIsImJvZHkiLCJnZXRFbGVtZW50c0J5Q2xhc3NOYW1lIiwiaXNOYU4iLCJ0YWdOYW1lIiwicGFyZW50RWxlbWVudCIsInB1c2giLCJnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUiLCJ0YXJnZXRMaW5lIiwibGluZU51bWJlciIsImZsb29yIiwibGluZXMiLCJwcmV2aW91cyIsImVudHJ5IiwibmV4dCIsImdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldCIsIm9mZnNldCIsInBvc2l0aW9uIiwic2Nyb2xsWSIsImxvIiwiaGkiLCJsZW5ndGgiLCJtaWQiLCJib3VuZHMiLCJnZXRFbGVtZW50Qm91bmRzIiwidG9wIiwiaGVpZ2h0IiwiaGlFbGVtZW50IiwiaGlCb3VuZHMiLCJteUJvdW5kcyIsImdldEJvdW5kaW5nQ2xpZW50UmVjdCIsImNvZGVMaW5lQ2hpbGQiLCJxdWVyeVNlbGVjdG9yIiwiY2hpbGRCb3VuZHMiLCJzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUiLCJzY3JvbGxQcmV2aWV3V2l0aEVkaXRvciIsInNjcm9sbCIsInNjcm9sbFgiLCJzY3JvbGxUbyIsInJlY3QiLCJwcmV2aW91c1RvcCIsInByb2dyZXNzSW5FbGVtZW50IiwiZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQiLCJwcmV2aW91c0JvdW5kcyIsIm9mZnNldEZyb21QcmV2aW91cyIsInByb2dyZXNzQmV0d2VlbkVsZW1lbnRzIiwicHJvZ3Jlc3NXaXRoaW5FbGVtZW50IiwiZ2V0TGluZUVsZW1lbnRGb3JGcmFnbWVudCIsImZyYWdtZW50IiwiZmluZCIsImlkIiwiYWN0aXZlTGluZU1hcmtlcl8xIiwiZXZlbnRzXzEiLCJtZXNzYWdpbmdfMSIsInNjcm9sbF9zeW5jXzEiLCJ0aHJvdHRsZSIsInNjcm9sbERpc2FibGVkIiwibWFya2VyIiwiQWN0aXZlTGluZU1hcmtlciIsInNldHRpbmdzIiwidnNjb2RlIiwiYWNxdWlyZVZzQ29kZUFwaSIsInN0YXRlIiwiZ2V0U3RhdGUiLCJzZXRTdGF0ZSIsIm1lc3NhZ2luZyIsImNyZWF0ZVBvc3RlckZvclZzQ29kZSIsImNzcEFsZXJ0ZXIiLCJzZXRQb3N0ZXIiLCJzdHlsZUxvYWRpbmdNb25pdG9yIiwib25sb2FkIiwidXBkYXRlSW1hZ2VTaXplcyIsIm9uY2VEb2N1bWVudExvYWRlZCIsInNjcm9sbFByb2dyZXNzIiwic2V0SW1tZWRpYXRlIiwiY2xpZW50SGVpZ2h0Iiwib25VcGRhdGVWaWV3IiwiZG9TY3JvbGwiLCJpbWFnZUluZm8iLCJpbWFnZXMiLCJnZXRFbGVtZW50c0J5VGFnTmFtZSIsImltZyIsImNsYXNzTGlzdCIsImNvbnRhaW5zIiwicmVtb3ZlIiwid2lkdGgiLCJwb3N0TWVzc2FnZSIsImFkZEV2ZW50TGlzdGVuZXIiLCJ1cGRhdGVTY3JvbGxQcm9ncmVzcyIsImV2ZW50Iiwic291cmNlIiwidHlwZSIsIm9uRGlkQ2hhbmdlVGV4dEVkaXRvclNlbGVjdGlvbiIsImRvdWJsZUNsaWNrVG9Td2l0Y2hUb0VkaXRvciIsIm5vZGUiLCJ0YXJnZXQiLCJwYXJlbnROb2RlIiwicGFnZVkiLCJwYXNzVGhyb3VnaExpbmtTY2hlbWVzIiwiaHJlZiIsInN0YXJ0c1dpdGgiLCJzb21lIiwic2NoZW1lIiwiaHJlZlRleHQiLCJ0ZXN0IiwicHJldmVudERlZmF1bHQiLCJzdG9wUHJvcGFnYXRpb24iLCJUaW1lb3V0IiwiY2xlYXJGbiIsIl9pZCIsIl9jbGVhckZuIiwic2V0VGltZW91dCIsImFwcGx5IiwiYXJndW1lbnRzIiwiY2xlYXJUaW1lb3V0Iiwic2V0SW50ZXJ2YWwiLCJjbGVhckludGVydmFsIiwidGltZW91dCIsImNsb3NlIiwidW5yZWYiLCJyZWYiLCJlbnJvbGwiLCJpdGVtIiwibXNlY3MiLCJfaWRsZVRpbWVvdXRJZCIsIl9pZGxlVGltZW91dCIsInVuZW5yb2xsIiwiX3VucmVmQWN0aXZlIiwiYWN0aXZlIiwiX29uVGltZW91dCIsInNlbGYiLCJnbG9iYWwiLCJjbGVhckltbWVkaWF0ZSIsInJlZ2lzdGVySW1tZWRpYXRlIiwiaHRtbCIsImNoYW5uZWwiLCJtZXNzYWdlUHJlZml4Iiwib25HbG9iYWxNZXNzYWdlIiwibmV4dEhhbmRsZSIsInRhc2tzQnlIYW5kbGUiLCJjdXJyZW50bHlSdW5uaW5nQVRhc2siLCJkb2MiLCJhdHRhY2hUbyIsImdldFByb3RvdHlwZU9mIiwidG9TdHJpbmciLCJwcm9jZXNzIiwiaGFuZGxlIiwibmV4dFRpY2siLCJydW5JZlByZXNlbnQiLCJpbXBvcnRTY3JpcHRzIiwicG9zdE1lc3NhZ2VJc0FzeW5jaHJvbm91cyIsIm9sZE9uTWVzc2FnZSIsIm9ubWVzc2FnZSIsImNhblVzZVBvc3RNZXNzYWdlIiwiTWVzc2FnZUNoYW5uZWwiLCJwb3J0MSIsInBvcnQyIiwiY3JlYXRlRWxlbWVudCIsImRvY3VtZW50RWxlbWVudCIsInNjcmlwdCIsIm9ucmVhZHlzdGF0ZWNoYW5nZSIsInJlbW92ZUNoaWxkIiwiYXBwZW5kQ2hpbGQiLCJyYW5kb20iLCJpbmRleE9mIiwic2xpY2UiLCJhdHRhY2hFdmVudCIsImNhbGxiYWNrIiwiYXJncyIsIkFycmF5IiwidGFzayIsInJ1biIsImNhY2hlZFNldFRpbWVvdXQiLCJjYWNoZWRDbGVhclRpbWVvdXQiLCJkZWZhdWx0U2V0VGltb3V0IiwiZGVmYXVsdENsZWFyVGltZW91dCIsInJ1blRpbWVvdXQiLCJmdW4iLCJjdXJyZW50UXVldWUiLCJxdWV1ZSIsImRyYWluaW5nIiwicXVldWVJbmRleCIsImNsZWFuVXBOZXh0VGljayIsImNvbmNhdCIsImRyYWluUXVldWUiLCJsZW4iLCJydW5DbGVhclRpbWVvdXQiLCJJdGVtIiwiYXJyYXkiLCJub29wIiwidGl0bGUiLCJicm93c2VyIiwiZW52IiwiYXJndiIsInZlcnNpb24iLCJ2ZXJzaW9ucyIsIm9uIiwiYWRkTGlzdGVuZXIiLCJvbmNlIiwib2ZmIiwicmVtb3ZlTGlzdGVuZXIiLCJyZW1vdmVBbGxMaXN0ZW5lcnMiLCJlbWl0IiwicHJlcGVuZExpc3RlbmVyIiwicHJlcGVuZE9uY2VMaXN0ZW5lciIsImxpc3RlbmVycyIsImJpbmRpbmciLCJjd2QiLCJjaGRpciIsImRpciIsInVtYXNrIiwiX3VwZGF0ZSIsImJlZm9yZSIsIl91bm1hcmtBY3RpdmVFbGVtZW50IiwiX2N1cnJlbnQiLCJfbWFya0FjdGl2ZUVsZW1lbnQiLCJjbGFzc05hbWUiLCJyZXBsYWNlIiwiZiIsInJlYWR5U3RhdGUiLCJGVU5DX0VSUk9SX1RFWFQiLCJOQU4iLCJzeW1ib2xUYWciLCJyZVRyaW0iLCJyZUlzQmFkSGV4IiwicmVJc0JpbmFyeSIsInJlSXNPY3RhbCIsImZyZWVQYXJzZUludCIsInBhcnNlSW50IiwiZnJlZUdsb2JhbCIsImZyZWVTZWxmIiwicm9vdCIsIm9iamVjdFRvU3RyaW5nIiwibmF0aXZlTWF4IiwibmF0aXZlTWluIiwibm93IiwiRGF0ZSIsImRlYm91bmNlIiwiZnVuYyIsIndhaXQiLCJvcHRpb25zIiwibGFzdEFyZ3MiLCJsYXN0VGhpcyIsIm1heFdhaXQiLCJyZXN1bHQiLCJ0aW1lcklkIiwibGFzdENhbGxUaW1lIiwibGFzdEludm9rZVRpbWUiLCJsZWFkaW5nIiwibWF4aW5nIiwidHJhaWxpbmciLCJUeXBlRXJyb3IiLCJpbnZva2VGdW5jIiwidGltZSIsInRoaXNBcmciLCJzaG91bGRJbnZva2UiLCJ0aW1lU2luY2VMYXN0Q2FsbCIsInRpbWVyRXhwaXJlZCIsInRyYWlsaW5nRWRnZSIsInJlbWFpbmluZ1dhaXQiLCJkZWJvdW5jZWQiLCJpc0ludm9raW5nIiwibGVhZGluZ0VkZ2UiLCJ0b051bWJlciIsImlzT2JqZWN0IiwiY2FuY2VsIiwiZmx1c2giLCJpc09iamVjdExpa2UiLCJpc1N5bWJvbCIsIm90aGVyIiwidmFsdWVPZiIsImlzQmluYXJ5Il0sIm1hcHBpbmdzIjoiYUFDRSxJQUFJQSxFQUFtQixHQUd2QixTQUFTQyxFQUFvQkMsR0FHNUIsR0FBR0YsRUFBaUJFLEdBQ25CLE9BQU9GLEVBQWlCRSxHQUFVQyxRQUduQyxJQUFJQyxFQUFTSixFQUFpQkUsR0FBWSxDQUN6Q0csRUFBR0gsRUFDSEksR0FBRyxFQUNISCxRQUFTLElBVVYsT0FOQUksRUFBUUwsR0FBVU0sS0FBS0osRUFBT0QsUUFBU0MsRUFBUUEsRUFBT0QsUUFBU0YsR0FHL0RHLEVBQU9FLEdBQUksRUFHSkYsRUFBT0QsUUFLZkYsRUFBb0JRLEVBQUlGLEVBR3hCTixFQUFvQlMsRUFBSVYsRUFHeEJDLEVBQW9CVSxFQUFJLFNBQVNSLEVBQVNTLEVBQU1DLEdBQzNDWixFQUFvQmEsRUFBRVgsRUFBU1MsSUFDbENHLE9BQU9DLGVBQWViLEVBQVNTLEVBQU0sQ0FBRUssWUFBWSxFQUFNQyxJQUFLTCxLQUtoRVosRUFBb0JrQixFQUFJLFNBQVNoQixHQUNYLG9CQUFYaUIsUUFBMEJBLE9BQU9DLGFBQzFDTixPQUFPQyxlQUFlYixFQUFTaUIsT0FBT0MsWUFBYSxDQUFFQyxNQUFPLFdBRTdEUCxPQUFPQyxlQUFlYixFQUFTLGFBQWMsQ0FBRW1CLE9BQU8sS0FRdkRyQixFQUFvQnNCLEVBQUksU0FBU0QsRUFBT0UsR0FFdkMsR0FEVSxFQUFQQSxJQUFVRixFQUFRckIsRUFBb0JxQixJQUMvQixFQUFQRSxFQUFVLE9BQU9GLEVBQ3BCLEdBQVcsRUFBUEUsR0FBOEIsaUJBQVZGLEdBQXNCQSxHQUFTQSxFQUFNRyxXQUFZLE9BQU9ILEVBQ2hGLElBQUlJLEVBQUtYLE9BQU9ZLE9BQU8sTUFHdkIsR0FGQTFCLEVBQW9Ca0IsRUFBRU8sR0FDdEJYLE9BQU9DLGVBQWVVLEVBQUksVUFBVyxDQUFFVCxZQUFZLEVBQU1LLE1BQU9BLElBQ3RELEVBQVBFLEdBQTRCLGlCQUFURixFQUFtQixJQUFJLElBQUlNLEtBQU9OLEVBQU9yQixFQUFvQlUsRUFBRWUsRUFBSUUsRUFBSyxTQUFTQSxHQUFPLE9BQU9OLEVBQU1NLElBQVFDLEtBQUssS0FBTUQsSUFDOUksT0FBT0YsR0FJUnpCLEVBQW9CNkIsRUFBSSxTQUFTMUIsR0FDaEMsSUFBSVMsRUFBU1QsR0FBVUEsRUFBT3FCLFdBQzdCLFdBQXdCLE9BQU9yQixFQUFnQixTQUMvQyxXQUE4QixPQUFPQSxHQUV0QyxPQURBSCxFQUFvQlUsRUFBRUUsRUFBUSxJQUFLQSxHQUM1QkEsR0FJUlosRUFBb0JhLEVBQUksU0FBU2lCLEVBQVFDLEdBQVksT0FBT2pCLE9BQU9rQixVQUFVQyxlQUFlMUIsS0FBS3VCLEVBQVFDLElBR3pHL0IsRUFBb0JrQyxFQUFJLEdBSWpCbEMsRUFBb0JBLEVBQW9CbUMsRUFBSSxHLCtCQzdFckRyQixPQUFPQyxlQUFlYixFQUFTLGFBQWMsQ0FBRW1CLE9BQU8sSUFDdEQsSUFBSWUsT0FBaUJDLEVBQ3JCLFNBQVNDLEVBQVFYLEdBQ2IsTUFBTVksRUFBVUMsU0FBU0MsZUFBZSxnQ0FDeEMsR0FBSUYsRUFBUyxDQUNULE1BQU1HLEVBQU9ILEVBQVFJLGFBQWFoQixHQUNsQyxHQUFJZSxFQUNBLE9BQU9FLEtBQUtDLE1BQU1ILEdBRzFCLE1BQU0sSUFBSUksTUFBTSwyQkFBMkJuQixLQUUvQ3pCLEVBQVFvQyxRQUFVQSxFQVdsQnBDLEVBQVE2QyxZQVZSLFdBQ0ksR0FBSVgsRUFDQSxPQUFPQSxFQUdYLEdBREFBLEVBQWlCRSxFQUFRLGlCQUVyQixPQUFPRixFQUVYLE1BQU0sSUFBSVUsTUFBTSw2QixjQzFCcEIsSUFBSUUsRUFHSkEsRUFBSSxXQUNILE9BQU9DLEtBREosR0FJSixJQUVDRCxFQUFJQSxHQUFLLElBQUlFLFNBQVMsY0FBYixHQUNSLE1BQU9DLEdBRWMsaUJBQVhDLFNBQXFCSixFQUFJSSxRQU9yQ2pELEVBQU9ELFFBQVU4QyxHLDZCQ2RqQmxDLE9BQU9DLGVBQWViLEVBQVMsYUFBYyxDQUFFbUIsT0FBTyxJQUN0RCxNQUFNZ0MsRUFBYSxFQUFRLEdBQ3JCQyxFQUFnQixZQUl0QixTQUFTQyxFQUFVQyxHQUNmLE9BSldDLEVBSUUsRUFKR0MsRUFJQUwsRUFBV04sY0FBY1ksVUFBWSxFQUpoQ3RDLEVBSW1DbUMsRUFIakRJLEtBQUtILElBQUlDLEVBQUtFLEtBQUtGLElBQUlELEVBQUtwQyxJQUR2QyxJQUFlb0MsRUFBS0MsRUFBS3JDLEVBTXpCLE1BQU13QyxFQUFzQixNQUN4QixJQUFJQyxFQUNKLE1BQU8sS0FDSCxJQUFLQSxFQUFVLENBQ1hBLEVBQVcsQ0FBQyxDQUFFdkIsUUFBU0MsU0FBU3VCLEtBQU1QLEtBQU0sSUFDNUMsSUFBSyxNQUFNakIsS0FBV0MsU0FBU3dCLHVCQUF1QlYsR0FBZ0IsQ0FDbEUsTUFBTUUsR0FBUWpCLEVBQVFJLGFBQWEsYUFDL0JzQixNQUFNVCxLQUdjLFNBQXBCakIsRUFBUTJCLFNBQXNCM0IsRUFBUTRCLGVBQW1ELFFBQWxDNUIsRUFBUTRCLGNBQWNELFFBRzdFSixFQUFTTSxLQUFLLENBQUU3QixRQUFTQSxFQUFRNEIsY0FBZVgsU0FHaERNLEVBQVNNLEtBQUssQ0FBRTdCLFFBQVNBLEVBQVNpQixXQUk5QyxPQUFPTSxJQXBCYSxHQTZCNUIsU0FBU08sRUFBeUJDLEdBQzlCLE1BQU1DLEVBQWFYLEtBQUtZLE1BQU1GLEdBQ3hCRyxFQUFRWixJQUNkLElBQUlhLEVBQVdELEVBQU0sSUFBTSxLQUMzQixJQUFLLE1BQU1FLEtBQVNGLEVBQU8sQ0FDdkIsR0FBSUUsRUFBTW5CLE9BQVNlLEVBQ2YsTUFBTyxDQUFFRyxTQUFVQyxFQUFPQyxVQUFNdkMsR0FFL0IsR0FBSXNDLEVBQU1uQixLQUFPZSxFQUNsQixNQUFPLENBQUVHLFdBQVVFLEtBQU1ELEdBRTdCRCxFQUFXQyxFQUVmLE1BQU8sQ0FBRUQsWUFNYixTQUFTRyxFQUE0QkMsR0FDakMsTUFBTUwsRUFBUVosSUFDUmtCLEVBQVdELEVBQVMxQixPQUFPNEIsUUFDakMsSUFBSUMsR0FBTSxFQUNOQyxFQUFLVCxFQUFNVSxPQUFTLEVBQ3hCLEtBQU9GLEVBQUssRUFBSUMsR0FBSSxDQUNoQixNQUFNRSxFQUFNeEIsS0FBS1ksT0FBT1MsRUFBS0MsR0FBTSxHQUM3QkcsRUFBU0MsRUFBaUJiLEVBQU1XLElBQ2xDQyxFQUFPRSxJQUFNRixFQUFPRyxRQUFVVCxFQUM5QkcsRUFBS0UsRUFHTEgsRUFBS0csRUFHYixNQUFNSyxFQUFZaEIsRUFBTVMsR0FDbEJRLEVBQVdKLEVBQWlCRyxHQUNsQyxHQUFJUCxHQUFNLEdBQUtRLEVBQVNILElBQU1SLEVBQVUsQ0FFcEMsTUFBTyxDQUFFTCxTQURTRCxFQUFNUSxHQUNNTCxLQUFNYSxHQUV4QyxPQUFJUCxFQUFLLEdBQUtBLEVBQUtULEVBQU1VLFFBQVVPLEVBQVNILElBQU1HLEVBQVNGLE9BQVNULEVBQ3pELENBQUVMLFNBQVVlLEVBQVdiLEtBQU1ILEVBQU1TLEVBQUssSUFFNUMsQ0FBRVIsU0FBVWUsR0FHdkIsU0FBU0gsR0FBaUIsUUFBRS9DLElBQ3hCLE1BQU1vRCxFQUFXcEQsRUFBUXFELHdCQUduQkMsRUFBZ0J0RCxFQUFRdUQsY0FBYyxJQUFJeEMsS0FDaEQsR0FBSXVDLEVBQWUsQ0FDZixNQUFNRSxFQUFjRixFQUFjRCx3QkFDNUJKLEVBQVM1QixLQUFLRixJQUFJLEVBQUlxQyxFQUFZUixJQUFNSSxFQUFTSixLQUN2RCxNQUFPLENBQ0hBLElBQUtJLEVBQVNKLElBQ2RDLE9BQVFBLEdBR2hCLE9BQU9HLEVBNUNYekYsRUFBUW1FLHlCQUEyQkEsRUE4Qm5DbkUsRUFBUTJFLDRCQUE4QkEsRUE4Q3RDM0UsRUFBUThGLHlCQTNCUixTQUFrQ3hDLEdBQzlCLElBQUtILEVBQVdOLGNBQWNrRCx3QkFDMUIsT0FFSixHQUFJekMsR0FBUSxFQUVSLFlBREFKLE9BQU84QyxPQUFPOUMsT0FBTytDLFFBQVMsR0FHbEMsTUFBTSxTQUFFekIsRUFBUSxLQUFFRSxHQUFTUCxFQUF5QmIsR0FDcEQsSUFBS2tCLEVBQ0QsT0FFSixJQUFJMEIsRUFBVyxFQUNmLE1BQU1DLEVBQU9mLEVBQWlCWixHQUN4QjRCLEVBQWNELEVBQUtkLElBQ3pCLEdBQUlYLEdBQVFBLEVBQUtwQixPQUFTa0IsRUFBU2xCLEtBQU0sQ0FJckM0QyxFQUFXRSxHQUZjOUMsRUFBT2tCLEVBQVNsQixPQUFTb0IsRUFBS3BCLEtBQU9rQixFQUFTbEIsT0FDakRvQixFQUFLckMsUUFBUXFELHdCQUF3QkwsSUFBTWUsT0FHaEUsQ0FDRCxNQUFNQyxFQUFvQi9DLEVBQU9JLEtBQUtZLE1BQU1oQixHQUM1QzRDLEVBQVdFLEVBQWVELEVBQUtiLE9BQVNlLEVBRTVDbkQsT0FBTzhDLE9BQU85QyxPQUFPK0MsUUFBU3ZDLEtBQUtGLElBQUksRUFBR04sT0FBTzRCLFFBQVVvQixLQXFCL0RsRyxFQUFRc0csaUNBbEJSLFNBQTBDMUIsR0FDdEMsTUFBTSxTQUFFSixFQUFRLEtBQUVFLEdBQVNDLEVBQTRCQyxHQUN2RCxHQUFJSixFQUFVLENBQ1YsTUFBTStCLEVBQWlCbkIsRUFBaUJaLEdBQ2xDZ0MsRUFBc0I1QixFQUFTMUIsT0FBTzRCLFFBQVV5QixFQUFlbEIsSUFDckUsR0FBSVgsRUFBTSxDQUNOLE1BQU0rQixFQUEwQkQsR0FBc0JwQixFQUFpQlYsR0FBTVcsSUFBTWtCLEVBQWVsQixLQUVsRyxPQUFPaEMsRUFETW1CLEVBQVNsQixLQUFPbUQsR0FBMkIvQixFQUFLcEIsS0FBT2tCLEVBQVNsQixPQUc1RSxDQUNELE1BQU1vRCxFQUF3QkYsRUFBc0JELEVBQXFCLE9BRXpFLE9BQU9sRCxFQURNbUIsRUFBU2xCLEtBQU9vRCxJQUlyQyxPQUFPLE1BV1gxRyxFQUFRMkcsMEJBTFIsU0FBbUNDLEdBQy9CLE9BQU9qRCxJQUFzQmtELEtBQU14RSxHQUN4QkEsRUFBUUEsUUFBUXlFLEtBQU9GLEssOEJDL0p0QyxZQUtBaEcsT0FBT0MsZUFBZWIsRUFBUyxhQUFjLENBQUVtQixPQUFPLElBQ3RELE1BQU00RixFQUFxQixFQUFRLEdBQzdCQyxFQUFXLEVBQVEsR0FDbkJDLEVBQWMsRUFBUSxHQUN0QkMsRUFBZ0IsRUFBUSxHQUN4Qi9ELEVBQWEsRUFBUSxHQUNyQmdFLEVBQVcsRUFBUSxJQUN6QixJQUFJQyxHQUFpQixFQUNyQixNQUFNQyxFQUFTLElBQUlOLEVBQW1CTyxpQkFDaENDLEVBQVdwRSxFQUFXTixjQUN0QjJFLEVBQVNDLG1CQUNUQyxFQUFRLElBQUtGLEVBQU9HLGNBQWV4RSxFQUFXZixRQUFRLGVBRTVEb0YsRUFBT0ksU0FBU0YsR0FDaEIsTUFBTUcsRUFBWVosRUFBWWEsc0JBQXNCTixHQUNwRHRFLE9BQU82RSxXQUFXQyxVQUFVSCxHQUM1QjNFLE9BQU8rRSxvQkFBb0JELFVBQVVILEdBQ3JDM0UsT0FBT2dGLE9BQVMsS0FDWkMsS0FFSm5CLEVBQVNvQixtQkFBbUIsS0FDeEIsTUFBTUMsRUFBaUJYLEVBQU1XLGVBQ0MsaUJBQW5CQSxHQUFnQ2QsRUFBU1gsU0FPaERXLEVBQVN4Qix5QkFDVHVDLEVBQWEsS0FFVCxHQUFJZixFQUFTWCxTQUFVLENBQ25CYyxFQUFNZCxjQUFXekUsRUFDakJxRixFQUFPSSxTQUFTRixHQUNoQixNQUFNckYsRUFBVTZFLEVBQWNQLDBCQUEwQlksRUFBU1gsVUFDN0R2RSxJQUNBK0UsR0FBaUIsRUFDakJGLEVBQWNwQix5QkFBeUJ6RCxFQUFRaUIsWUFJOUNTLE1BQU13RCxFQUFTakUsUUFDaEI4RCxHQUFpQixFQUNqQkYsRUFBY3BCLHlCQUF5QnlCLEVBQVNqRSxTQXJCNURnRixFQUFhLEtBQ1RsQixHQUFpQixFQUNqQmxFLE9BQU9nRCxTQUFTLEVBQUdtQyxFQUFpQi9GLFNBQVN1QixLQUFLMEUsa0JBeUI5RCxNQUFNQyxFQUFlLE1BQ2pCLE1BQU1DLEVBQVd0QixFQUFVN0QsSUFDdkI4RCxHQUFpQixFQUNqQkYsRUFBY3BCLHlCQUF5QnhDLElBQ3hDLElBQ0gsT0FBUUEsSUFDQ1MsTUFBTVQsS0FDUG9FLEVBQU1wRSxLQUFPQSxFQUNibUYsRUFBU25GLE1BUkEsR0FZckIsSUFBSTZFLEVBQW1CaEIsRUFBUyxLQUM1QixNQUFNdUIsRUFBWSxHQUNsQixJQUFJQyxFQUFTckcsU0FBU3NHLHFCQUFxQixPQUMzQyxHQUFJRCxFQUFRLENBQ1IsSUFBSXpJLEVBQ0osSUFBS0EsRUFBSSxFQUFHQSxFQUFJeUksRUFBTzFELE9BQVEvRSxJQUFLLENBQ2hDLE1BQU0ySSxFQUFNRixFQUFPekksR0FDZjJJLEVBQUlDLFVBQVVDLFNBQVMsWUFDdkJGLEVBQUlDLFVBQVVFLE9BQU8sV0FFekJOLEVBQVV4RSxLQUFLLENBQ1g0QyxHQUFJK0IsRUFBSS9CLEdBQ1J4QixPQUFRdUQsRUFBSXZELE9BQ1oyRCxNQUFPSixFQUFJSSxRQUduQnBCLEVBQVVxQixZQUFZLGtCQUFtQlIsS0FFOUMsSUFDSHhGLE9BQU9pRyxpQkFBaUIsU0FBVSxLQUM5Qi9CLEdBQWlCLEVBQ2pCZ0MsSUFDQWpCLE1BQ0QsR0FDSGpGLE9BQU9pRyxpQkFBaUIsVUFBV0UsSUFDL0IsR0FBSUEsRUFBTTdHLEtBQUs4RyxTQUFXL0IsRUFBUytCLE9BR25DLE9BQVFELEVBQU03RyxLQUFLK0csTUFDZixJQUFLLGlDQUNEbEMsRUFBT21DLCtCQUErQkgsRUFBTTdHLEtBQUtjLE1BQ2pELE1BQ0osSUFBSyxhQUNEa0YsRUFBYWEsRUFBTTdHLEtBQUtjLFNBR2pDLEdBQ0hoQixTQUFTNkcsaUJBQWlCLFdBQVlFLElBQ2xDLElBQUs5QixFQUFTa0MsNEJBQ1YsT0FHSixJQUFLLElBQUlDLEVBQU9MLEVBQU1NLE9BQVFELEVBQU1BLEVBQU9BLEVBQUtFLFdBQzVDLEdBQXFCLE1BQWpCRixFQUFLMUYsUUFDTCxPQUdSLE1BQU1ZLEVBQVN5RSxFQUFNUSxNQUNmdkcsRUFBTzRELEVBQWNaLGlDQUFpQzFCLEdBQ3hDLGlCQUFUdEIsR0FBc0JTLE1BQU1ULElBQ25DdUUsRUFBVXFCLFlBQVksV0FBWSxDQUFFNUYsS0FBTUksS0FBS1ksTUFBTWhCLE9BRzdELE1BQU13RyxFQUF5QixDQUFDLFFBQVMsU0FBVSxVQUFXLFVBQVcsb0JBd0N6RSxTQUFTVixJQUNMMUIsRUFBTVcsZUFBaUJuRixPQUFPNEIsUUFBVXhDLFNBQVN1QixLQUFLMEUsYUFDdERmLEVBQU9JLFNBQVNGLEdBekNwQnBGLFNBQVM2RyxpQkFBaUIsUUFBU0UsSUFDL0IsSUFBS0EsRUFDRCxPQUVKLElBQUlLLEVBQU9MLEVBQU1NLE9BQ2pCLEtBQU9ELEdBQU0sQ0FDVCxHQUFJQSxFQUFLMUYsU0FBNEIsTUFBakIwRixFQUFLMUYsU0FBbUIwRixFQUFLSyxLQUFNLENBQ25ELEdBQUlMLEVBQUtqSCxhQUFhLFFBQVF1SCxXQUFXLEtBQ3JDLE9BR0osR0FBSUYsRUFBdUJHLEtBQUtDLEdBQVVSLEVBQUtLLEtBQUtDLFdBQVdFLElBQzNELE9BRUosTUFBTUMsRUFBV1QsRUFBS2pILGFBQWEsY0FBZ0JpSCxFQUFLakgsYUFBYSxRQUVyRSxNQUFLLGNBQWMySCxLQUFLRCxRQU14QixHQUxJdEMsRUFBVXFCLFlBQVksV0FBWSxDQUFFYSxLQUFNSSxJQUMxQ2QsRUFBTWdCLHNCQUNOaEIsRUFBTWlCLG1CQUtkWixFQUFPQSxFQUFLRSxjQUVqQixHQUNIMUcsT0FBT2lHLGlCQUFpQixTQUFVaEMsRUFBUyxLQUV2QyxHQURBaUMsSUFDSWhDLEVBQ0FBLEdBQWlCLE1BRWhCLENBQ0QsTUFBTTlELEVBQU80RCxFQUFjWixpQ0FBaUNwRCxPQUFPNEIsU0FDL0MsaUJBQVR4QixHQUFzQlMsTUFBTVQsSUFDbkN1RSxFQUFVcUIsWUFBWSxhQUFjLENBQUU1RixXQUcvQyxPLCtDQy9KSCwyQ0FpQkEsU0FBU2lILEVBQVF6RCxFQUFJMEQsR0FDbkJ6SCxLQUFLMEgsSUFBTTNELEVBQ1gvRCxLQUFLMkgsU0FBV0YsRUFmbEJ4SyxFQUFRMkssV0FBYSxXQUNuQixPQUFPLElBQUlKLEVBQVFLLEVBQU12SyxLQUFLc0ssV0FBWXpILE9BQVEySCxXQUFZQyxlQUVoRTlLLEVBQVErSyxZQUFjLFdBQ3BCLE9BQU8sSUFBSVIsRUFBUUssRUFBTXZLLEtBQUswSyxZQUFhN0gsT0FBUTJILFdBQVlHLGdCQUVqRWhMLEVBQVE4SyxhQUNSOUssRUFBUWdMLGNBQWdCLFNBQVNDLEdBQzNCQSxHQUNGQSxFQUFRQyxTQVFaWCxFQUFRekksVUFBVXFKLE1BQVFaLEVBQVF6SSxVQUFVc0osSUFBTSxhQUNsRGIsRUFBUXpJLFVBQVVvSixNQUFRLFdBQ3hCbkksS0FBSzJILFNBQVNySyxLQUFLNkMsT0FBUUgsS0FBSzBILE1BSWxDekssRUFBUXFMLE9BQVMsU0FBU0MsRUFBTUMsR0FDOUJULGFBQWFRLEVBQUtFLGdCQUNsQkYsRUFBS0csYUFBZUYsR0FHdEJ2TCxFQUFRMEwsU0FBVyxTQUFTSixHQUMxQlIsYUFBYVEsRUFBS0UsZ0JBQ2xCRixFQUFLRyxjQUFnQixHQUd2QnpMLEVBQVEyTCxhQUFlM0wsRUFBUTRMLE9BQVMsU0FBU04sR0FDL0NSLGFBQWFRLEVBQUtFLGdCQUVsQixJQUFJRCxFQUFRRCxFQUFLRyxhQUNiRixHQUFTLElBQ1hELEVBQUtFLGVBQWlCYixZQUFXLFdBQzNCVyxFQUFLTyxZQUNQUCxFQUFLTyxlQUNOTixLQUtQLEVBQVEsR0FJUnZMLEVBQVFzSSxhQUFnQyxvQkFBVHdELE1BQXdCQSxLQUFLeEQsbUJBQ2xCLElBQVh5RCxHQUEwQkEsRUFBT3pELGNBQ3hDdkYsTUFBUUEsS0FBS3VGLGFBQ3JDdEksRUFBUWdNLGVBQWtDLG9CQUFURixNQUF3QkEsS0FBS0UscUJBQ2xCLElBQVhELEdBQTBCQSxFQUFPQyxnQkFDeENqSixNQUFRQSxLQUFLaUosaUIsa0NDM0R2Qyw2QkFDSSxhQUVBLElBQUlELEVBQU96RCxhQUFYLENBSUEsSUFJSTJELEVBNkhJQyxFQVpBQyxFQXJCQUMsRUFDQUMsRUFqR0pDLEVBQWEsRUFDYkMsRUFBZ0IsR0FDaEJDLEdBQXdCLEVBQ3hCQyxFQUFNVixFQUFPekosU0FvSmJvSyxFQUFXOUwsT0FBTytMLGdCQUFrQi9MLE9BQU8rTCxlQUFlWixHQUM5RFcsRUFBV0EsR0FBWUEsRUFBUy9CLFdBQWErQixFQUFXWCxFQUdmLHFCQUFyQyxHQUFHYSxTQUFTdk0sS0FBSzBMLEVBQU9jLFNBcEZ4QlosRUFBb0IsU0FBU2EsR0FDekJELEVBQVFFLFVBQVMsV0FBY0MsRUFBYUYsUUFJcEQsV0FHSSxHQUFJZixFQUFPN0MsY0FBZ0I2QyxFQUFPa0IsY0FBZSxDQUM3QyxJQUFJQyxHQUE0QixFQUM1QkMsRUFBZXBCLEVBQU9xQixVQU0xQixPQUxBckIsRUFBT3FCLFVBQVksV0FDZkYsR0FBNEIsR0FFaENuQixFQUFPN0MsWUFBWSxHQUFJLEtBQ3ZCNkMsRUFBT3FCLFVBQVlELEVBQ1pELEdBd0VKRyxHQUlBdEIsRUFBT3VCLGlCQTlDVm5CLEVBQVUsSUFBSW1CLGdCQUNWQyxNQUFNSCxVQUFZLFNBQVMvRCxHQUUvQjJELEVBRGEzRCxFQUFNN0csT0FJdkJ5SixFQUFvQixTQUFTYSxHQUN6QlgsRUFBUXFCLE1BQU10RSxZQUFZNEQsS0EyQ3ZCTCxHQUFPLHVCQUF3QkEsRUFBSWdCLGNBQWMsV0F0Q3BEdkIsRUFBT08sRUFBSWlCLGdCQUNmekIsRUFBb0IsU0FBU2EsR0FHekIsSUFBSWEsRUFBU2xCLEVBQUlnQixjQUFjLFVBQy9CRSxFQUFPQyxtQkFBcUIsV0FDeEJaLEVBQWFGLEdBQ2JhLEVBQU9DLG1CQUFxQixLQUM1QjFCLEVBQUsyQixZQUFZRixHQUNqQkEsRUFBUyxNQUViekIsRUFBSzRCLFlBQVlILEtBS3JCMUIsRUFBb0IsU0FBU2EsR0FDekJuQyxXQUFXcUMsRUFBYyxFQUFHRixLQWxENUJWLEVBQWdCLGdCQUFrQjFJLEtBQUtxSyxTQUFXLElBQ2xEMUIsRUFBa0IsU0FBU2hELEdBQ3ZCQSxFQUFNQyxTQUFXeUMsR0FDSyxpQkFBZjFDLEVBQU03RyxNQUN5QixJQUF0QzZHLEVBQU03RyxLQUFLd0wsUUFBUTVCLElBQ25CWSxHQUFjM0QsRUFBTTdHLEtBQUt5TCxNQUFNN0IsRUFBY25ILFVBSWpEOEcsRUFBTzVDLGlCQUNQNEMsRUFBTzVDLGlCQUFpQixVQUFXa0QsR0FBaUIsR0FFcEROLEVBQU9tQyxZQUFZLFlBQWE3QixHQUdwQ0osRUFBb0IsU0FBU2EsR0FDekJmLEVBQU83QyxZQUFZa0QsRUFBZ0JVLEVBQVEsT0FnRW5ESixFQUFTcEUsYUExS1QsU0FBc0I2RixHQUVJLG1CQUFiQSxJQUNUQSxFQUFXLElBQUluTCxTQUFTLEdBQUttTCxJQUkvQixJQURBLElBQUlDLEVBQU8sSUFBSUMsTUFBTXhELFVBQVU1RixPQUFTLEdBQy9CL0UsRUFBSSxFQUFHQSxFQUFJa08sRUFBS25KLE9BQVEvRSxJQUM3QmtPLEVBQUtsTyxHQUFLMkssVUFBVTNLLEVBQUksR0FHNUIsSUFBSW9PLEVBQU8sQ0FBRUgsU0FBVUEsRUFBVUMsS0FBTUEsR0FHdkMsT0FGQTdCLEVBQWNELEdBQWNnQyxFQUM1QnJDLEVBQWtCSyxHQUNYQSxLQTZKVEksRUFBU1YsZUFBaUJBLEVBMUoxQixTQUFTQSxFQUFlYyxVQUNiUCxFQUFjTyxHQXlCekIsU0FBU0UsRUFBYUYsR0FHbEIsR0FBSU4sRUFHQTdCLFdBQVdxQyxFQUFjLEVBQUdGLE9BQ3pCLENBQ0gsSUFBSXdCLEVBQU8vQixFQUFjTyxHQUN6QixHQUFJd0IsRUFBTSxDQUNOOUIsR0FBd0IsRUFDeEIsS0FqQ1osU0FBYThCLEdBQ1QsSUFBSUgsRUFBV0csRUFBS0gsU0FDaEJDLEVBQU9FLEVBQUtGLEtBQ2hCLE9BQVFBLEVBQUtuSixRQUNiLEtBQUssRUFDRGtKLElBQ0EsTUFDSixLQUFLLEVBQ0RBLEVBQVNDLEVBQUssSUFDZCxNQUNKLEtBQUssRUFDREQsRUFBU0MsRUFBSyxHQUFJQSxFQUFLLElBQ3ZCLE1BQ0osS0FBSyxFQUNERCxFQUFTQyxFQUFLLEdBQUlBLEVBQUssR0FBSUEsRUFBSyxJQUNoQyxNQUNKLFFBQ0lELEVBQVN2RCxNQUFNekksRUFBV2lNLElBaUJsQkcsQ0FBSUQsR0FDTixRQUNFdEMsRUFBZWMsR0FDZk4sR0FBd0IsTUF2RTVDLENBeUxrQixvQkFBVFYsVUFBeUMsSUFBWEMsRUFBeUJoSixLQUFPZ0osRUFBU0QsUSxvQ0N4TGhGLElBT0kwQyxFQUNBQyxFQVJBNUIsRUFBVTVNLEVBQU9ELFFBQVUsR0FVL0IsU0FBUzBPLElBQ0wsTUFBTSxJQUFJOUwsTUFBTSxtQ0FFcEIsU0FBUytMLElBQ0wsTUFBTSxJQUFJL0wsTUFBTSxxQ0FzQnBCLFNBQVNnTSxFQUFXQyxHQUNoQixHQUFJTCxJQUFxQjdELFdBRXJCLE9BQU9BLFdBQVdrRSxFQUFLLEdBRzNCLElBQUtMLElBQXFCRSxJQUFxQkYsSUFBcUI3RCxXQUVoRSxPQURBNkQsRUFBbUI3RCxXQUNaQSxXQUFXa0UsRUFBSyxHQUUzQixJQUVJLE9BQU9MLEVBQWlCSyxFQUFLLEdBQy9CLE1BQU01TCxHQUNKLElBRUksT0FBT3VMLEVBQWlCbk8sS0FBSyxLQUFNd08sRUFBSyxHQUMxQyxNQUFNNUwsR0FFSixPQUFPdUwsRUFBaUJuTyxLQUFLMEMsS0FBTThMLEVBQUssTUF2Q25ELFdBQ0csSUFFUUwsRUFEc0IsbUJBQWY3RCxXQUNZQSxXQUVBK0QsRUFFekIsTUFBT3pMLEdBQ0x1TCxFQUFtQkUsRUFFdkIsSUFFUUQsRUFEd0IsbUJBQWpCM0QsYUFDY0EsYUFFQTZELEVBRTNCLE1BQU8xTCxHQUNMd0wsRUFBcUJFLEdBakI3QixHQXdFQSxJQUVJRyxFQUZBQyxFQUFRLEdBQ1JDLEdBQVcsRUFFWEMsR0FBYyxFQUVsQixTQUFTQyxJQUNBRixHQUFhRixJQUdsQkUsR0FBVyxFQUNQRixFQUFhN0osT0FDYjhKLEVBQVFELEVBQWFLLE9BQU9KLEdBRTVCRSxHQUFjLEVBRWRGLEVBQU05SixRQUNObUssS0FJUixTQUFTQSxJQUNMLElBQUlKLEVBQUosQ0FHQSxJQUFJL0QsRUFBVTJELEVBQVdNLEdBQ3pCRixHQUFXLEVBR1gsSUFEQSxJQUFJSyxFQUFNTixFQUFNOUosT0FDVm9LLEdBQUssQ0FHUCxJQUZBUCxFQUFlQyxFQUNmQSxFQUFRLEtBQ0NFLEVBQWFJLEdBQ2RQLEdBQ0FBLEVBQWFHLEdBQVlWLE1BR2pDVSxHQUFjLEVBQ2RJLEVBQU1OLEVBQU05SixPQUVoQjZKLEVBQWUsS0FDZkUsR0FBVyxFQW5FZixTQUF5QjNILEdBQ3JCLEdBQUlvSCxJQUF1QjNELGFBRXZCLE9BQU9BLGFBQWF6RCxHQUd4QixJQUFLb0gsSUFBdUJFLElBQXdCRixJQUF1QjNELGFBRXZFLE9BREEyRCxFQUFxQjNELGFBQ2RBLGFBQWF6RCxHQUV4QixJQUVXb0gsRUFBbUJwSCxHQUM1QixNQUFPcEUsR0FDTCxJQUVJLE9BQU93TCxFQUFtQnBPLEtBQUssS0FBTWdILEdBQ3ZDLE1BQU9wRSxHQUdMLE9BQU93TCxFQUFtQnBPLEtBQUswQyxLQUFNc0UsS0FnRDdDaUksQ0FBZ0JyRSxJQWlCcEIsU0FBU3NFLEVBQUtWLEVBQUtXLEdBQ2Z6TSxLQUFLOEwsSUFBTUEsRUFDWDlMLEtBQUt5TSxNQUFRQSxFQVlqQixTQUFTQyxLQTVCVDVDLEVBQVFFLFNBQVcsU0FBVThCLEdBQ3pCLElBQUlULEVBQU8sSUFBSUMsTUFBTXhELFVBQVU1RixPQUFTLEdBQ3hDLEdBQUk0RixVQUFVNUYsT0FBUyxFQUNuQixJQUFLLElBQUkvRSxFQUFJLEVBQUdBLEVBQUkySyxVQUFVNUYsT0FBUS9FLElBQ2xDa08sRUFBS2xPLEVBQUksR0FBSzJLLFVBQVUzSyxHQUdoQzZPLEVBQU03SyxLQUFLLElBQUlxTCxFQUFLVixFQUFLVCxJQUNKLElBQWpCVyxFQUFNOUosUUFBaUIrSixHQUN2QkosRUFBV1EsSUFTbkJHLEVBQUt6TixVQUFVeU0sSUFBTSxXQUNqQnhMLEtBQUs4TCxJQUFJakUsTUFBTSxLQUFNN0gsS0FBS3lNLFFBRTlCM0MsRUFBUTZDLE1BQVEsVUFDaEI3QyxFQUFROEMsU0FBVSxFQUNsQjlDLEVBQVErQyxJQUFNLEdBQ2QvQyxFQUFRZ0QsS0FBTyxHQUNmaEQsRUFBUWlELFFBQVUsR0FDbEJqRCxFQUFRa0QsU0FBVyxHQUluQmxELEVBQVFtRCxHQUFLUCxFQUNiNUMsRUFBUW9ELFlBQWNSLEVBQ3RCNUMsRUFBUXFELEtBQU9ULEVBQ2Y1QyxFQUFRc0QsSUFBTVYsRUFDZDVDLEVBQVF1RCxlQUFpQlgsRUFDekI1QyxFQUFRd0QsbUJBQXFCWixFQUM3QjVDLEVBQVF5RCxLQUFPYixFQUNmNUMsRUFBUTBELGdCQUFrQmQsRUFDMUI1QyxFQUFRMkQsb0JBQXNCZixFQUU5QjVDLEVBQVE0RCxVQUFZLFNBQVVoUSxHQUFRLE1BQU8sSUFFN0NvTSxFQUFRNkQsUUFBVSxTQUFValEsR0FDeEIsTUFBTSxJQUFJbUMsTUFBTSxxQ0FHcEJpSyxFQUFROEQsSUFBTSxXQUFjLE1BQU8sS0FDbkM5RCxFQUFRK0QsTUFBUSxTQUFVQyxHQUN0QixNQUFNLElBQUlqTyxNQUFNLG1DQUVwQmlLLEVBQVFpRSxNQUFRLFdBQWEsT0FBTyxJLDZCQ3RMcENsUSxPQUFPQyxlQUFlYixFQUFTLGFBQWMsQ0FBRW1CLE9BQU8sSUFLdEQsTUFBTStGLEVBQWdCLEVBQVEsR0F3QjlCbEgsRUFBUXNILGlCQXZCUixNQUNJLCtCQUErQmhFLEdBQzNCLE1BQU0sU0FBRWtCLEdBQWEwQyxFQUFjL0MseUJBQXlCYixHQUM1RFAsS0FBS2dPLFFBQVF2TSxHQUFZQSxFQUFTbkMsU0FFdEMsUUFBUTJPLEdBQ0pqTyxLQUFLa08scUJBQXFCbE8sS0FBS21PLFVBQy9Cbk8sS0FBS29PLG1CQUFtQkgsR0FDeEJqTyxLQUFLbU8sU0FBV0YsRUFFcEIscUJBQXFCM08sR0FDWkEsSUFHTEEsRUFBUStPLFVBQVkvTyxFQUFRK08sVUFBVUMsUUFBUSx3QkFBeUIsS0FFM0UsbUJBQW1CaFAsR0FDVkEsSUFHTEEsRUFBUStPLFdBQWEsd0IsNkJDdEI3QnhRLE9BQU9DLGVBQWViLEVBQVMsYUFBYyxDQUFFbUIsT0FBTyxJQVN0RG5CLEVBQVFvSSxtQkFSUixTQUE0QmtKLEdBQ0ksWUFBeEJoUCxTQUFTaVAsWUFBb0Qsa0JBQXhCalAsU0FBU2lQLFdBQzlDalAsU0FBUzZHLGlCQUFpQixtQkFBb0JtSSxHQUc5Q0EsTSw2QkNOUjFRLE9BQU9DLGVBQWViLEVBQVMsYUFBYyxDQUFFbUIsT0FBTyxJQUN0RCxNQUFNZ0MsRUFBYSxFQUFRLEdBQzNCbkQsRUFBUThILHNCQUF5Qk4sR0FDdEIsSUFBSSxNQUNQLFlBQVkrQixFQUFNMUYsR0FDZDJELEVBQU8wQixZQUFZLENBQ2ZLLE9BQ0FELE9BQVFuRyxFQUFXTixjQUFjeUcsT0FDakN6RixZLGlCQ2JoQixZQVVBLElBQUkyTixFQUFrQixzQkFHbEJDLEVBQU0sSUFHTkMsRUFBWSxrQkFHWkMsRUFBUyxhQUdUQyxFQUFhLHFCQUdiQyxFQUFhLGFBR2JDLEVBQVksY0FHWkMsRUFBZUMsU0FHZkMsRUFBOEIsaUJBQVZsRyxHQUFzQkEsR0FBVUEsRUFBT25MLFNBQVdBLFFBQVVtTCxFQUdoRm1HLEVBQTBCLGlCQUFScEcsTUFBb0JBLE1BQVFBLEtBQUtsTCxTQUFXQSxRQUFVa0wsS0FHeEVxRyxFQUFPRixHQUFjQyxHQUFZbFAsU0FBUyxjQUFUQSxHQVVqQ29QLEVBUGN4UixPQUFPa0IsVUFPUThLLFNBRzdCeUYsRUFBWTNPLEtBQUtGLElBQ2pCOE8sRUFBWTVPLEtBQUtILElBa0JqQmdQLEVBQU0sV0FDUixPQUFPSixFQUFLSyxLQUFLRCxPQXlEbkIsU0FBU0UsRUFBU0MsRUFBTUMsRUFBTUMsR0FDNUIsSUFBSUMsRUFDQUMsRUFDQUMsRUFDQUMsRUFDQUMsRUFDQUMsRUFDQUMsRUFBaUIsRUFDakJDLEdBQVUsRUFDVkMsR0FBUyxFQUNUQyxHQUFXLEVBRWYsR0FBbUIsbUJBQVJaLEVBQ1QsTUFBTSxJQUFJYSxVQUFVL0IsR0FVdEIsU0FBU2dDLEVBQVdDLEdBQ2xCLElBQUlyRixFQUFPeUUsRUFDUGEsRUFBVVosRUFLZCxPQUhBRCxFQUFXQyxPQUFXM1EsRUFDdEJnUixFQUFpQk0sRUFDakJULEVBQVNOLEVBQUs5SCxNQUFNOEksRUFBU3RGLEdBcUIvQixTQUFTdUYsRUFBYUYsR0FDcEIsSUFBSUcsRUFBb0JILEVBQU9QLEVBTS9CLFlBQXlCL1EsSUFBakIrUSxHQUErQlUsR0FBcUJqQixHQUN6RGlCLEVBQW9CLEdBQU9QLEdBTkpJLEVBQU9OLEdBTThCSixFQUdqRSxTQUFTYyxJQUNQLElBQUlKLEVBQU9sQixJQUNYLEdBQUlvQixFQUFhRixHQUNmLE9BQU9LLEVBQWFMLEdBR3RCUixFQUFVdEksV0FBV2tKLEVBekJ2QixTQUF1QkosR0FDckIsSUFFSVQsRUFBU0wsR0FGV2MsRUFBT1AsR0FJL0IsT0FBT0csRUFBU2YsRUFBVVUsRUFBUUQsR0FIUlUsRUFBT04sSUFHa0NILEVBb0JoQ2UsQ0FBY04sSUFHbkQsU0FBU0ssRUFBYUwsR0FLcEIsT0FKQVIsT0FBVTlRLEVBSU5tUixHQUFZVCxFQUNQVyxFQUFXQyxJQUVwQlosRUFBV0MsT0FBVzNRLEVBQ2Y2USxHQWVULFNBQVNnQixJQUNQLElBQUlQLEVBQU9sQixJQUNQMEIsRUFBYU4sRUFBYUYsR0FNOUIsR0FKQVosRUFBV2hJLFVBQ1hpSSxFQUFXL1AsS0FDWG1RLEVBQWVPLEVBRVhRLEVBQVksQ0FDZCxRQUFnQjlSLElBQVo4USxFQUNGLE9BdkVOLFNBQXFCUSxHQU1uQixPQUpBTixFQUFpQk0sRUFFakJSLEVBQVV0SSxXQUFXa0osRUFBY2xCLEdBRTVCUyxFQUFVSSxFQUFXQyxHQUFRVCxFQWlFekJrQixDQUFZaEIsR0FFckIsR0FBSUcsRUFHRixPQURBSixFQUFVdEksV0FBV2tKLEVBQWNsQixHQUM1QmEsRUFBV04sR0FNdEIsWUFIZ0IvUSxJQUFaOFEsSUFDRkEsRUFBVXRJLFdBQVdrSixFQUFjbEIsSUFFOUJLLEVBSVQsT0F4R0FMLEVBQU93QixFQUFTeEIsSUFBUyxFQUNyQnlCLEVBQVN4QixLQUNYUSxJQUFZUixFQUFRUSxRQUVwQkwsR0FEQU0sRUFBUyxZQUFhVCxHQUNIUCxFQUFVOEIsRUFBU3ZCLEVBQVFHLFVBQVksRUFBR0osR0FBUUksRUFDckVPLEVBQVcsYUFBY1YsSUFBWUEsRUFBUVUsU0FBV0EsR0FpRzFEVSxFQUFVSyxPQW5DVixnQkFDa0JsUyxJQUFaOFEsR0FDRm5JLGFBQWFtSSxHQUVmRSxFQUFpQixFQUNqQk4sRUFBV0ssRUFBZUosRUFBV0csT0FBVTlRLEdBK0JqRDZSLEVBQVVNLE1BNUJWLFdBQ0UsWUFBbUJuUyxJQUFaOFEsRUFBd0JELEVBQVNjLEVBQWF2QixNQTRCaER5QixFQTBGVCxTQUFTSSxFQUFTalQsR0FDaEIsSUFBSW9JLFNBQWNwSSxFQUNsQixRQUFTQSxJQUFrQixVQUFSb0ksR0FBNEIsWUFBUkEsR0E0RXpDLFNBQVM0SyxFQUFTaFQsR0FDaEIsR0FBb0IsaUJBQVRBLEVBQ1QsT0FBT0EsRUFFVCxHQWhDRixTQUFrQkEsR0FDaEIsTUFBdUIsaUJBQVRBLEdBdEJoQixTQUFzQkEsR0FDcEIsUUFBU0EsR0FBeUIsaUJBQVRBLEVBc0J0Qm9ULENBQWFwVCxJQUFVaVIsRUFBZS9SLEtBQUtjLElBQVV1USxFQThCcEQ4QyxDQUFTclQsR0FDWCxPQUFPc1EsRUFFVCxHQUFJMkMsRUFBU2pULEdBQVEsQ0FDbkIsSUFBSXNULEVBQWdDLG1CQUFqQnRULEVBQU11VCxRQUF3QnZULEVBQU11VCxVQUFZdlQsRUFDbkVBLEVBQVFpVCxFQUFTSyxHQUFVQSxFQUFRLEdBQU1BLEVBRTNDLEdBQW9CLGlCQUFUdFQsRUFDVCxPQUFpQixJQUFWQSxFQUFjQSxHQUFTQSxFQUVoQ0EsRUFBUUEsRUFBTWtRLFFBQVFNLEVBQVEsSUFDOUIsSUFBSWdELEVBQVc5QyxFQUFXekgsS0FBS2pKLEdBQy9CLE9BQVF3VCxHQUFZN0MsRUFBVTFILEtBQUtqSixHQUMvQjRRLEVBQWE1USxFQUFNOE0sTUFBTSxHQUFJMEcsRUFBVyxFQUFJLEdBQzNDL0MsRUFBV3hILEtBQUtqSixHQUFTc1EsR0FBT3RRLEVBR3ZDbEIsRUFBT0QsUUE5SVAsU0FBa0IwUyxFQUFNQyxFQUFNQyxHQUM1QixJQUFJUSxHQUFVLEVBQ1ZFLEdBQVcsRUFFZixHQUFtQixtQkFBUlosRUFDVCxNQUFNLElBQUlhLFVBQVUvQixHQU10QixPQUpJNEMsRUFBU3hCLEtBQ1hRLEVBQVUsWUFBYVIsSUFBWUEsRUFBUVEsUUFBVUEsRUFDckRFLEVBQVcsYUFBY1YsSUFBWUEsRUFBUVUsU0FBV0EsR0FFbkRiLEVBQVNDLEVBQU1DLEVBQU0sQ0FDMUIsUUFBV1MsRUFDWCxRQUFXVCxFQUNYLFNBQVlXLE8iLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBnZXR0ZXIgfSk7XG4gXHRcdH1cbiBcdH07XG5cbiBcdC8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbiBcdFx0aWYodHlwZW9mIFN5bWJvbCAhPT0gJ3VuZGVmaW5lZCcgJiYgU3ltYm9sLnRvU3RyaW5nVGFnKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFN5bWJvbC50b1N0cmluZ1RhZywgeyB2YWx1ZTogJ01vZHVsZScgfSk7XG4gXHRcdH1cbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbiBcdH07XG5cbiBcdC8vIGNyZWF0ZSBhIGZha2UgbmFtZXNwYWNlIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDE6IHZhbHVlIGlzIGEgbW9kdWxlIGlkLCByZXF1aXJlIGl0XG4gXHQvLyBtb2RlICYgMjogbWVyZ2UgYWxsIHByb3BlcnRpZXMgb2YgdmFsdWUgaW50byB0aGUgbnNcbiBcdC8vIG1vZGUgJiA0OiByZXR1cm4gdmFsdWUgd2hlbiBhbHJlYWR5IG5zIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDh8MTogYmVoYXZlIGxpa2UgcmVxdWlyZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy50ID0gZnVuY3Rpb24odmFsdWUsIG1vZGUpIHtcbiBcdFx0aWYobW9kZSAmIDEpIHZhbHVlID0gX193ZWJwYWNrX3JlcXVpcmVfXyh2YWx1ZSk7XG4gXHRcdGlmKG1vZGUgJiA4KSByZXR1cm4gdmFsdWU7XG4gXHRcdGlmKChtb2RlICYgNCkgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJiB2YWx1ZS5fX2VzTW9kdWxlKSByZXR1cm4gdmFsdWU7XG4gXHRcdHZhciBucyA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18ucihucyk7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShucywgJ2RlZmF1bHQnLCB7IGVudW1lcmFibGU6IHRydWUsIHZhbHVlOiB2YWx1ZSB9KTtcbiBcdFx0aWYobW9kZSAmIDIgJiYgdHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSBmb3IodmFyIGtleSBpbiB2YWx1ZSkgX193ZWJwYWNrX3JlcXVpcmVfXy5kKG5zLCBrZXksIGZ1bmN0aW9uKGtleSkgeyByZXR1cm4gdmFsdWVba2V5XTsgfS5iaW5kKG51bGwsIGtleSkpO1xuIFx0XHRyZXR1cm4gbnM7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gMyk7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xubGV0IGNhY2hlZFNldHRpbmdzID0gdW5kZWZpbmVkO1xuZnVuY3Rpb24gZ2V0RGF0YShrZXkpIHtcbiAgICBjb25zdCBlbGVtZW50ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3ZzY29kZS1tYXJrZG93bi1wcmV2aWV3LWRhdGEnKTtcbiAgICBpZiAoZWxlbWVudCkge1xuICAgICAgICBjb25zdCBkYXRhID0gZWxlbWVudC5nZXRBdHRyaWJ1dGUoa2V5KTtcbiAgICAgICAgaWYgKGRhdGEpIHtcbiAgICAgICAgICAgIHJldHVybiBKU09OLnBhcnNlKGRhdGEpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcihgQ291bGQgbm90IGxvYWQgZGF0YSBmb3IgJHtrZXl9YCk7XG59XG5leHBvcnRzLmdldERhdGEgPSBnZXREYXRhO1xuZnVuY3Rpb24gZ2V0U2V0dGluZ3MoKSB7XG4gICAgaWYgKGNhY2hlZFNldHRpbmdzKSB7XG4gICAgICAgIHJldHVybiBjYWNoZWRTZXR0aW5ncztcbiAgICB9XG4gICAgY2FjaGVkU2V0dGluZ3MgPSBnZXREYXRhKCdkYXRhLXNldHRpbmdzJyk7XG4gICAgaWYgKGNhY2hlZFNldHRpbmdzKSB7XG4gICAgICAgIHJldHVybiBjYWNoZWRTZXR0aW5ncztcbiAgICB9XG4gICAgdGhyb3cgbmV3IEVycm9yKCdDb3VsZCBub3QgbG9hZCBzZXR0aW5ncycpO1xufVxuZXhwb3J0cy5nZXRTZXR0aW5ncyA9IGdldFNldHRpbmdzO1xuIiwidmFyIGc7XG5cbi8vIFRoaXMgd29ya3MgaW4gbm9uLXN0cmljdCBtb2RlXG5nID0gKGZ1bmN0aW9uKCkge1xuXHRyZXR1cm4gdGhpcztcbn0pKCk7XG5cbnRyeSB7XG5cdC8vIFRoaXMgd29ya3MgaWYgZXZhbCBpcyBhbGxvd2VkIChzZWUgQ1NQKVxuXHRnID0gZyB8fCBuZXcgRnVuY3Rpb24oXCJyZXR1cm4gdGhpc1wiKSgpO1xufSBjYXRjaCAoZSkge1xuXHQvLyBUaGlzIHdvcmtzIGlmIHRoZSB3aW5kb3cgcmVmZXJlbmNlIGlzIGF2YWlsYWJsZVxuXHRpZiAodHlwZW9mIHdpbmRvdyA9PT0gXCJvYmplY3RcIikgZyA9IHdpbmRvdztcbn1cblxuLy8gZyBjYW4gc3RpbGwgYmUgdW5kZWZpbmVkLCBidXQgbm90aGluZyB0byBkbyBhYm91dCBpdC4uLlxuLy8gV2UgcmV0dXJuIHVuZGVmaW5lZCwgaW5zdGVhZCBvZiBub3RoaW5nIGhlcmUsIHNvIGl0J3Ncbi8vIGVhc2llciB0byBoYW5kbGUgdGhpcyBjYXNlLiBpZighZ2xvYmFsKSB7IC4uLn1cblxubW9kdWxlLmV4cG9ydHMgPSBnO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmNvbnN0IHNldHRpbmdzXzEgPSByZXF1aXJlKFwiLi9zZXR0aW5nc1wiKTtcbmNvbnN0IGNvZGVMaW5lQ2xhc3MgPSAnY29kZS1saW5lJztcbmZ1bmN0aW9uIGNsYW1wKG1pbiwgbWF4LCB2YWx1ZSkge1xuICAgIHJldHVybiBNYXRoLm1pbihtYXgsIE1hdGgubWF4KG1pbiwgdmFsdWUpKTtcbn1cbmZ1bmN0aW9uIGNsYW1wTGluZShsaW5lKSB7XG4gICAgcmV0dXJuIGNsYW1wKDAsIHNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKS5saW5lQ291bnQgLSAxLCBsaW5lKTtcbn1cbmNvbnN0IGdldENvZGVMaW5lRWxlbWVudHMgPSAoKCkgPT4ge1xuICAgIGxldCBlbGVtZW50cztcbiAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgICBpZiAoIWVsZW1lbnRzKSB7XG4gICAgICAgICAgICBlbGVtZW50cyA9IFt7IGVsZW1lbnQ6IGRvY3VtZW50LmJvZHksIGxpbmU6IDAgfV07XG4gICAgICAgICAgICBmb3IgKGNvbnN0IGVsZW1lbnQgb2YgZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZShjb2RlTGluZUNsYXNzKSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGxpbmUgPSArZWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2RhdGEtbGluZScpO1xuICAgICAgICAgICAgICAgIGlmIChpc05hTihsaW5lKSkge1xuICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKGVsZW1lbnQudGFnTmFtZSA9PT0gJ0NPREUnICYmIGVsZW1lbnQucGFyZW50RWxlbWVudCAmJiBlbGVtZW50LnBhcmVudEVsZW1lbnQudGFnTmFtZSA9PT0gJ1BSRScpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gRmVuY2hlZCBjb2RlIGJsb2NrcyBhcmUgYSBzcGVjaWFsIGNhc2Ugc2luY2UgdGhlIGBjb2RlLWxpbmVgIGNhbiBvbmx5IGJlIG1hcmtlZCBvblxuICAgICAgICAgICAgICAgICAgICAvLyB0aGUgYDxjb2RlPmAgZWxlbWVudCBhbmQgbm90IHRoZSBwYXJlbnQgYDxwcmU+YCBlbGVtZW50LlxuICAgICAgICAgICAgICAgICAgICBlbGVtZW50cy5wdXNoKHsgZWxlbWVudDogZWxlbWVudC5wYXJlbnRFbGVtZW50LCBsaW5lIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgZWxlbWVudHMucHVzaCh7IGVsZW1lbnQ6IGVsZW1lbnQsIGxpbmUgfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBlbGVtZW50cztcbiAgICB9O1xufSkoKTtcbi8qKlxuICogRmluZCB0aGUgaHRtbCBlbGVtZW50cyB0aGF0IG1hcCB0byBhIHNwZWNpZmljIHRhcmdldCBsaW5lIGluIHRoZSBlZGl0b3IuXG4gKlxuICogSWYgYW4gZXhhY3QgbWF0Y2gsIHJldHVybnMgYSBzaW5nbGUgZWxlbWVudC4gSWYgdGhlIGxpbmUgaXMgYmV0d2VlbiBlbGVtZW50cyxcbiAqIHJldHVybnMgdGhlIGVsZW1lbnQgcHJpb3IgdG8gYW5kIHRoZSBlbGVtZW50IGFmdGVyIHRoZSBnaXZlbiBsaW5lLlxuICovXG5mdW5jdGlvbiBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUodGFyZ2V0TGluZSkge1xuICAgIGNvbnN0IGxpbmVOdW1iZXIgPSBNYXRoLmZsb29yKHRhcmdldExpbmUpO1xuICAgIGNvbnN0IGxpbmVzID0gZ2V0Q29kZUxpbmVFbGVtZW50cygpO1xuICAgIGxldCBwcmV2aW91cyA9IGxpbmVzWzBdIHx8IG51bGw7XG4gICAgZm9yIChjb25zdCBlbnRyeSBvZiBsaW5lcykge1xuICAgICAgICBpZiAoZW50cnkubGluZSA9PT0gbGluZU51bWJlcikge1xuICAgICAgICAgICAgcmV0dXJuIHsgcHJldmlvdXM6IGVudHJ5LCBuZXh0OiB1bmRlZmluZWQgfTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIGlmIChlbnRyeS5saW5lID4gbGluZU51bWJlcikge1xuICAgICAgICAgICAgcmV0dXJuIHsgcHJldmlvdXMsIG5leHQ6IGVudHJ5IH07XG4gICAgICAgIH1cbiAgICAgICAgcHJldmlvdXMgPSBlbnRyeTtcbiAgICB9XG4gICAgcmV0dXJuIHsgcHJldmlvdXMgfTtcbn1cbmV4cG9ydHMuZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lID0gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lO1xuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgYXJlIGF0IGEgc3BlY2lmaWMgcGl4ZWwgb2Zmc2V0IG9uIHRoZSBwYWdlLlxuICovXG5mdW5jdGlvbiBnZXRMaW5lRWxlbWVudHNBdFBhZ2VPZmZzZXQob2Zmc2V0KSB7XG4gICAgY29uc3QgbGluZXMgPSBnZXRDb2RlTGluZUVsZW1lbnRzKCk7XG4gICAgY29uc3QgcG9zaXRpb24gPSBvZmZzZXQgLSB3aW5kb3cuc2Nyb2xsWTtcbiAgICBsZXQgbG8gPSAtMTtcbiAgICBsZXQgaGkgPSBsaW5lcy5sZW5ndGggLSAxO1xuICAgIHdoaWxlIChsbyArIDEgPCBoaSkge1xuICAgICAgICBjb25zdCBtaWQgPSBNYXRoLmZsb29yKChsbyArIGhpKSAvIDIpO1xuICAgICAgICBjb25zdCBib3VuZHMgPSBnZXRFbGVtZW50Qm91bmRzKGxpbmVzW21pZF0pO1xuICAgICAgICBpZiAoYm91bmRzLnRvcCArIGJvdW5kcy5oZWlnaHQgPj0gcG9zaXRpb24pIHtcbiAgICAgICAgICAgIGhpID0gbWlkO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgbG8gPSBtaWQ7XG4gICAgICAgIH1cbiAgICB9XG4gICAgY29uc3QgaGlFbGVtZW50ID0gbGluZXNbaGldO1xuICAgIGNvbnN0IGhpQm91bmRzID0gZ2V0RWxlbWVudEJvdW5kcyhoaUVsZW1lbnQpO1xuICAgIGlmIChoaSA+PSAxICYmIGhpQm91bmRzLnRvcCA+IHBvc2l0aW9uKSB7XG4gICAgICAgIGNvbnN0IGxvRWxlbWVudCA9IGxpbmVzW2xvXTtcbiAgICAgICAgcmV0dXJuIHsgcHJldmlvdXM6IGxvRWxlbWVudCwgbmV4dDogaGlFbGVtZW50IH07XG4gICAgfVxuICAgIGlmIChoaSA+IDEgJiYgaGkgPCBsaW5lcy5sZW5ndGggJiYgaGlCb3VuZHMudG9wICsgaGlCb3VuZHMuaGVpZ2h0ID4gcG9zaXRpb24pIHtcbiAgICAgICAgcmV0dXJuIHsgcHJldmlvdXM6IGhpRWxlbWVudCwgbmV4dDogbGluZXNbaGkgKyAxXSB9O1xuICAgIH1cbiAgICByZXR1cm4geyBwcmV2aW91czogaGlFbGVtZW50IH07XG59XG5leHBvcnRzLmdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldCA9IGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldDtcbmZ1bmN0aW9uIGdldEVsZW1lbnRCb3VuZHMoeyBlbGVtZW50IH0pIHtcbiAgICBjb25zdCBteUJvdW5kcyA9IGVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgLy8gU29tZSBjb2RlIGxpbmUgZWxlbWVudHMgbWF5IGNvbnRhaW4gb3RoZXIgY29kZSBsaW5lIGVsZW1lbnRzLlxuICAgIC8vIEluIHRob3NlIGNhc2VzLCBvbmx5IHRha2UgdGhlIGhlaWdodCB1cCB0byB0aGF0IGNoaWxkLlxuICAgIGNvbnN0IGNvZGVMaW5lQ2hpbGQgPSBlbGVtZW50LnF1ZXJ5U2VsZWN0b3IoYC4ke2NvZGVMaW5lQ2xhc3N9YCk7XG4gICAgaWYgKGNvZGVMaW5lQ2hpbGQpIHtcbiAgICAgICAgY29uc3QgY2hpbGRCb3VuZHMgPSBjb2RlTGluZUNoaWxkLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgICBjb25zdCBoZWlnaHQgPSBNYXRoLm1heCgxLCAoY2hpbGRCb3VuZHMudG9wIC0gbXlCb3VuZHMudG9wKSk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICB0b3A6IG15Qm91bmRzLnRvcCxcbiAgICAgICAgICAgIGhlaWdodDogaGVpZ2h0XG4gICAgICAgIH07XG4gICAgfVxuICAgIHJldHVybiBteUJvdW5kcztcbn1cbi8qKlxuICogQXR0ZW1wdCB0byByZXZlYWwgdGhlIGVsZW1lbnQgZm9yIGEgc291cmNlIGxpbmUgaW4gdGhlIGVkaXRvci5cbiAqL1xuZnVuY3Rpb24gc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGxpbmUpIHtcbiAgICBpZiAoIXNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKS5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChsaW5lIDw9IDApIHtcbiAgICAgICAgd2luZG93LnNjcm9sbCh3aW5kb3cuc2Nyb2xsWCwgMCk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKGxpbmUpO1xuICAgIGlmICghcHJldmlvdXMpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBsZXQgc2Nyb2xsVG8gPSAwO1xuICAgIGNvbnN0IHJlY3QgPSBnZXRFbGVtZW50Qm91bmRzKHByZXZpb3VzKTtcbiAgICBjb25zdCBwcmV2aW91c1RvcCA9IHJlY3QudG9wO1xuICAgIGlmIChuZXh0ICYmIG5leHQubGluZSAhPT0gcHJldmlvdXMubGluZSkge1xuICAgICAgICAvLyBCZXR3ZWVuIHR3byBlbGVtZW50cy4gR28gdG8gcGVyY2VudGFnZSBvZmZzZXQgYmV0d2VlbiB0aGVtLlxuICAgICAgICBjb25zdCBiZXR3ZWVuUHJvZ3Jlc3MgPSAobGluZSAtIHByZXZpb3VzLmxpbmUpIC8gKG5leHQubGluZSAtIHByZXZpb3VzLmxpbmUpO1xuICAgICAgICBjb25zdCBlbGVtZW50T2Zmc2V0ID0gbmV4dC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLnRvcCAtIHByZXZpb3VzVG9wO1xuICAgICAgICBzY3JvbGxUbyA9IHByZXZpb3VzVG9wICsgYmV0d2VlblByb2dyZXNzICogZWxlbWVudE9mZnNldDtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIGNvbnN0IHByb2dyZXNzSW5FbGVtZW50ID0gbGluZSAtIE1hdGguZmxvb3IobGluZSk7XG4gICAgICAgIHNjcm9sbFRvID0gcHJldmlvdXNUb3AgKyAocmVjdC5oZWlnaHQgKiBwcm9ncmVzc0luRWxlbWVudCk7XG4gICAgfVxuICAgIHdpbmRvdy5zY3JvbGwod2luZG93LnNjcm9sbFgsIE1hdGgubWF4KDEsIHdpbmRvdy5zY3JvbGxZICsgc2Nyb2xsVG8pKTtcbn1cbmV4cG9ydHMuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lID0gc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lO1xuZnVuY3Rpb24gZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQob2Zmc2V0KSB7XG4gICAgY29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0KG9mZnNldCk7XG4gICAgaWYgKHByZXZpb3VzKSB7XG4gICAgICAgIGNvbnN0IHByZXZpb3VzQm91bmRzID0gZ2V0RWxlbWVudEJvdW5kcyhwcmV2aW91cyk7XG4gICAgICAgIGNvbnN0IG9mZnNldEZyb21QcmV2aW91cyA9IChvZmZzZXQgLSB3aW5kb3cuc2Nyb2xsWSAtIHByZXZpb3VzQm91bmRzLnRvcCk7XG4gICAgICAgIGlmIChuZXh0KSB7XG4gICAgICAgICAgICBjb25zdCBwcm9ncmVzc0JldHdlZW5FbGVtZW50cyA9IG9mZnNldEZyb21QcmV2aW91cyAvIChnZXRFbGVtZW50Qm91bmRzKG5leHQpLnRvcCAtIHByZXZpb3VzQm91bmRzLnRvcCk7XG4gICAgICAgICAgICBjb25zdCBsaW5lID0gcHJldmlvdXMubGluZSArIHByb2dyZXNzQmV0d2VlbkVsZW1lbnRzICogKG5leHQubGluZSAtIHByZXZpb3VzLmxpbmUpO1xuICAgICAgICAgICAgcmV0dXJuIGNsYW1wTGluZShsaW5lKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGNvbnN0IHByb2dyZXNzV2l0aGluRWxlbWVudCA9IG9mZnNldEZyb21QcmV2aW91cyAvIChwcmV2aW91c0JvdW5kcy5oZWlnaHQpO1xuICAgICAgICAgICAgY29uc3QgbGluZSA9IHByZXZpb3VzLmxpbmUgKyBwcm9ncmVzc1dpdGhpbkVsZW1lbnQ7XG4gICAgICAgICAgICByZXR1cm4gY2xhbXBMaW5lKGxpbmUpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xufVxuZXhwb3J0cy5nZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCA9IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0O1xuLyoqXG4gKiBUcnkgdG8gZmluZCB0aGUgaHRtbCBlbGVtZW50IGJ5IHVzaW5nIGEgZnJhZ21lbnQgaWRcbiAqL1xuZnVuY3Rpb24gZ2V0TGluZUVsZW1lbnRGb3JGcmFnbWVudChmcmFnbWVudCkge1xuICAgIHJldHVybiBnZXRDb2RlTGluZUVsZW1lbnRzKCkuZmluZCgoZWxlbWVudCkgPT4ge1xuICAgICAgICByZXR1cm4gZWxlbWVudC5lbGVtZW50LmlkID09PSBmcmFnbWVudDtcbiAgICB9KTtcbn1cbmV4cG9ydHMuZ2V0TGluZUVsZW1lbnRGb3JGcmFnbWVudCA9IGdldExpbmVFbGVtZW50Rm9yRnJhZ21lbnQ7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuY29uc3QgYWN0aXZlTGluZU1hcmtlcl8xID0gcmVxdWlyZShcIi4vYWN0aXZlTGluZU1hcmtlclwiKTtcbmNvbnN0IGV2ZW50c18xID0gcmVxdWlyZShcIi4vZXZlbnRzXCIpO1xuY29uc3QgbWVzc2FnaW5nXzEgPSByZXF1aXJlKFwiLi9tZXNzYWdpbmdcIik7XG5jb25zdCBzY3JvbGxfc3luY18xID0gcmVxdWlyZShcIi4vc2Nyb2xsLXN5bmNcIik7XG5jb25zdCBzZXR0aW5nc18xID0gcmVxdWlyZShcIi4vc2V0dGluZ3NcIik7XG5jb25zdCB0aHJvdHRsZSA9IHJlcXVpcmUoXCJsb2Rhc2gudGhyb3R0bGVcIik7XG5sZXQgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuY29uc3QgbWFya2VyID0gbmV3IGFjdGl2ZUxpbmVNYXJrZXJfMS5BY3RpdmVMaW5lTWFya2VyKCk7XG5jb25zdCBzZXR0aW5ncyA9IHNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKTtcbmNvbnN0IHZzY29kZSA9IGFjcXVpcmVWc0NvZGVBcGkoKTtcbmNvbnN0IHN0YXRlID0geyAuLi52c2NvZGUuZ2V0U3RhdGUoKSwgLi4uc2V0dGluZ3NfMS5nZXREYXRhKCdkYXRhLXN0YXRlJykgfTtcbi8vIE1ha2Ugc3VyZSB0byBzeW5jIFZTIENvZGUgc3RhdGUgaGVyZVxudnNjb2RlLnNldFN0YXRlKHN0YXRlKTtcbmNvbnN0IG1lc3NhZ2luZyA9IG1lc3NhZ2luZ18xLmNyZWF0ZVBvc3RlckZvclZzQ29kZSh2c2NvZGUpO1xud2luZG93LmNzcEFsZXJ0ZXIuc2V0UG9zdGVyKG1lc3NhZ2luZyk7XG53aW5kb3cuc3R5bGVMb2FkaW5nTW9uaXRvci5zZXRQb3N0ZXIobWVzc2FnaW5nKTtcbndpbmRvdy5vbmxvYWQgPSAoKSA9PiB7XG4gICAgdXBkYXRlSW1hZ2VTaXplcygpO1xufTtcbmV2ZW50c18xLm9uY2VEb2N1bWVudExvYWRlZCgoKSA9PiB7XG4gICAgY29uc3Qgc2Nyb2xsUHJvZ3Jlc3MgPSBzdGF0ZS5zY3JvbGxQcm9ncmVzcztcbiAgICBpZiAodHlwZW9mIHNjcm9sbFByb2dyZXNzID09PSAnbnVtYmVyJyAmJiAhc2V0dGluZ3MuZnJhZ21lbnQpIHtcbiAgICAgICAgc2V0SW1tZWRpYXRlKCgpID0+IHtcbiAgICAgICAgICAgIHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcbiAgICAgICAgICAgIHdpbmRvdy5zY3JvbGxUbygwLCBzY3JvbGxQcm9ncmVzcyAqIGRvY3VtZW50LmJvZHkuY2xpZW50SGVpZ2h0KTtcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKHNldHRpbmdzLnNjcm9sbFByZXZpZXdXaXRoRWRpdG9yKSB7XG4gICAgICAgIHNldEltbWVkaWF0ZSgoKSA9PiB7XG4gICAgICAgICAgICAvLyBUcnkgdG8gc2Nyb2xsIHRvIGZyYWdtZW50IGlmIGF2YWlsYWJsZVxuICAgICAgICAgICAgaWYgKHNldHRpbmdzLmZyYWdtZW50KSB7XG4gICAgICAgICAgICAgICAgc3RhdGUuZnJhZ21lbnQgPSB1bmRlZmluZWQ7XG4gICAgICAgICAgICAgICAgdnNjb2RlLnNldFN0YXRlKHN0YXRlKTtcbiAgICAgICAgICAgICAgICBjb25zdCBlbGVtZW50ID0gc2Nyb2xsX3N5bmNfMS5nZXRMaW5lRWxlbWVudEZvckZyYWdtZW50KHNldHRpbmdzLmZyYWdtZW50KTtcbiAgICAgICAgICAgICAgICBpZiAoZWxlbWVudCkge1xuICAgICAgICAgICAgICAgICAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIHNjcm9sbF9zeW5jXzEuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGVsZW1lbnQubGluZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgaWYgKCFpc05hTihzZXR0aW5ncy5saW5lKSkge1xuICAgICAgICAgICAgICAgICAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIHNjcm9sbF9zeW5jXzEuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKHNldHRpbmdzLmxpbmUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxufSk7XG5jb25zdCBvblVwZGF0ZVZpZXcgPSAoKCkgPT4ge1xuICAgIGNvbnN0IGRvU2Nyb2xsID0gdGhyb3R0bGUoKGxpbmUpID0+IHtcbiAgICAgICAgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuICAgICAgICBzY3JvbGxfc3luY18xLnNjcm9sbFRvUmV2ZWFsU291cmNlTGluZShsaW5lKTtcbiAgICB9LCA1MCk7XG4gICAgcmV0dXJuIChsaW5lKSA9PiB7XG4gICAgICAgIGlmICghaXNOYU4obGluZSkpIHtcbiAgICAgICAgICAgIHN0YXRlLmxpbmUgPSBsaW5lO1xuICAgICAgICAgICAgZG9TY3JvbGwobGluZSk7XG4gICAgICAgIH1cbiAgICB9O1xufSkoKTtcbmxldCB1cGRhdGVJbWFnZVNpemVzID0gdGhyb3R0bGUoKCkgPT4ge1xuICAgIGNvbnN0IGltYWdlSW5mbyA9IFtdO1xuICAgIGxldCBpbWFnZXMgPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnaW1nJyk7XG4gICAgaWYgKGltYWdlcykge1xuICAgICAgICBsZXQgaTtcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IGltYWdlcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgY29uc3QgaW1nID0gaW1hZ2VzW2ldO1xuICAgICAgICAgICAgaWYgKGltZy5jbGFzc0xpc3QuY29udGFpbnMoJ2xvYWRpbmcnKSkge1xuICAgICAgICAgICAgICAgIGltZy5jbGFzc0xpc3QucmVtb3ZlKCdsb2FkaW5nJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpbWFnZUluZm8ucHVzaCh7XG4gICAgICAgICAgICAgICAgaWQ6IGltZy5pZCxcbiAgICAgICAgICAgICAgICBoZWlnaHQ6IGltZy5oZWlnaHQsXG4gICAgICAgICAgICAgICAgd2lkdGg6IGltZy53aWR0aFxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgbWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdjYWNoZUltYWdlU2l6ZXMnLCBpbWFnZUluZm8pO1xuICAgIH1cbn0sIDUwKTtcbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCAoKSA9PiB7XG4gICAgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuICAgIHVwZGF0ZVNjcm9sbFByb2dyZXNzKCk7XG4gICAgdXBkYXRlSW1hZ2VTaXplcygpO1xufSwgdHJ1ZSk7XG53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsIGV2ZW50ID0+IHtcbiAgICBpZiAoZXZlbnQuZGF0YS5zb3VyY2UgIT09IHNldHRpbmdzLnNvdXJjZSkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIHN3aXRjaCAoZXZlbnQuZGF0YS50eXBlKSB7XG4gICAgICAgIGNhc2UgJ29uRGlkQ2hhbmdlVGV4dEVkaXRvclNlbGVjdGlvbic6XG4gICAgICAgICAgICBtYXJrZXIub25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGV2ZW50LmRhdGEubGluZSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAndXBkYXRlVmlldyc6XG4gICAgICAgICAgICBvblVwZGF0ZVZpZXcoZXZlbnQuZGF0YS5saW5lKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgIH1cbn0sIGZhbHNlKTtcbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2RibGNsaWNrJywgZXZlbnQgPT4ge1xuICAgIGlmICghc2V0dGluZ3MuZG91YmxlQ2xpY2tUb1N3aXRjaFRvRWRpdG9yKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgLy8gSWdub3JlIGNsaWNrcyBvbiBsaW5rc1xuICAgIGZvciAobGV0IG5vZGUgPSBldmVudC50YXJnZXQ7IG5vZGU7IG5vZGUgPSBub2RlLnBhcmVudE5vZGUpIHtcbiAgICAgICAgaWYgKG5vZGUudGFnTmFtZSA9PT0gJ0EnKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICB9XG4gICAgY29uc3Qgb2Zmc2V0ID0gZXZlbnQucGFnZVk7XG4gICAgY29uc3QgbGluZSA9IHNjcm9sbF9zeW5jXzEuZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQob2Zmc2V0KTtcbiAgICBpZiAodHlwZW9mIGxpbmUgPT09ICdudW1iZXInICYmICFpc05hTihsaW5lKSkge1xuICAgICAgICBtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ2RpZENsaWNrJywgeyBsaW5lOiBNYXRoLmZsb29yKGxpbmUpIH0pO1xuICAgIH1cbn0pO1xuY29uc3QgcGFzc1Rocm91Z2hMaW5rU2NoZW1lcyA9IFsnaHR0cDonLCAnaHR0cHM6JywgJ21haWx0bzonLCAndnNjb2RlOicsICd2c2NvZGUtaW5zaWRlcnM6J107XG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGV2ZW50ID0+IHtcbiAgICBpZiAoIWV2ZW50KSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgbGV0IG5vZGUgPSBldmVudC50YXJnZXQ7XG4gICAgd2hpbGUgKG5vZGUpIHtcbiAgICAgICAgaWYgKG5vZGUudGFnTmFtZSAmJiBub2RlLnRhZ05hbWUgPT09ICdBJyAmJiBub2RlLmhyZWYpIHtcbiAgICAgICAgICAgIGlmIChub2RlLmdldEF0dHJpYnV0ZSgnaHJlZicpLnN0YXJ0c1dpdGgoJyMnKSkge1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIFBhc3MgdGhyb3VnaCBrbm93biBzY2hlbWVzXG4gICAgICAgICAgICBpZiAocGFzc1Rocm91Z2hMaW5rU2NoZW1lcy5zb21lKHNjaGVtZSA9PiBub2RlLmhyZWYuc3RhcnRzV2l0aChzY2hlbWUpKSkge1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IGhyZWZUZXh0ID0gbm9kZS5nZXRBdHRyaWJ1dGUoJ2RhdGEtaHJlZicpIHx8IG5vZGUuZ2V0QXR0cmlidXRlKCdocmVmJyk7XG4gICAgICAgICAgICAvLyBJZiBvcmlnaW5hbCBsaW5rIGRvZXNuJ3QgbG9vayBsaWtlIGEgdXJsLCBkZWxlZ2F0ZSBiYWNrIHRvIFZTIENvZGUgdG8gcmVzb2x2ZVxuICAgICAgICAgICAgaWYgKCEvXlthLXpcXC1dKzovaS50ZXN0KGhyZWZUZXh0KSkge1xuICAgICAgICAgICAgICAgIG1lc3NhZ2luZy5wb3N0TWVzc2FnZSgnb3BlbkxpbmsnLCB7IGhyZWY6IGhyZWZUZXh0IH0pO1xuICAgICAgICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICAgICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIG5vZGUgPSBub2RlLnBhcmVudE5vZGU7XG4gICAgfVxufSwgdHJ1ZSk7XG53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignc2Nyb2xsJywgdGhyb3R0bGUoKCkgPT4ge1xuICAgIHVwZGF0ZVNjcm9sbFByb2dyZXNzKCk7XG4gICAgaWYgKHNjcm9sbERpc2FibGVkKSB7XG4gICAgICAgIHNjcm9sbERpc2FibGVkID0gZmFsc2U7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBjb25zdCBsaW5lID0gc2Nyb2xsX3N5bmNfMS5nZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCh3aW5kb3cuc2Nyb2xsWSk7XG4gICAgICAgIGlmICh0eXBlb2YgbGluZSA9PT0gJ251bWJlcicgJiYgIWlzTmFOKGxpbmUpKSB7XG4gICAgICAgICAgICBtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ3JldmVhbExpbmUnLCB7IGxpbmUgfSk7XG4gICAgICAgIH1cbiAgICB9XG59LCA1MCkpO1xuZnVuY3Rpb24gdXBkYXRlU2Nyb2xsUHJvZ3Jlc3MoKSB7XG4gICAgc3RhdGUuc2Nyb2xsUHJvZ3Jlc3MgPSB3aW5kb3cuc2Nyb2xsWSAvIGRvY3VtZW50LmJvZHkuY2xpZW50SGVpZ2h0O1xuICAgIHZzY29kZS5zZXRTdGF0ZShzdGF0ZSk7XG59XG4iLCJ2YXIgYXBwbHkgPSBGdW5jdGlvbi5wcm90b3R5cGUuYXBwbHk7XG5cbi8vIERPTSBBUElzLCBmb3IgY29tcGxldGVuZXNzXG5cbmV4cG9ydHMuc2V0VGltZW91dCA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gbmV3IFRpbWVvdXQoYXBwbHkuY2FsbChzZXRUaW1lb3V0LCB3aW5kb3csIGFyZ3VtZW50cyksIGNsZWFyVGltZW91dCk7XG59O1xuZXhwb3J0cy5zZXRJbnRlcnZhbCA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gbmV3IFRpbWVvdXQoYXBwbHkuY2FsbChzZXRJbnRlcnZhbCwgd2luZG93LCBhcmd1bWVudHMpLCBjbGVhckludGVydmFsKTtcbn07XG5leHBvcnRzLmNsZWFyVGltZW91dCA9XG5leHBvcnRzLmNsZWFySW50ZXJ2YWwgPSBmdW5jdGlvbih0aW1lb3V0KSB7XG4gIGlmICh0aW1lb3V0KSB7XG4gICAgdGltZW91dC5jbG9zZSgpO1xuICB9XG59O1xuXG5mdW5jdGlvbiBUaW1lb3V0KGlkLCBjbGVhckZuKSB7XG4gIHRoaXMuX2lkID0gaWQ7XG4gIHRoaXMuX2NsZWFyRm4gPSBjbGVhckZuO1xufVxuVGltZW91dC5wcm90b3R5cGUudW5yZWYgPSBUaW1lb3V0LnByb3RvdHlwZS5yZWYgPSBmdW5jdGlvbigpIHt9O1xuVGltZW91dC5wcm90b3R5cGUuY2xvc2UgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5fY2xlYXJGbi5jYWxsKHdpbmRvdywgdGhpcy5faWQpO1xufTtcblxuLy8gRG9lcyBub3Qgc3RhcnQgdGhlIHRpbWUsIGp1c3Qgc2V0cyB1cCB0aGUgbWVtYmVycyBuZWVkZWQuXG5leHBvcnRzLmVucm9sbCA9IGZ1bmN0aW9uKGl0ZW0sIG1zZWNzKSB7XG4gIGNsZWFyVGltZW91dChpdGVtLl9pZGxlVGltZW91dElkKTtcbiAgaXRlbS5faWRsZVRpbWVvdXQgPSBtc2Vjcztcbn07XG5cbmV4cG9ydHMudW5lbnJvbGwgPSBmdW5jdGlvbihpdGVtKSB7XG4gIGNsZWFyVGltZW91dChpdGVtLl9pZGxlVGltZW91dElkKTtcbiAgaXRlbS5faWRsZVRpbWVvdXQgPSAtMTtcbn07XG5cbmV4cG9ydHMuX3VucmVmQWN0aXZlID0gZXhwb3J0cy5hY3RpdmUgPSBmdW5jdGlvbihpdGVtKSB7XG4gIGNsZWFyVGltZW91dChpdGVtLl9pZGxlVGltZW91dElkKTtcblxuICB2YXIgbXNlY3MgPSBpdGVtLl9pZGxlVGltZW91dDtcbiAgaWYgKG1zZWNzID49IDApIHtcbiAgICBpdGVtLl9pZGxlVGltZW91dElkID0gc2V0VGltZW91dChmdW5jdGlvbiBvblRpbWVvdXQoKSB7XG4gICAgICBpZiAoaXRlbS5fb25UaW1lb3V0KVxuICAgICAgICBpdGVtLl9vblRpbWVvdXQoKTtcbiAgICB9LCBtc2Vjcyk7XG4gIH1cbn07XG5cbi8vIHNldGltbWVkaWF0ZSBhdHRhY2hlcyBpdHNlbGYgdG8gdGhlIGdsb2JhbCBvYmplY3RcbnJlcXVpcmUoXCJzZXRpbW1lZGlhdGVcIik7XG4vLyBPbiBzb21lIGV4b3RpYyBlbnZpcm9ubWVudHMsIGl0J3Mgbm90IGNsZWFyIHdoaWNoIG9iamVjdCBgc2V0aW1tZWlkYXRlYCB3YXNcbi8vIGFibGUgdG8gaW5zdGFsbCBvbnRvLiAgU2VhcmNoIGVhY2ggcG9zc2liaWxpdHkgaW4gdGhlIHNhbWUgb3JkZXIgYXMgdGhlXG4vLyBgc2V0aW1tZWRpYXRlYCBsaWJyYXJ5LlxuZXhwb3J0cy5zZXRJbW1lZGlhdGUgPSAodHlwZW9mIHNlbGYgIT09IFwidW5kZWZpbmVkXCIgJiYgc2VsZi5zZXRJbW1lZGlhdGUpIHx8XG4gICAgICAgICAgICAgICAgICAgICAgICh0eXBlb2YgZ2xvYmFsICE9PSBcInVuZGVmaW5lZFwiICYmIGdsb2JhbC5zZXRJbW1lZGlhdGUpIHx8XG4gICAgICAgICAgICAgICAgICAgICAgICh0aGlzICYmIHRoaXMuc2V0SW1tZWRpYXRlKTtcbmV4cG9ydHMuY2xlYXJJbW1lZGlhdGUgPSAodHlwZW9mIHNlbGYgIT09IFwidW5kZWZpbmVkXCIgJiYgc2VsZi5jbGVhckltbWVkaWF0ZSkgfHxcbiAgICAgICAgICAgICAgICAgICAgICAgICAodHlwZW9mIGdsb2JhbCAhPT0gXCJ1bmRlZmluZWRcIiAmJiBnbG9iYWwuY2xlYXJJbW1lZGlhdGUpIHx8XG4gICAgICAgICAgICAgICAgICAgICAgICAgKHRoaXMgJiYgdGhpcy5jbGVhckltbWVkaWF0ZSk7XG4iLCIoZnVuY3Rpb24gKGdsb2JhbCwgdW5kZWZpbmVkKSB7XG4gICAgXCJ1c2Ugc3RyaWN0XCI7XG5cbiAgICBpZiAoZ2xvYmFsLnNldEltbWVkaWF0ZSkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIG5leHRIYW5kbGUgPSAxOyAvLyBTcGVjIHNheXMgZ3JlYXRlciB0aGFuIHplcm9cbiAgICB2YXIgdGFza3NCeUhhbmRsZSA9IHt9O1xuICAgIHZhciBjdXJyZW50bHlSdW5uaW5nQVRhc2sgPSBmYWxzZTtcbiAgICB2YXIgZG9jID0gZ2xvYmFsLmRvY3VtZW50O1xuICAgIHZhciByZWdpc3RlckltbWVkaWF0ZTtcblxuICAgIGZ1bmN0aW9uIHNldEltbWVkaWF0ZShjYWxsYmFjaykge1xuICAgICAgLy8gQ2FsbGJhY2sgY2FuIGVpdGhlciBiZSBhIGZ1bmN0aW9uIG9yIGEgc3RyaW5nXG4gICAgICBpZiAodHlwZW9mIGNhbGxiYWNrICE9PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgY2FsbGJhY2sgPSBuZXcgRnVuY3Rpb24oXCJcIiArIGNhbGxiYWNrKTtcbiAgICAgIH1cbiAgICAgIC8vIENvcHkgZnVuY3Rpb24gYXJndW1lbnRzXG4gICAgICB2YXIgYXJncyA9IG5ldyBBcnJheShhcmd1bWVudHMubGVuZ3RoIC0gMSk7XG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGFyZ3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICBhcmdzW2ldID0gYXJndW1lbnRzW2kgKyAxXTtcbiAgICAgIH1cbiAgICAgIC8vIFN0b3JlIGFuZCByZWdpc3RlciB0aGUgdGFza1xuICAgICAgdmFyIHRhc2sgPSB7IGNhbGxiYWNrOiBjYWxsYmFjaywgYXJnczogYXJncyB9O1xuICAgICAgdGFza3NCeUhhbmRsZVtuZXh0SGFuZGxlXSA9IHRhc2s7XG4gICAgICByZWdpc3RlckltbWVkaWF0ZShuZXh0SGFuZGxlKTtcbiAgICAgIHJldHVybiBuZXh0SGFuZGxlKys7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gY2xlYXJJbW1lZGlhdGUoaGFuZGxlKSB7XG4gICAgICAgIGRlbGV0ZSB0YXNrc0J5SGFuZGxlW2hhbmRsZV07XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gcnVuKHRhc2spIHtcbiAgICAgICAgdmFyIGNhbGxiYWNrID0gdGFzay5jYWxsYmFjaztcbiAgICAgICAgdmFyIGFyZ3MgPSB0YXNrLmFyZ3M7XG4gICAgICAgIHN3aXRjaCAoYXJncy5sZW5ndGgpIHtcbiAgICAgICAgY2FzZSAwOlxuICAgICAgICAgICAgY2FsbGJhY2soKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIDE6XG4gICAgICAgICAgICBjYWxsYmFjayhhcmdzWzBdKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIDI6XG4gICAgICAgICAgICBjYWxsYmFjayhhcmdzWzBdLCBhcmdzWzFdKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIDM6XG4gICAgICAgICAgICBjYWxsYmFjayhhcmdzWzBdLCBhcmdzWzFdLCBhcmdzWzJdKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgY2FsbGJhY2suYXBwbHkodW5kZWZpbmVkLCBhcmdzKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gcnVuSWZQcmVzZW50KGhhbmRsZSkge1xuICAgICAgICAvLyBGcm9tIHRoZSBzcGVjOiBcIldhaXQgdW50aWwgYW55IGludm9jYXRpb25zIG9mIHRoaXMgYWxnb3JpdGhtIHN0YXJ0ZWQgYmVmb3JlIHRoaXMgb25lIGhhdmUgY29tcGxldGVkLlwiXG4gICAgICAgIC8vIFNvIGlmIHdlJ3JlIGN1cnJlbnRseSBydW5uaW5nIGEgdGFzaywgd2UnbGwgbmVlZCB0byBkZWxheSB0aGlzIGludm9jYXRpb24uXG4gICAgICAgIGlmIChjdXJyZW50bHlSdW5uaW5nQVRhc2spIHtcbiAgICAgICAgICAgIC8vIERlbGF5IGJ5IGRvaW5nIGEgc2V0VGltZW91dC4gc2V0SW1tZWRpYXRlIHdhcyB0cmllZCBpbnN0ZWFkLCBidXQgaW4gRmlyZWZveCA3IGl0IGdlbmVyYXRlZCBhXG4gICAgICAgICAgICAvLyBcInRvbyBtdWNoIHJlY3Vyc2lvblwiIGVycm9yLlxuICAgICAgICAgICAgc2V0VGltZW91dChydW5JZlByZXNlbnQsIDAsIGhhbmRsZSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB2YXIgdGFzayA9IHRhc2tzQnlIYW5kbGVbaGFuZGxlXTtcbiAgICAgICAgICAgIGlmICh0YXNrKSB7XG4gICAgICAgICAgICAgICAgY3VycmVudGx5UnVubmluZ0FUYXNrID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgICAgICBydW4odGFzayk7XG4gICAgICAgICAgICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgICAgICAgICAgICAgY2xlYXJJbW1lZGlhdGUoaGFuZGxlKTtcbiAgICAgICAgICAgICAgICAgICAgY3VycmVudGx5UnVubmluZ0FUYXNrID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gaW5zdGFsbE5leHRUaWNrSW1wbGVtZW50YXRpb24oKSB7XG4gICAgICAgIHJlZ2lzdGVySW1tZWRpYXRlID0gZnVuY3Rpb24oaGFuZGxlKSB7XG4gICAgICAgICAgICBwcm9jZXNzLm5leHRUaWNrKGZ1bmN0aW9uICgpIHsgcnVuSWZQcmVzZW50KGhhbmRsZSk7IH0pO1xuICAgICAgICB9O1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGNhblVzZVBvc3RNZXNzYWdlKCkge1xuICAgICAgICAvLyBUaGUgdGVzdCBhZ2FpbnN0IGBpbXBvcnRTY3JpcHRzYCBwcmV2ZW50cyB0aGlzIGltcGxlbWVudGF0aW9uIGZyb20gYmVpbmcgaW5zdGFsbGVkIGluc2lkZSBhIHdlYiB3b3JrZXIsXG4gICAgICAgIC8vIHdoZXJlIGBnbG9iYWwucG9zdE1lc3NhZ2VgIG1lYW5zIHNvbWV0aGluZyBjb21wbGV0ZWx5IGRpZmZlcmVudCBhbmQgY2FuJ3QgYmUgdXNlZCBmb3IgdGhpcyBwdXJwb3NlLlxuICAgICAgICBpZiAoZ2xvYmFsLnBvc3RNZXNzYWdlICYmICFnbG9iYWwuaW1wb3J0U2NyaXB0cykge1xuICAgICAgICAgICAgdmFyIHBvc3RNZXNzYWdlSXNBc3luY2hyb25vdXMgPSB0cnVlO1xuICAgICAgICAgICAgdmFyIG9sZE9uTWVzc2FnZSA9IGdsb2JhbC5vbm1lc3NhZ2U7XG4gICAgICAgICAgICBnbG9iYWwub25tZXNzYWdlID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgcG9zdE1lc3NhZ2VJc0FzeW5jaHJvbm91cyA9IGZhbHNlO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIGdsb2JhbC5wb3N0TWVzc2FnZShcIlwiLCBcIipcIik7XG4gICAgICAgICAgICBnbG9iYWwub25tZXNzYWdlID0gb2xkT25NZXNzYWdlO1xuICAgICAgICAgICAgcmV0dXJuIHBvc3RNZXNzYWdlSXNBc3luY2hyb25vdXM7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBmdW5jdGlvbiBpbnN0YWxsUG9zdE1lc3NhZ2VJbXBsZW1lbnRhdGlvbigpIHtcbiAgICAgICAgLy8gSW5zdGFsbHMgYW4gZXZlbnQgaGFuZGxlciBvbiBgZ2xvYmFsYCBmb3IgdGhlIGBtZXNzYWdlYCBldmVudDogc2VlXG4gICAgICAgIC8vICogaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4vRE9NL3dpbmRvdy5wb3N0TWVzc2FnZVxuICAgICAgICAvLyAqIGh0dHA6Ly93d3cud2hhdHdnLm9yZy9zcGVjcy93ZWItYXBwcy9jdXJyZW50LXdvcmsvbXVsdGlwYWdlL2NvbW1zLmh0bWwjY3Jvc3NEb2N1bWVudE1lc3NhZ2VzXG5cbiAgICAgICAgdmFyIG1lc3NhZ2VQcmVmaXggPSBcInNldEltbWVkaWF0ZSRcIiArIE1hdGgucmFuZG9tKCkgKyBcIiRcIjtcbiAgICAgICAgdmFyIG9uR2xvYmFsTWVzc2FnZSA9IGZ1bmN0aW9uKGV2ZW50KSB7XG4gICAgICAgICAgICBpZiAoZXZlbnQuc291cmNlID09PSBnbG9iYWwgJiZcbiAgICAgICAgICAgICAgICB0eXBlb2YgZXZlbnQuZGF0YSA9PT0gXCJzdHJpbmdcIiAmJlxuICAgICAgICAgICAgICAgIGV2ZW50LmRhdGEuaW5kZXhPZihtZXNzYWdlUHJlZml4KSA9PT0gMCkge1xuICAgICAgICAgICAgICAgIHJ1bklmUHJlc2VudCgrZXZlbnQuZGF0YS5zbGljZShtZXNzYWdlUHJlZml4Lmxlbmd0aCkpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9O1xuXG4gICAgICAgIGlmIChnbG9iYWwuYWRkRXZlbnRMaXN0ZW5lcikge1xuICAgICAgICAgICAgZ2xvYmFsLmFkZEV2ZW50TGlzdGVuZXIoXCJtZXNzYWdlXCIsIG9uR2xvYmFsTWVzc2FnZSwgZmFsc2UpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZ2xvYmFsLmF0dGFjaEV2ZW50KFwib25tZXNzYWdlXCIsIG9uR2xvYmFsTWVzc2FnZSk7XG4gICAgICAgIH1cblxuICAgICAgICByZWdpc3RlckltbWVkaWF0ZSA9IGZ1bmN0aW9uKGhhbmRsZSkge1xuICAgICAgICAgICAgZ2xvYmFsLnBvc3RNZXNzYWdlKG1lc3NhZ2VQcmVmaXggKyBoYW5kbGUsIFwiKlwiKTtcbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBpbnN0YWxsTWVzc2FnZUNoYW5uZWxJbXBsZW1lbnRhdGlvbigpIHtcbiAgICAgICAgdmFyIGNoYW5uZWwgPSBuZXcgTWVzc2FnZUNoYW5uZWwoKTtcbiAgICAgICAgY2hhbm5lbC5wb3J0MS5vbm1lc3NhZ2UgPSBmdW5jdGlvbihldmVudCkge1xuICAgICAgICAgICAgdmFyIGhhbmRsZSA9IGV2ZW50LmRhdGE7XG4gICAgICAgICAgICBydW5JZlByZXNlbnQoaGFuZGxlKTtcbiAgICAgICAgfTtcblxuICAgICAgICByZWdpc3RlckltbWVkaWF0ZSA9IGZ1bmN0aW9uKGhhbmRsZSkge1xuICAgICAgICAgICAgY2hhbm5lbC5wb3J0Mi5wb3N0TWVzc2FnZShoYW5kbGUpO1xuICAgICAgICB9O1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGluc3RhbGxSZWFkeVN0YXRlQ2hhbmdlSW1wbGVtZW50YXRpb24oKSB7XG4gICAgICAgIHZhciBodG1sID0gZG9jLmRvY3VtZW50RWxlbWVudDtcbiAgICAgICAgcmVnaXN0ZXJJbW1lZGlhdGUgPSBmdW5jdGlvbihoYW5kbGUpIHtcbiAgICAgICAgICAgIC8vIENyZWF0ZSBhIDxzY3JpcHQ+IGVsZW1lbnQ7IGl0cyByZWFkeXN0YXRlY2hhbmdlIGV2ZW50IHdpbGwgYmUgZmlyZWQgYXN5bmNocm9ub3VzbHkgb25jZSBpdCBpcyBpbnNlcnRlZFxuICAgICAgICAgICAgLy8gaW50byB0aGUgZG9jdW1lbnQuIERvIHNvLCB0aHVzIHF1ZXVpbmcgdXAgdGhlIHRhc2suIFJlbWVtYmVyIHRvIGNsZWFuIHVwIG9uY2UgaXQncyBiZWVuIGNhbGxlZC5cbiAgICAgICAgICAgIHZhciBzY3JpcHQgPSBkb2MuY3JlYXRlRWxlbWVudChcInNjcmlwdFwiKTtcbiAgICAgICAgICAgIHNjcmlwdC5vbnJlYWR5c3RhdGVjaGFuZ2UgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgcnVuSWZQcmVzZW50KGhhbmRsZSk7XG4gICAgICAgICAgICAgICAgc2NyaXB0Lm9ucmVhZHlzdGF0ZWNoYW5nZSA9IG51bGw7XG4gICAgICAgICAgICAgICAgaHRtbC5yZW1vdmVDaGlsZChzY3JpcHQpO1xuICAgICAgICAgICAgICAgIHNjcmlwdCA9IG51bGw7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgaHRtbC5hcHBlbmRDaGlsZChzY3JpcHQpO1xuICAgICAgICB9O1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGluc3RhbGxTZXRUaW1lb3V0SW1wbGVtZW50YXRpb24oKSB7XG4gICAgICAgIHJlZ2lzdGVySW1tZWRpYXRlID0gZnVuY3Rpb24oaGFuZGxlKSB7XG4gICAgICAgICAgICBzZXRUaW1lb3V0KHJ1bklmUHJlc2VudCwgMCwgaGFuZGxlKTtcbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICAvLyBJZiBzdXBwb3J0ZWQsIHdlIHNob3VsZCBhdHRhY2ggdG8gdGhlIHByb3RvdHlwZSBvZiBnbG9iYWwsIHNpbmNlIHRoYXQgaXMgd2hlcmUgc2V0VGltZW91dCBldCBhbC4gbGl2ZS5cbiAgICB2YXIgYXR0YWNoVG8gPSBPYmplY3QuZ2V0UHJvdG90eXBlT2YgJiYgT2JqZWN0LmdldFByb3RvdHlwZU9mKGdsb2JhbCk7XG4gICAgYXR0YWNoVG8gPSBhdHRhY2hUbyAmJiBhdHRhY2hUby5zZXRUaW1lb3V0ID8gYXR0YWNoVG8gOiBnbG9iYWw7XG5cbiAgICAvLyBEb24ndCBnZXQgZm9vbGVkIGJ5IGUuZy4gYnJvd3NlcmlmeSBlbnZpcm9ubWVudHMuXG4gICAgaWYgKHt9LnRvU3RyaW5nLmNhbGwoZ2xvYmFsLnByb2Nlc3MpID09PSBcIltvYmplY3QgcHJvY2Vzc11cIikge1xuICAgICAgICAvLyBGb3IgTm9kZS5qcyBiZWZvcmUgMC45XG4gICAgICAgIGluc3RhbGxOZXh0VGlja0ltcGxlbWVudGF0aW9uKCk7XG5cbiAgICB9IGVsc2UgaWYgKGNhblVzZVBvc3RNZXNzYWdlKCkpIHtcbiAgICAgICAgLy8gRm9yIG5vbi1JRTEwIG1vZGVybiBicm93c2Vyc1xuICAgICAgICBpbnN0YWxsUG9zdE1lc3NhZ2VJbXBsZW1lbnRhdGlvbigpO1xuXG4gICAgfSBlbHNlIGlmIChnbG9iYWwuTWVzc2FnZUNoYW5uZWwpIHtcbiAgICAgICAgLy8gRm9yIHdlYiB3b3JrZXJzLCB3aGVyZSBzdXBwb3J0ZWRcbiAgICAgICAgaW5zdGFsbE1lc3NhZ2VDaGFubmVsSW1wbGVtZW50YXRpb24oKTtcblxuICAgIH0gZWxzZSBpZiAoZG9jICYmIFwib25yZWFkeXN0YXRlY2hhbmdlXCIgaW4gZG9jLmNyZWF0ZUVsZW1lbnQoXCJzY3JpcHRcIikpIHtcbiAgICAgICAgLy8gRm9yIElFIDbigJM4XG4gICAgICAgIGluc3RhbGxSZWFkeVN0YXRlQ2hhbmdlSW1wbGVtZW50YXRpb24oKTtcblxuICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIEZvciBvbGRlciBicm93c2Vyc1xuICAgICAgICBpbnN0YWxsU2V0VGltZW91dEltcGxlbWVudGF0aW9uKCk7XG4gICAgfVxuXG4gICAgYXR0YWNoVG8uc2V0SW1tZWRpYXRlID0gc2V0SW1tZWRpYXRlO1xuICAgIGF0dGFjaFRvLmNsZWFySW1tZWRpYXRlID0gY2xlYXJJbW1lZGlhdGU7XG59KHR5cGVvZiBzZWxmID09PSBcInVuZGVmaW5lZFwiID8gdHlwZW9mIGdsb2JhbCA9PT0gXCJ1bmRlZmluZWRcIiA/IHRoaXMgOiBnbG9iYWwgOiBzZWxmKSk7XG4iLCIvLyBzaGltIGZvciB1c2luZyBwcm9jZXNzIGluIGJyb3dzZXJcbnZhciBwcm9jZXNzID0gbW9kdWxlLmV4cG9ydHMgPSB7fTtcblxuLy8gY2FjaGVkIGZyb20gd2hhdGV2ZXIgZ2xvYmFsIGlzIHByZXNlbnQgc28gdGhhdCB0ZXN0IHJ1bm5lcnMgdGhhdCBzdHViIGl0XG4vLyBkb24ndCBicmVhayB0aGluZ3MuICBCdXQgd2UgbmVlZCB0byB3cmFwIGl0IGluIGEgdHJ5IGNhdGNoIGluIGNhc2UgaXQgaXNcbi8vIHdyYXBwZWQgaW4gc3RyaWN0IG1vZGUgY29kZSB3aGljaCBkb2Vzbid0IGRlZmluZSBhbnkgZ2xvYmFscy4gIEl0J3MgaW5zaWRlIGFcbi8vIGZ1bmN0aW9uIGJlY2F1c2UgdHJ5L2NhdGNoZXMgZGVvcHRpbWl6ZSBpbiBjZXJ0YWluIGVuZ2luZXMuXG5cbnZhciBjYWNoZWRTZXRUaW1lb3V0O1xudmFyIGNhY2hlZENsZWFyVGltZW91dDtcblxuZnVuY3Rpb24gZGVmYXVsdFNldFRpbW91dCgpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ3NldFRpbWVvdXQgaGFzIG5vdCBiZWVuIGRlZmluZWQnKTtcbn1cbmZ1bmN0aW9uIGRlZmF1bHRDbGVhclRpbWVvdXQgKCkge1xuICAgIHRocm93IG5ldyBFcnJvcignY2xlYXJUaW1lb3V0IGhhcyBub3QgYmVlbiBkZWZpbmVkJyk7XG59XG4oZnVuY3Rpb24gKCkge1xuICAgIHRyeSB7XG4gICAgICAgIGlmICh0eXBlb2Ygc2V0VGltZW91dCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgY2FjaGVkU2V0VGltZW91dCA9IHNldFRpbWVvdXQ7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjYWNoZWRTZXRUaW1lb3V0ID0gZGVmYXVsdFNldFRpbW91dDtcbiAgICAgICAgfVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgY2FjaGVkU2V0VGltZW91dCA9IGRlZmF1bHRTZXRUaW1vdXQ7XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICAgIGlmICh0eXBlb2YgY2xlYXJUaW1lb3V0ID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICBjYWNoZWRDbGVhclRpbWVvdXQgPSBjbGVhclRpbWVvdXQ7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjYWNoZWRDbGVhclRpbWVvdXQgPSBkZWZhdWx0Q2xlYXJUaW1lb3V0O1xuICAgICAgICB9XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgICBjYWNoZWRDbGVhclRpbWVvdXQgPSBkZWZhdWx0Q2xlYXJUaW1lb3V0O1xuICAgIH1cbn0gKCkpXG5mdW5jdGlvbiBydW5UaW1lb3V0KGZ1bikge1xuICAgIGlmIChjYWNoZWRTZXRUaW1lb3V0ID09PSBzZXRUaW1lb3V0KSB7XG4gICAgICAgIC8vbm9ybWFsIGVudmlyb21lbnRzIGluIHNhbmUgc2l0dWF0aW9uc1xuICAgICAgICByZXR1cm4gc2V0VGltZW91dChmdW4sIDApO1xuICAgIH1cbiAgICAvLyBpZiBzZXRUaW1lb3V0IHdhc24ndCBhdmFpbGFibGUgYnV0IHdhcyBsYXR0ZXIgZGVmaW5lZFxuICAgIGlmICgoY2FjaGVkU2V0VGltZW91dCA9PT0gZGVmYXVsdFNldFRpbW91dCB8fCAhY2FjaGVkU2V0VGltZW91dCkgJiYgc2V0VGltZW91dCkge1xuICAgICAgICBjYWNoZWRTZXRUaW1lb3V0ID0gc2V0VGltZW91dDtcbiAgICAgICAgcmV0dXJuIHNldFRpbWVvdXQoZnVuLCAwKTtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgICAgLy8gd2hlbiB3aGVuIHNvbWVib2R5IGhhcyBzY3Jld2VkIHdpdGggc2V0VGltZW91dCBidXQgbm8gSS5FLiBtYWRkbmVzc1xuICAgICAgICByZXR1cm4gY2FjaGVkU2V0VGltZW91dChmdW4sIDApO1xuICAgIH0gY2F0Y2goZSl7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICAvLyBXaGVuIHdlIGFyZSBpbiBJLkUuIGJ1dCB0aGUgc2NyaXB0IGhhcyBiZWVuIGV2YWxlZCBzbyBJLkUuIGRvZXNuJ3QgdHJ1c3QgdGhlIGdsb2JhbCBvYmplY3Qgd2hlbiBjYWxsZWQgbm9ybWFsbHlcbiAgICAgICAgICAgIHJldHVybiBjYWNoZWRTZXRUaW1lb3V0LmNhbGwobnVsbCwgZnVuLCAwKTtcbiAgICAgICAgfSBjYXRjaChlKXtcbiAgICAgICAgICAgIC8vIHNhbWUgYXMgYWJvdmUgYnV0IHdoZW4gaXQncyBhIHZlcnNpb24gb2YgSS5FLiB0aGF0IG11c3QgaGF2ZSB0aGUgZ2xvYmFsIG9iamVjdCBmb3IgJ3RoaXMnLCBob3BmdWxseSBvdXIgY29udGV4dCBjb3JyZWN0IG90aGVyd2lzZSBpdCB3aWxsIHRocm93IGEgZ2xvYmFsIGVycm9yXG4gICAgICAgICAgICByZXR1cm4gY2FjaGVkU2V0VGltZW91dC5jYWxsKHRoaXMsIGZ1biwgMCk7XG4gICAgICAgIH1cbiAgICB9XG5cblxufVxuZnVuY3Rpb24gcnVuQ2xlYXJUaW1lb3V0KG1hcmtlcikge1xuICAgIGlmIChjYWNoZWRDbGVhclRpbWVvdXQgPT09IGNsZWFyVGltZW91dCkge1xuICAgICAgICAvL25vcm1hbCBlbnZpcm9tZW50cyBpbiBzYW5lIHNpdHVhdGlvbnNcbiAgICAgICAgcmV0dXJuIGNsZWFyVGltZW91dChtYXJrZXIpO1xuICAgIH1cbiAgICAvLyBpZiBjbGVhclRpbWVvdXQgd2Fzbid0IGF2YWlsYWJsZSBidXQgd2FzIGxhdHRlciBkZWZpbmVkXG4gICAgaWYgKChjYWNoZWRDbGVhclRpbWVvdXQgPT09IGRlZmF1bHRDbGVhclRpbWVvdXQgfHwgIWNhY2hlZENsZWFyVGltZW91dCkgJiYgY2xlYXJUaW1lb3V0KSB7XG4gICAgICAgIGNhY2hlZENsZWFyVGltZW91dCA9IGNsZWFyVGltZW91dDtcbiAgICAgICAgcmV0dXJuIGNsZWFyVGltZW91dChtYXJrZXIpO1xuICAgIH1cbiAgICB0cnkge1xuICAgICAgICAvLyB3aGVuIHdoZW4gc29tZWJvZHkgaGFzIHNjcmV3ZWQgd2l0aCBzZXRUaW1lb3V0IGJ1dCBubyBJLkUuIG1hZGRuZXNzXG4gICAgICAgIHJldHVybiBjYWNoZWRDbGVhclRpbWVvdXQobWFya2VyKTtcbiAgICB9IGNhdGNoIChlKXtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIC8vIFdoZW4gd2UgYXJlIGluIEkuRS4gYnV0IHRoZSBzY3JpcHQgaGFzIGJlZW4gZXZhbGVkIHNvIEkuRS4gZG9lc24ndCAgdHJ1c3QgdGhlIGdsb2JhbCBvYmplY3Qgd2hlbiBjYWxsZWQgbm9ybWFsbHlcbiAgICAgICAgICAgIHJldHVybiBjYWNoZWRDbGVhclRpbWVvdXQuY2FsbChudWxsLCBtYXJrZXIpO1xuICAgICAgICB9IGNhdGNoIChlKXtcbiAgICAgICAgICAgIC8vIHNhbWUgYXMgYWJvdmUgYnV0IHdoZW4gaXQncyBhIHZlcnNpb24gb2YgSS5FLiB0aGF0IG11c3QgaGF2ZSB0aGUgZ2xvYmFsIG9iamVjdCBmb3IgJ3RoaXMnLCBob3BmdWxseSBvdXIgY29udGV4dCBjb3JyZWN0IG90aGVyd2lzZSBpdCB3aWxsIHRocm93IGEgZ2xvYmFsIGVycm9yLlxuICAgICAgICAgICAgLy8gU29tZSB2ZXJzaW9ucyBvZiBJLkUuIGhhdmUgZGlmZmVyZW50IHJ1bGVzIGZvciBjbGVhclRpbWVvdXQgdnMgc2V0VGltZW91dFxuICAgICAgICAgICAgcmV0dXJuIGNhY2hlZENsZWFyVGltZW91dC5jYWxsKHRoaXMsIG1hcmtlcik7XG4gICAgICAgIH1cbiAgICB9XG5cblxuXG59XG52YXIgcXVldWUgPSBbXTtcbnZhciBkcmFpbmluZyA9IGZhbHNlO1xudmFyIGN1cnJlbnRRdWV1ZTtcbnZhciBxdWV1ZUluZGV4ID0gLTE7XG5cbmZ1bmN0aW9uIGNsZWFuVXBOZXh0VGljaygpIHtcbiAgICBpZiAoIWRyYWluaW5nIHx8ICFjdXJyZW50UXVldWUpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBkcmFpbmluZyA9IGZhbHNlO1xuICAgIGlmIChjdXJyZW50UXVldWUubGVuZ3RoKSB7XG4gICAgICAgIHF1ZXVlID0gY3VycmVudFF1ZXVlLmNvbmNhdChxdWV1ZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgcXVldWVJbmRleCA9IC0xO1xuICAgIH1cbiAgICBpZiAocXVldWUubGVuZ3RoKSB7XG4gICAgICAgIGRyYWluUXVldWUoKTtcbiAgICB9XG59XG5cbmZ1bmN0aW9uIGRyYWluUXVldWUoKSB7XG4gICAgaWYgKGRyYWluaW5nKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdmFyIHRpbWVvdXQgPSBydW5UaW1lb3V0KGNsZWFuVXBOZXh0VGljayk7XG4gICAgZHJhaW5pbmcgPSB0cnVlO1xuXG4gICAgdmFyIGxlbiA9IHF1ZXVlLmxlbmd0aDtcbiAgICB3aGlsZShsZW4pIHtcbiAgICAgICAgY3VycmVudFF1ZXVlID0gcXVldWU7XG4gICAgICAgIHF1ZXVlID0gW107XG4gICAgICAgIHdoaWxlICgrK3F1ZXVlSW5kZXggPCBsZW4pIHtcbiAgICAgICAgICAgIGlmIChjdXJyZW50UXVldWUpIHtcbiAgICAgICAgICAgICAgICBjdXJyZW50UXVldWVbcXVldWVJbmRleF0ucnVuKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcXVldWVJbmRleCA9IC0xO1xuICAgICAgICBsZW4gPSBxdWV1ZS5sZW5ndGg7XG4gICAgfVxuICAgIGN1cnJlbnRRdWV1ZSA9IG51bGw7XG4gICAgZHJhaW5pbmcgPSBmYWxzZTtcbiAgICBydW5DbGVhclRpbWVvdXQodGltZW91dCk7XG59XG5cbnByb2Nlc3MubmV4dFRpY2sgPSBmdW5jdGlvbiAoZnVuKSB7XG4gICAgdmFyIGFyZ3MgPSBuZXcgQXJyYXkoYXJndW1lbnRzLmxlbmd0aCAtIDEpO1xuICAgIGlmIChhcmd1bWVudHMubGVuZ3RoID4gMSkge1xuICAgICAgICBmb3IgKHZhciBpID0gMTsgaSA8IGFyZ3VtZW50cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgYXJnc1tpIC0gMV0gPSBhcmd1bWVudHNbaV07XG4gICAgICAgIH1cbiAgICB9XG4gICAgcXVldWUucHVzaChuZXcgSXRlbShmdW4sIGFyZ3MpKTtcbiAgICBpZiAocXVldWUubGVuZ3RoID09PSAxICYmICFkcmFpbmluZykge1xuICAgICAgICBydW5UaW1lb3V0KGRyYWluUXVldWUpO1xuICAgIH1cbn07XG5cbi8vIHY4IGxpa2VzIHByZWRpY3RpYmxlIG9iamVjdHNcbmZ1bmN0aW9uIEl0ZW0oZnVuLCBhcnJheSkge1xuICAgIHRoaXMuZnVuID0gZnVuO1xuICAgIHRoaXMuYXJyYXkgPSBhcnJheTtcbn1cbkl0ZW0ucHJvdG90eXBlLnJ1biA9IGZ1bmN0aW9uICgpIHtcbiAgICB0aGlzLmZ1bi5hcHBseShudWxsLCB0aGlzLmFycmF5KTtcbn07XG5wcm9jZXNzLnRpdGxlID0gJ2Jyb3dzZXInO1xucHJvY2Vzcy5icm93c2VyID0gdHJ1ZTtcbnByb2Nlc3MuZW52ID0ge307XG5wcm9jZXNzLmFyZ3YgPSBbXTtcbnByb2Nlc3MudmVyc2lvbiA9ICcnOyAvLyBlbXB0eSBzdHJpbmcgdG8gYXZvaWQgcmVnZXhwIGlzc3Vlc1xucHJvY2Vzcy52ZXJzaW9ucyA9IHt9O1xuXG5mdW5jdGlvbiBub29wKCkge31cblxucHJvY2Vzcy5vbiA9IG5vb3A7XG5wcm9jZXNzLmFkZExpc3RlbmVyID0gbm9vcDtcbnByb2Nlc3Mub25jZSA9IG5vb3A7XG5wcm9jZXNzLm9mZiA9IG5vb3A7XG5wcm9jZXNzLnJlbW92ZUxpc3RlbmVyID0gbm9vcDtcbnByb2Nlc3MucmVtb3ZlQWxsTGlzdGVuZXJzID0gbm9vcDtcbnByb2Nlc3MuZW1pdCA9IG5vb3A7XG5wcm9jZXNzLnByZXBlbmRMaXN0ZW5lciA9IG5vb3A7XG5wcm9jZXNzLnByZXBlbmRPbmNlTGlzdGVuZXIgPSBub29wO1xuXG5wcm9jZXNzLmxpc3RlbmVycyA9IGZ1bmN0aW9uIChuYW1lKSB7IHJldHVybiBbXSB9XG5cbnByb2Nlc3MuYmluZGluZyA9IGZ1bmN0aW9uIChuYW1lKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdwcm9jZXNzLmJpbmRpbmcgaXMgbm90IHN1cHBvcnRlZCcpO1xufTtcblxucHJvY2Vzcy5jd2QgPSBmdW5jdGlvbiAoKSB7IHJldHVybiAnLycgfTtcbnByb2Nlc3MuY2hkaXIgPSBmdW5jdGlvbiAoZGlyKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdwcm9jZXNzLmNoZGlyIGlzIG5vdCBzdXBwb3J0ZWQnKTtcbn07XG5wcm9jZXNzLnVtYXNrID0gZnVuY3Rpb24oKSB7IHJldHVybiAwOyB9O1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHsgdmFsdWU6IHRydWUgfSk7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbmNvbnN0IHNjcm9sbF9zeW5jXzEgPSByZXF1aXJlKFwiLi9zY3JvbGwtc3luY1wiKTtcbmNsYXNzIEFjdGl2ZUxpbmVNYXJrZXIge1xuICAgIG9uRGlkQ2hhbmdlVGV4dEVkaXRvclNlbGVjdGlvbihsaW5lKSB7XG4gICAgICAgIGNvbnN0IHsgcHJldmlvdXMgfSA9IHNjcm9sbF9zeW5jXzEuZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKGxpbmUpO1xuICAgICAgICB0aGlzLl91cGRhdGUocHJldmlvdXMgJiYgcHJldmlvdXMuZWxlbWVudCk7XG4gICAgfVxuICAgIF91cGRhdGUoYmVmb3JlKSB7XG4gICAgICAgIHRoaXMuX3VubWFya0FjdGl2ZUVsZW1lbnQodGhpcy5fY3VycmVudCk7XG4gICAgICAgIHRoaXMuX21hcmtBY3RpdmVFbGVtZW50KGJlZm9yZSk7XG4gICAgICAgIHRoaXMuX2N1cnJlbnQgPSBiZWZvcmU7XG4gICAgfVxuICAgIF91bm1hcmtBY3RpdmVFbGVtZW50KGVsZW1lbnQpIHtcbiAgICAgICAgaWYgKCFlbGVtZW50KSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgZWxlbWVudC5jbGFzc05hbWUgPSBlbGVtZW50LmNsYXNzTmFtZS5yZXBsYWNlKC9cXGJjb2RlLWFjdGl2ZS1saW5lXFxiL2csICcnKTtcbiAgICB9XG4gICAgX21hcmtBY3RpdmVFbGVtZW50KGVsZW1lbnQpIHtcbiAgICAgICAgaWYgKCFlbGVtZW50KSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgZWxlbWVudC5jbGFzc05hbWUgKz0gJyBjb2RlLWFjdGl2ZS1saW5lJztcbiAgICB9XG59XG5leHBvcnRzLkFjdGl2ZUxpbmVNYXJrZXIgPSBBY3RpdmVMaW5lTWFya2VyO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmZ1bmN0aW9uIG9uY2VEb2N1bWVudExvYWRlZChmKSB7XG4gICAgaWYgKGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICdsb2FkaW5nJyB8fCBkb2N1bWVudC5yZWFkeVN0YXRlID09PSAndW5pbml0aWFsaXplZCcpIHtcbiAgICAgICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsIGYpO1xuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgICAgZigpO1xuICAgIH1cbn1cbmV4cG9ydHMub25jZURvY3VtZW50TG9hZGVkID0gb25jZURvY3VtZW50TG9hZGVkO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmNvbnN0IHNldHRpbmdzXzEgPSByZXF1aXJlKFwiLi9zZXR0aW5nc1wiKTtcbmV4cG9ydHMuY3JlYXRlUG9zdGVyRm9yVnNDb2RlID0gKHZzY29kZSkgPT4ge1xuICAgIHJldHVybiBuZXcgY2xhc3Mge1xuICAgICAgICBwb3N0TWVzc2FnZSh0eXBlLCBib2R5KSB7XG4gICAgICAgICAgICB2c2NvZGUucG9zdE1lc3NhZ2Uoe1xuICAgICAgICAgICAgICAgIHR5cGUsXG4gICAgICAgICAgICAgICAgc291cmNlOiBzZXR0aW5nc18xLmdldFNldHRpbmdzKCkuc291cmNlLFxuICAgICAgICAgICAgICAgIGJvZHlcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfTtcbn07XG4iLCIvKipcbiAqIGxvZGFzaCAoQ3VzdG9tIEJ1aWxkKSA8aHR0cHM6Ly9sb2Rhc2guY29tLz5cbiAqIEJ1aWxkOiBgbG9kYXNoIG1vZHVsYXJpemUgZXhwb3J0cz1cIm5wbVwiIC1vIC4vYFxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnMgPGh0dHBzOi8vanF1ZXJ5Lm9yZy8+XG4gKiBSZWxlYXNlZCB1bmRlciBNSVQgbGljZW5zZSA8aHR0cHM6Ly9sb2Rhc2guY29tL2xpY2Vuc2U+XG4gKiBCYXNlZCBvbiBVbmRlcnNjb3JlLmpzIDEuOC4zIDxodHRwOi8vdW5kZXJzY29yZWpzLm9yZy9MSUNFTlNFPlxuICogQ29weXJpZ2h0IEplcmVteSBBc2hrZW5hcywgRG9jdW1lbnRDbG91ZCBhbmQgSW52ZXN0aWdhdGl2ZSBSZXBvcnRlcnMgJiBFZGl0b3JzXG4gKi9cblxuLyoqIFVzZWQgYXMgdGhlIGBUeXBlRXJyb3JgIG1lc3NhZ2UgZm9yIFwiRnVuY3Rpb25zXCIgbWV0aG9kcy4gKi9cbnZhciBGVU5DX0VSUk9SX1RFWFQgPSAnRXhwZWN0ZWQgYSBmdW5jdGlvbic7XG5cbi8qKiBVc2VkIGFzIHJlZmVyZW5jZXMgZm9yIHZhcmlvdXMgYE51bWJlcmAgY29uc3RhbnRzLiAqL1xudmFyIE5BTiA9IDAgLyAwO1xuXG4vKiogYE9iamVjdCN0b1N0cmluZ2AgcmVzdWx0IHJlZmVyZW5jZXMuICovXG52YXIgc3ltYm9sVGFnID0gJ1tvYmplY3QgU3ltYm9sXSc7XG5cbi8qKiBVc2VkIHRvIG1hdGNoIGxlYWRpbmcgYW5kIHRyYWlsaW5nIHdoaXRlc3BhY2UuICovXG52YXIgcmVUcmltID0gL15cXHMrfFxccyskL2c7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBiYWQgc2lnbmVkIGhleGFkZWNpbWFsIHN0cmluZyB2YWx1ZXMuICovXG52YXIgcmVJc0JhZEhleCA9IC9eWy0rXTB4WzAtOWEtZl0rJC9pO1xuXG4vKiogVXNlZCB0byBkZXRlY3QgYmluYXJ5IHN0cmluZyB2YWx1ZXMuICovXG52YXIgcmVJc0JpbmFyeSA9IC9eMGJbMDFdKyQvaTtcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IG9jdGFsIHN0cmluZyB2YWx1ZXMuICovXG52YXIgcmVJc09jdGFsID0gL14wb1swLTddKyQvaTtcblxuLyoqIEJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzIHdpdGhvdXQgYSBkZXBlbmRlbmN5IG9uIGByb290YC4gKi9cbnZhciBmcmVlUGFyc2VJbnQgPSBwYXJzZUludDtcblxuLyoqIERldGVjdCBmcmVlIHZhcmlhYmxlIGBnbG9iYWxgIGZyb20gTm9kZS5qcy4gKi9cbnZhciBmcmVlR2xvYmFsID0gdHlwZW9mIGdsb2JhbCA9PSAnb2JqZWN0JyAmJiBnbG9iYWwgJiYgZ2xvYmFsLk9iamVjdCA9PT0gT2JqZWN0ICYmIGdsb2JhbDtcblxuLyoqIERldGVjdCBmcmVlIHZhcmlhYmxlIGBzZWxmYC4gKi9cbnZhciBmcmVlU2VsZiA9IHR5cGVvZiBzZWxmID09ICdvYmplY3QnICYmIHNlbGYgJiYgc2VsZi5PYmplY3QgPT09IE9iamVjdCAmJiBzZWxmO1xuXG4vKiogVXNlZCBhcyBhIHJlZmVyZW5jZSB0byB0aGUgZ2xvYmFsIG9iamVjdC4gKi9cbnZhciByb290ID0gZnJlZUdsb2JhbCB8fCBmcmVlU2VsZiB8fCBGdW5jdGlvbigncmV0dXJuIHRoaXMnKSgpO1xuXG4vKiogVXNlZCBmb3IgYnVpbHQtaW4gbWV0aG9kIHJlZmVyZW5jZXMuICovXG52YXIgb2JqZWN0UHJvdG8gPSBPYmplY3QucHJvdG90eXBlO1xuXG4vKipcbiAqIFVzZWQgdG8gcmVzb2x2ZSB0aGVcbiAqIFtgdG9TdHJpbmdUYWdgXShodHRwOi8vZWNtYS1pbnRlcm5hdGlvbmFsLm9yZy9lY21hLTI2Mi83LjAvI3NlYy1vYmplY3QucHJvdG90eXBlLnRvc3RyaW5nKVxuICogb2YgdmFsdWVzLlxuICovXG52YXIgb2JqZWN0VG9TdHJpbmcgPSBvYmplY3RQcm90by50b1N0cmluZztcblxuLyogQnVpbHQtaW4gbWV0aG9kIHJlZmVyZW5jZXMgZm9yIHRob3NlIHdpdGggdGhlIHNhbWUgbmFtZSBhcyBvdGhlciBgbG9kYXNoYCBtZXRob2RzLiAqL1xudmFyIG5hdGl2ZU1heCA9IE1hdGgubWF4LFxuICAgIG5hdGl2ZU1pbiA9IE1hdGgubWluO1xuXG4vKipcbiAqIEdldHMgdGhlIHRpbWVzdGFtcCBvZiB0aGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0aGF0IGhhdmUgZWxhcHNlZCBzaW5jZVxuICogdGhlIFVuaXggZXBvY2ggKDEgSmFudWFyeSAxOTcwIDAwOjAwOjAwIFVUQykuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSAyLjQuMFxuICogQGNhdGVnb3J5IERhdGVcbiAqIEByZXR1cm5zIHtudW1iZXJ9IFJldHVybnMgdGhlIHRpbWVzdGFtcC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5kZWZlcihmdW5jdGlvbihzdGFtcCkge1xuICogICBjb25zb2xlLmxvZyhfLm5vdygpIC0gc3RhbXApO1xuICogfSwgXy5ub3coKSk7XG4gKiAvLyA9PiBMb2dzIHRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIGl0IHRvb2sgZm9yIHRoZSBkZWZlcnJlZCBpbnZvY2F0aW9uLlxuICovXG52YXIgbm93ID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiByb290LkRhdGUubm93KCk7XG59O1xuXG4vKipcbiAqIENyZWF0ZXMgYSBkZWJvdW5jZWQgZnVuY3Rpb24gdGhhdCBkZWxheXMgaW52b2tpbmcgYGZ1bmNgIHVudGlsIGFmdGVyIGB3YWl0YFxuICogbWlsbGlzZWNvbmRzIGhhdmUgZWxhcHNlZCBzaW5jZSB0aGUgbGFzdCB0aW1lIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gd2FzXG4gKiBpbnZva2VkLiBUaGUgZGVib3VuY2VkIGZ1bmN0aW9uIGNvbWVzIHdpdGggYSBgY2FuY2VsYCBtZXRob2QgdG8gY2FuY2VsXG4gKiBkZWxheWVkIGBmdW5jYCBpbnZvY2F0aW9ucyBhbmQgYSBgZmx1c2hgIG1ldGhvZCB0byBpbW1lZGlhdGVseSBpbnZva2UgdGhlbS5cbiAqIFByb3ZpZGUgYG9wdGlvbnNgIHRvIGluZGljYXRlIHdoZXRoZXIgYGZ1bmNgIHNob3VsZCBiZSBpbnZva2VkIG9uIHRoZVxuICogbGVhZGluZyBhbmQvb3IgdHJhaWxpbmcgZWRnZSBvZiB0aGUgYHdhaXRgIHRpbWVvdXQuIFRoZSBgZnVuY2AgaXMgaW52b2tlZFxuICogd2l0aCB0aGUgbGFzdCBhcmd1bWVudHMgcHJvdmlkZWQgdG8gdGhlIGRlYm91bmNlZCBmdW5jdGlvbi4gU3Vic2VxdWVudFxuICogY2FsbHMgdG8gdGhlIGRlYm91bmNlZCBmdW5jdGlvbiByZXR1cm4gdGhlIHJlc3VsdCBvZiB0aGUgbGFzdCBgZnVuY2BcbiAqIGludm9jYXRpb24uXG4gKlxuICogKipOb3RlOioqIElmIGBsZWFkaW5nYCBhbmQgYHRyYWlsaW5nYCBvcHRpb25zIGFyZSBgdHJ1ZWAsIGBmdW5jYCBpc1xuICogaW52b2tlZCBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dCBvbmx5IGlmIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb25cbiAqIGlzIGludm9rZWQgbW9yZSB0aGFuIG9uY2UgZHVyaW5nIHRoZSBgd2FpdGAgdGltZW91dC5cbiAqXG4gKiBJZiBgd2FpdGAgaXMgYDBgIGFuZCBgbGVhZGluZ2AgaXMgYGZhbHNlYCwgYGZ1bmNgIGludm9jYXRpb24gaXMgZGVmZXJyZWRcbiAqIHVudGlsIHRvIHRoZSBuZXh0IHRpY2ssIHNpbWlsYXIgdG8gYHNldFRpbWVvdXRgIHdpdGggYSB0aW1lb3V0IG9mIGAwYC5cbiAqXG4gKiBTZWUgW0RhdmlkIENvcmJhY2hvJ3MgYXJ0aWNsZV0oaHR0cHM6Ly9jc3MtdHJpY2tzLmNvbS9kZWJvdW5jaW5nLXRocm90dGxpbmctZXhwbGFpbmVkLWV4YW1wbGVzLylcbiAqIGZvciBkZXRhaWxzIG92ZXIgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gYF8uZGVib3VuY2VgIGFuZCBgXy50aHJvdHRsZWAuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSAwLjEuMFxuICogQGNhdGVnb3J5IEZ1bmN0aW9uXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmdW5jIFRoZSBmdW5jdGlvbiB0byBkZWJvdW5jZS5cbiAqIEBwYXJhbSB7bnVtYmVyfSBbd2FpdD0wXSBUaGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0byBkZWxheS5cbiAqIEBwYXJhbSB7T2JqZWN0fSBbb3B0aW9ucz17fV0gVGhlIG9wdGlvbnMgb2JqZWN0LlxuICogQHBhcmFtIHtib29sZWFufSBbb3B0aW9ucy5sZWFkaW5nPWZhbHNlXVxuICogIFNwZWNpZnkgaW52b2tpbmcgb24gdGhlIGxlYWRpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0aW9ucy5tYXhXYWl0XVxuICogIFRoZSBtYXhpbXVtIHRpbWUgYGZ1bmNgIGlzIGFsbG93ZWQgdG8gYmUgZGVsYXllZCBiZWZvcmUgaXQncyBpbnZva2VkLlxuICogQHBhcmFtIHtib29sZWFufSBbb3B0aW9ucy50cmFpbGluZz10cnVlXVxuICogIFNwZWNpZnkgaW52b2tpbmcgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcmV0dXJucyB7RnVuY3Rpb259IFJldHVybnMgdGhlIG5ldyBkZWJvdW5jZWQgZnVuY3Rpb24uXG4gKiBAZXhhbXBsZVxuICpcbiAqIC8vIEF2b2lkIGNvc3RseSBjYWxjdWxhdGlvbnMgd2hpbGUgdGhlIHdpbmRvdyBzaXplIGlzIGluIGZsdXguXG4gKiBqUXVlcnkod2luZG93KS5vbigncmVzaXplJywgXy5kZWJvdW5jZShjYWxjdWxhdGVMYXlvdXQsIDE1MCkpO1xuICpcbiAqIC8vIEludm9rZSBgc2VuZE1haWxgIHdoZW4gY2xpY2tlZCwgZGVib3VuY2luZyBzdWJzZXF1ZW50IGNhbGxzLlxuICogalF1ZXJ5KGVsZW1lbnQpLm9uKCdjbGljaycsIF8uZGVib3VuY2Uoc2VuZE1haWwsIDMwMCwge1xuICogICAnbGVhZGluZyc6IHRydWUsXG4gKiAgICd0cmFpbGluZyc6IGZhbHNlXG4gKiB9KSk7XG4gKlxuICogLy8gRW5zdXJlIGBiYXRjaExvZ2AgaXMgaW52b2tlZCBvbmNlIGFmdGVyIDEgc2Vjb25kIG9mIGRlYm91bmNlZCBjYWxscy5cbiAqIHZhciBkZWJvdW5jZWQgPSBfLmRlYm91bmNlKGJhdGNoTG9nLCAyNTAsIHsgJ21heFdhaXQnOiAxMDAwIH0pO1xuICogdmFyIHNvdXJjZSA9IG5ldyBFdmVudFNvdXJjZSgnL3N0cmVhbScpO1xuICogalF1ZXJ5KHNvdXJjZSkub24oJ21lc3NhZ2UnLCBkZWJvdW5jZWQpO1xuICpcbiAqIC8vIENhbmNlbCB0aGUgdHJhaWxpbmcgZGVib3VuY2VkIGludm9jYXRpb24uXG4gKiBqUXVlcnkod2luZG93KS5vbigncG9wc3RhdGUnLCBkZWJvdW5jZWQuY2FuY2VsKTtcbiAqL1xuZnVuY3Rpb24gZGVib3VuY2UoZnVuYywgd2FpdCwgb3B0aW9ucykge1xuICB2YXIgbGFzdEFyZ3MsXG4gICAgICBsYXN0VGhpcyxcbiAgICAgIG1heFdhaXQsXG4gICAgICByZXN1bHQsXG4gICAgICB0aW1lcklkLFxuICAgICAgbGFzdENhbGxUaW1lLFxuICAgICAgbGFzdEludm9rZVRpbWUgPSAwLFxuICAgICAgbGVhZGluZyA9IGZhbHNlLFxuICAgICAgbWF4aW5nID0gZmFsc2UsXG4gICAgICB0cmFpbGluZyA9IHRydWU7XG5cbiAgaWYgKHR5cGVvZiBmdW5jICE9ICdmdW5jdGlvbicpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKEZVTkNfRVJST1JfVEVYVCk7XG4gIH1cbiAgd2FpdCA9IHRvTnVtYmVyKHdhaXQpIHx8IDA7XG4gIGlmIChpc09iamVjdChvcHRpb25zKSkge1xuICAgIGxlYWRpbmcgPSAhIW9wdGlvbnMubGVhZGluZztcbiAgICBtYXhpbmcgPSAnbWF4V2FpdCcgaW4gb3B0aW9ucztcbiAgICBtYXhXYWl0ID0gbWF4aW5nID8gbmF0aXZlTWF4KHRvTnVtYmVyKG9wdGlvbnMubWF4V2FpdCkgfHwgMCwgd2FpdCkgOiBtYXhXYWl0O1xuICAgIHRyYWlsaW5nID0gJ3RyYWlsaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLnRyYWlsaW5nIDogdHJhaWxpbmc7XG4gIH1cblxuICBmdW5jdGlvbiBpbnZva2VGdW5jKHRpbWUpIHtcbiAgICB2YXIgYXJncyA9IGxhc3RBcmdzLFxuICAgICAgICB0aGlzQXJnID0gbGFzdFRoaXM7XG5cbiAgICBsYXN0QXJncyA9IGxhc3RUaGlzID0gdW5kZWZpbmVkO1xuICAgIGxhc3RJbnZva2VUaW1lID0gdGltZTtcbiAgICByZXN1bHQgPSBmdW5jLmFwcGx5KHRoaXNBcmcsIGFyZ3MpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBmdW5jdGlvbiBsZWFkaW5nRWRnZSh0aW1lKSB7XG4gICAgLy8gUmVzZXQgYW55IGBtYXhXYWl0YCB0aW1lci5cbiAgICBsYXN0SW52b2tlVGltZSA9IHRpbWU7XG4gICAgLy8gU3RhcnQgdGhlIHRpbWVyIGZvciB0aGUgdHJhaWxpbmcgZWRnZS5cbiAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgIC8vIEludm9rZSB0aGUgbGVhZGluZyBlZGdlLlxuICAgIHJldHVybiBsZWFkaW5nID8gaW52b2tlRnVuYyh0aW1lKSA6IHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIHJlbWFpbmluZ1dhaXQodGltZSkge1xuICAgIHZhciB0aW1lU2luY2VMYXN0Q2FsbCA9IHRpbWUgLSBsYXN0Q2FsbFRpbWUsXG4gICAgICAgIHRpbWVTaW5jZUxhc3RJbnZva2UgPSB0aW1lIC0gbGFzdEludm9rZVRpbWUsXG4gICAgICAgIHJlc3VsdCA9IHdhaXQgLSB0aW1lU2luY2VMYXN0Q2FsbDtcblxuICAgIHJldHVybiBtYXhpbmcgPyBuYXRpdmVNaW4ocmVzdWx0LCBtYXhXYWl0IC0gdGltZVNpbmNlTGFzdEludm9rZSkgOiByZXN1bHQ7XG4gIH1cblxuICBmdW5jdGlvbiBzaG91bGRJbnZva2UodGltZSkge1xuICAgIHZhciB0aW1lU2luY2VMYXN0Q2FsbCA9IHRpbWUgLSBsYXN0Q2FsbFRpbWUsXG4gICAgICAgIHRpbWVTaW5jZUxhc3RJbnZva2UgPSB0aW1lIC0gbGFzdEludm9rZVRpbWU7XG5cbiAgICAvLyBFaXRoZXIgdGhpcyBpcyB0aGUgZmlyc3QgY2FsbCwgYWN0aXZpdHkgaGFzIHN0b3BwZWQgYW5kIHdlJ3JlIGF0IHRoZVxuICAgIC8vIHRyYWlsaW5nIGVkZ2UsIHRoZSBzeXN0ZW0gdGltZSBoYXMgZ29uZSBiYWNrd2FyZHMgYW5kIHdlJ3JlIHRyZWF0aW5nXG4gICAgLy8gaXQgYXMgdGhlIHRyYWlsaW5nIGVkZ2UsIG9yIHdlJ3ZlIGhpdCB0aGUgYG1heFdhaXRgIGxpbWl0LlxuICAgIHJldHVybiAobGFzdENhbGxUaW1lID09PSB1bmRlZmluZWQgfHwgKHRpbWVTaW5jZUxhc3RDYWxsID49IHdhaXQpIHx8XG4gICAgICAodGltZVNpbmNlTGFzdENhbGwgPCAwKSB8fCAobWF4aW5nICYmIHRpbWVTaW5jZUxhc3RJbnZva2UgPj0gbWF4V2FpdCkpO1xuICB9XG5cbiAgZnVuY3Rpb24gdGltZXJFeHBpcmVkKCkge1xuICAgIHZhciB0aW1lID0gbm93KCk7XG4gICAgaWYgKHNob3VsZEludm9rZSh0aW1lKSkge1xuICAgICAgcmV0dXJuIHRyYWlsaW5nRWRnZSh0aW1lKTtcbiAgICB9XG4gICAgLy8gUmVzdGFydCB0aGUgdGltZXIuXG4gICAgdGltZXJJZCA9IHNldFRpbWVvdXQodGltZXJFeHBpcmVkLCByZW1haW5pbmdXYWl0KHRpbWUpKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHRyYWlsaW5nRWRnZSh0aW1lKSB7XG4gICAgdGltZXJJZCA9IHVuZGVmaW5lZDtcblxuICAgIC8vIE9ubHkgaW52b2tlIGlmIHdlIGhhdmUgYGxhc3RBcmdzYCB3aGljaCBtZWFucyBgZnVuY2AgaGFzIGJlZW5cbiAgICAvLyBkZWJvdW5jZWQgYXQgbGVhc3Qgb25jZS5cbiAgICBpZiAodHJhaWxpbmcgJiYgbGFzdEFyZ3MpIHtcbiAgICAgIHJldHVybiBpbnZva2VGdW5jKHRpbWUpO1xuICAgIH1cbiAgICBsYXN0QXJncyA9IGxhc3RUaGlzID0gdW5kZWZpbmVkO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBmdW5jdGlvbiBjYW5jZWwoKSB7XG4gICAgaWYgKHRpbWVySWQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgY2xlYXJUaW1lb3V0KHRpbWVySWQpO1xuICAgIH1cbiAgICBsYXN0SW52b2tlVGltZSA9IDA7XG4gICAgbGFzdEFyZ3MgPSBsYXN0Q2FsbFRpbWUgPSBsYXN0VGhpcyA9IHRpbWVySWQgPSB1bmRlZmluZWQ7XG4gIH1cblxuICBmdW5jdGlvbiBmbHVzaCgpIHtcbiAgICByZXR1cm4gdGltZXJJZCA9PT0gdW5kZWZpbmVkID8gcmVzdWx0IDogdHJhaWxpbmdFZGdlKG5vdygpKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGRlYm91bmNlZCgpIHtcbiAgICB2YXIgdGltZSA9IG5vdygpLFxuICAgICAgICBpc0ludm9raW5nID0gc2hvdWxkSW52b2tlKHRpbWUpO1xuXG4gICAgbGFzdEFyZ3MgPSBhcmd1bWVudHM7XG4gICAgbGFzdFRoaXMgPSB0aGlzO1xuICAgIGxhc3RDYWxsVGltZSA9IHRpbWU7XG5cbiAgICBpZiAoaXNJbnZva2luZykge1xuICAgICAgaWYgKHRpbWVySWQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICByZXR1cm4gbGVhZGluZ0VkZ2UobGFzdENhbGxUaW1lKTtcbiAgICAgIH1cbiAgICAgIGlmIChtYXhpbmcpIHtcbiAgICAgICAgLy8gSGFuZGxlIGludm9jYXRpb25zIGluIGEgdGlnaHQgbG9vcC5cbiAgICAgICAgdGltZXJJZCA9IHNldFRpbWVvdXQodGltZXJFeHBpcmVkLCB3YWl0KTtcbiAgICAgICAgcmV0dXJuIGludm9rZUZ1bmMobGFzdENhbGxUaW1lKTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKHRpbWVySWQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgdGltZXJJZCA9IHNldFRpbWVvdXQodGltZXJFeHBpcmVkLCB3YWl0KTtcbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuICBkZWJvdW5jZWQuY2FuY2VsID0gY2FuY2VsO1xuICBkZWJvdW5jZWQuZmx1c2ggPSBmbHVzaDtcbiAgcmV0dXJuIGRlYm91bmNlZDtcbn1cblxuLyoqXG4gKiBDcmVhdGVzIGEgdGhyb3R0bGVkIGZ1bmN0aW9uIHRoYXQgb25seSBpbnZva2VzIGBmdW5jYCBhdCBtb3N0IG9uY2UgcGVyXG4gKiBldmVyeSBgd2FpdGAgbWlsbGlzZWNvbmRzLiBUaGUgdGhyb3R0bGVkIGZ1bmN0aW9uIGNvbWVzIHdpdGggYSBgY2FuY2VsYFxuICogbWV0aG9kIHRvIGNhbmNlbCBkZWxheWVkIGBmdW5jYCBpbnZvY2F0aW9ucyBhbmQgYSBgZmx1c2hgIG1ldGhvZCB0b1xuICogaW1tZWRpYXRlbHkgaW52b2tlIHRoZW0uIFByb3ZpZGUgYG9wdGlvbnNgIHRvIGluZGljYXRlIHdoZXRoZXIgYGZ1bmNgXG4gKiBzaG91bGQgYmUgaW52b2tlZCBvbiB0aGUgbGVhZGluZyBhbmQvb3IgdHJhaWxpbmcgZWRnZSBvZiB0aGUgYHdhaXRgXG4gKiB0aW1lb3V0LiBUaGUgYGZ1bmNgIGlzIGludm9rZWQgd2l0aCB0aGUgbGFzdCBhcmd1bWVudHMgcHJvdmlkZWQgdG8gdGhlXG4gKiB0aHJvdHRsZWQgZnVuY3Rpb24uIFN1YnNlcXVlbnQgY2FsbHMgdG8gdGhlIHRocm90dGxlZCBmdW5jdGlvbiByZXR1cm4gdGhlXG4gKiByZXN1bHQgb2YgdGhlIGxhc3QgYGZ1bmNgIGludm9jYXRpb24uXG4gKlxuICogKipOb3RlOioqIElmIGBsZWFkaW5nYCBhbmQgYHRyYWlsaW5nYCBvcHRpb25zIGFyZSBgdHJ1ZWAsIGBmdW5jYCBpc1xuICogaW52b2tlZCBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dCBvbmx5IGlmIHRoZSB0aHJvdHRsZWQgZnVuY3Rpb25cbiAqIGlzIGludm9rZWQgbW9yZSB0aGFuIG9uY2UgZHVyaW5nIHRoZSBgd2FpdGAgdGltZW91dC5cbiAqXG4gKiBJZiBgd2FpdGAgaXMgYDBgIGFuZCBgbGVhZGluZ2AgaXMgYGZhbHNlYCwgYGZ1bmNgIGludm9jYXRpb24gaXMgZGVmZXJyZWRcbiAqIHVudGlsIHRvIHRoZSBuZXh0IHRpY2ssIHNpbWlsYXIgdG8gYHNldFRpbWVvdXRgIHdpdGggYSB0aW1lb3V0IG9mIGAwYC5cbiAqXG4gKiBTZWUgW0RhdmlkIENvcmJhY2hvJ3MgYXJ0aWNsZV0oaHR0cHM6Ly9jc3MtdHJpY2tzLmNvbS9kZWJvdW5jaW5nLXRocm90dGxpbmctZXhwbGFpbmVkLWV4YW1wbGVzLylcbiAqIGZvciBkZXRhaWxzIG92ZXIgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gYF8udGhyb3R0bGVgIGFuZCBgXy5kZWJvdW5jZWAuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSAwLjEuMFxuICogQGNhdGVnb3J5IEZ1bmN0aW9uXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmdW5jIFRoZSBmdW5jdGlvbiB0byB0aHJvdHRsZS5cbiAqIEBwYXJhbSB7bnVtYmVyfSBbd2FpdD0wXSBUaGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0byB0aHJvdHRsZSBpbnZvY2F0aW9ucyB0by5cbiAqIEBwYXJhbSB7T2JqZWN0fSBbb3B0aW9ucz17fV0gVGhlIG9wdGlvbnMgb2JqZWN0LlxuICogQHBhcmFtIHtib29sZWFufSBbb3B0aW9ucy5sZWFkaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgbGVhZGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHBhcmFtIHtib29sZWFufSBbb3B0aW9ucy50cmFpbGluZz10cnVlXVxuICogIFNwZWNpZnkgaW52b2tpbmcgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcmV0dXJucyB7RnVuY3Rpb259IFJldHVybnMgdGhlIG5ldyB0aHJvdHRsZWQgZnVuY3Rpb24uXG4gKiBAZXhhbXBsZVxuICpcbiAqIC8vIEF2b2lkIGV4Y2Vzc2l2ZWx5IHVwZGF0aW5nIHRoZSBwb3NpdGlvbiB3aGlsZSBzY3JvbGxpbmcuXG4gKiBqUXVlcnkod2luZG93KS5vbignc2Nyb2xsJywgXy50aHJvdHRsZSh1cGRhdGVQb3NpdGlvbiwgMTAwKSk7XG4gKlxuICogLy8gSW52b2tlIGByZW5ld1Rva2VuYCB3aGVuIHRoZSBjbGljayBldmVudCBpcyBmaXJlZCwgYnV0IG5vdCBtb3JlIHRoYW4gb25jZSBldmVyeSA1IG1pbnV0ZXMuXG4gKiB2YXIgdGhyb3R0bGVkID0gXy50aHJvdHRsZShyZW5ld1Rva2VuLCAzMDAwMDAsIHsgJ3RyYWlsaW5nJzogZmFsc2UgfSk7XG4gKiBqUXVlcnkoZWxlbWVudCkub24oJ2NsaWNrJywgdGhyb3R0bGVkKTtcbiAqXG4gKiAvLyBDYW5jZWwgdGhlIHRyYWlsaW5nIHRocm90dGxlZCBpbnZvY2F0aW9uLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3BvcHN0YXRlJywgdGhyb3R0bGVkLmNhbmNlbCk7XG4gKi9cbmZ1bmN0aW9uIHRocm90dGxlKGZ1bmMsIHdhaXQsIG9wdGlvbnMpIHtcbiAgdmFyIGxlYWRpbmcgPSB0cnVlLFxuICAgICAgdHJhaWxpbmcgPSB0cnVlO1xuXG4gIGlmICh0eXBlb2YgZnVuYyAhPSAnZnVuY3Rpb24nKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcihGVU5DX0VSUk9SX1RFWFQpO1xuICB9XG4gIGlmIChpc09iamVjdChvcHRpb25zKSkge1xuICAgIGxlYWRpbmcgPSAnbGVhZGluZycgaW4gb3B0aW9ucyA/ICEhb3B0aW9ucy5sZWFkaW5nIDogbGVhZGluZztcbiAgICB0cmFpbGluZyA9ICd0cmFpbGluZycgaW4gb3B0aW9ucyA/ICEhb3B0aW9ucy50cmFpbGluZyA6IHRyYWlsaW5nO1xuICB9XG4gIHJldHVybiBkZWJvdW5jZShmdW5jLCB3YWl0LCB7XG4gICAgJ2xlYWRpbmcnOiBsZWFkaW5nLFxuICAgICdtYXhXYWl0Jzogd2FpdCxcbiAgICAndHJhaWxpbmcnOiB0cmFpbGluZ1xuICB9KTtcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyB0aGVcbiAqIFtsYW5ndWFnZSB0eXBlXShodHRwOi8vd3d3LmVjbWEtaW50ZXJuYXRpb25hbC5vcmcvZWNtYS0yNjIvNy4wLyNzZWMtZWNtYXNjcmlwdC1sYW5ndWFnZS10eXBlcylcbiAqIG9mIGBPYmplY3RgLiAoZS5nLiBhcnJheXMsIGZ1bmN0aW9ucywgb2JqZWN0cywgcmVnZXhlcywgYG5ldyBOdW1iZXIoMClgLCBhbmQgYG5ldyBTdHJpbmcoJycpYClcbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBhbiBvYmplY3QsIGVsc2UgYGZhbHNlYC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5pc09iamVjdCh7fSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChbMSwgMiwgM10pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QoXy5ub29wKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KG51bGwpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNPYmplY3QodmFsdWUpIHtcbiAgdmFyIHR5cGUgPSB0eXBlb2YgdmFsdWU7XG4gIHJldHVybiAhIXZhbHVlICYmICh0eXBlID09ICdvYmplY3QnIHx8IHR5cGUgPT0gJ2Z1bmN0aW9uJyk7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgb2JqZWN0LWxpa2UuIEEgdmFsdWUgaXMgb2JqZWN0LWxpa2UgaWYgaXQncyBub3QgYG51bGxgXG4gKiBhbmQgaGFzIGEgYHR5cGVvZmAgcmVzdWx0IG9mIFwib2JqZWN0XCIuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSA0LjAuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgb2JqZWN0LWxpa2UsIGVsc2UgYGZhbHNlYC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5pc09iamVjdExpa2Uoe30pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKFsxLCAyLCAzXSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdExpa2UoXy5ub29wKTtcbiAqIC8vID0+IGZhbHNlXG4gKlxuICogXy5pc09iamVjdExpa2UobnVsbCk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc09iamVjdExpa2UodmFsdWUpIHtcbiAgcmV0dXJuICEhdmFsdWUgJiYgdHlwZW9mIHZhbHVlID09ICdvYmplY3QnO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIGNsYXNzaWZpZWQgYXMgYSBgU3ltYm9sYCBwcmltaXRpdmUgb3Igb2JqZWN0LlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIGEgc3ltYm9sLCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNTeW1ib2woU3ltYm9sLml0ZXJhdG9yKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzU3ltYm9sKCdhYmMnKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzU3ltYm9sKHZhbHVlKSB7XG4gIHJldHVybiB0eXBlb2YgdmFsdWUgPT0gJ3N5bWJvbCcgfHxcbiAgICAoaXNPYmplY3RMaWtlKHZhbHVlKSAmJiBvYmplY3RUb1N0cmluZy5jYWxsKHZhbHVlKSA9PSBzeW1ib2xUYWcpO1xufVxuXG4vKipcbiAqIENvbnZlcnRzIGB2YWx1ZWAgdG8gYSBudW1iZXIuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSA0LjAuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIHByb2Nlc3MuXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBSZXR1cm5zIHRoZSBudW1iZXIuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8udG9OdW1iZXIoMy4yKTtcbiAqIC8vID0+IDMuMlxuICpcbiAqIF8udG9OdW1iZXIoTnVtYmVyLk1JTl9WQUxVRSk7XG4gKiAvLyA9PiA1ZS0zMjRcbiAqXG4gKiBfLnRvTnVtYmVyKEluZmluaXR5KTtcbiAqIC8vID0+IEluZmluaXR5XG4gKlxuICogXy50b051bWJlcignMy4yJyk7XG4gKiAvLyA9PiAzLjJcbiAqL1xuZnVuY3Rpb24gdG9OdW1iZXIodmFsdWUpIHtcbiAgaWYgKHR5cGVvZiB2YWx1ZSA9PSAnbnVtYmVyJykge1xuICAgIHJldHVybiB2YWx1ZTtcbiAgfVxuICBpZiAoaXNTeW1ib2wodmFsdWUpKSB7XG4gICAgcmV0dXJuIE5BTjtcbiAgfVxuICBpZiAoaXNPYmplY3QodmFsdWUpKSB7XG4gICAgdmFyIG90aGVyID0gdHlwZW9mIHZhbHVlLnZhbHVlT2YgPT0gJ2Z1bmN0aW9uJyA/IHZhbHVlLnZhbHVlT2YoKSA6IHZhbHVlO1xuICAgIHZhbHVlID0gaXNPYmplY3Qob3RoZXIpID8gKG90aGVyICsgJycpIDogb3RoZXI7XG4gIH1cbiAgaWYgKHR5cGVvZiB2YWx1ZSAhPSAnc3RyaW5nJykge1xuICAgIHJldHVybiB2YWx1ZSA9PT0gMCA/IHZhbHVlIDogK3ZhbHVlO1xuICB9XG4gIHZhbHVlID0gdmFsdWUucmVwbGFjZShyZVRyaW0sICcnKTtcbiAgdmFyIGlzQmluYXJ5ID0gcmVJc0JpbmFyeS50ZXN0KHZhbHVlKTtcbiAgcmV0dXJuIChpc0JpbmFyeSB8fCByZUlzT2N0YWwudGVzdCh2YWx1ZSkpXG4gICAgPyBmcmVlUGFyc2VJbnQodmFsdWUuc2xpY2UoMiksIGlzQmluYXJ5ID8gMiA6IDgpXG4gICAgOiAocmVJc0JhZEhleC50ZXN0KHZhbHVlKSA/IE5BTiA6ICt2YWx1ZSk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gdGhyb3R0bGU7XG4iXSwic291cmNlUm9vdCI6IiJ9 \ 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=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&&in?{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{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;n1)for(var n=1;nnew 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 4facd2fbb94..f581cd00252 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, system-ui, -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 c75da6b8542..8268f1e2d5b 100644 --- a/extensions/markdown-language-features/media/pre.js +++ b/extensions/markdown-language-features/media/pre.js @@ -1,2 +1 @@ -!function(e){var t={};function n(s){if(t[s])return t[s].exports;var o=t[s]={i:s,l:!1,exports:{}};return e[s].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,s){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:s})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var s=Object.create(null);if(n.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(s,o,function(t){return e[t]}.bind(null,o));return s},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=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})}}}]); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvc2V0dGluZ3MudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvcHJlLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2NzcC50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zdHJpbmdzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2xvYWRpbmcudHMiXSwibmFtZXMiOlsiaW5zdGFsbGVkTW9kdWxlcyIsIl9fd2VicGFja19yZXF1aXJlX18iLCJtb2R1bGVJZCIsImV4cG9ydHMiLCJtb2R1bGUiLCJpIiwibCIsIm1vZHVsZXMiLCJjYWxsIiwibSIsImMiLCJkIiwibmFtZSIsImdldHRlciIsIm8iLCJPYmplY3QiLCJkZWZpbmVQcm9wZXJ0eSIsImVudW1lcmFibGUiLCJnZXQiLCJyIiwiU3ltYm9sIiwidG9TdHJpbmdUYWciLCJ2YWx1ZSIsInQiLCJtb2RlIiwiX19lc01vZHVsZSIsIm5zIiwiY3JlYXRlIiwia2V5IiwiYmluZCIsIm4iLCJvYmplY3QiLCJwcm9wZXJ0eSIsInByb3RvdHlwZSIsImhhc093blByb3BlcnR5IiwicCIsInMiLCJjYWNoZWRTZXR0aW5ncyIsInVuZGVmaW5lZCIsImdldERhdGEiLCJlbGVtZW50IiwiZG9jdW1lbnQiLCJnZXRFbGVtZW50QnlJZCIsImRhdGEiLCJnZXRBdHRyaWJ1dGUiLCJKU09OIiwicGFyc2UiLCJFcnJvciIsImdldFNldHRpbmdzIiwiY3NwXzEiLCJsb2FkaW5nXzEiLCJ3aW5kb3ciLCJjc3BBbGVydGVyIiwiQ3NwQWxlcnRlciIsInN0eWxlTG9hZGluZ01vbml0b3IiLCJTdHlsZUxvYWRpbmdNb25pdG9yIiwic2V0dGluZ3NfMSIsInN0cmluZ3NfMSIsInRoaXMiLCJkaWRTaG93IiwiZGlkSGF2ZUNzcFdhcm5pbmciLCJhZGRFdmVudExpc3RlbmVyIiwib25Dc3BXYXJuaW5nIiwiZXZlbnQiLCJwb3N0ZXIiLCJtZXNzYWdpbmciLCJzaG93Q3NwV2FybmluZyIsInN0cmluZ3MiLCJnZXRTdHJpbmdzIiwic2V0dGluZ3MiLCJkaXNhYmxlU2VjdXJpdHlXYXJuaW5ncyIsIm5vdGlmaWNhdGlvbiIsImNyZWF0ZUVsZW1lbnQiLCJpbm5lclRleHQiLCJjc3BBbGVydE1lc3NhZ2VUZXh0Iiwic2V0QXR0cmlidXRlIiwiY3NwQWxlcnRNZXNzYWdlVGl0bGUiLCJjc3BBbGVydE1lc3NhZ2VMYWJlbCIsIm9uY2xpY2siLCJwb3N0TWVzc2FnZSIsInNvdXJjZSIsImJvZHkiLCJhcHBlbmRDaGlsZCIsInN0b3JlIiwidW5sb2FkZWRTdHlsZXMiLCJmaW5pc2hlZExvYWRpbmciLCJvblN0eWxlTG9hZEVycm9yIiwidGFyZ2V0IiwiZGF0YXNldCIsInB1c2giLCJsaW5rIiwiZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSIsIm9uZXJyb3IiLCJsZW5ndGgiXSwibWFwcGluZ3MiOiJhQUNFLElBQUlBLEVBQW1CLEdBR3ZCLFNBQVNDLEVBQW9CQyxHQUc1QixHQUFHRixFQUFpQkUsR0FDbkIsT0FBT0YsRUFBaUJFLEdBQVVDLFFBR25DLElBQUlDLEVBQVNKLEVBQWlCRSxHQUFZLENBQ3pDRyxFQUFHSCxFQUNISSxHQUFHLEVBQ0hILFFBQVMsSUFVVixPQU5BSSxFQUFRTCxHQUFVTSxLQUFLSixFQUFPRCxRQUFTQyxFQUFRQSxFQUFPRCxRQUFTRixHQUcvREcsRUFBT0UsR0FBSSxFQUdKRixFQUFPRCxRQUtmRixFQUFvQlEsRUFBSUYsRUFHeEJOLEVBQW9CUyxFQUFJVixFQUd4QkMsRUFBb0JVLEVBQUksU0FBU1IsRUFBU1MsRUFBTUMsR0FDM0NaLEVBQW9CYSxFQUFFWCxFQUFTUyxJQUNsQ0csT0FBT0MsZUFBZWIsRUFBU1MsRUFBTSxDQUFFSyxZQUFZLEVBQU1DLElBQUtMLEtBS2hFWixFQUFvQmtCLEVBQUksU0FBU2hCLEdBQ1gsb0JBQVhpQixRQUEwQkEsT0FBT0MsYUFDMUNOLE9BQU9DLGVBQWViLEVBQVNpQixPQUFPQyxZQUFhLENBQUVDLE1BQU8sV0FFN0RQLE9BQU9DLGVBQWViLEVBQVMsYUFBYyxDQUFFbUIsT0FBTyxLQVF2RHJCLEVBQW9Cc0IsRUFBSSxTQUFTRCxFQUFPRSxHQUV2QyxHQURVLEVBQVBBLElBQVVGLEVBQVFyQixFQUFvQnFCLElBQy9CLEVBQVBFLEVBQVUsT0FBT0YsRUFDcEIsR0FBVyxFQUFQRSxHQUE4QixpQkFBVkYsR0FBc0JBLEdBQVNBLEVBQU1HLFdBQVksT0FBT0gsRUFDaEYsSUFBSUksRUFBS1gsT0FBT1ksT0FBTyxNQUd2QixHQUZBMUIsRUFBb0JrQixFQUFFTyxHQUN0QlgsT0FBT0MsZUFBZVUsRUFBSSxVQUFXLENBQUVULFlBQVksRUFBTUssTUFBT0EsSUFDdEQsRUFBUEUsR0FBNEIsaUJBQVRGLEVBQW1CLElBQUksSUFBSU0sS0FBT04sRUFBT3JCLEVBQW9CVSxFQUFFZSxFQUFJRSxFQUFLLFNBQVNBLEdBQU8sT0FBT04sRUFBTU0sSUFBUUMsS0FBSyxLQUFNRCxJQUM5SSxPQUFPRixHQUlSekIsRUFBb0I2QixFQUFJLFNBQVMxQixHQUNoQyxJQUFJUyxFQUFTVCxHQUFVQSxFQUFPcUIsV0FDN0IsV0FBd0IsT0FBT3JCLEVBQWdCLFNBQy9DLFdBQThCLE9BQU9BLEdBRXRDLE9BREFILEVBQW9CVSxFQUFFRSxFQUFRLElBQUtBLEdBQzVCQSxHQUlSWixFQUFvQmEsRUFBSSxTQUFTaUIsRUFBUUMsR0FBWSxPQUFPakIsT0FBT2tCLFVBQVVDLGVBQWUxQixLQUFLdUIsRUFBUUMsSUFHekcvQixFQUFvQmtDLEVBQUksR0FJakJsQyxFQUFvQkEsRUFBb0JtQyxFQUFJLEksK0JDN0VyRHJCLE9BQU9DLGVBQWViLEVBQVMsYUFBYyxDQUFFbUIsT0FBTyxJQUN0RCxJQUFJZSxPQUFpQkMsRUFDckIsU0FBU0MsRUFBUVgsR0FDYixNQUFNWSxFQUFVQyxTQUFTQyxlQUFlLGdDQUN4QyxHQUFJRixFQUFTLENBQ1QsTUFBTUcsRUFBT0gsRUFBUUksYUFBYWhCLEdBQ2xDLEdBQUllLEVBQ0EsT0FBT0UsS0FBS0MsTUFBTUgsR0FHMUIsTUFBTSxJQUFJSSxNQUFNLDJCQUEyQm5CLEtBRS9DekIsRUFBUW9DLFFBQVVBLEVBV2xCcEMsRUFBUTZDLFlBVlIsV0FDSSxHQUFJWCxFQUNBLE9BQU9BLEVBR1gsR0FEQUEsRUFBaUJFLEVBQVEsaUJBRXJCLE9BQU9GLEVBRVgsTUFBTSxJQUFJVSxNQUFNLDZCLHVDQ3JCcEJoQyxPQUFPQyxlQUFlYixFQUFTLGFBQWMsQ0FBRW1CLE9BQU8sSUFDdEQsTUFBTTJCLEVBQVEsRUFBUSxJQUNoQkMsRUFBWSxFQUFRLElBQzFCQyxPQUFPQyxXQUFhLElBQUlILEVBQU1JLFdBQzlCRixPQUFPRyxvQkFBc0IsSUFBSUosRUFBVUsscUIsNkJDSjNDeEMsT0FBT0MsZUFBZWIsRUFBUyxhQUFjLENBQUVtQixPQUFPLElBQ3RELE1BQU1rQyxFQUFhLEVBQVEsR0FDckJDLEVBQVksRUFBUSxJQThDMUJ0RCxFQUFRa0QsV0ExQ1IsTUFDSSxjQUNJSyxLQUFLQyxTQUFVLEVBQ2ZELEtBQUtFLG1CQUFvQixFQUN6Qm5CLFNBQVNvQixpQkFBaUIsMEJBQTJCLEtBQ2pESCxLQUFLSSxpQkFFVFgsT0FBT1UsaUJBQWlCLFVBQVlFLElBQzVCQSxHQUFTQSxFQUFNcEIsTUFBNEIseUJBQXBCb0IsRUFBTXBCLEtBQUsvQixNQUNsQzhDLEtBQUtJLGlCQUlqQixVQUFVRSxHQUNOTixLQUFLTyxVQUFZRCxFQUNiTixLQUFLRSxtQkFDTEYsS0FBS1EsaUJBR2IsZUFDSVIsS0FBS0UsbUJBQW9CLEVBQ3pCRixLQUFLUSxpQkFFVCxpQkFDSSxNQUFNQyxFQUFVVixFQUFVVyxhQUNwQkMsRUFBV2IsRUFBV1IsY0FDNUIsR0FBSVUsS0FBS0MsU0FBV1UsRUFBU0MsMEJBQTRCWixLQUFLTyxVQUMxRCxPQUVKUCxLQUFLQyxTQUFVLEVBQ2YsTUFBTVksRUFBZTlCLFNBQVMrQixjQUFjLEtBQzVDRCxFQUFhRSxVQUFZTixFQUFRTyxvQkFDakNILEVBQWFJLGFBQWEsS0FBTSxvQkFDaENKLEVBQWFJLGFBQWEsUUFBU1IsRUFBUVMsc0JBQzNDTCxFQUFhSSxhQUFhLE9BQVEsVUFDbENKLEVBQWFJLGFBQWEsYUFBY1IsRUFBUVUsc0JBQ2hETixFQUFhTyxRQUFVLEtBQ25CcEIsS0FBS08sVUFBVWMsWUFBWSw4QkFBK0IsQ0FBRUMsT0FBUVgsRUFBU1csVUFFakZ2QyxTQUFTd0MsS0FBS0MsWUFBWVgsTSw2QkM3Q2xDeEQsT0FBT0MsZUFBZWIsRUFBUyxhQUFjLENBQUVtQixPQUFPLElBV3REbkIsRUFBUWlFLFdBVlIsV0FDSSxNQUFNZSxFQUFRMUMsU0FBU0MsZUFBZSxnQ0FDdEMsR0FBSXlDLEVBQU8sQ0FDUCxNQUFNeEMsRUFBT3dDLEVBQU12QyxhQUFhLGdCQUNoQyxHQUFJRCxFQUNBLE9BQU9FLEtBQUtDLE1BQU1ILEdBRzFCLE1BQU0sSUFBSUksTUFBTSw0Qiw2QkNicEJoQyxPQUFPQyxlQUFlYixFQUFTLGFBQWMsQ0FBRW1CLE9BQU8sSUFpQ3REbkIsRUFBUW9ELG9CQWhDUixNQUNJLGNBQ0lHLEtBQUswQixlQUFpQixHQUN0QjFCLEtBQUsyQixpQkFBa0IsRUFDdkIsTUFBTUMsRUFBb0J2QixJQUN0QixNQUFNaUIsRUFBU2pCLEVBQU13QixPQUFPQyxRQUFRUixPQUNwQ3RCLEtBQUswQixlQUFlSyxLQUFLVCxJQUU3QjdCLE9BQU9VLGlCQUFpQixtQkFBb0IsS0FDeEMsSUFBSyxNQUFNNkIsS0FBUWpELFNBQVNrRCx1QkFBdUIsbUJBQzNDRCxFQUFLRixRQUFRUixTQUNiVSxFQUFLRSxRQUFVTixLQUkzQm5DLE9BQU9VLGlCQUFpQixPQUFRLEtBQ3ZCSCxLQUFLMEIsZUFBZVMsU0FHekJuQyxLQUFLMkIsaUJBQWtCLEVBQ25CM0IsS0FBS00sUUFDTE4sS0FBS00sT0FBT2UsWUFBWSx3QkFBeUIsQ0FBRUssZUFBZ0IxQixLQUFLMEIsb0JBSXBGLFVBQVVwQixHQUNOTixLQUFLTSxPQUFTQSxFQUNWTixLQUFLMkIsaUJBQ0xyQixFQUFPZSxZQUFZLHdCQUF5QixDQUFFSyxlQUFnQjFCLEtBQUswQiIsImZpbGUiOiJwcmUuanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBnZXR0ZXIgfSk7XG4gXHRcdH1cbiBcdH07XG5cbiBcdC8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbiBcdFx0aWYodHlwZW9mIFN5bWJvbCAhPT0gJ3VuZGVmaW5lZCcgJiYgU3ltYm9sLnRvU3RyaW5nVGFnKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFN5bWJvbC50b1N0cmluZ1RhZywgeyB2YWx1ZTogJ01vZHVsZScgfSk7XG4gXHRcdH1cbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbiBcdH07XG5cbiBcdC8vIGNyZWF0ZSBhIGZha2UgbmFtZXNwYWNlIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDE6IHZhbHVlIGlzIGEgbW9kdWxlIGlkLCByZXF1aXJlIGl0XG4gXHQvLyBtb2RlICYgMjogbWVyZ2UgYWxsIHByb3BlcnRpZXMgb2YgdmFsdWUgaW50byB0aGUgbnNcbiBcdC8vIG1vZGUgJiA0OiByZXR1cm4gdmFsdWUgd2hlbiBhbHJlYWR5IG5zIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDh8MTogYmVoYXZlIGxpa2UgcmVxdWlyZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy50ID0gZnVuY3Rpb24odmFsdWUsIG1vZGUpIHtcbiBcdFx0aWYobW9kZSAmIDEpIHZhbHVlID0gX193ZWJwYWNrX3JlcXVpcmVfXyh2YWx1ZSk7XG4gXHRcdGlmKG1vZGUgJiA4KSByZXR1cm4gdmFsdWU7XG4gXHRcdGlmKChtb2RlICYgNCkgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJiB2YWx1ZS5fX2VzTW9kdWxlKSByZXR1cm4gdmFsdWU7XG4gXHRcdHZhciBucyA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18ucihucyk7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShucywgJ2RlZmF1bHQnLCB7IGVudW1lcmFibGU6IHRydWUsIHZhbHVlOiB2YWx1ZSB9KTtcbiBcdFx0aWYobW9kZSAmIDIgJiYgdHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSBmb3IodmFyIGtleSBpbiB2YWx1ZSkgX193ZWJwYWNrX3JlcXVpcmVfXy5kKG5zLCBrZXksIGZ1bmN0aW9uKGtleSkgeyByZXR1cm4gdmFsdWVba2V5XTsgfS5iaW5kKG51bGwsIGtleSkpO1xuIFx0XHRyZXR1cm4gbnM7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gMTEpO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmxldCBjYWNoZWRTZXR0aW5ncyA9IHVuZGVmaW5lZDtcbmZ1bmN0aW9uIGdldERhdGEoa2V5KSB7XG4gICAgY29uc3QgZWxlbWVudCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd2c2NvZGUtbWFya2Rvd24tcHJldmlldy1kYXRhJyk7XG4gICAgaWYgKGVsZW1lbnQpIHtcbiAgICAgICAgY29uc3QgZGF0YSA9IGVsZW1lbnQuZ2V0QXR0cmlidXRlKGtleSk7XG4gICAgICAgIGlmIChkYXRhKSB7XG4gICAgICAgICAgICByZXR1cm4gSlNPTi5wYXJzZShkYXRhKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICB0aHJvdyBuZXcgRXJyb3IoYENvdWxkIG5vdCBsb2FkIGRhdGEgZm9yICR7a2V5fWApO1xufVxuZXhwb3J0cy5nZXREYXRhID0gZ2V0RGF0YTtcbmZ1bmN0aW9uIGdldFNldHRpbmdzKCkge1xuICAgIGlmIChjYWNoZWRTZXR0aW5ncykge1xuICAgICAgICByZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG4gICAgfVxuICAgIGNhY2hlZFNldHRpbmdzID0gZ2V0RGF0YSgnZGF0YS1zZXR0aW5ncycpO1xuICAgIGlmIChjYWNoZWRTZXR0aW5ncykge1xuICAgICAgICByZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGxvYWQgc2V0dGluZ3MnKTtcbn1cbmV4cG9ydHMuZ2V0U2V0dGluZ3MgPSBnZXRTZXR0aW5ncztcbiIsIlwidXNlIHN0cmljdFwiO1xuLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHsgdmFsdWU6IHRydWUgfSk7XG5jb25zdCBjc3BfMSA9IHJlcXVpcmUoXCIuL2NzcFwiKTtcbmNvbnN0IGxvYWRpbmdfMSA9IHJlcXVpcmUoXCIuL2xvYWRpbmdcIik7XG53aW5kb3cuY3NwQWxlcnRlciA9IG5ldyBjc3BfMS5Dc3BBbGVydGVyKCk7XG53aW5kb3cuc3R5bGVMb2FkaW5nTW9uaXRvciA9IG5ldyBsb2FkaW5nXzEuU3R5bGVMb2FkaW5nTW9uaXRvcigpO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmNvbnN0IHNldHRpbmdzXzEgPSByZXF1aXJlKFwiLi9zZXR0aW5nc1wiKTtcbmNvbnN0IHN0cmluZ3NfMSA9IHJlcXVpcmUoXCIuL3N0cmluZ3NcIik7XG4vKipcbiAqIFNob3dzIGFuIGFsZXJ0IHdoZW4gdGhlcmUgaXMgYSBjb250ZW50IHNlY3VyaXR5IHBvbGljeSB2aW9sYXRpb24uXG4gKi9cbmNsYXNzIENzcEFsZXJ0ZXIge1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLmRpZFNob3cgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5kaWRIYXZlQ3NwV2FybmluZyA9IGZhbHNlO1xuICAgICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdzZWN1cml0eXBvbGljeXZpb2xhdGlvbicsICgpID0+IHtcbiAgICAgICAgICAgIHRoaXMub25Dc3BXYXJuaW5nKCk7XG4gICAgICAgIH0pO1xuICAgICAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsIChldmVudCkgPT4ge1xuICAgICAgICAgICAgaWYgKGV2ZW50ICYmIGV2ZW50LmRhdGEgJiYgZXZlbnQuZGF0YS5uYW1lID09PSAndnNjb2RlLWRpZC1ibG9jay1zdmcnKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5vbkNzcFdhcm5pbmcoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuICAgIHNldFBvc3Rlcihwb3N0ZXIpIHtcbiAgICAgICAgdGhpcy5tZXNzYWdpbmcgPSBwb3N0ZXI7XG4gICAgICAgIGlmICh0aGlzLmRpZEhhdmVDc3BXYXJuaW5nKSB7XG4gICAgICAgICAgICB0aGlzLnNob3dDc3BXYXJuaW5nKCk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgb25Dc3BXYXJuaW5nKCkge1xuICAgICAgICB0aGlzLmRpZEhhdmVDc3BXYXJuaW5nID0gdHJ1ZTtcbiAgICAgICAgdGhpcy5zaG93Q3NwV2FybmluZygpO1xuICAgIH1cbiAgICBzaG93Q3NwV2FybmluZygpIHtcbiAgICAgICAgY29uc3Qgc3RyaW5ncyA9IHN0cmluZ3NfMS5nZXRTdHJpbmdzKCk7XG4gICAgICAgIGNvbnN0IHNldHRpbmdzID0gc2V0dGluZ3NfMS5nZXRTZXR0aW5ncygpO1xuICAgICAgICBpZiAodGhpcy5kaWRTaG93IHx8IHNldHRpbmdzLmRpc2FibGVTZWN1cml0eVdhcm5pbmdzIHx8ICF0aGlzLm1lc3NhZ2luZykge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuZGlkU2hvdyA9IHRydWU7XG4gICAgICAgIGNvbnN0IG5vdGlmaWNhdGlvbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2EnKTtcbiAgICAgICAgbm90aWZpY2F0aW9uLmlubmVyVGV4dCA9IHN0cmluZ3MuY3NwQWxlcnRNZXNzYWdlVGV4dDtcbiAgICAgICAgbm90aWZpY2F0aW9uLnNldEF0dHJpYnV0ZSgnaWQnLCAnY29kZS1jc3Atd2FybmluZycpO1xuICAgICAgICBub3RpZmljYXRpb24uc2V0QXR0cmlidXRlKCd0aXRsZScsIHN0cmluZ3MuY3NwQWxlcnRNZXNzYWdlVGl0bGUpO1xuICAgICAgICBub3RpZmljYXRpb24uc2V0QXR0cmlidXRlKCdyb2xlJywgJ2J1dHRvbicpO1xuICAgICAgICBub3RpZmljYXRpb24uc2V0QXR0cmlidXRlKCdhcmlhLWxhYmVsJywgc3RyaW5ncy5jc3BBbGVydE1lc3NhZ2VMYWJlbCk7XG4gICAgICAgIG5vdGlmaWNhdGlvbi5vbmNsaWNrID0gKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5tZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ3Nob3dQcmV2aWV3U2VjdXJpdHlTZWxlY3RvcicsIHsgc291cmNlOiBzZXR0aW5ncy5zb3VyY2UgfSk7XG4gICAgICAgIH07XG4gICAgICAgIGRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQobm90aWZpY2F0aW9uKTtcbiAgICB9XG59XG5leHBvcnRzLkNzcEFsZXJ0ZXIgPSBDc3BBbGVydGVyO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmZ1bmN0aW9uIGdldFN0cmluZ3MoKSB7XG4gICAgY29uc3Qgc3RvcmUgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndnNjb2RlLW1hcmtkb3duLXByZXZpZXctZGF0YScpO1xuICAgIGlmIChzdG9yZSkge1xuICAgICAgICBjb25zdCBkYXRhID0gc3RvcmUuZ2V0QXR0cmlidXRlKCdkYXRhLXN0cmluZ3MnKTtcbiAgICAgICAgaWYgKGRhdGEpIHtcbiAgICAgICAgICAgIHJldHVybiBKU09OLnBhcnNlKGRhdGEpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGxvYWQgc3RyaW5ncycpO1xufVxuZXhwb3J0cy5nZXRTdHJpbmdzID0gZ2V0U3RyaW5ncztcbiIsIlwidXNlIHN0cmljdFwiO1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuY2xhc3MgU3R5bGVMb2FkaW5nTW9uaXRvciB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMudW5sb2FkZWRTdHlsZXMgPSBbXTtcbiAgICAgICAgdGhpcy5maW5pc2hlZExvYWRpbmcgPSBmYWxzZTtcbiAgICAgICAgY29uc3Qgb25TdHlsZUxvYWRFcnJvciA9IChldmVudCkgPT4ge1xuICAgICAgICAgICAgY29uc3Qgc291cmNlID0gZXZlbnQudGFyZ2V0LmRhdGFzZXQuc291cmNlO1xuICAgICAgICAgICAgdGhpcy51bmxvYWRlZFN0eWxlcy5wdXNoKHNvdXJjZSk7XG4gICAgICAgIH07XG4gICAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgKCkgPT4ge1xuICAgICAgICAgICAgZm9yIChjb25zdCBsaW5rIG9mIGRvY3VtZW50LmdldEVsZW1lbnRzQnlDbGFzc05hbWUoJ2NvZGUtdXNlci1zdHlsZScpKSB7XG4gICAgICAgICAgICAgICAgaWYgKGxpbmsuZGF0YXNldC5zb3VyY2UpIHtcbiAgICAgICAgICAgICAgICAgICAgbGluay5vbmVycm9yID0gb25TdHlsZUxvYWRFcnJvcjtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbG9hZCcsICgpID0+IHtcbiAgICAgICAgICAgIGlmICghdGhpcy51bmxvYWRlZFN0eWxlcy5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLmZpbmlzaGVkTG9hZGluZyA9IHRydWU7XG4gICAgICAgICAgICBpZiAodGhpcy5wb3N0ZXIpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnBvc3Rlci5wb3N0TWVzc2FnZSgncHJldmlld1N0eWxlTG9hZEVycm9yJywgeyB1bmxvYWRlZFN0eWxlczogdGhpcy51bmxvYWRlZFN0eWxlcyB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuICAgIHNldFBvc3Rlcihwb3N0ZXIpIHtcbiAgICAgICAgdGhpcy5wb3N0ZXIgPSBwb3N0ZXI7XG4gICAgICAgIGlmICh0aGlzLmZpbmlzaGVkTG9hZGluZykge1xuICAgICAgICAgICAgcG9zdGVyLnBvc3RNZXNzYWdlKCdwcmV2aWV3U3R5bGVMb2FkRXJyb3InLCB7IHVubG9hZGVkU3R5bGVzOiB0aGlzLnVubG9hZGVkU3R5bGVzIH0pO1xuICAgICAgICB9XG4gICAgfVxufVxuZXhwb3J0cy5TdHlsZUxvYWRpbmdNb25pdG9yID0gU3R5bGVMb2FkaW5nTW9uaXRvcjtcbiJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file +!function(e){var t={};function n(s){if(t[s])return t[s].exports;var o=t[s]={i:s,l:!1,exports:{}};return e[s].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,s){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:s})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var s=Object.create(null);if(n.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(s,o,function(t){return e[t]}.bind(null,o));return s},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=11)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let s=void 0;function o(e){const t=document.getElementById("vscode-markdown-preview-data");if(t){const n=t.getAttribute(e);if(n)return JSON.parse(n)}throw new Error(`Could not load data for ${e}`)}t.getData=o,t.getSettings=function(){if(s)return s;if(s=o("data-settings"))return s;throw new Error("Could not load settings")}},,,,,,,,,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const s=n(12),o=n(14);window.cspAlerter=new s.CspAlerter,window.styleLoadingMonitor=new o.StyleLoadingMonitor},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const s=n(0),o=n(13);t.CspAlerter=class{constructor(){this.didShow=!1,this.didHaveCspWarning=!1,document.addEventListener("securitypolicyviolation",()=>{this.onCspWarning()}),window.addEventListener("message",e=>{e&&e.data&&"vscode-did-block-svg"===e.data.name&&this.onCspWarning()})}setPoster(e){this.messaging=e,this.didHaveCspWarning&&this.showCspWarning()}onCspWarning(){this.didHaveCspWarning=!0,this.showCspWarning()}showCspWarning(){const e=o.getStrings(),t=s.getSettings();if(this.didShow||t.disableSecurityWarnings||!this.messaging)return;this.didShow=!0;const n=document.createElement("a");n.innerText=e.cspAlertMessageText,n.setAttribute("id","code-csp-warning"),n.setAttribute("title",e.cspAlertMessageTitle),n.setAttribute("role","button"),n.setAttribute("aria-label",e.cspAlertMessageLabel),n.onclick=()=>{this.messaging.postMessage("showPreviewSecuritySelector",{source:t.source})},document.body.appendChild(n)}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getStrings=function(){const e=document.getElementById("vscode-markdown-preview-data");if(e){const t=e.getAttribute("data-strings");if(t)return JSON.parse(t)}throw new Error("Could not load strings")}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.StyleLoadingMonitor=class{constructor(){this.unloadedStyles=[],this.finishedLoading=!1;const e=e=>{const t=e.target.dataset.source;this.unloadedStyles.push(t)};window.addEventListener("DOMContentLoaded",()=>{for(const t of document.getElementsByClassName("code-user-style"))t.dataset.source&&(t.onerror=e)}),window.addEventListener("load",()=>{this.unloadedStyles.length&&(this.finishedLoading=!0,this.poster&&this.poster.postMessage("previewStyleLoadError",{unloadedStyles:this.unloadedStyles}))})}setPoster(e){this.poster=e,this.finishedLoading&&e.postMessage("previewStyleLoadError",{unloadedStyles:this.unloadedStyles})}}}]); \ No newline at end of file diff --git a/extensions/markdown-language-features/media/preview-right-dark.svg b/extensions/markdown-language-features/media/preview-right-dark.svg deleted file mode 100644 index 1d59877196b..00000000000 --- a/extensions/markdown-language-features/media/preview-right-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/extensions/markdown-language-features/media/preview-right-light.svg b/extensions/markdown-language-features/media/preview-right-light.svg deleted file mode 100644 index 3f1152fc3cd..00000000000 --- a/extensions/markdown-language-features/media/preview-right-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/extensions/markdown-language-features/media/view-source-dark.svg b/extensions/markdown-language-features/media/view-source-dark.svg deleted file mode 100644 index ed302ae1398..00000000000 --- a/extensions/markdown-language-features/media/view-source-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/extensions/markdown-language-features/media/view-source-light.svg b/extensions/markdown-language-features/media/view-source-light.svg deleted file mode 100644 index 392a840c5ef..00000000000 --- a/extensions/markdown-language-features/media/view-source-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index fcefaef0a56..34d6902fa4f 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -12,6 +12,7 @@ "vscode": "^1.20.0" }, "main": "./out/extension", + "browser": "./dist/browser/extension", "categories": [ "Programming Languages" ], @@ -43,28 +44,19 @@ "command": "markdown.showPreviewToSide", "title": "%markdown.previewSide.title%", "category": "Markdown", - "icon": { - "light": "./media/preview-right-light.svg", - "dark": "./media/preview-right-dark.svg" - } + "icon": "$(open-preview)" }, { "command": "markdown.showLockedPreviewToSide", "title": "%markdown.showLockedPreviewToSide.title%", "category": "Markdown", - "icon": { - "light": "./media/preview-right-light.svg", - "dark": "./media/preview-right-dark.svg" - } + "icon": "$(open-preview)" }, { "command": "markdown.showSource", "title": "%markdown.showSource.title%", "category": "Markdown", - "icon": { - "light": "./media/view-source-light.svg", - "dark": "./media/view-source-dark.svg" - } + "icon": "$(go-to-file)" }, { "command": "markdown.showPreviewSecuritySelector", @@ -86,7 +78,7 @@ "editor/title": [ { "command": "markdown.showPreviewToSide", - "when": "editorLangId == markdown", + "when": "editorLangId == markdown && !notebookEditorFocused", "alt": "markdown.showPreview", "group": "navigation" }, @@ -122,23 +114,23 @@ { "command": "markdown.showPreview", "when": "resourceLangId == markdown", - "group": "navigation" + "group": "1_open" } ], "commandPalette": [ { "command": "markdown.showPreview", - "when": "editorLangId == markdown", + "when": "editorLangId == markdown && !notebookEditorFocused", "group": "navigation" }, { "command": "markdown.showPreviewToSide", - "when": "editorLangId == markdown", + "when": "editorLangId == markdown && !notebookEditorFocused", "group": "navigation" }, { "command": "markdown.showLockedPreviewToSide", - "when": "editorLangId == markdown", + "when": "editorLangId == markdown && !notebookEditorFocused", "group": "navigation" }, { @@ -148,7 +140,7 @@ }, { "command": "markdown.showPreviewSecuritySelector", - "when": "editorLangId == markdown" + "when": "editorLangId == markdown && !notebookEditorFocused" }, { "command": "markdown.showPreviewSecuritySelector", @@ -160,7 +152,7 @@ }, { "command": "markdown.preview.refresh", - "when": "editorLangId == markdown" + "when": "editorLangId == markdown && !notebookEditorFocused" }, { "command": "markdown.preview.refresh", @@ -173,13 +165,13 @@ "command": "markdown.showPreview", "key": "shift+ctrl+v", "mac": "shift+cmd+v", - "when": "editorLangId == markdown" + "when": "editorLangId == markdown && !notebookEditorFocused" }, { "command": "markdown.showPreviewToSide", "key": "ctrl+k v", "mac": "cmd+k v", - "when": "editorLangId == markdown" + "when": "editorLangId == markdown && !notebookEditorFocused" } ], "configuration": { @@ -210,7 +202,7 @@ }, "markdown.preview.fontFamily": { "type": "string", - "default": "system-ui, -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" }, @@ -327,7 +319,9 @@ "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", diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json index fbbab999519..6cf645a4103 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json @@ -1,7 +1,7 @@ { "displayName": "Markdown Language Features", "description": "Provides rich language support for Markdown.", - "markdown.preview.breaks.desc": "Sets how line-breaks are rendered in the markdown preview. Setting it to 'true' creates a
for every newline.", + "markdown.preview.breaks.desc": "Sets how line-breaks are rendered in the markdown preview. Setting it to 'true' creates a
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 f55545dd149..064c30ac969 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -18,7 +18,13 @@ const settings = getSettings(); const vscode = acquireVsCodeApi(); -const state = { ...vscode.getState(), ...getData('data-state') }; +const originalState = vscode.getState(); + +const state = { + ...(typeof originalState === 'object' ? originalState : {}), + ...getData('data-state') +}; + // Make sure to sync VS Code state here vscode.setState(state); @@ -157,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 }); diff --git a/extensions/markdown-language-features/src/commands/openDocumentLink.ts b/extensions/markdown-language-features/src/commands/openDocumentLink.ts index 6194dbe7226..c17dc17cb76 100644 --- a/extensions/markdown-language-features/src/commands/openDocumentLink.ts +++ b/extensions/markdown-language-features/src/commands/openDocumentLink.ts @@ -13,9 +13,9 @@ import { isMarkdownFile } from '../util/file'; export interface OpenDocumentLinkArgs { - readonly path: string; + readonly path: {}; readonly fragment: string; - readonly fromResource: any; + readonly fromResource: {}; } enum OpenMarkdownLinks { @@ -29,13 +29,22 @@ export class OpenDocumentLinkCommand implements Command { public static createCommandUri( fromResource: vscode.Uri, - path: string, + path: vscode.Uri, fragment: string, ): vscode.Uri { + const toJson = (uri: vscode.Uri) => { + return { + scheme: uri.scheme, + authority: uri.authority, + path: uri.path, + fragment: uri.fragment, + query: uri.query, + }; + }; return vscode.Uri.parse(`command:${OpenDocumentLinkCommand.id}?${encodeURIComponent(JSON.stringify({ - path: encodeURIComponent(path), + path: toJson(path), fragment, - fromResource: encodeURIComponent(fromResource.toString(true)), + fromResource: toJson(fromResource), }))}`); } @@ -43,26 +52,29 @@ export class OpenDocumentLinkCommand implements Command { private readonly engine: MarkdownEngine ) { } - public execute(args: OpenDocumentLinkArgs) { - const fromResource = vscode.Uri.parse(decodeURIComponent(args.fromResource)); - const targetPath = decodeURIComponent(args.path); - const column = this.getViewColumn(fromResource); - return this.tryOpen(targetPath, args, column).catch(() => { - if (targetPath && extname(targetPath) === '') { - return this.tryOpen(targetPath + '.md', args, column); - } - const targetResource = vscode.Uri.file(targetPath); - return Promise.resolve(undefined) - .then(() => vscode.commands.executeCommand('vscode.open', targetResource, column)) - .then(() => undefined); - }); + public async execute(args: OpenDocumentLinkArgs) { + return OpenDocumentLinkCommand.execute(this.engine, args); } - private async tryOpen(path: string, args: OpenDocumentLinkArgs, column: vscode.ViewColumn) { - const resource = vscode.Uri.file(path); + public static async execute(engine: MarkdownEngine, args: OpenDocumentLinkArgs) { + const fromResource = vscode.Uri.parse('').with(args.fromResource); + const targetResource = vscode.Uri.parse('').with(args.path); + const column = this.getViewColumn(fromResource); + try { + return await this.tryOpen(engine, targetResource, args, column); + } catch { + if (extname(targetResource.path) === '') { + return this.tryOpen(engine, targetResource.with({ path: targetResource.path + '.md' }), args, column); + } + await vscode.commands.executeCommand('vscode.open', targetResource, column); + return undefined; + } + } + + private static async tryOpen(engine: MarkdownEngine, resource: vscode.Uri, args: OpenDocumentLinkArgs, column: vscode.ViewColumn) { if (vscode.window.activeTextEditor && isMarkdownFile(vscode.window.activeTextEditor.document)) { - if (!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); } } @@ -73,10 +85,10 @@ export class OpenDocumentLinkCommand implements Command { 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('links.openLocation', OpenMarkdownLinks.currentGroup); switch (openLinks) { @@ -88,18 +100,22 @@ export class OpenDocumentLinkCommand implements Command { } } - private async tryRevealLine(editor: vscode.TextEditor, fragment?: string) { + private static async tryRevealLine(engine: MarkdownEngine, editor: vscode.TextEditor, fragment?: string) { if (editor && fragment) { - const toc = new TableOfContentsProvider(this.engine, editor.document); + const toc = new TableOfContentsProvider(engine, editor.document); const entry = await toc.lookup(fragment); if (entry) { - return editor.revealRange(new vscode.Range(entry.line, 0, entry.line, 0), vscode.TextEditorRevealType.AtTop); + const lineStart = new vscode.Range(entry.line, 0, entry.line, 0); + editor.selection = new vscode.Selection(lineStart.start, lineStart.end); + return editor.revealRange(lineStart, vscode.TextEditorRevealType.AtTop); } const lineNumberFragment = fragment.match(/^L(\d+)$/i); if (lineNumberFragment) { const line = +lineNumberFragment[1] - 1; if (!isNaN(line)) { - return editor.revealRange(new vscode.Range(line, 0, line, 0), vscode.TextEditorRevealType.AtTop); + const lineStart = new vscode.Range(line, 0, line, 0); + editor.selection = new vscode.Selection(lineStart.start, lineStart.end); + return editor.revealRange(lineStart, vscode.TextEditorRevealType.AtTop); } } } diff --git a/extensions/markdown-language-features/src/extension.ts b/extensions/markdown-language-features/src/extension.ts index 0bcc8473745..f5c0b4114a4 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)); diff --git a/extensions/markdown-language-features/src/features/documentLinkProvider.ts b/extensions/markdown-language-features/src/features/documentLinkProvider.ts index 441055e5e71..89237df66d6 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/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index 4a45264def6..f2fb800347c 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -3,20 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; import * as path from 'path'; - -import { Logger } from '../logger'; -import { MarkdownContentProvider } from './previewContentProvider'; -import { Disposable } from '../util/dispose'; - +import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; +import { OpenDocumentLinkCommand, resolveLinkToMarkdownFile } from '../commands/openDocumentLink'; +import { Logger } from '../logger'; +import { MarkdownContributionProvider } from '../markdownExtensions'; +import { Disposable } from '../util/dispose'; +import { isMarkdownFile } from '../util/file'; +import { normalizeResource, WebviewResourceProvider } from '../util/resources'; import { getVisibleLine, TopmostLineMonitor } from '../util/topmostLineMonitor'; import { MarkdownPreviewConfigurationManager } from './previewConfig'; -import { MarkdownContributionProvider } from '../markdownExtensions'; -import { isMarkdownFile } from '../util/file'; -import { resolveLinkToMarkdownFile } from '../commands/openDocumentLink'; -import { WebviewResourceProvider, normalizeResource } from '../util/resources'; +import { MarkdownContentProvider } from './previewContentProvider'; +import { MarkdownEngine } from '../markdownEngine'; + const localize = nls.loadMessageBundle(); interface WebviewMessage { @@ -123,6 +123,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { resource: vscode.Uri, startingScroll: StartingScrollLocation | undefined, private readonly delegate: MarkdownPreviewDelegate, + private readonly engine: MarkdownEngine, private readonly _contentProvider: MarkdownContentProvider, private readonly _previewConfigurations: MarkdownPreviewConfigurationManager, private readonly _logger: Logger, @@ -227,10 +228,10 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { } 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'), }; } @@ -407,7 +408,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { } } - vscode.commands.executeCommand('_markdown.openDocumentLink', { path: hrefPath, fragment, fromResource: this.resource }); + OpenDocumentLinkCommand.execute(this.engine, { path: hrefPath, fragment, fromResource: this.resource.toJSON() }); } //#region WebviewResourceProvider @@ -452,8 +453,9 @@ export class StaticMarkdownPreview extends Disposable implements ManagedMarkdown previewConfigurations: MarkdownPreviewConfigurationManager, logger: Logger, contributionProvider: MarkdownContributionProvider, + engine: MarkdownEngine, ): StaticMarkdownPreview { - return new StaticMarkdownPreview(webview, resource, contentProvider, previewConfigurations, logger, contributionProvider); + return new StaticMarkdownPreview(webview, resource, contentProvider, previewConfigurations, logger, contributionProvider, engine); } private readonly preview: MarkdownPreview; @@ -465,13 +467,14 @@ export class StaticMarkdownPreview extends Disposable implements ManagedMarkdown 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 */ } - }, contentProvider, _previewConfigurations, logger, contributionProvider)); + }, engine, contentProvider, _previewConfigurations, logger, contributionProvider)); this._register(this._webviewPanel.onDidDispose(() => { this.dispose(); @@ -548,9 +551,10 @@ export class DynamicMarkdownPreview extends Disposable implements ManagedMarkdow logger: Logger, topmostLineMonitor: TopmostLineMonitor, contributionProvider: MarkdownContributionProvider, + engine: MarkdownEngine, ): DynamicMarkdownPreview { return new DynamicMarkdownPreview(webview, input, - contentProvider, previewConfigurations, logger, topmostLineMonitor, contributionProvider); + contentProvider, previewConfigurations, logger, topmostLineMonitor, contributionProvider, engine); } public static create( @@ -560,7 +564,8 @@ export class DynamicMarkdownPreview extends Disposable implements ManagedMarkdow previewConfigurations: MarkdownPreviewConfigurationManager, logger: Logger, topmostLineMonitor: TopmostLineMonitor, - contributionProvider: MarkdownContributionProvider + contributionProvider: MarkdownContributionProvider, + engine: MarkdownEngine, ): DynamicMarkdownPreview { const webview = vscode.window.createWebviewPanel( DynamicMarkdownPreview.viewType, @@ -568,7 +573,7 @@ export class DynamicMarkdownPreview extends Disposable implements ManagedMarkdow previewColumn, { enableFindWidget: true, }); return new DynamicMarkdownPreview(webview, input, - contentProvider, previewConfigurations, logger, topmostLineMonitor, contributionProvider); + contentProvider, previewConfigurations, logger, topmostLineMonitor, contributionProvider, engine); } private constructor( @@ -579,6 +584,7 @@ export class DynamicMarkdownPreview extends Disposable implements ManagedMarkdow private readonly _logger: Logger, private readonly _topmostLineMonitor: TopmostLineMonitor, private readonly _contributionProvider: MarkdownContributionProvider, + private readonly _engine: MarkdownEngine, ) { super(); @@ -612,7 +618,12 @@ export class DynamicMarkdownPreview extends Disposable implements ManagedMarkdow })); this._register(vscode.window.onDidChangeActiveTextEditor(editor => { - if (editor && isMarkdownFile(editor.document) && !this._locked && !this._preview.isPreviewOf(editor.document.uri)) { + // 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); } @@ -724,6 +735,7 @@ export class DynamicMarkdownPreview extends Disposable implements ManagedMarkdow this.update(link, fragment ? new StartingScrollFragment(fragment) : undefined); } }, + this._engine, this._contentProvider, this._previewConfigurations, this._logger, diff --git a/extensions/markdown-language-features/src/features/previewContentProvider.ts b/extensions/markdown-language-features/src/features/previewContentProvider.ts index 0be66d1d630..a8a64e5270d 100644 --- a/extensions/markdown-language-features/src/features/previewContentProvider.ts +++ b/extensions/markdown-language-features/src/features/previewContentProvider.ts @@ -111,7 +111,7 @@ export class MarkdownContentProvider { private extensionResourcePath(resourceProvider: WebviewResourceProvider, mediaFile: string): string { const webviewResource = resourceProvider.asWebviewUri( - vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile)))); + vscode.Uri.joinPath(this.context.extensionUri, 'media', mediaFile)); return webviewResource.toString(); } @@ -132,7 +132,7 @@ export class MarkdownContentProvider { // Use a workspace relative path if there is a workspace const root = vscode.workspace.getWorkspaceFolder(resource); if (root) { - return resourceProvider.asWebviewUri(vscode.Uri.file(path.join(root.uri.fsPath, href))).toString(); + return resourceProvider.asWebviewUri(vscode.Uri.joinPath(root.uri, href)).toString(); } // Otherwise look relative to the markdown file @@ -152,9 +152,9 @@ export class MarkdownContentProvider { private getSettingsOverrideStyles(config: MarkdownPreviewConfiguration): string { return [ - config.fontFamily ? `--vscode-markdown-font-family: ${config.fontFamily};` : '', - isNaN(config.fontSize) ? '' : `--vscode-markdown-font-size: ${config.fontSize}px;`, - isNaN(config.lineHeight) ? '' : `--vscode-markdown-line-height: ${config.lineHeight};`, + config.fontFamily ? `--markdown-font-family: ${config.fontFamily};` : '', + isNaN(config.fontSize) ? '' : `--markdown-font-size: ${config.fontSize}px;`, + isNaN(config.lineHeight) ? '' : `--markdown-line-height: ${config.lineHeight};`, ].join(' '); } diff --git a/extensions/markdown-language-features/src/features/previewManager.ts b/extensions/markdown-language-features/src/features/previewManager.ts index 93a7f466a90..3c8ddd77446 100644 --- a/extensions/markdown-language-features/src/features/previewManager.ts +++ b/extensions/markdown-language-features/src/features/previewManager.ts @@ -5,10 +5,11 @@ import * as vscode from 'vscode'; import { Logger } from '../logger'; +import { MarkdownEngine } from '../markdownEngine'; import { MarkdownContributionProvider } from '../markdownExtensions'; -import { disposeAll, Disposable } from '../util/dispose'; +import { Disposable, disposeAll } from '../util/dispose'; import { TopmostLineMonitor } from '../util/topmostLineMonitor'; -import { DynamicMarkdownPreview, StaticMarkdownPreview, ManagedMarkdownPreview } from './preview'; +import { DynamicMarkdownPreview, ManagedMarkdownPreview, StaticMarkdownPreview } from './preview'; import { MarkdownPreviewConfigurationManager } from './previewConfig'; import { MarkdownContentProvider } from './previewContentProvider'; @@ -68,7 +69,8 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview public constructor( private readonly _contentProvider: MarkdownContentProvider, private readonly _logger: Logger, - private readonly _contributions: MarkdownContributionProvider + private readonly _contributions: MarkdownContributionProvider, + private readonly _engine: MarkdownEngine, ) { super(); this._register(vscode.window.registerWebviewPanelSerializer(DynamicMarkdownPreview.viewType, this)); @@ -145,7 +147,8 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview this._previewConfigurations, this._logger, this._topmostLineMonitor, - this._contributions); + this._contributions, + this._engine); this.registerDynamicPreview(preview); } @@ -160,7 +163,8 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview this._contentProvider, this._previewConfigurations, this._logger, - this._contributions); + this._contributions, + this._engine); this.registerStaticPreview(preview); } @@ -179,7 +183,8 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview this._previewConfigurations, this._logger, this._topmostLineMonitor, - this._contributions); + this._contributions, + this._engine); this.setPreviewActiveContext(true); this._activePreview = preview; diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index c500b692eaa..8bb509d4565 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as crypto from 'crypto'; -import * as path from 'path'; import { MarkdownIt, Token } from 'markdown-it'; +import * as path from 'path'; import * as vscode from 'vscode'; import { MarkdownContributionProvider as MarkdownContributionProvider } from './markdownExtensions'; import { Slugifier } from './slugify'; import { SkinnyTextDocument } from './tableOfContentsProvider'; -import { MarkdownFileExtensions, 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(); 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(); + return engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}); } @@ -197,9 +198,7 @@ export class MarkdownEngine { const src = token.attrGet('src'); if (src) { - const hash = crypto.createHash('sha256'); - hash.update(src); - const imgHash = hash.digest('hex'); + const imgHash = hash(src); token.attrSet('id', `image-hash-${imgHash}`); } @@ -242,7 +241,7 @@ export class MarkdownEngine { if (uri.path[0] === '/') { const root = vscode.workspace.getWorkspaceFolder(this.currentDocument!); if (root) { - const fileUri = vscode.Uri.file(path.join(root.uri.fsPath, uri.fsPath)); + const fileUri = vscode.Uri.joinPath(root.uri, uri.fsPath); uri = fileUri.with({ scheme: uri.scheme, fragment: uri.fragment, @@ -357,4 +356,3 @@ function normalizeHighlightLang(lang: string | undefined) { return lang; } } - diff --git a/extensions/markdown-language-features/src/markdownExtensions.ts b/extensions/markdown-language-features/src/markdownExtensions.ts index b03c5df80bd..387a41388c0 100644 --- a/extensions/markdown-language-features/src/markdownExtensions.ts +++ b/extensions/markdown-language-features/src/markdownExtensions.ts @@ -4,12 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as path from 'path'; -import { Disposable } from './util/dispose'; import * as arrays from './util/arrays'; +import { Disposable } from './util/dispose'; const resolveExtensionResource = (extension: vscode.Extension, 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, 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; @@ -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()); 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/test/documentLink.test.ts b/extensions/markdown-language-features/src/test/documentLink.test.ts new file mode 100644 index 00000000000..9b26d995f27 --- /dev/null +++ b/extensions/markdown-language-features/src/test/documentLink.test.ts @@ -0,0 +1,145 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import 'mocha'; +import * as vscode from 'vscode'; +import { joinLines } from './util'; + +const testFileA = workspaceFile('a.md'); + +function workspaceFile(...segments: string[]) { + return vscode.Uri.joinPath(vscode.workspace.workspaceFolders![0].uri, ...segments); +} + +async function getLinksForFile(file: vscode.Uri): Promise { + return (await vscode.commands.executeCommand('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 { + const document = await vscode.workspace.openTextDocument(file); + const editor = await vscode.window.showTextDocument(document); + await editor.edit(edit => { + edit.replace(new vscode.Range(0, 0, 1000, 0), contents); + }); +} + +async function executeLink(link: vscode.DocumentLink) { + const args = JSON.parse(decodeURIComponent(link.target!.query)); + await vscode.commands.executeCommand(link.target!.path, args); +} + diff --git a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts index ba6cfa2567f..7edbec869c8 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(); @@ -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 cb74a5b695f..e75be4abac5 100644 --- a/extensions/markdown-language-features/src/test/engine.ts +++ b/extensions/markdown-language-features/src/test/engine.ts @@ -10,7 +10,7 @@ import { githubSlugifier } from '../slugify'; import { Disposable } from '../util/dispose'; const emptyContributions = new class extends Disposable implements MarkdownContributionProvider { - readonly extensionPath = ''; + readonly extensionUri = vscode.Uri.file('/'); readonly contributions = MarkdownContributions.Empty; readonly onContributionsChanged = this._register(new vscode.EventEmitter()).event; }; diff --git a/extensions/markdown-language-features/src/test/test-fixtures/marker.txt b/extensions/markdown-language-features/src/test/test-fixtures/marker.txt deleted file mode 100644 index a4c9ad40736..00000000000 --- a/extensions/markdown-language-features/src/test/test-fixtures/marker.txt +++ /dev/null @@ -1 +0,0 @@ -DO NOT DELETE, USED BY INTEGRATION TESTS diff --git a/extensions/markdown-language-features/src/test/util.ts b/extensions/markdown-language-features/src/test/util.ts new file mode 100644 index 00000000000..d50e9ca5db2 --- /dev/null +++ b/extensions/markdown-language-features/src/test/util.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as os from 'os'; + +export const joinLines = (...args: string[]) => + args.join(os.platform() === 'win32' ? '\r\n' : '\n'); diff --git a/extensions/markdown-language-features/src/util/hash.ts b/extensions/markdown-language-features/src/util/hash.ts new file mode 100644 index 00000000000..b009808968d --- /dev/null +++ b/extensions/markdown-language-features/src/util/hash.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * Return a hash value for an object. + */ +export function hash(obj: any, hashVal = 0): number { + switch (typeof obj) { + case 'object': + if (obj === null) { + return numberHash(349, hashVal); + } else if (Array.isArray(obj)) { + return arrayHash(obj, hashVal); + } + return objectHash(obj, hashVal); + case 'string': + return stringHash(obj, hashVal); + case 'boolean': + return booleanHash(obj, hashVal); + case 'number': + return numberHash(obj, hashVal); + case 'undefined': + return 937 * 31; + default: + return numberHash(obj, 617); + } +} + +function numberHash(val: number, initialHashVal: number): number { + return (((initialHashVal << 5) - initialHashVal) + val) | 0; // hashVal * 31 + ch, keep as int32 +} + +function booleanHash(b: boolean, initialHashVal: number): number { + return numberHash(b ? 433 : 863, initialHashVal); +} + +function stringHash(s: string, hashVal: number) { + hashVal = numberHash(149417, hashVal); + for (let i = 0, length = s.length; i < length; i++) { + hashVal = numberHash(s.charCodeAt(i), hashVal); + } + return hashVal; +} + +function arrayHash(arr: any[], initialHashVal: number): number { + initialHashVal = numberHash(104579, initialHashVal); + return arr.reduce((hashVal, item) => hash(item, hashVal), initialHashVal); +} + +function objectHash(obj: any, initialHashVal: number): number { + initialHashVal = numberHash(181387, initialHashVal); + return Object.keys(obj).sort().reduce((hashVal, key) => { + hashVal = stringHash(key, hashVal); + return hash(obj[key], hashVal); + }, initialHashVal); +} diff --git a/extensions/markdown-language-features/src/util/links.ts b/extensions/markdown-language-features/src/util/links.ts index 58725d6f778..3545d03a54d 100644 --- a/extensions/markdown-language-features/src/util/links.ts +++ b/extensions/markdown-language-features/src/util/links.ts @@ -9,6 +9,7 @@ export const Schemes = { http: 'http:', https: 'https:', file: 'file:', + untitled: 'untitled', mailto: 'mailto:', data: 'data:', vscode: 'vscode:', diff --git a/extensions/markdown-language-features/test-workspace/a.md b/extensions/markdown-language-features/test-workspace/a.md new file mode 100644 index 00000000000..9d70918067b --- /dev/null +++ b/extensions/markdown-language-features/test-workspace/a.md @@ -0,0 +1,4 @@ +[b](b) +[b](b.md) +[b](./b.md) +[b](/b.md) diff --git a/extensions/markdown-language-features/test-workspace/b.md b/extensions/markdown-language-features/test-workspace/b.md new file mode 100644 index 00000000000..730f53ee6ce --- /dev/null +++ b/extensions/markdown-language-features/test-workspace/b.md @@ -0,0 +1,3 @@ +# b + +[](./a) \ No newline at end of file diff --git a/extensions/markdown-language-features/test-workspace/sub/c.md b/extensions/markdown-language-features/test-workspace/sub/c.md new file mode 100644 index 00000000000..f5c9bec43ac --- /dev/null +++ b/extensions/markdown-language-features/test-workspace/sub/c.md @@ -0,0 +1,6 @@ +# First +# Second + +[b](/b.md) +[b](../b.md) +[b](./../b.md) diff --git a/src/vs/base/test/node/encoding/fixtures/empty.txt b/extensions/markdown-language-features/test-workspace/sub/d.md similarity index 100% rename from src/vs/base/test/node/encoding/fixtures/empty.txt rename to extensions/markdown-language-features/test-workspace/sub/d.md diff --git a/extensions/markdown-language-features/webpack.config.js b/extensions/markdown-language-features/webpack.config.js index 58996c3eb7d..865444125fe 100644 --- a/extensions/markdown-language-features/webpack.config.js +++ b/extensions/markdown-language-features/webpack.config.js @@ -21,9 +21,8 @@ module.exports = { resolve: { extensions: ['.tsx', '.ts', '.js'] }, - devtool: 'inline-source-map', output: { filename: '[name].js', path: path.resolve(__dirname, 'media') } -}; \ No newline at end of file +}; diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index 23cd1744005..008969d899e 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -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" @@ -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" diff --git a/extensions/merge-conflict/.vscodeignore b/extensions/merge-conflict/.vscodeignore index 36e8b0714fa..f071cfb7c71 100644 --- a/extensions/merge-conflict/.vscodeignore +++ b/extensions/merge-conflict/.vscodeignore @@ -2,4 +2,5 @@ src/** tsconfig.json out/** extension.webpack.config.js -yarn.lock \ No newline at end of file +extension-browser.webpack.config.js +yarn.lock diff --git a/extensions/merge-conflict/extension-browser.webpack.config.js b/extensions/merge-conflict/extension-browser.webpack.config.js new file mode 100644 index 00000000000..e4171bed927 --- /dev/null +++ b/extensions/merge-conflict/extension-browser.webpack.config.js @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withBrowserDefaults = require('../shared.webpack.config').browser; + +module.exports = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/mergeConflictMain.ts' + }, + output: { + filename: 'mergeConflictMain.js' + } +}); diff --git a/extensions/merge-conflict/extension.webpack.config.js b/extensions/merge-conflict/extension.webpack.config.js index 45600607fc5..7a04ca98e97 100644 --- a/extensions/merge-conflict/extension.webpack.config.js +++ b/extensions/merge-conflict/extension.webpack.config.js @@ -12,6 +12,9 @@ const withDefaults = require('../shared.webpack.config'); module.exports = withDefaults({ context: __dirname, entry: { - extension: './src/extension.ts' - } + extension: './src/mergeConflictMain.ts' + }, + output: { + filename: 'mergeConflictMain.js' + }, }); diff --git a/extensions/merge-conflict/package.json b/extensions/merge-conflict/package.json index 815a412492a..d438b96ca61 100644 --- a/extensions/merge-conflict/package.json +++ b/extensions/merge-conflict/package.json @@ -16,7 +16,8 @@ "activationEvents": [ "*" ], - "main": "./out/extension", + "main": "./out/mergeConflictMain", + "browser": "./dist/browser/mergeConflictMain", "scripts": { "compile": "gulp compile-extension:merge-conflict", "watch": "gulp watch-extension:merge-conflict" diff --git a/extensions/merge-conflict/src/commandHandler.ts b/extensions/merge-conflict/src/commandHandler.ts index f79c6650af0..ca45b9a16f8 100644 --- a/extensions/merge-conflict/src/commandHandler.ts +++ b/extensions/merge-conflict/src/commandHandler.ts @@ -5,7 +5,6 @@ import * as vscode from 'vscode'; import * as interfaces from './interfaces'; import ContentProvider from './contentProvider'; -import * as path from 'path'; import { loadMessageBundle } from 'vscode-nls'; const localize = loadMessageBundle(); @@ -86,7 +85,6 @@ export default class CommandHandler implements vscode.Disposable { } async compare(editor: vscode.TextEditor, conflict: interfaces.IDocumentMergeConflict | null) { - const fileName = path.basename(editor.document.uri.fsPath); // No conflict, command executed from command palette if (!conflict) { @@ -134,6 +132,8 @@ export default class CommandHandler implements vscode.Disposable { conflict.range.start.line - mergeConflictLineOffsets, conflict.range.start.character ); + const docPath = editor.document.uri.path; + const fileName = docPath.substring(docPath.lastIndexOf('/') + 1); // avoid NodeJS path to keep browser webpack small const title = localize('compareChangesTitle', '{0}: Current Changes ⟷ Incoming Changes', fileName); const mergeConflictConfig = vscode.workspace.getConfiguration('merge-conflict'); const openToTheSide = mergeConflictConfig.get('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/extension.ts b/extensions/merge-conflict/src/mergeConflictMain.ts similarity index 100% rename from extensions/merge-conflict/src/extension.ts rename to extensions/merge-conflict/src/mergeConflictMain.ts diff --git a/extensions/vscode-account/.vscodeignore b/extensions/microsoft-authentication/.vscodeignore similarity index 58% rename from extensions/vscode-account/.vscodeignore rename to extensions/microsoft-authentication/.vscodeignore index ed3f9d37c1f..46f23a20dba 100644 --- a/extensions/vscode-account/.vscodeignore +++ b/extensions/microsoft-authentication/.vscodeignore @@ -1,10 +1,14 @@ .vscode/** .vscode-test/** out/test/** +out/** +extension.webpack.config.js +extension-browser.webpack.config.js +yarn.lock src/** .gitignore vsc-extension-quickstart.md **/tsconfig.json **/tslint.json **/*.map -**/*.ts \ No newline at end of file +**/*.ts diff --git a/extensions/microsoft-authentication/extension-browser.webpack.config.js b/extensions/microsoft-authentication/extension-browser.webpack.config.js new file mode 100644 index 00000000000..d2f4d9ee94b --- /dev/null +++ b/extensions/microsoft-authentication/extension-browser.webpack.config.js @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const path = require('path'); +const withBrowserDefaults = require('../shared.webpack.config').browser; + +module.exports = withBrowserDefaults({ + context: __dirname, + node: false, + entry: { + extension: './src/extension.ts', + }, + externals: { + 'keytar': 'commonjs keytar' + }, + resolve: { + alias: { + './env/node': path.resolve(__dirname, 'src/env/browser'), + './authServer': path.resolve(__dirname, 'src/env/browser/authServer'), + 'buffer': path.resolve(__dirname, 'node_modules/buffer/index.js'), + 'node-fetch': path.resolve(__dirname, 'node_modules/node-fetch/browser.js'), + 'randombytes': path.resolve(__dirname, 'node_modules/randombytes/browser.js'), + 'stream': path.resolve(__dirname, 'node_modules/stream/index.js'), + 'uuid': path.resolve(__dirname, 'node_modules/uuid/dist/esm-browser/index.js') + } + } +}); diff --git a/extensions/vscode-account/extension.webpack.config.js b/extensions/microsoft-authentication/extension.webpack.config.js similarity index 95% rename from extensions/vscode-account/extension.webpack.config.js rename to extensions/microsoft-authentication/extension.webpack.config.js index a513ac5c3b5..e18229a8e17 100644 --- a/extensions/vscode-account/extension.webpack.config.js +++ b/extensions/microsoft-authentication/extension.webpack.config.js @@ -7,6 +7,7 @@ 'use strict'; +const path = require('path'); const withDefaults = require('../shared.webpack.config'); module.exports = withDefaults({ diff --git a/extensions/vscode-account/media/auth.css b/extensions/microsoft-authentication/media/auth.css similarity index 100% rename from extensions/vscode-account/media/auth.css rename to extensions/microsoft-authentication/media/auth.css diff --git a/extensions/vscode-account/media/auth.html b/extensions/microsoft-authentication/media/auth.html similarity index 100% rename from extensions/vscode-account/media/auth.html rename to extensions/microsoft-authentication/media/auth.html diff --git a/extensions/microsoft-authentication/package.json b/extensions/microsoft-authentication/package.json new file mode 100644 index 00000000000..81ed5c32e4f --- /dev/null +++ b/extensions/microsoft-authentication/package.json @@ -0,0 +1,52 @@ +{ + "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" + ], + "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/vscode-account/package.nls.json b/extensions/microsoft-authentication/package.nls.json similarity index 100% rename from extensions/vscode-account/package.nls.json rename to extensions/microsoft-authentication/package.nls.json diff --git a/extensions/vscode-account/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts similarity index 68% rename from extensions/vscode-account/src/AADHelper.ts rename to extensions/microsoft-authentication/src/AADHelper.ts index ebab64b377c..035c5af7350 100644 --- a/extensions/vscode-account/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -3,15 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as crypto from 'crypto'; -import * as https from 'https'; +import * as randomBytes from 'randombytes'; import * as querystring from 'querystring'; import * as vscode from 'vscode'; -import * as uuid from 'uuid'; 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/'; @@ -21,12 +23,12 @@ const tenant = 'organizations'; interface IToken { accessToken?: string; // When unable to refresh due to network problems, the access token becomes undefined - expiresIn?: string; // How long access token is valid, in seconds + expiresIn?: number; // How long access token is valid, in seconds expiresAt?: number; // UNIX epoch time at which token will expire refreshToken: string; account: { - displayName: string; + label: string; id: string; }; scope: string; @@ -48,11 +50,21 @@ interface IStoredSession { refreshToken: string; scope: string; // Scopes are alphabetized and joined with a space account: { - displayName: string, + 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('='); @@ -61,7 +73,7 @@ function parseQuery(uri: vscode.Uri) { }, {}); } -export const onDidChangeSessions = new vscode.EventEmitter(); +export const onDidChangeSessions = new vscode.EventEmitter(); export const REFRESH_NETWORK_FAILURE = 'Network failure'; @@ -82,14 +94,15 @@ export class AzureActiveDirectoryService { } public async initialize(): Promise { - // TODO remove, temporary migration - await keychain.migrateToken(); - 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) { @@ -100,7 +113,7 @@ export class AzureActiveDirectoryService { accessToken: undefined, refreshToken: session.refreshToken, account: { - displayName: session.account.displayName, + label: session.account.label ?? session.account.displayName!, id: session.account.id }, scope: session.scope, @@ -151,7 +164,7 @@ export class AzureActiveDirectoryService { 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) { + if (!matchesExisting && session.refreshToken) { try { await this.refreshToken(session.refreshToken, session.scope, session.id); addedIds.push(session.id); @@ -204,10 +217,11 @@ export class AzureActiveDirectoryService { }, 1000 * 30); } - private convertToSession(token: IToken): vscode.AuthenticationSession { + private async convertToSession(token: IToken): Promise { + const resolvedToken = await this.resolveAccessToken(token); return { id: token.sessionId, - getAccessToken: () => this.resolveAccessToken(token), + accessToken: resolvedToken, account: token.account, scopes: token.scope.split(' ') }; @@ -243,81 +257,89 @@ export class AzureActiveDirectoryService { } } - get sessions(): vscode.AuthenticationSession[] { - return this._tokens.map(token => this.convertToSession(token)); + get sessions(): Promise { + return Promise.all(this._tokens.map(token => this.convertToSession(token))); } - public async login(scope: string): Promise { + public async login(scope: string): Promise { Logger.info('Logging in...'); - - if (vscode.env.uiKind === vscode.UIKind.Web) { - await this.loginWithoutLocalServer(scope); - return; + 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.'); } - const nonce = crypto.randomBytes(16).toString('base64'); - const { server, redirectPromise, codePromise } = createServer(nonce); - - let token: IToken | undefined; - try { - const port = await startServer(server); - vscode.env.openExternal(vscode.Uri.parse(`http://localhost:${port}/signin?nonce=${encodeURIComponent(nonce)}`)); - - const redirectReq = await redirectPromise; - if ('err' in redirectReq) { - const { err, res } = redirectReq; - res.writeHead(302, { Location: `/?error=${encodeURIComponent(err && err.message || 'Unknown error')}` }); - res.end(); - throw err; + return new Promise(async (resolve, reject) => { + if (vscode.env.uiKind === vscode.UIKind.Web) { + resolve(this.loginWithoutLocalServer(scope)); + return; } - const host = redirectReq.req.headers.host || ''; - const updatedPortStr = (/^[^:]+:(\d+)$/.exec(Array.isArray(host) ? host[0] : host) || [])[1]; - const updatedPort = updatedPortStr ? parseInt(updatedPortStr, 10) : port; - - const state = `${updatedPort},${encodeURIComponent(nonce)}`; - - const codeVerifier = toBase64UrlEncoding(crypto.randomBytes(32).toString('base64')); - const codeChallenge = toBase64UrlEncoding(crypto.createHash('sha256').update(codeVerifier).digest('base64')); - const loginUrl = `${loginEndpointUrl}${tenant}/oauth2/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; + const nonce = randomBytes(16).toString('base64'); + const { server, redirectPromise, codePromise } = createServer(nonce); + let token: IToken | undefined; 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: '/' }); - res.end(); - } catch (err) { - res.writeHead(302, { Location: `/?error=${encodeURIComponent(err && err.message || 'Unknown error')}` }); - res.end(); - throw new Error(err.message); - } - } catch (e) { - Logger.error(e.message); + const port = await startServer(server); + vscode.env.openExternal(vscode.Uri.parse(`http://localhost:${port}/signin?nonce=${encodeURIComponent(nonce)}`)); - // 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); + 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); } - throw new Error(e.message); - } finally { - setTimeout(() => { - server.close(); - }, 5000); - } + }); } private getCallbackEnvironment(callbackUri: vscode.Uri): string { - if (callbackUri.authority.endsWith('.workspaces.github.com')) { + if (callbackUri.authority.endsWith('.workspaces.github.com') || callbackUri.authority.endsWith('.github.dev')) { return `${callbackUri.authority},`; } @@ -333,22 +355,22 @@ export class AzureActiveDirectoryService { } } - private async loginWithoutLocalServer(scope: string): Promise { - const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.vscode-account`)); - const nonce = crypto.randomBytes(16).toString('base64'); + 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(crypto.randomBytes(32).toString('base64')); - const codeChallenge = toBase64UrlEncoding(crypto.createHash('sha256').update(codeVerifier).digest('base64')); + 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: IToken) => void, reject) => { + const timeoutPromise = new Promise((_: (value: vscode.AuthenticationSession) => void, reject) => { const wait = setTimeout(() => { clearTimeout(wait); reject('Login timed out.'); @@ -358,9 +380,9 @@ export class AzureActiveDirectoryService { return Promise.race([this.handleCodeResponse(state, codeVerifier, scope), timeoutPromise]); } - private async handleCodeResponse(state: string, codeVerifier: string, scope: string) { + private async handleCodeResponse(state: string, codeVerifier: string, scope: string): Promise { let uriEventListener: vscode.Disposable; - return new Promise((resolve: (value: IToken) => void, reject) => { + return new Promise((resolve: (value: vscode.AuthenticationSession) => void, reject) => { uriEventListener = this._uriHandler.event(async (uri: vscode.Uri) => { try { const query = parseQuery(uri); @@ -374,7 +396,8 @@ export class AzureActiveDirectoryService { const token = await this.exchangeCodeForToken(code, codeVerifier, scope); this.setToken(token, scope); - resolve(token); + const session = await this.convertToSession(token); + resolve(session); } catch (err) { reject(err); } @@ -414,14 +437,13 @@ export class AzureActiveDirectoryService { onDidChangeSessions.fire({ added: [], removed: [token.sessionId], changed: [] }); } } - }, 1000 * (parseInt(token.expiresIn) - 30))); + }, 1000 * (token.expiresIn - 30))); } this.storeTokenData(); } - private getTokenFromResponse(buffer: Buffer[], scope: string, existingId?: string): IToken { - const json = JSON.parse(Buffer.concat(buffer).toString()); + private getTokenFromResponse(json: ITokenResponse, scope: string, existingId?: string): IToken { const claims = this.getTokenClaims(json.access_token); return { expiresIn: json.expires_in, @@ -431,67 +453,52 @@ export class AzureActiveDirectoryService { scope, sessionId: existingId || `${claims.tid}/${(claims.oid || (claims.altsecid || '' + claims.ipd || ''))}/${uuid()}`, account: { - displayName: claims.email || claims.unique_name || 'user@example.com', + label: claims.email || claims.unique_name || 'user@example.com', id: `${claims.tid}/${(claims.oid || (claims.altsecid || '' + claims.ipd || ''))}` } }; } private async exchangeCodeForToken(code: string, codeVerifier: string, scope: string): Promise { - return new Promise((resolve: (value: IToken) => void, reject) => { - Logger.info('Exchanging login code for token'); - try { - const postData = querystring.stringify({ - grant_type: 'authorization_code', - code: code, - client_id: clientId, - scope: scope, - code_verifier: codeVerifier, - redirect_uri: redirectUrl - }); + 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 tokenUrl = vscode.Uri.parse(`${loginEndpointUrl}${tenant}/oauth2/v2.0/token`); + const proxyEndpoints: { [providerId: string]: string } | undefined = await vscode.commands.executeCommand('workbench.getCodeExchangeProxyEndpoints'); + const endpoint = proxyEndpoints && proxyEndpoints['microsoft'] || `${loginEndpointUrl}${tenant}/oauth2/v2.0/token`; - const post = https.request({ - host: tokenUrl.authority, - path: tokenUrl.path, - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': postData.length - } - }, result => { - const buffer: Buffer[] = []; - result.on('data', (chunk: Buffer) => { - buffer.push(chunk); - }); - result.on('end', () => { - if (result.statusCode === 200) { - Logger.info('Exchanging login code for token success'); - resolve(this.getTokenFromResponse(buffer, scope)); - } else { - Logger.error('Exchanging login code for token failed'); - reject(new Error('Unable to login.')); - } - }); - }); + const result = await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': postData.length.toString() + }, + body: postData + }); - post.write(postData); - - post.end(); - post.on('error', err => { - reject(err); - }); - - } catch (e) { - Logger.error(e.message); - reject(e); + 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 { - return new Promise((resolve: (value: IToken) => void, reject) => { + try { Logger.info('Refreshing token...'); const postData = querystring.stringify({ refresh_token: refreshToken, @@ -500,40 +507,29 @@ export class AzureActiveDirectoryService { scope: scope }); - const post = https.request({ - host: 'login.microsoftonline.com', - path: `/${tenant}/oauth2/v2.0/token`, + 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 - } - }, result => { - const buffer: Buffer[] = []; - result.on('data', (chunk: Buffer) => { - buffer.push(chunk); - }); - result.on('end', async () => { - if (result.statusCode === 200) { - const token = this.getTokenFromResponse(buffer, scope, sessionId); - this.setToken(token, scope); - Logger.info('Token refresh success'); - resolve(token); - } else { - Logger.error('Refreshing token failed'); - reject(new Error('Refreshing token failed.')); - } - }); + 'Content-Length': postData.length.toString() + }, + body: postData }); - post.write(postData); - - post.end(); - post.on('error', err => { - Logger.error(err.message); - reject(new Error(REFRESH_NETWORK_FAILURE)); - }); - }); + 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 e; + } } private clearSessionTimeout(sessionId: string): void { diff --git a/extensions/vscode-account/src/authServer.ts b/extensions/microsoft-authentication/src/authServer.ts similarity index 79% rename from extensions/vscode-account/src/authServer.ts rename to extensions/microsoft-authentication/src/authServer.ts index c75247e0a78..caae72ba52c 100644 --- a/extensions/vscode-account/src/authServer.ts +++ b/extensions/microsoft-authentication/src/authServer.ts @@ -6,7 +6,6 @@ import * as http from 'http'; import * as url from 'url'; import * as fs from 'fs'; -import * as net from 'net'; import * as path from 'path'; interface Deferred { @@ -14,58 +13,17 @@ interface Deferred { reject: (reason: any) => void; } -const _typeof = { - number: 'number', - string: 'string', - undefined: 'undefined', - object: 'object', - function: 'function' -}; - -/** - * @returns whether the provided parameter is undefined. - */ -export function isUndefined(obj: any): obj is undefined { - return typeof (obj) === _typeof.undefined; -} - -/** - * @returns whether the provided parameter is undefined or null. - */ -export function isUndefinedOrNull(obj: any): obj is undefined | null { - return isUndefined(obj) || obj === null; -} - /** * Asserts that the argument passed in is neither undefined nor null. */ -export function assertIsDefined(arg: T | null | undefined): T { - if (isUndefinedOrNull(arg)) { +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 function createTerminateServer(server: http.Server) { - const sockets: Record = {}; - let socketCount = 0; - server.on('connection', socket => { - const id = socketCount++; - sockets[id] = socket; - socket.on('close', () => { - delete sockets[id]; - }); - }); - return async () => { - const result = new Promise(resolve => server.close(resolve)); - for (const id in sockets) { - sockets[id].destroy(); - } - return result; - }; -} - export async function startServer(server: http.Server): Promise { let portTimer: NodeJS.Timer; 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 00000000000..60b53c713a8 --- /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 00000000000..369a1533cf9 --- /dev/null +++ b/extensions/microsoft-authentication/src/env/browser/sha256.ts @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +export async function sha256(s: string | Uint8Array): Promise { + const createHash = require('sha.js'); + return createHash('sha256').update(s).digest('base64'); +} diff --git a/extensions/microsoft-authentication/src/env/node/sha256.ts b/extensions/microsoft-authentication/src/env/node/sha256.ts new file mode 100644 index 00000000000..691f7eedaf9 --- /dev/null +++ b/extensions/microsoft-authentication/src/env/node/sha256.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +export async function sha256(s: string | Uint8Array): Promise { + return (require('crypto')).createHash('sha256').update(s).digest('base64'); +} diff --git a/extensions/vscode-account/src/extension.ts b/extensions/microsoft-authentication/src/extension.ts similarity index 84% rename from extensions/vscode-account/src/extension.ts rename to extensions/microsoft-authentication/src/extension.ts index 0f0b33b96ec..102a0863c0e 100644 --- a/extensions/vscode-account/src/extension.ts +++ b/extensions/microsoft-authentication/src/extension.ts @@ -19,27 +19,42 @@ export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.authentication.registerAuthenticationProvider({ id: 'microsoft', - displayName: 'Microsoft', + label: 'Microsoft', + supportsMultipleAccounts: true, onDidChangeSessions: onDidChangeSessions.event, getSessions: () => Promise.resolve(loginService.sessions), login: async (scopes: string[]) => { try { + /* __GDPR__ + "login" : { } + */ telemetryReporter.sendTelemetryEvent('login'); - await loginService.login(scopes.sort().join(' ')); - const session = loginService.sessions[loginService.sessions.length - 1]; + + const session = await loginService.login(scopes.sort().join(' ')); onDidChangeSessions.fire({ added: [session.id], removed: [], changed: [] }); - return loginService.sessions[0]!; + 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'); } } diff --git a/extensions/vscode-account/src/keychain.ts b/extensions/microsoft-authentication/src/keychain.ts similarity index 85% rename from extensions/vscode-account/src/keychain.ts rename to extensions/microsoft-authentication/src/keychain.ts index afda7035521..bc0fcc55381 100644 --- a/extensions/vscode-account/src/keychain.ts +++ b/extensions/microsoft-authentication/src/keychain.ts @@ -43,22 +43,6 @@ export class Keychain { this.keytar = keytar; } - // TODO remove, temporary migration - async migrateToken(): Promise { - const oldServiceId = `${vscode.env.uriScheme}-vscode.login`; - try { - const data = await this.keytar.getPassword(oldServiceId, ACCOUNT_ID); - if (data) { - Logger.info('Migrating token...'); - this.setToken(data); - await this.keytar.deletePassword(oldServiceId, ACCOUNT_ID); - Logger.info('Migration successful'); - } - } catch (e) { - Logger.error(`Migrating token failed: ${e}`); - } - } - async setToken(token: string): Promise { try { diff --git a/extensions/vscode-account/src/logger.ts b/extensions/microsoft-authentication/src/logger.ts similarity index 100% rename from extensions/vscode-account/src/logger.ts rename to extensions/microsoft-authentication/src/logger.ts diff --git a/extensions/vscode-account/src/typings/refs.d.ts b/extensions/microsoft-authentication/src/typings/refs.d.ts similarity index 100% rename from extensions/vscode-account/src/typings/refs.d.ts rename to extensions/microsoft-authentication/src/typings/refs.d.ts diff --git a/extensions/vscode-account/src/utils.ts b/extensions/microsoft-authentication/src/utils.ts similarity index 100% rename from extensions/vscode-account/src/utils.ts rename to extensions/microsoft-authentication/src/utils.ts diff --git a/extensions/microsoft-authentication/tsconfig.json b/extensions/microsoft-authentication/tsconfig.json new file mode 100644 index 00000000000..86c288d3c6e --- /dev/null +++ b/extensions/microsoft-authentication/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "experimentalDecorators": true, + "forceConsistentCasingInFileNames": true, + "lib": ["es2019"], + "module": "commonjs", + "moduleResolution": "node", + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "noUnusedLocals": false, + "outDir": "dist", + "resolveJsonModule": true, + "rootDir": "src", + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "target": "es2019" + }, + "exclude": ["node_modules"] +} diff --git a/extensions/vscode-account/yarn.lock b/extensions/microsoft-authentication/yarn.lock similarity index 69% rename from extensions/vscode-account/yarn.lock rename to extensions/microsoft-authentication/yarn.lock index a09801af2c7..df970ce40d8 100644 --- a/extensions/vscode-account/yarn.lock +++ b/extensions/microsoft-authentication/yarn.lock @@ -2,22 +2,6 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" - integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== - dependencies: - "@babel/highlight" "^7.8.3" - -"@babel/highlight@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" - integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg== - dependencies: - chalk "^2.0.0" - esutils "^2.0.2" - js-tokens "^4.0.0" - "@types/keytar@^4.0.1": version "4.4.2" resolved "https://registry.yarnpkg.com/@types/keytar/-/keytar-4.4.2.tgz#49ef917d6cbb4f19241c0ab50cd35097b5729b32" @@ -25,15 +9,42 @@ dependencies: keytar "*" +"@types/node-fetch@^2.5.7": + version "2.5.7" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c" + integrity sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + +"@types/node@*": + version "14.0.23" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.23.tgz#676fa0883450ed9da0bb24156213636290892806" + integrity sha512-Z4U8yDAl5TFkmYsZdFPdjeMa57NOvnaf1tljHzhouaPEp7LCj2JKkejpI1ODviIAQuW4CcQmxkQ77rnLsOOoKw== + "@types/node@^10.12.21": version "10.17.13" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.13.tgz#ccebcdb990bd6139cd16e84c39dc2fb1023ca90c" integrity sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg== -"@types/uuid@^3.4.6": - version "3.4.8" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.8.tgz#4ba887fcef88bd9a7515ca2de336d691e3e18318" - integrity sha512-zHWce3allXWSmRx6/AGXKCtSOA7JjeWd2L3t4aHfysNk8mouQnWCocveaT7a4IEIlPVHp81jzlnknqTgCjCLXA== +"@types/randombytes@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/randombytes/-/randombytes-2.0.0.tgz#0087ff5e60ae68023b9bc4398b406fea7ad18304" + integrity sha512-bz8PhAVlwN72vqefzxa14DKNT8jK/mV66CSjwdVQM/k3Th3EPKfUtdMniwZgMedQTFuywAsfjnZsg+pEnltaMA== + dependencies: + "@types/node" "*" + +"@types/sha.js@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@types/sha.js/-/sha.js-2.4.0.tgz#bce682ef860b40f419d024fa08600c3b8d24bb01" + integrity sha512-amxKgPy6WJTKuw8mpUwjX2BSxuBtBmZfRwIUDIuPJKNwGN8CWDli8JTg5ONTWOtcTkHIstvT7oAhhYXqEjStHQ== + dependencies: + "@types/node" "*" + +"@types/uuid@^8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.0.0.tgz#165aae4819ad2174a17476dbe66feebd549556c0" + integrity sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw== ansi-regex@^2.0.0: version "2.1.1" @@ -45,13 +56,6 @@ ansi-regex@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - applicationinsights@1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" @@ -74,17 +78,15 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +base64-js@^1.0.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== bl@^3.0.0: version "3.0.0" @@ -93,27 +95,13 @@ bl@^3.0.0: dependencies: readable-stream "^3.0.1" -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== +buffer@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" + integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -builtin-modules@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= - -chalk@^2.0.0, chalk@^2.3.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" + base64-js "^1.0.2" + ieee754 "^1.1.4" chownr@^1.1.1: version "1.1.3" @@ -125,27 +113,12 @@ code-point-at@^1.0.0: resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: - color-name "1.1.3" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -commander@^2.12.1: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + delayed-stream "~1.0.0" console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" @@ -169,6 +142,11 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" @@ -191,10 +169,10 @@ diagnostic-channel@0.2.0: dependencies: semver "^5.3.0" -diff@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +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" @@ -203,36 +181,25 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - expand-template@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== +form-data@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" + integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -252,37 +219,17 @@ github-from-package@0.0.0: resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= -glob@^7.1.1: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" +ieee754@^1.1.4: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== -inherits@2, inherits@^2.0.3, inherits@~2.0.3: +inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -309,19 +256,6 @@ isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^3.13.1: - version "3.13.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" - integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - keytar@*: version "5.0.0" resolved "https://registry.yarnpkg.com/keytar/-/keytar-5.0.0.tgz#c89b6b7a4608fd7af633d9f8474b1a7eb97cbe6f" @@ -330,18 +264,23 @@ keytar@*: nan "2.14.0" prebuild-install "5.3.3" +mime-db@1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== + +mime-types@^2.1.12: + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== + dependencies: + mime-db "1.44.0" + mimic-response@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.0.0.tgz#996a51c60adf12cb8a87d7fb8ef24c2f3d5ebb46" integrity sha512-8ilDoEapqA4uQ3TwS0jakGONKXVJqpy+RpM+3b7pLdOjghCrEiGp9SRkFbUHAmZW9vdnrENWHjaweIoTIJExSQ== -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" @@ -376,6 +315,11 @@ node-abi@^2.7.0: dependencies: semver "^5.4.1" +node-fetch@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" + integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== + noop-logger@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" @@ -401,23 +345,13 @@ object-assign@^4.1.0: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= -once@^1.3.0, once@^1.3.1, once@^1.4.0: +once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== - prebuild-install@5.3.3: version "5.3.3" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.3.tgz#ef4052baac60d465f5ba6bf003c9c1de79b9da8e" @@ -452,6 +386,12 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" +"randombytes@github:rmacfarlane/randombytes#b28d4ecee46262801ea09f15fa1f1513a05c5971": + version "2.1.0" + resolved "https://codeload.github.com/rmacfarlane/randombytes/tar.gz/b28d4ecee46262801ea09f15fa1f1513a05c5971" + dependencies: + safe-buffer "^5.1.0" + rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -484,18 +424,16 @@ readable-stream@^3.0.1, readable-stream@^3.1.1: string_decoder "^1.1.1" util-deprecate "^1.0.1" -resolve@^1.3.2: - version "1.14.2" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.2.tgz#dbf31d0fa98b1f29aa5169783b9c290cb865fea2" - integrity sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ== - dependencies: - path-parse "^1.0.6" - safe-buffer@^5.0.1, safe-buffer@~5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== +safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -511,6 +449,14 @@ set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= +sha.js@2.4.11: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + signal-exit@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -530,10 +476,12 @@ simple-get@^3.0.3: once "^1.3.1" simple-concat "^1.0.0" -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= +stream@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stream/-/stream-0.0.2.tgz#7f5363f057f6592c5595f00bc80a27f5cec1f0ef" + integrity sha1-f1Nj8Ff2WSxVlfALyAon9c7B8O8= + dependencies: + emitter-component "^1.1.1" string-width@^1.0.1: version "1.0.2" @@ -585,13 +533,6 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - tar-fs@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.0.tgz#677700fc0c8b337a78bee3623fdc235f21d7afad" @@ -613,37 +554,6 @@ tar-stream@^2.0.0: inherits "^2.0.3" readable-stream "^3.1.1" -tslib@^1.8.0, tslib@^1.8.1: - version "1.10.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" - integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== - -tslint@^5.12.1: - version "5.20.1" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.20.1.tgz#e401e8aeda0152bc44dd07e614034f3f80c67b7d" - integrity sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg== - dependencies: - "@babel/code-frame" "^7.0.0" - builtin-modules "^1.1.1" - chalk "^2.3.0" - commander "^2.12.1" - diff "^4.0.1" - glob "^7.1.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" - mkdirp "^0.5.1" - resolve "^1.3.2" - semver "^5.3.0" - tslib "^1.8.0" - tsutils "^2.29.0" - -tsutils@^2.29.0: - version "2.29.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" - integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== - dependencies: - tslib "^1.8.1" - tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -661,10 +571,10 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -uuid@^3.3.3: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +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" diff --git a/extensions/npm/.vscodeignore b/extensions/npm/.vscodeignore index 27bb76ffd24..7700b94ebb0 100644 --- a/extensions/npm/.vscodeignore +++ b/extensions/npm/.vscodeignore @@ -3,4 +3,5 @@ out/** tsconfig.json .vscode/** extension.webpack.config.js -yarn.lock \ No newline at end of file +extension-browser.webpack.config.js +yarn.lock diff --git a/extensions/npm/README.md b/extensions/npm/README.md index f3707f85d32..144cdd27538 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 00000000000..b0c70c96d48 --- /dev/null +++ b/extensions/npm/extension-browser.webpack.config.js @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withBrowserDefaults = require('../shared.webpack.config').browser; + +module.exports = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/npmBrowserMain.ts' + }, + output: { + filename: 'npmBrowserMain.js' + }, + node: { + 'child_process': 'empty' + } +}); diff --git a/extensions/npm/extension.webpack.config.js b/extensions/npm/extension.webpack.config.js index 56a1589f460..1c6d9493e33 100644 --- a/extensions/npm/extension.webpack.config.js +++ b/extensions/npm/extension.webpack.config.js @@ -14,12 +14,10 @@ const withDefaults = require('../shared.webpack.config'); module.exports = withDefaults({ context: __dirname, entry: { - extension: './src/main.ts', + extension: './src/npmMain.ts', }, output: { - filename: 'main.js', - path: path.join(__dirname, 'dist'), - libraryTarget: 'commonjs', + filename: 'npmMain.js', }, resolve: { mainFields: ['module', 'main'], diff --git a/extensions/npm/images/code.svg b/extensions/npm/images/code.svg new file mode 100644 index 00000000000..f776259aad0 --- /dev/null +++ b/extensions/npm/images/code.svg @@ -0,0 +1,3 @@ + + + diff --git a/extensions/npm/package.json b/extensions/npm/package.json index 265dffcacf7..a77b48647cd 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -20,14 +20,15 @@ "dependencies": { "jsonc-parser": "^2.2.1", "minimatch": "^3.0.4", - "request-light": "^0.2.5", + "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,13 +40,13 @@ "languages": [ { "id": "ignore", - "filenames": [ + "extensions": [ ".npmignore" ] }, { "id": "properties", - "filenames": [ + "extensions": [ ".npmrc" ] } @@ -55,7 +56,9 @@ { "id": "npm", "name": "%view.name%", - "when": "npm:showScriptExplorer || config.npm.enableScriptExplorer" + "when": "npm:showScriptExplorer", + "icon": "images/code.svg", + "visibility": "hidden" } ] }, @@ -126,7 +129,7 @@ "editor/context": [ { "command": "npm.runSelectedScript", - "when": "resourceFilename == 'package.json'", + "when": "resourceFilename == 'package.json' && resourceScheme == file", "group": "navigation@+1" } ], @@ -180,12 +183,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", @@ -213,7 +216,8 @@ "type": "string", "enum": [ "npm", - "yarn" + "yarn", + "pnpm" ], "default": "npm", "description": "%config.npm.packageManager%" @@ -265,11 +269,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": [ @@ -287,7 +291,8 @@ "type": "string", "description": "%taskdef.path%" } - } + }, + "when": "shellExecutionSupported" } ] } diff --git a/extensions/npm/src/features/bowerJSONContribution.ts b/extensions/npm/src/features/bowerJSONContribution.ts index 705e40d34e7..cd648732fc7 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(); @@ -181,13 +180,15 @@ export class BowerJSONContribution implements IJSONContribution { }); } - public getInfoContribution(_resource: string, location: Location): Thenable | null { + public getInfoContribution(_resource: string, location: Location): Thenable | null { if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']))) { const pack = location.path[location.path.length - 1]; if (typeof pack === 'string') { return this.getInfo(pack).then(documentation => { if (documentation) { - return [textToMarkedString(documentation)]; + const str = new MarkdownString(); + str.appendText(documentation); + return [str]; } return null; }); diff --git a/extensions/npm/src/features/jsonContributions.ts b/extensions/npm/src/features/jsonContributions.ts index 4a255baf823..3873b2dc31a 100644 --- a/extensions/npm/src/features/jsonContributions.ts +++ b/extensions/npm/src/features/jsonContributions.ts @@ -30,8 +30,8 @@ export interface IJSONContribution { 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(); diff --git a/extensions/npm/src/features/packageJSONContribution.ts b/extensions/npm/src/features/packageJSONContribution.ts index 4789886891c..135b632071c 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 { @@ -191,23 +188,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 +216,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,21 +245,11 @@ 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; - } - - return []; - } - private async fetchPackageInfo(pack: string): Promise { - let info = await this.npmView(pack); + let info: ViewPackageInfo | undefined; + if (this.canRunNPM) { + info = await this.npmView(pack); + } if (!info) { info = await this.npmjsView(pack); } @@ -259,14 +259,14 @@ export class PackageJSONContribution implements IJSONContribution { 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 +280,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) { @@ -308,9 +306,9 @@ export class PackageJSONContribution implements IJSONContribution { 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 +337,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 +347,11 @@ interface SearchPackageInfo { name: string; description?: string; version?: string; + links?: { homepage?: string; }; } interface ViewPackageInfo { description: string; - distTagsLatest?: string; + version?: string; homepage?: string; } diff --git a/extensions/npm/src/npmBrowserMain.ts b/extensions/npm/src/npmBrowserMain.ts new file mode 100644 index 00000000000..96cfe579505 --- /dev/null +++ b/extensions/npm/src/npmBrowserMain.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as httpRequest from 'request-light'; +import * as vscode from 'vscode'; +import { addJSONProviders } from './features/jsonContributions'; + +export async function activate(context: vscode.ExtensionContext): Promise { + context.subscriptions.push(addJSONProviders(httpRequest.xhr, false)); +} + +export function deactivate(): void { +} diff --git a/extensions/npm/src/main.ts b/extensions/npm/src/npmMain.ts similarity index 86% rename from extensions/npm/src/main.ts rename to extensions/npm/src/npmMain.ts index b79638ed1f0..764be6ea0fc 100644 --- a/extensions/npm/src/main.ts +++ b/extensions/npm/src/npmMain.ts @@ -14,13 +14,19 @@ import { invalidateHoverScriptsCache, NpmScriptHoverProvider } from './scriptHov 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(); + 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) { @@ -32,15 +38,12 @@ export async function activate(context: vscode.ExtensionContext): Promise treeDataProvider.refresh(); } } - }); - context.subscriptions.push(d); + })); + + registerTaskProvider(context); + registerHoverProvider(context); - d = vscode.workspace.onDidChangeTextDocument((e) => { - invalidateHoverScriptsCache(e.document); - }); - context.subscriptions.push(d); context.subscriptions.push(vscode.commands.registerCommand('npm.runSelectedScript', runSelectedScript)); - context.subscriptions.push(addJSONProviders(httpRequest.xhr)); if (await hasPackageJson()) { vscode.commands.executeCommand('setContext', 'npm:showScriptExplorer', true); @@ -49,6 +52,13 @@ export async function activate(context: vscode.ExtensionContext): Promise 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() { diff --git a/extensions/npm/src/scriptHover.ts b/extensions/npm/src/scriptHover.ts index 46992af2088..aa803dbc1d4 100644 --- a/extensions/npm/src/scriptHover.ts +++ b/extensions/npm/src/scriptHover.ts @@ -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 { diff --git a/extensions/npm/yarn.lock b/extensions/npm/yarn.lock index 9b8a23c9264..5e85de9297e 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== @@ -96,16 +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" + 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/package.json b/extensions/package.json index 8744f4dff90..665553eeeae 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.9.2-insiders.20200509" + "typescript": "3.9.7" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/perl/test/colorize-results/test2_pl.json b/extensions/perl/test/colorize-results/test2_pl.json index 99e22463982..6fc00b0b064 100644 --- a/extensions/perl/test/colorize-results/test2_pl.json +++ b/extensions/perl/test/colorize-results/test2_pl.json @@ -1302,7 +1302,7 @@ "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" @@ -1324,7 +1324,7 @@ "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" @@ -1346,7 +1346,7 @@ "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" @@ -1775,7 +1775,7 @@ "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" diff --git a/extensions/perl/test/colorize-results/test_pl.json b/extensions/perl/test/colorize-results/test_pl.json index ecfd9660ad8..e47c0f95e84 100644 --- a/extensions/perl/test/colorize-results/test_pl.json +++ b/extensions/perl/test/colorize-results/test_pl.json @@ -389,7 +389,7 @@ "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" @@ -411,7 +411,7 @@ "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" @@ -433,7 +433,7 @@ "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" @@ -455,7 +455,7 @@ "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" @@ -477,7 +477,7 @@ "t": "source.perl string.regexp.find.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string.regexp: #D16969", "light_vs": "string.regexp: #811F3F", "hc_black": "constant.character: #569CD6" @@ -675,7 +675,7 @@ "t": "source.perl string.quoted.double.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" @@ -950,7 +950,7 @@ "t": "source.perl string.quoted.double.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" @@ -1445,7 +1445,7 @@ "t": "source.perl string.quoted.double.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" @@ -2281,7 +2281,7 @@ "t": "source.perl string.quoted.double.perl constant.character.escape.perl", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" diff --git a/extensions/php-language-features/package.json b/extensions/php-language-features/package.json index d301ca476ea..2856782f547 100644 --- a/extensions/php-language-features/package.json +++ b/extensions/php-language-features/package.json @@ -39,7 +39,7 @@ ], "default": null, "description": "%configuration.validate.executablePath%", - "scope": "machine" + "scope": "machine-overridable" }, "php.validate.run": { "type": "string", diff --git a/extensions/php/test/colorize-results/issue-28354_php.json b/extensions/php/test/colorize-results/issue-28354_php.json index 27983076fa7..eb36ede3a4e 100644 --- a/extensions/php/test/colorize-results/issue-28354_php.json +++ b/extensions/php/test/colorize-results/issue-28354_php.json @@ -279,7 +279,7 @@ "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php string.quoted.single.php constant.character.escape.php", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" @@ -378,7 +378,7 @@ "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php string.quoted.single.php constant.character.escape.php", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" diff --git a/extensions/postinstall.js b/extensions/postinstall.js index e31009ac6b8..da4fa3e9d04 100644 --- a/extensions/postinstall.js +++ b/extensions/postinstall.js @@ -2,29 +2,58 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +// @ts-check 'use strict'; const fs = require('fs'); const path = require('path'); -const toDelete = new Set(['tsc.js', 'tsserverlibrary.js', 'typescriptServices.js']); +const rimraf = require('rimraf'); -const root = path.join(__dirname, 'node_modules', 'typescript', 'lib'); -for (let name of fs.readdirSync(root)) { - if (name === 'lib.d.ts' || name.match(/^lib\..*\.d\.ts$/) || name === 'protocol.d.ts') { - continue; - } - if (name === 'typescript.js' || name === 'typescript.d.ts') { - // used by html and extension editing - continue; - } +const root = path.join(__dirname, 'node_modules', 'typescript'); - if (toDelete.has(name) || name.match(/\.d\.ts$/)) { - try { - fs.unlinkSync(path.join(root, name)); - console.log(`removed '${path.join(root, name)}'`); - } catch (e) { - console.warn(e); +function processRoot() { + const toKeep = new Set([ + 'lib', + 'package.json', + ]); + for (const name of fs.readdirSync(root)) { + if (!toKeep.has(name)) { + const filePath = path.join(root, name); + console.log(`Removed ${filePath}`); + rimraf.sync(filePath); } } -} \ No newline at end of file +} + +function processLib() { + const toDelete = new Set([ + 'tsc.js', + 'tsserverlibrary.js', + 'typescriptServices.js', + ]); + + const libRoot = path.join(root, 'lib'); + + for (const name of fs.readdirSync(libRoot)) { + if (name === 'lib.d.ts' || name.match(/^lib\..*\.d\.ts$/) || name === 'protocol.d.ts') { + continue; + } + if (name === 'typescript.js' || name === 'typescript.d.ts') { + // used by html and extension editing + continue; + } + + if (toDelete.has(name) || name.match(/\.d\.ts$/)) { + try { + fs.unlinkSync(path.join(libRoot, name)); + console.log(`removed '${path.join(libRoot, name)}'`); + } catch (e) { + console.warn(e); + } + } + } +} + +processRoot(); +processLib(); diff --git a/extensions/pug/package.json b/extensions/pug/package.json index 19049be77af..7565f0a0119 100644 --- a/extensions/pug/package.json +++ b/extensions/pug/package.json @@ -12,7 +12,7 @@ "contributes": { "languages": [{ "id": "jade", - "extensions": [ ".jade", ".pug" ], + "extensions": [ ".pug", ".jade" ], "aliases": [ "Pug", "Jade", "jade" ], "configuration": "./language-configuration.json" }], @@ -22,4 +22,4 @@ "path": "./syntaxes/pug.tmLanguage.json" }] } -} \ No newline at end of file +} diff --git a/extensions/python/.vscodeignore b/extensions/python/.vscodeignore index 4d5a14fc91e..b5c95d0fb64 100644 --- a/extensions/python/.vscodeignore +++ b/extensions/python/.vscodeignore @@ -1,6 +1,8 @@ test/** src/** +out/** tsconfig.json extension.webpack.config.js +extension-browser.webpack.config.js cgmanifest.json -.vscode \ No newline at end of file +.vscode diff --git a/extensions/python/cgmanifest.json b/extensions/python/cgmanifest.json index 37a21b2de54..6b1df10ed8f 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": "c9b3409deb69acec31bbf7913830e93a046b30cc" + "commitHash": "b4b2e6eb16fee36aea0788bf0aa1853c25f7d276" } }, "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 00000000000..9ffa8d167e9 --- /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 1a7a65b2dee..e7c75aa4ea1 100644 --- a/extensions/python/package.json +++ b/extensions/python/package.json @@ -8,7 +8,8 @@ "engines": { "vscode": "*" }, "activationEvents": ["onLanguage:python"], "main": "./out/pythonMain", - "extensionKind": [ "ui", "workspace" ], + "browser": "./dist/browser/pythonMain", + "extensionKind": [ "ui", "workspace", "web" ], "contributes": { "languages": [{ "id": "python", diff --git a/extensions/python/syntaxes/MagicPython.tmLanguage.json b/extensions/python/syntaxes/MagicPython.tmLanguage.json index eb3c96990b9..b8822299e63 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/2ca894f270f92e2bc8f09a2ebdcd482fbb3b1074", + "version": "https://github.com/MagicStack/MagicPython/commit/b4b2e6eb16fee36aea0788bf0aa1853c25f7d276", "name": "MagicPython", "scopeName": "source.python", "patterns": [ @@ -1229,13 +1229,13 @@ }, "generator": { "comment": "Match \"for ... in\" construct used in generators and for loops to\ncorrectly identify the \"in\" as a control flow keyword.\n", - "begin": "for", + "begin": "\\bfor\\b", "beginCaptures": { "0": { "name": "keyword.control.flow.python" } }, - "end": "in", + "end": "\\bin\\b", "endCaptures": { "0": { "name": "keyword.control.flow.python" 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 5038c289809..3eb0f85a2a1 100644 --- a/extensions/python/test/colorize-results/test-freeze-56377_py.json +++ b/extensions/python/test/colorize-results/test-freeze-56377_py.json @@ -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/ruby/test/colorize-results/test_rb.json b/extensions/ruby/test/colorize-results/test_rb.json index 8679ef23528..c5122336ae1 100644 --- a/extensions/ruby/test/colorize-results/test_rb.json +++ b/extensions/ruby/test/colorize-results/test_rb.json @@ -432,8 +432,8 @@ "c": "Models", "t": "source.ruby variable.other.constant.ruby", "r": { - "dark_plus": "variable.other.constant: #51B6C4", - "light_plus": "variable.other.constant: #328267", + "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.other.constant: #51B6C4", - "light_plus": "variable.other.constant: #328267", + "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.other.constant: #51B6C4", - "light_plus": "variable.other.constant: #328267", + "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.other.constant: #51B6C4", - "light_plus": "variable.other.constant: #328267", + "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.other.constant: #51B6C4", - "light_plus": "variable.other.constant: #328267", + "dark_plus": "variable.other.constant: #4FC1FF", + "light_plus": "variable.other.constant: #0070C1", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", "hc_black": "variable: #9CDCFE" @@ -2269,8 +2269,8 @@ "c": "MAVERICKS_PKG_PATH", "t": "source.ruby string.interpolated.ruby meta.embedded.line.ruby source.ruby variable.other.constant.ruby", "r": { - "dark_plus": "variable.other.constant: #51B6C4", - "light_plus": "variable.other.constant: #328267", + "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" diff --git a/extensions/scss/test/colorize-results/test_scss.json b/extensions/scss/test/colorize-results/test_scss.json index a95d72d9e16..5841c3b8e3f 100644 --- a/extensions/scss/test/colorize-results/test_scss.json +++ b/extensions/scss/test/colorize-results/test_scss.json @@ -20761,7 +20761,7 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss string.quoted.single.scss constant.character.escape.scss", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" @@ -20882,7 +20882,7 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-value.scss string.quoted.single.scss constant.character.escape.scss", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" @@ -20959,7 +20959,7 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-value.scss string.quoted.double.scss constant.character.escape.scss", "r": { "dark_plus": "constant.character.escape: #D7BA7D", - "light_plus": "constant.character.escape: #FF0000", + "light_plus": "constant.character.escape: #EE0000", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", "hc_black": "constant.character: #569CD6" @@ -21020,4 +21020,4 @@ "hc_black": "comment: #7CA668" } } -] +] \ No newline at end of file diff --git a/extensions/search-result/.vscodeignore b/extensions/search-result/.vscodeignore new file mode 100644 index 00000000000..da3d2763686 --- /dev/null +++ b/extensions/search-result/.vscodeignore @@ -0,0 +1,6 @@ +src/** +out/** +tsconfig.json +extension.webpack.config.js +extension-browser.webpack.config.js +yarn.lock diff --git a/extensions/search-result/extension-browser.webpack.config.js b/extensions/search-result/extension-browser.webpack.config.js new file mode 100644 index 00000000000..10c0a19e356 --- /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/package.json b/extensions/search-result/package.json index ffb5321ae4d..7b43da243cf 100644 --- a/extensions/search-result/package.json +++ b/extensions/search-result/package.json @@ -12,6 +12,7 @@ "Programming Languages" ], "main": "./out/extension.js", + "browser": "./dist/extension.js", "activationEvents": [ "*" ], diff --git a/extensions/search-result/src/extension.ts b/extensions/search-result/src/extension.ts index abb85dac201..3abaa97de56 100644 --- a/extensions/search-result/src/extension.ts +++ b/extensions/search-result/src/extension.ts @@ -126,6 +126,8 @@ function relativePathToUri(path: string, resultsUri: vscode.Uri): vscode.Uri | u 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); @@ -133,17 +135,18 @@ function relativePathToUri(path: string, resultsUri: vscode.Uri): vscode.Uri | u const [, workspaceName, workspacePath] = multiRootFormattedPath; const folder = vscode.workspace.workspaceFolders.filter(wf => wf.name === workspaceName)[0]; if (folder) { - return vscode.Uri.file(pathUtils.join(folder.uri.fsPath, workspacePath)); + return uriFromFolderWithPath(folder, workspacePath); } } - else if (vscode.workspace.workspaceFolders.length === 1) { - return vscode.Uri.file(pathUtils.join(vscode.workspace.workspaceFolders[0].uri.fsPath, path)); + return uriFromFolderWithPath(vscode.workspace.workspaceFolders[0], path); } else if (resultsUri.scheme !== 'untitled') { // We're in a multi-root workspace, but the path is not multi-root formatted // Possibly a saved search from a single root session. Try checking if the search result document's URI is in a current workspace folder. const prefixMatch = vscode.workspace.workspaceFolders.filter(wf => resultsUri.toString().startsWith(wf.uri.toString()))[0]; - if (prefixMatch) { return vscode.Uri.file(pathUtils.join(prefixMatch.uri.fsPath, path)); } + if (prefixMatch) { + return uriFromFolderWithPath(prefixMatch, path); + } } } diff --git a/extensions/search-result/syntaxes/generateTMLanguage.js b/extensions/search-result/syntaxes/generateTMLanguage.js index 23e0615a718..eac084ddbc1 100644 --- a/extensions/search-result/syntaxes/generateTMLanguage.js +++ b/extensions/search-result/syntaxes/generateTMLanguage.js @@ -12,7 +12,7 @@ const mappings = [ ['css', 'source.css'], ['dart', 'source.dart'], ['diff', 'source.diff'], - ['dockerfile', 'source.dockerfile', '(?:dockerfile|Dockerfile)'], + ['dockerfile', 'source.dockerfile', '(?:dockerfile|Dockerfile|containerfile|Containerfile)'], ['fs', 'source.fsharp'], ['go', 'source.go'], ['groovy', 'source.groovy'], diff --git a/extensions/shared.webpack.config.js b/extensions/shared.webpack.config.js index f9d93777c60..ab6a40c6b80 100644 --- a/extensions/shared.webpack.config.js +++ b/extensions/shared.webpack.config.js @@ -13,8 +13,9 @@ const fs = require('fs'); const merge = require('merge-options'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const { NLSBundlePlugin } = require('vscode-nls-dev/lib/webpack-bundler'); +const { DefinePlugin } = require('webpack'); -module.exports = function withDefaults(/**@type WebpackConfig*/extConfig) { +function withNodeDefaults(/**@type WebpackConfig*/extConfig) { // Need to find the top-most `package.json` file const folderName = path.relative(__dirname, extConfig.context).split(/[\\\/]/)[0]; const pkgPath = path.join(__dirname, folderName, 'package.json'); @@ -77,4 +78,67 @@ module.exports = function withDefaults(/**@type WebpackConfig*/extConfig) { }; return merge(defaultConfig, extConfig); -}; +} + + +function withBrowserDefaults(/**@type WebpackConfig*/extConfig) { + /** @type WebpackConfig */ + let defaultConfig = { + mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production') + target: 'webworker', // extensions run in a webworker context + resolve: { + mainFields: ['module', 'main'], + extensions: ['.ts', '.js'], // support ts-files and js-files + alias: { + 'vscode-nls': path.resolve(__dirname, '../build/polyfills/vscode-nls.js'), + 'vscode-extension-telemetry': path.resolve(__dirname, '../build/polyfills/vscode-extension-telemetry.js') + } + }, + module: { + rules: [{ + test: /\.ts$/, + exclude: /node_modules/, + use: [{ + // configure TypeScript loader: + // * enable sources maps for end-to-end source maps + loader: 'ts-loader', + options: { + compilerOptions: { + 'sourceMap': true, + } + } + }] + }] + }, + externals: { + 'vscode': 'commonjs vscode', // ignored because it doesn't exist + }, + performance: { + hints: false + }, + output: { + // all output goes into `dist`. + // packaging depends on that and this must always be like it + filename: '[name].js', + path: path.join(extConfig.context, 'dist', 'browser'), + libraryTarget: 'commonjs', + }, + // yes, really source maps + devtool: 'source-map', + plugins: [ + // @ts-expect-error + new CopyWebpackPlugin([ + { from: 'src', to: '.', ignore: ['**/test/**', '*.ts'] } + ]), + new DefinePlugin({ WEBWORKER: JSON.stringify(true) }) + ] + }; + + return merge(defaultConfig, extConfig); +} + + +module.exports = withNodeDefaults; +module.exports.node = withNodeDefaults; +module.exports.browser = withBrowserDefaults; + diff --git a/extensions/shellscript/package.json b/extensions/shellscript/package.json index 207ae27aa1b..50c3ffdb799 100644 --- a/extensions/shellscript/package.json +++ b/extensions/shellscript/package.json @@ -13,13 +13,45 @@ "languages": [{ "id": "shellscript", "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"], + "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" - ], - "firstLine": "^#!.*\\b(bash|zsh|sh|tcsh|ksh|ash|qsh|csh).*|^#\\s*-\\*-[^*]*mode:\\s*shell-script[^*]*-\\*-", + "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 56b576cdc79..3ef90a09d14 100644 --- a/extensions/sql/cgmanifest.json +++ b/extensions/sql/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Microsoft/vscode-mssql", "repositoryUrl": "https://github.com/Microsoft/vscode-mssql", - "commitHash": "37a22725186b5b481b2882a78c7b9fe024c13946" + "commitHash": "750d30dc48c4c0317b63bb5f1ed3e71487bb84a1" } }, "license": "MIT", diff --git a/extensions/sql/syntaxes/sql.tmLanguage.json b/extensions/sql/syntaxes/sql.tmLanguage.json index 778c35071fa..bc3b533bcb9 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/37a22725186b5b481b2882a78c7b9fe024c13946", + "version": "https://github.com/Microsoft/vscode-mssql/commit/750d30dc48c4c0317b63bb5f1ed3e71487bb84a1", "name": "SQL", "scopeName": "source.sql", "patterns": [ @@ -404,7 +404,7 @@ } }, "comment": "this is faster than the next begin/end rule since sub-pattern will match till end-of-line and SQL files tend to have very long lines.", - "match": "(N)?(')(?:[^'\\\\]|\\\\.)*(')", + "match": "(N)?(')[^']*(')", "name": "string.quoted.single.sql" }, { @@ -437,7 +437,7 @@ } }, "comment": "this is faster than the next begin/end rule since sub-pattern will match till end-of-line and SQL files tend to have very long lines.", - "match": "(`)(?:[^`\\\\]|\\\\.)*(`)", + "match": "(`)[^`\\\\]*(`)", "name": "string.quoted.other.backtick.sql" }, { @@ -470,7 +470,7 @@ } }, "comment": "this is faster than the next begin/end rule since sub-pattern will match till end-of-line and SQL files tend to have very long lines.", - "match": "(\")(?:[^\"#\\\\]|\\\\.)*(\")", + "match": "(\")[^\"#]*(\")", "name": "string.quoted.double.sql" }, { diff --git a/extensions/swift/syntaxes/swift.tmLanguage.json b/extensions/swift/syntaxes/swift.tmLanguage.json index 2d67b7a13e1..33cb2ca044a 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/ecba759c1c2f46f69795fe2d01691030214dd5ff", "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|#line|#column|#function|#dsohandle)\\b|\\b(?:__FILE__|__LINE__|__COLUMN__|__FUNCTION__|__DSO_HANDLE__)\\b", "name": "support.variable.swift" }, { diff --git a/extensions/theme-defaults/themes/dark_plus.json b/extensions/theme-defaults/themes/dark_plus.json index f83f763b9a6..4fd89793214 100644 --- a/extensions/theme-defaults/themes/dark_plus.json +++ b/extensions/theme-defaults/themes/dark_plus.json @@ -104,7 +104,7 @@ "variable.other.enummember" ], "settings": { - "foreground": "#51B6C4", + "foreground": "#4FC1FF", } }, { @@ -190,5 +190,11 @@ "foreground": "#C8C8C8" } } - ] + ], + "semanticTokenColors": { + "newOperator":"#C586C0", + "stringLiteral":"#ce9178", + "customLiteral": "#DCDCAA", + "numberLiteral": "#b5cea8", + } } diff --git a/extensions/theme-defaults/themes/dark_vs.json b/extensions/theme-defaults/themes/dark_vs.json index 6e5f4c25f80..1b4cf8b967e 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" @@ -226,7 +225,6 @@ { "scope": [ "string", - "entity.name.operator.custom-literal.string", "meta.embedded.assembly" ], "settings": { @@ -364,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 73309c34b44..436dfa52912 100644 --- a/extensions/theme-defaults/themes/hc_black.json +++ b/extensions/theme-defaults/themes/hc_black.json @@ -122,5 +122,11 @@ "foreground": "#CBEDCB", } } - ] + ], + "semanticTokenColors": { + "newOperator": "#FFFFFF", + "stringLiteral": "#ce9178", + "customLiteral": "#DCDCAA", + "numberLiteral": "#b5cea8", + } } diff --git a/extensions/theme-defaults/themes/light_defaults.json b/extensions/theme-defaults/themes/light_defaults.json index fa1e1dbc3c7..3fad0bc14f6 100644 --- a/extensions/theme-defaults/themes/light_defaults.json +++ b/extensions/theme-defaults/themes/light_defaults.json @@ -19,7 +19,10 @@ "statusBarItem.remoteForeground": "#FFF", "statusBarItem.remoteBackground": "#16825D", "sideBarSectionHeader.background": "#0000", - "sideBarSectionHeader.border": "#61616130" + "sideBarSectionHeader.border": "#61616130", + "notebook.focusedCellBackground": "#c8ddf150", + "notebook.cellBorderColor": "#dae3e9", + "notebook.outputContainerBackgroundColor": "#c8ddf150" }, "semanticHighlighting": true } diff --git a/extensions/theme-defaults/themes/light_plus.json b/extensions/theme-defaults/themes/light_plus.json index 00c2a6cb470..b743b1b998a 100644 --- a/extensions/theme-defaults/themes/light_plus.json +++ b/extensions/theme-defaults/themes/light_plus.json @@ -104,7 +104,7 @@ "variable.other.enummember" ], "settings": { - "foreground": "#328267", + "foreground": "#0070C1", } }, { @@ -169,7 +169,7 @@ "keyword.control.anchor.regexp" ], "settings": { - "foreground": "#ff0000" + "foreground": "#EE0000" } }, { @@ -181,7 +181,7 @@ { "scope": "constant.character.escape", "settings": { - "foreground": "#ff0000" + "foreground": "#EE0000" } }, { @@ -190,5 +190,11 @@ "foreground": "#000000" } } - ] + ], + "semanticTokenColors": { + "newOperator": "#AF00DB", + "stringLiteral": "#a31515", + "customLiteral": "#795E26", + "numberLiteral": "#098658", + } } diff --git a/extensions/theme-defaults/themes/light_vs.json b/extensions/theme-defaults/themes/light_vs.json index cbf19b22988..3410551898b 100644 --- a/extensions/theme-defaults/themes/light_vs.json +++ b/extensions/theme-defaults/themes/light_vs.json @@ -45,7 +45,6 @@ { "scope": [ "constant.numeric", - "entity.name.operator.custom-literal.number", "variable.other.enummember", "keyword.operator.plus.exponent", "keyword.operator.minus.exponent" @@ -218,7 +217,6 @@ { "scope": [ "string", - "entity.name.operator.custom-literal.string", "meta.embedded.assembly" ], "settings": { @@ -388,5 +386,11 @@ "foreground": "#0000ff" } } - ] + ], + "semanticTokenColors": { + "newOperator": "#0000ff", + "stringLiteral": "#a31515", + "customLiteral": "#000000", + "numberLiteral": "#098658", + } } 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 8b1fe2dd80e..935573463ee 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": [ { diff --git a/extensions/theme-seti/cgmanifest.json b/extensions/theme-seti/cgmanifest.json index a3df8c6b322..b3bb41d3eb9 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": "8f22764c37feb7f706465f5186132111a2401b6b" + "commitHash": "f3b2775662b0075aab56e5f0c03269f21f3f0f30" } }, "version": "0.1.0" diff --git a/extensions/theme-seti/icons/seti.woff b/extensions/theme-seti/icons/seti.woff index b4b8366f165..5dc3bb8d9b0 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 efa15be7bca..a1f5817da5c 100644 --- a/extensions/theme-seti/icons/vs-seti-icon-theme.json +++ b/extensions/theme-seti/icons/vs-seti-icon-theme.json @@ -1030,12 +1030,6 @@ "fontCharacter": "\\E074", "fontColor": "#cc3e44" }, - "_rmd_light": { - "fontColor": "#498ba7" - }, - "_rmd": { - "fontColor": "#519aba" - }, "_rollup_light": { "fontCharacter": "\\E075", "fontColor": "#b8383d" @@ -1483,7 +1477,7 @@ "test.tsx": "_react_2", "re": "_reasonml", "r": "_R", - "rmd": "_rmd", + "rmd": "_R", "erb": "_html_erb", "erb.html": "_html_erb", "html.erb": "_html_erb", @@ -1678,7 +1672,7 @@ "sql": "_db", "swift": "_swift", "typescript": "_typescript", - "typescriptreact": "_react", + "typescriptreact": "_typescript", "xml": "_xml", "yaml": "_yml", "argdown": "_argdown", @@ -1811,7 +1805,7 @@ "test.tsx": "_react_2_light", "re": "_reasonml_light", "r": "_R_light", - "rmd": "_rmd_light", + "rmd": "_R_light", "erb": "_html_erb_light", "erb.html": "_html_erb_light", "html.erb": "_html_erb_light", @@ -1949,7 +1943,7 @@ "sql": "_db_light", "swift": "_swift_light", "typescript": "_typescript_light", - "typescriptreact": "_react_light", + "typescriptreact": "_typescript_light", "xml": "_xml_light", "yaml": "_yml_light", "argdown": "_argdown_light", @@ -2030,5 +2024,5 @@ "npm-debug.log": "_npm_ignored_light" } }, - "version": "https://github.com/jesseweed/seti-ui/commit/8f22764c37feb7f706465f5186132111a2401b6b" + "version": "https://github.com/jesseweed/seti-ui/commit/f3b2775662b0075aab56e5f0c03269f21f3f0f30" } \ No newline at end of file diff --git a/extensions/typescript-basics/.vscodeignore b/extensions/typescript-basics/.vscodeignore index 06c7b11c5e6..0a0a50bc3e0 100644 --- a/extensions/typescript-basics/.vscodeignore +++ b/extensions/typescript-basics/.vscodeignore @@ -3,3 +3,4 @@ src/** test/** tsconfig.json cgmanifest.json +syntaxes/Readme.md diff --git a/extensions/typescript-basics/build/update-grammars.js b/extensions/typescript-basics/build/update-grammars.js index 3a7d1e7eb2b..a42b323ed14 100644 --- a/extensions/typescript-basics/build/update-grammars.js +++ b/extensions/typescript-basics/build/update-grammars.js @@ -25,7 +25,10 @@ function removeNodeTypes(grammar) { } } if (pattern.captures) { - if (Object.values(pattern.captures).some(capture => capture.name && capture.name.startsWith('support.variable.object.process'))) { + if (Object.values(pattern.captures).some(capture => + capture.name && (capture.name.startsWith('support.variable.object.process') + || capture.name.startsWith('support.class.console')) + )) { return false; } } diff --git a/extensions/typescript-basics/cgmanifest.json b/extensions/typescript-basics/cgmanifest.json index d95b4a54e7d..623873b2ce4 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": "84422d92e164c379ed817ef8e1d6c35b61de233e" + "commitHash": "fa4e0d3a918db0eab8e5c5be952f3bd649968456" } }, "license": "MIT", diff --git a/extensions/typescript-basics/package.json b/extensions/typescript-basics/package.json index 6b8c281538e..e3120789cf6 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" ] } ], diff --git a/extensions/typescript-basics/snippets/typescript.code-snippets b/extensions/typescript-basics/snippets/typescript.code-snippets index 0a7195751d8..0587884ee12 100644 --- a/extensions/typescript-basics/snippets/typescript.code-snippets +++ b/extensions/typescript-basics/snippets/typescript.code-snippets @@ -123,7 +123,7 @@ "Throw Exception": { "prefix": "throw", "body": [ - "throw \"$1\";", + "throw new Error(\"$1\");", "$0" ], "description": "Throw Exception" @@ -151,7 +151,7 @@ "prefix": "forin", "body": [ "for (const ${1:key} in ${2:object}) {", - "\tif (${2:object}.hasOwnProperty(${1:key})) {", + "\tif (Object.prototype.hasOwnProperty.call(${2:object}, ${1:key})) {", "\t\tconst ${3:element} = ${2:object}[${1:key}];", "\t\t$0", "\t}", @@ -167,7 +167,16 @@ "}" ], "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": [ diff --git a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json index d4489999f40..10187a61673 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/84422d92e164c379ed817ef8e1d6c35b61de233e", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/fa4e0d3a918db0eab8e5c5be952f3bd649968456", "name": "TypeScript", "scopeName": "source.ts", "patterns": [ @@ -3581,23 +3581,6 @@ } } }, - { - "match": "(?x)(? { + return Terser.minify(content.toString()).code; + + }, + transformPath: (targetPath) => { + return targetPath.replace('tsserver.js', 'tsserver.web.js'); + } + } + ], + }), + ], +}); diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 70d4c45d3c5..ff450a9745b 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -19,7 +19,7 @@ "jsonc-parser": "^2.2.1", "rimraf": "^2.6.3", "semver": "5.5.1", - "typescript-vscode-sh-plugin": "^0.6.13", + "typescript-vscode-sh-plugin": "^0.6.14", "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.1.1" }, @@ -27,10 +27,15 @@ "@types/node": "^12.11.7", "@types/rimraf": "2.0.2", "@types/semver": "^5.5.0", + "copy-webpack-plugin": "^6.0.3", + "terser": "^4.8.0", + "typescript-web-server": "git://github.com/mjbvz/ts-server-web-build", "vscode": "^1.1.36" }, "scripts": { - "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:typescript-language-features" + "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:typescript-language-features", + "compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none", + "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "activationEvents": [ "onLanguage:javascript", @@ -50,6 +55,7 @@ "onLanguage:jsonc" ], "main": "./out/extension", + "browser": "./dist/browser/extension", "contributes": { "jsonValidation": [ { @@ -58,7 +64,7 @@ }, { "fileMatch": "tsconfig.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/tsconfig.json" + "url": "https://json.schemastore.org/tsconfig" }, { "fileMatch": "tsconfig.json", @@ -66,7 +72,7 @@ }, { "fileMatch": "tsconfig.*.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/tsconfig.json" + "url": "https://json.schemastore.org/tsconfig" }, { "fileMatch": "tsconfig-*.json", @@ -74,7 +80,7 @@ }, { "fileMatch": "tsconfig-*.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/tsconfig.json" + "url": "https://json.schemastore.org/tsconfig" }, { "fileMatch": "tsconfig.*.json", @@ -82,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", @@ -110,7 +116,7 @@ }, { "fileMatch": "jsconfig.*.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/jsconfig.json" + "url": "https://json.schemastore.org/jsconfig" }, { "fileMatch": "jsconfig.*.json", @@ -680,6 +686,22 @@ "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, diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index 6f4241434ef..4322984e099 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -74,8 +74,12 @@ "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.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. Requires using TypeScript 2.9 or newer in the workspace.", "typescript.updateImportsOnFileMove.enabled.prompt": "Prompt on each rename.", "typescript.updateImportsOnFileMove.enabled.always": "Always update paths automatically.", diff --git a/extensions/typescript-language-features/src/utils/commandManager.ts b/extensions/typescript-language-features/src/commands/commandManager.ts similarity index 100% rename from extensions/typescript-language-features/src/utils/commandManager.ts rename to extensions/typescript-language-features/src/commands/commandManager.ts diff --git a/extensions/typescript-language-features/src/commands/configurePlugin.ts b/extensions/typescript-language-features/src/commands/configurePlugin.ts index 8af85d8b94e..f781c4a50fa 100644 --- a/extensions/typescript-language-features/src/commands/configurePlugin.ts +++ b/extensions/typescript-language-features/src/commands/configurePlugin.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Command } from '../utils/commandManager'; import { PluginManager } from '../utils/plugins'; +import { Command } from './commandManager'; export class ConfigurePluginCommand implements Command { public readonly id = '_typescript.configurePlugin'; diff --git a/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts b/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts index a4fb9c33b4e..11adec35251 100644 --- a/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts +++ b/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts @@ -5,9 +5,9 @@ import * as vscode from 'vscode'; import TypeScriptServiceClientHost from '../typeScriptServiceClientHost'; -import { Command } from '../utils/commandManager'; import { Lazy } from '../utils/lazy'; import { openProjectConfigForFile, ProjectType } from '../utils/tsconfig'; +import { Command } from './commandManager'; export class TypeScriptGoToProjectConfigCommand implements Command { public readonly id = 'typescript.goToProjectConfig'; diff --git a/extensions/typescript-language-features/src/commands/index.ts b/extensions/typescript-language-features/src/commands/index.ts index 8e4cfddcccd..7ab7ae09d20 100644 --- a/extensions/typescript-language-features/src/commands/index.ts +++ b/extensions/typescript-language-features/src/commands/index.ts @@ -4,22 +4,22 @@ *--------------------------------------------------------------------------------------------*/ import TypeScriptServiceClientHost from '../typeScriptServiceClientHost'; -import { CommandManager } from '../utils/commandManager'; import { Lazy } from '../utils/lazy'; import { PluginManager } from '../utils/plugins'; +import { CommandManager } from './commandManager'; import { ConfigurePluginCommand } from './configurePlugin'; import { JavaScriptGoToProjectConfigCommand, TypeScriptGoToProjectConfigCommand } from './goToProjectConfiguration'; +import { LearnMoreAboutRefactoringsCommand } from './learnMoreAboutRefactorings'; import { OpenTsServerLogCommand } from './openTsServerLog'; import { ReloadJavaScriptProjectsCommand, ReloadTypeScriptProjectsCommand } from './reloadProject'; import { RestartTsServerCommand } from './restartTsServer'; import { SelectTypeScriptVersionCommand } from './selectTypeScriptVersion'; -import { LearnMoreAboutRefactoringsCommand } from './learnMoreAboutRefactorings'; -export function registerCommands( +export function registerBaseCommands( commandManager: CommandManager, lazyClientHost: Lazy, pluginManager: PluginManager -) { +): void { commandManager.register(new ReloadTypeScriptProjectsCommand(lazyClientHost)); commandManager.register(new ReloadJavaScriptProjectsCommand(lazyClientHost)); commandManager.register(new SelectTypeScriptVersionCommand(lazyClientHost)); diff --git a/extensions/typescript-language-features/src/commands/learnMoreAboutRefactorings.ts b/extensions/typescript-language-features/src/commands/learnMoreAboutRefactorings.ts index 21366d6c607..b166397e38b 100644 --- a/extensions/typescript-language-features/src/commands/learnMoreAboutRefactorings.ts +++ b/extensions/typescript-language-features/src/commands/learnMoreAboutRefactorings.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { Command } from '../utils/commandManager'; import { isTypeScriptDocument } from '../utils/languageModeIds'; +import { Command } from './commandManager'; export class LearnMoreAboutRefactoringsCommand implements Command { public static readonly id = '_typescript.learnMoreAboutRefactorings'; diff --git a/extensions/typescript-language-features/src/commands/openTsServerLog.ts b/extensions/typescript-language-features/src/commands/openTsServerLog.ts index be47a985cd7..cd41445fef2 100644 --- a/extensions/typescript-language-features/src/commands/openTsServerLog.ts +++ b/extensions/typescript-language-features/src/commands/openTsServerLog.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import TypeScriptServiceClientHost from '../typeScriptServiceClientHost'; -import { Command } from '../utils/commandManager'; import { Lazy } from '../utils/lazy'; +import { Command } from './commandManager'; export class OpenTsServerLogCommand implements Command { public readonly id = 'typescript.openTsServerLog'; diff --git a/extensions/typescript-language-features/src/commands/reloadProject.ts b/extensions/typescript-language-features/src/commands/reloadProject.ts index fbba68eb94e..4da59685f67 100644 --- a/extensions/typescript-language-features/src/commands/reloadProject.ts +++ b/extensions/typescript-language-features/src/commands/reloadProject.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import TypeScriptServiceClientHost from '../typeScriptServiceClientHost'; -import { Command } from '../utils/commandManager'; import { Lazy } from '../utils/lazy'; +import { Command } from './commandManager'; export class ReloadTypeScriptProjectsCommand implements Command { public readonly id = 'typescript.reloadProjects'; diff --git a/extensions/typescript-language-features/src/commands/restartTsServer.ts b/extensions/typescript-language-features/src/commands/restartTsServer.ts index a357224d352..77dcae870ee 100644 --- a/extensions/typescript-language-features/src/commands/restartTsServer.ts +++ b/extensions/typescript-language-features/src/commands/restartTsServer.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import TypeScriptServiceClientHost from '../typeScriptServiceClientHost'; -import { Command } from '../utils/commandManager'; import { Lazy } from '../utils/lazy'; +import { Command } from './commandManager'; export class RestartTsServerCommand implements Command { public readonly id = 'typescript.restartTsServer'; diff --git a/extensions/typescript-language-features/src/commands/selectTypeScriptVersion.ts b/extensions/typescript-language-features/src/commands/selectTypeScriptVersion.ts index f375b55e938..d70f59472ff 100644 --- a/extensions/typescript-language-features/src/commands/selectTypeScriptVersion.ts +++ b/extensions/typescript-language-features/src/commands/selectTypeScriptVersion.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import TypeScriptServiceClientHost from '../typeScriptServiceClientHost'; -import { Command } from '../utils/commandManager'; import { Lazy } from '../utils/lazy'; +import { Command } from './commandManager'; export class SelectTypeScriptVersionCommand implements Command { public readonly id = 'typescript.selectTypeScriptVersion'; diff --git a/extensions/typescript-language-features/src/extension.browser.ts b/extensions/typescript-language-features/src/extension.browser.ts new file mode 100644 index 00000000000..9291e22ae36 --- /dev/null +++ b/extensions/typescript-language-features/src/extension.browser.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 * 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, + context.asAbsolutePath('dist/browser/typescript-web/tsserver.web.js'), + 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 36783cc5e6a..1d6ccd2914d 100644 --- a/extensions/typescript-language-features/src/extension.ts +++ b/extensions/typescript-language-features/src/extension.ts @@ -3,23 +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/largeProjectStatus'; -import TscTaskProvider from './features/task'; +import * as temp from './utils/temp.electron'; export function activate( context: vscode.ExtensionContext @@ -33,15 +30,29 @@ export function activate( const onCompletionAccepted = new vscode.EventEmitter(); context.subscriptions.push(onCompletionAccepted); - const lazyClientHost = createLazyClientHost(context, pluginManager, commandManager, item => { + const logDirectoryProvider = new NodeLogDirectoryProvider(context); + const versionProvider = new DiskTypeScriptVersionProvider(); + + context.subscriptions.push(new LanguageConfigurationManager()); + + const lazyClientHost = createLazyClientHost(context, onCaseInsenitiveFileSystem(), { + pluginManager, + commandManager, + logDirectoryProvider, + cancellerFactory: nodeRequestCancellerFactory, + versionProvider, + processFactory: ChildServerProcess, + }, item => { onCompletionAccepted.fire(item); }); - registerCommands(commandManager, lazyClientHost, pluginManager); - context.subscriptions.push(vscode.tasks.registerTaskProvider('typescript', new TscTaskProvider(lazyClientHost.map(x => x.serviceClient)))); - context.subscriptions.push(new LanguageConfigurationManager()); + registerBaseCommands(commandManager, lazyClientHost, pluginManager); - import('./features/tsconfig').then(module => { + import('./task/taskProvider').then(module => { + context.subscriptions.push(module.register(lazyClientHost.map(x => x.serviceClient))); + }); + + import('./languageFeatures/tsconfig').then(module => { context.subscriptions.push(module.register()); }); @@ -50,84 +61,6 @@ export function activate( return getExtensionApi(onCompletionAccepted.event, pluginManager); } -function createLazyClientHost( - context: vscode.ExtensionContext, - pluginManager: PluginManager, - commandManager: CommandManager, - onCompletionAccepted: (item: vscode.CompletionItem) => void, -): Lazy { - return lazy(() => { - const logDirectoryProvider = new LogDirectoryProvider(context); - - const clientHost = new TypeScriptServiceClientHost( - standardLanguageDescriptions, - context.workspaceState, - pluginManager, - commandManager, - logDirectoryProvider, - onCompletionAccepted); - - context.subscriptions.push(clientHost); - - clientHost.serviceClient.onReady(() => { - context.subscriptions.push( - ProjectStatus.create( - clientHost.serviceClient, - clientHost.serviceClient.telemetryReporter)); - }); - - return clientHost; - }); -} - -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 - void lazyClientHost.value; - - disposables.push(new ManagedFileContextManager(resource => { - return lazyClientHost.value.serviceClient.toPath(resource); - })); - return true; - } - return false; - }; - - const didActivate = vscode.workspace.textDocuments.some(maybeActivate); - if (!didActivate) { - const openListener = vscode.workspace.onDidOpenTextDocument(doc => { - if (maybeActivate(doc)) { - openListener.dispose(); - } - }, undefined, disposables); - } - - return vscode.Disposable.from(...disposables); -} - -function isSupportedDocument( - supportedLanguage: string[], - document: vscode.TextDocument -): boolean { - if (supportedLanguage.indexOf(document.languageId) < 0) { - return false; - } - return fileSchemes.isSupportedScheme(document.uri.scheme); -} - export function deactivate() { - rimraf.sync(electron.getInstanceDir()); + rimraf.sync(temp.getInstanceTempDir()); } diff --git a/extensions/typescript-language-features/src/features/fixAll.ts b/extensions/typescript-language-features/src/features/fixAll.ts deleted file mode 100644 index 0f2e0cc78d2..00000000000 --- a/extensions/typescript-language-features/src/features/fixAll.ts +++ /dev/null @@ -1,140 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import * as nls from 'vscode-nls'; -import type * 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 { - - private static readonly kind = vscode.CodeActionKind.SourceFixAll.append('ts'); - - public static readonly metadata: vscode.CodeActionProviderMetadata = { - providedCodeActionKinds: [TypeScriptAutoFixProvider.kind] - }; - - constructor( - private readonly client: ITypeScriptServiceClient, - private readonly fileConfigurationManager: FileConfigurationManager, - private readonly diagnosticsManager: DiagnosticsManager, - ) { } - - public async provideCodeActions( - document: vscode.TextDocument, - _range: vscode.Range, - context: vscode.CodeActionContext, - token: vscode.CancellationToken - ): Promise { - 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; - if (!edit.size) { - return undefined; - } - - const codeAction = new vscode.CodeAction( - localize('autoFix.label', 'Auto fix'), - TypeScriptAutoFixProvider.kind); - codeAction.edit = edit; - codeAction.diagnostics = fixedDiagnostics; - - return codeAction; - } - - private async getAutoFixEdit( - file: string, - diagnostics: ReadonlyArray, - token: vscode.CancellationToken, - ): Promise<{ edit: vscode.WorkspaceEdit, fixedDiagnostics: vscode.Diagnostic[] } | undefined> { - const edit = new vscode.WorkspaceEdit(); - const fixedDiagnostics: vscode.Diagnostic[] = []; - for (const diagnostic of diagnostics) { - const args: Proto.CodeFixRequestArgs = { - ...typeConverters.Range.toFileRangeRequestArgs(file, diagnostic.range), - errorCodes: [+(diagnostic.code!)] - }; - const response = await this.client.execute('getCodeFixes', args, token); - if (response.type !== 'response' || !response.body || response.body.length > 1) { - continue; - } - - const fix = response.body[0]; - if (['fixClassIncorrectlyImplementsInterface', 'spelling'].includes(fix.fixName)) { - typeConverters.WorkspaceEdit.withFileCodeEdits(edit, this.client, fix.changes); - fixedDiagnostics.push(diagnostic); - } - } - - if (!fixedDiagnostics.length) { - return undefined; - } - - return { edit, fixedDiagnostics }; - } -} - -export function register( - selector: vscode.DocumentSelector, - client: ITypeScriptServiceClient, - fileConfigurationManager: FileConfigurationManager, - diagnosticsManager: DiagnosticsManager) { - return new VersionDependentRegistration(client, API.v300, () => - new ConfigurationDependentRegistration('typescript', 'experimental.autoFix.enabled', () => - vscode.languages.registerCodeActionsProvider(selector, - new TypeScriptAutoFixProvider(client, fileConfigurationManager, diagnosticsManager), - TypeScriptAutoFixProvider.metadata))); -} diff --git a/extensions/typescript-language-features/src/features/callHierarchy.ts b/extensions/typescript-language-features/src/languageFeatures/callHierarchy.ts similarity index 77% rename from extensions/typescript-language-features/src/features/callHierarchy.ts rename to extensions/typescript-language-features/src/languageFeatures/callHierarchy.ts index a76d8a6bea8..a90e68fd27c 100644 --- a/extensions/typescript-language-features/src/features/callHierarchy.ts +++ b/extensions/typescript-language-features/src/languageFeatures/callHierarchy.ts @@ -3,14 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; -import { ITypeScriptServiceClient } from '../typescriptService'; -import * as typeConverters from '../utils/typeConverters'; -import API from '../utils/api'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; -import type * as Proto from '../protocol'; import * as path from 'path'; +import * as vscode from 'vscode'; +import type * as Proto from '../protocol'; import * as PConst from '../protocol.const'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; +import API from '../utils/api'; +import { conditionalRegistration, requireSomeCapability, requireMinVersion } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; +import { parseKindModifier } from '../utils/modifiers'; +import * as typeConverters from '../utils/typeConverters'; + +namespace Experimental { + export interface CallHierarchyItem extends Proto.CallHierarchyItem { + readonly kindModifiers?: string; + } +} class TypeScriptCallHierarchySupport implements vscode.CallHierarchyProvider { public static readonly minVersion = API.v380; @@ -75,11 +83,11 @@ 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 { +function fromProtocolCallHierarchyItem(item: Experimental.CallHierarchyItem): vscode.CallHierarchyItem { const useFileName = isSourceFileItem(item); const name = useFileName ? path.basename(item.file) : item.name; const detail = useFileName ? vscode.workspace.asRelativePath(path.dirname(item.file)) : ''; - return new vscode.CallHierarchyItem( + const result = new vscode.CallHierarchyItem( typeConverters.SymbolKind.fromProtocolScriptElementKind(item.kind), name, detail, @@ -87,6 +95,12 @@ function fromProtocolCallHierarchyItem(item: Proto.CallHierarchyItem): vscode.Ca typeConverters.Range.fromTextSpan(item.span), typeConverters.Range.fromTextSpan(item.selectionSpan) ); + + const kindModifiers = item.kindModifiers ? parseKindModifier(item.kindModifiers) : undefined; + if (kindModifiers?.has(PConst.KindModifiers.depreacted)) { + result.tags = [vscode.SymbolTag.Deprecated]; + } + return result; } function fromProtocolCallHierchyIncomingCall(item: Proto.CallHierarchyIncomingCall): vscode.CallHierarchyIncomingCall { @@ -104,10 +118,14 @@ function fromProtocolCallHierchyOutgoingCall(item: Proto.CallHierarchyOutgoingCa } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient ) { - return new VersionDependentRegistration(client, TypeScriptCallHierarchySupport.minVersion, - () => vscode.languages.registerCallHierarchyProvider(selector, - new TypeScriptCallHierarchySupport(client))); + return conditionalRegistration([ + requireMinVersion(client, TypeScriptCallHierarchySupport.minVersion), + requireSomeCapability(client, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerCallHierarchyProvider(selector.semantic, + new TypeScriptCallHierarchySupport(client)); + }); } diff --git a/extensions/typescript-language-features/src/features/baseCodeLensProvider.ts b/extensions/typescript-language-features/src/languageFeatures/codeLens/baseCodeLensProvider.ts similarity index 92% rename from extensions/typescript-language-features/src/features/baseCodeLensProvider.ts rename to extensions/typescript-language-features/src/languageFeatures/codeLens/baseCodeLensProvider.ts index f63853d9ec8..7f0e3e5f097 100644 --- a/extensions/typescript-language-features/src/features/baseCodeLensProvider.ts +++ b/extensions/typescript-language-features/src/languageFeatures/codeLens/baseCodeLensProvider.ts @@ -5,11 +5,11 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import type * 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 79% rename from extensions/typescript-language-features/src/features/implementationsCodeLens.ts rename to extensions/typescript-language-features/src/languageFeatures/codeLens/implementationsCodeLens.ts index c6ea7ca6dee..a340e21bed3 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 type * 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(); @@ -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/features/referencesCodeLens.ts b/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts similarity index 81% rename from extensions/typescript-language-features/src/features/referencesCodeLens.ts rename to extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts index 0cf8d3f0a51..8fe118de5d2 100644 --- a/extensions/typescript-language-features/src/features/referencesCodeLens.ts +++ b/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts @@ -5,12 +5,14 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import type * as Proto from '../protocol'; -import * as PConst from '../protocol.const'; -import { CachedResponse } from '../tsServer/cachedResponse'; -import { ITypeScriptServiceClient } from '../typescriptService'; -import { ConfigurationDependentRegistration } from '../utils/dependentRegistration'; -import * as typeConverters from '../utils/typeConverters'; +import type * as Proto from '../../protocol'; +import * as PConst from '../../protocol.const'; +import { CachedResponse } from '../../tsServer/cachedResponse'; +import { ExectuionTarget } from '../../tsServer/server'; +import { ClientCapability, ITypeScriptServiceClient } from '../../typescriptService'; +import { conditionalRegistration, requireConfiguration, requireSomeCapability } from '../../utils/dependentRegistration'; +import { DocumentSelector } from '../../utils/documentSelector'; +import * as typeConverters from '../../utils/typeConverters'; import { getSymbolRange, ReferencesCodeLens, TypeScriptBaseCodeLensProvider } from './baseCodeLensProvider'; const localize = nls.loadMessageBundle(); @@ -27,7 +29,11 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens public async resolveCodeLens(inputCodeLens: vscode.CodeLens, token: vscode.CancellationToken): Promise { const codeLens = inputCodeLens as ReferencesCodeLens; const args = typeConverters.Position.toFileLocationRequestArgs(codeLens.file, codeLens.range.start); - const response = await this.client.execute('references', args, token, { lowPriority: true, cancelOnResourceChange: codeLens.document }); + const response = await this.client.execute('references', args, token, { + lowPriority: true, + executionTarget: ExectuionTarget.Semantic, + cancelOnResourceChange: codeLens.document, + }); if (response.type !== 'response' || !response.body) { codeLens.command = response.type === 'cancelled' ? TypeScriptBaseCodeLensProvider.cancelledCommand @@ -122,13 +128,16 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, modeId: string, client: ITypeScriptServiceClient, cachedResponse: CachedResponse, ) { - return new ConfigurationDependentRegistration(modeId, 'referencesCodeLens.enabled', () => { - return vscode.languages.registerCodeLensProvider(selector, + return conditionalRegistration([ + requireConfiguration(modeId, 'referencesCodeLens.enabled'), + requireSomeCapability(client, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerCodeLensProvider(selector.semantic, new TypeScriptReferencesCodeLensProvider(client, cachedResponse, modeId)); }); } diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/languageFeatures/completions.ts similarity index 88% rename from extensions/typescript-language-features/src/features/completions.ts rename to extensions/typescript-language-features/src/languageFeatures/completions.ts index e25bd996c38..9cac559ef20 100644 --- a/extensions/typescript-language-features/src/features/completions.ts +++ b/extensions/typescript-language-features/src/languageFeatures/completions.ts @@ -5,14 +5,16 @@ 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 { ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; +import { ClientCapability, ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; import API from '../utils/api'; import { nulToken } from '../utils/cancellation'; import { applyCodeAction } from '../utils/codeAction'; -import { Command, CommandManager } from '../utils/commandManager'; -import { ConfigurationDependentRegistration } from '../utils/dependentRegistration'; +import { 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'; @@ -90,8 +92,8 @@ class MyCompletionItem extends vscode.CompletionItem { } if (tsEntry.kindModifiers) { - const kindModifiers = tsEntry.kindModifiers.split(/,|\s+/g); - if (kindModifiers.includes(PConst.KindModifiers.optional)) { + const kindModifiers = parseKindModifier(tsEntry.kindModifiers); + if (kindModifiers.has(PConst.KindModifiers.optional)) { if (!this.insertText) { this.insertText = this.label; } @@ -101,14 +103,17 @@ class MyCompletionItem extends vscode.CompletionItem { } this.label += '?'; } + if (kindModifiers.has(PConst.KindModifiers.depreacted)) { + this.tags = [vscode.CompletionItemTag.Deprecated]; + } - if (kindModifiers.includes(PConst.KindModifiers.color)) { + 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.includes(extModifier)) { + if (kindModifiers.has(extModifier)) { if (tsEntry.name.toLowerCase().endsWith(extModifier)) { this.detail = tsEntry.name; } else { @@ -325,10 +330,25 @@ class CompletionAcceptedCommand implements Command { public constructor( private readonly onCompletionAccepted: (item: vscode.CompletionItem) => void, + private readonly telemetryReporter: TelemetryReporter, ) { } public execute(item: vscode.CompletionItem) { this.onCompletionAccepted(item); + if (item instanceof MyCompletionItem) { + /* __GDPR__ + "completions.accept" : { + "isPackageJsonImport" : { "classification": "SystemMetadata", "purpose": "FeatureInsight" }, + "${include}": [ + "${TypeScriptCommonProperties}" + ] + } + */ + this.telemetryReporter.logTelemetry('completions.accept', { + // @ts-expect-error - remove after TS 4.0 protocol update + isPackageJsonImport: item.tsEntry.isPackageJsonImport ? 'true' : undefined, + }); + } } } @@ -380,7 +400,6 @@ interface CompletionConfiguration { readonly nameSuggestions: boolean; readonly pathSuggestions: boolean; readonly autoImportSuggestions: boolean; - readonly includeAutomaticOptionalChainCompletions: boolean; } namespace CompletionConfiguration { @@ -388,7 +407,6 @@ namespace CompletionConfiguration { export const nameSuggestions = 'suggest.names'; export const pathSuggestions = 'suggest.paths'; export const autoImportSuggestions = 'suggest.autoImports'; - export const includeAutomaticOptionalChainCompletions = 'suggest.includeAutomaticOptionalChainCompletions'; export function getConfigurationForResource( modeId: string, @@ -400,7 +418,6 @@ namespace CompletionConfiguration { pathSuggestions: config.get(CompletionConfiguration.pathSuggestions, true), autoImportSuggestions: config.get(CompletionConfiguration.autoImportSuggestions, true), nameSuggestions: config.get(CompletionConfiguration.nameSuggestions, true), - includeAutomaticOptionalChainCompletions: config.get(CompletionConfiguration.includeAutomaticOptionalChainCompletions, true), }; } } @@ -420,7 +437,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider< ) { commandManager.register(new ApplyCompletionCodeActionCommand(this.client)); commandManager.register(new CompositeCommand()); - commandManager.register(new CompletionAcceptedCommand(onCompletionAccepted)); + commandManager.register(new CompletionAcceptedCommand(onCompletionAccepted, this.telemetryReporter)); } public async provideCompletionItems( @@ -456,12 +473,11 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider< await this.client.interruptGetErr(() => this.fileConfigurationManager.ensureConfigurationForDocument(document, token)); - const args: Proto.CompletionsRequestArgs & { includeAutomaticOptionalChainCompletions?: boolean } = { + const args: Proto.CompletionsRequestArgs = { ...typeConverters.Position.toFileLocationRequestArgs(file, position), includeExternalModuleExports: completionConfiguration.autoImportSuggestions, includeInsertTextCompletions: true, triggerCharacter: this.getTsTriggerCharacter(context), - includeAutomaticOptionalChainCompletions: completionConfiguration.includeAutomaticOptionalChainCompletions, }; let isNewIdentifierLocation = true; @@ -470,34 +486,18 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider< 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(); - 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" }, - "updateGraphDurationMs" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, - "${include}": [ - "${TypeScriptCommonProperties}" - ] - } - */ - this.telemetryReporter.logTelemetry('completions.execute', { - duration: duration, - type: response?.type ?? 'unknown', - count: response?.type === 'response' && response.body ? response.body.entries.length : 0, - updateGraphDurationMs: response?.type === 'response' ? response.performanceData?.updateGraphDurationMs : undefined, - }); + duration = Date.now() - startTime; } if (response.type !== 'response' || !response.body) { + this.logCompletionsTelemetry(duration, response); return null; } isNewIdentifierLocation = response.body.isNewIdentifierLocation; @@ -535,15 +535,47 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider< 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)); + // @ts-expect-error - remove after TS 4.0 protocol update + 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" }, + "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, + 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 @@ -795,7 +827,7 @@ function shouldExcludeCompletionEntry( } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, modeId: string, client: ITypeScriptServiceClient, typingsStatus: TypingsStatus, @@ -804,8 +836,12 @@ export function register( telemetryReporter: TelemetryReporter, onCompletionAccepted: (item: vscode.CompletionItem) => void ) { - return new ConfigurationDependentRegistration(modeId, 'suggest.enabled', () => - vscode.languages.registerCompletionItemProvider(selector, + return conditionalRegistration([ + requireConfiguration(modeId, 'suggest.enabled'), + requireSomeCapability(client, ClientCapability.EnhancedSyntax, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerCompletionItemProvider(selector.syntax, new TypeScriptCompletionItemProvider(client, modeId, typingsStatus, fileConfigurationManager, commandManager, telemetryReporter, onCompletionAccepted), - ...TypeScriptCompletionItemProvider.triggerCharacters)); + ...TypeScriptCompletionItemProvider.triggerCharacters); + }); } diff --git a/extensions/typescript-language-features/src/features/definitionProviderBase.ts b/extensions/typescript-language-features/src/languageFeatures/definitionProviderBase.ts similarity index 100% rename from extensions/typescript-language-features/src/features/definitionProviderBase.ts rename to extensions/typescript-language-features/src/languageFeatures/definitionProviderBase.ts diff --git a/extensions/typescript-language-features/src/features/definitions.ts b/extensions/typescript-language-features/src/languageFeatures/definitions.ts similarity index 80% rename from extensions/typescript-language-features/src/features/definitions.ts rename to extensions/typescript-language-features/src/languageFeatures/definitions.ts index fd3a1f10e79..7c01400c206 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'; @@ -58,9 +60,13 @@ export default class TypeScriptDefinitionProvider extends DefinitionProviderBase } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, ) { - return vscode.languages.registerDefinitionProvider(selector, - new TypeScriptDefinitionProvider(client)); + return conditionalRegistration([ + requireSomeCapability(client, ClientCapability.EnhancedSyntax, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerDefinitionProvider(selector.syntax, + new TypeScriptDefinitionProvider(client)); + }); } diff --git a/extensions/typescript-language-features/src/features/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts similarity index 93% rename from extensions/typescript-language-features/src/features/diagnostics.ts rename to extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index 327573e8c18..25a9591cc56 100644 --- a/extensions/typescript-language-features/src/features/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -78,7 +78,7 @@ class FileDiagnostics { return this.get(DiagnosticKind.Suggestion).filter(x => { if (!enableSuggestions) { // Still show unused - return x.tags && x.tags.includes(vscode.DiagnosticTag.Unnecessary); + return x.tags && (x.tags.includes(vscode.DiagnosticTag.Unnecessary) || x.tags.includes(vscode.DiagnosticTag.Deprecated)); } return true; }); @@ -142,17 +142,21 @@ class DiagnosticSettings { } export class DiagnosticsManager extends Disposable { - private readonly _diagnostics = new ResourceMap(); + 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 96% rename from extensions/typescript-language-features/src/features/directiveCommentCompletions.ts rename to extensions/typescript-language-features/src/languageFeatures/directiveCommentCompletions.ts index cfa35571d1d..2b0f683e589 100644 --- a/extensions/typescript-language-features/src/features/directiveCommentCompletions.ts +++ b/extensions/typescript-language-features/src/languageFeatures/directiveCommentCompletions.ts @@ -7,6 +7,7 @@ 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(); @@ -80,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 95% rename from extensions/typescript-language-features/src/features/documentHighlight.ts rename to extensions/typescript-language-features/src/languageFeatures/documentHighlight.ts index 61477e06749..3a7b43ed5eb 100644 --- a/extensions/typescript-language-features/src/features/documentHighlight.ts +++ b/extensions/typescript-language-features/src/languageFeatures/documentHighlight.ts @@ -7,6 +7,7 @@ import * as vscode from 'vscode'; 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)); } diff --git a/extensions/typescript-language-features/src/features/documentSymbol.ts b/extensions/typescript-language-features/src/languageFeatures/documentSymbol.ts similarity index 92% rename from extensions/typescript-language-features/src/features/documentSymbol.ts rename to extensions/typescript-language-features/src/languageFeatures/documentSymbol.ts index e119b005bab..d0b8b9ad901 100644 --- a/extensions/typescript-language-features/src/features/documentSymbol.ts +++ b/extensions/typescript-language-features/src/languageFeatures/documentSymbol.ts @@ -6,9 +6,11 @@ import * as vscode from 'vscode'; import type * as Proto from '../protocol'; import * as PConst from '../protocol.const'; -import { ITypeScriptServiceClient } from '../typescriptService'; -import * as typeConverters from '../utils/typeConverters'; import { CachedResponse } from '../tsServer/cachedResponse'; +import { ITypeScriptServiceClient } from '../typescriptService'; +import { DocumentSelector } from '../utils/documentSelector'; +import { parseKindModifier } from '../utils/modifiers'; +import * as typeConverters from '../utils/typeConverters'; const getSymbolKind = (kind: string): vscode.SymbolKind => { switch (kind) { @@ -79,6 +81,12 @@ class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider 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)))) { const includedChild = TypeScriptDocumentSymbolProvider.convertNavTree(resource, symbolInfo.children, child); @@ -104,10 +112,10 @@ 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 86% rename from extensions/typescript-language-features/src/features/fileConfigurationManager.ts rename to extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts index 686c35e18ed..dbd3513f0d4 100644 --- a/extensions/typescript-language-features/src/features/fileConfigurationManager.ts +++ b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts @@ -10,17 +10,14 @@ 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'; - -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 { @@ -29,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 @@ -176,16 +172,22 @@ 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); - const preferences: Proto.UserPreferences = { - quotePreference: this.getQuoteStylePreference(config), - importModuleSpecifierPreference: getImportModuleSpecifierPreference(config), - importModuleSpecifierEnding: getImportModuleSpecifierEndingPreference(config), + const preferences: Experimental.UserPreferences = { + quotePreference: this.getQuoteStylePreference(preferencesConfig), + importModuleSpecifierPreference: getImportModuleSpecifierPreference(preferencesConfig), + importModuleSpecifierEnding: getImportModuleSpecifierEndingPreference(preferencesConfig), allowTextChangesInNewFiles: document.uri.scheme === fileSchemes.file, - providePrefixAndSuffixTextForRename: config.get('renameShorthandProperties', true) === false ? false : config.get('useAliasesForRenames', true), + providePrefixAndSuffixTextForRename: preferencesConfig.get('renameShorthandProperties', true) === false ? false : preferencesConfig.get('useAliasesForRenames', true), allowRenameOfImportPath: true, + includeAutomaticOptionalChainCompletions: config.get('suggest.includeAutomaticOptionalChainCompletions', true), + provideRefactorNotApplicableReason: true, }; return preferences; diff --git a/extensions/typescript-language-features/src/languageFeatures/fixAll.ts b/extensions/typescript-language-features/src/languageFeatures/fixAll.ts new file mode 100644 index 00000000000..6a43c535a8b --- /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/features/folding.ts b/extensions/typescript-language-features/src/languageFeatures/folding.ts similarity index 87% rename from extensions/typescript-language-features/src/features/folding.ts rename to extensions/typescript-language-features/src/languageFeatures/folding.ts index f9f04296bdd..5b18decb440 100644 --- a/extensions/typescript-language-features/src/features/folding.ts +++ b/extensions/typescript-language-features/src/languageFeatures/folding.ts @@ -8,7 +8,8 @@ import type * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { coalesce } from '../utils/arrays'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireMinVersion } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; import * as typeConverters from '../utils/typeConverters'; class TypeScriptFoldingProvider implements vscode.FoldingRangeProvider { @@ -73,11 +74,13 @@ class TypeScriptFoldingProvider implements vscode.FoldingRangeProvider { } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, ): vscode.Disposable { - return new VersionDependentRegistration(client, TypeScriptFoldingProvider.minVersion, () => { - return vscode.languages.registerFoldingRangeProvider(selector, + return conditionalRegistration([ + requireMinVersion(client, TypeScriptFoldingProvider.minVersion), + ], () => { + return vscode.languages.registerFoldingRangeProvider(selector.syntax, new TypeScriptFoldingProvider(client)); }); } diff --git a/extensions/typescript-language-features/src/features/formatting.ts b/extensions/typescript-language-features/src/languageFeatures/formatting.ts similarity index 90% rename from extensions/typescript-language-features/src/features/formatting.ts rename to extensions/typescript-language-features/src/languageFeatures/formatting.ts index fbe1db881e0..cc2469774a5 100644 --- a/extensions/typescript-language-features/src/features/formatting.ts +++ b/extensions/typescript-language-features/src/languageFeatures/formatting.ts @@ -6,7 +6,8 @@ import * as vscode from 'vscode'; import type * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; -import { ConfigurationDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireConfiguration } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; import * as typeConverters from '../utils/typeConverters'; import FileConfigurationManager from './fileConfigurationManager'; @@ -84,16 +85,18 @@ class TypeScriptFormattingProvider implements vscode.DocumentRangeFormattingEdit } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, modeId: string, client: ITypeScriptServiceClient, fileConfigurationManager: FileConfigurationManager ) { - return new ConfigurationDependentRegistration(modeId, 'format.enable', () => { + return conditionalRegistration([ + requireConfiguration(modeId, 'format.enable'), + ], () => { const formattingProvider = new TypeScriptFormattingProvider(client, fileConfigurationManager); return vscode.Disposable.from( - vscode.languages.registerOnTypeFormattingEditProvider(selector, formattingProvider, ';', '}', '\n'), - vscode.languages.registerDocumentRangeFormattingEditProvider(selector, formattingProvider), + vscode.languages.registerOnTypeFormattingEditProvider(selector.syntax, formattingProvider, ';', '}', '\n'), + vscode.languages.registerDocumentRangeFormattingEditProvider(selector.syntax, formattingProvider), ); }); } diff --git a/extensions/typescript-language-features/src/features/hover.ts b/extensions/typescript-language-features/src/languageFeatures/hover.ts similarity index 77% rename from extensions/typescript-language-features/src/features/hover.ts rename to extensions/typescript-language-features/src/languageFeatures/hover.ts index 2ff92d6e308..c6c4860f663 100644 --- a/extensions/typescript-language-features/src/features/hover.ts +++ b/extensions/typescript-language-features/src/languageFeatures/hover.ts @@ -5,7 +5,9 @@ import * as vscode from 'vscode'; import type * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; +import { conditionalRegistration, requireSomeCapability } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; import { markdownDocumentation } from '../utils/previewer'; import * as typeConverters from '../utils/typeConverters'; @@ -51,9 +53,13 @@ class TypeScriptHoverProvider implements vscode.HoverProvider { } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient ): vscode.Disposable { - return vscode.languages.registerHoverProvider(selector, - new TypeScriptHoverProvider(client)); + return conditionalRegistration([ + requireSomeCapability(client, ClientCapability.EnhancedSyntax, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerHoverProvider(selector.syntax, + new TypeScriptHoverProvider(client)); + }); } diff --git a/extensions/typescript-language-features/src/features/implementations.ts b/extensions/typescript-language-features/src/languageFeatures/implementations.ts similarity index 63% rename from extensions/typescript-language-features/src/features/implementations.ts rename to extensions/typescript-language-features/src/languageFeatures/implementations.ts index c7cdeeb755f..bf3ddfee414 100644 --- a/extensions/typescript-language-features/src/features/implementations.ts +++ b/extensions/typescript-language-features/src/languageFeatures/implementations.ts @@ -4,7 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; +import { conditionalRegistration, requireSomeCapability } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; import DefinitionProviderBase from './definitionProviderBase'; class TypeScriptImplementationProvider extends DefinitionProviderBase implements vscode.ImplementationProvider { @@ -14,9 +16,13 @@ class TypeScriptImplementationProvider extends DefinitionProviderBase implements } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, ) { - return vscode.languages.registerImplementationProvider(selector, - new TypeScriptImplementationProvider(client)); + return conditionalRegistration([ + requireSomeCapability(client, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerImplementationProvider(selector.semantic, + new TypeScriptImplementationProvider(client)); + }); } diff --git a/extensions/typescript-language-features/src/features/jsDocCompletions.ts b/extensions/typescript-language-features/src/languageFeatures/jsDocCompletions.ts similarity index 93% rename from extensions/typescript-language-features/src/features/jsDocCompletions.ts rename to extensions/typescript-language-features/src/languageFeatures/jsDocCompletions.ts index 80b0e219705..6e091af1692 100644 --- a/extensions/typescript-language-features/src/features/jsDocCompletions.ts +++ b/extensions/typescript-language-features/src/languageFeatures/jsDocCompletions.ts @@ -6,7 +6,8 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { ITypeScriptServiceClient } from '../typescriptService'; -import { ConfigurationDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireConfiguration } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; import * as typeConverters from '../utils/typeConverters'; @@ -110,12 +111,14 @@ export function templateToSnippet(template: string): vscode.SnippetString { } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, modeId: string, client: ITypeScriptServiceClient, ): vscode.Disposable { - return new ConfigurationDependentRegistration(modeId, 'suggest.completeJSDocs', () => { - return vscode.languages.registerCompletionItemProvider(selector, + return conditionalRegistration([ + requireConfiguration(modeId, 'suggest.completeJSDocs') + ], () => { + return vscode.languages.registerCompletionItemProvider(selector.syntax, new JsDocCompletionProvider(client), '*'); }); diff --git a/extensions/typescript-language-features/src/features/languageConfiguration.ts b/extensions/typescript-language-features/src/languageFeatures/languageConfiguration.ts similarity index 98% rename from extensions/typescript-language-features/src/features/languageConfiguration.ts rename to extensions/typescript-language-features/src/languageFeatures/languageConfiguration.ts index 91cb4a36c91..e7f0af0ff9c 100644 --- a/extensions/typescript-language-features/src/features/languageConfiguration.ts +++ b/extensions/typescript-language-features/src/languageFeatures/languageConfiguration.ts @@ -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 84% rename from extensions/typescript-language-features/src/features/organizeImports.ts rename to extensions/typescript-language-features/src/languageFeatures/organizeImports.ts index b5d492063f9..218cda4103f 100644 --- a/extensions/typescript-language-features/src/features/organizeImports.ts +++ b/extensions/typescript-language-features/src/languageFeatures/organizeImports.ts @@ -6,14 +6,15 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import type * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; -import { Command, CommandManager } from '../utils/commandManager'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import { nulToken } from '../utils/cancellation'; +import { Command, CommandManager } from '../commands/commandManager'; +import { conditionalRegistration, requireMinVersion, requireSomeCapability } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; +import { TelemetryReporter } from '../utils/telemetry'; import * as typeconverts from '../utils/typeConverters'; import FileConfigurationManager from './fileConfigurationManager'; -import { TelemetryReporter } from '../utils/telemetry'; -import { nulToken } from '../utils/cancellation'; const localize = nls.loadMessageBundle(); @@ -99,15 +100,18 @@ export class OrganizeImportsCodeActionProvider implements vscode.CodeActionProvi } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, commandManager: CommandManager, fileConfigurationManager: FileConfigurationManager, telemetryReporter: TelemetryReporter, ) { - return new VersionDependentRegistration(client, OrganizeImportsCodeActionProvider.minVersion, () => { + return conditionalRegistration([ + requireMinVersion(client, OrganizeImportsCodeActionProvider.minVersion), + requireSomeCapability(client, ClientCapability.Semantic), + ], () => { const organizeImportsProvider = new OrganizeImportsCodeActionProvider(client, commandManager, fileConfigurationManager, telemetryReporter); - return vscode.languages.registerCodeActionsProvider(selector, + return vscode.languages.registerCodeActionsProvider(selector.semantic, organizeImportsProvider, organizeImportsProvider.metadata); }); diff --git a/extensions/typescript-language-features/src/features/quickFix.ts b/extensions/typescript-language-features/src/languageFeatures/quickFix.ts similarity index 78% rename from extensions/typescript-language-features/src/features/quickFix.ts rename to extensions/typescript-language-features/src/languageFeatures/quickFix.ts index 0023c41600e..0965a574721 100644 --- a/extensions/typescript-language-features/src/features/quickFix.ts +++ b/extensions/typescript-language-features/src/languageFeatures/quickFix.ts @@ -5,13 +5,17 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; +import { Command, CommandManager } from '../commands/commandManager'; import type * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { nulToken } from '../utils/cancellation'; import { applyCodeActionCommands, getEditForCodeAction } from '../utils/codeAction'; -import { Command, CommandManager } from '../utils/commandManager'; +import { conditionalRegistration, requireSomeCapability } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; +import * as fixNames from '../utils/fixNames'; import { memoize } from '../utils/memoize'; +import { equals } from '../utils/objects'; import { TelemetryReporter } from '../utils/telemetry'; import * as typeConverters from '../utils/typeConverters'; import { DiagnosticsManager } from './diagnostics'; @@ -131,6 +135,7 @@ class VsCodeCodeAction extends vscode.CodeAction { public readonly tsAction: Proto.CodeFixAction, title: string, kind: vscode.CodeActionKind, + public readonly isFixAll: boolean, ) { super(title, kind); } @@ -145,7 +150,22 @@ class CodeActionSet { } 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) { @@ -231,9 +251,8 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { } const allActions = Array.from(results.values); - const allTsActions = allActions.map(x => x.tsAction); for (const action of allActions) { - action.isPreferred = isPreferredFix(action.tsAction, allTsActions); + action.isPreferred = isPreferredFix(action, allActions); } return allActions; } @@ -276,7 +295,7 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { diagnostic: vscode.Diagnostic, tsAction: Proto.CodeFixAction ): VsCodeCodeAction { - const codeAction = new VsCodeCodeAction(tsAction, tsAction.description, vscode.CodeActionKind.QuickFix); + const codeAction = new VsCodeCodeAction(tsAction, tsAction.description, vscode.CodeActionKind.QuickFix, false); codeAction.edit = getEditForCodeAction(this.client, tsAction); codeAction.diagnostics = [diagnostic]; codeAction.command = { @@ -312,7 +331,7 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { const action = new VsCodeCodeAction( tsAction, tsAction.fixAllDescription || localize('fixAllInFileLabel', '{0} (Fix all in file)', tsAction.description), - vscode.CodeActionKind.QuickFix); + vscode.CodeActionKind.QuickFix, true); action.diagnostics = [diagnostic]; action.command = { command: ApplyFixAllCodeAction.ID, @@ -332,49 +351,71 @@ const fixAllErrorCodes = new Map([ [2345, 2339], ]); - -const preferredFixes = new Map([ - ['annotateWithTypeFromJSDoc', 0], - ['constructorForDerivedNeedSuperCall', 0], - ['extendsInterfaceBecomesImplements', 0], - ['fixAwaitInSyncFunction', 0], - ['fixClassIncorrectlyImplementsInterface', 1], - ['fixUnreachableCode', 0], - ['unusedIdentifier', 0], - ['forgottenThisPropertyAccess', 0], - ['spelling', 1], - ['addMissingAwait', 0], +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( - tsAction: Proto.CodeFixAction, - allActions: readonly Proto.CodeFixAction[] + action: VsCodeCodeAction, + allActions: readonly VsCodeCodeAction[] ): boolean { - const priority = preferredFixes.get(tsAction.fixName); - if (typeof priority === 'undefined') { + if (action.isFixAll) { return false; } + + const fixPriority = preferredFixes.get(action.tsAction.fixName); + if (!fixPriority) { + return false; + } + return allActions.every(otherAction => { - if (otherAction === tsAction) { + if (otherAction === action) { return true; } - const otherPriority = preferredFixes.get(otherAction.fixName); - if (typeof otherPriority === 'undefined') { + + if (otherAction.isFixAll) { return true; } - return priority >= otherPriority; + + const otherFixPriority = preferredFixes.get(otherAction.tsAction.fixName); + if (!otherFixPriority || otherFixPriority.value < fixPriority.value) { + return true; + } else if (otherFixPriority.value > fixPriority.value) { + return false; + } + + if (fixPriority.thereCanOnlyBeOne && action.tsAction.fixName === otherAction.tsAction.fixName) { + return false; + } + + return true; }); } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, fileConfigurationManager: FileConfigurationManager, commandManager: CommandManager, diagnosticsManager: DiagnosticsManager, telemetryReporter: TelemetryReporter ) { - return vscode.languages.registerCodeActionsProvider(selector, - new TypeScriptQuickFixProvider(client, fileConfigurationManager, commandManager, diagnosticsManager, telemetryReporter), - TypeScriptQuickFixProvider.metadata); + return conditionalRegistration([ + requireSomeCapability(client, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerCodeActionsProvider(selector.semantic, + new TypeScriptQuickFixProvider(client, fileConfigurationManager, commandManager, diagnosticsManager, telemetryReporter), + TypeScriptQuickFixProvider.metadata); + }); } diff --git a/extensions/typescript-language-features/src/features/refactor.ts b/extensions/typescript-language-features/src/languageFeatures/refactor.ts similarity index 92% rename from extensions/typescript-language-features/src/features/refactor.ts rename to extensions/typescript-language-features/src/languageFeatures/refactor.ts index 39de21a6feb..fe1d3eeee3c 100644 --- a/extensions/typescript-language-features/src/features/refactor.ts +++ b/extensions/typescript-language-features/src/languageFeatures/refactor.ts @@ -5,13 +5,14 @@ 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 { ITypeScriptServiceClient } from '../typescriptService'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { nulToken } from '../utils/cancellation'; -import { Command, CommandManager } from '../utils/commandManager'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireMinVersion, requireSomeCapability } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; import * as fileSchemes from '../utils/fileSchemes'; import { TelemetryReporter } from '../utils/telemetry'; import * as typeConverters from '../utils/typeConverters'; @@ -22,14 +23,10 @@ const localize = nls.loadMessageBundle(); namespace Experimental { export interface RefactorActionInfo extends Proto.RefactorActionInfo { - readonly error?: string + readonly notApplicableReason?: string; } - export type RefactorTriggerReason = RefactorInvokedReason; - - export interface RefactorInvokedReason { - readonly kind: 'invoked'; - } + export type RefactorTriggerReason = 'implicit' | 'invoked'; export interface GetApplicableRefactorsRequestArgs extends Proto.FileRangeRequestArgs { readonly triggerReason?: RefactorTriggerReason; @@ -275,11 +272,11 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { return this.appendInvalidActions(actions); } - private toTsTriggerReason(context: vscode.CodeActionContext): Experimental.RefactorInvokedReason | undefined { + private toTsTriggerReason(context: vscode.CodeActionContext): Experimental.RefactorTriggerReason | undefined { if (!context.only) { return; } - return { kind: 'invoked' }; + return 'invoked'; } private convertApplicableRefactors( @@ -316,16 +313,16 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { const codeAction = new vscode.CodeAction(action.description, TypeScriptRefactorProvider.getKind(action)); // https://github.com/microsoft/TypeScript/pull/37871 - if (action.error) { - codeAction.disabled = { reason: action.error }; - return codeAction; + 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.command = { - title: action.description, - command: ApplyRefactoringCommand.ID, - arguments: [document, info.name, action.name, rangeOrSelection], - }; codeAction.isPreferred = TypeScriptRefactorProvider.isPreferred(action, allActions); return codeAction; } @@ -400,14 +397,17 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, formattingOptionsManager: FormattingOptionsManager, commandManager: CommandManager, telemetryReporter: TelemetryReporter, ) { - return new VersionDependentRegistration(client, TypeScriptRefactorProvider.minVersion, () => { - return vscode.languages.registerCodeActionsProvider(selector, + return conditionalRegistration([ + requireMinVersion(client, TypeScriptRefactorProvider.minVersion), + requireSomeCapability(client, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerCodeActionsProvider(selector.semantic, new TypeScriptRefactorProvider(client, formattingOptionsManager, commandManager, telemetryReporter), TypeScriptRefactorProvider.metadata); }); diff --git a/extensions/typescript-language-features/src/features/references.ts b/extensions/typescript-language-features/src/languageFeatures/references.ts similarity index 74% rename from extensions/typescript-language-features/src/features/references.ts rename to extensions/typescript-language-features/src/languageFeatures/references.ts index d77ecc5b10b..1ee8d150429 100644 --- a/extensions/typescript-language-features/src/features/references.ts +++ b/extensions/typescript-language-features/src/languageFeatures/references.ts @@ -4,7 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; +import { conditionalRegistration, requireSomeCapability } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; import * as typeConverters from '../utils/typeConverters'; class TypeScriptReferenceSupport implements vscode.ReferenceProvider { @@ -42,9 +44,13 @@ class TypeScriptReferenceSupport implements vscode.ReferenceProvider { } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient ) { - return vscode.languages.registerReferenceProvider(selector, - new TypeScriptReferenceSupport(client)); + return conditionalRegistration([ + requireSomeCapability(client, ClientCapability.EnhancedSyntax, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerReferenceProvider(selector.syntax, + new TypeScriptReferenceSupport(client)); + }); } diff --git a/extensions/typescript-language-features/src/features/rename.ts b/extensions/typescript-language-features/src/languageFeatures/rename.ts similarity index 89% rename from extensions/typescript-language-features/src/features/rename.ts rename to extensions/typescript-language-features/src/languageFeatures/rename.ts index 9074faa4372..e3e1b3c694e 100644 --- a/extensions/typescript-language-features/src/features/rename.ts +++ b/extensions/typescript-language-features/src/languageFeatures/rename.ts @@ -7,8 +7,10 @@ import * as path from 'path'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import type * as Proto from '../protocol'; -import { ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; +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'; @@ -137,10 +139,14 @@ class TypeScriptRenameProvider implements vscode.RenameProvider { } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, fileConfigurationManager: FileConfigurationManager, ) { - return vscode.languages.registerRenameProvider(selector, - new TypeScriptRenameProvider(client, fileConfigurationManager)); + return conditionalRegistration([ + requireSomeCapability(client, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerRenameProvider(selector.semantic, + new TypeScriptRenameProvider(client, fileConfigurationManager)); + }); } diff --git a/extensions/typescript-language-features/src/features/semanticTokens.ts b/extensions/typescript-language-features/src/languageFeatures/semanticTokens.ts similarity index 93% rename from extensions/typescript-language-features/src/features/semanticTokens.ts rename to extensions/typescript-language-features/src/languageFeatures/semanticTokens.ts index 92b6e479330..c404ac0b95a 100644 --- a/extensions/typescript-language-features/src/features/semanticTokens.ts +++ b/extensions/typescript-language-features/src/languageFeatures/semanticTokens.ts @@ -3,26 +3,33 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; -import { ITypeScriptServiceClient, ExecConfig, ServerResponse } from '../typescriptService'; -import * as Proto from '../protocol'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; -import API from '../utils/api'; - // all constants are const -import { TokenType, TokenModifier, TokenEncodingConsts, VersionRequirement } from 'typescript-vscode-sh-plugin/lib/constants'; +import { TokenEncodingConsts, TokenModifier, TokenType, VersionRequirement } from 'typescript-vscode-sh-plugin/lib/constants'; +import * as vscode from 'vscode'; +import * as Proto from '../protocol'; +import { ClientCapability, ExecConfig, ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; +import API from '../utils/api'; +import { conditionalRegistration, requireSomeCapability, requireMinVersion } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; + const minTypeScriptVersion = API.fromVersionString(`${VersionRequirement.major}.${VersionRequirement.minor}`); // 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: vscode.DocumentSelector, client: ITypeScriptServiceClient) { - return new VersionDependentRegistration(client, minTypeScriptVersion, () => { +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, provider, provider.getLegend()), + vscode.languages.registerDocumentRangeSemanticTokensProvider(selector.semantic, provider, provider.getLegend()), ); }); } diff --git a/extensions/typescript-language-features/src/features/signatureHelp.ts b/extensions/typescript-language-features/src/languageFeatures/signatureHelp.ts similarity index 86% rename from extensions/typescript-language-features/src/features/signatureHelp.ts rename to extensions/typescript-language-features/src/languageFeatures/signatureHelp.ts index 8e751f8c114..93901b9bb25 100644 --- a/extensions/typescript-language-features/src/features/signatureHelp.ts +++ b/extensions/typescript-language-features/src/languageFeatures/signatureHelp.ts @@ -5,7 +5,9 @@ import * as vscode from 'vscode'; import type * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; +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'; @@ -120,12 +122,16 @@ function toTsTriggerReason(context: vscode.SignatureHelpContext): Proto.Signatur } } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, ) { - return vscode.languages.registerSignatureHelpProvider(selector, - new TypeScriptSignatureHelpProvider(client), { - triggerCharacters: TypeScriptSignatureHelpProvider.triggerCharacters, - retriggerCharacters: TypeScriptSignatureHelpProvider.retriggerCharacters + return conditionalRegistration([ + requireSomeCapability(client, ClientCapability.EnhancedSyntax, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerSignatureHelpProvider(selector.syntax, + new TypeScriptSignatureHelpProvider(client), { + triggerCharacters: TypeScriptSignatureHelpProvider.triggerCharacters, + retriggerCharacters: TypeScriptSignatureHelpProvider.retriggerCharacters + }); }); } diff --git a/extensions/typescript-language-features/src/features/smartSelect.ts b/extensions/typescript-language-features/src/languageFeatures/smartSelect.ts similarity index 82% rename from extensions/typescript-language-features/src/features/smartSelect.ts rename to extensions/typescript-language-features/src/languageFeatures/smartSelect.ts index 651535c5bcf..f769347f15c 100644 --- a/extensions/typescript-language-features/src/features/smartSelect.ts +++ b/extensions/typescript-language-features/src/languageFeatures/smartSelect.ts @@ -7,7 +7,8 @@ import * as vscode from 'vscode'; 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))); + return conditionalRegistration([ + requireMinVersion(client, SmartSelection.minVersion), + ], () => { + return vscode.languages.registerSelectionRangeProvider(selector.syntax, new SmartSelection(client)); + }); } diff --git a/extensions/typescript-language-features/src/features/tagClosing.ts b/extensions/typescript-language-features/src/languageFeatures/tagClosing.ts similarity index 75% rename from extensions/typescript-language-features/src/features/tagClosing.ts rename to extensions/typescript-language-features/src/languageFeatures/tagClosing.ts index 03b65c4ea2a..289ce73b293 100644 --- a/extensions/typescript-language-features/src/features/tagClosing.ts +++ b/extensions/typescript-language-features/src/languageFeatures/tagClosing.ts @@ -7,8 +7,9 @@ import * as vscode from 'vscode'; import type * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; -import { ConditionalRegistration, ConfigurationDependentRegistration, VersionDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireMinVersion, requireConfiguration, Condition } from '../utils/dependentRegistration'; import { Disposable } from '../utils/dispose'; +import { DocumentSelector } from '../utils/documentSelector'; import * as typeConverters from '../utils/typeConverters'; class TagClosing extends Disposable { @@ -135,41 +136,29 @@ class TagClosing extends Disposable { } } -export class ActiveDocumentDependentRegistration extends Disposable { - private readonly _registration: ConditionalRegistration; - - constructor( - private readonly selector: vscode.DocumentSelector, - register: () => vscode.Disposable, - ) { - super(); - this._registration = this._register(new ConditionalRegistration(register)); - vscode.window.onDidChangeActiveTextEditor(this.update, this, this._disposables); - vscode.workspace.onDidOpenTextDocument(this.onDidOpenDocument, this, this._disposables); - this.update(); - } - - private update() { - const editor = vscode.window.activeTextEditor; - const enabled = !!(editor && vscode.languages.match(this.selector, editor.document)); - this._registration.update(enabled); - } - - private onDidOpenDocument(openedDocument: vscode.TextDocument) { - if (vscode.window.activeTextEditor && vscode.window.activeTextEditor.document === openedDocument) { - // The active document's language may have changed - this.update(); - } - } +function requireActiveDocument( + selector: vscode.DocumentSelector +) { + return new Condition( + () => { + const editor = vscode.window.activeTextEditor; + return !!(editor && vscode.languages.match(selector, editor.document)); + }, + handler => { + return vscode.Disposable.from( + vscode.window.onDidChangeActiveTextEditor(handler), + vscode.workspace.onDidOpenTextDocument(handler)); + }); } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, modeId: string, client: ITypeScriptServiceClient, ) { - return new VersionDependentRegistration(client, TagClosing.minVersion, () => - new ConfigurationDependentRegistration(modeId, 'autoClosingTags', () => - new ActiveDocumentDependentRegistration(selector, () => - new TagClosing(client)))); + return conditionalRegistration([ + requireMinVersion(client, TagClosing.minVersion), + requireConfiguration(modeId, 'autoClosingTags'), + requireActiveDocument(selector.syntax) + ], () => new TagClosing(client)); } diff --git a/extensions/typescript-language-features/src/features/tsconfig.ts b/extensions/typescript-language-features/src/languageFeatures/tsconfig.ts similarity index 100% rename from extensions/typescript-language-features/src/features/tsconfig.ts rename to extensions/typescript-language-features/src/languageFeatures/tsconfig.ts diff --git a/extensions/typescript-language-features/src/features/typeDefinitions.ts b/extensions/typescript-language-features/src/languageFeatures/typeDefinitions.ts similarity index 62% rename from extensions/typescript-language-features/src/features/typeDefinitions.ts rename to extensions/typescript-language-features/src/languageFeatures/typeDefinitions.ts index 6f63e44df0a..4aef4f41ed4 100644 --- a/extensions/typescript-language-features/src/features/typeDefinitions.ts +++ b/extensions/typescript-language-features/src/languageFeatures/typeDefinitions.ts @@ -4,7 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; +import { conditionalRegistration, requireSomeCapability } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; import DefinitionProviderBase from './definitionProviderBase'; export default class TypeScriptTypeDefinitionProvider extends DefinitionProviderBase implements vscode.TypeDefinitionProvider { @@ -14,9 +16,13 @@ export default class TypeScriptTypeDefinitionProvider extends DefinitionProvider } export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, ) { - return vscode.languages.registerTypeDefinitionProvider(selector, - new TypeScriptTypeDefinitionProvider(client)); + return conditionalRegistration([ + requireSomeCapability(client, ClientCapability.EnhancedSyntax, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerTypeDefinitionProvider(selector.syntax, + new TypeScriptTypeDefinitionProvider(client)); + }); } diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/languageFeatures/updatePathsOnRename.ts similarity index 95% rename from extensions/typescript-language-features/src/features/updatePathsOnRename.ts rename to extensions/typescript-language-features/src/languageFeatures/updatePathsOnRename.ts index 16b764d5256..2c6354c01a8 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/languageFeatures/updatePathsOnRename.ts @@ -7,11 +7,11 @@ import * as path from 'path'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import type * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { Delayer } from '../utils/async'; import { nulToken } from '../utils/cancellation'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireSomeCapability, requireMinVersion } from '../utils/dependentRegistration'; import { Disposable } from '../utils/dispose'; import * as fileSchemes from '../utils/fileSchemes'; import { doesResourceLookLikeATypeScriptFile } from '../utils/languageDescription'; @@ -294,6 +294,10 @@ export function register( fileConfigurationManager: FileConfigurationManager, handles: (uri: vscode.Uri) => Promise, ) { - return new VersionDependentRegistration(client, UpdateImportsOnFileRenameHandler.minVersion, () => - new UpdateImportsOnFileRenameHandler(client, fileConfigurationManager, handles)); + return conditionalRegistration([ + requireMinVersion(client, UpdateImportsOnFileRenameHandler.minVersion), + requireSomeCapability(client, ClientCapability.Semantic), + ], () => { + return new UpdateImportsOnFileRenameHandler(client, fileConfigurationManager, handles); + }); } diff --git a/extensions/typescript-language-features/src/features/workspaceSymbols.ts b/extensions/typescript-language-features/src/languageFeatures/workspaceSymbols.ts similarity index 90% rename from extensions/typescript-language-features/src/features/workspaceSymbols.ts rename to extensions/typescript-language-features/src/languageFeatures/workspaceSymbols.ts index fb548abf854..73a142440e5 100644 --- a/extensions/typescript-language-features/src/features/workspaceSymbols.ts +++ b/extensions/typescript-language-features/src/languageFeatures/workspaceSymbols.ts @@ -11,6 +11,7 @@ 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) { @@ -45,12 +46,9 @@ class TypeScriptWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvide file = undefined; } else { const document = this.getDocument(); - if (!document) { - return []; - } + file = document ? await this.toOpenedFiledPath(document) : undefined; - file = await this.toOpenedFiledPath(document); - if (!file) { + if (!file && this.client.apiVersion.lt(API.v390)) { return []; } } @@ -93,11 +91,16 @@ class TypeScriptWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvide private toSymbolInformation(item: Proto.NavtoItem) { const label = TypeScriptWorkspaceSymbolProvider.getLabel(item); - return new vscode.SymbolInformation( + 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) { diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 644eed40441..92ab84be308 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -5,15 +5,15 @@ import { basename } from 'path'; import * as vscode from 'vscode'; +import { DiagnosticKind } from './languageFeatures/diagnostics'; +import FileConfigurationManager from './languageFeatures/fileConfigurationManager'; import { CachedResponse } from './tsServer/cachedResponse'; -import { DiagnosticKind } from './features/diagnostics'; -import FileConfigurationManager from './features/fileConfigurationManager'; import TypeScriptServiceClient from './typescriptServiceClient'; -import { CommandManager } from './utils/commandManager'; +import { CommandManager } from './commands/commandManager'; import { Disposable } from './utils/dispose'; +import { DocumentSelector } from './utils/documentSelector'; import * as fileSchemes from './utils/fileSchemes'; import { LanguageDescription } from './utils/languageDescription'; -import { memoize } from './utils/memoize'; import { TelemetryReporter } from './utils/telemetry'; import TypingsStatus from './utils/typingsStatus'; @@ -39,15 +39,17 @@ export default class LanguageProvider extends Disposable { client.onReady(() => this.registerProviders()); } - @memoize - private get documentSelector(): vscode.DocumentFilter[] { - const documentSelector = []; + private get documentSelector(): DocumentSelector { + const semantic: vscode.DocumentFilter[] = []; + const syntax: vscode.DocumentFilter[] = []; for (const language of this.description.modeIds) { - for (const scheme of fileSchemes.supportedSchemes) { - documentSelector.push({ language, scheme }); + syntax.push({ language }); + for (const scheme of fileSchemes.semanticSupportedSchemes) { + semantic.push({ language, scheme }); } } - return documentSelector; + + return { semantic, syntax }; } private async registerProviders(): Promise { @@ -56,30 +58,30 @@ export default class LanguageProvider extends Disposable { const cachedResponse = new CachedResponse(); await Promise.all([ - import('./features/completions').then(provider => this._register(provider.register(selector, this.description.id, this.client, this.typingsStatus, this.fileConfigurationManager, this.commandManager, this.telemetryReporter, this.onCompletionAccepted))), - import('./features/definitions').then(provider => this._register(provider.register(selector, this.client))), - import('./features/directiveCommentCompletions').then(provider => this._register(provider.register(selector, this.client))), - import('./features/documentHighlight').then(provider => this._register(provider.register(selector, this.client))), - import('./features/documentSymbol').then(provider => this._register(provider.register(selector, this.client, cachedResponse))), - import('./features/folding').then(provider => this._register(provider.register(selector, this.client))), - import('./features/formatting').then(provider => this._register(provider.register(selector, this.description.id, this.client, this.fileConfigurationManager))), - import('./features/hover').then(provider => this._register(provider.register(selector, this.client))), - import('./features/implementations').then(provider => this._register(provider.register(selector, this.client))), - import('./features/implementationsCodeLens').then(provider => this._register(provider.register(selector, this.description.id, this.client, cachedResponse))), - import('./features/jsDocCompletions').then(provider => this._register(provider.register(selector, this.description.id, this.client))), - import('./features/organizeImports').then(provider => this._register(provider.register(selector, this.client, this.commandManager, this.fileConfigurationManager, this.telemetryReporter))), - import('./features/quickFix').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.client.diagnosticsManager, this.telemetryReporter))), - import('./features/fixAll').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager, this.client.diagnosticsManager))), - import('./features/refactor').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.telemetryReporter))), - import('./features/references').then(provider => this._register(provider.register(selector, this.client))), - import('./features/referencesCodeLens').then(provider => this._register(provider.register(selector, this.description.id, this.client, cachedResponse))), - import('./features/rename').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager))), - import('./features/smartSelect').then(provider => this._register(provider.register(selector, this.client))), - import('./features/signatureHelp').then(provider => this._register(provider.register(selector, this.client))), - import('./features/tagClosing').then(provider => this._register(provider.register(selector, this.description.id, this.client))), - import('./features/typeDefinitions').then(provider => this._register(provider.register(selector, this.client))), - import('./features/semanticTokens').then(provider => this._register(provider.register(selector, this.client))), - import('./features/callHierarchy').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/completions').then(provider => this._register(provider.register(selector, this.description.id, this.client, this.typingsStatus, this.fileConfigurationManager, this.commandManager, this.telemetryReporter, this.onCompletionAccepted))), + import('./languageFeatures/definitions').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/directiveCommentCompletions').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/documentHighlight').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/documentSymbol').then(provider => this._register(provider.register(selector, this.client, cachedResponse))), + import('./languageFeatures/folding').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/formatting').then(provider => this._register(provider.register(selector, this.description.id, this.client, this.fileConfigurationManager))), + import('./languageFeatures/hover').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/implementations').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/codeLens/implementationsCodeLens').then(provider => this._register(provider.register(selector, this.description.id, this.client, cachedResponse))), + import('./languageFeatures/jsDocCompletions').then(provider => this._register(provider.register(selector, this.description.id, this.client))), + import('./languageFeatures/organizeImports').then(provider => this._register(provider.register(selector, this.client, this.commandManager, this.fileConfigurationManager, this.telemetryReporter))), + import('./languageFeatures/quickFix').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.client.diagnosticsManager, this.telemetryReporter))), + import('./languageFeatures/fixAll').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager, this.client.diagnosticsManager))), + import('./languageFeatures/refactor').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.telemetryReporter))), + import('./languageFeatures/references').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/codeLens/referencesCodeLens').then(provider => this._register(provider.register(selector, this.description.id, this.client, cachedResponse))), + import('./languageFeatures/rename').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager))), + import('./languageFeatures/smartSelect').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/signatureHelp').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/tagClosing').then(provider => this._register(provider.register(selector, this.description.id, this.client))), + import('./languageFeatures/typeDefinitions').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/semanticTokens').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/callHierarchy').then(provider => this._register(provider.register(selector, this.client))), ]); } @@ -122,16 +124,22 @@ export default class LanguageProvider extends Disposable { this.client.bufferSyncSupport.requestAllDiagnostics(); } - public diagnosticsReceived(diagnosticsKind: DiagnosticKind, file: vscode.Uri, diagnostics: (vscode.Diagnostic & { reportUnnecessary: any })[]): void { + public diagnosticsReceived(diagnosticsKind: DiagnosticKind, file: vscode.Uri, diagnostics: (vscode.Diagnostic & { reportUnnecessary: any, reportDeprecated: any })[]): void { const config = vscode.workspace.getConfiguration(this.id, file); const reportUnnecessary = config.get('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; })); } diff --git a/extensions/typescript-language-features/src/lazyClientHost.ts b/extensions/typescript-language-features/src/lazyClientHost.ts new file mode 100644 index 00000000000..7f136285215 --- /dev/null +++ b/extensions/typescript-language-features/src/lazyClientHost.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { 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; +} diff --git a/extensions/typescript-language-features/src/protocol.const.ts b/extensions/typescript-language-features/src/protocol.const.ts index 37fe42fd830..210e962c9aa 100644 --- a/extensions/typescript-language-features/src/protocol.const.ts +++ b/extensions/typescript-language-features/src/protocol.const.ts @@ -45,6 +45,7 @@ export class DiagnosticCategory { export class KindModifiers { public static readonly optional = 'optional'; + public static readonly depreacted = 'deprecated'; public static readonly color = 'color'; public static readonly dtsFile = '.d.ts'; @@ -72,3 +73,19 @@ export class DisplayPartKind { public static readonly punctuation = 'punctuation'; public static readonly text = 'text'; } + +export enum EventName { + syntaxDiag = 'syntaxDiag', + semanticDiag = 'semanticDiag', + suggestionDiag = 'suggestionDiag', + configFileDiag = 'configFileDiag', + telemetry = 'telemetry', + projectLanguageServiceState = 'projectLanguageServiceState', + projectsUpdatedInBackground = 'projectsUpdatedInBackground', + beginInstallTypes = 'beginInstallTypes', + endInstallTypes = 'endInstallTypes', + typesInstallerInitializationFailed = 'typesInstallerInitializationFailed', + surveyReady = 'surveyReady', + projectLoadingStart = 'projectLoadingStart', + projectLoadingFinish = 'projectLoadingFinish', +} diff --git a/extensions/typescript-language-features/src/features/task.ts b/extensions/typescript-language-features/src/task/taskProvider.ts similarity index 97% rename from extensions/typescript-language-features/src/features/task.ts rename to extensions/typescript-language-features/src/task/taskProvider.ts index b63ff25019f..0024a3596f3 100644 --- a/extensions/typescript-language-features/src/features/task.ts +++ b/extensions/typescript-language-features/src/task/taskProvider.ts @@ -11,7 +11,7 @@ import { ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; import { isTsConfigFileName } from '../utils/languageDescription'; import { Lazy } from '../utils/lazy'; import { isImplicitProjectConfigFile } from '../utils/tsconfig'; -import TsConfigProvider, { TSConfig } from '../utils/tsconfigProvider'; +import { TSConfig, TsConfigProvider } from './tsconfigProvider'; const localize = nls.loadMessageBundle(); @@ -35,7 +35,7 @@ interface TypeScriptTaskDefinition extends vscode.TaskDefinition { /** * Provides tasks for building `tsconfig.json` files in a project. */ -export default class TscTaskProvider implements vscode.TaskProvider { +class TscTaskProvider implements vscode.TaskProvider { private readonly projectInfoRequestTimeout = 2000; private autoDetect: AutoDetect = 'on'; @@ -291,3 +291,9 @@ export default class TscTaskProvider implements vscode.TaskProvider { 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 96% rename from extensions/typescript-language-features/src/utils/tsconfigProvider.ts rename to extensions/typescript-language-features/src/task/tsconfigProvider.ts index e77dfb306a9..d44b828e384 100644 --- a/extensions/typescript-language-features/src/utils/tsconfigProvider.ts +++ b/extensions/typescript-language-features/src/task/tsconfigProvider.ts @@ -12,7 +12,7 @@ export interface TSConfig { readonly workspaceFolder?: vscode.WorkspaceFolder; } -export default class TsConfigProvider { +export class TsConfigProvider { public async getConfigsForWorkspace(): Promise> { if (!vscode.workspace.workspaceFolders) { return []; diff --git a/extensions/typescript-language-features/src/test/completions.test.ts b/extensions/typescript-language-features/src/test/completions.test.ts index 0c928fb4a39..92a7da63f92 100644 --- a/extensions/typescript-language-features/src/test/completions.test.ts +++ b/extensions/typescript-language-features/src/test/completions.test.ts @@ -16,17 +16,19 @@ const insertModes = Object.freeze(['insert', 'replace']); suite('TypeScript Completions', () => { const configDefaults: VsCodeConfiguration = Object.freeze({ [Config.autoClosingBrackets]: 'always', - [Config.completeFunctionCalls]: false, + [Config.typescriptCompleteFunctionCalls]: false, [Config.insertMode]: 'insert', [Config.snippetSuggestions]: 'none', [Config.suggestSelection]: 'first', + [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(testDocumentUri, configDefaults); @@ -178,7 +180,7 @@ suite('TypeScript Completions', () => { }); test('completeFunctionCalls should complete function parameters when at end of word', async () => { - await updateConfig(testDocumentUri, { [Config.completeFunctionCalls]: true }); + await updateConfig(testDocumentUri, { [Config.typescriptCompleteFunctionCalls]: true }); // Complete with-in word const editor = await createTestEditor(testDocumentUri, @@ -196,7 +198,7 @@ suite('TypeScript Completions', () => { }); test.skip('completeFunctionCalls should complete function parameters when within word', async () => { - await updateConfig(testDocumentUri, { [Config.completeFunctionCalls]: true }); + await updateConfig(testDocumentUri, { [Config.typescriptCompleteFunctionCalls]: true }); const editor = await createTestEditor(testDocumentUri, `function abcdef(x, y, z) { }`, @@ -213,7 +215,7 @@ 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(testDocumentUri, { [Config.completeFunctionCalls]: true }); + await updateConfig(testDocumentUri, { [Config.typescriptCompleteFunctionCalls]: true }); const editor = await createTestEditor(testDocumentUri, `function abcdef(x, y, z) { }`, @@ -230,7 +232,7 @@ 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(testDocumentUri, { [Config.completeFunctionCalls]: true }); + await updateConfig(testDocumentUri, { [Config.typescriptCompleteFunctionCalls]: true }); const editor = await createTestEditor(testDocumentUri, `function abcdef(x, y, z) { }`, diff --git a/extensions/typescript-language-features/src/test/fixAll.test.ts b/extensions/typescript-language-features/src/test/fixAll.test.ts new file mode 100644 index 00000000000..37fae0bcfc8 --- /dev/null +++ b/extensions/typescript-language-features/src/test/fixAll.test.ts @@ -0,0 +1,139 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'mocha'; +import * as assert from 'assert'; +import * as vscode from 'vscode'; +import { disposeAll } from '../utils/dispose'; +import { createTestEditor, wait, joinLines } from './testUtils'; + +const testDocumentUri = vscode.Uri.parse('untitled:test.ts'); + +const emptyRange = new vscode.Range(new vscode.Position(0, 0), new vscode.Position(0, 0)); + +suite('TypeScript Fix All', () => { + + const _disposables: vscode.Disposable[] = []; + + teardown(async () => { + disposeAll(_disposables); + + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + + test('Fix all should remove unreachable code', async () => { + const editor = await createTestEditor(testDocumentUri, + `function foo() {`, + ` return 1;`, + ` return 2;`, + `};`, + `function boo() {`, + ` return 3;`, + ` return 4;`, + `};`, + ); + + await wait(2000); + + const fixes = await vscode.commands.executeCommand('vscode.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/jsdocSnippet.test.ts b/extensions/typescript-language-features/src/test/jsdocSnippet.test.ts index a982e46221f..d9e17a75644 100644 --- a/extensions/typescript-language-features/src/test/jsdocSnippet.test.ts +++ b/extensions/typescript-language-features/src/test/jsdocSnippet.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import 'mocha'; -import { templateToSnippet } from '../features/jsDocCompletions'; +import { templateToSnippet } from '../languageFeatures/jsDocCompletions'; const joinLines = (...args: string[]) => args.join('\n'); diff --git a/extensions/typescript-language-features/src/test/onEnter.test.ts b/extensions/typescript-language-features/src/test/onEnter.test.ts index c7d6c3a45dd..72e59b4482f 100644 --- a/extensions/typescript-language-features/src/test/onEnter.test.ts +++ b/extensions/typescript-language-features/src/test/onEnter.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; -import { CURSOR, withRandomFileEditor } from './testUtils'; +import { CURSOR, withRandomFileEditor, wait, joinLines } from './testUtils'; const onDocumentChange = (doc: vscode.TextDocument): Promise => { 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`); + assert.strictEqual( + document.getText(), + joinLines(`({`, + ` 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`); - }); - }); -}); \ No newline at end of file +}); diff --git a/extensions/typescript-language-features/src/test/quickFix.test.ts b/extensions/typescript-language-features/src/test/quickFix.test.ts new file mode 100644 index 00000000000..13200dbcf50 --- /dev/null +++ b/extensions/typescript-language-features/src/test/quickFix.test.ts @@ -0,0 +1,173 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import 'mocha'; +import * as vscode from 'vscode'; +import { disposeAll } from '../utils/dispose'; +import { createTestEditor, joinLines, retryUntilDocumentChanges, wait } from './testUtils'; + +suite('TypeScript Quick Fix', () => { + + const _disposables: vscode.Disposable[] = []; + + teardown(async () => { + disposeAll(_disposables); + + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + + test('Fix all should not be marked as preferred #97866', async () => { + const testDocumentUri = vscode.Uri.parse('untitled:test.ts'); + + const editor = await createTestEditor(testDocumentUri, + `export const _ = 1;`, + `const a$0 = 1;`, + `const b = 2;`, + ); + + await retryUntilDocumentChanges(testDocumentUri, { retries: 10, timeout: 500 }, _disposables, () => { + return vscode.commands.executeCommand('editor.action.autoFix'); + }); + + assert.strictEqual(editor.document.getText(), joinLines( + `export const _ = 1;`, + `const b = 2;`, + )); + }); + + test('Add import should be a preferred fix if there is only one possible import', async () => { + const testDocumentUri = workspaceFile('foo.ts'); + + await createTestEditor(testDocumentUri, + `export const foo = 1;`); + + const editor = await createTestEditor(workspaceFile('index.ts'), + `export const _ = 1;`, + `foo$0;` + ); + + await retryUntilDocumentChanges(testDocumentUri, { retries: 10, timeout: 500 }, _disposables, () => { + return vscode.commands.executeCommand('editor.action.autoFix'); + }); + + // Document should not have been changed here + + assert.strictEqual(editor.document.getText(), joinLines( + `import { foo } from "./foo";`, + ``, + `export const _ = 1;`, + `foo;` + )); + }); + + test('Add import should not be a preferred fix if are multiple possible imports', async () => { + await createTestEditor(workspaceFile('foo.ts'), + `export const foo = 1;`); + + await createTestEditor(workspaceFile('bar.ts'), + `export const foo = 1;`); + + const editor = await createTestEditor(workspaceFile('index.ts'), + `export const _ = 1;`, + `foo$0;` + ); + + await wait(3000); + + await vscode.commands.executeCommand('editor.action.autoFix'); + + await wait(500); + + assert.strictEqual(editor.document.getText(), joinLines( + `export const _ = 1;`, + `foo;` + )); + }); + + test('Only a single ts-ignore should be returned if there are multiple errors on one line #98274', async () => { + const testDocumentUri = workspaceFile('foojs.js'); + const editor = await createTestEditor(testDocumentUri, + `//@ts-check`, + `const a = require('./bla');`); + + await wait(3000); + + const fixes = await vscode.commands.executeCommand('vscode.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 index 89689d57ae6..f40edb2ca5d 100644 --- a/extensions/typescript-language-features/src/test/referencesCodeLens.test.ts +++ b/extensions/typescript-language-features/src/test/referencesCodeLens.test.ts @@ -92,7 +92,7 @@ suite('TypeScript References', () => { assert.strictEqual(codeLenses?.length, 0); }); - test('Should not show duplicate references on ES5 class (https://github.com/microsoft/vscode/issues/90396)', async () => { + 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() {`, @@ -101,6 +101,7 @@ suite('TypeScript References', () => { `A.x = {};`, ); + await wait(500); const codeLenses = await getCodeLenses(testDocumentUri); assert.strictEqual(codeLenses?.length, 1); }); diff --git a/extensions/typescript-language-features/src/test/server.test.ts b/extensions/typescript-language-features/src/test/server.test.ts index 6803701e678..7e27e366b5c 100644 --- a/extensions/typescript-language-features/src/test/server.test.ts +++ b/extensions/typescript-language-features/src/test/server.test.ts @@ -6,12 +6,13 @@ 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 { nulToken } from '../utils/cancellation'; -import Logger from '../utils/logger'; +import { Logger } from '../utils/logger'; import { TelemetryReporter } from '../utils/telemetry'; import Tracer from '../utils/tracer'; -import type * as Proto from '../protocol'; const NoopTelemetryReporter = new class implements TelemetryReporter { @@ -43,8 +44,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 +64,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', 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 2a5f4368d74..f417ceaadb5 100644 --- a/extensions/typescript-language-features/src/test/suggestTestHelpers.ts +++ b/extensions/typescript-language-features/src/test/suggestTestHelpers.ts @@ -5,28 +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[]) { - const didChangeDocument = onChangedDocument(uri, _disposables); - await vscode.commands.executeCommand('editor.action.triggerSuggest'); - await wait(1000); // Give time for suggestions to show - await vscode.commands.executeCommand('acceptSelectedSuggestion'); - return didChangeDocument; + 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); await vscode.commands.executeCommand('editor.action.triggerSuggest'); - await wait(1000); // Give time for suggestions to show + 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)); -} diff --git a/extensions/typescript-language-features/src/test/testUtils.ts b/extensions/typescript-language-features/src/test/testUtils.ts index 7b2c95dcf1c..ea5e41a26a8 100644 --- a/extensions/typescript-language-features/src/test/testUtils.ts +++ b/extensions/typescript-language-features/src/test/testUtils.ts @@ -68,9 +68,9 @@ 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); @@ -102,6 +102,7 @@ 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) => @@ -113,10 +114,12 @@ export async function updateConfig(documentUri: vscode.Uri, newConfig: VsCodeCon export const Config = Object.freeze({ autoClosingBrackets: 'editor.autoClosingBrackets', - completeFunctionCalls: 'typescript.suggest.completeFunctionCalls', + 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']); @@ -133,3 +136,38 @@ export async function enumerateConfig( await f(JSON.stringify(newConfig)); } } + + +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/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/tsServer/bufferSyncSupport.ts similarity index 95% rename from extensions/typescript-language-features/src/features/bufferSyncSupport.ts rename to extensions/typescript-language-features/src/tsServer/bufferSyncSupport.ts index 647db7a1187..fdc552a293c 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/tsServer/bufferSyncSupport.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; import type * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { ITypeScriptServiceClient, ClientCapability } from '../typescriptService'; import API from '../utils/api'; import { coalesce } from '../utils/arrays'; import { Delayer } from '../utils/async'; @@ -68,11 +68,16 @@ type BufferOperation = CloseOperation | OpenOperation | ChangeOperation; */ class BufferSynchronizer { - private readonly _pending = new ResourceMap(); + private readonly _pending: ResourceMap; constructor( - private readonly client: ITypeScriptServiceClient - ) { } + private readonly client: ITypeScriptServiceClient, + onCaseInsenitiveFileSystem: boolean + ) { + this._pending = new ResourceMap(undefined, { + onCaseInsenitiveFileSystem + }); + } public open(resource: vscode.Uri, args: Proto.OpenRequestArgs) { if (this.supportsBatching) { @@ -275,7 +280,7 @@ class PendingDiagnostics extends ResourceMap { .sort((a, b) => a.value - b.value) .map(entry => entry.resource); - const map = new ResourceMap(); + const map = new ResourceMap(undefined, this.config); for (const resource of orderedResources) { map.set(resource, undefined); } @@ -302,9 +307,9 @@ class GetErrRequest { onDone: () => void ) { const allFiles = coalesce(Array.from(files.entries).map(entry => client.normalizedPath(entry.resource))); - if (!allFiles.length) { + if (!allFiles.length || !client.capabilities.has(ClientCapability.Semantic)) { this._done = true; - onDone(); + 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 @@ -347,7 +352,8 @@ export default class BufferSyncSupport extends Disposable { constructor( client: ITypeScriptServiceClient, - modeIds: readonly string[] + modeIds: readonly string[], + onCaseInsenitiveFileSystem: boolean ) { super(); this.client = client; @@ -356,9 +362,9 @@ export default class BufferSyncSupport extends Disposable { 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.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); diff --git a/extensions/typescript-language-features/src/tsServer/cancellation.electron.ts b/extensions/typescript-language-features/src/tsServer/cancellation.electron.ts new file mode 100644 index 00000000000..853ca0c1594 --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/cancellation.electron.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as fs from 'fs'; +import { getTempFile } from '../utils/temp.electron'; +import Tracer from '../utils/tracer'; +import { OngoingRequestCanceller, OngoingRequestCancellerFactory } from './cancellation'; + +export class NodeRequestCanceller implements OngoingRequestCanceller { + public readonly cancellationPipeName: string; + + public constructor( + private readonly _serverId: string, + private readonly _tracer: Tracer, + ) { + this.cancellationPipeName = getTempFile('tscancellation'); + } + + public tryCancelOngoingRequest(seq: number): boolean { + if (!this.cancellationPipeName) { + return false; + } + this._tracer.logTrace(this._serverId, `TypeScript Server: trying to cancel ongoing request with sequence number ${seq}`); + try { + fs.writeFileSync(this.cancellationPipeName + seq, ''); + } catch { + // noop + } + return true; + } +} + + +export const nodeRequestCancellerFactory = new class implements OngoingRequestCancellerFactory { + create(serverId: string, tracer: Tracer): OngoingRequestCanceller { + return new NodeRequestCanceller(serverId, tracer); + } +}; diff --git a/extensions/typescript-language-features/src/tsServer/cancellation.ts b/extensions/typescript-language-features/src/tsServer/cancellation.ts new file mode 100644 index 00000000000..0eda4e574dc --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/cancellation.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import Tracer from '../utils/tracer'; + +export interface OngoingRequestCanceller { + readonly cancellationPipeName: string | undefined; + tryCancelOngoingRequest(seq: number): boolean; +} + +export interface OngoingRequestCancellerFactory { + create(serverId: string, tracer: Tracer): OngoingRequestCanceller; +} + +const noopRequestCanceller = new class implements OngoingRequestCanceller { + public readonly cancellationPipeName = undefined; + + public tryCancelOngoingRequest(_seq: number): boolean { + return false; + } +}; + +export const noopRequestCancellerFactory = new class implements OngoingRequestCancellerFactory { + create(_serverId: string, _tracer: Tracer): OngoingRequestCanceller { + return noopRequestCanceller; + } +}; diff --git a/extensions/typescript-language-features/src/utils/logDirectoryProvider.ts b/extensions/typescript-language-features/src/tsServer/logDirectoryProvider.electron.ts similarity index 84% rename from extensions/typescript-language-features/src/utils/logDirectoryProvider.ts rename to extensions/typescript-language-features/src/tsServer/logDirectoryProvider.electron.ts index af6886e7043..60b6965b5a7 100644 --- a/extensions/typescript-language-features/src/utils/logDirectoryProvider.ts +++ b/extensions/typescript-language-features/src/tsServer/logDirectoryProvider.electron.ts @@ -6,9 +6,10 @@ import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; -import { memoize } from './memoize'; +import { ILogDirectoryProvider } from './logDirectoryProvider'; +import { memoize } from '../utils/memoize'; -export default class LogDirectoryProvider { +export class NodeLogDirectoryProvider implements ILogDirectoryProvider { public constructor( private readonly context: vscode.ExtensionContext ) { } diff --git a/extensions/typescript-language-features/src/tsServer/logDirectoryProvider.ts b/extensions/typescript-language-features/src/tsServer/logDirectoryProvider.ts new file mode 100644 index 00000000000..75ef2316309 --- /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/server.ts b/extensions/typescript-language-features/src/tsServer/server.ts index 9eef0fc7b62..fc7841322bd 100644 --- a/extensions/typescript-language-features/src/tsServer/server.ts +++ b/extensions/typescript-language-features/src/tsServer/server.ts @@ -3,58 +3,38 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as stream from 'stream'; import * as vscode from 'vscode'; import 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, TypeScriptRequests } from '../typescriptService'; +import { TypeScriptServiceConfiguration } from '../utils/configuration'; import { Disposable } from '../utils/dispose'; import { TelemetryReporter } from '../utils/telemetry'; import Tracer from '../utils/tracer'; -import { TypeScriptVersion } from '../utils/versionProvider'; -import { Reader } from '../utils/wireProtocol'; -import { CallbackMap } from './callbackMap'; -import { RequestItem, RequestQueue, RequestQueueingType } from './requestQueue'; -import { TypeScriptServerError } from './serverError'; +import { OngoingRequestCanceller } from './cancellation'; +import { TypeScriptVersionManager } from './versionManager'; +import { TypeScriptVersion } from './versionProvider'; -export interface OngoingRequestCanceller { - tryCancelOngoingRequest(seq: number): boolean; -} - -export class PipeRequestCanceller implements OngoingRequestCanceller { - public constructor( - private readonly _serverId: string, - private readonly _cancellationPipeName: string | undefined, - private readonly _tracer: Tracer, - ) { } - - public tryCancelOngoingRequest(seq: number): boolean { - if (!this._cancellationPipeName) { - return false; - } - this._tracer.logTrace(this._serverId, `TypeScript Server: trying to cancel ongoing request with sequence number ${seq}`); - try { - fs.writeFileSync(this._cancellationPipeName + seq, ''); - } catch { - // noop - } - return true; - } +export enum ExectuionTarget { + Semantic, + Syntax } export interface ITypeScriptServer { readonly onEvent: vscode.Event; 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; } @@ -63,18 +43,34 @@ export interface TsServerDelegate { onFatalError(command: string, error: Error): void; } +export const enum TsServerProcessKind { + Main = 'main', + Syntax = 'syntax', + Semantic = 'semantic', + Diagnostics = 'diagnostics' +} + +export interface TsServerProcessFactory { + fork( + tsServerPath: string, + args: readonly string[], + kind: TsServerProcessKind, + configuration: TypeScriptServiceConfiguration, + versionManager: TypeScriptVersionManager, + ): TsServerProcess; +} + export interface TsServerProcess { - readonly stdout: stream.Readable; write(serverRequest: Proto.Request): void; - on(name: 'exit', handler: (code: number | null) => void): void; - on(name: 'error', handler: (error: Error) => void): void; + onData(handler: (data: Proto.Response) => void): void; + onExit(handler: (code: number | null) => void): void; + onError(handler: (error: Error) => void): void; kill(): void; } export class ProcessBasedTsServer extends Disposable implements ITypeScriptServer { - private readonly _reader: Reader; private readonly _requestQueue = new RequestQueue(); private readonly _callbacks = new CallbackMap(); private readonly _pendingResponses = new Set(); @@ -89,14 +85,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 +110,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) { @@ -197,9 +194,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, @@ -297,6 +294,14 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe } +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([ @@ -309,13 +314,16 @@ class RequestRouter { ]); constructor( - private readonly servers: ReadonlyArray<{ readonly server: ITypeScriptServer, readonly preferredCommands?: ReadonlySet }>, + 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: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise> | undefined { - if (RequestRouter.sharedCommands.has(command)) { - // Dispatch shared commands to all server but only return from first one one + 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); @@ -368,8 +376,8 @@ class RequestRouter { return firstRequest; } - for (const { preferredCommands, server } of this.servers) { - if (!preferredCommands || preferredCommands.has(command)) { + for (const { canRun, server } of this.servers) { + if (!canRun || canRun(command, executeInfo)) { return server.executeImpl(command, args, executeInfo); } } @@ -378,82 +386,13 @@ class RequestRouter { } } - -export class SyntaxRoutingTsServer extends Disposable implements ITypeScriptServer { - - private static readonly syntaxCommands = new Set([ - 'navtree', - 'getOutliningSpans', - 'jsxClosingTag', - 'selectionRange', - 'format', - 'formatonkey', - 'docCommentTemplate', - ]); - - private readonly syntaxServer: ITypeScriptServer; - private readonly semanticServer: ITypeScriptServer; - private readonly router: RequestRouter; - - public constructor( - servers: { syntax: ITypeScriptServer, semantic: ITypeScriptServer }, - delegate: TsServerDelegate, - ) { - super(); - - this.syntaxServer = servers.syntax; - this.semanticServer = servers.semantic; - - this.router = new RequestRouter( - [ - { server: this.syntaxServer, preferredCommands: SyntaxRoutingTsServer.syntaxCommands }, - { server: this.semanticServer, preferredCommands: undefined /* gets all other commands */ } - ], - delegate); - - this._register(this.syntaxServer.onEvent(e => this._onEvent.fire(e))); - this._register(this.semanticServer.onEvent(e => 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 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 onReaderError() { return this.semanticServer.onReaderError; } - - 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 }): 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 { - return this.router.execute(command, args, executeInfo); - } -} - - export class GetErrRoutingTsServer extends Disposable implements ITypeScriptServer { - private static readonly diagnosticEvents = new Set([ - 'configFileDiag', - 'syntaxDiag', - 'semanticDiag', - 'suggestionDiag' + private static readonly diagnosticEvents = new Set([ + EventName.configFileDiag, + EventName.syntaxDiag, + EventName.semanticDiag, + EventName.suggestionDiag ]); private readonly getErrServer: ITypeScriptServer; @@ -471,8 +410,8 @@ export class GetErrRoutingTsServer extends Disposable implements ITypeScriptServ this.router = new RequestRouter( [ - { server: this.getErrServer, preferredCommands: new Set(['geterr', 'geterrForProject']) }, - { server: this.mainServer, preferredCommands: undefined /* gets all other commands */ } + { server: this.getErrServer, canRun: (command) => ['geterr', 'geterrForProject'].includes(command) }, + { server: this.mainServer, canRun: undefined /* gets all other commands */ } ], delegate); @@ -507,8 +446,6 @@ export class GetErrRoutingTsServer extends Disposable implements ITypeScriptServ private readonly _onError = this._register(new vscode.EventEmitter()); public readonly onError = this._onError.event; - public get onReaderError() { return this.mainServer.onReaderError; } - public get tsServerLogFile() { return this.mainServer.tsServerLogFile; } public kill(): void { @@ -516,14 +453,154 @@ export class GetErrRoutingTsServer extends Disposable implements ITypeScriptServ this.mainServer.kill(); } - 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 { 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', + 'selectionRange', + 'format', + 'formatonkey', + 'docCommentTemplate', + ]); + + /** + * Commands that should always be run on the semantic server. + */ + private static readonly semanticCommands = new Set([ + 'geterr', + 'geterrForProject', + 'projectInfo' + ]); + + /** + * 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', + ]); + + private readonly syntaxServer: ITypeScriptServer; + private readonly semanticServer: ITypeScriptServer; + private readonly router: RequestRouter; + + private _projectLoading = true; + + public constructor( + servers: { syntax: ITypeScriptServer, semantic: ITypeScriptServer }, + delegate: TsServerDelegate, + enableDynamicRouting: boolean, + ) { + super(); + + this.syntaxServer = servers.syntax; + this.semanticServer = servers.semantic; + + this.router = new RequestRouter( + [ + { + server: this.syntaxServer, + canRun: (command, execInfo) => { + switch (execInfo.executionTarget) { + case ExectuionTarget.Semantic: return false; + case ExectuionTarget.Syntax: return true; + } + + if (SyntaxRoutingTsServer.syntaxAlwaysCommands.has(command)) { + return true; + } + if (SyntaxRoutingTsServer.semanticCommands.has(command)) { + return false; + } + if (enableDynamicRouting && this.projectLoading && SyntaxRoutingTsServer.syntaxAllowedCommands.has(command)) { + return true; + } + return false; + } + }, { + 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 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 } diff --git a/extensions/typescript-language-features/src/tsServer/serverError.ts b/extensions/typescript-language-features/src/tsServer/serverError.ts index ace77d8a903..42622669498 100644 --- a/extensions/typescript-language-features/src/tsServer/serverError.ts +++ b/extensions/typescript-language-features/src/tsServer/serverError.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import type * as Proto from '../protocol'; -import { TypeScriptVersion } from '../utils/versionProvider'; +import { TypeScriptVersion } from './versionProvider'; export class TypeScriptServerError extends Error { @@ -18,7 +18,7 @@ export class TypeScriptServerError extends Error { } private constructor( - serverId: string, + public readonly serverId: string, public readonly version: TypeScriptVersion, private readonly response: Proto.Response, public readonly serverMessage: string | undefined, @@ -38,11 +38,13 @@ export class TypeScriptServerError extends Error { /* __GDPR__FRAGMENT__ "TypeScriptRequestErrorProperties" : { "command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "sanitizedstack" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } + "serverid" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "sanitizedstack" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, } */ return { command: this.serverCommand, + serverid: this.serverId, sanitizedstack: this.sanitizedStack || '', } as const; } @@ -79,7 +81,7 @@ export class TypeScriptServerError extends Error { if (!message) { return ''; } - const regex = /(tsserver)?(\.(?:ts|tsx|js|jsx)(?::\d+(?::\d+)?)?)\)?$/igm; + const regex = /(\btsserver)?(\.(?:ts|tsx|js|jsx)(?::\d+(?::\d+)?)?)\)?$/igm; let serverStack = ''; while (true) { const match = regex.exec(message); diff --git a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts new file mode 100644 index 00000000000..01071f6e10e --- /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/utils/wireProtocol.ts b/extensions/typescript-language-features/src/tsServer/serverProcess.electron.ts similarity index 53% rename from extensions/typescript-language-features/src/utils/wireProtocol.ts rename to extensions/typescript-language-features/src/tsServer/serverProcess.electron.ts index 64450f14280..f9d70d858e0 100644 --- a/extensions/typescript-language-features/src/utils/wireProtocol.ts +++ b/extensions/typescript-language-features/src/tsServer/serverProcess.electron.ts @@ -3,9 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as stream from 'stream'; +import * as child_process from 'child_process'; +import * as fs from 'fs'; +import * as path from 'path'; +import type { Readable } from 'stream'; import * as vscode from 'vscode'; -import { Disposable } from './dispose'; +import * as nls from 'vscode-nls'; +import type * as Proto from '../protocol'; +import { TypeScriptServiceConfiguration } from '../utils/configuration'; +import { Disposable } from '../utils/dispose'; +import { TsServerProcess, TsServerProcessKind } from './server'; +import { TypeScriptVersionManager } from './versionManager'; + +const localize = nls.loadMessageBundle(); const defaultSize: number = 8192; const contentLength: string = 'Content-Length: '; @@ -80,12 +90,12 @@ class ProtocolBuffer { } } -export class Reader extends Disposable { +class Reader extends Disposable { private readonly buffer: ProtocolBuffer = new ProtocolBuffer(); private nextMessageLength: number = -1; - public constructor(readable: stream.Readable) { + public constructor(readable: Readable) { super(); readable.on('data', data => this.onLengthData(data)); } @@ -123,3 +133,95 @@ export class Reader extends Disposable { } } } + +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 debugPort = this.getDebugPort(kind); + const inspectFlag = process.env['TSS_DEBUG_BRK'] ? '--inspect-brk' : '--inspect'; + return [ + ...(debugPort ? [`${inspectFlag}=${debugPort}`] : []), + ...(configuration.maxTsServerMemory ? [`--max-old-space-size=${configuration.maxTsServerMemory}`] : []) + ]; + } + + 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 = process.env['TSS_DEBUG_BRK'] || process.env['TSS_DEBUG']; + if (value) { + const port = parseInt(value); + if (!isNaN(port)) { + return port; + } + } + return undefined; + } + + 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 89e4261fb05..a3187efc35e 100644 --- a/extensions/typescript-language-features/src/tsServer/spawner.ts +++ b/extensions/typescript-language-features/src/tsServer/spawner.ts @@ -3,59 +3,84 @@ * 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 type * as Proto from '../protocol'; +import { OngoingRequestCancellerFactory } from '../tsServer/cancellation'; +import { ClientCapabilities, ClientCapability } 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 Tracer from '../utils/tracer'; -import { TypeScriptVersion, TypeScriptVersionProvider } from '../utils/versionProvider'; -import { ITypeScriptServer, PipeRequestCanceller, ProcessBasedTsServer, SyntaxRoutingTsServer, TsServerProcess, TsServerDelegate, GetErrRoutingTsServer } 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'; -const enum ServerKind { - Main = 'main', - Syntax = 'syntax', - Semantic = 'semantic', - Diagnostics = 'diagnostics' +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 { let primaryServer: ITypeScriptServer; - if (this.shouldUseSeparateSyntaxServer(version, configuration)) { - primaryServer = new SyntaxRoutingTsServer({ - syntax: this.spawnTsServer(ServerKind.Syntax, version, configuration, pluginManager), - semantic: this.spawnTsServer(ServerKind.Semantic, version, configuration, pluginManager) - }, delegate); - } else { - primaryServer = this.spawnTsServer(ServerKind.Main, version, configuration, pluginManager); + 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; + } } if (this.shouldUseSeparateDiagnosticsServer(configuration)) { return new GetErrRoutingTsServer({ - getErr: this.spawnTsServer(ServerKind.Diagnostics, version, configuration, pluginManager), + getErr: this.spawnTsServer(TsServerProcessKind.Diagnostics, version, configuration, pluginManager, cancellerFactory), primary: primaryServer, }, delegate); } @@ -63,11 +88,27 @@ export class TypeScriptServerSpawner { return primaryServer; } - private shouldUseSeparateSyntaxServer( + private getCompositeServerType( version: TypeScriptVersion, + capabilities: ClientCapabilities, configuration: TypeScriptServiceConfiguration, - ): boolean { - return configuration.useSeparateSyntaxServer && !!version.apiVersion && version.apiVersion.gte(API.v340); + ): 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( @@ -77,14 +118,16 @@ export class TypeScriptServerSpawner { } 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) { @@ -95,41 +138,31 @@ 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), + 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 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 === ServerKind.Syntax) { + if (kind === TsServerProcessKind.Syntax) { args.push('--syntaxOnly'); } @@ -139,16 +172,17 @@ export class TypeScriptServerSpawner { args.push('--useSingleInferredProject'); } - if (configuration.disableAutomaticTypeAcquisition || kind === ServerKind.Syntax || kind === ServerKind.Diagnostics) { + if (configuration.disableAutomaticTypeAcquisition || kind === TsServerProcessKind.Syntax || kind === TsServerProcessKind.Diagnostics) { args.push('--disableAutomaticTypingAcquisition'); } - if (kind === ServerKind.Semantic || kind === ServerKind.Main) { + 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(); @@ -192,22 +226,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) { @@ -221,25 +240,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/utils/versionManager.ts b/extensions/typescript-language-features/src/tsServer/versionManager.ts similarity index 96% rename from extensions/typescript-language-features/src/utils/versionManager.ts rename to extensions/typescript-language-features/src/tsServer/versionManager.ts index 08f31787e40..4811fefd3a7 100644 --- a/extensions/typescript-language-features/src/utils/versionManager.ts +++ b/extensions/typescript-language-features/src/tsServer/versionManager.ts @@ -5,9 +5,9 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import { TypeScriptVersion, TypeScriptVersionProvider } from './versionProvider'; -import { Disposable } from './dispose'; import { TypeScriptServiceConfiguration } from '../utils/configuration'; +import { Disposable } from '../utils/dispose'; +import { ITypeScriptVersionProvider, TypeScriptVersion } from './versionProvider'; const localize = nls.loadMessageBundle(); @@ -24,7 +24,7 @@ export class TypeScriptVersionManager extends Disposable { public constructor( private configuration: TypeScriptServiceConfiguration, - private readonly versionProvider: TypeScriptVersionProvider, + private readonly versionProvider: ITypeScriptVersionProvider, private readonly workspaceState: vscode.Memento ) { super(); diff --git a/extensions/typescript-language-features/src/utils/versionProvider.ts b/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts similarity index 69% rename from extensions/typescript-language-features/src/utils/versionProvider.ts rename to extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts index 742efda6d28..15107bc4b5b 100644 --- a/extensions/typescript-language-features/src/utils/versionProvider.ts +++ b/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts @@ -2,70 +2,151 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; -import * as nls from 'vscode-nls'; -import API from './api'; -import { TypeScriptServiceConfiguration } from './configuration'; -import { RelativeWorkspacePathResolver } from './relativePathResolver'; +import API from '../utils/api'; +import { TypeScriptServiceConfiguration } from '../utils/configuration'; +import { RelativeWorkspacePathResolver } from '../utils/relativePathResolver'; +import { ITypeScriptVersionProvider, localize, TypeScriptVersion, TypeScriptVersionSource } from './versionProvider'; -const localize = nls.loadMessageBundle(); +export class DiskTypeScriptVersionProvider implements ITypeScriptVersionProvider { -const enum TypeScriptVersionSource { - Bundled = 'bundled', - TsNightlyExtension = 'ts-nightly-extension', - NodeModules = 'node-modules', - UserSetting = 'user-setting', - WorkspaceSetting = 'workspace-setting', -} + public constructor( + private configuration?: TypeScriptServiceConfiguration + ) { } -export class TypeScriptVersion { - - public readonly apiVersion: API | undefined; - - constructor( - public readonly source: TypeScriptVersionSource, - public readonly path: string, - private readonly _pathLabel?: string - ) { - this.apiVersion = TypeScriptVersion.getApiVersion(this.tsServerPath); + public updateConfiguration(configuration: TypeScriptServiceConfiguration): void { + this.configuration = configuration; } - public get tsServerPath(): string { - return path.join(this.path, 'tsserver.js'); + public get defaultVersion(): TypeScriptVersion { + return this.globalVersion || this.bundledVersion; } - public get pathLabel(): string { - return this._pathLabel ?? this.path; + 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 isValid(): boolean { - return this.apiVersion !== undefined; - } - - public eq(other: TypeScriptVersion): boolean { - if (this.path !== other.path) { - return false; + public get localVersion(): TypeScriptVersion | undefined { + const tsdkVersions = this.localTsdkVersions; + if (tsdkVersions && tsdkVersions.length) { + return tsdkVersions[0]; } - if (this.apiVersion === other.apiVersion) { + 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; - } - 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'); + 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'); } - public static getApiVersion(serverPath: string): API | undefined { - const version = TypeScriptVersion.getTypeScriptVersion(serverPath); + 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)) + ]; + } + + 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; } @@ -114,125 +195,3 @@ export class TypeScriptVersion { return desc.version ? API.fromVersionString(desc.version) : undefined; } } - -export class TypeScriptVersionProvider { - - public constructor( - private configuration: TypeScriptServiceConfiguration - ) { } - - public updateConfiguration(configuration: TypeScriptServiceConfiguration): void { - this.configuration = configuration; - } - - public get defaultVersion(): TypeScriptVersion { - return this.globalVersion || this.bundledVersion; - } - - public get globalVersion(): TypeScriptVersion | undefined { - if (this.configuration.globalTsdk) { - const globals = this.loadVersionsFromSetting(TypeScriptVersionSource.UserSetting, this.configuration.globalTsdk); - if (globals && globals.length) { - return globals[0]; - } - } - return this.contributedTsNextVersion; - } - - public get localVersion(): TypeScriptVersion | undefined { - const tsdkVersions = this.localTsdkVersions; - if (tsdkVersions && tsdkVersions.length) { - return tsdkVersions[0]; - } - - const nodeVersions = this.localNodeModulesVersions; - if (nodeVersions && nodeVersions.length === 1) { - return nodeVersions[0]; - } - return undefined; - } - - public get localVersions(): TypeScriptVersion[] { - const allVersions = this.localTsdkVersions.concat(this.localNodeModulesVersions); - const paths = new Set(); - 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.length > 1) { - label = path.join(root.name, relativePath); - } - - versions.push(new TypeScriptVersion(source, path.join(root.uri.fsPath, relativePath), label)); - } - return versions; - } -} diff --git a/extensions/typescript-language-features/src/tsServer/versionProvider.ts b/extensions/typescript-language-features/src/tsServer/versionProvider.ts new file mode 100644 index 00000000000..43f16c7c19d --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/versionProvider.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vscode-nls'; +import API from '../utils/api'; +import { TypeScriptServiceConfiguration } from '../utils/configuration'; + +export const localize = nls.loadMessageBundle(); + +export const enum TypeScriptVersionSource { + Bundled = 'bundled', + TsNightlyExtension = 'ts-nightly-extension', + NodeModules = 'node-modules', + UserSetting = 'user-setting', + WorkspaceSetting = 'workspace-setting', +} + +export class TypeScriptVersion { + + constructor( + public readonly source: TypeScriptVersionSource, + public readonly path: string, + public readonly apiVersion: API | undefined, + private readonly _pathLabel?: string, + ) { } + + public get tsServerPath(): string { + return this.path; + } + + public get pathLabel(): string { + return this._pathLabel ?? this.path; + } + + public get isValid(): boolean { + return this.apiVersion !== undefined; + } + + public eq(other: TypeScriptVersion): boolean { + if (this.path !== other.path) { + return false; + } + + if (this.apiVersion === other.apiVersion) { + return true; + } + if (!this.apiVersion || !other.apiVersion) { + return false; + } + return this.apiVersion.eq(other.apiVersion); + } + + public get displayName(): string { + const version = this.apiVersion; + return version ? version.displayName : localize( + 'couldNotLoadTsVersion', 'Could not load the TypeScript version at this path'); + } +} + +export interface ITypeScriptVersionProvider { + updateConfiguration(configuration: TypeScriptServiceConfiguration): void; + + readonly defaultVersion: TypeScriptVersion; + readonly globalVersion: TypeScriptVersion | undefined; + readonly localVersion: TypeScriptVersion | undefined; + readonly localVersions: readonly TypeScriptVersion[]; + readonly bundledVersion: TypeScriptVersion; +} diff --git a/extensions/typescript-language-features/src/utils/versionStatus.ts b/extensions/typescript-language-features/src/tsServer/versionStatus.ts similarity index 94% rename from extensions/typescript-language-features/src/utils/versionStatus.ts rename to extensions/typescript-language-features/src/tsServer/versionStatus.ts index e0b388e9eb1..20b9debc27b 100644 --- a/extensions/typescript-language-features/src/utils/versionStatus.ts +++ b/extensions/typescript-language-features/src/tsServer/versionStatus.ts @@ -5,12 +5,12 @@ 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 { Command, CommandManager } from '../utils/commandManager'; +import { Disposable } from '../utils/dispose'; import { isTypeScriptDocument } from '../utils/languageModeIds'; -import { isImplicitProjectConfigFile, openOrCreateConfig, openProjectConfigOrPromptToCreate, openProjectConfigForFile, ProjectType } from '../utils/tsconfig'; -import { Disposable } from './dispose'; +import { isImplicitProjectConfigFile, openOrCreateConfig, openProjectConfigForFile, openProjectConfigOrPromptToCreate, ProjectType } from '../utils/tsconfig'; import { TypeScriptVersion } from './versionProvider'; const localize = nls.loadMessageBundle(); @@ -151,9 +151,11 @@ export default class VersionStatus extends Disposable { this._ready = true; this.updateStatus(); }); + + this._register(this._client.onTsServerStarted(({ version }) => this.onDidChangeTypeScriptVersion(version))); } - public onDidChangeTypeScriptVersion(version: TypeScriptVersion) { + private onDidChangeTypeScriptVersion(version: TypeScriptVersion) { this._statusBarEntry.text = version.displayName; this._statusBarEntry.tooltip = version.path; this.updateStatus(); diff --git a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts index 1e0d59f1057..908735a7f2d 100644 --- a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts +++ b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts @@ -9,32 +9,43 @@ * ------------------------------------------------------------------------------------------ */ 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 { coalesce, flatten } from './utils/arrays'; -import { CommandManager } from './utils/commandManager'; +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 * 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 { @@ -43,28 +54,36 @@ export default class TypeScriptServiceClientHost extends Disposable { private readonly languagePerId = new Map(); private readonly typingsStatus: TypingsStatus; - private readonly versionStatus: VersionStatus; 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 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 }) => { @@ -74,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(this.client, commandManager)); - + 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); @@ -87,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); } }); @@ -125,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), @@ -217,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)); @@ -239,11 +274,19 @@ export default class TypeScriptServiceClientHost extends Disposable { return new vscode.DiagnosticRelatedInformation(typeConverters.Location.fromTextSpan(this.client.toResource(span.file), span), info.message); })); } + const tags: vscode.DiagnosticTag[] = []; if (diagnostic.reportsUnnecessary) { - converted.tags = [vscode.DiagnosticTag.Unnecessary]; + tags.push(vscode.DiagnosticTag.Unnecessary); } - (converted as vscode.Diagnostic & { reportUnnecessary: any }).reportUnnecessary = diagnostic.reportsUnnecessary; - return converted as vscode.Diagnostic & { reportUnnecessary: any }; + if (diagnostic.reportsDeprecated) { + tags.push(vscode.DiagnosticTag.Deprecated); + } + converted.tags = tags.length ? tags : undefined; + + const resultConverted = converted as vscode.Diagnostic & { reportUnnecessary: any, reportDeprecated: any }; + resultConverted.reportUnnecessary = diagnostic.reportsUnnecessary; + resultConverted.reportDeprecated = diagnostic.reportsDeprecated; + return resultConverted; } private getDiagnosticSeverity(diagnostic: Proto.Diagnostic): vscode.DiagnosticSeverity { @@ -270,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 eb6ff651b30..de32927d816 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -4,11 +4,14 @@ *--------------------------------------------------------------------------------------------*/ 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 { PluginManager } from './utils/plugins'; +import { TelemetryReporter } from './utils/telemetry'; export namespace ServerResponse { @@ -32,7 +35,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]; @@ -81,9 +84,39 @@ export type TypeScriptRequests = StandardTsServerRequests & NoResponseTsServerRe export type ExecConfig = { readonly lowPriority?: boolean; readonly nonRecoverable?: boolean; - readonly cancelOnResourceChange?: vscode.Uri + readonly cancelOnResourceChange?: vscode.Uri; + readonly executionTarget?: ExectuionTarget; }; +export enum ClientCapability { + /** + * Basic syntax server. All clients should support this. + */ + Syntax, + + /** + * Advanced syntax server that can provide single file IntelliSense. + */ + EnhancedSyntax, + + /** + * Complete, multi-file semantic server + */ + Semantic, +} + +export class ClientCapabilities { + private readonly capabilities: ReadonlySet; + + 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). @@ -113,20 +146,25 @@ export interface ITypeScriptServiceClient { 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 bufferSyncSupport: BufferSyncSupport; + readonly telemetryReporter: TelemetryReporter; execute( command: K, diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index ab94aa6a00e..69a3c049040 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -3,30 +3,32 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import BufferSyncSupport from './features/bufferSyncSupport'; -import { DiagnosticKind, DiagnosticsManager } from './features/diagnostics'; +import { DiagnosticKind, DiagnosticsManager } from './languageFeatures/diagnostics'; import * as Proto from './protocol'; -import { ITypeScriptServer } from './tsServer/server'; +import { EventName } from './protocol.const'; +import BufferSyncSupport from './tsServer/bufferSyncSupport'; +import { OngoingRequestCancellerFactory } from './tsServer/cancellation'; +import { ILogDirectoryProvider } from './tsServer/logDirectoryProvider'; +import { ITypeScriptServer, TsServerProcessFactory } from './tsServer/server'; import { TypeScriptServerError } from './tsServer/serverError'; import { TypeScriptServerSpawner } from './tsServer/spawner'; -import { ExecConfig, ITypeScriptServiceClient, ServerResponse, TypeScriptRequests } from './typescriptService'; +import { TypeScriptVersionManager } from './tsServer/versionManager'; +import { ITypeScriptVersionProvider, TypeScriptVersion } from './tsServer/versionProvider'; +import { ClientCapabilities, ClientCapability, ExecConfig, ITypeScriptServiceClient, ServerResponse, TypeScriptRequests } from './typescriptService'; import API from './utils/api'; import { TsServerLogLevel, TypeScriptServiceConfiguration } from './utils/configuration'; import { Disposable } from './utils/dispose'; import * as fileSchemes from './utils/fileSchemes'; -import LogDirectoryProvider from './utils/logDirectoryProvider'; -import Logger from './utils/logger'; +import { Logger } from './utils/logger'; +import { isWeb } from './utils/platform'; import { TypeScriptPluginPathsProvider } from './utils/pluginPathsProvider'; import { PluginManager } from './utils/plugins'; -import { TelemetryReporter, VSCodeTelemetryReporter, TelemetryProperties } from './utils/telemetry'; +import { TelemetryProperties, TelemetryReporter, VSCodeTelemetryReporter } from './utils/telemetry'; import Tracer from './utils/tracer'; import { inferredProjectCompilerOptions, ProjectType } from './utils/tsconfig'; -import { TypeScriptVersionManager } from './utils/versionManager'; -import { TypeScriptVersion, TypeScriptVersionProvider } from './utils/versionProvider'; const localize = nls.loadMessageBundle(); @@ -65,7 +67,7 @@ namespace ServerState { * Version reported by currently-running tsserver. */ public tsserverVersion: string | undefined, - public langaugeServiceEnabled: boolean, + public languageServiceEnabled: boolean, ) { } public readonly toCancelOnResourceChange = new Set(); @@ -74,8 +76,8 @@ namespace ServerState { this.tsserverVersion = tsserverVersion; } - updateLangaugeServiceEnabled(enabled: boolean) { - this.langaugeServiceEnabled = enabled; + updateLanguageServiceEnabled(enabled: boolean) { + this.languageServiceEnabled = enabled; } } @@ -83,6 +85,7 @@ namespace ServerState { readonly type = Type.Errored; constructor( public readonly error: Error, + public readonly tsServerLogFile: string | undefined, ) { } } @@ -90,14 +93,12 @@ namespace ServerState { } export default class TypeScriptServiceClient extends Disposable implements ITypeScriptServiceClient { - private static readonly WALK_THROUGH_SNIPPET_SCHEME_COLON = `${fileSchemes.walkThroughSnippet}:`; private readonly pathSeparator: string; private readonly inMemoryResourcePrefix = '^'; private _onReady?: { promise: Promise; resolve: () => void; reject: () => void; }; private _configuration: TypeScriptServiceConfiguration; - private versionProvider: TypeScriptVersionProvider; private pluginPathsProvider: TypeScriptPluginPathsProvider; private readonly _versionManager: TypeScriptVersionManager; @@ -114,41 +115,61 @@ export default class TypeScriptServiceClient extends Disposable implements IType 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, + onCaseInsenitiveFileSystem: boolean, + services: { + pluginManager: PluginManager, + logDirectoryProvider: ILogDirectoryProvider, + cancellerFactory: OngoingRequestCancellerFactory, + versionProvider: ITypeScriptVersionProvider, + processFactory: TsServerProcessFactory, + }, allModeIds: readonly string[] ) { super(); + + this.pluginManager = services.pluginManager; + this.logDirectoryProvider = services.logDirectoryProvider; + this.cancellerFactory = services.cancellerFactory; + this.versionProvider = services.versionProvider; + this.processFactory = services.processFactory; + this.pathSeparator = path.sep; this.lastStart = Date.now(); - // eslint-disable-next-line no-var - var p = new Promise((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.versionProvider.updateConfiguration(this._configuration); + 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); @@ -189,7 +210,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType return this.apiVersion.fullVersionString; })); - this.typescriptServerSpawner = new TypeScriptServerSpawner(this.versionProvider, this.logDirectoryProvider, this.pluginPathsProvider, this.logger, this.telemetryReporter, this.tracer); + this.typescriptServerSpawner = new TypeScriptServerSpawner(this.versionProvider, this._versionManager, this.logDirectoryProvider, this.pluginPathsProvider, this.logger, this.telemetryReporter, this.tracer, this.processFactory); this._register(this.pluginManager.onDidUpdateConfig(update => { this.configurePlugin(update.pluginId, update.config); @@ -200,6 +221,28 @@ export default class TypeScriptServiceClient extends Disposable implements IType })); } + public get capabilities() { + if (isWeb()) { + return new ClientCapabilities( + ClientCapability.Syntax, + ClientCapability.EnhancedSyntax); + } + + if (this.apiVersion.gte(API.v400)) { + return new ClientCapabilities( + ClientCapability.Syntax, + ClientCapability.EnhancedSyntax, + ClientCapability.Semantic); + } + + return new ClientCapabilities( + ClientCapability.Syntax, + ClientCapability.Semantic); + } + + private readonly _onDidChangeCapabilities = this._register(new vscode.EventEmitter()); + readonly onDidChangeCapabilities = this._onDidChangeCapabilities.event; + private cancelInflightRequestsForResource(resource: vscode.Uri): void { if (this.serverState.type !== ServerState.Type.Running) { return; @@ -238,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()); @@ -323,22 +366,21 @@ export default class TypeScriptServiceClient extends Disposable implements IType } let version = this._versionManager.currentVersion; - - this.info(`Using tsserver from: ${version.path}`); - if (!fs.existsSync(version.tsServerPath)) { + if (!version.isValid) { vscode.window.showWarningMessage(localize('noServerFound', 'The path {0} doesn\'t point to a valid tsserver install. Falling back to bundled TypeScript version.', version.path)); this._versionManager.reset(); version = this._versionManager.currentVersion; } + this.info(`Using tsserver from: ${version.path}`); + const apiVersion = version.apiVersion || API.defaultVersion; let mytoken = ++this.token; - const handle = this.typescriptServerSpawner.spawn(version, this.configuration, this.pluginManager, { + const handle = this.typescriptServerSpawner.spawn(version, this.capabilities, this.configuration, this.pluginManager, this.cancellerFactory, { onFatalError: (command, err) => this.fatalError(command, err), }); this.serverState = new ServerState.Running(handle, apiVersion, undefined, true); - this.onDidChangeTypeScriptVersion(version); this.lastStart = Date.now(); /* __GDPR__ @@ -365,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}`); @@ -412,18 +454,17 @@ 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(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; } @@ -491,6 +532,8 @@ export default class TypeScriptServiceClient extends Disposable implements IType preferences: { providePrefixAndSuffixTextForRename: true, allowRenameOfImportPath: true, + // @ts-expect-error, remove after 4.0 protocol update + includePackageJsonAutoImports: this._configuration.includePackageJsonAutoImports, }, watchOptions }; @@ -581,7 +624,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType if (item === reportIssueItem) { const args = previousState.type === ServerState.Type.Errored && previousState.error instanceof TypeScriptServerError - ? getReportIssueArgsForError(previousState.error) + ? getReportIssueArgsForError(previousState.error, previousState.tsServerLogFile) : undefined; vscode.commands.executeCommand('workbench.action.openIssueReporter', args); } @@ -594,27 +637,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); - } + switch (resource.scheme) { + case fileSchemes.file: + { + let result = resource.fsPath; + if (!result) { + return undefined; + } + result = path.normalize(result); - if (resource.scheme !== fileSchemes.file) { - return undefined; + // Both \ and / must be escaped in regular expressions + return result.replace(new RegExp('\\' + this.pathSeparator, 'g'), '/'); + } + case fileSchemes.git: + { + return undefined; + } + default: + { + return this.inMemoryResourcePrefix + resource.toString(true); + } } - - let result = resource.fsPath; - if (!result) { - return undefined; - } - - if (resource.scheme === fileSchemes.file) { - result = path.normalize(result); - } - - // Both \ and / must be escaped in regular expressions - return result.replace(new RegExp('\\' + this.pathSeparator, 'g'), '/'); } public toPath(resource: vscode.Uri): string | undefined { @@ -630,20 +673,10 @@ export default class TypeScriptServiceClient extends Disposable implements IType } public toResource(filepath: string): vscode.Uri { - if (filepath.startsWith(TypeScriptServiceClient.WALK_THROUGH_SNIPPET_SCHEME_COLON) || (filepath.startsWith(fileSchemes.untitled + ':')) - ) { - let resource = vscode.Uri.parse(filepath); - const dirName = path.dirname(resource.path); - const fileName = path.basename(resource.path); - if (fileName.startsWith(this.inMemoryResourcePrefix)) { - resource = resource.with({ - path: path.posix.join(dirName, fileName.slice(this.inMemoryResourcePrefix.length)) - }); - } - + if (filepath.startsWith(this.inMemoryResourcePrefix)) { + const resource = vscode.Uri.parse(filepath.slice(1)); return this.bufferSyncSupport.toVsCodeResource(resource); } - return this.bufferSyncSupport.toResource(filepath); } @@ -721,9 +754,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); @@ -751,18 +784,19 @@ export default class TypeScriptServiceClient extends Disposable implements IType if (this.serverState.type === ServerState.Type.Running) { this.info('Killing TS Server'); + const logfile = this.serverState.server.tsServerLogFile; this.serverState.server.kill(); if (error instanceof TypeScriptServerError) { - this.serverState = new ServerState.Errored(error); + 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(); @@ -776,52 +810,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.updateLangaugeServiceEnabled(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; } @@ -884,30 +918,62 @@ export default class TypeScriptServiceClient extends Disposable implements IType } } -function getReportIssueArgsForError(error: TypeScriptServerError): { extensionId: string, issueTitle: string, issueBody: string } | undefined { +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: `**TypeScript Version:** ${error.version.apiVersion?.fullVersionString} - -**Steps to reproduce crash** - -1. -2. -3. - -**TS Server Error Stack** - -\`\`\` -${error.serverStack} -\`\`\``, + issueBody: sections.join('\n\n') }; } diff --git a/extensions/typescript-language-features/src/utils/api.ts b/extensions/typescript-language-features/src/utils/api.ts index 1845285caa4..2a72b19004c 100644 --- a/extensions/typescript-language-features/src/utils/api.ts +++ b/extensions/typescript-language-features/src/utils/api.ts @@ -34,6 +34,7 @@ export default class API { 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 fromVersionString(versionString: string): API { let version = semver.valid(versionString); diff --git a/extensions/typescript-language-features/src/utils/configuration.ts b/extensions/typescript-language-features/src/utils/configuration.ts index f306503b83d..cde1e14b9a0 100644 --- a/extensions/typescript-language-features/src/utils/configuration.ts +++ b/extensions/typescript-language-features/src/utils/configuration.ts @@ -46,6 +46,11 @@ export namespace TsServerLogLevel { } } +export const enum SeparateSyntaxServerConfiguration { + Disabled, + Enabled, +} + export class TypeScriptServiceConfiguration { public readonly locale: string | null; public readonly globalTsdk: string | null; @@ -56,11 +61,12 @@ export class TypeScriptServiceConfiguration { 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: string | undefined; public static loadFromWorkspace(): TypeScriptServiceConfiguration { return new TypeScriptServiceConfiguration(); @@ -78,11 +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 { @@ -95,11 +102,12 @@ export class TypeScriptServiceConfiguration { && this.experimentalDecorators === other.experimentalDecorators && this.disableAutomaticTypeAcquisition === other.disableAutomaticTypeAcquisition && arrays.equals(this.tsServerPluginPaths, other.tsServerPluginPaths) - && this.useSeparateSyntaxServer === other.useSeparateSyntaxServer + && this.separateSyntaxServer === other.separateSyntaxServer && this.enableProjectDiagnostics === other.enableProjectDiagnostics && this.maxTsServerMemory === other.maxTsServerMemory && objects.equals(this.watchOptions, other.watchOptions) - && this.enablePromptUseWorkspaceTsdk === other.enablePromptUseWorkspaceTsdk; + && this.enablePromptUseWorkspaceTsdk === other.enablePromptUseWorkspaceTsdk + && this.includePackageJsonAutoImports === other.includePackageJsonAutoImports; } private static fixPathPrefixes(inspectValue: string): string { @@ -157,8 +165,12 @@ 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 { @@ -169,6 +181,10 @@ export class TypeScriptServiceConfiguration { return configuration.get('typescript.tsserver.watchOptions'); } + private static readIncludePackageJsonAutoImports(configuration: vscode.WorkspaceConfiguration): string | undefined { + return configuration.get('typescript.preferences.includePackageJsonAutoImports'); + } + private static readMaxTsServerMemory(configuration: vscode.WorkspaceConfiguration): number { const defaultMaxMemory = 3072; const minimumMaxMemory = 128; diff --git a/extensions/typescript-language-features/src/utils/dependentRegistration.ts b/extensions/typescript-language-features/src/utils/dependentRegistration.ts index 434379f57e1..29597ab50cb 100644 --- a/extensions/typescript-language-features/src/utils/dependentRegistration.ts +++ b/extensions/typescript-language-features/src/utils/dependentRegistration.ts @@ -4,28 +4,58 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { ITypeScriptServiceClient, ClientCapability } from '../typescriptService'; import API from './api'; import { Disposable } from './dispose'; -export class ConditionalRegistration { +export class Condition extends Disposable { + private _value: boolean; + + constructor( + private readonly getValue: () => boolean, + onUpdate: (handler: () => void) => void, + ) { + super(); + this._value = this.getValue(); + + onUpdate(() => { + const newValue = this.getValue(); + if (newValue !== this._value) { + this._value = newValue; + this._onDidChange.fire(); + } + }); + } + + public get value(): boolean { return this._value; } + + private readonly _onDidChange = this._register(new vscode.EventEmitter()); + public readonly onDidChange = this._onDidChange.event; +} + +class ConditionalRegistration { private registration: vscode.Disposable | undefined = undefined; public constructor( - private readonly _doRegister: () => vscode.Disposable - ) { } - - public dispose() { - if (this.registration) { - this.registration.dispose(); - this.registration = undefined; + private readonly conditions: readonly Condition[], + private readonly doRegister: () => vscode.Disposable + ) { + for (const condition of conditions) { + condition.onDidChange(() => this.update()); } + this.update(); } - public update(enabled: boolean) { + public dispose() { + this.registration?.dispose(); + this.registration = undefined; + } + + private update() { + const enabled = this.conditions.every(condition => condition.value); if (enabled) { if (!this.registration) { - this.registration = this._doRegister(); + this.registration = this.doRegister(); } } else { if (this.registration) { @@ -36,56 +66,42 @@ export class ConditionalRegistration { } } -export class VersionDependentRegistration extends Disposable { - private readonly _registration: ConditionalRegistration; - - constructor( - private readonly client: ITypeScriptServiceClient, - private readonly minVersion: API, - register: () => vscode.Disposable, - ) { - super(); - this._registration = new ConditionalRegistration(register); - - this.update(client.apiVersion); - - this.client.onTsServerStarted(() => { - this.update(this.client.apiVersion); - }, null, this._disposables); - } - - public dispose() { - super.dispose(); - this._registration.dispose(); - } - - private update(api: API) { - this._registration.update(api.gte(this.minVersion)); - } +export function conditionalRegistration( + conditions: readonly Condition[], + doRegister: () => vscode.Disposable, +): vscode.Disposable { + return new ConditionalRegistration(conditions, doRegister); } - -export class ConfigurationDependentRegistration extends Disposable { - private readonly _registration: ConditionalRegistration; - - constructor( - private readonly language: string, - private readonly configValue: string, - register: () => vscode.Disposable, - ) { - super(); - this._registration = new ConditionalRegistration(register); - this.update(); - vscode.workspace.onDidChangeConfiguration(this.update, this, this._disposables); - } - - public dispose() { - super.dispose(); - this._registration.dispose(); - } - - private update() { - const config = vscode.workspace.getConfiguration(this.language, null); - this._registration.update(!!config.get(this.configValue)); - } +export function requireMinVersion( + client: ITypeScriptServiceClient, + minVersion: API, +) { + return new Condition( + () => client.apiVersion.gte(minVersion), + client.onTsServerStarted + ); +} + +export function requireConfiguration( + language: string, + configValue: string, +) { + return new Condition( + () => { + const config = vscode.workspace.getConfiguration(language, null); + return !!config.get(configValue); + }, + vscode.workspace.onDidChangeConfiguration + ); +} + +export function requireSomeCapability( + client: ITypeScriptServiceClient, + ...capabilities: readonly ClientCapability[] +) { + return new Condition( + () => capabilities.some(requiredCapability => client.capabilities.has(requiredCapability)), + client.onDidChangeCapabilities + ); } diff --git a/extensions/typescript-language-features/src/utils/documentSelector.ts b/extensions/typescript-language-features/src/utils/documentSelector.ts new file mode 100644 index 00000000000..0574c3f587d --- /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 ea16bae4277..00000000000 --- a/extensions/typescript-language-features/src/utils/electron.ts +++ /dev/null @@ -1,72 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as temp from './temp'; -import path = require('path'); -import fs = require('fs'); -import cp = require('child_process'); -import process = require('process'); - - -const getRootTempDir = (() => { - let dir: string | undefined; - return () => { - if (!dir) { - dir = temp.getTempFile(`vscode-typescript${process.platform !== 'win32' && process.getuid ? process.getuid() : ''}`); - } - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir); - } - return dir; - }; -})(); - -export const getInstanceDir = (() => { - let dir: string | undefined; - return () => { - if (!dir) { - dir = path.join(getRootTempDir(), temp.makeRandomHexString(20)); - } - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir); - } - return dir; - }; -})(); - -export function getTempFile(prefix: string): string { - return path.join(getInstanceDir(), `${prefix}-${temp.makeRandomHexString(20)}.tmp`); -} - -function generatePatchedEnv(env: any, modulePath: string): any { - const newEnv = Object.assign({}, env); - - newEnv['ELECTRON_RUN_AS_NODE'] = '1'; - newEnv['NODE_PATH'] = path.join(modulePath, '..', '..', '..'); - - // Ensure we always have a PATH set - newEnv['PATH'] = newEnv['PATH'] || process.env.PATH; - - return newEnv; -} - -export interface ForkOptions { - readonly cwd?: string; - readonly execArgv?: string[]; -} - -export function fork( - modulePath: string, - args: string[], - options: ForkOptions, -): cp.ChildProcess { - const newEnv = generatePatchedEnv(process.env, modulePath); - return cp.fork(modulePath, args, { - silent: true, - cwd: options.cwd, - env: newEnv, - execArgv: options.execArgv - }); -} diff --git a/extensions/typescript-language-features/src/utils/errorCodes.ts b/extensions/typescript-language-features/src/utils/errorCodes.ts new file mode 100644 index 00000000000..2f554e96584 --- /dev/null +++ b/extensions/typescript-language-features/src/utils/errorCodes.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const variableDeclaredButNeverUsed = new Set([6196, 6133]); +export const propertyDeclaretedButNeverUsed = new Set([6138]); +export const allImportsAreUnused = new Set([6192]); +export const unreachableCode = new Set([7027]); +export const unusedLabel = new Set([7028]); +export const fallThroughCaseInSwitch = new Set([7029]); +export const notAllCodePathsReturnAValue = new Set([7030]); +export const incorrectlyImplementsInterface = new Set([2420]); +export const cannotFindName = new Set([2552, 2304]); +export const extendsShouldBeImplements = new Set([2689]); +export const asyncOnlyAllowedInAsyncFunctions = new Set([1308]); diff --git a/extensions/typescript-language-features/src/utils/fileSchemes.ts b/extensions/typescript-language-features/src/utils/fileSchemes.ts index 6efcfab4d3b..4e94d547bd6 100644 --- a/extensions/typescript-language-features/src/utils/fileSchemes.ts +++ b/extensions/typescript-language-features/src/utils/fileSchemes.ts @@ -8,12 +8,7 @@ export const untitled = 'untitled'; export const git = 'git'; export const walkThroughSnippet = 'walkThroughSnippet'; -export const supportedSchemes = [ +export const semanticSupportedSchemes = [ file, untitled, - walkThroughSnippet ]; - -export function isSupportedScheme(scheme: string): boolean { - return supportedSchemes.indexOf(scheme) >= 0; -} diff --git a/extensions/typescript-language-features/src/utils/fileSystem.electron.ts b/extensions/typescript-language-features/src/utils/fileSystem.electron.ts new file mode 100644 index 00000000000..3a6711224e2 --- /dev/null +++ b/extensions/typescript-language-features/src/utils/fileSystem.electron.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as fs from 'fs'; +import { getTempFile } from './temp.electron'; + +export const onCaseInsenitiveFileSystem = (() => { + let value: boolean | undefined; + return (): boolean => { + if (typeof value === 'undefined') { + if (process.platform === 'win32') { + value = true; + } else if (process.platform !== 'darwin') { + value = false; + } else { + const temp = getTempFile('typescript-case-check'); + fs.writeFileSync(temp, ''); + value = fs.existsSync(temp.toUpperCase()); + } + } + return value; + }; +})(); diff --git a/extensions/typescript-language-features/src/utils/fixNames.ts b/extensions/typescript-language-features/src/utils/fixNames.ts new file mode 100644 index 00000000000..98e47f0ebb2 --- /dev/null +++ b/extensions/typescript-language-features/src/utils/fixNames.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const annotateWithTypeFromJSDoc = 'annotateWithTypeFromJSDoc'; +export const constructorForDerivedNeedSuperCall = 'constructorForDerivedNeedSuperCall'; +export const extendsInterfaceBecomesImplements = 'extendsInterfaceBecomesImplements'; +export const awaitInSyncFunction = 'fixAwaitInSyncFunction'; +export const classIncorrectlyImplementsInterface = 'fixClassIncorrectlyImplementsInterface'; +export const classDoesntImplementInheritedAbstractMember = 'fixClassDoesntImplementInheritedAbstractMember'; +export const unreachableCode = 'fixUnreachableCode'; +export const unusedIdentifier = 'unusedIdentifier'; +export const forgottenThisPropertyAccess = 'forgottenThisPropertyAccess'; +export const spelling = 'spelling'; +export const fixImport = 'import'; +export const addMissingAwait = 'addMissingAwait'; diff --git a/extensions/typescript-language-features/src/utils/largeProjectStatus.ts b/extensions/typescript-language-features/src/utils/largeProjectStatus.ts index f820101f1b1..223d7cb4716 100644 --- a/extensions/typescript-language-features/src/utils/largeProjectStatus.ts +++ b/extensions/typescript-language-features/src/utils/largeProjectStatus.ts @@ -111,16 +111,15 @@ function onConfigureExcludesSelected( export function create( client: ITypeScriptServiceClient, - telemetryReporter: TelemetryReporter -) { +): vscode.Disposable { const toDispose: vscode.Disposable[] = []; - const item = new ExcludeHintItem(telemetryReporter); + const item = new ExcludeHintItem(client.telemetryReporter); toDispose.push(vscode.commands.registerCommand('js.projectStatus.command', () => { if (item.configFileName) { onConfigureExcludesSelected(client, item.configFileName); } - let { message } = item.getCurrentHint(); + const { message } = item.getCurrentHint(); return vscode.window.showInformationMessage(message); })); @@ -128,4 +127,3 @@ export function create( return vscode.Disposable.from(...toDispose); } - diff --git a/extensions/typescript-language-features/src/utils/logger.ts b/extensions/typescript-language-features/src/utils/logger.ts index 18317e58e00..74b9fbcbf08 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 { diff --git a/extensions/typescript-language-features/src/utils/modifiers.ts b/extensions/typescript-language-features/src/utils/modifiers.ts new file mode 100644 index 00000000000..589b5da3d52 --- /dev/null +++ b/extensions/typescript-language-features/src/utils/modifiers.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export function parseKindModifier(kindModifiers: string): Set { + return new Set(kindModifiers.split(/,|\s+/g)); +} diff --git a/extensions/typescript-language-features/src/utils/platform.ts b/extensions/typescript-language-features/src/utils/platform.ts new file mode 100644 index 00000000000..2d754bf4054 --- /dev/null +++ b/extensions/typescript-language-features/src/utils/platform.ts @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +export function isWeb(): boolean { + // @ts-expect-error + return typeof navigator !== 'undefined' && vscode.env.uiKind === vscode.UIKind.Web; +} diff --git a/extensions/typescript-language-features/src/utils/plugins.ts b/extensions/typescript-language-features/src/utils/plugins.ts index 71ce2430cb8..6d0708b15db 100644 --- a/extensions/typescript-language-features/src/utils/plugins.ts +++ b/extensions/typescript-language-features/src/utils/plugins.ts @@ -12,6 +12,7 @@ export interface TypeScriptServerPlugin { readonly name: string; readonly enableForWorkspaceTypeScriptVersions: boolean; readonly languages: ReadonlyArray; + 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/resourceMap.ts b/extensions/typescript-language-features/src/utils/resourceMap.ts index e942fae4583..ea4889ded35 100644 --- a/extensions/typescript-language-features/src/utils/resourceMap.ts +++ b/extensions/typescript-language-features/src/utils/resourceMap.ts @@ -3,10 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; import * as vscode from 'vscode'; -import { memoize } from './memoize'; -import { getTempFile } from './temp'; /** * Maps of file resources @@ -18,7 +15,10 @@ export class ResourceMap { 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 { +function isWindowsPath(path: string): boolean { return /^[a-zA-Z]:[\/\\]/.test(path); } diff --git a/extensions/typescript-language-features/src/utils/temp.electron.ts b/extensions/typescript-language-features/src/utils/temp.electron.ts new file mode 100644 index 00000000000..cd79b36415d --- /dev/null +++ b/extensions/typescript-language-features/src/utils/temp.electron.ts @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as os from 'os'; +import * as fs from 'fs'; +import * as path from 'path'; + +function makeRandomHexString(length: number): string { + const chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; + let result = ''; + for (let i = 0; i < length; i++) { + const idx = Math.floor(chars.length * Math.random()); + result += chars[idx]; + } + return result; +} + +const getRootTempDir = (() => { + let dir: string | undefined; + return () => { + if (!dir) { + const filename = `vscode-typescript${process.platform !== 'win32' && process.getuid ? process.getuid() : ''}`; + dir = path.join(os.tmpdir(), filename); + } + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir); + } + return dir; + }; +})(); + +export const getInstanceTempDir = (() => { + let dir: string | undefined; + return () => { + if (!dir) { + dir = path.join(getRootTempDir(), makeRandomHexString(20)); + } + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir); + } + return dir; + }; +})(); + +export function getTempFile(prefix: string): string { + return path.join(getInstanceTempDir(), `${prefix}-${makeRandomHexString(20)}.tmp`); +} diff --git a/extensions/typescript-language-features/src/utils/temp.ts b/extensions/typescript-language-features/src/utils/temp.ts deleted file mode 100644 index 2af5f1732b0..00000000000 --- a/extensions/typescript-language-features/src/utils/temp.ts +++ /dev/null @@ -1,21 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import path = require('path'); -import os = require('os'); - -export function makeRandomHexString(length: number): string { - const chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; - let result = ''; - for (let i = 0; i < length; i++) { - const idx = Math.floor(chars.length * Math.random()); - result += chars[idx]; - } - return result; -} - -export function getTempFile(name: string): string { - return path.join(os.tmpdir(), name); -} \ No newline at end of file diff --git a/extensions/typescript-language-features/src/utils/tracer.ts b/extensions/typescript-language-features/src/utils/tracer.ts index 134ae13d5de..445b828d0c6 100644 --- a/extensions/typescript-language-features/src/utils/tracer.ts +++ b/extensions/typescript-language-features/src/utils/tracer.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; import type * as Proto from '../protocol'; -import Logger from './logger'; +import { Logger } from './logger'; enum Trace { Off, diff --git a/extensions/typescript-language-features/test-workspace/bar.ts b/extensions/typescript-language-features/test-workspace/bar.ts new file mode 100644 index 00000000000..4098e94951d --- /dev/null +++ b/extensions/typescript-language-features/test-workspace/bar.ts @@ -0,0 +1 @@ +// export const foo = 1; \ No newline at end of file diff --git a/extensions/typescript-language-features/test-workspace/foo.ts b/extensions/typescript-language-features/test-workspace/foo.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/extensions/typescript-language-features/test-workspace/foojs.js b/extensions/typescript-language-features/test-workspace/foojs.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/extensions/typescript-language-features/test-workspace/index.ts b/extensions/typescript-language-features/test-workspace/index.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/extensions/typescript-language-features/test-workspace/tsconfig.json b/extensions/typescript-language-features/test-workspace/tsconfig.json new file mode 100644 index 00000000000..bf3bdd34f19 --- /dev/null +++ b/extensions/typescript-language-features/test-workspace/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "target": "es2018", + "noEmit": true + } +} diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index 57e8c4c5f7d..147be55e1c8 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,6 +44,11 @@ "@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" @@ -51,6 +84,29 @@ agent-base@4, agent-base@^4.3.0: dependencies: es6-promisify "^5.0.0" +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: + clean-stack "^2.0.0" + indent-string "^4.0.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== + +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: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + ajv@^6.5.5: version "6.10.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" @@ -70,6 +126,11 @@ applicationinsights@1.0.8: diagnostic-channel-publishers "0.2.1" zone.js "0.7.6" +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + asn1@~0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" @@ -109,6 +170,11 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -117,6 +183,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +braces@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" @@ -127,11 +200,44 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== +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= +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + 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" @@ -144,11 +250,38 @@ commander@2.15.1: resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +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" @@ -197,6 +330,13 @@ diff@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" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -205,6 +345,11 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + es6-promise@^4.0.3: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" @@ -242,11 +387,59 @@ fast-deep-equal@^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.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==" +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: + reusify "^1.0.4" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-cache-dir@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" + integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -261,6 +454,13 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" +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" @@ -273,6 +473,13 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +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: + is-glob "^4.0.1" + glob@7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" @@ -285,7 +492,7 @@ glob@7.1.2: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.2, glob@^7.1.3: +glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -297,6 +504,18 @@ glob@^7.1.2, glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +globby@^11.0.1: + version "11.0.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" + integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" + growl@1.10.5: version "1.10.5" resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" @@ -350,6 +569,26 @@ https-proxy-agent@^2.2.1: 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" @@ -363,6 +602,23 @@ inherits@2: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +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@^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-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -393,6 +649,13 @@ 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= +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" + jsonc-parser@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.1.tgz#db73cd59d78cce28723199466b2a03d1be1df2bc" @@ -408,6 +671,49 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +loader-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" + integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +make-dir@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +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" + mime-db@1.43.0: version "1.43.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" @@ -432,6 +738,47 @@ minimist@0.0.8: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +minipass-collect@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" + integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== + dependencies: + minipass "^3.0.0" + +minipass-flush@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== + dependencies: + minipass "^3.0.0" + +minipass-pipeline@^1.2.2: + version "1.2.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: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" @@ -439,6 +786,11 @@ mkdirp@0.5.1: dependencies: minimist "0.0.8" +mkdirp@^1.0.3, mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + mocha@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" @@ -466,6 +818,11 @@ ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +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== + oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" @@ -478,16 +835,76 @@ once@^1.3.0: dependencies: wrappy "1" +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.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: + p-try "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= +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= +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: + find-up "^4.0.0" + +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= + psl@^1.1.24: version "1.7.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c" @@ -513,6 +930,13 @@ querystringify@^2.1.1: resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== +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.88.0: version "2.88.0" resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" @@ -544,6 +968,11 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" @@ -551,16 +980,42 @@ rimraf@^2.6.3: 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" + +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" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" @@ -571,6 +1026,28 @@ semver@^5.3.0, semver@^5.4.1: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== +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: + 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.16" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" @@ -579,7 +1056,15 @@ source-map-support@^0.5.0: buffer-from "^1.0.0" source-map "^0.6.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.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -599,6 +1084,13 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" +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: + minipass "^3.1.1" + supports-color@5.4.0: version "5.4.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" @@ -606,6 +1098,34 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" +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: + is-number "^7.0.0" + tough-cookie@~2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" @@ -626,10 +1146,28 @@ 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= -typescript-vscode-sh-plugin@^0.6.13: - version "0.6.13" - resolved "https://registry.yarnpkg.com/typescript-vscode-sh-plugin/-/typescript-vscode-sh-plugin-0.6.13.tgz#96d92976c25d36dfa5761230a02f1b217046e947" - integrity sha512-hl6EkNtH90Cn6c5xAmvCn5YpvMzVedqMhUxxe+FWxX/xE0qT8ef1q2vyNlRnUGW95Q6A//5Yl07p9xkSrZi5hw== +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: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" + integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== + dependencies: + imurmurhash "^0.1.4" uri-js@^4.2.2: version "4.2.2" @@ -693,11 +1231,24 @@ vscode@^1.1.36: url-parse "^1.4.4" vscode-test "^0.4.1" +webpack-sources@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + zone.js@0.7.6: version "0.7.6" resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009" diff --git a/extensions/vscode-account/package.json b/extensions/vscode-account/package.json deleted file mode 100644 index 6466cc59663..00000000000 --- a/extensions/vscode-account/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "vscode-account", - "publisher": "vscode", - "displayName": "%displayName%", - "description": "%description%", - "version": "0.0.1", - "engines": { - "vscode": "^1.42.0" - }, - "categories": [ - "Other" - ], - "enableProposedApi": true, - "activationEvents": [ - "*" - ], - "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", - "main": "./out/extension.js", - "scripts": { - "vscode:prepublish": "npm run compile", - "compile": "gulp compile-extension:vscode-account", - "watch": "gulp watch-extension:vscode-account" - }, - "devDependencies": { - "typescript": "^3.7.4", - "tslint": "^5.12.1", - "@types/node": "^10.12.21", - "@types/keytar": "^4.0.1", - "@types/uuid": "^3.4.6" - }, - "dependencies": { - "uuid": "^3.3.3", - "vscode-extension-telemetry": "0.1.1", - "vscode-nls": "^4.1.1" - } -} diff --git a/extensions/vscode-api-tests/src/memfs.ts b/extensions/vscode-api-tests/src/memfs.ts index 2b4a5f751ab..1b4b1e5adf7 100644 --- a/extensions/vscode-api-tests/src/memfs.ts +++ b/extensions/vscode-api-tests/src/memfs.ts @@ -48,9 +48,12 @@ class Directory implements vscode.FileStat { export type Entry = File | Directory; -export class MemFS implements vscode.FileSystemProvider { +export class TestFS implements vscode.FileSystemProvider { - readonly scheme = 'fake-fs'; + constructor( + readonly scheme: string, + readonly isCaseSensitive: boolean + ) { } readonly root = new Directory(''); @@ -161,12 +164,22 @@ export class MemFS implements vscode.FileSystemProvider { let parts = uri.path.split('/'); let entry: Entry = this.root; for (const part of parts) { + const partLow = part.toLowerCase(); if (!part) { continue; } let child: Entry | undefined; if (entry instanceof Directory) { - child = entry.entries.get(part); + if (this.isCaseSensitive) { + child = entry.entries.get(part); + } else { + for (let [key, value] of entry.entries) { + if (key.toLowerCase() === partLow) { + child = value; + break; + } + } + } } if (!child) { if (!silent) { diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/editor.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/editor.test.ts index 20bf80b0b05..f83a319aeba 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/editor.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/editor.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 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('vscode API - editors', () => { @@ -47,6 +47,32 @@ suite('vscode API - editors', () => { }); }); + 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/languages.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/languages.test.ts index 9c30a873e85..8c407fb6c8b 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/languages.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/languages.test.ts @@ -29,31 +29,34 @@ suite('vscode API - languages', () => { 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/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index 194fdd3b7f6..8b2f8c057e0 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -22,6 +22,8 @@ import { doesNotThrow, equal, ok, deepEqual, throws } from 'assert'; 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', () => { @@ -47,7 +49,7 @@ import { doesNotThrow, equal, ok, deepEqual, throws } from 'assert'; doesNotThrow(terminal.sendText.bind(terminal, 'echo "foo"')); }); - test('echo works in the default shell', (done) => { + (process.platform === 'linux' ? test.skip : test)('echo works in the default shell', (done) => { disposables.push(window.onDidOpenTerminal(term => { try { equal(terminal, term); @@ -67,7 +69,9 @@ import { doesNotThrow, equal, ok, deepEqual, throws } from 'assert'; if (data.indexOf(expected) !== 0) { dataDisposable.dispose(); terminal.dispose(); - disposables.push(window.onDidCloseTerminal(() => done())); + disposables.push(window.onDidCloseTerminal(() => { + done(); + })); } }); disposables.push(dataDisposable); 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 704d2f115ed..fd97ea91728 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts @@ -6,15 +6,18 @@ import * as assert from 'assert'; import 'mocha'; import * as os from 'os'; -import { join } from 'path'; import * as vscode from 'vscode'; -import { closeAllEditors, conditionalTest, delay, disposeAll } 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); +} -suite('vscode API - webview', () => { +const testDocument = workspaceFile('bower.json'); + +suite.skip('vscode API - webview', () => { const disposables: vscode.Disposable[] = []; function _register(disposable: T) { @@ -83,7 +86,7 @@ suite('vscode API - webview', () => { } }); - conditionalTest('webviews should preserve vscode API state when they are hidden', async () => { + test.skip('webviews should preserve vscode API state when they are hidden', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true })); const ready = getMesssage(webview); webview.webview.html = createHtmlDocumentWithBody(/*html*/` @@ -126,7 +129,7 @@ suite('vscode API - webview', () => { assert.strictEqual(secondResponse.value, 1); }); - conditionalTest('webviews should preserve their context when they are moved between view columns', async () => { + test('webviews should preserve their context when they are moved between view columns', async () => { const doc = await vscode.workspace.openTextDocument(testDocument); await vscode.window.showTextDocument(doc, vscode.ViewColumn.One); @@ -147,7 +150,7 @@ suite('vscode API - webview', () => { assert.strictEqual(secondResponse.value, 1); }); - conditionalTest('webviews with retainContextWhenHidden should preserve their context when they are hidden', async () => { + test('webviews with retainContextWhenHidden should preserve their context when they are hidden', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, retainContextWhenHidden: true })); const ready = getMesssage(webview); @@ -169,7 +172,7 @@ suite('vscode API - webview', () => { assert.strictEqual(secondResponse.value, 1); }); - conditionalTest('webviews with retainContextWhenHidden should preserve their page position when hidden', async () => { + test('webviews with retainContextWhenHidden should preserve their page position when hidden', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, retainContextWhenHidden: true })); const ready = getMesssage(webview); webview.webview.html = createHtmlDocumentWithBody(/*html*/` @@ -190,13 +193,12 @@ suite('vscode API - webview', () => { } }); 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); @@ -207,10 +209,10 @@ suite('vscode API - webview', () => { // We should still have old scroll pos const secondResponse = await sendRecieveMessage(webview, { type: 'get' }); - assert.strictEqual(secondResponse.value, 100); + assert.strictEqual(Math.round(secondResponse.value), 100); }); - conditionalTest('webviews with retainContextWhenHidden should be able to recive messages while hidden', async () => { + test('webviews with retainContextWhenHidden should be able to recive messages while hidden', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, retainContextWhenHidden: true })); const ready = getMesssage(webview); @@ -237,47 +239,67 @@ suite('vscode API - webview', () => { }); - 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 = webview.webview.asWebviewUri(workspaceFile('no-such-image.png')); + const response = await sendRecieveMessage(webview, { src: imagePath.toString() }); + assert.strictEqual(response.value, false); } { - const imagePath = await asWebviewUri('/no-such-image.png'); - const response = sendRecieveMessage(webview, { src: imagePath }); - assert.strictEqual((await response).value, false); - } - { - const imagePath = vscode.Uri.file(join(vscode.workspace.rootPath!, '..', '..', '..', 'resources', 'linux', 'code.png')).with({ scheme: 'vscode-resource' }); - const response = sendRecieveMessage(webview, { src: imagePath.toString() }); - assert.strictEqual((await response).value, false); + const imagePath = webview.webview.asWebviewUri(workspaceFile('..', '..', '..', 'resources', 'linux', 'code.png')); + const response = await sendRecieveMessage(webview, { src: imagePath.toString() }); + assert.strictEqual(response.value, false); } }); - conditionalTest('webviews should allow overriding allowed resource paths using localResourceRoots', async () => { + test.skip('webviews should allow overriding allowed resource paths using localResourceRoots', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, - localResourceRoots: [vscode.Uri.file(join(vscode.workspace.rootPath!, 'sub'))] + localResourceRoots: [workspaceFile('sub')] })); webview.webview.html = createHtmlDocumentWithBody(/*html*/` @@ -292,18 +314,38 @@ suite('vscode API - webview', () => { }); `); - 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 })); @@ -336,7 +378,7 @@ suite('vscode API - webview', () => { }); if (os.platform() === 'darwin') { - conditionalTest('webview can copy text from webview', async () => { + 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 })); @@ -352,7 +394,7 @@ suite('vscode API - webview', () => { `); await ready; - await vscode.commands.executeCommand('editor.action.webvieweditor.copy'); + 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); }); 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 d1d6f3e7fb4..8c8d5e64ca7 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts @@ -4,10 +4,11 @@ *--------------------------------------------------------------------------------------------*/ 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('vscode API - window', () => { teardown(closeAllEditors); @@ -146,6 +147,23 @@ suite('vscode API - window', () => { }); test('active editor not always correct... #49125', async function () { + if (process.env['BUILD_SOURCEVERSION']) { + this.skip(); + return; + } + function assertActiveEditor(editor: TextEditor) { + if (window.activeTextEditor === editor) { + assert.ok(true); + return; + } + function printEditor(editor: TextEditor): string { + return `doc: ${editor.document.uri.toString()}, column: ${editor.viewColumn}, active: ${editor === window.activeTextEditor}`; + } + const visible = window.visibleTextEditors.map(editor => printEditor(editor)); + assert.ok(false, `ACTIVE editor should be ${printEditor(editor)}, BUT HAVING ${visible.join(', ')}`); + + } + const randomFile1 = await createRandomFile(); const randomFile2 = await createRandomFile(); @@ -155,10 +173,10 @@ suite('vscode API - window', () => { ]); for (let c = 0; c < 4; c++) { let editorA = await window.showTextDocument(docA, ViewColumn.One); - assert.equal(window.activeTextEditor, editorA); + assertActiveEditor(editorA); let editorB = await window.showTextDocument(docB, ViewColumn.Two); - assert.equal(window.activeTextEditor, editorB); + assertActiveEditor(editorB); } }); @@ -383,33 +401,31 @@ suite('vscode API - window', () => { assert.equal(await two, 'notempty'); }); - // TODO@chrmarti Disabled due to flaky behaviour (https://github.com/Microsoft/vscode/issues/70887) - // test('showQuickPick, accept first', async function () { - // const pick = window.showQuickPick(['eins', 'zwei', 'drei']); - // await new Promise(resolve => setTimeout(resolve, 10)); // Allow UI to update. - // await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); - // assert.equal(await pick, 'eins'); - // }); - - test('showQuickPick, accept second', async function () { - const resolves: ((value: string) => void)[] = []; - let done: () => void; - const unexpected = new Promise((resolve, reject) => { - done = () => resolve(); - resolves.push(reject); - }); - const first = new Promise(resolve => resolves.push(resolve)); + test('showQuickPick, accept first', async function () { + const tracker = createQuickPickTracker(); + const first = tracker.nextItem(); const pick = window.showQuickPick(['eins', 'zwei', 'drei'], { - onDidSelectItem: item => resolves.pop()!(item as string) + onDidSelectItem: tracker.onDidSelectItem }); assert.equal(await first, 'eins'); - const second = new Promise(resolve => resolves.push(resolve)); + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + assert.equal(await pick, 'eins'); + return tracker.done(); + }); + + test('showQuickPick, accept second', async function () { + const tracker = createQuickPickTracker(); + const first = tracker.nextItem(); + const pick = window.showQuickPick(['eins', 'zwei', 'drei'], { + onDidSelectItem: tracker.onDidSelectItem + }); + assert.equal(await first, 'eins'); + const second = tracker.nextItem(); await commands.executeCommand('workbench.action.quickOpenSelectNext'); assert.equal(await second, 'zwei'); await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); assert.equal(await pick, 'zwei'); - done!(); - return unexpected; + return tracker.done(); }); test('showQuickPick, select first two', async function () { @@ -438,19 +454,27 @@ suite('vscode API - window', () => { 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(); @@ -521,20 +545,24 @@ suite('vscode API - window', () => { 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({ @@ -551,6 +579,23 @@ suite('vscode API - window', () => { 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.tasks.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts index fba7d920b50..53acdcf23dc 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,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { window, tasks, Disposable, TaskDefinition, Task, EventEmitter, CustomExecution, Pseudoterminal, TaskScope, commands, Task2, env, UIKind } from 'vscode'; +import { window, tasks, Disposable, TaskDefinition, Task, EventEmitter, CustomExecution, Pseudoterminal, TaskScope, commands, Task2, env, UIKind, ShellExecution, TaskExecution, Terminal, Event } from 'vscode'; // Disable tasks tests: // - Web https://github.com/microsoft/vscode/issues/90528 @@ -28,26 +28,55 @@ import { window, tasks, Disposable, TaskDefinition, Task, EventEmitter, CustomEx const taskType: string = 'customTesting'; const taskName = 'First custom task'; let isPseudoterminalClosed = false; + let terminal: Terminal | undefined; + // There's a strict order that should be observed here: + // 1. The terminal opens + // 2. The terminal is written to. + // 3. The terminal is closed. + enum TestOrder { + Start, + TerminalOpened, + TerminalWritten, + TerminalClosed + } + + let testOrder = TestOrder.Start; + disposables.push(window.onDidOpenTerminal(term => { - disposables.push(window.onDidWriteTerminalData(e => { - try { - assert.equal(e.data, 'testing\r\n'); - } catch (e) { - done(e); - } - disposables.push(window.onDidCloseTerminal(() => { - try { - // Pseudoterminal.close should have fired by now, additionally we want - // to make sure all events are flushed before continuing with more tests - assert.ok(isPseudoterminalClosed); - } catch (e) { - done(e); - return; - } - done(); - })); - term.dispose(); - })); + try { + assert.equal(testOrder, TestOrder.Start); + } catch (e) { + done(e); + } + testOrder = TestOrder.TerminalOpened; + terminal = term; + })); + disposables.push(window.onDidWriteTerminalData(e => { + try { + assert.equal(testOrder, TestOrder.TerminalOpened); + testOrder = TestOrder.TerminalWritten; + assert.notEqual(terminal, undefined); + assert.equal(e.data, 'testing\r\n'); + } catch (e) { + done(e); + } + + if (terminal) { + terminal.dispose(); + } + })); + disposables.push(window.onDidCloseTerminal(() => { + try { + assert.equal(testOrder, TestOrder.TerminalWritten); + testOrder = TestOrder.TerminalClosed; + // Pseudoterminal.close should have fired by now, additionally we want + // to make sure all events are flushed before continuing with more tests + assert.ok(isPseudoterminalClosed); + } catch (e) { + done(e); + return; + } + done(); })); disposables.push(tasks.registerTaskProvider(taskType, { provideTasks: () => { @@ -118,7 +147,7 @@ import { window, tasks, Disposable, TaskDefinition, Task, EventEmitter, CustomEx writeEmitter.fire('exiting'); closeEmitter.fire(); }, - close: () => {} + close: () => { } }; return Promise.resolve(pty); }); @@ -137,5 +166,100 @@ import { window, tasks, Disposable, TaskDefinition, Task, EventEmitter, CustomEx })); 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 2a58c4630e0..6e1034001f4 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -5,9 +5,10 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; -import { createRandomFile, deleteFile, closeAllEditors, pathEquals, rndName, disposeAll, testFs, delay, withLogDisabled } from '../utils'; +import { createRandomFile, deleteFile, closeAllEditors, pathEquals, rndName, disposeAll, testFs, delay, withLogDisabled, revertAllDirty } from '../utils'; import { join, posix, basename } from 'path'; import * as fs from 'fs'; +import { TestFS } from '../memfs'; suite('vscode API - workspace', () => { @@ -59,12 +60,19 @@ suite('vscode API - workspace', () => { } }); - test('openTextDocument', () => { - let len = vscode.workspace.textDocuments.length; - return vscode.workspace.openTextDocument(join(vscode.workspace.rootPath || '', './simple.txt')).then(doc => { - assert.ok(doc); - assert.equal(vscode.workspace.textDocuments.length, len + 1); - }); + test('openTextDocument', async () => { + const uri = await createRandomFile(); + + // not yet there + const existing1 = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === uri.toString()); + assert.equal(existing1, undefined); + + // open and assert its there + const doc = await vscode.workspace.openTextDocument(uri); + assert.ok(doc); + assert.equal(doc.uri.toString(), uri.toString()); + const existing2 = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === uri.toString()); + assert.equal(existing2 === doc, true); }); test('openTextDocument, illegal path', () => { @@ -163,6 +171,40 @@ suite('vscode API - workspace', () => { }); }); + 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 => { @@ -246,7 +288,7 @@ suite('vscode API - workspace', () => { const file = await createRandomFile(); let disposables: vscode.Disposable[] = []; - await vscode.workspace.saveAll(); + await revertAllDirty(); // needed for a clean state for `onDidSaveTextDocument` (#102365) let pendingAsserts: Function[] = []; let onDidOpenTextDocument = false; @@ -288,6 +330,8 @@ suite('vscode API - workspace', () => { let disposables: vscode.Disposable[] = []; let pendingAsserts: Function[] = []; + await revertAllDirty(); // needed for a clean state for `onDidSaveTextDocument` (#102365) + let onDidSaveTextDocument = false; disposables.push(vscode.workspace.onDidSaveTextDocument(e => { pendingAsserts.push(() => assertEqualPath(e.uri.fsPath, file.fsPath)); @@ -507,7 +551,7 @@ suite('vscode API - workspace', () => { }); 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'); }); @@ -528,14 +572,14 @@ suite('vscode API - workspace', () => { }); 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'); }); diff --git a/extensions/vscode-api-tests/src/utils.ts b/extensions/vscode-api-tests/src/utils.ts index e270cd73adf..475fa6cfbf4 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; @@ -48,25 +48,14 @@ 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)()); - } - } -} - -function isTestTypeActive(): boolean { - return !!vscode.extensions.getExtension('vscode-resolver-test'); -} - export function delay(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)); } diff --git a/extensions/vscode-api-tests/testWorkspace/image%.png b/extensions/vscode-api-tests/testWorkspace/image%.png new file mode 100644 index 00000000000..15b462975be Binary files /dev/null and b/extensions/vscode-api-tests/testWorkspace/image%.png differ diff --git a/extensions/vscode-api-tests/testWorkspace/image%02.png b/extensions/vscode-api-tests/testWorkspace/image%02.png new file mode 100644 index 00000000000..15b462975be Binary files /dev/null and b/extensions/vscode-api-tests/testWorkspace/image%02.png differ diff --git a/extensions/vscode-custom-editor-tests/customEditorMedia/textEditor.js b/extensions/vscode-custom-editor-tests/customEditorMedia/textEditor.js new file mode 100644 index 00000000000..8978b201c9f --- /dev/null +++ b/extensions/vscode-custom-editor-tests/customEditorMedia/textEditor.js @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// @ts-check +(function () { + // @ts-ignore + const vscode = acquireVsCodeApi(); + + const textArea = document.querySelector('textarea'); + + const initialState = vscode.getState(); + if (initialState) { + textArea.value = initialState.value; + } + + window.addEventListener('message', e => { + switch (e.data.type) { + case 'fakeInput': + { + const value = e.data.value; + textArea.value = value; + onInput(); + break; + } + + case 'setValue': + { + const value = e.data.value; + textArea.value = value; + vscode.setState({ value }); + + vscode.postMessage({ + type: 'didChangeContent', + value: value + }); + break; + } + } + }); + + const onInput = () => { + const value = textArea.value; + vscode.setState({ value }); + vscode.postMessage({ + type: 'edit', + value: value + }); + vscode.postMessage({ + type: 'didChangeContent', + value: value + }); + }; + + textArea.addEventListener('input', onInput); +}()); diff --git a/extensions/vscode-custom-editor-tests/package.json b/extensions/vscode-custom-editor-tests/package.json new file mode 100644 index 00000000000..08b6702b80a --- /dev/null +++ b/extensions/vscode-custom-editor-tests/package.json @@ -0,0 +1,44 @@ +{ + "name": "vscode-custom-editor-tests", + "description": "Custom editor tests for VS Code", + "version": "0.0.1", + "publisher": "vscode", + "license": "MIT", + "private": true, + "activationEvents": [ + "onCustomEditor:testWebviewEditor.abc" + ], + "main": "./out/extension", + "enableProposedApi": true, + "engines": { + "vscode": "^1.48.0" + }, + "scripts": { + "compile": "node ./node_modules/vscode/bin/compile -watch -p ./", + "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-notebook-tests ./tsconfig.json" + }, + "dependencies": { + "p-limit": "^3.0.2" + }, + "devDependencies": { + "@types/node": "^12.11.7", + "@types/p-limit": "^2.2.0", + "mocha": "^2.3.3", + "mocha-junit-reporter": "^1.17.0", + "mocha-multi-reporters": "^1.1.7", + "vscode": "^1.1.36" + }, + "contributes": { + "customEditors": [ + { + "viewType": "testWebviewEditor.abc", + "displayName": "Test ABC editor", + "selector": [ + { + "filenamePattern": "*.abc" + } + ] + } + ] + } +} diff --git a/extensions/vscode-custom-editor-tests/src/customTextEditor.ts b/extensions/vscode-custom-editor-tests/src/customTextEditor.ts new file mode 100644 index 00000000000..cced14b9401 --- /dev/null +++ b/extensions/vscode-custom-editor-tests/src/customTextEditor.ts @@ -0,0 +1,165 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as pLimit from 'p-limit'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import { Disposable } from './dispose'; + +export namespace Testing { + export const abcEditorContentChangeCommand = '_abcEditor.contentChange'; + export const abcEditorTypeCommand = '_abcEditor.type'; + + export interface CustomEditorContentChangeEvent { + readonly content: string; + readonly source: vscode.Uri; + } +} + +export class AbcTextEditorProvider implements vscode.CustomTextEditorProvider { + + public static readonly viewType = 'testWebviewEditor.abc'; + + private activeEditor?: AbcEditor; + + public constructor( + private readonly context: vscode.ExtensionContext, + ) { } + + public register(): vscode.Disposable { + const provider = vscode.window.registerCustomEditorProvider(AbcTextEditorProvider.viewType, this); + + const commands: vscode.Disposable[] = []; + commands.push(vscode.commands.registerCommand(Testing.abcEditorTypeCommand, (content: string) => { + this.activeEditor?.testing_fakeInput(content); + })); + + return vscode.Disposable.from(provider, ...commands); + } + + public async resolveCustomTextEditor(document: vscode.TextDocument, panel: vscode.WebviewPanel) { + const editor = new AbcEditor(document, this.context.extensionPath, panel); + + this.activeEditor = editor; + + panel.onDidChangeViewState(({ webviewPanel }) => { + if (this.activeEditor === editor && !webviewPanel.active) { + this.activeEditor = undefined; + } + if (webviewPanel.active) { + this.activeEditor = editor; + } + }); + } +} + +class AbcEditor extends Disposable { + + public readonly _onDispose = this._register(new vscode.EventEmitter()); + public readonly onDispose = this._onDispose.event; + + private readonly limit = pLimit(1); + private syncedVersion: number = -1; + private currentWorkspaceEdit?: Thenable; + + constructor( + private readonly document: vscode.TextDocument, + private readonly _extensionPath: string, + private readonly panel: vscode.WebviewPanel, + ) { + super(); + + panel.webview.options = { + enableScripts: true, + }; + panel.webview.html = this.html; + + this._register(vscode.workspace.onDidChangeTextDocument(e => { + if (e.document === this.document) { + this.update(); + } + })); + + this._register(panel.webview.onDidReceiveMessage(message => { + switch (message.type) { + case 'edit': + this.doEdit(message.value); + break; + + case 'didChangeContent': + vscode.commands.executeCommand(Testing.abcEditorContentChangeCommand, { + content: message.value, + source: document.uri, + } as Testing.CustomEditorContentChangeEvent); + break; + } + })); + + this._register(panel.onDidDispose(() => { this.dispose(); })); + + this.update(); + } + + public testing_fakeInput(value: string) { + this.panel.webview.postMessage({ + type: 'fakeInput', + value: value, + }); + } + + private async doEdit(value: string) { + const edit = new vscode.WorkspaceEdit(); + edit.replace(this.document.uri, this.document.validateRange(new vscode.Range(new vscode.Position(0, 0), new vscode.Position(999999, 999999))), value); + this.limit(() => { + this.currentWorkspaceEdit = vscode.workspace.applyEdit(edit).then(() => { + this.syncedVersion = this.document.version; + this.currentWorkspaceEdit = undefined; + }); + return this.currentWorkspaceEdit; + }); + } + + public dispose() { + if (this.isDisposed) { + return; + } + + this._onDispose.fire(); + super.dispose(); + } + + private get html() { + const contentRoot = path.join(this._extensionPath, 'customEditorMedia'); + const scriptUri = vscode.Uri.file(path.join(contentRoot, 'textEditor.js')); + const nonce = Date.now() + ''; + return /* html */` + + + + + + Document + + + + + + `; + } + + public async update() { + await this.currentWorkspaceEdit; + + if (this.isDisposed || this.syncedVersion >= this.document.version) { + return; + } + + this.panel.webview.postMessage({ + type: 'setValue', + value: this.document.getText(), + }); + this.syncedVersion = this.document.version; + } +} diff --git a/extensions/vscode-custom-editor-tests/src/dispose.ts b/extensions/vscode-custom-editor-tests/src/dispose.ts new file mode 100644 index 00000000000..548094c28e5 --- /dev/null +++ b/extensions/vscode-custom-editor-tests/src/dispose.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +export function disposeAll(disposables: vscode.Disposable[]) { + while (disposables.length) { + const item = disposables.pop(); + if (item) { + item.dispose(); + } + } +} + +export abstract class Disposable { + private _isDisposed = false; + + protected _disposables: vscode.Disposable[] = []; + + public dispose(): any { + if (this._isDisposed) { + return; + } + this._isDisposed = true; + disposeAll(this._disposables); + } + + protected _register(value: T): T { + if (this._isDisposed) { + value.dispose(); + } else { + this._disposables.push(value); + } + return value; + } + + protected get isDisposed() { + return this._isDisposed; + } +} \ No newline at end of file diff --git a/extensions/vscode-custom-editor-tests/src/extension.ts b/extensions/vscode-custom-editor-tests/src/extension.ts new file mode 100644 index 00000000000..0a83f97fce2 --- /dev/null +++ b/extensions/vscode-custom-editor-tests/src/extension.ts @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { AbcTextEditorProvider } from './customTextEditor'; + +export function activate(context: vscode.ExtensionContext) { + context.subscriptions.push(new AbcTextEditorProvider(context).register()); +} diff --git a/extensions/vscode-custom-editor-tests/src/test/customEditor.test.ts b/extensions/vscode-custom-editor-tests/src/test/customEditor.test.ts new file mode 100644 index 00000000000..d66ab665b8e --- /dev/null +++ b/extensions/vscode-custom-editor-tests/src/test/customEditor.test.ts @@ -0,0 +1,314 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import { Testing } from '../customTextEditor'; +import { closeAllEditors, delay, disposeAll, randomFilePath } from './utils'; + +assert.ok(vscode.workspace.rootPath); +const testWorkspaceRoot = vscode.Uri.file(path.join(vscode.workspace.rootPath!, 'customEditors')); + +const commands = Object.freeze({ + open: 'vscode.open', + openWith: 'vscode.openWith', + save: 'workbench.action.files.save', + undo: 'undo', +}); + +async function writeRandomFile(options: { ext: string; contents: string; }): Promise { + const fakeFile = randomFilePath({ root: testWorkspaceRoot, ext: options.ext }); + await fs.promises.writeFile(fakeFile.fsPath, Buffer.from(options.contents)); + return fakeFile; +} + +const disposables: vscode.Disposable[] = []; +function _register(disposable: T) { + disposables.push(disposable); + return disposable; +} + +class CustomEditorUpdateListener { + + public static create() { + return _register(new CustomEditorUpdateListener()); + } + + private readonly commandSubscription: vscode.Disposable; + + private readonly unconsumedResponses: Array = []; + private readonly callbackQueue: Array<(data: Testing.CustomEditorContentChangeEvent) => void> = []; + + private constructor() { + this.commandSubscription = vscode.commands.registerCommand(Testing.abcEditorContentChangeCommand, (data: Testing.CustomEditorContentChangeEvent) => { + if (this.callbackQueue.length) { + const callback = this.callbackQueue.shift(); + assert.ok(callback); + callback!(data); + } else { + this.unconsumedResponses.push(data); + } + }); + } + + dispose() { + this.commandSubscription.dispose(); + } + + async nextResponse(): Promise { + if (this.unconsumedResponses.length) { + return this.unconsumedResponses.shift()!; + } + + return new Promise(resolve => { + this.callbackQueue.push(resolve); + }); + } +} + + +suite('CustomEditor tests', () => { + setup(async () => { + await closeAllEditors(); + await resetTestWorkspace(); + }); + + teardown(async () => { + await closeAllEditors(); + disposeAll(disposables); + await resetTestWorkspace(); + }); + + test('Should load basic content from disk', async () => { + const startingContent = `load, init`; + const testDocument = await writeRandomFile({ ext: '.abc', contents: startingContent }); + + const listener = CustomEditorUpdateListener.create(); + + await vscode.commands.executeCommand(commands.open, testDocument); + + const { content } = await listener.nextResponse(); + assert.equal(content, startingContent); + }); + + test('Should support basic edits', async () => { + const startingContent = `basic edit, init`; + const testDocument = await writeRandomFile({ ext: '.abc', contents: startingContent }); + + const listener = CustomEditorUpdateListener.create(); + + await vscode.commands.executeCommand(commands.open, testDocument); + await listener.nextResponse(); + + const newContent = `basic edit test`; + await vscode.commands.executeCommand(Testing.abcEditorTypeCommand, newContent); + const { content } = await listener.nextResponse(); + assert.equal(content, newContent); + }); + + test('Should support single undo', async () => { + const startingContent = `single undo, init`; + const testDocument = await writeRandomFile({ ext: '.abc', contents: startingContent }); + + const listener = CustomEditorUpdateListener.create(); + + await vscode.commands.executeCommand(commands.open, testDocument); + await listener.nextResponse(); + + const newContent = `undo test`; + { + await vscode.commands.executeCommand(Testing.abcEditorTypeCommand, newContent); + const { content } = await listener.nextResponse(); + assert.equal(content, newContent); + } + await delay(100); + { + await vscode.commands.executeCommand(commands.undo); + const { content } = await listener.nextResponse(); + assert.equal(content, startingContent); + } + }); + + test('Should support multiple undo', async () => { + const startingContent = `multiple undo, init`; + const testDocument = await writeRandomFile({ ext: '.abc', contents: startingContent }); + + const listener = CustomEditorUpdateListener.create(); + + await vscode.commands.executeCommand(commands.open, testDocument); + await listener.nextResponse(); + + const count = 10; + + // Make edits + for (let i = 0; i < count; ++i) { + await vscode.commands.executeCommand(Testing.abcEditorTypeCommand, `${i}`); + const { content } = await listener.nextResponse(); + assert.equal(`${i}`, content); + } + + // Then undo them in order + for (let i = count - 1; i; --i) { + await delay(100); + await vscode.commands.executeCommand(commands.undo); + const { content } = await listener.nextResponse(); + assert.equal(`${i - 1}`, content); + } + + { + await delay(100); + await vscode.commands.executeCommand(commands.undo); + const { content } = await listener.nextResponse(); + assert.equal(content, startingContent); + } + }); + + test('Should update custom editor on file move', async () => { + const startingContent = `file move, init`; + const testDocument = await writeRandomFile({ ext: '.abc', contents: startingContent }); + + const listener = CustomEditorUpdateListener.create(); + + await vscode.commands.executeCommand(commands.open, testDocument); + await listener.nextResponse(); + + const newFileName = vscode.Uri.file(path.join(testWorkspaceRoot.fsPath, 'y.abc')); + + const edit = new vscode.WorkspaceEdit(); + edit.renameFile(testDocument, newFileName); + + await vscode.workspace.applyEdit(edit); + + const response = (await listener.nextResponse()); + assert.equal(response.content, startingContent); + assert.equal(response.source.toString(), newFileName.toString()); + }); + + test('Should support saving custom editors', async () => { + const startingContent = `save, init`; + const testDocument = await writeRandomFile({ ext: '.abc', contents: startingContent }); + + const listener = CustomEditorUpdateListener.create(); + + await vscode.commands.executeCommand(commands.open, testDocument); + await listener.nextResponse(); + + const newContent = `save, new`; + { + await vscode.commands.executeCommand(Testing.abcEditorTypeCommand, newContent); + const { content } = await listener.nextResponse(); + assert.equal(content, newContent); + } + { + await vscode.commands.executeCommand(commands.save); + const fileContent = (await fs.promises.readFile(testDocument.fsPath)).toString(); + assert.equal(fileContent, newContent); + } + }); + + test('Should undo after saving custom editor', async () => { + const startingContent = `undo after save, init`; + const testDocument = await writeRandomFile({ ext: '.abc', contents: startingContent }); + + const listener = CustomEditorUpdateListener.create(); + + await vscode.commands.executeCommand(commands.open, testDocument); + await listener.nextResponse(); + + const newContent = `undo after save, new`; + { + await vscode.commands.executeCommand(Testing.abcEditorTypeCommand, newContent); + const { content } = await listener.nextResponse(); + assert.equal(content, newContent); + } + { + await vscode.commands.executeCommand(commands.save); + const fileContent = (await fs.promises.readFile(testDocument.fsPath)).toString(); + assert.equal(fileContent, newContent); + } + await delay(100); + { + await vscode.commands.executeCommand(commands.undo); + const { content } = await listener.nextResponse(); + assert.equal(content, startingContent); + } + }); + + test.skip('Should support untitled custom editors', async () => { + const listener = CustomEditorUpdateListener.create(); + + const untitledFile = randomFilePath({ root: testWorkspaceRoot, ext: '.abc' }).with({ scheme: 'untitled' }); + + await vscode.commands.executeCommand(commands.open, untitledFile); + assert.equal((await listener.nextResponse()).content, ''); + + await vscode.commands.executeCommand(Testing.abcEditorTypeCommand, `123`); + assert.equal((await listener.nextResponse()).content, '123'); + + await vscode.commands.executeCommand(commands.save); + const content = await fs.promises.readFile(untitledFile.fsPath); + assert.equal(content.toString(), '123'); + }); + + test.skip('When switching away from a non-default custom editors and then back, we should continue using the non-default editor', async () => { + const startingContent = `switch, init`; + const testDocument = await writeRandomFile({ ext: '.abc', contents: startingContent }); + + const listener = CustomEditorUpdateListener.create(); + + { + await vscode.commands.executeCommand(commands.open, testDocument, { preview: false }); + const { content } = await listener.nextResponse(); + assert.strictEqual(content, startingContent.toString()); + assert.ok(!vscode.window.activeTextEditor); + } + + // Switch to non-default editor + await vscode.commands.executeCommand(commands.openWith, testDocument, 'default', { preview: false }); + assert.strictEqual(vscode.window.activeTextEditor!?.document.uri.toString(), testDocument.toString()); + + // Then open a new document (hiding existing one) + const otherFile = vscode.Uri.file(path.join(testWorkspaceRoot.fsPath, 'other.json')); + await vscode.commands.executeCommand(commands.open, otherFile); + assert.strictEqual(vscode.window.activeTextEditor!?.document.uri.toString(), otherFile.toString()); + + // And then back + await vscode.commands.executeCommand('workbench.action.navigateBack'); + await vscode.commands.executeCommand('workbench.action.navigateBack'); + + // Make sure we have the file on as text + assert.ok(vscode.window.activeTextEditor); + assert.strictEqual(vscode.window.activeTextEditor!?.document.uri.toString(), testDocument.toString()); + }); + + test('Should release the text document when the editor is closed', async () => { + const startingContent = `release document init,`; + const testDocument = await writeRandomFile({ ext: '.abc', contents: startingContent }); + + const listener = CustomEditorUpdateListener.create(); + + await vscode.commands.executeCommand(commands.open, testDocument); + await listener.nextResponse(); + + const doc = vscode.workspace.textDocuments.find(x => x.uri.toString() === testDocument.toString()); + assert.ok(doc); + assert.ok(!doc!.isClosed); + + await closeAllEditors(); + await delay(100); + assert.ok(doc!.isClosed); + }); +}); + +async function resetTestWorkspace() { + try { + await vscode.workspace.fs.delete(testWorkspaceRoot, { recursive: true }); + } catch { + // ok if file doesn't exist + } + await vscode.workspace.fs.createDirectory(testWorkspaceRoot); +} diff --git a/extensions/vscode-custom-editor-tests/src/test/index.ts b/extensions/vscode-custom-editor-tests/src/test/index.ts new file mode 100644 index 00000000000..6d80cca8048 --- /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}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) + } + }; +} + +testRunner.configure(options); + +export = testRunner; diff --git a/extensions/vscode-custom-editor-tests/src/test/utils.ts b/extensions/vscode-custom-editor-tests/src/test/utils.ts new file mode 100644 index 00000000000..5edd53b27cc --- /dev/null +++ b/extensions/vscode-custom-editor-tests/src/test/utils.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +export function randomFilePath(args: { root: vscode.Uri, ext: string }): vscode.Uri { + const fileName = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10); + return (vscode.Uri as any).joinPath(args.root, fileName + args.ext); +} + +export function closeAllEditors(): Thenable { + return vscode.commands.executeCommand('workbench.action.closeAllEditors'); +} + +export function disposeAll(disposables: vscode.Disposable[]) { + vscode.Disposable.from(...disposables).dispose(); +} + +export function delay(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)); +} diff --git a/extensions/vscode-custom-editor-tests/src/typings/ref.d.ts b/extensions/vscode-custom-editor-tests/src/typings/ref.d.ts new file mode 100644 index 00000000000..bf67b19225d --- /dev/null +++ b/extensions/vscode-custom-editor-tests/src/typings/ref.d.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// +/// + diff --git a/extensions/vscode-custom-editor-tests/tsconfig.json b/extensions/vscode-custom-editor-tests/tsconfig.json new file mode 100644 index 00000000000..296ddb38fcb --- /dev/null +++ b/extensions/vscode-custom-editor-tests/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../shared.tsconfig.json", + "compilerOptions": { + "outDir": "./out" + }, + "include": [ + "src/**/*" + ] +} \ No newline at end of file diff --git a/extensions/vscode-custom-editor-tests/yarn.lock b/extensions/vscode-custom-editor-tests/yarn.lock new file mode 100644 index 00000000000..0d39ebbaa5e --- /dev/null +++ b/extensions/vscode-custom-editor-tests/yarn.lock @@ -0,0 +1,507 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + +"@types/node@^12.11.7": + version "12.12.53" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.53.tgz#be0d375933c3d15ef2380dafb3b0350ea7021129" + integrity sha512-51MYTDTyCziHb70wtGNFRwB4l+5JNvdqzFSkbDvpbftEgVUBEE+T5f7pROhWMp/fxp07oNIEQZd5bbfAH22ohQ== + +"@types/p-limit@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@types/p-limit/-/p-limit-2.2.0.tgz#94a608e9b258a6c6156a13d1a14fd720dba70b97" + integrity sha512-fGFbybl1r0oE9mqgfc2EHHUin9ZL5rbQIexWI6jYRU1ADVn4I3LHzT+g/kpPpZsfp8PB94CQ655pfAjNF8LP6A== + dependencies: + p-limit "*" + +agent-base@4, agent-base@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" + integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== + dependencies: + es6-promisify "^5.0.0" + +agent-base@6: + version "6.0.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.1.tgz#808007e4e5867decb0ab6ab2f928fbdb5a596db4" + integrity sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg== + dependencies: + debug "4" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +charenc@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= + +commander@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-0.6.1.tgz#fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06" + integrity sha1-+mihT2qUXVTbvlDYzbMyDp47GgY= + +commander@2.15.1: + version "2.15.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" + integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== + +commander@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.3.0.tgz#fd430e889832ec353b9acd1de217c11cb3eef873" + integrity sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM= + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +crypt@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= + +debug@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" + integrity sha1-+HBX6ZWxofauaklgZkE3vFbwOdo= + dependencies: + ms "0.7.1" + +debug@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@4: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +debug@^2.2.0: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^3.1.0: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +diff@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" + integrity sha1-fyjS657nsVqX79ic5j3P2qPMur8= + +diff@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + +es6-promise@^4.0.3: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= + dependencies: + es6-promise "^4.0.3" + +escape-string-regexp@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1" + integrity sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE= + +escape-string-regexp@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +glob@3.2.11: + version "3.2.11" + resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.11.tgz#4a973f635b9190f715d10987d5c00fd2815ebe3d" + integrity sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0= + dependencies: + inherits "2" + minimatch "0.3" + +glob@7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.2: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + +growl@1.9.2: + version "1.9.2" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" + integrity sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8= + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +he@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" + integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= + +http-proxy-agent@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" + integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== + dependencies: + agent-base "4" + debug "3.1.0" + +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + +https-proxy-agent@^2.2.1: + version "2.2.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" + integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== + dependencies: + agent-base "^4.3.0" + debug "^3.1.0" + +https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-buffer@~1.1.1: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +jade@0.26.3: + version "0.26.3" + resolved "https://registry.yarnpkg.com/jade/-/jade-0.26.3.tgz#8f10d7977d8d79f2f6ff862a81b0513ccb25686c" + integrity sha1-jxDXl32NefL2/4YqgbBRPMslaGw= + dependencies: + commander "0.6.1" + mkdirp "0.3.0" + +lodash@^4.16.4: + version "4.17.19" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" + integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== + +lru-cache@2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" + integrity sha1-bUUk6LlV+V1PW1iFHOId1y+06VI= + +md5@^2.1.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" + integrity sha1-U6s41f48iJG6RlMp6iP6wFQBJvk= + dependencies: + charenc "~0.0.1" + crypt "~0.0.1" + is-buffer "~1.1.1" + +minimatch@0.3: + version "0.3.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd" + integrity sha1-J12O2qxPG7MyZHIInnlJyDlGmd0= + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +minimatch@3.0.4, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +mkdirp@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" + integrity sha1-G79asbqCevI1dRQ0kEJkVfSB/h4= + +mkdirp@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +mkdirp@~0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +mocha-junit-reporter@^1.17.0: + version "1.23.3" + resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.23.3.tgz#941e219dd759ed732f8641e165918aa8b167c981" + integrity sha512-ed8LqbRj1RxZfjt/oC9t12sfrWsjZ3gNnbhV1nuj9R/Jb5/P3Xb4duv2eCfCDMYH+fEu0mqca7m4wsiVjsxsvA== + dependencies: + debug "^2.2.0" + md5 "^2.1.0" + mkdirp "~0.5.1" + strip-ansi "^4.0.0" + xml "^1.0.0" + +mocha-multi-reporters@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/mocha-multi-reporters/-/mocha-multi-reporters-1.1.7.tgz#cc7f3f4d32f478520941d852abb64d9988587d82" + integrity sha1-zH8/TTL0eFIJQdhSq7ZNmYhYfYI= + dependencies: + debug "^3.1.0" + lodash "^4.16.4" + +mocha@^2.3.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-2.5.3.tgz#161be5bdeb496771eb9b35745050b622b5aefc58" + integrity sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg= + dependencies: + commander "2.3.0" + debug "2.2.0" + diff "1.4.0" + escape-string-regexp "1.0.2" + glob "3.2.11" + growl "1.9.2" + jade "0.26.3" + mkdirp "0.5.1" + supports-color "1.2.0" + to-iso-string "0.0.2" + +mocha@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" + integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== + dependencies: + browser-stdout "1.3.1" + commander "2.15.1" + debug "3.1.0" + diff "3.5.0" + escape-string-regexp "1.0.5" + glob "7.1.2" + growl "1.10.5" + he "1.1.1" + minimatch "3.0.4" + mkdirp "0.5.1" + supports-color "5.4.0" + +ms@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" + integrity sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg= + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +p-limit@*, p-limit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.0.2.tgz#1664e010af3cadc681baafd3e2a437be7b0fb5fe" + integrity sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg== + dependencies: + p-try "^2.0.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +semver@^5.4.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +sigmund@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= + +source-map-support@^0.5.0: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +supports-color@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e" + integrity sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4= + +supports-color@5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" + integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w== + dependencies: + has-flag "^3.0.0" + +to-iso-string@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/to-iso-string/-/to-iso-string-0.0.2.tgz#4dc19e664dfccbe25bd8db508b00c6da158255d1" + integrity sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE= + +vscode-test@^0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-0.4.3.tgz#461ebf25fc4bc93d77d982aed556658a2e2b90b8" + integrity sha512-EkMGqBSefZH2MgW65nY05rdRSko15uvzq4VAPM5jVmwYuFQKE7eikKXNJDRxL+OITXHB6pI+a3XqqD32Y3KC5w== + dependencies: + http-proxy-agent "^2.1.0" + https-proxy-agent "^2.2.1" + +vscode@^1.1.36: + version "1.1.37" + resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.37.tgz#c2a770bee4bb3fff765e2b72c7bcc813b8a6bb0a" + integrity sha512-vJNj6IlN7IJPdMavlQa1KoFB3Ihn06q1AiN3ZFI/HfzPNzbKZWPPuiU+XkpNOfGU5k15m4r80nxNPlM7wcc0wg== + dependencies: + glob "^7.1.2" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + mocha "^5.2.0" + semver "^5.4.1" + source-map-support "^0.5.0" + vscode-test "^0.4.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +xml@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" + integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU= diff --git a/extensions/vscode-notebook-tests/package.json b/extensions/vscode-notebook-tests/package.json index 8d7e92bd255..4ed7605f894 100644 --- a/extensions/vscode-notebook-tests/package.json +++ b/extensions/vscode-notebook-tests/package.json @@ -27,6 +27,17 @@ "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", @@ -37,7 +48,35 @@ "excludeFileNamePattern": "" } ] + }, + { + "viewType": "notebookSmokeTest", + "displayName": "Notebook Smoke Test", + "selector": [ + { + "filenamePattern": "*.smoke-nb", + "excludeFileNamePattern": "" + } + ] } - ] + ], + "notebookOutputRenderer": [ + { + "viewType": "notebookCoreTestRenderer", + "displayName": "Notebook Core Test Renderer", + "mimeTypes": [ + "text/custom" + ] + } + ], + "menus": { + "notebook/cell/title": [ + { + "command": "vscode-notebook-tests.debugAction", + "when": "notebookViewType == notebookSmokeTest", + "group": "inline@1" + } + ] + } } } diff --git a/extensions/vscode-notebook-tests/src/customRenderer.js b/extensions/vscode-notebook-tests/src/customRenderer.js new file mode 100644 index 00000000000..75e2ec1eb7a --- /dev/null +++ b/extensions/vscode-notebook-tests/src/customRenderer.js @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * 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 + } +}); diff --git a/extensions/vscode-notebook-tests/src/notebook.test.ts b/extensions/vscode-notebook-tests/src/notebook.test.ts index 0de2917cf3f..378008c4d82 100644 --- a/extensions/vscode-notebook-tests/src/notebook.test.ts +++ b/extensions/vscode-notebook-tests/src/notebook.test.ts @@ -6,38 +6,408 @@ import 'mocha'; import * as assert from 'assert'; import * as vscode from 'vscode'; -import { join } from 'path'; +import { createRandomFile } from './utils'; -function waitFor(ms: number): Promise { - let resolveFunc: () => void; - - const promise = new Promise(resolve => { - resolveFunc = resolve; +export function timeoutAsync(n: number): Promise { + return new Promise(resolve => { + setTimeout(() => { + resolve(); + }, n); }); - setTimeout(() => { - resolveFunc!(); - }, ms); - - return promise; } +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) { + 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.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('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', 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.insert(1, 'test 2', 'javascript', vscode.CellKind.Code, [], undefined); + }); + + 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); + assert.deepEqual(cellChangeEventRet.changes[0].deletedCount, 0); + assert.equal(cellChangeEventRet.changes[0].items[0], vscode.notebook.activeNotebookEditor!.document.cells[1]); + + await saveFileAndCloseAll(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 () { - const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb')); + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - - await waitFor(500); assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); - assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, 'test'); + 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?.source, ''); + 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!.source, ''); + assert.equal(activeCell!.document.getText(), ''); assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 3); assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1); @@ -46,23 +416,22 @@ suite('notebook workflow', () => { }); test('notebook cell actions', async function () { - const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb')); + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - - await waitFor(500); assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); - assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, 'test'); + 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?.source, ''); + 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!.source, ''); + assert.equal(activeCell!.document.getText(), ''); assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 3); assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1); @@ -79,21 +448,21 @@ suite('notebook workflow', () => { 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?.source, 'test'); + 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?.source, ''); + 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].source, 'test'); - assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[1].source, 'test'); - assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[2].source, ''); - assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[3].source, ''); + 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); @@ -101,14 +470,18 @@ suite('notebook workflow', () => { // ---- move up and down ---- // await vscode.commands.executeCommand('notebook.cell.moveDown'); - await vscode.commands.executeCommand('notebook.cell.moveDown'); - activeCell = vscode.notebook.activeNotebookEditor!.selection; + 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()}`); - assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 2); - assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[0].source, 'test'); - assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[1].source, ''); - assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[2].source, 'test'); - assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[3].source, ''); + // 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(), ''); // ---- ---- // @@ -116,11 +489,54 @@ suite('notebook workflow', () => { await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); }); - // test.only('document metadata is respected', async function () { - // const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb')); - // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + 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 waitFor(500); + 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!; @@ -143,11 +559,9 @@ suite('notebook workflow', () => { // }); test('cell runnable metadata is respected', async () => { - const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb')); + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - - await waitFor(500); - assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); const editor = vscode.notebook.activeNotebookEditor!; @@ -167,11 +581,9 @@ suite('notebook workflow', () => { }); test('document runnable metadata is respected', async () => { - const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb')); + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - - await waitFor(500); - assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); const editor = vscode.notebook.activeNotebookEditor!; @@ -192,131 +604,428 @@ suite('notebook workflow', () => { suite('notebook dirty state', () => { test('notebook open', async function () { - const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb')); + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - - await waitFor(500); assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); - assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, 'test'); + 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?.source, ''); + 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!.source, ''); + 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('default:type', { text: 'var abc = 0;' }); - await vscode.commands.executeCommand('workbench.action.files.newUntitledFile'); - await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); - - await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - - await waitFor(500); + 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?.source, 'var abc = 0;'); + assert.equal(vscode.notebook.activeNotebookEditor?.selection?.document.getText(), 'var abc = 0;'); - await vscode.commands.executeCommand('workbench.action.files.save'); - await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + await saveFileAndCloseAll(resource); }); }); suite('notebook undo redo', () => { test('notebook open', async function () { - const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb')); + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - - await waitFor(500); assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); - assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, 'test'); + 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?.source, ''); + 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!.source, ''); + 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 - await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' }); + 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('notebook.undo'); + 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?.source, 'var abc = 0;'); + 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?.source, 'test'); + // assert.equal(vscode.notebook.activeNotebookEditor?.selection?.document.getText(), 'test'); - await vscode.commands.executeCommand('workbench.action.files.save'); - await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + 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 = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb')); - await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + // 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 waitFor(500); + // 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?.source, ''); + 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;' }); + 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); - // close active editor from command will revert the file + const secondResource = await createRandomFile('', undefined, 'second', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest'); await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); - await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - await waitFor(500); + // 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[0], vscode.notebook.activeNotebookEditor?.selection); - assert.equal(vscode.notebook.activeNotebookEditor?.selection?.source, 'test'); + 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 vscode.commands.executeCommand('workbench.action.files.save'); - await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + await saveFileAndCloseAll(resource); }); - test('notebook revert', async function () { - const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb')); + 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 waitFor(500); await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); - assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, ''); + 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'); + 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[0], vscode.notebook.activeNotebookEditor?.selection); - assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells.length, 1); - assert.equal(vscode.notebook.activeNotebookEditor?.selection?.source, 'test'); + 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 vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + // 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(); + const resource = await createRandomFile('', undefined, 'empty', '.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(), ''); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript'); + await saveFileAndCloseAll(resource); + }); + + 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 00000000000..0cec3fe06d6 --- /dev/null +++ b/extensions/vscode-notebook-tests/src/notebookSmokeTestMain.ts @@ -0,0 +1,109 @@ +/*--------------------------------------------------------------------------------------------- + * 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: () => { } + }; + } + })); + + context.subscriptions.push(vscode.notebook.registerNotebookKernel('notebookSmokeTest', ['*.smoke-nb'], { + label: 'notebookSmokeTest', + 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.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 index 4281ba002fd..c8ea3282c2c 100644 --- a/extensions/vscode-notebook-tests/src/notebookTestMain.ts +++ b/extensions/vscode-notebook-tests/src/notebookTestMain.ts @@ -4,36 +4,46 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import * as path from 'path'; +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: new vscode.EventEmitter().event, + onDidChangeNotebook: _onDidChangeNotebook.event, openNotebook: async (_resource: vscode.Uri) => { - return { + if (/.*empty\-.*\.vsctestnb$/.test(_resource.path)) { + return { + languages: ['typescript'], + metadata: {}, + cells: [] + }; + } + + const dto: vscode.NotebookData = { languages: ['typescript'], - metadata: {}, + metadata: { + custom: { testMetadata: false } + }, cells: [ { source: 'test', language: 'typescript', cellKind: vscode.CellKind.Code, outputs: [], - metadata: {} + metadata: { + custom: { testCellMetadata: 123 } + } } ] }; - }, - executeCell: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell | undefined, _token: vscode.CancellationToken) => { - if (!_cell) { - _cell = _document.cells[0]; - } - _cell.outputs = [{ - outputKind: vscode.CellOutputKind.Rich, - data: { - 'text/plain': ['my output'] - } - }]; + return dto; + }, + resolveNotebook: async (_document: vscode.NotebookDocument) => { return; }, saveNotebook: async (_document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => { @@ -41,6 +51,82 @@ export function activate(context: vscode.ExtensionContext): any { }, 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: () => { } + }; + } + })); + + context.subscriptions.push(vscode.notebook.registerNotebookKernel('notebookKernelTest', ['*.vsctestnb'], { + label: 'Notebook Test Kernel', + 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) => { } + })); + + const preloadUri = vscode.Uri.file(path.resolve(__dirname, '../src/customRenderer.js')); + context.subscriptions.push(vscode.notebook.registerNotebookOutputRenderer('notebookCoreTestRenderer', { + mimeTypes: [ + 'text/custom' + ] + }, { + preloads: [preloadUri], + render(_document: vscode.NotebookDocument, _request: vscode.NotebookRenderRequest): string { + return '
test
'; } })); } diff --git a/extensions/vscode-notebook-tests/src/utils.ts b/extensions/vscode-notebook-tests/src/utils.ts new file mode 100644 index 00000000000..0a8f20a1942 --- /dev/null +++ b/extensions/vscode-notebook-tests/src/utils.ts @@ -0,0 +1,261 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +import * as path from 'path'; +import * as vscode from 'vscode'; + +class File implements vscode.FileStat { + + type: vscode.FileType; + ctime: number; + mtime: number; + size: number; + + name: string; + data?: Uint8Array; + + constructor(name: string) { + this.type = vscode.FileType.File; + this.ctime = Date.now(); + this.mtime = Date.now(); + this.size = 0; + this.name = name; + } +} + +class Directory implements vscode.FileStat { + + type: vscode.FileType; + ctime: number; + mtime: number; + size: number; + + name: string; + entries: Map; + + constructor(name: string) { + this.type = vscode.FileType.Directory; + this.ctime = Date.now(); + this.mtime = Date.now(); + this.size = 0; + this.name = name; + this.entries = new Map(); + } +} + +export type Entry = File | Directory; + +export class TestFS implements vscode.FileSystemProvider { + + constructor( + readonly scheme: string, + readonly isCaseSensitive: boolean + ) { } + + readonly root = new Directory(''); + + // --- manage file metadata + + stat(uri: vscode.Uri): vscode.FileStat { + return this._lookup(uri, false); + } + + readDirectory(uri: vscode.Uri): [string, vscode.FileType][] { + const entry = this._lookupAsDirectory(uri, false); + const result: [string, vscode.FileType][] = []; + for (const [name, child] of entry.entries) { + result.push([name, child.type]); + } + return result; + } + + // --- manage file contents + + readFile(uri: vscode.Uri): Uint8Array { + const data = this._lookupAsFile(uri, false).data; + if (data) { + return data; + } + throw vscode.FileSystemError.FileNotFound(); + } + + writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean, overwrite: boolean }): void { + const basename = path.posix.basename(uri.path); + const parent = this._lookupParentDirectory(uri); + let entry = parent.entries.get(basename); + if (entry instanceof Directory) { + throw vscode.FileSystemError.FileIsADirectory(uri); + } + if (!entry && !options.create) { + throw vscode.FileSystemError.FileNotFound(uri); + } + if (entry && options.create && !options.overwrite) { + throw vscode.FileSystemError.FileExists(uri); + } + if (!entry) { + entry = new File(basename); + parent.entries.set(basename, entry); + this._fireSoon({ type: vscode.FileChangeType.Created, uri }); + } + entry.mtime = Date.now(); + entry.size = content.byteLength; + entry.data = content; + + this._fireSoon({ type: vscode.FileChangeType.Changed, uri }); + } + + // --- manage files/folders + + rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean }): void { + + if (!options.overwrite && this._lookup(newUri, true)) { + throw vscode.FileSystemError.FileExists(newUri); + } + + const entry = this._lookup(oldUri, false); + const oldParent = this._lookupParentDirectory(oldUri); + + const newParent = this._lookupParentDirectory(newUri); + const newName = path.posix.basename(newUri.path); + + oldParent.entries.delete(entry.name); + entry.name = newName; + newParent.entries.set(newName, entry); + + this._fireSoon( + { type: vscode.FileChangeType.Deleted, uri: oldUri }, + { type: vscode.FileChangeType.Created, uri: newUri } + ); + } + + delete(uri: vscode.Uri): void { + const dirname = uri.with({ path: path.posix.dirname(uri.path) }); + const basename = path.posix.basename(uri.path); + const parent = this._lookupAsDirectory(dirname, false); + if (!parent.entries.has(basename)) { + throw vscode.FileSystemError.FileNotFound(uri); + } + parent.entries.delete(basename); + parent.mtime = Date.now(); + parent.size -= 1; + this._fireSoon({ type: vscode.FileChangeType.Changed, uri: dirname }, { uri, type: vscode.FileChangeType.Deleted }); + } + + createDirectory(uri: vscode.Uri): void { + const basename = path.posix.basename(uri.path); + const dirname = uri.with({ path: path.posix.dirname(uri.path) }); + const parent = this._lookupAsDirectory(dirname, false); + + const entry = new Directory(basename); + parent.entries.set(entry.name, entry); + parent.mtime = Date.now(); + parent.size += 1; + this._fireSoon({ type: vscode.FileChangeType.Changed, uri: dirname }, { type: vscode.FileChangeType.Created, uri }); + } + + // --- lookup + + private _lookup(uri: vscode.Uri, silent: false): Entry; + private _lookup(uri: vscode.Uri, silent: boolean): Entry | undefined; + private _lookup(uri: vscode.Uri, silent: boolean): Entry | undefined { + const parts = uri.path.split('/'); + let entry: Entry = this.root; + for (const part of parts) { + const partLow = part.toLowerCase(); + if (!part) { + continue; + } + let child: Entry | undefined; + if (entry instanceof Directory) { + if (this.isCaseSensitive) { + child = entry.entries.get(part); + } else { + for (const [key, value] of entry.entries) { + if (key.toLowerCase() === partLow) { + child = value; + break; + } + } + } + } + if (!child) { + if (!silent) { + throw vscode.FileSystemError.FileNotFound(uri); + } else { + return undefined; + } + } + entry = child; + } + return entry; + } + + private _lookupAsDirectory(uri: vscode.Uri, silent: boolean): Directory { + const entry = this._lookup(uri, silent); + if (entry instanceof Directory) { + return entry; + } + throw vscode.FileSystemError.FileNotADirectory(uri); + } + + private _lookupAsFile(uri: vscode.Uri, silent: boolean): File { + const entry = this._lookup(uri, silent); + if (entry instanceof File) { + return entry; + } + throw vscode.FileSystemError.FileIsADirectory(uri); + } + + private _lookupParentDirectory(uri: vscode.Uri): Directory { + const dirname = uri.with({ path: path.posix.dirname(uri.path) }); + return this._lookupAsDirectory(dirname, false); + } + + // --- manage file events + + private _emitter = new vscode.EventEmitter(); + private _bufferedEvents: vscode.FileChangeEvent[] = []; + private _fireSoonHandle?: NodeJS.Timer; + + readonly onDidChangeFile: vscode.Event = this._emitter.event; + + watch(_resource: vscode.Uri): vscode.Disposable { + // ignore, fires for all changes... + return new vscode.Disposable(() => { }); + } + + private _fireSoon(...events: vscode.FileChangeEvent[]): void { + this._bufferedEvents.push(...events); + + if (this._fireSoonHandle) { + clearTimeout(this._fireSoonHandle); + } + + this._fireSoonHandle = setTimeout(() => { + this._emitter.fire(this._bufferedEvents); + this._bufferedEvents.length = 0; + }, 5); + } +} + +export function rndName() { + return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10); +} + +export const testFs = new TestFS('fake-fs', true); +vscode.workspace.registerFileSystemProvider(testFs.scheme, testFs, { isCaseSensitive: testFs.isCaseSensitive }); + +export async function createRandomFile(contents = '', dir: vscode.Uri | undefined = undefined, prefix = '', ext = ''): Promise { + let fakeFile: vscode.Uri; + if (dir) { + fakeFile = dir.with({ path: dir.path + '/' + rndName() + ext }); + } else { + fakeFile = vscode.Uri.parse(`${testFs.scheme}:/${prefix}-${rndName() + ext}`); + } + + await testFs.writeFile(fakeFile, Buffer.from(contents), { create: true, overwrite: true }); + return fakeFile; +} diff --git a/extensions/vscode-notebook-tests/test/customRenderer.vsctestnb b/extensions/vscode-notebook-tests/test/customRenderer.vsctestnb new file mode 100644 index 00000000000..a4a092d8349 --- /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 00000000000..a4a092d8349 --- /dev/null +++ b/extensions/vscode-notebook-tests/test/empty.vsctestnb @@ -0,0 +1,4 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ diff --git a/extensions/vscode-notebook-tests/test/second.vsctestnb b/extensions/vscode-notebook-tests/test/second.vsctestnb new file mode 100644 index 00000000000..a4a092d8349 --- /dev/null +++ b/extensions/vscode-notebook-tests/test/second.vsctestnb @@ -0,0 +1,4 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ diff --git a/extensions/vscode-web-playground/.gitignore b/extensions/vscode-web-playground/.gitignore index 8e5962ee727..c19bd94aaa7 100644 --- a/extensions/vscode-web-playground/.gitignore +++ b/extensions/vscode-web-playground/.gitignore @@ -1,2 +1,3 @@ +dist out -node_modules \ No newline at end of file +node_modules diff --git a/extensions/vscode-web-playground/.vscodeignore b/extensions/vscode-web-playground/.vscodeignore index eb6a48615c7..32fe3f03697 100644 --- a/extensions/vscode-web-playground/.vscodeignore +++ b/extensions/vscode-web-playground/.vscodeignore @@ -1,6 +1,11 @@ .vscode/** +build/** +dist/** +out/** +src/** typings/** -**/*.ts -**/*.map .gitignore +extension-browser.webpack.config.js +extension.webpack.config.js tsconfig.json +yarn.lock diff --git a/extensions/vscode-web-playground/extension-browser.webpack.config.js b/extensions/vscode-web-playground/extension-browser.webpack.config.js new file mode 100644 index 00000000000..dfd50aeff96 --- /dev/null +++ b/extensions/vscode-web-playground/extension-browser.webpack.config.js @@ -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. + *--------------------------------------------------------------------------------------------*/ + +//@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', + } +}); diff --git a/extensions/vscode-web-playground/extension.webpack.config.js b/extensions/vscode-web-playground/extension.webpack.config.js new file mode 100644 index 00000000000..45600607fc5 --- /dev/null +++ b/extensions/vscode-web-playground/extension.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 withDefaults = require('../shared.webpack.config'); + +module.exports = withDefaults({ + context: __dirname, + entry: { + extension: './src/extension.ts' + } +}); diff --git a/extensions/vscode-web-playground/package.json b/extensions/vscode-web-playground/package.json index 46b6102c10e..954aec0fae9 100644 --- a/extensions/vscode-web-playground/package.json +++ b/extensions/vscode-web-playground/package.json @@ -8,8 +8,10 @@ "private": true, "activationEvents": [ "onFileSystem:memfs", + "onFileSystem:github", "onDebug" ], + "browser": "./dist/browser/extension", "main": "./out/extension", "engines": { "vscode": "^1.25.0" @@ -78,15 +80,27 @@ } ] } + ], + "resourceLabelFormatters": [ + { + "scheme": "github", + "authority": "*", + "formatting": { + "label": "${authority}${path}", + "separator": "/", + "workspaceSuffix": "GitHub" + } + } ] }, "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" + "compile-web": "npx webpack-cli --config extension.webpack.config --mode none", + "watch-web": "npx webpack-cli --config extension.webpack.config --mode none --watch --info-verbosity verbose", + "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-web-playground ./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" } diff --git a/extensions/vscode-web-playground/src/exampleFiles.ts b/extensions/vscode-web-playground/src/exampleFiles.ts new file mode 100644 index 00000000000..a385f7b72e7 --- /dev/null +++ b/extensions/vscode-web-playground/src/exampleFiles.ts @@ -0,0 +1,310 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const largeTSFile = `/// +/// + +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; + } +} +} +`; + +export const debuggableFile = `# VS Code Mock Debug + +This is a starter sample for developing VS Code debug adapters. + +**Mock Debug** simulates a debug adapter for Visual Studio Code. +It supports *step*, *continue*, *breakpoints*, *exceptions*, and +*variable access* but it is not connected to any real debugger. + +The sample is meant as an educational piece showing how to implement a debug +adapter for VS Code. It can be used as a starting point for developing a real adapter. + +More information about how to develop a new debug adapter can be found +[here](https://code.visualstudio.com/docs/extensions/example-debuggers). +Or discuss debug adapters on Gitter: +[![Gitter Chat](https://img.shields.io/badge/chat-online-brightgreen.svg)](https://gitter.im/Microsoft/vscode) + +## Using Mock Debug + +* Install the **Mock Debug** extension in VS Code. +* Create a new 'program' file 'readme.md' and enter several lines of arbitrary text. +* Switch to the debug viewlet and press the gear dropdown. +* Select the debug environment "Mock Debug". +* Press the green 'play' button to start debugging. + +You can now 'step through' the 'readme.md' file, set and hit breakpoints, and run into exceptions (if the word exception appears in a line). + +![Mock Debug](file.jpg) + +## Build and Run + +[![build status](https://travis-ci.org/Microsoft/vscode-mock-debug.svg?branch=master)](https://travis-ci.org/Microsoft/vscode-mock-debug) +[![build status](https://ci.appveyor.com/api/projects/status/empmw5q1tk6h1fly/branch/master?svg=true)](https://ci.appveyor.com/project/weinand/vscode-mock-debug) + + +* Clone the project [https://github.com/Microsoft/vscode-mock-debug.git](https://github.com/Microsoft/vscode-mock-debug.git) +* Open the project folder in VS Code. +* Press 'F5' to build and launch Mock Debug in another VS Code window. In that window: +* Open a new workspace, create a new 'program' file 'readme.md' and enter several lines of arbitrary text. +* Switch to the debug viewlet and press the gear dropdown. +* Select the debug environment "Mock Debug". +* Press 'F5' to start debugging.`; + +export function getImageFile(): Uint8Array { + const data = atob(`/9j/4AAQSkZJRgABAQAASABIAAD/2wCEAA4ODg4ODhcODhchFxcXIS0hISEhLTktLS0tLTlFOTk5OTk5RUVFRUVFRUVSUlJSUlJgYGBgYGxsbGxsbGxsbGwBERISGxkbLxkZL3FMP0xxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcf/AABEIAFYAZAMBIgACEQEDEQH/xAB1AAACAwEBAQAAAAAAAAAAAAAABAMFBgIBBxAAAgIBAwMCBQQCAwAAAAAAAQIAAxEEBSESMUFRcRMiIzJhFIGRoQbBQlKxAQEBAQEAAAAAAAAAAAAAAAABAgADEQEBAQADAQEAAAAAAAAAAAAAARESITECQf/aAAwDAQACEQMRAD8A2LEZkLc/bKxbdYEHWoyfEze56zXpqRTTYUyPHiVrY2TVZyMzhFZMg8iYE6jcVXAusY98KMnj2lhRu+4aLoGuTNTYPV5APnyDNyPFp6EY3EsO3kxnVVLZVg8z2tw9YsXkGQpcbGIbxHQzep0vw8Jgc8n28CJJRY30lBwzf1iaa2ku/HmMV01VW/k/6hh0abTDTafpPcTytmckEewjeosAqJEj0yDo6yO/rFLzoGME5nIAXtGSM9uwnjLn8zFECw7QneITMWouR7gj9/Ep94061bjXa32WDGfzOGuCXKy9/wDc0FlFe5aX4OpHJHBHcSfT4w246bWJar6MsCwKnp9DOF0r6XRiu5snvg9hNK217vQeih0tXwzcED895R7voNfWoN9gOT2QH/2T3mHrda3Y+p9ppZuSV/qR0j6r+5ju2oun2ypOwCAASGikISzdySf5lxLsAdRPpIqw91xC/wDHvGbAAh88RnSVCjT9b8E/MYsguerTqWuYKo8k4ESTcttsPSmoQ+zCZPWPbvWqsvLE0IxCL4wPP7xEW7TXeKsvaGABOMdLef2ky7ejevX0tBWy5Qhh6jmS9IIxPm6XazbW69K56M/aeRibnSaqyytWtGCfE0+tazDhrHpCdixT5EJSWD1BPkcjsYxpN21FWEcdu0dG3hl8rIX0YqUgDqkSrq/0+6oyfOOZT7hqxqLMKMk8ARfS0fqGatAR04yCY+u3OpLt38e0rQl0tzsFrc8rxj0lqqDHMzujIXUMGPI4mjS1MTCvG8gRLddYE2811n5nHTJ9RaAsztzZ1AZhlX9fBi0VWgWzbSqahfpWfa/iSnatMuqOpVgVPIHGMzc6erS3aQVOoZSMFTK19i2pTwGA9Axx/E58b+K2M8lP6/Urp6BkA5Y+OPE112nrIFeOw8RMajQ7dWU0iAH8TyrVG0mw8EypMFuk7K9TS5RGJHiEYsuUtmEWO1KO2RGDRSVJzj1MiQhOQIx8QEYK5hGpUUJVc1lTgcDjEe1FPxqGQHBZSMiQqa8/Z38xgOoHB/aIfJNVZrdFqirsVbsfzLXT7+UQLYmcDHBlh/k+g+KP1dOCV+4efcTNbdtGq3CxQiMKyeX7CGqxqtDuK7lYK2BXnAz3JMuNZoPpDAyV5zHNt2bRbcA1S/Pjljyf7jerWxx0V4wQeZgynxrUXoUnIif629GJY595cptr1N9XJYjOfEi1G3LYMLgH1m04qxelrAtnj/qZYIvUPpMcHwYtTT8FzVaMN6+sslqVF6gcQ1sRivPccwjS314+bGYRBnqzws6FhUfL7CQ8gdI7+TDIHHgcSVGBYRznMXfUL2J5ngPUOYCpfM2tiq1tnUpVRnMe0DGtAKyQIw+mU4GJCKmrPy+I6V0lxYYIzxOCtdjZyVIMRqtPsYx8RT37+sdRhsFlHzcyC0J0kmcfqFX5cxC7VAk4OPUQtM+UVtYf7vH8iKP8SnKg5U9xHQwsGV7jxF9QnWACMEcgwlUjT4ZUE+YRRLGRehwciEpLRMAAT6SALlIQkF4kl7HEIQLwuQfac9RPeEJi5H3TruvvmEJo1QOcgGQuvVg+sITM8rDKeDHVItXkQhKgqM6esnJEIQlJf//Z`); + return Uint8Array.from([...data].map(x => x.charCodeAt(0))); +} + +// encoded from 'АБВГДЕЖЗИЙКЛМНОПРСĐĸĐŖĐ¤ĐĨĐĻЧШЊĐĒĐĢĐŦĐ­ĐŽĐ¯Đ°ĐąĐ˛ĐŗĐ´ĐĩĐļСиКĐēĐģĐŧĐŊĐžĐŋŅ€ŅŅ‚ŅƒŅ„Ņ…Ņ†Ņ‡ŅˆŅ‰ŅŠŅ‹ŅŒŅŅŽŅ' +export const windows1251File = Uint8Array.from([192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255]); + +// encoded from '中å›Ŋabc' +export const gbkFile = Uint8Array.from([214, 208, 185, 250, 97, 98, 99]); diff --git a/extensions/vscode-web-playground/src/extension.ts b/extensions/vscode-web-playground/src/extension.ts index 5bd7ba43ad6..1d5df92bbcc 100644 --- a/extensions/vscode-web-playground/src/extension.ts +++ b/extensions/vscode-web-playground/src/extension.ts @@ -13,373 +13,32 @@ // import * as vscode from 'vscode'; +import { MemFS } from './memfs'; -declare const window: unknown; - -const textEncoder = new TextEncoder(); -const SCHEME = 'memfs'; +declare const navigator: unknown; export function activate(context: vscode.ExtensionContext) { - if (typeof window !== 'undefined') { // do not run under node.js + if (typeof navigator === 'object') { // do not run under node.js const memFs = enableFs(context); - enableProblems(context); - enableSearch(context, memFs); - enableTasks(); - enableDebug(context, memFs); - vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(`memfs:/sample-folder/large.ts`)); + if (vscode.workspace.workspaceFolders?.some(f => f.uri.scheme === MemFS.scheme)) { + memFs.seed(); + enableProblems(context); + enableTasks(); + enableDebug(context, memFs); + + vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(`memfs:/sample-folder/large.ts`)); + } } } function enableFs(context: vscode.ExtensionContext): MemFS { const memFs = new MemFS(); - context.subscriptions.push(vscode.workspace.registerFileSystemProvider(SCHEME, memFs, { isCaseSensitive: true })); - - memFs.createDirectory(vscode.Uri.parse(`memfs:/sample-folder/`)); - - // most common files types - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/large.ts`), textEncoder.encode(getLargeTSFile()), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.txt`), textEncoder.encode('foo'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.html`), textEncoder.encode('

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(getDebuggableFile()), { 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; - } - } -} -`; - } - - function getDebuggableFile(): string { - return `# VS Code Mock Debug - -This is a starter sample for developing VS Code debug adapters. - -**Mock Debug** simulates a debug adapter for Visual Studio Code. -It supports *step*, *continue*, *breakpoints*, *exceptions*, and -*variable access* but it is not connected to any real debugger. - -The sample is meant as an educational piece showing how to implement a debug -adapter for VS Code. It can be used as a starting point for developing a real adapter. - -More information about how to develop a new debug adapter can be found -[here](https://code.visualstudio.com/docs/extensions/example-debuggers). -Or discuss debug adapters on Gitter: -[![Gitter Chat](https://img.shields.io/badge/chat-online-brightgreen.svg)](https://gitter.im/Microsoft/vscode) - -## Using Mock Debug - -* Install the **Mock Debug** extension in VS Code. -* Create a new 'program' file 'readme.md' and enter several lines of arbitrary text. -* Switch to the debug viewlet and press the gear dropdown. -* Select the debug environment "Mock Debug". -* Press the green 'play' button to start debugging. - -You can now 'step through' the 'readme.md' file, set and hit breakpoints, and run into exceptions (if the word exception appears in a line). - -![Mock Debug](images/mock-debug.gif) - -## Build and Run - -[![build status](https://travis-ci.org/Microsoft/vscode-mock-debug.svg?branch=master)](https://travis-ci.org/Microsoft/vscode-mock-debug) -[![build status](https://ci.appveyor.com/api/projects/status/empmw5q1tk6h1fly/branch/master?svg=true)](https://ci.appveyor.com/project/weinand/vscode-mock-debug) - - -* Clone the project [https://github.com/Microsoft/vscode-mock-debug.git](https://github.com/Microsoft/vscode-mock-debug.git) -* Open the project folder in VS Code. -* Press 'F5' to build and launch Mock Debug in another VS Code window. In that window: - * Open a new workspace, create a new 'program' file 'readme.md' and enter several lines of arbitrary text. - * Switch to the debug viewlet and press the gear dropdown. - * Select the debug environment "Mock Debug". - * Press 'F5' to start debugging.`; - } + context.subscriptions.push(memFs); 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) { @@ -415,11 +74,6 @@ function updateDiagnostics(document: vscode.TextDocument, collection: vscode.Dia } } -function enableSearch(context: vscode.ExtensionContext, memFs: MemFS): void { - context.subscriptions.push(vscode.workspace.registerFileSearchProvider(SCHEME, memFs)); - context.subscriptions.push(vscode.workspace.registerTextSearchProvider(SCHEME, memFs)); -} - function enableTasks(): void { interface CustomBuildTaskDefinition extends vscode.TaskDefinition { @@ -554,347 +208,6 @@ function enableTasks(): void { 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, vscode.TextSearchProvider { - - root = new Directory(vscode.Uri.parse('memfs:/'), ''); - - // --- manage file metadata - - stat(uri: vscode.Uri): vscode.FileStat { - return this._lookup(uri, false); - } - - readDirectory(uri: vscode.Uri): [string, vscode.FileType][] { - const entry = this._lookupAsDirectory(uri, false); - let result: [string, vscode.FileType][] = []; - for (const [name, child] of entry.entries) { - result.push([name, child.type]); - } - return result; - } - - // --- manage file contents - - readFile(uri: vscode.Uri): Uint8Array { - const data = this._lookupAsFile(uri, false).data; - if (data) { - return data; - } - throw vscode.FileSystemError.FileNotFound(); - } - - writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean, overwrite: boolean }): void { - let basename = this._basename(uri.path); - let parent = this._lookupParentDirectory(uri); - let entry = parent.entries.get(basename); - if (entry instanceof Directory) { - throw vscode.FileSystemError.FileIsADirectory(uri); - } - if (!entry && !options.create) { - throw vscode.FileSystemError.FileNotFound(uri); - } - if (entry && options.create && !options.overwrite) { - throw vscode.FileSystemError.FileExists(uri); - } - if (!entry) { - entry = new File(uri, basename); - parent.entries.set(basename, entry); - this._fireSoon({ type: vscode.FileChangeType.Created, uri }); - } - entry.mtime = Date.now(); - entry.size = content.byteLength; - entry.data = content; - - this._fireSoon({ type: vscode.FileChangeType.Changed, uri }); - } - - // --- manage files/folders - - rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean }): void { - if (!options.overwrite && this._lookup(newUri, true)) { - throw vscode.FileSystemError.FileExists(newUri); - } - - let entry = this._lookup(oldUri, false); - let oldParent = this._lookupParentDirectory(oldUri); - - let newParent = this._lookupParentDirectory(newUri); - let newName = this._basename(newUri.path); - - oldParent.entries.delete(entry.name); - entry.name = newName; - newParent.entries.set(newName, entry); - - this._fireSoon( - { type: vscode.FileChangeType.Deleted, uri: oldUri }, - { type: vscode.FileChangeType.Created, uri: newUri } - ); - } - - delete(uri: vscode.Uri): void { - let dirname = uri.with({ path: this._dirname(uri.path) }); - let basename = this._basename(uri.path); - let parent = this._lookupAsDirectory(dirname, false); - if (!parent.entries.has(basename)) { - throw vscode.FileSystemError.FileNotFound(uri); - } - parent.entries.delete(basename); - parent.mtime = Date.now(); - parent.size -= 1; - this._fireSoon({ type: vscode.FileChangeType.Changed, uri: dirname }, { uri, type: vscode.FileChangeType.Deleted }); - } - - createDirectory(uri: vscode.Uri): void { - let basename = this._basename(uri.path); - let dirname = uri.with({ path: this._dirname(uri.path) }); - let parent = this._lookupAsDirectory(dirname, false); - - let entry = new Directory(uri, basename); - parent.entries.set(entry.name, entry); - parent.mtime = Date.now(); - parent.size += 1; - this._fireSoon({ type: vscode.FileChangeType.Changed, uri: dirname }, { type: vscode.FileChangeType.Created, uri }); - } - - // --- lookup - - private _lookup(uri: vscode.Uri, silent: false): Entry; - private _lookup(uri: vscode.Uri, silent: boolean): Entry | undefined; - private _lookup(uri: vscode.Uri, silent: boolean): Entry | undefined { - let parts = uri.path.split('/'); - let entry: Entry = this.root; - for (const part of parts) { - if (!part) { - continue; - } - let child: Entry | undefined; - if (entry instanceof Directory) { - child = entry.entries.get(part); - } - if (!child) { - if (!silent) { - throw vscode.FileSystemError.FileNotFound(uri); - } else { - return undefined; - } - } - entry = child; - } - return entry; - } - - private _lookupAsDirectory(uri: vscode.Uri, silent: boolean): Directory { - let entry = this._lookup(uri, silent); - if (entry instanceof Directory) { - return entry; - } - throw vscode.FileSystemError.FileNotADirectory(uri); - } - - private _lookupAsFile(uri: vscode.Uri, silent: boolean): File { - let entry = this._lookup(uri, silent); - if (entry instanceof File) { - return entry; - } - throw vscode.FileSystemError.FileIsADirectory(uri); - } - - private _lookupParentDirectory(uri: vscode.Uri): Directory { - const dirname = uri.with({ path: this._dirname(uri.path) }); - return this._lookupAsDirectory(dirname, false); - } - - // --- manage file events - - private _emitter = new vscode.EventEmitter(); - 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 { - return this._findFiles(query.pattern); - } - - private _findFiles(query: string | undefined): vscode.Uri[] { - const files = this._getFiles(); - const result: vscode.Uri[] = []; - - const pattern = query ? new RegExp(this._convertSimple2RegExpPattern(query)) : null; - - for (const file of files) { - if (!pattern || pattern.exec(file.name)) { - result.push(file.uri); - } - } - - return result; - } - - private _textDecoder = new TextDecoder(); - - provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, _token: vscode.CancellationToken) { - const result: vscode.TextSearchComplete = { limitHit: false }; - - const files = this._findFiles(options.includes[0]); - if (files) { - for (const file of files) { - const content = this._textDecoder.decode(this.readFile(file)); - - const lines = content.split('\n'); - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - const index = line.indexOf(query.pattern); - if (index !== -1) { - progress.report({ - uri: file, - ranges: new vscode.Range(new vscode.Position(i, index), new vscode.Position(i, index + query.pattern.length)), - preview: { - text: line, - matches: new vscode.Range(new vscode.Position(0, index), new vscode.Position(0, index + query.pattern.length)) - } - }); - } - } - } - } - - return result; - } -} - //--------------------------------------------------------------------------- // DEBUG //--------------------------------------------------------------------------- diff --git a/extensions/vscode-web-playground/src/memfs.ts b/extensions/vscode-web-playground/src/memfs.ts new file mode 100644 index 00000000000..9a135109fb4 --- /dev/null +++ b/extensions/vscode-web-playground/src/memfs.ts @@ -0,0 +1,449 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { + CancellationToken, + Disposable, + Event, + EventEmitter, + FileChangeEvent, + FileChangeType, + FileSearchOptions, + FileSearchProvider, + FileSearchQuery, + FileStat, + FileSystemError, + FileSystemProvider, + FileType, + Position, + Progress, + ProviderResult, + Range, + TextSearchComplete, + TextSearchOptions, + TextSearchQuery, + TextSearchProvider, + TextSearchResult, + Uri, + workspace, +} from 'vscode'; +import { largeTSFile, getImageFile, debuggableFile, windows1251File, gbkFile } from './exampleFiles'; + +export class File implements FileStat { + + type: FileType; + ctime: number; + mtime: number; + size: number; + + name: string; + data?: Uint8Array; + + constructor(public uri: Uri, name: string) { + this.type = FileType.File; + this.ctime = Date.now(); + this.mtime = Date.now(); + this.size = 0; + this.name = name; + } +} + +export class Directory implements FileStat { + + type: FileType; + ctime: number; + mtime: number; + size: number; + + name: string; + entries: Map; + + constructor(public uri: Uri, name: string) { + this.type = 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; + +const textEncoder = new TextEncoder(); + +export class MemFS implements FileSystemProvider, FileSearchProvider, TextSearchProvider, Disposable { + static scheme = 'memfs'; + + private readonly disposable: Disposable; + + constructor() { + this.disposable = Disposable.from( + workspace.registerFileSystemProvider(MemFS.scheme, this, { isCaseSensitive: true }), + workspace.registerFileSearchProvider(MemFS.scheme, this), + workspace.registerTextSearchProvider(MemFS.scheme, this) + ); + } + + dispose() { + this.disposable?.dispose(); + } + + seed() { + this.createDirectory(Uri.parse(`memfs:/sample-folder/`)); + + // most common files types + this.writeFile(Uri.parse(`memfs:/sample-folder/large.ts`), textEncoder.encode(largeTSFile), { create: true, overwrite: true }); + this.writeFile(Uri.parse(`memfs:/sample-folder/file.txt`), textEncoder.encode('foo'), { create: true, overwrite: true }); + this.writeFile(Uri.parse(`memfs:/sample-folder/file.html`), textEncoder.encode('

Hello

'), { create: true, overwrite: true }); + this.writeFile(Uri.parse(`memfs:/sample-folder/file.js`), textEncoder.encode('console.log("JavaScript")'), { create: true, overwrite: true }); + this.writeFile(Uri.parse(`memfs:/sample-folder/file.json`), textEncoder.encode('{ "json": true }'), { create: true, overwrite: true }); + this.writeFile(Uri.parse(`memfs:/sample-folder/file.ts`), textEncoder.encode('console.log("TypeScript")'), { create: true, overwrite: true }); + this.writeFile(Uri.parse(`memfs:/sample-folder/file.css`), textEncoder.encode('* { color: green; }'), { create: true, overwrite: true }); + this.writeFile(Uri.parse(`memfs:/sample-folder/file.md`), textEncoder.encode(debuggableFile), { create: true, overwrite: true }); + this.writeFile(Uri.parse(`memfs:/sample-folder/file.xml`), textEncoder.encode(''), { create: true, overwrite: true }); + this.writeFile(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 }); + this.writeFile(Uri.parse(`memfs:/sample-folder/file.php`), textEncoder.encode('&1\'); ?>'), { create: true, overwrite: true }); + this.writeFile(Uri.parse(`memfs:/sample-folder/file.yaml`), textEncoder.encode('- just: write something'), { create: true, overwrite: true }); + this.writeFile(Uri.parse(`memfs:/sample-folder/file.jpg`), getImageFile(), { create: true, overwrite: true }); + + // some more files & folders + this.createDirectory(Uri.parse(`memfs:/sample-folder/folder/`)); + this.createDirectory(Uri.parse(`memfs:/sample-folder/large/`)); + this.createDirectory(Uri.parse(`memfs:/sample-folder/xyz/`)); + this.createDirectory(Uri.parse(`memfs:/sample-folder/xyz/abc`)); + this.createDirectory(Uri.parse(`memfs:/sample-folder/xyz/def`)); + + this.writeFile(Uri.parse(`memfs:/sample-folder/folder/empty.txt`), new Uint8Array(0), { create: true, overwrite: true }); + this.writeFile(Uri.parse(`memfs:/sample-folder/folder/empty.foo`), new Uint8Array(0), { create: true, overwrite: true }); + this.writeFile(Uri.parse(`memfs:/sample-folder/folder/file.ts`), textEncoder.encode('let a:number = true; console.log(a);'), { create: true, overwrite: true }); + this.writeFile(Uri.parse(`memfs:/sample-folder/large/rnd.foo`), randomData(50000), { create: true, overwrite: true }); + this.writeFile(Uri.parse(`memfs:/sample-folder/xyz/UPPER.txt`), textEncoder.encode('UPPER'), { create: true, overwrite: true }); + this.writeFile(Uri.parse(`memfs:/sample-folder/xyz/upper.txt`), textEncoder.encode('upper'), { create: true, overwrite: true }); + this.writeFile(Uri.parse(`memfs:/sample-folder/xyz/def/foo.md`), textEncoder.encode('*MemFS*'), { create: true, overwrite: true }); + + // some files in different encodings + this.createDirectory(Uri.parse(`memfs:/sample-folder/encodings/`)); + this.writeFile( + Uri.parse(`memfs:/sample-folder/encodings/windows1251.txt`), + windows1251File, + { create: true, overwrite: true } + ); + this.writeFile( + Uri.parse(`memfs:/sample-folder/encodings/gbk.txt`), + gbkFile, + { create: true, overwrite: true } + ); + } + + root = new Directory(Uri.parse('memfs:/'), ''); + + // --- manage file metadata + + stat(uri: Uri): FileStat { + return this._lookup(uri, false); + } + + readDirectory(uri: Uri): [string, FileType][] { + const entry = this._lookupAsDirectory(uri, false); + let result: [string, FileType][] = []; + for (const [name, child] of entry.entries) { + result.push([name, child.type]); + } + return result; + } + + // --- manage file contents + + readFile(uri: Uri): Uint8Array { + const data = this._lookupAsFile(uri, false).data; + if (data) { + return data; + } + throw FileSystemError.FileNotFound(); + } + + writeFile(uri: 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 FileSystemError.FileIsADirectory(uri); + } + if (!entry && !options.create) { + throw FileSystemError.FileNotFound(uri); + } + if (entry && options.create && !options.overwrite) { + throw FileSystemError.FileExists(uri); + } + if (!entry) { + entry = new File(uri, basename); + parent.entries.set(basename, entry); + this._fireSoon({ type: FileChangeType.Created, uri }); + } + entry.mtime = Date.now(); + entry.size = content.byteLength; + entry.data = content; + + this._fireSoon({ type: FileChangeType.Changed, uri }); + } + + // --- manage files/folders + + rename(oldUri: Uri, newUri: Uri, options: { overwrite: boolean }): void { + if (!options.overwrite && this._lookup(newUri, true)) { + throw 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: FileChangeType.Deleted, uri: oldUri }, + { type: FileChangeType.Created, uri: newUri } + ); + } + + delete(uri: 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 FileSystemError.FileNotFound(uri); + } + parent.entries.delete(basename); + parent.mtime = Date.now(); + parent.size -= 1; + this._fireSoon({ type: FileChangeType.Changed, uri: dirname }, { uri, type: FileChangeType.Deleted }); + } + + createDirectory(uri: 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: FileChangeType.Changed, uri: dirname }, { type: FileChangeType.Created, uri }); + } + + // --- lookup + + private _lookup(uri: Uri, silent: false): Entry; + private _lookup(uri: Uri, silent: boolean): Entry | undefined; + private _lookup(uri: 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 FileSystemError.FileNotFound(uri); + } else { + return undefined; + } + } + entry = child; + } + return entry; + } + + private _lookupAsDirectory(uri: Uri, silent: boolean): Directory { + let entry = this._lookup(uri, silent); + if (entry instanceof Directory) { + return entry; + } + throw FileSystemError.FileNotADirectory(uri); + } + + private _lookupAsFile(uri: Uri, silent: boolean): File { + let entry = this._lookup(uri, silent); + if (entry instanceof File) { + return entry; + } + throw FileSystemError.FileIsADirectory(uri); + } + + private _lookupParentDirectory(uri: Uri): Directory { + const dirname = uri.with({ path: this._dirname(uri.path) }); + return this._lookupAsDirectory(dirname, false); + } + + // --- manage file events + + private _emitter = new EventEmitter(); + private _bufferedEvents: FileChangeEvent[] = []; + private _fireSoonHandle?: any; + + readonly onDidChangeFile: Event = this._emitter.event; + + watch(_resource: Uri): Disposable { + // ignore, fires for all changes... + return new Disposable(() => { }); + } + + private _fireSoon(...events: 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: FileSearchQuery, _options: FileSearchOptions, _token: CancellationToken): ProviderResult { + return this._findFiles(query.pattern); + } + + private _findFiles(query: string | undefined): Uri[] { + const files = this._getFiles(); + const result: Uri[] = []; + + const pattern = query ? new RegExp(this._convertSimple2RegExpPattern(query)) : null; + + for (const file of files) { + if (!pattern || pattern.exec(file.name)) { + result.push(file.uri); + } + } + + return result; + } + + private _textDecoder = new TextDecoder(); + + provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress, _token: CancellationToken) { + const result: TextSearchComplete = { limitHit: false }; + + const files = this._findFiles(options.includes[0]); + if (files) { + for (const file of files) { + const content = this._textDecoder.decode(this.readFile(file)); + + const lines = content.split('\n'); + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const index = line.indexOf(query.pattern); + if (index !== -1) { + progress.report({ + uri: file, + ranges: new Range(new Position(i, index), new Position(i, index + query.pattern.length)), + preview: { + text: line, + matches: new Range(new Position(0, index), new Position(0, index + query.pattern.length)) + } + }); + } + } + } + } + + return result; + } +} + +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')); +} diff --git a/extensions/vscode-web-playground/tsconfig.json b/extensions/vscode-web-playground/tsconfig.json index 296ddb38fcb..633da7fad77 100644 --- a/extensions/vscode-web-playground/tsconfig.json +++ b/extensions/vscode-web-playground/tsconfig.json @@ -1,9 +1,14 @@ { "extends": "../shared.tsconfig.json", "compilerOptions": { - "outDir": "./out" + "outDir": "./out", + "lib": [ + "dom", + "dom.iterable", + "es2018" + ] }, "include": [ "src/**/*" ] -} \ No newline at end of file +} diff --git a/extensions/vscode-web-playground/yarn.lock b/extensions/vscode-web-playground/yarn.lock index e595aafe8cd..b29fc8fc61d 100644 --- a/extensions/vscode-web-playground/yarn.lock +++ b/extensions/vscode-web-playground/yarn.lock @@ -7,11 +7,6 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.43.tgz#03c54589c43ad048cbcbfd63999b55d0424eec27" integrity sha512-xNlAmH+lRJdUMXClMTI9Y0pRqIojdxfm7DHsIxoB2iTzu3fnPmSMEN8SsSx0cdwV36d02PWCWaDUoZPDSln+xw== -"@types/node@^12.11.7": - version "12.11.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.11.7.tgz#57682a9771a3f7b09c2497f28129a0462966524a" - integrity sha512-JNbGaHFCLwgHn/iCckiGSOZ1XYHsKFwREtzPwSGCVld1SGhOlmZw2D4ZI94HQCrBHbADzW9m4LER/8olJTRGHA== - ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" @@ -35,11 +30,11 @@ debug@^2.2.0: ms "2.0.0" debug@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== dependencies: - ms "2.0.0" + ms "^2.1.1" is-buffer@~1.1.1: version "1.1.6" @@ -47,9 +42,9 @@ is-buffer@~1.1.1: integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== 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.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== md5@^2.1.0: version "2.2.1" @@ -60,22 +55,22 @@ md5@^2.1.0: crypt "~0.0.1" is-buffer "~1.1.1" -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.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + 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 "0.0.8" + minimist "^1.2.5" mocha-junit-reporter@^1.17.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.17.0.tgz#2e5149ed40fc5d2e3ca71e42db5ab1fec9c6d85c" - integrity sha1-LlFJ7UD8XS48px5C21qx/snG2Fw= + 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" @@ -96,6 +91,11 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" diff --git a/extensions/xml/package.json b/extensions/xml/package.json index 77e36951b7c..3e5042df702 100644 --- a/extensions/xml/package.json +++ b/extensions/xml/package.json @@ -23,8 +23,8 @@ ".dita", ".ditamap", ".dtd", - ".ent", - ".mod", + ".ent", + ".mod", ".dtml", ".fsproj", ".fxml", diff --git a/extensions/xml/xsl.language-configuration.json b/extensions/xml/xsl.language-configuration.json index 7605fc4a7d1..cf787c79edd 100644 --- a/extensions/xml/xsl.language-configuration.json +++ b/extensions/xml/xsl.language-configuration.json @@ -4,7 +4,11 @@ "blockComment": [""] }, "brackets": [ - ["<", ">"] + [""], + ["<", ">"], + ["{", "}"], + ["(", ")"], + ["[", "]"] ] // enhancedBrackets: [{ diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 4e0ca82acd1..102d128edb6 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@^3.9.2-insiders.20200509: - version "3.9.2-insiders.20200509" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.2-insiders.20200509.tgz#8c90ed86a91f9692f10f5ac9c1fd6cb241419e6c" - integrity sha512-AAbhs55BZMbyHGfJd0pNfO3+B6jjPpa38zgaIb9MRExkRGLkIUpbUetoh+HgmM5LAtg128sHGiwhLc49pOcgFw== +typescript@3.9.7: + version "3.9.7" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" + integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== diff --git a/package.json b/package.json index 61e48f8384f..2274f27083f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", - "version": "1.46.0", - "distro": "6ec59b3610ac45e47ab420d15a0588817178a043", + "version": "1.48.0", + "distro": "aab483217aa0c9ab8fa10a1ccdbe6ae904b4d501", "author": { "name": "Microsoft Corporation" }, @@ -14,11 +14,19 @@ "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", + "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", @@ -32,9 +40,10 @@ "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", - "eslint": "eslint -c .eslintrc.json --rulesdir ./build/lib/eslint --ext .ts --ext .js ./src/vs ./extensions", - "generate-github-config": "node extensions/github-authentication/build/generateconfig.js" + "web": "node resources/serverless/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", @@ -42,29 +51,29 @@ "graceful-fs": "4.2.3", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.3", - "iconv-lite": "0.5.0", - "jschardet": "2.1.1", + "iconv-lite-umd": "0.6.8", + "jschardet": "2.2.1", "keytar": "^5.5.0", "minimist": "^1.2.5", "native-is-elevated": "0.4.1", "native-keymap": "2.1.2", "native-watchdog": "1.3.0", "node-pty": "0.10.0-beta8", - "semver-umd": "^5.5.6", + "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-nsfw": "1.2.8", "vscode-oniguruma": "1.3.1", "vscode-proxy-agent": "^0.5.2", - "vscode-ripgrep": "^1.5.8", + "vscode-ripgrep": "^1.8.0", "vscode-sqlite3": "4.0.10", - "vscode-textmate": "5.1.1", - "xterm": "4.6.0-beta.44", - "xterm-addon-search": "0.7.0-beta.2", - "xterm-addon-unicode11": "0.2.0-beta.5", - "xterm-addon-web-links": "0.4.0-beta.6", - "xterm-addon-webgl": "0.7.0-beta.10", + "vscode-textmate": "5.2.0", + "xterm": "4.9.0-beta.8", + "xterm-addon-search": "0.7.0", + "xterm-addon-unicode11": "0.2.0", + "xterm-addon-webgl": "0.8.0", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, @@ -76,7 +85,6 @@ "@types/debug": "^4.1.5", "@types/graceful-fs": "4.1.2", "@types/http-proxy-agent": "^2.0.1", - "@types/iconv-lite": "0.0.1", "@types/keytar": "^4.4.0", "@types/minimist": "^1.2.0", "@types/mocha": "2.2.39", @@ -90,18 +98,18 @@ "@types/winreg": "^1.2.30", "@types/yauzl": "^2.9.1", "@types/yazl": "^2.4.2", - "@typescript-eslint/eslint-plugin": "2.3.2", - "@typescript-eslint/parser": "^2.12.0", + "@typescript-eslint/eslint-plugin": "3.2.0", + "@typescript-eslint/parser": "^3.3.0", "ansi-colors": "^3.2.3", "asar": "^0.14.0", "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": "7.2.4", + "electron": "7.3.2", "eslint": "6.8.0", "eslint-plugin-jsdoc": "^19.1.0", "event-stream": "3.3.4", @@ -129,7 +137,7 @@ "gulp-untar": "^0.0.7", "gulp-vinyl-zip": "^2.1.2", "husky": "^0.13.1", - "innosetup": "5.6.1", + "innosetup": "6.0.5", "is": "^3.1.0", "istanbul-lib-coverage": "^3.0.0", "istanbul-lib-instrument": "^4.0.0", @@ -156,17 +164,17 @@ "source-map": "^0.4.4", "style-loader": "^1.0.0", "ts-loader": "^4.4.2", - "typescript": "^3.9.1-rc", + "typescript": "^4.0.0-dev.20200729", "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.40.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": { diff --git a/product.json b/product.json index 91b3b926780..fa15be73949 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,13 +23,15 @@ "reportIssueUrl": "https://github.com/Microsoft/vscode/issues/new", "urlProtocol": "code-oss", "extensionAllowedProposedApi": [ + "ms-vscode.vscode-js-profile-flame", "ms-vscode.vscode-js-profile-table", - "ms-vscode.references-view" + "ms-vscode.references-view", + "ms-vscode.github-browser" ], "builtInExtensions": [ { "name": "ms-vscode.node-debug", - "version": "1.44.5", + "version": "1.44.7", "repo": "https://github.com/Microsoft/vscode-node-debug", "metadata": { "id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6", @@ -42,7 +46,7 @@ }, { "name": "ms-vscode.node-debug2", - "version": "1.42.2", + "version": "1.42.5", "repo": "https://github.com/Microsoft/vscode-node-debug2", "metadata": { "id": "36d19e17-7569-4841-a001-947eb18602b2", @@ -57,7 +61,7 @@ }, { "name": "ms-vscode.references-view", - "version": "0.0.51", + "version": "0.0.61", "repo": "https://github.com/Microsoft/vscode-reference-view", "metadata": { "id": "dc489f46-520d-4556-ae85-1f9eab3c412d", @@ -72,7 +76,7 @@ }, { "name": "ms-vscode.js-debug-companion", - "version": "1.0.0", + "version": "1.0.2", "repo": "https://github.com/microsoft/vscode-js-debug-companion", "metadata": { "id": "99cb0b7f-7354-4278-b8da-6cc79972169d", @@ -86,11 +90,11 @@ } }, { - "name": "ms-vscode.js-debug-nightly", - "version": "2020.5.417", + "name": "ms-vscode.js-debug", + "version": "1.48.1", "repo": "https://github.com/Microsoft/vscode-js-debug", "metadata": { - "id": "7acbb4ce-c85a-49d4-8d95-a8054406ae97", + "id": "25629058-ddac-4e17-abba-74678e126c5d", "publisherId": { "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee", "publisherName": "ms-vscode", @@ -102,7 +106,7 @@ }, { "name": "ms-vscode.vscode-js-profile-table", - "version": "0.0.2", + "version": "0.0.6", "repo": "https://github.com/Microsoft/vscode-js-debug", "metadata": { "id": "7e52b41b-71ad-457b-ab7e-0620f1fc4feb", @@ -115,5 +119,22 @@ "publisherDisplayName": "Microsoft" } } + ], + "webBuiltInExtensions": [ + { + "name": "ms-vscode.github-browser", + "version": "0.0.1", + "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/package.json b/remote/package.json index b8927215f16..c10fbe792c2 100644 --- a/remote/package.json +++ b/remote/package.json @@ -8,23 +8,22 @@ "graceful-fs": "4.2.3", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.3", - "iconv-lite": "0.5.0", - "jschardet": "2.1.1", + "iconv-lite-umd": "0.6.8", + "jschardet": "2.2.1", "minimist": "^1.2.5", "native-watchdog": "1.3.0", "node-pty": "0.10.0-beta8", - "semver-umd": "^5.5.6", + "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.5.8", - "vscode-textmate": "5.1.1", - "xterm": "4.6.0-beta.44", - "xterm-addon-search": "0.7.0-beta.2", - "xterm-addon-unicode11": "0.2.0-beta.5", - "xterm-addon-web-links": "0.4.0-beta.6", - "xterm-addon-webgl": "0.7.0-beta.10", + "vscode-ripgrep": "^1.8.0", + "vscode-textmate": "5.2.0", + "xterm": "4.9.0-beta.8", + "xterm-addon-search": "0.7.0", + "xterm-addon-unicode11": "0.2.0", + "xterm-addon-webgl": "0.8.0", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/web/package.json b/remote/web/package.json index 98baff7fd26..de712cfdeb3 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -2,13 +2,14 @@ "name": "vscode-web", "version": "0.0.0", "dependencies": { - "semver-umd": "^5.5.6", + "iconv-lite-umd": "0.6.8", + "jschardet": "2.2.1", + "semver-umd": "^5.5.7", "vscode-oniguruma": "1.3.1", - "vscode-textmate": "5.1.1", - "xterm": "4.6.0-beta.44", - "xterm-addon-search": "0.7.0-beta.2", - "xterm-addon-unicode11": "0.2.0-beta.5", - "xterm-addon-web-links": "0.4.0-beta.6", - "xterm-addon-webgl": "0.7.0-beta.10" + "vscode-textmate": "5.2.0", + "xterm": "4.9.0-beta.8", + "xterm-addon-search": "0.7.0", + "xterm-addon-unicode11": "0.2.0", + "xterm-addon-webgl": "0.8.0" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 4b7efc15550..15fb0958eea 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -2,42 +2,47 @@ # yarn lockfile v1 -semver-umd@^5.5.6: - version "5.5.6" - resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.6.tgz#1d185bbd2caec825c564b54907cd09e14083f228" - integrity sha512-6ARYXVi4Y4VO5HfyCjT/6xyykBtJwEXSGQ8ON4UPQSFOjZUDsbAE0J614QcBBsLTTyQMEqvsXN804vAqpydjzw== +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.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.1.1.tgz#d88dbf271bee7cede455a21bd4894ba5724a4a7e" - integrity sha512-5VHjF+Fglf9d2JI5OyQ7FHutK6/29G0qYyD920K0SWO7uY8JTWbqyKAHEtfB/ZDk2fOe/E23n3wz9fHXKi63yg== +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-beta.2: - version "0.7.0-beta.2" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.7.0-beta.2.tgz#384bda136c707f97a77eefc76cc7d9e572ce0719" - integrity sha512-A9fyiBBvG6ZNIwSJ03+sRCv9y20/uzd1wjCoaYUqp9fu3YGiHaGwyo9rAfm2M/fQM5vBmyJk4Qw/lwVq7TtlAw== +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-beta.5: - version "0.2.0-beta.5" - resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.2.0-beta.5.tgz#5961850162df20b5e966166423cd6957ac2db298" - integrity sha512-IjnbBcyfS5JgJDXPO0W2nk/VBtGwx6GWE2snMC676z4DmAABUqPXfTzJKfUoWqoT6UcbxB0oIjDzykCfoRJp6Q== +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-web-links@0.4.0-beta.6: - version "0.4.0-beta.6" - resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.4.0-beta.6.tgz#d159d4542eb9a02d57977fe7eb5f42f8ef2f27fa" - integrity sha512-dsQVD/EyVq8PtAYGh2PGQTCt009UipIfX6Q2SBDlz+W9x7IkXjhRxRaryMmLsBCca20qeVKwmbQ+ANhLi+nTaQ== +xterm-addon-webgl@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.8.0.tgz#4bc6bb4dbfea5b0d2d7978d6c5cef922d584fb4f" + integrity sha512-dlpYPsv0C9S6v6+T/h/d/otSbdUTizMJdxvSoS34tUpMOHev6iW7Zqt5KRFqYxl4vCqpDk9Wmhb3fKL3kwX5fQ== -xterm-addon-webgl@0.7.0-beta.10: - version "0.7.0-beta.10" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.7.0-beta.10.tgz#39fdb96351e97a1bf15f4c4c8944ba3d05cacee4" - integrity sha512-nQl/ASk+ck11aSrBZXb2a0tu+SNDnm89owBk/sAZeZzi5MHNo6bB8y2VTKNNC6D3i3aFouTz4VorYB25LUgNFg== - -xterm@4.6.0-beta.44: - version "4.6.0-beta.44" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.6.0-beta.44.tgz#76b2a6b8e147595ab44aa752c0e721d935464615" - integrity sha512-vYtfz4spFcSKLEUpC6anH7TwDams71+k2wAtUzCJ47dNL2IrwYafcFsvGPm46QLTtq4M2Bp9rQo3R3V746yxNg== +xterm@4.9.0-beta.8: + version "4.9.0-beta.8" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.9.0-beta.8.tgz#ca121934d63f88668d2d5b11d9b2fc3bde7bd805" + integrity sha512-EEonYBLANDUBfEeEnHG632bZdgBaAUWst8LFr6oC6f2uLFfJGHQvVJuLaEkPtRvS+jOeoorEXZRPmso1/ANHXA== diff --git a/remote/yarn.lock b/remote/yarn.lock index 20fbc3637a4..499dc73ca5c 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" @@ -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" @@ -176,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" @@ -212,10 +230,10 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -jschardet@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.1.1.tgz#af6f8fd0b3b0f5d46a8fd9614a4fce490575c184" - integrity sha512-pA5qG9Zwm8CBpGlK/lo2GE9jPxwqRgMV7Lzc/1iaPccw6v4Rhj8Zg2BTyrdmHmxlJojnbLupLeRnaPLsq03x6Q== +jschardet@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.2.1.tgz#03b0264669a90c7a5c436a68c5a7d4e4cb0c9823" + integrity sha512-Ks2JNuUJoc7PGaZ7bVFtSEvOcr0rBq6Q1J5/7+zKWLT+g+4zziL63O0jg7y2jxhzIa1LVsHUbPXrbaWmz9iwDw== jsonfile@^4.0.0: version "4.0.0" @@ -256,6 +274,11 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + nan@^2.10.0, nan@^2.14.0: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" @@ -293,6 +316,11 @@ picomatch@^2.0.4: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6" integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA== +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + readdirp@~3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" @@ -300,15 +328,10 @@ readdirp@~3.2.0: dependencies: picomatch "^2.0.4" -"safer-buffer@>= 2.1.2 < 3": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -semver-umd@^5.5.6: - version "5.5.6" - resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.6.tgz#1d185bbd2caec825c564b54907cd09e14083f228" - integrity sha512-6ARYXVi4Y4VO5HfyCjT/6xyykBtJwEXSGQ8ON4UPQSFOjZUDsbAE0J614QcBBsLTTyQMEqvsXN804vAqpydjzw== +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" @@ -382,15 +405,18 @@ vscode-proxy-agent@^0.5.2: https-proxy-agent "^2.2.3" socks-proxy-agent "^4.0.1" -vscode-ripgrep@^1.5.8: - version "1.5.8" - resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.8.tgz#32cb33da6d1a9ca8f5de8c2813ed5114fd55fc11" - integrity sha512-l6Pv/t1Jk63RU+kEkMO04XxnNRYdyzuesizj9AzFpcfrUxxpAjEJBK1qO9Mov30UUGZl7uDUBn+uCv9koaHPPA== +vscode-ripgrep@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.8.0.tgz#dfe7c2ae2a2032df8a8108765c2feef73474888a" + integrity sha512-/Q5XtePkTLLi8yplr5ai24pVEymRF62xH9xXrtj35GTaDCJg3zq1s1/L1UqhVbfNDv4OcMBYjyIAt/quEi3d5w== + dependencies: + https-proxy-agent "^4.0.0" + proxy-from-env "^1.1.0" -vscode-textmate@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.1.1.tgz#d88dbf271bee7cede455a21bd4894ba5724a4a7e" - integrity sha512-5VHjF+Fglf9d2JI5OyQ7FHutK6/29G0qYyD920K0SWO7uY8JTWbqyKAHEtfB/ZDk2fOe/E23n3wz9fHXKi63yg== +vscode-textmate@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.2.0.tgz#01f01760a391e8222fe4f33fbccbd1ad71aed74e" + integrity sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ== vscode-windows-ca-certs@0.2.0: version "0.2.0" @@ -404,30 +430,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.7.0-beta.2: - version "0.7.0-beta.2" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.7.0-beta.2.tgz#384bda136c707f97a77eefc76cc7d9e572ce0719" - integrity sha512-A9fyiBBvG6ZNIwSJ03+sRCv9y20/uzd1wjCoaYUqp9fu3YGiHaGwyo9rAfm2M/fQM5vBmyJk4Qw/lwVq7TtlAw== +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-beta.5: - version "0.2.0-beta.5" - resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.2.0-beta.5.tgz#5961850162df20b5e966166423cd6957ac2db298" - integrity sha512-IjnbBcyfS5JgJDXPO0W2nk/VBtGwx6GWE2snMC676z4DmAABUqPXfTzJKfUoWqoT6UcbxB0oIjDzykCfoRJp6Q== +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-web-links@0.4.0-beta.6: - version "0.4.0-beta.6" - resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.4.0-beta.6.tgz#d159d4542eb9a02d57977fe7eb5f42f8ef2f27fa" - integrity sha512-dsQVD/EyVq8PtAYGh2PGQTCt009UipIfX6Q2SBDlz+W9x7IkXjhRxRaryMmLsBCca20qeVKwmbQ+ANhLi+nTaQ== +xterm-addon-webgl@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.8.0.tgz#4bc6bb4dbfea5b0d2d7978d6c5cef922d584fb4f" + integrity sha512-dlpYPsv0C9S6v6+T/h/d/otSbdUTizMJdxvSoS34tUpMOHev6iW7Zqt5KRFqYxl4vCqpDk9Wmhb3fKL3kwX5fQ== -xterm-addon-webgl@0.7.0-beta.10: - version "0.7.0-beta.10" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.7.0-beta.10.tgz#39fdb96351e97a1bf15f4c4c8944ba3d05cacee4" - integrity sha512-nQl/ASk+ck11aSrBZXb2a0tu+SNDnm89owBk/sAZeZzi5MHNo6bB8y2VTKNNC6D3i3aFouTz4VorYB25LUgNFg== - -xterm@4.6.0-beta.44: - version "4.6.0-beta.44" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.6.0-beta.44.tgz#76b2a6b8e147595ab44aa752c0e721d935464615" - integrity sha512-vYtfz4spFcSKLEUpC6anH7TwDams71+k2wAtUzCJ47dNL2IrwYafcFsvGPm46QLTtq4M2Bp9rQo3R3V746yxNg== +xterm@4.9.0-beta.8: + version "4.9.0-beta.8" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.9.0-beta.8.tgz#ca121934d63f88668d2d5b11d9b2fc3bde7bd805" + integrity sha512-EEonYBLANDUBfEeEnHG632bZdgBaAUWst8LFr6oC6f2uLFfJGHQvVJuLaEkPtRvS+jOeoorEXZRPmso1/ANHXA== yauzl@^2.9.2: version "2.10.0" diff --git a/resources/linux/bin/code.sh b/resources/linux/bin/code.sh index 8bb25311496..06973937f14 100755 --- a/resources/linux/bin/code.sh +++ b/resources/linux/bin/code.sh @@ -1,39 +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 no longer see this prompt, start @@PRODNAME@@ with the environment variable DONT_PROMPT_WSL_INSTALL defined." + 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/debian/control.template b/resources/linux/debian/control.template index 27d36308c88..903640cd503 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/snap/snapcraft.yaml b/resources/linux/snap/snapcraft.yaml index c39e5f4f849..7dbc1680ba2 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/serverless/callback.html b/resources/serverless/callback.html new file mode 100644 index 00000000000..81f217e980b --- /dev/null +++ b/resources/serverless/callback.html @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + Visual Studio Code + + + + + + + + Visual Studio Code + +
+
+ You can close this page now. +
+
+ + + diff --git a/resources/serverless/code-web.js b/resources/serverless/code-web.js new file mode 100644 index 00000000000..5448e20e60d --- /dev/null +++ b/resources/serverless/code-web.js @@ -0,0 +1,518 @@ +#!/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 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_MAIN = path.join(APP_ROOT, 'src', 'vs', 'code', 'browser', 'workbench', 'workbench-dev.html'); + +const args = minimist(process.argv, { + boolean: [ + 'no-launch', + 'help', + 'verbose' + ], + string: [ + 'scheme', + 'host', + 'port', + 'local_port', + 'extension' + ], +}); + +if (args.help) { + console.log( + 'yarn web [options]\n' + + ' --no-launch Do not open VSCode web in the browser\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' + + ' --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] = await Promise.all([ + extensions.scanBuiltinExtensions(BUILTIN_EXTENSIONS_ROOT), + extensions.scanBuiltinExtensions(BUILTIN_MARKETPLACE_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); + } + return { extensions: allExtensions, locations }; +} + +async function getDefaultExtensionInfos() { + 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 + } + + if (packageJSON.browser) { + let mainFilePath = path.join(extensionPath, packageJSON.browser); + if (path.extname(mainFilePath) !== '.js') { + mainFilePath += '.js'; + } + if (!await exists(mainFilePath)) { + fancyLog(`${ansiColors.yellow('Warning')}: Could not find ${mainFilePath}. Use ${ansiColors.cyan('yarn gulp watch-web')} to build the built-in extensions.`); + } + } + + 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 defaultExtensionsPromise = getDefaultExtensionInfos(); + +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 (/^\/builtin-extension\//.test(pathname)) { + // built-in extension requests + return handleBuiltInExtension(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 + */ +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 + */ +async function handleExtension(req, res, parsedUrl) { + // Strip `/extension/` from the path + const relativePath = decodeURIComponent(parsedUrl.pathname.substr('/extension/'.length)); + const filePath = getExtensionFilePath(relativePath, (await defaultExtensionsPromise).locations); + if (!filePath) { + return serveError(req, res, 400, `Bad request.`); + } + return serveFile(req, res, filePath); +} + +/** + * @param {import('http').IncomingMessage} req + * @param {import('http').ServerResponse} res + * @param {import('url').UrlWithParsedQuery} parsedUrl + */ +async function handleBuiltInExtension(req, res, parsedUrl) { + // Strip `/builtin-extension/` from the path + const relativePath = decodeURIComponent(parsedUrl.pathname.substr('/builtin-extension/'.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': '*' + }); +} + +/** + * @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 } = await defaultExtensionsPromise; + + if (args.verbose) { + fancyLog(`${ansiColors.magenta('BuiltIn extensions')}: ${builtInExtensions.map(e => path.basename(e.extensionPath)).join(', ')}`); + fancyLog(`${ansiColors.magenta('Additional extensions')}: ${staticExtensions.map(e => path.basename(e.extensionLocation.path)).join(', ') || 'None'}`); + } + + const webConfigJSON = escapeAttribute(JSON.stringify({ + folderUri: folderUri, + staticExtensions, + builtinExtensionsServiceUrl: `${SCHEME}://${AUTHORITY}/builtin-extension` + })); + + const data = (await readFile(WEB_MAIN)).toString() + .replace('{{WORKBENCH_WEB_CONFIGURATION}}', () => webConfigJSON) // use a replace function to avoid that regexp replace patterns ($&, $0, ...) are applied + .replace('{{WORKBENCH_BUILTIN_EXTENSIONS}}', () => escapeAttribute(JSON.stringify(builtInExtensions))) + .replace('{{WEBVIEW_ENDPOINT}}', '') + .replace('{{REMOTE_USER_DATA_URI}}', ''); + + res.writeHead(200, { 'Content-Type': 'text/html' }); + 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', 'serverless', '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 66e11adc2f0..9f029e5522a 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 1e7f37712ca..2e4e34ff32e 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 -REM set ELECTRON_DEFAULT_ERROR_MODE=1 TODO@ben to investigate if this helps with builds reporting stacks if renderer crashes set ELECTRON_ENABLE_LOGGING=1 set ELECTRON_ENABLE_STACK_DUMPING=1 diff --git a/scripts/code-cli.sh b/scripts/code-cli.sh index e4fa552e642..a792c08532e 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 b7441ea0158..00000000000 --- a/scripts/code-web.js +++ /dev/null @@ -1,275 +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('minimist'); - -const APP_ROOT = path.dirname(__dirname); -const EXTENSIONS_ROOT = path.join(APP_ROOT, 'extensions'); -const WEB_MAIN = path.join(APP_ROOT, 'src', 'vs', 'code', 'browser', 'workbench', 'workbench-dev.html'); - -const args = minimist(process.argv, { - boolean: [ - 'no-launch', - 'help' - ], - string: [ - 'scheme', - 'host', - 'port', - 'local_port' - ], -}); - -if (args.help) { - console.log( - 'yarn web [options]\n' + - ' --no-launch Do not open VSCode web in the browser\n' + - ' --scheme Protocol (https or http)\n' + - ' --host Remote host\n' + - ' --port Remote/Local port\n' + - ' --local_port Local port override\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 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 (/^\/static-extension\//.test(pathname)) { - // static extension requests - return handleStaticExtension(req, res, parsedUrl); - } - if (pathname === '/') { - // main web - return handleRoot(req, res); - } - - return serveError(req, res, 404, 'Not found.'); - } catch (error) { - console.error(error.toString()); - - return serveError(req, res, 500, 'Internal Server Error.'); - } -}); - -server.listen(LOCAL_PORT, () => { - if (LOCAL_PORT !== PORT) { - console.log(`Operating location at http://0.0.0.0:${LOCAL_PORT}`); - } - console.log(`Web UI available at ${SCHEME}://${AUTHORITY}`); -}); - -server.on('error', err => { - console.error(`Error occurred in server:`); - console.error(err); -}); - -/** - * @param {import('http').IncomingMessage} req - * @param {import('http').ServerResponse} res - * @param {import('url').UrlWithParsedQuery} parsedUrl - */ -function handleStatic(req, res, parsedUrl) { - - // Strip `/static/` from the path - const relativeFilePath = path.normalize(decodeURIComponent(parsedUrl.pathname.substr('/static/'.length))); - - return serveFile(req, res, path.join(APP_ROOT, relativeFilePath)); -} - -/** - * @param {import('http').IncomingMessage} req - * @param {import('http').ServerResponse} res - * @param {import('url').UrlWithParsedQuery} parsedUrl - */ -function handleStaticExtension(req, res, parsedUrl) { - - // Strip `/static-extension/` from the path - const relativeFilePath = path.normalize(decodeURIComponent(parsedUrl.pathname.substr('/static-extension/'.length))); - - const filePath = path.join(EXTENSIONS_ROOT, relativeFilePath); - - return serveFile(req, res, filePath); -} - -/** - * @param {import('http').IncomingMessage} req - * @param {import('http').ServerResponse} res - */ -async function handleRoot(req, res) { - const extensionFolders = await util.promisify(fs.readdir)(EXTENSIONS_ROOT); - const mapExtensionFolderToExtensionPackageJSON = new Map(); - - await Promise.all(extensionFolders.map(async extensionFolder => { - try { - const packageJSON = JSON.parse((await util.promisify(fs.readFile)(path.join(EXTENSIONS_ROOT, extensionFolder, 'package.json'))).toString()); - if (packageJSON.main && packageJSON.name !== 'vscode-web-playground') { - return; // unsupported - } - - if (packageJSON.name === 'scss') { - return; // seems to fail to JSON.parse()?! - } - - packageJSON.extensionKind = ['web']; // enable for Web - - mapExtensionFolderToExtensionPackageJSON.set(extensionFolder, packageJSON); - } catch (error) { - return null; - } - })); - - const staticExtensions = []; - - // Built in extensions - mapExtensionFolderToExtensionPackageJSON.forEach((packageJSON, extensionFolder) => { - staticExtensions.push({ - packageJSON, - extensionLocation: { scheme: SCHEME, authority: AUTHORITY, path: `/static-extension/${extensionFolder}` } - }); - }); - - const data = (await util.promisify(fs.readFile)(WEB_MAIN)).toString() - .replace('{{WORKBENCH_WEB_CONFIGURATION}}', escapeAttribute(JSON.stringify({ - staticExtensions, - folderUri: { scheme: 'memfs', path: `/sample-folder` } - }))) - .replace('{{WEBVIEW_ENDPOINT}}', '') - .replace('{{REMOTE_USER_DATA_URI}}', ''); - - res.writeHead(200, { 'Content-Type': 'text/html' }); - return res.end(data); -} - -/** - * @param {string} value - */ -function escapeAttribute(value) { - return value.replace(/"/g, '"'); -} - -/** - * @param {import('http').IncomingMessage} req - * @param {import('http').ServerResponse} res - * @param {string} errorMessage - */ -function serveError(req, res, errorCode, errorMessage) { - res.writeHead(errorCode, { 'Content-Type': 'text/plain' }); - res.end(errorMessage); -} - -const textMimeType = { - '.html': 'text/html', - '.js': 'text/javascript', - '.json': 'application/json', - '.css': 'text/css', - '.svg': 'image/svg+xml', -}; - -const mapExtToMediaMimes = { - '.bmp': 'image/bmp', - '.gif': 'image/gif', - '.ico': 'image/x-icon', - '.jpe': 'image/jpg', - '.jpeg': 'image/jpg', - '.jpg': 'image/jpg', - '.png': 'image/png', - '.tga': 'image/x-tga', - '.tif': 'image/tiff', - '.tiff': 'image/tiff', - '.woff': 'application/font-woff' -}; - -/** - * @param {string} forPath - */ -function getMediaMime(forPath) { - const ext = path.extname(forPath); - - return mapExtToMediaMimes[ext.toLowerCase()]; -} - -/** - * @param {import('http').IncomingMessage} req - * @param {import('http').ServerResponse} res - * @param {string} filePath - */ -async function serveFile(req, res, filePath, responseHeaders = Object.create(null)) { - try { - - // Sanity checks - filePath = path.normalize(filePath); // ensure no "." and ".." - if (filePath.indexOf(`${APP_ROOT}${path.sep}`) !== 0) { - // invalid location outside of APP_ROOT - return serveError(req, res, 400, `Bad request.`); - } - - const stat = await util.promisify(fs.stat)(filePath); - - // Check if file modified since - const etag = `W/"${[stat.ino, stat.size, stat.mtime.getTime()].join('-')}"`; // weak validator (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) - if (req.headers['if-none-match'] === etag) { - res.writeHead(304); - return res.end(); - } - - // Headers - responseHeaders['Content-Type'] = textMimeType[path.extname(filePath)] || getMediaMime(filePath) || 'text/plain'; - responseHeaders['Etag'] = etag; - - res.writeHead(200, responseHeaders); - - // Data - fs.createReadStream(filePath).pipe(res); - } catch (error) { - console.error(error.toString()); - res.writeHead(404, { 'Content-Type': 'text/plain' }); - return res.end('Not found'); - } -} - -if (args.launch !== false) { - opn(`${SCHEME}://${HOST}:${PORT}`); -} diff --git a/scripts/code.bat b/scripts/code.bat index d23724ef143..7ef1fd33fe0 100644 --- a/scripts/code.bat +++ b/scripts/code.bat @@ -5,31 +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%" -:: Get electron -call yarn electron - :: Manage built-in extensions if "%1"=="--builtin" goto builtin -:: Sync built-in extensions -node build\lib\builtInExtensions.js - -:: Build -if not exist out yarn compile - :: Configuration set NODE_ENV=development set VSCODE_DEV=1 set VSCODE_CLI=1 -REM set ELECTRON_DEFAULT_ERROR_MODE=1 TODO@ben to investigate if this helps with builds reporting stacks if renderer crashes 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 d0f79b53a97..b19cc0df9ff 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 diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat index e5e6c7d8892..3133c7869a5 100644 --- a/scripts/test-integration.bat +++ b/scripts/test-integration.bat @@ -22,6 +22,9 @@ if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" ( 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^ @@ -37,30 +40,36 @@ if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" ( ) :: 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 --crash-reporter-directory=%VSCODECRASHDIR% --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 --crash-reporter-directory=%VSCODECRASHDIR% --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% --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% if %errorlevel% neq 0 exit /b %errorlevel% -call "%INTEGRATION_TEST_ELECTRON_PATH%" $%~dp0\..\extensions\markdown-language-features\out\test\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\markdown-language-features --extensionTestsPath=%~dp0\..\extensions\markdown-language-features\out\test --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --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\out\test\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --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% + +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-notebook-tests\test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-notebook-tests --extensionTestsPath=%~dp0\..\extensions\vscode-notebook-tests\out --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% if %errorlevel% neq 0 exit /b %errorlevel% for /f "delims=" %%i in ('node -p "require('fs').realpathSync.native(require('os').tmpdir())"') do set TEMPDIR=%%i set GITWORKSPACE=%TEMPDIR%\git-%RANDOM% mkdir %GITWORKSPACE% -call "%INTEGRATION_TEST_ELECTRON_PATH%" %GITWORKSPACE% --extensionDevelopmentPath=%~dp0\..\extensions\git --extensionTestsPath=%~dp0\..\extensions\git\out\test --enable-proposed-api=vscode.git --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +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 (HTML, CSS, JSON language server tests...) diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index 0342b23c838..5412a5c0ecd 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -27,8 +27,10 @@ else # 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 \ @@ -48,16 +50,17 @@ fi ./scripts/test.sh --runGlob **/*.integrationTest.js "$@" # Tests in the extension host -"$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 --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --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 --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 --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/markdown-language-features/out/test/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --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 --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 --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/markdown-language-features/test-workspace --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +#"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/typescript-language-features/test-workspace --extensionDevelopmentPath=$ROOT/extensions/typescript-language-features --extensionTestsPath=$ROOT/extensions/typescript-language-features/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/emmet/out/test/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $(mktemp -d 2>/dev/null) --enable-proposed-api=vscode.git --extensionDevelopmentPath=$ROOT/extensions/git --extensionTestsPath=$ROOT/extensions/git/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-notebook-tests/test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-notebook-tests --extensionTestsPath=$ROOT/extensions/vscode-notebook-tests/out/ --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR # Tests in commonJS 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/src/bootstrap-amd.js b/src/bootstrap-amd.js index a8144075dd5..b0b266557a6 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 c4c69869d98..97a4d9934cd 100644 --- a/src/bootstrap-fork.js +++ b/src/bootstrap-fork.js @@ -7,15 +7,16 @@ 'use strict'; const bootstrap = require('./bootstrap'); +const bootstrapNode = require('./bootstrap-node'); // Remove global paths from the node module lookup -bootstrap.removeGlobalNodeModuleLookupPaths(); +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 @@ -39,6 +40,7 @@ configureCrashReporter(); // Load AMD entry point require('./bootstrap-amd').load(process.env['AMD_ENTRYPOINT']); + //#region Helpers function pipeLoggingToParent() { @@ -49,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++) { @@ -85,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)) { @@ -98,17 +98,20 @@ function pipeLoggingToParent() { return value; }); + + if (res.length > MAX_LENGTH) { + return 'Output omitted for a large object that exceeds the limits'; + } + + return res; } catch (error) { - return 'Output omitted for an object that cannot be inspected (' + error.toString() + ')'; + return `Output omitted for an object that cannot be inspected ('${error.toString()}')`; } - - if (res && res.length > MAX_LENGTH) { - return 'Output omitted for a large object that exceeds the limits'; - } - - return res; } + /** + * @param {{ type: string; severity: string; arguments: string; }} arg + */ function safeSend(arg) { try { process.send(arg); @@ -117,6 +120,9 @@ function pipeLoggingToParent() { } } + /** + * @param {unknown} obj + */ function isObject(obj) { return typeof obj === 'object' && obj !== null diff --git a/src/bootstrap-node.js b/src/bootstrap-node.js new file mode 100644 index 00000000000..2686249978d --- /dev/null +++ b/src/bootstrap-node.js @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check +'use strict'; + +/** + * Add support for redirecting the loading of node modules + * + * @param {string} injectPath + */ +exports.injectNodeModuleLookupPath = function (injectPath) { + if (!injectPath) { + throw new Error('Missing injectPath'); + } + + const Module = require('module'); + const path = require('path'); + + const nodeModulesPath = path.join(__dirname, '../node_modules'); + + // @ts-ignore + const originalResolveLookupPaths = Module._resolveLookupPaths; + + // @ts-ignore + Module._resolveLookupPaths = function (moduleName, parent) { + const paths = originalResolveLookupPaths(moduleName, parent); + if (Array.isArray(paths)) { + for (let i = 0, len = paths.length; i < len; i++) { + if (paths[i] === nodeModulesPath) { + paths.splice(i, 0, injectPath); + break; + } + } + } + + return paths; + }; +}; + +exports.removeGlobalNodeModuleLookupPaths = function () { + const Module = require('module'); + // @ts-ignore + const globalPaths = Module.globalPaths; + + // @ts-ignore + const originalResolveLookupPaths = Module._resolveLookupPaths; + + // @ts-ignore + Module._resolveLookupPaths = function (moduleName, parent) { + const paths = originalResolveLookupPaths(moduleName, parent); + let commonSuffixLength = 0; + while (commonSuffixLength < paths.length && paths[paths.length - 1 - commonSuffixLength] === globalPaths[globalPaths.length - 1 - commonSuffixLength]) { + commonSuffixLength++; + } + return paths.slice(0, paths.length - commonSuffixLength); + }; +}; diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js index ec93f2a6516..76c00585ad8 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -3,212 +3,250 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/// + //@ts-check 'use strict'; -const bootstrap = require('./bootstrap'); +// Simple module style to support node.js and browser environments +(function (globalThis, factory) { -/** - * @param {object} destination - * @param {object} source - * @returns {object} - */ -exports.assign = function assign(destination, source) { - return Object.keys(source).reduce(function (r, key) { r[key] = source[key]; return r; }, destination); -}; + // Node.js + if (typeof exports === 'object') { + module.exports = factory(); + } -/** - * - * @param {string[]} modulePaths - * @param {(result, configuration: object) => any} resultCallback - * @param {{ forceEnableDeveloperKeybindings?: boolean, disallowReloadKeybinding?: boolean, removeDeveloperKeybindingsAfterLoad?: boolean, canModifyDOM?: (config: object) => void, beforeLoaderConfig?: (config: object, loaderConfig: object) => void, beforeRequire?: () => void }=} options - */ -exports.load = function (modulePaths, resultCallback, options) { + // Browser + else { + globalThis.MonacoBootstrapWindow = factory(); + } +}(this, function () { + const preloadGlobals = globals(); + const sandbox = preloadGlobals.context.sandbox; + const safeProcess = sandbox ? preloadGlobals.process : process; - const webFrame = require('electron').webFrame; - const path = require('path'); - - 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'] || '{}') || {}; + * @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 {{ + * 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); + // Error handler + safeProcess.on('uncaughtException', function (error) { + onUnexpectedError(error, enableDeveloperTools); + }); + + // Developer tools + const enableDeveloperTools = (safeProcess.env['VSCODE_DEV'] || !!configuration.extensionDevelopmentPath) && !configuration.extensionTestsPath; + let developerToolsUnbind; + if (enableDeveloperTools || (options && options.forceEnableDeveloperKeybindings)) { + developerToolsUnbind = registerDeveloperKeybindings(options && options.disallowReloadKeybinding); + } + + // Correctly inherit the parent's environment (TODO@sandbox non-sandboxed only) + if (!sandbox) { + Object.assign(safeProcess.env, configuration.userEnv); + } + + // Enable ASAR support (TODO@sandbox non-sandboxed only) + if (!sandbox) { + globalThis.MonacoBootstrap.enableASARSupport(configuration.appRoot); + } + + if (options && typeof options.canModifyDOM === 'function') { + options.canModifyDOM(configuration); + } + + // Get the nls configuration into the process.env as early as possible (TODO@sandbox non-sandboxed only) + const nlsConfig = sandbox ? { availableLanguages: {} } : globalThis.MonacoBootstrap.setupNLS(); + + let locale = nlsConfig.availableLanguages['*'] || 'en'; + if (locale === 'zh-tw') { + locale = 'zh-Hant'; + } else if (locale === 'zh-cn') { + locale = 'zh-Hans'; + } + + window.document.documentElement.setAttribute('lang', locale); + + // do not advertise AMD to avoid confusing UMD modules loaded with nodejs (TODO@sandbox non-sandboxed only) + if (!sandbox) { + window['define'] = undefined; + } + + // replace the patched electron fs with the original node fs for all AMD code (TODO@sandbox non-sandboxed only) + if (!sandbox) { + require.define('fs', ['original-fs'], function (originalFS) { return originalFS; }); + } + + window['MonacoEnvironment'] = {}; + + const loaderConfig = { + baseUrl: `${uriFromPath(configuration.appRoot)}/out`, + 'vs/nls': nlsConfig, + amdModulesPattern: /^vs\//, + }; + + // cached data config + if (configuration.nodeCachedDataDir) { + loaderConfig.nodeCachedData = { + path: configuration.nodeCachedDataDir, + seed: modulePaths.join('') + }; + } + + if (options && typeof options.beforeLoaderConfig === 'function') { + options.beforeLoaderConfig(configuration, loaderConfig); + } + + require.config(loaderConfig); + + if (nlsConfig.pseudo) { + require(['vs/nls'], function (nlsPlugin) { + nlsPlugin.setPseudoTranslation(nlsConfig.pseudo); + }); + } + + if (options && typeof options.beforeRequire === 'function') { + options.beforeRequire(); + } + + require(modulePaths, result => { + try { + const callbackResult = resultCallback(result, configuration); + if (callbackResult && typeof callbackResult.then === 'function') { + callbackResult.then(() => { + if (developerToolsUnbind && options && options.removeDeveloperKeybindingsAfterLoad) { + developerToolsUnbind(); + } + }, error => { + onUnexpectedError(error, enableDeveloperTools); + }); + } + } catch (error) { + onUnexpectedError(error, enableDeveloperTools); + } + }, onUnexpectedError); } - // Error handler - process.on('uncaughtException', function (error) { - onUnexpectedError(error, enableDeveloperTools); - }); + /** + * @returns {{[param: string]: string }} + */ + function parseURLQueryArgs() { + const search = window.location.search || ''; - // Developer tools - const enableDeveloperTools = (process.env['VSCODE_DEV'] || !!configuration.extensionDevelopmentPath) && !configuration.extensionTestsPath; - let developerToolsUnbind; - if (enableDeveloperTools || (options && options.forceEnableDeveloperKeybindings)) { - developerToolsUnbind = registerDeveloperKeybindings(options && options.disallowReloadKeybinding); + return search.split(/[?&]/) + .filter(function (param) { return !!param; }) + .map(function (param) { return param.split('='); }) + .filter(function (param) { return param.length === 2; }) + .reduce(function (r, param) { r[param[0]] = decodeURIComponent(param[1]); return r; }, {}); } - // Correctly inherit the parent's environment - exports.assign(process.env, configuration.userEnv); + /** + * @param {boolean} disallowReloadKeybinding + * @returns {() => void} + */ + function registerDeveloperKeybindings(disallowReloadKeybinding) { + const ipcRenderer = preloadGlobals.ipcRenderer; - // Enable ASAR support - bootstrap.enableASARSupport(path.join(configuration.appRoot, 'node_modules')); + const extractKey = function (e) { + return [ + e.ctrlKey ? 'ctrl-' : '', + e.metaKey ? 'meta-' : '', + e.altKey ? 'alt-' : '', + e.shiftKey ? 'shift-' : '', + e.keyCode + ].join(''); + }; - if (options && typeof options.canModifyDOM === 'function') { - options.canModifyDOM(configuration); - } + // Devtools & reload support + const TOGGLE_DEV_TOOLS_KB = (safeProcess.platform === 'darwin' ? 'meta-alt-73' : 'ctrl-shift-73'); // mac: Cmd-Alt-I, rest: Ctrl-Shift-I + const TOGGLE_DEV_TOOLS_KB_ALT = '123'; // F12 + const RELOAD_KB = (safeProcess.platform === 'darwin' ? 'meta-82' : 'ctrl-82'); // mac: Cmd-R, rest: Ctrl-R - // Get the nls configuration into the process.env as early as possible. - const nlsConfig = bootstrap.setupNLS(); + let listener = function (e) { + const key = extractKey(e); + if (key === TOGGLE_DEV_TOOLS_KB || key === TOGGLE_DEV_TOOLS_KB_ALT) { + ipcRenderer.send('vscode:toggleDevTools'); + } else if (key === RELOAD_KB && !disallowReloadKeybinding) { + ipcRenderer.send('vscode:reloadWindow'); + } + }; - let locale = nlsConfig.availableLanguages['*'] || 'en'; - if (locale === 'zh-tw') { - locale = 'zh-Hant'; - } else if (locale === 'zh-cn') { - locale = 'zh-Hans'; - } + window.addEventListener('keydown', listener); - window.document.documentElement.setAttribute('lang', locale); - - // Load the loader - const amdLoader = require(configuration.appRoot + '/out/vs/loader.js'); - const amdRequire = amdLoader.require; - const amdDefine = amdLoader.require.define; - const nodeRequire = amdLoader.require.nodeRequire; - - window['nodeRequire'] = nodeRequire; - window['require'] = amdRequire; - - // replace the patched electron fs with the original node fs for all AMD code - amdDefine('fs', ['original-fs'], function (originalFS) { return originalFS; }); - - window['MonacoEnvironment'] = {}; - - const loaderConfig = { - baseUrl: bootstrap.uriFromPath(configuration.appRoot) + '/out', - 'vs/nls': nlsConfig, - nodeModules: [/*BUILD->INSERT_NODE_MODULES*/] - }; - - // cached data config - if (configuration.nodeCachedDataDir) { - loaderConfig.nodeCachedData = { - path: configuration.nodeCachedDataDir, - seed: modulePaths.join('') + return function () { + if (listener) { + window.removeEventListener('keydown', listener); + listener = undefined; + } }; } - if (options && typeof options.beforeLoaderConfig === 'function') { - options.beforeLoaderConfig(configuration, loaderConfig); - } - - amdRequire.config(loaderConfig); - - if (nlsConfig.pseudo) { - amdRequire(['vs/nls'], function (nlsPlugin) { - nlsPlugin.setPseudoTranslation(nlsConfig.pseudo); - }); - } - - if (options && typeof options.beforeRequire === 'function') { - options.beforeRequire(); - } - - amdRequire(modulePaths, result => { - try { - const callbackResult = resultCallback(result, configuration); - if (callbackResult && typeof callbackResult.then === 'function') { - callbackResult.then(() => { - if (developerToolsUnbind && options && options.removeDeveloperKeybindingsAfterLoad) { - developerToolsUnbind(); - } - }, error => { - onUnexpectedError(error, enableDeveloperTools); - }); - } - } catch (error) { - onUnexpectedError(error, enableDeveloperTools); + /** + * @param {string | Error} error + * @param {boolean} [enableDeveloperTools] + */ + function onUnexpectedError(error, enableDeveloperTools) { + if (enableDeveloperTools) { + const ipcRenderer = preloadGlobals.ipcRenderer; + ipcRenderer.send('vscode:openDevTools'); } - }, onUnexpectedError); -}; -/** - * @returns {{[param: string]: string }} - */ -function parseURLQueryArgs() { - const search = window.location.search || ''; + console.error(`[uncaught exception]: ${error}`); - return search.split(/[?&]/) - .filter(function (param) { return !!param; }) - .map(function (param) { return param.split('='); }) - .filter(function (param) { return param.length === 2; }) - .reduce(function (r, param) { r[param[0]] = decodeURIComponent(param[1]); return r; }, {}); -} - -/** - * @param {boolean} disallowReloadKeybinding - * @returns {() => void} - */ -function registerDeveloperKeybindings(disallowReloadKeybinding) { - - const ipc = require('electron').ipcRenderer; - - const extractKey = function (e) { - return [ - e.ctrlKey ? 'ctrl-' : '', - e.metaKey ? 'meta-' : '', - e.altKey ? 'alt-' : '', - e.shiftKey ? 'shift-' : '', - e.keyCode - ].join(''); - }; - - // Devtools & reload support - const TOGGLE_DEV_TOOLS_KB = (process.platform === 'darwin' ? 'meta-alt-73' : 'ctrl-shift-73'); // mac: Cmd-Alt-I, rest: Ctrl-Shift-I - const TOGGLE_DEV_TOOLS_KB_ALT = '123'; // F12 - const RELOAD_KB = (process.platform === 'darwin' ? 'meta-82' : 'ctrl-82'); // mac: Cmd-R, rest: Ctrl-R - - let listener = function (e) { - const key = extractKey(e); - if (key === TOGGLE_DEV_TOOLS_KB || key === TOGGLE_DEV_TOOLS_KB_ALT) { - ipc.send('vscode:toggleDevTools'); - } else if (key === RELOAD_KB && !disallowReloadKeybinding) { - ipc.send('vscode:reloadWindow'); + if (error && typeof error !== 'string' && error.stack) { + console.error(error.stack); } - }; - - window.addEventListener('keydown', listener); - - return function () { - if (listener) { - window.removeEventListener('keydown', listener); - listener = undefined; - } - }; -} - -function onUnexpectedError(error, enableDeveloperTools) { - - const ipc = require('electron').ipcRenderer; - - if (enableDeveloperTools) { - ipc.send('vscode:openDevTools'); } - console.error('[uncaught exception]: ' + error); - - if (error && error.stack) { - console.error(error.stack); + /** + * @return {typeof import('./vs/base/parts/sandbox/electron-sandbox/globals')} + */ + function globals() { + // @ts-ignore (defined in globals.js) + return window.vscode; } -} + + /** + * 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}`); + } + + return uri.replace(/#/g, '%23'); + } + + return { + load, + globals + }; +})); diff --git a/src/bootstrap.js b/src/bootstrap.js index cc63fc39422..d1abd5502df 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -6,309 +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; - -// Workaround for Electron not installing a handler to ignore SIGPIPE -// (https://github.com/electron/electron/issues/13254) -// @ts-ignore -process.on('SIGPIPE', () => { - console.error(new Error('Unexpected SIGPIPE')); -}); - -//#endregion - -//#region Add support for redirecting the loading of node modules - -exports.injectNodeModuleLookupPath = function (injectPath) { - if (!injectPath) { - throw new Error('Missing injectPath'); + // Node.js + if (typeof exports === 'object') { + module.exports = factory(); } - // @ts-ignore - const Module = require('module'); - const path = require('path'); - - const nodeModulesPath = path.join(__dirname, '../node_modules'); - - // @ts-ignore - const originalResolveLookupPaths = Module._resolveLookupPaths; - - // @ts-ignore - Module._resolveLookupPaths = function (moduleName, parent) { - const paths = originalResolveLookupPaths(moduleName, parent); - if (Array.isArray(paths)) { - for (let i = 0, len = paths.length; i < len; i++) { - if (paths[i] === nodeModulesPath) { - paths.splice(i, 0, injectPath); - break; - } - } - } - - return paths; - }; -}; -//#endregion - -//#region Remove global paths from the node lookup paths - -exports.removeGlobalNodeModuleLookupPaths = function() { - // @ts-ignore - 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); - }; -}; -//#endregion - -//#region Add support for using node_modules.asar -/** - * @param {string=} nodeModulesPath - */ -exports.enableASARSupport = function (nodeModulesPath) { - - // @ts-ignore - const Module = require('module'); - const path = require('path'); - - let NODE_MODULES_PATH = nodeModulesPath; - if (!NODE_MODULES_PATH) { - NODE_MODULES_PATH = path.join(__dirname, '../node_modules'); - } - - const NODE_MODULES_ASAR_PATH = NODE_MODULES_PATH + '.asar'; - - // @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; - } - } - } - - return paths; - }; -}; -//#endregion - -//#region URI helpers -/** - * @param {string} _path - * @returns {string} - */ -exports.uriFromPath = function (_path) { - const path = require('path'); - - let pathName = path.resolve(_path).replace(/\\/g, '/'); - if (pathName.length > 0 && pathName.charAt(0) !== '/') { - pathName = '/' + pathName; - } - - /** @type {string} */ - let uri; - if (process.platform === 'win32' && pathName.startsWith('//')) { // specially handle Windows UNC paths - uri = encodeURI('file:' + pathName); - } else { - uri = encodeURI('file://' + pathName); - } - - return uri.replace(/#/g, '%23'); -}; -//#endregion - -//#region FS helpers -/** - * @param {string} file - * @returns {Promise} - */ -exports.readFile = function (file) { - const fs = require('fs'); - - return new Promise(function (resolve, reject) { - fs.readFile(file, 'utf8', function (err, data) { - if (err) { - reject(err); - return; - } - resolve(data); - }); - }); -}; - -/** - * @param {string} file - * @param {string} content - * @returns {Promise} - */ -exports.writeFile = function (file, content) { - const fs = require('fs'); - - return new Promise(function (resolve, reject) { - fs.writeFile(file, content, 'utf8', function (err) { - if (err) { - reject(err); - return; - } - resolve(); - }); - }); -}; - -/** - * @param {string} dir - * @returns {Promise} - */ -exports.mkdirp = function mkdirp(dir) { - const fs = require('fs'); - - return new Promise((c, e) => fs.mkdir(dir, { recursive: true }, err => (err && err.code !== 'EEXIST') ? e(err) : c(dir))); -}; -//#endregion - -//#region NLS helpers -/** - * @returns {{locale?: string, availableLanguages: {[lang: string]: string;}, pseudo?: boolean }} - */ -exports.setupNLS = function () { - const path = require('path'); - - // Get the nls configuration into the process.env as early as possible. - let nlsConfig = { availableLanguages: {} }; - if (process.env['VSCODE_NLS_CONFIG']) { + // Browser + else { try { - nlsConfig = JSON.parse(process.env['VSCODE_NLS_CONFIG']); - } catch (e) { - // Ignore + 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'); - if (nlsConfig._resolvedLanguagePackCoreLocation) { - const bundles = Object.create(null); + //#region global bootstrapping - nlsConfig.loadBundle = function (bundle, language, cb) { - const result = bundles[bundle]; - if (result) { - cb(undefined, result); + // increase number of stack frames(from 10, https://github.com/v8/v8/wiki/Stack-Trace-API) + Error.stackTraceLimit = 100; - return; + // 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')); + }); + + //#endregion + + + //#region Add support for using node_modules.asar + + /** + * @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); + } + } + + const NODE_MODULES_ASAR_PATH = `${NODE_MODULES_PATH}.asar`; + + // @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; + } + } } - 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; - - cb(undefined, json); - }).catch((error) => { - try { - if (nlsConfig._corruptedFile) { - exports.writeFile(nlsConfig._corruptedFile, 'corrupted').catch(function (error) { console.error(error); }); - } - } finally { - cb(error, undefined); - } - }); + return paths; }; } - return nlsConfig; -}; -//#endregion + //#endregion -//#region Portable helpers -/** - * @returns {{ portableDataPath: string, isPortable: boolean }} - */ -exports.configurePortable = function () { - // @ts-ignore - const product = require('../product.json'); - const path = require('path'); - const fs = require('fs'); - const appRoot = path.dirname(__dirname); + //#region URI helpers - function getApplicationPath() { - if (process.env['VSCODE_DEV']) { - return appRoot; + /** + * @param {string} _path + * @returns {string} + */ + function fileUriFromPath(_path) { + let pathName = path.resolve(_path).replace(/\\/g, '/'); + if (pathName.length > 0 && pathName.charAt(0) !== '/') { + pathName = `/${pathName}`; } - if (process.platform === 'darwin') { - return path.dirname(path.dirname(path.dirname(appRoot))); - } - - return path.dirname(path.dirname(appRoot)); - } - - function getPortableDataPath() { - if (process.env['VSCODE_PORTABLE']) { - return process.env['VSCODE_PORTABLE']; - } - - if (process.platform === 'win32' || process.platform === 'linux') { - return path.join(getApplicationPath(), 'data'); - } - - const portableDataName = product.portable || `${product.applicationName}-portable-data`; - return path.join(path.dirname(getApplicationPath()), portableDataName); - } - - const portableDataPath = getPortableDataPath(); - const isPortable = !('target' in product) && fs.existsSync(portableDataPath); - const portableTempPath = path.join(portableDataPath, 'tmp'); - const isTempPortable = isPortable && fs.existsSync(portableTempPath); - - if (isPortable) { - process.env['VSCODE_PORTABLE'] = portableDataPath; - } else { - delete process.env['VSCODE_PORTABLE']; - } - - if (isTempPortable) { - if (process.platform === 'win32') { - process.env['TMP'] = portableTempPath; - process.env['TEMP'] = portableTempPath; + /** @type {string} */ + let uri; + if (process.platform === 'win32' && pathName.startsWith('//')) { // specially handle Windows UNC paths + uri = encodeURI(`file:${pathName}`); } else { - process.env['TMPDIR'] = portableTempPath; + uri = encodeURI(`file://${pathName}`); } + + return uri.replace(/#/g, '%23'); } + //#endregion + + + //#region NLS helpers + + /** + * @returns {{locale?: string, availableLanguages: {[lang: string]: string;}, pseudo?: boolean }} + */ + function setupNLS() { + + // Get the nls configuration into the process.env as early as possible. + let nlsConfig = { availableLanguages: {} }; + if (process.env['VSCODE_NLS_CONFIG']) { + try { + nlsConfig = JSON.parse(process.env['VSCODE_NLS_CONFIG']); + } catch (e) { + // Ignore + } + } + + if (nlsConfig._resolvedLanguagePackCoreLocation) { + const bundles = Object.create(null); + + nlsConfig.loadBundle = function (bundle, language, cb) { + const result = bundles[bundle]; + if (result) { + cb(undefined, result); + + return; + } + + const bundleFile = path.join(nlsConfig._resolvedLanguagePackCoreLocation, `${bundle.replace(/\//g, '!')}.nls.json`); + fs.promises.readFile(bundleFile, 'utf8').then(function (content) { + const json = JSON.parse(content); + bundles[bundle] = json; + + cb(undefined, json); + }).catch((error) => { + try { + if (nlsConfig._corruptedFile) { + fs.promises.writeFile(nlsConfig._corruptedFile, 'corrupted', 'utf8').catch(function (error) { console.error(error); }); + } + } finally { + cb(error, undefined); + } + }); + }; + } + + return nlsConfig; + } + + //#endregion + + + //#region Portable helpers + + /** + * @param {{ portable: string; applicationName: string; }} product + * @returns {{portableDataPath: string;isPortable: boolean;}} + */ + function configurePortable(product) { + const appRoot = path.dirname(__dirname); + + function getApplicationPath() { + if (process.env['VSCODE_DEV']) { + return appRoot; + } + + if (process.platform === 'darwin') { + return path.dirname(path.dirname(path.dirname(appRoot))); + } + + return path.dirname(path.dirname(appRoot)); + } + + function getPortableDataPath() { + if (process.env['VSCODE_PORTABLE']) { + return process.env['VSCODE_PORTABLE']; + } + + if (process.platform === 'win32' || process.platform === 'linux') { + return path.join(getApplicationPath(), 'data'); + } + + // @ts-ignore + const portableDataName = product.portable || `${product.applicationName}-portable-data`; + return path.join(path.dirname(getApplicationPath()), portableDataName); + } + + const portableDataPath = getPortableDataPath(); + const isPortable = !('target' in product) && fs.existsSync(portableDataPath); + const portableTempPath = path.join(portableDataPath, 'tmp'); + const isTempPortable = isPortable && fs.existsSync(portableTempPath); + + if (isPortable) { + process.env['VSCODE_PORTABLE'] = portableDataPath; + } else { + delete process.env['VSCODE_PORTABLE']; + } + + if (isTempPortable) { + if (process.platform === 'win32') { + process.env['TMP'] = portableTempPath; + process.env['TEMP'] = portableTempPath; + } else { + process.env['TMPDIR'] = portableTempPath; + } + } + + return { + portableDataPath, + isPortable + }; + } + + //#endregion + + + //#region ApplicationInsights + + // Prevents appinsights from monkey patching modules. + // This should be called before importing the applicationinsights module + function avoidMonkeyPatchFromAppInsights() { + // @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/cli.js b/src/cli.js index 4be0bad61d8..872cd66733d 100644 --- a/src/cli.js +++ b/src/cli.js @@ -7,15 +7,16 @@ 'use strict'; const bootstrap = require('./bootstrap'); +const product = require('../product.json'); // Avoid Monkey Patches from Application Insights bootstrap.avoidMonkeyPatchFromAppInsights(); // Enable portable support -bootstrap.configurePortable(); +bootstrap.configurePortable(product); // Enable ASAR support bootstrap.enableASARSupport(); // Load CLI through AMD loader -require('./bootstrap-amd').load('vs/code/node/cli'); \ No newline at end of file +require('./bootstrap-amd').load('vs/code/node/cli'); diff --git a/src/main.js b/src/main.js index 23915126b8f..f179a672031 100644 --- a/src/main.js +++ b/src/main.js @@ -16,13 +16,12 @@ 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'); // Enable portable support -const portable = bootstrap.configurePortable(); +const portable = bootstrap.configurePortable(product); // Enable ASAR support bootstrap.enableASARSupport(); @@ -42,6 +41,11 @@ let crashReporterDirectory = args['crash-reporter-directory']; 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); @@ -58,9 +62,11 @@ if (crashReporterDirectory) { // Start crash reporter const { crashReporter } = require('electron'); + const productName = (product.crashReporter && product.crashReporter.productName) || product.nameShort; + const companyName = (product.crashReporter && product.crashReporter.companyName) || 'Microsoft'; crashReporter.start({ - companyName: 'Microsoft', - productName: product.nameShort, + companyName: companyName, + productName: process.env['VSCODE_DEV'] ? `${productName} Dev` : productName, submitURL: '', uploadToServer: false }); @@ -79,7 +85,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 @@ -114,7 +136,6 @@ if (locale) { // Load our code once ready app.once('ready', function () { if (args['trace']) { - // @ts-ignore const contentTracing = require('electron').contentTracing; const traceOptions = { @@ -178,33 +199,51 @@ function configureCommandlineSwitchesSync(cliArgs) { ]; 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]; - // Color profile - if (argvKey === 'force-color-profile') { - if (argvValue) { - app.commandLine.appendSwitch(argvKey, argvValue); + // 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); + } } } - // Others - else if (argvValue === true || argvValue === 'true') { - if (argvKey === 'disable-hardware-acceleration') { - app.disableHardwareAcceleration(); // needs to be called explicitly - } else { - app.commandLine.appendSwitch(argvKey); + // Append main process flags to process.argv + else if (SUPPORTED_MAIN_PROCESS_SWITCHES.indexOf(argvKey) !== -1) { + if (argvKey === 'enable-proposed-api') { + if (Array.isArray(argvValue)) { + argvValue.forEach(id => id && typeof id === 'string' && process.argv.push('--enable-proposed-api', id)); + } else { + console.error(`Unexpected value for \`enable-proposed-api\` in argv.json. Expected array of extension ids.`); + } } } }); @@ -431,7 +470,7 @@ function getNodeCachedDir() { async ensureExists() { try { - await bootstrap.mkdirp(this.value); + await mkdirp(this.value); return this.value; } catch (error) { @@ -460,7 +499,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 * @@ -556,4 +608,5 @@ function getLegacyUserDefinedLocaleSync(localeConfigPath) { // ignore } } + //#endregion diff --git a/src/paths.js b/src/paths.js index a6d3c052284..a88927d200b 100644 --- a/src/paths.js +++ b/src/paths.js @@ -6,7 +6,6 @@ //@ts-check 'use strict'; -// @ts-expect-error const pkg = require('../package.json'); const path = require('path'); const os = require('os'); diff --git a/src/typings/require.d.ts b/src/typings/require.d.ts index dfffac43e56..cc4a4043ce1 100644 --- a/src/typings/require.d.ts +++ b/src/typings/require.d.ts @@ -47,6 +47,7 @@ interface NodeRequire { onError: Function; __$__nodeRequire(moduleName: string): T; getStats(): ReadonlyArray; + define(amdModuleId: string, dependencies: string[], callback: (...args: any[]) => any): any; } declare var require: NodeRequire; diff --git a/src/vs/base/browser/contextmenu.ts b/src/vs/base/browser/contextmenu.ts index caf1d299038..2119700f859 100644 --- a/src/vs/base/browser/contextmenu.ts +++ b/src/vs/base/browser/contextmenu.ts @@ -3,10 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IAction, IActionRunner } from 'vs/base/common/actions'; -import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IAction, IActionRunner, IActionViewItem } from 'vs/base/common/actions'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; -import { SubmenuAction } from 'vs/base/browser/ui/menu/menu'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; export interface IContextMenuEvent { @@ -16,15 +14,9 @@ export interface IContextMenuEvent { readonly metaKey?: boolean; } -export class ContextSubMenu extends SubmenuAction { - constructor(label: string, public entries: Array) { - super(label, entries, 'contextsubmenu'); - } -} - export interface IContextMenuDelegate { getAnchor(): HTMLElement | { x: number; y: number; width?: number; height?: number; }; - getActions(): ReadonlyArray; + getActions(): IAction[]; getCheckedActionsRepresentation?(action: IAction): 'radio' | 'checkbox'; getActionViewItem?(action: IAction): IActionViewItem | undefined; getActionsContext?(event?: IContextMenuEvent): any; @@ -34,4 +26,9 @@ export interface IContextMenuDelegate { actionRunner?: IActionRunner; autoSelectFirstItem?: boolean; anchorAlignment?: AnchorAlignment; + domForShadowRoot?: HTMLElement; +} + +export interface IContextMenuProvider { + showContextMenu(delegate: IContextMenuDelegate): void; } diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 3e5eaeaa4fa..f203119f8f6 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -749,6 +749,16 @@ export function getShadowRoot(domNode: Node): ShadowRoot | null { return isShadowRoot(domNode) ? domNode : null; } +export function getActiveElement(): Element | null { + let result = document.activeElement; + + while (result?.shadowRoot) { + result = result.shadowRoot.activeElement; + } + + return result; +} + export function createStyleSheet(container: HTMLElement = document.getElementsByTagName('head')[0]): HTMLStyleElement { let style = document.createElement('style'); style.type = 'text/css'; @@ -820,6 +830,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', diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index cf3c0daf9db..e79f159100b 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -16,16 +16,22 @@ import { escape } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { renderCodicons, markdownEscapeEscapedCodicons } from 'vs/base/common/codicons'; +import { resolvePath } from 'vs/base/common/resources'; + +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 = {}, markedOptions: marked.MarkedOptions = {}): HTMLElement { +export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRenderOptions = {}, markedOptions: MarkedOptions = {}): HTMLElement { const element = createElement(options); const _uriMassage = function (part: string): string { @@ -82,6 +88,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) { @@ -101,6 +114,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 ( @@ -173,6 +192,13 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende })); } + // 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; @@ -181,25 +207,46 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende allowedSchemes.push(Schemas.command); } + // 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(markdown.value || '') - : (markdown.value || ''), + 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'], - 'span': ['class'], + 'span': ['class', 'style'], // https://github.com/microsoft/vscode/issues/95937 'th': ['align'], 'td': ['align'] - } + }, + filter }); signalInnerHTML!(); diff --git a/src/vs/base/browser/ui/actionbar/actionViewItems.ts b/src/vs/base/browser/ui/actionbar/actionViewItems.ts new file mode 100644 index 00000000000..b7916bf107d --- /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 DOM from 'vs/base/browser/dom'; +import * as types from 'vs/base/common/types'; +import { EventType, 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'; + +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(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'); + } + })); + + 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(DOM.addDisposableListener(element, DOM.EventType.CONTEXT_MENU, e => { + if (e.button === 0 && e.ctrlKey === true) { + this.onClick(e); + } + })); + } + + this._register(DOM.addDisposableListener(element, DOM.EventType.CLICK, e => { + DOM.EventHelper.stop(e, true); + + // menus do not use the click event + if (!(this.options && this.options.isMenu)) { + 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); + + 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(); + 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 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 = 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'); + } + } + } +} + +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 9b304e81a80..79c2a927201 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; } diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 7f14dec71e2..bbc0918db24 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -4,381 +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: 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(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'); - } - })); - - 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(DOM.addDisposableListener(element, DOM.EventType.CONTEXT_MENU, e => { - if (e.button === 0 && e.ctrlKey === true) { - this.onClick(e); - } - })); - } - - 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); - - const context = types.isUndefinedOrNull(this._context) ? undefined : this._context; - 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: 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 = 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 { IActionViewItemOptions, ActionViewItem, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; export const enum ActionsOrientation { HORIZONTAL, @@ -392,39 +25,30 @@ 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: unknown; + private _orientation: ActionsOrientation; + private _triggerKeys: ActionTrigger; // View Items viewItems: IActionViewItem[]; @@ -447,15 +71,16 @@ export class ActionBar extends Disposable implements IActionRunner { private _onDidBeforeRun = this._register(new Emitter()); readonly onDidBeforeRun: Event = 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; @@ -477,27 +102,27 @@ export class ActionBar extends Disposable implements IActionRunner { DOM.addClass(this.domNode, 'animated'); } - let previousKeys: KeyCode[]; - let nextKeys: KeyCode[]; + let previousKey: KeyCode; + let nextKey: KeyCode; - switch (this.options.orientation) { + switch (this._orientation) { case ActionsOrientation.HORIZONTAL: - previousKeys = [KeyCode.LeftArrow, KeyCode.UpArrow]; - nextKeys = [KeyCode.RightArrow, KeyCode.DownArrow]; + previousKey = KeyCode.LeftArrow; + nextKey = KeyCode.RightArrow; break; case ActionsOrientation.HORIZONTAL_REVERSE: - previousKeys = [KeyCode.RightArrow, KeyCode.DownArrow]; - nextKeys = [KeyCode.LeftArrow, KeyCode.UpArrow]; + previousKey = KeyCode.RightArrow; + nextKey = KeyCode.LeftArrow; this.domNode.className += ' reverse'; break; case ActionsOrientation.VERTICAL: - previousKeys = [KeyCode.LeftArrow, KeyCode.UpArrow]; - nextKeys = [KeyCode.RightArrow, KeyCode.DownArrow]; + previousKey = KeyCode.UpArrow; + nextKey = KeyCode.DownArrow; this.domNode.className += ' vertical'; break; case ActionsOrientation.VERTICAL_REVERSE: - previousKeys = [KeyCode.RightArrow, KeyCode.DownArrow]; - nextKeys = [KeyCode.LeftArrow, KeyCode.UpArrow]; + previousKey = KeyCode.DownArrow; + nextKey = KeyCode.UpArrow; this.domNode.className += ' vertical reverse'; break; } @@ -506,15 +131,15 @@ export class ActionBar extends Disposable implements IActionRunner { const event = new StandardKeyboardEvent(e); let eventHandled = true; - if (previousKeys && (event.equals(previousKeys[0]) || event.equals(previousKeys[1]))) { - this.focusPrevious(); - } else if (nextKeys && (event.equals(nextKeys[0]) || event.equals(nextKeys[1]))) { - this.focusNext(); + if (event.equals(previousKey)) { + eventHandled = this.focusPrevious(); + } else if (event.equals(nextKey)) { + eventHandled = this.focusNext(); } else if (event.equals(KeyCode.Escape)) { 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 { @@ -532,7 +157,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); } @@ -548,7 +173,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; } @@ -579,11 +204,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; } @@ -591,7 +214,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; } @@ -633,9 +256,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) => { - DOM.EventHelper.stop(e, true); - })); + 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; @@ -692,7 +317,8 @@ export class ActionBar extends Disposable implements IActionRunner { } clear(): void { - this.viewItems = dispose(this.viewItems); + dispose(this.viewItems); + this.viewItems = []; DOM.clearNode(this.actionsList); } @@ -719,7 +345,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 +356,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 +365,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 +379,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 +394,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 +410,7 @@ export class ActionBar extends Disposable implements IActionRunner { } this.updateFocus(true); + return true; } protected updateFocus(fromRight?: boolean, preventScroll?: boolean): void { @@ -827,53 +465,6 @@ export class ActionBar extends Disposable implements IActionRunner { } } -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); - } -} - export function prepareActions(actions: IAction[]): IAction[] { if (!actions.length) { return actions; diff --git a/src/vs/base/browser/ui/aria/aria.ts b/src/vs/base/browser/ui/aria/aria.ts index a971f2f4ce3..641e5f339a3 100644 --- a/src/vs/base/browser/ui/aria/aria.ts +++ b/src/vs/base/browser/ui/aria/aria.ts @@ -11,50 +11,78 @@ import * as dom from 'vs/base/browser/dom'; 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', 'complementary'); - statusContainer.setAttribute('aria-live', 'polite'); - 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): void { - insertMessage(alertContainer, msg); + if (!ariaContainer) { + return; + } + + // Use alternate containers such that duplicated messages get read out by screen readers #99466 + if (alertContainer.textContent !== msg) { + dom.clearNode(alertContainer2); + insertMessage(alertContainer, msg); + } else { + dom.clearNode(alertContainer); + insertMessage(alertContainer2, msg); + } } /** * Given the provided message, will make sure that it is read as status to screen readers. */ export function status(msg: string): void { - if (isMacintosh) { - alert(msg); // VoiceOver does not seem to support status role - } else { - insertMessage(statusContainer, msg); - } -} - -function insertMessage(target: HTMLElement, msg: string): void { if (!ariaContainer) { return; } + if (isMacintosh) { + alert(msg); // VoiceOver does not seem to support status role + } else { + if (statusContainer.textContent !== msg) { + dom.clearNode(statusContainer2); + insertMessage(statusContainer, msg); + } else { + 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); diff --git a/src/vs/base/browser/ui/button/button.ts b/src/vs/base/browser/ui/button/button.ts index bfadf3dbe83..592586c718c 100644 --- a/src/vs/base/browser/ui/button/button.ts +++ b/src/vs/base/browser/ui/button/button.ts @@ -18,12 +18,16 @@ import { escape } from 'vs/base/common/strings'; export interface IButtonOptions extends IButtonStyles { 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; } @@ -41,6 +45,9 @@ 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()); @@ -54,9 +61,14 @@ 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'); @@ -114,7 +126,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; } @@ -124,6 +141,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(); @@ -131,8 +151,15 @@ export class Button extends Disposable { private applyStyles(): void { if (this._element) { - const background = this.buttonBackground ? this.buttonBackground.toString() : ''; - const foreground = this.buttonForeground ? this.buttonForeground.toString() : ''; + let background, foreground; + if (this.options.secondary) { + foreground = this.buttonSecondaryForeground ? this.buttonSecondaryForeground.toString() : ''; + background = this.buttonSecondaryBackground ? this.buttonSecondaryBackground.toString() : ''; + } else { + foreground = this.buttonForeground ? this.buttonForeground.toString() : ''; + background = this.buttonBackground ? this.buttonBackground.toString() : ''; + } + const border = this.buttonBorder ? this.buttonBorder.toString() : ''; this._element.style.color = foreground; diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts index d97f303111f..e29d71d60b1 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.ts +++ b/src/vs/base/browser/ui/checkbox/checkbox.ts @@ -10,9 +10,9 @@ 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 { 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; @@ -23,6 +23,7 @@ export interface ICheckboxOpts extends ICheckboxStyles { export interface ICheckboxStyles { inputActiveOptionBorder?: Color; + inputActiveOptionForeground?: Color; inputActiveOptionBackground?: Color; } @@ -34,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; @@ -170,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; } @@ -179,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'; } } diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index ab67bd300fc..82acc8995b8 100644 Binary files a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf and b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf differ diff --git a/src/vs/base/browser/ui/codicons/codiconStyles.ts b/src/vs/base/browser/ui/codicons/codiconStyles.ts index 899af845e8f..b3dc12fb3f3 100644 --- a/src/vs/base/browser/ui/codicons/codiconStyles.ts +++ b/src/vs/base/browser/ui/codicons/codiconStyles.ts @@ -28,7 +28,7 @@ function initialize() { delayer.schedule(); } -function formatRule(c: Codicon) { +export function formatRule(c: Codicon) { let def = c.definition; while (def instanceof Codicon) { def = def.definition; diff --git a/src/vs/base/browser/ui/contextview/contextview.css b/src/vs/base/browser/ui/contextview/contextview.css index b1b67bbe3c3..bb7ebbcfdb2 100644 --- a/src/vs/base/browser/ui/contextview/contextview.css +++ b/src/vs/base/browser/ui/contextview/contextview.css @@ -7,3 +7,12 @@ position: absolute; z-index: 2500; } + +.context-view.fixed { + all: initial; + font-family: inherit; + font-size: 13px; + position: fixed; + z-index: 2500; + color: inherit; +} diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index eacf37f358a..60a97219140 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -10,6 +10,12 @@ import { IDisposable, toDisposable, Disposable, DisposableStore } from 'vs/base/ import { Range } from 'vs/base/common/range'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; +export const enum ContextViewDOMPosition { + ABSOLUTE = 1, + FIXED, + FIXED_SHADOW +} + export interface IAnchor { x: number; y: number; @@ -38,7 +44,7 @@ export interface IDelegate { } export interface IContextViewProvider { - showContextView(delegate: IDelegate): void; + showContextView(delegate: IDelegate, container?: HTMLElement): void; hideContextView(): void; layout(): void; } @@ -104,31 +110,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; + DOM.removeNode(this.shadowRootHostElement!); + 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(); @@ -158,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 @@ -175,6 +215,10 @@ export class ContextView extends Disposable { } } + getViewElement(): HTMLElement { + return this.view; + } + layout(): void { if (!this.isVisible()) { return; @@ -254,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'; } @@ -294,3 +339,45 @@ 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-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 ce26af5e6a1..eb0c0837ee9 100644 --- a/src/vs/base/browser/ui/countBadge/countBadge.css +++ b/src/vs/base/browser/ui/countBadge/countBadge.css @@ -19,4 +19,6 @@ .monaco-count-badge.long { padding: 2px 3px; border-radius: 2px; + min-height: auto; + line-height: normal; } diff --git a/src/vs/base/browser/ui/dialog/dialog.css b/src/vs/base/browser/ui/dialog/dialog.css index 911b5616e70..0a0c821553b 100644 --- a/src/vs/base/browser/ui/dialog/dialog.css +++ b/src/vs/base/browser/ui/dialog/dialog.css @@ -55,7 +55,7 @@ padding: 0 10px; } -.monaco-dialog-box .dialog-message-row > .codicon { +.monaco-dialog-box .dialog-message-row > .dialog-icon.codicon { flex: 0 0 48px; height: 48px; align-self: baseline; diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index 089af090e28..623aa9c00d0 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -73,6 +73,7 @@ export class Dialog extends Disposable { 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 @@ -109,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; @@ -242,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 diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts index 99c11cdd6e2..b74cab78bb6 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.ts +++ b/src/vs/base/browser/ui/dropdown/dropdown.ts @@ -5,14 +5,13 @@ 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, removeClass, addClass, 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'; @@ -52,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]) { @@ -122,7 +121,7 @@ export class BaseDropdown extends ActionRunner { return !!this.visible; } - protected onEvent(e: Event, activeElement: HTMLElement): void { + protected onEvent(e: DOMEvent, activeElement: HTMLElement): void { this.hide(); } @@ -201,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; + actions?: IAction[]; 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); @@ -230,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) { @@ -240,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(); } @@ -248,7 +246,7 @@ export class DropdownMenu extends BaseDropdown { return this._actions; } - private set actions(actions: ReadonlyArray) { + private set actions(actions: IAction[]) { this._actions = actions; } @@ -266,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 }); } @@ -279,95 +278,3 @@ export class DropdownMenu extends BaseDropdown { removeClass(this.element, 'active'); } } - -export class DropdownMenuActionViewItem extends BaseActionViewItem { - private menuActionsOrProvider: ReadonlyArray | IActionProvider; - 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')); // todo@aeschli: remove codicon, should come through `this.clazz` - 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.setAttribute('aria-expanded', 'false'); - 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 as IActionProvider; - } - - this.dropdownMenu = this._register(new DropdownMenu(container, options)); - this._register(this.dropdownMenu.onDidChangeVisibility(visible => this.element?.setAttribute('aria-expanded', `${visible}`))); - - 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: 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/dropdown/dropdownActionViewItem.ts b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts new file mode 100644 index 00000000000..db045b26c13 --- /dev/null +++ b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts @@ -0,0 +1,136 @@ +/*--------------------------------------------------------------------------------------------- + * 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, $, addClasses } 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')); + + const classNames = this.options.classNames ? asArray(this.options.classNames) : []; + + // todo@aeschli: remove codicon, should come through `this.options.classNames` + if (!classNames.find(c => c === 'icon')) { + classNames.push('codicon'); + } + + addClasses(this.element, ...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 options: IDropdownMenuOptions = { + contextMenuProvider: this.contextMenuProvider, + labelRenderer: labelRenderer, + menuAsChild: this.options.menuAsChild + }; + + // Render the DropdownMenu around a simple action to toggle it + if (Array.isArray(this.menuActionsOrProvider)) { + options.actions = this.menuActionsOrProvider; + } else { + options.actionProvider = 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 4c5392c87dc..464ba3d3ffd 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 c9fb2a5f4fc..07e80f78c7f 100644 --- a/src/vs/base/browser/ui/findinput/findInputCheckboxes.ts +++ b/src/vs/base/browser/ui/findinput/findInputCheckboxes.ts @@ -12,6 +12,7 @@ export interface IFindInputCheckboxOpts { readonly appendTitle: string; readonly isChecked: boolean; readonly inputActiveOptionBorder?: Color; + readonly inputActiveOptionForeground?: Color; readonly inputActiveOptionBackground?: Color; } @@ -26,6 +27,7 @@ export class CaseSensitiveCheckbox extends Checkbox { title: NLS_CASE_SENSITIVE_CHECKBOX_LABEL + opts.appendTitle, isChecked: opts.isChecked, inputActiveOptionBorder: opts.inputActiveOptionBorder, + inputActiveOptionForeground: opts.inputActiveOptionForeground, inputActiveOptionBackground: opts.inputActiveOptionBackground }); } @@ -38,6 +40,7 @@ export class WholeWordsCheckbox extends Checkbox { title: NLS_WHOLE_WORD_CHECKBOX_LABEL + opts.appendTitle, isChecked: opts.isChecked, inputActiveOptionBorder: opts.inputActiveOptionBorder, + inputActiveOptionForeground: opts.inputActiveOptionForeground, inputActiveOptionBackground: opts.inputActiveOptionBackground }); } @@ -50,6 +53,7 @@ export class RegexCheckbox extends Checkbox { 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 d0d2d3b277c..1e9d8194ccc 100644 --- a/src/vs/base/browser/ui/findinput/replaceInput.ts +++ b/src/vs/base/browser/ui/findinput/replaceInput.ts @@ -33,6 +33,7 @@ export interface IReplaceInputOptions extends IReplaceInputStyles { export interface IReplaceInputStyles extends IInputBoxStyles { inputActiveOptionBorder?: Color; + inputActiveOptionForeground?: Color; inputActiveOptionBackground?: Color; } @@ -47,6 +48,7 @@ export class PreserveCaseCheckbox extends Checkbox { title: NLS_PRESERVE_CASE_LABEL + opts.appendTitle, isChecked: opts.isChecked, inputActiveOptionBorder: opts.inputActiveOptionBorder, + inputActiveOptionForeground: opts.inputActiveOptionForeground, inputActiveOptionBackground: opts.inputActiveOptionBackground }); } @@ -63,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; @@ -109,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; @@ -160,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 => { @@ -271,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; @@ -293,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 a8105574ac3..db736d5da23 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -10,7 +10,7 @@ import { tail2 as tail, equals } from 'vs/base/common/arrays'; 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, diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index a61fd2c5bab..b08b72a9479 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -380,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(); } @@ -791,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; diff --git a/src/vs/editor/contrib/hover/hover.css b/src/vs/base/browser/ui/hover/hover.css similarity index 56% rename from src/vs/editor/contrib/hover/hover.css rename to src/vs/base/browser/ui/hover/hover.css index 9c7aae9f330..9fbe9418b48 100644 --- a/src/vs/editor/contrib/hover/hover.css +++ b/src/vs/base/browser/ui/hover/hover.css @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-editor-hover { +.monaco-hover { cursor: default; position: absolute; overflow: hidden; @@ -16,113 +16,116 @@ line-height: 1.5em; } -.monaco-editor-hover.hidden { +.monaco-hover.hidden { display: none; } -.monaco-editor-hover .hover-contents { +.monaco-hover .hover-contents { padding: 4px 8px; } -.monaco-editor-hover .markdown-hover > .hover-contents:not(.code-hover-contents) { +.monaco-hover .markdown-hover > .hover-contents:not(.code-hover-contents) { max-width: 500px; word-wrap: break-word; } -.monaco-editor-hover .markdown-hover > .hover-contents:not(.code-hover-contents) hr { +.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-editor-hover p, -.monaco-editor-hover ul { +.monaco-hover p, +.monaco-hover .code, +.monaco-hover ul { margin: 8px 0; } -.monaco-editor-hover code { +.monaco-hover code { font-family: var(--monaco-monospace-font); } -.monaco-editor-hover hr { +.monaco-hover hr { margin-top: 4px; - margin-bottom: -6px; + margin-bottom: -4px; margin-left: -10px; margin-right: -10px; height: 1px; } -.monaco-editor-hover p:first-child, -.monaco-editor-hover ul:first-child { +.monaco-hover p:first-child, +.monaco-hover .code:first-child, +.monaco-hover ul:first-child { margin-top: 0; } -.monaco-editor-hover p:last-child, -.monaco-editor-hover ul:last-child { +.monaco-hover p:last-child, +.monaco-hover .code:last-child, +.monaco-hover ul:last-child { margin-bottom: 0; } /* MarkupContent Layout */ -.monaco-editor-hover ul { +.monaco-hover ul { padding-left: 20px; } -.monaco-editor-hover ol { +.monaco-hover ol { padding-left: 20px; } -.monaco-editor-hover li > p { +.monaco-hover li > p { margin-bottom: 0; } -.monaco-editor-hover li > ul { +.monaco-hover li > ul { margin-top: 0; } -.monaco-editor-hover code { +.monaco-hover code { border-radius: 3px; padding: 0 0.4em; } -.monaco-editor-hover .monaco-tokenized-source { +.monaco-hover .monaco-tokenized-source { white-space: pre-wrap; word-break: break-all; } -.monaco-editor-hover .hover-row.status-bar { +.monaco-hover .hover-row.status-bar { font-size: 12px; line-height: 22px; } -.monaco-editor-hover .hover-row.status-bar .actions { +.monaco-hover .hover-row.status-bar .actions { display: flex; padding: 0px 8px; } -.monaco-editor-hover .hover-row.status-bar .actions .action-container { +.monaco-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 { +.monaco-hover .hover-row.status-bar .actions .action-container .action .icon { padding-right: 4px; } -.monaco-editor-hover .markdown-hover .hover-contents .codicon { +.monaco-hover .markdown-hover .hover-contents .codicon { color: inherit; font-size: inherit; vertical-align: middle; } -.monaco-editor-hover .hover-contents a.code-link:before { +.monaco-hover .hover-contents a.code-link:before { content: '('; } -.monaco-editor-hover .hover-contents a.code-link:after { +.monaco-hover .hover-contents a.code-link:after { content: ')'; } -.monaco-editor-hover .hover-contents a.code-link { +.monaco-hover .hover-contents a.code-link { color: inherit; } -.monaco-editor-hover .hover-contents a.code-link > span { +.monaco-hover .hover-contents a.code-link > span { text-decoration: underline; /** Hack to force underline to show **/ border-bottom: 1px solid transparent; diff --git a/src/vs/base/browser/ui/hover/hoverWidget.ts b/src/vs/base/browser/ui/hover/hoverWidget.ts new file mode 100644 index 00000000000..6162569486e --- /dev/null +++ b/src/vs/base/browser/ui/hover/hoverWidget.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./hover'; +import * as dom from 'vs/base/browser/dom'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; + +const $ = dom.$; + +export class HoverWidget extends Disposable { + + public readonly containerDomNode: HTMLElement; + public readonly contentsDomNode: HTMLElement; + private readonly _scrollbar: DomScrollableElement; + + constructor() { + super(); + + this.containerDomNode = document.createElement('div'); + this.containerDomNode.className = 'monaco-hover'; + this.containerDomNode.tabIndex = 0; + this.containerDomNode.setAttribute('role', 'tooltip'); + + this.contentsDomNode = document.createElement('div'); + this.contentsDomNode.className = 'monaco-hover-content'; + + this._scrollbar = this._register(new DomScrollableElement(this.contentsDomNode, {})); + this.containerDomNode.appendChild(this._scrollbar.getDomNode()); + } + + public onContentsChanged(): void { + this._scrollbar.scanDomNode(); + } +} + +export function renderHoverAction(parent: HTMLElement, actionOptions: { label: string, iconClass?: string, run: (target: HTMLElement) => void, commandId: string }, keybindingLabel: string | null): IDisposable { + const actionContainer = dom.append(parent, $('div.action-container')); + const action = dom.append(actionContainer, $('a.action')); + action.setAttribute('href', '#'); + action.setAttribute('role', 'button'); + if (actionOptions.iconClass) { + dom.append(action, $(`span.icon.${actionOptions.iconClass}`)); + } + const label = dom.append(action, $('span')); + label.textContent = keybindingLabel ? `${actionOptions.label} (${keybindingLabel})` : actionOptions.label; + return dom.addDisposableListener(actionContainer, dom.EventType.CLICK, e => { + e.stopPropagation(); + e.preventDefault(); + actionOptions.run(actionContainer); + }); +} diff --git a/src/vs/base/browser/ui/iconLabel/iconlabel.css b/src/vs/base/browser/ui/iconLabel/iconlabel.css index 3c1392a2ff7..3175fd34959 100644 --- a/src/vs/base/browser/ui/iconLabel/iconlabel.css +++ b/src/vs/base/browser/ui/iconLabel/iconlabel.css @@ -55,6 +55,10 @@ white-space: pre; /* enable to show labels that include multiple whitespaces */ } +.vs .monaco-icon-label > .monaco-icon-label-container > .monaco-icon-description-container > .label-description { + opacity: .95; +} + .monaco-icon-label.italic > .monaco-icon-label-container > .monaco-icon-name-container > .label-name, .monaco-icon-label.italic > .monaco-icon-description-container > .label-description { font-style: italic; diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 39e4b80cbcd..a910e375661 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -163,8 +163,8 @@ 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; @@ -368,11 +368,11 @@ 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}` : ''; @@ -385,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(); @@ -460,7 +460,7 @@ export class InputBox extends Widget { const spanElement = (this.message.formatContent ? renderFormattedText(this.message.content, renderOptions) : renderText(this.message.content, renderOptions)); - dom.addClass(spanElement, this.classForType(this.message.type)); + spanElement.classList.add(this.classForType(this.message.type)); const styles = this.stylesForType(this.message.type); spanElement.style.backgroundColor = styles.background ? styles.background.toString() : ''; @@ -509,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(); diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index 508fb3dcabd..03db9d2a60d 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -6,12 +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, IListAccessibilityProvider } 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; @@ -119,7 +120,7 @@ function fromPagedListOptions(modelProvider: () => IPagedModel, options: I }; } -export class PagedList implements IDisposable { +export class PagedList implements IThemable, IDisposable { private list: List; private _model!: IPagedModel; @@ -136,6 +137,10 @@ export class PagedList implements IDisposable { this.list = new List(user, container, virtualDelegate, pagedRenderers, fromPagedListOptions(modelProvider, options)); } + updateOptions(options: IListOptionsUpdate) { + this.list.updateOptions(options); + } + getHTMLElement(): HTMLElement { return this.list.getHTMLElement(); } @@ -164,22 +169,30 @@ export class PagedList implements IDisposable { return this.list.onDidDispose; } + get onMouseClick(): Event> { + return Event.map(this.list.onMouseClick, ({ element, index, browserEvent }) => ({ element: element === undefined ? undefined : this._model.get(element), index, browserEvent })); + } + + get onMouseDblClick(): Event> { + return Event.map(this.list.onMouseDblClick, ({ element, index, browserEvent }) => ({ element: element === undefined ? undefined : this._model.get(element), index, browserEvent })); + } + + get onTap(): Event> { + return Event.map(this.list.onTap, ({ element, index, browserEvent }) => ({ element: element === undefined ? undefined : this._model.get(element), index, browserEvent })); + } + + 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 onDidOpen(): Event> { - return Event.map(this.list.onDidOpen, ({ 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 onPin(): Event> { - return Event.map(this.list.onDidPin, ({ elements, indexes, browserEvent }) => ({ elements: elements.map(e => this._model.get(e)), indexes, browserEvent })); - } - get onContextMenu(): Event> { return Event.map(this.list.onContextMenu, ({ element, index, anchor, browserEvent }) => (typeof element === 'undefined' ? { element, index, anchor, browserEvent } : { element: this._model.get(element), index, anchor, browserEvent })); } @@ -213,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); } diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index cea7064b563..5cecaeb44e4 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -9,8 +9,8 @@ import { Gesture, EventType as TouchEventType, GestureEvent } from 'vs/base/brow 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'; @@ -44,11 +44,17 @@ export interface IListViewDragAndDrop extends IListDragAndDrop { export interface IListViewAccessibilityProvider { getSetSize?(element: T, index: number, listLength: number): number; getPosInSet?(element: T, index: number): number; - getRole?(element: T): string; + 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; @@ -56,9 +62,7 @@ export interface IListViewOptions { readonly setRowHeight?: boolean; readonly supportDynamicHeights?: boolean; readonly mouseSupport?: boolean; - readonly horizontalScrolling?: boolean; readonly accessibilityProvider?: IListViewAccessibilityProvider; - readonly additionalScrollHeight?: number; readonly transformOptimization?: boolean; } @@ -82,7 +86,14 @@ const DefaultOptions = { export class ElementsDragAndDropData implements IDragAndDropData { readonly elements: T[]; - context: TContext | undefined; + + private _context: TContext | undefined; + public get context(): TContext | undefined { + return this._context; + } + public set context(value: TContext | undefined) { + this._context = value; + } constructor(elements: T[]) { this.elements = elements; @@ -158,7 +169,7 @@ class ListViewAccessibilityProvider implements Required number; readonly getPosInSet: (element: any, index: number) => number; - readonly getRole: (element: T) => string; + readonly getRole: (element: T) => string | undefined; readonly isChecked: (element: T) => boolean | undefined; constructor(accessibilityProvider?: IListViewAccessibilityProvider) { @@ -204,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); @@ -215,7 +227,6 @@ export class ListView implements ISpliceable, IDisposable { private setRowLineHeight: boolean; private setRowHeight: boolean; private supportDynamicHeights: boolean; - private horizontalScrolling: boolean; private additionalScrollHeight: number; private accessibilityProvider: ListViewAccessibilityProvider; private scrollWidth: number | undefined; @@ -237,6 +248,35 @@ export class ListView implements ISpliceable, IDisposable { 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; + DOM.toggleClass(this.domNode, 'horizontal-scrolling', this._horizontalScrolling); + + if (this._horizontalScrolling) { + for (const item of this.items) { + this.measureItemWidth(item); + } + + this.updateScrollWidth(); + this.scrollableElement.setScrollDimensions({ width: DOM.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, private virtualDelegate: IListVirtualDelegate, @@ -268,8 +308,8 @@ export class ListView implements ISpliceable, IDisposable { DOM.toggleClass(this.domNode, '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); + DOM.toggleClass(this.domNode, 'horizontal-scrolling', this._horizontalScrolling); this.additionalScrollHeight = typeof options.additionalScrollHeight === 'undefined' ? 0 : options.additionalScrollHeight; @@ -278,18 +318,20 @@ export class ListView implements ISpliceable, IDisposable { this.rowsContainer = document.createElement('div'); this.rowsContainer.className = 'monaco-list-rows'; - if (options.transformOptimization) { + 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 => DOM.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); @@ -315,10 +357,18 @@ export class ListView implements ISpliceable, IDisposable { this.layout(); } - updateOptions(options: IListViewOptions) { + 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) { @@ -326,6 +376,10 @@ export class ListView implements ISpliceable, IDisposable { } updateElementHeight(index: number, size: number, anchorIndex: number | null): void { + if (index < 0 || index >= this.items.length) { + return; + } + if (this.items[index].size === size) { return; } @@ -357,7 +411,6 @@ export class ListView implements ISpliceable, IDisposable { if (this.supportDynamicHeights) { this._rerender(this.lastRenderTop, this.lastRenderHeight); } - return; } splice(start: number, deleteCount: number, elements: T[] = []): T[] { @@ -467,6 +520,7 @@ export class ListView implements ISpliceable, IDisposable { private eventuallyUpdateScrollWidth(): void { if (!this.horizontalScrolling) { + this.scrollableElementWidthDelayer.cancel(); return; } @@ -478,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) { @@ -491,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 { @@ -651,7 +701,7 @@ export class ListView implements ISpliceable, IDisposable { if (!item.row) { item.row = this.cache.alloc(item.templateId); - const role = this.accessibilityProvider.getRole(item.element); + const role = this.accessibilityProvider.getRole(item.element) || 'listitem'; item.row!.domNode!.setAttribute('role', role); const checked = this.accessibilityProvider.isChecked(item.element); if (typeof checked !== 'undefined') { @@ -739,8 +789,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!); diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index b271eea461b..d7e466466bd 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ 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'; @@ -17,7 +16,7 @@ import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardE 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, ListError, IKeyboardNavigationDelegate } from './list'; -import { ListView, IListViewOptions, IListViewDragAndDrop, IListViewAccessibilityProvider } from './listView'; +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'; @@ -27,6 +26,7 @@ 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'; interface ITraitChangeEvent { indexes: number[]; @@ -225,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 (DOM.hasClass(e, 'monaco-editor')) { + return true; + } + + if (DOM.hasClass(e, '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, @@ -241,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)); @@ -263,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 { @@ -528,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); @@ -553,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) { @@ -564,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 { @@ -590,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; } @@ -633,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; } @@ -651,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 { @@ -695,10 +698,6 @@ export interface IMultipleSelectionController { isSelectionRangeChangeEvent(event: IListMouseEvent | IListTouchEvent): boolean; } -export interface IOpenController { - shouldOpen(event: UIEvent): boolean; -} - export interface IStyleController { style(styles: IListStyles): void; } @@ -842,7 +841,6 @@ export interface IListOptions { readonly keyboardSupport?: boolean; readonly multipleSelectionSupport?: boolean; readonly multipleSelectionController?: IMultipleSelectionController; - readonly openController?: IOpenController; readonly styleController?: (suffix: string) => IStyleController; readonly accessibilityProvider?: IListAccessibilityProvider; @@ -856,6 +854,7 @@ export interface IListOptions { readonly horizontalScrolling?: boolean; readonly additionalScrollHeight?: number; readonly transformOptimization?: boolean; + readonly smoothScrolling?: boolean; } export interface IListStyles { @@ -1108,13 +1107,12 @@ class ListViewDragAndDrop implements IListViewDragAndDrop { } } -export interface IListOptionsUpdate { +export interface IListOptionsUpdate extends IListViewOptionsUpdate { readonly enableKeyboardNavigation?: boolean; readonly automaticKeyboardNavigation?: boolean; - readonly additionalScrollHeight?: number; } -export class List implements ISpliceable, IDisposable { +export class List implements ISpliceable, IThemable, IDisposable { private focus: Trait; private selection: Trait; @@ -1124,6 +1122,7 @@ export class List implements ISpliceable, IDisposable { private styleController: IStyleController; private typeLabelController?: TypeLabelController; private accessibilityProvider?: IListAccessibilityProvider; + private mouseController: MouseController; private _ariaLabel: string = ''; protected readonly disposables = new DisposableStore(); @@ -1136,17 +1135,12 @@ export class List implements ISpliceable, IDisposable { 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; } @@ -1265,7 +1259,8 @@ 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.onDidChangeFocus(this._onFocusChange, this, this.disposables); this.onDidChangeSelection(this._onSelectionChange, this, this.disposables); @@ -1289,9 +1284,7 @@ export class List implements ISpliceable, IDisposable { this.typeLabelController.updateOptions(this._options); } - if (optionsUpdate.additionalScrollHeight !== undefined) { - this.view.updateOptions(optionsUpdate); - } + this.view.updateOptions(optionsUpdate); } get options(): IListOptions { @@ -1380,7 +1373,7 @@ export class List implements ISpliceable, IDisposable { set ariaLabel(value: string) { this._ariaLabel = value; - this.view.domNode.setAttribute('aria-label', localize('aria list', "{0}. Use the navigation keys to navigate.", value)); + this.view.domNode.setAttribute('aria-label', value); } domFocus(): void { @@ -1629,26 +1622,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); } @@ -1691,8 +1664,6 @@ export class List implements ISpliceable, IDisposable { this._onDidDispose.fire(); this.disposables.dispose(); - this._onDidOpen.dispose(); - this._onDidPin.dispose(); this._onDidDispose.dispose(); } } 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 140168678a2..00000000000 --- a/src/vs/base/browser/ui/menu/menu.css +++ /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. - *--------------------------------------------------------------------------------------------*/ - -.monaco-menu .monaco-action-bar.vertical { - margin-left: 0; - overflow: visible; -} - -.monaco-menu .monaco-action-bar.vertical .actions-container { - display: block; -} - -.monaco-menu .monaco-action-bar.vertical .action-item { - padding: 0; - transform: none; - display: flex; -} - -.monaco-menu .monaco-action-bar.vertical .action-item.active { - transform: none; -} - -.monaco-menu .monaco-action-bar.vertical .action-menu-item { - flex: 1 1 auto; - display: flex; - height: 2em; - align-items: center; - position: relative; -} - -.monaco-menu .monaco-action-bar.vertical .action-label { - flex: 1 1 auto; - text-decoration: none; - padding: 0 1em; - background: none; - font-size: 12px; - line-height: 1; -} - -.monaco-menu .monaco-action-bar.vertical .keybinding, -.monaco-menu .monaco-action-bar.vertical .submenu-indicator { - display: inline-block; - flex: 2 1 auto; - padding: 0 1em; - text-align: right; - font-size: 12px; - line-height: 1; -} - -.monaco-menu .monaco-action-bar.vertical .submenu-indicator { - height: 100%; -} - -.monaco-menu .monaco-action-bar.vertical .submenu-indicator.codicon { - font-size: 16px !important; - display: flex; - align-items: center; -} - -.monaco-menu .monaco-action-bar.vertical .submenu-indicator.codicon::before { - margin-left: auto; - margin-right: -20px; -} - -.monaco-menu .monaco-action-bar.vertical .action-item.disabled .keybinding, -.monaco-menu .monaco-action-bar.vertical .action-item.disabled .submenu-indicator { - opacity: 0.4; -} - -.monaco-menu .monaco-action-bar.vertical .action-label:not(.separator) { - display: inline-block; - box-sizing: border-box; - margin: 0; -} - -.monaco-menu .monaco-action-bar.vertical .action-item { - position: static; - overflow: visible; -} - -.monaco-menu .monaco-action-bar.vertical .action-item .monaco-submenu { - position: absolute; -} - -.monaco-menu .monaco-action-bar.vertical .action-label.separator { - padding: 0.5em 0 0 0; - margin-bottom: 0.5em; - width: 100%; -} - -.monaco-menu .monaco-action-bar.vertical .action-label.separator.text { - padding: 0.7em 1em 0.1em 1em; - font-weight: bold; - opacity: 1; -} - -.monaco-menu .monaco-action-bar.vertical .action-label:hover { - color: inherit; -} - -.monaco-menu .monaco-action-bar.vertical .menu-item-check { - position: absolute; - visibility: hidden; - width: 1em; - height: 100%; -} - -.monaco-menu .monaco-action-bar.vertical .action-menu-item.checked .menu-item-check { - visibility: visible; - display: flex; - align-items: center; - justify-content: center; -} - -/* Context Menu */ - -.context-view.monaco-menu-container { - outline: 0; - border: none; - animation: fadeIn 0.083s linear; -} - -.context-view.monaco-menu-container :focus, -.context-view.monaco-menu-container .monaco-action-bar.vertical:focus, -.context-view.monaco-menu-container .monaco-action-bar.vertical :focus { - outline: 0; -} - -.monaco-menu .monaco-action-bar.vertical .action-item { - border: thin solid transparent; /* prevents jumping behaviour on hover or focus */ -} - - -/* High Contrast Theming */ -.hc-black .context-view.monaco-menu-container { - box-shadow: none; -} - -.hc-black .monaco-menu .monaco-action-bar.vertical .action-item.focused { - background: none; -} - -/* Menubar styles */ - -.menubar { - display: flex; - flex-shrink: 1; - box-sizing: border-box; - height: 30px; - overflow: hidden; - flex-wrap: wrap; -} - -.fullscreen .menubar:not(.compact) { - margin: 0px; - padding: 0px 5px; -} - -.menubar > .menubar-menu-button { - align-items: center; - box-sizing: border-box; - padding: 0px 8px; - cursor: default; - -webkit-app-region: no-drag; - zoom: 1; - white-space: nowrap; - outline: 0; -} - -.menubar.compact { - flex-shrink: 0; - 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/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 8612b5b1b84..18ab9d91f1c 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, clearNode } 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,9 +16,13 @@ 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; @@ -41,6 +44,8 @@ export interface IMenuOptions { enableMnemonics?: boolean; anchorAlignment?: AnchorAlignment; expandDirection?: Direction; + useEventAsContext?: boolean; + submenuIds?: Set; } export interface IMenuStyles { @@ -54,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; @@ -70,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'); @@ -95,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); @@ -202,7 +205,16 @@ 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 }); @@ -214,6 +226,20 @@ export class Menu extends ActionBar { }); } + private initializeStyleSheet(container: HTMLElement): void { + if (isInShadowDOM(container)) { + this.styleSheet = createStyleSheet(container); + this.styleSheet.innerHTML = MENU_WIDGET_CSS; + } else { + if (!Menu.globalStyleSheet) { + Menu.globalStyleSheet = createStyleSheet(); + Menu.globalStyleSheet.innerHTML = MENU_WIDGET_CSS; + } + + this.styleSheet = Menu.globalStyleSheet; + } + } + style(style: IMenuStyles): void { const container = this.getContainer(); @@ -298,7 +324,8 @@ 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 actions = Array.isArray(action.actions) ? action.actions : action.actions(); + const menuActionViewItem = new SubmenuMenuActionViewItem(action, actions, parentData, { ...options, submenuIds: new Set([...(options.submenuIds || []), action.id]) }); if (options.enableMnemonics) { const mnemonic = menuActionViewItem.getMnemonic(); @@ -316,7 +343,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) { @@ -394,12 +421,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); @@ -655,7 +711,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); } @@ -689,7 +745,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); } @@ -709,7 +765,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(); } })); @@ -745,6 +801,33 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { } } + private calculateSubmenuMenuLayout(windowDimensions: Dimension, submenu: Dimension, entry: IDomNodePagePosition, expandDirection: Direction): { top: number, left: number } { + const ret = { top: 0, left: 0 }; + + // Start with horizontal + ret.left = layout(windowDimensions.width, submenu.width, { position: expandDirection === Direction.Right ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After, offset: entry.left, size: entry.width }); + + // We don't have enough room to layout the menu fully, so we are overlapping the menu + if (ret.left >= entry.left && ret.left < entry.left + entry.width) { + if (entry.left + 10 + submenu.width <= windowDimensions.width) { + ret.left = entry.left + 10; + } + + entry.top += 10; + entry.height = 0; + } + + // Now that we have a horizontal position, try layout vertically + ret.top = layout(windowDimensions.height, submenu.height, { position: LayoutAnchorPosition.Before, offset: entry.top, size: 0 }); + + // We didn't have enough room below, but we did above, so we shift down to align the menu + if (ret.top + submenu.height === entry.top && ret.top + entry.height + submenu.height <= windowDimensions.height) { + ret.top += entry.height; + } + + return ret; + } + private createSubmenu(selectFirstItem = true): void { if (!this.element) { return; @@ -752,36 +835,40 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { if (!this.parentData.submenu) { this.updateAriaExpanded('true'); - this.submenuContainer = append(this.element, $('div.monaco-submenu')); + this.submenuContainer = document.createElement('div.monaco-submenu'); addClasses(this.submenuContainer, 'menubar-menu-items-holder', 'context-view'); // Set the top value of the menu container before construction // This allows the menu constructor to calculate the proper max height const computedStyles = getComputedStyle(this.parentData.parent.domNode); const paddingTop = parseFloat(computedStyles.paddingTop || '0') || 0; - this.submenuContainer.style.top = `${this.element.offsetTop - this.parentData.parent.scrollOffset - paddingTop}px`; + // this.submenuContainer.style.top = `${this.element.offsetTop - this.parentData.parent.scrollOffset - paddingTop}px`; + this.submenuContainer.style.zIndex = '1'; + this.submenuContainer.style.position = 'fixed'; + this.submenuContainer.style.top = '0'; + this.submenuContainer.style.left = '0'; this.parentData.submenu = new Menu(this.submenuContainer, this.submenuActions, this.submenuOptions); if (this.menuStyle) { this.parentData.submenu.style(this.menuStyle); } - const boundingRect = this.element.getBoundingClientRect(); - const childBoundingRect = this.submenuContainer.getBoundingClientRect(); + this.element.appendChild(this.submenuContainer); - 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`; - } + // layout submenu + const entryBox = this.element.getBoundingClientRect(); + const entryBoxUpdated = { + top: entryBox.top - paddingTop, + left: entryBox.left, + height: entryBox.height + 2 * paddingTop, + width: entryBox.width + }; + + 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); @@ -877,3 +964,400 @@ export function cleanMnemonic(label: string): string { return label.replace(regex, mnemonicInText ? '$2$3' : '').trim(); } + +let MENU_WIDGET_CSS: string = /* css */` +.monaco-menu { + font-size: 13px; + +} + +${formatRule(menuSelectionIcon)} +${formatRule(menuSubmenuIcon)} + +.monaco-menu .monaco-action-bar { + text-align: right; + overflow: hidden; + white-space: nowrap; +} + +.monaco-menu .monaco-action-bar .actions-container { + display: flex; + margin: 0 auto; + padding: 0; + width: 100%; + justify-content: flex-end; +} + +.monaco-menu .monaco-action-bar.vertical .actions-container { + display: inline-block; +} + +.monaco-menu .monaco-action-bar.reverse .actions-container { + flex-direction: row-reverse; +} + +.monaco-menu .monaco-action-bar .action-item { + cursor: pointer; + display: inline-block; + transition: transform 50ms ease; + position: relative; /* DO NOT REMOVE - this is the key to preventing the ghosting icon bug in Chrome 42 */ +} + +.monaco-menu .monaco-action-bar .action-item.disabled { + cursor: default; +} + +.monaco-menu .monaco-action-bar.animated .action-item.active { + transform: scale(1.272019649, 1.272019649); /* 1.272019649 = âˆšĪ† */ +} + +.monaco-menu .monaco-action-bar .action-item .icon, +.monaco-menu .monaco-action-bar .action-item .codicon { + display: inline-block; +} + +.monaco-menu .monaco-action-bar .action-item .codicon { + display: flex; + align-items: center; +} + +.monaco-menu .monaco-action-bar .action-label { + font-size: 11px; + margin-right: 4px; +} + +.monaco-menu .monaco-action-bar .action-item.disabled .action-label, +.monaco-menu .monaco-action-bar .action-item.disabled .action-label:hover { + opacity: 0.4; +} + +/* Vertical actions */ + +.monaco-menu .monaco-action-bar.vertical { + text-align: left; +} + +.monaco-menu .monaco-action-bar.vertical .action-item { + display: block; +} + +.monaco-menu .monaco-action-bar.vertical .action-label.separator { + display: block; + border-bottom: 1px solid #bbb; + padding-top: 1px; + margin-left: .8em; + margin-right: .8em; +} + +.monaco-menu .secondary-actions .monaco-action-bar .action-label { + margin-left: 6px; +} + +/* Action Items */ +.monaco-menu .monaco-action-bar .action-item.select-container { + overflow: hidden; /* somehow the dropdown overflows its container, we prevent it here to not push */ + flex: 1; + max-width: 170px; + min-width: 60px; + display: flex; + align-items: center; + justify-content: center; + margin-right: 10px; +} + +.monaco-menu .monaco-action-bar.vertical { + margin-left: 0; + overflow: visible; +} + +.monaco-menu .monaco-action-bar.vertical .actions-container { + display: block; +} + +.monaco-menu .monaco-action-bar.vertical .action-item { + padding: 0; + transform: none; + display: flex; +} + +.monaco-menu .monaco-action-bar.vertical .action-item.active { + transform: none; +} + +.monaco-menu .monaco-action-bar.vertical .action-menu-item { + flex: 1 1 auto; + display: flex; + height: 2em; + align-items: center; + position: relative; +} + +.monaco-menu .monaco-action-bar.vertical .action-label { + flex: 1 1 auto; + text-decoration: none; + padding: 0 1em; + background: none; + font-size: 12px; + line-height: 1; +} + +.monaco-menu .monaco-action-bar.vertical .keybinding, +.monaco-menu .monaco-action-bar.vertical .submenu-indicator { + display: inline-block; + flex: 2 1 auto; + padding: 0 1em; + text-align: right; + font-size: 12px; + line-height: 1; +} + +.monaco-menu .monaco-action-bar.vertical .submenu-indicator { + height: 100%; +} + +.monaco-menu .monaco-action-bar.vertical .submenu-indicator.codicon { + font-size: 16px !important; + display: flex; + align-items: center; +} + +.monaco-menu .monaco-action-bar.vertical .submenu-indicator.codicon::before { + margin-left: auto; + margin-right: -20px; +} + +.monaco-menu .monaco-action-bar.vertical .action-item.disabled .keybinding, +.monaco-menu .monaco-action-bar.vertical .action-item.disabled .submenu-indicator { + opacity: 0.4; +} + +.monaco-menu .monaco-action-bar.vertical .action-label:not(.separator) { + display: inline-block; + box-sizing: border-box; + margin: 0; +} + +.monaco-menu .monaco-action-bar.vertical .action-item { + position: static; + overflow: visible; +} + +.monaco-menu .monaco-action-bar.vertical .action-item .monaco-submenu { + position: absolute; +} + +.monaco-menu .monaco-action-bar.vertical .action-label.separator { + padding: 0.5em 0 0 0; + margin-bottom: 0.5em; + width: 100%; + height: 0px !important; + margin-left: .8em !important; + margin-right: .8em !important; +} + +.monaco-menu .monaco-action-bar.vertical .action-label.separator.text { + padding: 0.7em 1em 0.1em 1em; + font-weight: bold; + opacity: 1; +} + +.monaco-menu .monaco-action-bar.vertical .action-label:hover { + color: inherit; +} + +.monaco-menu .monaco-action-bar.vertical .menu-item-check { + position: absolute; + visibility: hidden; + width: 1em; + height: 100%; +} + +.monaco-menu .monaco-action-bar.vertical .action-menu-item.checked .menu-item-check { + visibility: visible; + display: flex; + align-items: center; + justify-content: center; +} + +/* Context Menu */ + +.context-view.monaco-menu-container { + outline: 0; + border: none; + animation: fadeIn 0.083s linear; +} + +.context-view.monaco-menu-container :focus, +.context-view.monaco-menu-container .monaco-action-bar.vertical:focus, +.context-view.monaco-menu-container .monaco-action-bar.vertical :focus { + outline: 0; +} + +.monaco-menu .monaco-action-bar.vertical .action-item { + border: thin solid transparent; /* prevents jumping behaviour on hover or focus */ +} + + +/* High Contrast Theming */ +:host-context(.hc-black) .context-view.monaco-menu-container { + box-shadow: none; +} + +:host-context(.hc-black) .monaco-menu .monaco-action-bar.vertical .action-item.focused { + background: none; +} + +/* Vertical Action Bar Styles */ + +.monaco-menu .monaco-action-bar.vertical { + padding: .5em 0; +} + +.monaco-menu .monaco-action-bar.vertical .action-menu-item { + height: 1.8em; +} + +.monaco-menu .monaco-action-bar.vertical .action-label:not(.separator), +.monaco-menu .monaco-action-bar.vertical .keybinding { + font-size: inherit; + padding: 0 2em; +} + +.monaco-menu .monaco-action-bar.vertical .menu-item-check { + font-size: inherit; + width: 2em; +} + +.monaco-menu .monaco-action-bar.vertical .action-label.separator { + font-size: inherit; + padding: 0.2em 0 0 0; + margin-bottom: 0.2em; +} + +:host-context(.linux) .monaco-menu .monaco-action-bar.vertical .action-label.separator { + margin-left: 0; + margin-right: 0; +} + +.monaco-menu .monaco-action-bar.vertical .submenu-indicator { + font-size: 60%; + padding: 0 1.8em; +} + +:host-context(.linux) .monaco-menu .monaco-action-bar.vertical .submenu-indicator { + height: 100%; + mask-size: 10px 10px; + -webkit-mask-size: 10px 10px; +} + +.monaco-menu .action-item { + cursor: default; +} + +/* Arrows */ +.monaco-scrollable-element > .scrollbar > .scra { + cursor: pointer; + font-size: 11px !important; +} + +.monaco-scrollable-element > .visible { + opacity: 1; + + /* Background rule added for IE9 - to allow clicks on dom node */ + background:rgba(0,0,0,0); + + transition: opacity 100ms linear; +} +.monaco-scrollable-element > .invisible { + opacity: 0; + pointer-events: none; +} +.monaco-scrollable-element > .invisible.fade { + transition: opacity 800ms linear; +} + +/* Scrollable Content Inset Shadow */ +.monaco-scrollable-element > .shadow { + position: absolute; + display: none; +} +.monaco-scrollable-element > .shadow.top { + display: block; + top: 0; + left: 3px; + height: 3px; + width: 100%; + box-shadow: #DDD 0 6px 6px -6px inset; +} +.monaco-scrollable-element > .shadow.left { + display: block; + top: 3px; + left: 0; + height: 100%; + width: 3px; + box-shadow: #DDD 6px 0 6px -6px inset; +} +.monaco-scrollable-element > .shadow.top-left-corner { + display: block; + top: 0; + left: 0; + height: 3px; + width: 3px; +} +.monaco-scrollable-element > .shadow.top.left { + box-shadow: #DDD 6px 6px 6px -6px inset; +} + +/* ---------- Default Style ---------- */ + +:host-context(.vs) .monaco-scrollable-element > .scrollbar > .slider { + background: rgba(100, 100, 100, .4); +} +:host-context(.vs-dark) .monaco-scrollable-element > .scrollbar > .slider { + background: rgba(121, 121, 121, .4); +} +:host-context(.hc-black) .monaco-scrollable-element > .scrollbar > .slider { + background: rgba(111, 195, 223, .6); +} + +.monaco-scrollable-element > .scrollbar > .slider:hover { + background: rgba(100, 100, 100, .7); +} +:host-context(.hc-black) .monaco-scrollable-element > .scrollbar > .slider:hover { + background: rgba(111, 195, 223, .8); +} + +.monaco-scrollable-element > .scrollbar > .slider.active { + background: rgba(0, 0, 0, .6); +} +:host-context(.vs-dark) .monaco-scrollable-element > .scrollbar > .slider.active { + background: rgba(191, 191, 191, .4); +} +:host-context(.hc-black) .monaco-scrollable-element > .scrollbar > .slider.active { + background: rgba(111, 195, 223, 1); +} + +:host-context(.vs-dark) .monaco-scrollable-element .shadow.top { + box-shadow: none; +} + +:host-context(.vs-dark) .monaco-scrollable-element .shadow.left { + box-shadow: #000 6px 0 6px -6px inset; +} + +:host-context(.vs-dark) .monaco-scrollable-element .shadow.top.left { + box-shadow: #000 6px 6px 6px -6px inset; +} + +:host-context(.hc-black) .monaco-scrollable-element .shadow.top { + box-shadow: none; +} + +:host-context(.hc-black) .monaco-scrollable-element .shadow.left { + box-shadow: none; +} + +:host-context(.hc-black) .monaco-scrollable-element .shadow.top.left { + box-shadow: none; +} +`; diff --git a/src/vs/base/browser/ui/menu/menubar.css b/src/vs/base/browser/ui/menu/menubar.css new file mode 100644 index 00000000000..d815cfeddb9 --- /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 fd720b76db7..65249946405 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,7 +22,6 @@ 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 { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { Codicon, registerIcon } from 'vs/base/common/codicons'; const $ = DOM.$; @@ -39,7 +39,7 @@ export interface IMenuBarOptions { } export interface MenuBarMenu { - actions: ReadonlyArray; + actions: IAction[]; label: string; } @@ -58,7 +58,7 @@ export class MenuBar extends Disposable { buttonElement: HTMLElement; titleElement: HTMLElement; label: string; - actions?: ReadonlyArray; + actions?: IAction[]; }[]; private overflowMenu!: { @@ -140,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(); } @@ -315,7 +315,7 @@ export class MenuBar extends Disposable { createOverflowMenu(): void { const label = this.options.compactMode !== undefined ? nls.localize('mAppMenu', 'Application Menu') : nls.localize('mMore', 'More'); const title = this.options.compactMode !== undefined ? label : undefined; - const buttonElement = $('div.menubar-menu-button', { 'role': 'menuitem', 'tabindex': -1, 'aria-label': label, 'title': title, 'aria-haspopup': true }); + const 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); @@ -326,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; @@ -446,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; @@ -487,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) { @@ -932,7 +950,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`; @@ -945,7 +962,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/mouseCursor/mouseCursor.css b/src/vs/base/browser/ui/mouseCursor/mouseCursor.css new file mode 100644 index 00000000000..2a998c4d970 --- /dev/null +++ b/src/vs/base/browser/ui/mouseCursor/mouseCursor.css @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-mouse-cursor-text { + cursor: text; +} + +/* The following selector looks a bit funny, but that is needed to cover all the workbench and the editor!! */ +.vs-dark .mac .monaco-mouse-cursor-text, .hc-black .mac .monaco-mouse-cursor-text, +.vs-dark.mac .monaco-mouse-cursor-text, .hc-black.mac .monaco-mouse-cursor-text { + cursor: -webkit-image-set(url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAL0lEQVQoz2NgCD3x//9/BhBYBWdhgFVAiVW4JBFKGIa4AqD0//9D3pt4I4tAdAMAHTQ/j5Zom30AAAAASUVORK5CYII=') 1x, url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAz0lEQVRIx2NgYGBY/R8I/vx5eelX3n82IJ9FxGf6tksvf/8FiTMQAcAGQMDvSwu09abffY8QYSAScNk45G198eX//yev73/4///701eh//kZSARckrNBRvz//+8+6ZohwCzjGNjdgQxkAg7B9WADeBjIBqtJCbhRA0YNoIkBSNmaPEMoNmA0FkYNoFKhapJ6FGyAH3nauaSmPfwI0v/3OukVi0CIZ+F25KrtYcx/CTIy0e+rC7R1Z4KMICVTQQ14feVXIbR695u14+Ir4gwAAD49E54wc1kWAAAAAElFTkSuQmCC') 2x) 5 8, text; +} diff --git a/src/vs/base/browser/ui/mouseCursor/mouseCursor.ts b/src/vs/base/browser/ui/mouseCursor/mouseCursor.ts new file mode 100644 index 00000000000..8e5fd174cd8 --- /dev/null +++ b/src/vs/base/browser/ui/mouseCursor/mouseCursor.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./mouseCursor'; + +export const MOUSE_CURSOR_TEXT_CSS_CLASS_NAME = `monaco-mouse-cursor-text`; diff --git a/src/vs/base/browser/ui/sash/sash.css b/src/vs/base/browser/ui/sash/sash.css index 27be21169e7..e8ccd5521d2 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 7c2a52a89bf..7941babc3f4 100644 --- a/src/vs/base/browser/ui/sash/sash.ts +++ b/src/vs/base/browser/ui/sash/sash.ts @@ -5,7 +5,6 @@ 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'; @@ -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; } @@ -127,7 +143,9 @@ 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')); @@ -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,11 +182,7 @@ 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'); @@ -169,9 +192,9 @@ export class Sash extends Disposable { addClass(this.el, 'vertical'); } - if (this.layoutProvider) { - this.layout(); - } + toggleClass(this.el, 'debug', DEBUG); + + this.layout(); } private onMouseDown(e: MouseEvent): void { @@ -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'; @@ -384,15 +405,15 @@ export class Sash extends Disposable { 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/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index da2f2b6cee8..00bb9830d54 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -17,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; @@ -130,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 { @@ -343,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}`); @@ -533,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 { diff --git a/src/vs/base/browser/ui/selectBox/selectBox.css b/src/vs/base/browser/ui/selectBox/selectBox.css index f684dd1085c..2997fd77e02 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 cff14f50206..79f0da0e6cd 100644 --- a/src/vs/base/browser/ui/selectBox/selectBox.ts +++ b/src/vs/base/browser/ui/selectBox/selectBox.ts @@ -40,6 +40,7 @@ export interface ISelectBoxOptions { useCustomDrawn?: boolean; ariaLabel?: string; minBottomMargin?: number; + optionsAsChildren?: boolean; } // Utilize optionItem interface to capture all option parameters diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.css b/src/vs/base/browser/ui/selectBox/selectBoxCustom.css index b717cde73f6..536a9422359 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.css +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.css @@ -30,7 +30,7 @@ .monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown code { line-height: 15px; /** For some reason, this is needed, otherwise will take up 20px height */ - font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; + font-family: var(--monaco-monospace-font); } diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 4924646f11c..a8f9bb24c5a 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -89,6 +89,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi private _isVisible: boolean; private selectBoxOptions: ISelectBoxOptions; private selectElement: HTMLSelectElement; + private container?: HTMLElement; private options: ISelectOptionItem[] = []; private selected: number; private readonly _onDidSelect: Emitter; @@ -307,6 +308,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi } public render(container: HTMLElement): void { + this.container = container; dom.addClass(container, 'select-container'); container.appendChild(this.selectElement); this.applyStyles(); @@ -442,7 +444,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi dom.toggleClass(this.selectElement, 'synthetic-focus', false); }, anchorPosition: this._dropDownPosition - }); + }, this.selectBoxOptions.optionsAsChildren ? this.container : undefined); // Hide so we can relay out this._isVisible = true; @@ -457,11 +459,12 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi dom.toggleClass(this.selectElement, 'synthetic-focus', false); }, anchorPosition: this._dropDownPosition - }); + }, this.selectBoxOptions.optionsAsChildren ? this.container : undefined); // Track initial selection the case user escape, blur this._currentSelection = this.selected; this._isVisible = true; + this.selectElement.setAttribute('aria-expanded', 'true'); } private hideSelectDropDown(focusSelect: boolean) { @@ -470,6 +473,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi } this._isVisible = false; + this.selectElement.setAttribute('aria-expanded', 'false'); if (focusSelect) { this.selectElement.focus(); @@ -727,7 +731,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi mouseSupport: false, accessibilityProvider: { getAriaLabel: (element) => element.text, - getWidgetAriaLabel: () => localize('selectBox', "Select Box"), + getWidgetAriaLabel: () => localize({ key: 'selectBox', comment: ['Behave like native select dropdown element.'] }, "Select Box"), getRole: () => 'option', getWidgetRole: () => 'listbox' } diff --git a/src/vs/base/browser/ui/splitview/paneview.css b/src/vs/base/browser/ui/splitview/paneview.css index 679bf211e94..ec0370aab36 100644 --- a/src/vs/base/browser/ui/splitview/paneview.css +++ b/src/vs/base/browser/ui/splitview/paneview.css @@ -16,7 +16,12 @@ flex-direction: column; } +.monaco-pane-view .pane.horizontal:not(.expanded) { + flex-direction: row; +} + .monaco-pane-view .pane > .pane-header { + height: 22px; font-size: 11px; font-weight: bold; text-transform: uppercase; @@ -24,6 +29,13 @@ display: flex; cursor: pointer; align-items: center; + box-sizing: border-box; +} + +.monaco-pane-view .pane.horizontal:not(.expanded) > .pane-header { + flex-direction: column; + height: 100%; + width: 22px; } .monaco-pane-view .pane > .pane-header > .twisties { @@ -36,6 +48,11 @@ flex-shrink: 0; } +.monaco-pane-view .pane.horizontal:not(.expanded) > .pane-header > .twisties { + margin-top: 2px; + margin-bottom: 2px; +} + .monaco-pane-view .pane > .pane-header.expanded > .twisties::before { transform: rotate(90deg); } @@ -132,6 +149,7 @@ 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; diff --git a/src/vs/base/browser/ui/splitview/paneview.ts b/src/vs/base/browser/ui/splitview/paneview.ts index e4c62b298b9..292764c8807 100644 --- a/src/vs/base/browser/ui/splitview/paneview.ts +++ b/src/vs/base/browser/ui/splitview/paneview.ts @@ -106,7 +106,7 @@ export abstract class Pane extends Disposable implements IView { get minimumSize(): number { const headerSize = this.headerSize; const expanded = !this.headerVisible || this.isExpanded(); - const minimumBodySize = expanded ? this.minimumBodySize : this._orientation === Orientation.HORIZONTAL ? 50 : 0; + const minimumBodySize = expanded ? this.minimumBodySize : 0; return headerSize + minimumBodySize; } @@ -114,7 +114,7 @@ export abstract class Pane extends Disposable implements IView { get maximumSize(): number { const headerSize = this.headerSize; const expanded = !this.headerVisible || this.isExpanded(); - const maximumBodySize = expanded ? this.maximumBodySize : this._orientation === Orientation.HORIZONTAL ? 50 : 0; + const maximumBodySize = expanded ? this.maximumBodySize : 0; return headerSize + maximumBodySize; } @@ -126,7 +126,7 @@ export abstract class Pane extends Disposable implements IView { 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 : 120; + 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'); @@ -141,6 +141,10 @@ export abstract class Pane extends Disposable implements IView { return false; } + if (this.element) { + toggleClass(this.element, 'expanded', expanded); + } + this._expanded = !!expanded; this.updateHeader(); @@ -185,12 +189,21 @@ export abstract class Pane extends Disposable implements IView { this._orientation = orientation; + if (this.element) { + toggleClass(this.element, 'horizontal', this.orientation === Orientation.HORIZONTAL); + toggleClass(this.element, 'vertical', this.orientation === Orientation.VERTICAL); + } + if (this.header) { this.updateHeader(); } } render(): void { + toggleClass(this.element, 'expanded', this.isExpanded()); + toggleClass(this.element, 'horizontal', this.orientation === Orientation.HORIZONTAL); + toggleClass(this.element, 'vertical', this.orientation === Orientation.VERTICAL); + this.header = $('.pane-header'); append(this.element, this.header); this.header.setAttribute('tabindex', '0'); @@ -250,8 +263,6 @@ export abstract class Pane extends Disposable implements IView { style(styles: IPaneStyles): void { this.styles = styles; - this.element.style.borderLeft = this.styles.leftBorder && this.orientation === Orientation.HORIZONTAL ? `1px solid ${this.styles.leftBorder}` : ''; - if (!this.header) { return; } @@ -262,7 +273,6 @@ export abstract class Pane extends Disposable implements IView { protected updateHeader(): void { const expanded = !this.headerVisible || this.isExpanded(); - this.header.style.height = `${this.headerSize}px`; this.header.style.lineHeight = `${this.headerSize}px`; toggleClass(this.header, 'hidden', !this.headerVisible); toggleClass(this.header, 'expanded', expanded); @@ -272,6 +282,7 @@ export abstract class Pane extends Disposable implements IView { 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; diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 028e4f839f6..482a3a3d51e 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -331,8 +331,8 @@ export class SplitView extends Disposable { } } - 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 { @@ -689,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 }) diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 2ce6e5599dc..4e41977fb17 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -5,16 +5,16 @@ 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'; - -export const CONTEXT = 'context.toolbar'; +import { EventMultiplexer } from 'vs/base/common/event'; +import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; +import { IContextMenuProvider } from 'vs/base/browser/contextmenu'; const toolBarMoreIcon = registerIcon('toolbar-more', Codicon.more); @@ -35,17 +35,22 @@ 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 _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(); 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'; @@ -56,27 +61,57 @@ export class ToolBar extends Disposable { ariaLabel: options.ariaLabel, actionRunner: options.actionRunner, actionViewItemProvider: (action: IAction) => { - - // Return special action item for the toggle menu action if (action.id === ToggleMenuAction.ID) { - - // Create new - this.toggleMenuActionViewItem.value = new DropdownMenuActionViewItem( + this.toggleMenuActionViewItem = new DropdownMenuActionViewItem( action, (action).menuActions, contextMenuProvider, - this.options.actionViewItemProvider, - this.actionRunner, - this.options.getKeyBinding, - toolBarMoreIcon.classNames, - this.options.anchorAlignmentProvider + { + actionViewItemProvider: this.options.actionViewItemProvider, + actionRunner: this.actionRunner, + keybindingProvider: this.options.getKeyBinding, + classNames: toolBarMoreIcon.classNames, + anchorAlignmentProvider: this.options.anchorAlignmentProvider, + menuAsChild: true + } ); - this.toggleMenuActionViewItem.value.setActionContext(this.actionBar.context); + this.toggleMenuActionViewItem.setActionContext(this.actionBar.context); + this.disposables.add(this._onDidChangeDropdownVisibility.add(this.toggleMenuActionViewItem.onDidChangeVisibility)); - return this.toggleMenuActionViewItem.value; + return this.toggleMenuActionViewItem; } - return options.actionViewItemProvider ? options.actionViewItemProvider(action) : undefined; + if (options.actionViewItemProvider) { + const result = options.actionViewItemProvider(action); + + if (result) { + return result; + } + } + + if (action instanceof SubmenuAction) { + const actions = Array.isArray(action.actions) ? action.actions : action.actions(); + const result = new DropdownMenuActionViewItem( + action, + actions, + contextMenuProvider, + { + actionViewItemProvider: this.options.actionViewItemProvider, + actionRunner: this.actionRunner, + keybindingProvider: this.options.getKeyBinding, + classNames: action.class, + anchorAlignmentProvider: this.options.anchorAlignmentProvider, + menuAsChild: true + } + ); + result.setActionContext(this.actionBar.context); + this.submenuActionViewItems.push(result); + this.disposables.add(this._onDidChangeDropdownVisibility.add(result.onDidChangeVisibility)); + + return result; + } + + return undefined; } })); } @@ -91,8 +126,11 @@ export class ToolBar extends Disposable { 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); } } @@ -112,23 +150,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 { @@ -137,20 +173,15 @@ export class ToolBar extends Disposable { return withNullAsUndefined(key?.getLabel()); } - addPrimaryAction(primaryAction: IAction): () => void { - return () => { + private clear(): void { + this.submenuActionViewItems = []; + this.disposables.clear(); + this.actionBar.clear(); + } - // Add after the "..." action if we have secondary actions - if (this.hasSecondaryActions) { - let itemCount = this.actionBar.length(); - this.actionBar.push(primaryAction, { icon: true, label: false, index: itemCount, keybinding: this.getKeybindingLabel(primaryAction) }); - } - - // Otherwise just add to the end - else { - this.actionBar.push(primaryAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(primaryAction) }); - } - }; + dispose(): void { + this.clear(); + super.dispose(); } } diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index a71e4d6ae48..be82f6156e2 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -5,8 +5,8 @@ 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 { 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, $, toggleClass, getDomNodePagePosition, removeClass, addClass, hasClass, hasParentWithClass, createStyleSheet, clearNode, addClasses, removeClasses } from 'vs/base/browser/dom'; import { Event, Relay, Emitter, EventBufferer } from 'vs/base/common/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -22,7 +22,6 @@ 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'; @@ -707,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; @@ -918,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; @@ -961,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 { @@ -1041,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; } @@ -1090,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 = hasClass(target, 'monaco-tl-twistie') + || (hasClass(target, 'monaco-icon-label') && hasClass(target, 'folder-icon') && e.browserEvent.offsetX < 16); let expandOnlyOnTwistieClick = false; @@ -1120,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) { @@ -1134,7 +1126,7 @@ class TreeNodeListMouseController extends MouseController< } } - super.onPointer(e); + super.onViewPointer(e); } protected onDoubleClick(e: IListMouseEvent>): void { @@ -1237,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; } @@ -1260,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>(); @@ -1327,7 +1319,7 @@ export abstract class AbstractTree implements IDisposable set.add(node); } - return values(set); + return [...set.values()]; }).event; if (_options.keyboardSupport !== false) { @@ -1360,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) { @@ -1599,11 +1593,6 @@ export abstract class AbstractTree implements IDisposable return this.focus.get(); } - open(elements: TRef[], browserEvent?: UIEvent): void { - const indexes = elements.map(e => this.model.getListIndex(e)).filter(i => i >= 0); - 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 1e9600e2bff..8d82bf60705 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -16,7 +16,6 @@ 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 { removeClasses, addClasses } 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'; @@ -331,12 +330,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; } @@ -345,7 +345,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; @@ -408,6 +407,10 @@ export class AsyncDataTree implements IDisposable this.tree.updateOptions(options); } + get options(): IAsyncDataTreeOptions { + return this.tree.options as IAsyncDataTreeOptions; + } + // Widget getHTMLElement(): HTMLElement { @@ -668,11 +671,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); } @@ -904,7 +902,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)); } diff --git a/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts b/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts index d3a0f95131b..150f49ce53f 100644 --- a/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ISpliceable } from 'vs/base/common/sequence'; 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 { @@ -126,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); @@ -290,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(); } @@ -340,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); } }; } @@ -402,7 +415,7 @@ export class CompressibleObjectTreeModel, TFilterData constructor( user: string, - list: ISpliceable>, + list: IList>, options: ICompressibleObjectTreeModelOptions = {} ) { this.elementMapper = options.elementMapper || DefaultElementMapper; @@ -492,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 cf7e1110e95..4e97f4ed9b6 100644 --- a/src/vs/base/browser/ui/tree/dataTree.ts +++ b/src/vs/base/browser/ui/tree/dataTree.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree'; -import { ISpliceable } from 'vs/base/common/sequence'; import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter, IDataSource, TreeError } from 'vs/base/browser/ui/tree/tree'; import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; import { Iterable } from 'vs/base/common/iterator'; +import { IList } from 'vs/base/browser/ui/tree/indexTreeModel'; export interface IDataTreeOptions extends IAbstractTreeOptions { readonly sorter?: ITreeSorter; @@ -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); } diff --git a/src/vs/base/browser/ui/tree/indexTree.ts b/src/vs/base/browser/ui/tree/indexTree.ts index d4be627730e..5272fa1971a 100644 --- a/src/vs/base/browser/ui/tree/indexTree.ts +++ b/src/vs/base/browser/ui/tree/indexTree.ts @@ -6,8 +6,7 @@ import 'vs/css!./media/tree'; 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'; @@ -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 548429eda7a..362ef7e1a30 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -56,6 +56,10 @@ function isCollapsibleStateUpdate(update: CollapseStateUpdate): update is Collap return typeof (update as any).collapsible === 'boolean'; } +export interface IList extends ISpliceable { + updateElementHeight(index: number, height: number): void; +} + export class IndexTreeModel, TFilterData = void> implements ITreeModel { readonly rootRef = []; @@ -78,7 +82,7 @@ export class IndexTreeModel, TFilterData = voi constructor( private user: string, - private list: ISpliceable>, + private list: IList>, rootElement: T, options: IIndexTreeModelOptions = {} ) { @@ -212,6 +216,15 @@ export class IndexTreeModel, TFilterData = voi } } + 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); } diff --git a/src/vs/base/browser/ui/tree/objectTree.ts b/src/vs/base/browser/ui/tree/objectTree.ts index 38647079853..60601a51775 100644 --- a/src/vs/base/browser/ui/tree/objectTree.ts +++ b/src/vs/base/browser/ui/tree/objectTree.ts @@ -5,13 +5,13 @@ 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; @@ -46,6 +46,10 @@ 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); } @@ -54,7 +58,7 @@ export class ObjectTree, TFilterData = void> extends return this.model.has(element); } - protected createModel(user: string, view: ISpliceable>, options: IObjectTreeOptions): ITreeModel { + protected createModel(user: string, view: IList>, options: IObjectTreeOptions): ITreeModel { return new ObjectTreeModel(user, view, options); } } @@ -188,7 +192,7 @@ export class CompressibleObjectTree, TFilterData = vo 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 12c839ab10c..59afa8d4b98 100644 --- a/src/vs/base/browser/ui/tree/objectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/objectTreeModel.ts @@ -3,9 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ISpliceable } from 'vs/base/common/sequence'; import { Iterable } from 'vs/base/common/iterator'; -import { IndexTreeModel, IIndexTreeModelOptions } from 'vs/base/browser/ui/tree/indexTreeModel'; +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'; @@ -16,6 +15,7 @@ export type ITreeNodeCallback = (node: ITreeNode export interface IObjectTreeModel, TFilterData extends NonNullable = void> extends ITreeModel { 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); @@ -169,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; diff --git a/src/vs/base/common/actions.ts b/src/vs/base/common/actions.ts index 135008aa81d..a3b396524eb 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,22 @@ 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 { + constructor(id: string, label: string, readonly actions: SubmenuActions, cssClass?: string) { + super(id, label, cssClass, true); + } +} diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index a037cb95c67..cf9786a2076 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -473,11 +473,10 @@ export function range(arg: number, to?: number): number[] { } 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)); } @@ -489,12 +488,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; } /** @@ -582,6 +590,14 @@ export function asArray(x: T | T[]): T[] { return Array.isArray(x) ? x : [x]; } +export function toArray(iterable: IterableIterator): T[] { + const result: T[] = []; + for (let element of iterable) { + result.push(element); + } + return result; +} + export function getRandomElement(arr: T[]): T | undefined { return arr[Math.floor(Math.random() * arr.length)]; } diff --git a/src/vs/base/common/buffer.ts b/src/vs/base/common/buffer.ts index f5408cded9f..6c6285e0c3b 100644 --- a/src/vs/base/common/buffer.ts +++ b/src/vs/base/common/buffer.ts @@ -194,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)); } @@ -206,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)); } @@ -214,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/charCode.ts b/src/vs/base/common/charCode.ts index 3b8c6426710..23b840b2942 100644 --- a/src/vs/base/common/charCode.ts +++ b/src/vs/base/common/charCode.ts @@ -342,7 +342,17 @@ export const enum CharCode { * Unicode Character 'LINE SEPARATOR' (U+2028) * http://www.fileformat.info/info/unicode/char/2028/index.htm */ - LINE_SEPARATOR_2028 = 8232, + LINE_SEPARATOR = 0x2028, + /** + * Unicode Character 'PARAGRAPH SEPARATOR' (U+2029) + * http://www.fileformat.info/info/unicode/char/2029/index.htm + */ + PARAGRAPH_SEPARATOR = 0x2029, + /** + * Unicode Character 'NEXT LINE' (U+0085) + * http://www.fileformat.info/info/unicode/char/0085/index.htm + */ + NEXT_LINE = 0x0085, // http://www.fileformat.info/info/unicode/category/Sk/list.htm U_CIRCUMFLEX = 0x005E, // U+005E CIRCUMFLEX @@ -422,4 +432,4 @@ export const enum CharCode { * http://www.fileformat.info/info/unicode/char/feff/index.htm */ UTF8_BOM = 65279 -} \ No newline at end of file +} diff --git a/src/vs/base/common/codicons.ts b/src/vs/base/common/codicons.ts index 6ce447caebb..36696e4a455 100644 --- a/src/vs/base/common/codicons.ts +++ b/src/vs/base/common/codicons.ts @@ -52,6 +52,8 @@ export class Codicon { _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; } } @@ -244,7 +246,7 @@ export namespace Codicon { 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: '\\eac8' }); + 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' }); @@ -448,7 +450,6 @@ export namespace Codicon { 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 debugAlternate = new Codicon('debug-alternate', { character: '\\eb91' }); 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' }); @@ -465,8 +466,17 @@ export namespace Codicon { 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 debugAlt2 = new Codicon('debug-alt-2', { character: '\\f101' }); - export const debugAlt = new Codicon('debug-alt', { character: '\\f102' }); + 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' }); } @@ -491,6 +501,7 @@ export function markdownUnescapeCodicons(text: string): string { const renderCodiconsRegex = /(\\)?\$\((([a-z0-9\-]+?)(?:~([a-z0-9\-]*?))?)\)/gi; export function renderCodicons(text: string): string { return text.replace(renderCodiconsRegex, (_, escaped, codicon, name, animation) => { + // If the class for codicons is changed, it should also be updated in src\vs\base\browser\markdownRenderer.ts return escaped ? `$(${codicon})` : ``; diff --git a/src/vs/base/common/collections.ts b/src/vs/base/common/collections.ts index aac7c67e807..8729b7303ae 100644 --- a/src/vs/base/common/collections.ts +++ b/src/vs/base/common/collections.ts @@ -32,25 +32,6 @@ 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 dictionary. The iterator allows * to remove elements and will stop when the callback returns {{false}}. diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index b2440204634..f6d4f5cf893 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -203,3 +203,12 @@ export class NotImplementedError extends Error { } } } + +export class NotSupportedError extends Error { + constructor(message?: string) { + super('NotSupported'); + if (message) { + this.message = message; + } + } +} diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index 032f447081e..f70aa9d8d2f 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -328,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 { @@ -440,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!); diff --git a/src/vs/base/common/extpath.ts b/src/vs/base/common/extpath.ts index e064829c5a0..d6e7d6ef5d7 100644 --- a/src/vs/base/common/extpath.ts +++ b/src/vs/base/common/extpath.ts @@ -7,6 +7,7 @@ import { isWindows } from 'vs/base/common/platform'; 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; @@ -284,7 +285,7 @@ export function isRootOrDriveLetter(path: string): boolean { return pathNormalized === posix.sep; } -export function indexOfPath(path: string, candidate: string, ignoreCase: boolean): number { +export function indexOfPath(path: string, candidate: string, ignoreCase?: boolean): number { if (candidate.length > path.length) { return -1; } @@ -300,3 +301,38 @@ export function indexOfPath(path: string, candidate: string, ignoreCase: boolean 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/fuzzyScorer.ts b/src/vs/base/common/fuzzyScorer.ts index 8599e947205..9a4f7bc51bc 100644 --- a/src/vs/base/common/fuzzyScorer.ts +++ b/src/vs/base/common/fuzzyScorer.ts @@ -42,8 +42,7 @@ export function scoreFuzzy(target: string, query: string, queryLower: string, fu // When not searching fuzzy, we require the query to be contained fully // in the target string contiguously. if (!fuzzy) { - const indexOfQueryInTarget = targetLower.indexOf(queryLower); - if (indexOfQueryInTarget === -1) { + if (!targetLower.includes(queryLower)) { // if (DEBUG) { // console.log(`Characters not matching consecutively ${queryLower} within ${targetLower}`); // } @@ -833,7 +832,7 @@ export interface IPreparedQuery extends IPreparedQueryPiece { values: IPreparedQueryPiece[] | undefined; /** - * Wether the query contains path separator(s) or not. + * Whether the query contains path separator(s) or not. */ containsPathSeparator: boolean; } diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts index be074865f2b..229ae0cffbf 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -6,6 +6,7 @@ import { equals } from 'vs/base/common/arrays'; import { UriComponents } from 'vs/base/common/uri'; import { escapeCodicons } from 'vs/base/common/codicons'; +import { illegalArgument } from 'vs/base/common/errors'; export interface IMarkdownString { readonly value: string; @@ -22,6 +23,10 @@ export class MarkdownString implements IMarkdownString { private _value: string = '', isTrustedOrOptions: boolean | { isTrusted?: boolean, supportThemeIcons?: boolean } = false, ) { + if (typeof this._value !== 'string') { + throw illegalArgument('value'); + } + if (typeof isTrustedOrOptions === 'boolean') { this._isTrusted = isTrustedOrOptions; this._supportThemeIcons = false; @@ -30,7 +35,6 @@ export class MarkdownString implements IMarkdownString { this._isTrusted = isTrustedOrOptions.isTrusted ?? false; this._supportThemeIcons = isTrustedOrOptions.supportThemeIcons ?? false; } - } get value() { return this._value; } diff --git a/src/vs/base/common/insane/insane.d.ts b/src/vs/base/common/insane/insane.d.ts index 9b5a77c3b8e..13fa1f2662b 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 c88acad9e23..d66882ac09f 100644 --- a/src/vs/base/common/iterator.ts +++ b/src/vs/base/common/iterator.ts @@ -5,6 +5,10 @@ export namespace Iterable { + export function is(thing: any): thing is IterableIterator { + return thing && typeof thing === 'object' && typeof thing[Symbol.iterator] === 'function'; + } + const _empty: Iterable = Object.freeze([]); export function empty(): Iterable { return _empty; diff --git a/src/vs/base/common/json.ts b/src/vs/base/common/json.ts index df362c06589..933e370f92c 100644 --- a/src/vs/base/common/json.ts +++ b/src/vs/base/common/json.ts @@ -72,6 +72,7 @@ export interface JSONScanner { } + export interface ParseError { error: ParseErrorCode; offset: number; diff --git a/src/vs/base/common/keybindingLabels.ts b/src/vs/base/common/keybindingLabels.ts index 671816830bd..1cdbc8d8e9e 100644 --- a/src/vs/base/common/keybindingLabels.ts +++ b/src/vs/base/common/keybindingLabels.ts @@ -182,7 +182,9 @@ function _simpleAsString(modifiers: Modifiers, key: string, labels: ModifierLabe } // the actual key - result.push(key); + if (key !== '') { + result.push(key); + } return result.join(labels.separator); } diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index 7394eab4ce0..406a9c801d4 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. @@ -54,26 +55,26 @@ export function isDisposable(thing: E): thing is E & IDisposab 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)) { + for (let d of arg) { if (d) { markTracked(d); d.dispose(); } - }); - return []; - } else if (disposables) { - markTracked(disposables); - disposables.dispose(); - return disposables; - } else { - return undefined; + } + 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) }); @@ -218,11 +219,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); } @@ -239,7 +240,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/map.ts b/src/vs/base/common/map.ts index 8df6ef68bea..0b8c7460f79 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -9,27 +9,6 @@ import { compareSubstringIgnoreCase, compare, compareSubstring } from 'vs/base/c import { Schemas } from 'vs/base/common/network'; import { isLinux } from 'vs/base/common/platform'; -/** - * @deprecated ES6: use `[...SetOrMap.values()]` - */ -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; -} - -/** - * @deprecated ES6: use `[...map.keys()]` - */ -export function keys(map: Map): K[] { - const result: K[] = []; - map.forEach((_value, key) => result.push(key)); - - return result; -} - export function getOrSet(map: Map, key: K, value: V): V { let result = map.get(key); if (result === undefined) { @@ -206,7 +185,7 @@ export class UriIterator implements IKeyIterator { cmp(a: string): number { if (this._states[this._stateIdx] === UriIteratorState.Scheme) { - return compareSubstringIgnoreCase(a, this._value.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) { @@ -481,16 +460,40 @@ export class TernarySearchTree { } } +interface ResourceMapKeyFn { + (resource: URI): string; +} + export class ResourceMap implements Map { + private static readonly defaultToKey = (resource: URI) => resource.toString(); + readonly [Symbol.toStringTag] = 'ResourceMap'; - protected readonly map: Map; - protected readonly ignoreCase?: boolean; + private readonly map: Map; + private readonly toKey: ResourceMapKeyFn; - constructor() { - this.map = new Map(); - this.ignoreCase = false; // in the future this should be an uri-comparator + /** + * + * @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): this { @@ -548,23 +551,6 @@ export class ResourceMap implements Map { yield [URI.parse(item[0]), item[1]]; } } - - private toKey(resource: URI): string { - let key = resource.toString(); - if (this.ignoreCase) { - key = key.toLowerCase(); - } - - return key; - } - - clone(): ResourceMap { - const resourceMap = new ResourceMap(); - - this.map.forEach((value, key) => resourceMap.map.set(key, value)); - - return resourceMap; - } } interface Item { diff --git a/src/vs/base/common/marshalling.ts b/src/vs/base/common/marshalling.ts index 335ee5691e0..e76ba91738f 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 } from 'vs/base/common/uri'; export function stringify(obj: any): string { return JSON.stringify(obj, replacer); @@ -44,10 +45,23 @@ export function revive(obj: any, depth = 0): any { 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 0bf9ce1cc6b..73c8e05549d 100644 --- a/src/vs/base/common/mime.ts +++ b/src/vs/base/common/mime.ts @@ -5,7 +5,6 @@ import { basename, posix, extname } from 'vs/base/common/path'; import { startsWithUTF8BOM } from 'vs/base/common/strings'; -import { coalesce } from 'vs/base/common/arrays'; import { match } from 'vs/base/common/glob'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; @@ -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 => assoc.startsWith('.')); - - if (extensionsWithDotFirst.length > 0) { - const candidateExtension = extensionsWithDotFirst[0]; - if (prefix.endsWith(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/network.ts b/src/vs/base/common/network.ts index e4546b2cf60..1286c5117a4 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -56,9 +56,26 @@ export namespace Schemas { export const vscodeCustomEditor = 'vscode-custom-editor'; + export const vscodeNotebook = 'vscode-notebook'; + 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/performance.d.ts b/src/vs/base/common/performance.d.ts index 657f250d8b3..a26c1ebd731 100644 --- a/src/vs/base/common/performance.d.ts +++ b/src/vs/base/common/performance.d.ts @@ -5,7 +5,7 @@ export interface PerformanceEntry { readonly name: string; - readonly timestamp: number; + readonly startTime: number; } export function mark(name: string): void; @@ -15,8 +15,6 @@ export function mark(name: string): void; */ export function getEntries(): PerformanceEntry[]; -export function getEntry(name: string): PerformanceEntry; - export function getDuration(from: string, to: string): number; type ExportData = any[]; diff --git a/src/vs/base/common/performance.js b/src/vs/base/common/performance.js index 16960cea069..5893db44495 100644 --- a/src/vs/base/common/performance.js +++ b/src/vs/base/common/performance.js @@ -9,45 +9,33 @@ function _factory(sharedObj) { - sharedObj._performanceEntries = sharedObj._performanceEntries || []; + sharedObj.MonacoPerformanceMarks = sharedObj.MonacoPerformanceMarks || []; const _dataLen = 2; const _timeStamp = typeof console.timeStamp === 'function' ? console.timeStamp.bind(console) : () => { }; function importEntries(entries) { - sharedObj._performanceEntries.splice(0, 0, ...entries); + sharedObj.MonacoPerformanceMarks.splice(0, 0, ...entries); } function exportEntries() { - return sharedObj._performanceEntries.slice(0); + return sharedObj.MonacoPerformanceMarks.slice(0); } function getEntries() { const result = []; - const entries = sharedObj._performanceEntries; + const entries = sharedObj.MonacoPerformanceMarks; for (let i = 0; i < entries.length; i += _dataLen) { result.push({ name: entries[i], - timestamp: entries[i + 1], + startTime: entries[i + 1], }); } return result; } - function getEntry(name) { - const entries = sharedObj._performanceEntries; - for (let i = 0; i < entries.length; i += _dataLen) { - if (entries[i] === name) { - return { - name: entries[i], - timestamp: entries[i + 1], - }; - } - } - } - function getDuration(from, to) { - const entries = sharedObj._performanceEntries; + const entries = sharedObj.MonacoPerformanceMarks; let target = to; let endIndex = 0; for (let i = entries.length - _dataLen; i >= 0; i -= _dataLen) { @@ -66,14 +54,13 @@ function _factory(sharedObj) { } function mark(name) { - sharedObj._performanceEntries.push(name, Date.now()); + sharedObj.MonacoPerformanceMarks.push(name, Date.now()); _timeStamp(name); } const exports = { mark: mark, getEntries: getEntries, - getEntry: getEntry, getDuration: getDuration, importEntries: importEntries, exportEntries: exportEntries @@ -86,7 +73,8 @@ function _factory(sharedObj) { // Because we want both instances to use the same perf-data // we store them globally -let sharedObj; +// eslint-disable-next-line no-var +var sharedObj; if (typeof global === 'object') { // nodejs sharedObj = global; @@ -104,5 +92,5 @@ if (typeof define === 'function') { // commonjs module.exports = _factory(sharedObj); } else { - // invalid context... + sharedObj.perf = _factory(sharedObj); } diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts index 2c30aaa188f..0bbc5d6ef91 100644 --- a/src/vs/base/common/platform.ts +++ b/src/vs/base/common/platform.ts @@ -54,7 +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 && !!navigator.maxTouchPoints && navigator.maxTouchPoints > 0; + _isIOS = (_userAgent.indexOf('Macintosh') >= 0 || _userAgent.indexOf('iPad') >= 0 || _userAgent.indexOf('iPhone') >= 0) && !!navigator.maxTouchPoints && navigator.maxTouchPoints > 0; _isLinux = _userAgent.indexOf('Linux') >= 0; _isWeb = true; _locale = navigator.language; @@ -208,7 +208,7 @@ 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; diff --git a/src/vs/base/common/resourceTree.ts b/src/vs/base/common/resourceTree.ts index ef537317389..23f661e1b80 100644 --- a/src/vs/base/common/resourceTree.ts +++ b/src/vs/base/common/resourceTree.ts @@ -7,7 +7,7 @@ import { memoize } from 'vs/base/common/decorators'; import * as paths from 'vs/base/common/path'; import { relativePath, joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { PathIterator, values } from 'vs/base/common/map'; +import { PathIterator } from 'vs/base/common/map'; export interface IResourceNode { readonly uri: URI; @@ -30,7 +30,7 @@ class Node implements IResourceNode { } get children(): Iterable> { - return [...values(this._children)]; + return this._children.values(); } @memoize diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index a4d1a870a2b..902076cc1cb 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -6,9 +6,9 @@ import * as extpath from 'vs/base/common/extpath'; import * as paths from 'vs/base/common/path'; import { URI, uriToFsPath } from 'vs/base/common/uri'; -import { equalsIgnoreCase } from 'vs/base/common/strings'; +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'; @@ -17,249 +17,362 @@ export function originalFSPath(uri: URI): string { return uriToFsPath(uri, true); } -/** - * Creates a key from a resource URI to be used to resource comparison and for resource maps. - * URI queries are included, fragments are ignored. - */ -export function getComparisonKey(resource: URI, caseInsensitivePath = hasToIgnoreCase(resource)): string { - let path = resource.path || '/'; - if (caseInsensitivePath) { - path = path.toLowerCase(); - } - return resource.with({ authority: resource.authority.toLowerCase(), path: path, fragment: null }).toString(); +//#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 hasToIgnoreCase(resource: URI | undefined): boolean { - // A file scheme resource is in the same platform as code, so ignore case for non linux platforms - // Resource can be from another platform. Lowering the case as an hack. Should come from File system provider - return resource && resource.scheme === Schemas.file ? !isLinux : true; -} +export class ExtUri implements IExtUri { -export function basenameOrAuthority(resource: URI): string { - return basename(resource) || resource.authority; -} + constructor(private _ignorePathCasing: (uri: URI) => boolean) { } -/** - * Tests whether a `candidate` URI is a parent or equal of a given `base` URI. - * URI queries must match, fragments are ignored. - * @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) && base.query === parentCandidate.query; + compare(uri1: URI, uri2: URI, ignoreFragment: boolean = false): number { + if (uri1 === uri2) { + return 0; } - if (isEqualAuthority(base.authority, parentCandidate.authority)) { - return extpath.isEqualOrParent(base.path || '/', parentCandidate.path || '/', ignoreCase, '/') && base.query === parentCandidate.query; + return strCompare(this.getComparisonKey(uri1, ignoreFragment), this.getComparisonKey(uri2, ignoreFragment)); + } + + isEqual(uri1: URI | undefined, uri2: URI | undefined, ignoreFragment: boolean = false): boolean { + if (uri1 === uri2) { + return true; } - } - return false; -} - -/** - * Tests whether the two authorities are the same - */ -export function isEqualAuthority(a1: string, a2: string) { - return a1 === a2 || equalsIgnoreCase(a1, a2); -} - -/** - * Tests whether two resources are the same. URI queries must match, fragments are ignored unless requested. - */ -export function isEqual(first: URI | undefined, second: URI | undefined, caseInsensitivePath = hasToIgnoreCase(first), ignoreFragment = true): boolean { - if (first === second) { - return true; - } - - if (!first || !second) { - return false; - } - - if (first.scheme !== second.scheme || !isEqualAuthority(first.authority, second.authority)) { - return false; - } - - const p1 = first.path || '/', p2 = second.path || '/'; - return (p1 === p2 || caseInsensitivePath && equalsIgnoreCase(p1, p2)) && first.query === second.query && (ignoreFragment || first.fragment === second.fragment); -} - -export function basename(resource: URI): string { - return paths.posix.basename(resource.path); -} - -export function extname(resource: URI): string { - return paths.posix.extname(resource.path); -} - -/** - * Return a URI representing the directory of a URI path. - * - * @param resource The input URI. - * @returns The URI representing the directory of the input URI. - */ -export function dirname(resource: URI): URI { - if (resource.path.length === 0) { - return resource; - } - 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 + if (!uri1 || !uri2) { + return false; } + return this.getComparisonKey(uri1, ignoreFragment) === this.getComparisonKey(uri2, ignoreFragment); } - 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 === 'file') { - joinedPath = URI.file(paths.join(originalFSPath(resource), ...pathFragment)).path; - } else { - joinedPath = paths.posix.join(resource.path || '/', ...pathFragment); + getComparisonKey(uri: URI, ignoreFragment: boolean = false): string { + return uri.with({ + path: this._ignorePathCasing(uri) ? uri.path.toLowerCase() : undefined, + fragment: ignoreFragment ? null : undefined + }).toString(); } - return resource.with({ - path: joinedPath - }); -} -/** - * Normalizes the path part of a URI: Resolves `.` and `..` elements with directory names. - * - * @param resource The URI to normalize the path. - * @returns The URI with the normalized path. - */ -export function normalizePath(resource: URI): URI { - if (!resource.path.length) { - return resource; - } - let normalizedPath: string; - if (resource.scheme === Schemas.file) { - normalizedPath = URI.file(paths.normalize(originalFSPath(resource))).path; - } else { - normalizedPath = paths.posix.normalize(resource.path); - } - return resource.with({ - path: normalizedPath - }); -} - -/** - * Returns true if the URI path is absolute. - */ -export function isAbsolutePath(resource: URI): boolean { - return !!resource.path && resource.path[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) && !(/^[a-zA-Z]:(\/$|\\$)/.test(resource.fsPath)); // ignore the slash at offset 0 - } -} - -/** - * 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 { - // 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; -} - -/** - * Adds a trailing path separator to the URI if there isn't one already. - * For example, c:\ would be unchanged, but c:\users would become c:\users\ - */ -export function addTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI { - let isRootSep: boolean = false; - if (resource.scheme === Schemas.file) { - const fsp = originalFSPath(resource); - isRootSep = ((fsp !== undefined) && (fsp.length === extpath.getRoot(fsp).length) && (fsp[fsp.length - 1] === sep)); - } else { - sep = '/'; - const p = resource.path; - isRootSep = p.length === 1 && p.charCodeAt(p.length - 1) === CharCode.Slash; - } - if (!isRootSep && !hasTrailingPathSeparator(resource, sep)) { - return resource.with({ path: resource.path + '/' }); - } - return resource; -} - -/** - * Returns a relative path between two URIs. If the URIs don't have the same schema or authority, `undefined` is returned. - * The returned relative path always uses forward slashes. - */ -export function relativePath(from: URI, to: URI, caseInsensitivePath = 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(originalFSPath(from), originalFSPath(to)); - return isWindows ? extpath.toSlashes(relativePath) : relativePath; - } - let fromPath = from.path || '/', toPath = to.path || '/'; - if (caseInsensitivePath) { - // make casing of fromPath match toPath - let i = 0; - for (const len = Math.min(fromPath.length, toPath.length); i < len; i++) { - if (fromPath.charCodeAt(i) !== toPath.charCodeAt(i)) { - if (fromPath.charAt(i).toLowerCase() !== toPath.charAt(i).toLowerCase()) { - break; - } + isEqualOrParent(base: URI, parentCandidate: URI, ignoreFragment: boolean = false): boolean { + if (base.scheme === parentCandidate.scheme) { + if (base.scheme === Schemas.file) { + return extpath.isEqualOrParent(originalFSPath(base), originalFSPath(parentCandidate), this._ignorePathCasing(base)) && base.query === parentCandidate.query && (ignoreFragment || base.fragment === parentCandidate.fragment); + } + if (isEqualAuthority(base.authority, parentCandidate.authority)) { + return extpath.isEqualOrParent(base.path, parentCandidate.path, this._ignorePathCasing(base), '/') && base.query === parentCandidate.query && (ignoreFragment || base.fragment === parentCandidate.fragment); } } - fromPath = toPath.substr(0, i) + fromPath.substr(i); + return false; } - return paths.posix.relative(fromPath, toPath); -} -/** - * Resolves an absolute or relative path against a base URI. - * The path can be relative or absolute posix or a Windows path - */ -export function resolvePath(base: URI, path: string): URI { - if (base.scheme === Schemas.file) { - const newURI = URI.file(paths.resolve(originalFSPath(base), path)); - return base.with({ - authority: newURI.authority, - path: newURI.path + // --- path math + + joinPath(resource: URI, ...pathFragment: string[]): URI { + return URI.joinPath(resource, ...pathFragment); + } + + basenameOrAuthority(resource: URI): string { + return basename(resource) || resource.authority; + } + + basename(resource: URI): string { + return paths.posix.basename(resource.path); + } + + extname(resource: URI): string { + return paths.posix.extname(resource.path); + } + + dirname(resource: URI): URI { + if (resource.path.length === 0) { + return resource; + } + let dirname; + if (resource.scheme === Schemas.file) { + dirname = URI.file(paths.dirname(originalFSPath(resource))).path; + } else { + dirname = paths.posix.dirname(resource.path); + if (resource.authority && dirname.length && dirname.charCodeAt(0) !== CharCode.Slash) { + console.error(`dirname("${resource.toString})) resulted in a relative path`); + dirname = '/'; // If a URI contains an authority component, then the path component must either be empty or begin with a CharCode.Slash ("/") character + } + } + return resource.with({ + path: dirname }); } - if (path.indexOf('/') === -1) { // no slashes? it's likely a Windows path - path = extpath.toSlashes(path); - if (/^[a-zA-Z]:(\/|$)/.test(path)) { // starts with a drive letter - path = '/' + path; + + normalizePath(resource: URI): URI { + if (!resource.path.length) { + return resource; + } + let normalizedPath: string; + if (resource.scheme === Schemas.file) { + normalizedPath = URI.file(paths.normalize(originalFSPath(resource))).path; + } else { + normalizedPath = paths.posix.normalize(resource.path); + } + return resource.with({ + path: normalizedPath + }); + } + + relativePath(from: URI, to: URI): string | undefined { + if (from.scheme !== to.scheme || !isEqualAuthority(from.authority, to.authority)) { + return undefined; + } + if (from.scheme === Schemas.file) { + const relativePath = paths.relative(originalFSPath(from), originalFSPath(to)); + return isWindows ? extpath.toSlashes(relativePath) : relativePath; + } + let fromPath = from.path || '/', toPath = to.path || '/'; + if (this._ignorePathCasing(from)) { + // make casing of fromPath match toPath + let i = 0; + for (const len = Math.min(fromPath.length, toPath.length); i < len; i++) { + if (fromPath.charCodeAt(i) !== toPath.charCodeAt(i)) { + if (fromPath.charAt(i).toLowerCase() !== toPath.charAt(i).toLowerCase()) { + break; + } + } + } + fromPath = toPath.substr(0, i) + fromPath.substr(i); + } + return paths.posix.relative(fromPath, toPath); + } + + resolvePath(base: URI, path: string): URI { + if (base.scheme === Schemas.file) { + const newURI = URI.file(paths.resolve(originalFSPath(base), path)); + return base.with({ + authority: newURI.authority, + path: newURI.path + }); + } + if (path.indexOf('/') === -1) { // no slashes? it's likely a Windows path + path = extpath.toSlashes(path); + if (/^[a-zA-Z]:(\/|$)/.test(path)) { // starts with a drive letter + path = '/' + path; + } + } + return base.with({ + path: paths.posix.resolve(base.path, path) + }); + } + + // --- misc + + isAbsolutePath(resource: URI): boolean { + return !!resource.path && resource.path[0] === '/'; + } + + isEqualAuthority(a1: string, a2: string) { + return a1 === a2 || equalsIgnoreCase(a1, a2); + } + + hasTrailingPathSeparator(resource: URI, sep: string = paths.sep): boolean { + if (resource.scheme === Schemas.file) { + const fsp = originalFSPath(resource); + return fsp.length > extpath.getRoot(fsp).length && fsp[fsp.length - 1] === sep; + } else { + const p = resource.path; + return (p.length > 1 && p.charCodeAt(p.length - 1) === CharCode.Slash) && !(/^[a-zA-Z]:(\/$|\\$)/.test(resource.fsPath)); // ignore the slash at offset 0 } } - return base.with({ - path: paths.posix.resolve(base.path, path) - }); + + removeTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI { + // Make sure that the path isn't a drive letter. A trailing separator there is not removable. + if (hasTrailingPathSeparator(resource, sep)) { + return resource.with({ path: resource.path.substr(0, resource.path.length - 1) }); + } + return resource; + } + + addTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI { + let isRootSep: boolean = false; + if (resource.scheme === Schemas.file) { + const fsp = originalFSPath(resource); + isRootSep = ((fsp !== undefined) && (fsp.length === extpath.getRoot(fsp).length) && (fsp[fsp.length - 1] === sep)); + } else { + sep = '/'; + const p = resource.path; + isRootSep = p.length === 1 && p.charCodeAt(p.length - 1) === CharCode.Slash; + } + if (!isRootSep && !hasTrailingPathSeparator(resource, sep)) { + return resource.with({ path: resource.path + '/' }); + } + return resource; + } } + +/** + * Unbiased utility that takes uris "as they are". This means it can be interchanged with + * uri#toString() usages. The following is true + * ``` + * assertEqual(aUri.toString() === bUri.toString(), exturi.isEqual(aUri, bUri)) + * ``` + */ +export const extUri = new ExtUri(() => false); + +/** + * BIASED utility that _mostly_ ignored the case of urs paths. ONLY use this util if you + * understand what you are doing. + * + * This utility is INCOMPATIBLE with `uri.toString()`-usages and both CANNOT be used interchanged. + * + * When dealing with uris from files or documents, `extUri` (the unbiased friend)is sufficient + * because those uris come from a "trustworthy source". When creating unknown uris it's always + * better to use `IUriIdentityService` which exposes an `IExtUri`-instance which knows when path + * casing matters. + */ +export const extUriBiasedIgnorePathCase = new ExtUri(uri => { + // A file scheme resource is in the same platform as code, so ignore case for non linux platforms + // Resource can be from another platform. Lowering the case as an hack. Should come from File system provider + return uri.scheme === Schemas.file ? !isLinux : true; +}); + + +/** + * BIASED utility that always ignores the casing of uris paths. ONLY use this util if you + * understand what you are doing. + * + * This utility is INCOMPATIBLE with `uri.toString()`-usages and both CANNOT be used interchanged. + * + * When dealing with uris from files or documents, `extUri` (the unbiased friend)is sufficient + * because those uris come from a "trustworthy source". When creating unknown uris it's always + * better to use `IUriIdentityService` which exposes an `IExtUri`-instance which knows when path + * casing matters. + */ +export const extUriIgnorePathCase = new ExtUri(_ => true); + +export const isEqual = extUri.isEqual.bind(extUri); +export const isEqualOrParent = extUri.isEqualOrParent.bind(extUri); +export const getComparisonKey = extUri.getComparisonKey.bind(extUri); +export const basenameOrAuthority = extUri.basenameOrAuthority.bind(extUri); +export const basename = extUri.basename.bind(extUri); +export const extname = extUri.extname.bind(extUri); +export const dirname = extUri.dirname.bind(extUri); +export const joinPath = extUri.joinPath.bind(extUri); +export const normalizePath = extUri.normalizePath.bind(extUri); +export const relativePath = extUri.relativePath.bind(extUri); +export const resolvePath = extUri.resolvePath.bind(extUri); +export const isAbsolutePath = extUri.isAbsolutePath.bind(extUri); +export const isEqualAuthority = extUri.isEqualAuthority.bind(extUri); +export const hasTrailingPathSeparator = extUri.hasTrailingPathSeparator.bind(extUri); +export const removeTrailingPathSeparator = extUri.removeTrailingPathSeparator.bind(extUri); +export const addTrailingPathSeparator = extUri.addTrailingPathSeparator.bind(extUri); + +//#endregion + export function distinctParents(items: T[], resourceAccessor: (item: T) => URI): T[] { const distinctParents: T[] = []; for (let i = 0; i < items.length; i++) { diff --git a/src/vs/base/common/scrollable.ts b/src/vs/base/common/scrollable.ts index c507366e853..cb3fe20dc79 100644 --- a/src/vs/base/common/scrollable.ts +++ b/src/vs/base/common/scrollable.ts @@ -13,10 +13,18 @@ export const enum ScrollbarVisibility { } export interface ScrollEvent { + oldWidth: number; + oldScrollWidth: number; + oldScrollLeft: number; + width: number; scrollWidth: number; scrollLeft: number; + oldHeight: number; + oldScrollHeight: number; + oldScrollTop: number; + height: number; scrollHeight: number; scrollTop: number; @@ -134,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, @@ -316,6 +332,12 @@ export class Scrollable extends Disposable { this._setState(newState); + if (!this._smoothScrolling) { + // Looks like someone canceled the smooth scrolling + // from the scroll event handler + return; + } + if (update.isDone) { this._smoothScrolling.dispose(); this._smoothScrolling = null; diff --git a/src/vs/base/common/search.ts b/src/vs/base/common/search.ts index 7b57f1e5a66..2eec434e9a7 100644 --- a/src/vs/base/common/search.ts +++ b/src/vs/base/common/search.ts @@ -18,7 +18,7 @@ export function buildReplaceStringWithCasePreserved(matches: string[] | null, pa return pattern.toUpperCase(); } else if (matches[0].toLowerCase() === matches[0]) { return pattern.toLowerCase(); - } else if (strings.containsUppercaseCharacter(matches[0][0])) { + } else if (strings.containsUppercaseCharacter(matches[0][0]) && pattern.length > 0) { return pattern[0].toUpperCase() + pattern.substr(1); } else { // we don't understand its pattern yet. diff --git a/src/vs/base/common/sequence.ts b/src/vs/base/common/sequence.ts index 418d3907c99..dc47fe8c095 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 602df43e598..0937e4ddd19 100644 --- a/src/vs/base/common/severity.ts +++ b/src/vs/base/common/severity.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; import * as strings from 'vs/base/common/strings'; enum Severity { @@ -20,11 +19,6 @@ namespace Severity { const _warn = 'warn'; const _info = 'info'; - const _displayStrings: { [value: number]: string; } = Object.create(null); - _displayStrings[Severity.Error] = nls.localize('sev.error', "Error"); - _displayStrings[Severity.Warning] = nls.localize('sev.warning', "Warning"); - _displayStrings[Severity.Info] = nls.localize('sev.info', "Info"); - /** * Parses 'error', 'warning', 'warn', 'info' in call casings * and falls back to ignore. diff --git a/src/vs/base/common/skipList.ts b/src/vs/base/common/skipList.ts new file mode 100644 index 00000000000..1243ebdddf7 --- /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 = 1; + 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; 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; 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 + 1; 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; 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 >= 1 && list._header.forward[list._level] === NIL) { + list._level -= 1; + } + return true; + } + +} diff --git a/src/vs/base/common/stream.ts b/src/vs/base/common/stream.ts index 8234770bd19..7ed2befbe0d 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. */ @@ -49,6 +51,11 @@ 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; } /** @@ -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; } +/** + * 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 3f8e89b0dc3..5003acabe09 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -728,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))) { diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index 634c6e5143a..ee9172a4f84 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -8,7 +8,7 @@ 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[] { +export function isArray(array: T | {}): array is T extends readonly any[] ? (unknown extends T ? never : readonly any[]) : any[] { return Array.isArray(array); } @@ -64,6 +64,13 @@ export function isUndefined(obj: any): obj is 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. */ @@ -164,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) { diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index a278e5a5ef4..5ac44dc8426 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -252,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 ------------------------ @@ -266,9 +266,9 @@ export class URI implements UriComponents { static parse(value: string, _strict: boolean = false): URI { const match = _regexp.exec(value); if (!match) { - return new _URI(_empty, _empty, _empty, _empty, _empty); + return new Uri(_empty, _empty, _empty, _empty, _empty); } - return new _URI( + return new Uri( match[2] || _empty, percentDecode(match[4] || _empty), percentDecode(match[5] || _empty), @@ -323,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, @@ -387,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; @@ -412,8 +412,8 @@ interface UriState extends UriComponents { const _pathSepMarker = isWindows ? 1 : undefined; -// eslint-disable-next-line @typescript-eslint/class-name-casing -class _URI extends URI { +// This class exists so that URI is compatibile with vscode.Uri (API). +class Uri extends URI { _formatted: string | null = null; _fsPath: string | null = null; diff --git a/src/vs/base/node/encoding.ts b/src/vs/base/node/encoding.ts deleted file mode 100644 index ec3392a780e..00000000000 --- a/src/vs/base/node/encoding.ts +++ /dev/null @@ -1,346 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as iconv from 'iconv-lite'; -import { Readable, Writable } from 'stream'; -import { VSBuffer } from 'vs/base/common/buffer'; - -export const UTF8 = 'utf8'; -export const UTF8_with_bom = 'utf8bom'; -export const UTF16be = 'utf16be'; -export const UTF16le = 'utf16le'; - -export type UTF_ENCODING = typeof UTF8 | typeof UTF8_with_bom | typeof UTF16be | typeof UTF16le; - -export function isUTFEncoding(encoding: string): encoding is UTF_ENCODING { - return [UTF8, UTF8_with_bom, UTF16be, UTF16le].some(utfEncoding => utfEncoding === encoding); -} - -export const UTF16be_BOM = [0xFE, 0xFF]; -export const UTF16le_BOM = [0xFF, 0xFE]; -export const UTF8_BOM = [0xEF, 0xBB, 0xBF]; - -const ZERO_BYTE_DETECTION_BUFFER_MAX_LEN = 512; // number of bytes to look at to decide about a file being binary or not -const NO_ENCODING_GUESS_MIN_BYTES = 512; // when not auto guessing the encoding, small number of bytes are enough -const AUTO_ENCODING_GUESS_MIN_BYTES = 512 * 8; // with auto guessing we want a lot more content to be read for guessing -const AUTO_ENCODING_GUESS_MAX_BYTES = 512 * 128; // set an upper limit for the number of bytes we pass on to jschardet - -export interface IDecodeStreamOptions { - guessEncoding: boolean; - minBytesRequiredForDetection?: number; - - overwriteEncoding(detectedEncoding: string | null): string; -} - -export interface IDecodeStreamResult { - stream: NodeJS.ReadableStream; - detected: IDetectedEncodingResult; -} - -export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions): Promise { - if (!options.minBytesRequiredForDetection) { - options.minBytesRequiredForDetection = options.guessEncoding ? AUTO_ENCODING_GUESS_MIN_BYTES : NO_ENCODING_GUESS_MIN_BYTES; - } - - return new Promise((resolve, reject) => { - const writer = new class extends Writable { - private decodeStream: NodeJS.ReadWriteStream | undefined; - private decodeStreamPromise: Promise | undefined; - - private bufferedChunks: Buffer[] = []; - private bytesBuffered = 0; - - _write(chunk: Buffer, encoding: string, callback: (error: Error | null | undefined) => 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 | undefined) => 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 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); -} - -export function toNodeEncoding(enc: string | null): string { - if (enc === UTF8_with_bom || enc === null) { - return UTF8; // iconv does not distinguish UTF 8 with or without BOM, so we need to help it - } - - return enc; -} - -export function detectEncodingByBOMFromBuffer(buffer: Buffer | VSBuffer | null, bytesRead: number): typeof UTF8_with_bom | typeof UTF16le | typeof UTF16be | null { - if (!buffer || bytesRead < UTF16be_BOM.length) { - return null; - } - - const b0 = buffer.readUInt8(0); - const b1 = buffer.readUInt8(1); - - // UTF-16 BE - if (b0 === UTF16be_BOM[0] && b1 === UTF16be_BOM[1]) { - return UTF16be; - } - - // UTF-16 LE - if (b0 === UTF16le_BOM[0] && b1 === UTF16le_BOM[1]) { - return UTF16le; - } - - if (bytesRead < UTF8_BOM.length) { - return null; - } - - const b2 = buffer.readUInt8(2); - - // UTF-8 - if (b0 === UTF8_BOM[0] && b1 === UTF8_BOM[1] && b2 === UTF8_BOM[2]) { - return UTF8_with_bom; - } - - return null; -} - -// we explicitly ignore a specific set of encodings from auto guessing -// - ASCII: we never want this encoding (most UTF-8 files would happily detect as -// ASCII files and then you could not type non-ASCII characters anymore) -// - UTF-16: we have our own detection logic for UTF-16 -// - UTF-32: we do not support this encoding in VSCode -const IGNORE_ENCODINGS = ['ascii', 'utf-16', 'utf-32']; - -/** - * Guesses the encoding from buffer. - */ -async function guessEncodingByBuffer(buffer: Buffer): Promise { - const jschardet = await import('jschardet'); - - const guessed = jschardet.detect(buffer.slice(0, AUTO_ENCODING_GUESS_MAX_BYTES)); // ensure to limit buffer for guessing due to https://github.com/aadsm/jschardet/issues/53 - if (!guessed || !guessed.encoding) { - return null; - } - - const enc = guessed.encoding.toLowerCase(); - if (0 <= IGNORE_ENCODINGS.indexOf(enc)) { - return null; // see comment above why we ignore some encodings - } - - return toIconvLiteEncoding(guessed.encoding); -} - -const JSCHARDET_TO_ICONV_ENCODINGS: { [name: string]: string } = { - 'ibm866': 'cp866', - 'big5': 'cp950' -}; - -function toIconvLiteEncoding(encodingName: string): string { - const normalizedEncodingName = encodingName.replace(/[^a-zA-Z0-9]/g, '').toLowerCase(); - const mapped = JSCHARDET_TO_ICONV_ENCODINGS[normalizedEncodingName]; - - return mapped || normalizedEncodingName; -} - -/** - * The encodings that are allowed in a settings file don't match the canonical encoding labels specified by WHATWG. - * See https://encoding.spec.whatwg.org/#names-and-labels - * Iconv-lite strips all non-alphanumeric characters, but ripgrep doesn't. For backcompat, allow these labels. - */ -export function toCanonicalName(enc: string): string { - switch (enc) { - case 'shiftjis': - return 'shift-jis'; - case 'utf16le': - return 'utf-16le'; - case 'utf16be': - return 'utf-16be'; - case 'big5hkscs': - return 'big5-hkscs'; - case 'eucjp': - return 'euc-jp'; - case 'euckr': - return 'euc-kr'; - case 'koi8r': - return 'koi8-r'; - case 'koi8u': - return 'koi8-u'; - case 'macroman': - return 'x-mac-roman'; - case 'utf8bom': - return 'utf8'; - default: - const m = enc.match(/windows(\d+)/); - if (m) { - return 'windows-' + m[1]; - } - - return enc; - } -} - -export interface IDetectedEncodingResult { - encoding: string | null; - seemsBinary: boolean; -} - -export interface IReadResult { - buffer: Buffer | null; - bytesRead: number; -} - -export function detectEncodingFromBuffer(readResult: IReadResult, autoGuessEncoding?: false): IDetectedEncodingResult; -export function detectEncodingFromBuffer(readResult: IReadResult, autoGuessEncoding?: boolean): Promise; -export function detectEncodingFromBuffer({ buffer, bytesRead }: IReadResult, autoGuessEncoding?: boolean): Promise | IDetectedEncodingResult { - - // Always first check for BOM to find out about encoding - let encoding = detectEncodingByBOMFromBuffer(buffer, bytesRead); - - // Detect 0 bytes to see if file is binary or UTF-16 LE/BE - // unless we already know that this file has a UTF-16 encoding - let seemsBinary = false; - if (encoding !== UTF16be && encoding !== UTF16le && buffer) { - let couldBeUTF16LE = true; // e.g. 0xAA 0x00 - let couldBeUTF16BE = true; // e.g. 0x00 0xAA - let containsZeroByte = false; - - // This is a simplified guess to detect UTF-16 BE or LE by just checking if - // the first 512 bytes have the 0-byte at a specific location. For UTF-16 LE - // this would be the odd byte index and for UTF-16 BE the even one. - // Note: this can produce false positives (a binary file that uses a 2-byte - // encoding of the same format as UTF-16) and false negatives (a UTF-16 file - // that is using 4 bytes to encode a character). - for (let i = 0; i < bytesRead && i < ZERO_BYTE_DETECTION_BUFFER_MAX_LEN; i++) { - const isEndian = (i % 2 === 1); // assume 2-byte sequences typical for UTF-16 - const isZeroByte = (buffer.readInt8(i) === 0); - - if (isZeroByte) { - containsZeroByte = true; - } - - // UTF-16 LE: expect e.g. 0xAA 0x00 - if (couldBeUTF16LE && (isEndian && !isZeroByte || !isEndian && isZeroByte)) { - couldBeUTF16LE = false; - } - - // UTF-16 BE: expect e.g. 0x00 0xAA - if (couldBeUTF16BE && (isEndian && isZeroByte || !isEndian && !isZeroByte)) { - couldBeUTF16BE = false; - } - - // Return if this is neither UTF16-LE nor UTF16-BE and thus treat as binary - if (isZeroByte && !couldBeUTF16LE && !couldBeUTF16BE) { - break; - } - } - - // Handle case of 0-byte included - if (containsZeroByte) { - if (couldBeUTF16LE) { - encoding = UTF16le; - } else if (couldBeUTF16BE) { - encoding = UTF16be; - } else { - seemsBinary = true; - } - } - } - - // Auto guess encoding if configured - if (autoGuessEncoding && !seemsBinary && !encoding && buffer) { - return guessEncodingByBuffer(buffer.slice(0, bytesRead)).then(guessedEncoding => { - return { - seemsBinary: false, - encoding: guessedEncoding - }; - }); - } - - return { seemsBinary, encoding }; -} diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index 1fb6c3c2354..9f2bb1cf287 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -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/stream.ts b/src/vs/base/node/stream.ts deleted file mode 100644 index 12ba5e5d929..00000000000 --- a/src/vs/base/node/stream.ts +++ /dev/null @@ -1,112 +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 { 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'; - -export function streamToNodeReadable(stream: VSBufferReadableStream): Readable { - return new class extends Readable { - private listening = false; - - _read(size?: number): void { - if (!this.listening) { - this.listening = true; - - // Data - stream.on('data', data => { - try { - if (!this.push(data.buffer)) { - stream.pause(); // pause the stream if we should not push anymore - } - } catch (error) { - this.emit(error); - } - }); - - // End - stream.on('end', () => { - try { - this.push(null); // signal EOS - } catch (error) { - this.emit(error); - } - }); - - // Error - stream.on('error', error => this.emit('error', error)); - } - - // ensure the stream is flowing - stream.resume(); - } - - _destroy(error: Error | null, callback: (error: Error | null) => void): void { - stream.destroy(); - - callback(null); - } - }; -} - -export function nodeReadableToString(stream: NodeJS.ReadableStream): Promise { - return new Promise((resolve, reject) => { - let result = ''; - - stream.on('data', chunk => result += chunk); - stream.on('error', reject); - stream.on('end', () => resolve(result)); - }); -} - -export function nodeStreamToVSBufferReadable(stream: NodeJS.ReadWriteStream, addBOM?: { encoding: UTF_ENCODING }): VSBufferReadable { - let bytesRead = 0; - let done = false; - - return { - read(): VSBuffer | null { - if (done) { - return null; - } - - const res = stream.read(); - if (isUndefinedOrNull(res)) { - done = true; - - // If we are instructed to add a BOM but we detect that no - // bytes have been read, we must ensure to return the BOM - // ourselves so that we comply with the contract. - if (bytesRead === 0 && addBOM) { - switch (addBOM.encoding) { - case UTF8: - case UTF8_with_bom: - return VSBuffer.wrap(Buffer.from(UTF8_BOM)); - case UTF16be: - return VSBuffer.wrap(Buffer.from(UTF16be_BOM)); - case UTF16le: - return VSBuffer.wrap(Buffer.from(UTF16le_BOM)); - } - } - - return null; - } - - // Handle String - if (typeof res === 'string') { - bytesRead += res.length; - - return VSBuffer.fromString(res); - } - - // Handle Buffer - else { - bytesRead += res.byteLength; - - return VSBuffer.wrap(res); - } - } - }; -} diff --git a/src/vs/base/node/terminalEncoding.ts b/src/vs/base/node/terminalEncoding.ts index 3077f3a9633..fdfc268c48a 100644 --- a/src/vs/base/node/terminalEncoding.ts +++ b/src/vs/base/node/terminalEncoding.ts @@ -7,7 +7,7 @@ * This code is also used by standalone cli's. Avoid adding dependencies to keep the size of the cli small. */ import { exec } from 'child_process'; -import * as os from 'os'; +import { isWindows } from 'vs/base/common/platform'; const windowsTerminalEncodings = { '437': 'cp437', // United States @@ -39,7 +39,6 @@ const JSCHARDET_TO_ICONV_ENCODINGS: { [name: string]: string } = { const UTF8 = 'utf8'; - export async function resolveTerminalEncoding(verbose?: boolean): Promise { let rawEncodingPromise: Promise; @@ -54,7 +53,7 @@ export async function resolveTerminalEncoding(verbose?: boolean): Promise(resolve => { if (verbose) { console.log('Running "chcp" to detect terminal encoding...'); @@ -62,6 +61,10 @@ export async function resolveTerminalEncoding(verbose?: boolean): Promise { if (stdout) { + if (verbose) { + console.log(`Output from "chcp" command is: ${stdout}`); + } + const windowsTerminalEncodingKeys = Object.keys(windowsTerminalEncodings) as Array; for (const key of windowsTerminalEncodingKeys) { if (stdout.indexOf(key) >= 0) { diff --git a/src/vs/base/parts/contextmenu/common/contextmenu.ts b/src/vs/base/parts/contextmenu/common/contextmenu.ts index c31a49474f1..0b99dbcb328 100644 --- a/src/vs/base/parts/contextmenu/common/contextmenu.ts +++ b/src/vs/base/parts/contextmenu/common/contextmenu.ts @@ -36,8 +36,7 @@ export interface IPopupOptions { x?: number; y?: number; positioningItem?: number; - onHide?: () => void; } export const CONTEXT_MENU_CHANNEL = 'vscode:contextmenu'; -export const CONTEXT_MENU_CLOSE_CHANNEL = 'vscode:onCloseContextMenu'; \ No newline at end of file +export const CONTEXT_MENU_CLOSE_CHANNEL = 'vscode:onCloseContextMenu'; diff --git a/src/vs/base/parts/contextmenu/electron-browser/contextmenu.ts b/src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts similarity index 84% rename from src/vs/base/parts/contextmenu/electron-browser/contextmenu.ts rename to src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts index da008e59930..4b8917c3426 100644 --- a/src/vs/base/parts/contextmenu/electron-browser/contextmenu.ts +++ b/src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ipcRenderer, Event } from 'electron'; +import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { IContextMenuItem, ISerializableContextMenuItem, CONTEXT_MENU_CLOSE_CHANNEL, CONTEXT_MENU_CHANNEL, IPopupOptions, IContextMenuEvent } from 'vs/base/parts/contextmenu/common/contextmenu'; let contextMenuIdPool = 0; -export function popup(items: IContextMenuItem[], options?: IPopupOptions): void { +export function popup(items: IContextMenuItem[], options?: IPopupOptions, onHide?: () => void): void { const processedItems: IContextMenuItem[] = []; const contextMenuId = contextMenuIdPool++; const onClickChannel = `vscode:onContextMenu${contextMenuId}`; - const onClickChannelHandler = (_event: Event, itemId: number, context: IContextMenuEvent) => { + const onClickChannelHandler = (event: unknown, itemId: number, context: IContextMenuEvent) => { const item = processedItems[itemId]; if (item.click) { item.click(context); @@ -21,15 +21,15 @@ export function popup(items: IContextMenuItem[], options?: IPopupOptions): void }; ipcRenderer.once(onClickChannel, onClickChannelHandler); - ipcRenderer.once(CONTEXT_MENU_CLOSE_CHANNEL, (_event: Event, closedContextMenuId: number) => { + ipcRenderer.once(CONTEXT_MENU_CLOSE_CHANNEL, (event: unknown, closedContextMenuId: number) => { if (closedContextMenuId !== contextMenuId) { return; } ipcRenderer.removeListener(onClickChannel, onClickChannelHandler); - if (options?.onHide) { - options.onHide(); + if (onHide) { + onHide(); } }); diff --git a/src/vs/base/parts/ipc/node/ipc.electron.ts b/src/vs/base/parts/ipc/common/ipc.electron.ts similarity index 83% rename from src/vs/base/parts/ipc/node/ipc.electron.ts rename to src/vs/base/parts/ipc/common/ipc.electron.ts index 09c97ba47b1..516351e1509 100644 --- a/src/vs/base/parts/ipc/node/ipc.electron.ts +++ b/src/vs/base/parts/ipc/common/ipc.electron.ts @@ -8,7 +8,7 @@ import { Event } from 'vs/base/common/event'; import { VSBuffer } from 'vs/base/common/buffer'; export interface Sender { - send(channel: string, msg: Buffer | null): void; + send(channel: string, msg: unknown): void; } export class Protocol implements IMessagePassingProtocol { @@ -17,13 +17,13 @@ export class Protocol implements IMessagePassingProtocol { send(message: VSBuffer): void { try { - this.sender.send('ipc:message', (message.buffer)); + this.sender.send('vscode:message', message.buffer); } catch (e) { // systems are going down } } dispose(): void { - this.sender.send('ipc:disconnect', null); + this.sender.send('vscode:disconnect', null); } -} \ No newline at end of file +} diff --git a/src/vs/base/parts/ipc/common/ipc.net.ts b/src/vs/base/parts/ipc/common/ipc.net.ts index 132654b320f..c3fede90bf3 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 { @@ -619,6 +629,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); diff --git a/src/vs/base/parts/ipc/common/ipc.ts b/src/vs/base/parts/ipc/common/ipc.ts index 8acfc6bce4e..ad2e7066eff 100644 --- a/src/vs/base/parts/ipc/common/ipc.ts +++ b/src/vs/base/parts/ipc/common/ipc.ts @@ -10,7 +10,9 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance import * as errors from 'vs/base/common/errors'; import { VSBuffer } from 'vs/base/common/buffer'; import { getRandomElement } from 'vs/base/common/arrays'; -import { isFunction } from 'vs/base/common/types'; +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. @@ -40,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; }; @@ -54,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 } }; @@ -68,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 { @@ -262,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 }); } @@ -276,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 { @@ -480,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: @@ -508,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) { @@ -565,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; } } @@ -597,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 }); } } @@ -830,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 { @@ -919,3 +1014,203 @@ export class StaticRouter implements IClientRouter 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-main/ipc.electron-main.ts b/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts index bac2223ede6..b84a1d653dc 100644 --- a/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts +++ b/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts @@ -5,7 +5,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { IPCServer, ClientConnectionEvent } from 'vs/base/parts/ipc/common/ipc'; -import { Protocol } from 'vs/base/parts/ipc/node/ipc.electron'; +import { Protocol } from 'vs/base/parts/ipc/common/ipc.electron'; import { ipcMain, WebContents } from 'electron'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { VSBuffer } from 'vs/base/common/buffer'; @@ -26,7 +26,7 @@ export class Server extends IPCServer { private static readonly Clients = new Map(); private static getOnDidClientConnect(): Event { - const onHello = Event.fromNodeEventEmitter(ipcMain, 'ipc:hello', ({ sender }) => sender); + const onHello = Event.fromNodeEventEmitter(ipcMain, 'vscode:hello', ({ sender }) => sender); return Event.map(onHello, webContents => { const id = webContents.id; @@ -39,8 +39,8 @@ export class Server extends IPCServer { const onDidClientReconnect = new Emitter(); Server.Clients.set(id, toDisposable(() => onDidClientReconnect.fire())); - const onMessage = createScopedOnMessageEvent(id, 'ipc:message') as Event; - const onDidClientDisconnect = Event.any(Event.signal(createScopedOnMessageEvent(id, 'ipc:disconnect')), onDidClientReconnect.event); + const onMessage = createScopedOnMessageEvent(id, 'vscode:message') as Event; + const onDidClientDisconnect = Event.any(Event.signal(createScopedOnMessageEvent(id, 'vscode:disconnect')), onDidClientReconnect.event); const protocol = new Protocol(webContents, onMessage); return { protocol, onDidClientDisconnect }; diff --git a/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts b/src/vs/base/parts/ipc/electron-sandbox/ipc.electron-sandbox.ts similarity index 80% rename from src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts rename to src/vs/base/parts/ipc/electron-sandbox/ipc.electron-sandbox.ts index 2690300b83a..19ca487c890 100644 --- a/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts +++ b/src/vs/base/parts/ipc/electron-sandbox/ipc.electron-sandbox.ts @@ -5,18 +5,18 @@ import { Event } from 'vs/base/common/event'; import { IPCClient } from 'vs/base/parts/ipc/common/ipc'; -import { Protocol } from 'vs/base/parts/ipc/node/ipc.electron'; -import { ipcRenderer } from 'electron'; +import { Protocol } from 'vs/base/parts/ipc/common/ipc.electron'; import { IDisposable } from 'vs/base/common/lifecycle'; import { VSBuffer } from 'vs/base/common/buffer'; +import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; export class Client extends IPCClient implements IDisposable { private protocol: Protocol; private static createProtocol(): Protocol { - const onMessage = Event.fromNodeEventEmitter(ipcRenderer, 'ipc:message', (_, message: Buffer) => VSBuffer.wrap(message)); - ipcRenderer.send('ipc:hello'); + const onMessage = Event.fromNodeEventEmitter(ipcRenderer, 'vscode:message', (_, message) => VSBuffer.wrap(message)); + ipcRenderer.send('vscode:hello'); return new Protocol(ipcRenderer, onMessage); } @@ -29,4 +29,4 @@ export class Client extends IPCClient implements IDisposable { dispose(): void { this.protocol.dispose(); } -} \ No newline at end of file +} diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index ec80ba3f1c3..2b6c70afa70 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; @@ -57,12 +58,47 @@ export class NodeSocket implements ISocket { // > https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback // > However, the false return value is only advisory and the writable stream will unconditionally // > accept and buffer chunk even if it has not been allowed to drain. - this.socket.write(buffer.buffer); + try { + this.socket.write(buffer.buffer); + } catch (err) { + if (err.code === 'EPIPE') { + // An EPIPE exception at the wrong time can lead to a renderer process crash + // so ignore the error since the socket will fire the close event soon anyways: + // > https://nodejs.org/api/errors.html#errors_common_system_errors + // > EPIPE (Broken pipe): A write on a pipe, socket, or FIFO for which there is no + // > process to read the data. Commonly encountered at the net and http layers, + // > indicative that the remote side of the stream being written to has been closed. + return; + } + onUnexpectedError(err); + } } public end(): void { this.socket.end(); } + + public drain(): Promise { + return new Promise((resolve, reject) => { + if (this.socket.bufferSize === 0) { + resolve(); + return; + } + const finished = () => { + this.socket.off('close', finished); + this.socket.off('end', finished); + this.socket.off('error', finished); + this.socket.off('timeout', finished); + this.socket.off('drain', finished); + resolve(); + }; + this.socket.on('close', finished); + this.socket.on('end', finished); + this.socket.on('error', finished); + this.socket.on('timeout', finished); + this.socket.on('drain', finished); + }); + } } const enum Constants { @@ -229,6 +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 631e5139133..00000000000 --- a/src/vs/base/parts/ipc/node/ipc.ts +++ /dev/null @@ -1,133 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Event } from 'vs/base/common/event'; -import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { revive } from 'vs/base/common/marshalling'; -import { isUndefinedOrNull } from 'vs/base/common/types'; -import { isUpperAsciiLetter } from 'vs/base/common/strings'; - -/** - * Use both `createChannelReceiver` and `createChannelSender` - * for automated process <=> process communication over methods - * and events. You do not need to spell out each method on both - * sides, a proxy will take care of this. - * - * Rules: - * - if marshalling is enabled, only `URI` and `RegExp` is converted - * automatically for you - * - events must follow the naming convention `onUppercase` - * - `CancellationToken` is currently not supported - * - if a context is provided, you can use `AddFirstParameterToFunctions` - * utility to signal this in the receiving side type - */ - -export interface IBaseChannelOptions { - - /** - * Disables automatic marshalling of `URI`. - * If marshalling is disabled, `UriComponents` - * must be used instead. - */ - disableMarshalling?: boolean; -} - -export interface IChannelReceiverOptions extends IBaseChannelOptions { } - -export function createChannelReceiver(service: unknown, options?: IChannelReceiverOptions): IServerChannel { - const handler = service as { [key: string]: unknown }; - const disableMarshalling = options && options.disableMarshalling; - - // Buffer any event that should be supported by - // iterating over all property keys and finding them - const mapEventNameToEvent = new Map>(); - for (const key in handler) { - if (propertyIsEvent(key)) { - mapEventNameToEvent.set(key, Event.buffer(handler[key] as Event, true)); - } - } - - return new class implements IServerChannel { - - listen(_: unknown, event: string): Event { - const eventImpl = mapEventNameToEvent.get(event); - if (eventImpl) { - return eventImpl as Event; - } - - throw new Error(`Event not found: ${event}`); - } - - call(_: unknown, command: string, args?: any[]): Promise { - const target = handler[command]; - if (typeof target === 'function') { - - // Revive unless marshalling disabled - if (!disableMarshalling && Array.isArray(args)) { - for (let i = 0; i < args.length; i++) { - args[i] = revive(args[i]); - } - } - - return target.apply(handler, args); - } - - throw new Error(`Method not found: ${command}`); - } - }; -} - -export interface IChannelSenderOptions extends IBaseChannelOptions { - - /** - * If provided, will add the value of `context` - * to each method call to the target. - */ - context?: unknown; -} - -export function createChannelSender(channel: IChannel, options?: IChannelSenderOptions): T { - const disableMarshalling = options && options.disableMarshalling; - - return new Proxy({}, { - get(_target: T, propKey: PropertyKey) { - if (typeof propKey === 'string') { - - // Event - if (propertyIsEvent(propKey)) { - return channel.listen(propKey); - } - - // Function - return async function (...args: any[]) { - - // Add context if any - let methodArgs: any[]; - if (options && !isUndefinedOrNull(options.context)) { - methodArgs = [options.context, ...args]; - } else { - methodArgs = args; - } - - const result = await channel.call(propKey, methodArgs); - - // Revive unless marshalling disabled - if (!disableMarshalling) { - return revive(result); - } - - return result; - }; - } - - throw new Error(`Property not found: ${String(propKey)}`); - } - }) as T; -} - -function propertyIsEvent(name: string): boolean { - // Assume a property is an event if it has a form of "onSomething" - return name[0] === 'o' && name[1] === 'n' && isUpperAsciiLetter(name.charCodeAt(2)); -} diff --git a/src/vs/base/parts/ipc/test/node/ipc.test.ts b/src/vs/base/parts/ipc/test/common/ipc.test.ts similarity index 95% 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 3539f5dea8c..8ed0f3797f1 100644 --- a/src/vs/base/parts/ipc/test/node/ipc.test.ts +++ b/src/vs/base/parts/ipc/test/common/ipc.test.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IChannel, IServerChannel, IMessagePassingProtocol, IPCServer, ClientConnectionEvent, IPCClient } from 'vs/base/parts/ipc/common/ipc'; -import { createChannelReceiver, createChannelSender } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel, IMessagePassingProtocol, IPCServer, ClientConnectionEvent, IPCClient, createChannelReceiver, createChannelSender } from 'vs/base/parts/ipc/common/ipc'; import { Emitter, Event } from 'vs/base/common/event'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; @@ -103,7 +102,7 @@ interface ITestService { error(message: string): Promise; neverComplete(): Promise; neverCompleteCT(cancellationToken: CancellationToken): Promise; - buffersLength(buffers: Buffer[]): Promise; + buffersLength(buffers: VSBuffer[]): Promise; marshall(uri: URI): Promise; context(): Promise; @@ -135,8 +134,8 @@ class TestService implements ITestService { return new Promise((_, e) => cancellationToken.onCancellationRequested(() => e(canceled()))); } - buffersLength(buffers: Buffer[]): Promise { - return Promise.resolve(buffers.reduce((r, b) => r + b.length, 0)); + buffersLength(buffers: VSBuffer[]): Promise { + return Promise.resolve(buffers.reduce((r, b) => r + b.buffer.length, 0)); } ping(msg: string): void { @@ -199,7 +198,7 @@ class TestChannelClient implements ITestService { return this.channel.call('neverCompleteCT', undefined, cancellationToken); } - buffersLength(buffers: Buffer[]): Promise { + buffersLength(buffers: VSBuffer[]): Promise { return this.channel.call('buffersLength', buffers); } @@ -317,7 +316,7 @@ suite('Base IPC', function () { }); test('buffers in arrays', async function () { - const r = await ipcService.buffersLength([Buffer.allocUnsafe(2), Buffer.allocUnsafe(3)]); + const r = await ipcService.buffersLength([VSBuffer.alloc(2), VSBuffer.alloc(3)]); return assert.equal(r, 5); }); }); @@ -383,7 +382,7 @@ suite('Base IPC', function () { }); test('buffers in arrays', async function () { - const r = await ipcService.buffersLength([Buffer.allocUnsafe(2), Buffer.allocUnsafe(3)]); + const r = await ipcService.buffersLength([VSBuffer.alloc(2), VSBuffer.alloc(3)]); return assert.equal(r, 5); }); }); diff --git a/src/vs/base/parts/quickinput/browser/media/quickInput.css b/src/vs/base/parts/quickinput/browser/media/quickInput.css index c086b587c14..547d47ba0b2 100644 --- a/src/vs/base/parts/quickinput/browser/media/quickInput.css +++ b/src/vs/base/parts/quickinput/browser/media/quickInput.css @@ -105,6 +105,8 @@ vertical-align: middle; padding: 2px 4px; border-radius: 2px; + min-height: auto; + line-height: normal; } .quick-input-action { diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index 5c0b5abec67..25475e8e20d 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -18,7 +18,7 @@ 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, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Action } from 'vs/base/common/actions'; import { equals } from 'vs/base/common/arrays'; import { TimeoutTimer } from 'vs/base/common/async'; @@ -28,6 +28,7 @@ import { List, IListOptions, IListStyles } from 'vs/base/browser/ui/list/listWid 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; @@ -353,10 +354,12 @@ class QuickInput extends Disposable implements IQuickInput { this.ui.inputBox.showDecoration(severity); if (severity === Severity.Error) { const styles = this.ui.inputBox.stylesForType(severity); + this.ui.message.style.color = styles.foreground ? `${styles.foreground}` : ''; this.ui.message.style.backgroundColor = styles.background ? `${styles.background}` : ''; this.ui.message.style.border = styles.border ? `1px solid ${styles.border}` : ''; this.ui.message.style.paddingBottom = '4px'; } else { + this.ui.message.style.color = ''; this.ui.message.style.backgroundColor = ''; this.ui.message.style.border = ''; this.ui.message.style.paddingBottom = ''; @@ -719,13 +722,13 @@ class QuickPick extends QuickInput implements IQuickPi break; case KeyCode.Home: - if (event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey) { + 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.shiftKey && !event.altKey && !event.metaKey) { + if ((event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey) { this.ui.list.focus(QuickInputListFocus.Last); dom.EventHelper.stop(event, true); } @@ -1228,10 +1231,10 @@ export class QuickInputController extends Disposable { this.previousFocusElement = e.relatedTarget instanceof HTMLElement ? e.relatedTarget : undefined; }, true)); this._register(focusTracker.onDidBlur(() => { - this.previousFocusElement = undefined; if (!this.getUI().ignoreFocusOut && !this.options.ignoreFocusOut()) { - this.hide(true); + this.hide(); } + this.previousFocusElement = undefined; })); this._register(dom.addDisposableListener(container, dom.EventType.FOCUS, (e: FocusEvent) => { inputBox.setFocus(); @@ -1572,13 +1575,14 @@ export class QuickInputController extends Disposable { } } - hide(focusLost?: boolean) { + 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 (!focusLost) { + if (!focusChanged) { if (this.previousFocusElement && this.previousFocusElement.offsetParent) { this.previousFocusElement.focus(); this.previousFocusElement = undefined; diff --git a/src/vs/base/parts/quickinput/browser/quickInputList.ts b/src/vs/base/parts/quickinput/browser/quickInputList.ts index 1d37629e94a..7c98a6bd880 100644 --- a/src/vs/base/parts/quickinput/browser/quickInputList.ts +++ b/src/vs/base/parts/quickinput/browser/quickInputList.ts @@ -283,7 +283,6 @@ export class QuickInputList { const accessibilityProvider = new QuickInputAccessibilityProvider(); this.list = options.createList('QuickInput', this.container, delegate, [new ListElementRenderer()], { identityProvider: { getId: element => element.saneLabel }, - openController: { shouldOpen: () => false }, // Workaround #58124 setRowLineHeight: false, multipleSelectionSupport: false, horizontalScrolling: false, diff --git a/src/vs/base/parts/quickinput/common/quickInput.ts b/src/vs/base/parts/quickinput/common/quickInput.ts index f09e508bed7..ebad819fffa 100644 --- a/src/vs/base/parts/quickinput/common/quickInput.ts +++ b/src/vs/base/parts/quickinput/common/quickInput.ts @@ -307,7 +307,7 @@ export interface IQuickInputButton { iconClass?: string; tooltip?: string; /** - * Wether to always show the button. By default buttons + * Whether to always show the button. By default buttons * are only visible when hovering over them with the mouse */ alwaysVisible?: boolean; diff --git a/src/vs/base/parts/request/browser/request.ts b/src/vs/base/parts/request/browser/request.ts index f8532bbe608..5dd4fdd449c 100644 --- a/src/vs/base/parts/request/browser/request.ts +++ b/src/vs/base/parts/request/browser/request.ts @@ -21,7 +21,7 @@ export function request(options: IRequestOptions, token: CancellationToken): Pro setRequestHeaders(xhr, options); xhr.responseType = 'arraybuffer'; - xhr.onerror = e => reject(new Error(xhr.statusText && ('XHR failed: ' + xhr.statusText))); + xhr.onerror = e => reject(new Error(xhr.statusText && ('XHR failed: ' + xhr.statusText) || 'XHR failed')); xhr.onload = (e) => { resolve({ res: { diff --git a/src/vs/base/parts/sandbox/common/electronTypes.ts b/src/vs/base/parts/sandbox/common/electronTypes.ts new file mode 100644 index 00000000000..8a5f4120862 --- /dev/null +++ b/src/vs/base/parts/sandbox/common/electronTypes.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. + *--------------------------------------------------------------------------------------------*/ + + +// ####################################################################### +// ### ### +// ### 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 CrashReporterStartOptions { + companyName: string; + /** + * URL that crash reports will be sent to as POST. + */ + submitURL: string; + /** + * Defaults to `app.name`. + */ + 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. When using + * Windows, the property names and values must be fewer than 64 characters. + */ + extra?: Record; + /** + * Directory to store the crash reports temporarily (only used when the crash + * reporter is started via `process.crashReporter.start`). + */ + crashesDirectory?: 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; +} diff --git a/src/vs/base/parts/sandbox/electron-browser/preload.js b/src/vs/base/parts/sandbox/electron-browser/preload.js new file mode 100644 index 00000000000..d10c4be3ae1 --- /dev/null +++ b/src/vs/base/parts/sandbox/electron-browser/preload.js @@ -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. + *--------------------------------------------------------------------------------------------*/ + +// @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 {Electron.CrashReporterStartOptions} options + */ + start(options) { + crashReporter.start(options); + } + }, + + /** + * Support for a subset of access to node.js global `process`. + */ + process: { + platform: process.platform, + env: process.env, + 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; + } + + //#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 00000000000..573e4735933 --- /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. + *--------------------------------------------------------------------------------------------*/ + +import { CrashReporterStartOptions } from 'vs/base/parts/sandbox/common/electronTypes'; + +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 { + + /** + * 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; +}; + +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 }; + + /** + * A listener on the process. Only a small subset of listener types are allowed. + */ + on: (type: string, callback: Function) => void; +}; + +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 00000000000..ac3e6511710 --- /dev/null +++ b/src/vs/base/parts/sandbox/test/electron-sandbox/globals.test.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { ipcRenderer, crashReporter, webFrame } from 'vs/base/parts/sandbox/electron-sandbox/globals'; + +suite('Sandbox', () => { + test('globals', () => { + assert.ok(ipcRenderer); + assert.ok(crashReporter); + assert.ok(webFrame); + }); +}); diff --git a/src/vs/base/test/browser/actionbar.test.ts b/src/vs/base/test/browser/actionbar.test.ts index 0a57211472b..8915318f99a 100644 --- a/src/vs/base/test/browser/actionbar.test.ts +++ b/src/vs/base/test/browser/actionbar.test.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { Separator, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar'; -import { Action } from 'vs/base/common/actions'; +import { prepareActions } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Action, Separator } from 'vs/base/common/actions'; suite('Actionbar', () => { 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 d13b997ea75..18eb84d5183 100644 --- a/src/vs/base/test/browser/ui/tree/compressedObjectTreeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/compressedObjectTreeModel.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { compress, ICompressedTreeElement, ICompressedTreeNode, decompress, CompressedObjectTreeModel } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; 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; @@ -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,7 +314,7 @@ 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, [ { element: 0 }, @@ -340,7 +341,7 @@ suite('CompressedObjectTree', function () { test('nested', () => { const list: ITreeNode>[] = []; - const model = new CompressedObjectTreeModel('test', toSpliceable(list)); + const model = new CompressedObjectTreeModel('test', toList(list)); model.setChildren(null, [ { @@ -376,7 +377,7 @@ suite('CompressedObjectTree', function () { test('compressed', () => { const list: ITreeNode>[] = []; - const model = new CompressedObjectTreeModel('test', toSpliceable(list)); + const model = new CompressedObjectTreeModel('test', toList(list)); model.setChildren(null, [ { 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 a5f46d06f2f..6c9dfeae8ea 100644 --- a/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts @@ -5,14 +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 { 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() { } }; } @@ -24,14 +24,14 @@ 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, [ { element: 0 }, @@ -53,7 +53,7 @@ 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, [ { @@ -90,7 +90,7 @@ 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, [ { @@ -118,7 +118,7 @@ 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, [ { element: 0 }, @@ -143,7 +143,7 @@ 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, [ { @@ -177,7 +177,7 @@ 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, [ { @@ -205,7 +205,7 @@ 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, [ { @@ -230,7 +230,7 @@ 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, [ { @@ -261,7 +261,7 @@ 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, [ { @@ -301,7 +301,7 @@ 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, [ { @@ -334,7 +334,7 @@ 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, [ { @@ -403,7 +403,7 @@ 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, [ { @@ -437,7 +437,7 @@ 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, [ { @@ -460,7 +460,7 @@ 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, [ { @@ -499,7 +499,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, [ { @@ -545,7 +545,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, [ { @@ -591,7 +591,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, [ { @@ -639,7 +639,7 @@ 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, [ { @@ -669,7 +669,7 @@ 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, [ { @@ -701,7 +701,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: 'silver' }, @@ -735,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/objectTreeModel.test.ts b/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts index e4b6afe23c1..4663962b557 100644 --- a/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts @@ -5,15 +5,16 @@ import * as assert from 'assert'; import { ITreeNode, ITreeFilter, TreeVisibility } from 'vs/base/browser/ui/tree/tree'; -import { ISpliceable } from 'vs/base/common/sequence'; import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel'; +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,7 +34,7 @@ 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, [ { element: 0 }, @@ -60,7 +61,7 @@ suite('ObjectTreeModel', function () { test('nested', () => { const list: ITreeNode[] = []; - const model = new ObjectTreeModel('test', toSpliceable(list)); + const model = new ObjectTreeModel('test', toList(list)); model.setChildren(null, [ { @@ -96,7 +97,7 @@ suite('ObjectTreeModel', function () { 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, [ { element: 0, collapsed: true } @@ -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, [ { @@ -254,7 +255,7 @@ suite('ObjectTreeModel', function () { return fn(element) ? TreeVisibility.Visible : parentVisibility; } }; - const model = new ObjectTreeModel('test', toSpliceable(list), { filter }); + const model = new ObjectTreeModel('test', toList(list), { filter }); model.setChildren(null, [{ element: 'file', children: [{ element: 'hello' }] }]); assert.deepEqual(toArray(list), ['file', 'hello']); diff --git a/src/vs/base/test/common/arrays.test.ts b/src/vs/base/test/common/arrays.test.ts index 4c0fe3e3e17..288bd265f8a 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/extpath.test.ts b/src/vs/base/test/common/extpath.test.ts index 02aa3a96377..0760e2c8b91 100644 --- a/src/vs/base/test/common/extpath.test.ts +++ b/src/vs/base/test/common/extpath.test.ts @@ -130,4 +130,41 @@ suite('Paths', () => { 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/lifecycle.test.ts b/src/vs/base/test/common/lifecycle.test.ts index 4d15ad2046c..91f17aedb44 100644 --- a/src/vs/base/test/common/lifecycle.test.ts +++ b/src/vs/base/test/common/lifecycle.test.ts @@ -48,6 +48,21 @@ suite('Lifecycle', () => { assert(disposable.isDisposed); assert(disposable2.isDisposed); }); + + 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('Reference Collection', () => { diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index 7d7f768e3bc..47af3ca64a9 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -6,6 +6,7 @@ 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 { extUriIgnorePathCase } from 'vs/base/common/resources'; suite('Map', () => { @@ -371,7 +372,8 @@ suite('Map', () => { 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.cmp('file'), 0); assert.equal(iter.hasNext(), true); iter.next(); @@ -391,7 +393,8 @@ suite('Map', () => { // scheme assert.equal(iter.value(), 'file'); - assert.equal(iter.cmp('FILE'), 0); + // assert.equal(iter.cmp('FILE'), 0); + assert.equal(iter.cmp('file'), 0); assert.equal(iter.hasNext(), true); iter.next(); @@ -809,32 +812,32 @@ suite('Map', () => { assert.equal(map.get(uncFile), 'true'); }); - // test('ResourceMap - files (ignorecase)', function () { - // const map = new ResourceMap(true); + test('ResourceMap - files (ignorecase)', function () { + const map = new ResourceMap(uri => extUriIgnorePathCase.getComparisonKey(uri)); - // const fileA = URI.parse('file://some/filea'); - // const fileB = URI.parse('some://some/other/fileb'); - // const fileAUpper = URI.parse('file://SOME/FILEA'); + const fileA = URI.parse('file://some/filea'); + const fileB = URI.parse('some://some/other/fileb'); + const fileAUpper = URI.parse('file://SOME/FILEA'); - // map.set(fileA, 'true'); - // assert.equal(map.get(fileA), 'true'); + map.set(fileA, 'true'); + assert.equal(map.get(fileA), 'true'); - // assert.equal(map.get(fileAUpper), 'true'); + assert.equal(map.get(fileAUpper), 'true'); - // assert.ok(!map.get(fileB)); + assert.ok(!map.get(fileB)); - // map.set(fileAUpper, 'false'); - // assert.equal(map.get(fileAUpper), 'false'); + map.set(fileAUpper, 'false'); + assert.equal(map.get(fileAUpper), 'false'); - // assert.equal(map.get(fileA), 'false'); + assert.equal(map.get(fileA), 'false'); - // const windowsFile = URI.file('c:\\test with %25\\c#code'); - // const uncFile = URI.file('\\\\shäres\\path\\c#\\plugin.json'); + const windowsFile = URI.file('c:\\test with %25\\c#code'); + const uncFile = URI.file('\\\\shäres\\path\\c#\\plugin.json'); - // map.set(windowsFile, 'true'); - // map.set(uncFile, 'true'); + map.set(windowsFile, 'true'); + map.set(uncFile, 'true'); - // assert.equal(map.get(windowsFile), 'true'); - // assert.equal(map.get(uncFile), 'true'); - // }); + assert.equal(map.get(windowsFile), 'true'); + assert.equal(map.get(uncFile), 'true'); + }); }); diff --git a/src/vs/base/test/common/mime.test.ts b/src/vs/base/test/common/mime.test.ts index 3d163580a6d..a7ac5a177a6 100644 --- a/src/vs/base/test/common/mime.test.ts +++ b/src/vs/base/test/common/mime.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { guessMimeTypes, registerTextMime, suggestFilename } from 'vs/base/common/mime'; +import { guessMimeTypes, registerTextMime } from 'vs/base/common/mime'; import { URI } from 'vs/base/common/uri'; suite('Mime', () => { @@ -126,53 +126,4 @@ suite('Mime', () => { assert.deepEqual(guessMimeTypes(URI.parse(`data:;label:something.data;description:data,`)), ['text/data', 'text/plain']); }); - - test('Filename Suggestion - Suggest prefix only when there are no relevant extensions', () => { - const id = 'plumbus0'; - const mime = `text/${id}`; - for (let extension of ['one', 'two']) { - registerTextMime({ id, mime, extension }); - } - - let suggested = suggestFilename('shleem', 'Untitled-1'); - assert.equal(suggested, 'Untitled-1'); - }); - - test('Filename Suggestion - Suggest prefix with first extension that begins with a dot', () => { - const id = 'plumbus1'; - const mime = `text/${id}`; - for (let extension of ['plumbus', '.shleem', '.gazorpazorp']) { - registerTextMime({ id, mime, extension }); - } - - let suggested = suggestFilename('plumbus1', 'Untitled-1'); - assert.equal(suggested, 'Untitled-1.shleem'); - }); - - test('Filename Suggestion - Suggest first relevant extension when there are none that begin with a dot', () => { - const id = 'plumbus2'; - const mime = `text/${id}`; - for (let extension of ['plumbus', 'shleem', 'gazorpazorp']) { - registerTextMime({ id, mime, extension }); - } - - let suggested = suggestFilename('plumbus2', 'Untitled-1'); - assert.equal(suggested, 'plumbus'); - }); - - test('Filename Suggestion - Should ignore user-configured associations', () => { - registerTextMime({ id: 'plumbus3', mime: 'text/plumbus3', extension: 'plumbus', userConfigured: true }); - registerTextMime({ id: 'plumbus3', mime: 'text/plumbus3', extension: '.shleem', userConfigured: true }); - registerTextMime({ id: 'plumbus3', mime: 'text/plumbus3', extension: '.gazorpazorp', userConfigured: false }); - - let suggested = suggestFilename('plumbus3', 'Untitled-1'); - assert.equal(suggested, 'Untitled-1.gazorpazorp'); - - registerTextMime({ id: 'plumbus4', mime: 'text/plumbus4', extension: 'plumbus', userConfigured: true }); - registerTextMime({ id: 'plumbus4', mime: 'text/plumbus4', extension: '.shleem', userConfigured: true }); - registerTextMime({ id: 'plumbus4', mime: 'text/plumbus4', extension: '.gazorpazorp', userConfigured: true }); - - suggested = suggestFilename('plumbus4', 'Untitled-1'); - assert.equal(suggested, 'Untitled-1'); - }); }); diff --git a/src/vs/workbench/test/browser/api/mock.ts b/src/vs/base/test/common/mock.ts similarity index 100% rename from src/vs/workbench/test/browser/api/mock.ts rename to src/vs/base/test/common/mock.ts diff --git a/src/vs/base/test/common/resources.test.ts b/src/vs/base/test/common/resources.test.ts index 9732aacf05a..1e0d4496e26 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, getComparisonKey } 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'; @@ -235,16 +235,19 @@ suite('Resources', () => { } }); - function assertEqualURI(actual: URI, expected: URI, message?: string) { - if (!isEqual(expected, actual, hasToIgnoreCase(expected), false)) { + 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); } } @@ -254,14 +257,14 @@ 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/'), 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); @@ -346,10 +349,17 @@ suite('Resources', () => { }); - function assertIsEqual(u1: URI, u2: URI, ignoreCase: boolean, expected: boolean) { - assert.equal(isEqual(u1, u2, ignoreCase), expected, `${u1.toString()}${expected ? '===' : '!=='}${u2.toString()}`); - assert.equal(getComparisonKey(u1, ignoreCase) === getComparisonKey(u2, ignoreCase), expected, `comparison keys ${u1.toString()}, ${u2.toString()}`); - assert.equal(isEqualOrParent(u1, u2, ignoreCase), expected, `isEqualOrParent ${u1.toString()}, ${u2.toString()}`); + 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); + } } @@ -358,7 +368,7 @@ suite('Resources', () => { let fileURI2 = isWindows ? URI.file('C:\\foo\\Bar') : URI.file('/foo/Bar'); assertIsEqual(fileURI, fileURI, true, true); assertIsEqual(fileURI, fileURI, false, true); - assertIsEqual(fileURI, fileURI, hasToIgnoreCase(fileURI), true); + assertIsEqual(fileURI, fileURI, undefined, true); assertIsEqual(fileURI, fileURI2, true, true); assertIsEqual(fileURI, fileURI2, false, false); @@ -366,13 +376,15 @@ suite('Resources', () => { let fileURI4 = URI.parse('foo://server:453/foo/Bar'); assertIsEqual(fileURI3, fileURI3, true, true); assertIsEqual(fileURI3, fileURI3, false, true); - assertIsEqual(fileURI3, fileURI3, hasToIgnoreCase(fileURI3), true); + assertIsEqual(fileURI3, fileURI3, undefined, true); assertIsEqual(fileURI3, fileURI4, true, true); assertIsEqual(fileURI3, fileURI4, false, false); assertIsEqual(fileURI, fileURI3, true, false); - assertIsEqual(URI.parse('foo://server'), URI.parse('foo://server/'), true, true); + assertIsEqual(URI.parse('file://server'), URI.parse('file://server/'), true, true); + assertIsEqual(URI.parse('http://server'), URI.parse('http://server/'), true, true); + assertIsEqual(URI.parse('foo://server'), URI.parse('foo://server/'), true, false); // only selected scheme have / as the default path assertIsEqual(URI.parse('foo://server/foo'), URI.parse('foo://server/foo/'), true, false); assertIsEqual(URI.parse('foo://server/foo'), URI.parse('foo://server/foo?'), true, true); @@ -383,38 +395,39 @@ suite('Resources', () => { assertIsEqual(fileURI5, fileURI3, true, false); assertIsEqual(fileURI6, fileURI6, true, true); assertIsEqual(fileURI6, fileURI5, true, false); - assertIsEqual(fileURI6, fileURI3, true, true); + 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(isEqualOrParent(fileURI6, fileURI5, true), false, '17'); - assert.equal(isEqualOrParent(fileURI6, fileURI6, true), true, '18'); - assert.equal(isEqualOrParent(fileURI7, fileURI6, true), true, '19'); - assert.equal(isEqualOrParent(fileURI7, fileURI5, true), false, '20'); + assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI6, fileURI5), false, '17'); + assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI6, fileURI6), true, '18'); + assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI7, fileURI6), true, '19'); + assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI7, fileURI5), false, '20'); }); }); diff --git a/src/vs/base/test/common/skipList.test.ts b/src/vs/base/test/common/skipList.test.ts new file mode 100644 index 00000000000..f6a083e3cb2 --- /dev/null +++ b/src/vs/base/test/common/skipList.test.ts @@ -0,0 +1,218 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { SkipList } from 'vs/base/common/skipList'; +import { StopWatch } from 'vs/base/common/stopwatch'; +import { binarySearch } from 'vs/base/common/arrays'; + + +suite('SkipList', function () { + + function assertValues(list: SkipList, expected: V[]) { + assert.equal(list.size, expected.length); + assert.deepEqual([...list.values()], expected); + + let valuesFromEntries = [...list.entries()].map(entry => entry[1]); + assert.deepEqual(valuesFromEntries, expected); + + let valuesFromIter = [...list].map(entry => entry[1]); + assert.deepEqual(valuesFromIter, expected); + + let i = 0; + list.forEach((value, _key, map) => { + assert.ok(map === list); + assert.deepEqual(value, expected[i++]); + }); + } + + function assertKeys(list: SkipList, expected: K[]) { + assert.equal(list.size, expected.length); + assert.deepEqual([...list.keys()], expected); + + let keysFromEntries = [...list.entries()].map(entry => entry[0]); + assert.deepEqual(keysFromEntries, expected); + + let keysFromIter = [...list].map(entry => entry[0]); + assert.deepEqual(keysFromIter, expected); + + let i = 0; + list.forEach((_value, key, map) => { + assert.ok(map === list); + assert.deepEqual(key, expected[i++]); + }); + } + + test('set/get/delete', function () { + let list = new SkipList((a, b) => a - b); + + assert.equal(list.get(3), undefined); + list.set(3, 1); + assert.equal(list.get(3), 1); + assertValues(list, [1]); + + list.set(3, 3); + assertValues(list, [3]); + + list.set(1, 1); + list.set(4, 4); + assert.equal(list.get(3), 3); + assert.equal(list.get(1), 1); + assert.equal(list.get(4), 4); + assertValues(list, [1, 3, 4]); + + assert.equal(list.delete(17), false); + + assert.equal(list.delete(1), true); + assert.equal(list.get(1), undefined); + assert.equal(list.get(3), 3); + assert.equal(list.get(4), 4); + + assertValues(list, [3, 4]); + }); + + test('Figure 3', function () { + let list = new SkipList((a, b) => a - b); + list.set(3, true); + list.set(6, true); + list.set(7, true); + list.set(9, true); + list.set(12, true); + list.set(19, true); + list.set(21, true); + list.set(25, true); + + assertKeys(list, [3, 6, 7, 9, 12, 19, 21, 25]); + + list.set(17, true); + assert.deepEqual(list.size, 9); + assertKeys(list, [3, 6, 7, 9, 12, 17, 19, 21, 25]); + }); + + test('capacity max', function () { + let list = new SkipList((a, b) => a - b, 10); + list.set(1, true); + list.set(2, true); + list.set(3, true); + list.set(4, true); + list.set(5, true); + list.set(6, true); + list.set(7, true); + list.set(8, true); + list.set(9, true); + list.set(10, true); + list.set(11, true); + list.set(12, true); + + assertKeys(list, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + }); + + const cmp = (a: number, b: number): number => { + if (a < b) { + return -1; + } else if (a > b) { + return 1; + } else { + return 0; + } + }; + + function insertArraySorted(array: number[], element: number) { + let idx = binarySearch(array, element, cmp); + if (idx >= 0) { + array[idx] = element; + } else { + idx = ~idx; + // array = array.slice(0, idx).concat(element, array.slice(idx)); + array.splice(idx, 0, element); + } + return array; + } + + function delArraySorted(array: number[], element: number) { + let idx = binarySearch(array, element, cmp); + if (idx >= 0) { + // array = array.slice(0, idx).concat(array.slice(idx)); + array.splice(idx, 1); + } + return array; + } + + + test('perf', function () { + this.skip(); + + // data + const max = 2 ** 16; + const values = new Set(); + for (let i = 0; i < max; i++) { + let value = Math.floor(Math.random() * max); + values.add(value); + } + console.log(values.size); + + // init + let list = new SkipList(cmp, max); + let sw = new StopWatch(true); + values.forEach(value => list.set(value, true)); + sw.stop(); + console.log(`[LIST] ${list.size} elements after ${sw.elapsed()}ms`); + let array: number[] = []; + sw = new StopWatch(true); + values.forEach(value => array = insertArraySorted(array, value)); + sw.stop(); + console.log(`[ARRAY] ${array.length} elements after ${sw.elapsed()}ms`); + + // get + sw = new StopWatch(true); + let someValues = [...values].slice(0, values.size / 4); + someValues.forEach(key => { + let value = list.get(key); // find + console.assert(value, '[LIST] must have ' + key); + list.get(-key); // miss + }); + sw.stop(); + console.log(`[LIST] retrieve ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`); + sw = new StopWatch(true); + someValues.forEach(key => { + let idx = binarySearch(array, key, cmp); // find + console.assert(idx >= 0, '[ARRAY] must have ' + key); + binarySearch(array, -key, cmp); // miss + }); + sw.stop(); + console.log(`[ARRAY] retrieve ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`); + + + // insert + sw = new StopWatch(true); + someValues.forEach(key => { + list.set(-key, false); + }); + sw.stop(); + console.log(`[LIST] insert ${sw.elapsed()}ms (${(sw.elapsed() / someValues.length).toPrecision(4)}ms/op)`); + sw = new StopWatch(true); + someValues.forEach(key => { + array = insertArraySorted(array, -key); + }); + sw.stop(); + console.log(`[ARRAY] insert ${sw.elapsed()}ms (${(sw.elapsed() / someValues.length).toPrecision(4)}ms/op)`); + + // delete + sw = new StopWatch(true); + someValues.forEach(key => { + list.delete(key); // find + list.delete(-key); // miss + }); + sw.stop(); + console.log(`[LIST] delete ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`); + sw = new StopWatch(true); + someValues.forEach(key => { + array = delArraySorted(array, key); // find + array = delArraySorted(array, -key); // miss + }); + sw.stop(); + console.log(`[ARRAY] delete ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`); + }); +}); diff --git a/src/vs/base/test/common/stream.test.ts b/src/vs/base/test/common/stream.test.ts index 6d86fbc2eb2..0ef3b7730e9 100644 --- a/src/vs/base/test/common/stream.test.ts +++ b/src/vs/base/test/common/stream.test.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { isReadableStream, newWriteableStream, Readable, consumeReadable, consumeReadableWithLimit, consumeStream, ReadableStream, toStream, toReadable, transform, consumeStreamWithLimit } from 'vs/base/common/stream'; +import { isReadableStream, newWriteableStream, Readable, consumeReadable, peekReadable, consumeStream, ReadableStream, toStream, toReadable, transform, peekStream, isReadableBufferedStream } from 'vs/base/common/stream'; +import { timeout } from 'vs/base/common/async'; suite('Stream', () => { @@ -13,7 +14,16 @@ suite('Stream', () => { assert.ok(isReadableStream(newWriteableStream(d => d))); }); - test('WriteableStream', () => { + test('isReadableBufferedStream', async () => { + assert.ok(!isReadableBufferedStream(Object.create(null))); + + const stream = newWriteableStream(d => d); + stream.end(); + const bufferedStream = await peekStream(stream, 1); + assert.ok(isReadableBufferedStream(bufferedStream)); + }); + + test('WriteableStream - basics', () => { const stream = newWriteableStream(strings => strings.join()); let error = false; @@ -66,17 +76,92 @@ suite('Stream', () => { assert.equal(chunks.length, 4); }); + test('WriteableStream - removeListener', () => { + const stream = newWriteableStream(strings => strings.join()); + + let error = false; + const errorListener = (e: Error) => { + error = true; + }; + stream.on('error', errorListener); + + let data = false; + const dataListener = () => { + data = true; + }; + stream.on('data', dataListener); + + stream.write('Hello'); + assert.equal(data, true); + + data = false; + stream.removeListener('data', dataListener); + + stream.write('World'); + assert.equal(data, false); + + stream.error(new Error()); + assert.equal(error, true); + + error = false; + stream.removeListener('error', errorListener); + + stream.error(new Error()); + assert.equal(error, false); + }); + + test('WriteableStream - highWaterMark', async () => { + const stream = newWriteableStream(strings => strings.join(), { highWaterMark: 3 }); + + let res = stream.write('1'); + assert.ok(!res); + + res = stream.write('2'); + assert.ok(!res); + + res = stream.write('3'); + assert.ok(!res); + + let promise1 = stream.write('4'); + assert.ok(promise1 instanceof Promise); + + let promise2 = stream.write('5'); + assert.ok(promise2 instanceof Promise); + + let drained1 = false; + (async () => { + await promise1; + drained1 = true; + })(); + + let drained2 = false; + (async () => { + await promise2; + drained2 = true; + })(); + + let data: string | undefined = undefined; + stream.on('data', chunk => { + data = chunk; + }); + assert.ok(data); + + await timeout(0); + assert.equal(drained1, true); + assert.equal(drained2, true); + }); + test('consumeReadable', () => { const readable = arrayToReadable(['1', '2', '3', '4', '5']); const consumed = consumeReadable(readable, strings => strings.join()); assert.equal(consumed, '1,2,3,4,5'); }); - test('consumeReadableWithLimit', () => { + test('peekReadable', () => { for (let i = 0; i < 5; i++) { const readable = arrayToReadable(['1', '2', '3', '4', '5']); - const consumedOrReadable = consumeReadableWithLimit(readable, strings => strings.join(), i); + const consumedOrReadable = peekReadable(readable, strings => strings.join(), i); if (typeof consumedOrReadable === 'string') { assert.fail('Unexpected result'); } else { @@ -86,14 +171,75 @@ suite('Stream', () => { } let readable = arrayToReadable(['1', '2', '3', '4', '5']); - let consumedOrReadable = consumeReadableWithLimit(readable, strings => strings.join(), 5); + let consumedOrReadable = peekReadable(readable, strings => strings.join(), 5); assert.equal(consumedOrReadable, '1,2,3,4,5'); readable = arrayToReadable(['1', '2', '3', '4', '5']); - consumedOrReadable = consumeReadableWithLimit(readable, strings => strings.join(), 6); + consumedOrReadable = peekReadable(readable, strings => strings.join(), 6); assert.equal(consumedOrReadable, '1,2,3,4,5'); }); + test('peekReadable - error handling', async () => { + + // 0 Chunks + let stream = newWriteableStream(data => data); + + let error: Error | undefined = undefined; + let promise = (async () => { + try { + await peekStream(stream, 1); + } catch (err) { + error = err; + } + })(); + + stream.error(new Error()); + await promise; + + assert.ok(error); + + // 1 Chunk + stream = newWriteableStream(data => data); + + error = undefined; + promise = (async () => { + try { + await peekStream(stream, 1); + } catch (err) { + error = err; + } + })(); + + stream.write('foo'); + stream.error(new Error()); + await promise; + + assert.ok(error); + + // 2 Chunks + stream = newWriteableStream(data => data); + + error = undefined; + promise = (async () => { + try { + await peekStream(stream, 1); + } catch (err) { + error = err; + } + })(); + + stream.write('foo'); + stream.write('bar'); + stream.error(new Error()); + await promise; + + assert.ok(!error); + + stream.on('error', err => error = err); + stream.on('data', chunk => { }); + assert.ok(error); + }); + function arrayToReadable(array: T[]): Readable { return { read: () => array.shift() || null @@ -122,26 +268,39 @@ suite('Stream', () => { assert.equal(consumed, '1,2,3,4,5'); }); - test('consumeStreamWithLimit', async () => { + test('peekStream', async () => { for (let i = 0; i < 5; i++) { - const readable = readableToStream(arrayToReadable(['1', '2', '3', '4', '5'])); + const stream = readableToStream(arrayToReadable(['1', '2', '3', '4', '5'])); - const consumedOrStream = await consumeStreamWithLimit(readable, strings => strings.join(), i); - if (typeof consumedOrStream === 'string') { - assert.fail('Unexpected result'); + const result = await peekStream(stream, i); + assert.equal(stream, result.stream); + if (result.ended) { + assert.fail('Unexpected result, stream should not have ended yet'); } else { - const consumed = await consumeStream(consumedOrStream, strings => strings.join()); - assert.equal(consumed, '1,2,3,4,5'); + assert.equal(result.buffer.length, i + 1, `maxChunks: ${i}`); + + const additionalResult: string[] = []; + await consumeStream(stream, strings => { + additionalResult.push(...strings); + + return strings.join(); + }); + + assert.equal([...result.buffer, ...additionalResult].join(), '1,2,3,4,5'); } } let stream = readableToStream(arrayToReadable(['1', '2', '3', '4', '5'])); - let consumedOrStream = await consumeStreamWithLimit(stream, strings => strings.join(), 5); - assert.equal(consumedOrStream, '1,2,3,4,5'); + let result = await peekStream(stream, 5); + assert.equal(stream, result.stream); + assert.equal(result.buffer.join(), '1,2,3,4,5'); + assert.equal(result.ended, true); stream = readableToStream(arrayToReadable(['1', '2', '3', '4', '5'])); - consumedOrStream = await consumeStreamWithLimit(stream, strings => strings.join(), 6); - assert.equal(consumedOrStream, '1,2,3,4,5'); + result = await peekStream(stream, 6); + assert.equal(stream, result.stream); + assert.equal(result.buffer.join(), '1,2,3,4,5'); + assert.equal(result.ended, true); }); test('toStream', async () => { diff --git a/src/vs/base/test/node/buffer.test.ts b/src/vs/base/test/node/buffer.test.ts index 69dfb89e8a1..4c7d0ba3a2f 100644 --- a/src/vs/base/test/node/buffer.test.ts +++ b/src/vs/base/test/node/buffer.test.ts @@ -4,8 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { VSBuffer, bufferToReadable, readableToBuffer, bufferToStream, streamToBuffer, newWriteableBufferStream } from 'vs/base/common/buffer'; +import { VSBuffer, bufferToReadable, readableToBuffer, bufferToStream, streamToBuffer, newWriteableBufferStream, bufferedStreamToBuffer } from 'vs/base/common/buffer'; import { timeout } from 'vs/base/common/async'; +import { peekStream } from 'vs/base/common/stream'; suite('Buffer', () => { @@ -29,6 +30,13 @@ suite('Buffer', () => { assert.equal((await streamToBuffer(stream)).toString(), content); }); + test('bufferedStreamToBuffer', async () => { + const content = 'Hello World'; + const stream = await peekStream(bufferToStream(VSBuffer.fromString(content)), 1); + + assert.equal((await bufferedStreamToBuffer(stream)).toString(), content); + }); + test('bufferWriteableStream - basics (no error)', async () => { const stream = newWriteableBufferStream(); diff --git a/src/vs/base/test/node/path.test.ts b/src/vs/base/test/node/path.test.ts index 14ef9c92df1..3150f8d6094 100644 --- a/src/vs/base/test/node/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}`); } @@ -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}`); } @@ -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}`); } @@ -585,9 +581,7 @@ suite('Paths (Node Implementation)', () => { 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/worker/defaultWorkerFactory.ts b/src/vs/base/worker/defaultWorkerFactory.ts index 45f3b41a457..1f2d5dbb79e 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/code/browser/workbench/callback.html b/src/vs/code/browser/workbench/callback.html index da6c907b666..9aec539bb55 100644 --- a/src/vs/code/browser/workbench/callback.html +++ b/src/vs/code/browser/workbench/callback.html @@ -35,7 +35,7 @@ display: flex; flex-direction: column; color: white; - font-family: "Segoe UI", "Helvetica Neue", "Helvetica", Arial, sans-serif; + font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", system-ui, "Ubuntu", "Droid Sans", sans-serif; background-color: #373277; } diff --git a/src/vs/code/browser/workbench/workbench-dev.html b/src/vs/code/browser/workbench/workbench-dev.html index d7547c626a0..6196015ad64 100644 --- a/src/vs/code/browser/workbench/workbench-dev.html +++ b/src/vs/code/browser/workbench/workbench-dev.html @@ -2,6 +2,10 @@ + @@ -10,6 +14,9 @@ + + + @@ -26,19 +33,24 @@ // NOTE: Changes to inline scripts require update of content security policy self.require = { baseUrl: `${window.location.origin}/static/out`, + recordStats: true, paths: { 'vscode-textmate': `${window.location.origin}/static/remote/web/node_modules/vscode-textmate/release/main`, 'vscode-oniguruma': `${window.location.origin}/static/remote/web/node_modules/vscode-oniguruma/release/main`, 'xterm': `${window.location.origin}/static/remote/web/node_modules/xterm/lib/xterm.js`, 'xterm-addon-search': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-search/lib/xterm-addon-search.js`, 'xterm-addon-unicode11': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js`, - 'xterm-addon-web-links': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`, 'xterm-addon-webgl': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`, 'semver-umd': `${window.location.origin}/static/remote/web/node_modules/semver-umd/lib/semver-umd.js`, + 'iconv-lite-umd': `${window.location.origin}/static/remote/web/node_modules/iconv-lite-umd/lib/iconv-lite-umd.js`, + 'jschardet': `${window.location.origin}/static/remote/web/node_modules/jschardet/dist/jschardet.min.js`, } }; + @@ -30,19 +34,24 @@ // NOTE: Changes to inline scripts require update of content security policy self.require = { baseUrl: `${window.location.origin}/static/out`, + recordStats: true, paths: { 'vscode-textmate': `${window.location.origin}/static/node_modules/vscode-textmate/release/main`, 'vscode-oniguruma': `${window.location.origin}/static/node_modules/vscode-oniguruma/release/main`, 'xterm': `${window.location.origin}/static/node_modules/xterm/lib/xterm.js`, 'xterm-addon-search': `${window.location.origin}/static/node_modules/xterm-addon-search/lib/xterm-addon-search.js`, 'xterm-addon-unicode11': `${window.location.origin}/static/node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js`, - 'xterm-addon-web-links': `${window.location.origin}/static/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`, 'xterm-addon-webgl': `${window.location.origin}/static/node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`, 'semver-umd': `${window.location.origin}/static/node_modules/semver-umd/lib/semver-umd.js`, + 'iconv-lite-umd': `${window.location.origin}/static/node_modules/iconv-lite-umd/lib/iconv-lite-umd.js`, + 'jschardet': `${window.location.origin}/static/node_modules/jschardet/dist/jschardet.min.js`, } }; + diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 556c03a03ab..c629f7fffa1 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -12,6 +12,7 @@ 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'; interface ICredential { service: string; @@ -345,6 +346,11 @@ class WorkspaceProvider implements IWorkspaceProvider { // Finally create workbench create(document.body, { ...config, + homeIndicator: { + href: 'https://github.com/Microsoft/vscode', + icon: 'code', + title: localize('home', "Home") + }, workspaceProvider: new WorkspaceProvider(workspace, payload), urlCallbackProvider: new PollingURLCallbackProvider(), credentialsProvider: new LocalStorageCredentialsProvider() diff --git a/src/vs/code/buildfile.js b/src/vs/code/buildfile.js index 221bea21443..9b1ee16680e 100644 --- a/src/vs/code/buildfile.js +++ b/src/vs/code/buildfile.js @@ -22,10 +22,9 @@ exports.collectModules = function () { createModuleDescription('vs/code/electron-main/main', []), createModuleDescription('vs/code/node/cli', []), createModuleDescription('vs/code/node/cliProcessMain', ['vs/code/node/cli']), - createModuleDescription('vs/code/electron-browser/issue/issueReporterMain', []), + createModuleDescription('vs/code/electron-sandbox/issue/issueReporterMain', []), createModuleDescription('vs/code/electron-browser/sharedProcess/sharedProcessMain', []), - createModuleDescription('vs/code/electron-browser/issue/issueReporterMain', []), createModuleDescription('vs/platform/driver/node/driver', []), - createModuleDescription('vs/code/electron-browser/processExplorer/processExplorerMain', []) + createModuleDescription('vs/code/electron-sandbox/processExplorer/processExplorerMain', []) ]; }; diff --git a/src/vs/code/electron-browser/issue/issueReporter.html b/src/vs/code/electron-browser/issue/issueReporter.html deleted file mode 100644 index 695de78a4cb..00000000000 --- a/src/vs/code/electron-browser/issue/issueReporter.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/src/vs/code/electron-browser/processExplorer/media/processExplorer.css b/src/vs/code/electron-browser/processExplorer/media/processExplorer.css deleted file mode 100644 index 606ec4c84a8..00000000000 --- a/src/vs/code/electron-browser/processExplorer/media/processExplorer.css +++ /dev/null @@ -1,102 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -html { - font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif; - font-size: 13px; -} - -html:lang(zh-Hans) { - font-family: system-ui, -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: system-ui, -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: system-ui, -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: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Malgun Gothic", "Nanum Gothic", "Dotom", "Apple SD Gothic Neo", "AppleGothic", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; -} - -body { - margin: 0; - padding: 0; - height: 100%; - width: 100%; - user-select: none; - color: #cccccc; -} - -.cpu { - width: 45px; -} - -.pid { - width: 50px -} - -.memory { - width: 90px; -} - -.process-item { - line-height: 22px; -} - -table { - border-collapse: collapse; - width: 100%; - table-layout: fixed; -} -th[scope='col'] { - vertical-align: bottom; - border-bottom: 1px solid #cccccc; - padding: .5rem; - border-top: 1px solid #cccccc; - cursor: default; -} -td { - padding: .25rem; - vertical-align: top; - cursor: default; -} - -.centered { - text-align: center; -} - -.nameLabel{ - text-align: left; -} - -.data { - white-space: pre; - padding-left: .5rem; - font-weight: normal; - text-align: left; - height: 22px; -} - -.error { - padding-left: 20px; - white-space: nowrap; -} - -tbody > tr:hover { - background-color: #2A2D2E; -} - -.hidden { - display: none; -} - -img { - width: 16px; - margin-right: 4px; -} diff --git a/src/vs/code/electron-browser/processExplorer/processExplorer.html b/src/vs/code/electron-browser/processExplorer/processExplorer.html deleted file mode 100644 index 3ef3be23f8f..00000000000 --- a/src/vs/code/electron-browser/processExplorer/processExplorer.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - -
- - - - - - \ No newline at end of file diff --git a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts deleted file mode 100644 index 9684cbd4c20..00000000000 --- a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts +++ /dev/null @@ -1,423 +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 => { - if (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 f0fc7231e34..00000000000 --- a/src/vs/code/electron-browser/proxy/auth.html +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - -

-
-

-
-

-

-

- - -

-
-
- - - - - diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts index 7672ec3c7d8..44dfe650c6f 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts @@ -35,7 +35,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 => { @@ -44,7 +44,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 26890a9fc6b..07fd9bd0478 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcess.html +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcess.html @@ -11,7 +11,12 @@ Shared Process + + + + + - \ No newline at end of file + diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcess.js b/src/vs/code/electron-browser/sharedProcess/sharedProcess.js index 2ecd5f650a8..54815a8ed1b 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 927e10e7577..1e8351687cf 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -27,7 +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 { 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,26 +35,25 @@ import { ILocalizationsService } from 'vs/platform/localizations/common/localiza import { combinedDisposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { DownloadService } from 'vs/platform/download/common/downloadService'; import { IDownloadService } from 'vs/platform/download/common/download'; -import { IChannel, IServerChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc'; -import { createChannelSender, createChannelReceiver } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel, StaticRouter, createChannelSender, createChannelReceiver } from 'vs/base/parts/ipc/common/ipc'; import { NodeCachedDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner'; import { LanguagePackCachedDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner'; import { StorageDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner'; import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner'; -import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; import { SpdLogService } from 'vs/platform/log/node/spdlogService'; import { DiagnosticsService, IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService'; import { DiagnosticsChannel } from 'vs/platform/diagnostics/node/diagnosticsIpc'; import { FileService } from 'vs/platform/files/common/fileService'; import { IFileService } from 'vs/platform/files/common/files'; -import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider'; +import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { Schemas } from 'vs/base/common/network'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync'; +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 } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; -import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, StorageKeysSyncRegistryChannelClient } from 'vs/platform/userDataSync/common/userDataSyncIpc'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { UserDataSyncStoreService, UserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; +import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, 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 { ICredentialsService } from 'vs/platform/credentials/common/credentials'; @@ -64,12 +63,12 @@ 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 { UserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSyncEnablementService'; -import { IAuthenticationTokenService, AuthenticationTokenService } from 'vs/platform/authentication/common/authentication'; -import { AuthenticationTokenServiceChannel } from 'vs/platform/authentication/common/authenticationIpc'; +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; @@ -89,9 +88,13 @@ interface ISharedProcessInitData { 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); @@ -109,7 +112,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat const onExit = () => disposables.dispose(); process.once('exit', onExit); - ipcRenderer.once('electron-main->shared-process: exit', onExit); + ipcRenderer.once('vscode:electron-main->shared-process=exit', onExit); disposables.add(server); @@ -195,13 +198,15 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat services.set(IExtensionTipsService, new SyncDescriptor(ExtensionTipsService)); services.set(ICredentialsService, new SyncDescriptor(KeytarCredentialsService)); - services.set(IAuthenticationTokenService, new SyncDescriptor(AuthenticationTokenService)); + services.set(IUserDataSyncAccountService, new SyncDescriptor(UserDataSyncAccountService)); services.set(IUserDataSyncLogService, new SyncDescriptor(UserDataSyncLogService)); 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(IUserDataSyncEnablementService, new SyncDescriptor(UserDataSyncEnablementService)); + services.set(IUserDataSyncResourceEnablementService, new SyncDescriptor(UserDataSyncResourceEnablementService)); services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService)); registerConfiguration(); @@ -225,12 +230,20 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat const extensionTipsChannel = new ExtensionTipsChannel(extensionTipsService); server.registerChannel('extensionTipsService', extensionTipsChannel); - const authTokenService = accessor.get(IAuthenticationTokenService); - const authTokenChannel = new AuthenticationTokenServiceChannel(authTokenService); - server.registerChannel('authToken', authTokenChannel); + 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); @@ -292,17 +305,17 @@ async function handshake(configuration: ISharedProcessConfiguration): Promise(c => { - ipcRenderer.once('electron-main->shared-process: payload', (_: any, r: ISharedProcessInitData) => c(r)); + 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('shared-process->electron-main: ready-for-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('shared-process->electron-main: ipc-ready'); + 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('shared-process->electron-main: init-done'); + 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 4268647aabb..40737461d29 100644 --- a/src/vs/code/electron-browser/workbench/workbench.html +++ b/src/vs/code/electron-browser/workbench/workbench.html @@ -3,11 +3,16 @@ - + - + + + + + + diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index 022394edd75..d5b7ff6728e 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -3,13 +3,35 @@ * 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(); @@ -24,7 +46,10 @@ bootstrapWindow.load([ 'vs/css!vs/workbench/workbench.desktop.main' ], function (workbench, configuration) { + + // Mark start of workbench perf.mark('didLoadWorkbenchMain'); + performance.mark('workbench-start'); return process['lazyEnv'].then(function () { perf.mark('main/startup'); @@ -32,23 +57,26 @@ bootstrapWindow.load([ // @ts-ignore return require('vs/workbench/electron-browser/desktop.main').main(configuration); }); - }, { - removeDeveloperKeybindingsAfterLoad: true, - canModifyDOM: function (windowConfig) { - showPartsSplash(windowConfig); }, - beforeLoaderConfig: function (windowConfig, loaderConfig) { - loaderConfig.recordStats = true; - }, - beforeRequire: function () { - perf.mark('willLoadWorkbenchMain'); + { + removeDeveloperKeybindingsAfterLoad: true, + canModifyDOM: function (windowConfig) { + showPartsSplash(windowConfig); + }, + beforeLoaderConfig: function (windowConfig, loaderConfig) { + loaderConfig.recordStats = true; + }, + beforeRequire: function () { + perf.mark('willLoadWorkbenchMain'); + } } -}); +); /** * @param {{ * partsSplashPath?: string, * highContrast?: boolean, + * defaultThemeType?: string, * extensionDevelopmentPath?: string[], * folderUri?: object, * workspace?: object @@ -60,7 +88,7 @@ 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 } @@ -77,13 +105,27 @@ 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 (configuration.highContrast || configuration.defaultThemeType === 'hc') { + baseTheme = 'hc-black'; + shellBackground = '#000000'; + shellForeground = '#FFFFFF'; + } else if (configuration.defaultThemeType === 'vs') { + baseTheme = 'vs'; + shellBackground = '#FFFFFF'; + shellForeground = '#000000'; + } 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; }`; if (data && data.layoutInfo) { @@ -91,6 +133,7 @@ function showPartsSplash(configuration) { const { id, layoutInfo, colorInfo } = data; const splash = document.createElement('div'); splash.id = id; + splash.className = baseTheme; if (layoutInfo.windowBorder) { splash.style.position = 'relative'; @@ -133,8 +176,7 @@ function showPartsSplash(configuration) { * @returns {Promise} */ function getLazyEnv() { - // @ts-ignore - const ipc = require('electron').ipcRenderer; + const ipcRenderer = bootstrapWindow.globals().ipcRenderer; return new Promise(function (resolve) { const handle = setTimeout(function () { @@ -142,13 +184,13 @@ function getLazyEnv() { console.warn('renderer did not receive lazyEnv in time'); }, 10000); - ipc.once('vscode:acceptShellEnv', function (event, shellEnv) { + ipcRenderer.once('vscode:acceptShellEnv', function (event, shellEnv) { clearTimeout(handle); - bootstrapWindow.assign(process.env, shellEnv); + Object.assign(process.env, shellEnv); // @ts-ignore resolve(process.env); }); - ipc.send('vscode:fetchShellEnv'); + ipcRenderer.send('vscode:fetchShellEnv'); }); } diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index fd287c25045..8b726400b97 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -3,12 +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, dialog, session } 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 { IWindowOpenable } from 'vs/platform/windows/common/windows'; import { OpenContext } from 'vs/platform/windows/node/window'; -import { ActiveWindowManager } from 'vs/code/node/activeWindowTracker'; 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'; @@ -27,17 +26,17 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IURLService } from 'vs/platform/url/common/url'; import { URLHandlerChannelClient, URLHandlerRouter } from 'vs/platform/url/common/urlIpc'; -import { ITelemetryService, machineIdKey, trueMachineIdKey } 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'; @@ -45,14 +44,12 @@ import { getMachineId } from 'vs/base/node/id'; import { Win32UpdateService } from 'vs/platform/update/electron-main/updateService.win32'; import { LinuxUpdateService } from 'vs/platform/update/electron-main/updateService.linux'; import { DarwinUpdateService } from 'vs/platform/update/electron-main/updateService.darwin'; -import { IIssueService } from 'vs/platform/issue/node/issue'; -import { IssueMainService } from 'vs/platform/issue/electron-main/issueMainService'; +import { IssueMainService, IIssueMainService } from 'vs/platform/issue/electron-main/issueMainService'; import { LoggerChannel } from 'vs/platform/log/common/logIpc'; import { setUnexpectedErrorHandler, onUnexpectedError } from 'vs/base/common/errors'; import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener'; import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver'; -import { IMenubarService } from 'vs/platform/menubar/node/menubar'; -import { MenubarMainService } from 'vs/platform/menubar/electron-main/menubarMainService'; +import { IMenubarMainService, MenubarMainService } from 'vs/platform/menubar/electron-main/menubarMainService'; import { RunOnceScheduler } from 'vs/base/common/async'; import { registerContextMenuListener } from 'vs/base/parts/contextmenu/electron-main/contextmenu'; import { homedir } from 'os'; @@ -65,7 +62,7 @@ import { GlobalStorageDatabaseChannel } from 'vs/platform/storage/node/storageIp 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'; @@ -81,6 +78,10 @@ import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common import { StorageKeysSyncRegistryChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; 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 { createServer, AddressInfo } from 'net'; +import { IOpenExtensionWindowResult } from 'vs/platform/debug/common/extensionHostDebug'; export class CodeApplication extends Disposable { private windowsMainService: IWindowsMainService | undefined; @@ -133,7 +134,11 @@ export class CodeApplication extends Disposable { // // !!! DO NOT CHANGE without consulting the documentation !!! // - // app.on('remote-get-guest-web-contents', event => event.preventDefault()); // TODO@Matt revisit this need for + app.on('remote-get-guest-web-contents', event => { + this.logService.trace('App#on(remote-get-guest-web-contents): prevented'); + + event.preventDefault(); + }); app.on('remote-require', (event, sender, module) => { this.logService.trace('App#on(remote-require): prevented'); @@ -173,11 +178,12 @@ export class CodeApplication extends Disposable { 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%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%20role%3D%22document%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 srcUri.startsWith(rootUri + sep); @@ -262,13 +268,6 @@ export class CodeApplication extends Disposable { } }); - 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; @@ -295,13 +294,6 @@ 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(() => { @@ -366,8 +358,8 @@ 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); @@ -388,7 +380,7 @@ export class CodeApplication extends Disposable { }); // Services - const appInstantiationService = await this.createServices(machineId, trueMachineId, sharedProcess, sharedProcessReady); + const appInstantiationService = await this.createServices(machineId, sharedProcess, sharedProcessReady); // Create driver if (this.environmentService.driverHandle) { @@ -413,32 +405,21 @@ 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 + // 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) { + if (!machineId || (isMacintosh && machineId === '6c9d2bc8f91b89624add29c0abeae7fb42bf539fa1cdb2e3e57cd668fa9bcead')) { machineId = await getMachineId(); this.stateService.setItem(machineIdKey, machineId); } - // Check if machineId is hashed iBridge Device - let trueMachineId: string | undefined; - if (isMacintosh && machineId === '6c9d2bc8f91b89624add29c0abeae7fb42bf539fa1cdb2e3e57cd668fa9bcead') { - trueMachineId = this.stateService.getItem(trueMachineIdKey); - if (!trueMachineId) { - trueMachineId = await getMachineId(); - - this.stateService.setItem(trueMachineIdKey, trueMachineId); - } - } - - return { machineId, trueMachineId }; + return machineId; } - private async createServices(machineId: string, trueMachineId: string | undefined, sharedProcess: SharedProcess, sharedProcessReady: Promise>): Promise { + private async createServices(machineId: string, sharedProcess: SharedProcess, sharedProcessReady: Promise>): Promise { const services = new ServiceCollection(); switch (process.platform) { @@ -467,10 +448,11 @@ export class CodeApplication extends Disposable { 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); @@ -480,7 +462,7 @@ 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 @@ -489,7 +471,7 @@ export class CodeApplication extends Disposable { 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, sendErrorTelemetry: true }; + const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths, sendErrorTelemetry: true }; services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config])); } else { @@ -551,8 +533,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); @@ -568,14 +550,18 @@ 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); @@ -852,20 +838,93 @@ class ElectronExtensionHostDebugBroadcastChannel extends ExtensionHost super(); } - async call(ctx: TContext, command: string, arg?: any): Promise { + 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 this.openExtensionDevelopmentHostWindow(arg[0], arg[1], arg[2]); } else { return super.call(ctx, command, arg); } } + + private async openExtensionDevelopmentHostWindow(args: string[], env: IProcessEnvironment, debugRenderer: boolean): Promise { + const pargs = parseArgs(args, OPTIONS); + const extDevPaths = pargs.extensionDevelopmentPath; + if (!extDevPaths) { + return {}; + } + + const [codeWindow] = this.windowsMainService.openExtensionDevelopmentHostWindow(extDevPaths, { + context: OpenContext.API, + cli: pargs, + userEnv: Object.keys(env).length > 0 ? env : undefined + }); + + if (!debugRenderer) { + return {}; + } + + const debug = codeWindow.win.webContents.debugger; + + let listeners = debug.isAttached() ? Infinity : 0; + const server = createServer(listener => { + if (listeners++ === 0) { + debug.attach(); + } + + let closed = false; + const writeMessage = (message: object) => { + if (!closed) { // in case sendCommand promises settle after closed + listener.write(JSON.stringify(message) + '\0'); // null-delimited, CDP-compatible + } + }; + + const onMessage = (_event: Event, method: string, params: unknown, sessionId?: string) => + writeMessage(({ method, params, sessionId })); + + codeWindow.win.on('close', () => { + debug.removeListener('message', onMessage); + listener.end(); + closed = true; + }); + + debug.addListener('message', onMessage); + + let buf = Buffer.alloc(0); + listener.on('data', data => { + buf = Buffer.concat([buf, data]); + for (let delimiter = buf.indexOf(0); delimiter !== -1; delimiter = buf.indexOf(0)) { + let data: { id: number; sessionId: string; params: {} }; + try { + const contents = buf.slice(0, delimiter).toString('utf8'); + buf = buf.slice(delimiter + 1); + data = JSON.parse(contents); + } catch (e) { + console.error('error reading cdp line', e); + } + + // depends on a new API for which electron.d.ts has not been updated: + // @ts-ignore + debug.sendCommand(data.method, data.params, data.sessionId) + .then((result: object) => writeMessage({ id: data.id, sessionId: data.sessionId, result })) + .catch((error: Error) => writeMessage({ id: data.id, sessionId: data.sessionId, error: { code: 0, message: error.message } })); + } + }); + + listener.on('error', err => { + console.error('error on cdp pipe:', err); + }); + + listener.on('close', () => { + closed = true; + if (--listeners === 0) { + debug.detach(); + } + }); + }); + + await new Promise(r => server.listen(0, r)); + codeWindow.win.on('close', () => server.close()); + + return { rendererDebugPort: (server.address() as AddressInfo).port }; + } } diff --git a/src/vs/code/electron-main/auth.ts b/src/vs/code/electron-main/auth.ts index b74e1c4e9c1..c1e2a84946a 100644 --- a/src/vs/code/electron-main/auth.ts +++ b/src/vs/code/electron-main/auth.ts @@ -6,6 +6,7 @@ import { localize } from 'vs/nls'; import { Disposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; +import { URI } from 'vs/base/common/uri'; import { BrowserWindow, BrowserWindowConstructorOptions, app, AuthInfo, WebContents, Event as ElectronEvent } from 'electron'; type LoginEvent = { @@ -23,7 +24,7 @@ type Credentials = { export class ProxyAuthHandler extends Disposable { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private retryCount = 0; @@ -58,8 +59,12 @@ export class ProxyAuthHandler extends Disposable { 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, + devTools: false } }; @@ -70,24 +75,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.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 27ecfe6f5b3..757602c8672 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -5,7 +5,7 @@ import 'vs/platform/update/common/update.config.contribution'; import { app, dialog } from 'electron'; -import { isWindows, IProcessEnvironment, isMacintosh } from 'vs/base/common/platform'; +import { isWindows, IProcessEnvironment } 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'; @@ -13,7 +13,7 @@ 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'; @@ -47,6 +47,9 @@ import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemPro 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'; class ExpectedError extends Error { readonly isExpected = true; @@ -162,6 +165,8 @@ class CodeMain { 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, environmentService]; } @@ -173,8 +178,8 @@ class CodeMain { environmentService.extensionsPath, environmentService.nodeCachedDataDir, environmentService.logsPath, - environmentService.globalStorageHome, - environmentService.workspaceStorageHome, + environmentService.globalStorageHome.fsPath, + environmentService.workspaceStorageHome.fsPath, environmentService.backupHome.fsPath ].map((path): undefined | Promise => path ? mkdirp(path) : undefined)); @@ -226,11 +231,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 { @@ -330,11 +330,6 @@ class CodeMain { 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); diff --git a/src/vs/code/electron-main/sharedProcess.ts b/src/vs/code/electron-main/sharedProcess.ts index 782cb228ea4..d225e6eb31d 100644 --- a/src/vs/code/electron-main/sharedProcess.ts +++ b/src/vs/code/electron-main/sharedProcess.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { URI } from 'vs/base/common/uri'; import { memoize } from 'vs/base/common/decorators'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { BrowserWindow, ipcMain, WebContents, Event as ElectronEvent } from 'electron'; @@ -32,7 +33,7 @@ export class SharedProcess implements ISharedProcess { @IThemeMainService private readonly themeMainService: IThemeMainService ) { // overall ready promise when shared process signals initialization is done - this._whenReady = new Promise(c => ipcMain.once('shared-process->electron-main: init-done', () => c(undefined))); + this._whenReady = new Promise(c => ipcMain.once('vscode:shared-process->electron-main=init-done', () => c(undefined))); } @memoize @@ -41,8 +42,12 @@ export class SharedProcess implements ISharedProcess { 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, + nativeWindowOpen: true, + images: false, webgl: false, disableBlinkFeatures: 'Auxclick' // do NOT change, allows us to identify this window as shared-process in the process explorer } @@ -104,18 +109,18 @@ export class SharedProcess implements ISharedProcess { return new Promise(c => { // send payload once shared process is ready to receive it - disposables.add(Event.once(Event.fromNodeEventEmitter(ipcMain, 'shared-process->electron-main: ready-for-payload', ({ sender }: { sender: WebContents }) => sender))(sender => { - sender.send('electron-main->shared-process: payload', { + 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() }); // signal exit to shared process when we get disposed - disposables.add(toDisposable(() => sender.send('electron-main->shared-process: exit'))); + disposables.add(toDisposable(() => sender.send('vscode:electron-main->shared-process=exit'))); // complete IPC-ready promise when shared process signals this to us - ipcMain.once('shared-process->electron-main: ipc-ready', () => c(undefined)); + ipcMain.once('vscode:shared-process->electron-main=ipc-ready', () => c(undefined)); })); }); } diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 2cdb6b90d6d..c300a83ac0e 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -8,14 +8,14 @@ import * as objects from 'vs/base/common/objects'; import * as nls from 'vs/nls'; import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; -import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, nativeTheme } from 'electron'; +import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, nativeTheme, Event } from 'electron'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { ILogService } from 'vs/platform/log/common/log'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { parseArgs, OPTIONS, ParsedArgs } from 'vs/platform/environment/node/argv'; import product from 'vs/platform/product/common/product'; -import { IWindowSettings, MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility } from 'vs/platform/windows/common/windows'; +import { IWindowSettings, MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, zoomLevelToZoomFactor } 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'; @@ -36,8 +36,6 @@ import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifec import { IStorageMainService } from 'vs/platform/storage/node/storageMainService'; import { IFileService } from 'vs/platform/files/common/files'; -const RUN_TEXTMATE_IN_WORKER = false; - export interface IWindowCreationOptions { state: IWindowState; extensionDevelopmentPath?: string[]; @@ -91,15 +89,18 @@ export class CodeWindow extends Disposable implements ICodeWindow { 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 = this._onClose.event; private readonly _onDestroy = this._register(new Emitter()); readonly onDestroy = this._onDestroy.event; - private readonly _onLoad = this._register(new Emitter()); - readonly onLoad = this._onLoad.event; - private hiddenTitleBarStyle: boolean | undefined; private showTimeoutHandle: NodeJS.Timeout | undefined; private _lastFocusTime: number; @@ -152,6 +153,8 @@ export class CodeWindow extends Disposable implements ICodeWindow { // in case we are maximized or fullscreen, only show later after the call to maximize/fullscreen (see below) const isFullscreenOrMaximized = (this.windowState.mode === WindowMode.Maximized || this.windowState.mode === WindowMode.Fullscreen); + const windowConfig = this.configurationService.getValue('window'); + const options: BrowserWindowConstructorOptions = { width: this.windowState.width, height: this.windowState.height, @@ -163,9 +166,13 @@ export class CodeWindow extends Disposable implements ICodeWindow { show: !isFullscreenOrMaximized, title: product.nameLong, webPreferences: { + preload: URI.parse(this.doGetPreloadUrl()).fsPath, nodeIntegration: true, - nodeIntegrationInWorker: RUN_TEXTMATE_IN_WORKER, - webviewTag: true + enableWebSQL: false, + enableRemoteModule: false, + nativeWindowOpen: true, + webviewTag: true, + zoomFactor: zoomLevelToZoomFactor(windowConfig?.zoomLevel) } }; @@ -178,8 +185,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { options.icon = path.join(this.environmentService.appRoot, 'resources/win32/code_150x150.png'); } - const windowConfig = this.configurationService.getValue('window'); - if (isMacintosh && !this.useNativeFullScreen()) { options.fullscreenable = false; // enables simple fullscreen mode } @@ -210,6 +215,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 } @@ -344,6 +354,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { while (this.whenReadyCallbacks.length) { this.whenReadyCallbacks.pop()!(this); } + + // Events + this._onReady.fire(); } ready(): Promise { @@ -429,24 +442,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); } - - this.sendWhenReady('vscode:displayChanged'); }, 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))); @@ -599,9 +622,12 @@ export class CodeWindow extends Disposable implements ICodeWindow { } // Do not set to empty configuration at startup if setting is empty to not override configuration through CLI options: const env = process.env; - const newHttpProxy = (this.configurationService.getValue('http.proxy') || '').trim() + 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; @@ -776,6 +802,10 @@ export class CodeWindow extends Disposable implements ICodeWindow { return `${require.toUrl('vs/code/electron-browser/workbench/workbench.html')}?config=${encodeURIComponent(JSON.stringify(config))}`; } + private doGetPreloadUrl(): string { + return require.toUrl('vs/base/parts/sandbox/electron-browser/preload.js'); + } + serializeWindowState(): IWindowState { if (!this._win) { return defaultWindowState(); @@ -783,7 +813,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(); @@ -950,8 +987,17 @@ export class CodeWindow extends Disposable implements ICodeWindow { } // Multi Monitor (non-fullscreen): ensure window is within display bounds - const display = screen.getDisplayMatching({ x: state.x, y: state.y, width: state.width, height: state.height }); - const displayWorkingArea = this.getWorkingArea(display); + let display: Display | undefined; + let displayWorkingArea: Rectangle | undefined; + try { + display = screen.getDisplayMatching({ x: state.x, y: state.y, width: state.width, height: state.height }); + displayWorkingArea = this.getWorkingArea(display); + } catch (error) { + // Electron has weird conditions under which it throws errors + // e.g. https://github.com/microsoft/vscode/issues/100334 when + // large numbers are passed in + } + if ( display && // we have a display matching the desired bounds displayWorkingArea && // we have valid working area bounds diff --git a/src/vs/code/electron-sandbox/issue/issueReporter.html b/src/vs/code/electron-sandbox/issue/issueReporter.html new file mode 100644 index 00000000000..3a0cb4be742 --- /dev/null +++ b/src/vs/code/electron-sandbox/issue/issueReporter.html @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/vs/code/electron-browser/issue/issueReporter.js b/src/vs/code/electron-sandbox/issue/issueReporter.js similarity index 63% rename from src/vs/code/electron-browser/issue/issueReporter.js rename to src/vs/code/electron-sandbox/issue/issueReporter.js index 5d11dd15ae4..6991c2bba2a 100644 --- a/src/vs/code/electron-browser/issue/issueReporter.js +++ b/src/vs/code/electron-sandbox/issue/issueReporter.js @@ -6,8 +6,14 @@ //@ts-check 'use strict'; -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; +})(); -bootstrapWindow.load(['vs/code/electron-browser/issue/issueReporterMain'], function (issueReporter, configuration) { +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-browser/issue/issueReporterMain.ts b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts similarity index 85% rename from src/vs/code/electron-browser/issue/issueReporterMain.ts rename to src/vs/code/electron-sandbox/issue/issueReporterMain.ts index 24c2b5372ac..9e7634b2791 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts @@ -3,43 +3,28 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { clipboard, ipcRenderer, shell, webFrame } from 'electron'; -import * as os from 'os'; -import * as browser from 'vs/base/browser/browser'; -import { $ } from 'vs/base/browser/dom'; -import { Button } from 'vs/base/browser/ui/button/button'; +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 { $, 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 { getDelayedChannel } from 'vs/base/parts/ipc/common/ipc'; -import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; -import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net'; import { normalizeGitHubUrl } from 'vs/platform/issue/common/issueReporterUtil'; -import { IssueReporterData as IssueReporterModelData, IssueReporterModel } from 'vs/code/electron-browser/issue/issueReporterModel'; -import BaseHtml from 'vs/code/electron-browser/issue/issueReporterPage'; -import 'vs/css!./media/issueReporter'; +import { 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 { EnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { IMainProcessService, MainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; -import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; -import { ISettingsSearchIssueReporterData, IssueReporterData, IssueReporterExtensionData, IssueReporterFeatures, IssueReporterStyles, IssueType } from 'vs/platform/issue/node/issue'; -import { getLogLevel, ILogService } from 'vs/platform/log/common/log'; -import { FollowerLogService, LoggerChannelClient } from 'vs/platform/log/common/logIpc'; -import { SpdLogService } from 'vs/platform/log/node/spdlogService'; -import product from 'vs/platform/product/common/product'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ITelemetryServiceConfig, TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; -import { combinedAppender, LogAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; -import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc'; -import { INativeWindowConfiguration } from 'vs/platform/windows/node/window'; +import { IMainProcessService, MainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { ISettingsSearchIssueReporterData, IssueReporterData, IssueReporterExtensionData, IssueReporterFeatures, IssueReporterStyles, IssueType } from 'vs/platform/issue/common/issue'; +import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; const MAX_URL_LENGTH = 2045; @@ -49,12 +34,29 @@ interface SearchResult { state?: string; } -export interface IssueReporterConfiguration extends INativeWindowConfiguration { +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(); @@ -63,9 +65,7 @@ export function startup(configuration: IssueReporterConfiguration) { } export class IssueReporter extends Disposable { - private environmentService!: INativeEnvironmentService; - private telemetryService!: ITelemetryService; - private logService!: ILogService; + private electronService!: IElectronService; private readonly issueReporterModel: IssueReporterModel; private numberOfSearchResultsDisplayed = 0; private receivedSystemInfo = false; @@ -75,7 +75,7 @@ export class IssueReporter extends Disposable { private readonly previewButton!: Button; - constructor(configuration: IssueReporterConfiguration) { + constructor(private readonly configuration: IssueReporterConfiguration) { super(); this.initServices(configuration); @@ -86,10 +86,10 @@ export class IssueReporter extends Disposable { this.issueReporterModel = new IssueReporterModel({ issueType: configuration.data.issueType || IssueType.Bug, versionInfo: { - vscodeVersion: `${product.nameShort} ${product.version} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})`, - os: `${os.type()} ${os.arch()} ${os.release()}${isSnap ? ' snap' : ''}` + vscodeVersion: `${configuration.product.nameShort} ${configuration.product.version} (${configuration.product.commit || 'Commit unknown'}, ${configuration.product.date || 'Date unknown'})`, + os: `${this.configuration.os.type} ${this.configuration.os.arch} ${this.configuration.os.release}${isSnap ? ' snap' : ''}` }, - extensionsDisabled: !!this.environmentService.disableExtensions, + extensionsDisabled: !!configuration.disableExtensions, fileOnExtension: configuration.data.extensionId ? !targetExtension?.isBuiltin : undefined, selectedExtension: targetExtension, }); @@ -117,7 +117,6 @@ export class IssueReporter extends Disposable { } ipcRenderer.on('vscode:issuePerformanceInfoResponse', (_: unknown, info: Partial) => { - this.logService.trace('issueReporter: Received performance data'); this.issueReporterModel.update(info); this.receivedPerformanceInfo = true; @@ -128,7 +127,6 @@ export class IssueReporter extends Disposable { }); ipcRenderer.on('vscode:issueSystemInfoResponse', (_: unknown, info: SystemInfo) => { - this.logService.trace('issueReporter: Received system data'); this.issueReporterModel.update({ systemInfo: info }); this.receivedSystemInfo = true; @@ -140,7 +138,6 @@ export class IssueReporter extends Disposable { if (configuration.data.issueType === IssueType.PerformanceIssue) { ipcRenderer.send('vscode:issuePerformanceInfoRequest'); } - this.logService.trace('issueReporter: Sent data requests'); if (window.document.documentElement.lang !== 'en') { show(this.getElementById('english')); @@ -148,7 +145,7 @@ export class IssueReporter extends Disposable { this.setUpTypes(); this.setEventHandlers(); - this.applyZoom(configuration.data.zoomLevel); + applyZoom(configuration.data.zoomLevel); this.applyStyles(configuration.data.styles); this.handleExtensionData(configuration.data.enabledExtensions); @@ -176,15 +173,6 @@ export class IssueReporter extends Disposable { } } - private applyZoom(zoomLevel: number) { - webFrame.setZoomLevel(zoomLevel); - browser.setZoomFactor(webFrame.getZoomFactor()); - // See https://github.com/Microsoft/vscode/issues/26151 - // Cannot be trusted because the webFrame might take some time - // until it really applies the new zoom level - browser.setZoomLevel(webFrame.getZoomLevel(), /*isTrusted*/false); - } - private applyStyles(styles: IssueReporterStyles) { const styleTag = document.createElement('style'); const content: string[] = []; @@ -271,7 +259,7 @@ export class IssueReporter extends Disposable { this.issueReporterModel.update({ numberOfThemeExtesions, enabledNonThemeExtesions: nonThemes, allExtensions: installedExtensions }); this.updateExtensionTable(nonThemes, numberOfThemeExtesions); - if (this.environmentService.disableExtensions || installedExtensions.length === 0) { + if (this.configuration.disableExtensions || installedExtensions.length === 0) { (this.getElementById('disableExtensions')).disabled = true; } @@ -319,37 +307,13 @@ export class IssueReporter extends Disposable { } } - private initServices(configuration: INativeWindowConfiguration): void { + private initServices(configuration: IssueReporterConfiguration): void { const serviceCollection = new ServiceCollection(); const mainProcessService = new MainProcessService(configuration.windowId); serviceCollection.set(IMainProcessService, mainProcessService); - this.environmentService = new EnvironmentService(configuration, configuration.execPath); - - const logService = new SpdLogService(`issuereporter${configuration.windowId}`, this.environmentService.logsPath, getLogLevel(this.environmentService)); - const loggerClient = new LoggerChannelClient(mainProcessService.getChannel('logger')); - this.logService = new FollowerLogService(loggerClient, logService); - - const sharedProcessService = createChannelSender(mainProcessService.getChannel('sharedProcess')); - - const sharedProcess = sharedProcessService.whenSharedProcessReady() - .then(() => connectNet(this.environmentService.sharedIPCHandle, `window:${configuration.windowId}`)); - - const instantiationService = new InstantiationService(serviceCollection, true); - if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) { - const channel = getDelayedChannel(sharedProcess.then(c => c.getChannel('telemetryAppender'))); - const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(logService)); - const commonProperties = resolveCommonProperties(product.commit || 'Commit unknown', product.version, configuration.machineId, product.msftInternalDomains, this.environmentService.installSourcePath); - const piiPaths = this.environmentService.extensionsPath ? [this.environmentService.appRoot, this.environmentService.extensionsPath] : [this.environmentService.appRoot]; - const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths, sendErrorTelemetry: true }; - - const telemetryService = instantiationService.createInstance(TelemetryService, config); - this._register(telemetryService); - - this.telemetryService = telemetryService; - } else { - this.telemetryService = NullTelemetryService; - } + this.electronService = new ElectronService(configuration.windowId, mainProcessService) as IElectronService; + serviceCollection.set(IElectronService, this.electronService); } private setEventHandlers(): void { @@ -462,7 +426,7 @@ export class IssueReporter extends Disposable { this.addEventListener('extensionBugsLink', 'click', (e: Event) => { const url = (e.target).innerText; - shell.openExternal(url); + windowOpenNoOpener(url); }); this.addEventListener('disableExtensions', 'keydown', (e: Event) => { @@ -498,12 +462,12 @@ export class IssueReporter extends Disposable { // Cmd/Ctrl + zooms in if (cmdOrCtrlKey && e.keyCode === 187) { - this.applyZoom(webFrame.getZoomLevel() + 1); + zoomIn(); } // Cmd/Ctrl - zooms out if (cmdOrCtrlKey && e.keyCode === 189) { - this.applyZoom(webFrame.getZoomLevel() - 1); + zoomOut(); } // With latest electron upgrade, cmd+a is no longer propagating correctly for inputs in this window on mac @@ -619,11 +583,11 @@ export class IssueReporter extends Disposable { }, timeToWait * 1000); } } - }).catch(e => { - this.logSearchError(e); + }).catch(_ => { + // Ignore }); - }).catch(e => { - this.logSearchError(e); + }).catch(_ => { + // Ignore }); } @@ -650,11 +614,11 @@ export class IssueReporter extends Disposable { } else { throw new Error('Unexpected response, no candidates property'); } - }).catch((error) => { - this.logSearchError(error); + }).catch(_ => { + // Ignore }); - }).catch((error) => { - this.logSearchError(error); + }).catch(_ => { + // Ignore }); } @@ -707,18 +671,6 @@ export class IssueReporter extends Disposable { } } - private logSearchError(error: Error) { - this.logService.warn('issueReporter#search ', error.message); - type IssueReporterSearchErrorClassification = { - message: { classification: 'CallstackOrException', purpose: 'PerformanceAndHealth' } - }; - - type IssueReporterSearchError = { - message: string; - }; - this.telemetryService.publicLogError2('issueReporterSearchError', { message: error.message }); - } - private setUpTypes(): void { const makeOption = (issueType: IssueType, description: string) => ``; @@ -912,15 +864,6 @@ export class IssueReporter extends Disposable { return false; } - type IssueReporterSubmitClassification = { - issueType: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; - numSimilarIssuesDisplayed: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; - }; - type IssueReporterSubmitEvent = { - issueType: any; - numSimilarIssuesDisplayed: number; - }; - this.telemetryService.publicLog2('issueReporterSubmit', { issueType: this.issueReporterModel.getData().issueType, numSimilarIssuesDisplayed: this.numberOfSearchResultsDisplayed }); this.hasBeenSubmitted = true; const baseUrl = this.getIssueUrlWithTitle((this.getElementById('issue-title')).value); @@ -941,9 +884,9 @@ export class IssueReporter extends Disposable { private async writeToClipboard(baseUrl: string, issueBody: string): Promise { return new Promise((resolve, reject) => { - ipcRenderer.once('vscode:issueReporterClipboardResponse', (_: unknown, shouldWrite: boolean) => { + ipcRenderer.once('vscode:issueReporterClipboardResponse', async (event: unknown, shouldWrite: boolean) => { if (shouldWrite) { - clipboard.writeText(issueBody); + await this.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(); @@ -969,7 +912,7 @@ export class IssueReporter extends Disposable { } private getIssueUrlWithTitle(issueTitle: string): string { - let repositoryUrl = product.reportIssueUrl; + let repositoryUrl = this.configuration.product.reportIssueUrl; if (this.issueReporterModel.fileOnExtension()) { const extensionGitHubUrl = this.getExtensionGitHubUrl(); if (extensionGitHubUrl) { @@ -977,7 +920,7 @@ export class IssueReporter extends Disposable { } } - const queryStringPrefix = product.reportIssueUrl && product.reportIssueUrl.indexOf('?') === -1 ? '?' : '&'; + const queryStringPrefix = this.configuration.product.reportIssueUrl && this.configuration.product.reportIssueUrl.indexOf('?') === -1 ? '?' : '&'; return `${repositoryUrl}${queryStringPrefix}title=${encodeURIComponent(issueTitle)}`; } @@ -1138,7 +1081,7 @@ export class IssueReporter extends Disposable { private updateExtensionTable(extensions: IssueReporterExtensionData[], numThemeExtensions: number): void { const target = document.querySelector('.block-extensions .block-info'); if (target) { - if (this.environmentService.disableExtensions) { + if (this.configuration.disableExtensions) { target.innerHTML = localize('disabledExtensions', "Extensions are disabled"); return; } @@ -1194,8 +1137,7 @@ export class IssueReporter extends Disposable { event.stopPropagation(); // Exclude right click if (event.which < 3) { - shell.openExternal((event.target).href); - this.telemetryService.publicLog2('issueReporterViewSimilarIssue'); + windowOpenNoOpener((event.target).href); } } diff --git a/src/vs/code/electron-browser/issue/issueReporterModel.ts b/src/vs/code/electron-sandbox/issue/issueReporterModel.ts similarity index 97% rename from src/vs/code/electron-browser/issue/issueReporterModel.ts rename to src/vs/code/electron-sandbox/issue/issueReporterModel.ts index 96bae9110ee..08bb22994a9 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 { diff --git a/src/vs/code/electron-browser/issue/issueReporterPage.ts b/src/vs/code/electron-sandbox/issue/issueReporterPage.ts similarity index 100% rename from src/vs/code/electron-browser/issue/issueReporterPage.ts rename to src/vs/code/electron-sandbox/issue/issueReporterPage.ts diff --git a/src/vs/code/electron-browser/issue/media/issueReporter.css b/src/vs/code/electron-sandbox/issue/media/issueReporter.css similarity index 73% rename from src/vs/code/electron-browser/issue/media/issueReporter.css rename to src/vs/code/electron-sandbox/issue/media/issueReporter.css index ad29591ada0..e3da9bd25e4 100644 --- a/src/vs/code/electron-browser/issue/media/issueReporter.css +++ b/src/vs/code/electron-sandbox/issue/media/issueReporter.css @@ -95,26 +95,30 @@ textarea, input, select { } html { - font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif; color: #CCCCCC; height: 100%; } -html:lang(zh-Hans) { - font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; -} +/* Font Families (with CJK support) */ -html:lang(zh-Hant) { - font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Microsoft Jhenghei", "PingFang TC", "Source Han Sans TC", "Source Han Sans", "Source Han Sans TW", sans-serif; -} +.mac { font-family: -apple-system, BlinkMacSystemFont, sans-serif; } +.mac:lang(zh-Hans) { font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", sans-serif; } +.mac:lang(zh-Hant) { font-family: -apple-system, BlinkMacSystemFont, "PingFang TC", sans-serif; } +.mac:lang(ja) { font-family: -apple-system, BlinkMacSystemFont, "Hiragino Kaku Gothic Pro", sans-serif; } +.mac:lang(ko) { font-family: -apple-system, BlinkMacSystemFont, "Nanum Gothic", "Apple SD Gothic Neo", "AppleGothic", sans-serif; } -html:lang(ja) { - font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Yu Gothic UI", "Meiryo UI", "Hiragino Kaku Gothic Pro", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", "Sazanami Gothic", "IPA Gothic", sans-serif; -} +.windows { font-family: "Segoe WPC", "Segoe UI", sans-serif; } +.windows:lang(zh-Hans) { font-family: "Segoe WPC", "Segoe UI", "Microsoft YaHei", sans-serif; } +.windows:lang(zh-Hant) { font-family: "Segoe WPC", "Segoe UI", "Microsoft Jhenghei", sans-serif; } +.windows:lang(ja) { font-family: "Segoe WPC", "Segoe UI", "Yu Gothic UI", "Meiryo UI", sans-serif; } +.windows:lang(ko) { font-family: "Segoe WPC", "Segoe UI", "Malgun Gothic", "Dotom", sans-serif; } -html:lang(ko) { - font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Malgun Gothic", "Nanum Gothic", "Dotom", "Apple SD Gothic Neo", "AppleGothic", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; -} +/* Linux: add `system-ui` as first font and not `Ubuntu` to allow other distribution pick their standard OS font */ +.linux { font-family: system-ui, "Ubuntu", "Droid Sans", sans-serif; } +.linux:lang(zh-Hans) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; } +.linux:lang(zh-Hant) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans TC", "Source Han Sans TW", "Source Han Sans", sans-serif; } +.linux:lang(ja) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", sans-serif; } +.linux:lang(ko) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; } body { margin: 0; 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 97% 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 2dda07e6fed..4d31af3dde7 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 { IssueReporterModel } from 'vs/code/electron-sandbox/issue/issueReporterModel'; import { normalizeGitHubUrl } from 'vs/platform/issue/common/issueReporterUtil'; -import { IssueType } from 'vs/platform/issue/node/issue'; +import { IssueType } from 'vs/platform/issue/common/issue'; suite('IssueReporter', () => { diff --git a/src/vs/code/electron-browser/processExplorer/media/collapsed.svg b/src/vs/code/electron-sandbox/processExplorer/media/collapsed.svg similarity index 100% rename from src/vs/code/electron-browser/processExplorer/media/collapsed.svg rename to src/vs/code/electron-sandbox/processExplorer/media/collapsed.svg diff --git a/src/vs/code/electron-browser/processExplorer/media/expanded.svg b/src/vs/code/electron-sandbox/processExplorer/media/expanded.svg similarity index 100% rename from src/vs/code/electron-browser/processExplorer/media/expanded.svg rename to src/vs/code/electron-sandbox/processExplorer/media/expanded.svg diff --git a/src/vs/code/electron-sandbox/processExplorer/media/processExplorer.css b/src/vs/code/electron-sandbox/processExplorer/media/processExplorer.css new file mode 100644 index 00000000000..fbb637e83cd --- /dev/null +++ b/src/vs/code/electron-sandbox/processExplorer/media/processExplorer.css @@ -0,0 +1,106 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +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; +} + +img { + width: 16px; + margin-right: 4px; +} diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorer.html b/src/vs/code/electron-sandbox/processExplorer/processExplorer.html new file mode 100644 index 00000000000..517805abdb7 --- /dev/null +++ b/src/vs/code/electron-sandbox/processExplorer/processExplorer.html @@ -0,0 +1,19 @@ + + + + + + + + +
+ + + + + + + + + + diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorer.js b/src/vs/code/electron-sandbox/processExplorer/processExplorer.js new file mode 100644 index 00000000000..d6c6886e6fd --- /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 00000000000..3c88a619a55 --- /dev/null +++ b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts @@ -0,0 +1,434 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { 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'; + +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: HTMLImageElement, sectionName: string) { + if (shouldExpand) { + body.classList.remove('hidden'); + this.collapsedStateCache.set(sectionName, false); + twistie.src = './media/expanded.svg'; + } else { + body.classList.add('hidden'); + this.collapsedStateCache.set(sectionName, true); + twistie.src = './media/collapsed.svg'; + } + } + + 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 data = document.createElement('td'); + data.textContent = sectionName; + data.colSpan = 4; + headerRow.appendChild(data); + + const twistie = document.createElement('img'); + this.updateSectionCollapsedState(!this.collapsedStateCache.get(sectionName), body, twistie, sectionName); + data.prepend(twistie); + + this.listeners.add(addDisposableListener(data, 'click', (e) => { + const isHidden = body.classList.contains('hidden'); + this.updateSectionCollapsedState(isHidden, body, twistie, 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.innerHTML = ''; + this.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; + 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 00000000000..788b68fce72 --- /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 00000000000..5e0db3c2dc7 --- /dev/null +++ b/src/vs/code/electron-sandbox/proxy/auth.js @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const { ipcRenderer } = window.vscode; + +function promptForCredentials(data) { + return new Promise((c, e) => { + const $title = document.getElementById('title'); + const $username = document.getElementById('username'); + const $password = document.getElementById('password'); + const $form = document.getElementById('form'); + const $cancel = document.getElementById('cancel'); + const $message = document.getElementById('message'); + + function submit() { + c({ username: $username.value, password: $password.value }); + return false; + } + + function cancel() { + c({ username: '', password: '' }); + return false; + } + + $form.addEventListener('submit', submit); + $cancel.addEventListener('click', cancel); + + document.body.addEventListener('keydown', function (e) { + switch (e.keyCode) { + case 27: e.preventDefault(); e.stopPropagation(); return cancel(); + case 13: e.preventDefault(); e.stopPropagation(); return submit(); + } + }); + + $title.textContent = data.title; + $message.textContent = data.message; + $username.focus(); + }); +} + +ipcRenderer.on('vscode:openProxyAuthDialog', async (event, data) => { + const response = await promptForCredentials(data); + ipcRenderer.send('vscode:proxyAuthResponse', response); +}); diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 81be5f203b3..83a9bc61e54 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -43,7 +43,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)); } diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index ddbfcd5e8c7..9b06d39b0bc 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 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'; @@ -35,7 +36,7 @@ import { isPromiseCanceledError } from 'vs/base/common/errors'; import { areSameExtensions, adoptToGalleryExtensionId, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { URI } from 'vs/base/common/uri'; import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; -import { IExtensionManifest, ExtensionType, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; +import { IExtensionManifest, ExtensionType, isLanguagePackExtension, EXTENSION_CATEGORIES } from 'vs/platform/extensions/common/extensions'; import { CancellationToken } from 'vs/base/common/cancellation'; import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; import { Schemas } from 'vs/base/common/network'; @@ -85,7 +86,7 @@ export class Main { } 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 +102,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 +125,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 +134,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 +149,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 +157,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 +204,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; })); } @@ -226,11 +226,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)) { @@ -361,8 +361,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 index 24c2529c587..b2912383c3c 100644 --- a/src/vs/code/node/paths.ts +++ b/src/vs/code/node/paths.ts @@ -33,9 +33,9 @@ function doValidatePaths(args: string[], gotoLineMode?: boolean): string[] { const result = args.map(arg => { let pathCandidate = String(arg); - let parsedPath: IPathWithLineAndColumn | undefined = undefined; + let parsedPath: extpath.IPathWithLineAndColumn | undefined = undefined; if (gotoLineMode) { - parsedPath = parseLineAndColumnAware(pathCandidate); + parsedPath = extpath.parseLineAndColumnAware(pathCandidate); pathCandidate = parsedPath.path; } @@ -87,42 +87,7 @@ function preparePath(cwd: string, p: string): string { 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 { +function toPath(p: extpath.IPathWithLineAndColumn): string { const segments = [p.path]; if (types.isNumber(p.line)) { diff --git a/src/vs/css.build.js b/src/vs/css.build.js index 74f0f161ddf..11041a22628 100644 --- a/src/vs/css.build.js +++ b/src/vs/css.build.js @@ -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/editor/browser/config/configuration.ts b/src/vs/editor/browser/config/configuration.ts index 9dc1f11dc39..ee76fd4b903 100644 --- a/src/vs/editor/browser/config/configuration.ts +++ b/src/vs/editor/browser/config/configuration.ts @@ -11,10 +11,11 @@ import * as platform from 'vs/base/common/platform'; import { CharWidthRequest, CharWidthRequestType, readCharWidths } from 'vs/editor/browser/config/charWidthReader'; import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; import { CommonEditorConfiguration, IEnvConfiguration } from 'vs/editor/common/config/commonEditorConfig'; -import { EditorOption, IEditorConstructionOptions, EditorFontLigatures } from 'vs/editor/common/config/editorOptions'; +import { EditorOption, EditorFontLigatures } from 'vs/editor/common/config/editorOptions'; import { BareFontInfo, FontInfo } from 'vs/editor/common/config/fontInfo'; import { IDimension } from 'vs/editor/common/editorCommon'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; +import { IEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; class CSSBasedConfigurationCache { diff --git a/src/vs/editor/browser/controller/coreCommands.ts b/src/vs/editor/browser/controller/coreCommands.ts index b2a8e0c96e2..2013ad47528 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,26 +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, 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_ { @@ -275,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 { @@ -286,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); } } @@ -312,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; } @@ -338,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); } }); @@ -364,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); } }); @@ -383,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); } }); @@ -397,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); } } @@ -435,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); } } @@ -473,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); + } + + return null; } } @@ -501,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 @@ -516,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); } } @@ -733,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) { @@ -764,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 @@ -777,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 @@ -794,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 @@ -821,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); } } @@ -865,17 +940,17 @@ 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, - 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]; @@ -919,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); } } @@ -935,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 + } + } + } + }] } })); @@ -947,10 +1039,27 @@ 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 + } + } + } + }] } })); @@ -963,22 +1072,22 @@ 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, - 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); + const maxColumn = viewModel.model.getLineMaxColumn(lineNumber); result[i] = CursorState.fromModelState(cursor.modelState.move(this._inSelectionMode, lineNumber, maxColumn, 0)); } return result; @@ -1018,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); } } @@ -1062,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); } } @@ -1106,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) { @@ -1146,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; } } @@ -1182,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, @@ -1208,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, @@ -1233,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, @@ -1259,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, @@ -1279,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); } } @@ -1312,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 @@ -1339,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); } } @@ -1372,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 @@ -1413,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); } }); @@ -1439,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); } }); @@ -1466,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); } }); @@ -1488,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; @@ -1521,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() { @@ -1555,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, [ @@ -1588,97 +1703,6 @@ registerColumnSelection(CoreNavigationCommands.CursorColumnSelectPageUp.id, KeyM registerColumnSelection(CoreNavigationCommands.CursorColumnSelectDown.id, KeyMod.Shift | KeyCode.DownArrow); registerColumnSelection(CoreNavigationCommands.CursorColumnSelectPageDown.id, KeyMod.Shift | KeyCode.PageDown); -/** - * 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. - */ -abstract class EditorOrNativeTextInputCommand extends Command { - - 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.runEditorCommand(accessor, focusedEditor, args); - } - - // 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) { - return this.runDOMCommand(); - } - - // Redirecting to active editor - const activeEditor = accessor.get(ICodeEditorService).getActiveCodeEditor(); - if (activeEditor) { - activeEditor.focus(); - return this.runEditorCommand(accessor, activeEditor, args); - } - } - - public abstract runDOMCommand(): void; - public abstract runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void; -} - -class SelectAllCommand extends EditorOrNativeTextInputCommand { - constructor() { - super({ - id: 'editor.action.selectAll', - precondition: EditorContextKeys.textInputFocus, - kbOpts: { - weight: CORE_WEIGHT, - kbExpr: null, - primary: KeyMod.CtrlCmd | KeyCode.KEY_A - }, - menuOpts: [{ - menuId: MenuId.MenubarSelectionMenu, - group: '1_basic', - title: nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All"), - order: 1 - }, { - menuId: MenuId.CommandPalette, - group: '', - title: nls.localize('selectAll', "Select All"), - order: 1 - }] - }); - } - public runDOMCommand(): void { - document.execCommand('selectAll'); - } - public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void { - args = args || {}; - args.source = 'keyboard'; - CoreNavigationCommands.SelectAll.runEditorCommand(accessor, editor, args); - } -} - -class UndoCommand extends EditorOrNativeTextInputCommand { - public runDOMCommand(): void { - document.execCommand('undo'); - } - public runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: any): void { - if (!editor.hasModel() || editor.getOption(EditorOption.readOnly) === true) { - return; - } - editor.getModel().undo(); - } -} - -class RedoCommand extends EditorOrNativeTextInputCommand { - 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(); - } -} - function registerCommand(command: T): T { command.register(); return command; @@ -1688,15 +1712,15 @@ 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 { @@ -1713,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))); } }); @@ -1735,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(); } }); @@ -1758,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(); } }); @@ -1780,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); } }); @@ -1804,63 +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); } }); - export const Undo: UndoCommand = registerCommand(new UndoCommand({ - id: 'undo', - precondition: EditorContextKeys.writable, - kbOpts: { - weight: CORE_WEIGHT, - kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | KeyCode.KEY_Z - }, - menuOpts: [{ - menuId: MenuId.MenubarEditMenu, - group: '1_do', - title: nls.localize({ key: 'miUndo', comment: ['&& denotes a mnemonic'] }, "&&Undo"), - order: 1 - }, { - menuId: MenuId.CommandPalette, - group: '', - title: nls.localize('undo', "Undo"), - order: 1 - }] - })); + export const Undo = new class extends EditorOrNativeTextInputCommand { + constructor() { + super(UndoCommand); + } + public runDOMCommand(): void { + document.execCommand('undo'); + } + public runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: any): void { + if (!editor.hasModel() || editor.getOption(EditorOption.readOnly) === true) { + return; + } + editor.getModel().undo(); + } + }(); - export const DefaultUndo: UndoCommand = registerCommand(new UndoCommand({ id: 'default:undo', precondition: EditorContextKeys.writable })); - - export const Redo: RedoCommand = registerCommand(new RedoCommand({ - id: 'redo', - precondition: EditorContextKeys.writable, - kbOpts: { - weight: CORE_WEIGHT, - kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | KeyCode.KEY_Y, - secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z], - mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z } - }, - menuOpts: [{ - menuId: MenuId.MenubarEditMenu, - group: '1_do', - title: nls.localize({ key: 'miRedo', comment: ['&& denotes a mnemonic'] }, "&&Redo"), - order: 2 - }, { - menuId: MenuId.CommandPalette, - group: '', - title: nls.localize('redo', "Redo"), - order: 1 - }] - })); - - export const DefaultRedo: RedoCommand = registerCommand(new RedoCommand({ id: 'default:redo', precondition: EditorContextKeys.writable })); + 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(); + } + }(); } /** @@ -1889,8 +1895,6 @@ class EditorHandlerCommand extends Command { } } -registerCommand(new SelectAllCommand()); - 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/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index f0ad0cf79dc..6123c7971ae 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -420,7 +420,7 @@ class HitTestRequest extends BareHitTestRequest { let mouseColumn = this.mouseColumn; if (position && position.column < this._ctx.model.getLineMaxColumn(position.lineNumber)) { // Most likely, the line contains foreign decorations... - mouseColumn = CursorColumns.visibleColumnFromColumn(this._ctx.model.getLineContent(position.lineNumber), position.column, this._ctx.model.getOptions().tabSize) + 1; + mouseColumn = CursorColumns.visibleColumnFromColumn(this._ctx.model.getLineContent(position.lineNumber), position.column, this._ctx.model.getTextModelOptions().tabSize) + 1; } return new MouseTarget(this.target, type, mouseColumn, position, range, detail); } diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts index ffe55acb07c..2b930bb8461 100644 --- a/src/vs/editor/browser/controller/pointerHandler.ts +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -100,7 +100,7 @@ class StandardPointerHandler extends MouseHandler implements IDisposable { } private _onGestureChange(e: IThrottledGestureEvent): void { - this._context.viewLayout.deltaScrollNow(-e.translationX, -e.translationY); + this._context.model.deltaScrollNow(-e.translationX, -e.translationY); } public dispose(): void { @@ -177,7 +177,7 @@ export class PointerEventHandler extends MouseHandler { private onChange(e: GestureEvent): void { if (this._lastPointerType === 'touch') { - this._context.viewLayout.deltaScrollNow(-e.translationX, -e.translationY); + this._context.model.deltaScrollNow(-e.translationX, -e.translationY); } } @@ -215,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); } } diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index 3203e8bae37..9eee0dfe476 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -30,6 +30,7 @@ import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { IEditorAriaOptions } from 'vs/editor/browser/editorBrowser'; +import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; export interface ITextAreaHandlerHelper { visibleRangeForPositionRelativeToEditor(lineNumber: number, column: number): HorizontalPosition | null; @@ -117,7 +118,7 @@ export class TextAreaHandler extends ViewPart { // Text Area (The focus will always be in the textarea when the cursor is blinking) this.textArea = createFastDomNode(document.createElement('textarea')); PartFingerprints.write(this.textArea, PartFingerprint.TextArea); - this.textArea.setClassName('inputarea'); + this.textArea.setClassName(`inputarea ${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`); this.textArea.setAttribute('wrap', 'off'); this.textArea.setAttribute('autocorrect', 'off'); this.textArea.setAttribute('autocapitalize', 'off'); @@ -234,37 +235,36 @@ export class TextAreaHandler extends ViewPart { multicursorText = (typeof e.metadata.multicursorText !== 'undefined' ? e.metadata.multicursorText : null); mode = e.metadata.mode; } - this._viewController.paste('keyboard', e.text, pasteOnNewLine, multicursorText, mode); + this._viewController.paste(e.text, pasteOnNewLine, multicursorText, mode); })); this._register(this._textAreaInput.onCut(() => { - this._viewController.cut('keyboard'); + this._viewController.cut(); })); this._register(this._textAreaInput.onType((e: ITypeData) => { if (e.replaceCharCnt) { - this._viewController.replacePreviousChar('keyboard', e.text, e.replaceCharCnt); + this._viewController.replacePreviousChar(e.text, e.replaceCharCnt); } else { - this._viewController.type('keyboard', e.text); + this._viewController.type(e.text); } })); this._register(this._textAreaInput.onSelectionChangeRequest((modelSelection: Selection) => { - this._viewController.setSelection('keyboard', modelSelection); + this._viewController.setSelection(modelSelection); })); this._register(this._textAreaInput.onCompositionStart((e) => { const lineNumber = this._selections[0].startLineNumber; const column = this._selections[0].startColumn - (e.moveOneCharacterLeft ? 1 : 0); - this._context.privateViewEventBus.emit(new viewEvents.ViewRevealRangeRequestEvent( + this._context.model.revealRange( 'keyboard', - new Range(lineNumber, column, lineNumber, column), - null, - viewEvents.VerticalRevealType.Simple, true, + new Range(lineNumber, column, lineNumber, column), + viewEvents.VerticalRevealType.Simple, ScrollType.Immediate - )); + ); // Find range pixel position const visibleRange = this._viewHelper.visibleRangeForPositionRelativeToEditor(lineNumber, column); @@ -279,9 +279,9 @@ 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) => { @@ -301,16 +301,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); })); } diff --git a/src/vs/editor/browser/controller/textAreaInput.ts b/src/vs/editor/browser/controller/textAreaInput.ts index 225d9e73e8a..238e47cd542 100644 --- a/src/vs/editor/browser/controller/textAreaInput.ts +++ b/src/vs/editor/browser/controller/textAreaInput.ts @@ -380,7 +380,7 @@ export class TextAreaInput extends Disposable { } 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. // @@ -406,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; } diff --git a/src/vs/editor/browser/core/keybindingCancellation.ts b/src/vs/editor/browser/core/keybindingCancellation.ts index df5795ffe28..1f260f9b6cd 100644 --- a/src/vs/editor/browser/core/keybindingCancellation.ts +++ b/src/vs/editor/browser/core/keybindingCancellation.ts @@ -17,7 +17,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; const IEditorCancellationTokens = createDecorator('IEditorCancelService'); interface IEditorCancellationTokens { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; add(editor: ICodeEditor, cts: CancellationTokenSource): () => void; cancel(editor: ICodeEditor): void; } @@ -26,7 +26,7 @@ const ctxCancellableOperation = new RawContextKey('cancellableOperation', false) registerSingleton(IEditorCancellationTokens, class implements IEditorCancellationTokens { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _tokens = new WeakMap, tokens: LinkedList }>(); diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index e8993db6133..b69c69a1c2d 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -7,7 +7,6 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IMouseEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { IDisposable } from 'vs/base/common/lifecycle'; import { OverviewRulerPosition, ConfigurationChangedEvent, EditorLayoutInfo, IComputedEditorOptions, EditorOption, FindComputedEditorOptionValueById, IEditorOptions, IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { ICursors } from 'vs/editor/common/controller/cursorCommon'; import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; @@ -19,6 +18,7 @@ import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager'; import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IDiffComputationResult } from 'vs/editor/common/services/editorWorkerService'; +import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; /** * A view zone is a full horizontal rectangle that 'pushes' text down. @@ -336,6 +336,18 @@ export interface IEditorAriaOptions { 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; +} + /** * A rich code editor. */ @@ -616,15 +628,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. @@ -639,7 +651,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. @@ -653,19 +665,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). @@ -858,7 +870,7 @@ export interface IActiveCodeEditor extends ICodeEditor { /** * @internal */ - _getCursors(): ICursors; + _getViewModel(): IViewModel; /** * Get all the decorations on a line (filtering out decorations from other editors). diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index 08ea58ef1be..e4e081bd300 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,15 +14,17 @@ 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, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; import { IConstructorSignature1, ServicesAccessor as InstantiationServicesAccessor, BrandedService } from 'vs/platform/instantiation/common/instantiation'; -import { IKeybindings, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IKeybindings, KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { withNullAsUndefined, assertType } from 'vs/base/common/types'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; export type ServicesAccessor = InstantiationServicesAccessor; @@ -43,6 +46,10 @@ export interface IDiffEditorContributionDescription { export interface ICommandKeybindingsOptions extends IKeybindings { kbExpr?: ContextKeyExpression | null; weight: number; + /** + * the default keybinding arguments + */ + args?: any; } export interface ICommandMenuOptions { menuId: MenuId; @@ -96,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, @@ -134,6 +142,66 @@ export abstract class Command { //#endregion Command +//#region MultiplexingCommand + +/** + * Potential override for a command. + * + * @return `true` if the command was successfully run. This stops other overrides from being executed. + */ +export type CommandImplementation = (accessor: ServicesAccessor, args: unknown) => boolean; + +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 { @@ -269,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) { @@ -325,7 +457,7 @@ export function registerModelAndPositionCommand(id: string, handler: (model: ITe const model = accessor.get(IModelService).getModel(resource); if (model) { const editorPosition = Position.lift(position); - return handler(model, editorPosition, args.slice(2)); + return handler(model, editorPosition, ...args.slice(2)); } return accessor.get(ITextModelService).createModelReference(resource).then(reference => { @@ -374,8 +506,15 @@ export function registerEditorCommand(editorCommand: T) return editorCommand; } -export function registerEditorAction(ctor: { new(): EditorAction; }): void { - EditorContributionRegistry.INSTANCE.registerEditorAction(new ctor()); +export function registerEditorAction(ctor: { new(): T; }): T { + const action = new ctor(); + EditorContributionRegistry.INSTANCE.registerEditorAction(action); + return action; +} + +export function registerMultiEditorAction(action: T): T { + EditorContributionRegistry.INSTANCE.registerEditorAction(action); + return action; } export function registerInstantiatedEditorAction(editorAction: EditorAction): void { @@ -470,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 2aeb0b21b42..f48a9cff839 100644 --- a/src/vs/editor/browser/services/abstractCodeEditorService.ts +++ b/src/vs/editor/browser/services/abstractCodeEditorService.ts @@ -10,10 +10,11 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { IDecorationRenderOptions } from 'vs/editor/common/editorCommon'; import { IModelDecorationOptions, ITextModel } from 'vs/editor/common/model'; import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; +import { URI } from 'vs/base/common/uri'; export abstract class AbstractCodeEditorService extends Disposable implements ICodeEditorService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _onCodeEditorAdd: Emitter = this._register(new Emitter()); public readonly onCodeEditorAdd: Event = this._onCodeEditorAdd.event; @@ -94,6 +95,29 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC abstract resolveDecorationOptions(decorationTypeKey: string | undefined, writable: boolean): IModelDecorationOptions; private readonly _transientWatchers: { [uri: string]: ModelTransientSettingWatcher; } = {}; + private readonly _modelProperties = new Map>(); + + public setModelProperty(resource: URI, key: string, value: any): void { + const key1 = resource.toString(); + let dest: Map; + if (this._modelProperties.has(key1)) { + dest = this._modelProperties.get(key1)!; + } else { + dest = new Map(); + this._modelProperties.set(key1, dest); + } + + dest.set(key, value); + } + + public getModelProperty(resource: URI, key: string): any { + const key1 = resource.toString(); + if (this._modelProperties.has(key1)) { + const innerMap = this._modelProperties.get(key1)!; + return innerMap.get(key); + } + return undefined; + } public setTransientModelProperty(model: ITextModel, key: string, value: any): void { const uri = model.uri.toString(); diff --git a/src/vs/editor/browser/services/bulkEditService.ts b/src/vs/editor/browser/services/bulkEditService.ts index f6fdac9c874..37393b426e3 100644 --- a/src/vs/editor/browser/services/bulkEditService.ts +++ b/src/vs/editor/browser/services/bulkEditService.ts @@ -26,7 +26,7 @@ export interface IBulkEditResult { export type IBulkEditPreviewHandler = (edit: WorkspaceEdit, options?: IBulkEditOptions) => Promise; export interface IBulkEditService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; hasPreviewHandler(): boolean; diff --git a/src/vs/editor/browser/services/codeEditorService.ts b/src/vs/editor/browser/services/codeEditorService.ts index c38fccad1ff..0174886d201 100644 --- a/src/vs/editor/browser/services/codeEditorService.ts +++ b/src/vs/editor/browser/services/codeEditorService.ts @@ -9,11 +9,12 @@ import { IDecorationRenderOptions } from 'vs/editor/common/editorCommon'; import { IModelDecorationOptions, ITextModel } from 'vs/editor/common/model'; import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { URI } from 'vs/base/common/uri'; export const ICodeEditorService = createDecorator('codeEditorService'); export interface ICodeEditorService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly onCodeEditorAdd: Event; readonly onCodeEditorRemove: Event; @@ -41,6 +42,9 @@ export interface ICodeEditorService { removeDecorationType(key: string): void; resolveDecorationOptions(typeKey: string, writable: boolean): IModelDecorationOptions; + setModelProperty(resource: URI, key: string, value: any): void; + getModelProperty(resource: URI, key: string): any; + setTransientModelProperty(model: ITextModel, key: string, value: any): void; getTransientModelProperty(model: ITextModel, key: string): any; getTransientModelProperties(model: ITextModel): [string, any][] | undefined; diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 0390927a4c4..207e7a3bfa7 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -85,7 +85,7 @@ class EditorOpener implements IOpener { export class OpenerService implements IOpenerService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _openers = new LinkedList(); private readonly _validators = new LinkedList(); diff --git a/src/vs/editor/browser/view/domLineBreaksComputer.ts b/src/vs/editor/browser/view/domLineBreaksComputer.ts index 81611253b01..45c568d0715 100644 --- a/src/vs/editor/browser/view/domLineBreaksComputer.ts +++ b/src/vs/editor/browser/view/domLineBreaksComputer.ts @@ -217,7 +217,9 @@ function renderLine(lineContent: string, initialVisibleColumn: number, tabSize: 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); break; diff --git a/src/vs/editor/browser/view/viewController.ts b/src/vs/editor/browser/view/viewController.ts index d99af7b2b66..69880b0133d 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, mode: 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, mode: string | null): void { - this.commandDelegate.paste(source, text, pasteOnNewLine, multicursorText, mode); + 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 }); } @@ -214,22 +207,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, @@ -239,7 +234,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 @@ -247,39 +243,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' }); } // ---------------------- @@ -289,42 +285,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 8b9c8cd1d27..b656a3f8ec6 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'; @@ -13,7 +14,7 @@ import { PointerHandler } from 'vs/editor/browser/controller/pointerHandler'; import { ITextAreaHandlerHelper, TextAreaHandler } from 'vs/editor/browser/controller/textAreaHandler'; 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,13 +37,11 @@ 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 { Range } from 'vs/editor/common/core/range'; -import { IConfiguration } from 'vs/editor/common/editorCommon'; +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'; @@ -64,31 +63,27 @@ export interface IOverlayWidgetData { 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,78 +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); - - // The event dispatcher will always go through _renderOnce before dispatching any events - this.eventDispatcher = new ViewEventDispatcher((callback: () => void) => this._renderOnce(callback)); - - // Ensure the view is the first event handler in order to update the layout - this.eventDispatcher.addEventHandler(this); + const viewController = new ViewController(configuration, model, userInputEvents, commandDelegate); // The view context is passed on to most classes (basically to reduce param. counts in ctors) - this._context = new ViewContext(configuration, themeService.getColorTheme(), model, this.eventDispatcher); + this._context = new ViewContext(configuration, themeService.getColorTheme(), model); + + // Ensure the view is the first event handler in order to update the layout + this._context.addEventHandler(this); this._register(themeService.onDidColorThemeChange(theme => { this._context.theme.update(theme); - this.eventDispatcher.emit(new viewEvents.ViewThemeChangedEvent()); + 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)); @@ -177,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 @@ -205,82 +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(); }, getLastRenderData: (): PointerHandlerLastRenderData => { - const lastViewCursorsRenderData = this.viewCursors.getLastRenderData() || []; + 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)); } }; } @@ -292,45 +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 onContentSizeChanged(e: viewEvents.ViewContentSizeChangedEvent): boolean { - this.outgoingEvents.emitContentSizeChange(e); + public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { + this._selections = e.selections; 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(); - } - return false; - } - public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { - this.outgoingEvents.emitScrollChanged(e); + 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; } @@ -342,26 +323,20 @@ export class View extends ViewEventHandler { this._renderAnimationFrame = null; } - this.eventDispatcher.removeEventHandler(this); - this.outgoingEvents.dispose(); + this._contentWidgets.overflowingContentWidgetsDomNode.domNode.remove(); - this.viewLines.dispose(); + this._context.removeEventHandler(this); + + 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: () => T): T { - const r = safeInvokeNoArg(callback); - this._scheduleRender(); - return r; - } - private _scheduleRender(): void { if (this._renderAnimationFrame === null) { this._renderAnimationFrame = dom.runAtThisOrScheduleAtNextAnimationFrame(this._onRenderScheduled.bind(this), 100); @@ -379,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; } @@ -395,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; } @@ -404,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++) { @@ -445,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 { @@ -459,7 +434,7 @@ 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; } @@ -467,34 +442,28 @@ export class View extends ViewEventHandler { } public getTargetAtClientPoint(clientX: number, clientY: number): IMouseTarget | null { - const mouseTarget = this.pointerHandler.getTargetAtClientPoint(clientX, clientY); + const mouseTarget = this._pointerHandler.getTargetAtClientPoint(clientX, clientY); if (!mouseTarget) { return null; } - return ViewOutgoingEvents.convertViewToModelMouseTarget(mouseTarget, this._context.model.coordinatesConverter); + return ViewUserInputEvents.convertViewToModelMouseTarget(mouseTarget, this._context.model.coordinatesConverter); } public createOverviewRuler(cssClassName: string): OverviewRuler { return new OverviewRuler(this._context, cssClassName); } - public change(callback: (changeAccessor: IViewZoneChangeAccessor) => any): boolean { - return this._renderOnce(() => { - const zonesHaveChanged = this.viewZones.changeViewZones(callback); - 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(); } } @@ -522,7 +491,7 @@ export class View extends ViewEventHandler { } public addContentWidget(widgetData: IContentWidgetData): void { - this.contentWidgets.addWidget(widgetData.widget); + this._contentWidgets.addWidget(widgetData.widget); this.layoutContentWidget(widgetData); this._scheduleRender(); } @@ -536,31 +505,31 @@ export class View extends ViewEventHandler { } } const newPreference = widgetData.position ? widgetData.position.preference : null; - this.contentWidgets.setWidgetPosition(widgetData.widget, 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(); } diff --git a/src/vs/editor/browser/view/viewOutgoingEvents.ts b/src/vs/editor/browser/view/viewUserInputEvents.ts similarity index 76% rename from src/vs/editor/browser/view/viewOutgoingEvents.ts rename to src/vs/editor/browser/view/viewUserInputEvents.ts index 6ba6ff3f123..22906bda60e 100644 --- a/src/vs/editor/browser/view/viewOutgoingEvents.ts +++ b/src/vs/editor/browser/view/viewUserInputEvents.ts @@ -4,66 +4,34 @@ *--------------------------------------------------------------------------------------------*/ 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, IContentSizeChangedEvent } from 'vs/editor/common/editorCommon'; -import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { IViewModel, ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel'; +import { ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; export interface EventCallback { (event: T): void; } -export class ViewOutgoingEvents extends Disposable { +export class ViewUserInputEvents { - public onDidContentSizeChange: EventCallback | null = null; - 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 onMouseUp: EventCallback | null = null; public onMouseDrag: EventCallback | null = null; public onMouseDrop: EventCallback | null = null; public onMouseWheel: EventCallback | null = null; - private readonly _viewModel: IViewModel; + private readonly _coordinatesConverter: ICoordinatesConverter; - constructor(viewModel: IViewModel) { - super(); - this._viewModel = viewModel; - } - - public emitContentSizeChange(e: viewEvents.ViewContentSizeChangedEvent): void { - if (this.onDidContentSizeChange) { - this.onDidContentSizeChange(e); - } - } - - 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); - } + constructor(coordinatesConverter: ICoordinatesConverter) { + this._coordinatesConverter = coordinatesConverter; } public emitKeyDown(e: IKeyboardEvent): void { @@ -96,18 +64,18 @@ export class ViewOutgoingEvents extends Disposable { } } - 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 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)); @@ -139,7 +107,7 @@ export class ViewOutgoingEvents extends Disposable { } private _convertViewToModelMouseTarget(target: IMouseTarget): IMouseTarget { - return ViewOutgoingEvents.convertViewToModelMouseTarget(target, this._viewModel.coordinatesConverter); + return ViewUserInputEvents.convertViewToModelMouseTarget(target, this._coordinatesConverter); } public static convertViewToModelMouseTarget(target: IMouseTarget, coordinatesConverter: ICoordinatesConverter): IMouseTarget { diff --git a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts index c6d7ed55ead..21193bac130 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'; @@ -88,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. diff --git a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts index 33979994f9d..f94c06247b4 100644 --- a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts +++ b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts @@ -112,7 +112,7 @@ export class IndentGuidesOverlay extends DynamicViewOverlay { const visibleStartLineNumber = ctx.visibleRange.startLineNumber; const visibleEndLineNumber = ctx.visibleRange.endLineNumber; - const { indentSize } = this._context.model.getOptions(); + const { indentSize } = this._context.model.getTextModelOptions(); const indentWidth = indentSize * this._spaceWidth; const scrollWidth = ctx.scrollWidth; const lineHeight = this._lineHeight; diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.css b/src/vs/editor/browser/viewParts/lines/viewLines.css index 8e4dcc659bd..2db08e15428 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLines.css +++ b/src/vs/editor/browser/viewParts/lines/viewLines.css @@ -23,7 +23,6 @@ } .monaco-editor .view-lines { - cursor: text; white-space: nowrap; } diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.ts b/src/vs/editor/browser/viewParts/lines/viewLines.ts index 09ee4fb5516..6e92c558e11 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLines.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLines.ts @@ -22,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 { @@ -134,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 @@ -280,11 +281,8 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, } 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; } @@ -309,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 { @@ -589,15 +587,9 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, this._ensureMaxLineWidth(newScrollLeft.maxHorizontalOffset); } // set `scrollLeft` - if (horizontalRevealRequest.scrollType === ScrollType.Smooth) { - this._context.viewLayout.setScrollPositionSmooth({ - scrollLeft: newScrollLeft.scrollLeft - }); - } else { - this._context.viewLayout.setScrollPositionNow({ - scrollLeft: newScrollLeft.scrollLeft - }); - } + this._context.model.setScrollPosition({ + scrollLeft: newScrollLeft.scrollLeft + }, horizontalRevealRequest.scrollType); } } } @@ -634,11 +626,11 @@ 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 | null, selections: Selection[] | null, 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; diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 1c1e3d7ade1..1be53aad7a3 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -1004,27 +1004,26 @@ export class Minimap extends ViewPart implements IMinimapModel { } public getOptions(): TextModelResolvedOptions { - return this._context.model.getOptions(); + return this._context.model.getTextModelOptions(); } public revealLineNumber(lineNumber: number): void { if (this._samplingState) { lineNumber = this._samplingState.minimapLines[lineNumber - 1]; } - this._context.privateViewEventBus.emit(new viewEvents.ViewRevealRangeRequestEvent( + this._context.model.revealRange( 'mouse', - new Range(lineNumber, 1, lineNumber, 1), - null, - viewEvents.VerticalRevealType.Center, false, + new Range(lineNumber, 1, lineNumber, 1), + viewEvents.VerticalRevealType.Center, ScrollType.Smooth - )); + ); } public setScrollTop(scrollTop: number): void { - this._context.viewLayout.setScrollPositionNow({ + this._context.model.setScrollPosition({ scrollTop: scrollTop - }); + }, ScrollType.Immediate); } //#endregion diff --git a/src/vs/editor/browser/viewParts/rulers/rulers.ts b/src/vs/editor/browser/viewParts/rulers/rulers.ts index 68621081a8e..696e088de58 100644 --- a/src/vs/editor/browser/viewParts/rulers/rulers.ts +++ b/src/vs/editor/browser/viewParts/rulers/rulers.ts @@ -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) { diff --git a/src/vs/editor/browser/viewParts/selections/selections.ts b/src/vs/editor/browser/viewParts/selections/selections.ts index bdedea154e3..47a977fe45c 100644 --- a/src/vs/editor/browser/viewParts/selections/selections.ts +++ b/src/vs/editor/browser/viewParts/selections/selections.ts @@ -419,7 +419,7 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.monaco-editor .selected-text { background-color: ${editorInactiveSelectionColor}; }`); } const editorSelectionForegroundColor = theme.getColor(editorSelectionForeground); - if (editorSelectionForegroundColor) { + if (editorSelectionForegroundColor && !editorSelectionForegroundColor.isTransparent()) { collector.addRule(`.monaco-editor .view-line span.inline-selected-text { color: ${editorSelectionForegroundColor}; }`); } }); diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts index 6173ba7a6fa..7456eb657ea 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts @@ -13,6 +13,7 @@ import { Range } from 'vs/editor/common/core/range'; import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; export interface IViewCursorRenderData { domNode: HTMLElement; @@ -63,7 +64,7 @@ export class ViewCursor { // Create the dom node this._domNode = createFastDomNode(document.createElement('div')); - this._domNode.setClassName('cursor'); + this._domNode.setClassName(`cursor ${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`); this._domNode.setHeight(this._lineHeight); this._domNode.setTop(0); this._domNode.setLeft(0); @@ -200,7 +201,7 @@ export class ViewCursor { this._domNode.domNode.textContent = this._lastRenderedContent; } - this._domNode.setClassName('cursor ' + this._renderData.textContentClassName); + this._domNode.setClassName(`cursor ${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME} ${this._renderData.textContentClassName}`); this._domNode.setDisplay('block'); this._domNode.setTop(this._renderData.top); diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css index 59b084e5922..484b79befe0 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css @@ -9,7 +9,6 @@ .monaco-editor .cursors-layer > .cursor { position: absolute; - cursor: text; overflow: hidden; } diff --git a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts index 5e28b7785ec..a73026a3f9b 100644 --- a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts +++ b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts @@ -79,9 +79,8 @@ export class ViewZones extends ViewPart { for (const whitespace of whitespaces) { oldWhitespaces.set(whitespace.id, whitespace); } - return this._context.viewLayout.changeWhitespace((whitespaceAccessor: IWhitespaceChangeAccessor) => { - let hadAChange = false; - + let hadAChange = false; + this._context.model.changeWhitespace((whitespaceAccessor: IWhitespaceChangeAccessor) => { const keys = Object.keys(this._zones); for (let i = 0, len = keys.length; i < len; i++) { const id = keys[i]; @@ -94,9 +93,8 @@ export class ViewZones extends ViewPart { hadAChange = true; } } - - return hadAChange; }); + return hadAChange; } public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { @@ -115,11 +113,7 @@ export class ViewZones extends ViewPart { } public onLineMappingChanged(e: viewEvents.ViewLineMappingChangedEvent): boolean { - const hadAChange = this._recomputeWhitespacesProps(); - if (hadAChange) { - this._context.viewLayout.onHeightMaybeChanged(); - } - return hadAChange; + return this._recomputeWhitespacesProps(); } public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { @@ -199,9 +193,9 @@ export class ViewZones extends ViewPart { } public changeViewZones(callback: (changeAccessor: IViewZoneChangeAccessor) => any): boolean { + let zonesHaveChanged = false; - return this._context.viewLayout.changeWhitespace((whitespaceAccessor: IWhitespaceChangeAccessor) => { - let zonesHaveChanged = false; + this._context.model.changeWhitespace((whitespaceAccessor: IWhitespaceChangeAccessor) => { const changeAccessor: IViewZoneChangeAccessor = { addZone: (zone: IViewZone): string => { @@ -228,9 +222,9 @@ export class ViewZones extends ViewPart { changeAccessor.addZone = invalidFunc; changeAccessor.removeZone = invalidFunc; changeAccessor.layoutZone = invalidFunc; - - return zonesHaveChanged; }); + + return zonesHaveChanged; } private _addZone(whitespaceAccessor: IWhitespaceChangeAccessor, zone: IViewZone): string { diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 4efa56bd159..aec462f71f0 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -15,16 +15,15 @@ import { hash } from 'vs/base/common/hash'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { Configuration } from 'vs/editor/browser/config/configuration'; -import { CoreEditorCommand } from 'vs/editor/browser/controller/coreCommands'; import * as editorBrowser from 'vs/editor/browser/editorBrowser'; import { EditorExtensionsRegistry, IEditorContributionDescription } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { ICommandDelegate } from 'vs/editor/browser/view/viewController'; import { IContentWidgetData, IOverlayWidgetData, View } from 'vs/editor/browser/view/viewImpl'; -import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents'; -import { ConfigurationChangedEvent, EditorLayoutInfo, IEditorOptions, EditorOption, IComputedEditorOptions, FindComputedEditorOptionValueById, IEditorConstructionOptions, filterValidationDecorations } from 'vs/editor/common/config/editorOptions'; -import { Cursor, CursorStateChangedEvent } from 'vs/editor/common/controller/cursor'; -import { CursorColumns, ICursors } from 'vs/editor/common/controller/cursorCommon'; +import { ViewUserInputEvents } from 'vs/editor/browser/view/viewUserInputEvents'; +import { ConfigurationChangedEvent, EditorLayoutInfo, IEditorOptions, EditorOption, IComputedEditorOptions, FindComputedEditorOptionValueById, filterValidationDecorations } from 'vs/editor/common/config/editorOptions'; +import { Cursor } from 'vs/editor/common/controller/cursor'; +import { CursorColumns } from 'vs/editor/common/controller/cursorCommon'; import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; @@ -53,6 +52,8 @@ 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; @@ -79,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; @@ -99,7 +98,6 @@ class ModelData { if (this.hasRealView) { this.view.dispose(); } - this.cursor.dispose(); this.viewModel.dispose(); } } @@ -210,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; @@ -239,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, @@ -250,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); @@ -326,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); } @@ -529,7 +531,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 { @@ -539,7 +541,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, @@ -557,7 +559,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, null, verticalType, revealHorizontal, scrollType); + this._modelData.viewModel.revealRange('api', revealHorizontal, viewRange, verticalType, scrollType); } public revealLine(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { @@ -642,14 +644,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; @@ -683,7 +685,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 { @@ -814,7 +816,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE throw new Error('Invalid arguments'); } } - this._modelData.cursor.setSelections(source, ranges); + this._modelData.viewModel.setSelections(source, ranges); } public getContentWidth(): number { @@ -857,33 +859,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 { @@ -900,7 +902,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, @@ -917,10 +919,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 || {}; @@ -975,43 +977,34 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return this._actions[id] || null; } - public trigger(source: string, handlerId: string, payload: any): void { + public trigger(source: string | null | undefined, handlerId: string, payload: any): void { payload = payload || {}; - // Special case for typing - if (handlerId === editorCommon.Handler.Type) { - if (!this._modelData || typeof payload.text !== 'string' || payload.text.length === 0) { - // nothing to do + switch (handlerId) { + case editorCommon.Handler.CompositionStart: + this._startComposition(); + return; + case editorCommon.Handler.CompositionEnd: + this._endComposition(source); + return; + case editorCommon.Handler.Type: { + const args = >payload; + this._type(source, args.text || ''); return; } - if (source === 'keyboard') { - this._onWillType.fire(payload.text); - } - this._modelData.cursor.trigger(source, handlerId, payload); - if (source === 'keyboard') { - this._onDidType.fire(payload.text); - } - return; - } - - // Special case for pasting - if (handlerId === editorCommon.Handler.Paste) { - if (!this._modelData || typeof payload.text !== 'string' || payload.text.length === 0) { - // nothing to do + case editorCommon.Handler.ReplacePreviousChar: { + const args = >payload; + this._replacePreviousChar(source, args.text || '', args.replaceCharCnt || 0); return; } - const startPosition = this._modelData.cursor.getSelection().getStartPosition(); - this._modelData.cursor.trigger(source, handlerId, payload); - const endPosition = this._modelData.cursor.getSelection().getStartPosition(); - if (source === 'keyboard') { - this._onDidPaste.fire( - { - range: new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column), - mode: payload.mode - } - ); + case editorCommon.Handler.Paste: { + const args = >payload; + this._paste(source, args.text || '', args.pasteOnNewLine || false, args.multicursorText || null, args.mode || null); + return; } - return; + case editorCommon.Handler.Cut: + this._cut(source); + return; } const action = this.getAction(handlerId); @@ -1027,18 +1020,67 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE if (this._triggerEditorCommand(source, handlerId, payload)) { return; } + } - this._modelData.cursor.trigger(source, handlerId, payload); - - if (handlerId === editorCommon.Handler.CompositionStart) { - this._onDidCompositionStart.fire(); + private _startComposition(): void { + if (!this._modelData) { + return; } - if (handlerId === editorCommon.Handler.CompositionEnd) { - this._onDidCompositionEnd.fire(); + this._modelData.viewModel.startComposition(); + this._onDidCompositionStart.fire(); + } + + private _endComposition(source: string | null | undefined): void { + if (!this._modelData) { + return; + } + this._modelData.viewModel.endComposition(source); + this._onDidCompositionEnd.fire(); + } + + private _type(source: string | null | undefined, text: string): void { + if (!this._modelData || text.length === 0) { + return; + } + if (source === 'keyboard') { + this._onWillType.fire(text); + } + this._modelData.viewModel.type(text, source); + if (source === 'keyboard') { + this._onDidType.fire(text); } } - private _triggerEditorCommand(source: string, handlerId: string, payload: any): boolean { + private _replacePreviousChar(source: string | null | undefined, text: string, replaceCharCnt: number): void { + if (!this._modelData) { + return; + } + this._modelData.viewModel.replacePreviousChar(text, replaceCharCnt, source); + } + + private _paste(source: string | null | undefined, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null): void { + if (!this._modelData || text.length === 0) { + return; + } + const startPosition = this._modelData.viewModel.getSelection().getStartPosition(); + this._modelData.viewModel.paste(text, pasteOnNewLine, multicursorText, source); + const endPosition = this._modelData.viewModel.getSelection().getStartPosition(); + if (source === 'keyboard') { + this._onDidPaste.fire({ + range: new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column), + mode: mode + }); + } + } + + private _cut(source: string | null | undefined): void { + if (!this._modelData) { + return; + } + this._modelData.viewModel.cut(source); + } + + private _triggerEditorCommand(source: string | null | undefined, handlerId: string, payload: any): boolean { const command = EditorExtensionsRegistry.getEditorCommand(handlerId); if (command) { payload = payload || {}; @@ -1052,11 +1094,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 { @@ -1071,7 +1113,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; } @@ -1089,22 +1131,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 { @@ -1346,10 +1388,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 { @@ -1437,43 +1476,59 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE // Someone might destroy the model from under the editor, so prevent any exceptions by setting a null model listenersToRemove.push(model.onWillDispose(() => this.setModel(null))); - const cursor = new Cursor(this._configuration, model, viewModel); + listenersToRemove.push(viewModel.onEvent((e) => { + switch (e.kind) { + case OutgoingViewModelEventKind.ContentSizeChanged: + this._onDidContentSizeChange.fire(e); + break; + case OutgoingViewModelEventKind.FocusChanged: + this._editorTextFocus.setValue(e.hasFocus); + break; + case OutgoingViewModelEventKind.ScrollChanged: + this._onDidScrollChange.fire(e); + break; + case OutgoingViewModelEventKind.ViewZonesChanged: + this._onDidChangeViewZones.fire(); + break; + case OutgoingViewModelEventKind.ReadOnlyEditAttempt: + this._onDidAttemptReadOnlyEdit.fire(); + break; + case OutgoingViewModelEventKind.CursorStateChanged: { + if (e.reachedMaxCursorCount) { + this._notificationService.warn(nls.localize('cursors.maximum', "The number of cursors has been limited to {0}.", Cursor.MAX_CURSOR_COUNT)); + } - listenersToRemove.push(cursor.onDidReachMaxCursorCount(() => { - this._notificationService.warn(nls.localize('cursors.maximum', "The number of cursors has been limited to {0}.", Cursor.MAX_CURSOR_COUNT)); - })); + const positions: Position[] = []; + for (let i = 0, len = e.selections.length; i < len; i++) { + positions[i] = e.selections[i].getPosition(); + } - listenersToRemove.push(cursor.onDidAttemptReadOnlyEdit(() => { - this._onDidAttemptReadOnlyEdit.fire(undefined); - })); + const e1: ICursorPositionChangedEvent = { + position: positions[0], + secondaryPositions: positions.slice(1), + reason: e.reason, + source: e.source + }; + this._onDidChangeCursorPosition.fire(e1); + + const e2: ICursorSelectionChangedEvent = { + selection: e.selections[0], + secondarySelections: e.selections.slice(1), + modelVersionId: e.modelVersionId, + oldSelections: e.oldSelections, + oldModelVersionId: e.oldModelVersionId, + source: e.source, + reason: e.reason + }; + this._onDidChangeCursorSelection.fire(e2); + + break; + } - listenersToRemove.push(cursor.onDidChange((e: CursorStateChangedEvent) => { - const positions: Position[] = []; - for (let i = 0, len = e.selections.length; i < len; i++) { - positions[i] = e.selections[i].getPosition(); } - - const e1: ICursorPositionChangedEvent = { - position: positions[0], - secondaryPositions: positions.slice(1), - reason: e.reason, - source: e.source - }; - this._onDidChangeCursorPosition.fire(e1); - - const e2: ICursorSelectionChangedEvent = { - selection: e.selections[0], - secondarySelections: e.selections.slice(1), - modelVersionId: e.modelVersionId, - oldSelections: e.oldSelections, - oldModelVersionId: e.oldModelVersionId, - source: e.source, - reason: e.reason - }; - this._onDidChangeCursorSelection.fire(e2); })); - const [view, hasRealView] = this._createView(viewModel, cursor); + const [view, hasRealView] = this._createView(viewModel); if (hasRealView) { this._domElement.appendChild(view.domNode.domNode); @@ -1493,101 +1548,77 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE view.domNode.domNode.setAttribute('data-uri', model.uri.toString()); } - this._modelData = new ModelData(model, viewModel, cursor, view, hasRealView, listenersToRemove); + this._modelData = new ModelData(model, viewModel, view, hasRealView, listenersToRemove); } - protected _createView(viewModel: ViewModel, cursor: Cursor): [View, boolean] { + protected _createView(viewModel: ViewModel): [View, boolean] { let commandDelegate: ICommandDelegate; if (this.isSimpleWidget) { commandDelegate = { - executeEditorCommand: (editorCommand: CoreEditorCommand, args: any): void => { - editorCommand.runCoreEditorCommand(cursor, args); + paste: (text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null) => { + this._paste('keyboard', text, pasteOnNewLine, multicursorText, mode); }, - paste: (source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null) => { - this.trigger(source, editorCommon.Handler.Paste, { text, pasteOnNewLine, multicursorText, mode }); + type: (text: string) => { + this._type('keyboard', text); }, - type: (source: string, text: string) => { - this.trigger(source, editorCommon.Handler.Type, { text }); + replacePreviousChar: (text: string, replaceCharCnt: number) => { + this._replacePreviousChar('keyboard', text, replaceCharCnt); }, - replacePreviousChar: (source: string, text: string, replaceCharCnt: number) => { - this.trigger(source, editorCommon.Handler.ReplacePreviousChar, { text, replaceCharCnt }); + startComposition: () => { + this._startComposition(); }, - compositionStart: (source: string) => { - this.trigger(source, editorCommon.Handler.CompositionStart, undefined); + endComposition: () => { + this._endComposition('keyboard'); }, - compositionEnd: (source: string) => { - this.trigger(source, editorCommon.Handler.CompositionEnd, undefined); - }, - cut: (source: string) => { - this.trigger(source, editorCommon.Handler.Cut, undefined); + cut: () => { + this._cut('keyboard'); } }; } else { commandDelegate = { - executeEditorCommand: (editorCommand: CoreEditorCommand, args: any): void => { - editorCommand.runCoreEditorCommand(cursor, args); + paste: (text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null) => { + const payload: editorCommon.PastePayload = { text, pasteOnNewLine, multicursorText, mode }; + this._commandService.executeCommand(editorCommon.Handler.Paste, payload); }, - paste: (source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null) => { - this._commandService.executeCommand(editorCommon.Handler.Paste, { - text: text, - pasteOnNewLine: pasteOnNewLine, - multicursorText: multicursorText, - mode - }); + type: (text: string) => { + const payload: editorCommon.TypePayload = { text }; + this._commandService.executeCommand(editorCommon.Handler.Type, payload); }, - type: (source: string, text: string) => { - this._commandService.executeCommand(editorCommon.Handler.Type, { - text: text - }); + replacePreviousChar: (text: string, replaceCharCnt: number) => { + const payload: editorCommon.ReplacePreviousCharPayload = { text, replaceCharCnt }; + this._commandService.executeCommand(editorCommon.Handler.ReplacePreviousChar, payload); }, - replacePreviousChar: (source: string, text: string, replaceCharCnt: number) => { - this._commandService.executeCommand(editorCommon.Handler.ReplacePreviousChar, { - text: text, - replaceCharCnt: replaceCharCnt - }); - }, - compositionStart: (source: string) => { + startComposition: () => { this._commandService.executeCommand(editorCommon.Handler.CompositionStart, {}); }, - compositionEnd: (source: string) => { + endComposition: () => { this._commandService.executeCommand(editorCommon.Handler.CompositionEnd, {}); }, - cut: (source: string) => { + cut: () => { this._commandService.executeCommand(editorCommon.Handler.Cut, {}); } }; } - const onDidChangeTextFocus = (textFocus: boolean) => { - if (this._modelData) { - this._modelData.cursor.setHasFocus(textFocus); - } - this._editorTextFocus.setValue(textFocus); - }; - - const viewOutgoingEvents = new ViewOutgoingEvents(viewModel); - viewOutgoingEvents.onDidContentSizeChange = (e) => this._onDidContentSizeChange.fire(e); - viewOutgoingEvents.onDidScroll = (e) => this._onDidScrollChange.fire(e); - viewOutgoingEvents.onDidGainFocus = () => onDidChangeTextFocus(true); - viewOutgoingEvents.onDidLoseFocus = () => onDidChangeTextFocus(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]; @@ -1985,5 +2016,5 @@ registerThemingParticipant((theme, collector) => { } const deprecatedForeground = theme.getColor(editorForeground) || 'inherit'; - collector.addRule(`.monaco-editor .${ClassName.EditorDeprecatedInlineDecoration} { text-decoration: line-through; text-decoration-color: ${deprecatedForeground}}`); + collector.addRule(`.monaco-editor.showDeprecated .${ClassName.EditorDeprecatedInlineDecoration} { text-decoration: line-through; text-decoration-color: ${deprecatedForeground}}`); }); diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index 268c2787466..3ad36126dc8 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -7,7 +7,7 @@ import 'vs/css!./media/diffEditor'; import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { ISashEvent, IVerticalSashLayoutProvider, Sash, SashState } from 'vs/base/browser/ui/sash/sash'; +import { ISashEvent, IVerticalSashLayoutProvider, Sash, SashState, Orientation } from 'vs/base/browser/ui/sash/sash'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; @@ -49,6 +49,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { IEditorProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; interface IEditorDiffDecorations { decorations: IModelDeltaDecoration[]; @@ -205,6 +206,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; @@ -280,6 +283,16 @@ 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'); @@ -468,7 +481,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } private _createLeftHandSideEditor(options: IDiffEditorOptions, instantiationService: IInstantiationService): CodeEditorWidget { - const editor = this._createInnerEditor(instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options, this._originalIsEditable)); + const editor = this._createInnerEditor(instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options, this._originalIsEditable, this._originalCodeLens)); this._register(editor.onDidScrollChange((e) => { if (this._isHandlingScrollEvent) { @@ -501,7 +514,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } private _createRightHandSideEditor(options: IDiffEditorOptions, instantiationService: IInstantiationService): CodeEditorWidget { - const editor = this._createInnerEditor(instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options)); + const editor = this._createInnerEditor(instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options, this._modifiedCodeLens)); this._register(editor.onDidScrollChange((e) => { if (this._isHandlingScrollEvent) { @@ -661,9 +674,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') { @@ -887,7 +906,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); } @@ -1058,15 +1077,21 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE return clonedOptions; } - private _adjustOptionsForLeftHandSide(options: IDiffEditorOptions, isEditable: boolean): IEditorOptions { + private _adjustOptionsForLeftHandSide(options: IDiffEditorOptions, isEditable: boolean, isCodeLensEnabled: boolean): IEditorOptions { 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: IDiffEditorOptions, isCodeLensEnabled: boolean): IEditorOptions { 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'; @@ -1657,7 +1682,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; @@ -2095,7 +2120,7 @@ class InlineViewZonesComputer extends ViewZonesComputer { 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); diff --git a/src/vs/editor/browser/widget/diffReview.ts b/src/vs/editor/browser/widget/diffReview.ts index c32c43fef19..3afb89e70a6 100644 --- a/src/vs/editor/browser/widget/diffReview.ts +++ b/src/vs/editor/browser/widget/diffReview.ts @@ -269,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(); @@ -541,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) { @@ -750,7 +752,7 @@ export class DiffReview extends Disposable { switch (type) { case DiffEntryType.Equal: if (originalLine === modifiedLine) { - ariaLabel = nls.localize('unchangedLine', "{0} unchanged line {1}", lineContent, originalLine); + 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); } diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index 609f97f351d..3bde5dfd839 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -283,6 +283,9 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC private _onDidChange = this._register(new Emitter()); public readonly onDidChange: Event = this._onDidChange.event; + private _onDidChangeFast = this._register(new Emitter()); + public readonly onDidChangeFast: Event = this._onDidChangeFast.event; + public readonly isSimpleWidget: boolean; private _computeOptionsMemory: ComputeOptionsMemory; public options!: ComputedEditorOptions; @@ -334,6 +337,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC } this.options = newOptions; + this._onDidChangeFast.fire(changeEvent); this._onDidChange.fire(changeEvent); } } @@ -377,7 +381,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC } continue; } - if (typeof baseValue === 'object' && typeof subsetValue === 'object') { + if (baseValue && typeof baseValue === 'object' && subsetValue && typeof subsetValue === 'object') { if (!this._subsetEquals(baseValue, subsetValue)) { return false; } @@ -499,8 +503,13 @@ const editorConfiguration: IConfigurationNode = { description: nls.localize('wordBasedSuggestions', "Controls whether completions should be computed based on words in the document.") }, 'editor.semanticHighlighting.enabled': { - type: 'boolean', - default: true, + 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': { @@ -532,6 +541,11 @@ const editorConfiguration: IConfigurationNode = { 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 4cd972ef3c3..6176dcd8fa0 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -11,7 +11,6 @@ import { Constants } from 'vs/base/common/uint'; import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; -import { IDimension } from 'vs/editor/common/editorCommon'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; //#region typed options @@ -93,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. @@ -589,13 +593,10 @@ export interface IEditorOptions { * Defaults to false. */ definitionLinkOpensInPeek?: boolean; -} - -export interface IEditorConstructionOptions extends IEditorOptions { /** - * The initial editor dimension (to avoid measuring the container). + * Controls strikethrough deprecated variables. */ - dimension?: IDimension; + showDeprecated?: boolean; } /** @@ -638,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 @@ -1061,6 +1072,11 @@ export interface IEditorCommentsOptions { * Defaults to true. */ insertSpace?: boolean; + /** + * Ignore empty lines when inserting line comments. + * Defaults to true. + */ + ignoreEmptyLines?: boolean; } export type EditorCommentsOptions = Readonly>; @@ -1070,6 +1086,7 @@ class EditorComments extends BaseEditorOption>; @@ -3576,6 +3605,7 @@ export const enum EditorOption { suggestOnTriggerCharacters, suggestSelection, tabCompletion, + unusualLineTerminators, useTabStops, wordSeparators, wordWrap, @@ -3585,6 +3615,7 @@ export const enum EditorOption { wordWrapMinified, wrappingIndent, wrappingStrategy, + showDeprecated, // Leave these at the end (because they have dependencies!) editorClassName, @@ -4071,6 +4102,10 @@ export const EditorOptions = { 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', @@ -4134,6 +4169,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.") } diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts index 07a39961aa6..7a966ed223e 100644 --- a/src/vs/editor/common/controller/cursor.ts +++ b/src/vs/editor/common/controller/cursor.ts @@ -4,10 +4,9 @@ *--------------------------------------------------------------------------------------------*/ 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'; @@ -17,56 +16,10 @@ import { ISelection, Selection, SelectionDirection } from 'vs/editor/common/core import * as editorCommon from 'vs/editor/common/editorCommon'; import { ITextModel, TrackedRangeStickiness, IModelDeltaDecoration, ICursorStateComputer, IIdentifiedSingleEditOperation, IValidEditOperation } from 'vs/editor/common/model'; import { RawContentChangedType, ModelRawContentChangedEvent } 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 { 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,23 +119,14 @@ 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; @@ -194,13 +138,13 @@ 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; @@ -210,53 +154,6 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { this._columnSelectData = null; this._autoClosedActions = []; this._prevEditOperationType = EditOperationType.Other; - - this._register(this._model.onDidChangeRawContent((e) => { - this._knownModelVersionId = e.versionId; - if (this._isHandling) { - return; - } - - this._onModelContentChanged(e); - })); - - 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 { @@ -265,6 +162,26 @@ 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; } @@ -285,7 +202,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { // ------ some getters/setters - public getPrimaryCursor(): CursorState { + public getPrimaryCursorState(): CursorState { return this._cursors.getPrimaryCursor(); } @@ -293,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): boolean { + 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); @@ -311,25 +229,38 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { this._validateAutoClosedActions(); - return 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, null, 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[] { @@ -356,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[] = []; @@ -393,11 +324,16 @@ 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(e: ModelRawContentChangedEvent): 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; @@ -407,16 +343,16 @@ 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 { if (this._hasFocus && e.resultingSelection && e.resultingSelection.length > 0) { const cursorState = CursorState.fromModelSelections(e.resultingSelection); - if (this.setStates('modelChange', e.isUndoing ? CursorChangeReason.Undo : e.isRedoing ? CursorChangeReason.Redo : CursorChangeReason.RecoverFromMarkers, cursorState)) { - this._revealRange('modelChange', RevealTarget.Primary, viewEvents.VerticalRevealType.Simple, true, editorCommon.ScrollType.Smooth); + 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('modelChange', CursorChangeReason.RecoverFromMarkers, CursorState.fromModelSelections(selectionsFromMarkers)); + this.setStates(eventsCollector, 'modelChange', CursorChangeReason.RecoverFromMarkers, CursorState.fromModelSelections(selectionsFromMarkers)); } } } @@ -425,7 +361,15 @@ 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; } @@ -435,9 +379,9 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { return { isReal: false, fromViewLineNumber: viewSelectionStart.lineNumber, - fromViewVisualColumn: CursorColumns.visibleColumnFromColumn2(this.context.config, this.context.viewModel, viewSelectionStart), + fromViewVisualColumn: CursorColumns.visibleColumnFromColumn2(this.context.cursorConfig, this._viewModel, viewSelectionStart), toViewLineNumber: viewPosition.lineNumber, - toViewVisualColumn: CursorColumns.visibleColumnFromColumn2(this.context.config, this.context.viewModel, viewPosition), + toViewVisualColumn: CursorColumns.visibleColumnFromColumn2(this.context.cursorConfig, this._viewModel, viewPosition), }; } @@ -445,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 { @@ -545,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; @@ -555,12 +495,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { const viewSelections = this._cursors.getViewSelections(); // Let the view get the event first. - try { - const eventsCollector = this._beginEmit(); - eventsCollector.emit(new viewEvents.ViewCursorStateChangedEvent(viewSelections, selections)); - } finally { - this._endEmit(); - } + eventsCollector.emitViewEvent(new ViewCursorStateChangedEvent(viewSelections, selections)); // Only after the view has been notified, let the rest of the world know... if (!oldState @@ -569,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) { - this.emitCursorRevealRange(source, null, this._cursors.getViewSelections(), verticalType, revealHorizontal, scrollType); - return; - } - } - - const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column); - this.emitCursorRevealRange(source, viewRange, null, verticalType, revealHorizontal, scrollType); - } - - public emitCursorRevealRange(source: string, viewRange: Range | null, viewSelections: Selection[] | null, verticalType: viewEvents.VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType) { - try { - const eventsCollector = this._beginEmit(); - eventsCollector.emit(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, viewSelections, verticalType, revealHorizontal, scrollType)); - } finally { - this._endEmit(); - } - } - // ----------------------------------------------------------------------------------------------------------- // ----- handlers beyond this point @@ -633,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; } @@ -651,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); @@ -686,146 +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; - this._cursors.stopTrackingSelections(); - - // ensure valid state on all cursors - this._cursors.ensureValidState(); - 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.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; - this._cursors.startTrackingSelections(); - this._validateAutoClosedActions(); - - if (this._emitStateChangedIfNecessary(source, cursorChangeReason, oldState)) { - this._revealRange(source, RevealTarget.Primary, viewEvents.VerticalRevealType.Simple, true, editorCommon.ScrollType.Smooth); + if (this._emitStateChangedIfNecessary(eventsCollector, source, cursorChangeReason, oldState, false)) { + this._revealPrimaryCursor(eventsCollector, source, VerticalRevealType.Simple, true, editorCommon.ScrollType.Smooth); } } - private _interpretCompositionEnd(source: string) { - if (!this._isDoingComposition && source === 'keyboard') { - // composition finishes, let's check if we need to auto complete if necessary. - const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions); - this._executeEditOperation(TypeOperations.compositionEndWithInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this._selectionsWhenCompositionStarted, this.getSelections(), autoClosedCharacters)); - this._selectionsWhenCompositionStarted = null; - } + public setIsDoingComposition(isDoingComposition: boolean): void { + this._isDoingComposition = isDoingComposition; } - private _type(source: string, text: string): void { - if (source === 'keyboard') { - // If this event is coming straight from the keyboard, look for electric characters and enter + public startComposition(eventsCollector: ViewModelEventsCollector): void { + this._selectionsWhenCompositionStarted = this.getSelections().slice(0); + } - const len = text.length; - let offset = 0; - while (offset < len) { - const charLength = strings.nextCharLength(text, offset); - const chr = text.substr(offset, charLength); - - // Here we must interpret each typed character individually + public endComposition(eventsCollector: ViewModelEventsCollector, source?: string | null | undefined): void { + this._executeEdit(() => { + if (source === 'keyboard') { + // composition finishes, let's check if we need to auto complete if necessary. const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions); - this._executeEditOperation(TypeOperations.typeWithInterceptors(this._isDoingComposition, this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), autoClosedCharacters, chr)); - - offset += charLength; + this._executeEditOperation(TypeOperations.compositionEndWithInterceptors(this._prevEditOperationType, this.context.cursorConfig, this._model, this._selectionsWhenCompositionStarted, this.getSelections(), autoClosedCharacters)); + this._selectionsWhenCompositionStarted = null; } - - } else { - this._executeEditOperation(TypeOperations.typeWithoutInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), text)); - } + }, eventsCollector, source); } - private _replacePreviousChar(text: string, replaceCharCnt: number): void { - this._executeEditOperation(TypeOperations.replacePreviousChar(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), text, replaceCharCnt)); + public type(eventsCollector: ViewModelEventsCollector, text: string, source?: string | null | undefined): void { + this._executeEdit(() => { + if (source === 'keyboard') { + // If this event is coming straight from the keyboard, look for electric characters and enter + + const len = text.length; + let offset = 0; + while (offset < len) { + const charLength = strings.nextCharLength(text, offset); + const chr = text.substr(offset, charLength); + + // Here we must interpret each typed character individually + const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions); + this._executeEditOperation(TypeOperations.typeWithInterceptors(this._isDoingComposition, this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), autoClosedCharacters, chr)); + + offset += charLength; + } + + } else { + this._executeEditOperation(TypeOperations.typeWithoutInterceptors(this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), text)); + } + }, eventsCollector, source); } - private _paste(text: string, pasteOnNewLine: boolean, multicursorText: string[]): void { - this._executeEditOperation(TypeOperations.paste(this.context.config, this.context.model, this.getSelections(), text, pasteOnNewLine, multicursorText)); + public replacePreviousChar(eventsCollector: ViewModelEventsCollector, text: string, replaceCharCnt: number, source?: string | null | undefined): void { + this._executeEdit(() => { + this._executeEditOperation(TypeOperations.replacePreviousChar(this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), text, replaceCharCnt)); + }, eventsCollector, source); } - private _cut(): void { - this._executeEditOperation(DeleteOperations.cut(this.context.config, this.context.model, this.getSelections())); + public paste(eventsCollector: ViewModelEventsCollector, text: string, pasteOnNewLine: boolean, multicursorText?: string[] | null | undefined, source?: string | null | undefined): void { + this._executeEdit(() => { + this._executeEditOperation(TypeOperations.paste(this.context.cursorConfig, this._model, this.getSelections(), text, pasteOnNewLine, multicursorText || [])); + }, eventsCollector, source, CursorChangeReason.Paste); } - private _externalExecuteCommand(command: editorCommon.ICommand): void { - this._cursors.killSecondaryCursors(); - - this._executeEditOperation(new EditOperationResult(EditOperationType.Other, [command], { - shouldPushStackElementBefore: false, - shouldPushStackElementAfter: false - })); + public cut(eventsCollector: ViewModelEventsCollector, source?: string | null | undefined): void { + this._executeEdit(() => { + this._executeEditOperation(DeleteOperations.cut(this.context.cursorConfig, this._model, this.getSelections())); + }, eventsCollector, source); } - private _externalExecuteCommands(commands: editorCommon.ICommand[]): void { - this._executeEditOperation(new EditOperationResult(EditOperationType.Other, commands, { - shouldPushStackElementBefore: false, - shouldPushStackElementAfter: false - })); + public executeCommand(eventsCollector: ViewModelEventsCollector, command: editorCommon.ICommand, source?: string | null | undefined): void { + this._executeEdit(() => { + this._cursors.killSecondaryCursors(); + + this._executeEditOperation(new EditOperationResult(EditOperationType.Other, [command], { + shouldPushStackElementBefore: false, + shouldPushStackElementAfter: false + })); + }, eventsCollector, source); + } + + public executeCommands(eventsCollector: ViewModelEventsCollector, commands: editorCommon.ICommand[], source?: string | null | undefined): void { + this._executeEdit(() => { + this._executeEditOperation(new EditOperationResult(EditOperationType.Other, commands, { + shouldPushStackElementBefore: false, + shouldPushStackElementAfter: false + })); + }, eventsCollector, source); } } diff --git a/src/vs/editor/common/controller/cursorCollection.ts b/src/vs/editor/common/controller/cursorCollection.ts index 63cc38e4862..746039533ad 100644 --- a/src/vs/editor/common/controller/cursorCollection.ts +++ b/src/vs/editor/common/controller/cursorCollection.ts @@ -82,6 +82,28 @@ export class CursorCollection { return result; } + public getTopMostViewPosition(): Position { + let result = this.primaryCursor.viewState.position; + for (let i = 0, len = this.secondaryCursors.length; i < len; i++) { + const viewPosition = this.secondaryCursors[i].viewState.position; + if (viewPosition.isBefore(result)) { + result = viewPosition; + } + } + return result; + } + + public getBottomMostViewPosition(): Position { + let result = this.primaryCursor.viewState.position; + for (let i = 0, len = this.secondaryCursors.length; i < len; i++) { + const viewPosition = this.secondaryCursors[i].viewState.position; + if (result.isBeforeOrEqual(viewPosition)) { + result = viewPosition; + } + } + return result; + } + public getSelections(): Selection[] { let result: Selection[] = []; result[0] = this.primaryCursor.modelState.selection; @@ -204,7 +226,7 @@ export class CursorCollection { const currentSelection = current.selection; const nextSelection = next.selection; - if (!this.context.config.multiCursorMergeOverlapping) { + if (!this.context.cursorConfig.multiCursorMergeOverlapping) { continue; } diff --git a/src/vs/editor/common/controller/cursorCommon.ts b/src/vs/editor/common/controller/cursorCommon.ts index ac269997918..e49c7dea933 100644 --- a/src/vs/editor/common/controller/cursorCommon.ts +++ b/src/vs/editor/common/controller/cursorCommon.ts @@ -7,18 +7,16 @@ import { CharCode } from 'vs/base/common/charCode'; import { onUnexpectedError } from 'vs/base/common/errors'; import * as strings from 'vs/base/common/strings'; import { EditorAutoClosingStrategy, EditorAutoSurroundStrategy, ConfigurationChangedEvent, EditorAutoClosingOvertypeStrategy, EditorOption, EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions'; -import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { ISelection, Selection } from 'vs/editor/common/core/selection'; -import { ICommand, IConfiguration, ScrollType } from 'vs/editor/common/editorCommon'; +import { ICommand, IConfiguration } from 'vs/editor/common/editorCommon'; import { ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import { LanguageIdentifier } from 'vs/editor/common/modes'; import { IAutoClosingPair, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; -import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; +import { ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel'; import { Constants } from 'vs/base/common/uint'; export interface IColumnSelectData { @@ -46,25 +44,6 @@ export const enum EditOperationType { DeletingRight = 3 } -export interface ICursors { - readonly context: CursorContext; - getPrimaryCursor(): CursorState; - getLastAddedCursorIndex(): number; - getAll(): CursorState[]; - - getColumnSelectData(): IColumnSelectData; - setColumnSelectData(columnSelectData: IColumnSelectData): void; - - setStates(source: string, reason: CursorChangeReason, states: PartialCursorState[] | null): void; - reveal(source: string, horizontal: boolean, target: RevealTarget, scrollType: ScrollType): void; - revealRange(source: string, revealHorizontal: boolean, viewRange: Range, verticalType: VerticalRevealType, scrollType: ScrollType): void; - - scrollTo(desiredScrollTop: number): void; - - getPrevEditOperationType(): EditOperationType; - setPrevEditOperationType(type: EditOperationType): void; -} - export interface CharacterMap { [char: string]: string; } @@ -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; } } diff --git a/src/vs/editor/common/controller/cursorMoveCommands.ts b/src/vs/editor/common/controller/cursorMoveCommands.ts index 8545cb2a485..68d4e69ebf0 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,108 +249,107 @@ 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); } + } + + 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; } @@ -358,8 +358,7 @@ export class CursorMoveCommands { 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 +372,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 +403,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 (!cursor.viewState.hasSelection() && 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 +425,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 (!cursor.viewState.hasSelection() && 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 +458,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 +769,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 +795,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 0687dbb6426..20ca28947b2 100644 --- a/src/vs/editor/common/controller/cursorMoveOperations.ts +++ b/src/vs/editor/common/controller/cursorMoveOperations.ts @@ -222,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, Constants.MAX_SAFE_SMALL_INTEGER - maxColumn); + 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/cursorWordOperations.ts b/src/vs/editor/common/controller/cursorWordOperations.ts index 13acacc4cbe..0ab6c931326 100644 --- a/src/vs/editor/common/controller/cursorWordOperations.ts +++ b/src/vs/editor/common/controller/cursorWordOperations.ts @@ -163,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); } @@ -176,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); } diff --git a/src/vs/editor/common/controller/oneCursor.ts b/src/vs/editor/common/controller/oneCursor.ts index 2bb09583133..f79fafe21ae 100644 --- a/src/vs/editor/common/controller/oneCursor.ts +++ b/src/vs/editor/common/controller/oneCursor.ts @@ -81,11 +81,11 @@ export class OneCursor { } // We only have the view state => compute the model state const selectionStart = context.model.validateRange( - context.convertViewRangeToModelRange(viewState.selectionStart) + context.coordinatesConverter.convertViewRangeToModelRange(viewState.selectionStart) ); const position = context.model.validatePosition( - context.convertViewPositionToModelPosition(viewState.position.lineNumber, viewState.position.column) + context.coordinatesConverter.convertViewPositionToModelPosition(viewState.position) ); modelState = new SingleCursorState(selectionStart, viewState.selectionStartLeftoverVisibleColumns, position, viewState.leftoverVisibleColumns); @@ -104,15 +104,15 @@ export class OneCursor { if (!viewState) { // We only have the model state => compute the view state - const viewSelectionStart1 = context.convertModelPositionToViewPosition(new Position(modelState.selectionStart.startLineNumber, modelState.selectionStart.startColumn)); - const viewSelectionStart2 = context.convertModelPositionToViewPosition(new Position(modelState.selectionStart.endLineNumber, modelState.selectionStart.endColumn)); + const viewSelectionStart1 = context.coordinatesConverter.convertModelPositionToViewPosition(new Position(modelState.selectionStart.startLineNumber, modelState.selectionStart.startColumn)); + const viewSelectionStart2 = context.coordinatesConverter.convertModelPositionToViewPosition(new Position(modelState.selectionStart.endLineNumber, modelState.selectionStart.endColumn)); const viewSelectionStart = new Range(viewSelectionStart1.lineNumber, viewSelectionStart1.column, viewSelectionStart2.lineNumber, viewSelectionStart2.column); - const viewPosition = context.convertModelPositionToViewPosition(modelState.position); + const viewPosition = context.coordinatesConverter.convertModelPositionToViewPosition(modelState.position); viewState = new SingleCursorState(viewSelectionStart, modelState.selectionStartLeftoverVisibleColumns, viewPosition, modelState.leftoverVisibleColumns); } else { // Validate new view state - const viewSelectionStart = context.validateViewRange(viewState.selectionStart, modelState.selectionStart); - const viewPosition = context.validateViewPosition(viewState.position, modelState.position); + const viewSelectionStart = context.coordinatesConverter.validateViewRange(viewState.selectionStart, modelState.selectionStart); + const viewPosition = context.coordinatesConverter.validateViewPosition(viewState.position, modelState.position); viewState = new SingleCursorState(viewSelectionStart, modelState.selectionStartLeftoverVisibleColumns, viewPosition, modelState.leftoverVisibleColumns); } diff --git a/src/vs/editor/common/editorAction.ts b/src/vs/editor/common/editorAction.ts index d8a1e71ade2..055a67fbd15 100644 --- a/src/vs/editor/common/editorAction.ts +++ b/src/vs/editor/common/editorAction.ts @@ -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 b236bb7fe7e..94c4a3c48df 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -150,6 +150,7 @@ 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; @@ -480,7 +481,7 @@ export interface IEditor { * @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. @@ -688,14 +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', - Cut: 'cut', -}; +/** + * @internal + */ +export interface TypePayload { + text: string; +} + +/** + * @internal + */ +export interface ReplacePreviousCharPayload { + text: string; + replaceCharCnt: number; +} + +/** + * @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 471e2472b43..487e6d4f0fd 100644 --- a/src/vs/editor/common/editorContextKeys.ts +++ b/src/vs/editor/common/editorContextKeys.ts @@ -36,6 +36,8 @@ export namespace EditorContextKeys { 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 diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index c4cadbb7181..56bb9bab50c 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -551,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. @@ -1281,6 +1293,8 @@ export interface IReadonlyTextBuffer { onDidChangeContent: Event; equals(other: ITextBuffer): boolean; mightContainRTL(): boolean; + mightContainUnusualLineTerminators(): boolean; + resetMightContainUnusualLineTerminators(): void; mightContainNonBasicASCII(): boolean; getBOM(): string; getEOL(): string; diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/model/editStack.ts index 24c38f88141..5316fce9e95 100644 --- a/src/vs/editor/common/model/editStack.ts +++ b/src/vs/editor/common/model/editStack.ts @@ -12,6 +12,7 @@ import { IUndoRedoService, IResourceUndoRedoElement, UndoRedoElementType, IWorks 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(); @@ -138,6 +139,10 @@ class SingleModelEditStackData { } } +export interface IUndoRedoDelegate { + prepareUndoRedo(element: MultiModelEditStackElement): Promise | IDisposable | void; +} + export class SingleModelEditStackElement implements IResourceUndoRedoElement { public model: ITextModel | URI; @@ -163,6 +168,16 @@ export class SingleModelEditStackElement implements IResourceUndoRedoElement { this._data = SingleModelEditStackData.create(model, beforeCursorState); } + public toString(): string { + const data = (this._data instanceof SingleModelEditStackData ? this._data : SingleModelEditStackData.deserialize(this._data)); + return data.changes.map(change => change.toString()).join(', '); + } + + public matchesResource(resource: URI): boolean { + const uri = (URI.isUri(this.model) ? this.model : this.model.uri); + return (uri.toString() === resource.toString()); + } + public setModel(model: ITextModel | URI): void { this.model = model; } @@ -224,6 +239,8 @@ export class MultiModelEditStackElement implements IWorkspaceUndoRedoElement { private readonly _editStackElementsArr: SingleModelEditStackElement[]; private readonly _editStackElementsMap: Map; + private _delegate: IUndoRedoDelegate | null; + public get resources(): readonly URI[] { return this._editStackElementsArr.map(editStackElement => editStackElement.resource); } @@ -240,6 +257,32 @@ export class MultiModelEditStackElement implements IWorkspaceUndoRedoElement { const key = uriGetComparisonKey(editStackElement.resource); this._editStackElementsMap.set(key, editStackElement); } + this._delegate = null; + } + + public setDelegate(delegate: IUndoRedoDelegate): void { + this._delegate = delegate; + } + + public prepareUndoRedo(): Promise | IDisposable | void { + if (this._delegate) { + return this._delegate.prepareUndoRedo(this); + } + } + + public getMissingModels(): URI[] { + const result: URI[] = []; + for (const editStackElement of this._editStackElementsArr) { + if (URI.isUri(editStackElement.model)) { + result.push(editStackElement.model); + } + } + return result; + } + + public matchesResource(resource: URI): boolean { + const key = uriGetComparisonKey(resource); + return (this._editStackElementsMap.has(key)); } public setModel(model: ITextModel | URI): void { @@ -310,7 +353,7 @@ function getModelEOL(model: ITextModel): EndOfLineSequence { } } -function isKnownStackElement(element: IResourceUndoRedoElement | IWorkspaceUndoRedoElement | null): element is EditStackElement { +export function isEditStackElement(element: IResourceUndoRedoElement | IWorkspaceUndoRedoElement | null): element is EditStackElement { if (!element) { return false; } @@ -329,7 +372,7 @@ export class EditStack { public pushStackElement(): void { const lastElement = this._undoRedoService.getLastElement(this._model.uri); - if (isKnownStackElement(lastElement)) { + if (isEditStackElement(lastElement)) { lastElement.close(); } } @@ -340,7 +383,7 @@ export class EditStack { private _getOrCreateEditStackElement(beforeCursorState: Selection[] | null): EditStackElement { const lastElement = this._undoRedoService.getLastElement(this._model.uri); - if (isKnownStackElement(lastElement) && lastElement.canAppend(this._model)) { + if (isEditStackElement(lastElement) && lastElement.canAppend(this._model)) { return lastElement; } const newElement = new SingleModelEditStackElement(this._model, beforeCursorState); diff --git a/src/vs/editor/common/model/mirrorTextModel.ts b/src/vs/editor/common/model/mirrorTextModel.ts index 25b3d150ce0..6a1272552b0 100644 --- a/src/vs/editor/common/model/mirrorTextModel.ts +++ b/src/vs/editor/common/model/mirrorTextModel.ts @@ -31,6 +31,7 @@ export class MirrorTextModel { protected _eol: string; protected _versionId: number; protected _lineStarts: PrefixSumComputer | null; + private _cachedTextValue: string | null; constructor(uri: URI, lines: string[], eol: string, versionId: number) { this._uri = uri; @@ -38,6 +39,7 @@ export class MirrorTextModel { this._eol = eol; this._versionId = versionId; this._lineStarts = null; + this._cachedTextValue = null; } dispose(): void { @@ -49,7 +51,10 @@ export class MirrorTextModel { } getText(): string { - return this._lines.join(this._eol); + if (this._cachedTextValue === null) { + this._cachedTextValue = this._lines.join(this._eol); + } + return this._cachedTextValue; } onEvents(e: IModelChangedEvent): void { @@ -66,6 +71,7 @@ export class MirrorTextModel { } this._versionId = e.versionId; + this._cachedTextValue = null; } protected _ensureLineStarts(): void { diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts index e8e737a23c3..e0c7e80cfa6 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++) { @@ -838,7 +838,7 @@ export class PieceTreeBase { if (nodeStartOffset === offset) { this.insertContentToNodeLeft(value, node); - this._searchCache.valdiate(offset); + this._searchCache.validate(offset); } else if (nodeStartOffset + node.piece.length > offset) { // we are inserting into the middle of a node. let nodesToDel: TreeNode[] = []; @@ -938,7 +938,7 @@ export class PieceTreeBase { return; } this.deleteNodeHead(startNode, endSplitPosInBuffer); - this._searchCache.valdiate(offset); + this._searchCache.validate(offset); this.validateCRLFWithPrevNode(startNode); this.computeBufferMetadata(); return; @@ -961,7 +961,7 @@ export class PieceTreeBase { let startSplitPosInBuffer = this.positionInBuffer(startNode, startPosition.remainder); this.deleteNodeTail(startNode, startSplitPosInBuffer); - this._searchCache.valdiate(offset); + this._searchCache.validate(offset); if (startNode.piece.length === 0) { nodesToDel.push(startNode); } @@ -1225,14 +1225,14 @@ export class PieceTreeBase { let cache = this._searchCache.get2(lineNumber); if (cache) { x = cache.node; - let prevAccumualtedValue = this.getAccumulatedValue(x, lineNumber - cache.nodeStartLineNumber - 1); + let prevAccumulatedValue = this.getAccumulatedValue(x, lineNumber - cache.nodeStartLineNumber - 1); let buffer = this._buffers[x.piece.bufferIndex].buffer; let startOffset = this.offsetInBuffer(x.piece.bufferIndex, x.piece.start); if (cache.nodeStartLineNumber + x.piece.lineFeedCnt === lineNumber) { - ret = buffer.substring(startOffset + prevAccumualtedValue, startOffset + x.piece.length); + ret = buffer.substring(startOffset + prevAccumulatedValue, startOffset + x.piece.length); } else { - let accumualtedValue = this.getAccumulatedValue(x, lineNumber - cache.nodeStartLineNumber); - return buffer.substring(startOffset + prevAccumualtedValue, startOffset + accumualtedValue - endOffset); + let accumulatedValue = this.getAccumulatedValue(x, lineNumber - cache.nodeStartLineNumber); + return buffer.substring(startOffset + prevAccumulatedValue, startOffset + accumulatedValue - endOffset); } } else { let nodeStartOffset = 0; @@ -1241,8 +1241,8 @@ export class PieceTreeBase { if (x.left !== SENTINEL && x.lf_left >= lineNumber - 1) { x = x.left; } else if (x.lf_left + x.piece.lineFeedCnt > lineNumber - 1) { - let prevAccumualtedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 2); - let accumualtedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 1); + let prevAccumulatedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 2); + let accumulatedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 1); let buffer = this._buffers[x.piece.bufferIndex].buffer; let startOffset = this.offsetInBuffer(x.piece.bufferIndex, x.piece.start); nodeStartOffset += x.size_left; @@ -1252,13 +1252,13 @@ export class PieceTreeBase { nodeStartLineNumber: originalLineNumber - (lineNumber - 1 - x.lf_left) }); - return buffer.substring(startOffset + prevAccumualtedValue, startOffset + accumualtedValue - endOffset); + return buffer.substring(startOffset + prevAccumulatedValue, startOffset + accumulatedValue - endOffset); } else if (x.lf_left + x.piece.lineFeedCnt === lineNumber - 1) { - let prevAccumualtedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 2); + let prevAccumulatedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 2); let buffer = this._buffers[x.piece.bufferIndex].buffer; let startOffset = this.offsetInBuffer(x.piece.bufferIndex, x.piece.start); - ret = buffer.substring(startOffset + prevAccumualtedValue, startOffset + x.piece.length); + ret = buffer.substring(startOffset + prevAccumulatedValue, startOffset + x.piece.length); break; } else { lineNumber -= x.lf_left + x.piece.lineFeedCnt; @@ -1274,10 +1274,10 @@ export class PieceTreeBase { let buffer = this._buffers[x.piece.bufferIndex].buffer; if (x.piece.lineFeedCnt > 0) { - let accumualtedValue = this.getAccumulatedValue(x, 0); + let accumulatedValue = this.getAccumulatedValue(x, 0); let startOffset = this.offsetInBuffer(x.piece.bufferIndex, x.piece.start); - ret += buffer.substring(startOffset, startOffset + accumualtedValue - endOffset); + ret += buffer.substring(startOffset, startOffset + accumulatedValue - endOffset); return ret; } else { let startOffset = this.offsetInBuffer(x.piece.bufferIndex, x.piece.start); @@ -1304,7 +1304,7 @@ export class PieceTreeBase { this._lineCnt = lfCnt; this._length = len; - this._searchCache.valdiate(this._length); + this._searchCache.validate(this._length); } // #region node operations @@ -1504,12 +1504,12 @@ export class PieceTreeBase { x = x.left; } else if (x.lf_left + x.piece.lineFeedCnt > lineNumber - 1) { let prevAccumualtedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 2); - let accumualtedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 1); + let accumulatedValue = this.getAccumulatedValue(x, lineNumber - x.lf_left - 1); nodeStartOffset += x.size_left; return { node: x, - remainder: Math.min(prevAccumualtedValue + column - 1, accumualtedValue), + remainder: Math.min(prevAccumualtedValue + column - 1, accumulatedValue), nodeStartOffset }; } else if (x.lf_left + x.piece.lineFeedCnt === lineNumber - 1) { @@ -1536,11 +1536,11 @@ export class PieceTreeBase { while (x !== SENTINEL) { if (x.piece.lineFeedCnt > 0) { - let accumualtedValue = this.getAccumulatedValue(x, 0); + let accumulatedValue = this.getAccumulatedValue(x, 0); let nodeStartOffset = this.offsetOfNode(x); return { node: x, - remainder: Math.min(column - 1, accumualtedValue), + remainder: Math.min(column - 1, accumulatedValue), nodeStartOffset }; } else { diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts index 63ca1517de7..1c81c18a0a6 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts @@ -36,15 +36,17 @@ export class PieceTreeTextBuffer implements ITextBuffer, IDisposable { private readonly _pieceTree: PieceTreeBase; private readonly _BOM: string; private _mightContainRTL: boolean; + private _mightContainUnusualLineTerminators: boolean; private _mightContainNonBasicASCII: 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, isBasicASCII: boolean, eolNormalized: boolean) { + 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 { @@ -67,6 +69,12 @@ export class PieceTreeTextBuffer implements ITextBuffer, IDisposable { public mightContainRTL(): boolean { return this._mightContainRTL; } + public mightContainUnusualLineTerminators(): boolean { + return this._mightContainUnusualLineTerminators; + } + public resetMightContainUnusualLineTerminators(): void { + this._mightContainUnusualLineTerminators = false; + } public mightContainNonBasicASCII(): boolean { return this._mightContainNonBasicASCII; } @@ -216,6 +224,7 @@ export class PieceTreeTextBuffer implements ITextBuffer, IDisposable { public applyEdits(rawOperations: ValidAnnotatedEditOperation[], recordTrimAutoWhitespace: boolean, computeUndoEdits: boolean): ApplyEditsResult { let mightContainRTL = this._mightContainRTL; + let mightContainUnusualLineTerminators = this._mightContainUnusualLineTerminators; let mightContainNonBasicASCII = this._mightContainNonBasicASCII; let canReduceOperations = true; @@ -226,12 +235,20 @@ export class PieceTreeTextBuffer implements ITextBuffer, IDisposable { canReduceOperations = false; } let validatedRange = op.range; - if (!mightContainRTL && op.text) { - // check if the new inserted text contains RTL - mightContainRTL = strings.containsRTL(op.text); - } - if (!mightContainNonBasicASCII && op.text) { - mightContainNonBasicASCII = !strings.isBasicASCII(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); + } } let validText = ''; @@ -340,6 +357,7 @@ export class PieceTreeTextBuffer implements ITextBuffer, IDisposable { this._mightContainRTL = mightContainRTL; + this._mightContainUnusualLineTerminators = mightContainUnusualLineTerminators; this._mightContainNonBasicASCII = mightContainNonBasicASCII; const contentChanges = this._doApplyEdits(operations); @@ -492,6 +510,32 @@ export class PieceTreeTextBuffer implements ITextBuffer, IDisposable { 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 */ diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts index fa8d98e94e6..d134517ba15 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts @@ -18,6 +18,7 @@ export class PieceTreeTextBufferFactory implements ITextBufferFactory { private readonly _lf: number, private readonly _crlf: number, private readonly _containsRTL: boolean, + private readonly _containsUnusualLineTerminators: boolean, private readonly _isBasicASCII: boolean, private readonly _normalizeEOL: boolean ) { } @@ -53,7 +54,7 @@ export class PieceTreeTextBufferFactory implements ITextBufferFactory { } } - return new PieceTreeTextBuffer(chunks, this._bom, eol, this._containsRTL, this._isBasicASCII, this._normalizeEOL); + return new PieceTreeTextBuffer(chunks, this._bom, eol, this._containsRTL, this._containsUnusualLineTerminators, this._isBasicASCII, this._normalizeEOL); } public getFirstLineText(lengthLimit: number): string { @@ -73,6 +74,7 @@ export class PieceTreeTextBufferBuilder implements ITextBufferBuilder { private lf: number; private crlf: number; private containsRTL: boolean; + private containsUnusualLineTerminators: boolean; private isBasicASCII: boolean; constructor() { @@ -87,6 +89,7 @@ export class PieceTreeTextBufferBuilder implements ITextBufferBuilder { this.lf = 0; this.crlf = 0; this.containsRTL = false; + this.containsUnusualLineTerminators = false; this.isBasicASCII = true; } @@ -140,9 +143,13 @@ export class PieceTreeTextBufferBuilder implements ITextBufferBuilder { this.isBasicASCII = lineStarts.isBasicASCII; } if (!this.isBasicASCII && !this.containsRTL) { - // No need to check if is basic ASCII + // No need to check if it is basic ASCII this.containsRTL = strings.containsRTL(chunk); } + if (!this.isBasicASCII && !this.containsUnusualLineTerminators) { + // No need to check if it is basic ASCII + this.containsUnusualLineTerminators = strings.containsUnusualLineTerminators(chunk); + } } public finish(normalizeEOL: boolean = true): PieceTreeTextBufferFactory { @@ -154,6 +161,7 @@ export class PieceTreeTextBufferBuilder implements ITextBufferBuilder { this.lf, this.crlf, this.containsRTL, + this.containsUnusualLineTerminators, this.isBasicASCII, normalizeEOL ); diff --git a/src/vs/editor/common/model/textChange.ts b/src/vs/editor/common/model/textChange.ts index 5f4024cfbe6..c441bf7abc9 100644 --- a/src/vs/editor/common/model/textChange.ts +++ b/src/vs/editor/common/model/textChange.ts @@ -31,6 +31,16 @@ export class TextChange { 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 diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 9774fc60eaf..4e542299323 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -30,13 +30,13 @@ 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 { ThemeColor } from 'vs/platform/theme/common/themeService'; -import { withUndefinedAsNull } from 'vs/base/common/types'; import { VSBufferReadableStream, VSBuffer } from 'vs/base/common/buffer'; import { TokensStore, MultilineTokens, countEOL, MultilineTokens2, TokensStore2 } from 'vs/editor/common/model/tokensStore'; import { Color } from 'vs/base/common/color'; import { EditorTheme } from 'vs/editor/common/view/viewContext'; -import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; +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(); @@ -277,6 +277,7 @@ 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; @@ -350,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; @@ -699,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(); } @@ -708,6 +720,11 @@ export class TextModel extends Disposable implements model.ITextModel { return this._alternativeVersionId; } + public getInitialUndoRedoSnapshot(): ResourceEditStackSnapshot | null { + this._assertNotDisposed(); + return this._initialUndoRedoSnapshot; + } + public getOffsetAt(rawPosition: IPosition): number { this._assertNotDisposed(); let position = this._validatePosition(rawPosition.lineNumber, rawPosition.column, StringOffsetValidationType.Relaxed); @@ -733,6 +750,10 @@ export class TextModel extends Disposable implements model.ITextModel { 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(); @@ -1097,7 +1118,7 @@ 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; @@ -1176,6 +1197,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(); @@ -1300,6 +1324,9 @@ 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); } @@ -3157,8 +3184,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; diff --git a/src/vs/editor/common/model/textModelSearch.ts b/src/vs/editor/common/model/textModelSearch.ts index ce5b545c6f9..8ebfdd22216 100644 --- a/src/vs/editor/common/model/textModelSearch.ts +++ b/src/vs/editor/common/model/textModelSearch.ts @@ -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/modes.ts b/src/vs/editor/common/modes.ts index b7ba9581fba..b89285449aa 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -1411,20 +1411,29 @@ export interface RenameProvider { */ export interface AuthenticationSession { id: string; - getAccessToken(): Thenable; + accessToken: string; account: { - displayName: string; + label: string; id: string; } + scopes: ReadonlyArray; } /** * @internal */ export interface AuthenticationSessionsChangeEvent { - added: string[]; - removed: string[]; - changed: string[]; + added: ReadonlyArray; + removed: ReadonlyArray; + changed: ReadonlyArray; +} + +/** + * @internal + */ +export interface AuthenticationProviderInformation { + id: string; + label: string; } export interface Command { @@ -1602,7 +1611,7 @@ export interface IWebviewPortMapping { export interface IWebviewOptions { readonly enableScripts?: boolean; readonly enableCommandUris?: boolean; - readonly localResourceRoots?: ReadonlyArray; + readonly localResourceRoots?: ReadonlyArray; readonly portMapping?: ReadonlyArray; } diff --git a/src/vs/editor/common/modes/languageSelector.ts b/src/vs/editor/common/modes/languageSelector.ts index 4cf93dc4d23..f3bbac42f8e 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 f3097d5f6f4..c0ca979fb9b 100644 --- a/src/vs/editor/common/modes/linkComputer.ts +++ b/src/vs/editor/common/modes/linkComputer.ts @@ -154,7 +154,7 @@ function getClassifier(): CharacterClassifier { if (_classifier === null) { _classifier = new CharacterClassifier(CharacterClass.None); - const FORCE_TERMINATION_CHARACTERS = ' \t<>\'\"、。īŊĄīŊ¤īŧŒīŧŽīŧšīŧ›īŧŸīŧīŧ īŧƒīŧ„īŧ…īŧ†īŧŠâ€˜â€œã€ˆã€Šã€Œã€Žã€ã€”īŧˆīŧģīŊ›īŊĸīŊŖīŊīŧŊīŧ‰ã€•】』」》〉”’īŊ€īŊžâ€Ļ'; + const FORCE_TERMINATION_CHARACTERS = ' \t<>\'\"、。īŊĄīŊ¤īŧŒīŧŽīŧšīŧ›â€˜â€œã€ˆã€Šã€Œã€Žã€ã€”īŧˆīŧģīŊ›īŊĸīŊŖīŊīŧŊīŧ‰ã€•】』」》〉”’īŊ€īŊžâ€Ļ'; for (let i = 0; i < FORCE_TERMINATION_CHARACTERS.length; i++) { _classifier.set(FORCE_TERMINATION_CHARACTERS.charCodeAt(i), CharacterClass.ForceTermination); } diff --git a/src/vs/editor/common/modes/modesRegistry.ts b/src/vs/editor/common/modes/modesRegistry.ts index f5c523e6574..c2ef63388de 100644 --- a/src/vs/editor/common/modes/modesRegistry.ts +++ b/src/vs/editor/common/modes/modesRegistry.ts @@ -62,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/textToHtmlTokenizer.ts b/src/vs/editor/common/modes/textToHtmlTokenizer.ts index c8c4acac430..dd64e79ded6 100644 --- a/src/vs/editor/common/modes/textToHtmlTokenizer.ts +++ b/src/vs/editor/common/modes/textToHtmlTokenizer.ts @@ -68,7 +68,9 @@ export function tokenizeLineToHTML(text: string, viewLineTokens: IViewLineTokens break; case CharCode.UTF8_BOM: - case CharCode.LINE_SEPARATOR_2028: + case CharCode.LINE_SEPARATOR: + case CharCode.PARAGRAPH_SEPARATOR: + case CharCode.NEXT_LINE: partContent += '\ufffd'; break; diff --git a/src/vs/editor/common/modes/tokenizationRegistry.ts b/src/vs/editor/common/modes/tokenizationRegistry.ts index fc3423047fb..3cb7a15ea65 100644 --- a/src/vs/editor/common/modes/tokenizationRegistry.ts +++ b/src/vs/editor/common/modes/tokenizationRegistry.ts @@ -7,8 +7,7 @@ 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'; +import { toArray } from 'vs/base/common/arrays'; export class TokenizationRegistryImpl implements ITokenizationRegistry { @@ -77,13 +76,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: toArray(this._map.keys()), changedColorMap: true }); } diff --git a/src/vs/editor/common/services/editorWorkerService.ts b/src/vs/editor/common/services/editorWorkerService.ts index ab850e96a2c..a99c84822d8 100644 --- a/src/vs/editor/common/services/editorWorkerService.ts +++ b/src/vs/editor/common/services/editorWorkerService.ts @@ -19,7 +19,7 @@ export interface IDiffComputationResult { } export interface IEditorWorkerService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; canComputeDiff(original: URI, modified: URI): boolean; computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean, maxComputationTime: number): Promise; diff --git a/src/vs/editor/common/services/editorWorkerServiceImpl.ts b/src/vs/editor/common/services/editorWorkerServiceImpl.ts index 0afaa0abca4..3ee2f5ad182 100644 --- a/src/vs/editor/common/services/editorWorkerServiceImpl.ts +++ b/src/vs/editor/common/services/editorWorkerServiceImpl.ts @@ -22,6 +22,7 @@ import { regExpFlags } from 'vs/base/common/strings'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import { ILogService } from 'vs/platform/log/common/log'; import { StopWatch } from 'vs/base/common/stopwatch'; +import { canceled } from 'vs/base/common/errors'; /** * Stop syncing a model to the worker if it was not needed for 1 min. @@ -46,7 +47,7 @@ function canSyncModel(modelService: IModelService, resource: URI): boolean { export class EditorWorkerServiceImpl extends Disposable implements IEditorWorkerService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _modelService: IModelService; private readonly _workerManager: WorkerManager; @@ -380,6 +381,7 @@ export class EditorWorkerClient extends Disposable { private _worker: IWorkerClient | null; private readonly _workerFactory: DefaultWorkerFactory; private _modelManager: EditorModelManager | null; + private _disposed = false; constructor(modelService: IModelService, keepIdleModels: boolean, label: string | undefined) { super(); @@ -427,6 +429,9 @@ export class EditorWorkerClient extends Disposable { } protected _withSyncedResources(resources: URI[]): Promise { + if (this._disposed) { + return Promise.reject(canceled()); + } return this._getProxy().then((proxy) => { this._getOrCreateModelManager(proxy).ensureSyncedResources(resources); return proxy; @@ -495,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/languagesRegistry.ts b/src/vs/editor/common/services/languagesRegistry.ts index 8a12a471f95..c734735a733 100644 --- a/src/vs/editor/common/services/languagesRegistry.ts +++ b/src/vs/editor/common/services/languagesRegistry.ts @@ -15,7 +15,6 @@ import { NULL_LANGUAGE_IDENTIFIER, NULL_MODE_ID } from 'vs/editor/common/modes/n import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService'; import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; -import { withUndefinedAsNull } from 'vs/base/common/types'; const hasOwnProperty = Object.prototype.hasOwnProperty; @@ -154,9 +153,14 @@ export class LanguagesRegistry extends Disposable { } if (Array.isArray(lang.extensions)) { + if (lang.configuration) { + // insert first as this appears to be the 'primary' language definition + resolvedLanguage.extensions = lang.extensions.concat(resolvedLanguage.extensions); + } else { + resolvedLanguage.extensions = resolvedLanguage.extensions.concat(lang.extensions); + } for (let extension of lang.extensions) { mime.registerTextMime({ id: langId, mime: primaryMime, extension: extension }, this._warnOnOverwrite); - resolvedLanguage.extensions.push(extension); } } @@ -268,7 +272,7 @@ export class LanguagesRegistry extends Disposable { return null; } const language = this._languages[modeId]; - return withUndefinedAsNull(language.mimetypes[0]); + return (language.mimetypes[0] || null); } public extractModeIds(commaSeparatedMimetypesOrCommaSeparatedIds: string | undefined): string[] { diff --git a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts index 2e591f861c9..cb80908b649 100644 --- a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts +++ b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts @@ -12,11 +12,9 @@ import { themeColorFromId, ThemeColor } from 'vs/platform/theme/common/themeServ import { overviewRulerWarning, overviewRulerInfo, overviewRulerError } from 'vs/editor/common/view/editorColorRegistry'; import { IModelService } from 'vs/editor/common/services/modelService'; import { Range } from 'vs/editor/common/core/range'; -import { keys } from 'vs/base/common/map'; import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService'; import { Schemas } from 'vs/base/common/network'; import { Emitter, Event } from 'vs/base/common/event'; -import { withUndefinedAsNull } from 'vs/base/common/types'; import { minimapWarning, minimapError } from 'vs/platform/theme/common/colorRegistry'; import { Delayer } from 'vs/base/common/async'; @@ -33,7 +31,7 @@ class MarkerDecorations extends Disposable { ) { super(); this._register(toDisposable(() => { - this.model.deltaDecorations(keys(this._markersData), []); + this.model.deltaDecorations([...this._markersData.keys()], []); this._markersData.clear(); })); } @@ -43,7 +41,7 @@ class MarkerDecorations extends Disposable { } public update(markers: IMarker[], newDecorations: IModelDeltaDecoration[]): boolean { - const oldIds = keys(this._markersData); + const oldIds = [...this._markersData.keys()]; this._markersData.clear(); const ids = this.model.deltaDecorations(oldIds, newDecorations); for (let index = 0; index < ids.length; index++) { @@ -70,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; @@ -96,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][] { diff --git a/src/vs/editor/common/services/markersDecorationService.ts b/src/vs/editor/common/services/markersDecorationService.ts index cd1c541cbc0..745260c8884 100644 --- a/src/vs/editor/common/services/markersDecorationService.ts +++ b/src/vs/editor/common/services/markersDecorationService.ts @@ -12,7 +12,7 @@ import { Range } from 'vs/editor/common/core/range'; export const IMarkerDecorationsService = createDecorator('markerDecorationsService'); export interface IMarkerDecorationsService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; onDidChangeMarker: Event; diff --git a/src/vs/editor/common/services/modeService.ts b/src/vs/editor/common/services/modeService.ts index 678b15836af..2739d652551 100644 --- a/src/vs/editor/common/services/modeService.ts +++ b/src/vs/editor/common/services/modeService.ts @@ -28,7 +28,7 @@ export interface ILanguageSelection extends IDisposable { } export interface IModeService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; onDidCreateMode: Event; onLanguagesMaybeChanged: Event; diff --git a/src/vs/editor/common/services/modelService.ts b/src/vs/editor/common/services/modelService.ts index 60ce39358dc..ab583a7390c 100644 --- a/src/vs/editor/common/services/modelService.ts +++ b/src/vs/editor/common/services/modelService.ts @@ -16,7 +16,7 @@ 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; diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index 9ab05132790..576a7dd4ce8 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -24,14 +24,14 @@ 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 } from 'vs/platform/undoRedo/common/undoRedo'; +import { IUndoRedoService, IUndoRedoElement, IPastFutureElements, ResourceEditStackSnapshot } from 'vs/platform/undoRedo/common/undoRedo'; import { StringSHA1 } from 'vs/base/common/hash'; -import { SingleModelEditStackElement, MultiModelEditStackElement, EditStackElement } from 'vs/editor/common/model/editStack'; +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?: boolean; + enabled: true | false | 'configuredByTheme'; } function MODEL_ID(resource: URI): string { @@ -51,7 +51,7 @@ function computeModelSha1(model: ITextModel): string { class ModelData implements IDisposable { - public readonly model: ITextModel; + public readonly model: TextModel; private _languageSelection: ILanguageSelection | null; private _languageSelectionListener: IDisposable | null; @@ -59,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 ) { @@ -113,12 +113,12 @@ interface IRawConfig { const DEFAULT_EOL = (platform.isLinux || platform.isMacintosh) ? DefaultEndOfLine.LF : DefaultEndOfLine.CRLF; -interface EditStackPastFutureElements { +export interface EditStackPastFutureElements { past: EditStackElement[]; future: EditStackElement[]; } -function isEditStackPastFutureElements(undoElements: IPastFutureElements): undoElements is EditStackPastFutureElements { +export function isEditStackPastFutureElements(undoElements: IPastFutureElements): undoElements is EditStackPastFutureElements { return (isEditStackElements(undoElements.past) && isEditStackElements(undoElements.future)); } @@ -138,7 +138,9 @@ function isEditStackElements(elements: IUndoRedoElement[]): elements is EditStac 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, @@ -352,12 +354,18 @@ export class ModelServiceImpl extends Disposable implements IModelService { if (this._disposedModelsHeapSize > maxModelsHeapSize) { // we must remove some old undo stack elements to free up some memory const disposedModels: DisposedModelInfo[] = []; - this._disposedModels.forEach(entry => disposedModels.push(entry)); + 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); - this._undoRedoService.removeElements(disposedModel.uri); + if (disposedModel.initialUndoRedoSnapshot !== null) { + this._undoRedoService.restoreSnapshot(disposedModel.initialUndoRedoSnapshot); + } } } } @@ -369,18 +377,28 @@ export class ModelServiceImpl extends Disposable implements IModelService { if (resource && this._disposedModels.has(MODEL_ID(resource))) { const disposedModelData = this._removeDisposedModel(resource)!; const elements = this._undoRedoService.getElements(resource); - if (computeModelSha1(model) === disposedModelData.sha1 && isEditStackPastFutureElements(elements)) { + const sha1IsEqual = (computeModelSha1(model) === disposedModelData.sha1); + if (sha1IsEqual || disposedModelData.sharesUndoRedoStack) { for (const element of elements.past) { - element.setModel(model); + if (isEditStackElement(element) && element.matchesResource(resource)) { + element.setModel(model); + } } for (const element of elements.future) { - element.setModel(model); + 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); } - this._undoRedoService.setElementsIsValid(resource, true); - model._overwriteVersionId(disposedModelData.versionId); - model._overwriteAlternativeVersionId(disposedModelData.alternativeVersionId); } else { - this._undoRedoService.removeElements(resource); + if (disposedModelData.initialUndoRedoSnapshot !== null) { + this._undoRedoService.restoreSnapshot(disposedModelData.initialUndoRedoSnapshot); + } } } const modelId = MODEL_ID(model.uri); @@ -504,33 +522,47 @@ export class ModelServiceImpl extends Disposable implements IModelService { return; } const model = modelData.model; + const sharesUndoRedoStack = (this._undoRedoService.getUriComparisonKey(model.uri) !== model.uri.toString()); let maintainUndoRedoStack = false; let heapSize = 0; - if (this._shouldRestoreUndoStack() && (resource.scheme === Schemas.file || resource.scheme === Schemas.vscodeRemote || resource.scheme === Schemas.userData)) { + 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) && isEditStackPastFutureElements(elements)) { - maintainUndoRedoStack = true; + if (elements.past.length > 0 || elements.future.length > 0) { for (const element of elements.past) { - heapSize += element.heapSize(resource); - element.setModel(resource); // remove reference from text buffer instance + 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) { - heapSize += element.heapSize(resource); - element.setModel(resource); // remove reference from text buffer instance + if (isEditStackElement(element) && element.matchesResource(resource)) { + maintainUndoRedoStack = true; + heapSize += element.heapSize(resource); + element.setModel(resource); // remove reference from text buffer instance + } } } } if (!maintainUndoRedoStack) { - this._undoRedoService.removeElements(resource); + 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 (heapSize > maxMemory) { + if (!sharesUndoRedoStack && heapSize > maxMemory) { // the undo stack for this file would never fit in the configured memory, so don't bother with it. - this._undoRedoService.removeElements(resource); + const initialUndoRedoSnapshot = modelData.model.getInitialUndoRedoSnapshot(); + if (initialUndoRedoSnapshot !== null) { + this._undoRedoService.restoreSnapshot(initialUndoRedoSnapshot); + } modelData.model.dispose(); return; } @@ -538,8 +570,8 @@ export class ModelServiceImpl extends Disposable implements IModelService { this._ensureDisposedModelsHeapSize(maxMemory - heapSize); // We only invalidate the elements, but they remain in the undo-redo service. - this._undoRedoService.setElementsIsValid(resource, false); - this._insertDisposedModel(new DisposedModelInfo(resource, Date.now(), heapSize, computeModelSha1(model), model.getVersionId(), model.getAlternativeVersionId())); + 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(); } @@ -601,11 +633,11 @@ export interface ILineSequence { export const SEMANTIC_HIGHLIGHTING_SETTING_ID = 'editor.semanticHighlighting'; export function isSemanticColoringEnabled(model: ITextModel, themeService: IThemeService, configurationService: IConfigurationService): boolean { - if (!themeService.getColorTheme().semanticHighlighting) { - return false; + const setting = configurationService.getValue(SEMANTIC_HIGHLIGHTING_SETTING_ID, { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri })?.enabled; + if (typeof setting === 'boolean') { + return setting; } - const options = configurationService.getValue(SEMANTIC_HIGHLIGHTING_SETTING_ID, { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri }); - return Boolean(options && options.enabled); + return themeService.getColorTheme().semanticHighlighting; } class SemanticColoringFeature extends Disposable { diff --git a/src/vs/editor/common/services/modelUndoRedoParticipant.ts b/src/vs/editor/common/services/modelUndoRedoParticipant.ts new file mode 100644 index 00000000000..0862ccf8d6e --- /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 46da05998ea..74470a81017 100644 --- a/src/vs/editor/common/services/resolverService.ts +++ b/src/vs/editor/common/services/resolverService.ts @@ -12,7 +12,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' export const ITextModelService = createDecorator('textModelService'); export interface ITextModelService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Provided a resource URI, it will return a model reference @@ -26,9 +26,9 @@ export interface ITextModelService { registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable; /** - * Check if a provider for the given `scheme` exists + * Check if the given resource can be resolved to a text model. */ - hasTextModelContentProvider(scheme: string): boolean; + canHandleResource(resource: URI): boolean; } export interface ITextModelContentProvider { diff --git a/src/vs/editor/common/services/textResourceConfigurationService.ts b/src/vs/editor/common/services/textResourceConfigurationService.ts index 792cacda5b3..23a2335b6df 100644 --- a/src/vs/editor/common/services/textResourceConfigurationService.ts +++ b/src/vs/editor/common/services/textResourceConfigurationService.ts @@ -31,7 +31,7 @@ export interface ITextResourceConfigurationChangeEvent { export interface ITextResourceConfigurationService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Event that fires when the configuration changes. @@ -70,7 +70,7 @@ export const ITextResourcePropertiesService = createDecorator this._onDidChangeConfiguration.fire(this.toResourceConfigurationChangeEvent(e)))); } - getValue(resource: URI, section?: string): T; - getValue(resource: URI, at?: IPosition, section?: string): T; - getValue(resource: URI, arg2?: any, arg3?: any): T { + getValue(resource: URI | undefined, section?: string): T; + getValue(resource: URI | undefined, at?: IPosition, section?: string): T; + getValue(resource: URI | undefined, arg2?: any, arg3?: any): T { if (typeof arg3 === 'string') { return this._getValue(resource, Position.isIPosition(arg2) ? arg2 : null, arg3); } @@ -98,7 +98,7 @@ export class TextResourceConfigurationService extends Disposable implements ITex return ConfigurationTarget.USER_LOCAL; } - private _getValue(resource: URI, position: IPosition | null, section: string | undefined): T { + private _getValue(resource: URI | undefined, position: IPosition | null, section: string | undefined): T { const language = resource ? this.getLanguage(resource, position) : undefined; if (typeof section === 'undefined') { return this.configurationService.getValue({ resource, overrideIdentifier: language }); diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 7a67090482c..b1832df3859 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -269,20 +269,22 @@ export enum EditorOption { suggestOnTriggerCharacters = 99, suggestSelection = 100, tabCompletion = 101, - useTabStops = 102, - wordSeparators = 103, - wordWrap = 104, - wordWrapBreakAfterCharacters = 105, - wordWrapBreakBeforeCharacters = 106, - wordWrapColumn = 107, - wordWrapMinified = 108, - wrappingIndent = 109, - wrappingStrategy = 110, - editorClassName = 111, - pixelRatio = 112, - tabFocusMode = 113, - layoutInfo = 114, - wrappingInfo = 115 + 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 } /** diff --git a/src/vs/editor/common/view/viewContext.ts b/src/vs/editor/common/view/viewContext.ts index 573b0827f0a..74628ebb857 100644 --- a/src/vs/editor/common/view/viewContext.ts +++ b/src/vs/editor/common/view/viewContext.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ 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 { IColorTheme, ThemeType } from 'vs/platform/theme/common/themeService'; @@ -37,27 +36,24 @@ export class ViewContext { public readonly configuration: IConfiguration; public readonly model: IViewModel; public readonly viewLayout: IViewLayout; - public readonly privateViewEventBus: ViewEventDispatcher; public readonly theme: EditorTheme; constructor( configuration: IConfiguration, theme: IColorTheme, - model: IViewModel, - privateViewEventBus: ViewEventDispatcher + model: IViewModel ) { this.configuration = configuration; this.theme = new EditorTheme(theme); this.model = model; this.viewLayout = model.viewLayout; - this.privateViewEventBus = privateViewEventBus; } public addEventHandler(eventHandler: ViewEventHandler): void { - this.privateViewEventBus.addEventHandler(eventHandler); + this.model.addViewEventHandler(eventHandler); } public removeEventHandler(eventHandler: ViewEventHandler): void { - this.privateViewEventBus.removeEventHandler(eventHandler); + this.model.removeViewEventHandler(eventHandler); } } diff --git a/src/vs/editor/common/view/viewEventDispatcher.ts b/src/vs/editor/common/view/viewEventDispatcher.ts deleted file mode 100644 index 54bd7a6e986..00000000000 --- a/src/vs/editor/common/view/viewEventDispatcher.ts +++ /dev/null @@ -1,92 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ViewEvent } from 'vs/editor/common/view/viewEvents'; -import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; - -export class ViewEventDispatcher { - - private readonly _eventHandlerGateKeeper: (callback: () => void) => void; - private readonly _eventHandlers: ViewEventHandler[]; - private _eventQueue: ViewEvent[] | null; - private _isConsumingQueue: boolean; - - constructor(eventHandlerGateKeeper: (callback: () => void) => void) { - this._eventHandlerGateKeeper = eventHandlerGateKeeper; - this._eventHandlers = []; - this._eventQueue = null; - this._isConsumingQueue = false; - } - - public addEventHandler(eventHandler: ViewEventHandler): void { - for (let i = 0, len = this._eventHandlers.length; i < len; i++) { - if (this._eventHandlers[i] === eventHandler) { - console.warn('Detected duplicate listener in ViewEventDispatcher', eventHandler); - } - } - this._eventHandlers.push(eventHandler); - } - - public removeEventHandler(eventHandler: ViewEventHandler): void { - for (let i = 0; i < this._eventHandlers.length; i++) { - if (this._eventHandlers[i] === eventHandler) { - this._eventHandlers.splice(i, 1); - break; - } - } - } - - public emit(event: ViewEvent): void { - - if (this._eventQueue) { - this._eventQueue.push(event); - } else { - this._eventQueue = [event]; - } - - if (!this._isConsumingQueue) { - this.consumeQueue(); - } - } - - public emitMany(events: ViewEvent[]): void { - if (this._eventQueue) { - this._eventQueue = this._eventQueue.concat(events); - } else { - this._eventQueue = events; - } - - if (!this._isConsumingQueue) { - this.consumeQueue(); - } - } - - private consumeQueue(): void { - this._eventHandlerGateKeeper(() => { - try { - this._isConsumingQueue = true; - - this._doConsumeQueue(); - - } finally { - this._isConsumingQueue = false; - } - }); - } - - private _doConsumeQueue(): void { - while (this._eventQueue) { - // Empty event queue, as events might come in while sending these off - let events = this._eventQueue; - this._eventQueue = null; - - // Use a clone of the event handlers list, as they might remove themselves - let eventHandlers = this._eventHandlers.slice(0); - for (let i = 0, len = eventHandlers.length; i < len; i++) { - eventHandlers[i].handleEvents(events); - } - } - } -} diff --git a/src/vs/editor/common/view/viewEvents.ts b/src/vs/editor/common/view/viewEvents.ts index 19c81ba6dce..804d1b977f5 100644 --- a/src/vs/editor/common/view/viewEvents.ts +++ b/src/vs/editor/common/view/viewEvents.ts @@ -3,33 +3,30 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as errors from 'vs/base/common/errors'; -import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ScrollEvent } from 'vs/base/common/scrollable'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { ScrollType, IContentSizeChangedEvent } from 'vs/editor/common/editorCommon'; +import { ScrollType } from 'vs/editor/common/editorCommon'; import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents'; export const enum ViewEventType { - ViewConfigurationChanged = 1, - ViewContentSizeChanged = 2, - ViewCursorStateChanged = 3, - ViewDecorationsChanged = 4, - ViewFlushed = 5, - ViewFocusChanged = 6, - ViewLanguageConfigurationChanged = 7, - ViewLineMappingChanged = 8, - ViewLinesChanged = 9, - ViewLinesDeleted = 10, - ViewLinesInserted = 11, - ViewRevealRangeRequest = 12, - ViewScrollChanged = 13, - ViewThemeChanged = 14, - ViewTokensChanged = 15, - ViewTokensColorsChanged = 16, - ViewZonesChanged = 17, + ViewConfigurationChanged, + ViewCursorStateChanged, + ViewDecorationsChanged, + ViewFlushed, + ViewFocusChanged, + ViewLanguageConfigurationChanged, + ViewLineMappingChanged, + ViewLinesChanged, + ViewLinesDeleted, + ViewLinesInserted, + ViewRevealRangeRequest, + ViewScrollChanged, + ViewThemeChanged, + ViewTokensChanged, + ViewTokensColorsChanged, + ViewZonesChanged, } export class ViewConfigurationChangedEvent { @@ -47,25 +44,6 @@ export class ViewConfigurationChangedEvent { } } -export class ViewContentSizeChangedEvent implements IContentSizeChangedEvent { - - public readonly type = ViewEventType.ViewContentSizeChanged; - - public readonly contentWidth: number; - public readonly contentHeight: number; - - public readonly contentWidthChanged: boolean; - public readonly contentHeightChanged: boolean; - - constructor(source: IContentSizeChangedEvent) { - this.contentWidth = source.contentWidth; - this.contentHeight = source.contentHeight; - - this.contentWidthChanged = source.contentWidthChanged; - this.contentHeightChanged = source.contentHeightChanged; - } -} - export class ViewCursorStateChangedEvent { public readonly type = ViewEventType.ViewCursorStateChanged; @@ -224,9 +202,9 @@ export class ViewRevealRangeRequestEvent { /** * Source of the call that caused the event. */ - readonly source: string; + readonly source: string | null | undefined; - constructor(source: string, range: Range | null, selections: Selection[] | null, 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; @@ -308,7 +286,6 @@ export class ViewZonesChangedEvent { export type ViewEvent = ( ViewConfigurationChangedEvent - | ViewContentSizeChangedEvent | ViewCursorStateChangedEvent | ViewDecorationsChangedEvent | ViewFlushedEvent @@ -325,94 +302,3 @@ export type ViewEvent = ( | ViewTokensColorsChangedEvent | ViewZonesChangedEvent ); - -export interface IViewEventListener { - (events: ViewEvent[]): void; -} - -export class ViewEventEmitter extends Disposable { - private _listeners: IViewEventListener[]; - private _collector: ViewEventsCollector | null; - private _collectorCnt: number; - - constructor() { - super(); - this._listeners = []; - this._collector = null; - this._collectorCnt = 0; - } - - public dispose(): void { - this._listeners = []; - super.dispose(); - } - - protected _beginEmit(): ViewEventsCollector { - this._collectorCnt++; - if (this._collectorCnt === 1) { - this._collector = new ViewEventsCollector(); - } - return this._collector!; - } - - protected _endEmit(): void { - this._collectorCnt--; - if (this._collectorCnt === 0) { - const events = this._collector!.finalize(); - this._collector = null; - if (events.length > 0) { - this._emit(events); - } - } - } - - private _emit(events: ViewEvent[]): void { - const listeners = this._listeners.slice(0); - for (let i = 0, len = listeners.length; i < len; i++) { - safeInvokeListener(listeners[i], events); - } - } - - public addEventListener(listener: (events: ViewEvent[]) => void): IDisposable { - this._listeners.push(listener); - return toDisposable(() => { - let listeners = this._listeners; - for (let i = 0, len = listeners.length; i < len; i++) { - if (listeners[i] === listener) { - listeners.splice(i, 1); - break; - } - } - }); - } -} - -export class ViewEventsCollector { - - private _events: ViewEvent[]; - private _eventsLen = 0; - - constructor() { - this._events = []; - this._eventsLen = 0; - } - - public emit(event: ViewEvent) { - this._events[this._eventsLen++] = event; - } - - public finalize(): ViewEvent[] { - let result = this._events; - this._events = []; - return result; - } - -} - -function safeInvokeListener(listener: IViewEventListener, events: ViewEvent[]): void { - try { - listener(events); - } catch (e) { - errors.onUnexpectedError(e); - } -} diff --git a/src/vs/editor/common/viewLayout/linesLayout.ts b/src/vs/editor/common/viewLayout/linesLayout.ts index c549df8d22c..ceef064de9a 100644 --- a/src/vs/editor/common/viewLayout/linesLayout.ts +++ b/src/vs/editor/common/viewLayout/linesLayout.ts @@ -180,33 +180,36 @@ export class LinesLayout { this._lineCount = lineCount; } - public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => T): T { + public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => void): boolean { + let hadAChange = false; try { - const accessor = { + const accessor: IWhitespaceChangeAccessor = { insertWhitespace: (afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string => { + hadAChange = true; afterLineNumber = afterLineNumber | 0; ordinal = ordinal | 0; heightInPx = heightInPx | 0; minWidth = minWidth | 0; - const id = this._instanceId + (++this._lastWhitespaceId); this._pendingChanges.insert(new EditorWhitespace(id, afterLineNumber, ordinal, heightInPx, minWidth)); return id; }, changeOneWhitespace: (id: string, newAfterLineNumber: number, newHeight: number): void => { + hadAChange = true; newAfterLineNumber = newAfterLineNumber | 0; newHeight = newHeight | 0; - this._pendingChanges.change({ id, newAfterLineNumber, newHeight }); }, removeWhitespace: (id: string): void => { + hadAChange = true; this._pendingChanges.remove({ id }); } }; - return callback(accessor); + callback(accessor); } finally { this._pendingChanges.commit(this); } + return hadAChange; } public _commitPendingChanges(inserts: EditorWhitespace[], changes: IPendingChange[], removes: IPendingRemove[]): void { diff --git a/src/vs/editor/common/viewLayout/viewLayout.ts b/src/vs/editor/common/viewLayout/viewLayout.ts index aa103a55b49..0964eac915f 100644 --- a/src/vs/editor/common/viewLayout/viewLayout.ts +++ b/src/vs/editor/common/viewLayout/viewLayout.ts @@ -7,10 +7,11 @@ import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility, INewScrollPosition } from 'vs/base/common/scrollable'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; -import { IConfiguration, IContentSizeChangedEvent } from 'vs/editor/common/editorCommon'; +import { IConfiguration, ScrollType } from 'vs/editor/common/editorCommon'; import { LinesLayout, IEditorWhitespace, IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout'; import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { IViewLayout, IViewWhitespaceViewportData, Viewport } from 'vs/editor/common/viewModel/viewModel'; +import { ContentSizeChangedEvent } from 'vs/editor/common/viewModel/viewModelEventDispatcher'; const SMOOTH_SCROLLING_TIME = 125; @@ -75,8 +76,8 @@ class EditorScrollable extends Disposable { public readonly onDidScroll: Event; - private readonly _onDidContentSizeChange = this._register(new Emitter()); - public readonly onDidContentSizeChange: Event = this._onDidContentSizeChange.event; + private readonly _onDidContentSizeChange = this._register(new Emitter()); + public readonly onDidContentSizeChange: Event = this._onDidContentSizeChange.event; constructor(smoothScrollDuration: number, scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable) { super(); @@ -119,13 +120,10 @@ class EditorScrollable extends Disposable { const contentWidthChanged = (oldDimensions.contentWidth !== dimensions.contentWidth); const contentHeightChanged = (oldDimensions.contentHeight !== dimensions.contentHeight); if (contentWidthChanged || contentHeightChanged) { - this._onDidContentSizeChange.fire({ - contentWidth: dimensions.contentWidth, - contentHeight: dimensions.contentHeight, - - contentWidthChanged: contentWidthChanged, - contentHeightChanged: contentHeightChanged - }); + this._onDidContentSizeChange.fire(new ContentSizeChangedEvent( + oldDimensions.contentWidth, oldDimensions.contentHeight, + dimensions.contentWidth, dimensions.contentHeight + )); } } @@ -153,7 +151,7 @@ export class ViewLayout extends Disposable implements IViewLayout { private readonly _scrollable: EditorScrollable; public readonly onDidScroll: Event; - public readonly onDidContentSizeChange: Event; + public readonly onDidContentSizeChange: Event; constructor(configuration: IConfiguration, lineCount: number, scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable) { super(); @@ -212,12 +210,12 @@ export class ViewLayout extends Disposable implements IViewLayout { const width = layoutInfo.contentWidth; const height = layoutInfo.height; const scrollDimensions = this._scrollable.getScrollDimensions(); - const scrollWidth = scrollDimensions.scrollWidth; + const contentWidth = scrollDimensions.contentWidth; this._scrollable.setScrollDimensions(new EditorScrollDimensions( width, scrollDimensions.contentWidth, height, - this._getContentHeight(width, height, scrollWidth) + this._getContentHeight(width, height, contentWidth) )); } else { this._updateHeight(); @@ -252,14 +250,14 @@ export class ViewLayout extends Disposable implements IViewLayout { return scrollbar.horizontalScrollbarSize; } - private _getContentHeight(width: number, height: number, scrollWidth: number): number { + private _getContentHeight(width: number, height: number, contentWidth: number): number { const options = this._configuration.options; let result = this._linesLayout.getLinesTotalHeight(); if (options.get(EditorOption.scrollBeyondLastLine)) { result += height - options.get(EditorOption.lineHeight); } else { - result += this._getHorizontalScrollbarHeight(width, scrollWidth); + result += this._getHorizontalScrollbarHeight(width, contentWidth); } return result; @@ -269,12 +267,12 @@ export class ViewLayout extends Disposable implements IViewLayout { const scrollDimensions = this._scrollable.getScrollDimensions(); const width = scrollDimensions.width; const height = scrollDimensions.height; - const scrollWidth = scrollDimensions.scrollWidth; + const contentWidth = scrollDimensions.contentWidth; this._scrollable.setScrollDimensions(new EditorScrollDimensions( width, scrollDimensions.contentWidth, height, - this._getContentHeight(width, height, scrollWidth) + this._getContentHeight(width, height, contentWidth) )); } @@ -324,7 +322,7 @@ export class ViewLayout extends Disposable implements IViewLayout { } } - public onMaxLineWidthChanged(maxLineWidth: number): void { + public setMaxLineWidth(maxLineWidth: number): void { const scrollDimensions = this._scrollable.getScrollDimensions(); // const newScrollWidth = ; this._scrollable.setScrollDimensions(new EditorScrollDimensions( @@ -353,8 +351,12 @@ export class ViewLayout extends Disposable implements IViewLayout { } // ---- IVerticalLayoutProvider - public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => T): T { - return this._linesLayout.changeWhitespace(callback); + public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => void): boolean { + const hadAChange = this._linesLayout.changeWhitespace(callback); + if (hadAChange) { + this.onHeightMaybeChanged(); + } + return hadAChange; } public getVerticalOffsetForLineNumber(lineNumber: number): number { return this._linesLayout.getVerticalOffsetForLineNumber(lineNumber); @@ -424,12 +426,12 @@ export class ViewLayout extends Disposable implements IViewLayout { return this._scrollable.validateScrollPosition(scrollPosition); } - public setScrollPositionNow(position: INewScrollPosition): void { - this._scrollable.setScrollPositionNow(position); - } - - public setScrollPositionSmooth(position: INewScrollPosition): void { - this._scrollable.setScrollPositionSmooth(position); + public setScrollPosition(position: INewScrollPosition, type: ScrollType): void { + if (type === ScrollType.Immediate) { + this._scrollable.setScrollPositionNow(position); + } else { + this._scrollable.setScrollPositionSmooth(position); + } } public deltaScrollNow(deltaScrollLeft: number, deltaScrollTop: number): void { diff --git a/src/vs/editor/common/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts index c698400c183..3ff97b83674 100644 --- a/src/vs/editor/common/viewLayout/viewLineRenderer.ts +++ b/src/vs/editor/common/viewLayout/viewLineRenderer.ts @@ -934,7 +934,9 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render 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); break; diff --git a/src/vs/editor/common/viewModel/viewEventHandler.ts b/src/vs/editor/common/viewModel/viewEventHandler.ts index b8d0bc823a6..aad7a0b4957 100644 --- a/src/vs/editor/common/viewModel/viewEventHandler.ts +++ b/src/vs/editor/common/viewModel/viewEventHandler.ts @@ -36,9 +36,7 @@ export class ViewEventHandler extends Disposable { public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { return false; } - public onContentSizeChanged(e: viewEvents.ViewContentSizeChangedEvent): boolean { - return false; - } + public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { return false; } @@ -102,12 +100,6 @@ export class ViewEventHandler extends Disposable { } break; - case viewEvents.ViewEventType.ViewContentSizeChanged: - if (this.onContentSizeChanged(e)) { - shouldRender = true; - } - break; - case viewEvents.ViewEventType.ViewCursorStateChanged: if (this.onCursorStateChanged(e)) { shouldRender = true; diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index 529f386a135..91ab8513c4c 100644 --- a/src/vs/editor/common/viewModel/viewModel.ts +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -3,18 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable } from 'vs/base/common/lifecycle'; import { IScrollPosition, Scrollable } from 'vs/base/common/scrollable'; import * as strings from 'vs/base/common/strings'; import { IViewLineTokens } from 'vs/editor/common/core/lineTokens'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; -import { INewScrollPosition } from 'vs/editor/common/editorCommon'; -import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecorationOptions, TextModelResolvedOptions } from 'vs/editor/common/model'; -import { IViewEventListener } from 'vs/editor/common/view/viewEvents'; +import { INewScrollPosition, ScrollType } from 'vs/editor/common/editorCommon'; +import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecorationOptions, TextModelResolvedOptions, ITextModel } from 'vs/editor/common/model'; +import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { IEditorWhitespace, IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout'; import { EditorTheme } from 'vs/editor/common/view/viewContext'; +import { ICursorSimpleModel, PartialCursorState, CursorState, IColumnSelectData, EditOperationType, CursorConfiguration } from 'vs/editor/common/controller/cursorCommon'; +import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; export interface IViewWhitespaceViewportData { readonly id: string; @@ -43,8 +45,6 @@ export interface IViewLayout { getScrollable(): Scrollable; - onMaxLineWidthChanged(width: number): void; - getScrollWidth(): number; getScrollHeight(): number; @@ -55,9 +55,6 @@ export interface IViewLayout { getFutureViewport(): Viewport; validateScrollPosition(scrollPosition: INewScrollPosition): IScrollPosition; - setScrollPositionNow(position: INewScrollPosition): void; - setScrollPositionSmooth(position: INewScrollPosition): void; - deltaScrollNow(deltaScrollLeft: number, deltaScrollTop: number): void; getLinesViewportData(): IPartialViewLinesViewportData; getLinesViewportDataAtScrollTop(scrollTop: number): IPartialViewLinesViewportData; @@ -68,18 +65,10 @@ export interface IViewLayout { getVerticalOffsetForLineNumber(lineNumber: number): number; getWhitespaceAtVerticalOffset(verticalOffset: number): IViewWhitespaceViewportData | null; - // --------------- Begin vertical whitespace management - changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => T): T; - /** * Get the layout information for whitespaces currently in the viewport */ getWhitespaceViewportData(): IViewWhitespaceViewportData[]; - - // TODO@Alex whitespace management should work via a change accessor sort of thing - onHeightMaybeChanged(): void; - - // --------------- End vertical whitespace management } export interface ICoordinatesConverter { @@ -95,20 +84,26 @@ export interface ICoordinatesConverter { modelPositionIsVisible(modelPosition: Position): boolean; } -export interface IViewModel { +export interface IViewModel extends ICursorSimpleModel { - addEventListener(listener: IViewEventListener): IDisposable; + readonly model: ITextModel; readonly coordinatesConverter: ICoordinatesConverter; readonly viewLayout: IViewLayout; + readonly cursorConfig: CursorConfiguration; + + addViewEventHandler(eventHandler: ViewEventHandler): void; + removeViewEventHandler(eventHandler: ViewEventHandler): void; + /** * Gives a hint that a lot of requests are about to come in for these line numbers. */ setViewport(startLineNumber: number, endLineNumber: number, centeredLineNumber: number): void; tokenizeViewport(): void; setHasFocus(hasFocus: boolean): void; + onDidColorThemeChange(): void; getDecorationsInViewport(visibleRange: Range): ViewModelDecoration[]; getViewLineRenderingData(visibleRange: Range, lineNumber: number): ViewLineRenderingData; @@ -117,7 +112,7 @@ export interface IViewModel { getCompletelyVisibleViewRange(): Range; getCompletelyVisibleViewRangeAtScrollTop(scrollTop: number): Range; - getOptions(): TextModelResolvedOptions; + getTextModelOptions(): TextModelResolvedOptions; getLineCount(): number; getLineContent(lineNumber: number): string; getLineLength(lineNumber: number): number; @@ -140,6 +135,38 @@ export interface IViewModel { getEOL(): string; getPlainTextToCopy(modelRanges: Range[], emptySelectionClipboard: boolean, forceCRLF: boolean): string | string[]; getRichTextToCopy(modelRanges: Range[], emptySelectionClipboard: boolean): { html: string, mode: string } | null; + + //#region model + + pushStackElement(): void; + + //#endregion + + + //#region cursor + getPrimaryCursorState(): CursorState; + getLastAddedCursorIndex(): number; + getCursorStates(): CursorState[]; + setCursorStates(source: string | null | undefined, reason: CursorChangeReason, states: PartialCursorState[] | null): void; + getCursorColumnSelectData(): IColumnSelectData; + setCursorColumnSelectData(columnSelectData: IColumnSelectData): void; + getPrevEditOperationType(): EditOperationType; + setPrevEditOperationType(type: EditOperationType): void; + revealPrimaryCursor(source: string | null | undefined, revealHorizontal: boolean): void; + revealTopMostCursor(source: string | null | undefined): void; + revealBottomMostCursor(source: string | null | undefined): void; + revealRange(source: string | null | undefined, revealHorizontal: boolean, viewRange: Range, verticalType: VerticalRevealType, scrollType: ScrollType): void; + //#endregion + + //#region viewLayout + getVerticalOffsetForLineNumber(viewLineNumber: number): number; + getScrollTop(): number; + setScrollTop(newScrollTop: number, scrollType: ScrollType): void; + setScrollPosition(position: INewScrollPosition, type: ScrollType): void; + deltaScrollNow(deltaScrollLeft: number, deltaScrollTop: number): void; + changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => void): void; + setMaxLineWidth(maxLineWidth: number): void; + //#endregion } export class MinimapLinesRenderingData { diff --git a/src/vs/editor/common/viewModel/viewModelEventDispatcher.ts b/src/vs/editor/common/viewModel/viewModelEventDispatcher.ts new file mode 100644 index 00000000000..0691b542cd1 --- /dev/null +++ b/src/vs/editor/common/viewModel/viewModelEventDispatcher.ts @@ -0,0 +1,393 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; +import { ViewEvent } from 'vs/editor/common/view/viewEvents'; +import { IContentSizeChangedEvent } from 'vs/editor/common/editorCommon'; +import { Emitter } from 'vs/base/common/event'; +import { Selection } from 'vs/editor/common/core/selection'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; + +export class ViewModelEventDispatcher extends Disposable { + + private readonly _onEvent = this._register(new Emitter()); + public readonly onEvent = this._onEvent.event; + + private readonly _eventHandlers: ViewEventHandler[]; + private _viewEventQueue: ViewEvent[] | null; + private _isConsumingViewEventQueue: boolean; + private _collector: ViewModelEventsCollector | null; + private _collectorCnt: number; + private _outgoingEvents: OutgoingViewModelEvent[]; + + constructor() { + super(); + this._eventHandlers = []; + this._viewEventQueue = null; + this._isConsumingViewEventQueue = false; + this._collector = null; + this._collectorCnt = 0; + this._outgoingEvents = []; + } + + public emitOutgoingEvent(e: OutgoingViewModelEvent): void { + this._addOutgoingEvent(e); + this._emitOugoingEvents(); + } + + private _addOutgoingEvent(e: OutgoingViewModelEvent): void { + for (let i = 0, len = this._outgoingEvents.length; i < len; i++) { + if (this._outgoingEvents[i].kind === e.kind) { + this._outgoingEvents[i] = this._outgoingEvents[i].merge(e); + return; + } + } + // not merged + this._outgoingEvents.push(e); + } + + private _emitOugoingEvents(): void { + while (this._outgoingEvents.length > 0) { + if (this._collector || this._isConsumingViewEventQueue) { + // right now collecting or emitting view events, so let's postpone emitting + return; + } + const event = this._outgoingEvents.shift()!; + if (event.isNoOp()) { + continue; + } + this._onEvent.fire(event); + } + } + + public addViewEventHandler(eventHandler: ViewEventHandler): void { + for (let i = 0, len = this._eventHandlers.length; i < len; i++) { + if (this._eventHandlers[i] === eventHandler) { + console.warn('Detected duplicate listener in ViewEventDispatcher', eventHandler); + } + } + this._eventHandlers.push(eventHandler); + } + + public removeViewEventHandler(eventHandler: ViewEventHandler): void { + for (let i = 0; i < this._eventHandlers.length; i++) { + if (this._eventHandlers[i] === eventHandler) { + this._eventHandlers.splice(i, 1); + break; + } + } + } + + public beginEmitViewEvents(): ViewModelEventsCollector { + this._collectorCnt++; + if (this._collectorCnt === 1) { + this._collector = new ViewModelEventsCollector(); + } + return this._collector!; + } + + public endEmitViewEvents(): void { + this._collectorCnt--; + if (this._collectorCnt === 0) { + const outgoingEvents = this._collector!.outgoingEvents; + const viewEvents = this._collector!.viewEvents; + this._collector = null; + + for (const outgoingEvent of outgoingEvents) { + this._addOutgoingEvent(outgoingEvent); + } + + if (viewEvents.length > 0) { + this._emitMany(viewEvents); + } + } + this._emitOugoingEvents(); + } + + public emitSingleViewEvent(event: ViewEvent): void { + try { + const eventsCollector = this.beginEmitViewEvents(); + eventsCollector.emitViewEvent(event); + } finally { + this.endEmitViewEvents(); + } + } + + private _emitMany(events: ViewEvent[]): void { + if (this._viewEventQueue) { + this._viewEventQueue = this._viewEventQueue.concat(events); + } else { + this._viewEventQueue = events; + } + + if (!this._isConsumingViewEventQueue) { + this._consumeViewEventQueue(); + } + } + + private _consumeViewEventQueue(): void { + try { + this._isConsumingViewEventQueue = true; + this._doConsumeQueue(); + } finally { + this._isConsumingViewEventQueue = false; + } + } + + private _doConsumeQueue(): void { + while (this._viewEventQueue) { + // Empty event queue, as events might come in while sending these off + const events = this._viewEventQueue; + this._viewEventQueue = null; + + // Use a clone of the event handlers list, as they might remove themselves + const eventHandlers = this._eventHandlers.slice(0); + for (const eventHandler of eventHandlers) { + eventHandler.handleEvents(events); + } + } + } +} + +export class ViewModelEventsCollector { + + public readonly viewEvents: ViewEvent[]; + public readonly outgoingEvents: OutgoingViewModelEvent[]; + + constructor() { + this.viewEvents = []; + this.outgoingEvents = []; + } + + public emitViewEvent(event: ViewEvent) { + this.viewEvents.push(event); + } + + public emitOutgoingEvent(e: OutgoingViewModelEvent): void { + this.outgoingEvents.push(e); + } +} + +export const enum OutgoingViewModelEventKind { + ContentSizeChanged, + FocusChanged, + ScrollChanged, + ViewZonesChanged, + ReadOnlyEditAttempt, + CursorStateChanged, +} + +export class ContentSizeChangedEvent implements IContentSizeChangedEvent { + + public readonly kind = OutgoingViewModelEventKind.ContentSizeChanged; + + private readonly _oldContentWidth: number; + private readonly _oldContentHeight: number; + + readonly contentWidth: number; + readonly contentHeight: number; + readonly contentWidthChanged: boolean; + readonly contentHeightChanged: boolean; + + constructor(oldContentWidth: number, oldContentHeight: number, contentWidth: number, contentHeight: number) { + this._oldContentWidth = oldContentWidth; + this._oldContentHeight = oldContentHeight; + this.contentWidth = contentWidth; + this.contentHeight = contentHeight; + this.contentWidthChanged = (this._oldContentWidth !== this.contentWidth); + this.contentHeightChanged = (this._oldContentHeight !== this.contentHeight); + } + + public isNoOp(): boolean { + return (!this.contentWidthChanged && !this.contentHeightChanged); + } + + + public merge(other: OutgoingViewModelEvent): ContentSizeChangedEvent { + if (other.kind !== OutgoingViewModelEventKind.ContentSizeChanged) { + return this; + } + return new ContentSizeChangedEvent(this._oldContentWidth, this._oldContentHeight, other.contentWidth, other.contentHeight); + } +} + +export class FocusChangedEvent { + + public readonly kind = OutgoingViewModelEventKind.FocusChanged; + + readonly oldHasFocus: boolean; + readonly hasFocus: boolean; + + constructor(oldHasFocus: boolean, hasFocus: boolean) { + this.oldHasFocus = oldHasFocus; + this.hasFocus = hasFocus; + } + + public isNoOp(): boolean { + return (this.oldHasFocus === this.hasFocus); + } + + public merge(other: OutgoingViewModelEvent): FocusChangedEvent { + if (other.kind !== OutgoingViewModelEventKind.FocusChanged) { + return this; + } + return new FocusChangedEvent(this.oldHasFocus, other.hasFocus); + } +} + +export class ScrollChangedEvent { + + public readonly kind = OutgoingViewModelEventKind.ScrollChanged; + + private readonly _oldScrollWidth: number; + private readonly _oldScrollLeft: number; + private readonly _oldScrollHeight: number; + private readonly _oldScrollTop: number; + + public readonly scrollWidth: number; + public readonly scrollLeft: number; + public readonly scrollHeight: number; + public readonly scrollTop: number; + + public readonly scrollWidthChanged: boolean; + public readonly scrollLeftChanged: boolean; + public readonly scrollHeightChanged: boolean; + public readonly scrollTopChanged: boolean; + + constructor( + oldScrollWidth: number, oldScrollLeft: number, oldScrollHeight: number, oldScrollTop: number, + scrollWidth: number, scrollLeft: number, scrollHeight: number, scrollTop: number, + ) { + this._oldScrollWidth = oldScrollWidth; + this._oldScrollLeft = oldScrollLeft; + this._oldScrollHeight = oldScrollHeight; + this._oldScrollTop = oldScrollTop; + + this.scrollWidth = scrollWidth; + this.scrollLeft = scrollLeft; + this.scrollHeight = scrollHeight; + this.scrollTop = scrollTop; + + this.scrollWidthChanged = (this._oldScrollWidth !== this.scrollWidth); + this.scrollLeftChanged = (this._oldScrollLeft !== this.scrollLeft); + this.scrollHeightChanged = (this._oldScrollHeight !== this.scrollHeight); + this.scrollTopChanged = (this._oldScrollTop !== this.scrollTop); + } + + public isNoOp(): boolean { + return (!this.scrollWidthChanged && !this.scrollLeftChanged && !this.scrollHeightChanged && !this.scrollTopChanged); + } + + public merge(other: OutgoingViewModelEvent): ScrollChangedEvent { + if (other.kind !== OutgoingViewModelEventKind.ScrollChanged) { + return this; + } + return new ScrollChangedEvent( + this._oldScrollWidth, this._oldScrollLeft, this._oldScrollHeight, this._oldScrollTop, + other.scrollWidth, other.scrollLeft, other.scrollHeight, other.scrollTop + ); + } +} + +export class ViewZonesChangedEvent { + + public readonly kind = OutgoingViewModelEventKind.ViewZonesChanged; + + constructor() { + } + + public isNoOp(): boolean { + return false; + } + + public merge(other: OutgoingViewModelEvent): ViewZonesChangedEvent { + return this; + } +} + +export class CursorStateChangedEvent { + + public readonly kind = OutgoingViewModelEventKind.CursorStateChanged; + + public readonly oldSelections: Selection[] | null; + public readonly selections: Selection[]; + public readonly oldModelVersionId: number; + public readonly modelVersionId: number; + public readonly source: string; + public readonly reason: CursorChangeReason; + public readonly reachedMaxCursorCount: boolean; + + constructor(oldSelections: Selection[] | null, selections: Selection[], oldModelVersionId: number, modelVersionId: number, source: string, reason: CursorChangeReason, reachedMaxCursorCount: boolean) { + this.oldSelections = oldSelections; + this.selections = selections; + this.oldModelVersionId = oldModelVersionId; + this.modelVersionId = modelVersionId; + this.source = source; + this.reason = reason; + this.reachedMaxCursorCount = reachedMaxCursorCount; + } + + private static _selectionsAreEqual(a: Selection[] | null, b: Selection[] | null): boolean { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + const aLen = a.length; + const bLen = b.length; + if (aLen !== bLen) { + return false; + } + for (let i = 0; i < aLen; i++) { + if (!a[i].equalsSelection(b[i])) { + return false; + } + } + return true; + } + + public isNoOp(): boolean { + return ( + CursorStateChangedEvent._selectionsAreEqual(this.oldSelections, this.selections) + && this.oldModelVersionId === this.modelVersionId + ); + } + + public merge(other: OutgoingViewModelEvent): CursorStateChangedEvent { + if (other.kind !== OutgoingViewModelEventKind.CursorStateChanged) { + return this; + } + return new CursorStateChangedEvent( + this.oldSelections, other.selections, this.oldModelVersionId, other.modelVersionId, other.source, other.reason, this.reachedMaxCursorCount || other.reachedMaxCursorCount + ); + } +} + +export class ReadOnlyEditAttemptEvent { + + public readonly kind = OutgoingViewModelEventKind.ReadOnlyEditAttempt; + + constructor() { + } + + public isNoOp(): boolean { + return false; + } + + public merge(other: OutgoingViewModelEvent): ReadOnlyEditAttemptEvent { + return this; + } +} + +export type OutgoingViewModelEvent = ( + ContentSizeChangedEvent + | FocusChangedEvent + | ScrollChangedEvent + | ViewZonesChangedEvent + | ReadOnlyEditAttemptEvent + | CursorStateChangedEvent +); diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index 8556f11f3fd..f7f130f0866 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -4,13 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { Color } from 'vs/base/common/color'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { Event } from 'vs/base/common/event'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import { ConfigurationChangedEvent, EDITOR_FONT_DEFAULTS, EditorOption, filterValidationDecorations } from 'vs/editor/common/config/editorOptions'; import { IPosition, Position } from 'vs/editor/common/core/position'; +import { ISelection, Selection } from 'vs/editor/common/core/selection'; import { IRange, Range } from 'vs/editor/common/core/range'; -import { IConfiguration, IViewState } from 'vs/editor/common/editorCommon'; -import { EndOfLinePreference, IActiveIndentGuideInfo, ITextModel, TrackedRangeStickiness, TextModelResolvedOptions } from 'vs/editor/common/model'; +import { IConfiguration, IViewState, ScrollType, ICursorState, ICommand, INewScrollPosition } from 'vs/editor/common/editorCommon'; +import { EndOfLinePreference, IActiveIndentGuideInfo, ITextModel, TrackedRangeStickiness, TextModelResolvedOptions, IIdentifiedSingleEditOperation, ICursorStateComputer } from 'vs/editor/common/model'; import { ModelDecorationOverviewRulerOptions, ModelDecorationMinimapOptions } from 'vs/editor/common/model/textModel'; import * as textModelEvents from 'vs/editor/common/model/textModelEvents'; import { ColorId, LanguageId, TokenizationRegistry } from 'vs/editor/common/modes'; @@ -24,24 +26,34 @@ import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecora import { RunOnceScheduler } from 'vs/base/common/async'; import * as platform from 'vs/base/common/platform'; import { EditorTheme } from 'vs/editor/common/view/viewContext'; +import { Cursor } from 'vs/editor/common/controller/cursor'; +import { PartialCursorState, CursorState, IColumnSelectData, EditOperationType, CursorConfiguration } from 'vs/editor/common/controller/cursorCommon'; +import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; +import { IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout'; +import { ViewModelEventDispatcher, OutgoingViewModelEvent, FocusChangedEvent, ScrollChangedEvent, ViewZonesChangedEvent, ViewModelEventsCollector, ReadOnlyEditAttemptEvent } from 'vs/editor/common/viewModel/viewModelEventDispatcher'; +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; const USE_IDENTITY_LINES_COLLECTION = true; -export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel { +export class ViewModel extends Disposable implements IViewModel { - private readonly editorId: number; - private readonly configuration: IConfiguration; - private readonly model: ITextModel; + private readonly _editorId: number; + private readonly _configuration: IConfiguration; + public readonly model: ITextModel; + private readonly _eventDispatcher: ViewModelEventDispatcher; + public readonly onEvent: Event; + public cursorConfig: CursorConfiguration; private readonly _tokenizeViewportSoon: RunOnceScheduler; private readonly _updateConfigurationViewLineCount: RunOnceScheduler; - private hasFocus: boolean; - private viewportStartLine: number; - private viewportStartLineTrackedRange: string | null; - private viewportStartLineDelta: number; - private readonly lines: IViewModelLinesCollection; + private _hasFocus: boolean; + private _viewportStartLine: number; + private _viewportStartLineTrackedRange: string | null; + private _viewportStartLineDelta: number; + private readonly _lines: IViewModelLinesCollection; public readonly coordinatesConverter: ICoordinatesConverter; public readonly viewLayout: ViewLayout; - private readonly decorations: ViewModelDecorations; + private readonly _cursor: Cursor; + private readonly _decorations: ViewModelDecorations; constructor( editorId: number, @@ -53,28 +65,31 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel ) { super(); - this.editorId = editorId; - this.configuration = configuration; + this._editorId = editorId; + this._configuration = configuration; this.model = model; + this._eventDispatcher = new ViewModelEventDispatcher(); + this.onEvent = this._eventDispatcher.onEvent; + this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this._configuration); this._tokenizeViewportSoon = this._register(new RunOnceScheduler(() => this.tokenizeViewport(), 50)); this._updateConfigurationViewLineCount = this._register(new RunOnceScheduler(() => this._updateConfigurationViewLineCountNow(), 0)); - this.hasFocus = false; - this.viewportStartLine = -1; - this.viewportStartLineTrackedRange = null; - this.viewportStartLineDelta = 0; + this._hasFocus = false; + this._viewportStartLine = -1; + this._viewportStartLineTrackedRange = null; + this._viewportStartLineDelta = 0; if (USE_IDENTITY_LINES_COLLECTION && this.model.isTooLargeForTokenization()) { - this.lines = new IdentityLinesCollection(this.model); + this._lines = new IdentityLinesCollection(this.model); } else { - const options = this.configuration.options; + const options = this._configuration.options; const fontInfo = options.get(EditorOption.fontInfo); const wrappingStrategy = options.get(EditorOption.wrappingStrategy); const wrappingInfo = options.get(EditorOption.wrappingInfo); const wrappingIndent = options.get(EditorOption.wrappingIndent); - this.lines = new SplitLinesCollection( + this._lines = new SplitLinesCollection( this.model, domLineBreaksComputerFactory, monospaceLineBreaksComputerFactory, @@ -86,51 +101,42 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel ); } - this.coordinatesConverter = this.lines.createCoordinatesConverter(); + this.coordinatesConverter = this._lines.createCoordinatesConverter(); - this.viewLayout = this._register(new ViewLayout(this.configuration, this.getLineCount(), scheduleAtNextAnimationFrame)); + this._cursor = this._register(new Cursor(model, this, this.coordinatesConverter, this.cursorConfig)); + + this.viewLayout = this._register(new ViewLayout(this._configuration, this.getLineCount(), scheduleAtNextAnimationFrame)); this._register(this.viewLayout.onDidScroll((e) => { if (e.scrollTopChanged) { this._tokenizeViewportSoon.schedule(); } - try { - const eventsCollector = this._beginEmit(); - eventsCollector.emit(new viewEvents.ViewScrollChangedEvent(e)); - } finally { - this._endEmit(); - } + this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewScrollChangedEvent(e)); + this._eventDispatcher.emitOutgoingEvent(new ScrollChangedEvent( + e.oldScrollWidth, e.oldScrollLeft, e.oldScrollHeight, e.oldScrollTop, + e.scrollWidth, e.scrollLeft, e.scrollHeight, e.scrollTop + )); })); this._register(this.viewLayout.onDidContentSizeChange((e) => { - try { - const eventsCollector = this._beginEmit(); - eventsCollector.emit(new viewEvents.ViewContentSizeChangedEvent(e)); - } finally { - this._endEmit(); - } + this._eventDispatcher.emitOutgoingEvent(e); })); - this.decorations = new ViewModelDecorations(this.editorId, this.model, this.configuration, this.lines, this.coordinatesConverter); + this._decorations = new ViewModelDecorations(this._editorId, this.model, this._configuration, this._lines, this.coordinatesConverter); this._registerModelEvents(); - this._register(this.configuration.onDidChange((e) => { + this._register(this._configuration.onDidChangeFast((e) => { try { - const eventsCollector = this._beginEmit(); + const eventsCollector = this._eventDispatcher.beginEmitViewEvents(); this._onConfigurationChanged(eventsCollector, e); } finally { - this._endEmit(); + this._eventDispatcher.endEmitViewEvents(); } })); this._register(MinimapTokensColorTracker.getInstance().onDidChange(() => { - try { - const eventsCollector = this._beginEmit(); - eventsCollector.emit(new viewEvents.ViewTokensColorsChangedEvent()); - } finally { - this._endEmit(); - } + this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewTokensColorsChangedEvent()); })); this._updateConfigurationViewLineCountNow(); @@ -140,14 +146,23 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel // 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()); + this._configuration.setViewLineCount(this._lines.getViewLineCount()); } public tokenizeViewport(): void { @@ -158,30 +173,38 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel } public setHasFocus(hasFocus: boolean): void { - this.hasFocus = hasFocus; + this._hasFocus = hasFocus; + this._cursor.setHasFocus(hasFocus); + this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewFocusChangedEvent(hasFocus)); + this._eventDispatcher.emitOutgoingEvent(new FocusChangedEvent(!hasFocus, hasFocus)); } - private _onConfigurationChanged(eventsCollector: viewEvents.ViewEventsCollector, e: ConfigurationChangedEvent): void { + public onDidColorThemeChange(): void { + this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewThemeChangedEvent()); + } + + private _onConfigurationChanged(eventsCollector: ViewModelEventsCollector, e: ConfigurationChangedEvent): void { // We might need to restore the current centered view range, so save it (if available) let previousViewportStartModelPosition: Position | null = null; - if (this.viewportStartLine !== -1) { - let previousViewportStartViewPosition = new Position(this.viewportStartLine, this.getLineMinColumn(this.viewportStartLine)); + if (this._viewportStartLine !== -1) { + let previousViewportStartViewPosition = new Position(this._viewportStartLine, this.getLineMinColumn(this._viewportStartLine)); previousViewportStartModelPosition = this.coordinatesConverter.convertViewPositionToModelPosition(previousViewportStartViewPosition); } let restorePreviousViewportStart = false; - const options = this.configuration.options; + const options = this._configuration.options; const fontInfo = options.get(EditorOption.fontInfo); const wrappingStrategy = options.get(EditorOption.wrappingStrategy); const wrappingInfo = options.get(EditorOption.wrappingInfo); const wrappingIndent = options.get(EditorOption.wrappingIndent); - if (this.lines.setWrappingSettings(fontInfo, wrappingStrategy, wrappingInfo.wrappingColumn, wrappingIndent)) { - eventsCollector.emit(new viewEvents.ViewFlushedEvent()); - eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent()); - eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null)); - 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) { @@ -194,17 +217,22 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel if (e.hasChanged(EditorOption.readOnly)) { // Must read again all decorations due to readOnly filtering - this.decorations.reset(); - eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null)); + 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); } } @@ -212,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; @@ -221,7 +249,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel const versionId = e.versionId; // Do a first pass to compute line mappings, and a second pass to actually interpret them - const lineBreaksComputer = this.lines.createLineBreaksComputer(); + const lineBreaksComputer = this._lines.createLineBreaksComputer(); for (const change of changes) { switch (change.changeType) { case textModelEvents.RawContentChangedType.LinesInserted: { @@ -243,17 +271,17 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel switch (change.changeType) { case textModelEvents.RawContentChangedType.Flush: { - this.lines.onModelFlushed(); - eventsCollector.emit(new viewEvents.ViewFlushedEvent()); - this.decorations.reset(); + this._lines.onModelFlushed(); + eventsCollector.emitViewEvent(new viewEvents.ViewFlushedEvent()); + this._decorations.reset(); this.viewLayout.onFlushed(this.getLineCount()); hadOtherModelChange = true; break; } case textModelEvents.RawContentChangedType.LinesDeleted: { - const linesDeletedEvent = this.lines.onModelLinesDeleted(versionId, change.fromLineNumber, change.toLineNumber); + const linesDeletedEvent = this._lines.onModelLinesDeleted(versionId, change.fromLineNumber, change.toLineNumber); if (linesDeletedEvent !== null) { - eventsCollector.emit(linesDeletedEvent); + eventsCollector.emitViewEvent(linesDeletedEvent); this.viewLayout.onLinesDeleted(linesDeletedEvent.fromLineNumber, linesDeletedEvent.toLineNumber); } hadOtherModelChange = true; @@ -263,9 +291,9 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel const insertedLineBreaks = lineBreaks.slice(lineBreaksOffset, lineBreaksOffset + change.detail.length); lineBreaksOffset += change.detail.length; - const linesInsertedEvent = this.lines.onModelLinesInserted(versionId, change.fromLineNumber, change.toLineNumber, insertedLineBreaks); + const linesInsertedEvent = this._lines.onModelLinesInserted(versionId, change.fromLineNumber, change.toLineNumber, insertedLineBreaks); if (linesInsertedEvent !== null) { - eventsCollector.emit(linesInsertedEvent); + eventsCollector.emitViewEvent(linesInsertedEvent); this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber); } hadOtherModelChange = true; @@ -275,17 +303,17 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel const changedLineBreakData = lineBreaks[lineBreaksOffset]; lineBreaksOffset++; - const [lineMappingChanged, linesChangedEvent, linesInsertedEvent, linesDeletedEvent] = this.lines.onModelLineChanged(versionId, change.lineNumber, changedLineBreakData); + const [lineMappingChanged, linesChangedEvent, linesInsertedEvent, linesDeletedEvent] = this._lines.onModelLineChanged(versionId, change.lineNumber, changedLineBreakData); hadModelLineChangeThatChangedLineMapping = lineMappingChanged; if (linesChangedEvent) { - eventsCollector.emit(linesChangedEvent); + eventsCollector.emitViewEvent(linesChangedEvent); } if (linesInsertedEvent) { - eventsCollector.emit(linesInsertedEvent); + eventsCollector.emitViewEvent(linesInsertedEvent); this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber); } if (linesDeletedEvent) { - eventsCollector.emit(linesDeletedEvent); + eventsCollector.emitViewEvent(linesDeletedEvent); this.viewLayout.onLinesDeleted(linesDeletedEvent.fromLineNumber, linesDeletedEvent.toLineNumber); } break; @@ -296,32 +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(null)); - 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) => { @@ -335,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(); @@ -348,63 +379,65 @@ 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(null)); + 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(e)); - } 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(null)); - 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 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); @@ -423,7 +456,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel 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]; @@ -528,48 +561,48 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel return this.model.getOptions().tabSize; } - public getOptions(): TextModelResolvedOptions { + public getTextModelOptions(): TextModelResolvedOptions { return this.model.getOptions(); } public getLineCount(): number { - return this.lines.getViewLineCount(); + return this._lines.getViewLineCount(); } /** * Gives a hint that a lot of requests are about to come in for these line numbers. */ public setViewport(startLineNumber: number, endLineNumber: number, centeredLineNumber: number): void { - this.viewportStartLine = startLineNumber; + this._viewportStartLine = startLineNumber; let position = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(startLineNumber, this.getLineMinColumn(startLineNumber))); - this.viewportStartLineTrackedRange = this.model._setTrackedRange(this.viewportStartLineTrackedRange, new Range(position.lineNumber, position.column, position.lineNumber, position.column), TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges); + this._viewportStartLineTrackedRange = this.model._setTrackedRange(this._viewportStartLineTrackedRange, new Range(position.lineNumber, position.column, position.lineNumber, position.column), TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges); const viewportStartLineTop = this.viewLayout.getVerticalOffsetForLineNumber(startLineNumber); const scrollTop = this.viewLayout.getCurrentScrollTop(); - this.viewportStartLineDelta = scrollTop - viewportStartLineTop; + this._viewportStartLineDelta = scrollTop - viewportStartLineTop; } public getActiveIndentGuide(lineNumber: number, minLineNumber: number, maxLineNumber: number): IActiveIndentGuideInfo { - return this.lines.getActiveIndentGuide(lineNumber, minLineNumber, maxLineNumber); + return this._lines.getActiveIndentGuide(lineNumber, minLineNumber, maxLineNumber); } public getLinesIndentGuides(startLineNumber: number, endLineNumber: number): number[] { - return this.lines.getViewLinesIndentGuides(startLineNumber, endLineNumber); + return this._lines.getViewLinesIndentGuides(startLineNumber, endLineNumber); } public getLineContent(lineNumber: number): string { - return this.lines.getViewLineContent(lineNumber); + return this._lines.getViewLineContent(lineNumber); } public getLineLength(lineNumber: number): number { - return this.lines.getViewLineLength(lineNumber); + return this._lines.getViewLineLength(lineNumber); } public getLineMinColumn(lineNumber: number): number { - return this.lines.getViewLineMinColumn(lineNumber); + return this._lines.getViewLineMinColumn(lineNumber); } public getLineMaxColumn(lineNumber: number): number { - return this.lines.getViewLineMaxColumn(lineNumber); + return this._lines.getViewLineMaxColumn(lineNumber); } public getLineFirstNonWhitespaceColumn(lineNumber: number): number { @@ -589,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( @@ -615,11 +648,11 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel } public getViewLineData(lineNumber: number): ViewLineData { - return this.lines.getViewLineData(lineNumber); + return this._lines.getViewLineData(lineNumber); } public getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): MinimapLinesRenderingData { - let result = this.lines.getViewLinesData(startLineNumber, endLineNumber, needed); + let result = this._lines.getViewLinesData(startLineNumber, endLineNumber, needed); return new MinimapLinesRenderingData( this.getTabSize(), result @@ -627,7 +660,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel } public getAllOverviewRulerDecorations(theme: EditorTheme): IOverviewRulerDecorations { - return this.lines.getAllOverviewRulerDecorations(this.editorId, filterValidationDecorations(this.configuration.options), theme); + return this._lines.getAllOverviewRulerDecorations(this._editorId, filterValidationDecorations(this._configuration.options), theme); } public invalidateOverviewRulerColorCache(): void { @@ -769,7 +802,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel range = new Range(lineNumber, this.model.getLineMinColumn(lineNumber), lineNumber, this.model.getLineMaxColumn(lineNumber)); } - const fontInfo = this.configuration.options.get(EditorOption.fontInfo); + const fontInfo = this._configuration.options.get(EditorOption.fontInfo); const colorMap = this._getColorMap(); const fontFamily = fontInfo.fontFamily === EDITOR_FONT_DEFAULTS.fontFamily ? fontInfo.fontFamily : `'${fontInfo.fontFamily}', ${EDITOR_FONT_DEFAULTS.fontFamily}`; @@ -827,4 +860,150 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel } return result; } + + //#region model + + public pushStackElement(): void { + this.model.pushStackElement(); + } + + //#endregion + + //#region cursor operations + + public getPrimaryCursorState(): CursorState { + return this._cursor.getPrimaryCursorState(); + } + public getLastAddedCursorIndex(): number { + return this._cursor.getLastAddedCursorIndex(); + } + public getCursorStates(): CursorState[] { + return this._cursor.getCursorStates(); + } + public setCursorStates(source: string | null | undefined, reason: CursorChangeReason, states: PartialCursorState[] | null): void { + this._withViewEventsCollector(eventsCollector => this._cursor.setStates(eventsCollector, source, reason, states)); + } + public getCursorColumnSelectData(): IColumnSelectData { + return this._cursor.getCursorColumnSelectData(); + } + public setCursorColumnSelectData(columnSelectData: IColumnSelectData): void { + this._cursor.setCursorColumnSelectData(columnSelectData); + } + public getPrevEditOperationType(): EditOperationType { + return this._cursor.getPrevEditOperationType(); + } + public setPrevEditOperationType(type: EditOperationType): void { + this._cursor.setPrevEditOperationType(type); + } + public getSelection(): Selection { + return this._cursor.getSelection(); + } + public getSelections(): Selection[] { + return this._cursor.getSelections(); + } + public getPosition(): Position { + return this._cursor.getPrimaryCursorState().modelState.position; + } + public setSelections(source: string | null | undefined, selections: readonly ISelection[]): void { + this._withViewEventsCollector(eventsCollector => this._cursor.setSelections(eventsCollector, source, selections)); + } + public saveCursorState(): ICursorState[] { + return this._cursor.saveState(); + } + public restoreCursorState(states: ICursorState[]): void { + this._withViewEventsCollector(eventsCollector => this._cursor.restoreState(eventsCollector, states)); + } + + private _executeCursorEdit(callback: (eventsCollector: ViewModelEventsCollector) => void): void { + if (this._cursor.context.cursorConfig.readOnly) { + // we cannot edit when read only... + this._eventDispatcher.emitOutgoingEvent(new ReadOnlyEditAttemptEvent()); + return; + } + this._withViewEventsCollector(callback); + } + public executeEdits(source: string | null | undefined, edits: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): void { + this._executeCursorEdit(eventsCollector => this._cursor.executeEdits(eventsCollector, source, edits, cursorStateComputer)); + } + public startComposition(): void { + this._cursor.setIsDoingComposition(true); + this._executeCursorEdit(eventsCollector => this._cursor.startComposition(eventsCollector)); + } + public endComposition(source?: string | null | undefined): void { + this._cursor.setIsDoingComposition(false); + this._executeCursorEdit(eventsCollector => this._cursor.endComposition(eventsCollector, source)); + } + public type(text: string, source?: string | null | undefined): void { + this._executeCursorEdit(eventsCollector => this._cursor.type(eventsCollector, text, source)); + } + public replacePreviousChar(text: string, replaceCharCnt: number, source?: string | null | undefined): void { + this._executeCursorEdit(eventsCollector => this._cursor.replacePreviousChar(eventsCollector, text, replaceCharCnt, source)); + } + public paste(text: string, pasteOnNewLine: boolean, multicursorText?: string[] | null | undefined, source?: string | null | undefined): void { + this._executeCursorEdit(eventsCollector => this._cursor.paste(eventsCollector, text, pasteOnNewLine, multicursorText, source)); + } + public cut(source?: string | null | undefined): void { + this._executeCursorEdit(eventsCollector => this._cursor.cut(eventsCollector, source)); + } + public executeCommand(command: ICommand, source?: string | null | undefined): void { + this._executeCursorEdit(eventsCollector => this._cursor.executeCommand(eventsCollector, command, source)); + } + public executeCommands(commands: ICommand[], source?: string | null | undefined): void { + this._executeCursorEdit(eventsCollector => this._cursor.executeCommands(eventsCollector, commands, source)); + } + public revealPrimaryCursor(source: string | null | undefined, revealHorizontal: boolean): void { + this._withViewEventsCollector(eventsCollector => this._cursor.revealPrimary(eventsCollector, source, revealHorizontal, ScrollType.Smooth)); + } + public revealTopMostCursor(source: string | null | undefined): void { + const viewPosition = this._cursor.getTopMostViewPosition(); + const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column); + this._withViewEventsCollector(eventsCollector => eventsCollector.emitViewEvent(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, null, viewEvents.VerticalRevealType.Simple, true, ScrollType.Smooth))); + } + public revealBottomMostCursor(source: string | null | undefined): void { + const viewPosition = this._cursor.getBottomMostViewPosition(); + const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column); + this._withViewEventsCollector(eventsCollector => eventsCollector.emitViewEvent(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, null, viewEvents.VerticalRevealType.Simple, true, ScrollType.Smooth))); + } + public revealRange(source: string | null | undefined, revealHorizontal: boolean, viewRange: Range, verticalType: viewEvents.VerticalRevealType, scrollType: ScrollType): void { + this._withViewEventsCollector(eventsCollector => eventsCollector.emitViewEvent(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, null, verticalType, revealHorizontal, scrollType))); + } + + //#endregion + + //#region viewLayout + public getVerticalOffsetForLineNumber(viewLineNumber: number): number { + return this.viewLayout.getVerticalOffsetForLineNumber(viewLineNumber); + } + public getScrollTop(): number { + return this.viewLayout.getCurrentScrollTop(); + } + public setScrollTop(newScrollTop: number, scrollType: ScrollType): void { + this.viewLayout.setScrollPosition({ scrollTop: newScrollTop }, scrollType); + } + public setScrollPosition(position: INewScrollPosition, type: ScrollType): void { + this.viewLayout.setScrollPosition(position, type); + } + public deltaScrollNow(deltaScrollLeft: number, deltaScrollTop: number): void { + this.viewLayout.deltaScrollNow(deltaScrollLeft, deltaScrollTop); + } + public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => void): void { + const hadAChange = this.viewLayout.changeWhitespace(callback); + if (hadAChange) { + this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewZonesChangedEvent()); + this._eventDispatcher.emitOutgoingEvent(new ViewZonesChangedEvent()); + } + } + public setMaxLineWidth(maxLineWidth: number): void { + this.viewLayout.setMaxLineWidth(maxLineWidth); + } + //#endregion + + private _withViewEventsCollector(callback: (eventsCollector: ViewModelEventsCollector) => void): void { + try { + const eventsCollector = this._eventDispatcher.beginEmitViewEvents(); + callback(eventsCollector); + } finally { + this._eventDispatcher.endEmitViewEvents(); + } + } } diff --git a/src/vs/platform/files/node/files.ts b/src/vs/editor/contrib/anchorSelect/anchorSelect.css similarity index 79% rename from src/vs/platform/files/node/files.ts rename to src/vs/editor/contrib/anchorSelect/anchorSelect.css index bbb85376228..46ea76d2011 100644 --- a/src/vs/platform/files/node/files.ts +++ b/src/vs/editor/contrib/anchorSelect/anchorSelect.css @@ -3,5 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -export const MIN_MAX_MEMORY_SIZE_MB = 2048; -export const FALLBACK_MAX_MEMORY_SIZE_MB = 4096; +.monaco-editor .selection-anchor { + background-color: #007ACC; + width: 2px !important; +} diff --git a/src/vs/editor/contrib/anchorSelect/anchorSelect.ts b/src/vs/editor/contrib/anchorSelect/anchorSelect.ts new file mode 100644 index 00000000000..80011cf5909 --- /dev/null +++ b/src/vs/editor/contrib/anchorSelect/anchorSelect.ts @@ -0,0 +1,178 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./anchorSelect'; +import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { localize } from 'vs/nls'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { Selection } from 'vs/editor/common/core/selection'; +import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { TrackedRangeStickiness } from 'vs/editor/common/model'; +import { MarkdownString } from 'vs/base/common/htmlContent'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { alert } from 'vs/base/browser/ui/aria/aria'; + +export const SelectionAnchorSet = new RawContextKey('selectionAnchorSet', false); + +class SelectionAnchorController implements IEditorContribution { + + public static readonly ID = 'editor.contrib.selectionAnchorController'; + + static get(editor: ICodeEditor): SelectionAnchorController { + return editor.getContribution(SelectionAnchorController.ID); + } + + private decorationId: string | undefined; + private selectionAnchorSetContextKey: IContextKey; + private modelChangeListener: IDisposable; + + constructor( + private editor: ICodeEditor, + @IContextKeyService contextKeyService: IContextKeyService + ) { + this.selectionAnchorSetContextKey = SelectionAnchorSet.bindTo(contextKeyService); + this.modelChangeListener = editor.onDidChangeModel(() => this.selectionAnchorSetContextKey.reset()); + } + + setSelectionAnchor(): void { + if (this.editor.hasModel()) { + const position = this.editor.getPosition(); + const previousDecorations = this.decorationId ? [this.decorationId] : []; + const newDecorationId = this.editor.deltaDecorations(previousDecorations, [{ + range: Selection.fromPositions(position, position), + options: { + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + hoverMessage: new MarkdownString().appendText(localize('selectionAnchor', "Selection Anchor")), + className: 'selection-anchor' + } + }]); + this.decorationId = newDecorationId[0]; + this.selectionAnchorSetContextKey.set(!!this.decorationId); + alert(localize('anchorSet', "Anchor set at {0}:{1}", position.lineNumber, position.column)); + } + } + + goToSelectionAnchor(): void { + if (this.editor.hasModel() && this.decorationId) { + const anchorPosition = this.editor.getModel().getDecorationRange(this.decorationId); + if (anchorPosition) { + this.editor.setPosition(anchorPosition.getStartPosition()); + } + } + } + + selectFromAnchorToCursor(): void { + if (this.editor.hasModel() && this.decorationId) { + const start = this.editor.getModel().getDecorationRange(this.decorationId); + if (start) { + const end = this.editor.getPosition(); + this.editor.setSelection(Selection.fromPositions(start.getStartPosition(), end)); + this.cancelSelectionAnchor(); + } + } + } + + cancelSelectionAnchor(): void { + if (this.decorationId) { + this.editor.deltaDecorations([this.decorationId], []); + this.decorationId = undefined; + this.selectionAnchorSetContextKey.set(false); + } + } + + dispose(): void { + this.cancelSelectionAnchor(); + this.modelChangeListener.dispose(); + } +} + +class SetSelectionAnchor extends EditorAction { + constructor() { + super({ + id: 'editor.action.setSelectionAnchor', + label: localize('setSelectionAnchor', "Set Selection Anchor"), + alias: 'Set Selection Anchor', + precondition: undefined, + kbOpts: { + kbExpr: EditorContextKeys.editorTextFocus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_B), + weight: KeybindingWeight.EditorContrib + } + }); + } + + async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { + const controller = SelectionAnchorController.get(editor); + controller.setSelectionAnchor(); + } +} + +class GoToSelectionAnchor extends EditorAction { + constructor() { + super({ + id: 'editor.action.goToSelectionAnchor', + label: localize('goToSelectionAnchor', "Go to Selection Anchor"), + alias: 'Go to Selection Anchor', + precondition: SelectionAnchorSet, + }); + } + + async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { + const controller = SelectionAnchorController.get(editor); + controller.goToSelectionAnchor(); + } +} + +class SelectFromAnchorToCursor extends EditorAction { + constructor() { + super({ + id: 'editor.action.selectFromAnchorToCursor', + label: localize('selectFromAnchorToCursor', "Select from Anchor to Cursor"), + alias: 'Select from Anchor to Cursor', + precondition: SelectionAnchorSet, + kbOpts: { + kbExpr: EditorContextKeys.editorTextFocus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K), + weight: KeybindingWeight.EditorContrib + } + }); + } + + async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { + const controller = SelectionAnchorController.get(editor); + controller.selectFromAnchorToCursor(); + } +} + +class CancelSelectionAnchor extends EditorAction { + constructor() { + super({ + id: 'editor.action.cancelSelectionAnchor', + label: localize('cancelSelectionAnchor', "Cancel Selection Anchor"), + alias: 'Cancel Selection Anchor', + precondition: SelectionAnchorSet, + kbOpts: { + kbExpr: EditorContextKeys.editorTextFocus, + primary: KeyCode.Escape, + weight: KeybindingWeight.EditorContrib + } + }); + } + + async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { + const controller = SelectionAnchorController.get(editor); + controller.cancelSelectionAnchor(); + } +} + +registerEditorContribution(SelectionAnchorController.ID, SelectionAnchorController); +registerEditorAction(SetSelectionAnchor); +registerEditorAction(GoToSelectionAnchor); +registerEditorAction(SelectFromAnchorToCursor); +registerEditorAction(CancelSelectionAnchor); diff --git a/src/vs/editor/contrib/bracketMatching/test/bracketMatching.test.ts b/src/vs/editor/contrib/bracketMatching/test/bracketMatching.test.ts index ae17d2f9de1..0a11a1a5f99 100644 --- a/src/vs/editor/contrib/bracketMatching/test/bracketMatching.test.ts +++ b/src/vs/editor/contrib/bracketMatching/test/bracketMatching.test.ts @@ -33,7 +33,7 @@ suite('bracket matching', () => { let mode = new BracketMode(); let model = createTextModel('var x = (3 + (5-7)) + ((5+3)+5);', undefined, mode.getLanguageIdentifier()); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor(null, { model: model }, (editor) => { let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); // start on closing bracket @@ -65,7 +65,7 @@ suite('bracket matching', () => { let mode = new BracketMode(); let model = createTextModel('var x = (3 + (5-7)); y();', undefined, mode.getLanguageIdentifier()); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor(null, { model: model }, (editor) => { let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); // start position between brackets @@ -102,7 +102,7 @@ suite('bracket matching', () => { let mode = new BracketMode(); let model = createTextModel('var x = (3 + (5-7)); y();', undefined, mode.getLanguageIdentifier()); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor(null, { model: model }, (editor) => { let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); @@ -154,7 +154,7 @@ suite('bracket matching', () => { const mode = new BracketMode(); const model = createTextModel(text, undefined, mode.getLanguageIdentifier()); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor(null, { model: model }, (editor) => { const bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); editor.setPosition(new Position(3, 5)); @@ -179,7 +179,7 @@ suite('bracket matching', () => { const mode = new BracketMode(); const model = createTextModel(text, undefined, mode.getLanguageIdentifier()); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor(null, { model: model }, (editor) => { const bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); editor.setPosition(new Position(3, 5)); @@ -197,7 +197,7 @@ suite('bracket matching', () => { let mode = new BracketMode(); let model = createTextModel('{ } { } { }', undefined, mode.getLanguageIdentifier()); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor(null, { model: model }, (editor) => { let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); // cursors inside brackets become selections of the entire bracket contents diff --git a/src/vs/editor/contrib/clipboard/clipboard.ts b/src/vs/editor/contrib/clipboard/clipboard.ts index 4ad6007754a..114004565c5 100644 --- a/src/vs/editor/contrib/clipboard/clipboard.ts +++ b/src/vs/editor/contrib/clipboard/clipboard.ts @@ -9,7 +9,7 @@ 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 { 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'; @@ -28,171 +28,108 @@ const supportsCopyWithSyntaxHighlighting = (supportsCopy && !browser.isEdge); // 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); - } +function registerCommand(command: T): T { + command.register(); + return command; } -class ExecCommandCutAction extends ExecCommandAction { - - constructor() { - let kbOpts: ICommandKeybindingsOptions | undefined = { - kbExpr: EditorContextKeys.textInputFocus, +export const CutAction = supportsCut ? registerCommand(new MultiCommand({ + id: 'editor.action.clipboardCutAction', + precondition: undefined, + kbOpts: ( + // Do not bind cut keybindings in the browser, + // since browsers do that for us and it avoids security prompts + platform.isNative ? { primary: KeyMod.CtrlCmd | KeyCode.KEY_X, win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_X, secondary: [KeyMod.Shift | KeyCode.Delete] }, weight: KeybindingWeight.EditorContrib - }; - // Do not bind cut keybindings in the browser, + } : undefined + ), + menuOpts: [{ + menuId: MenuId.MenubarEditMenu, + group: '2_ccp', + title: nls.localize({ key: 'miCut', comment: ['&& denotes a mnemonic'] }, "Cu&&t"), + order: 1 + }, { + menuId: MenuId.EditorContext, + group: CLIPBOARD_CONTEXT_MENU_GROUP, + title: nls.localize('actions.clipboard.cutLabel', "Cut"), + when: EditorContextKeys.writable, + order: 1, + }, { + menuId: MenuId.CommandPalette, + group: '', + title: nls.localize('actions.clipboard.cutLabel', "Cut"), + order: 1 + }] +})) : undefined; + +export const CopyAction = supportsCopy ? registerCommand(new MultiCommand({ + id: 'editor.action.clipboardCopyAction', + precondition: undefined, + kbOpts: ( + // Do not bind copy keybindings in the browser, // since browsers do that for us and it avoids security prompts - if (!platform.isNative) { - kbOpts = undefined; - } - super('cut', { - id: 'editor.action.clipboardCutAction', - label: nls.localize('actions.clipboard.cutLabel', "Cut"), - alias: 'Cut', - precondition: EditorContextKeys.writable, - kbOpts: kbOpts, - contextMenuOpts: { - group: CLIPBOARD_CONTEXT_MENU_GROUP, - order: 1 - }, - menuOpts: { - menuId: MenuId.MenubarEditMenu, - group: '2_ccp', - title: nls.localize({ key: 'miCut', comment: ['&& denotes a mnemonic'] }, "Cu&&t"), - order: 1 - } - }); - } - - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { - if (!editor.hasModel()) { - return; - } - - const emptySelectionClipboard = editor.getOption(EditorOption.emptySelectionClipboard); - - if (!emptySelectionClipboard && editor.getSelection().isEmpty()) { - return; - } - - super.run(accessor, editor); - } -} - -class ExecCommandCopyAction extends ExecCommandAction { - - constructor() { - let kbOpts: ICommandKeybindingsOptions | undefined = { - kbExpr: EditorContextKeys.textInputFocus, + platform.isNative ? { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, secondary: [KeyMod.CtrlCmd | KeyCode.Insert] }, weight: KeybindingWeight.EditorContrib - }; - // Do not bind copy keybindings in the browser, + } : 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, - contextMenuOpts: { - group: CLIPBOARD_CONTEXT_MENU_GROUP, - order: 2 - }, - menuOpts: { - menuId: MenuId.MenubarEditMenu, - group: '2_ccp', - title: nls.localize({ key: 'miCopy', comment: ['&& denotes a mnemonic'] }, "&&Copy"), - order: 2 - } - }); - } - - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { - if (!editor.hasModel()) { - return; - } - - const emptySelectionClipboard = editor.getOption(EditorOption.emptySelectionClipboard); - - if (!emptySelectionClipboard && editor.getSelection().isEmpty()) { - return; - } - - super.run(accessor, editor); - } -} - -class ExecCommandPasteAction extends ExecCommandAction { - - constructor() { - let kbOpts: ICommandKeybindingsOptions | undefined = { - kbExpr: EditorContextKeys.textInputFocus, + 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; - } + } : undefined + ), + menuOpts: [{ + menuId: MenuId.MenubarEditMenu, + group: '2_ccp', + title: nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"), + order: 3 + }, { + menuId: MenuId.EditorContext, + group: CLIPBOARD_CONTEXT_MENU_GROUP, + title: nls.localize('actions.clipboard.pasteLabel', "Paste"), + when: EditorContextKeys.writable, + order: 3, + }, { + menuId: MenuId.CommandPalette, + group: '', + title: nls.localize('actions.clipboard.pasteLabel', "Paste"), + order: 1 + }] +})) : undefined; - super('paste', { - id: 'editor.action.clipboardPasteAction', - label: nls.localize('actions.clipboard.pasteLabel', "Paste"), - alias: 'Paste', - precondition: EditorContextKeys.writable, - kbOpts: kbOpts, - contextMenuOpts: { - group: CLIPBOARD_CONTEXT_MENU_GROUP, - order: 3 - }, - menuOpts: { - menuId: MenuId.MenubarEditMenu, - group: '2_ccp', - title: nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"), - order: 3 - } - }); - } -} - -class ExecCommandCopyWithSyntaxHighlightingAction extends ExecCommandAction { +class ExecCommandCopyWithSyntaxHighlightingAction extends EditorAction { constructor() { - super('copy', { + super({ id: 'editor.action.clipboardCopyWithSyntaxHighlightingAction', label: nls.localize('actions.clipboard.copyWithSyntaxHighlightingLabel', "Copy With Syntax Highlighting"), alias: 'Copy With Syntax Highlighting', @@ -217,20 +154,48 @@ 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); -} -if (supportsPaste) { - registerEditorAction(ExecCommandPasteAction); +function registerExecCommandImpl(target: MultiCommand | undefined, browserCommand: 'cut' | 'copy' | 'paste'): 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()) { + if (browserCommand === 'cut' || browserCommand === 'copy') { + // 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) => { + // Only if editor text focus (i.e. not if editor has widget focus). + document.execCommand(browserCommand); + return true; + }); } + +registerExecCommandImpl(CutAction, 'cut'); +registerExecCommandImpl(CopyAction, 'copy'); +registerExecCommandImpl(PasteAction, 'paste'); + if (supportsCopyWithSyntaxHighlighting) { registerEditorAction(ExecCommandCopyWithSyntaxHighlightingAction); } diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index b9e30b80525..f54e722566e 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -73,6 +73,9 @@ class ManagedCodeActionSet extends Disposable implements CodeActionSet { } } + +const emptyCodeActionsResponse = { actions: [] as modes.CodeAction[], documentation: undefined }; + export function getCodeActions( model: ITextModel, rangeOrSelection: Range | Selection, @@ -100,7 +103,7 @@ export function getCodeActions( } if (cts.token.isCancellationRequested) { - return { actions: [] as modes.CodeAction[], documentation: undefined }; + return emptyCodeActionsResponse; } const filteredActions = (providedCodeActions?.actions || []).filter(action => action && filtersAction(filter, action)); @@ -111,7 +114,7 @@ export function getCodeActions( throw err; } onUnexpectedExternalError(err); - return { actions: [] as modes.CodeAction[], documentation: undefined }; + return emptyCodeActionsResponse; } }); diff --git a/src/vs/editor/contrib/codeAction/codeActionMenu.ts b/src/vs/editor/contrib/codeAction/codeActionMenu.ts index b736d9c2884..0c9ef9ec71a 100644 --- a/src/vs/editor/contrib/codeAction/codeActionMenu.ts +++ b/src/vs/editor/contrib/codeAction/codeActionMenu.ts @@ -4,9 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { getDomNodePagePosition } from 'vs/base/browser/dom'; -import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; -import { Action, IAction } from 'vs/base/common/actions'; +import { Action, IAction, Separator } from 'vs/base/common/actions'; import { canceled } from 'vs/base/common/errors'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { Lazy } from 'vs/base/common/lazy'; @@ -90,6 +89,7 @@ export class CodeActionMenu extends Disposable { const resolver = this._keybindingResolver.getResolver(); this._contextMenuService.showContextMenu({ + domForShadowRoot: this._editor.getDomNode()!, getAnchor: () => anchor, getActions: () => menuActions, onHide: () => { diff --git a/src/vs/editor/contrib/codeAction/codeActionModel.ts b/src/vs/editor/contrib/codeAction/codeActionModel.ts index fc35f1398ed..4afe90c451e 100644 --- a/src/vs/editor/contrib/codeAction/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/codeActionModel.ts @@ -214,8 +214,8 @@ export class CodeActionModel extends Disposable { } const actions = createCancelablePromise(token => getCodeActions(model, trigger.selection, trigger.trigger, Progress.None, token)); - if (this._progressService && trigger.trigger.type === CodeActionTriggerType.Manual) { - this._progressService.showWhile(actions, 250); + if (trigger.trigger.type === CodeActionTriggerType.Manual) { + this._progressService?.showWhile(actions, 250); } this.setState(new CodeActionsState.Triggered(trigger.trigger, trigger.selection, trigger.position, actions)); diff --git a/src/vs/editor/contrib/codelens/codeLensCache.ts b/src/vs/editor/contrib/codelens/codeLensCache.ts index 82f8b6d3a1f..5f8c147657d 100644 --- a/src/vs/editor/contrib/codelens/codeLensCache.ts +++ b/src/vs/editor/contrib/codelens/codeLensCache.ts @@ -17,7 +17,7 @@ import { once } from 'vs/base/common/functional'; export const ICodeLensCache = createDecorator('ICodeLensCache'); export interface ICodeLensCache { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; put(model: ITextModel, data: CodeLensModel): void; get(model: ITextModel): CodeLensModel | undefined; delete(model: ITextModel): void; @@ -38,7 +38,7 @@ class CacheItem { export class CodeLensCache implements ICodeLensCache { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _fakeProvider = new class implements CodeLensProvider { provideCodeLenses(): CodeLensList { @@ -96,7 +96,7 @@ 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); @@ -105,7 +105,7 @@ export class CodeLensCache implements ICodeLensCache { lineCount: value.lineCount, lines: [...lines.values()] }; - }); + } return JSON.stringify(data); } diff --git a/src/vs/editor/contrib/colorPicker/colorDetector.ts b/src/vs/editor/contrib/colorPicker/colorDetector.ts index 90f0db8ac67..4b678d0edda 100644 --- a/src/vs/editor/contrib/colorPicker/colorDetector.ts +++ b/src/vs/editor/contrib/colorPicker/colorDetector.ts @@ -173,7 +173,7 @@ export class ColorDetector extends Disposable implements IEditorContribution { for (let i = 0; i < colorData.length && decorations.length < MAX_DECORATORS; i++) { const { red, green, blue, alpha } = colorData[i].colorInfo.color; const rgba = new RGBA(Math.round(red * 255), Math.round(green * 255), Math.round(blue * 255), alpha); - let subKey = hash(rgba).toString(16); + let subKey = hash(`rgba(${rgba.r},${rgba.g},${rgba.b},${rgba.a})`).toString(16); let color = `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})`; let key = 'colorBox-' + subKey; diff --git a/src/vs/editor/contrib/colorPicker/colorPickerModel.ts b/src/vs/editor/contrib/colorPicker/colorPickerModel.ts index 9b974876732..772f252613f 100644 --- a/src/vs/editor/contrib/colorPicker/colorPickerModel.ts +++ b/src/vs/editor/contrib/colorPicker/colorPickerModel.ts @@ -64,7 +64,7 @@ export class ColorPickerModel { guessColorPresentation(color: Color, originalText: string): void { for (let i = 0; i < this.colorPresentations.length; i++) { - if (originalText === this.colorPresentations[i].label) { + if (originalText.toLowerCase() === this.colorPresentations[i].label) { this.presentationIndex = i; this._onDidChangePresentation.fire(this.presentation); break; diff --git a/src/vs/editor/contrib/comment/comment.ts b/src/vs/editor/contrib/comment/comment.ts index 243a45655bf..1dbe337e1ce 100644 --- a/src/vs/editor/contrib/comment/comment.ts +++ b/src/vs/editor/contrib/comment/comment.ts @@ -36,7 +36,13 @@ abstract class CommentLineAction extends EditorAction { const commentsOptions = editor.getOption(EditorOption.comments); for (const selection of selections) { - commands.push(new LineCommentCommand(selection, modelOptions.tabSize, this._type, commentsOptions.insertSpace)); + commands.push(new LineCommentCommand( + selection, + modelOptions.tabSize, + this._type, + commentsOptions.insertSpace, + commentsOptions.ignoreEmptyLines + )); } editor.pushUndoStop(); diff --git a/src/vs/editor/contrib/comment/lineCommentCommand.ts b/src/vs/editor/contrib/comment/lineCommentCommand.ts index bf088f0d67c..8952727a9d9 100644 --- a/src/vs/editor/contrib/comment/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/lineCommentCommand.ts @@ -53,11 +53,18 @@ export class LineCommentCommand implements ICommand { private readonly _tabSize: number; private readonly _type: Type; private readonly _insertSpace: boolean; + private readonly _ignoreEmptyLines: boolean; private _selectionId: string | null; private _deltaColumn: number; private _moveEndPositionDown: boolean; - constructor(selection: Selection, tabSize: number, type: Type, insertSpace: boolean) { + constructor( + selection: Selection, + tabSize: number, + type: Type, + insertSpace: boolean, + ignoreEmptyLines: boolean + ) { this._selection = selection; this._tabSize = tabSize; this._type = type; @@ -65,6 +72,7 @@ export class LineCommentCommand implements ICommand { this._selectionId = null; this._deltaColumn = 0; this._moveEndPositionDown = false; + this._ignoreEmptyLines = ignoreEmptyLines; } /** @@ -100,7 +108,7 @@ export class LineCommentCommand implements ICommand { * Analyze lines and decide which lines are relevant and what the toggle should do. * Also, build up several offsets and lengths useful in the generation of editor operations. */ - public static _analyzeLines(type: Type, insertSpace: boolean, model: ISimpleModel, lines: ILinePreflightData[], startLineNumber: number): IPreflightData { + public static _analyzeLines(type: Type, insertSpace: boolean, model: ISimpleModel, lines: ILinePreflightData[], startLineNumber: number, ignoreEmptyLines: boolean): IPreflightData { let onlyWhitespaceLines = true; let shouldRemoveComments: boolean; @@ -121,13 +129,7 @@ export class LineCommentCommand implements ICommand { if (lineContentStartOffset === -1) { // Empty or whitespace only line - if (type === Type.Toggle) { - lineData.ignore = true; - } else if (type === Type.ForceAdd) { - lineData.ignore = true; - } else { - lineData.ignore = true; - } + lineData.ignore = ignoreEmptyLines; lineData.commentStrOffset = lineContent.length; continue; } @@ -176,7 +178,7 @@ export class LineCommentCommand implements ICommand { /** * Analyze all lines and decide exactly what to do => not supported | insert line comments | remove line comments */ - public static _gatherPreflightData(type: Type, insertSpace: boolean, model: ITextModel, startLineNumber: number, endLineNumber: number): IPreflightData { + public static _gatherPreflightData(type: Type, insertSpace: boolean, model: ITextModel, startLineNumber: number, endLineNumber: number, ignoreEmptyLines: boolean): IPreflightData { const lines = LineCommentCommand._gatherPreflightCommentStrings(model, startLineNumber, endLineNumber); if (lines === null) { return { @@ -184,7 +186,7 @@ export class LineCommentCommand implements ICommand { }; } - return LineCommentCommand._analyzeLines(type, insertSpace, model, lines, startLineNumber); + return LineCommentCommand._analyzeLines(type, insertSpace, model, lines, startLineNumber, ignoreEmptyLines); } /** @@ -326,7 +328,15 @@ export class LineCommentCommand implements ICommand { s = s.setEndPosition(s.endLineNumber - 1, model.getLineMaxColumn(s.endLineNumber - 1)); } - const data = LineCommentCommand._gatherPreflightData(this._type, this._insertSpace, model, s.startLineNumber, s.endLineNumber); + const data = LineCommentCommand._gatherPreflightData( + this._type, + this._insertSpace, + model, + s.startLineNumber, + s.endLineNumber, + this._ignoreEmptyLines + ); + if (data.supported) { return this._executeLineComments(model, builder, data, s); } diff --git a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts index 66593786b96..0689b4209d5 100644 --- a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts +++ b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts @@ -19,13 +19,13 @@ suite('Editor Contrib - Line Comment Command', () => { function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: '!@#', blockComment: [''] }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true, true), expectedLines, expectedSelection); mode.dispose(); } function testAddLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: '!@#', blockComment: [''] }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.ForceAdd, true), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.ForceAdd, true, true), expectedLines, expectedSelection); mode.dispose(); } @@ -47,7 +47,7 @@ suite('Editor Contrib - Line Comment Command', () => { test('case insensitive', function () { function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: 'rem' }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true, true), expectedLines, expectedSelection); mode.dispose(); } @@ -91,7 +91,7 @@ suite('Editor Contrib - Line Comment Command', () => { ' ', ' c', '\t\td' - ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1); + ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1, true); if (!r.supported) { throw new Error(`unexpected`); } @@ -122,7 +122,7 @@ suite('Editor Contrib - Line Comment Command', () => { ' rem ', ' !@# c', '\t\t!@#d' - ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1); + ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1, true); if (!r.supported) { throw new Error(`unexpected`); } @@ -631,7 +631,7 @@ suite('Editor Contrib - Line Comment Command', () => { test('insertSpace false', () => { function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: '!@#' }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, false), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, false, true), expectedLines, expectedSelection); mode.dispose(); } @@ -650,7 +650,7 @@ suite('Editor Contrib - Line Comment Command', () => { test('insertSpace false does not remove space', () => { function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: '!@#' }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, false), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, false, true), expectedLines, expectedSelection); mode.dispose(); } @@ -665,13 +665,104 @@ suite('Editor Contrib - Line Comment Command', () => { new Selection(1, 1, 1, 1) ); }); + + suite('ignoreEmptyLines false', () => { + function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + let mode = new CommentMode({ lineComment: '!@#', blockComment: [''] }); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true, false), expectedLines, expectedSelection); + mode.dispose(); + } + + test('does not ignore whitespace lines', () => { + testLineCommentCommand( + [ + '\tsome text', + '\t ', + '', + '\tsome more text' + ], + new Selection(4, 2, 1, 1), + [ + '!@# \tsome text', + '!@# \t ', + '!@# ', + '!@# \tsome more text' + ], + new Selection(4, 6, 1, 5) + ); + }); + + test('removes its own', function () { + testLineCommentCommand( + [ + '\t!@# some text', + '\t ', + '\t\t!@# some more text' + ], + new Selection(3, 2, 1, 1), + [ + '\tsome text', + '\t ', + '\t\tsome more text' + ], + new Selection(3, 2, 1, 1) + ); + }); + + test('works in only whitespace', function () { + testLineCommentCommand( + [ + '\t ', + '\t', + '\t\tsome more text' + ], + new Selection(3, 1, 1, 1), + [ + '\t!@# ', + '\t!@# ', + '\t\tsome more text' + ], + new Selection(3, 1, 1, 1) + ); + }); + + test('comments single line', function () { + testLineCommentCommand( + [ + 'some text', + '\tsome more text' + ], + new Selection(1, 1, 1, 1), + [ + '!@# some text', + '\tsome more text' + ], + new Selection(1, 5, 1, 5) + ); + }); + + test('detects indentation', function () { + testLineCommentCommand( + [ + '\tsome text', + '\tsome more text' + ], + new Selection(2, 2, 1, 1), + [ + '\t!@# some text', + '\t!@# some more text' + ], + new Selection(2, 2, 1, 1) + ); + }); + }); }); suite('Editor Contrib - Line Comment As Block Comment', () => { function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: '', blockComment: ['(', ')'] }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true, true), expectedLines, expectedSelection); mode.dispose(); } @@ -782,7 +873,7 @@ suite('Editor Contrib - Line Comment As Block Comment', () => { suite('Editor Contrib - Line Comment As Block Comment 2', () => { function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: null, blockComment: [''] }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true, true), expectedLines, expectedSelection); mode.dispose(); } @@ -1023,7 +1114,7 @@ suite('Editor Contrib - Line Comment in mixed modes', () => { lines, outerMode.getLanguageIdentifier(), selection, - (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), + (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true, true), expectedLines, expectedSelection, true diff --git a/src/vs/editor/contrib/contextmenu/contextmenu.ts b/src/vs/editor/contrib/contextmenu/contextmenu.ts index 6114c170cce..8a2a53b8dde 100644 --- a/src/vs/editor/contrib/contextmenu/contextmenu.ts +++ b/src/vs/editor/contrib/contextmenu/contextmenu.ts @@ -6,9 +6,8 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { ActionViewItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; -import { IAction } from 'vs/base/common/actions'; +import { IAction, Separator, SubmenuAction } from 'vs/base/common/actions'; import { KeyCode, KeyMod, ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; @@ -23,7 +22,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { ITextModel } from 'vs/editor/common/model'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { ContextSubMenu } from 'vs/base/browser/contextmenu'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; export class ContextMenuController implements IEditorContribution { @@ -50,7 +49,14 @@ export class ContextMenuController implements IEditorContribution { this._toDispose.add(this._editor.onContextMenu((e: IEditorMouseEvent) => this._onContextMenu(e))); this._toDispose.add(this._editor.onMouseWheel((e: IMouseWheelEvent) => { if (this._contextMenuIsBeingShownCount > 0) { - this._contextViewService.hideContextView(); + const view = this._contextViewService.getContextViewElement(); + const target = e.srcElement as HTMLElement; + + // Event triggers on shadow root host first + // Check if the context view is under this host before hiding it #103169 + if (!(target.shadowRoot && dom.getShadowRoot(view) === target.shadowRoot)) { + this._contextViewService.hideContextView(); + } } })); this._toDispose.add(this._editor.onKeyDown((e: IKeyboardEvent) => { @@ -153,7 +159,7 @@ export class ContextMenuController implements IEditorContribution { if (action instanceof SubmenuItemAction) { const subActions = this._getMenuActions(model, action.item.submenu); if (subActions.length > 0) { - result.push(new ContextSubMenu(action.label, subActions)); + result.push(new SubmenuAction(action.id, action.label, subActions)); addedItems++; } } else { @@ -174,7 +180,7 @@ export class ContextMenuController implements IEditorContribution { return result; } - private _doShowContextMenu(actions: ReadonlyArray, anchor: IAnchor | null = null): void { + private _doShowContextMenu(actions: IAction[], anchor: IAnchor | null = null): void { if (!this._editor.hasModel()) { return; } @@ -205,6 +211,8 @@ export class ContextMenuController implements IEditorContribution { // Show menu this._contextMenuIsBeingShownCount++; this._contextMenuService.showContextMenu({ + domForShadowRoot: this._editor.getDomNode(), + getAnchor: () => anchor!, getActions: () => actions, diff --git a/src/vs/editor/contrib/dnd/dnd.ts b/src/vs/editor/contrib/dnd/dnd.ts index 599e804eb05..b4bdd4336fd 100644 --- a/src/vs/editor/contrib/dnd/dnd.ts +++ b/src/vs/editor/contrib/dnd/dnd.ts @@ -54,6 +54,7 @@ export class DragAndDropController extends Disposable implements IEditorContribu this._register(this._editor.onKeyDown((e: IKeyboardEvent) => this.onEditorKeyDown(e))); this._register(this._editor.onKeyUp((e: IKeyboardEvent) => this.onEditorKeyUp(e))); this._register(this._editor.onDidBlurEditorWidget(() => this.onEditorBlur())); + this._register(this._editor.onDidBlurEditorText(() => this.onEditorBlur())); this._dndDecorationIds = []; this._mouseDown = false; this._modifierPressed = false; diff --git a/src/vs/editor/contrib/find/findController.ts b/src/vs/editor/contrib/find/findController.ts index b22036d6b2f..2730f6b8040 100644 --- a/src/vs/editor/contrib/find/findController.ts +++ b/src/vs/editor/contrib/find/findController.ts @@ -9,7 +9,7 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { EditorAction, EditorCommand, ServicesAccessor, registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { EditorAction, EditorCommand, ServicesAccessor, registerEditorAction, registerEditorCommand, registerEditorContribution, MultiEditorAction, registerMultiEditorAction } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_FIND_WIDGET_VISIBLE, FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleSearchScopeKeybinding, ToggleWholeWordKeybinding, CONTEXT_REPLACE_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel'; @@ -255,7 +255,7 @@ export class CommonFindController extends Disposable implements IEditorContribut // overwritten in subclass } - protected _start(opts: IFindStartOptions): void { + protected async _start(opts: IFindStartOptions): Promise { this.disposeModel(); if (!this._editor.hasModel()) { @@ -279,7 +279,13 @@ export class CommonFindController extends Disposable implements IEditorContribut } 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; } @@ -308,8 +314,8 @@ export class CommonFindController extends Disposable implements IEditorContribut } } - public start(opts: IFindStartOptions): void { - this._start(opts); + public start(opts: IFindStartOptions): Promise { + return this._start(opts); } public moveToNextMatch(): boolean { @@ -353,7 +359,7 @@ export class CommonFindController extends Disposable implements IEditorContribut return false; } - public getGlobalBufferTerm(): string { + public async getGlobalBufferTerm(): Promise { if (this._editor.getOption(EditorOption.find).globalFindClipboard && this._clipboardService && this._editor.hasModel() @@ -364,12 +370,13 @@ export class CommonFindController extends Disposable implements IEditorContribut return ''; } - public setGlobalBufferTerm(text: string) { + public setGlobalBufferTerm(text: string): void { if (this._editor.getOption(EditorOption.find).globalFindClipboard && this._clipboardService && this._editor.hasModel() && !this._editor.getModel().isTooLargeForSyncing() ) { + // intentionally not awaited this._clipboardService.writeFindText(text); } } @@ -396,7 +403,7 @@ export class FindController extends CommonFindController implements IFindControl this._findOptionsWidget = null; } - protected _start(opts: IFindStartOptions): void { + protected async _start(opts: IFindStartOptions): Promise { if (!this._widget) { this._createFindWidget(); } @@ -422,12 +429,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(); + } } } @@ -448,7 +457,7 @@ export class FindController extends CommonFindController implements IFindControl } } -export class StartFindAction extends EditorAction { +export class StartFindAction extends MultiEditorAction { constructor() { super({ @@ -470,10 +479,10 @@ 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, @@ -505,10 +514,10 @@ 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, @@ -523,10 +532,10 @@ 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, @@ -629,7 +638,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; @@ -639,7 +648,7 @@ 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, @@ -697,7 +706,7 @@ export class PreviousSelectionMatchFindAction extends SelectionMatchFindAction { } } -export class StartFindReplaceAction extends EditorAction { +export class StartFindReplaceAction extends MultiEditorAction { constructor() { super({ @@ -720,7 +729,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; } @@ -745,7 +754,7 @@ export class StartFindReplaceAction extends EditorAction { if (controller) { - controller.start({ + await controller.start({ forceRevealReplace: true, seedSearchStringFromSelection: seedSearchStringFromSelection, seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).seedSearchStringFromSelection, @@ -760,7 +769,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); @@ -768,7 +778,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/findModel.ts b/src/vs/editor/contrib/find/findModel.ts index c083478dd00..eca1f89f2e7 100644 --- a/src/vs/editor/contrib/find/findModel.ts +++ b/src/vs/editor/contrib/find/findModel.ts @@ -23,6 +23,7 @@ import { ReplacePattern, parseReplaceString } from 'vs/editor/contrib/find/repla 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 = CONTEXT_FIND_WIDGET_VISIBLE.toNegated(); @@ -189,8 +190,17 @@ export class FindModelBoundToEditorModel { let findMatches = this._findMatches(findScope, false, MATCHES_LIMIT); this._decorations.set(findMatches, findScope); + 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 ); @@ -252,6 +262,13 @@ 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) { @@ -340,6 +357,13 @@ 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) { diff --git a/src/vs/editor/contrib/find/findOptionsWidget.ts b/src/vs/editor/contrib/find/findOptionsWidget.ts index e56b512e893..d541e0d0608 100644 --- a/src/vs/editor/contrib/find/findOptionsWidget.ts +++ b/src/vs/editor/contrib/find/findOptionsWidget.ts @@ -11,7 +11,7 @@ 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 { 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 { @@ -47,12 +47,14 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget { this._domNode.setAttribute('aria-hidden', 'true'); 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); @@ -185,6 +189,7 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget { 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/findWidget.css b/src/vs/editor/contrib/find/findWidget.css index 3805801379b..303f435450a 100644 --- a/src/vs/editor/contrib/find/findWidget.css +++ b/src/vs/editor/contrib/find/findWidget.css @@ -6,7 +6,7 @@ /* Find widget */ .monaco-editor .find-widget { position: absolute; - z-index: 10; + z-index: 20; height: 33px; overflow: hidden; line-height: 19px; @@ -203,8 +203,7 @@ } .monaco-editor .find-widget .monaco-sash { - width: 2px !important; - margin-left: -4px; + left: 0 !important; } .monaco-editor.hc-black .find-widget .button:before { diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index 7207d80ea55..82af435546f 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,7 +30,7 @@ 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 { 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'; @@ -52,7 +52,7 @@ export const findNextMatchIcon = registerIcon('find-next-match', Codicon.arrowDo export interface IFindController { replace(): void; replaceAll(): void; - getGlobalBufferTerm(): string; + getGlobalBufferTerm(): Promise; } const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find"); @@ -113,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; @@ -224,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(); @@ -662,6 +662,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas 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), @@ -903,16 +904,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 { @@ -1173,7 +1167,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; @@ -1365,11 +1359,11 @@ registerThemingParticipant((theme, collector) => { const resizeBorderBackground = theme.getColor(editorWidgetResizeBorder); if (resizeBorderBackground) { - collector.addRule(`.monaco-editor .find-widget .monaco-sash { background-color: ${resizeBorderBackground}; width: 3px !important; margin-left: -4px;}`); + collector.addRule(`.monaco-editor .find-widget .monaco-sash { background-color: ${resizeBorderBackground}; }`); } else { const border = theme.getColor(editorWidgetBorder); if (border) { - collector.addRule(`.monaco-editor .find-widget .monaco-sash { background-color: ${border}; width: 3px !important; margin-left: -4px;}`); + collector.addRule(`.monaco-editor .find-widget .monaco-sash { background-color: ${border}; }`); } } diff --git a/src/vs/editor/contrib/find/replacePattern.ts b/src/vs/editor/contrib/find/replacePattern.ts index 12a05d93536..e829fe6c108 100644 --- a/src/vs/editor/contrib/find/replacePattern.ts +++ b/src/vs/editor/contrib/find/replacePattern.ts @@ -20,7 +20,7 @@ class StaticValueReplacePattern { } /** - * Assigned when the replace pattern has replacemend patterns. + * Assigned when the replace pattern has replacement patterns. */ class DynamicPiecesReplacePattern { public readonly kind = ReplacePatternKind.DynamicPieces; @@ -68,7 +68,38 @@ export class ReplacePattern { } // match index ReplacePiece - result += ReplacePattern._substitute(piece.matchIndex, matches); + let match: string = ReplacePattern._substitute(piece.matchIndex, matches); + if (piece.caseOps !== null && piece.caseOps.length > 0) { + let repl: string[] = []; + let lenOps: number = piece.caseOps.length; + let opIdx: number = 0; + for (let idx: number = 0, len: number = match.length; idx < len; idx++) { + if (opIdx >= lenOps) { + repl.push(match.slice(idx)); + break; + } + switch (piece.caseOps[opIdx]) { + case 'U': + repl.push(match[idx].toUpperCase()); + break; + case 'u': + repl.push(match[idx].toUpperCase()); + opIdx++; + break; + case 'L': + repl.push(match[idx].toLowerCase()); + break; + case 'l': + repl.push(match[idx].toLowerCase()); + opIdx++; + break; + default: + repl.push(match[idx]); + } + } + match = repl.join(''); + } + result += match; } return result; @@ -102,19 +133,29 @@ export class ReplacePattern { export class ReplacePiece { public static staticValue(value: string): ReplacePiece { - return new ReplacePiece(value, -1); + return new ReplacePiece(value, -1, null); } public static matchIndex(index: number): ReplacePiece { - return new ReplacePiece(null, index); + return new ReplacePiece(null, index, null); + } + + public static caseOps(index: number, caseOps: string[]): ReplacePiece { + return new ReplacePiece(null, index, caseOps); } public readonly staticValue: string | null; public readonly matchIndex: number; + public readonly caseOps: string[] | null; - private constructor(staticValue: string | null, matchIndex: number) { + private constructor(staticValue: string | null, matchIndex: number, caseOps: string[] | null) { this.staticValue = staticValue; this.matchIndex = matchIndex; + if (!caseOps || caseOps.length === 0) { + this.caseOps = null; + } else { + this.caseOps = caseOps.slice(0); + } } } @@ -151,12 +192,12 @@ class ReplacePieceBuilder { this._currentStaticPiece += value; } - public emitMatchIndex(index: number, toCharIndex: number): void { + public emitMatchIndex(index: number, toCharIndex: number, caseOps: string[]): void { if (this._currentStaticPiece.length !== 0) { this._result[this._resultLen++] = ReplacePiece.staticValue(this._currentStaticPiece); this._currentStaticPiece = ''; } - this._result[this._resultLen++] = ReplacePiece.matchIndex(index); + this._result[this._resultLen++] = ReplacePiece.caseOps(index, caseOps); this._lastCharIndex = toCharIndex; } @@ -175,6 +216,10 @@ class ReplacePieceBuilder { * \n => inserts a LF * \t => inserts a TAB * \\ => inserts a "\". + * \u => upper-cases one character in a match. + * \U => upper-cases ALL remaining characters in a match. + * \l => lower-cases one character in a match. + * \L => lower-cases ALL remaining characters in a match. * $$ => inserts a "$". * $& and $0 => inserts the matched substring. * $n => Where n is a non-negative integer lesser than 100, inserts the nth parenthesized submatch string @@ -187,6 +232,7 @@ export function parseReplaceString(replaceString: string): ReplacePattern { return new ReplacePattern(null); } + let caseOps: string[] = []; let result = new ReplacePieceBuilder(replaceString); for (let i = 0, len = replaceString.length; i < len; i++) { @@ -221,6 +267,20 @@ export function parseReplaceString(replaceString: string): ReplacePattern { result.emitUnchanged(i - 1); result.emitStatic('\t', i + 1); break; + // Case modification of string replacements, patterned after Boost, but only applied + // to the replacement text, not subsequent content. + case CharCode.u: + // \u => upper-cases one character. + case CharCode.U: + // \U => upper-cases ALL following characters. + case CharCode.l: + // \l => lower-cases one character. + case CharCode.L: + // \L => lower-cases ALL following characters. + result.emitUnchanged(i - 1); + result.emitStatic('', i + 1); + caseOps.push(String.fromCharCode(nextChCode)); + break; } continue; @@ -248,7 +308,8 @@ export function parseReplaceString(replaceString: string): ReplacePattern { if (nextChCode === CharCode.Digit0 || nextChCode === CharCode.Ampersand) { // $& and $0 => inserts the matched substring. result.emitUnchanged(i - 1); - result.emitMatchIndex(0, i + 1); + result.emitMatchIndex(0, i + 1, caseOps); + caseOps.length = 0; continue; } @@ -268,13 +329,15 @@ export function parseReplaceString(replaceString: string): ReplacePattern { matchIndex = matchIndex * 10 + (nextNextChCode - CharCode.Digit0); result.emitUnchanged(i - 2); - result.emitMatchIndex(matchIndex, i + 1); + result.emitMatchIndex(matchIndex, i + 1, caseOps); + caseOps.length = 0; continue; } } result.emitUnchanged(i - 1); - result.emitMatchIndex(matchIndex, i + 1); + result.emitMatchIndex(matchIndex, i + 1, caseOps); + caseOps.length = 0; continue; } } diff --git a/src/vs/editor/contrib/find/test/find.test.ts b/src/vs/editor/contrib/find/test/find.test.ts index a0e8e987ef3..98f31f328cb 100644 --- a/src/vs/editor/contrib/find/test/find.test.ts +++ b/src/vs/editor/contrib/find/test/find.test.ts @@ -16,7 +16,7 @@ suite('Find', () => { withTestCodeEditor([ 'ABC DEF', '0123 456' - ], {}, (editor, cursor) => { + ], {}, (editor) => { // The cursor is at the very top, of the file, at the first ABC let searchStringAtTop = getSelectionSearchString(editor); @@ -39,7 +39,7 @@ suite('Find', () => { withTestCodeEditor([ 'ABC DEF', '0123 456' - ], {}, (editor, cursor) => { + ], {}, (editor) => { // Select A of ABC editor.setSelection(new Range(1, 1, 1, 2)); @@ -63,7 +63,7 @@ suite('Find', () => { withTestCodeEditor([ 'ABC DEF', '0123 456' - ], {}, (editor, cursor) => { + ], {}, (editor) => { // Select first line and newline editor.setSelection(new Range(1, 1, 2, 1)); diff --git a/src/vs/editor/contrib/find/test/findController.test.ts b/src/vs/editor/contrib/find/test/findController.test.ts index 633a70af76e..eaac102b78c 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,13 +77,13 @@ 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); @@ -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) { @@ -128,13 +128,13 @@ 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); @@ -152,13 +152,13 @@ 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); @@ -167,7 +167,7 @@ suite('FindController', () => { 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,10 +210,10 @@ 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 nextMatchFindAction = new NextMatchFindAction(); @@ -223,22 +223,22 @@ 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 startFindAction = new StartFindAction(); @@ -247,22 +247,22 @@ suite('FindController', () => { 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 nextMatchFindAction = new NextMatchFindAction(); @@ -270,7 +270,7 @@ suite('FindController', () => { findController.toggleRegex(); findController.setSearchString(testRegexString); - findController.start({ + await findController.start({ forceRevealReplace: false, seedSearchStringFromSelection: false, seedSearchStringFromGlobalClipboard: false, @@ -279,8 +279,8 @@ suite('FindController', () => { 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); @@ -288,15 +288,15 @@ 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({ + await findController.start({ forceRevealReplace: false, seedSearchStringFromSelection: false, seedSearchStringFromGlobalClipboard: false, @@ -319,15 +319,15 @@ suite('FindController', () => { }); }); - 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 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(); @@ -344,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 startFindAction = new StartFindAction(); - startFindAction.run(null, editor); + await startFindAction.run(null, editor); findController.getState().change({ searchString: '^', replaceString: 'x', isRegex: true }, false); findController.moveToNextMatch(); @@ -371,12 +371,12 @@ 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 nextSelectionMatchFindAction = new NextSelectionMatchFindAction(); @@ -388,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] @@ -398,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 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); @@ -419,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] @@ -430,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; @@ -447,13 +447,13 @@ 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); @@ -461,7 +461,7 @@ suite('FindController query options persistence', () => { 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); @@ -474,13 +474,13 @@ 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); @@ -488,7 +488,7 @@ suite('FindController query options persistence', () => { 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); @@ -499,13 +499,13 @@ 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); @@ -516,17 +516,17 @@ 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({ + await findController.start({ forceRevealReplace: false, seedSearchStringFromSelection: false, seedSearchStringFromGlobalClipboard: false, @@ -540,17 +540,17 @@ suite('FindController query options persistence', () => { }); }); - 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); - findController.start({ + await findController.start({ forceRevealReplace: false, seedSearchStringFromSelection: false, seedSearchStringFromGlobalClipboard: false, @@ -564,17 +564,17 @@ suite('FindController query options persistence', () => { }); }); - 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); - findController.start({ + await findController.start({ forceRevealReplace: false, seedSearchStringFromSelection: false, seedSearchStringFromGlobalClipboard: false, @@ -589,17 +589,17 @@ suite('FindController query options persistence', () => { }); - 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); - findController.start({ + await findController.start({ forceRevealReplace: false, seedSearchStringFromSelection: false, seedSearchStringFromGlobalClipboard: false, diff --git a/src/vs/editor/contrib/find/test/findModel.test.ts b/src/vs/editor/contrib/find/test/findModel.test.ts index d83c9ce4d34..1a515fdddcc 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'; @@ -21,7 +20,7 @@ import { TestNotificationService } from 'vs/platform/notification/test/common/te 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', @@ -37,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(); @@ -49,7 +48,7 @@ suite('FindModel', () => { { 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) ); }); } @@ -91,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); @@ -241,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); @@ -271,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); @@ -303,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); @@ -356,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); @@ -442,7 +441,7 @@ 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); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -494,7 +493,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('find model prev', (editor, cursor) => { + findTest('find model prev', (editor) => { let findState = new FindReplaceState(); findState.change({ searchString: 'hello', wholeWord: true }, false); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -580,7 +579,7 @@ 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); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -632,7 +631,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); @@ -664,7 +663,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); @@ -713,7 +712,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); @@ -784,7 +783,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); @@ -876,7 +875,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); @@ -928,7 +927,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); @@ -957,7 +956,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); @@ -1028,7 +1027,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); @@ -1099,7 +1098,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); @@ -1151,7 +1150,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); @@ -1247,7 +1246,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); @@ -1312,7 +1311,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); @@ -1360,7 +1359,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); @@ -1402,7 +1401,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); @@ -1431,7 +1430,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); @@ -1463,7 +1462,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); @@ -1493,7 +1492,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); @@ -1522,7 +1521,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); @@ -1564,7 +1563,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); @@ -1610,7 +1609,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); @@ -1648,7 +1647,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); @@ -1714,7 +1713,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); @@ -1785,7 +1784,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); @@ -1818,7 +1817,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); @@ -1884,7 +1883,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); @@ -1919,7 +1918,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); @@ -1950,7 +1949,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); @@ -1986,7 +1985,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); @@ -2018,7 +2017,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'; @@ -2041,7 +2040,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); @@ -2072,7 +2071,7 @@ 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); let findModel = new FindModelBoundToEditorModel(editor, findState); @@ -2090,7 +2089,7 @@ suite('FindModel', () => { findState.dispose(); }); - findTest('issue #3516: Control behavior of "Next" operations (not looping back to beginning)', (editor, cursor) => { + 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); @@ -2170,7 +2169,7 @@ suite('FindModel', () => { }); - findTest('issue #3516: Control behavior of "Next" operations (looping back to beginning)', (editor, cursor) => { + 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); diff --git a/src/vs/editor/contrib/find/test/replacePattern.test.ts b/src/vs/editor/contrib/find/test/replacePattern.test.ts index 5a110c8160a..907292fd7ad 100644 --- a/src/vs/editor/contrib/find/test/replacePattern.test.ts +++ b/src/vs/editor/contrib/find/test/replacePattern.test.ts @@ -69,6 +69,38 @@ suite('Replace Pattern test', () => { testParse('hello$\'', [ReplacePiece.staticValue('hello$\'')]); }); + test('parse replace string with case modifiers', () => { + let testParse = (input: string, expectedPieces: ReplacePiece[]) => { + let actual = parseReplaceString(input); + let expected = new ReplacePattern(expectedPieces); + assert.deepEqual(actual, expected, 'Parsing ' + input); + }; + function assertReplace(target: string, search: RegExp, replaceString: string, expected: string): void { + let replacePattern = parseReplaceString(replaceString); + let m = search.exec(target); + let actual = replacePattern.buildReplaceString(m); + + assert.equal(actual, expected, `${target}.replace(${search}, ${replaceString}) === ${expected}`); + } + + // \U, \u => uppercase \L, \l => lowercase \E => cancel + + testParse('hello\\U$1', [ReplacePiece.staticValue('hello'), ReplacePiece.caseOps(1, ['U'])]); + assertReplace('func privateFunc(', /func (\w+)\(/, 'func \\U$1(', 'func PRIVATEFUNC('); + + testParse('hello\\u$1', [ReplacePiece.staticValue('hello'), ReplacePiece.caseOps(1, ['u'])]); + assertReplace('func privateFunc(', /func (\w+)\(/, 'func \\u$1(', 'func PrivateFunc('); + + testParse('hello\\L$1', [ReplacePiece.staticValue('hello'), ReplacePiece.caseOps(1, ['L'])]); + assertReplace('func privateFunc(', /func (\w+)\(/, 'func \\L$1(', 'func privatefunc('); + + testParse('hello\\l$1', [ReplacePiece.staticValue('hello'), ReplacePiece.caseOps(1, ['l'])]); + assertReplace('func PrivateFunc(', /func (\w+)\(/, 'func \\l$1(', 'func privateFunc('); + + testParse('hello$1\\u\\u\\U$4goodbye', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(1), ReplacePiece.caseOps(4, ['u', 'u', 'U']), ReplacePiece.staticValue('goodbye')]); + assertReplace('hellogooDbye', /hello(\w+)/, 'hello\\u\\u\\l\\l\\U$1', 'helloGOodBYE'); + }); + test('replace has JavaScript semantics', () => { let testJSReplaceSemantics = (target: string, search: RegExp, replaceString: string, expected: string) => { let replacePattern = parseReplaceString(replaceString); diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts index 7c2eed05550..01a7dc55aa0 100644 --- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts @@ -26,9 +26,9 @@ import { IActionBarOptions, ActionsOrientation } from 'vs/base/browser/ui/action import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { MenuId, IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { MenuId, IMenuService } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { createAndFillInActionBarActions, MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; class MessageWidget { @@ -246,10 +246,10 @@ export class MarkerNavigationWidget extends PeekViewWidget { @IThemeService private readonly _themeService: IThemeService, @IOpenerService private readonly _openerService: IOpenerService, @IMenuService private readonly _menuService: IMenuService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, + @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; @@ -311,8 +311,8 @@ export class MarkerNavigationWidget extends PeekViewWidget { protected _getActionBarOptions(): IActionBarOptions { return { - orientation: ActionsOrientation.HORIZONTAL, - actionViewItemProvider: action => action instanceof MenuItemAction ? this._instantiationService.createInstance(MenuEntryActionViewItem, action) : undefined + ...super._getActionBarOptions(), + orientation: ActionsOrientation.HORIZONTAL }; } diff --git a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts index 678b0836338..232aab8d0f8 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts @@ -722,9 +722,10 @@ CommandsRegistry.registerCommand({ { name: 'position', description: 'The position at which to start', constraint: corePosition.Position.isIPosition }, { name: 'locations', description: 'An array of locations.', constraint: Array }, { name: 'multiple', description: 'Define what to do when having multiple results, either `peek`, `gotoAndPeek`, or `goto' }, + { name: 'noResultsMessage', description: 'Human readable message that shows when locations is empty.' }, ] }, - handler: async (accessor: ServicesAccessor, resource: any, position: any, references: any, multiple?: any, openInPeek?: boolean) => { + handler: async (accessor: ServicesAccessor, resource: any, position: any, references: any, multiple?: any, noResultsMessage?: string, openInPeek?: boolean) => { assertType(URI.isUri(resource)); assertType(corePosition.Position.isIPosition(position)); assertType(Array.isArray(references)); @@ -739,7 +740,16 @@ CommandsRegistry.registerCommand({ editor.revealPositionInCenterIfOutsideViewport(position, ScrollType.Smooth); return editor.invokeWithinContext(accessor => { - const command = new GenericGoToLocationAction({ muteMessage: true, openInPeek: Boolean(openInPeek), openToSide: false }, references, multiple as GoToLocationValues); + const command = new class extends GenericGoToLocationAction { + _getNoResultFoundMessage(info: IWordAtPosition | null) { + return noResultsMessage || super._getNoResultFoundMessage(info); + } + }({ + muteMessage: !Boolean(noResultsMessage), + openInPeek: Boolean(openInPeek), + openToSide: false + }, references, multiple as GoToLocationValues); + accessor.get(IInstantiationService).invokeFunction(command.run.bind(command), editor); }); } @@ -758,7 +768,7 @@ CommandsRegistry.registerCommand({ ] }, handler: async (accessor: ServicesAccessor, resource: any, position: any, references: any, multiple?: any) => { - accessor.get(ICommandService).executeCommand('editor.action.goToLocations', resource, position, references, multiple, true); + accessor.get(ICommandService).executeCommand('editor.action.goToLocations', resource, position, references, multiple, undefined, true); } }); diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesTree.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesTree.ts index b7d076b2a64..f448805d288 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesTree.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesTree.ts @@ -36,7 +36,7 @@ export class DataSource implements IAsyncDataSource 1) { + if (len > 1) { this.badge.setTitleFormat(localize('referencesCount', "{0} references", len)); } else { this.badge.setTitleFormat(localize('referenceCount', "{0} reference", len)); @@ -174,10 +171,9 @@ 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 diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts index 4c41acb0056..4f8b1656870 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts @@ -6,13 +6,12 @@ import 'vs/css!./referencesWidget'; import * as dom from 'vs/base/browser/dom'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; -import { GestureEvent } from 'vs/base/browser/touch'; import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { dispose, IDisposable, IReference, DisposableStore } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; -import { basenameOrAuthority, dirname, isEqual } from 'vs/base/common/resources'; +import { basenameOrAuthority, dirname } from 'vs/base/common/resources'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; @@ -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(); } } @@ -219,7 +220,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget { @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.getColorTheme()); this._callOnDispose.add(themeService.onDidColorThemeChange(this._applyTheme.bind(this))); @@ -315,6 +316,8 @@ export class ReferenceWidget extends peekView.PeekViewWidget { accessibilityProvider: new AccessibilityProvider(), keyboardNavigationLabelProvider: this._instantiationService.createInstance(StringRepresentationProvider), identityProvider: new IdentityProvider(), + openOnSingleClick: true, + openOnFocus: true, overrideStyles: { listBackground: peekView.peekViewResultsBackground } @@ -370,22 +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) || (e.browserEvent).tapCount === 2) { - // keybinding (list service command) - // OR double click - // OR double tap - // -> close widget and goto target - onEvent(e.elements[0], 'goto'); + if (e.sideBySide) { + onEvent(e.element, 'side'); + } else if (e.editorOptions.pinned) { + onEvent(e.element, 'goto'); } else { - // preview location - onEvent(e.elements[0], 'show'); + onEvent(e.element, 'show'); } }); diff --git a/src/vs/editor/contrib/gotoSymbol/referencesModel.ts b/src/vs/editor/contrib/gotoSymbol/referencesModel.ts index fa0f2c14db2..5c4bbe4d021 100644 --- a/src/vs/editor/contrib/gotoSymbol/referencesModel.ts +++ b/src/vs/editor/contrib/gotoSymbol/referencesModel.ts @@ -5,7 +5,7 @@ import { localize } from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; -import { basename } from 'vs/base/common/resources'; +import { basename, extUri } from 'vs/base/common/resources'; import { IDisposable, dispose, IReference, DisposableStore } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; @@ -16,6 +16,8 @@ import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/r import { Position } from 'vs/editor/common/core/position'; import { IMatch } from 'vs/base/common/filters'; import { Constants } from 'vs/base/common/uint'; +import { ResourceMap } from 'vs/base/common/map'; +import { onUnexpectedError } from 'vs/base/common/errors'; export class OneReference { @@ -24,14 +26,11 @@ export class OneReference { constructor( readonly isProviderFirst: boolean, readonly parent: FileReferences, + readonly uri: URI, private _range: IRange, private _rangeCallback: (ref: OneReference) => void ) { } - get uri(): URI { - return this.parent.uri; - } - get range(): IRange { return this._range; } @@ -86,9 +85,7 @@ export class FileReferences implements IDisposable { readonly children: OneReference[] = []; - private _preview?: FilePreview; - private _resolved?: boolean; - private _loadFailure?: any; + private _previews = new ResourceMap(); constructor( readonly parent: ReferencesModel, @@ -96,16 +93,12 @@ export class FileReferences implements IDisposable { ) { } dispose(): void { - dispose(this._preview); - this._preview = undefined; + dispose(this._previews.values()); + this._previews.clear(); } - get preview(): FilePreview | undefined { - return this._preview; - } - - get failure(): any { - return this._loadFailure; + getPreview(child: OneReference): FilePreview | undefined { + return this._previews.get(child.uri); } get ariaMessage(): string { @@ -117,31 +110,22 @@ export class FileReferences implements IDisposable { } } - resolve(textModelResolverService: ITextModelService): Promise { - - if (this._resolved) { - return Promise.resolve(this); + async resolve(textModelResolverService: ITextModelService): Promise { + if (this._previews.size !== 0) { + return this; } - - return Promise.resolve(textModelResolverService.createModelReference(this.uri).then(modelReference => { - const model = modelReference.object; - - if (!model) { - modelReference.dispose(); - throw new Error(); + for (let child of this.children) { + if (this._previews.has(child.uri)) { + continue; } - - this._preview = new FilePreview(modelReference); - this._resolved = true; - return this; - - }, err => { - // something wrong here - this.children.length = 0; - this._resolved = true; - this._loadFailure = err; - return this; - })); + try { + const ref = await textModelResolverService.createModelReference(child.uri); + this._previews.set(child.uri, new FilePreview(ref)); + } catch (err) { + onUnexpectedError(err); + } + } + return this; } } @@ -167,17 +151,20 @@ export class ReferencesModel implements IDisposable { let current: FileReferences | undefined; for (let link of links) { - if (!current || current.uri.toString() !== link.uri.toString()) { + if (!current || !extUri.isEqual(current.uri, link.uri, true)) { // new group current = new FileReferences(this, link.uri); this.groups.push(current); } // append, check for equality first! - if (current.children.length === 0 || !Range.equalsRange(link.range, current.children[current.children.length - 1].range)) { + if (current.children.length === 0 || ReferencesModel._compareReferences(link, current.children[current.children.length - 1]) !== 0) { const oneRef = new OneReference( - providersFirst === link, current, link.targetSelectionRange || link.range, + providersFirst === link, + current, + link.uri, + link.targetSelectionRange || link.range, ref => this._onDidChangeReferenceRange.fire(ref) ); this.references.push(oneRef); @@ -294,6 +281,6 @@ export class ReferencesModel implements IDisposable { } private static _compareReferences(a: Location, b: Location): number { - return strings.compare(a.uri.toString(), b.uri.toString()) || Range.compareRangesUsingStarts(a.range, b.range); + return extUri.compare(a.uri, b.uri) || Range.compareRangesUsingStarts(a.range, b.range); } } diff --git a/src/vs/editor/contrib/gotoSymbol/symbolNavigation.ts b/src/vs/editor/contrib/gotoSymbol/symbolNavigation.ts index d082bb98f43..1196c0293a7 100644 --- a/src/vs/editor/contrib/gotoSymbol/symbolNavigation.ts +++ b/src/vs/editor/contrib/gotoSymbol/symbolNavigation.ts @@ -26,7 +26,7 @@ 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; @@ -34,7 +34,7 @@ export interface ISymbolNavigationService { class SymbolNavigationService implements ISymbolNavigationService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _ctxHasSymbols: IContextKey; @@ -198,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 { diff --git a/src/vs/editor/contrib/hover/hover.ts b/src/vs/editor/contrib/hover/hover.ts index 3a0f4eea252..01b4416ffc5 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/hoverWidgets.ts b/src/vs/editor/contrib/hover/hoverWidgets.ts index e3f9f62125a..06278716db2 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; - protected readonly _domNode: HTMLElement; protected _showAtPosition: Position | null; protected _showAtRange: Range | null; private _stoleFocus: boolean; - private readonly scrollbar: DomScrollableElement; // Editor.IContentWidget.allowEditorOverflow public allowEditorOverflow = true; @@ -34,29 +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._containerDomNode.setAttribute('role', 'tooltip'); - - 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(); } @@ -82,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); @@ -97,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(); } } @@ -106,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); @@ -134,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`; } } @@ -177,7 +182,7 @@ 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', 'tooltip'); @@ -198,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 71055810784..1d7fb929b81 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -41,6 +41,7 @@ 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.$; @@ -212,13 +213,14 @@ export class ModesContentHoverWidget extends ContentHoverWidget { constructor( editor: ICodeEditor, + _hoverVisibleKey: IContextKey, markerDecorationsService: IMarkerDecorationsService, + keybindingService: IKeybindingService, private readonly _themeService: IThemeService, - private readonly _keybindingService: IKeybindingService, private readonly _modeService: IModeService, private readonly _openerService: IOpenerService = NullOpenerService, ) { - super(ModesContentHoverWidget.ID, editor); + super(ModesContentHoverWidget.ID, editor, _hoverVisibleKey, keybindingService); this._messages = []; this._lastRange = null; @@ -249,7 +251,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { })); this._register(TokenizationRegistry.onDidChange((e) => { if (this.isVisible && this._lastRange && this._messages.length > 0) { - this._domNode.textContent = ''; + this._hover.contentsDomNode.textContent = ''; this._renderMessages(this._lastRange, this._messages); } })); @@ -461,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); @@ -562,7 +564,7 @@ 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: () => { @@ -600,7 +602,7 @@ 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) => { @@ -633,25 +635,6 @@ export class ModesContentHoverWidget extends ContentHoverWidget { }); } - 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')); - action.setAttribute('href', '#'); - action.setAttribute('role', 'button'); - if (actionOptions.iconClass) { - dom.append(action, $(`span.icon.${actionOptions.iconClass}`)); - } - const label = dom.append(action, $('span')); - const keybinding = this._keybindingService.lookupKeybinding(actionOptions.commandId); - const keybindingLabel = keybinding ? keybinding.getLabel() : null; - label.textContent = keybindingLabel ? `${actionOptions.label} (${keybindingLabel})` : actionOptions.label; - return dom.addDisposableListener(actionContainer, dom.EventType.CLICK, e => { - e.stopPropagation(); - e.preventDefault(); - actionOptions.run(actionContainer); - }); - } - private static readonly _DECORATION_OPTIONS = ModelDecorationOptions.register({ className: 'hoverHighlight' }); @@ -683,6 +666,6 @@ function hoverContentsEquals(first: HoverPart[], second: HoverPart[]): boolean { registerThemingParticipant((theme, collector) => { const linkFg = theme.getColor(textLinkForeground); if (linkFg) { - collector.addRule(`.monaco-editor-hover .hover-contents a.code-link span:hover { color: ${linkFg}; }`); + collector.addRule(`.monaco-hover .hover-contents a.code-link span:hover { color: ${linkFg}; }`); } }); diff --git a/src/vs/editor/contrib/linesOperations/linesOperations.ts b/src/vs/editor/contrib/linesOperations/linesOperations.ts index 174302f90a1..bc7fd660d84 100644 --- a/src/vs/editor/contrib/linesOperations/linesOperations.ts +++ b/src/vs/editor/contrib/linesOperations/linesOperations.ts @@ -454,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(); } } @@ -500,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())); } } @@ -525,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())); } } diff --git a/src/vs/editor/contrib/linesOperations/test/copyLinesCommand.test.ts b/src/vs/editor/contrib/linesOperations/test/copyLinesCommand.test.ts index 446c947cf8d..6777693d4ea 100644 --- a/src/vs/editor/contrib/linesOperations/test/copyLinesCommand.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/copyLinesCommand.test.ts @@ -204,7 +204,7 @@ suite('Editor Contrib - Duplicate Selection', () => { const duplicateSelectionAction = new DuplicateSelectionAction(); function testDuplicateSelectionAction(lines: string[], selections: Selection[], expectedLines: string[], expectedSelections: Selection[]): void { - withTestCodeEditor(lines.join('\n'), {}, (editor, cursor) => { + withTestCodeEditor(lines.join('\n'), {}, (editor) => { editor.setSelections(selections); duplicateSelectionAction.run(null!, editor, {}); assert.deepEqual(editor.getValue(), expectedLines.join('\n')); diff --git a/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts b/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts index d0dc6b57285..cdcb7f49128 100644 --- a/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands'; -import { Cursor } from 'vs/editor/common/controller/cursor'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; import { Handler } from 'vs/editor/common/editorCommon'; @@ -14,6 +13,7 @@ import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import type { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction } from 'vs/editor/browser/editorExtensions'; +import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; function assertSelection(editor: ICodeEditor, expected: Selection | Selection[]): void { if (!Array.isArray(expected)) { @@ -831,39 +831,39 @@ suite('Editor Contrib - Line Operations', () => { }); test('InsertLineBeforeAction', () => { - function testInsertLineBefore(lineNumber: number, column: number, callback: (model: ITextModel, cursor: Cursor) => void): void { + function testInsertLineBefore(lineNumber: number, column: number, callback: (model: ITextModel, viewModel: ViewModel) => void): void { const TEXT = [ 'First line', 'Second line', 'Third line' ]; - withTestCodeEditor(TEXT, {}, (editor, cursor) => { + withTestCodeEditor(TEXT, {}, (editor, viewModel) => { editor.setPosition(new Position(lineNumber, column)); let insertLineBeforeAction = new InsertLineBeforeAction(); executeAction(insertLineBeforeAction, editor); - callback(editor.getModel()!, cursor); + callback(editor.getModel()!, viewModel); }); } - testInsertLineBefore(1, 3, (model, cursor) => { - assert.deepEqual(cursor.getSelection(), new Selection(1, 1, 1, 1)); + testInsertLineBefore(1, 3, (model, viewModel) => { + assert.deepEqual(viewModel.getSelection(), new Selection(1, 1, 1, 1)); assert.equal(model.getLineContent(1), ''); assert.equal(model.getLineContent(2), 'First line'); assert.equal(model.getLineContent(3), 'Second line'); assert.equal(model.getLineContent(4), 'Third line'); }); - testInsertLineBefore(2, 3, (model, cursor) => { - assert.deepEqual(cursor.getSelection(), new Selection(2, 1, 2, 1)); + testInsertLineBefore(2, 3, (model, viewModel) => { + assert.deepEqual(viewModel.getSelection(), new Selection(2, 1, 2, 1)); assert.equal(model.getLineContent(1), 'First line'); assert.equal(model.getLineContent(2), ''); assert.equal(model.getLineContent(3), 'Second line'); assert.equal(model.getLineContent(4), 'Third line'); }); - testInsertLineBefore(3, 3, (model, cursor) => { - assert.deepEqual(cursor.getSelection(), new Selection(3, 1, 3, 1)); + testInsertLineBefore(3, 3, (model, viewModel) => { + assert.deepEqual(viewModel.getSelection(), new Selection(3, 1, 3, 1)); assert.equal(model.getLineContent(1), 'First line'); assert.equal(model.getLineContent(2), 'Second line'); assert.equal(model.getLineContent(3), ''); @@ -872,39 +872,39 @@ suite('Editor Contrib - Line Operations', () => { }); test('InsertLineAfterAction', () => { - function testInsertLineAfter(lineNumber: number, column: number, callback: (model: ITextModel, cursor: Cursor) => void): void { + function testInsertLineAfter(lineNumber: number, column: number, callback: (model: ITextModel, viewModel: ViewModel) => void): void { const TEXT = [ 'First line', 'Second line', 'Third line' ]; - withTestCodeEditor(TEXT, {}, (editor, cursor) => { + withTestCodeEditor(TEXT, {}, (editor, viewModel) => { editor.setPosition(new Position(lineNumber, column)); let insertLineAfterAction = new InsertLineAfterAction(); executeAction(insertLineAfterAction, editor); - callback(editor.getModel()!, cursor); + callback(editor.getModel()!, viewModel); }); } - testInsertLineAfter(1, 3, (model, cursor) => { - assert.deepEqual(cursor.getSelection(), new Selection(2, 1, 2, 1)); + testInsertLineAfter(1, 3, (model, viewModel) => { + assert.deepEqual(viewModel.getSelection(), new Selection(2, 1, 2, 1)); assert.equal(model.getLineContent(1), 'First line'); assert.equal(model.getLineContent(2), ''); assert.equal(model.getLineContent(3), 'Second line'); assert.equal(model.getLineContent(4), 'Third line'); }); - testInsertLineAfter(2, 3, (model, cursor) => { - assert.deepEqual(cursor.getSelection(), new Selection(3, 1, 3, 1)); + testInsertLineAfter(2, 3, (model, viewModel) => { + assert.deepEqual(viewModel.getSelection(), new Selection(3, 1, 3, 1)); assert.equal(model.getLineContent(1), 'First line'); assert.equal(model.getLineContent(2), 'Second line'); assert.equal(model.getLineContent(3), ''); assert.equal(model.getLineContent(4), 'Third line'); }); - testInsertLineAfter(3, 3, (model, cursor) => { - assert.deepEqual(cursor.getSelection(), new Selection(4, 1, 4, 1)); + testInsertLineAfter(3, 3, (model, viewModel) => { + assert.deepEqual(viewModel.getSelection(), new Selection(4, 1, 4, 1)); assert.equal(model.getLineContent(1), 'First line'); assert.equal(model.getLineContent(2), 'Second line'); assert.equal(model.getLineContent(3), 'Third line'); diff --git a/src/vs/editor/contrib/message/messageController.css b/src/vs/editor/contrib/message/messageController.css index 36c68465b2c..a3910415dd4 100644 --- a/src/vs/editor/contrib/message/messageController.css +++ b/src/vs/editor/contrib/message/messageController.css @@ -5,6 +5,7 @@ .monaco-editor .monaco-editor-overlaymessage { padding-bottom: 8px; + z-index: 10000; } @keyframes fadeIn { diff --git a/src/vs/editor/contrib/message/messageController.ts b/src/vs/editor/contrib/message/messageController.ts index a4fcb5f2d1a..e94a981d167 100644 --- a/src/vs/editor/contrib/message/messageController.ts +++ b/src/vs/editor/contrib/message/messageController.ts @@ -29,8 +29,6 @@ export class MessageController extends Disposable implements IEditorContribution return editor.getContribution(MessageController.ID); } - private readonly closeTimeout = 3000; // close after 3s - private readonly _editor: ICodeEditor; private readonly _visible: IContextKey; private readonly _messageWidget = this._register(new MutableDisposable()); @@ -70,7 +68,8 @@ export class MessageController extends Disposable implements IEditorContribution this._messageListeners.add(this._editor.onDidDispose(() => this.closeMessage())); this._messageListeners.add(this._editor.onDidChangeModel(() => this.closeMessage())); - this._messageListeners.add(new TimeoutTimer(() => this.closeMessage(), this.closeTimeout)); + // 3sec + this._messageListeners.add(new TimeoutTimer(() => this.closeMessage(), 3000)); // close on mouse move let bounds: Range; diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index abebedd5080..cdb7d0c6190 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -9,7 +9,6 @@ import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { RevealTarget } from 'vs/editor/common/controller/cursorCommon'; import { CursorChangeReason, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { CursorMoveCommands } from 'vs/editor/common/controller/cursorMoveCommands'; import { Range } from 'vs/editor/common/core/range'; @@ -61,20 +60,19 @@ export class InsertCursorAbove extends EditorAction { } const useLogicalLine = (args && args.logicalLine === true); - const cursors = editor._getCursors(); - const context = cursors.context; + const viewModel = editor._getViewModel(); - if (context.config.readOnly) { + if (viewModel.cursorConfig.readOnly) { return; } - context.model.pushStackElement(); - cursors.setStates( + viewModel.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, - CursorMoveCommands.addCursorUp(context, cursors.getAll(), useLogicalLine) + CursorMoveCommands.addCursorUp(viewModel, viewModel.getCursorStates(), useLogicalLine) ); - cursors.reveal(args.source, true, RevealTarget.TopMost, ScrollType.Smooth); + viewModel.revealTopMostCursor(args.source); } } @@ -110,20 +108,19 @@ export class InsertCursorBelow extends EditorAction { } const useLogicalLine = (args && args.logicalLine === true); - const cursors = editor._getCursors(); - const context = cursors.context; + const viewModel = editor._getViewModel(); - if (context.config.readOnly) { + if (viewModel.cursorConfig.readOnly) { return; } - context.model.pushStackElement(); - cursors.setStates( + viewModel.pushStackElement(); + viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, - CursorMoveCommands.addCursorDown(context, cursors.getAll(), useLogicalLine) + CursorMoveCommands.addCursorDown(viewModel, viewModel.getCursorStates(), useLogicalLine) ); - cursors.reveal(args.source, true, RevealTarget.BottomMost, ScrollType.Smooth); + viewModel.revealBottomMostCursor(args.source); } } diff --git a/src/vs/editor/contrib/multicursor/test/multicursor.test.ts b/src/vs/editor/contrib/multicursor/test/multicursor.test.ts index a1276a70c7d..8b25ecac5e1 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); }); }); @@ -70,7 +70,8 @@ suite('Multicursor selection', () => { remove: (key) => undefined, logStorage: () => undefined, migrate: (toWorkspace) => Promise.resolve(undefined), - flush: () => undefined + flush: () => undefined, + isNew: () => true } as IStorageService); test('issue #8817: Cursor position changes when you cancel multicursor', () => { @@ -78,7 +79,7 @@ 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); @@ -108,7 +109,7 @@ 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); @@ -142,7 +143,7 @@ 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); @@ -170,7 +171,7 @@ 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); @@ -225,7 +226,7 @@ suite('Multicursor selection', () => { 'rty', 'qwe', 'rty' - ], { serviceCollection: serviceCollection }, (editor, cursor) => { + ], { serviceCollection: serviceCollection }, (editor) => { editor.getModel()!.setEOL(EndOfLineSequence.CRLF); @@ -250,8 +251,8 @@ suite('Multicursor selection', () => { }); }); - function testMulticursor(text: string[], callback: (editor: TestCodeEditor, findController: CommonFindController) => void): void { - withTestCodeEditor(text, { serviceCollection: serviceCollection }, (editor, cursor) => { + 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); @@ -262,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/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index 809192427fa..e140bdc4cd5 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -24,6 +24,7 @@ import { HIGH_CONTRAST, registerThemingParticipant } from 'vs/platform/theme/com 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'; const $ = dom.$; @@ -263,16 +264,16 @@ 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; diff --git a/src/vs/editor/contrib/peekView/media/peekViewWidget.css b/src/vs/editor/contrib/peekView/media/peekViewWidget.css index d0e9bc3c552..e6b5ccb09f2 100644 --- a/src/vs/editor/contrib/peekView/media/peekViewWidget.css +++ b/src/vs/editor/contrib/peekView/media/peekViewWidget.css @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ .monaco-editor .peekview-widget .head { - box-sizing: border-box; + box-sizing: border-box; display: flex; } @@ -14,6 +14,7 @@ font-size: 13px; margin-left: 20px; cursor: pointer; + min-width: 0; } .monaco-editor .peekview-widget .head .peekview-title .dirname:not(:empty) { @@ -25,6 +26,15 @@ white-space: nowrap; } +.monaco-editor .peekview-widget .head .peekview-title .dirname { + white-space: nowrap; +} + +.monaco-editor .peekview-widget .head .peekview-title .filename { + overflow: hidden; + text-overflow: ellipsis; +} + .monaco-editor .peekview-widget .head .peekview-title .meta:not(:empty)::before { content: '-'; padding: 0 0.3em; diff --git a/src/vs/editor/contrib/peekView/peekView.ts b/src/vs/editor/contrib/peekView/peekView.ts index 47fac3224d0..cbada7a1e48 100644 --- a/src/vs/editor/contrib/peekView/peekView.ts +++ b/src/vs/editor/contrib/peekView/peekView.ts @@ -18,7 +18,7 @@ import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeE import { IOptions, IStyles, ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget'; import * as nls from 'vs/nls'; import { RawContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { ServicesAccessor, createDecorator } from 'vs/platform/instantiation/common/instantiation'; +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'; @@ -26,16 +26,17 @@ 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(); @@ -103,7 +104,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; @@ -115,7 +116,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); } @@ -169,7 +174,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)); @@ -187,17 +192,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.classNames, 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 { diff --git a/src/vs/editor/contrib/rename/media/onTypeRename.css b/src/vs/editor/contrib/rename/media/onTypeRename.css index 2c80c5957b1..16bb0178528 100644 --- a/src/vs/editor/contrib/rename/media/onTypeRename.css +++ b/src/vs/editor/contrib/rename/media/onTypeRename.css @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ .monaco-editor .on-type-rename-decoration { - background: rgba(255, 0, 0, 0.3); - border-left: 1px solid rgba(255, 0, 0, 0.3); + 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 index 8a3177f139f..51cc31bbefe 100644 --- a/src/vs/editor/contrib/rename/onTypeRename.ts +++ b/src/vs/editor/contrib/rename/onTypeRename.ts @@ -26,6 +26,9 @@ import { URI } from 'vs/base/common/uri'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { 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'; export const CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE = new RawContextKey('onTypeRenameInputVisible', false); @@ -195,9 +198,9 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr try { this._ignoreChangeEvent = true; - const prevEditOperationType = this._editor._getCursors().getPrevEditOperationType(); + const prevEditOperationType = this._editor._getViewModel().getPrevEditOperationType(); this._editor.executeEdits('onTypeRename', edits); - this._editor._getCursors().setPrevEditOperationType(prevEditOperationType); + this._editor._getViewModel().setPrevEditOperationType(prevEditOperationType); } finally { this._ignoreChangeEvent = false; } @@ -360,6 +363,13 @@ export function getOnTypeRenameRanges(model: ITextModel, position: Position, tok }), 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)); diff --git a/src/vs/editor/contrib/rename/rename.ts b/src/vs/editor/contrib/rename/rename.ts index 865d05eccdf..7ae883bcd2e 100644 --- a/src/vs/editor/contrib/rename/rename.ts +++ b/src/vs/editor/contrib/rename/rename.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { illegalArgument, onUnexpectedError } from 'vs/base/common/errors'; +import { onUnexpectedError } from 'vs/base/common/errors'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IEditorProgressService } from 'vs/platform/progress/common/progress'; -import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, EditorCommand, registerEditorCommand, registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; +import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, EditorCommand, registerEditorCommand, registerModelAndPositionCommand } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; @@ -28,16 +28,17 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IdleValue, raceCancellation } from 'vs/base/common/async'; -import { withNullAsUndefined } from 'vs/base/common/types'; import { ILogService } from 'vs/platform/log/common/log'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, ConfigurationScope, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; +import { assertType } from 'vs/base/common/types'; class RenameSkeleton { private readonly _providers: RenameProvider[]; + private _providerRenameIdx: number = 0; constructor( private readonly model: ITextModel, @@ -51,30 +52,45 @@ class RenameSkeleton { } async resolveRenameLocation(token: CancellationToken): Promise { - const firstProvider = this._providers[0]; - if (!firstProvider) { - return undefined; - } - let res: RenameLocation & Rejection | undefined; - if (firstProvider.resolveRenameLocation) { - res = withNullAsUndefined(await firstProvider.resolveRenameLocation(this.model, this.position, token)); - } + const rejects: string[] = []; - if (!res) { - const word = this.model.getWordAtPosition(this.position); - if (word) { - return { - range: new Range(this.position.lineNumber, word.startColumn, this.position.lineNumber, word.endColumn), - text: word.word - }; + for (this._providerRenameIdx = 0; this._providerRenameIdx < this._providers.length; this._providerRenameIdx++) { + const provider = this._providers[this._providerRenameIdx]; + if (!provider.resolveRenameLocation) { + break; } + let res = await provider.resolveRenameLocation(this.model, this.position, token); + if (!res) { + continue; + } + if (res.rejectReason) { + rejects.push(res.rejectReason); + continue; + } + return res; } - return res; + const word = this.model.getWordAtPosition(this.position); + if (!word) { + return { + range: Range.fromPositions(this.position), + text: '', + rejectReason: rejects.length > 0 ? rejects.join('\n') : undefined + }; + } + return { + range: new Range(this.position.lineNumber, word.startColumn, this.position.lineNumber, word.endColumn), + text: word.word, + rejectReason: rejects.length > 0 ? rejects.join('\n') : undefined + }; } - async provideRenameEdits(newName: string, i: number, rejects: string[], token: CancellationToken): Promise { + async provideRenameEdits(newName: string, token: CancellationToken): Promise { + return this._provideRenameEdits(newName, this._providerRenameIdx, [], token); + } + + private async _provideRenameEdits(newName: string, i: number, rejects: string[], token: CancellationToken): Promise { const provider = this._providers[i]; if (!provider) { return { @@ -85,16 +101,21 @@ class RenameSkeleton { const result = await provider.provideRenameEdits(this.model, this.position, newName, token); if (!result) { - return this.provideRenameEdits(newName, i + 1, rejects.concat(nls.localize('no result', "No result.")), token); + return this._provideRenameEdits(newName, i + 1, rejects.concat(nls.localize('no result', "No result.")), token); } else if (result.rejectReason) { - return this.provideRenameEdits(newName, i + 1, rejects.concat(result.rejectReason), token); + return this._provideRenameEdits(newName, i + 1, rejects.concat(result.rejectReason), token); } return result; } } export async function rename(model: ITextModel, position: Position, newName: string): Promise { - return new RenameSkeleton(model, position).provideRenameEdits(newName, 0, [], CancellationToken.None); + const skeleton = new RenameSkeleton(model, position); + const loc = await skeleton.resolveRenameLocation(CancellationToken.None); + if (loc?.rejectReason) { + return { edits: [], rejectReason: loc.rejectReason }; + } + return skeleton.provideRenameEdits(newName, CancellationToken.None); } // --- register actions and commands @@ -194,7 +215,7 @@ class RenameController implements IEditorContribution { this.editor.focus(); - const renameOperation = raceCancellation(skeleton.provideRenameEdits(inputFieldResult.newName, 0, [], this._cts.token), this._cts.token).then(async renameResult => { + const renameOperation = raceCancellation(skeleton.provideRenameEdits(inputFieldResult.newName, this._cts.token), this._cts.token).then(async renameResult => { if (!renameResult || !this.editor.hasModel()) { return; @@ -330,11 +351,9 @@ 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); }); diff --git a/src/vs/editor/contrib/rename/renameInputField.ts b/src/vs/editor/contrib/rename/renameInputField.ts index d527c20ea96..2be443c8c55 100644 --- a/src/vs/editor/contrib/rename/renameInputField.ts +++ b/src/vs/editor/contrib/rename/renameInputField.ts @@ -82,7 +82,7 @@ export class RenameInputField implements IContentWidget { const updateLabel = () => { const [accept, preview] = this._acceptKeybindings; this._keybindingService.lookupKeybinding(accept); - this._label!.innerText = localize('label', "{0} to Rename, {1} to Preview", this._keybindingService.lookupKeybinding(accept)?.getLabel(), this._keybindingService.lookupKeybinding(preview)?.getLabel()); + this._label!.innerText = localize({ key: 'label', comment: ['placeholders are keybindings, e.g "F2 to Rename, Shift+F2 to Preview"'] }, "{0} to Rename, {1} to Preview", this._keybindingService.lookupKeybinding(accept)?.getLabel(), this._keybindingService.lookupKeybinding(preview)?.getLabel()); }; updateLabel(); this._disposables.add(this._keybindingService.onDidUpdateKeybindings(updateLabel)); diff --git a/src/vs/editor/contrib/rename/test/onTypeRename.test.ts b/src/vs/editor/contrib/rename/test/onTypeRename.test.ts index f57996f48c3..86177205b90 100644 --- a/src/vs/editor/contrib/rename/test/onTypeRename.test.ts +++ b/src/vs/editor/contrib/rename/test/onTypeRename.test.ts @@ -11,7 +11,7 @@ import { 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, TestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +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'; @@ -30,7 +30,7 @@ suite('On type rename', () => { disposables.clear(); }); - function createMockEditor(text: string | string[]) { + function createMockEditor(text: string | string[]): ITestCodeEditor { const model = typeof text === 'string' ? createTextModel(text, undefined, undefined, mockFile) : createTextModel(text.join('\n'), undefined, undefined, mockFile); @@ -46,7 +46,7 @@ suite('On type rename', () => { function testCase( name: string, initialState: { text: string | string[], ranges: Range[], stopPattern?: RegExp }, - operations: (editor: TestCodeEditor, contrib: OnTypeRenameContribution) => Promise, + operations: (editor: ITestCodeEditor, contrib: OnTypeRenameContribution) => Promise, expectedEndText: string | string[] ) { test(name, async () => { diff --git a/src/vs/editor/contrib/snippet/snippetParser.ts b/src/vs/editor/contrib/snippet/snippetParser.ts index c82aebf6181..b6bd2eb37d3 100644 --- a/src/vs/editor/contrib/snippet/snippetParser.ts +++ b/src/vs/editor/contrib/snippet/snippetParser.ts @@ -586,6 +586,10 @@ export class SnippetParser { return value.replace(/\$|}|\\/g, '\\$&'); } + static guessNeedsClipboard(template: string): boolean { + return /\${?CLIPBOARD/.test(template); + } + private _scanner: Scanner = new Scanner(); private _token: Token = { type: TokenType.EOF, pos: 0, len: 0 }; diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index fd2a141e9b1..717b223cb4a 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'; @@ -65,9 +64,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 +167,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 +302,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; } } @@ -396,9 +393,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; @@ -455,7 +450,7 @@ export class SnippetSession { modelBasedVariableResolver, new ClipboardBasedVariableResolver(readClipboardText, idx, indexedSelections.length, editor.getOption(EditorOption.multiCursorPaste) === 'spread'), new SelectionBasedVariableResolver(model, selection), - new CommentBasedVariableResolver(model), + new CommentBasedVariableResolver(model, selection), new TimeBasedVariableResolver, new WorkspaceBasedVariableResolver(workspaceService), new RandomBasedVariableResolver, @@ -508,11 +503,9 @@ export class SnippetSession { if (this._snippets[0].hasPlaceholder) { return this._move(true); } else { - return ( - undoEdits - .filter(edit => !!edit.identifier) // only use our undo edits - .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]); @@ -603,8 +596,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)) { @@ -612,7 +604,7 @@ export class SnippetSession { break; } } - }); + } } if (allPossibleSelections.size === 0) { @@ -633,11 +625,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); @@ -645,10 +636,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 e614ec9cfee..3253898bdee 100644 --- a/src/vs/editor/contrib/snippet/snippetVariables.ts +++ b/src/vs/editor/contrib/snippet/snippetVariables.ts @@ -208,14 +208,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; } 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 8a1d8bfa9c2..8c9be33cf11 100644 --- a/src/vs/editor/contrib/snippet/test/snippetController2.old.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetController2.old.test.ts @@ -6,8 +6,7 @@ import * as assert from 'assert'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; -import { TestCodeEditor, withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; -import { Cursor } from 'vs/editor/common/controller/cursor'; +import { ITestCodeEditor, withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { NullLogService } from 'vs/platform/log/common/log'; @@ -29,7 +28,7 @@ class TestSnippetController extends SnippetController2 { suite('SnippetController', () => { - function snippetTest(cb: (editor: TestCodeEditor, cursor: Cursor, template: string, snippetController: TestSnippetController) => void, lines?: string[]): void { + function snippetTest(cb: (editor: ITestCodeEditor, template: string, snippetController: TestSnippetController) => void, lines?: string[]): void { if (!lines) { lines = [ @@ -41,7 +40,7 @@ suite('SnippetController', () => { ]; } - withTestCodeEditor(lines, {}, (editor, cursor) => { + withTestCodeEditor(lines, {}, (editor) => { editor.getModel()!.updateOptions({ insertSpaces: false }); @@ -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 4f67e00ac4e..c430c263857 100644 --- a/src/vs/editor/contrib/snippet/test/snippetController2.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetController2.test.ts @@ -439,4 +439,13 @@ suite('SnippetController2', function () { 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/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts index b7012bec477..bb95bdfcadf 100644 --- a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts @@ -302,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 cce7e5aaa78..8926c628e43 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; diff --git a/src/vs/editor/contrib/suggest/suggest.ts b/src/vs/editor/contrib/suggest/suggest.ts index a5d89b53ce8..ff1e75b7d9e 100644 --- a/src/vs/editor/contrib/suggest/suggest.ts +++ b/src/vs/editor/contrib/suggest/suggest.ts @@ -14,8 +14,9 @@ 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), @@ -55,12 +56,15 @@ export class CompletionItem { 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 @@ -94,35 +98,38 @@ export class CompletionItem { 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 - || Range.compareRangesUsingStarts(completion.range.insert, completion.range.replace) !== 0; + || completion.range.insert.startColumn !== completion.range.replace.startColumn; } // create the suggestion resolver if (typeof provider.resolveCompletionItem !== 'function') { this._resolveCache = Promise.resolve(); + this._isResolved = true; } } - // resolving - get isResolved() { - return Boolean(this._resolveCache); - } + // ---- resolving - private _resolveCache?: Promise; + 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; } }); } @@ -157,13 +164,21 @@ export function setSnippetSuggestSupport(support: modes.CompletionItemProvider): return old; } +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(); @@ -174,12 +189,13 @@ export async function provideSuggestionItems( const result: CompletionItem[] = []; const disposables = new DisposableStore(); + let needsClipboard = false; const onCompletionList = (provider: modes.CompletionItemProvider, container: modes.CompletionList | null | undefined) => { if (!container) { return; } - for (let suggestion of container.suggestions || []) { + for (let suggestion of container.suggestions) { if (!options.kindFilter.has(suggestion.kind)) { // fill in default range when missing if (!suggestion.range) { @@ -189,7 +205,10 @@ export async function provideSuggestionItems( if (!suggestion.sortText) { suggestion.sortText = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.name; } - result.push(new CompletionItem(position, suggestion, container, provider, model)); + 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)) { @@ -199,18 +218,16 @@ export async function provideSuggestionItems( // 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 = new Promise((resolve, reject) => { + const snippetCompletions = (async () => { if (!_snippetSuggestSupport || options.kindFilter.has(modes.CompletionItemKind.Snippet)) { - resolve(); + return; } if (options.providerFilter.size > 0 && !options.providerFilter.has(_snippetSuggestSupport)) { - resolve(); + return; } - Promise.resolve(_snippetSuggestSupport.provideCompletionItems(model, position, context, token)).then(list => { - onCompletionList(_snippetSuggestSupport, list); - resolve(); - }, reject); - }); + 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 @@ -244,7 +261,11 @@ export async function provideSuggestionItems( return Promise.reject(canceled()); } // console.log(`${result.length} items AFTER ${Date.now() - t1}ms`); - return result.sort(getSuggestionComparator(options.snippetSortOrder)); + return new CompletionItemModel( + result.sort(getSuggestionComparator(options.snippetSortOrder)), + needsClipboard, + disposables + ); } @@ -306,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/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index d19c3ce2b09..60dd04f3d72 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -39,6 +39,10 @@ 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'; // sticky suggest widget which doesn't disappear on focus out and such let _sticky = false; @@ -116,9 +120,11 @@ export class SuggestController implements IEditorContribution { @ICommandService private readonly _commandService: ICommandService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IInstantiationService private readonly _instantiationService: IInstantiationService, + @ILogService private readonly _logService: ILogService, + @IClipboardService clipboardService: IClipboardService, ) { this.editor = editor; - this.model = new SuggestModel(this.editor, editorWorker); + this.model = new SuggestModel(this.editor, editorWorker, clipboardService); this.widget = this._toDispose.add(new IdleValue(() => { @@ -261,6 +267,10 @@ export class SuggestController implements IEditorContribution { const modelVersionNow = model.getAlternativeVersionId(); const { item } = event; + // + const tasks: Promise[] = []; + const cts = new CancellationTokenSource(); + // pushing undo stops *before* additional text edits and // *after* the main edit if (!(flags & InsertFlags.NoBeforeUndoStop)) { @@ -273,10 +283,71 @@ export class SuggestController implements IEditorContribution { // keep item in memory this._memoryService.memorize(model, this.editor.getPosition(), item); - const scrollState = StableEditorScrollState.capture(this.editor); if (Array.isArray(item.completion.additionalTextEdits)) { - this.editor.executeEdits('suggestController.additionalTextEdits', item.completion.additionalTextEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text))); + // sync additional edits + const scrollState = StableEditorScrollState.capture(this.editor); + this.editor.executeEdits( + 'suggestController.additionalTextEdits.sync', + item.completion.additionalTextEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text)) + ); + scrollState.restoreRelativeVerticalPositionOfCursor(this.editor); + + } else if (!item.isResolved) { + // async additional edits + const sw = new StopWatch(true); + let position: IPosition | undefined; + + const docListener = model.onDidChangeContent(e => { + if (e.isFlush) { + cts.cancel(); + docListener.dispose(); + return; + } + for (let change of e.changes) { + const thisPosition = Range.getEndPosition(change.range); + if (!position || Position.isBefore(thisPosition, position)) { + position = thisPosition; + } + } + }); + + let oldFlags = flags; + flags |= InsertFlags.NoAfterUndoStop; + let didType = false; + let typeListener = this.editor.onWillType(() => { + typeListener.dispose(); + didType = true; + if (!(oldFlags & InsertFlags.NoAfterUndoStop)) { + this.editor.pushUndoStop(); + } + }); + + tasks.push(item.resolve(cts.token).then(() => { + if (!item.completion.additionalTextEdits || cts.token.isCancellationRequested) { + return false; + } + if (position && item.completion.additionalTextEdits.some(edit => Position.isBefore(position!, Range.getStartPosition(edit.range)))) { + return false; + } + if (didType) { + this.editor.pushUndoStop(); + } + const scrollState = StableEditorScrollState.capture(this.editor); + this.editor.executeEdits( + 'suggestController.additionalTextEdits.async', + item.completion.additionalTextEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text)) + ); + scrollState.restoreRelativeVerticalPositionOfCursor(this.editor); + if (didType || !(oldFlags & InsertFlags.NoAfterUndoStop)) { + this.editor.pushUndoStop(); + } + return true; + }).then(applied => { + this._logService.trace('[suggest] async resolving of edits DONE (ms, applied?)', sw.elapsed(), applied); + docListener.dispose(); + typeListener.dispose(); + })); } let { insertText } = item.completion; @@ -289,11 +360,10 @@ export class SuggestController implements IEditorContribution { overwriteAfter: info.overwriteAfter, undoStopBefore: false, undoStopAfter: false, - adjustWhitespace: !(item.completion.insertTextRules! & CompletionItemInsertTextRule.KeepWhitespace) + adjustWhitespace: !(item.completion.insertTextRules! & CompletionItemInsertTextRule.KeepWhitespace), + clipboardText: event.model.clipboardText }); - scrollState.restoreRelativeVerticalPositionOfCursor(this.editor); - if (!(flags & InsertFlags.NoAfterUndoStop)) { this.editor.pushUndoStop(); } @@ -301,7 +371,6 @@ export class SuggestController implements IEditorContribution { if (!item.completion.command) { // done this.model.cancel(); - this.model.clear(); } else if (item.completion.command.id === TriggerSuggestAction.id) { // retigger @@ -309,14 +378,16 @@ export class SuggestController implements IEditorContribution { } else { // exec command, done - this._commandService.executeCommand(item.completion.command.id, ...(item.completion.command.arguments ? [...item.completion.command.arguments] : [])) - .catch(onUnexpectedError) - .finally(() => this.model.clear()); // <- clear only now, keep commands alive + tasks.push(this._commandService.executeCommand(item.completion.command.id, ...(item.completion.command.arguments ? [...item.completion.command.arguments] : [])).catch(onUnexpectedError)); this.model.cancel(); } if (flags & InsertFlags.KeepAlternativeSuggestions) { this._alternatives.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... @@ -334,6 +405,12 @@ export class SuggestController implements IEditorContribution { } 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 } { diff --git a/src/vs/editor/contrib/suggest/suggestMemory.ts b/src/vs/editor/contrib/suggest/suggestMemory.ts index 9fff61fe466..b2c98b605e2 100644 --- a/src/vs/editor/contrib/suggest/suggestMemory.ts +++ b/src/vs/editor/contrib/suggest/suggestMemory.ts @@ -26,7 +26,7 @@ export abstract class Memory { 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 @@ -132,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 { @@ -308,7 +304,7 @@ export class SuggestMemoryService implements ISuggestMemoryService { 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 42fb74bfa9c..d3daa598ebb 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'; @@ -22,6 +22,7 @@ import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerServ 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; @@ -116,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); @@ -422,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), @@ -432,7 +434,7 @@ 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); @@ -444,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); @@ -458,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); diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 3956148566d..7b19fefb903 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -43,9 +43,10 @@ import { MarkdownString } from 'vs/base/common/htmlContent'; 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, IActionViewItemProvider, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IAction } from 'vs/base/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; @@ -159,7 +160,7 @@ class ItemRenderer implements IListRenderer { const options = this.editor.getOptions(); @@ -321,7 +322,7 @@ class SuggestionDetails { this.header = append(this.body, $('.header')); this.close = append(this.header, $('span' + Codicon.close.cssSelector)); - this.close.title = nls.localize('readLess', "Read less...{0}", this.kbToggleDetails); + this.close.title = nls.localize('readLess', "Read Less ({0})", this.kbToggleDetails); this.type = append(this.header, $('p.type')); this.docs = append(this.body, $('p.docs')); @@ -380,7 +381,7 @@ class SuggestionDetails { // --- details if (detail) { - this.type.innerText = detail; + this.type.innerText = detail.length > 100_000 ? `${detail.substr(0, 100_000)}â€Ļ` : detail; show(this.type); } else { this.type.innerText = ''; @@ -605,7 +606,6 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate false }, mouseSupport: false, accessibilityProvider: { getRole: () => 'option', @@ -618,7 +618,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate().add(CompletionItemKind.Snippet))); + const { items } = await provideSuggestionItems(model, new Position(1, 1), new CompletionOptions(undefined, new Set().add(CompletionItemKind.Snippet))); assert.equal(items.length, 1); assert.equal(items[0].completion.label, 'fff'); }); @@ -99,11 +99,54 @@ suite('Suggest', function () { }; const registration = CompletionProviderRegistry.register({ pattern: 'bar/path', scheme: 'foo' }, foo); - provideSuggestionItems(model, new Position(1, 1), new CompletionOptions(undefined, undefined, new Set().add(foo))).then(items => { + provideSuggestionItems(model, new Position(1, 1), new CompletionOptions(undefined, undefined, new Set().add(foo))).then(({ items }) => { registration.dispose(); assert.equal(items.length, 1); assert.ok(items[0].provider === foo); }); }); + + test('Ctrl+space completions stopped working with the latest Insiders, #97650', async function () { + + + const foo = new class implements CompletionItemProvider { + + triggerCharacters = []; + + provideCompletionItems() { + return { + suggestions: [{ + label: 'one', + kind: CompletionItemKind.Class, + insertText: 'one', + range: { + insert: new Range(0, 0, 0, 0), + replace: new Range(0, 0, 0, 10) + } + }, { + label: 'two', + kind: CompletionItemKind.Class, + insertText: 'two', + range: { + insert: new Range(0, 0, 0, 0), + replace: new Range(0, 1, 0, 10) + } + }] + }; + } + }; + + const registration = CompletionProviderRegistry.register({ pattern: 'bar/path', scheme: 'foo' }, foo); + const { items } = await provideSuggestionItems(model, new Position(0, 0), new CompletionOptions(undefined, undefined, new Set().add(foo))); + registration.dispose(); + + assert.equal(items.length, 2); + const [a, b] = items; + + assert.equal(a.completion.label, 'one'); + assert.equal(a.isInvalid, false); + assert.equal(b.completion.label, 'two'); + assert.equal(b.isInvalid, true); + }); }); diff --git a/src/vs/editor/contrib/suggest/test/suggestController.test.ts b/src/vs/editor/contrib/suggest/test/suggestController.test.ts index 06c95543ec3..ffeccaf1ba3 100644 --- a/src/vs/editor/contrib/suggest/test/suggestController.test.ts +++ b/src/vs/editor/contrib/suggest/test/suggestController.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { SuggestController } from 'vs/editor/contrib/suggest/suggestController'; -import { createTestCodeEditor, TestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { createTestCodeEditor, ITestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { TextModel } from 'vs/editor/common/model/textModel'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -24,13 +24,16 @@ import { Event } from 'vs/base/common/event'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; import { IMenuService, IMenu } from 'vs/platform/actions/common/actions'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; +import { Range } from 'vs/editor/common/core/range'; +import { timeout } from 'vs/base/common/async'; +import { NullLogService, ILogService } from 'vs/platform/log/common/log'; suite('SuggestController', function () { const disposables = new DisposableStore(); let controller: SuggestController; - let editor: TestCodeEditor; + let editor: ITestCodeEditor; let model: TextModel; setup(function () { @@ -38,6 +41,7 @@ suite('SuggestController', function () { const serviceCollection = new ServiceCollection( [ITelemetryService, NullTelemetryService], + [ILogService, new NullLogService()], [IStorageService, new InMemoryStorageService()], [IKeybindingService, new MockKeybindingService()], [IEditorWorkerService, new class extends mock() { @@ -102,4 +106,312 @@ suite('SuggestController', function () { assert.equal(editor.getValue(), ' let name = foo'); }); + + test('use additionalTextEdits sync when possible', async function () { + + disposables.add(CompletionProviderRegistry.register({ scheme: 'test-ctrl' }, { + provideCompletionItems(doc, pos) { + return { + suggestions: [{ + kind: CompletionItemKind.Snippet, + label: 'let', + insertText: 'hello', + range: Range.fromPositions(pos), + additionalTextEdits: [{ + text: 'I came sync', + range: { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 } + }] + }] + }; + }, + async resolveCompletionItem(item) { + return item; + } + })); + + editor.setValue('hello\nhallo'); + editor.setSelection(new Selection(2, 6, 2, 6)); + + // trigger + let p1 = Event.toPromise(controller.model.onDidSuggest); + controller.triggerSuggest(); + await p1; + + // + let p2 = Event.toPromise(controller.model.onDidCancel); + controller.acceptSelectedSuggestion(false, false); + await p2; + + // insertText happens sync! + assert.equal(editor.getValue(), 'I came synchello\nhallohello'); + }); + + test('resolve additionalTextEdits async when needed', async function () { + + let resolveCallCount = 0; + + disposables.add(CompletionProviderRegistry.register({ scheme: 'test-ctrl' }, { + provideCompletionItems(doc, pos) { + return { + suggestions: [{ + kind: CompletionItemKind.Snippet, + label: 'let', + insertText: 'hello', + range: Range.fromPositions(pos) + }] + }; + }, + async resolveCompletionItem(item) { + resolveCallCount += 1; + await timeout(10); + item.additionalTextEdits = [{ + text: 'I came late', + range: { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 } + }]; + return item; + } + })); + + editor.setValue('hello\nhallo'); + editor.setSelection(new Selection(2, 6, 2, 6)); + + // trigger + let p1 = Event.toPromise(controller.model.onDidSuggest); + controller.triggerSuggest(); + await p1; + + // + let p2 = Event.toPromise(controller.model.onDidCancel); + controller.acceptSelectedSuggestion(false, false); + await p2; + + // insertText happens sync! + assert.equal(editor.getValue(), 'hello\nhallohello'); + assert.equal(resolveCallCount, 1); + + // additional edits happened after a litte wait + await timeout(20); + assert.equal(editor.getValue(), 'I came latehello\nhallohello'); + + // single undo stop + editor.getModel()?.undo(); + assert.equal(editor.getValue(), 'hello\nhallo'); + }); + + test('resolve additionalTextEdits async when needed (typing)', async function () { + + let resolveCallCount = 0; + let resolve: Function = () => { }; + disposables.add(CompletionProviderRegistry.register({ scheme: 'test-ctrl' }, { + provideCompletionItems(doc, pos) { + return { + suggestions: [{ + kind: CompletionItemKind.Snippet, + label: 'let', + insertText: 'hello', + range: Range.fromPositions(pos) + }] + }; + }, + async resolveCompletionItem(item) { + resolveCallCount += 1; + await new Promise(_resolve => resolve = _resolve); + item.additionalTextEdits = [{ + text: 'I came late', + range: { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 } + }]; + return item; + } + })); + + editor.setValue('hello\nhallo'); + editor.setSelection(new Selection(2, 6, 2, 6)); + + // trigger + let p1 = Event.toPromise(controller.model.onDidSuggest); + controller.triggerSuggest(); + await p1; + + // + let p2 = Event.toPromise(controller.model.onDidCancel); + controller.acceptSelectedSuggestion(false, false); + await p2; + + // insertText happens sync! + assert.equal(editor.getValue(), 'hello\nhallohello'); + assert.equal(resolveCallCount, 1); + + // additional edits happened after a litte wait + assert.ok(editor.getSelection()?.equalsSelection(new Selection(2, 11, 2, 11))); + editor.trigger('test', 'type', { text: 'TYPING' }); + + assert.equal(editor.getValue(), 'hello\nhallohelloTYPING'); + + resolve(); + await timeout(10); + assert.equal(editor.getValue(), 'I came latehello\nhallohelloTYPING'); + assert.ok(editor.getSelection()?.equalsSelection(new Selection(2, 17, 2, 17))); + }); + + // additional edit come late and are AFTER the selection -> cancel + test('resolve additionalTextEdits async when needed (simple conflict)', async function () { + + let resolveCallCount = 0; + let resolve: Function = () => { }; + disposables.add(CompletionProviderRegistry.register({ scheme: 'test-ctrl' }, { + provideCompletionItems(doc, pos) { + return { + suggestions: [{ + kind: CompletionItemKind.Snippet, + label: 'let', + insertText: 'hello', + range: Range.fromPositions(pos) + }] + }; + }, + async resolveCompletionItem(item) { + resolveCallCount += 1; + await new Promise(_resolve => resolve = _resolve); + item.additionalTextEdits = [{ + text: 'I came late', + range: { startLineNumber: 1, startColumn: 6, endLineNumber: 1, endColumn: 6 } + }]; + return item; + } + })); + + editor.setValue(''); + editor.setSelection(new Selection(1, 1, 1, 1)); + + // trigger + let p1 = Event.toPromise(controller.model.onDidSuggest); + controller.triggerSuggest(); + await p1; + + // + let p2 = Event.toPromise(controller.model.onDidCancel); + controller.acceptSelectedSuggestion(false, false); + await p2; + + // insertText happens sync! + assert.equal(editor.getValue(), 'hello'); + assert.equal(resolveCallCount, 1); + + resolve(); + await timeout(10); + assert.equal(editor.getValue(), 'hello'); + }); + + // additional edit come late and are AFTER the position at which the user typed -> cancelled + test('resolve additionalTextEdits async when needed (conflict)', async function () { + + let resolveCallCount = 0; + let resolve: Function = () => { }; + disposables.add(CompletionProviderRegistry.register({ scheme: 'test-ctrl' }, { + provideCompletionItems(doc, pos) { + return { + suggestions: [{ + kind: CompletionItemKind.Snippet, + label: 'let', + insertText: 'hello', + range: Range.fromPositions(pos) + }] + }; + }, + async resolveCompletionItem(item) { + resolveCallCount += 1; + await new Promise(_resolve => resolve = _resolve); + item.additionalTextEdits = [{ + text: 'I came late', + range: { startLineNumber: 1, startColumn: 2, endLineNumber: 1, endColumn: 2 } + }]; + return item; + } + })); + + editor.setValue('hello\nhallo'); + editor.setSelection(new Selection(2, 6, 2, 6)); + + // trigger + let p1 = Event.toPromise(controller.model.onDidSuggest); + controller.triggerSuggest(); + await p1; + + // + let p2 = Event.toPromise(controller.model.onDidCancel); + controller.acceptSelectedSuggestion(false, false); + await p2; + + // insertText happens sync! + assert.equal(editor.getValue(), 'hello\nhallohello'); + assert.equal(resolveCallCount, 1); + + // additional edits happened after a litte wait + editor.setSelection(new Selection(1, 1, 1, 1)); + editor.trigger('test', 'type', { text: 'TYPING' }); + + assert.equal(editor.getValue(), 'TYPINGhello\nhallohello'); + + resolve(); + await timeout(10); + assert.equal(editor.getValue(), 'TYPINGhello\nhallohello'); + assert.ok(editor.getSelection()?.equalsSelection(new Selection(1, 7, 1, 7))); + }); + + test('resolve additionalTextEdits async when needed (cancel)', async function () { + + let resolve: Function[] = []; + disposables.add(CompletionProviderRegistry.register({ scheme: 'test-ctrl' }, { + provideCompletionItems(doc, pos) { + return { + suggestions: [{ + kind: CompletionItemKind.Snippet, + label: 'let', + insertText: 'hello', + range: Range.fromPositions(pos) + }, { + kind: CompletionItemKind.Snippet, + label: 'let', + insertText: 'hallo', + range: Range.fromPositions(pos) + }] + }; + }, + async resolveCompletionItem(item) { + await new Promise(_resolve => resolve.push(_resolve)); + item.additionalTextEdits = [{ + text: 'additionalTextEdits', + range: { startLineNumber: 1, startColumn: 2, endLineNumber: 1, endColumn: 2 } + }]; + return item; + } + })); + + editor.setValue('abc'); + editor.setSelection(new Selection(1, 1, 1, 1)); + + // trigger + let p1 = Event.toPromise(controller.model.onDidSuggest); + controller.triggerSuggest(); + await p1; + + // + let p2 = Event.toPromise(controller.model.onDidCancel); + controller.acceptSelectedSuggestion(true, false); + await p2; + + // insertText happens sync! + assert.equal(editor.getValue(), 'helloabc'); + + // next + controller.acceptNextSuggestion(); + + // resolve additional edits (MUST be cancelled) + resolve.forEach(fn => fn); + resolve.length = 0; + await timeout(10); + + // next suggestion used + assert.equal(editor.getValue(), 'halloabc'); + }); }); diff --git a/src/vs/editor/contrib/suggest/test/suggestModel.test.ts b/src/vs/editor/contrib/suggest/test/suggestModel.test.ts index 778ed2ece6f..edf5997475f 100644 --- a/src/vs/editor/contrib/suggest/test/suggestModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/suggestModel.test.ts @@ -21,7 +21,7 @@ import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2 import { SuggestController } from 'vs/editor/contrib/suggest/suggestController'; import { LineContext, SuggestModel } from 'vs/editor/contrib/suggest/suggestModel'; import { ISelectedSuggestion } from 'vs/editor/contrib/suggest/suggestWidget'; -import { TestCodeEditor, createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { ITestCodeEditor, createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage'; @@ -33,6 +33,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; export interface Ctor { new(): T; @@ -43,7 +44,7 @@ export function mock(): Ctor { } -function createMockEditor(model: TextModel): TestCodeEditor { +function createMockEditor(model: TextModel): ITestCodeEditor { let editor = createTestCodeEditor({ model: model, serviceCollection: new ServiceCollection( @@ -51,7 +52,7 @@ function createMockEditor(model: TextModel): TestCodeEditor { [IStorageService, new InMemoryStorageService()], [IKeybindingService, new MockKeybindingService()], [ISuggestMemoryService, new class implements ISuggestMemoryService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; memorize(): void { } select(): number { @@ -192,16 +193,23 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { disposables.push(model); }); - function withOracle(callback: (model: SuggestModel, editor: TestCodeEditor) => any): Promise { + function withOracle(callback: (model: SuggestModel, editor: ITestCodeEditor) => any): Promise { return new Promise((resolve, reject) => { const editor = createMockEditor(model); - const oracle = new SuggestModel(editor, new class extends mock() { - computeWordRanges() { - return Promise.resolve({}); + const oracle = new SuggestModel( + editor, + new class extends mock() { + computeWordRanges() { + return Promise.resolve({}); + } + }, + new class extends mock() { + readText() { + return Promise.resolve('CLIPPY'); + } } - - }); + ); disposables.push(oracle, editor); try { diff --git a/src/vs/editor/contrib/suggest/test/wordDistance.test.ts b/src/vs/editor/contrib/suggest/test/wordDistance.test.ts index 7ff65873f98..ede0459b22c 100644 --- a/src/vs/editor/contrib/suggest/test/wordDistance.test.ts +++ b/src/vs/editor/contrib/suggest/test/wordDistance.test.ts @@ -101,7 +101,7 @@ suite('suggest, word distance', function () { return; } }; - return new CompletionItem(position, suggestion, container, provider, undefined!); + return new CompletionItem(position, suggestion, container, provider); } test('Suggest locality bonus can boost current word #90515', function () { diff --git a/src/vs/editor/contrib/unusualLineTerminators/unusualLineTerminators.ts b/src/vs/editor/contrib/unusualLineTerminators/unusualLineTerminators.ts new file mode 100644 index 00000000000..2890a2852a2 --- /dev/null +++ b/src/vs/editor/contrib/unusualLineTerminators/unusualLineTerminators.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { Disposable } from 'vs/base/common/lifecycle'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { ITextModel } from 'vs/editor/common/model'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; + +const ignoreUnusualLineTerminators = 'ignoreUnusualLineTerminators'; + +function writeIgnoreState(codeEditorService: ICodeEditorService, model: ITextModel, state: boolean): void { + codeEditorService.setModelProperty(model.uri, ignoreUnusualLineTerminators, state); +} + +function readIgnoreState(codeEditorService: ICodeEditorService, model: ITextModel): boolean | undefined { + return codeEditorService.getModelProperty(model.uri, ignoreUnusualLineTerminators); +} + +class UnusualLineTerminatorsDetector extends Disposable implements IEditorContribution { + + public static readonly ID = 'editor.contrib.unusualLineTerminatorsDetector'; + + private _config: 'off' | 'prompt' | 'auto'; + + constructor( + private readonly _editor: ICodeEditor, + @IDialogService private readonly _dialogService: IDialogService, + @ICodeEditorService private readonly _codeEditorService: ICodeEditorService + ) { + super(); + + this._config = this._editor.getOption(EditorOption.unusualLineTerminators); + this._register(this._editor.onDidChangeConfiguration((e) => { + if (e.hasChanged(EditorOption.unusualLineTerminators)) { + this._config = this._editor.getOption(EditorOption.unusualLineTerminators); + this._checkForUnusualLineTerminators(); + } + })); + + this._register(this._editor.onDidChangeModel(() => { + this._checkForUnusualLineTerminators(); + })); + + this._register(this._editor.onDidChangeModelContent((e) => { + if (e.isUndoing) { + // skip checking in case of undoing + return; + } + this._checkForUnusualLineTerminators(); + })); + } + + private async _checkForUnusualLineTerminators(): Promise { + if (this._config === 'off') { + return; + } + if (!this._editor.hasModel()) { + return; + } + const model = this._editor.getModel(); + if (!model.mightContainUnusualLineTerminators()) { + return; + } + const ignoreState = readIgnoreState(this._codeEditorService, model); + if (ignoreState === true) { + // this model should be ignored + return; + } + if (this._editor.getOption(EditorOption.readOnly)) { + // read only editor => sorry! + return; + } + + if (this._config === 'auto') { + // just do it! + model.removeUnusualLineTerminators(this._editor.getSelections()); + return; + } + + const result = await this._dialogService.confirm({ + title: nls.localize('unusualLineTerminators.title', "Unusual Line Terminators"), + message: nls.localize('unusualLineTerminators.message', "Detected unusual line terminators"), + detail: nls.localize('unusualLineTerminators.detail', "This file contains one or more unusual line terminator characters, like Line Separator (LS) or Paragraph Separator (PS).\n\nIt is recommended to remove them from the file. This can be configured via `editor.unusualLineTerminators`."), + primaryButton: nls.localize('unusualLineTerminators.fix', "Fix this file"), + secondaryButton: nls.localize('unusualLineTerminators.ignore', "Ignore problem for this file") + }); + + if (!result.confirmed) { + // this model should be ignored + writeIgnoreState(this._codeEditorService, model, true); + return; + } + + model.removeUnusualLineTerminators(this._editor.getSelections()); + } +} + +registerEditorContribution(UnusualLineTerminatorsDetector.ID, UnusualLineTerminatorsDetector); diff --git a/src/vs/editor/contrib/wordOperations/test/wordOperations.test.ts b/src/vs/editor/contrib/wordOperations/test/wordOperations.test.ts index 2b1d99f2aa9..eadfa9ed3d8 100644 --- a/src/vs/editor/contrib/wordOperations/test/wordOperations.test.ts +++ b/src/vs/editor/contrib/wordOperations/test/wordOperations.test.ts @@ -11,9 +11,8 @@ import { Selection } from 'vs/editor/common/core/selection'; import { deserializePipePositions, serializePipePositions, testRepeatedActionAndExtractPositions } from 'vs/editor/contrib/wordOperations/test/wordTestUtils'; import { CursorWordEndLeft, CursorWordEndLeftSelect, CursorWordEndRight, CursorWordEndRightSelect, CursorWordLeft, CursorWordLeftSelect, CursorWordRight, CursorWordRightSelect, CursorWordStartLeft, CursorWordStartLeftSelect, CursorWordStartRight, CursorWordStartRightSelect, DeleteWordEndLeft, DeleteWordEndRight, DeleteWordLeft, DeleteWordRight, DeleteWordStartLeft, DeleteWordStartRight, CursorWordAccessibilityLeft, CursorWordAccessibilityLeftSelect, CursorWordAccessibilityRight, CursorWordAccessibilityRightSelect } from 'vs/editor/contrib/wordOperations/wordOperations'; import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; -import { Handler } from 'vs/editor/common/editorCommon'; -import { Cursor } from 'vs/editor/common/controller/cursor'; import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands'; +import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; suite('WordOperations', () => { @@ -113,7 +112,7 @@ suite('WordOperations', () => { ' Third LineđŸļ', '', '1', - ], {}, (editor, _) => { + ], {}, (editor) => { editor.setPosition(new Position(5, 2)); cursorWordLeft(editor, true); assert.deepEqual(editor.getSelection(), new Selection(5, 2, 5, 1)); @@ -168,7 +167,7 @@ suite('WordOperations', () => { test('cursorWordStartLeft', () => { // This is the behaviour observed in Visual Studio, please do not touch test - const EXPECTED = ['| |/* |Just |some |more |text |a|+= |3 |+|5|-|3 |+ |7 |*/| '].join('\n'); + const EXPECTED = ['| |/* |Just |some |more |text |a|+= |3 |+|5|-|3 |+ |7 |*/ '].join('\n'); const [text,] = deserializePipePositions(EXPECTED); const actualStops = testRepeatedActionAndExtractPositions( text, @@ -197,23 +196,19 @@ suite('WordOperations', () => { }); test('issue #51275 - cursorWordStartLeft does not push undo/redo stack element', () => { - function cursorCommand(cursor: Cursor, command: string, extraData?: any, overwriteSource?: string) { - cursor.trigger(overwriteSource || 'tests', command, extraData); - } - - function type(cursor: Cursor, text: string) { + function type(viewModel: ViewModel, text: string) { for (let i = 0; i < text.length; i++) { - cursorCommand(cursor, Handler.Type, { text: text.charAt(i) }, 'keyboard'); + viewModel.type(text.charAt(i), 'keyboard'); } } - withTestCodeEditor('', {}, (editor, cursor) => { - type(cursor, 'foo bar baz'); + withTestCodeEditor('', {}, (editor, viewModel) => { + type(viewModel, 'foo bar baz'); assert.equal(editor.getValue(), 'foo bar baz'); cursorWordStartLeft(editor); cursorWordStartLeft(editor); - type(cursor, 'q'); + type(viewModel, 'q'); assert.equal(editor.getValue(), 'foo qbar baz'); diff --git a/src/vs/editor/contrib/wordOperations/test/wordTestUtils.ts b/src/vs/editor/contrib/wordOperations/test/wordTestUtils.ts index 68a6a2f6738..7ecf61a6925 100644 --- a/src/vs/editor/contrib/wordOperations/test/wordTestUtils.ts +++ b/src/vs/editor/contrib/wordOperations/test/wordTestUtils.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Position } from 'vs/editor/common/core/position'; -import { TestCodeEditor, withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { ITestCodeEditor, withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; export function deserializePipePositions(text: string): [string, Position[]] { let resultText = ''; @@ -58,9 +58,9 @@ export function serializePipePositions(text: string, positions: Position[]): str return resultText; } -export function testRepeatedActionAndExtractPositions(text: string, initialPosition: Position, action: (editor: TestCodeEditor) => void, record: (editor: TestCodeEditor) => Position, stopCondition: (editor: TestCodeEditor) => boolean): Position[] { +export function testRepeatedActionAndExtractPositions(text: string, initialPosition: Position, action: (editor: ITestCodeEditor) => void, record: (editor: ITestCodeEditor) => Position, stopCondition: (editor: ITestCodeEditor) => boolean): Position[] { let actualStops: Position[] = []; - withTestCodeEditor(text, {}, (editor, _) => { + withTestCodeEditor(text, {}, (editor) => { editor.setPosition(initialPosition); while (true) { action(editor); diff --git a/src/vs/editor/contrib/wordOperations/wordOperations.ts b/src/vs/editor/contrib/wordOperations/wordOperations.ts index ae5429ed0cc..791e831aab0 100644 --- a/src/vs/editor/contrib/wordOperations/wordOperations.ts +++ b/src/vs/editor/contrib/wordOperations/wordOperations.ts @@ -53,7 +53,7 @@ export abstract class MoveWordCommand extends EditorCommand { }); model.pushStackElement(); - editor._getCursors().setStates('moveWordCommand', CursorChangeReason.NotSet, result.map(r => CursorState.fromModelSelection(r))); + editor._getViewModel().setCursorStates('moveWordCommand', CursorChangeReason.NotSet, result.map(r => CursorState.fromModelSelection(r))); if (result.length === 1) { const pos = new Position(result[0].positionLineNumber, result[0].positionColumn); editor.revealPosition(pos, ScrollType.Smooth); diff --git a/src/vs/editor/editor.all.ts b/src/vs/editor/editor.all.ts index 89eb4edd67f..a591facd8b7 100644 --- a/src/vs/editor/editor.all.ts +++ b/src/vs/editor/editor.all.ts @@ -7,6 +7,7 @@ import 'vs/editor/browser/controller/coreCommands'; import 'vs/editor/browser/widget/codeEditorWidget'; import 'vs/editor/browser/widget/diffEditorWidget'; import 'vs/editor/browser/widget/diffNavigator'; +import 'vs/editor/contrib/anchorSelect/anchorSelect'; import 'vs/editor/contrib/bracketMatching/bracketMatching'; import 'vs/editor/contrib/caretOperations/caretOperations'; import 'vs/editor/contrib/caretOperations/transpose'; @@ -40,6 +41,7 @@ import 'vs/editor/contrib/snippet/snippetController2'; import 'vs/editor/contrib/suggest/suggestController'; import 'vs/editor/contrib/tokenization/tokenization'; import 'vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode'; +import 'vs/editor/contrib/unusualLineTerminators/unusualLineTerminators'; import 'vs/editor/contrib/viewportSemanticTokens/viewportSemanticTokens'; import 'vs/editor/contrib/wordHighlighter/wordHighlighter'; import 'vs/editor/contrib/wordOperations/wordOperations'; diff --git a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.css b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.css index 1685c5b9833..b8bbbd662e6 100644 --- a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.css +++ b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.css @@ -17,7 +17,7 @@ } .monaco-editor .tokens-inspect-widget .tm-token { - font-family: monospace; + font-family: var(--monaco-monospace-font); } .monaco-editor .tokens-inspect-widget .tm-token-length { @@ -31,10 +31,10 @@ } .monaco-editor .tokens-inspect-widget .tm-metadata-value { - font-family: monospace; + font-family: var(--monaco-monospace-font); text-align: right; } .monaco-editor .tokens-inspect-widget .tm-token-type { - font-family: monospace; + font-family: var(--monaco-monospace-font); } diff --git a/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputServiceImpl.ts b/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputServiceImpl.ts index 4a7c020fae7..099b1196362 100644 --- a/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputServiceImpl.ts +++ b/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputServiceImpl.ts @@ -52,7 +52,7 @@ export class EditorScopedQuickInputServiceImpl extends QuickInputService { export class StandaloneQuickInputServiceImpl implements IQuickInputService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private mapEditorToService = new Map(); private get activeService(): IQuickInputService { diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index ee0bfcecc8d..9051fb2cc78 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -108,12 +108,11 @@ function withTypedEditor(widget: IEditor, codeEditorCallback: (editor: ICodeE export class SimpleEditorModelResolverService implements ITextModelService { public _serviceBrand: undefined; - private readonly modelService: IModelService | undefined; private editor?: IEditor; - constructor(modelService: IModelService | undefined) { - this.modelService = modelService; - } + constructor( + @IModelService private readonly modelService: IModelService + ) { } public setEditor(editor: IEditor): void { this.editor = editor; @@ -141,12 +140,12 @@ export class SimpleEditorModelResolverService implements ITextModelService { }; } - public hasTextModelContentProvider(scheme: string): boolean { + public canHandleResource(resource: URI): boolean { return false; } private findModel(editor: ICodeEditor, resource: URI): ITextModel | null { - let model = this.modelService ? this.modelService.getModel(resource) : editor.getModel(); + let model = this.modelService.getModel(resource); if (model && model.uri.toString() !== resource.toString()) { return null; } @@ -156,7 +155,7 @@ export class SimpleEditorModelResolverService implements ITextModelService { } export class SimpleEditorProgressService implements IEditorProgressService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private static NULL_PROGRESS_RUNNER: IProgressRunner = { done: () => { }, @@ -252,7 +251,7 @@ export class SimpleNotificationService implements INotificationService { } export class StandaloneCommandService implements ICommandService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _instantiationService: IInstantiationService; @@ -421,7 +420,7 @@ function isConfigurationOverrides(thing: any): thing is IConfigurationOverrides export class SimpleConfigurationService implements IConfigurationService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _onDidChangeConfiguration = new Emitter(); public readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; @@ -499,7 +498,7 @@ export class SimpleConfigurationService implements IConfigurationService { export class SimpleResourceConfigurationService implements ITextResourceConfigurationService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _onDidChangeConfiguration = new Emitter(); public readonly onDidChangeConfiguration = this._onDidChangeConfiguration.event; @@ -528,7 +527,7 @@ export class SimpleResourceConfigurationService implements ITextResourceConfigur export class SimpleResourcePropertiesService implements ITextResourcePropertiesService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @IConfigurationService private readonly configurationService: IConfigurationService, @@ -545,13 +544,17 @@ export class SimpleResourcePropertiesService implements ITextResourcePropertiesS } export class StandaloneTelemetryService implements ITelemetryService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; public isOptedIn = false; + public sendErrorTelemetry = false; public setEnabled(value: boolean): void { } + public setExperimentProperty(name: string, value: string): void { + } + public publicLog(eventName: string, data?: any): Promise { return Promise.resolve(undefined); } @@ -648,7 +651,7 @@ export function applyConfigurationValues(configurationService: IConfigurationSer } export class SimpleBulkEditService implements IBulkEditService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(private readonly _modelService: IModelService) { // @@ -703,7 +706,7 @@ export class SimpleBulkEditService implements IBulkEditService { export class SimpleUriLabelService implements ILabelService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; public readonly onDidChangeFormatters: Event = Event.None; @@ -736,7 +739,7 @@ export class SimpleUriLabelService implements ILabelService { } export class SimpleLayoutService implements ILayoutService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; public onLayout = Event.None; diff --git a/src/vs/editor/standalone/browser/standalone-tokens.css b/src/vs/editor/standalone/browser/standalone-tokens.css index 4d173457365..e97572e0575 100644 --- a/src/vs/editor/standalone/browser/standalone-tokens.css +++ b/src/vs/editor/standalone/browser/standalone-tokens.css @@ -4,9 +4,10 @@ *--------------------------------------------------------------------------------------------*/ -/* Default standalone editor font */ +/* Default standalone editor fonts */ .monaco-editor { - font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif; + font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", system-ui, "Ubuntu", "Droid Sans", sans-serif; + --monaco-monospace-font: "SF Mono", Monaco, Menlo, Consolas, "Ubuntu Mono", "Liberation Mono", "DejaVu Sans Mono", "Courier New", monospace; } .monaco-menu .monaco-action-bar.vertical .action-item .action-menu-item:focus .action-label { @@ -18,7 +19,7 @@ stroke-width: 1.2px; } -.monaco-editor-hover p { +.monaco-hover p { margin: 0; } diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index 6ff1f85c8f7..e06bd35a8d0 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -5,11 +5,11 @@ import * as aria from 'vs/base/browser/ui/aria/aria'; import { Disposable, IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, IDiffEditor, IEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; -import { IDiffEditorOptions, IEditorOptions, IEditorConstructionOptions } from 'vs/editor/common/config/editorOptions'; +import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { InternalEditorAction } from 'vs/editor/common/editorAction'; import { IModelChangedEvent } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; @@ -115,9 +115,12 @@ export interface IGlobalEditorOptions { wordBasedSuggestions?: boolean; /** * Controls whether the semanticHighlighting is shown for the languages that support it. - * Defaults to true. + * true: semanticHighlighting is enabled for all themes + * false: semanticHighlighting is disabled for all themes + * 'configuredByTheme': semanticHighlighting is controlled by the current color theme's semanticHighlighting setting. + * Defaults to 'byTheme'. */ - 'semanticHighlighting.enabled'?: boolean; + 'semanticHighlighting.enabled'?: true | false | 'configuredByTheme'; /** * Keep peek editors open even when double clicking their content or when hitting `Escape`. * Defaults to false. diff --git a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts index 3d4db53c943..9e3f3ac0c9c 100644 --- a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts +++ b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts @@ -48,6 +48,10 @@ class StandaloneTheme implements IStandaloneTheme { this._tokenTheme = null; } + public get label(): string { + return this.themeName; + } + public get base(): string { return this.themeData.base; } @@ -168,7 +172,7 @@ function newBuiltInTheme(builtinTheme: BuiltinTheme): StandaloneTheme { export class StandaloneThemeServiceImpl extends Disposable implements IStandaloneThemeService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _onColorThemeChange = this._register(new Emitter()); public readonly onDidColorThemeChange = this._onColorThemeChange.event; diff --git a/src/vs/editor/standalone/common/monarch/monarchCommon.ts b/src/vs/editor/standalone/common/monarch/monarchCommon.ts index 757e208bade..6dbf14dc803 100644 --- a/src/vs/editor/standalone/common/monarch/monarchCommon.ts +++ b/src/vs/editor/standalone/common/monarch/monarchCommon.ts @@ -24,6 +24,7 @@ export interface ILexerMin { languageId: string; noThrow: boolean; ignoreCase: boolean; + unicode: boolean; usesEmbedded: boolean; defaultToken: string; stateNames: { [stateName: string]: any; }; @@ -34,6 +35,7 @@ export interface ILexer extends ILexerMin { maxStack: number; start: string | null; ignoreCase: boolean; + unicode: boolean; tokenPostfix: string; tokenizer: { [stateName: string]: IRule[]; }; diff --git a/src/vs/editor/standalone/common/monarch/monarchCompile.ts b/src/vs/editor/standalone/common/monarch/monarchCompile.ts index 2c98c6ba429..289d045aba0 100644 --- a/src/vs/editor/standalone/common/monarch/monarchCompile.ts +++ b/src/vs/editor/standalone/common/monarch/monarchCompile.ts @@ -79,7 +79,7 @@ function createKeywordMatcher(arr: string[], caseInsensitive: boolean = false): // Lexer helpers /** - * Compiles a regular expression string, adding the 'i' flag if 'ignoreCase' is set. + * Compiles a regular expression string, adding the 'i' flag if 'ignoreCase' is set, and the 'u' flag if 'unicode' is set. * Also replaces @\w+ or sequences with the content of the specified attribute */ function compileRegExp(lexer: monarchCommon.ILexerMin, str: string): RegExp { @@ -103,7 +103,8 @@ function compileRegExp(lexer: monarchCommon.ILexerMin, str: string): RegExp { }); } - return new RegExp(str, (lexer.ignoreCase ? 'i' : '')); + let flags = (lexer.ignoreCase ? 'i' : '') + (lexer.unicode ? 'u' : ''); + return new RegExp(str, flags); } /** @@ -400,6 +401,7 @@ export function compile(languageId: string, json: IMonarchLanguage): monarchComm // Set standard fields: be defensive about types lexer.start = (typeof json.start === 'string' ? json.start : null); lexer.ignoreCase = bool(json.ignoreCase, false); + lexer.unicode = bool(json.unicode, false); lexer.tokenPostfix = string(json.tokenPostfix, '.' + lexer.languageId); lexer.defaultToken = string(json.defaultToken, 'source'); @@ -410,6 +412,7 @@ export function compile(languageId: string, json: IMonarchLanguage): monarchComm let lexerMin: monarchCommon.ILexerMin = json; lexerMin.languageId = languageId; lexerMin.ignoreCase = lexer.ignoreCase; + lexerMin.unicode = lexer.unicode; lexerMin.noThrow = lexer.noThrow; lexerMin.usesEmbedded = lexer.usesEmbedded; lexerMin.stateNames = json.tokenizer; diff --git a/src/vs/editor/standalone/common/monarch/monarchLexer.ts b/src/vs/editor/standalone/common/monarch/monarchLexer.ts index e4ddf8778d4..f120b5383d7 100644 --- a/src/vs/editor/standalone/common/monarch/monarchLexer.ts +++ b/src/vs/editor/standalone/common/monarch/monarchLexer.ts @@ -497,7 +497,8 @@ export class MonarchTokenizer implements modes.ITokenizationSupport { let regex = rule.regex; let regexSource = rule.regex.source; if (regexSource.substr(0, 4) === '^(?:' && regexSource.substr(regexSource.length - 1, 1) === ')') { - regex = new RegExp(regexSource.substr(4, regexSource.length - 5), regex.ignoreCase ? 'i' : ''); + let flags = (regex.ignoreCase ? 'i' : '') + (regex.unicode ? 'u' : ''); + regex = new RegExp(regexSource.substr(4, regexSource.length - 5), flags); } let result = line.search(regex); diff --git a/src/vs/editor/standalone/common/monarch/monarchTypes.ts b/src/vs/editor/standalone/common/monarch/monarchTypes.ts index cd0fd107992..19936be8dc8 100644 --- a/src/vs/editor/standalone/common/monarch/monarchTypes.ts +++ b/src/vs/editor/standalone/common/monarch/monarchTypes.ts @@ -21,6 +21,10 @@ export interface IMonarchLanguage { * is the language case insensitive? */ ignoreCase?: boolean; + /** + * is the language unicode-aware? (i.e., /\u{1D306}/) + */ + unicode?: boolean; /** * if no match in the tokenizer assign this token class (default 'source') */ diff --git a/src/vs/editor/standalone/common/standaloneThemeService.ts b/src/vs/editor/standalone/common/standaloneThemeService.ts index 6cef079a639..c70a713dd08 100644 --- a/src/vs/editor/standalone/common/standaloneThemeService.ts +++ b/src/vs/editor/standalone/common/standaloneThemeService.ts @@ -26,7 +26,7 @@ export interface IStandaloneTheme extends IColorTheme { } export interface IStandaloneThemeService extends IThemeService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; setTheme(themeName: string): string; diff --git a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts index 07bfa4b14bb..282422dcbd2 100644 --- a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts +++ b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts @@ -33,7 +33,7 @@ suite('TokenizationSupport2Adapter', () => { } class MockThemeService implements IStandaloneThemeService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; public setTheme(themeName: string): string { throw new Error('Not implemented'); } @@ -42,6 +42,8 @@ suite('TokenizationSupport2Adapter', () => { } public getColorTheme(): IStandaloneTheme { return { + label: 'mock', + tokenTheme: new MockTokenTheme(), themeName: LIGHT, diff --git a/src/vs/editor/test/browser/commands/sideEditing.test.ts b/src/vs/editor/test/browser/commands/sideEditing.test.ts index 49514eef350..be07a56821e 100644 --- a/src/vs/editor/test/browser/commands/sideEditing.test.ts +++ b/src/vs/editor/test/browser/commands/sideEditing.test.ts @@ -4,29 +4,24 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { Cursor } from 'vs/editor/common/controller/cursor'; 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 { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; -import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; -import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; -import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; -import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer'; function testCommand(lines: string[], selections: Selection[], edits: IIdentifiedSingleEditOperation[], expectedLines: string[], expectedSelections: Selection[]): void { - withTestCodeEditor(lines, {}, (editor, cursor) => { + withTestCodeEditor(lines, {}, (editor, viewModel) => { const model = editor.getModel()!; - cursor.setSelections('tests', selections); + viewModel.setSelections('tests', selections); model.applyEdits(edits); assert.deepEqual(model.getLinesContent(), expectedLines); - let actualSelections = cursor.getSelections(); + let actualSelections = viewModel.getSelections(); assert.deepEqual(actualSelections.map(s => s.toString()), expectedSelections.map(s => s.toString())); }); @@ -199,25 +194,16 @@ suite('SideEditing', () => { ]; function _runTest(selection: Selection, editRange: Range, editText: string, editForceMoveMarkers: boolean, expected: Selection, msg: string): void { - const model = createTextModel(LINES.join('\n')); - const config = new TestConfiguration({}); - const monospaceLineBreaksComputerFactory = MonospaceLineBreaksComputerFactory.create(config.options); - const viewModel = new ViewModel(0, config, model, monospaceLineBreaksComputerFactory, monospaceLineBreaksComputerFactory, null!); - const cursor = new Cursor(config, model, viewModel); - - cursor.setSelections('tests', [selection]); - model.applyEdits([{ - range: editRange, - text: editText, - forceMoveMarkers: editForceMoveMarkers - }]); - const actual = cursor.getSelection(); - assert.deepEqual(actual.toString(), expected.toString(), msg); - - cursor.dispose(); - viewModel.dispose(); - config.dispose(); - model.dispose(); + withTestCodeEditor(LINES.join('\n'), {}, (editor, viewModel) => { + viewModel.setSelections('tests', [selection]); + editor.getModel().applyEdits([{ + range: editRange, + text: editText, + forceMoveMarkers: editForceMoveMarkers + }]); + const actual = viewModel.getSelection(); + assert.deepEqual(actual.toString(), expected.toString(), msg); + }); } function runTest(selection: Range, editRange: Range, editText: string, expected: Selection[][]): void { diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index b37d17d3ce6..7f757690c16 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -6,117 +6,104 @@ import * as assert from 'assert'; import { CoreEditingCommands, CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { Cursor, CursorStateChangedEvent } from 'vs/editor/common/controller/cursor'; 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 { TokenizationResult2 } from 'vs/editor/common/core/token'; -import { Handler, ICommand, ICursorStateComputerData, IEditOperationBuilder, IConfiguration } from 'vs/editor/common/editorCommon'; +import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from 'vs/editor/common/editorCommon'; import { EndOfLinePreference, EndOfLineSequence, ITextModel } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import { IState, ITokenizationSupport, LanguageIdentifier, TokenizationRegistry } from 'vs/editor/common/modes'; import { IndentAction, IndentationRule } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; -import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; -import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { withTestCodeEditor, TestCodeEditorCreationOptions, ITestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { IRelaxedTextModelCreationOptions, createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; -import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; import { javascriptOnEnterRules } from 'vs/editor/test/common/modes/supports/javascriptOnEnterRules'; -import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer'; - -const H = Handler; +import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; +import { OutgoingViewModelEventKind } from 'vs/editor/common/viewModel/viewModelEventDispatcher'; // --------- utils -function cursorCommand(cursor: Cursor, command: string, extraData?: any, overwriteSource?: string) { - cursor.trigger(overwriteSource || 'tests', command, extraData); -} - -function cursorCommandAndTokenize(model: TextModel, cursor: Cursor, command: string, extraData?: any, overwriteSource?: string) { - cursor.trigger(overwriteSource || 'tests', command, extraData); - model.forceTokenization(model.getLineCount()); -} - -function moveTo(cursor: Cursor, lineNumber: number, column: number, inSelectionMode: boolean = false) { +function moveTo(editor: ITestCodeEditor, viewModel: ViewModel, lineNumber: number, column: number, inSelectionMode: boolean = false) { if (inSelectionMode) { - CoreNavigationCommands.MoveToSelect.runCoreEditorCommand(cursor, { + CoreNavigationCommands.MoveToSelect.runCoreEditorCommand(viewModel, { position: new Position(lineNumber, column) }); } else { - CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { + CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(lineNumber, column) }); } } -function moveLeft(cursor: Cursor, inSelectionMode: boolean = false) { +function moveLeft(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) { if (inSelectionMode) { - CoreNavigationCommands.CursorLeftSelect.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorLeftSelect.runCoreEditorCommand(viewModel, {}); } else { - CoreNavigationCommands.CursorLeft.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorLeft.runCoreEditorCommand(viewModel, {}); } } -function moveRight(cursor: Cursor, inSelectionMode: boolean = false) { +function moveRight(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) { if (inSelectionMode) { - CoreNavigationCommands.CursorRightSelect.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorRightSelect.runCoreEditorCommand(viewModel, {}); } else { - CoreNavigationCommands.CursorRight.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorRight.runCoreEditorCommand(viewModel, {}); } } -function moveDown(cursor: Cursor, inSelectionMode: boolean = false) { +function moveDown(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) { if (inSelectionMode) { - CoreNavigationCommands.CursorDownSelect.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorDownSelect.runCoreEditorCommand(viewModel, {}); } else { - CoreNavigationCommands.CursorDown.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorDown.runCoreEditorCommand(viewModel, {}); } } -function moveUp(cursor: Cursor, inSelectionMode: boolean = false) { +function moveUp(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) { if (inSelectionMode) { - CoreNavigationCommands.CursorUpSelect.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorUpSelect.runCoreEditorCommand(viewModel, {}); } else { - CoreNavigationCommands.CursorUp.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorUp.runCoreEditorCommand(viewModel, {}); } } -function moveToBeginningOfLine(cursor: Cursor, inSelectionMode: boolean = false) { +function moveToBeginningOfLine(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) { if (inSelectionMode) { - CoreNavigationCommands.CursorHomeSelect.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorHomeSelect.runCoreEditorCommand(viewModel, {}); } else { - CoreNavigationCommands.CursorHome.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorHome.runCoreEditorCommand(viewModel, {}); } } -function moveToEndOfLine(cursor: Cursor, inSelectionMode: boolean = false) { +function moveToEndOfLine(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) { if (inSelectionMode) { - CoreNavigationCommands.CursorEndSelect.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorEndSelect.runCoreEditorCommand(viewModel, {}); } else { - CoreNavigationCommands.CursorEnd.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorEnd.runCoreEditorCommand(viewModel, {}); } } -function moveToBeginningOfBuffer(cursor: Cursor, inSelectionMode: boolean = false) { +function moveToBeginningOfBuffer(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) { if (inSelectionMode) { - CoreNavigationCommands.CursorTopSelect.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorTopSelect.runCoreEditorCommand(viewModel, {}); } else { - CoreNavigationCommands.CursorTop.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorTop.runCoreEditorCommand(viewModel, {}); } } -function moveToEndOfBuffer(cursor: Cursor, inSelectionMode: boolean = false) { +function moveToEndOfBuffer(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) { if (inSelectionMode) { - CoreNavigationCommands.CursorBottomSelect.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorBottomSelect.runCoreEditorCommand(viewModel, {}); } else { - CoreNavigationCommands.CursorBottom.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorBottom.runCoreEditorCommand(viewModel, {}); } } -function assertCursor(cursor: Cursor, what: Position | Selection | Selection[]): void { +function assertCursor(viewModel: ViewModel, what: Position | Selection | Selection[]): void { let selections: Selection[]; if (what instanceof Position) { selections = [new Selection(what.lineNumber, what.column, what.lineNumber, what.column)]; @@ -125,17 +112,12 @@ function assertCursor(cursor: Cursor, what: Position | Selection | Selection[]): } else { selections = what; } - let actual = cursor.getSelections().map(s => s.toString()); + let actual = viewModel.getSelections().map(s => s.toString()); let expected = selections.map(s => s.toString()); assert.deepEqual(actual, expected); } -function createViewModel(configuration: IConfiguration, model: ITextModel): ViewModel { - const monospaceLineBreaksComputerFactory = MonospaceLineBreaksComputerFactory.create(configuration.options); - return new ViewModel(0, configuration, model, monospaceLineBreaksComputerFactory, monospaceLineBreaksComputerFactory, null!); -} - suite('Editor Controller - Cursor', () => { const LINE1 = ' \tMy First Line\t '; const LINE2 = '\tMy Second Line'; @@ -143,581 +125,724 @@ suite('Editor Controller - Cursor', () => { const LINE4 = ''; const LINE5 = '1'; - let thisModel: TextModel; - let thisConfiguration: TestConfiguration; - let thisViewModel: ViewModel; - let thisCursor: Cursor; + const TEXT = + LINE1 + '\r\n' + + LINE2 + '\n' + + LINE3 + '\n' + + LINE4 + '\r\n' + + LINE5; - setup(() => { - let text = - LINE1 + '\r\n' + - LINE2 + '\n' + - LINE3 + '\n' + - LINE4 + '\r\n' + - LINE5; + // let thisModel: TextModel; + // let thisConfiguration: TestConfiguration; + // let thisViewModel: ViewModel; + // let cursor: Cursor; - thisModel = createTextModel(text); - thisConfiguration = new TestConfiguration({}); - thisViewModel = createViewModel(thisConfiguration, thisModel); + // setup(() => { + // let text = + // LINE1 + '\r\n' + + // LINE2 + '\n' + + // LINE3 + '\n' + + // LINE4 + '\r\n' + + // LINE5; - thisCursor = new Cursor(thisConfiguration, thisModel, thisViewModel); - }); + // thisModel = createTextModel(text); + // thisConfiguration = new TestConfiguration({}); + // thisViewModel = createViewModel(thisConfiguration, thisModel); - teardown(() => { - thisCursor.dispose(); - thisViewModel.dispose(); - thisModel.dispose(); - thisConfiguration.dispose(); - }); + // cursor = new Cursor(thisConfiguration, thisModel, thisViewModel); + // }); + + // teardown(() => { + // cursor.dispose(); + // thisViewModel.dispose(); + // thisModel.dispose(); + // thisConfiguration.dispose(); + // }); + + function runTest(callback: (editor: ITestCodeEditor, viewModel: ViewModel) => void): void { + withTestCodeEditor(TEXT, {}, (editor, viewModel) => { + callback(editor, viewModel); + }); + } test('cursor initialized', () => { - assertCursor(thisCursor, new Position(1, 1)); + runTest((editor, viewModel) => { + assertCursor(viewModel, new Position(1, 1)); + }); }); // --------- absolute move test('no move', () => { - moveTo(thisCursor, 1, 1); - assertCursor(thisCursor, new Position(1, 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 1); + assertCursor(viewModel, new Position(1, 1)); + }); }); test('move', () => { - moveTo(thisCursor, 1, 2); - assertCursor(thisCursor, new Position(1, 2)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 2); + assertCursor(viewModel, new Position(1, 2)); + }); }); test('move in selection mode', () => { - moveTo(thisCursor, 1, 2, true); - assertCursor(thisCursor, new Selection(1, 1, 1, 2)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 2, true); + assertCursor(viewModel, new Selection(1, 1, 1, 2)); + }); }); test('move beyond line end', () => { - moveTo(thisCursor, 1, 25); - assertCursor(thisCursor, new Position(1, LINE1.length + 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 25); + assertCursor(viewModel, new Position(1, LINE1.length + 1)); + }); }); test('move empty line', () => { - moveTo(thisCursor, 4, 20); - assertCursor(thisCursor, new Position(4, 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 4, 20); + assertCursor(viewModel, new Position(4, 1)); + }); }); test('move one char line', () => { - moveTo(thisCursor, 5, 20); - assertCursor(thisCursor, new Position(5, 2)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 5, 20); + assertCursor(viewModel, new Position(5, 2)); + }); }); test('selection down', () => { - moveTo(thisCursor, 2, 1, true); - assertCursor(thisCursor, new Selection(1, 1, 2, 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 2, 1, true); + assertCursor(viewModel, new Selection(1, 1, 2, 1)); + }); }); test('move and then select', () => { - moveTo(thisCursor, 2, 3); - assertCursor(thisCursor, new Position(2, 3)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 2, 3); + assertCursor(viewModel, new Position(2, 3)); - moveTo(thisCursor, 2, 15, true); - assertCursor(thisCursor, new Selection(2, 3, 2, 15)); + moveTo(editor, viewModel, 2, 15, true); + assertCursor(viewModel, new Selection(2, 3, 2, 15)); - moveTo(thisCursor, 1, 2, true); - assertCursor(thisCursor, new Selection(2, 3, 1, 2)); + moveTo(editor, viewModel, 1, 2, true); + assertCursor(viewModel, new Selection(2, 3, 1, 2)); + }); }); // --------- move left test('move left on top left position', () => { - moveLeft(thisCursor); - assertCursor(thisCursor, new Position(1, 1)); + runTest((editor, viewModel) => { + moveLeft(editor, viewModel); + assertCursor(viewModel, new Position(1, 1)); + }); }); test('move left', () => { - moveTo(thisCursor, 1, 3); - assertCursor(thisCursor, new Position(1, 3)); - moveLeft(thisCursor); - assertCursor(thisCursor, new Position(1, 2)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 3); + assertCursor(viewModel, new Position(1, 3)); + moveLeft(editor, viewModel); + assertCursor(viewModel, new Position(1, 2)); + }); }); test('move left with surrogate pair', () => { - moveTo(thisCursor, 3, 17); - assertCursor(thisCursor, new Position(3, 17)); - moveLeft(thisCursor); - assertCursor(thisCursor, new Position(3, 15)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 3, 17); + assertCursor(viewModel, new Position(3, 17)); + moveLeft(editor, viewModel); + assertCursor(viewModel, new Position(3, 15)); + }); }); test('move left goes to previous row', () => { - moveTo(thisCursor, 2, 1); - assertCursor(thisCursor, new Position(2, 1)); - moveLeft(thisCursor); - assertCursor(thisCursor, new Position(1, 21)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 2, 1); + assertCursor(viewModel, new Position(2, 1)); + moveLeft(editor, viewModel); + assertCursor(viewModel, new Position(1, 21)); + }); }); test('move left selection', () => { - moveTo(thisCursor, 2, 1); - assertCursor(thisCursor, new Position(2, 1)); - moveLeft(thisCursor, true); - assertCursor(thisCursor, new Selection(2, 1, 1, 21)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 2, 1); + assertCursor(viewModel, new Position(2, 1)); + moveLeft(editor, viewModel, true); + assertCursor(viewModel, new Selection(2, 1, 1, 21)); + }); }); // --------- move right test('move right on bottom right position', () => { - moveTo(thisCursor, 5, 2); - assertCursor(thisCursor, new Position(5, 2)); - moveRight(thisCursor); - assertCursor(thisCursor, new Position(5, 2)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 5, 2); + assertCursor(viewModel, new Position(5, 2)); + moveRight(editor, viewModel); + assertCursor(viewModel, new Position(5, 2)); + }); }); test('move right', () => { - moveTo(thisCursor, 1, 3); - assertCursor(thisCursor, new Position(1, 3)); - moveRight(thisCursor); - assertCursor(thisCursor, new Position(1, 4)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 3); + assertCursor(viewModel, new Position(1, 3)); + moveRight(editor, viewModel); + assertCursor(viewModel, new Position(1, 4)); + }); }); test('move right with surrogate pair', () => { - moveTo(thisCursor, 3, 15); - assertCursor(thisCursor, new Position(3, 15)); - moveRight(thisCursor); - assertCursor(thisCursor, new Position(3, 17)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 3, 15); + assertCursor(viewModel, new Position(3, 15)); + moveRight(editor, viewModel); + assertCursor(viewModel, new Position(3, 17)); + }); }); test('move right goes to next row', () => { - moveTo(thisCursor, 1, 21); - assertCursor(thisCursor, new Position(1, 21)); - moveRight(thisCursor); - assertCursor(thisCursor, new Position(2, 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 21); + assertCursor(viewModel, new Position(1, 21)); + moveRight(editor, viewModel); + assertCursor(viewModel, new Position(2, 1)); + }); }); test('move right selection', () => { - moveTo(thisCursor, 1, 21); - assertCursor(thisCursor, new Position(1, 21)); - moveRight(thisCursor, true); - assertCursor(thisCursor, new Selection(1, 21, 2, 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 21); + assertCursor(viewModel, new Position(1, 21)); + moveRight(editor, viewModel, true); + assertCursor(viewModel, new Selection(1, 21, 2, 1)); + }); }); // --------- move down test('move down', () => { - moveDown(thisCursor); - assertCursor(thisCursor, new Position(2, 1)); - moveDown(thisCursor); - assertCursor(thisCursor, new Position(3, 1)); - moveDown(thisCursor); - assertCursor(thisCursor, new Position(4, 1)); - moveDown(thisCursor); - assertCursor(thisCursor, new Position(5, 1)); - moveDown(thisCursor); - assertCursor(thisCursor, new Position(5, 2)); + runTest((editor, viewModel) => { + moveDown(editor, viewModel); + assertCursor(viewModel, new Position(2, 1)); + moveDown(editor, viewModel); + assertCursor(viewModel, new Position(3, 1)); + moveDown(editor, viewModel); + assertCursor(viewModel, new Position(4, 1)); + moveDown(editor, viewModel); + assertCursor(viewModel, new Position(5, 1)); + moveDown(editor, viewModel); + assertCursor(viewModel, new Position(5, 2)); + }); }); test('move down with selection', () => { - moveDown(thisCursor, true); - assertCursor(thisCursor, new Selection(1, 1, 2, 1)); - moveDown(thisCursor, true); - assertCursor(thisCursor, new Selection(1, 1, 3, 1)); - moveDown(thisCursor, true); - assertCursor(thisCursor, new Selection(1, 1, 4, 1)); - moveDown(thisCursor, true); - assertCursor(thisCursor, new Selection(1, 1, 5, 1)); - moveDown(thisCursor, true); - assertCursor(thisCursor, new Selection(1, 1, 5, 2)); + runTest((editor, viewModel) => { + moveDown(editor, viewModel, true); + assertCursor(viewModel, new Selection(1, 1, 2, 1)); + moveDown(editor, viewModel, true); + assertCursor(viewModel, new Selection(1, 1, 3, 1)); + moveDown(editor, viewModel, true); + assertCursor(viewModel, new Selection(1, 1, 4, 1)); + moveDown(editor, viewModel, true); + assertCursor(viewModel, new Selection(1, 1, 5, 1)); + moveDown(editor, viewModel, true); + assertCursor(viewModel, new Selection(1, 1, 5, 2)); + }); }); test('move down with tabs', () => { - moveTo(thisCursor, 1, 5); - assertCursor(thisCursor, new Position(1, 5)); - moveDown(thisCursor); - assertCursor(thisCursor, new Position(2, 2)); - moveDown(thisCursor); - assertCursor(thisCursor, new Position(3, 5)); - moveDown(thisCursor); - assertCursor(thisCursor, new Position(4, 1)); - moveDown(thisCursor); - assertCursor(thisCursor, new Position(5, 2)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 5); + assertCursor(viewModel, new Position(1, 5)); + moveDown(editor, viewModel); + assertCursor(viewModel, new Position(2, 2)); + moveDown(editor, viewModel); + assertCursor(viewModel, new Position(3, 5)); + moveDown(editor, viewModel); + assertCursor(viewModel, new Position(4, 1)); + moveDown(editor, viewModel); + assertCursor(viewModel, new Position(5, 2)); + }); }); // --------- move up test('move up', () => { - moveTo(thisCursor, 3, 5); - assertCursor(thisCursor, new Position(3, 5)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 3, 5); + assertCursor(viewModel, new Position(3, 5)); - moveUp(thisCursor); - assertCursor(thisCursor, new Position(2, 2)); + moveUp(editor, viewModel); + assertCursor(viewModel, new Position(2, 2)); - moveUp(thisCursor); - assertCursor(thisCursor, new Position(1, 5)); + moveUp(editor, viewModel); + assertCursor(viewModel, new Position(1, 5)); + }); }); test('move up with selection', () => { - moveTo(thisCursor, 3, 5); - assertCursor(thisCursor, new Position(3, 5)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 3, 5); + assertCursor(viewModel, new Position(3, 5)); - moveUp(thisCursor, true); - assertCursor(thisCursor, new Selection(3, 5, 2, 2)); + moveUp(editor, viewModel, true); + assertCursor(viewModel, new Selection(3, 5, 2, 2)); - moveUp(thisCursor, true); - assertCursor(thisCursor, new Selection(3, 5, 1, 5)); + moveUp(editor, viewModel, true); + assertCursor(viewModel, new Selection(3, 5, 1, 5)); + }); }); test('move up and down with tabs', () => { - moveTo(thisCursor, 1, 5); - assertCursor(thisCursor, new Position(1, 5)); - moveDown(thisCursor); - moveDown(thisCursor); - moveDown(thisCursor); - moveDown(thisCursor); - assertCursor(thisCursor, new Position(5, 2)); - moveUp(thisCursor); - assertCursor(thisCursor, new Position(4, 1)); - moveUp(thisCursor); - assertCursor(thisCursor, new Position(3, 5)); - moveUp(thisCursor); - assertCursor(thisCursor, new Position(2, 2)); - moveUp(thisCursor); - assertCursor(thisCursor, new Position(1, 5)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 5); + assertCursor(viewModel, new Position(1, 5)); + moveDown(editor, viewModel); + moveDown(editor, viewModel); + moveDown(editor, viewModel); + moveDown(editor, viewModel); + assertCursor(viewModel, new Position(5, 2)); + moveUp(editor, viewModel); + assertCursor(viewModel, new Position(4, 1)); + moveUp(editor, viewModel); + assertCursor(viewModel, new Position(3, 5)); + moveUp(editor, viewModel); + assertCursor(viewModel, new Position(2, 2)); + moveUp(editor, viewModel); + assertCursor(viewModel, new Position(1, 5)); + }); }); test('move up and down with end of lines starting from a long one', () => { - moveToEndOfLine(thisCursor); - assertCursor(thisCursor, new Position(1, LINE1.length + 1)); - moveToEndOfLine(thisCursor); - assertCursor(thisCursor, new Position(1, LINE1.length + 1)); - moveDown(thisCursor); - assertCursor(thisCursor, new Position(2, LINE2.length + 1)); - moveDown(thisCursor); - assertCursor(thisCursor, new Position(3, LINE3.length + 1)); - moveDown(thisCursor); - assertCursor(thisCursor, new Position(4, LINE4.length + 1)); - moveDown(thisCursor); - assertCursor(thisCursor, new Position(5, LINE5.length + 1)); - moveUp(thisCursor); - moveUp(thisCursor); - moveUp(thisCursor); - moveUp(thisCursor); - assertCursor(thisCursor, new Position(1, LINE1.length + 1)); + runTest((editor, viewModel) => { + moveToEndOfLine(editor, viewModel); + assertCursor(viewModel, new Position(1, LINE1.length + 1)); + moveToEndOfLine(editor, viewModel); + assertCursor(viewModel, new Position(1, LINE1.length + 1)); + moveDown(editor, viewModel); + assertCursor(viewModel, new Position(2, LINE2.length + 1)); + moveDown(editor, viewModel); + assertCursor(viewModel, new Position(3, LINE3.length + 1)); + moveDown(editor, viewModel); + assertCursor(viewModel, new Position(4, LINE4.length + 1)); + moveDown(editor, viewModel); + assertCursor(viewModel, new Position(5, LINE5.length + 1)); + moveUp(editor, viewModel); + moveUp(editor, viewModel); + moveUp(editor, viewModel); + moveUp(editor, viewModel); + assertCursor(viewModel, new Position(1, LINE1.length + 1)); + }); }); test('issue #44465: cursor position not correct when move', () => { - thisCursor.setSelections('test', [new Selection(1, 5, 1, 5)]); - // going once up on the first line remembers the offset visual columns - moveUp(thisCursor); - assertCursor(thisCursor, new Position(1, 1)); - moveDown(thisCursor); - assertCursor(thisCursor, new Position(2, 2)); - moveUp(thisCursor); - assertCursor(thisCursor, new Position(1, 5)); + runTest((editor, viewModel) => { + viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]); + // going once up on the first line remembers the offset visual columns + moveUp(editor, viewModel); + assertCursor(viewModel, new Position(1, 1)); + moveDown(editor, viewModel); + assertCursor(viewModel, new Position(2, 2)); + moveUp(editor, viewModel); + assertCursor(viewModel, new Position(1, 5)); - // going twice up on the first line discards the offset visual columns - moveUp(thisCursor); - assertCursor(thisCursor, new Position(1, 1)); - moveUp(thisCursor); - assertCursor(thisCursor, new Position(1, 1)); - moveDown(thisCursor); - assertCursor(thisCursor, new Position(2, 1)); + // going twice up on the first line discards the offset visual columns + moveUp(editor, viewModel); + assertCursor(viewModel, new Position(1, 1)); + moveUp(editor, viewModel); + assertCursor(viewModel, new Position(1, 1)); + moveDown(editor, viewModel); + assertCursor(viewModel, new Position(2, 1)); + }); }); // --------- move to beginning of line test('move to beginning of line', () => { - moveToBeginningOfLine(thisCursor); - assertCursor(thisCursor, new Position(1, 6)); - moveToBeginningOfLine(thisCursor); - assertCursor(thisCursor, new Position(1, 1)); + runTest((editor, viewModel) => { + moveToBeginningOfLine(editor, viewModel); + assertCursor(viewModel, new Position(1, 6)); + moveToBeginningOfLine(editor, viewModel); + assertCursor(viewModel, new Position(1, 1)); + }); }); test('move to beginning of line from within line', () => { - moveTo(thisCursor, 1, 8); - moveToBeginningOfLine(thisCursor); - assertCursor(thisCursor, new Position(1, 6)); - moveToBeginningOfLine(thisCursor); - assertCursor(thisCursor, new Position(1, 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 8); + moveToBeginningOfLine(editor, viewModel); + assertCursor(viewModel, new Position(1, 6)); + moveToBeginningOfLine(editor, viewModel); + assertCursor(viewModel, new Position(1, 1)); + }); }); test('move to beginning of line from whitespace at beginning of line', () => { - moveTo(thisCursor, 1, 2); - moveToBeginningOfLine(thisCursor); - assertCursor(thisCursor, new Position(1, 6)); - moveToBeginningOfLine(thisCursor); - assertCursor(thisCursor, new Position(1, 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 2); + moveToBeginningOfLine(editor, viewModel); + assertCursor(viewModel, new Position(1, 6)); + moveToBeginningOfLine(editor, viewModel); + assertCursor(viewModel, new Position(1, 1)); + }); }); test('move to beginning of line from within line selection', () => { - moveTo(thisCursor, 1, 8); - moveToBeginningOfLine(thisCursor, true); - assertCursor(thisCursor, new Selection(1, 8, 1, 6)); - moveToBeginningOfLine(thisCursor, true); - assertCursor(thisCursor, new Selection(1, 8, 1, 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 8); + moveToBeginningOfLine(editor, viewModel, true); + assertCursor(viewModel, new Selection(1, 8, 1, 6)); + moveToBeginningOfLine(editor, viewModel, true); + assertCursor(viewModel, new Selection(1, 8, 1, 1)); + }); }); test('move to beginning of line with selection multiline forward', () => { - moveTo(thisCursor, 1, 8); - moveTo(thisCursor, 3, 9, true); - moveToBeginningOfLine(thisCursor, false); - assertCursor(thisCursor, new Selection(3, 5, 3, 5)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 8); + moveTo(editor, viewModel, 3, 9, true); + moveToBeginningOfLine(editor, viewModel, false); + assertCursor(viewModel, new Selection(3, 5, 3, 5)); + }); }); test('move to beginning of line with selection multiline backward', () => { - moveTo(thisCursor, 3, 9); - moveTo(thisCursor, 1, 8, true); - moveToBeginningOfLine(thisCursor, false); - assertCursor(thisCursor, new Selection(1, 6, 1, 6)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 3, 9); + moveTo(editor, viewModel, 1, 8, true); + moveToBeginningOfLine(editor, viewModel, false); + assertCursor(viewModel, new Selection(1, 6, 1, 6)); + }); }); test('move to beginning of line with selection single line forward', () => { - moveTo(thisCursor, 3, 2); - moveTo(thisCursor, 3, 9, true); - moveToBeginningOfLine(thisCursor, false); - assertCursor(thisCursor, new Selection(3, 5, 3, 5)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 3, 2); + moveTo(editor, viewModel, 3, 9, true); + moveToBeginningOfLine(editor, viewModel, false); + assertCursor(viewModel, new Selection(3, 5, 3, 5)); + }); }); test('move to beginning of line with selection single line backward', () => { - moveTo(thisCursor, 3, 9); - moveTo(thisCursor, 3, 2, true); - moveToBeginningOfLine(thisCursor, false); - assertCursor(thisCursor, new Selection(3, 5, 3, 5)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 3, 9); + moveTo(editor, viewModel, 3, 2, true); + moveToBeginningOfLine(editor, viewModel, false); + assertCursor(viewModel, new Selection(3, 5, 3, 5)); + }); }); test('issue #15401: "End" key is behaving weird when text is selected part 1', () => { - moveTo(thisCursor, 1, 8); - moveTo(thisCursor, 3, 9, true); - moveToBeginningOfLine(thisCursor, false); - assertCursor(thisCursor, new Selection(3, 5, 3, 5)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 8); + moveTo(editor, viewModel, 3, 9, true); + moveToBeginningOfLine(editor, viewModel, false); + assertCursor(viewModel, new Selection(3, 5, 3, 5)); + }); }); test('issue #17011: Shift+home/end now go to the end of the selection start\'s line, not the selection\'s end', () => { - moveTo(thisCursor, 1, 8); - moveTo(thisCursor, 3, 9, true); - moveToBeginningOfLine(thisCursor, true); - assertCursor(thisCursor, new Selection(1, 8, 3, 5)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 8); + moveTo(editor, viewModel, 3, 9, true); + moveToBeginningOfLine(editor, viewModel, true); + assertCursor(viewModel, new Selection(1, 8, 3, 5)); + }); }); // --------- move to end of line test('move to end of line', () => { - moveToEndOfLine(thisCursor); - assertCursor(thisCursor, new Position(1, LINE1.length + 1)); - moveToEndOfLine(thisCursor); - assertCursor(thisCursor, new Position(1, LINE1.length + 1)); + runTest((editor, viewModel) => { + moveToEndOfLine(editor, viewModel); + assertCursor(viewModel, new Position(1, LINE1.length + 1)); + moveToEndOfLine(editor, viewModel); + assertCursor(viewModel, new Position(1, LINE1.length + 1)); + }); }); test('move to end of line from within line', () => { - moveTo(thisCursor, 1, 6); - moveToEndOfLine(thisCursor); - assertCursor(thisCursor, new Position(1, LINE1.length + 1)); - moveToEndOfLine(thisCursor); - assertCursor(thisCursor, new Position(1, LINE1.length + 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 6); + moveToEndOfLine(editor, viewModel); + assertCursor(viewModel, new Position(1, LINE1.length + 1)); + moveToEndOfLine(editor, viewModel); + assertCursor(viewModel, new Position(1, LINE1.length + 1)); + }); }); test('move to end of line from whitespace at end of line', () => { - moveTo(thisCursor, 1, 20); - moveToEndOfLine(thisCursor); - assertCursor(thisCursor, new Position(1, LINE1.length + 1)); - moveToEndOfLine(thisCursor); - assertCursor(thisCursor, new Position(1, LINE1.length + 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 20); + moveToEndOfLine(editor, viewModel); + assertCursor(viewModel, new Position(1, LINE1.length + 1)); + moveToEndOfLine(editor, viewModel); + assertCursor(viewModel, new Position(1, LINE1.length + 1)); + }); }); test('move to end of line from within line selection', () => { - moveTo(thisCursor, 1, 6); - moveToEndOfLine(thisCursor, true); - assertCursor(thisCursor, new Selection(1, 6, 1, LINE1.length + 1)); - moveToEndOfLine(thisCursor, true); - assertCursor(thisCursor, new Selection(1, 6, 1, LINE1.length + 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 6); + moveToEndOfLine(editor, viewModel, true); + assertCursor(viewModel, new Selection(1, 6, 1, LINE1.length + 1)); + moveToEndOfLine(editor, viewModel, true); + assertCursor(viewModel, new Selection(1, 6, 1, LINE1.length + 1)); + }); }); test('move to end of line with selection multiline forward', () => { - moveTo(thisCursor, 1, 1); - moveTo(thisCursor, 3, 9, true); - moveToEndOfLine(thisCursor, false); - assertCursor(thisCursor, new Selection(3, 17, 3, 17)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 1); + moveTo(editor, viewModel, 3, 9, true); + moveToEndOfLine(editor, viewModel, false); + assertCursor(viewModel, new Selection(3, 17, 3, 17)); + }); }); test('move to end of line with selection multiline backward', () => { - moveTo(thisCursor, 3, 9); - moveTo(thisCursor, 1, 1, true); - moveToEndOfLine(thisCursor, false); - assertCursor(thisCursor, new Selection(1, 21, 1, 21)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 3, 9); + moveTo(editor, viewModel, 1, 1, true); + moveToEndOfLine(editor, viewModel, false); + assertCursor(viewModel, new Selection(1, 21, 1, 21)); + }); }); test('move to end of line with selection single line forward', () => { - moveTo(thisCursor, 3, 1); - moveTo(thisCursor, 3, 9, true); - moveToEndOfLine(thisCursor, false); - assertCursor(thisCursor, new Selection(3, 17, 3, 17)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 3, 1); + moveTo(editor, viewModel, 3, 9, true); + moveToEndOfLine(editor, viewModel, false); + assertCursor(viewModel, new Selection(3, 17, 3, 17)); + }); }); test('move to end of line with selection single line backward', () => { - moveTo(thisCursor, 3, 9); - moveTo(thisCursor, 3, 1, true); - moveToEndOfLine(thisCursor, false); - assertCursor(thisCursor, new Selection(3, 17, 3, 17)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 3, 9); + moveTo(editor, viewModel, 3, 1, true); + moveToEndOfLine(editor, viewModel, false); + assertCursor(viewModel, new Selection(3, 17, 3, 17)); + }); }); test('issue #15401: "End" key is behaving weird when text is selected part 2', () => { - moveTo(thisCursor, 1, 1); - moveTo(thisCursor, 3, 9, true); - moveToEndOfLine(thisCursor, false); - assertCursor(thisCursor, new Selection(3, 17, 3, 17)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 1); + moveTo(editor, viewModel, 3, 9, true); + moveToEndOfLine(editor, viewModel, false); + assertCursor(viewModel, new Selection(3, 17, 3, 17)); + }); }); // --------- move to beginning of buffer test('move to beginning of buffer', () => { - moveToBeginningOfBuffer(thisCursor); - assertCursor(thisCursor, new Position(1, 1)); + runTest((editor, viewModel) => { + moveToBeginningOfBuffer(editor, viewModel); + assertCursor(viewModel, new Position(1, 1)); + }); }); test('move to beginning of buffer from within first line', () => { - moveTo(thisCursor, 1, 3); - moveToBeginningOfBuffer(thisCursor); - assertCursor(thisCursor, new Position(1, 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 3); + moveToBeginningOfBuffer(editor, viewModel); + assertCursor(viewModel, new Position(1, 1)); + }); }); test('move to beginning of buffer from within another line', () => { - moveTo(thisCursor, 3, 3); - moveToBeginningOfBuffer(thisCursor); - assertCursor(thisCursor, new Position(1, 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 3, 3); + moveToBeginningOfBuffer(editor, viewModel); + assertCursor(viewModel, new Position(1, 1)); + }); }); test('move to beginning of buffer from within first line selection', () => { - moveTo(thisCursor, 1, 3); - moveToBeginningOfBuffer(thisCursor, true); - assertCursor(thisCursor, new Selection(1, 3, 1, 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 1, 3); + moveToBeginningOfBuffer(editor, viewModel, true); + assertCursor(viewModel, new Selection(1, 3, 1, 1)); + }); }); test('move to beginning of buffer from within another line selection', () => { - moveTo(thisCursor, 3, 3); - moveToBeginningOfBuffer(thisCursor, true); - assertCursor(thisCursor, new Selection(3, 3, 1, 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 3, 3); + moveToBeginningOfBuffer(editor, viewModel, true); + assertCursor(viewModel, new Selection(3, 3, 1, 1)); + }); }); // --------- move to end of buffer test('move to end of buffer', () => { - moveToEndOfBuffer(thisCursor); - assertCursor(thisCursor, new Position(5, LINE5.length + 1)); + runTest((editor, viewModel) => { + moveToEndOfBuffer(editor, viewModel); + assertCursor(viewModel, new Position(5, LINE5.length + 1)); + }); }); test('move to end of buffer from within last line', () => { - moveTo(thisCursor, 5, 1); - moveToEndOfBuffer(thisCursor); - assertCursor(thisCursor, new Position(5, LINE5.length + 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 5, 1); + moveToEndOfBuffer(editor, viewModel); + assertCursor(viewModel, new Position(5, LINE5.length + 1)); + }); }); test('move to end of buffer from within another line', () => { - moveTo(thisCursor, 3, 3); - moveToEndOfBuffer(thisCursor); - assertCursor(thisCursor, new Position(5, LINE5.length + 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 3, 3); + moveToEndOfBuffer(editor, viewModel); + assertCursor(viewModel, new Position(5, LINE5.length + 1)); + }); }); test('move to end of buffer from within last line selection', () => { - moveTo(thisCursor, 5, 1); - moveToEndOfBuffer(thisCursor, true); - assertCursor(thisCursor, new Selection(5, 1, 5, LINE5.length + 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 5, 1); + moveToEndOfBuffer(editor, viewModel, true); + assertCursor(viewModel, new Selection(5, 1, 5, LINE5.length + 1)); + }); }); test('move to end of buffer from within another line selection', () => { - moveTo(thisCursor, 3, 3); - moveToEndOfBuffer(thisCursor, true); - assertCursor(thisCursor, new Selection(3, 3, 5, LINE5.length + 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 3, 3); + moveToEndOfBuffer(editor, viewModel, true); + assertCursor(viewModel, new Selection(3, 3, 5, LINE5.length + 1)); + }); }); // --------- misc test('select all', () => { - CoreNavigationCommands.SelectAll.runCoreEditorCommand(thisCursor, {}); - assertCursor(thisCursor, new Selection(1, 1, 5, LINE5.length + 1)); + runTest((editor, viewModel) => { + CoreNavigationCommands.SelectAll.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, new Selection(1, 1, 5, LINE5.length + 1)); + }); }); test('expandLineSelection', () => { - // 0 1 2 - // 01234 56789012345678 0 - // let LINE1 = ' \tMy First Line\t '; - moveTo(thisCursor, 1, 1); - CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); - assertCursor(thisCursor, new Selection(1, 1, 2, 1)); + runTest((editor, viewModel) => { + // 0 1 2 + // 01234 56789012345678 0 + // let LINE1 = ' \tMy First Line\t '; + moveTo(editor, viewModel, 1, 1); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, new Selection(1, 1, 2, 1)); - moveTo(thisCursor, 1, 2); - CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); - assertCursor(thisCursor, new Selection(1, 1, 2, 1)); + moveTo(editor, viewModel, 1, 2); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, new Selection(1, 1, 2, 1)); - moveTo(thisCursor, 1, 5); - CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); - assertCursor(thisCursor, new Selection(1, 1, 2, 1)); + moveTo(editor, viewModel, 1, 5); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, new Selection(1, 1, 2, 1)); - moveTo(thisCursor, 1, 19); - CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); - assertCursor(thisCursor, new Selection(1, 1, 2, 1)); + moveTo(editor, viewModel, 1, 19); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, new Selection(1, 1, 2, 1)); - moveTo(thisCursor, 1, 20); - CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); - assertCursor(thisCursor, new Selection(1, 1, 2, 1)); + moveTo(editor, viewModel, 1, 20); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, new Selection(1, 1, 2, 1)); - moveTo(thisCursor, 1, 21); - CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); - assertCursor(thisCursor, new Selection(1, 1, 2, 1)); - CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); - assertCursor(thisCursor, new Selection(1, 1, 3, 1)); - CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); - assertCursor(thisCursor, new Selection(1, 1, 4, 1)); - CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); - assertCursor(thisCursor, new Selection(1, 1, 5, 1)); - CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); - assertCursor(thisCursor, new Selection(1, 1, 5, LINE5.length + 1)); - CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(thisCursor, {}); - assertCursor(thisCursor, new Selection(1, 1, 5, LINE5.length + 1)); + moveTo(editor, viewModel, 1, 21); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, new Selection(1, 1, 2, 1)); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, new Selection(1, 1, 3, 1)); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, new Selection(1, 1, 4, 1)); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, new Selection(1, 1, 5, 1)); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, new Selection(1, 1, 5, LINE5.length + 1)); + CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, new Selection(1, 1, 5, LINE5.length + 1)); + }); }); // --------- eventing test('no move doesn\'t trigger event', () => { - thisCursor.onDidChange((e) => { - assert.ok(false, 'was not expecting event'); + runTest((editor, viewModel) => { + viewModel.onEvent((e) => { + assert.ok(false, 'was not expecting event'); + }); + moveTo(editor, viewModel, 1, 1); }); - moveTo(thisCursor, 1, 1); }); test('move eventing', () => { - let events = 0; - thisCursor.onDidChange((e: CursorStateChangedEvent) => { - events++; - assert.deepEqual(e.selections, [new Selection(1, 2, 1, 2)]); + runTest((editor, viewModel) => { + let events = 0; + viewModel.onEvent((e) => { + if (e.kind === OutgoingViewModelEventKind.CursorStateChanged) { + events++; + assert.deepEqual(e.selections, [new Selection(1, 2, 1, 2)]); + } + }); + moveTo(editor, viewModel, 1, 2); + assert.equal(events, 1, 'receives 1 event'); }); - moveTo(thisCursor, 1, 2); - assert.equal(events, 1, 'receives 1 event'); }); test('move in selection mode eventing', () => { - let events = 0; - thisCursor.onDidChange((e: CursorStateChangedEvent) => { - events++; - assert.deepEqual(e.selections, [new Selection(1, 1, 1, 2)]); + runTest((editor, viewModel) => { + let events = 0; + viewModel.onEvent((e) => { + if (e.kind === OutgoingViewModelEventKind.CursorStateChanged) { + events++; + assert.deepEqual(e.selections, [new Selection(1, 1, 1, 2)]); + } + }); + moveTo(editor, viewModel, 1, 2, true); + assert.equal(events, 1, 'receives 1 event'); }); - moveTo(thisCursor, 1, 2, true); - assert.equal(events, 1, 'receives 1 event'); }); // --------- state save & restore test('saveState & restoreState', () => { - moveTo(thisCursor, 2, 1, true); - assertCursor(thisCursor, new Selection(1, 1, 2, 1)); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 2, 1, true); + assertCursor(viewModel, new Selection(1, 1, 2, 1)); - let savedState = JSON.stringify(thisCursor.saveState()); + let savedState = JSON.stringify(viewModel.saveCursorState()); - moveTo(thisCursor, 1, 1, false); - assertCursor(thisCursor, new Position(1, 1)); + moveTo(editor, viewModel, 1, 1, false); + assertCursor(viewModel, new Position(1, 1)); - thisCursor.restoreState(JSON.parse(savedState)); - assertCursor(thisCursor, new Selection(1, 1, 2, 1)); + viewModel.restoreCursorState(JSON.parse(savedState)); + assertCursor(viewModel, new Selection(1, 1, 2, 1)); + }); }); // --------- updating cursor test('Independent model edit 1', () => { - moveTo(thisCursor, 2, 16, true); + runTest((editor, viewModel) => { + moveTo(editor, viewModel, 2, 16, true); - thisModel.applyEdits([EditOperation.delete(new Range(2, 1, 2, 2))]); - assertCursor(thisCursor, new Selection(1, 1, 2, 15)); + editor.getModel().applyEdits([EditOperation.delete(new Range(2, 1, 2, 2))]); + assertCursor(viewModel, new Selection(1, 1, 2, 15)); + }); }); test('column select 1', () => { @@ -727,12 +852,12 @@ suite('Editor Controller - Cursor', () => { '\t\t\treturn false;', '\t\t}', '\t}' - ], {}, (editor, cursor) => { + ], {}, (editor, viewModel) => { - moveTo(cursor, 1, 7, false); - assertCursor(cursor, new Position(1, 7)); + moveTo(editor, viewModel, 1, 7, false); + assertCursor(viewModel, new Position(1, 7)); - CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(cursor, { + CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(viewModel, { position: new Position(4, 4), viewPosition: new Position(4, 4), mouseColumn: 15, @@ -746,7 +871,7 @@ suite('Editor Controller - Cursor', () => { new Selection(4, 4, 4, 4), ]; - assertCursor(cursor, expectedSelections); + assertCursor(viewModel, expectedSelections); }); }); @@ -757,41 +882,41 @@ suite('Editor Controller - Cursor', () => { 'a˃a˃a˃a˃a˃a˃', 'čžģķ „€čžģķ „€čžģķ „€', 'āŽĒ❁', - ], {}, (editor, cursor) => { + ], {}, (editor, viewModel) => { - cursor.setSelections('test', [new Selection(2, 1, 2, 1)]); - moveRight(cursor); - assertCursor(cursor, new Position(2, 3)); - moveLeft(cursor); - assertCursor(cursor, new Position(2, 1)); + viewModel.setSelections('test', [new Selection(2, 1, 2, 1)]); + moveRight(editor, viewModel); + assertCursor(viewModel, new Position(2, 3)); + moveLeft(editor, viewModel); + assertCursor(viewModel, new Position(2, 1)); - cursor.setSelections('test', [new Selection(3, 1, 3, 1)]); - moveRight(cursor); - assertCursor(cursor, new Position(3, 4)); - moveLeft(cursor); - assertCursor(cursor, new Position(3, 1)); + viewModel.setSelections('test', [new Selection(3, 1, 3, 1)]); + moveRight(editor, viewModel); + assertCursor(viewModel, new Position(3, 4)); + moveLeft(editor, viewModel); + assertCursor(viewModel, new Position(3, 1)); - cursor.setSelections('test', [new Selection(4, 1, 4, 1)]); - moveRight(cursor); - assertCursor(cursor, new Position(4, 3)); - moveLeft(cursor); - assertCursor(cursor, new Position(4, 1)); + viewModel.setSelections('test', [new Selection(4, 1, 4, 1)]); + moveRight(editor, viewModel); + assertCursor(viewModel, new Position(4, 3)); + moveLeft(editor, viewModel); + assertCursor(viewModel, new Position(4, 1)); - cursor.setSelections('test', [new Selection(1, 3, 1, 3)]); - moveDown(cursor); - assertCursor(cursor, new Position(2, 5)); - moveDown(cursor); - assertCursor(cursor, new Position(3, 4)); - moveUp(cursor); - assertCursor(cursor, new Position(2, 5)); - moveUp(cursor); - assertCursor(cursor, new Position(1, 3)); + viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]); + moveDown(editor, viewModel); + assertCursor(viewModel, new Position(2, 5)); + moveDown(editor, viewModel); + assertCursor(viewModel, new Position(3, 4)); + moveUp(editor, viewModel); + assertCursor(viewModel, new Position(2, 5)); + moveUp(editor, viewModel); + assertCursor(viewModel, new Position(1, 3)); }); }); test('issue #4905 - column select is biased to the right', () => { - const model = createTextModel([ + withTestCodeEditor([ 'var gulp = require("gulp");', 'var path = require("path");', 'var rimraf = require("rimraf");', @@ -799,36 +924,28 @@ suite('Editor Controller - Cursor', () => { 'var merge = require("merge-stream");', 'var concat = require("gulp-concat");', 'var newer = require("gulp-newer");', - ].join('\n')); - const config = new TestConfiguration({}); - const viewModel = createViewModel(config, model); - const cursor = new Cursor(config, model, viewModel); + ].join('\n'), {}, (editor, viewModel) => { + moveTo(editor, viewModel, 1, 4, false); + assertCursor(viewModel, new Position(1, 4)); - moveTo(cursor, 1, 4, false); - assertCursor(cursor, new Position(1, 4)); + CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(viewModel, { + position: new Position(4, 1), + viewPosition: new Position(4, 1), + mouseColumn: 1, + doColumnSelect: true + }); - CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(cursor, { - position: new Position(4, 1), - viewPosition: new Position(4, 1), - mouseColumn: 1, - doColumnSelect: true + assertCursor(viewModel, [ + new Selection(1, 4, 1, 1), + new Selection(2, 4, 2, 1), + new Selection(3, 4, 3, 1), + new Selection(4, 4, 4, 1), + ]); }); - - assertCursor(cursor, [ - new Selection(1, 4, 1, 1), - new Selection(2, 4, 2, 1), - new Selection(3, 4, 3, 1), - new Selection(4, 4, 4, 1), - ]); - - cursor.dispose(); - viewModel.dispose(); - config.dispose(); - model.dispose(); }); test('issue #20087: column select with mouse', () => { - const model = createTextModel([ + withTestCodeEditor([ '', '', '', @@ -839,60 +956,54 @@ suite('Editor Controller - Cursor', () => { '', '', '', - ].join('\n')); - const config = new TestConfiguration({}); - const viewModel = createViewModel(config, model); - const cursor = new Cursor(config, model, viewModel); + ].join('\n'), {}, (editor, viewModel) => { - moveTo(cursor, 10, 10, false); - assertCursor(cursor, new Position(10, 10)); + moveTo(editor, viewModel, 10, 10, false); + assertCursor(viewModel, new Position(10, 10)); + + CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(viewModel, { + position: new Position(1, 1), + viewPosition: new Position(1, 1), + mouseColumn: 1, + doColumnSelect: true + }); + assertCursor(viewModel, [ + new Selection(10, 10, 10, 1), + new Selection(9, 10, 9, 1), + new Selection(8, 10, 8, 1), + new Selection(7, 10, 7, 1), + new Selection(6, 10, 6, 1), + new Selection(5, 10, 5, 1), + new Selection(4, 10, 4, 1), + new Selection(3, 10, 3, 1), + new Selection(2, 10, 2, 1), + new Selection(1, 10, 1, 1), + ]); + + CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(viewModel, { + position: new Position(1, 1), + viewPosition: new Position(1, 1), + mouseColumn: 1, + doColumnSelect: true + }); + assertCursor(viewModel, [ + new Selection(10, 10, 10, 1), + new Selection(9, 10, 9, 1), + new Selection(8, 10, 8, 1), + new Selection(7, 10, 7, 1), + new Selection(6, 10, 6, 1), + new Selection(5, 10, 5, 1), + new Selection(4, 10, 4, 1), + new Selection(3, 10, 3, 1), + new Selection(2, 10, 2, 1), + new Selection(1, 10, 1, 1), + ]); - CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(cursor, { - position: new Position(1, 1), - viewPosition: new Position(1, 1), - mouseColumn: 1, - doColumnSelect: true }); - assertCursor(cursor, [ - new Selection(10, 10, 10, 1), - new Selection(9, 10, 9, 1), - new Selection(8, 10, 8, 1), - new Selection(7, 10, 7, 1), - new Selection(6, 10, 6, 1), - new Selection(5, 10, 5, 1), - new Selection(4, 10, 4, 1), - new Selection(3, 10, 3, 1), - new Selection(2, 10, 2, 1), - new Selection(1, 10, 1, 1), - ]); - - CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(cursor, { - position: new Position(1, 1), - viewPosition: new Position(1, 1), - mouseColumn: 1, - doColumnSelect: true - }); - assertCursor(cursor, [ - new Selection(10, 10, 10, 1), - new Selection(9, 10, 9, 1), - new Selection(8, 10, 8, 1), - new Selection(7, 10, 7, 1), - new Selection(6, 10, 6, 1), - new Selection(5, 10, 5, 1), - new Selection(4, 10, 4, 1), - new Selection(3, 10, 3, 1), - new Selection(2, 10, 2, 1), - new Selection(1, 10, 1, 1), - ]); - - cursor.dispose(); - viewModel.dispose(); - config.dispose(); - model.dispose(); }); test('issue #20087: column select with keyboard', () => { - const model = createTextModel([ + withTestCodeEditor([ '', '', '', @@ -903,48 +1014,41 @@ suite('Editor Controller - Cursor', () => { '', '', '', - ].join('\n')); - const config = new TestConfiguration({}); - const viewModel = createViewModel(config, model); - const cursor = new Cursor(config, model, viewModel); + ].join('\n'), {}, (editor, viewModel) => { - moveTo(cursor, 10, 10, false); - assertCursor(cursor, new Position(10, 10)); + moveTo(editor, viewModel, 10, 10, false); + assertCursor(viewModel, new Position(10, 10)); - CoreNavigationCommands.CursorColumnSelectLeft.runCoreEditorCommand(cursor, {}); - assertCursor(cursor, [ - new Selection(10, 10, 10, 9) - ]); + CoreNavigationCommands.CursorColumnSelectLeft.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, [ + new Selection(10, 10, 10, 9) + ]); - CoreNavigationCommands.CursorColumnSelectLeft.runCoreEditorCommand(cursor, {}); - assertCursor(cursor, [ - new Selection(10, 10, 10, 8) - ]); + CoreNavigationCommands.CursorColumnSelectLeft.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, [ + new Selection(10, 10, 10, 8) + ]); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - assertCursor(cursor, [ - new Selection(10, 10, 10, 9) - ]); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, [ + new Selection(10, 10, 10, 9) + ]); - CoreNavigationCommands.CursorColumnSelectUp.runCoreEditorCommand(cursor, {}); - assertCursor(cursor, [ - new Selection(10, 10, 10, 9), - new Selection(9, 10, 9, 9), - ]); + CoreNavigationCommands.CursorColumnSelectUp.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, [ + new Selection(10, 10, 10, 9), + new Selection(9, 10, 9, 9), + ]); - CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(cursor, {}); - assertCursor(cursor, [ - new Selection(10, 10, 10, 9) - ]); - - cursor.dispose(); - viewModel.dispose(); - config.dispose(); - model.dispose(); + CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, [ + new Selection(10, 10, 10, 9) + ]); + }); }); test('column select with keyboard', () => { - const model = createTextModel([ + withTestCodeEditor([ 'var gulp = require("gulp");', 'var path = require("path");', 'var rimraf = require("rimraf");', @@ -952,209 +1056,202 @@ suite('Editor Controller - Cursor', () => { 'var merge = require("merge-stream");', 'var concat = require("gulp-concat");', 'var newer = require("gulp-newer");', - ].join('\n')); - const config = new TestConfiguration({}); - const viewModel = createViewModel(config, model); - const cursor = new Cursor(config, model, viewModel); + ].join('\n'), {}, (editor, viewModel) => { - moveTo(cursor, 1, 4, false); - assertCursor(cursor, new Position(1, 4)); + moveTo(editor, viewModel, 1, 4, false); + assertCursor(viewModel, new Position(1, 4)); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - assertCursor(cursor, [ - new Selection(1, 4, 1, 5) - ]); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, [ + new Selection(1, 4, 1, 5) + ]); - CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(cursor, {}); - assertCursor(cursor, [ - new Selection(1, 4, 1, 5), - new Selection(2, 4, 2, 5) - ]); + CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, [ + new Selection(1, 4, 1, 5), + new Selection(2, 4, 2, 5) + ]); - CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(cursor, {}); - assertCursor(cursor, [ - new Selection(1, 4, 1, 5), - new Selection(2, 4, 2, 5), - new Selection(3, 4, 3, 5), - ]); + CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, [ + new Selection(1, 4, 1, 5), + new Selection(2, 4, 2, 5), + new Selection(3, 4, 3, 5), + ]); - CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(cursor, {}); - assertCursor(cursor, [ - new Selection(1, 4, 1, 5), - new Selection(2, 4, 2, 5), - new Selection(3, 4, 3, 5), - new Selection(4, 4, 4, 5), - new Selection(5, 4, 5, 5), - new Selection(6, 4, 6, 5), - new Selection(7, 4, 7, 5), - ]); + CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, [ + new Selection(1, 4, 1, 5), + new Selection(2, 4, 2, 5), + new Selection(3, 4, 3, 5), + new Selection(4, 4, 4, 5), + new Selection(5, 4, 5, 5), + new Selection(6, 4, 6, 5), + new Selection(7, 4, 7, 5), + ]); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - assertCursor(cursor, [ - new Selection(1, 4, 1, 6), - new Selection(2, 4, 2, 6), - new Selection(3, 4, 3, 6), - new Selection(4, 4, 4, 6), - new Selection(5, 4, 5, 6), - new Selection(6, 4, 6, 6), - new Selection(7, 4, 7, 6), - ]); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, [ + new Selection(1, 4, 1, 6), + new Selection(2, 4, 2, 6), + new Selection(3, 4, 3, 6), + new Selection(4, 4, 4, 6), + new Selection(5, 4, 5, 6), + new Selection(6, 4, 6, 6), + new Selection(7, 4, 7, 6), + ]); - // 10 times - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - assertCursor(cursor, [ - new Selection(1, 4, 1, 16), - new Selection(2, 4, 2, 16), - new Selection(3, 4, 3, 16), - new Selection(4, 4, 4, 16), - new Selection(5, 4, 5, 16), - new Selection(6, 4, 6, 16), - new Selection(7, 4, 7, 16), - ]); + // 10 times + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, [ + new Selection(1, 4, 1, 16), + new Selection(2, 4, 2, 16), + new Selection(3, 4, 3, 16), + new Selection(4, 4, 4, 16), + new Selection(5, 4, 5, 16), + new Selection(6, 4, 6, 16), + new Selection(7, 4, 7, 16), + ]); - // 10 times - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - assertCursor(cursor, [ - new Selection(1, 4, 1, 26), - new Selection(2, 4, 2, 26), - new Selection(3, 4, 3, 26), - new Selection(4, 4, 4, 26), - new Selection(5, 4, 5, 26), - new Selection(6, 4, 6, 26), - new Selection(7, 4, 7, 26), - ]); + // 10 times + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, [ + new Selection(1, 4, 1, 26), + new Selection(2, 4, 2, 26), + new Selection(3, 4, 3, 26), + new Selection(4, 4, 4, 26), + new Selection(5, 4, 5, 26), + new Selection(6, 4, 6, 26), + new Selection(7, 4, 7, 26), + ]); - // 2 times => reaching the ending of lines 1 and 2 - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - assertCursor(cursor, [ - new Selection(1, 4, 1, 28), - new Selection(2, 4, 2, 28), - new Selection(3, 4, 3, 28), - new Selection(4, 4, 4, 28), - new Selection(5, 4, 5, 28), - new Selection(6, 4, 6, 28), - new Selection(7, 4, 7, 28), - ]); + // 2 times => reaching the ending of lines 1 and 2 + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, [ + new Selection(1, 4, 1, 28), + new Selection(2, 4, 2, 28), + new Selection(3, 4, 3, 28), + new Selection(4, 4, 4, 28), + new Selection(5, 4, 5, 28), + new Selection(6, 4, 6, 28), + new Selection(7, 4, 7, 28), + ]); - // 4 times => reaching the ending of line 3 - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - assertCursor(cursor, [ - new Selection(1, 4, 1, 28), - new Selection(2, 4, 2, 28), - new Selection(3, 4, 3, 32), - new Selection(4, 4, 4, 32), - new Selection(5, 4, 5, 32), - new Selection(6, 4, 6, 32), - new Selection(7, 4, 7, 32), - ]); + // 4 times => reaching the ending of line 3 + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, [ + new Selection(1, 4, 1, 28), + new Selection(2, 4, 2, 28), + new Selection(3, 4, 3, 32), + new Selection(4, 4, 4, 32), + new Selection(5, 4, 5, 32), + new Selection(6, 4, 6, 32), + new Selection(7, 4, 7, 32), + ]); - // 2 times => reaching the ending of line 4 - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - assertCursor(cursor, [ - new Selection(1, 4, 1, 28), - new Selection(2, 4, 2, 28), - new Selection(3, 4, 3, 32), - new Selection(4, 4, 4, 34), - new Selection(5, 4, 5, 34), - new Selection(6, 4, 6, 34), - new Selection(7, 4, 7, 34), - ]); + // 2 times => reaching the ending of line 4 + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, [ + new Selection(1, 4, 1, 28), + new Selection(2, 4, 2, 28), + new Selection(3, 4, 3, 32), + new Selection(4, 4, 4, 34), + new Selection(5, 4, 5, 34), + new Selection(6, 4, 6, 34), + new Selection(7, 4, 7, 34), + ]); - // 1 time => reaching the ending of line 7 - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - assertCursor(cursor, [ - new Selection(1, 4, 1, 28), - new Selection(2, 4, 2, 28), - new Selection(3, 4, 3, 32), - new Selection(4, 4, 4, 34), - new Selection(5, 4, 5, 35), - new Selection(6, 4, 6, 35), - new Selection(7, 4, 7, 35), - ]); + // 1 time => reaching the ending of line 7 + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, [ + new Selection(1, 4, 1, 28), + new Selection(2, 4, 2, 28), + new Selection(3, 4, 3, 32), + new Selection(4, 4, 4, 34), + new Selection(5, 4, 5, 35), + new Selection(6, 4, 6, 35), + new Selection(7, 4, 7, 35), + ]); - // 3 times => reaching the ending of lines 5 & 6 - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - assertCursor(cursor, [ - new Selection(1, 4, 1, 28), - new Selection(2, 4, 2, 28), - new Selection(3, 4, 3, 32), - new Selection(4, 4, 4, 34), - new Selection(5, 4, 5, 37), - new Selection(6, 4, 6, 37), - new Selection(7, 4, 7, 35), - ]); + // 3 times => reaching the ending of lines 5 & 6 + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, [ + new Selection(1, 4, 1, 28), + new Selection(2, 4, 2, 28), + new Selection(3, 4, 3, 32), + new Selection(4, 4, 4, 34), + new Selection(5, 4, 5, 37), + new Selection(6, 4, 6, 37), + new Selection(7, 4, 7, 35), + ]); - // cannot go anywhere anymore - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - assertCursor(cursor, [ - new Selection(1, 4, 1, 28), - new Selection(2, 4, 2, 28), - new Selection(3, 4, 3, 32), - new Selection(4, 4, 4, 34), - new Selection(5, 4, 5, 37), - new Selection(6, 4, 6, 37), - new Selection(7, 4, 7, 35), - ]); + // cannot go anywhere anymore + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, [ + new Selection(1, 4, 1, 28), + new Selection(2, 4, 2, 28), + new Selection(3, 4, 3, 32), + new Selection(4, 4, 4, 34), + new Selection(5, 4, 5, 37), + new Selection(6, 4, 6, 37), + new Selection(7, 4, 7, 35), + ]); - // cannot go anywhere anymore even if we insist - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(cursor, {}); - assertCursor(cursor, [ - new Selection(1, 4, 1, 28), - new Selection(2, 4, 2, 28), - new Selection(3, 4, 3, 32), - new Selection(4, 4, 4, 34), - new Selection(5, 4, 5, 37), - new Selection(6, 4, 6, 37), - new Selection(7, 4, 7, 35), - ]); + // cannot go anywhere anymore even if we insist + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, [ + new Selection(1, 4, 1, 28), + new Selection(2, 4, 2, 28), + new Selection(3, 4, 3, 32), + new Selection(4, 4, 4, 34), + new Selection(5, 4, 5, 37), + new Selection(6, 4, 6, 37), + new Selection(7, 4, 7, 35), + ]); - // can easily go back - CoreNavigationCommands.CursorColumnSelectLeft.runCoreEditorCommand(cursor, {}); - assertCursor(cursor, [ - new Selection(1, 4, 1, 28), - new Selection(2, 4, 2, 28), - new Selection(3, 4, 3, 32), - new Selection(4, 4, 4, 34), - new Selection(5, 4, 5, 36), - new Selection(6, 4, 6, 36), - new Selection(7, 4, 7, 35), - ]); - - cursor.dispose(); - viewModel.dispose(); - config.dispose(); - model.dispose(); + // can easily go back + CoreNavigationCommands.CursorColumnSelectLeft.runCoreEditorCommand(viewModel, {}); + assertCursor(viewModel, [ + new Selection(1, 4, 1, 28), + new Selection(2, 4, 2, 28), + new Selection(3, 4, 3, 32), + new Selection(4, 4, 4, 34), + new Selection(5, 4, 5, 36), + new Selection(6, 4, 6, 36), + new Selection(7, 4, 7, 35), + ]); + }); }); }); @@ -1209,12 +1306,12 @@ suite('Editor Controller - Regression tests', () => { }, ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - cursor.setSelections('test', [new Selection(1, 1, 1, 13)]); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + viewModel.setSelections('test', [new Selection(1, 1, 1, 13)]); // Check that indenting maintains the selection start at column 1 CoreEditingCommands.Tab.runEditorCommand(null, editor, null); - assert.deepEqual(cursor.getSelection(), new Selection(1, 1, 1, 14)); + assert.deepEqual(viewModel.getSelection(), new Selection(1, 1, 1, 14)); }); model.dispose(); @@ -1231,20 +1328,20 @@ suite('Editor Controller - Regression tests', () => { }, ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + viewModel.type('\n', 'keyboard'); assert.equal(model.getValue(EndOfLinePreference.LF), '\n', 'assert1'); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t', 'assert2'); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + viewModel.type('\n', 'keyboard'); assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t\n\t', 'assert3'); - cursorCommand(cursor, H.Type, { text: 'x' }); + viewModel.type('x'); assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t\n\tx', 'assert4'); - CoreNavigationCommands.CursorLeft.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorLeft.runCoreEditorCommand(viewModel, {}); assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t\n\tx', 'assert5'); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); @@ -1285,10 +1382,10 @@ suite('Editor Controller - Regression tests', () => { withTestCodeEditor([ 'Hello', 'world' - ], {}, (editor, cursor) => { + ], {}, (editor, viewModel) => { const model = editor.getModel()!; - assertCursor(cursor, new Position(1, 1)); + assertCursor(viewModel, new Position(1, 1)); model.setEOL(EndOfLineSequence.LF); assert.equal(model.getValue(), 'Hello\nworld'); @@ -1314,10 +1411,10 @@ suite('Editor Controller - Regression tests', () => { const mode = new MyMode(); const model = createTextModel('\'👁\'', undefined, languageId); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { editor.setSelection(new Selection(1, 1, 1, 2)); - cursorCommand(cursor, H.Type, { text: '%' }, 'keyboard'); + viewModel.type('%', 'keyboard'); assert.equal(model.getValue(EndOfLinePreference.LF), '%\'%👁\'', 'assert1'); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); @@ -1331,56 +1428,56 @@ suite('Editor Controller - Regression tests', () => { test('issue #46208: Allow empty selections in the undo/redo stack', () => { let model = createTextModel(''); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - cursorCommand(cursor, H.Type, { text: 'Hello' }, 'keyboard'); - cursorCommand(cursor, H.Type, { text: ' ' }, 'keyboard'); - cursorCommand(cursor, H.Type, { text: 'world' }, 'keyboard'); - cursorCommand(cursor, H.Type, { text: ' ' }, 'keyboard'); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + viewModel.type('Hello', 'keyboard'); + viewModel.type(' ', 'keyboard'); + viewModel.type('world', 'keyboard'); + viewModel.type(' ', 'keyboard'); assert.equal(model.getLineContent(1), 'Hello world '); - assertCursor(cursor, new Position(1, 13)); + assertCursor(viewModel, new Position(1, 13)); - moveLeft(cursor); - moveRight(cursor); + moveLeft(editor, viewModel); + moveRight(editor, viewModel); model.pushEditOperations([], [EditOperation.replaceMove(new Range(1, 12, 1, 13), '')], () => []); assert.equal(model.getLineContent(1), 'Hello world'); - assertCursor(cursor, new Position(1, 12)); + assertCursor(viewModel, new Position(1, 12)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), 'Hello world '); - assertCursor(cursor, new Selection(1, 12, 1, 13)); + assertCursor(viewModel, new Selection(1, 12, 1, 13)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), 'Hello world'); - assertCursor(cursor, new Position(1, 12)); + assertCursor(viewModel, new Position(1, 12)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), 'Hello'); - assertCursor(cursor, new Position(1, 6)); + assertCursor(viewModel, new Position(1, 6)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), ''); - assertCursor(cursor, new Position(1, 1)); + assertCursor(viewModel, new Position(1, 1)); CoreEditingCommands.Redo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), 'Hello'); - assertCursor(cursor, new Position(1, 6)); + assertCursor(viewModel, new Position(1, 6)); CoreEditingCommands.Redo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), 'Hello world'); - assertCursor(cursor, new Position(1, 12)); + assertCursor(viewModel, new Position(1, 12)); CoreEditingCommands.Redo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), 'Hello world '); - assertCursor(cursor, new Position(1, 13)); + assertCursor(viewModel, new Position(1, 13)); CoreEditingCommands.Redo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), 'Hello world'); - assertCursor(cursor, new Position(1, 12)); + assertCursor(viewModel, new Position(1, 12)); CoreEditingCommands.Redo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), 'Hello world'); - assertCursor(cursor, new Position(1, 12)); + assertCursor(viewModel, new Position(1, 12)); }); model.dispose(); @@ -1396,13 +1493,13 @@ suite('Editor Controller - Regression tests', () => { mode.getLanguageIdentifier() ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - moveTo(cursor, 1, 6, false); - assertCursor(cursor, new Selection(1, 6, 1, 6)); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + moveTo(editor, viewModel, 1, 6, false); + assertCursor(viewModel, new Selection(1, 6, 1, 6)); CoreEditingCommands.Outdent.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), ' function baz() {'); - assertCursor(cursor, new Selection(1, 5, 1, 5)); + assertCursor(viewModel, new Selection(1, 5, 1, 5)); }); model.dispose(); @@ -1416,13 +1513,13 @@ suite('Editor Controller - Regression tests', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - moveTo(cursor, 1, 7, false); - assertCursor(cursor, new Selection(1, 7, 1, 7)); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + moveTo(editor, viewModel, 1, 7, false); + assertCursor(viewModel, new Selection(1, 7, 1, 7)); CoreEditingCommands.Outdent.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), ' '); - assertCursor(cursor, new Selection(1, 5, 1, 5)); + assertCursor(viewModel, new Selection(1, 5, 1, 5)); }); model.dispose(); @@ -1438,13 +1535,13 @@ suite('Editor Controller - Regression tests', () => { withTestCodeEditor(null, { model: model, useTabStops: false - }, (editor, cursor) => { - moveTo(cursor, 1, 9, false); - assertCursor(cursor, new Selection(1, 9, 1, 9)); + }, (editor, viewModel) => { + moveTo(editor, viewModel, 1, 9, false); + assertCursor(viewModel, new Selection(1, 9, 1, 9)); CoreEditingCommands.Outdent.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), ' '); - assertCursor(cursor, new Selection(1, 5, 1, 5)); + assertCursor(viewModel, new Selection(1, 5, 1, 5)); }); model.dispose(); @@ -1466,13 +1563,13 @@ suite('Editor Controller - Regression tests', () => { }, ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - moveTo(cursor, 7, 1, false); - assertCursor(cursor, new Selection(7, 1, 7, 1)); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + moveTo(editor, viewModel, 7, 1, false); + assertCursor(viewModel, new Selection(7, 1, 7, 1)); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(7), '\t'); - assertCursor(cursor, new Selection(7, 2, 7, 2)); + assertCursor(viewModel, new Selection(7, 2, 7, 2)); }); model.dispose(); @@ -1484,13 +1581,13 @@ suite('Editor Controller - Regression tests', () => { withTestCodeEditor([ 'asdasd', 'qwerty' - ], {}, (editor, cursor) => { + ], {}, (editor, viewModel) => { const model = editor.getModel()!; - moveTo(cursor, 2, 1, false); - assertCursor(cursor, new Selection(2, 1, 2, 1)); + moveTo(editor, viewModel, 2, 1, false); + assertCursor(viewModel, new Selection(2, 1, 2, 1)); - cursorCommand(cursor, H.Cut, null, 'keyboard'); + viewModel.cut('keyboard'); assert.equal(model.getLineCount(), 1); assert.equal(model.getLineContent(1), 'asdasd'); @@ -1500,17 +1597,17 @@ suite('Editor Controller - Regression tests', () => { withTestCodeEditor([ 'asdasd', '' - ], {}, (editor, cursor) => { + ], {}, (editor, viewModel) => { const model = editor.getModel()!; - moveTo(cursor, 2, 1, false); - assertCursor(cursor, new Selection(2, 1, 2, 1)); + moveTo(editor, viewModel, 2, 1, false); + assertCursor(viewModel, new Selection(2, 1, 2, 1)); - cursorCommand(cursor, H.Cut, null, 'keyboard'); + viewModel.cut('keyboard'); assert.equal(model.getLineCount(), 1); assert.equal(model.getLineContent(1), 'asdasd'); - cursorCommand(cursor, H.Cut, null, 'keyboard'); + viewModel.cut('keyboard'); assert.equal(model.getLineCount(), 1); assert.equal(model.getLineContent(1), ''); }); @@ -1523,16 +1620,16 @@ suite('Editor Controller - Regression tests', () => { 'hello' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 1, 3, false); - moveTo(cursor, 1, 5, true); - assertCursor(cursor, new Selection(1, 3, 1, 5)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 1, 3, false); + moveTo(editor, viewModel, 1, 5, true); + assertCursor(viewModel, new Selection(1, 3, 1, 5)); - cursorCommand(cursor, H.Type, { text: '(' }, 'keyboard'); - assertCursor(cursor, new Selection(1, 4, 1, 6)); + viewModel.type('(', 'keyboard'); + assertCursor(viewModel, new Selection(1, 4, 1, 6)); - cursorCommand(cursor, H.Type, { text: '(' }, 'keyboard'); - assertCursor(cursor, new Selection(1, 5, 1, 7)); + viewModel.type('(', 'keyboard'); + assertCursor(viewModel, new Selection(1, 5, 1, 7)); }); mode.dispose(); }); @@ -1547,13 +1644,13 @@ suite('Editor Controller - Regression tests', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - moveTo(cursor, 3, 2, false); - moveTo(cursor, 1, 14, true); - assertCursor(cursor, new Selection(3, 2, 1, 14)); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + moveTo(editor, viewModel, 3, 2, false); + moveTo(editor, viewModel, 1, 14, true); + assertCursor(viewModel, new Selection(3, 2, 1, 14)); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); - assertCursor(cursor, new Selection(1, 14, 1, 14)); + assertCursor(viewModel, new Selection(1, 14, 1, 14)); assert.equal(model.getLineCount(), 1); assert.equal(model.getLineContent(1), 'function baz(;'); }); @@ -1568,11 +1665,11 @@ suite('Editor Controller - Regression tests', () => { 'line1', 'line2' ], - }, (model, cursor) => { - moveTo(cursor, 2, 1, false); - moveTo(cursor, 2, 6, true); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 2, 1, false); + moveTo(editor, viewModel, 2, 6, true); - cursorCommand(cursor, H.Paste, { text: 'line1\n', pasteOnNewLine: true }); + viewModel.paste('line1\n', true); assert.equal(model.getLineContent(1), 'line1'); assert.equal(model.getLineContent(2), 'line1'); @@ -1587,10 +1684,10 @@ suite('Editor Controller - Regression tests', () => { 'line sel 2', 'line3' ], - }, (model, cursor) => { - cursor.setSelections('test', [new Selection(2, 6, 2, 9)]); + }, (editor, model, viewModel) => { + viewModel.setSelections('test', [new Selection(2, 6, 2, 9)]); - cursorCommand(cursor, H.Paste, { text: 'line1\n', pasteOnNewLine: true }); + viewModel.paste('line1\n', true); assert.equal(model.getLineContent(1), 'line1'); assert.equal(model.getLineContent(2), 'line line1'); @@ -1606,17 +1703,17 @@ suite('Editor Controller - Regression tests', () => { 'line2', 'line3' ], - }, (model, cursor) => { - cursor.setSelections('test', [new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1)]); + }, (editor, model, viewModel) => { + viewModel.setSelections('test', [new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1)]); - cursorCommand(cursor, H.Paste, { - text: 'a\nb\nc\nd', - pasteOnNewLine: false, - multicursorText: [ + viewModel.paste( + 'a\nb\nc\nd', + false, + [ 'a\nb', 'c\nd' ] - }); + ); assert.equal(model.getValue(), [ 'a', @@ -1636,19 +1733,19 @@ suite('Editor Controller - Regression tests', () => { 'test', 'test' ], - }, (model, cursor) => { - cursor.setSelections('test', [ + }, (editor, model, viewModel) => { + viewModel.setSelections('test', [ new Selection(1, 1, 1, 5), new Selection(2, 1, 2, 5), new Selection(3, 1, 3, 5), new Selection(4, 1, 4, 5), ]); - cursorCommand(cursor, H.Paste, { - text: 'aaa\nbbb\nccc\n', - pasteOnNewLine: false, - multicursorText: null - }); + viewModel.paste( + 'aaa\nbbb\nccc\n', + false, + null + ); assert.equal(model.getValue(), [ 'aaa', @@ -1679,19 +1776,19 @@ suite('Editor Controller - Regression tests', () => { 'test', 'test' ], - }, (model, cursor) => { - cursor.setSelections('test', [ + }, (editor, model, viewModel) => { + viewModel.setSelections('test', [ new Selection(1, 1, 1, 5), new Selection(2, 1, 2, 5), new Selection(3, 1, 3, 5), new Selection(4, 1, 4, 5), ]); - cursorCommand(cursor, H.Paste, { - text: 'aaa\r\nbbb\r\nccc\r\nddd\r\n', - pasteOnNewLine: false, - multicursorText: null - }); + viewModel.paste( + 'aaa\r\nbbb\r\nccc\r\nddd\r\n', + false, + null + ); assert.equal(model.getValue(), [ 'aaa', @@ -1709,14 +1806,14 @@ suite('Editor Controller - Regression tests', () => { 'line2', 'line3' ], - }, (model, cursor) => { - cursor.setSelections('test', [new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1), new Selection(3, 1, 3, 1)]); + }, (editor, model, viewModel) => { + viewModel.setSelections('test', [new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1), new Selection(3, 1, 3, 1)]); - cursorCommand(cursor, H.Paste, { - text: 'a\nb\nc', - pasteOnNewLine: false, - multicursorText: null - }); + viewModel.paste( + 'a\nb\nc', + false, + null + ); assert.equal(model.getValue(), [ 'aline1', @@ -1733,14 +1830,14 @@ suite('Editor Controller - Regression tests', () => { 'line2', 'line3' ], - }, (model, cursor) => { - cursor.setSelections('test', [new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1), new Selection(3, 1, 3, 1)]); + }, (editor, model, viewModel) => { + viewModel.setSelections('test', [new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1), new Selection(3, 1, 3, 1)]); - cursorCommand(cursor, H.Paste, { - text: 'a\nb\nc\n', - pasteOnNewLine: false, - multicursorText: null - }); + viewModel.paste( + 'a\nb\nc\n', + false, + null + ); assert.equal(model.getValue(), [ 'aline1', @@ -1759,15 +1856,15 @@ suite('Editor Controller - Regression tests', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - moveTo(cursor, 1, 1, false); - moveTo(cursor, 3, 4, true); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + moveTo(editor, viewModel, 1, 1, false); + moveTo(editor, viewModel, 3, 4, true); let isFirst = true; model.onDidChangeContent(() => { if (isFirst) { isFirst = false; - cursorCommand(cursor, H.Type, { text: '\t' }, 'keyboard'); + viewModel.type('\t', 'keyboard'); } }); @@ -1809,10 +1906,10 @@ suite('Editor Controller - Regression tests', () => { 'just some text', ], languageIdentifier: null - }, (model, cursor) => { - moveTo(cursor, 3, 1, false); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 3, 1, false); - cursorCommand(cursor, H.Type, { text: '😍' }, 'keyboard'); + viewModel.type('😍', 'keyboard'); assert.equal(model.getValue(), [ 'some lines', @@ -1833,8 +1930,8 @@ suite('Editor Controller - Regression tests', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - moveTo(cursor, 3, 2, false); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + moveTo(editor, viewModel, 3, 2, false); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(3), '\t \tx: 3'); }); @@ -1853,9 +1950,9 @@ suite('Editor Controller - Regression tests', () => { } ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - moveTo(cursor, 1, 15, false); - moveTo(cursor, 1, 22, true); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + moveTo(editor, viewModel, 1, 15, false); + moveTo(editor, viewModel, 1, 22, true); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), 'var foo = 123;\t// this is a comment'); }); @@ -1869,8 +1966,8 @@ suite('Editor Controller - Regression tests', () => { text: [ ' /* Just some more text a+= 3 +5-3 + 7 */ ' ], - }, (model, cursor) => { - moveTo(cursor, 1, 1, false); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 1, 1, false); function assertWordRight(col: number, expectedCol: number) { let args = { @@ -1880,13 +1977,13 @@ suite('Editor Controller - Regression tests', () => { } }; if (col === 1) { - CoreNavigationCommands.WordSelect.runCoreEditorCommand(cursor, args); + CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, args); } else { - CoreNavigationCommands.WordSelectDrag.runCoreEditorCommand(cursor, args); + CoreNavigationCommands.WordSelectDrag.runCoreEditorCommand(viewModel, args); } - assert.equal(cursor.getSelection().startColumn, 1, 'TEST FOR ' + col); - assert.equal(cursor.getSelection().endColumn, expectedCol, 'TEST FOR ' + col); + assert.equal(viewModel.getSelection().startColumn, 1, 'TEST FOR ' + col); + assert.equal(viewModel.getSelection().endColumn, expectedCol, 'TEST FOR ' + col); } assertWordRight(1, ' '.length + 1); @@ -1949,12 +2046,12 @@ suite('Editor Controller - Regression tests', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - CoreNavigationCommands.WordSelect.runCoreEditorCommand(cursor, { position: new Position(1, 8) }); - assert.deepEqual(cursor.getSelection(), new Selection(1, 6, 1, 10)); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 8) }); + assert.deepEqual(viewModel.getSelection(), new Selection(1, 6, 1, 10)); - CoreNavigationCommands.WordSelectDrag.runCoreEditorCommand(cursor, { position: new Position(1, 8) }); - assert.deepEqual(cursor.getSelection(), new Selection(1, 6, 1, 10)); + CoreNavigationCommands.WordSelectDrag.runCoreEditorCommand(viewModel, { position: new Position(1, 8) }); + assert.deepEqual(viewModel.getSelection(), new Selection(1, 6, 1, 10)); }); model.dispose(); @@ -1967,38 +2064,38 @@ suite('Editor Controller - Regression tests', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - CoreNavigationCommands.WordSelect.runCoreEditorCommand(cursor, { position: new Position(1, 5) }); - assert.deepEqual(cursor.getSelection(), new Selection(1, 5, 1, 8)); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 5) }); + assert.deepEqual(viewModel.getSelection(), new Selection(1, 5, 1, 8)); }); model.dispose(); }); test('issue #9675: Undo/Redo adds a stop in between CHN Characters', () => { - withTestCodeEditor([], {}, (editor, cursor) => { + withTestCodeEditor([], {}, (editor, viewModel) => { const model = editor.getModel()!; - assertCursor(cursor, new Position(1, 1)); + assertCursor(viewModel, new Position(1, 1)); // Typing sennsei in Japanese - Hiragana - cursorCommand(cursor, H.Type, { text: 'īŊ“' }, 'keyboard'); - cursorCommand(cursor, H.ReplacePreviousChar, { text: 'せ', replaceCharCnt: 1 }); - cursorCommand(cursor, H.ReplacePreviousChar, { text: 'せīŊŽ', replaceCharCnt: 1 }); - cursorCommand(cursor, H.ReplacePreviousChar, { text: 'せん', replaceCharCnt: 2 }); - cursorCommand(cursor, H.ReplacePreviousChar, { text: 'せんīŊ“', replaceCharCnt: 2 }); - cursorCommand(cursor, H.ReplacePreviousChar, { text: 'せんせ', replaceCharCnt: 3 }); - cursorCommand(cursor, H.ReplacePreviousChar, { text: 'せんせ', replaceCharCnt: 3 }); - cursorCommand(cursor, H.ReplacePreviousChar, { text: 'せんせい', replaceCharCnt: 3 }); - cursorCommand(cursor, H.ReplacePreviousChar, { text: 'せんせい', replaceCharCnt: 4 }); - cursorCommand(cursor, H.ReplacePreviousChar, { text: 'せんせい', replaceCharCnt: 4 }); - cursorCommand(cursor, H.ReplacePreviousChar, { text: 'せんせい', replaceCharCnt: 4 }); + viewModel.type('īŊ“', 'keyboard'); + viewModel.replacePreviousChar('せ', 1); + viewModel.replacePreviousChar('せīŊŽ', 1); + viewModel.replacePreviousChar('せん', 2); + viewModel.replacePreviousChar('せんīŊ“', 2); + viewModel.replacePreviousChar('せんせ', 3); + viewModel.replacePreviousChar('せんせ', 3); + viewModel.replacePreviousChar('せんせい', 3); + viewModel.replacePreviousChar('せんせい', 4); + viewModel.replacePreviousChar('せんせい', 4); + viewModel.replacePreviousChar('せんせい', 4); assert.equal(model.getLineContent(1), 'せんせい'); - assertCursor(cursor, new Position(1, 5)); + assertCursor(viewModel, new Position(1, 5)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), ''); - assertCursor(cursor, new Position(1, 1)); + assertCursor(viewModel, new Position(1, 1)); }); }); @@ -2012,23 +2109,23 @@ suite('Editor Controller - Regression tests', () => { } usingCursor({ text: text - }, (model, cursor) => { + }, (editor, model, viewModel) => { let selections: Selection[] = []; for (let i = 0; i < LINE_CNT; i++) { selections[i] = new Selection(i + 1, 1, i + 1, 1); } - cursor.setSelections('test', selections); + viewModel.setSelections('test', selections); - cursorCommand(cursor, H.Type, { text: 'n' }, 'keyboard'); - cursorCommand(cursor, H.Type, { text: 'n' }, 'keyboard'); + viewModel.type('n', 'keyboard'); + viewModel.type('n', 'keyboard'); for (let i = 0; i < LINE_CNT; i++) { assert.equal(model.getLineContent(i + 1), 'nnasd', 'line #' + (i + 1)); } - assert.equal(cursor.getSelections().length, LINE_CNT); - assert.equal(cursor.getSelections()[LINE_CNT - 1].startLineNumber, LINE_CNT); + assert.equal(viewModel.getSelections().length, LINE_CNT); + assert.equal(viewModel.getSelections()[LINE_CNT - 1].startLineNumber, LINE_CNT); }); }); @@ -2038,13 +2135,13 @@ suite('Editor Controller - Regression tests', () => { 'first line', 'second line' ] - }, (model, cursor) => { + }, (editor, model, viewModel) => { model.setEOL(EndOfLineSequence.CRLF); - cursor.setSelections('test', [new Selection(2, 2, 2, 2)]); + viewModel.setSelections('test', [new Selection(2, 2, 2, 2)]); model.setEOL(EndOfLineSequence.LF); - assertCursor(cursor, new Selection(2, 2, 2, 2)); + assertCursor(viewModel, new Selection(2, 2, 2, 2)); }); }); @@ -2054,17 +2151,17 @@ suite('Editor Controller - Regression tests', () => { 'first line', 'second line' ] - }, (model, cursor) => { + }, (editor, model, viewModel) => { model.setEOL(EndOfLineSequence.CRLF); - cursor.setSelections('test', [new Selection(2, 2, 2, 2)]); + viewModel.setSelections('test', [new Selection(2, 2, 2, 2)]); model.setValue([ 'different first line', 'different second line', 'new third line' ].join('\n')); - assertCursor(cursor, new Selection(1, 1, 1, 1)); + assertCursor(viewModel, new Selection(1, 1, 1, 1)); }); }); @@ -2077,66 +2174,119 @@ suite('Editor Controller - Regression tests', () => { 'consectetur ', 'adipiscing elit', ].join('') - ], { wordWrap: 'wordWrapColumn', wordWrapColumn: 16 }, (editor, cursor) => { - cursor.setSelections('test', [new Selection(1, 7, 1, 7)]); + ], { wordWrap: 'wordWrapColumn', wordWrapColumn: 16 }, (editor, viewModel) => { + viewModel.setSelections('test', [new Selection(1, 7, 1, 7)]); - moveRight(cursor); - assertCursor(cursor, new Selection(1, 8, 1, 8)); + moveRight(editor, viewModel); + assertCursor(viewModel, new Selection(1, 8, 1, 8)); - moveRight(cursor); - assertCursor(cursor, new Selection(1, 9, 1, 9)); + moveRight(editor, viewModel); + assertCursor(viewModel, new Selection(1, 9, 1, 9)); - moveRight(cursor); - assertCursor(cursor, new Selection(1, 10, 1, 10)); + moveRight(editor, viewModel); + assertCursor(viewModel, new Selection(1, 10, 1, 10)); - moveRight(cursor); - assertCursor(cursor, new Selection(1, 11, 1, 11)); + moveRight(editor, viewModel); + assertCursor(viewModel, new Selection(1, 11, 1, 11)); - moveRight(cursor); - assertCursor(cursor, new Selection(1, 12, 1, 12)); + moveRight(editor, viewModel); + assertCursor(viewModel, new Selection(1, 12, 1, 12)); - moveRight(cursor); - assertCursor(cursor, new Selection(1, 13, 1, 13)); + moveRight(editor, viewModel); + assertCursor(viewModel, new Selection(1, 13, 1, 13)); // moving to view line 2 - moveRight(cursor); - assertCursor(cursor, new Selection(1, 14, 1, 14)); + moveRight(editor, viewModel); + assertCursor(viewModel, new Selection(1, 14, 1, 14)); - moveLeft(cursor); - assertCursor(cursor, new Selection(1, 13, 1, 13)); + moveLeft(editor, viewModel); + assertCursor(viewModel, new Selection(1, 13, 1, 13)); // moving back to view line 1 - moveLeft(cursor); - assertCursor(cursor, new Selection(1, 12, 1, 12)); + moveLeft(editor, viewModel); + assertCursor(viewModel, new Selection(1, 12, 1, 12)); + }); + }); + + test('issue #98320: Multi-Cursor, Wrap lines and cursorSelectRight ==> cursors out of sync', () => { + // a single model line => 4 view lines + withTestCodeEditor([ + [ + 'lorem_ipsum-1993x11x13', + 'dolor_sit_amet-1998x04x27', + 'consectetur-2007x10x08', + 'adipiscing-2012x07x27', + 'elit-2015x02x27', + ].join('\n') + ], { wordWrap: 'wordWrapColumn', wordWrapColumn: 16 }, (editor, viewModel) => { + viewModel.setSelections('test', [ + new Selection(1, 13, 1, 13), + new Selection(2, 16, 2, 16), + new Selection(3, 13, 3, 13), + new Selection(4, 12, 4, 12), + new Selection(5, 6, 5, 6), + ]); + assertCursor(viewModel, [ + new Selection(1, 13, 1, 13), + new Selection(2, 16, 2, 16), + new Selection(3, 13, 3, 13), + new Selection(4, 12, 4, 12), + new Selection(5, 6, 5, 6), + ]); + + moveRight(editor, viewModel, true); + assertCursor(viewModel, [ + new Selection(1, 13, 1, 14), + new Selection(2, 16, 2, 17), + new Selection(3, 13, 3, 14), + new Selection(4, 12, 4, 13), + new Selection(5, 6, 5, 7), + ]); + + moveRight(editor, viewModel, true); + assertCursor(viewModel, [ + new Selection(1, 13, 1, 15), + new Selection(2, 16, 2, 18), + new Selection(3, 13, 3, 15), + new Selection(4, 12, 4, 14), + new Selection(5, 6, 5, 8), + ]); + + moveRight(editor, viewModel, true); + assertCursor(viewModel, [ + new Selection(1, 13, 1, 16), + new Selection(2, 16, 2, 19), + new Selection(3, 13, 3, 16), + new Selection(4, 12, 4, 15), + new Selection(5, 6, 5, 9), + ]); + + moveRight(editor, viewModel, true); + assertCursor(viewModel, [ + new Selection(1, 13, 1, 17), + new Selection(2, 16, 2, 20), + new Selection(3, 13, 3, 17), + new Selection(4, 12, 4, 16), + new Selection(5, 6, 5, 10), + ]); }); }); test('issue #41573 - delete across multiple lines does not shrink the selection when word wraps', () => { - const model = createTextModel([ + withTestCodeEditor([ 'Authorization: \'Bearer pHKRfCTFSnGxs6akKlb9ddIXcca0sIUSZJutPHYqz7vEeHdMTMh0SGN0IGU3a0n59DXjTLRsj5EJ2u33qLNIFi9fk5XF8pK39PndLYUZhPt4QvHGLScgSkK0L4gwzkzMloTQPpKhqiikiIOvyNNSpd2o8j29NnOmdTUOKi9DVt74PD2ohKxyOrWZ6oZprTkb3eKajcpnS0LABKfaw2rmv4\',' - ].join('\n')); - const config = new TestConfiguration({ - wordWrap: 'wordWrapColumn', - wordWrapColumn: 100 + ].join('\n'), { wordWrap: 'wordWrapColumn', wordWrapColumn: 100 }, (editor, viewModel) => { + moveTo(editor, viewModel, 1, 43, false); + moveTo(editor, viewModel, 1, 147, true); + assertCursor(viewModel, new Selection(1, 43, 1, 147)); + + editor.getModel().applyEdits([{ + range: new Range(1, 1, 1, 43), + text: '' + }]); + + assertCursor(viewModel, new Selection(1, 1, 1, 105)); }); - const viewModel = createViewModel(config, model); - const cursor = new Cursor(config, model, viewModel); - - moveTo(cursor, 1, 43, false); - moveTo(cursor, 1, 147, true); - assertCursor(cursor, new Selection(1, 43, 1, 147)); - - model.applyEdits([{ - range: new Range(1, 1, 1, 43), - text: '' - }]); - - assertCursor(cursor, new Selection(1, 1, 1, 105)); - - cursor.dispose(); - viewModel.dispose(); - config.dispose(); - model.dispose(); }); test('issue #22717: Moving text cursor cause an incorrect position in Chinese', () => { @@ -2146,20 +2296,20 @@ suite('Editor Controller - Regression tests', () => { '一äēŒä¸‰å››äē”六七å…Ģ䚝十', '12345678901234567890', ].join('\n') - ], {}, (editor, cursor) => { - cursor.setSelections('test', [new Selection(1, 5, 1, 5)]); + ], {}, (editor, viewModel) => { + viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]); - moveDown(cursor); - assertCursor(cursor, new Selection(2, 9, 2, 9)); + moveDown(editor, viewModel); + assertCursor(viewModel, new Selection(2, 9, 2, 9)); - moveRight(cursor); - assertCursor(cursor, new Selection(2, 10, 2, 10)); + moveRight(editor, viewModel); + assertCursor(viewModel, new Selection(2, 10, 2, 10)); - moveRight(cursor); - assertCursor(cursor, new Selection(2, 11, 2, 11)); + moveRight(editor, viewModel); + assertCursor(viewModel, new Selection(2, 11, 2, 11)); - moveUp(cursor); - assertCursor(cursor, new Selection(1, 6, 1, 6)); + moveUp(editor, viewModel); + assertCursor(viewModel, new Selection(1, 6, 1, 6)); }); }); @@ -2170,7 +2320,7 @@ suite('Editor Controller - Regression tests', () => { ].join('\n') ); - withTestCodeEditor(null, { readOnly: true, model: model }, (editor, cursor) => { + withTestCodeEditor(null, { readOnly: true, model: model }, (editor, viewModel) => { model.pushEditOperations([new Selection(1, 1, 1, 1)], [{ range: new Range(1, 1, 1, 1), text: 'Hello world!' @@ -2221,7 +2371,7 @@ suite('Editor Controller - Regression tests', () => { ].join('\n') ); - withTestCodeEditor(null, { multiCursorMergeOverlapping: false, model: model }, (editor, cursor) => { + withTestCodeEditor(null, { multiCursorMergeOverlapping: false, model: model }, (editor, viewModel) => { editor.setSelections([ new Selection(1, 12, 1, 12), new Selection(1, 16, 1, 16), @@ -2231,14 +2381,14 @@ suite('Editor Controller - Regression tests', () => { CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); - assertCursor(cursor, [ + assertCursor(viewModel, [ new Selection(1, 11, 1, 11), new Selection(1, 14, 1, 14), new Selection(2, 11, 2, 11), new Selection(2, 11, 2, 11), ]); - cursorCommand(cursor, H.Type, { text: '\'' }, 'keyboard'); + viewModel.type('\'', 'keyboard'); assert.equal(model.getLineContent(1), 'const a = \'foo\';'); assert.equal(model.getLineContent(2), 'const b = \'\''); @@ -2254,7 +2404,7 @@ suite('Editor Controller - Regression tests', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { editor.setSelections([ new Selection(1, 4, 1, 4) ]); @@ -2264,17 +2414,17 @@ suite('Editor Controller - Regression tests', () => { text: '*', forceMoveMarkers: true }]); - assertCursor(cursor, [ + assertCursor(viewModel, [ new Selection(1, 5, 1, 5), ]); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); - assertCursor(cursor, [ + assertCursor(viewModel, [ new Selection(1, 4, 1, 4), ]); CoreEditingCommands.Redo.runEditorCommand(null, editor, null); - assertCursor(cursor, [ + assertCursor(viewModel, [ new Selection(1, 5, 1, 5), ]); }); @@ -2289,7 +2439,7 @@ suite('Editor Controller - Regression tests', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { editor.setSelections([ new Selection(1, 1, 1, 1) ]); @@ -2298,12 +2448,12 @@ suite('Editor Controller - Regression tests', () => { range: new Range(1, 1, 1, 3), text: '' }]); - assertCursor(cursor, [ + assertCursor(viewModel, [ new Selection(1, 1, 1, 1), ]); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); - assertCursor(cursor, [ + assertCursor(viewModel, [ new Selection(1, 1, 1, 1), ]); @@ -2311,7 +2461,7 @@ suite('Editor Controller - Regression tests', () => { range: new Range(1, 1, 1, 2), text: '' }]); - assertCursor(cursor, [ + assertCursor(viewModel, [ new Selection(1, 1, 1, 1), ]); }); @@ -2327,17 +2477,17 @@ suite('Editor Controller - Regression tests', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { editor.setSelections([ new Selection(2, 1, 2, 1) ]); - cursorCommand(cursor, H.Paste, { text: 'something\n', pasteOnNewLine: true }); + viewModel.paste('something\n', true); assert.equal(model.getValue(), [ 'abc123', 'something', '' ].join('\n')); - assertCursor(cursor, new Position(3, 1)); + assertCursor(viewModel, new Position(3, 1)); }); model.dispose(); @@ -2350,7 +2500,7 @@ suite('Editor Controller - Regression tests', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { editor.setSelections([ new Selection(1, 7, 1, 7) ]); @@ -2389,9 +2539,9 @@ suite('Editor Controller - Cursor Configuration', () => { '', '1' ] - }, (model, cursor) => { - CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { position: new Position(1, 21), source: 'keyboard' }); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + }, (editor, model, viewModel) => { + CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(1, 21), source: 'keyboard' }); + viewModel.type('\n', 'keyboard'); assert.equal(model.getLineContent(1), ' \tMy First Line\t '); assert.equal(model.getLineContent(2), ' '); }); @@ -2412,58 +2562,58 @@ suite('Editor Controller - Cursor Configuration', () => { } ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { // Tab on column 1 - CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { position: new Position(2, 1) }); + CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 1) }); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), ' My Second Line123'); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); // Tab on column 2 assert.equal(model.getLineContent(2), 'My Second Line123'); - CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { position: new Position(2, 2) }); + CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 2) }); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), 'M y Second Line123'); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); // Tab on column 3 assert.equal(model.getLineContent(2), 'My Second Line123'); - CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { position: new Position(2, 3) }); + CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 3) }); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), 'My Second Line123'); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); // Tab on column 4 assert.equal(model.getLineContent(2), 'My Second Line123'); - CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { position: new Position(2, 4) }); + CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 4) }); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), 'My Second Line123'); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); // Tab on column 5 assert.equal(model.getLineContent(2), 'My Second Line123'); - CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { position: new Position(2, 5) }); + CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 5) }); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), 'My S econd Line123'); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); // Tab on column 5 assert.equal(model.getLineContent(2), 'My Second Line123'); - CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { position: new Position(2, 5) }); + CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 5) }); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), 'My S econd Line123'); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); // Tab on column 13 assert.equal(model.getLineContent(2), 'My Second Line123'); - CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { position: new Position(2, 13) }); + CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 13) }); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), 'My Second Li ne123'); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); // Tab on column 14 assert.equal(model.getLineContent(2), 'My Second Line123'); - CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { position: new Position(2, 14) }); + CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 14) }); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), 'My Second Lin e123'); }); @@ -2478,11 +2628,11 @@ suite('Editor Controller - Cursor Configuration', () => { '\thello' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 1, 7, false); - assertCursor(cursor, new Selection(1, 7, 1, 7)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 1, 7, false); + assertCursor(viewModel, new Selection(1, 7, 1, 7)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + viewModel.type('\n', 'keyboard'); assert.equal(model.getValue(EndOfLinePreference.CRLF), '\thello\r\n '); }); mode.dispose(); @@ -2495,11 +2645,11 @@ suite('Editor Controller - Cursor Configuration', () => { '\thello' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 1, 7, false); - assertCursor(cursor, new Selection(1, 7, 1, 7)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 1, 7, false); + assertCursor(viewModel, new Selection(1, 7, 1, 7)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + viewModel.type('\n', 'keyboard'); assert.equal(model.getValue(EndOfLinePreference.CRLF), '\thello\r\n '); }); mode.dispose(); @@ -2512,11 +2662,11 @@ suite('Editor Controller - Cursor Configuration', () => { '\thell()' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 1, 7, false); - assertCursor(cursor, new Selection(1, 7, 1, 7)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 1, 7, false); + assertCursor(viewModel, new Selection(1, 7, 1, 7)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + viewModel.type('\n', 'keyboard'); assert.equal(model.getValue(EndOfLinePreference.CRLF), '\thell(\r\n \r\n )'); }); mode.dispose(); @@ -2530,16 +2680,16 @@ suite('Editor Controller - Cursor Configuration', () => { modelOpts: { trimAutoWhitespace: false } - }, (model, cursor) => { + }, (editor, model, viewModel) => { // Move cursor to the end, verify that we do not trim whitespaces if line has values - moveTo(cursor, 1, model.getLineContent(1).length + 1); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + moveTo(editor, viewModel, 1, model.getLineContent(1).length + 1); + viewModel.type('\n', 'keyboard'); assert.equal(model.getLineContent(1), ' some line abc '); assert.equal(model.getLineContent(2), ' '); // Try to enter again, we should trimmed previous line - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + viewModel.type('\n', 'keyboard'); assert.equal(model.getLineContent(1), ' some line abc '); assert.equal(model.getLineContent(2), ' '); assert.equal(model.getLineContent(3), ' '); @@ -2551,13 +2701,13 @@ suite('Editor Controller - Cursor Configuration', () => { text: [ ' ' ] - }, (model, cursor) => { - moveTo(cursor, 1, model.getLineContent(1).length + 1); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 1, model.getLineContent(1).length + 1); + viewModel.type('\n', 'keyboard'); assert.equal(model.getLineContent(1), ' '); assert.equal(model.getLineContent(2), ' '); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + viewModel.type('\n', 'keyboard'); assert.equal(model.getLineContent(1), ' '); assert.equal(model.getLineContent(2), ''); assert.equal(model.getLineContent(3), ' '); @@ -2571,10 +2721,10 @@ suite('Editor Controller - Cursor Configuration', () => { 'function foo (params: string) {}' ], languageIdentifier: mode.getLanguageIdentifier(), - }, (model, cursor) => { + }, (editor, model, viewModel) => { - moveTo(cursor, 1, 32); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + moveTo(editor, viewModel, 1, 32); + viewModel.type('\n', 'keyboard'); assert.equal(model.getLineContent(1), 'function foo (params: string) {'); assert.equal(model.getLineContent(2), ' '); assert.equal(model.getLineContent(3), '}'); @@ -2585,7 +2735,7 @@ suite('Editor Controller - Cursor Configuration', () => { public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { builder.addEditOperation(new Range(1, 13, 1, 14), ''); - this._selectionId = builder.trackSelection(cursor.getSelection()); + this._selectionId = builder.trackSelection(viewModel.getSelection()); } public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { @@ -2594,7 +2744,7 @@ suite('Editor Controller - Cursor Configuration', () => { } - cursor.trigger('autoFormat', Handler.ExecuteCommand, new TestCommand()); + viewModel.executeCommand(new TestCommand(), 'autoFormat'); assert.equal(model.getLineContent(1), 'function foo(params: string) {'); assert.equal(model.getLineContent(2), ' '); assert.equal(model.getLineContent(3), '}'); @@ -2613,9 +2763,9 @@ suite('Editor Controller - Cursor Configuration', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { - moveTo(cursor, 3, 1); + moveTo(editor, viewModel, 3, 1); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), ' if (a) {'); assert.equal(model.getLineContent(2), ' '); @@ -2623,7 +2773,7 @@ suite('Editor Controller - Cursor Configuration', () => { assert.equal(model.getLineContent(4), ''); assert.equal(model.getLineContent(5), ' }'); - moveTo(cursor, 4, 1); + moveTo(editor, viewModel, 4, 1); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), ' if (a) {'); assert.equal(model.getLineContent(2), ' '); @@ -2631,8 +2781,8 @@ suite('Editor Controller - Cursor Configuration', () => { assert.equal(model.getLineContent(4), ' '); assert.equal(model.getLineContent(5), ' }'); - moveTo(cursor, 5, model.getLineMaxColumn(5)); - cursorCommand(cursor, H.Type, { text: 'something' }, 'keyboard'); + moveTo(editor, viewModel, 5, model.getLineMaxColumn(5)); + viewModel.type('something', 'keyboard'); assert.equal(model.getLineContent(1), ' if (a) {'); assert.equal(model.getLineContent(2), ' '); assert.equal(model.getLineContent(3), ''); @@ -2650,16 +2800,16 @@ suite('Editor Controller - Cursor Configuration', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { // Move cursor to the end, verify that we do not trim whitespaces if line has values - moveTo(cursor, 1, model.getLineContent(1).length + 1); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + moveTo(editor, viewModel, 1, model.getLineContent(1).length + 1); + viewModel.type('\n', 'keyboard'); assert.equal(model.getLineContent(1), ' some line abc '); assert.equal(model.getLineContent(2), ' '); // Try to enter again, we should trimmed previous line - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + viewModel.type('\n', 'keyboard'); assert.equal(model.getLineContent(1), ' some line abc '); assert.equal(model.getLineContent(2), ''); assert.equal(model.getLineContent(3), ' '); @@ -2671,15 +2821,15 @@ suite('Editor Controller - Cursor Configuration', () => { assert.equal(model.getLineContent(3), ' '); // Enter and verify that trimmed again - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + viewModel.type('\n', 'keyboard'); assert.equal(model.getLineContent(1), ' some line abc '); assert.equal(model.getLineContent(2), ''); assert.equal(model.getLineContent(3), ''); assert.equal(model.getLineContent(4), ' '); // Trimmed if we will keep only text - moveTo(cursor, 1, 5); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + moveTo(editor, viewModel, 1, 5); + viewModel.type('\n', 'keyboard'); assert.equal(model.getLineContent(1), ' '); assert.equal(model.getLineContent(2), ' some line abc '); assert.equal(model.getLineContent(3), ''); @@ -2687,9 +2837,9 @@ suite('Editor Controller - Cursor Configuration', () => { assert.equal(model.getLineContent(5), ''); // Trimmed if we will keep only text by selection - moveTo(cursor, 2, 5); - moveTo(cursor, 3, 1, true); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + moveTo(editor, viewModel, 2, 5); + moveTo(editor, viewModel, 3, 1, true); + viewModel.type('\n', 'keyboard'); assert.equal(model.getLineContent(1), ' '); assert.equal(model.getLineContent(2), ' '); assert.equal(model.getLineContent(3), ' '); @@ -2710,10 +2860,10 @@ suite('Editor Controller - Cursor Configuration', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { - moveTo(cursor, 3, model.getLineMaxColumn(3)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + moveTo(editor, viewModel, 3, model.getLineMaxColumn(3)); + viewModel.type('\n', 'keyboard'); assert.equal(model.getValue(), [ ' function f() {', @@ -2722,9 +2872,9 @@ suite('Editor Controller - Cursor Configuration', () => { ' ', ' }', ].join('\n')); - assertCursor(cursor, new Position(4, model.getLineMaxColumn(4))); + assertCursor(viewModel, new Position(4, model.getLineMaxColumn(4))); - cursorCommand(cursor, H.Paste, { text: ' // I\'m gonna copy this line\n', pasteOnNewLine: true }); + viewModel.paste(' // I\'m gonna copy this line\n', true); assert.equal(model.getValue(), [ ' function f() {', ' // I\'m gonna copy this line', @@ -2733,7 +2883,7 @@ suite('Editor Controller - Cursor Configuration', () => { '', ' }', ].join('\n')); - assertCursor(cursor, new Position(5, 1)); + assertCursor(viewModel, new Position(5, 1)); }); model.dispose(); @@ -2750,10 +2900,10 @@ suite('Editor Controller - Cursor Configuration', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { editor.setSelections([new Selection(4, 10, 4, 10)]); - cursorCommand(cursor, H.Paste, { text: ' // I\'m gonna copy this line\n', pasteOnNewLine: true }); + viewModel.paste(' // I\'m gonna copy this line\n', true); assert.equal(model.getValue(), [ ' function f() {', @@ -2763,7 +2913,7 @@ suite('Editor Controller - Cursor Configuration', () => { ' return 3;', ' }', ].join('\n')); - assertCursor(cursor, new Position(5, 10)); + assertCursor(viewModel, new Position(5, 10)); }); model.dispose(); @@ -2778,9 +2928,9 @@ suite('Editor Controller - Cursor Configuration', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model, useTabStops: false }, (editor, cursor) => { + withTestCodeEditor(null, { model: model, useTabStops: false }, (editor, viewModel) => { // DeleteLeft removes just one whitespace - moveTo(cursor, 2, 9); + moveTo(editor, viewModel, 2, 9); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), ' a '); }); @@ -2797,14 +2947,14 @@ suite('Editor Controller - Cursor Configuration', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model, useTabStops: true }, (editor, cursor) => { + withTestCodeEditor(null, { model: model, useTabStops: true }, (editor, viewModel) => { // DeleteLeft does not remove tab size, because some text exists before - moveTo(cursor, 2, model.getLineContent(2).length + 1); + moveTo(editor, viewModel, 2, model.getLineContent(2).length + 1); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), ' a '); // DeleteLeft removes tab size = 4 - moveTo(cursor, 2, 9); + moveTo(editor, viewModel, 2, 9); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), ' a '); @@ -2817,12 +2967,12 @@ suite('Editor Controller - Cursor Configuration', () => { assert.equal(model.getLineContent(2), ' a '); // Nothing is broken when cursor is in (1,1) - moveTo(cursor, 1, 1); + moveTo(editor, viewModel, 1, 1); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), ' \t \t x'); // DeleteLeft stops at tab stops even in mixed whitespace case - moveTo(cursor, 1, 10); + moveTo(editor, viewModel, 1, 10); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), ' \t \t x'); @@ -2836,7 +2986,7 @@ suite('Editor Controller - Cursor Configuration', () => { assert.equal(model.getLineContent(1), 'x'); // DeleteLeft on last line - moveTo(cursor, 3, model.getLineContent(3).length + 1); + moveTo(editor, viewModel, 3, model.getLineContent(3).length + 1); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(3), ''); @@ -2845,8 +2995,8 @@ suite('Editor Controller - Cursor Configuration', () => { assert.equal(model.getValue(EndOfLinePreference.LF), 'x\n a '); // In case of selection DeleteLeft only deletes selected text - moveTo(cursor, 2, 3); - moveTo(cursor, 2, 4, true); + moveTo(editor, viewModel, 2, 3); + moveTo(editor, viewModel, 2, 4, true); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), ' a '); }); @@ -2864,23 +3014,23 @@ suite('Editor Controller - Cursor Configuration', () => { } ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + viewModel.type('\n', 'keyboard'); assert.equal(model.getValue(EndOfLinePreference.LF), '\n', 'assert1'); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getValue(EndOfLinePreference.LF), '\n\t', 'assert2'); - cursorCommand(cursor, H.Type, { text: 'y' }, 'keyboard'); + viewModel.type('y', 'keyboard'); assert.equal(model.getValue(EndOfLinePreference.LF), '\n\ty', 'assert2'); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + viewModel.type('\n', 'keyboard'); assert.equal(model.getValue(EndOfLinePreference.LF), '\n\ty\n\t', 'assert3'); - cursorCommand(cursor, H.Type, { text: 'x' }); + viewModel.type('x'); assert.equal(model.getValue(EndOfLinePreference.LF), '\n\ty\n\tx', 'assert4'); - CoreNavigationCommands.CursorLeft.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorLeft.runCoreEditorCommand(viewModel, {}); assert.equal(model.getValue(EndOfLinePreference.LF), '\n\ty\n\tx', 'assert5'); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); @@ -2930,10 +3080,10 @@ suite('Editor Controller - Cursor Configuration', () => { } ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { const beforeVersion = model.getVersionId(); const beforeAltVersion = model.getAlternativeVersionId(); - cursorCommand(cursor, H.Type, { text: 'Hello' }, 'keyboard'); + viewModel.type('Hello', 'keyboard'); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); const afterVersion = model.getVersionId(); const afterAltVersion = model.getAlternativeVersionId(); @@ -2965,18 +3115,19 @@ suite('Editor Controller - Indentation Rules', () => { languageIdentifier: mode.getLanguageIdentifier(), modelOpts: { insertSpaces: false }, editorOpts: { autoIndent: 'full' } - }, (model, cursor) => { - moveTo(cursor, 1, 12, false); - assertCursor(cursor, new Selection(1, 12, 1, 12)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 1, 12, false); + assertCursor(viewModel, new Selection(1, 12, 1, 12)); - cursorCommandAndTokenize(model, cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(2, 2, 2, 2)); + viewModel.type('\n', 'keyboard'); + model.forceTokenization(model.getLineCount()); + assertCursor(viewModel, new Selection(2, 2, 2, 2)); - moveTo(cursor, 3, 13, false); - assertCursor(cursor, new Selection(3, 13, 3, 13)); + moveTo(editor, viewModel, 3, 13, false); + assertCursor(viewModel, new Selection(3, 13, 3, 13)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(4, 3, 4, 3)); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(4, 3, 4, 3)); }); }); @@ -2988,12 +3139,12 @@ suite('Editor Controller - Indentation Rules', () => { ], languageIdentifier: mode.getLanguageIdentifier(), editorOpts: { autoIndent: 'full' } - }, (model, cursor) => { - moveTo(cursor, 2, 2, false); - assertCursor(cursor, new Selection(2, 2, 2, 2)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 2, 2, false); + assertCursor(viewModel, new Selection(2, 2, 2, 2)); - cursorCommand(cursor, H.Type, { text: '}' }, 'keyboard'); - assertCursor(cursor, new Selection(2, 2, 2, 2)); + viewModel.type('}', 'keyboard'); + assertCursor(viewModel, new Selection(2, 2, 2, 2)); assert.equal(model.getLineContent(2), '}', '001'); }); }); @@ -3007,12 +3158,12 @@ suite('Editor Controller - Indentation Rules', () => { languageIdentifier: mode.getLanguageIdentifier(), modelOpts: { insertSpaces: false }, editorOpts: { autoIndent: 'full' } - }, (model, cursor) => { - moveTo(cursor, 2, 15, false); - assertCursor(cursor, new Selection(2, 15, 2, 15)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 2, 15, false); + assertCursor(viewModel, new Selection(2, 15, 2, 15)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(3, 2, 3, 2)); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(3, 2, 3, 2)); }); }); @@ -3027,18 +3178,19 @@ suite('Editor Controller - Indentation Rules', () => { languageIdentifier: mode.getLanguageIdentifier(), modelOpts: { insertSpaces: false }, editorOpts: { autoIndent: 'full' } - }, (model, cursor) => { - moveTo(cursor, 2, 14, false); - assertCursor(cursor, new Selection(2, 14, 2, 14)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 2, 14, false); + assertCursor(viewModel, new Selection(2, 14, 2, 14)); - cursorCommandAndTokenize(model, cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(3, 1, 3, 1)); + viewModel.type('\n', 'keyboard'); + model.forceTokenization(model.getLineCount()); + assertCursor(viewModel, new Selection(3, 1, 3, 1)); - moveTo(cursor, 5, 16, false); - assertCursor(cursor, new Selection(5, 16, 5, 16)); + moveTo(editor, viewModel, 5, 16, false); + assertCursor(viewModel, new Selection(5, 16, 5, 16)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(6, 2, 6, 2)); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(6, 2, 6, 2)); }); }); @@ -3054,16 +3206,17 @@ suite('Editor Controller - Indentation Rules', () => { mode.getLanguageIdentifier() ); - withTestCodeEditor(null, { model: model, autoIndent: 'full' }, (editor, cursor) => { - moveTo(cursor, 2, 11, false); - assertCursor(cursor, new Selection(2, 11, 2, 11)); + withTestCodeEditor(null, { model: model, autoIndent: 'full' }, (editor, viewModel) => { + moveTo(editor, viewModel, 2, 11, false); + assertCursor(viewModel, new Selection(2, 11, 2, 11)); - cursorCommandAndTokenize(model, cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(3, 3, 3, 3)); + viewModel.type('\n', 'keyboard'); + model.forceTokenization(model.getLineCount()); + assertCursor(viewModel, new Selection(3, 3, 3, 3)); - cursorCommand(cursor, H.Type, { text: 'console.log();' }, 'keyboard'); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(4, 1, 4, 1)); + viewModel.type('console.log();', 'keyboard'); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(4, 1, 4, 1)); }); model.dispose(); @@ -3079,12 +3232,12 @@ suite('Editor Controller - Indentation Rules', () => { ], languageIdentifier: mode.getLanguageIdentifier(), editorOpts: { autoIndent: 'full' } - }, (model, cursor) => { - moveTo(cursor, 3, 13, false); - assertCursor(cursor, new Selection(3, 13, 3, 13)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 3, 13, false); + assertCursor(viewModel, new Selection(3, 13, 3, 13)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(4, 1, 4, 1)); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(4, 1, 4, 1)); assert.equal(model.getLineContent(3), 'return true;', '001'); }); }); @@ -3099,13 +3252,13 @@ suite('Editor Controller - Indentation Rules', () => { ], languageIdentifier: mode.getLanguageIdentifier(), modelOpts: { insertSpaces: false } - }, (model, cursor) => { - moveTo(cursor, 4, 3, false); - moveTo(cursor, 4, 4, true); - assertCursor(cursor, new Selection(4, 3, 4, 4)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 4, 3, false); + moveTo(editor, viewModel, 4, 4, true); + assertCursor(viewModel, new Selection(4, 3, 4, 4)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(5, 1, 5, 1)); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(5, 1, 5, 1)); assert.equal(model.getLineContent(4), '\t}', '001'); }); }); @@ -3118,16 +3271,16 @@ suite('Editor Controller - Indentation Rules', () => { ], languageIdentifier: mode.getLanguageIdentifier(), modelOpts: { insertSpaces: false } - }, (model, cursor) => { - moveTo(cursor, 2, 12, false); - moveTo(cursor, 2, 13, true); - assertCursor(cursor, new Selection(2, 12, 2, 13)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 2, 12, false); + moveTo(editor, viewModel, 2, 13, true); + assertCursor(viewModel, new Selection(2, 12, 2, 13)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(3, 3, 3, 3)); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(3, 3, 3, 3)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(4, 3, 4, 3)); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(4, 3, 4, 3)); }); }); @@ -3138,20 +3291,20 @@ suite('Editor Controller - Indentation Rules', () => { '\tif (true) {' ], languageIdentifier: mode.getLanguageIdentifier(), - }, (model, cursor) => { - moveTo(cursor, 1, 12, false); - assertCursor(cursor, new Selection(1, 12, 1, 12)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 1, 12, false); + assertCursor(viewModel, new Selection(1, 12, 1, 12)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(2, 5, 2, 5)); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(2, 5, 2, 5)); model.forceTokenization(model.getLineCount()); - moveTo(cursor, 3, 13, false); - assertCursor(cursor, new Selection(3, 13, 3, 13)); + moveTo(editor, viewModel, 3, 13, false); + assertCursor(viewModel, new Selection(3, 13, 3, 13)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(4, 9, 4, 9)); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(4, 9, 4, 9)); }); }); @@ -3162,19 +3315,20 @@ suite('Editor Controller - Indentation Rules', () => { ' if (true) {' ], languageIdentifier: mode.getLanguageIdentifier(), - }, (model, cursor) => { - moveTo(cursor, 1, 12, false); - assertCursor(cursor, new Selection(1, 12, 1, 12)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 1, 12, false); + assertCursor(viewModel, new Selection(1, 12, 1, 12)); - cursorCommandAndTokenize(model, cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(2, 5, 2, 5)); + viewModel.type('\n', 'keyboard'); + model.forceTokenization(model.getLineCount()); + assertCursor(viewModel, new Selection(2, 5, 2, 5)); - moveTo(cursor, 3, 16, false); - assertCursor(cursor, new Selection(3, 16, 3, 16)); + moveTo(editor, viewModel, 3, 16, false); + assertCursor(viewModel, new Selection(3, 16, 3, 16)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + viewModel.type('\n', 'keyboard'); assert.equal(model.getLineContent(3), ' if (true) {'); - assertCursor(cursor, new Selection(4, 9, 4, 9)); + assertCursor(viewModel, new Selection(4, 9, 4, 9)); }); }); @@ -3186,19 +3340,20 @@ suite('Editor Controller - Indentation Rules', () => { ], languageIdentifier: mode.getLanguageIdentifier(), modelOpts: { insertSpaces: false } - }, (model, cursor) => { - moveTo(cursor, 1, 12, false); - assertCursor(cursor, new Selection(1, 12, 1, 12)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 1, 12, false); + assertCursor(viewModel, new Selection(1, 12, 1, 12)); - cursorCommandAndTokenize(model, cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(2, 2, 2, 2)); + viewModel.type('\n', 'keyboard'); + model.forceTokenization(model.getLineCount()); + assertCursor(viewModel, new Selection(2, 2, 2, 2)); - moveTo(cursor, 3, 16, false); - assertCursor(cursor, new Selection(3, 16, 3, 16)); + moveTo(editor, viewModel, 3, 16, false); + assertCursor(viewModel, new Selection(3, 16, 3, 16)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + viewModel.type('\n', 'keyboard'); assert.equal(model.getLineContent(3), ' if (true) {'); - assertCursor(cursor, new Selection(4, 3, 4, 3)); + assertCursor(viewModel, new Selection(4, 3, 4, 3)); }); }); @@ -3215,13 +3370,13 @@ suite('Editor Controller - Indentation Rules', () => { languageIdentifier: mode.getLanguageIdentifier(), modelOpts: { insertSpaces: false }, editorOpts: { autoIndent: 'full' } - }, (model, cursor) => { - moveTo(cursor, 5, 4, false); - assertCursor(cursor, new Selection(5, 4, 5, 4)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 5, 4, false); + assertCursor(viewModel, new Selection(5, 4, 5, 4)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + viewModel.type('\n', 'keyboard'); assert.equal(model.getLineContent(5), '\t\t}'); - assertCursor(cursor, new Selection(6, 3, 6, 3)); + assertCursor(viewModel, new Selection(6, 3, 6, 3)); }); }); @@ -3235,12 +3390,12 @@ suite('Editor Controller - Indentation Rules', () => { ], languageIdentifier: mode.getLanguageIdentifier(), modelOpts: { insertSpaces: false } - }, (model, cursor) => { - moveTo(cursor, 3, 9, false); - assertCursor(cursor, new Selection(3, 9, 3, 9)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 3, 9, false); + assertCursor(viewModel, new Selection(3, 9, 3, 9)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(4, 3, 4, 3)); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(4, 3, 4, 3)); assert.equal(model.getLineContent(4), '\t\t true;', '001'); }); }); @@ -3255,12 +3410,12 @@ suite('Editor Controller - Indentation Rules', () => { ], languageIdentifier: mode.getLanguageIdentifier(), modelOpts: { insertSpaces: false } - }, (model, cursor) => { - moveTo(cursor, 3, 3, false); - assertCursor(cursor, new Selection(3, 3, 3, 3)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 3, 3, false); + assertCursor(viewModel, new Selection(3, 3, 3, 3)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(4, 3, 4, 3)); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(4, 3, 4, 3)); assert.equal(model.getLineContent(4), '\t\treturn true;', '001'); }); }); @@ -3274,12 +3429,12 @@ suite('Editor Controller - Indentation Rules', () => { ' }a}' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 3, 11, false); - assertCursor(cursor, new Selection(3, 11, 3, 11)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 3, 11, false); + assertCursor(viewModel, new Selection(3, 11, 3, 11)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(4, 5, 4, 5)); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(4, 5, 4, 5)); assert.equal(model.getLineContent(4), ' true;', '001'); }); }); @@ -3294,19 +3449,19 @@ suite('Editor Controller - Indentation Rules', () => { ], languageIdentifier: mode.getLanguageIdentifier(), modelOpts: { insertSpaces: false } - }, (model, cursor) => { - moveTo(cursor, 3, 2, false); - assertCursor(cursor, new Selection(3, 2, 3, 2)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 3, 2, false); + assertCursor(viewModel, new Selection(3, 2, 3, 2)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(4, 2, 4, 2)); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(4, 2, 4, 2)); assert.equal(model.getLineContent(4), '\t\treturn true;', '001'); - moveTo(cursor, 4, 1, false); - assertCursor(cursor, new Selection(4, 1, 4, 1)); + moveTo(editor, viewModel, 4, 1, false); + assertCursor(viewModel, new Selection(4, 1, 4, 1)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(5, 1, 5, 1)); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(5, 1, 5, 1)); assert.equal(model.getLineContent(5), '\t\treturn true;', '002'); }); }); @@ -3321,19 +3476,19 @@ suite('Editor Controller - Indentation Rules', () => { ], languageIdentifier: mode.getLanguageIdentifier(), modelOpts: { insertSpaces: false } - }, (model, cursor) => { - moveTo(cursor, 3, 4, false); - assertCursor(cursor, new Selection(3, 4, 3, 4)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 3, 4, false); + assertCursor(viewModel, new Selection(3, 4, 3, 4)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(4, 3, 4, 3)); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(4, 3, 4, 3)); assert.equal(model.getLineContent(4), '\t\t\treturn true;', '001'); - moveTo(cursor, 4, 1, false); - assertCursor(cursor, new Selection(4, 1, 4, 1)); + moveTo(editor, viewModel, 4, 1, false); + assertCursor(viewModel, new Selection(4, 1, 4, 1)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(5, 1, 5, 1)); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(5, 1, 5, 1)); assert.equal(model.getLineContent(5), '\t\t\treturn true;', '002'); }); }); @@ -3347,17 +3502,17 @@ suite('Editor Controller - Indentation Rules', () => { '}a}' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 3, 2, false); - assertCursor(cursor, new Selection(3, 2, 3, 2)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 3, 2, false); + assertCursor(viewModel, new Selection(3, 2, 3, 2)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(4, 2, 4, 2)); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(4, 2, 4, 2)); assert.equal(model.getLineContent(4), ' return true;', '001'); - moveTo(cursor, 4, 3, false); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(5, 3, 5, 3)); + moveTo(editor, viewModel, 4, 3, false); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(5, 3, 5, 3)); assert.equal(model.getLineContent(5), ' return true;', '002'); }); }); @@ -3380,17 +3535,17 @@ suite('Editor Controller - Indentation Rules', () => { tabSize: 2, indentSize: 2 } - }, (model, cursor) => { - moveTo(cursor, 3, 3, false); - assertCursor(cursor, new Selection(3, 3, 3, 3)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 3, 3, false); + assertCursor(viewModel, new Selection(3, 3, 3, 3)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(4, 4, 4, 4)); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(4, 4, 4, 4)); assert.equal(model.getLineContent(4), ' return true;', '001'); - moveTo(cursor, 9, 4, false); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(10, 5, 10, 5)); + moveTo(editor, viewModel, 9, 4, false); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(10, 5, 10, 5)); assert.equal(model.getLineContent(10), ' return true;', '001'); }); }); @@ -3406,13 +3561,13 @@ suite('Editor Controller - Indentation Rules', () => { ], languageIdentifier: mode.getLanguageIdentifier(), modelOpts: { tabSize: 2 } - }, (model, cursor) => { - moveTo(cursor, 3, 5, false); - moveTo(cursor, 4, 3, true); - assertCursor(cursor, new Selection(3, 5, 4, 3)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 3, 5, false); + moveTo(editor, viewModel, 4, 3, true); + assertCursor(viewModel, new Selection(3, 5, 4, 3)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(4, 3, 4, 3)); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(4, 3, 4, 3)); assert.equal(model.getLineContent(4), ' return true;', '001'); }); }); @@ -3429,14 +3584,14 @@ suite('Editor Controller - Indentation Rules', () => { insertSpaces: false, }, languageIdentifier: mode.getLanguageIdentifier(), - }, (model, cursor) => { - moveTo(cursor, 3, 8, false); - moveTo(cursor, 2, 12, true); - assertCursor(cursor, new Selection(3, 8, 2, 12)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 3, 8, false); + moveTo(editor, viewModel, 2, 12, true); + assertCursor(viewModel, new Selection(3, 8, 2, 12)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + viewModel.type('\n', 'keyboard'); assert.equal(model.getLineContent(3), '\treturn x;'); - assertCursor(cursor, new Position(3, 2)); + assertCursor(viewModel, new Position(3, 2)); }); }); @@ -3452,14 +3607,14 @@ suite('Editor Controller - Indentation Rules', () => { insertSpaces: false, }, languageIdentifier: mode.getLanguageIdentifier(), - }, (model, cursor) => { - moveTo(cursor, 2, 12, false); - moveTo(cursor, 3, 8, true); - assertCursor(cursor, new Selection(2, 12, 3, 8)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 2, 12, false); + moveTo(editor, viewModel, 3, 8, true); + assertCursor(viewModel, new Selection(2, 12, 3, 8)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + viewModel.type('\n', 'keyboard'); assert.equal(model.getLineContent(3), '\treturn x;'); - assertCursor(cursor, new Position(3, 2)); + assertCursor(viewModel, new Position(3, 2)); }); }); @@ -3474,13 +3629,13 @@ suite('Editor Controller - Indentation Rules', () => { '?>' ], modelOpts: { insertSpaces: false } - }, (model, cursor) => { - moveTo(cursor, 5, 3, false); - assertCursor(cursor, new Selection(5, 3, 5, 3)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 5, 3, false); + assertCursor(viewModel, new Selection(5, 3, 5, 3)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + viewModel.type('\n', 'keyboard'); assert.equal(model.getLineContent(6), '\t'); - assertCursor(cursor, new Selection(6, 2, 6, 2)); + assertCursor(viewModel, new Selection(6, 2, 6, 2)); assert.equal(model.getLineContent(5), '\t}'); }); }); @@ -3493,12 +3648,12 @@ suite('Editor Controller - Indentation Rules', () => { ' ' ], modelOpts: { insertSpaces: false } - }, (model, cursor) => { - moveTo(cursor, 3, 2, false); - assertCursor(cursor, new Selection(3, 2, 3, 2)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 3, 2, false); + assertCursor(viewModel, new Selection(3, 2, 3, 2)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); - assertCursor(cursor, new Selection(4, 2, 4, 2)); + viewModel.type('\n', 'keyboard'); + assertCursor(viewModel, new Selection(4, 2, 4, 2)); assert.equal(model.getLineContent(4), '\t'); }); }); @@ -3519,9 +3674,9 @@ suite('Editor Controller - Indentation Rules', () => { mode.getLanguageIdentifier() ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - moveTo(cursor, 4, 1, false); - assertCursor(cursor, new Selection(4, 1, 4, 1)); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + moveTo(editor, viewModel, 4, 1, false); + assertCursor(viewModel, new Selection(4, 1, 4, 1)); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(4), '\t\t'); @@ -3547,9 +3702,9 @@ suite('Editor Controller - Indentation Rules', () => { mode.getLanguageIdentifier() ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - moveTo(cursor, 4, 2, false); - assertCursor(cursor, new Selection(4, 2, 4, 2)); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + moveTo(editor, viewModel, 4, 2, false); + assertCursor(viewModel, new Selection(4, 2, 4, 2)); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(4), '\t\t\t'); @@ -3575,9 +3730,9 @@ suite('Editor Controller - Indentation Rules', () => { mode.getLanguageIdentifier() ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - moveTo(cursor, 4, 1, false); - assertCursor(cursor, new Selection(4, 1, 4, 1)); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + moveTo(editor, viewModel, 4, 1, false); + assertCursor(viewModel, new Selection(4, 1, 4, 1)); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(4), '\t\t\t'); @@ -3602,9 +3757,9 @@ suite('Editor Controller - Indentation Rules', () => { mode.getLanguageIdentifier() ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - moveTo(cursor, 4, 3, false); - assertCursor(cursor, new Selection(4, 3, 4, 3)); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + moveTo(editor, viewModel, 4, 3, false); + assertCursor(viewModel, new Selection(4, 3, 4, 3)); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(4), '\t\t\t\t'); @@ -3629,9 +3784,9 @@ suite('Editor Controller - Indentation Rules', () => { mode.getLanguageIdentifier() ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - moveTo(cursor, 4, 4, false); - assertCursor(cursor, new Selection(4, 4, 4, 4)); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + moveTo(editor, viewModel, 4, 4, false); + assertCursor(viewModel, new Selection(4, 4, 4, 4)); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(4), '\t\t\t\t\t'); @@ -3654,9 +3809,9 @@ suite('Editor Controller - Indentation Rules', () => { mode.getLanguageIdentifier() ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { - moveTo(cursor, 3, 1); + moveTo(editor, viewModel, 3, 1); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), ' if (a) {'); assert.equal(model.getLineContent(2), ' '); @@ -3684,11 +3839,11 @@ suite('Editor Controller - Indentation Rules', () => { rubyMode.getLanguageIdentifier() ); - withTestCodeEditor(null, { model: model, autoIndent: 'full' }, (editor, cursor) => { - moveTo(cursor, 4, 7, false); - assertCursor(cursor, new Selection(4, 7, 4, 7)); + withTestCodeEditor(null, { model: model, autoIndent: 'full' }, (editor, viewModel) => { + moveTo(editor, viewModel, 4, 7, false); + assertCursor(viewModel, new Selection(4, 7, 4, 7)); - cursorCommand(cursor, H.Type, { text: 'd' }, 'keyboard'); + viewModel.type('d', 'keyboard'); assert.equal(model.getLineContent(4), ' end'); }); @@ -3706,12 +3861,12 @@ suite('Editor Controller - Indentation Rules', () => { '\t}' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 5, 3, false); - assertCursor(cursor, new Selection(5, 3, 5, 3)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 5, 3, false); + assertCursor(viewModel, new Selection(5, 3, 5, 3)); - cursorCommand(cursor, H.Type, { text: 'e' }, 'keyboard'); - assertCursor(cursor, new Selection(5, 4, 5, 4)); + viewModel.type('e', 'keyboard'); + assertCursor(viewModel, new Selection(5, 4, 5, 4)); assert.equal(model.getLineContent(5), '\t}e', 'This line should not decrease indent'); }); }); @@ -3727,12 +3882,12 @@ suite('Editor Controller - Indentation Rules', () => { '}' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 2, 3, false); - assertCursor(cursor, new Selection(2, 3, 2, 3)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 2, 3, false); + assertCursor(viewModel, new Selection(2, 3, 2, 3)); - cursorCommand(cursor, H.Type, { text: ' ' }, 'keyboard'); - assertCursor(cursor, new Selection(2, 4, 2, 4)); + viewModel.type(' ', 'keyboard'); + assertCursor(viewModel, new Selection(2, 4, 2, 4)); assert.equal(model.getLineContent(2), '\t ) {', 'This line should not decrease indent'); }); }); @@ -3746,12 +3901,12 @@ suite('Editor Controller - Indentation Rules', () => { ], languageIdentifier: mode.getLanguageIdentifier(), editorOpts: { autoIndent: 'full' } - }, (model, cursor) => { - moveTo(cursor, 3, 3, false); - assertCursor(cursor, new Selection(3, 3, 3, 3)); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 3, 3, false); + assertCursor(viewModel, new Selection(3, 3, 3, 3)); - cursorCommand(cursor, H.Type, { text: '}' }, 'keyboard'); - assertCursor(cursor, new Selection(3, 2, 3, 2)); + viewModel.type('}', 'keyboard'); + assertCursor(viewModel, new Selection(3, 2, 3, 2)); assert.equal(model.getLineContent(3), '}'); }); }); @@ -3794,11 +3949,11 @@ suite('Editor Controller - Indentation Rules', () => { mode.getLanguageIdentifier() ); - withTestCodeEditor(null, { model: model, autoIndent: 'advanced' }, (editor, cursor) => { - moveTo(cursor, 7, 6, false); - assertCursor(cursor, new Selection(7, 6, 7, 6)); + withTestCodeEditor(null, { model: model, autoIndent: 'advanced' }, (editor, viewModel) => { + moveTo(editor, viewModel, 7, 6, false); + assertCursor(viewModel, new Selection(7, 6, 7, 6)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + viewModel.type('\n', 'keyboard'); assert.equal(model.getValue(), [ 'class ItemCtrl {', @@ -3812,7 +3967,7 @@ suite('Editor Controller - Indentation Rules', () => { '}', ].join('\n') ); - assertCursor(cursor, new Selection(8, 5, 8, 5)); + assertCursor(viewModel, new Selection(8, 5, 8, 5)); }); model.dispose(); @@ -3858,9 +4013,9 @@ suite('Editor Controller - Indentation Rules', () => { mode.getLanguageIdentifier() ); - withTestCodeEditor(null, { model: model, autoIndent: 'advanced' }, (editor, cursor) => { - moveTo(cursor, 8, 1, false); - assertCursor(cursor, new Selection(8, 1, 8, 1)); + withTestCodeEditor(null, { model: model, autoIndent: 'advanced' }, (editor, viewModel) => { + moveTo(editor, viewModel, 8, 1, false); + assertCursor(viewModel, new Selection(8, 1, 8, 1)); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); assert.equal(model.getValue(), @@ -3876,7 +4031,7 @@ suite('Editor Controller - Indentation Rules', () => { ')', ].join('\n') ); - assert.deepEqual(cursor.getSelection(), new Selection(8, 3, 8, 3)); + assert.deepEqual(viewModel.getSelection(), new Selection(8, 3, 8, 3)); }); model.dispose(); @@ -3921,30 +4076,30 @@ suite('Editor Controller - Indentation Rules', () => { mode.getLanguageIdentifier() ); - withTestCodeEditor(null, { model: model, autoIndent: 'full' }, (editor, cursor) => { - moveTo(cursor, 3, 19, false); - assertCursor(cursor, new Selection(3, 19, 3, 19)); + withTestCodeEditor(null, { model: model, autoIndent: 'full' }, (editor, viewModel) => { + moveTo(editor, viewModel, 3, 19, false); + assertCursor(viewModel, new Selection(3, 19, 3, 19)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + viewModel.type('\n', 'keyboard'); assert.deepEqual(model.getLineContent(4), ' '); - moveTo(cursor, 5, 18, false); - assertCursor(cursor, new Selection(5, 18, 5, 18)); + moveTo(editor, viewModel, 5, 18, false); + assertCursor(viewModel, new Selection(5, 18, 5, 18)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + viewModel.type('\n', 'keyboard'); assert.deepEqual(model.getLineContent(6), ' '); - moveTo(cursor, 7, 15, false); - assertCursor(cursor, new Selection(7, 15, 7, 15)); + moveTo(editor, viewModel, 7, 15, false); + assertCursor(viewModel, new Selection(7, 15, 7, 15)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + viewModel.type('\n', 'keyboard'); assert.deepEqual(model.getLineContent(8), ' '); assert.deepEqual(model.getLineContent(9), ' ]'); - moveTo(cursor, 10, 18, false); - assertCursor(cursor, new Selection(10, 18, 10, 18)); + moveTo(editor, viewModel, 10, 18, false); + assertCursor(viewModel, new Selection(10, 18, 10, 18)); - cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + viewModel.type('\n', 'keyboard'); assert.deepEqual(model.getLineContent(11), ' ]'); }); @@ -3960,19 +4115,13 @@ interface ICursorOpts { editorOpts?: IEditorOptions; } -function usingCursor(opts: ICursorOpts, callback: (model: TextModel, cursor: Cursor) => void): void { - let model = createTextModel(opts.text.join('\n'), opts.modelOpts, opts.languageIdentifier); - model.forceTokenization(model.getLineCount()); - let config = new TestConfiguration(opts.editorOpts || {}); - let viewModel = createViewModel(config, model); - let cursor = new Cursor(config, model, viewModel); - - callback(model, cursor); - - cursor.dispose(); - viewModel.dispose(); - config.dispose(); - model.dispose(); +function usingCursor(opts: ICursorOpts, callback: (editor: ITestCodeEditor, model: TextModel, viewModel: ViewModel) => void): void { + const model = createTextModel(opts.text.join('\n'), opts.modelOpts, opts.languageIdentifier); + const editorOptions: TestCodeEditorCreationOptions = opts.editorOpts || {}; + editorOptions.model = model; + withTestCodeEditor(null, editorOptions, (editor, viewModel) => { + callback(editor, model, viewModel); + }); } class ElectricCharMode extends MockMode { @@ -4003,9 +4152,9 @@ suite('ElectricCharacter', () => { '' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 2, 1); - cursorCommand(cursor, H.Type, { text: '*' }, 'keyboard'); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 2, 1); + viewModel.type('*', 'keyboard'); assert.deepEqual(model.getLineContent(2), '*'); }); mode.dispose(); @@ -4019,9 +4168,9 @@ suite('ElectricCharacter', () => { '' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 2, 1); - cursorCommand(cursor, H.Type, { text: '}' }, 'keyboard'); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 2, 1); + viewModel.type('}', 'keyboard'); assert.deepEqual(model.getLineContent(2), ' }'); }); mode.dispose(); @@ -4035,9 +4184,9 @@ suite('ElectricCharacter', () => { ' ' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 2, 5); - cursorCommand(cursor, H.Type, { text: '}' }, 'keyboard'); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 2, 5); + viewModel.type('}', 'keyboard'); assert.deepEqual(model.getLineContent(2), ' }'); }); mode.dispose(); @@ -4053,9 +4202,9 @@ suite('ElectricCharacter', () => { ' ' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 4, 1); - cursorCommand(cursor, H.Type, { text: '}' }, 'keyboard'); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 4, 1); + viewModel.type('}', 'keyboard'); assert.deepEqual(model.getLineContent(4), ' } '); }); mode.dispose(); @@ -4071,9 +4220,9 @@ suite('ElectricCharacter', () => { ' } ' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 4, 6); - cursorCommand(cursor, H.Type, { text: '}' }, 'keyboard'); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 4, 6); + viewModel.type('}', 'keyboard'); assert.deepEqual(model.getLineContent(4), ' } }'); }); mode.dispose(); @@ -4087,9 +4236,9 @@ suite('ElectricCharacter', () => { '// hello' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 2, 1); - cursorCommand(cursor, H.Type, { text: '}' }, 'keyboard'); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 2, 1); + viewModel.type('}', 'keyboard'); assert.deepEqual(model.getLineContent(2), ' }// hello'); }); mode.dispose(); @@ -4103,9 +4252,9 @@ suite('ElectricCharacter', () => { ' ' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 2, 3); - cursorCommand(cursor, H.Type, { text: '}' }, 'keyboard'); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 2, 3); + viewModel.type('}', 'keyboard'); assert.deepEqual(model.getLineContent(2), ' }'); }); mode.dispose(); @@ -4119,9 +4268,9 @@ suite('ElectricCharacter', () => { 'a' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 2, 2); - cursorCommand(cursor, H.Type, { text: '}' }, 'keyboard'); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 2, 2); + viewModel.type('}', 'keyboard'); assert.deepEqual(model.getLineContent(2), 'a}'); }); mode.dispose(); @@ -4136,9 +4285,9 @@ suite('ElectricCharacter', () => { '})' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 2, 13); - cursorCommand(cursor, H.Type, { text: '*' }, 'keyboard'); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 2, 13); + viewModel.type('*', 'keyboard'); assert.deepEqual(model.getLineContent(2), ' ( 1 + 2 ) *'); }); mode.dispose(); @@ -4151,13 +4300,13 @@ suite('ElectricCharacter', () => { '(div', ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 1, 5); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 1, 5); let changeText: string | null = null; model.onDidChangeContent(e => { changeText = e.changes[0].text; }); - cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard'); + viewModel.type(')', 'keyboard'); assert.deepEqual(model.getLineContent(1), '(div)'); assert.deepEqual(changeText, ')'); }); @@ -4173,9 +4322,9 @@ suite('ElectricCharacter', () => { '\t3' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 3, 3); - cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard'); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 3, 3); + viewModel.type(')', 'keyboard'); assert.deepEqual(model.getLineContent(3), '\t3)'); }); mode.dispose(); @@ -4189,9 +4338,9 @@ suite('ElectricCharacter', () => { '/*' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 2, 3); - cursorCommand(cursor, H.Type, { text: '*' }, 'keyboard'); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 2, 3); + viewModel.type('*', 'keyboard'); assert.deepEqual(model.getLineContent(2), '/** */'); }); mode.dispose(); @@ -4205,9 +4354,9 @@ suite('ElectricCharacter', () => { ' /*' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 2, 5); - cursorCommand(cursor, H.Type, { text: '*' }, 'keyboard'); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 2, 5); + viewModel.type('*', 'keyboard'); assert.deepEqual(model.getLineContent(2), ' /** */'); }); mode.dispose(); @@ -4221,10 +4370,10 @@ suite('ElectricCharacter', () => { 'word' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - moveTo(cursor, 2, 5); - moveTo(cursor, 2, 1, true); - cursorCommand(cursor, H.Type, { text: '}' }, 'keyboard'); + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 2, 5); + moveTo(editor, viewModel, 2, 1, true); + viewModel.type('}', 'keyboard'); assert.deepEqual(model.getLineContent(2), '}'); }); mode.dispose(); @@ -4296,11 +4445,11 @@ suite('autoClosingPairs', () => { return result; } - function assertType(model: TextModel, cursor: Cursor, lineNumber: number, column: number, chr: string, expectedInsert: string, message: string): void { + function assertType(editor: ITestCodeEditor, model: TextModel, viewModel: ViewModel, lineNumber: number, column: number, chr: string, expectedInsert: string, message: string): void { let lineContent = model.getLineContent(lineNumber); let expected = lineContent.substr(0, column - 1) + expectedInsert + lineContent.substr(column - 1); - moveTo(cursor, lineNumber, column); - cursorCommand(cursor, H.Type, { text: chr }, 'keyboard'); + moveTo(editor, viewModel, lineNumber, column); + viewModel.type(chr, 'keyboard'); assert.deepEqual(model.getLineContent(lineNumber), expected, message); model.undo(); } @@ -4319,7 +4468,7 @@ suite('autoClosingPairs', () => { 'var h = { a: \'value\' };', ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { + }, (editor, model, viewModel) => { let autoClosePositions = [ 'var| a| |=| [|]|;|', @@ -4338,9 +4487,9 @@ suite('autoClosingPairs', () => { for (let column = 1; column < autoCloseColumns.length; column++) { model.forceTokenization(lineNumber); if (autoCloseColumns[column] === ColumnType.Special1) { - assertType(model, cursor, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`); + assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`); } else { - assertType(model, cursor, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`); + assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`); } } } @@ -4365,7 +4514,7 @@ suite('autoClosingPairs', () => { editorOpts: { autoClosingBrackets: 'beforeWhitespace' } - }, (model, cursor) => { + }, (editor, model, viewModel) => { let autoClosePositions = [ 'var| a| =| [|];|', @@ -4384,9 +4533,9 @@ suite('autoClosingPairs', () => { for (let column = 1; column < autoCloseColumns.length; column++) { model.forceTokenization(lineNumber); if (autoCloseColumns[column] === ColumnType.Special1) { - assertType(model, cursor, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`); + assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`); } else { - assertType(model, cursor, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`); + assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`); } } } @@ -4405,7 +4554,7 @@ suite('autoClosingPairs', () => { autoClosingBrackets: 'beforeWhitespace', autoClosingQuotes: 'never' } - }, (model, cursor) => { + }, (editor, model, viewModel) => { let autoClosePositions = [ 'var| a| =| [|];|', @@ -4417,11 +4566,11 @@ suite('autoClosingPairs', () => { for (let column = 1; column < autoCloseColumns.length; column++) { model.forceTokenization(lineNumber); if (autoCloseColumns[column] === ColumnType.Special1) { - assertType(model, cursor, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`); + assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`); } else { - assertType(model, cursor, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`); + assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`); } - assertType(model, cursor, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`); + assertType(editor, model, viewModel, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`); } } }); @@ -4435,7 +4584,7 @@ suite('autoClosingPairs', () => { autoClosingBrackets: 'never', autoClosingQuotes: 'beforeWhitespace' } - }, (model, cursor) => { + }, (editor, model, viewModel) => { let autoClosePositions = [ 'var b =| [|];|', @@ -4447,11 +4596,11 @@ suite('autoClosingPairs', () => { for (let column = 1; column < autoCloseColumns.length; column++) { model.forceTokenization(lineNumber); if (autoCloseColumns[column] === ColumnType.Special1) { - assertType(model, cursor, lineNumber, column, '\'', '\'\'', `auto closes @ (${lineNumber}, ${column})`); + assertType(editor, model, viewModel, lineNumber, column, '\'', '\'\'', `auto closes @ (${lineNumber}, ${column})`); } else { - assertType(model, cursor, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`); + assertType(editor, model, viewModel, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`); } - assertType(model, cursor, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`); + assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`); } } }); @@ -4476,7 +4625,7 @@ suite('autoClosingPairs', () => { editorOpts: { autoClosingBrackets: 'languageDefined' } - }, (model, cursor) => { + }, (editor, model, viewModel) => { let autoClosePositions = [ 'v|ar |a = [|];|', @@ -4495,9 +4644,9 @@ suite('autoClosingPairs', () => { for (let column = 1; column < autoCloseColumns.length; column++) { model.forceTokenization(lineNumber); if (autoCloseColumns[column] === ColumnType.Special1) { - assertType(model, cursor, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`); + assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`); } else { - assertType(model, cursor, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`); + assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`); } } } @@ -4523,7 +4672,7 @@ suite('autoClosingPairs', () => { autoClosingBrackets: 'never', autoClosingQuotes: 'never' } - }, (model, cursor) => { + }, (editor, model, viewModel) => { let autoClosePositions = [ 'var a = [];', @@ -4542,11 +4691,11 @@ suite('autoClosingPairs', () => { for (let column = 1; column < autoCloseColumns.length; column++) { model.forceTokenization(lineNumber); if (autoCloseColumns[column] === ColumnType.Special1) { - assertType(model, cursor, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`); - assertType(model, cursor, lineNumber, column, '"', '""', `auto closes @ (${lineNumber}, ${column})`); + assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`); + assertType(editor, model, viewModel, lineNumber, column, '"', '""', `auto closes @ (${lineNumber}, ${column})`); } else { - assertType(model, cursor, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`); - assertType(model, cursor, lineNumber, column, '"', '"', `does not auto close @ (${lineNumber}, ${column})`); + assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`); + assertType(editor, model, viewModel, lineNumber, column, '"', '"', `does not auto close @ (${lineNumber}, ${column})`); } } } @@ -4561,20 +4710,20 @@ suite('autoClosingPairs', () => { 'var a = asd' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { + }, (editor, model, viewModel) => { - cursor.setSelections('test', [ + viewModel.setSelections('test', [ new Selection(1, 1, 1, 4), new Selection(1, 9, 1, 12), ]); // type a ` - cursorCommand(cursor, H.Type, { text: '`' }, 'keyboard'); + viewModel.type('`', 'keyboard'); assert.equal(model.getValue(), '`var` a = `asd`'); // type a ( - cursorCommand(cursor, H.Type, { text: '(' }, 'keyboard'); + viewModel.type('(', 'keyboard'); assert.equal(model.getValue(), '`(var)` a = `(asd)`'); }); @@ -4587,14 +4736,14 @@ suite('autoClosingPairs', () => { editorOpts: { autoSurround: 'never' } - }, (model, cursor) => { + }, (editor, model, viewModel) => { - cursor.setSelections('test', [ + viewModel.setSelections('test', [ new Selection(1, 1, 1, 4), ]); // type a ` - cursorCommand(cursor, H.Type, { text: '`' }, 'keyboard'); + viewModel.type('`', 'keyboard'); assert.equal(model.getValue(), '` a = asd'); }); @@ -4607,18 +4756,18 @@ suite('autoClosingPairs', () => { editorOpts: { autoSurround: 'quotes' } - }, (model, cursor) => { + }, (editor, model, viewModel) => { - cursor.setSelections('test', [ + viewModel.setSelections('test', [ new Selection(1, 1, 1, 4), ]); // type a ` - cursorCommand(cursor, H.Type, { text: '`' }, 'keyboard'); + viewModel.type('`', 'keyboard'); assert.equal(model.getValue(), '`var` a = asd'); // type a ( - cursorCommand(cursor, H.Type, { text: '(' }, 'keyboard'); + viewModel.type('(', 'keyboard'); assert.equal(model.getValue(), '`(` a = asd'); }); @@ -4630,18 +4779,18 @@ suite('autoClosingPairs', () => { editorOpts: { autoSurround: 'brackets' } - }, (model, cursor) => { + }, (editor, model, viewModel) => { - cursor.setSelections('test', [ + viewModel.setSelections('test', [ new Selection(1, 1, 1, 4), ]); // type a ( - cursorCommand(cursor, H.Type, { text: '(' }, 'keyboard'); + viewModel.type('(', 'keyboard'); assert.equal(model.getValue(), '(var) a = asd'); // type a ` - cursorCommand(cursor, H.Type, { text: '`' }, 'keyboard'); + viewModel.type('`', 'keyboard'); assert.equal(model.getValue(), '(`) a = asd'); }); mode.dispose(); @@ -4661,7 +4810,7 @@ suite('autoClosingPairs', () => { 'var h = { a: \'value\' };', ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { + }, (editor, model, viewModel) => { let autoClosePositions = [ 'var a |=| [|]|;|', @@ -4680,11 +4829,11 @@ suite('autoClosingPairs', () => { for (let column = 1; column < autoCloseColumns.length; column++) { model.forceTokenization(lineNumber); if (autoCloseColumns[column] === ColumnType.Special1) { - assertType(model, cursor, lineNumber, column, '\'', '\'\'', `auto closes @ (${lineNumber}, ${column})`); + assertType(editor, model, viewModel, lineNumber, column, '\'', '\'\'', `auto closes @ (${lineNumber}, ${column})`); } else if (autoCloseColumns[column] === ColumnType.Special2) { - assertType(model, cursor, lineNumber, column, '\'', '', `over types @ (${lineNumber}, ${column})`); + assertType(editor, model, viewModel, lineNumber, column, '\'', '', `over types @ (${lineNumber}, ${column})`); } else { - assertType(model, cursor, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`); + assertType(editor, model, viewModel, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`); } } } @@ -4699,16 +4848,16 @@ suite('autoClosingPairs', () => { '', ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { + }, (editor, model, viewModel) => { model.setValue('begi'); - cursor.setSelections('test', [new Selection(1, 5, 1, 5)]); - cursorCommand(cursor, H.Type, { text: 'n' }, 'keyboard'); + viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]); + viewModel.type('n', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'beginend'); model.setValue('/*'); - cursor.setSelections('test', [new Selection(1, 3, 1, 3)]); - cursorCommand(cursor, H.Type, { text: '*' }, 'keyboard'); + viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]); + viewModel.type('*', 'keyboard'); assert.strictEqual(model.getLineContent(1), '/** */'); }); mode.dispose(); @@ -4744,17 +4893,17 @@ suite('autoClosingPairs', () => { 'Big LAMB' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { + }, (editor, model, viewModel) => { model.forceTokenization(model.getLineCount()); - assertType(model, cursor, 1, 4, '"', '"', `does not double quote when ending with open`); + assertType(editor, model, viewModel, 1, 4, '"', '"', `does not double quote when ending with open`); model.forceTokenization(model.getLineCount()); - assertType(model, cursor, 2, 4, '"', '"', `does not double quote when ending with open`); + assertType(editor, model, viewModel, 2, 4, '"', '"', `does not double quote when ending with open`); model.forceTokenization(model.getLineCount()); - assertType(model, cursor, 3, 4, '"', '"', `does not double quote when ending with open`); + assertType(editor, model, viewModel, 3, 4, '"', '"', `does not double quote when ending with open`); model.forceTokenization(model.getLineCount()); - assertType(model, cursor, 4, 2, '"', '"', `does not double quote when ending with open`); + assertType(editor, model, viewModel, 4, 2, '"', '"', `does not double quote when ending with open`); model.forceTokenization(model.getLineCount()); - assertType(model, cursor, 4, 3, '"', '"', `does not double quote when ending with open`); + assertType(editor, model, viewModel, 4, 3, '"', '"', `does not double quote when ending with open`); }); mode.dispose(); }); @@ -4766,8 +4915,8 @@ suite('autoClosingPairs', () => { 'var arr = ["b", "c"];' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - assertType(model, cursor, 1, 12, '"', '""', `does not over type and will auto close`); + }, (editor, model, viewModel) => { + assertType(editor, model, viewModel, 1, 12, '"', '""', `does not over type and will auto close`); }); mode.dispose(); }); @@ -4779,60 +4928,60 @@ suite('autoClosingPairs', () => { '', ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { + }, (editor, model, viewModel) => { - function typeCharacters(cursor: Cursor, chars: string): void { + function typeCharacters(viewModel: ViewModel, chars: string): void { for (let i = 0, len = chars.length; i < len; i++) { - cursorCommand(cursor, H.Type, { text: chars[i] }, 'keyboard'); + viewModel.type(chars[i], 'keyboard'); } } // First gif model.forceTokenization(model.getLineCount()); - typeCharacters(cursor, 'teste1 = teste\' ok'); + typeCharacters(viewModel, 'teste1 = teste\' ok'); assert.equal(model.getLineContent(1), 'teste1 = teste\' ok'); - cursor.setSelections('test', [new Selection(1, 1000, 1, 1000)]); - typeCharacters(cursor, '\n'); + viewModel.setSelections('test', [new Selection(1, 1000, 1, 1000)]); + typeCharacters(viewModel, '\n'); model.forceTokenization(model.getLineCount()); - typeCharacters(cursor, 'teste2 = teste \'ok'); + typeCharacters(viewModel, 'teste2 = teste \'ok'); assert.equal(model.getLineContent(2), 'teste2 = teste \'ok\''); - cursor.setSelections('test', [new Selection(2, 1000, 2, 1000)]); - typeCharacters(cursor, '\n'); + viewModel.setSelections('test', [new Selection(2, 1000, 2, 1000)]); + typeCharacters(viewModel, '\n'); model.forceTokenization(model.getLineCount()); - typeCharacters(cursor, 'teste3 = teste" ok'); + typeCharacters(viewModel, 'teste3 = teste" ok'); assert.equal(model.getLineContent(3), 'teste3 = teste" ok'); - cursor.setSelections('test', [new Selection(3, 1000, 3, 1000)]); - typeCharacters(cursor, '\n'); + viewModel.setSelections('test', [new Selection(3, 1000, 3, 1000)]); + typeCharacters(viewModel, '\n'); model.forceTokenization(model.getLineCount()); - typeCharacters(cursor, 'teste4 = teste "ok'); + typeCharacters(viewModel, 'teste4 = teste "ok'); assert.equal(model.getLineContent(4), 'teste4 = teste "ok"'); // Second gif - cursor.setSelections('test', [new Selection(4, 1000, 4, 1000)]); - typeCharacters(cursor, '\n'); + viewModel.setSelections('test', [new Selection(4, 1000, 4, 1000)]); + typeCharacters(viewModel, '\n'); model.forceTokenization(model.getLineCount()); - typeCharacters(cursor, 'teste \''); + typeCharacters(viewModel, 'teste \''); assert.equal(model.getLineContent(5), 'teste \'\''); - cursor.setSelections('test', [new Selection(5, 1000, 5, 1000)]); - typeCharacters(cursor, '\n'); + viewModel.setSelections('test', [new Selection(5, 1000, 5, 1000)]); + typeCharacters(viewModel, '\n'); model.forceTokenization(model.getLineCount()); - typeCharacters(cursor, 'teste "'); + typeCharacters(viewModel, 'teste "'); assert.equal(model.getLineContent(6), 'teste ""'); - cursor.setSelections('test', [new Selection(6, 1000, 6, 1000)]); - typeCharacters(cursor, '\n'); + viewModel.setSelections('test', [new Selection(6, 1000, 6, 1000)]); + typeCharacters(viewModel, '\n'); model.forceTokenization(model.getLineCount()); - typeCharacters(cursor, 'teste\''); + typeCharacters(viewModel, 'teste\''); assert.equal(model.getLineContent(7), 'teste\''); - cursor.setSelections('test', [new Selection(7, 1000, 7, 1000)]); - typeCharacters(cursor, '\n'); + viewModel.setSelections('test', [new Selection(7, 1000, 7, 1000)]); + typeCharacters(viewModel, '\n'); model.forceTokenization(model.getLineCount()); - typeCharacters(cursor, 'teste"'); + typeCharacters(viewModel, 'teste"'); assert.equal(model.getLineContent(8), 'teste"'); }); mode.dispose(); @@ -4846,22 +4995,22 @@ suite('autoClosingPairs', () => { 'y=();' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - assertCursor(cursor, new Position(1, 1)); + }, (editor, model, viewModel) => { + assertCursor(viewModel, new Position(1, 1)); - cursorCommand(cursor, H.Type, { text: 'x=(' }, 'keyboard'); + viewModel.type('x=(', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'x=()'); - cursorCommand(cursor, H.Type, { text: 'asd' }, 'keyboard'); + viewModel.type('asd', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'x=(asd)'); // overtype! - cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard'); + viewModel.type(')', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'x=(asd)'); // do not overtype! - cursor.setSelections('test', [new Selection(2, 4, 2, 4)]); - cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard'); + viewModel.setSelections('test', [new Selection(2, 4, 2, 4)]); + viewModel.type(')', 'keyboard'); assert.strictEqual(model.getLineContent(2), 'y=());'); }); @@ -4876,14 +5025,14 @@ suite('autoClosingPairs', () => { 'y=();' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - assertCursor(cursor, new Position(1, 1)); + }, (editor, model, viewModel) => { + assertCursor(viewModel, new Position(1, 1)); - cursorCommand(cursor, H.Type, { text: 'x=(' }, 'keyboard'); + viewModel.type('x=(', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'x=()'); - cursor.setSelections('test', [new Selection(1, 5, 1, 5)]); - cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard'); + viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]); + viewModel.type(')', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'x=())'); }); mode.dispose(); @@ -4897,17 +5046,17 @@ suite('autoClosingPairs', () => { 'y=();' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - assertCursor(cursor, new Position(1, 1)); + }, (editor, model, viewModel) => { + assertCursor(viewModel, new Position(1, 1)); - cursorCommand(cursor, H.Type, { text: 'x=(' }, 'keyboard'); + viewModel.type('x=(', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'x=()'); - cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard'); + viewModel.type(')', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'x=()'); - cursor.setSelections('test', [new Selection(1, 4, 1, 4)]); - cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard'); + viewModel.setSelections('test', [new Selection(1, 4, 1, 4)]); + viewModel.type(')', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'x=())'); }); mode.dispose(); @@ -4921,19 +5070,19 @@ suite('autoClosingPairs', () => { 'y=();' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - assertCursor(cursor, new Position(1, 1)); + }, (editor, model, viewModel) => { + assertCursor(viewModel, new Position(1, 1)); - cursorCommand(cursor, H.Type, { text: 'x=(' }, 'keyboard'); + viewModel.type('x=(', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'x=()'); - cursorCommand(cursor, H.Type, { text: '(' }, 'keyboard'); + viewModel.type('(', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'x=(())'); - cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard'); + viewModel.type(')', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'x=(())'); - cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard'); + viewModel.type(')', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'x=(())'); }); mode.dispose(); @@ -4946,22 +5095,22 @@ suite('autoClosingPairs', () => { 'std::cout << \'"\' << entryMap' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - cursor.setSelections('test', [new Selection(1, 29, 1, 29)]); + }, (editor, model, viewModel) => { + viewModel.setSelections('test', [new Selection(1, 29, 1, 29)]); - cursorCommand(cursor, H.Type, { text: '[' }, 'keyboard'); + viewModel.type('[', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap[]'); - cursorCommand(cursor, H.Type, { text: '"' }, 'keyboard'); + viewModel.type('"', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap[""]'); - cursorCommand(cursor, H.Type, { text: 'a' }, 'keyboard'); + viewModel.type('a', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap["a"]'); - cursorCommand(cursor, H.Type, { text: '"' }, 'keyboard'); + viewModel.type('"', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap["a"]'); - cursorCommand(cursor, H.Type, { text: ']' }, 'keyboard'); + viewModel.type(']', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap["a"]'); }); mode.dispose(); @@ -5006,8 +5155,8 @@ suite('autoClosingPairs', () => { 'foo\'hello\'' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - assertType(model, cursor, 1, 4, '(', '(', `does not auto close @ (1, 4)`); + }, (editor, model, viewModel) => { + assertType(editor, model, viewModel, 1, 4, '(', '(', `does not auto close @ (1, 4)`); }); mode.dispose(); }); @@ -5019,16 +5168,16 @@ suite('autoClosingPairs', () => { '
{ - cursor.setSelections('test', [new Selection(1, 8, 1, 8)]); + }, (editor, model, viewModel) => { + viewModel.setSelections('test', [new Selection(1, 8, 1, 8)]); - cursor.executeEdits('snippet', [{ range: new Range(1, 6, 1, 8), text: 'id=""' }], () => [new Selection(1, 10, 1, 10)]); + viewModel.executeEdits('snippet', [{ range: new Range(1, 6, 1, 8), text: 'id=""' }], () => [new Selection(1, 10, 1, 10)]); assert.strictEqual(model.getLineContent(1), '
{ editorOpts: { autoClosingOvertype: 'always' } - }, (model, cursor) => { - assertCursor(cursor, new Position(1, 1)); + }, (editor, model, viewModel) => { + assertCursor(viewModel, new Position(1, 1)); - cursorCommand(cursor, H.Type, { text: 'x=(' }, 'keyboard'); + viewModel.type('x=(', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'x=()'); - cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard'); + viewModel.type(')', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'x=()'); - cursor.setSelections('test', [new Selection(1, 4, 1, 4)]); - cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard'); + viewModel.setSelections('test', [new Selection(1, 4, 1, 4)]); + viewModel.type(')', 'keyboard'); assert.strictEqual(model.getLineContent(1), 'x=()'); - cursor.setSelections('test', [new Selection(2, 4, 2, 4)]); - cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard'); + viewModel.setSelections('test', [new Selection(2, 4, 2, 4)]); + viewModel.type(')', 'keyboard'); assert.strictEqual(model.getLineContent(2), 'y=();'); }); mode.dispose(); @@ -5071,14 +5220,14 @@ suite('autoClosingPairs', () => { text: [ ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - assertCursor(cursor, new Position(1, 1)); + }, (editor, model, viewModel) => { + assertCursor(viewModel, new Position(1, 1)); // Typing ` + e on the mac US intl kb layout - cursorCommand(cursor, H.CompositionStart, null, 'keyboard'); - cursorCommand(cursor, H.Type, { text: '`' }, 'keyboard'); - cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: 'è' }, 'keyboard'); - cursorCommand(cursor, H.CompositionEnd, null, 'keyboard'); + viewModel.startComposition(); + viewModel.type('`', 'keyboard'); + viewModel.replacePreviousChar('è', 1, 'keyboard'); + viewModel.endComposition('keyboard'); assert.equal(model.getValue(), 'è'); }); @@ -5092,15 +5241,15 @@ suite('autoClosingPairs', () => { 'test' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - cursor.setSelections('test', [new Selection(1, 1, 1, 5)]); + }, (editor, model, viewModel) => { + viewModel.setSelections('test', [new Selection(1, 1, 1, 5)]); // Typing ` + e on the mac US intl kb layout - cursorCommand(cursor, H.CompositionStart, null, 'keyboard'); - cursorCommand(cursor, H.Type, { text: '\'' }, 'keyboard'); - cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '\'' }, 'keyboard'); - cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '\'' }, 'keyboard'); - cursorCommand(cursor, H.CompositionEnd, null, 'keyboard'); + viewModel.startComposition(); + viewModel.type('\'', 'keyboard'); + viewModel.replacePreviousChar('\'', 1, 'keyboard'); + viewModel.replacePreviousChar('\'', 1, 'keyboard'); + viewModel.endComposition('keyboard'); assert.equal(model.getValue(), '\'test\''); }); @@ -5114,20 +5263,20 @@ suite('autoClosingPairs', () => { 'console.log();' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { + }, (editor, model, viewModel) => { - cursor.setSelections('test', [new Selection(1, 13, 1, 13)]); + viewModel.setSelections('test', [new Selection(1, 13, 1, 13)]); - cursorCommand(cursor, H.Type, { text: '\'' }, 'keyboard'); + viewModel.type('\'', 'keyboard'); assert.equal(model.getValue(), 'console.log(\'\');'); - cursorCommand(cursor, H.Type, { text: 'it' }, 'keyboard'); + viewModel.type('it', 'keyboard'); assert.equal(model.getValue(), 'console.log(\'it\');'); - cursorCommand(cursor, H.Type, { text: '\\' }, 'keyboard'); + viewModel.type('\\', 'keyboard'); assert.equal(model.getValue(), 'console.log(\'it\\\');'); - cursorCommand(cursor, H.Type, { text: '\'' }, 'keyboard'); + viewModel.type('\'', 'keyboard'); assert.equal(model.getValue(), 'console.log(\'it\\\'\'\');'); }); mode.dispose(); @@ -5140,23 +5289,23 @@ suite('autoClosingPairs', () => { '' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { + }, (editor, model, viewModel) => { - cursor.setSelections('test', [new Selection(1, 1, 1, 1)]); + viewModel.setSelections('test', [new Selection(1, 1, 1, 1)]); - cursorCommand(cursor, H.Type, { text: '\\' }, 'keyboard'); + viewModel.type('\\', 'keyboard'); assert.equal(model.getValue(), '\\'); - cursorCommand(cursor, H.Type, { text: '(' }, 'keyboard'); + viewModel.type('(', 'keyboard'); assert.equal(model.getValue(), '\\()'); - cursorCommand(cursor, H.Type, { text: 'abc' }, 'keyboard'); + viewModel.type('abc', 'keyboard'); assert.equal(model.getValue(), '\\(abc)'); - cursorCommand(cursor, H.Type, { text: '\\' }, 'keyboard'); + viewModel.type('\\', 'keyboard'); assert.equal(model.getValue(), '\\(abc\\)'); - cursorCommand(cursor, H.Type, { text: ')' }, 'keyboard'); + viewModel.type(')', 'keyboard'); assert.equal(model.getValue(), '\\(abc\\)'); }); mode.dispose(); @@ -5170,20 +5319,20 @@ suite('autoClosingPairs', () => { 'world' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - assertCursor(cursor, new Position(1, 1)); + }, (editor, model, viewModel) => { + assertCursor(viewModel, new Position(1, 1)); // Typing ` and pressing shift+down on the mac US intl kb layout // Here we're just replaying what the cursor gets - cursorCommand(cursor, H.CompositionStart, null, 'keyboard'); - cursorCommand(cursor, H.Type, { text: '`' }, 'keyboard'); - moveDown(cursor, true); - cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '`' }, 'keyboard'); - cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '`' }, 'keyboard'); - cursorCommand(cursor, H.CompositionEnd, null, 'keyboard'); + viewModel.startComposition(); + viewModel.type('`', 'keyboard'); + moveDown(editor, viewModel, true); + viewModel.replacePreviousChar('`', 1, 'keyboard'); + viewModel.replacePreviousChar('`', 1, 'keyboard'); + viewModel.endComposition('keyboard'); assert.equal(model.getValue(), '`hello\nworld'); - assertCursor(cursor, new Selection(1, 2, 2, 2)); + assertCursor(viewModel, new Selection(1, 2, 2, 2)); }); mode.dispose(); }); @@ -5195,60 +5344,60 @@ suite('autoClosingPairs', () => { '' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - assertCursor(cursor, new Position(1, 1)); + }, (editor, model, viewModel) => { + assertCursor(viewModel, new Position(1, 1)); // on the mac US intl kb layout // Typing ' + space - cursorCommand(cursor, H.CompositionStart, null, 'keyboard'); - cursorCommand(cursor, H.Type, { text: '\'' }, 'keyboard'); - cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '\'' }, 'keyboard'); - cursorCommand(cursor, H.CompositionEnd, null, 'keyboard'); + viewModel.startComposition(); + viewModel.type('\'', 'keyboard'); + viewModel.replacePreviousChar('\'', 1, 'keyboard'); + viewModel.endComposition('keyboard'); assert.equal(model.getValue(), '\'\''); // Typing one more ' + space - cursorCommand(cursor, H.CompositionStart, null, 'keyboard'); - cursorCommand(cursor, H.Type, { text: '\'' }, 'keyboard'); - cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '\'' }, 'keyboard'); - cursorCommand(cursor, H.CompositionEnd, null, 'keyboard'); + viewModel.startComposition(); + viewModel.type('\'', 'keyboard'); + viewModel.replacePreviousChar('\'', 1, 'keyboard'); + viewModel.endComposition('keyboard'); assert.equal(model.getValue(), '\'\''); // Typing ' as a closing tag model.setValue('\'abc'); - cursor.setSelections('test', [new Selection(1, 5, 1, 5)]); - cursorCommand(cursor, H.CompositionStart, null, 'keyboard'); - cursorCommand(cursor, H.Type, { text: '\'' }, 'keyboard'); - cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '\'' }, 'keyboard'); - cursorCommand(cursor, H.CompositionEnd, null, 'keyboard'); + viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]); + viewModel.startComposition(); + viewModel.type('\'', 'keyboard'); + viewModel.replacePreviousChar('\'', 1, 'keyboard'); + viewModel.endComposition('keyboard'); assert.equal(model.getValue(), '\'abc\''); // quotes before the newly added character are all paired. model.setValue('\'abc\'def '); - cursor.setSelections('test', [new Selection(1, 10, 1, 10)]); - cursorCommand(cursor, H.CompositionStart, null, 'keyboard'); - cursorCommand(cursor, H.Type, { text: '\'' }, 'keyboard'); - cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '\'' }, 'keyboard'); - cursorCommand(cursor, H.CompositionEnd, null, 'keyboard'); + viewModel.setSelections('test', [new Selection(1, 10, 1, 10)]); + viewModel.startComposition(); + viewModel.type('\'', 'keyboard'); + viewModel.replacePreviousChar('\'', 1, 'keyboard'); + viewModel.endComposition('keyboard'); assert.equal(model.getValue(), '\'abc\'def \'\''); // No auto closing if there is non-whitespace character after the cursor model.setValue('abc'); - cursor.setSelections('test', [new Selection(1, 1, 1, 1)]); - cursorCommand(cursor, H.CompositionStart, null, 'keyboard'); - cursorCommand(cursor, H.Type, { text: '\'' }, 'keyboard'); - cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '\'' }, 'keyboard'); - cursorCommand(cursor, H.CompositionEnd, null, 'keyboard'); + viewModel.setSelections('test', [new Selection(1, 1, 1, 1)]); + viewModel.startComposition(); + viewModel.type('\'', 'keyboard'); + viewModel.replacePreviousChar('\'', 1, 'keyboard'); + viewModel.endComposition('keyboard'); // No auto closing if it's after a word. model.setValue('abc'); - cursor.setSelections('test', [new Selection(1, 4, 1, 4)]); - cursorCommand(cursor, H.CompositionStart, null, 'keyboard'); - cursorCommand(cursor, H.Type, { text: '\'' }, 'keyboard'); - cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '\'' }, 'keyboard'); - cursorCommand(cursor, H.CompositionEnd, null, 'keyboard'); + viewModel.setSelections('test', [new Selection(1, 4, 1, 4)]); + viewModel.startComposition(); + viewModel.type('\'', 'keyboard'); + viewModel.replacePreviousChar('\'', 1, 'keyboard'); + viewModel.endComposition('keyboard'); assert.equal(model.getValue(), 'abc\''); }); @@ -5262,14 +5411,14 @@ suite('autoClosingPairs', () => { '{}' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { - cursor.setSelections('test', [new Selection(1, 2, 1, 2)]); + }, (editor, model, viewModel) => { + viewModel.setSelections('test', [new Selection(1, 2, 1, 2)]); // Typing a + backspace - cursorCommand(cursor, H.CompositionStart, null, 'keyboard'); - cursorCommand(cursor, H.Type, { text: 'a' }, 'keyboard'); - cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '' }, 'keyboard'); - cursorCommand(cursor, H.CompositionEnd, null, 'keyboard'); + viewModel.startComposition(); + viewModel.type('a', 'keyboard'); + viewModel.replacePreviousChar('', 1, 'keyboard'); + viewModel.endComposition('keyboard'); assert.equal(model.getValue(), '{}'); }); mode.dispose(); @@ -5282,15 +5431,15 @@ suite('autoClosingPairs', () => { 'var a = asd' ], languageIdentifier: mode.getLanguageIdentifier() - }, (model, cursor) => { + }, (editor, model, viewModel) => { - cursor.setSelections('test', [ + viewModel.setSelections('test', [ new Selection(1, 9, 1, 9), new Selection(1, 12, 1, 12), ]); // type a ` - cursorCommand(cursor, H.Type, { text: '`' }, 'keyboard'); + viewModel.type('`', 'keyboard'); assert.equal(model.getValue(), 'var a = `asd`'); }); @@ -5314,19 +5463,19 @@ suite('autoClosingPairs', () => { const mode = new MyMode(); const model = createTextModel('var x = \'hi\';', undefined, languageId); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { editor.setSelections([ new Selection(1, 9, 1, 10), new Selection(1, 12, 1, 13) ]); - cursorCommand(cursor, H.Type, { text: '"' }, 'keyboard'); + viewModel.type('"', 'keyboard'); assert.equal(model.getValue(EndOfLinePreference.LF), 'var x = "hi";', 'assert1'); editor.setSelections([ new Selection(1, 9, 1, 10), new Selection(1, 12, 1, 13) ]); - cursorCommand(cursor, H.Type, { text: '\'' }, 'keyboard'); + viewModel.type('\'', 'keyboard'); assert.equal(model.getValue(EndOfLinePreference.LF), 'var x = \'hi\';', 'assert2'); }); @@ -5344,8 +5493,8 @@ suite('autoClosingPairs', () => { mode.getLanguageIdentifier() ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - cursor.setSelections('test', [ + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + viewModel.setSelections('test', [ new Selection(1, 4, 1, 4), new Selection(1, 10, 1, 10), ]); @@ -5373,16 +5522,16 @@ suite('autoClosingPairs', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { CoreNavigationCommands.WordSelect.runEditorCommand(null, editor, { position: new Position(3, 7) }); - assertCursor(cursor, new Selection(3, 7, 3, 7)); + assertCursor(viewModel, new Selection(3, 7, 3, 7)); CoreNavigationCommands.WordSelectDrag.runEditorCommand(null, editor, { position: new Position(4, 7) }); - assertCursor(cursor, new Selection(3, 7, 4, 7)); + assertCursor(viewModel, new Selection(3, 7, 4, 7)); }); }); }); @@ -5397,24 +5546,24 @@ suite('Undo stops', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - cursor.setSelections('test', [new Selection(1, 3, 1, 3)]); - cursorCommand(cursor, H.Type, { text: 'first' }, 'keyboard'); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]); + viewModel.type('first', 'keyboard'); assert.equal(model.getLineContent(1), 'A first line'); - assertCursor(cursor, new Selection(1, 8, 1, 8)); + assertCursor(viewModel, new Selection(1, 8, 1, 8)); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), 'A fir line'); - assertCursor(cursor, new Selection(1, 6, 1, 6)); + assertCursor(viewModel, new Selection(1, 6, 1, 6)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), 'A first line'); - assertCursor(cursor, new Selection(1, 8, 1, 8)); + assertCursor(viewModel, new Selection(1, 8, 1, 8)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), 'A line'); - assertCursor(cursor, new Selection(1, 3, 1, 3)); + assertCursor(viewModel, new Selection(1, 3, 1, 3)); }); }); @@ -5426,24 +5575,24 @@ suite('Undo stops', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - cursor.setSelections('test', [new Selection(1, 3, 1, 3)]); - cursorCommand(cursor, H.Type, { text: 'first' }, 'keyboard'); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]); + viewModel.type('first', 'keyboard'); assert.equal(model.getLineContent(1), 'A first line'); - assertCursor(cursor, new Selection(1, 8, 1, 8)); + assertCursor(viewModel, new Selection(1, 8, 1, 8)); CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), 'A firstine'); - assertCursor(cursor, new Selection(1, 8, 1, 8)); + assertCursor(viewModel, new Selection(1, 8, 1, 8)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), 'A first line'); - assertCursor(cursor, new Selection(1, 8, 1, 8)); + assertCursor(viewModel, new Selection(1, 8, 1, 8)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), 'A line'); - assertCursor(cursor, new Selection(1, 3, 1, 3)); + assertCursor(viewModel, new Selection(1, 3, 1, 3)); }); }); @@ -5455,8 +5604,8 @@ suite('Undo stops', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - cursor.setSelections('test', [new Selection(2, 8, 2, 8)]); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + viewModel.setSelections('test', [new Selection(2, 8, 2, 8)]); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); @@ -5465,19 +5614,19 @@ suite('Undo stops', () => { CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), ' line'); - assertCursor(cursor, new Selection(2, 1, 2, 1)); + assertCursor(viewModel, new Selection(2, 1, 2, 1)); - cursorCommand(cursor, H.Type, { text: 'Second' }, 'keyboard'); + viewModel.type('Second', 'keyboard'); assert.equal(model.getLineContent(2), 'Second line'); - assertCursor(cursor, new Selection(2, 7, 2, 7)); + assertCursor(viewModel, new Selection(2, 7, 2, 7)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), ' line'); - assertCursor(cursor, new Selection(2, 1, 2, 1)); + assertCursor(viewModel, new Selection(2, 1, 2, 1)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), 'Another line'); - assertCursor(cursor, new Selection(2, 8, 2, 8)); + assertCursor(viewModel, new Selection(2, 8, 2, 8)); }); }); @@ -5489,8 +5638,8 @@ suite('Undo stops', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - cursor.setSelections('test', [new Selection(2, 8, 2, 8)]); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + viewModel.setSelections('test', [new Selection(2, 8, 2, 8)]); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); @@ -5499,7 +5648,7 @@ suite('Undo stops', () => { CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), ' line'); - assertCursor(cursor, new Selection(2, 1, 2, 1)); + assertCursor(viewModel, new Selection(2, 1, 2, 1)); CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); @@ -5507,15 +5656,15 @@ suite('Undo stops', () => { CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), ''); - assertCursor(cursor, new Selection(2, 1, 2, 1)); + assertCursor(viewModel, new Selection(2, 1, 2, 1)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), ' line'); - assertCursor(cursor, new Selection(2, 1, 2, 1)); + assertCursor(viewModel, new Selection(2, 1, 2, 1)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), 'Another line'); - assertCursor(cursor, new Selection(2, 8, 2, 8)); + assertCursor(viewModel, new Selection(2, 8, 2, 8)); }); }); @@ -5527,26 +5676,26 @@ suite('Undo stops', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - cursor.setSelections('test', [new Selection(2, 9, 2, 9)]); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + viewModel.setSelections('test', [new Selection(2, 9, 2, 9)]); CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), 'Another '); - assertCursor(cursor, new Selection(2, 9, 2, 9)); + assertCursor(viewModel, new Selection(2, 9, 2, 9)); - cursorCommand(cursor, H.Type, { text: 'text' }, 'keyboard'); + viewModel.type('text', 'keyboard'); assert.equal(model.getLineContent(2), 'Another text'); - assertCursor(cursor, new Selection(2, 13, 2, 13)); + assertCursor(viewModel, new Selection(2, 13, 2, 13)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), 'Another '); - assertCursor(cursor, new Selection(2, 9, 2, 9)); + assertCursor(viewModel, new Selection(2, 9, 2, 9)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), 'Another line'); - assertCursor(cursor, new Selection(2, 9, 2, 9)); + assertCursor(viewModel, new Selection(2, 9, 2, 9)); }); }); @@ -5558,14 +5707,14 @@ suite('Undo stops', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - cursor.setSelections('test', [new Selection(2, 9, 2, 9)]); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + viewModel.setSelections('test', [new Selection(2, 9, 2, 9)]); CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), 'Another '); - assertCursor(cursor, new Selection(2, 9, 2, 9)); + assertCursor(viewModel, new Selection(2, 9, 2, 9)); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); @@ -5574,15 +5723,15 @@ suite('Undo stops', () => { CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), 'An'); - assertCursor(cursor, new Selection(2, 3, 2, 3)); + assertCursor(viewModel, new Selection(2, 3, 2, 3)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), 'Another '); - assertCursor(cursor, new Selection(2, 9, 2, 9)); + assertCursor(viewModel, new Selection(2, 9, 2, 9)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(2), 'Another line'); - assertCursor(cursor, new Selection(2, 9, 2, 9)); + assertCursor(viewModel, new Selection(2, 9, 2, 9)); }); }); @@ -5594,23 +5743,23 @@ suite('Undo stops', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - cursor.setSelections('test', [new Selection(1, 3, 1, 3)]); - cursorCommand(cursor, H.Type, { text: 'first and interesting' }, 'keyboard'); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]); + viewModel.type('first and interesting', 'keyboard'); assert.equal(model.getLineContent(1), 'A first and interesting line'); - assertCursor(cursor, new Selection(1, 24, 1, 24)); + assertCursor(viewModel, new Selection(1, 24, 1, 24)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), 'A first and line'); - assertCursor(cursor, new Selection(1, 12, 1, 12)); + assertCursor(viewModel, new Selection(1, 12, 1, 12)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), 'A first line'); - assertCursor(cursor, new Selection(1, 8, 1, 8)); + assertCursor(viewModel, new Selection(1, 8, 1, 8)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getLineContent(1), 'A line'); - assertCursor(cursor, new Selection(1, 3, 1, 3)); + assertCursor(viewModel, new Selection(1, 3, 1, 3)); }); }); @@ -5622,19 +5771,19 @@ suite('Undo stops', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - cursor.setSelections('test', [new Selection(1, 3, 1, 3)]); - cursorCommand(cursor, H.Type, { text: 'first' }, 'keyboard'); + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]); + viewModel.type('first', 'keyboard'); assert.equal(model.getValue(), 'A first line\nAnother line'); - assertCursor(cursor, new Selection(1, 8, 1, 8)); + assertCursor(viewModel, new Selection(1, 8, 1, 8)); model.pushEOL(EndOfLineSequence.CRLF); assert.equal(model.getValue(), 'A first line\r\nAnother line'); - assertCursor(cursor, new Selection(1, 8, 1, 8)); + assertCursor(viewModel, new Selection(1, 8, 1, 8)); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); assert.equal(model.getValue(), 'A line\nAnother line'); - assertCursor(cursor, new Selection(1, 3, 1, 3)); + assertCursor(viewModel, new Selection(1, 3, 1, 3)); }); }); @@ -5646,12 +5795,12 @@ suite('Undo stops', () => { ].join('\n') ); - withTestCodeEditor(null, { model: model }, (editor, cursor) => { - cursor.setSelections('test', [ + withTestCodeEditor(null, { model: model }, (editor, viewModel) => { + viewModel.setSelections('test', [ new Selection(2, 7, 2, 12), new Selection(1, 7, 1, 12), ]); - cursorCommand(cursor, H.Type, { text: 'no' }, 'keyboard'); + viewModel.type('no', 'keyboard'); assert.equal(model.getValue(), 'hello no\nhello no'); CoreEditingCommands.Undo.runEditorCommand(null, editor, null); diff --git a/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts b/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts index e918feacef0..3c85cc22eb2 100644 --- a/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts +++ b/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts @@ -5,474 +5,482 @@ import * as assert from 'assert'; import { CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands'; -import { Cursor } from 'vs/editor/common/controller/cursor'; import { CursorMove } from 'vs/editor/common/controller/cursorMoveCommands'; 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 { TextModel } from 'vs/editor/common/model/textModel'; +import { withTestCodeEditor, ITestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; -import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; -import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer'; -import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; suite('Cursor move command test', () => { - let thisModel: TextModel; - let thisConfiguration: TestConfiguration; - let thisViewModel: ViewModel; - let thisCursor: Cursor; + const TEXT = [ + ' \tMy First Line\t ', + '\tMy Second Line', + ' Third LineđŸļ', + '', + '1' + ].join('\n'); - setup(() => { - let text = [ - ' \tMy First Line\t ', - '\tMy Second Line', - ' Third LineđŸļ', - '', - '1' - ].join('\n'); - - thisModel = createTextModel(text); - thisConfiguration = new TestConfiguration({}); - const monospaceLineBreaksComputerFactory = MonospaceLineBreaksComputerFactory.create(thisConfiguration.options); - thisViewModel = new ViewModel(0, thisConfiguration, thisModel, monospaceLineBreaksComputerFactory, monospaceLineBreaksComputerFactory, null!); - thisCursor = new Cursor(thisConfiguration, thisModel, thisViewModel); - }); - - teardown(() => { - thisCursor.dispose(); - thisViewModel.dispose(); - thisModel.dispose(); - thisConfiguration.dispose(); - }); + function executeTest(callback: (editor: ITestCodeEditor, viewModel: ViewModel) => void): void { + withTestCodeEditor(TEXT, {}, (editor, viewModel) => { + callback(editor, viewModel); + }); + } test('move left should move to left character', () => { - moveTo(thisCursor, 1, 8); - - moveLeft(thisCursor); - - cursorEqual(thisCursor, 1, 7); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 8); + moveLeft(viewModel); + cursorEqual(viewModel, 1, 7); + }); }); test('move left should move to left by n characters', () => { - moveTo(thisCursor, 1, 8); - - moveLeft(thisCursor, 3); - - cursorEqual(thisCursor, 1, 5); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 8); + moveLeft(viewModel, 3); + cursorEqual(viewModel, 1, 5); + }); }); test('move left should move to left by half line', () => { - moveTo(thisCursor, 1, 8); - - moveLeft(thisCursor, 1, CursorMove.RawUnit.HalfLine); - - cursorEqual(thisCursor, 1, 1); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 8); + moveLeft(viewModel, 1, CursorMove.RawUnit.HalfLine); + cursorEqual(viewModel, 1, 1); + }); }); test('move left moves to previous line', () => { - moveTo(thisCursor, 2, 3); - - moveLeft(thisCursor, 10); - - cursorEqual(thisCursor, 1, 21); + executeTest((editor, viewModel) => { + moveTo(viewModel, 2, 3); + moveLeft(viewModel, 10); + cursorEqual(viewModel, 1, 21); + }); }); test('move right should move to right character', () => { - moveTo(thisCursor, 1, 5); - - moveRight(thisCursor); - - cursorEqual(thisCursor, 1, 6); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 5); + moveRight(viewModel); + cursorEqual(viewModel, 1, 6); + }); }); test('move right should move to right by n characters', () => { - moveTo(thisCursor, 1, 2); - - moveRight(thisCursor, 6); - - cursorEqual(thisCursor, 1, 8); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 2); + moveRight(viewModel, 6); + cursorEqual(viewModel, 1, 8); + }); }); test('move right should move to right by half line', () => { - moveTo(thisCursor, 1, 4); - - moveRight(thisCursor, 1, CursorMove.RawUnit.HalfLine); - - cursorEqual(thisCursor, 1, 14); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 4); + moveRight(viewModel, 1, CursorMove.RawUnit.HalfLine); + cursorEqual(viewModel, 1, 14); + }); }); test('move right moves to next line', () => { - moveTo(thisCursor, 1, 8); - - moveRight(thisCursor, 100); - - cursorEqual(thisCursor, 2, 1); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 8); + moveRight(viewModel, 100); + cursorEqual(viewModel, 2, 1); + }); }); test('move to first character of line from middle', () => { - moveTo(thisCursor, 1, 8); - moveToLineStart(thisCursor); - cursorEqual(thisCursor, 1, 1); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 8); + moveToLineStart(viewModel); + cursorEqual(viewModel, 1, 1); + }); }); test('move to first character of line from first non white space character', () => { - moveTo(thisCursor, 1, 6); - - moveToLineStart(thisCursor); - - cursorEqual(thisCursor, 1, 1); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 6); + moveToLineStart(viewModel); + cursorEqual(viewModel, 1, 1); + }); }); test('move to first character of line from first character', () => { - moveTo(thisCursor, 1, 1); - - moveToLineStart(thisCursor); - - cursorEqual(thisCursor, 1, 1); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 1); + moveToLineStart(viewModel); + cursorEqual(viewModel, 1, 1); + }); }); test('move to first non white space character of line from middle', () => { - moveTo(thisCursor, 1, 8); - - moveToLineFirstNonWhitespaceCharacter(thisCursor); - - cursorEqual(thisCursor, 1, 6); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 8); + moveToLineFirstNonWhitespaceCharacter(viewModel); + cursorEqual(viewModel, 1, 6); + }); }); test('move to first non white space character of line from first non white space character', () => { - moveTo(thisCursor, 1, 6); - - moveToLineFirstNonWhitespaceCharacter(thisCursor); - - cursorEqual(thisCursor, 1, 6); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 6); + moveToLineFirstNonWhitespaceCharacter(viewModel); + cursorEqual(viewModel, 1, 6); + }); }); test('move to first non white space character of line from first character', () => { - moveTo(thisCursor, 1, 1); - - moveToLineFirstNonWhitespaceCharacter(thisCursor); - - cursorEqual(thisCursor, 1, 6); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 1); + moveToLineFirstNonWhitespaceCharacter(viewModel); + cursorEqual(viewModel, 1, 6); + }); }); test('move to end of line from middle', () => { - moveTo(thisCursor, 1, 8); - - moveToLineEnd(thisCursor); - - cursorEqual(thisCursor, 1, 21); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 8); + moveToLineEnd(viewModel); + cursorEqual(viewModel, 1, 21); + }); }); test('move to end of line from last non white space character', () => { - moveTo(thisCursor, 1, 19); - - moveToLineEnd(thisCursor); - - cursorEqual(thisCursor, 1, 21); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 19); + moveToLineEnd(viewModel); + cursorEqual(viewModel, 1, 21); + }); }); test('move to end of line from line end', () => { - moveTo(thisCursor, 1, 21); - - moveToLineEnd(thisCursor); - - cursorEqual(thisCursor, 1, 21); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 21); + moveToLineEnd(viewModel); + cursorEqual(viewModel, 1, 21); + }); }); test('move to last non white space character from middle', () => { - moveTo(thisCursor, 1, 8); - - moveToLineLastNonWhitespaceCharacter(thisCursor); - - cursorEqual(thisCursor, 1, 19); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 8); + moveToLineLastNonWhitespaceCharacter(viewModel); + cursorEqual(viewModel, 1, 19); + }); }); test('move to last non white space character from last non white space character', () => { - moveTo(thisCursor, 1, 19); - - moveToLineLastNonWhitespaceCharacter(thisCursor); - - cursorEqual(thisCursor, 1, 19); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 19); + moveToLineLastNonWhitespaceCharacter(viewModel); + cursorEqual(viewModel, 1, 19); + }); }); test('move to last non white space character from line end', () => { - moveTo(thisCursor, 1, 21); - - moveToLineLastNonWhitespaceCharacter(thisCursor); - - cursorEqual(thisCursor, 1, 19); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 21); + moveToLineLastNonWhitespaceCharacter(viewModel); + cursorEqual(viewModel, 1, 19); + }); }); test('move to center of line not from center', () => { - moveTo(thisCursor, 1, 8); - - moveToLineCenter(thisCursor); - - cursorEqual(thisCursor, 1, 11); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 8); + moveToLineCenter(viewModel); + cursorEqual(viewModel, 1, 11); + }); }); test('move to center of line from center', () => { - moveTo(thisCursor, 1, 11); - - moveToLineCenter(thisCursor); - - cursorEqual(thisCursor, 1, 11); + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 11); + moveToLineCenter(viewModel); + cursorEqual(viewModel, 1, 11); + }); }); test('move to center of line from start', () => { - moveToLineStart(thisCursor); - - moveToLineCenter(thisCursor); - - cursorEqual(thisCursor, 1, 11); + executeTest((editor, viewModel) => { + moveToLineStart(viewModel); + moveToLineCenter(viewModel); + cursorEqual(viewModel, 1, 11); + }); }); test('move to center of line from end', () => { - moveToLineEnd(thisCursor); - - moveToLineCenter(thisCursor); - - cursorEqual(thisCursor, 1, 11); + executeTest((editor, viewModel) => { + moveToLineEnd(viewModel); + moveToLineCenter(viewModel); + cursorEqual(viewModel, 1, 11); + }); }); test('move up by cursor move command', () => { + executeTest((editor, viewModel) => { + moveTo(viewModel, 3, 5); + cursorEqual(viewModel, 3, 5); - moveTo(thisCursor, 3, 5); - cursorEqual(thisCursor, 3, 5); + moveUp(viewModel, 2); + cursorEqual(viewModel, 1, 5); - moveUp(thisCursor, 2); - cursorEqual(thisCursor, 1, 5); - - moveUp(thisCursor, 1); - cursorEqual(thisCursor, 1, 1); + moveUp(viewModel, 1); + cursorEqual(viewModel, 1, 1); + }); }); test('move up by model line cursor move command', () => { + executeTest((editor, viewModel) => { + moveTo(viewModel, 3, 5); + cursorEqual(viewModel, 3, 5); - moveTo(thisCursor, 3, 5); - cursorEqual(thisCursor, 3, 5); + moveUpByModelLine(viewModel, 2); + cursorEqual(viewModel, 1, 5); - moveUpByModelLine(thisCursor, 2); - cursorEqual(thisCursor, 1, 5); - - moveUpByModelLine(thisCursor, 1); - cursorEqual(thisCursor, 1, 1); + moveUpByModelLine(viewModel, 1); + cursorEqual(viewModel, 1, 1); + }); }); test('move down by model line cursor move command', () => { + executeTest((editor, viewModel) => { + moveTo(viewModel, 3, 5); + cursorEqual(viewModel, 3, 5); - moveTo(thisCursor, 3, 5); - cursorEqual(thisCursor, 3, 5); + moveDownByModelLine(viewModel, 2); + cursorEqual(viewModel, 5, 2); - moveDownByModelLine(thisCursor, 2); - cursorEqual(thisCursor, 5, 2); - - moveDownByModelLine(thisCursor, 1); - cursorEqual(thisCursor, 5, 2); + moveDownByModelLine(viewModel, 1); + cursorEqual(viewModel, 5, 2); + }); }); test('move up with selection by cursor move command', () => { + executeTest((editor, viewModel) => { + moveTo(viewModel, 3, 5); + cursorEqual(viewModel, 3, 5); - moveTo(thisCursor, 3, 5); - cursorEqual(thisCursor, 3, 5); + moveUp(viewModel, 1, true); + cursorEqual(viewModel, 2, 2, 3, 5); - moveUp(thisCursor, 1, true); - cursorEqual(thisCursor, 2, 2, 3, 5); - - moveUp(thisCursor, 1, true); - cursorEqual(thisCursor, 1, 5, 3, 5); + moveUp(viewModel, 1, true); + cursorEqual(viewModel, 1, 5, 3, 5); + }); }); test('move up and down with tabs by cursor move command', () => { + executeTest((editor, viewModel) => { + moveTo(viewModel, 1, 5); + cursorEqual(viewModel, 1, 5); - moveTo(thisCursor, 1, 5); - cursorEqual(thisCursor, 1, 5); + moveDown(viewModel, 4); + cursorEqual(viewModel, 5, 2); - moveDown(thisCursor, 4); - cursorEqual(thisCursor, 5, 2); + moveUp(viewModel, 1); + cursorEqual(viewModel, 4, 1); - moveUp(thisCursor, 1); - cursorEqual(thisCursor, 4, 1); + moveUp(viewModel, 1); + cursorEqual(viewModel, 3, 5); - moveUp(thisCursor, 1); - cursorEqual(thisCursor, 3, 5); + moveUp(viewModel, 1); + cursorEqual(viewModel, 2, 2); - moveUp(thisCursor, 1); - cursorEqual(thisCursor, 2, 2); - - moveUp(thisCursor, 1); - cursorEqual(thisCursor, 1, 5); + moveUp(viewModel, 1); + cursorEqual(viewModel, 1, 5); + }); }); test('move up and down with end of lines starting from a long one by cursor move command', () => { + executeTest((editor, viewModel) => { + moveToEndOfLine(viewModel); + cursorEqual(viewModel, 1, 21); - moveToEndOfLine(thisCursor); - cursorEqual(thisCursor, 1, 21); + moveToEndOfLine(viewModel); + cursorEqual(viewModel, 1, 21); - moveToEndOfLine(thisCursor); - cursorEqual(thisCursor, 1, 21); + moveDown(viewModel, 2); + cursorEqual(viewModel, 3, 17); - moveDown(thisCursor, 2); - cursorEqual(thisCursor, 3, 17); + moveDown(viewModel, 1); + cursorEqual(viewModel, 4, 1); - moveDown(thisCursor, 1); - cursorEqual(thisCursor, 4, 1); + moveDown(viewModel, 1); + cursorEqual(viewModel, 5, 2); - moveDown(thisCursor, 1); - cursorEqual(thisCursor, 5, 2); - - moveUp(thisCursor, 4); - cursorEqual(thisCursor, 1, 21); + moveUp(viewModel, 4); + cursorEqual(viewModel, 1, 21); + }); }); test('move to view top line moves to first visible line if it is first line', () => { - thisViewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 10, 1); + executeTest((editor, viewModel) => { + viewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 10, 1); - moveTo(thisCursor, 2, 2); - moveToTop(thisCursor); + moveTo(viewModel, 2, 2); + moveToTop(viewModel); - cursorEqual(thisCursor, 1, 6); + cursorEqual(viewModel, 1, 6); + }); }); test('move to view top line moves to top visible line when first line is not visible', () => { - thisViewModel.getCompletelyVisibleViewRange = () => new Range(2, 1, 10, 1); + executeTest((editor, viewModel) => { + viewModel.getCompletelyVisibleViewRange = () => new Range(2, 1, 10, 1); - moveTo(thisCursor, 4, 1); - moveToTop(thisCursor); + moveTo(viewModel, 4, 1); + moveToTop(viewModel); - cursorEqual(thisCursor, 2, 2); + cursorEqual(viewModel, 2, 2); + }); }); test('move to view top line moves to nth line from top', () => { - thisViewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 10, 1); + executeTest((editor, viewModel) => { + viewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 10, 1); - moveTo(thisCursor, 4, 1); - moveToTop(thisCursor, 3); + moveTo(viewModel, 4, 1); + moveToTop(viewModel, 3); - cursorEqual(thisCursor, 3, 5); + cursorEqual(viewModel, 3, 5); + }); }); test('move to view top line moves to last line if n is greater than last visible line number', () => { - thisViewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 3, 1); + executeTest((editor, viewModel) => { + viewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 3, 1); - moveTo(thisCursor, 2, 2); - moveToTop(thisCursor, 4); + moveTo(viewModel, 2, 2); + moveToTop(viewModel, 4); - cursorEqual(thisCursor, 3, 5); + cursorEqual(viewModel, 3, 5); + }); }); test('move to view center line moves to the center line', () => { - thisViewModel.getCompletelyVisibleViewRange = () => new Range(3, 1, 3, 1); + executeTest((editor, viewModel) => { + viewModel.getCompletelyVisibleViewRange = () => new Range(3, 1, 3, 1); - moveTo(thisCursor, 2, 2); - moveToCenter(thisCursor); + moveTo(viewModel, 2, 2); + moveToCenter(viewModel); - cursorEqual(thisCursor, 3, 5); + cursorEqual(viewModel, 3, 5); + }); }); test('move to view bottom line moves to last visible line if it is last line', () => { - thisViewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 5, 1); + executeTest((editor, viewModel) => { + viewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 5, 1); - moveTo(thisCursor, 2, 2); - moveToBottom(thisCursor); + moveTo(viewModel, 2, 2); + moveToBottom(viewModel); - cursorEqual(thisCursor, 5, 1); + cursorEqual(viewModel, 5, 1); + }); }); test('move to view bottom line moves to last visible line when last line is not visible', () => { - thisViewModel.getCompletelyVisibleViewRange = () => new Range(2, 1, 3, 1); + executeTest((editor, viewModel) => { + viewModel.getCompletelyVisibleViewRange = () => new Range(2, 1, 3, 1); - moveTo(thisCursor, 2, 2); - moveToBottom(thisCursor); + moveTo(viewModel, 2, 2); + moveToBottom(viewModel); - cursorEqual(thisCursor, 3, 5); + cursorEqual(viewModel, 3, 5); + }); }); test('move to view bottom line moves to nth line from bottom', () => { - thisViewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 5, 1); + executeTest((editor, viewModel) => { + viewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 5, 1); - moveTo(thisCursor, 4, 1); - moveToBottom(thisCursor, 3); + moveTo(viewModel, 4, 1); + moveToBottom(viewModel, 3); - cursorEqual(thisCursor, 3, 5); + cursorEqual(viewModel, 3, 5); + }); }); test('move to view bottom line moves to first line if n is lesser than first visible line number', () => { - thisViewModel.getCompletelyVisibleViewRange = () => new Range(2, 1, 5, 1); + executeTest((editor, viewModel) => { + viewModel.getCompletelyVisibleViewRange = () => new Range(2, 1, 5, 1); - moveTo(thisCursor, 4, 1); - moveToBottom(thisCursor, 5); + moveTo(viewModel, 4, 1); + moveToBottom(viewModel, 5); - cursorEqual(thisCursor, 2, 2); + cursorEqual(viewModel, 2, 2); + }); }); }); // Move command -function move(cursor: Cursor, args: any) { - CoreNavigationCommands.CursorMove.runCoreEditorCommand(cursor, args); +function move(viewModel: ViewModel, args: any) { + CoreNavigationCommands.CursorMove.runCoreEditorCommand(viewModel, args); } -function moveToLineStart(cursor: Cursor) { - move(cursor, { to: CursorMove.RawDirection.WrappedLineStart }); +function moveToLineStart(viewModel: ViewModel) { + move(viewModel, { to: CursorMove.RawDirection.WrappedLineStart }); } -function moveToLineFirstNonWhitespaceCharacter(cursor: Cursor) { - move(cursor, { to: CursorMove.RawDirection.WrappedLineFirstNonWhitespaceCharacter }); +function moveToLineFirstNonWhitespaceCharacter(viewModel: ViewModel) { + move(viewModel, { to: CursorMove.RawDirection.WrappedLineFirstNonWhitespaceCharacter }); } -function moveToLineCenter(cursor: Cursor) { - move(cursor, { to: CursorMove.RawDirection.WrappedLineColumnCenter }); +function moveToLineCenter(viewModel: ViewModel) { + move(viewModel, { to: CursorMove.RawDirection.WrappedLineColumnCenter }); } -function moveToLineEnd(cursor: Cursor) { - move(cursor, { to: CursorMove.RawDirection.WrappedLineEnd }); +function moveToLineEnd(viewModel: ViewModel) { + move(viewModel, { to: CursorMove.RawDirection.WrappedLineEnd }); } -function moveToLineLastNonWhitespaceCharacter(cursor: Cursor) { - move(cursor, { to: CursorMove.RawDirection.WrappedLineLastNonWhitespaceCharacter }); +function moveToLineLastNonWhitespaceCharacter(viewModel: ViewModel) { + move(viewModel, { to: CursorMove.RawDirection.WrappedLineLastNonWhitespaceCharacter }); } -function moveLeft(cursor: Cursor, value?: number, by?: string, select?: boolean) { - move(cursor, { to: CursorMove.RawDirection.Left, by: by, value: value, select: select }); +function moveLeft(viewModel: ViewModel, value?: number, by?: string, select?: boolean) { + move(viewModel, { to: CursorMove.RawDirection.Left, by: by, value: value, select: select }); } -function moveRight(cursor: Cursor, value?: number, by?: string, select?: boolean) { - move(cursor, { to: CursorMove.RawDirection.Right, by: by, value: value, select: select }); +function moveRight(viewModel: ViewModel, value?: number, by?: string, select?: boolean) { + move(viewModel, { to: CursorMove.RawDirection.Right, by: by, value: value, select: select }); } -function moveUp(cursor: Cursor, noOfLines: number = 1, select?: boolean) { - move(cursor, { to: CursorMove.RawDirection.Up, by: CursorMove.RawUnit.WrappedLine, value: noOfLines, select: select }); +function moveUp(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) { + move(viewModel, { to: CursorMove.RawDirection.Up, by: CursorMove.RawUnit.WrappedLine, value: noOfLines, select: select }); } -function moveUpByModelLine(cursor: Cursor, noOfLines: number = 1, select?: boolean) { - move(cursor, { to: CursorMove.RawDirection.Up, value: noOfLines, select: select }); +function moveUpByModelLine(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) { + move(viewModel, { to: CursorMove.RawDirection.Up, value: noOfLines, select: select }); } -function moveDown(cursor: Cursor, noOfLines: number = 1, select?: boolean) { - move(cursor, { to: CursorMove.RawDirection.Down, by: CursorMove.RawUnit.WrappedLine, value: noOfLines, select: select }); +function moveDown(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) { + move(viewModel, { to: CursorMove.RawDirection.Down, by: CursorMove.RawUnit.WrappedLine, value: noOfLines, select: select }); } -function moveDownByModelLine(cursor: Cursor, noOfLines: number = 1, select?: boolean) { - move(cursor, { to: CursorMove.RawDirection.Down, value: noOfLines, select: select }); +function moveDownByModelLine(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) { + move(viewModel, { to: CursorMove.RawDirection.Down, value: noOfLines, select: select }); } -function moveToTop(cursor: Cursor, noOfLines: number = 1, select?: boolean) { - move(cursor, { to: CursorMove.RawDirection.ViewPortTop, value: noOfLines, select: select }); +function moveToTop(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) { + move(viewModel, { to: CursorMove.RawDirection.ViewPortTop, value: noOfLines, select: select }); } -function moveToCenter(cursor: Cursor, select?: boolean) { - move(cursor, { to: CursorMove.RawDirection.ViewPortCenter, select: select }); +function moveToCenter(viewModel: ViewModel, select?: boolean) { + move(viewModel, { to: CursorMove.RawDirection.ViewPortCenter, select: select }); } -function moveToBottom(cursor: Cursor, noOfLines: number = 1, select?: boolean) { - move(cursor, { to: CursorMove.RawDirection.ViewPortBottom, value: noOfLines, select: select }); +function moveToBottom(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) { + move(viewModel, { to: CursorMove.RawDirection.ViewPortBottom, value: noOfLines, select: select }); } -function cursorEqual(cursor: Cursor, posLineNumber: number, posColumn: number, selLineNumber: number = posLineNumber, selColumn: number = posColumn) { - positionEqual(cursor.getPosition(), posLineNumber, posColumn); - selectionEqual(cursor.getSelection(), posLineNumber, posColumn, selLineNumber, selColumn); +function cursorEqual(viewModel: ViewModel, posLineNumber: number, posColumn: number, selLineNumber: number = posLineNumber, selColumn: number = posColumn) { + positionEqual(viewModel.getPosition(), posLineNumber, posColumn); + selectionEqual(viewModel.getSelection(), posLineNumber, posColumn, selLineNumber, selColumn); } function positionEqual(position: Position, lineNumber: number, column: number) { @@ -493,22 +501,22 @@ function selectionEqual(selection: Selection, posLineNumber: number, posColumn: }, 'selection equal'); } -function moveTo(cursor: Cursor, lineNumber: number, column: number, inSelectionMode: boolean = false) { +function moveTo(viewModel: ViewModel, lineNumber: number, column: number, inSelectionMode: boolean = false) { if (inSelectionMode) { - CoreNavigationCommands.MoveToSelect.runCoreEditorCommand(cursor, { + CoreNavigationCommands.MoveToSelect.runCoreEditorCommand(viewModel, { position: new Position(lineNumber, column) }); } else { - CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursor, { + CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(lineNumber, column) }); } } -function moveToEndOfLine(cursor: Cursor, inSelectionMode: boolean = false) { +function moveToEndOfLine(viewModel: ViewModel, inSelectionMode: boolean = false) { if (inSelectionMode) { - CoreNavigationCommands.CursorEndSelect.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorEndSelect.runCoreEditorCommand(viewModel, {}); } else { - CoreNavigationCommands.CursorEnd.runCoreEditorCommand(cursor, {}); + CoreNavigationCommands.CursorEnd.runCoreEditorCommand(viewModel, {}); } } diff --git a/src/vs/editor/test/browser/controller/inputRecorder.html b/src/vs/editor/test/browser/controller/inputRecorder.html index 3f538b8a04f..65d8a39c59a 100644 --- a/src/vs/editor/test/browser/controller/inputRecorder.html +++ b/src/vs/editor/test/browser/controller/inputRecorder.html @@ -31,14 +31,14 @@ var RECORDED_EVENTS = []; var input = document.getElementById('input'); -var blackListedProps = [ +var blockedProperties = [ 'currentTarget', 'path', 'srcElement', 'target', 'view' ]; -blackListedProps = blackListedProps.concat([ +blockedProperties = blockedProperties.concat([ 'AT_TARGET', 'BLUR', 'BUBBLING_PHASE', @@ -68,7 +68,7 @@ blackListedProps = blackListedProps.concat([ function toSerializable(e) { var r = {}; for (var k in e) { - if (blackListedProps.indexOf(k) >= 0) { + if (blockedProperties.indexOf(k) >= 0) { continue; } if (typeof e[k] === 'function') { @@ -112,4 +112,4 @@ document.getElementById('stop').onclick = function() { - \ No newline at end of file + diff --git a/src/vs/editor/test/browser/editorTestServices.ts b/src/vs/editor/test/browser/editorTestServices.ts index 747895a7a60..e9fba5fbb91 100644 --- a/src/vs/editor/test/browser/editorTestServices.ts +++ b/src/vs/editor/test/browser/editorTestServices.ts @@ -25,7 +25,7 @@ export class TestCodeEditorService extends AbstractCodeEditorService { } export class TestCommandService implements ICommandService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _instantiationService: IInstantiationService; diff --git a/src/vs/editor/test/browser/services/openerService.test.ts b/src/vs/editor/test/browser/services/openerService.test.ts index 697dc99402d..e0ab2ba61f1 100644 --- a/src/vs/editor/test/browser/services/openerService.test.ts +++ b/src/vs/editor/test/browser/services/openerService.test.ts @@ -16,7 +16,7 @@ suite('OpenerService', function () { let lastCommand: { id: string; args: any[] } | undefined; const commandService = new (class implements ICommandService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; onWillExecuteCommand = () => Disposable.None; onDidExecuteCommand = () => Disposable.None; executeCommand(id: string, ...args: any[]): Promise { diff --git a/src/vs/editor/test/browser/testCodeEditor.ts b/src/vs/editor/test/browser/testCodeEditor.ts index e81e049553a..cc39f304474 100644 --- a/src/vs/editor/test/browser/testCodeEditor.ts +++ b/src/vs/editor/test/browser/testCodeEditor.ts @@ -3,13 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, IActiveCodeEditor, IEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; import { IEditorContributionCtor } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { View } from 'vs/editor/browser/view/viewImpl'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; import * as editorOptions from 'vs/editor/common/config/editorOptions'; -import { Cursor } from 'vs/editor/common/controller/cursor'; import { IConfiguration, IEditorContribution } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; @@ -27,21 +26,26 @@ import { TestNotificationService } from 'vs/platform/notification/test/common/te import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; -export class TestCodeEditor extends CodeEditorWidget implements ICodeEditor { +export interface ITestCodeEditor extends IActiveCodeEditor { + getViewModel(): ViewModel | undefined; + registerAndInstantiateContribution(id: string, ctor: new (editor: ICodeEditor, ...services: Services) => T): T; +} + +class TestCodeEditor extends CodeEditorWidget implements ICodeEditor { //#region testing overrides - protected _createConfiguration(options: editorOptions.IEditorConstructionOptions): IConfiguration { + protected _createConfiguration(options: IEditorConstructionOptions): IConfiguration { return new TestConfiguration(options); } - protected _createView(viewModel: ViewModel, cursor: Cursor): [View, boolean] { + protected _createView(viewModel: ViewModel): [View, boolean] { // Never create a view return [null! as View, false]; } //#endregion //#region Testing utils - public getCursor(): Cursor | undefined { - return this._modelData ? this._modelData.cursor : undefined; + public getViewModel(): ViewModel | undefined { + return this._modelData ? this._modelData.viewModel : undefined; } public registerAndInstantiateContribution(id: string, ctor: new (editor: ICodeEditor, ...services: Services) => T): T { const r: T = this._instantiationService.createInstance(ctor as IEditorContributionCtor, this); @@ -74,7 +78,7 @@ export interface TestCodeEditorCreationOptions extends editorOptions.IEditorOpti serviceCollection?: ServiceCollection; } -export function withTestCodeEditor(text: string | string[] | null, options: TestCodeEditorCreationOptions, callback: (editor: TestCodeEditor, cursor: Cursor) => void): void { +export function withTestCodeEditor(text: string | string[] | null, options: TestCodeEditorCreationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel) => void): void { // create a model if necessary and remember it in order to dispose it. if (!options.model) { if (typeof text === 'string') { @@ -84,14 +88,33 @@ export function withTestCodeEditor(text: string | string[] | null, options: Test } } - let editor = createTestCodeEditor(options); - editor.getCursor()!.setHasFocus(true); - callback(editor, editor.getCursor()!); + const editor = createTestCodeEditor(options); + const viewModel = editor.getViewModel()!; + viewModel.setHasFocus(true); + callback(editor, editor.getViewModel()!); editor.dispose(); } -export function createTestCodeEditor(options: TestCodeEditorCreationOptions): TestCodeEditor { +export async function withAsyncTestCodeEditor(text: string | string[] | null, options: TestCodeEditorCreationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel) => Promise): Promise { + // create a model if necessary and remember it in order to dispose it. + if (!options.model) { + if (typeof text === 'string') { + options.model = createTextModel(text); + } else if (text) { + options.model = createTextModel(text.join('\n')); + } + } + + const editor = createTestCodeEditor(options); + const viewModel = editor.getViewModel()!; + viewModel.setHasFocus(true); + await callback(editor, editor.getViewModel()!); + + editor.dispose(); +} + +export function createTestCodeEditor(options: TestCodeEditorCreationOptions): ITestCodeEditor { const model = options.model; delete options.model; @@ -127,5 +150,5 @@ export function createTestCodeEditor(options: TestCodeEditorCreationOptions): Te codeEditorWidgetOptions ); editor.setModel(model); - return editor; + return editor; } diff --git a/src/vs/editor/test/browser/testCommand.ts b/src/vs/editor/test/browser/testCommand.ts index 4c280c1d960..c3f73be0274 100644 --- a/src/vs/editor/test/browser/testCommand.ts +++ b/src/vs/editor/test/browser/testCommand.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { IRange } from 'vs/editor/common/core/range'; import { Selection, ISelection } from 'vs/editor/common/core/selection'; -import { ICommand, Handler, IEditOperationBuilder } from 'vs/editor/common/editorCommon'; +import { ICommand, IEditOperationBuilder } from 'vs/editor/common/editorCommon'; import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { LanguageIdentifier } from 'vs/editor/common/modes'; @@ -33,7 +33,7 @@ export function testCommand( cursor.setSelections('tests', [selection]); - cursor.trigger('tests', Handler.ExecuteCommand, commandFactory(cursor.getSelection())); + cursor.executeCommand(commandFactory(cursor.getSelection()), 'tests'); assert.deepEqual(model.getLinesContent(), expectedLines); diff --git a/src/vs/editor/test/common/config/commonEditorConfig.test.ts b/src/vs/editor/test/common/config/commonEditorConfig.test.ts index c0196b37bd1..d0edc8f44c0 100644 --- a/src/vs/editor/test/common/config/commonEditorConfig.test.ts +++ b/src/vs/editor/test/common/config/commonEditorConfig.test.ts @@ -211,4 +211,15 @@ suite('Common Editor Config', () => { strings: false }); }); + + test('issue #102920: Can\'t snap or split view with JSON files', () => { + const config = new TestConfiguration({ quickSuggestions: null! }); + config.updateOptions({ quickSuggestions: { strings: true } }); + const actual = >>config.options.get(EditorOption.quickSuggestions); + assert.deepEqual(actual, { + other: true, + comments: false, + strings: true + }); + }); }); diff --git a/src/vs/editor/test/common/model/benchmark/searchNReplace.benchmark.ts b/src/vs/editor/test/common/model/benchmark/searchNReplace.benchmark.ts index aecb8ad46ca..7a417a22aab 100644 --- a/src/vs/editor/test/common/model/benchmark/searchNReplace.benchmark.ts +++ b/src/vs/editor/test/common/model/benchmark/searchNReplace.benchmark.ts @@ -7,30 +7,30 @@ import { ITextBufferBuilder } from 'vs/editor/common/model'; import { BenchmarkSuite } from 'vs/editor/test/common/model/benchmark/benchmarkUtils'; import { generateRandomChunkWithLF, generateRandomReplaces } from 'vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils'; -let fileSizes = [1, 1000, 64 * 1000, 32 * 1000 * 1000]; +const fileSizes = [1, 1000, 64 * 1000, 32 * 1000 * 1000]; -for (let fileSize of fileSizes) { - let chunks: string[] = []; +for (const fileSize of fileSizes) { + const chunks: string[] = []; - let chunkCnt = Math.floor(fileSize / (64 * 1000)); + const chunkCnt = Math.floor(fileSize / (64 * 1000)); if (chunkCnt === 0) { chunks.push(generateRandomChunkWithLF(fileSize, fileSize)); } else { - let chunk = generateRandomChunkWithLF(64 * 1000, 64 * 1000); + const chunk = generateRandomChunkWithLF(64 * 1000, 64 * 1000); // try to avoid OOM for (let j = 0; j < chunkCnt; j++) { chunks.push(Buffer.from(chunk + j).toString()); } } - let replaceSuite = new BenchmarkSuite({ + const replaceSuite = new BenchmarkSuite({ name: `File Size: ${fileSize}Byte`, iterations: 10 }); - let edits = generateRandomReplaces(chunks, 500, 5, 10); + const edits = generateRandomReplaces(chunks, 500, 5, 10); - for (let i of [10, 100, 500]) { + for (const i of [10, 100, 500]) { replaceSuite.add({ name: `replace ${i} occurrences`, buildBuffer: (textBufferBuilder: ITextBufferBuilder) => { diff --git a/src/vs/editor/test/common/model/textModelSearch.test.ts b/src/vs/editor/test/common/model/textModelSearch.test.ts index e5af37e781b..715cd3032b5 100644 --- a/src/vs/editor/test/common/model/textModelSearch.test.ts +++ b/src/vs/editor/test/common/model/textModelSearch.test.ts @@ -781,4 +781,29 @@ suite('TextModelSearch', () => { model.dispose(); }); + + test('issue #100134. Zero-length matches should properly step over surrogate pairs', () => { + // 1[Laptop]1 - there shoud be no matches inside of [Laptop] emoji + assertFindMatches('1\uD83D\uDCBB1', '()', true, false, null, + [ + [1, 1, 1, 1], + [1, 2, 1, 2], + [1, 4, 1, 4], + [1, 5, 1, 5], + + ] + ); + // 1[Hacker Cat]1 = 1[Cat Face][ZWJ][Laptop]1 - there shoud be matches between emoji and ZWJ + // there shoud be no matches inside of [Cat Face] and [Laptop] emoji + assertFindMatches('1\uD83D\uDC31\u200D\uD83D\uDCBB1', '()', true, false, null, + [ + [1, 1, 1, 1], + [1, 2, 1, 2], + [1, 4, 1, 4], + [1, 5, 1, 5], + [1, 7, 1, 7], + [1, 8, 1, 8] + ] + ); + }); }); diff --git a/src/vs/editor/test/common/modes/languageSelector.test.ts b/src/vs/editor/test/common/modes/languageSelector.test.ts index 5ae99749ad5..0886c65bbee 100644 --- a/src/vs/editor/test/common/modes/languageSelector.test.ts +++ b/src/vs/editor/test/common/modes/languageSelector.test.ts @@ -104,4 +104,15 @@ suite('LanguageSelector', function () { let value = score(selector, URI.parse('file:///C:/Users/zlhe/Desktop/test.interface.json'), 'json', true); assert.equal(value, 10); }); + + test('Document selector match - platform paths #99938', function () { + let selector = { + pattern: { + base: '/home/user/Desktop', + pattern: '*.json' + } + }; + let value = score(selector, URI.file('/home/user/Desktop/test.json'), 'json', true); + assert.equal(value, 10); + }); }); diff --git a/src/vs/editor/test/common/modes/linkComputer.test.ts b/src/vs/editor/test/common/modes/linkComputer.test.ts index 9ed515c2a2e..9cf9ca77c2f 100644 --- a/src/vs/editor/test/common/modes/linkComputer.test.ts +++ b/src/vs/editor/test/common/modes/linkComputer.test.ts @@ -223,4 +223,11 @@ suite('Editor Modes - Link Computer', () => { ' https://foo.bar/[this is foo site] ' ); }); + + test('issue #100353: Link detection stops at īŧ†(double-byte)', () => { + assertLink( + 'aa http://tree-mark.chips.jp/ãƒŦãƒŧã‚ēãƒŗīŧ†ãƒ™ãƒĒãƒŧミック゚ aa', + ' http://tree-mark.chips.jp/ãƒŦãƒŧã‚ēãƒŗīŧ†ãƒ™ãƒĒãƒŧミック゚ ' + ); + }); }); diff --git a/src/vs/editor/test/common/modes/supports/javascriptOnEnterRules.ts b/src/vs/editor/test/common/modes/supports/javascriptOnEnterRules.ts index 8ad60864350..7222048074e 100644 --- a/src/vs/editor/test/common/modes/supports/javascriptOnEnterRules.ts +++ b/src/vs/editor/test/common/modes/supports/javascriptOnEnterRules.ts @@ -18,7 +18,7 @@ export const javascriptOnEnterRules = [ }, { // e.g. * ...| beforeText: /^(\t|[ ])*[ ]\*([ ]([^\*]|\*(?!\/))*)?$/, - oneLineAboveText: /^(\s*(\/\*\*|\*)).*/, + oneLineAboveText: /(?=^(\s*(\/\*\*|\*)).*)(?=(?!(\s*\*\/)))/, action: { indentAction: IndentAction.None, appendText: '* ' } }, { // e.g. */| diff --git a/src/vs/editor/test/common/modes/supports/onEnter.test.ts b/src/vs/editor/test/common/modes/supports/onEnter.test.ts index 6fc397d62fe..f799c66e76e 100644 --- a/src/vs/editor/test/common/modes/supports/onEnter.test.ts +++ b/src/vs/editor/test/common/modes/supports/onEnter.test.ts @@ -118,6 +118,7 @@ suite('OnEnter', () => { testIndentAction(' *', ' * asdfsfagadfg * / * / * /*', '', IndentAction.None, '* '); testIndentAction('', ' */', '', IndentAction.None, null, 1); + testIndentAction(' */', ' * test() {', '', IndentAction.Indent, null, 0); testIndentAction('', '\t */', '', IndentAction.None, null, 1); testIndentAction('', '\t\t */', '', IndentAction.None, null, 1); testIndentAction('', ' */', '', IndentAction.None, null, 1); diff --git a/src/vs/editor/test/common/services/languagesRegistry.test.ts b/src/vs/editor/test/common/services/languagesRegistry.test.ts index 45c3ba17966..09ef74cd000 100644 --- a/src/vs/editor/test/common/services/languagesRegistry.test.ts +++ b/src/vs/editor/test/common/services/languagesRegistry.test.ts @@ -221,6 +221,32 @@ suite('LanguagesRegistry', () => { assert.deepEqual(registry.getExtensions('aName'), ['aExt', 'aExt2']); }); + test('extensions of primary language registration come first', () => { + let registry = new LanguagesRegistry(false); + + registry._registerLanguages([{ + id: 'a', + extensions: ['aExt3'] + }]); + + assert.deepEqual(registry.getExtensions('a')[0], 'aExt3'); + + registry._registerLanguages([{ + id: 'a', + configuration: URI.file('conf.json'), + extensions: ['aExt'] + }]); + + assert.deepEqual(registry.getExtensions('a')[0], 'aExt'); + + registry._registerLanguages([{ + id: 'a', + extensions: ['aExt2'] + }]); + + assert.deepEqual(registry.getExtensions('a')[0], 'aExt'); + }); + test('filenames', () => { let registry = new LanguagesRegistry(false); diff --git a/src/vs/editor/test/common/services/modelService.test.ts b/src/vs/editor/test/common/services/modelService.test.ts index 6aef4d186a5..52672ffe3a1 100644 --- a/src/vs/editor/test/common/services/modelService.test.ts +++ b/src/vs/editor/test/common/services/modelService.test.ts @@ -442,7 +442,7 @@ assertComputeEdits(file1, file2); export class TestTextResourcePropertiesService implements ITextResourcePropertiesService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @IConfigurationService private readonly configurationService: IConfigurationService, diff --git a/src/vs/editor/test/common/viewLayout/linesLayout.test.ts b/src/vs/editor/test/common/viewLayout/linesLayout.test.ts index e210f797ebb..3205a10ce2b 100644 --- a/src/vs/editor/test/common/viewLayout/linesLayout.test.ts +++ b/src/vs/editor/test/common/viewLayout/linesLayout.test.ts @@ -8,9 +8,11 @@ import { LinesLayout, EditorWhitespace } from 'vs/editor/common/viewLayout/lines suite('Editor ViewLayout - LinesLayout', () => { function insertWhitespace(linesLayout: LinesLayout, afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string { - return linesLayout.changeWhitespace((accessor) => { - return accessor.insertWhitespace(afterLineNumber, ordinal, heightInPx, minWidth); + let id: string; + linesLayout.changeWhitespace((accessor) => { + id = accessor.insertWhitespace(afterLineNumber, ordinal, heightInPx, minWidth); }); + return id!; } function changeOneWhitespace(linesLayout: LinesLayout, id: string, newAfterLineNumber: number, newHeight: number): void { diff --git a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts index 7124aba04e5..1147b17c205 100644 --- a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts +++ b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts @@ -703,6 +703,36 @@ suite('viewLineRenderer.renderLine', () => { assert.equal(actual.containsRTL, true); }); + test('issue #95685: Uses unicode replacement character for Paragraph Separator', () => { + const lineText = 'var ftext = [\u2029"Und", "dann", "eines"];'; + const lineParts = createViewLineTokens([createPart(lineText.length, 1)]); + const expectedOutput = [ + 'var\u00a0ftext\u00a0=\u00a0[\uFFFD"Und",\u00a0"dann",\u00a0"eines"];' + ]; + const actual = renderViewLine(new RenderLineInput( + false, + true, + lineText, + false, + false, + false, + 0, + lineParts, + [], + 4, + 0, + 10, + 10, + 10, + -1, + 'none', + false, + false, + null + )); + assert.equal(actual.html, '' + expectedOutput.join('') + ''); + }); + test('issue #19673: Monokai Theme bad-highlighting in line wrap', () => { let lineText = ' MongoCallback): void {'; diff --git a/src/vs/editor/test/common/viewModel/viewModelDecorations.test.ts b/src/vs/editor/test/common/viewModel/viewModelDecorations.test.ts index 099e4d4e915..cd9906811b8 100644 --- a/src/vs/editor/test/common/viewModel/viewModelDecorations.test.ts +++ b/src/vs/editor/test/common/viewModel/viewModelDecorations.test.ts @@ -77,7 +77,7 @@ suite('ViewModelDecorations', () => { new Range(2, viewModel.getLineMinColumn(2), 3, viewModel.getLineMaxColumn(3)) ).map((dec) => { return dec.options.className; - }); + }).filter(Boolean); assert.deepEqual(actualDecorations, [ 'dec1', @@ -292,7 +292,7 @@ suite('ViewModelDecorations', () => { let decorations = viewModel.getDecorationsInViewport( new Range(2, viewModel.getLineMinColumn(2), 3, viewModel.getLineMaxColumn(3)) - ); + ).filter(x => Boolean(x.options.beforeContentClassName)); assert.deepEqual(decorations, []); let inlineDecorations1 = viewModel.getViewLineRenderingData( diff --git a/src/vs/editor/test/common/viewModel/viewModelImpl.test.ts b/src/vs/editor/test/common/viewModel/viewModelImpl.test.ts index dd012520e9f..6fe30a1284a 100644 --- a/src/vs/editor/test/common/viewModel/viewModelImpl.test.ts +++ b/src/vs/editor/test/common/viewModel/viewModelImpl.test.ts @@ -7,6 +7,8 @@ import * as assert from 'assert'; import { Range } from 'vs/editor/common/core/range'; import { EndOfLineSequence } from 'vs/editor/common/model'; import { testViewModel } from 'vs/editor/test/common/viewModel/testViewModel'; +import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; +import { ViewEvent } from 'vs/editor/common/view/viewEvents'; suite('ViewModel', () => { @@ -63,14 +65,16 @@ suite('ViewModel', () => { let viewLineCount: number[] = []; viewLineCount.push(viewModel.getLineCount()); - viewModel.addEventListener((events) => { - // Access the view model - viewLineCount.push(viewModel.getLineCount()); + viewModel.addViewEventHandler(new class extends ViewEventHandler { + handleEvents(events: ViewEvent[]): void { + // Access the view model + viewLineCount.push(viewModel.getLineCount()); + } }); model.undo(); viewLineCount.push(viewModel.getLineCount()); - assert.deepEqual(viewLineCount, [4, 1, 1, 1]); + assert.deepEqual(viewLineCount, [4, 1, 1, 1, 1]); }); }); diff --git a/src/vs/loader.js b/src/vs/loader.js index e81ef52603f..5da6a16b13e 100644 --- a/src/vs/loader.js +++ b/src/vs/loader.js @@ -195,7 +195,7 @@ var AMDLoader; return isEmpty; }; Utilities.recursiveClone = function (obj) { - if (!obj || typeof obj !== 'object') { + if (!obj || typeof obj !== 'object' || obj instanceof RegExp) { return obj; } var result = Array.isArray(obj) ? [] : {}; @@ -304,6 +304,9 @@ var AMDLoader; if (typeof options.cspNonce !== 'string') { options.cspNonce = ''; } + if (typeof options.preferScriptTags === 'undefined') { + options.preferScriptTags = false; + } if (!Array.isArray(options.nodeModules)) { options.nodeModules = []; } @@ -459,7 +462,9 @@ var AMDLoader; * Transform a module id to a location. Appends .js to module ids */ Configuration.prototype.moduleIdToPaths = function (moduleId) { - if (this.nodeModulesMap[moduleId] === true) { + var isNodeModule = ((this.nodeModulesMap[moduleId] === true) + || (this.options.amdModulesPattern instanceof RegExp && !this.options.amdModulesPattern.test(moduleId))); + if (isNodeModule) { // This is a node module... if (this.isBuild()) { // ...and we are at build time, drop it @@ -567,11 +572,24 @@ var AMDLoader; OnlyOnceScriptLoader.prototype.load = function (moduleManager, scriptSrc, callback, errorback) { var _this = this; if (!this._scriptLoader) { - this._scriptLoader = (this._env.isWebWorker - ? new WorkerScriptLoader() - : this._env.isNode - ? new NodeScriptLoader(this._env) - : new BrowserScriptLoader()); + if (this._env.isWebWorker) { + this._scriptLoader = new WorkerScriptLoader(); + } + else if (this._env.isElectronRenderer) { + var preferScriptTags = moduleManager.getConfig().getOptionsLiteral().preferScriptTags; + if (preferScriptTags) { + this._scriptLoader = new BrowserScriptLoader(); + } + else { + this._scriptLoader = new NodeScriptLoader(this._env); + } + } + else if (this._env.isNode) { + this._scriptLoader = new NodeScriptLoader(this._env); + } + else { + this._scriptLoader = new BrowserScriptLoader(); + } } var scriptCallbacks = { callback: callback, @@ -624,17 +642,34 @@ var AMDLoader; script.addEventListener('error', errorEventListener); }; BrowserScriptLoader.prototype.load = function (moduleManager, scriptSrc, callback, errorback) { - var script = document.createElement('script'); - script.setAttribute('async', 'async'); - script.setAttribute('type', 'text/javascript'); - this.attachListeners(script, callback, errorback); - script.setAttribute('src', scriptSrc); - // Propagate CSP nonce to dynamically created script tag. - var cspNonce = moduleManager.getConfig().getOptionsLiteral().cspNonce; - if (cspNonce) { - script.setAttribute('nonce', cspNonce); + if (/^node\|/.test(scriptSrc)) { + var opts = moduleManager.getConfig().getOptionsLiteral(); + var nodeRequire = (opts.nodeRequire || AMDLoader.global.nodeRequire); + var pieces = scriptSrc.split('|'); + var moduleExports_1 = null; + try { + moduleExports_1 = nodeRequire(pieces[1]); + } + catch (err) { + errorback(err); + return; + } + moduleManager.enqueueDefineAnonymousModule([], function () { return moduleExports_1; }); + callback(); + } + else { + var script = document.createElement('script'); + script.setAttribute('async', 'async'); + script.setAttribute('type', 'text/javascript'); + this.attachListeners(script, callback, errorback); + script.setAttribute('src', scriptSrc); + // Propagate CSP nonce to dynamically created script tag. + var cspNonce = moduleManager.getConfig().getOptionsLiteral().cspNonce; + if (cspNonce) { + script.setAttribute('nonce', cspNonce); + } + document.getElementsByTagName('head')[0].appendChild(script); } - document.getElementsByTagName('head')[0].appendChild(script); }; return BrowserScriptLoader; }()); @@ -742,15 +777,15 @@ var AMDLoader; var recorder = moduleManager.getRecorder(); if (/^node\|/.test(scriptSrc)) { var pieces = scriptSrc.split('|'); - var moduleExports_1 = null; + var moduleExports_2 = null; try { - moduleExports_1 = nodeRequire(pieces[1]); + moduleExports_2 = nodeRequire(pieces[1]); } catch (err) { errorback(err); return; } - moduleManager.enqueueDefineAnonymousModule([], function () { return moduleExports_1; }); + moduleManager.enqueueDefineAnonymousModule([], function () { return moduleExports_2; }); callback(); } else { @@ -853,6 +888,12 @@ var AMDLoader; } var cachedData = script.createCachedData(); if (cachedData.length === 0 || cachedData.length === lastSize || iteration >= 5) { + // done + return; + } + if (cachedData.length < lastSize) { + // less data than before: skip, try again next round + createLoop(); return; } lastSize = cachedData.length; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index b4bd9b76fcf..0593f65eb4a 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -1117,9 +1117,12 @@ declare namespace monaco.editor { wordBasedSuggestions?: boolean; /** * Controls whether the semanticHighlighting is shown for the languages that support it. - * Defaults to true. + * true: semanticHighlighting is enabled for all themes + * false: semanticHighlighting is disabled for all themes + * 'configuredByTheme': semanticHighlighting is controlled by the current color theme's semanticHighlighting setting. + * Defaults to 'byTheme'. */ - 'semanticHighlighting.enabled'?: boolean; + 'semanticHighlighting.enabled'?: true | false | 'configuredByTheme'; /** * Keep peek editors open even when double clicking their content or when hitting `Escape`. * Defaults to false. @@ -2328,7 +2331,7 @@ declare namespace monaco.editor { * @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. */ @@ -2628,6 +2631,11 @@ declare namespace monaco.editor { * 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. @@ -3124,13 +3132,10 @@ declare namespace monaco.editor { * Defaults to false. */ definitionLinkOpensInPeek?: boolean; - } - - export interface IEditorConstructionOptions extends IEditorOptions { /** - * The initial editor dimension (to avoid measuring the container). + * Controls strikethrough deprecated variables. */ - dimension?: IDimension; + showDeprecated?: boolean; } /** @@ -3167,6 +3172,16 @@ declare namespace monaco.editor { * 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; } /** @@ -3198,6 +3213,11 @@ declare namespace monaco.editor { * Defaults to true. */ insertSpace?: boolean; + /** + * Ignore empty lines when inserting line comments. + * Defaults to true. + */ + ignoreEmptyLines?: boolean; } export type EditorCommentsOptions = Readonly>; @@ -3537,9 +3557,9 @@ declare namespace monaco.editor { * Configuration options for quick suggestions */ export interface IQuickSuggestionsOptions { - other: boolean; - comments: boolean; - strings: boolean; + other?: boolean; + comments?: boolean; + strings?: boolean; } export type ValidQuickSuggestionsOptions = boolean | Readonly>; @@ -3930,20 +3950,22 @@ declare namespace monaco.editor { suggestOnTriggerCharacters = 99, suggestSelection = 100, tabCompletion = 101, - useTabStops = 102, - wordSeparators = 103, - wordWrap = 104, - wordWrapBreakAfterCharacters = 105, - wordWrapBreakBeforeCharacters = 106, - wordWrapColumn = 107, - wordWrapMinified = 108, - wrappingIndent = 109, - wrappingStrategy = 110, - editorClassName = 111, - pixelRatio = 112, - tabFocusMode = 113, - layoutInfo = 114, - wrappingInfo = 115 + 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 } export const EditorOptions: { acceptSuggestionOnCommitCharacter: IEditorOption; @@ -4039,6 +4061,7 @@ declare namespace monaco.editor { selectOnLineNumbers: IEditorOption; showFoldingControls: IEditorOption; showUnused: IEditorOption; + showDeprecated: IEditorOption; snippetSuggestions: IEditorOption; smoothScrolling: IEditorOption; stopRenderingLineAfter: IEditorOption; @@ -4048,6 +4071,7 @@ declare namespace monaco.editor { suggestOnTriggerCharacters: IEditorOption; suggestSelection: IEditorOption; tabCompletion: IEditorOption; + unusualLineTerminators: IEditorOption; useTabStops: IEditorOption; wordSeparators: IEditorOption; wordWrap: IEditorOption; @@ -4374,6 +4398,18 @@ declare namespace monaco.editor { readonly mode: string | null; } + export interface IEditorConstructionOptions extends IEditorOptions { + /** + * The initial editor dimension (to avoid measuring the container). + */ + dimension?: IDimension; + /** + * Place overflow widgets inside an external DOM node. + * Defaults to an internal DOM node. + */ + overflowWidgetsDomNode?: HTMLElement; + } + /** * A rich code editor. */ @@ -4591,15 +4627,15 @@ declare namespace monaco.editor { /** * Change the scrollLeft of the editor's viewport. */ - setScrollLeft(newScrollLeft: number): void; + setScrollLeft(newScrollLeft: number, scrollType?: ScrollType): void; /** * Change the scrollTop of the editor's viewport. */ - setScrollTop(newScrollTop: number): void; + setScrollTop(newScrollTop: number, scrollType?: ScrollType): void; /** * Change the scroll position of the editor's viewport. */ - setScrollPosition(position: INewScrollPosition): void; + setScrollPosition(position: INewScrollPosition, scrollType?: ScrollType): void; /** * Get an action that is a contribution to this editor. * @id Unique identifier of the contribution. @@ -4612,7 +4648,7 @@ declare namespace monaco.editor { * @param source The source of the call. * @param command The command to execute */ - executeCommand(source: string, command: ICommand): void; + executeCommand(source: string | null | undefined, command: ICommand): void; /** * Push an "undo stop" in the undo-redo stack. */ @@ -4624,13 +4660,13 @@ declare namespace monaco.editor { * @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: (ICommand | null)[]): void; + executeCommands(source: string | null | undefined, commands: (ICommand | null)[]): void; /** * Get all the decorations on a line (filtering out decorations from other editors). */ @@ -6279,6 +6315,10 @@ declare namespace monaco.languages { * is the language case insensitive? */ ignoreCase?: boolean; + /** + * is the language unicode-aware? (i.e., /\u{1D306}/) + */ + unicode?: boolean; /** * if no match in the tokenizer assign this token class (default 'source') */ diff --git a/src/vs/platform/accessibility/common/accessibility.ts b/src/vs/platform/accessibility/common/accessibility.ts index 3a025be247a..fa420edc7d3 100644 --- a/src/vs/platform/accessibility/common/accessibility.ts +++ b/src/vs/platform/accessibility/common/accessibility.ts @@ -10,7 +10,7 @@ import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; export const IAccessibilityService = createDecorator('accessibilityService'); export interface IAccessibilityService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly onDidChangeScreenReaderOptimized: Event; @@ -32,3 +32,8 @@ export const enum AccessibilitySupport { } export const CONTEXT_ACCESSIBILITY_MODE_ENABLED = new RawContextKey('accessibilityModeEnabled', false); + +export interface IAccessibilityInformation { + label: string; + role?: string; +} diff --git a/src/vs/platform/accessibility/common/accessibilityService.ts b/src/vs/platform/accessibility/common/accessibilityService.ts index 25a277f53ea..029a5914d31 100644 --- a/src/vs/platform/accessibility/common/accessibilityService.ts +++ b/src/vs/platform/accessibility/common/accessibilityService.ts @@ -10,7 +10,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class AccessibilityService extends Disposable implements IAccessibilityService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _accessibilityModeEnabledContext: IContextKey; protected _accessibilitySupport = AccessibilitySupport.Unknown; diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts index 4e3e1dec7a2..d0867eb9d8d 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -5,8 +5,7 @@ import { addClasses, createCSSRule, removeClasses, asCSSUrl } from 'vs/base/browser/dom'; import { domEvent } from 'vs/base/browser/event'; -import { ActionViewItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IAction } from 'vs/base/common/actions'; +import { IAction, Separator } from 'vs/base/common/actions'; import { Emitter } from 'vs/base/common/event'; import { IdGenerator } from 'vs/base/common/idGenerator'; import { IDisposable, toDisposable, MutableDisposable, DisposableStore } from 'vs/base/common/lifecycle'; @@ -17,6 +16,8 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; // The alternative key on all platforms is alt. On windows we also support shift as an alternative key #44136 class AlternativeKeyEmitter extends Emitter { @@ -124,19 +125,11 @@ function fillInActions(groups: ReadonlyArray<[string, ReadonlyArray(); - static readonly ICON_PATH_TO_CSS_RULES: Map = new Map(); +export class MenuEntryActionViewItem extends ActionViewItem { private _wantsAltCommand: boolean = false; private readonly _itemClassDispose = this._register(new MutableDisposable()); @@ -164,7 +157,7 @@ export class MenuEntryActionViewItem extends ActionViewItem { this._altKey.suppressAltKeyUp(); } - this.actionRunner.run(this._commandAction) + this.actionRunner.run(this._commandAction, this._context) .then(undefined, err => this._notificationService.error(err)); } @@ -235,7 +228,7 @@ export class MenuEntryActionViewItem extends ActionViewItem { } } - _updateItemClass(item: ICommandAction): void { + private _updateItemClass(item: ICommandAction): void { this._itemClassDispose.value = undefined; const icon = this._commandAction.checked && (item.toggled as { icon?: Icon })?.icon ? (item.toggled as { icon: Icon }).icon : item.icon; @@ -256,17 +249,17 @@ export class MenuEntryActionViewItem extends ActionViewItem { // icon path let iconClass: string; - if (icon?.dark?.scheme) { + if (icon.dark?.scheme) { const iconPathMapKey = icon.dark.toString(); - if (MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.has(iconPathMapKey)) { - iconClass = MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.get(iconPathMapKey)!; + if (ICON_PATH_TO_CSS_RULES.has(iconPathMapKey)) { + iconClass = ICON_PATH_TO_CSS_RULES.get(iconPathMapKey)!; } else { iconClass = ids.nextId(); createCSSRule(`.icon.${iconClass}`, `background-image: ${asCSSUrl(icon.light || icon.dark)}`); createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: ${asCSSUrl(icon.dark)}`); - MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass); + ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass); } if (this.label) { @@ -283,16 +276,33 @@ export class MenuEntryActionViewItem extends ActionViewItem { } } -// Need to subclass MenuEntryActionViewItem in order to respect -// the action context coming from any action bar, without breaking -// existing users -export class ContextAwareMenuEntryActionViewItem extends MenuEntryActionViewItem { +export class SubmenuEntryActionViewItem extends DropdownMenuActionViewItem { - onClick(event: MouseEvent): void { - event.preventDefault(); - event.stopPropagation(); + constructor( + action: SubmenuItemAction, + @INotificationService _notificationService: INotificationService, + @IContextMenuService _contextMenuService: IContextMenuService + ) { + const classNames: string[] = []; - this.actionRunner.run(this._commandAction, this._context) - .then(undefined, err => this._notificationService.error(err)); + if (action.item.icon) { + if (ThemeIcon.isThemeIcon(action.item.icon)) { + classNames.push(ThemeIcon.asClassName(action.item.icon)!); + } else if (action.item.icon.dark?.scheme) { + const iconPathMapKey = action.item.icon.dark.toString(); + + if (ICON_PATH_TO_CSS_RULES.has(iconPathMapKey)) { + classNames.push('icon', ICON_PATH_TO_CSS_RULES.get(iconPathMapKey)!); + } else { + const className = ids.nextId(); + classNames.push('icon', className); + createCSSRule(`.icon.${className}`, `background-image: ${asCSSUrl(action.item.icon.light || action.item.icon.dark)}`); + createCSSRule(`.vs-dark .icon.${className}, .hc-black .icon.${className}`, `background-image: ${asCSSUrl(action.item.icon.dark)}`); + ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, className); + } + } + } + + super(action, Array.isArray(action.actions) ? action.actions : action.actions(), _contextMenuService, { classNames }); } } diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index c20e8327058..36781a62572 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Action } from 'vs/base/common/actions'; +import { Action, IAction, Separator, SubmenuAction } from 'vs/base/common/actions'; import { SyncDescriptor0, createSyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IConstructorSignature2, createDecorator, BrandedService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindings, KeybindingsRegistry, IKeybindingRule } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -47,6 +47,7 @@ export interface IMenuItem { export interface ISubmenuItem { title: string | ILocalizedString; submenu: MenuId; + icon?: Icon; when?: ContextKeyExpression; group?: 'navigation' | string; order?: number; @@ -111,6 +112,7 @@ export class MenuId { static readonly TunnelInline = new MenuId('TunnelInline'); static readonly TunnelTitle = new MenuId('TunnelTitle'); static readonly ViewItemContext = new MenuId('ViewItemContext'); + static readonly ViewContainerTitleContext = new MenuId('ViewContainerTitleContext'); static readonly ViewTitle = new MenuId('ViewTitle'); static readonly ViewTitleContext = new MenuId('ViewTitleContext'); static readonly CommentThreadTitle = new MenuId('CommentThreadTitle'); @@ -118,6 +120,7 @@ export class MenuId { static readonly CommentTitle = new MenuId('CommentTitle'); static readonly CommentActions = new MenuId('CommentActions'); static readonly NotebookCellTitle = new MenuId('NotebookCellTitle'); + static readonly NotebookCellBetween = new MenuId('NotebookCellBetween'); static readonly BulkEditTitle = new MenuId('BulkEditTitle'); static readonly BulkEditContext = new MenuId('BulkEditContext'); static readonly TimelineItemContext = new MenuId('TimelineItemContext'); @@ -148,7 +151,7 @@ export const IMenuService = createDecorator('menuService'); export interface IMenuService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; createMenu(id: MenuId, scopedKeybindingService: IContextKeyService): IMenu; } @@ -293,12 +296,35 @@ export class ExecuteCommandAction extends Action { } } -export class SubmenuItemAction extends Action { +export class SubmenuItemAction extends SubmenuAction { - readonly item: ISubmenuItem; - constructor(item: ISubmenuItem) { - typeof item.title === 'string' ? super('', item.title, 'submenu') : super('', item.title.value, 'submenu'); - this.item = item; + constructor( + readonly item: ISubmenuItem, + menuService: IMenuService, + contextKeyService: IContextKeyService, + options?: IMenuActionOptions + ) { + super(`submenuitem.${item.submenu.id}`, typeof item.title === 'string' ? item.title : item.title.value, () => { + const result: IAction[] = []; + const menu = menuService.createMenu(item.submenu, contextKeyService); + const groups = menu.getActions(options); + menu.dispose(); + + for (let group of groups) { + const [, actions] = group; + + if (actions.length > 0) { + result.push(...actions); + result.push(new Separator()); + } + } + + if (result.length) { + result.pop(); // remove last separator + } + + return result; + }, 'submenu'); } } @@ -477,7 +503,8 @@ export function registerAction2(ctor: { new(): Action2 }): IDisposable { disposables.add(MenuRegistry.appendMenuItem(menu.id, { command, ...menu })); } if (f1) { - disposables.add(MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command })); + disposables.add(MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command, when: command.precondition })); + disposables.add(MenuRegistry.addCommand(command)); } // keybinding diff --git a/src/vs/platform/actions/common/menuService.ts b/src/vs/platform/actions/common/menuService.ts index 90a71cae4fb..62c1dc8350e 100644 --- a/src/vs/platform/actions/common/menuService.ts +++ b/src/vs/platform/actions/common/menuService.ts @@ -11,7 +11,7 @@ import { IContextKeyService, IContextKeyChangeEvent, ContextKeyExpression } from export class MenuService implements IMenuService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @ICommandService private readonly _commandService: ICommandService @@ -20,7 +20,7 @@ export class MenuService implements IMenuService { } createMenu(id: MenuId, contextKeyService: IContextKeyService): IMenu { - return new Menu(id, this._commandService, contextKeyService); + return new Menu(id, this._commandService, contextKeyService, this); } } @@ -38,7 +38,8 @@ class Menu implements IMenu { constructor( private readonly _id: MenuId, @ICommandService private readonly _commandService: ICommandService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService + @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @IMenuService private readonly _menuService: IMenuService ) { this._build(); @@ -114,7 +115,7 @@ class Menu implements IMenu { if (this._contextKeyService.contextMatchesRules(item.when)) { const action = isIMenuItem(item) ? new MenuItemAction(item.command, item.alt, options, this._contextKeyService, this._commandService) - : new SubmenuItemAction(item); + : new SubmenuItemAction(item, this._menuService, this._contextKeyService, options); activeActions.push(action); } diff --git a/src/vs/platform/authentication/common/authentication.ts b/src/vs/platform/authentication/common/authentication.ts deleted file mode 100644 index d3142ae022a..00000000000 --- a/src/vs/platform/authentication/common/authentication.ts +++ /dev/null @@ -1,60 +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 { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; - -export const IAuthenticationTokenService = createDecorator('IAuthenticationTokenService'); - -export interface IUserDataSyncAuthToken { - readonly authenticationProviderId: string; - readonly token: string; -} - -export interface IAuthenticationTokenService { - _serviceBrand: undefined; - - readonly onDidChangeToken: Event; - readonly onTokenFailed: Event; - - getToken(): Promise; - setToken(userDataSyncAuthToken: IUserDataSyncAuthToken | undefined): Promise; - sendTokenFailed(): void; -} - -export class AuthenticationTokenService extends Disposable implements IAuthenticationTokenService { - - _serviceBrand: any; - - private _onDidChangeToken = this._register(new Emitter()); - readonly onDidChangeToken = this._onDidChangeToken.event; - - private _onTokenFailed: Emitter = this._register(new Emitter()); - readonly onTokenFailed: Event = this._onTokenFailed.event; - - private _token: IUserDataSyncAuthToken | undefined; - - constructor() { - super(); - } - - async getToken(): Promise { - return this._token; - } - - async setToken(token: IUserDataSyncAuthToken | undefined): Promise { - if (token && this._token ? token.token !== this._token.token || token.authenticationProviderId !== this._token.authenticationProviderId : token !== this._token) { - this._token = token; - this._onDidChangeToken.fire(token); - } - } - - sendTokenFailed(): void { - this.setToken(undefined); - this._onTokenFailed.fire(); - } -} - diff --git a/src/vs/platform/authentication/common/authenticationIpc.ts b/src/vs/platform/authentication/common/authenticationIpc.ts deleted file mode 100644 index 9836ee14682..00000000000 --- a/src/vs/platform/authentication/common/authenticationIpc.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. - *--------------------------------------------------------------------------------------------*/ - -import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { Event } from 'vs/base/common/event'; -import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication'; - - -export class AuthenticationTokenServiceChannel implements IServerChannel { - constructor(private readonly service: IAuthenticationTokenService) { } - - listen(_: unknown, event: string): Event { - switch (event) { - case 'onDidChangeToken': return this.service.onDidChangeToken; - case 'onTokenFailed': return this.service.onTokenFailed; - } - throw new Error(`Event not found: ${event}`); - } - - call(context: any, command: string, args?: any): Promise { - switch (command) { - case 'setToken': return this.service.setToken(args); - case 'getToken': return this.service.getToken(); - } - throw new Error('Invalid call'); - } -} diff --git a/src/vs/platform/backup/electron-main/backup.ts b/src/vs/platform/backup/electron-main/backup.ts index 3c314a3b61c..7c002809de9 100644 --- a/src/vs/platform/backup/electron-main/backup.ts +++ b/src/vs/platform/backup/electron-main/backup.ts @@ -22,7 +22,7 @@ export function isWorkspaceBackupInfo(obj: unknown): obj is IWorkspaceBackupInfo } export interface IBackupMainService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; isHotExitEnabled(): boolean; diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index a2458c46741..8b797cf0fb6 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -17,13 +17,13 @@ import { IFilesConfiguration, HotExitConfiguration } from 'vs/platform/files/com import { ILogService } from 'vs/platform/log/common/log'; import { IWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { URI } from 'vs/base/common/uri'; -import { isEqual as areResourcesEquals, getComparisonKey, hasToIgnoreCase } from 'vs/base/common/resources'; import { isEqual } from 'vs/base/common/extpath'; import { Schemas } from 'vs/base/common/network'; +import { extUriBiasedIgnorePathCase } from 'vs/base/common/resources'; export class BackupMainService implements IBackupMainService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; protected backupHome: string; protected workspacesJsonPath: string; @@ -32,6 +32,12 @@ export class BackupMainService implements IBackupMainService { private folders: URI[] = []; private emptyWindows: IEmptyWindowBackupInfo[] = []; + // Comparers for paths and resources that will + // - ignore path casing on Windows/macOS + // - respect path casing on Linux + private readonly backupUriComparer = extUriBiasedIgnorePathCase; + private readonly backupPathComparer = { isEqual: (pathA: string, pathB: string) => isEqual(pathA, pathB, !platform.isLinux) }; + constructor( @IEnvironmentService environmentService: INativeEnvironmentService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -196,7 +202,7 @@ export class BackupMainService implements IBackupMainService { } registerFolderBackupSync(folderUri: URI): string { - if (!this.folders.some(folder => areResourcesEquals(folderUri, folder))) { + if (!this.folders.some(folder => this.backupUriComparer.isEqual(folderUri, folder))) { this.folders.push(folderUri); this.saveSync(); } @@ -205,7 +211,7 @@ export class BackupMainService implements IBackupMainService { } unregisterFolderBackupSync(folderUri: URI): void { - const index = this.folders.findIndex(folder => areResourcesEquals(folderUri, folder)); + const index = this.folders.findIndex(folder => this.backupUriComparer.isEqual(folderUri, folder)); if (index !== -1) { this.folders.splice(index, 1); this.saveSync(); @@ -216,7 +222,7 @@ export class BackupMainService implements IBackupMainService { // Generate a new folder if this is a new empty workspace const backupFolder = backupFolderCandidate || this.getRandomEmptyWindowId(); - if (!this.emptyWindows.some(emptyWindow => !!emptyWindow.backupFolder && isEqual(emptyWindow.backupFolder, backupFolder, !platform.isLinux))) { + if (!this.emptyWindows.some(emptyWindow => !!emptyWindow.backupFolder && this.backupPathComparer.isEqual(emptyWindow.backupFolder, backupFolder))) { this.emptyWindows.push({ backupFolder, remoteAuthority }); this.saveSync(); } @@ -225,7 +231,7 @@ export class BackupMainService implements IBackupMainService { } unregisterEmptyWindowBackupSync(backupFolder: string): void { - const index = this.emptyWindows.findIndex(emptyWindow => !!emptyWindow.backupFolder && isEqual(emptyWindow.backupFolder, backupFolder, !platform.isLinux)); + const index = this.emptyWindows.findIndex(emptyWindow => !!emptyWindow.backupFolder && this.backupPathComparer.isEqual(emptyWindow.backupFolder, backupFolder)); if (index !== -1) { this.emptyWindows.splice(index, 1); this.saveSync(); @@ -282,7 +288,7 @@ export class BackupMainService implements IBackupMainService { const result: URI[] = []; const seenIds: Set = new Set(); for (let folderURI of folderWorkspaces) { - const key = getComparisonKey(folderURI); + const key = this.backupUriComparer.getComparisonKey(folderURI); if (!seenIds.has(key)) { seenIds.add(key); @@ -350,7 +356,7 @@ export class BackupMainService implements IBackupMainService { // New empty window backup let newBackupFolder = this.getRandomEmptyWindowId(); - while (this.emptyWindows.some(emptyWindow => !!emptyWindow.backupFolder && isEqual(emptyWindow.backupFolder, newBackupFolder, platform.isLinux))) { + while (this.emptyWindows.some(emptyWindow => !!emptyWindow.backupFolder && this.backupPathComparer.isEqual(emptyWindow.backupFolder, newBackupFolder))) { newBackupFolder = this.getRandomEmptyWindowId(); } @@ -371,7 +377,7 @@ export class BackupMainService implements IBackupMainService { // New empty window backup let newBackupFolder = this.getRandomEmptyWindowId(); - while (this.emptyWindows.some(emptyWindow => !!emptyWindow.backupFolder && isEqual(emptyWindow.backupFolder, newBackupFolder, platform.isLinux))) { + while (this.emptyWindows.some(emptyWindow => !!emptyWindow.backupFolder && this.backupPathComparer.isEqual(emptyWindow.backupFolder, newBackupFolder))) { newBackupFolder = this.getRandomEmptyWindowId(); } @@ -486,7 +492,7 @@ export class BackupMainService implements IBackupMainService { // for backward compatibility, use the fspath as key key = platform.isLinux ? folderUri.fsPath : folderUri.fsPath.toLowerCase(); } else { - key = hasToIgnoreCase(folderUri) ? folderUri.toString().toLowerCase() : folderUri.toString(); + key = folderUri.toString().toLowerCase(); } return crypto.createHash('md5').update(key).digest('hex'); diff --git a/src/vs/platform/browser/checkbox.ts b/src/vs/platform/browser/checkbox.ts new file mode 100644 index 00000000000..c479126eeba --- /dev/null +++ b/src/vs/platform/browser/checkbox.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +import { CheckboxActionViewItem } from 'vs/base/browser/ui/checkbox/checkbox'; +import { IAction } from 'vs/base/common/actions'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { attachCheckboxStyler } from 'vs/platform/theme/common/styler'; +import { IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; + +export class ThemableCheckboxActionViewItem extends CheckboxActionViewItem { + + constructor(context: any, action: IAction, options: IBaseActionViewItemOptions | undefined, private readonly themeService: IThemeService) { + super(context, action, options); + } + + render(container: HTMLElement): void { + super.render(container); + if (this.checkbox) { + this.disposables.add(attachCheckboxStyler(this.checkbox, this.themeService)); + } + } + +} + diff --git a/src/vs/platform/clipboard/browser/clipboardService.ts b/src/vs/platform/clipboard/browser/clipboardService.ts index 8cbddd37004..e67d5f59bd1 100644 --- a/src/vs/platform/clipboard/browser/clipboardService.ts +++ b/src/vs/platform/clipboard/browser/clipboardService.ts @@ -9,13 +9,17 @@ import { $ } from 'vs/base/browser/dom'; export class BrowserClipboardService implements IClipboardService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; - private _internalResourcesClipboard: URI[] | undefined; + private readonly mapTextToType = new Map(); // unsupported in web (only in-memory) async writeText(text: string, type?: string): Promise { + + // With type: only in-memory is supported if (type) { - return; // TODO@sbatten support for writing a specific type into clipboard is unsupported + this.mapTextToType.set(type, text); + + return; } // Guard access to navigator.clipboard with try/catch @@ -52,8 +56,10 @@ export class BrowserClipboardService implements IClipboardService { } async readText(type?: string): Promise { + + // With type: only in-memory is supported if (type) { - return ''; // TODO@sbatten support for reading a specific type from clipboard is unsupported + return this.mapTextToType.get(type) || ''; } // Guard access to navigator.clipboard with try/catch @@ -68,26 +74,27 @@ export class BrowserClipboardService implements IClipboardService { } } - readTextSync(): string | undefined { - return undefined; + private findText = ''; // unsupported in web (only in-memory) + + async readFindText(): Promise { + return this.findText; } - readFindText(): string { - // @ts-expect-error - return undefined; + async writeFindText(text: string): Promise { + this.findText = text; } - writeFindText(text: string): void { } + private resources: URI[] = []; // unsupported in web (only in-memory) - writeResources(resources: URI[]): void { - this._internalResourcesClipboard = resources; + async writeResources(resources: URI[]): Promise { + this.resources = resources; } - readResources(): URI[] { - return this._internalResourcesClipboard || []; + async readResources(): Promise { + return this.resources; } - hasResources(): boolean { - return this._internalResourcesClipboard !== undefined && this._internalResourcesClipboard.length > 0; + async hasResources(): Promise { + return this.resources.length > 0; } } diff --git a/src/vs/platform/clipboard/common/clipboardService.ts b/src/vs/platform/clipboard/common/clipboardService.ts index 11bf563393a..22cc5b30004 100644 --- a/src/vs/platform/clipboard/common/clipboardService.ts +++ b/src/vs/platform/clipboard/common/clipboardService.ts @@ -10,7 +10,7 @@ export const IClipboardService = createDecorator('clipboardSe export interface IClipboardService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Writes text to the system clipboard. @@ -22,30 +22,28 @@ export interface IClipboardService { */ readText(type?: string): Promise; - readTextSync(): string | undefined; - /** * Reads text from the system find pasteboard. */ - readFindText(): string; + readFindText(): Promise; /** * Writes text to the system find pasteboard. */ - writeFindText(text: string): void; + writeFindText(text: string): Promise; /** * Writes resources to the system clipboard. */ - writeResources(resources: URI[]): void; + writeResources(resources: URI[]): Promise; /** * Reads resources from the system clipboard. */ - readResources(): URI[]; + readResources(): Promise; /** * Find out if resources are copied to the clipboard. */ - hasResources(): boolean; + hasResources(): Promise; } diff --git a/src/vs/platform/commands/common/commands.ts b/src/vs/platform/commands/common/commands.ts index ae8451eb84f..5a4032727c6 100644 --- a/src/vs/platform/commands/common/commands.ts +++ b/src/vs/platform/commands/common/commands.ts @@ -19,7 +19,7 @@ export interface ICommandEvent { } export interface ICommandService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; onWillExecuteCommand: Event; onDidExecuteCommand: Event; executeCommand(commandId: string, ...args: any[]): Promise; diff --git a/src/vs/platform/configuration/common/configuration.ts b/src/vs/platform/configuration/common/configuration.ts index 16bf8c384f9..e09910f5458 100644 --- a/src/vs/platform/configuration/common/configuration.ts +++ b/src/vs/platform/configuration/common/configuration.ts @@ -10,7 +10,7 @@ import { Event } from 'vs/base/common/event'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IConfigurationRegistry, Extensions, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationRegistry, Extensions, OVERRIDE_PROPERTY_PATTERN, overrideIdentifierFromKey } from 'vs/platform/configuration/common/configurationRegistry'; import { IStringDictionary } from 'vs/base/common/collections'; export const IConfigurationService = createDecorator('configurationService'); @@ -88,7 +88,7 @@ export interface IConfigurationValue { } export interface IConfigurationService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; onDidChangeConfiguration: Event; @@ -323,14 +323,16 @@ export function getConfigurationValue(config: any, settingPath: string, defau export function merge(base: any, add: any, overwrite: boolean): void { Object.keys(add).forEach(key => { - if (key in base) { - if (types.isObject(base[key]) && types.isObject(add[key])) { - merge(base[key], add[key], overwrite); - } else if (overwrite) { + if (key !== '__proto__') { + if (key in base) { + if (types.isObject(base[key]) && types.isObject(add[key])) { + merge(base[key], add[key], overwrite); + } else if (overwrite) { + base[key] = add[key]; + } + } else { base[key] = add[key]; } - } else { - base[key] = add[key]; } }); } @@ -352,10 +354,6 @@ export function getDefaultValues(): any { return valueTreeRoot; } -export function overrideIdentifierFromKey(key: string): string { - return key.substring(1, key.length - 1); -} - export function keyFromOverrideIdentifier(overrideIdentifier: string): string { return `[${overrideIdentifier}]`; } diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts index 5fe331da136..5af10fc3155 100644 --- a/src/vs/platform/configuration/common/configurationModels.ts +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as json from 'vs/base/common/json'; -import { ResourceMap, values, getOrSet } from 'vs/base/common/map'; +import { ResourceMap, getOrSet } from 'vs/base/common/map'; import * as arrays from 'vs/base/common/arrays'; import * as types from 'vs/base/common/types'; import * as objects from 'vs/base/common/objects'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { OVERRIDE_PROPERTY_PATTERN, ConfigurationScope, IConfigurationRegistry, Extensions, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; -import { IOverrides, overrideIdentifierFromKey, addToValueTree, toValuesTree, IConfigurationModel, getConfigurationValue, IConfigurationOverrides, IConfigurationData, getDefaultValues, getConfigurationKeys, removeFromValueTree, toOverrides, IConfigurationValue, ConfigurationTarget, compare, IConfigurationChangeEvent, IConfigurationChange } from 'vs/platform/configuration/common/configuration'; +import { OVERRIDE_PROPERTY_PATTERN, ConfigurationScope, IConfigurationRegistry, Extensions, IConfigurationPropertySchema, overrideIdentifierFromKey } from 'vs/platform/configuration/common/configurationRegistry'; +import { IOverrides, addToValueTree, toValuesTree, IConfigurationModel, getConfigurationValue, IConfigurationOverrides, IConfigurationData, getDefaultValues, getConfigurationKeys, removeFromValueTree, toOverrides, IConfigurationValue, ConfigurationTarget, compare, IConfigurationChangeEvent, IConfigurationChange } from 'vs/platform/configuration/common/configuration'; import { Workspace } from 'vs/platform/workspace/common/workspace'; import { Registry } from 'vs/platform/registry/common/platform'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -692,7 +692,7 @@ export class Configuration { this.userConfiguration.freeze().keys.forEach(key => keys.add(key)); this._workspaceConfiguration.freeze().keys.forEach(key => keys.add(key)); this._folderConfigurations.forEach(folderConfiguraiton => folderConfiguraiton.freeze().keys.forEach(key => keys.add(key))); - return values(keys); + return [...keys.values()]; } protected getAllKeysForOverrideIdentifier(overrideIdentifier: string): string[] { @@ -701,7 +701,7 @@ export class Configuration { this.userConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key)); this._workspaceConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key)); this._folderConfigurations.forEach(folderConfiguraiton => folderConfiguraiton.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key))); - return values(keys); + return [...keys.values()]; } static parse(data: IConfigurationData): Configuration { @@ -738,8 +738,8 @@ export function mergeChanges(...changes: IConfigurationChange[]): IConfiguration }); } const overrides: [string, string[]][] = []; - overridesMap.forEach((keys, identifier) => overrides.push([identifier, values(keys)])); - return { keys: values(keysSet), overrides }; + overridesMap.forEach((keys, identifier) => overrides.push([identifier, [...keys.values()]])); + return { keys: [...keysSet.values()], overrides }; } export class ConfigurationChangeEvent implements IConfigurationChangeEvent { @@ -753,7 +753,7 @@ export class ConfigurationChangeEvent implements IConfigurationChangeEvent { const keysSet = new Set(); change.keys.forEach(key => keysSet.add(key)); change.overrides.forEach(([, keys]) => keys.forEach(key => keysSet.add(key))); - this.affectedKeys = values(keysSet); + this.affectedKeys = [...keysSet.values()]; const configurationModel = new ConfigurationModel(); this.affectedKeys.forEach(key => configurationModel.setValue(key, {})); diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index 1c5d957d8f7..66096d08813 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -9,8 +9,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { Registry } from 'vs/platform/registry/common/platform'; import * as types from 'vs/base/common/types'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { values } from 'vs/base/common/map'; +import { IStringDictionary } from 'vs/base/common/collections'; export const Extensions = { Configuration: 'base.contributions.configuration' @@ -36,12 +35,12 @@ export interface IConfigurationRegistry { /** * Register multiple default configurations to the registry. */ - registerDefaultConfigurations(defaultConfigurations: IDefaultConfigurationExtension[]): void; + registerDefaultConfigurations(defaultConfigurations: IStringDictionary[]): void; /** * Deregister multiple default configurations from the registry. */ - deregisterDefaultConfigurations(defaultConfigurations: IDefaultConfigurationExtension[]): void; + deregisterDefaultConfigurations(defaultConfigurations: IStringDictionary[]): void; /** * Signal that the schema of a configuration setting has changes. It is currently only supported to change enumeration values. @@ -132,12 +131,6 @@ export interface IConfigurationNode { extensionInfo?: IConfigurationExtensionInfo; } -export interface IDefaultConfigurationExtension { - id: ExtensionIdentifier; - name: string; - defaults: { [key: string]: {} }; -} - type SettingProperties = { [key: string]: any }; export const allSettings: { properties: SettingProperties, patternProperties: SettingProperties } = { properties: {}, patternProperties: {} }; @@ -153,7 +146,8 @@ const contributionRegistry = Registry.as(JSONExtensio class ConfigurationRegistry implements IConfigurationRegistry { - private readonly defaultOverridesConfigurationNode: IConfigurationNode; + private readonly defaultValues: IStringDictionary; + private readonly defaultLanguageConfigurationOverridesNode: IConfigurationNode; private readonly configurationContributors: IConfigurationNode[]; private readonly configurationProperties: { [qualifiedKey: string]: IJSONSchema }; private readonly excludedConfigurationProperties: { [qualifiedKey: string]: IJSONSchema }; @@ -167,12 +161,13 @@ class ConfigurationRegistry implements IConfigurationRegistry { readonly onDidUpdateConfiguration: Event = this._onDidUpdateConfiguration.event; constructor() { - this.defaultOverridesConfigurationNode = { + this.defaultValues = {}; + this.defaultLanguageConfigurationOverridesNode = { id: 'defaultOverrides', - title: nls.localize('defaultConfigurations.title', "Default Configuration Overrides"), + title: nls.localize('defaultLanguageConfigurationOverrides.title', "Default Language Configuration Overrides"), properties: {} }; - this.configurationContributors = [this.defaultOverridesConfigurationNode]; + this.configurationContributors = [this.defaultLanguageConfigurationOverridesNode]; this.resourceLanguageSettingsSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown editor configuration setting', allowTrailingCommas: true, allowComments: true }; this.configurationProperties = {}; this.excludedConfigurationProperties = {}; @@ -203,29 +198,8 @@ class ConfigurationRegistry implements IConfigurationRegistry { if (configuration.properties) { for (const key in configuration.properties) { properties.push(key); - delete this.configurationProperties[key]; - - // Delete from schema - delete allSettings.properties[key]; - switch (configuration.properties[key].scope) { - case ConfigurationScope.APPLICATION: - delete applicationSettings.properties[key]; - break; - case ConfigurationScope.MACHINE: - delete machineSettings.properties[key]; - break; - case ConfigurationScope.MACHINE_OVERRIDABLE: - delete machineOverridableSettings.properties[key]; - break; - case ConfigurationScope.WINDOW: - delete windowSettings.properties[key]; - break; - case ConfigurationScope.RESOURCE: - case ConfigurationScope.LANGUAGE_OVERRIDABLE: - delete resourceSettings.properties[key]; - break; - } + this.removeFromSchema(key, configuration.properties[key]); } } if (configuration.allOf) { @@ -245,41 +219,60 @@ class ConfigurationRegistry implements IConfigurationRegistry { this._onDidUpdateConfiguration.fire(properties); } - public registerDefaultConfigurations(defaultConfigurations: IDefaultConfigurationExtension[]): void { + public registerDefaultConfigurations(defaultConfigurations: IStringDictionary[]): void { const properties: string[] = []; + const overrideIdentifiers: string[] = []; for (const defaultConfiguration of defaultConfigurations) { - for (const key in defaultConfiguration.defaults) { - const defaultValue = defaultConfiguration.defaults[key]; - if (OVERRIDE_PROPERTY_PATTERN.test(key) && typeof defaultValue === 'object') { - const propertySchema: IConfigurationPropertySchema = { + for (const key in defaultConfiguration) { + properties.push(key); + this.defaultValues[key] = defaultConfiguration[key]; + + if (OVERRIDE_PROPERTY_PATTERN.test(key)) { + const property: IConfigurationPropertySchema = { type: 'object', - default: defaultValue, - description: nls.localize('overrideSettings.description', "Configure editor settings to be overridden for {0} language.", key), + default: this.defaultValues[key], + description: nls.localize('defaultLanguageConfiguration.description', "Configure settings to be overridden for {0} language.", key), $ref: resourceLanguageSettingsSchemaId }; - allSettings.properties[key] = propertySchema; - this.defaultOverridesConfigurationNode.properties![key] = propertySchema; - this.configurationProperties[key] = propertySchema; - properties.push(key); + overrideIdentifiers.push(overrideIdentifierFromKey(key)); + this.configurationProperties[key] = property; + this.defaultLanguageConfigurationOverridesNode.properties![key] = property; + } else { + const property = this.configurationProperties[key]; + if (property) { + this.updatePropertyDefaultValue(key, property); + this.updateSchema(key, property); + } } } } + this.registerOverrideIdentifiers(overrideIdentifiers); this._onDidSchemaChange.fire(); this._onDidUpdateConfiguration.fire(properties); } - public deregisterDefaultConfigurations(defaultConfigurations: IDefaultConfigurationExtension[]): void { + public deregisterDefaultConfigurations(defaultConfigurations: IStringDictionary[]): void { const properties: string[] = []; for (const defaultConfiguration of defaultConfigurations) { - for (const key in defaultConfiguration.defaults) { + for (const key in defaultConfiguration) { properties.push(key); - delete allSettings.properties[key]; - delete this.defaultOverridesConfigurationNode.properties![key]; - delete this.configurationProperties[key]; + delete this.defaultValues[key]; + if (OVERRIDE_PROPERTY_PATTERN.test(key)) { + delete this.configurationProperties[key]; + delete this.defaultLanguageConfigurationOverridesNode.properties![key]; + } else { + const property = this.configurationProperties[key]; + if (property) { + this.updatePropertyDefaultValue(key, property); + this.updateSchema(key, property); + } + } } } + + this.updateOverridePropertyPatternKey(); this._onDidSchemaChange.fire(); this._onDidUpdateConfiguration.fire(properties); } @@ -292,7 +285,6 @@ class ConfigurationRegistry implements IConfigurationRegistry { for (const overrideIdentifier of overrideIdentifiers) { this.overrideIdentifiers.add(overrideIdentifier); } - this.updateOverridePropertyPatternKey(); } @@ -306,12 +298,13 @@ class ConfigurationRegistry implements IConfigurationRegistry { delete properties[key]; continue; } - // fill in default values - let property = properties[key]; - let defaultValue = property.default; - if (types.isUndefined(defaultValue)) { - property.default = getDefaultValue(property.type); - } + + const property = properties[key]; + + // update default value + this.updatePropertyDefaultValue(key, property); + + // update scope if (OVERRIDE_PROPERTY_PATTERN.test(key)) { property.scope = undefined; // No scope for overridable properties `[${identifier}]` } else { @@ -362,28 +355,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { let properties = configuration.properties; if (properties) { for (const key in properties) { - allSettings.properties[key] = properties[key]; - switch (properties[key].scope) { - case ConfigurationScope.APPLICATION: - applicationSettings.properties[key] = properties[key]; - break; - case ConfigurationScope.MACHINE: - machineSettings.properties[key] = properties[key]; - break; - case ConfigurationScope.MACHINE_OVERRIDABLE: - machineOverridableSettings.properties[key] = properties[key]; - break; - case ConfigurationScope.WINDOW: - windowSettings.properties[key] = properties[key]; - break; - case ConfigurationScope.RESOURCE: - resourceSettings.properties[key] = properties[key]; - break; - case ConfigurationScope.LANGUAGE_OVERRIDABLE: - resourceSettings.properties[key] = properties[key]; - this.resourceLanguageSettingsSchema.properties![key] = properties[key]; - break; - } + this.updateSchema(key, properties[key]); } } let subNodes = configuration.allOf; @@ -394,16 +366,63 @@ class ConfigurationRegistry implements IConfigurationRegistry { register(configuration); } + private updateSchema(key: string, property: IConfigurationPropertySchema): void { + allSettings.properties[key] = property; + switch (property.scope) { + case ConfigurationScope.APPLICATION: + applicationSettings.properties[key] = property; + break; + case ConfigurationScope.MACHINE: + machineSettings.properties[key] = property; + break; + case ConfigurationScope.MACHINE_OVERRIDABLE: + machineOverridableSettings.properties[key] = property; + break; + case ConfigurationScope.WINDOW: + windowSettings.properties[key] = property; + break; + case ConfigurationScope.RESOURCE: + resourceSettings.properties[key] = property; + break; + case ConfigurationScope.LANGUAGE_OVERRIDABLE: + resourceSettings.properties[key] = property; + this.resourceLanguageSettingsSchema.properties![key] = property; + break; + } + } + + private removeFromSchema(key: string, property: IConfigurationPropertySchema): void { + delete allSettings.properties[key]; + switch (property.scope) { + case ConfigurationScope.APPLICATION: + delete applicationSettings.properties[key]; + break; + case ConfigurationScope.MACHINE: + delete machineSettings.properties[key]; + break; + case ConfigurationScope.MACHINE_OVERRIDABLE: + delete machineOverridableSettings.properties[key]; + break; + case ConfigurationScope.WINDOW: + delete windowSettings.properties[key]; + break; + case ConfigurationScope.RESOURCE: + case ConfigurationScope.LANGUAGE_OVERRIDABLE: + delete resourceSettings.properties[key]; + break; + } + } + private updateOverridePropertyPatternKey(): void { - for (const overrideIdentifier of values(this.overrideIdentifiers)) { + for (const overrideIdentifier of this.overrideIdentifiers.values()) { const overrideIdentifierProperty = `[${overrideIdentifier}]`; const resourceLanguagePropertiesSchema: IJSONSchema = { type: 'object', description: nls.localize('overrideSettings.defaultDescription', "Configure editor settings to be overridden for a language."), errorMessage: nls.localize('overrideSettings.errorMessage', "This setting does not support per-language configuration."), $ref: resourceLanguageSettingsSchemaId, - default: this.defaultOverridesConfigurationNode.properties![overrideIdentifierProperty]?.default }; + this.updatePropertyDefaultValue(overrideIdentifierProperty, resourceLanguagePropertiesSchema); allSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema; applicationSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema; machineSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema; @@ -413,11 +432,26 @@ class ConfigurationRegistry implements IConfigurationRegistry { } this._onDidSchemaChange.fire(); } + + private updatePropertyDefaultValue(key: string, property: IConfigurationPropertySchema): void { + let defaultValue = this.defaultValues[key]; + if (types.isUndefined(defaultValue)) { + defaultValue = property.default; + } + if (types.isUndefined(defaultValue)) { + defaultValue = getDefaultValue(property.type); + } + property.default = defaultValue; + } } const OVERRIDE_PROPERTY = '\\[.*\\]$'; export const OVERRIDE_PROPERTY_PATTERN = new RegExp(OVERRIDE_PROPERTY); +export function overrideIdentifierFromKey(key: string): string { + return key.substring(1, key.length - 1); +} + export function getDefaultValue(type: string | string[] | undefined): any { const t = Array.isArray(type) ? (type)[0] : type; switch (t) { diff --git a/src/vs/platform/configuration/common/configurationService.ts b/src/vs/platform/configuration/common/configurationService.ts index 21a12826ee4..2e8b85b1f09 100644 --- a/src/vs/platform/configuration/common/configurationService.ts +++ b/src/vs/platform/configuration/common/configurationService.ts @@ -15,7 +15,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; export class ConfigurationService extends Disposable implements IConfigurationService, IDisposable { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private configuration: Configuration; private userConfiguration: UserSettings; diff --git a/src/vs/platform/configuration/test/common/configurationService.test.ts b/src/vs/platform/configuration/test/common/configurationService.test.ts new file mode 100644 index 00000000000..2d19fcf1308 --- /dev/null +++ b/src/vs/platform/configuration/test/common/configurationService.test.ts @@ -0,0 +1,246 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { Registry } from 'vs/platform/registry/common/platform'; +import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { URI } from 'vs/base/common/uri'; +import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { Event } from 'vs/base/common/event'; +import { NullLogService } from 'vs/platform/log/common/log'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; +import { IFileService } from 'vs/platform/files/common/files'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; + +suite('ConfigurationService', () => { + + let fileService: IFileService; + let settingsResource: URI; + const disposables: DisposableStore = new DisposableStore(); + + setup(async () => { + fileService = disposables.add(new FileService(new NullLogService())); + const diskFileSystemProvider = disposables.add(new InMemoryFileSystemProvider()); + fileService.registerProvider(Schemas.file, diskFileSystemProvider); + settingsResource = URI.file('settings.json'); + }); + + teardown(() => disposables.clear()); + + test('simple', async () => { + await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + await testObject.initialize(); + const config = testObject.getValue<{ + foo: string; + }>(); + + assert.ok(config); + assert.equal(config.foo, 'bar'); + }); + + test('config gets flattened', async () => { + await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); + + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + await testObject.initialize(); + const config = testObject.getValue<{ + testworkbench: { + editor: { + tabs: boolean; + }; + }; + }>(); + + assert.ok(config); + assert.ok(config.testworkbench); + assert.ok(config.testworkbench.editor); + assert.equal(config.testworkbench.editor.tabs, true); + }); + + test('error case does not explode', async () => { + await fileService.writeFile(settingsResource, VSBuffer.fromString(',,,,')); + + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + await testObject.initialize(); + const config = testObject.getValue<{ + foo: string; + }>(); + + assert.ok(config); + }); + + test('missing file does not explode', async () => { + const testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService)); + await testObject.initialize(); + + const config = testObject.getValue<{ foo: string }>(); + + assert.ok(config); + }); + + test('trigger configuration change event when file does not exist', async () => { + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + await testObject.initialize(); + return new Promise(async (c, e) => { + disposables.add(Event.filter(testObject.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(() => { + assert.equal(testObject.getValue('foo'), 'bar'); + c(); + })); + await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); + }); + + }); + + test('trigger configuration change event when file exists', async () => { + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); + await testObject.initialize(); + + return new Promise((c, e) => { + disposables.add(Event.filter(testObject.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(async (e) => { + assert.equal(testObject.getValue('foo'), 'barz'); + c(); + })); + fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "barz" }')); + }); + }); + + test('reloadConfiguration', async () => { + await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); + + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + await testObject.initialize(); + let config = testObject.getValue<{ + foo: string; + }>(); + assert.ok(config); + assert.equal(config.foo, 'bar'); + await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "changed" }')); + + // force a reload to get latest + await testObject.reloadConfiguration(); + config = testObject.getValue<{ + foo: string; + }>(); + assert.ok(config); + assert.equal(config.foo, 'changed'); + }); + + test('model defaults', async () => { + interface ITestSetting { + configuration: { + service: { + testSetting: string; + } + }; + } + + const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + configurationRegistry.registerConfiguration({ + 'id': '_test', + 'type': 'object', + 'properties': { + 'configuration.service.testSetting': { + 'type': 'string', + 'default': 'isSet' + } + } + }); + + let testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService)); + await testObject.initialize(); + let setting = testObject.getValue(); + + assert.ok(setting); + assert.equal(setting.configuration.service.testSetting, 'isSet'); + + await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); + testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + + setting = testObject.getValue(); + + assert.ok(setting); + assert.equal(setting.configuration.service.testSetting, 'isSet'); + + await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "configuration.service.testSetting": "isChanged" }')); + + await testObject.reloadConfiguration(); + setting = testObject.getValue(); + assert.ok(setting); + assert.equal(setting.configuration.service.testSetting, 'isChanged'); + }); + + test('lookup', async () => { + const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + configurationRegistry.registerConfiguration({ + 'id': '_test', + 'type': 'object', + 'properties': { + 'lookup.service.testSetting': { + 'type': 'string', + 'default': 'isSet' + } + } + }); + + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + testObject.initialize(); + + let res = testObject.inspect('something.missing'); + assert.strictEqual(res.value, undefined); + assert.strictEqual(res.defaultValue, undefined); + assert.strictEqual(res.userValue, undefined); + + res = testObject.inspect('lookup.service.testSetting'); + assert.strictEqual(res.defaultValue, 'isSet'); + assert.strictEqual(res.value, 'isSet'); + assert.strictEqual(res.userValue, undefined); + + await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "lookup.service.testSetting": "bar" }')); + + await testObject.reloadConfiguration(); + res = testObject.inspect('lookup.service.testSetting'); + assert.strictEqual(res.defaultValue, 'isSet'); + assert.strictEqual(res.userValue, 'bar'); + assert.strictEqual(res.value, 'bar'); + + }); + + test('lookup with null', async () => { + const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + configurationRegistry.registerConfiguration({ + 'id': '_testNull', + 'type': 'object', + 'properties': { + 'lookup.service.testNullSetting': { + 'type': 'null', + } + } + }); + + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + testObject.initialize(); + + let res = testObject.inspect('lookup.service.testNullSetting'); + assert.strictEqual(res.defaultValue, null); + assert.strictEqual(res.value, null); + assert.strictEqual(res.userValue, undefined); + + await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "lookup.service.testNullSetting": null }')); + + await testObject.reloadConfiguration(); + + res = testObject.inspect('lookup.service.testNullSetting'); + assert.strictEqual(res.defaultValue, null); + assert.strictEqual(res.value, null); + assert.strictEqual(res.userValue, null); + }); +}); diff --git a/src/vs/platform/configuration/test/node/configurationService.test.ts b/src/vs/platform/configuration/test/node/configurationService.test.ts deleted file mode 100644 index f5fb471fcdb..00000000000 --- a/src/vs/platform/configuration/test/node/configurationService.test.ts +++ /dev/null @@ -1,304 +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 { Registry } from 'vs/platform/registry/common/platform'; -import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; -import * as uuid from 'vs/base/common/uuid'; -import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; -import { testFile } from 'vs/base/test/node/utils'; -import { URI } from 'vs/base/common/uri'; -import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { Event } from 'vs/base/common/event'; -import { NullLogService } from 'vs/platform/log/common/log'; -import { FileService } from 'vs/platform/files/common/fileService'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; -import { Schemas } from 'vs/base/common/network'; -import { IFileService } from 'vs/platform/files/common/files'; -import { VSBuffer } from 'vs/base/common/buffer'; - -suite('ConfigurationService - Node', () => { - - let fileService: IFileService; - const disposables: IDisposable[] = []; - - setup(() => { - const logService = new NullLogService(); - fileService = new FileService(logService); - disposables.push(fileService); - const diskFileSystemProvider = new DiskFileSystemProvider(logService); - disposables.push(diskFileSystemProvider); - fileService.registerProvider(Schemas.file, diskFileSystemProvider); - }); - - test('simple', async () => { - const res = await testFile('config', 'config.json'); - fs.writeFileSync(res.testFile, '{ "foo": "bar" }'); - - const service = new ConfigurationService(URI.file(res.testFile), fileService); - await service.initialize(); - const config = service.getValue<{ - foo: string; - }>(); - - assert.ok(config); - assert.equal(config.foo, 'bar'); - service.dispose(); - - return res.cleanUp(); - }); - - test('config gets flattened', async () => { - const res = await testFile('config', 'config.json'); - - fs.writeFileSync(res.testFile, '{ "testworkbench.editor.tabs": true }'); - - const service = new ConfigurationService(URI.file(res.testFile), fileService); - await service.initialize(); - const config = service.getValue<{ - testworkbench: { - editor: { - tabs: boolean; - }; - }; - }>(); - assert.ok(config); - assert.ok(config.testworkbench); - assert.ok(config.testworkbench.editor); - assert.equal(config.testworkbench.editor.tabs, true); - - service.dispose(); - return res.cleanUp(); - }); - - test('error case does not explode', async () => { - const res = await testFile('config', 'config.json'); - - fs.writeFileSync(res.testFile, ',,,,'); - - const service = new ConfigurationService(URI.file(res.testFile), fileService); - await service.initialize(); - const config = service.getValue<{ - foo: string; - }>(); - assert.ok(config); - - service.dispose(); - return res.cleanUp(); - }); - - test('missing file does not explode', async () => { - 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'); - - const service = new ConfigurationService(URI.file(testFile), fileService); - await service.initialize(); - - const config = service.getValue<{ foo: string }>(); - assert.ok(config); - - service.dispose(); - }); - - test('trigger configuration change event when file does not exist', async () => { - const res = await testFile('config', 'config.json'); - const settingsFile = URI.file(res.testFile); - const service = new ConfigurationService(settingsFile, fileService); - await service.initialize(); - return new Promise(async (c, e) => { - const disposable = Event.filter(service.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(async (e) => { - disposable.dispose(); - assert.equal(service.getValue('foo'), 'bar'); - service.dispose(); - await res.cleanUp(); - c(); - }); - await fileService.writeFile(settingsFile, VSBuffer.fromString('{ "foo": "bar" }')); - }); - - }); - - test('trigger configuration change event when file exists', async () => { - const res = await testFile('config', 'config.json'); - - const service = new ConfigurationService(URI.file(res.testFile), fileService); - fs.writeFileSync(res.testFile, '{ "foo": "bar" }'); - await service.initialize(); - return new Promise((c, e) => { - const disposable = Event.filter(service.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(async (e) => { - disposable.dispose(); - assert.equal(service.getValue('foo'), 'barz'); - service.dispose(); - await res.cleanUp(); - c(); - }); - fs.writeFileSync(res.testFile, '{ "foo": "barz" }'); - }); - - }); - - test('reloadConfiguration', async () => { - const res = await testFile('config', 'config.json'); - - fs.writeFileSync(res.testFile, '{ "foo": "bar" }'); - - const service = new ConfigurationService(URI.file(res.testFile), fileService); - await service.initialize(); - let config = service.getValue<{ - foo: string; - }>(); - assert.ok(config); - assert.equal(config.foo, 'bar'); - fs.writeFileSync(res.testFile, '{ "foo": "changed" }'); - - // still outdated - config = service.getValue<{ - foo: string; - }>(); - assert.ok(config); - assert.equal(config.foo, 'bar'); - - // force a reload to get latest - await service.reloadConfiguration(); - config = service.getValue<{ - foo: string; - }>(); - assert.ok(config); - assert.equal(config.foo, 'changed'); - - service.dispose(); - return res.cleanUp(); - }); - - test('model defaults', async () => { - interface ITestSetting { - configuration: { - service: { - testSetting: string; - } - }; - } - - const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); - configurationRegistry.registerConfiguration({ - 'id': '_test', - 'type': 'object', - 'properties': { - 'configuration.service.testSetting': { - 'type': 'string', - 'default': 'isSet' - } - } - }); - - let serviceWithoutFile = new ConfigurationService(URI.file('__testFile'), fileService); - await serviceWithoutFile.initialize(); - let setting = serviceWithoutFile.getValue(); - - assert.ok(setting); - assert.equal(setting.configuration.service.testSetting, 'isSet'); - - return testFile('config', 'config.json').then(async res => { - fs.writeFileSync(res.testFile, '{ "testworkbench.editor.tabs": true }'); - - const service = new ConfigurationService(URI.file(res.testFile), fileService); - - let setting = service.getValue(); - - assert.ok(setting); - assert.equal(setting.configuration.service.testSetting, 'isSet'); - - fs.writeFileSync(res.testFile, '{ "configuration.service.testSetting": "isChanged" }'); - - await service.reloadConfiguration(); - let setting_1 = service.getValue(); - assert.ok(setting_1); - assert.equal(setting_1.configuration.service.testSetting, 'isChanged'); - service.dispose(); - serviceWithoutFile.dispose(); - return res.cleanUp(); - }); - }); - - test('lookup', async () => { - const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); - configurationRegistry.registerConfiguration({ - 'id': '_test', - 'type': 'object', - 'properties': { - 'lookup.service.testSetting': { - 'type': 'string', - 'default': 'isSet' - } - } - }); - - const r = await testFile('config', 'config.json'); - const service = new ConfigurationService(URI.file(r.testFile), fileService); - service.initialize(); - - let res = service.inspect('something.missing'); - assert.strictEqual(res.value, undefined); - assert.strictEqual(res.defaultValue, undefined); - assert.strictEqual(res.userValue, undefined); - - res = service.inspect('lookup.service.testSetting'); - assert.strictEqual(res.defaultValue, 'isSet'); - assert.strictEqual(res.value, 'isSet'); - assert.strictEqual(res.userValue, undefined); - - fs.writeFileSync(r.testFile, '{ "lookup.service.testSetting": "bar" }'); - - await service.reloadConfiguration(); - res = service.inspect('lookup.service.testSetting'); - assert.strictEqual(res.defaultValue, 'isSet'); - assert.strictEqual(res.userValue, 'bar'); - assert.strictEqual(res.value, 'bar'); - - service.dispose(); - return r.cleanUp(); - }); - - test('lookup with null', async () => { - const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); - configurationRegistry.registerConfiguration({ - 'id': '_testNull', - 'type': 'object', - 'properties': { - 'lookup.service.testNullSetting': { - 'type': 'null', - } - } - }); - - const r = await testFile('config', 'config.json'); - const service = new ConfigurationService(URI.file(r.testFile), fileService); - service.initialize(); - - let res = service.inspect('lookup.service.testNullSetting'); - assert.strictEqual(res.defaultValue, null); - assert.strictEqual(res.value, null); - assert.strictEqual(res.userValue, undefined); - - fs.writeFileSync(r.testFile, '{ "lookup.service.testNullSetting": null }'); - - await service.reloadConfiguration(); - - res = service.inspect('lookup.service.testNullSetting'); - assert.strictEqual(res.defaultValue, null); - assert.strictEqual(res.value, null); - assert.strictEqual(res.userValue, null); - - service.dispose(); - return r.cleanUp(); - }); -}); diff --git a/src/vs/platform/contextkey/browser/contextKeyService.ts b/src/vs/platform/contextkey/browser/contextKeyService.ts index 78f7843ea46..89720995be2 100644 --- a/src/vs/platform/contextkey/browser/contextKeyService.ts +++ b/src/vs/platform/contextkey/browser/contextKeyService.ts @@ -5,11 +5,11 @@ import { Emitter, Event, PauseableEmitter } from 'vs/base/common/event'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { keys } from 'vs/base/common/map'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContext, IContextKey, IContextKeyChangeEvent, IContextKeyService, IContextKeyServiceTarget, IReadableSet, SET_CONTEXT_COMMAND_ID, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; +import { toArray } from 'vs/base/common/arrays'; const KEYBINDING_CONTEXT_ATTR = 'data-keybinding-context'; @@ -102,7 +102,7 @@ class ConfigAwareContextValuesContainer extends Context { this._listener = this._configurationService.onDidChangeConfiguration(event => { if (event.source === ConfigurationTarget.DEFAULT) { // new setting, reset everything - const allKeys = keys(this._values); + const allKeys = toArray(this._values.keys()); this._values.clear(); emitter.fire(new ArrayContextKeyChangeEvent(allKeys)); } else { diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index 414b6a3d3c0..55e154645ec 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -637,16 +637,7 @@ export class ContextKeyNotRegexExpr implements IContextKeyExpression { export class ContextKeyAndExpr implements IContextKeyExpression { public static create(_expr: ReadonlyArray): ContextKeyExpression | undefined { - const expr = ContextKeyAndExpr._normalizeArr(_expr); - if (expr.length === 0) { - return undefined; - } - - if (expr.length === 1) { - return expr[0]; - } - - return new ContextKeyAndExpr(expr); + return ContextKeyAndExpr._normalizeArr(_expr); } public readonly type = ContextKeyExprType.And; @@ -697,7 +688,7 @@ export class ContextKeyAndExpr implements IContextKeyExpression { return true; } - private static _normalizeArr(arr: ReadonlyArray): ContextKeyExpression[] { + private static _normalizeArr(arr: ReadonlyArray): ContextKeyExpression | undefined { const expr: ContextKeyExpression[] = []; let hasTrue = false; @@ -714,7 +705,7 @@ export class ContextKeyAndExpr implements IContextKeyExpression { if (e.type === ContextKeyExprType.False) { // anything && false ==> false - return [ContextKeyFalseExpr.INSTANCE]; + return ContextKeyFalseExpr.INSTANCE; } if (e.type === ContextKeyExprType.And) { @@ -722,21 +713,48 @@ export class ContextKeyAndExpr implements IContextKeyExpression { continue; } - if (e.type === ContextKeyExprType.Or) { - // Not allowed, because we don't have parens! - throw new Error(`It is not allowed to have an or expression here due to lack of parens! For example "a && (b||c)" is not supported, use "(a&&b) || (a&&c)" instead.`); - } - expr.push(e); } if (expr.length === 0 && hasTrue) { - return [ContextKeyTrueExpr.INSTANCE]; + return ContextKeyTrueExpr.INSTANCE; + } + + if (expr.length === 0) { + return undefined; + } + + if (expr.length === 1) { + return expr[0]; } expr.sort(cmp); - return expr; + // We must distribute any OR expression because we don't support parens + // OR extensions will be at the end (due to sorting rules) + while (expr.length > 1) { + const lastElement = expr[expr.length - 1]; + if (lastElement.type !== ContextKeyExprType.Or) { + break; + } + // pop the last element + expr.pop(); + + // pop the second to last element + const secondToLastElement = expr.pop()!; + + // distribute `lastElement` over `secondToLastElement` + const resultElement = ContextKeyOrExpr.create( + lastElement.expr.map(el => ContextKeyAndExpr.create([el, secondToLastElement])) + ); + + if (resultElement) { + expr.push(resultElement); + expr.sort(cmp); + } + } + + return new ContextKeyAndExpr(expr); } public serialize(): string { @@ -974,13 +992,12 @@ export interface IContextKeyChangeEvent { } export interface IContextKeyService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; dispose(): void; onDidChangeContext: Event; bufferChangeEvents(callback: Function): void; - createKey(key: string, defaultValue: T | undefined): IContextKey; contextMatchesRules(rules: ContextKeyExpression | undefined): boolean; getContextKeyValue(key: string): T | undefined; diff --git a/src/vs/platform/contextkey/test/common/contextkey.test.ts b/src/vs/platform/contextkey/test/common/contextkey.test.ts index f5a04ce1cf2..c7784c888e1 100644 --- a/src/vs/platform/contextkey/test/common/contextkey.test.ts +++ b/src/vs/platform/contextkey/test/common/contextkey.test.ts @@ -136,4 +136,18 @@ suite('ContextKeyExpr', () => { testNormalize('isLinux', isLinux ? 'true' : 'false'); testNormalize('isWindows', isWindows ? 'true' : 'false'); }); + + test('issue #101015: distribute OR', () => { + function t(expr1: string, expr2: string, expected: string | undefined): void { + const e1 = ContextKeyExpr.deserialize(expr1); + const e2 = ContextKeyExpr.deserialize(expr2); + const actual = ContextKeyExpr.and(e1, e2)?.serialize(); + assert.strictEqual(actual, expected); + } + t('a', 'b', 'a && b'); + t('a || b', 'c', 'a && c || b && c'); + t('a || b', 'c || d', 'a && c || b && c || a && d || b && d'); + t('a || b', 'c && d', 'a && c && d || b && c && d'); + t('a || b', 'c && d || e', 'a && e || b && e || a && c && d || b && c && d'); + }); }); diff --git a/src/vs/platform/contextview/browser/contextMenuHandler.css b/src/vs/platform/contextview/browser/contextMenuHandler.css index 97ac10bc5be..51a9e400923 100644 --- a/src/vs/platform/contextview/browser/contextMenuHandler.css +++ b/src/vs/platform/contextview/browser/contextMenuHandler.css @@ -7,11 +7,3 @@ min-width: 130px; } -.context-view-block { - position: fixed; - left:0; - top:0; - z-index: -1; - width: 100%; - height: 100%; -} \ No newline at end of file diff --git a/src/vs/platform/contextview/browser/contextMenuHandler.ts b/src/vs/platform/contextview/browser/contextMenuHandler.ts index 921a5f810a5..21162d2b5bf 100644 --- a/src/vs/platform/contextview/browser/contextMenuHandler.ts +++ b/src/vs/platform/contextview/browser/contextMenuHandler.ts @@ -14,7 +14,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IContextMenuDelegate } from 'vs/base/browser/contextmenu'; -import { EventType, $, removeNode } from 'vs/base/browser/dom'; +import { EventType, $, removeNode, isHTMLElement } from 'vs/base/browser/dom'; import { attachMenuStyler } from 'vs/platform/theme/common/styler'; import { domEvent } from 'vs/base/browser/event'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; @@ -50,6 +50,7 @@ export class ContextMenuHandler { let menu: Menu | undefined; + let shadowRootElement = isHTMLElement(delegate.domForShadowRoot) ? delegate.domForShadowRoot : undefined; this.contextViewService.showContextView({ getAnchor: () => delegate.getAnchor(), canRelayout: false, @@ -65,6 +66,13 @@ export class ContextMenuHandler { // Render invisible div to block mouse interaction in the rest of the UI if (this.options.blockMouse) { this.block = container.appendChild($('.context-view-block')); + this.block.style.position = 'fixed'; + this.block.style.cursor = 'initial'; + this.block.style.left = '0'; + this.block.style.top = '0'; + this.block.style.width = '100%'; + this.block.style.height = '100%'; + domEvent(this.block, EventType.MOUSE_DOWN)((e: MouseEvent) => e.stopPropagation()); } const menuDisposables = new DisposableStore(); @@ -131,7 +139,7 @@ export class ContextMenuHandler { this.focusToReturn.focus(); } } - }); + }, shadowRootElement, !!shadowRootElement); } private onActionRun(e: IRunEvent): void { diff --git a/src/vs/platform/contextview/browser/contextMenuService.ts b/src/vs/platform/contextview/browser/contextMenuService.ts index 539b819e643..3ef087c575b 100644 --- a/src/vs/platform/contextview/browser/contextMenuService.ts +++ b/src/vs/platform/contextview/browser/contextMenuService.ts @@ -14,7 +14,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Disposable } from 'vs/base/common/lifecycle'; export class ContextMenuService extends Disposable implements IContextMenuService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _onDidContextMenu = this._register(new Emitter()); readonly onDidContextMenu: Event = this._onDidContextMenu.event; diff --git a/src/vs/platform/contextview/browser/contextView.ts b/src/vs/platform/contextview/browser/contextView.ts index 2104e66c0b4..c6511397a9f 100644 --- a/src/vs/platform/contextview/browser/contextView.ts +++ b/src/vs/platform/contextview/browser/contextView.ts @@ -13,10 +13,11 @@ export const IContextViewService = createDecorator('context export interface IContextViewService extends IContextViewProvider { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; - showContextView(delegate: IContextViewDelegate): void; + showContextView(delegate: IContextViewDelegate, container?: HTMLElement, shadowRoot?: boolean): IDisposable; hideContextView(data?: any): void; + getContextViewElement(): HTMLElement; layout(): void; anchorAlignment?: AnchorAlignment; } @@ -37,7 +38,7 @@ export const IContextMenuService = createDecorator('context export interface IContextMenuService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; showContextMenu(delegate: IContextMenuDelegate): void; onDidContextMenu: Event; // TODO@isidor these event should be removed once we get async context menus diff --git a/src/vs/platform/contextview/browser/contextViewService.ts b/src/vs/platform/contextview/browser/contextViewService.ts index 1cbd4908c2d..cb7578f0500 100644 --- a/src/vs/platform/contextview/browser/contextViewService.ts +++ b/src/vs/platform/contextview/browser/contextViewService.ts @@ -4,21 +4,24 @@ *--------------------------------------------------------------------------------------------*/ import { IContextViewService, IContextViewDelegate } from './contextView'; -import { ContextView } from 'vs/base/browser/ui/contextview/contextview'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { ContextView, ContextViewDOMPosition } from 'vs/base/browser/ui/contextview/contextview'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; export class ContextViewService extends Disposable implements IContextViewService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; + private currentViewDisposable: IDisposable = Disposable.None; private contextView: ContextView; + private container: HTMLElement; constructor( @ILayoutService readonly layoutService: ILayoutService ) { super(); - this.contextView = this._register(new ContextView(layoutService.container)); + this.container = layoutService.container; + this.contextView = this._register(new ContextView(this.container, ContextViewDOMPosition.ABSOLUTE)); this.layout(); this._register(layoutService.onLayout(() => this.layout())); @@ -26,12 +29,37 @@ export class ContextViewService extends Disposable implements IContextViewServic // ContextView - setContainer(container: HTMLElement): void { - this.contextView.setContainer(container); + setContainer(container: HTMLElement, domPosition?: ContextViewDOMPosition): void { + this.contextView.setContainer(container, domPosition || ContextViewDOMPosition.ABSOLUTE); } - showContextView(delegate: IContextViewDelegate): void { + showContextView(delegate: IContextViewDelegate, container?: HTMLElement, shadowRoot?: boolean): IDisposable { + if (container) { + if (container !== this.container) { + this.container = container; + this.setContainer(container, shadowRoot ? ContextViewDOMPosition.FIXED_SHADOW : ContextViewDOMPosition.FIXED); + } + } else { + if (this.container !== this.layoutService.container) { + this.container = this.layoutService.container; + this.setContainer(this.container, ContextViewDOMPosition.ABSOLUTE); + } + } + this.contextView.show(delegate); + + const disposable = toDisposable(() => { + if (this.currentViewDisposable === disposable) { + this.hideContextView(); + } + }); + + this.currentViewDisposable = disposable; + return disposable; + } + + getContextViewElement(): HTMLElement { + return this.contextView.getViewElement(); } layout(): void { @@ -41,4 +69,4 @@ export class ContextViewService extends Disposable implements IContextViewServic hideContextView(data?: any): void { this.contextView.hide(data); } -} \ No newline at end of file +} diff --git a/src/vs/platform/credentials/common/credentials.ts b/src/vs/platform/credentials/common/credentials.ts index 6fcb7dffaa0..2799abeed19 100644 --- a/src/vs/platform/credentials/common/credentials.ts +++ b/src/vs/platform/credentials/common/credentials.ts @@ -9,7 +9,7 @@ export const ICredentialsService = createDecorator('ICreden export interface ICredentialsService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; getPassword(service: string, account: string): Promise; setPassword(service: string, account: string, password: string): Promise; diff --git a/src/vs/platform/credentials/node/credentialsService.ts b/src/vs/platform/credentials/node/credentialsService.ts index 3641cf65d33..cce2f77f6b3 100644 --- a/src/vs/platform/credentials/node/credentialsService.ts +++ b/src/vs/platform/credentials/node/credentialsService.ts @@ -9,7 +9,7 @@ import { IdleValue } from 'vs/base/common/async'; type KeytarModule = typeof import('keytar'); export class KeytarCredentialsService implements ICredentialsService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _keytar = new IdleValue>(() => import('keytar')); diff --git a/src/vs/platform/debug/common/extensionHostDebug.ts b/src/vs/platform/debug/common/extensionHostDebug.ts index 57a604c2350..b263bdd9c1c 100644 --- a/src/vs/platform/debug/common/extensionHostDebug.ts +++ b/src/vs/platform/debug/common/extensionHostDebug.ts @@ -34,8 +34,12 @@ export interface ICloseSessionEvent { sessionId: string; } +export interface IOpenExtensionWindowResult { + rendererDebugPort?: number; +} + export interface IExtensionHostDebugService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; reload(sessionId: string): void; readonly onReload: Event; @@ -52,5 +56,5 @@ export interface IExtensionHostDebugService { terminateSession(sessionId: string, subId?: string): void; readonly onTerminateSession: Event; - openExtensionDevelopmentHostWindow(args: string[], env: IProcessEnvironment): Promise; + openExtensionDevelopmentHostWindow(args: string[], env: IProcessEnvironment, debugRenderer: boolean): Promise; } diff --git a/src/vs/platform/debug/common/extensionHostDebugIpc.ts b/src/vs/platform/debug/common/extensionHostDebugIpc.ts index 72aa7fcc2ea..60011be13e3 100644 --- a/src/vs/platform/debug/common/extensionHostDebugIpc.ts +++ b/src/vs/platform/debug/common/extensionHostDebugIpc.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IReloadSessionEvent, ICloseSessionEvent, IAttachSessionEvent, ILogToSessionEvent, ITerminateSessionEvent, IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; +import { IReloadSessionEvent, ICloseSessionEvent, IAttachSessionEvent, ILogToSessionEvent, ITerminateSessionEvent, IExtensionHostDebugService, IOpenExtensionWindowResult } from 'vs/platform/debug/common/extensionHostDebug'; import { Event, Emitter } from 'vs/base/common/event'; import { IRemoteConsoleLog } from 'vs/base/common/console'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -55,7 +55,7 @@ export class ExtensionHostDebugBroadcastChannel implements IServerChan export class ExtensionHostDebugChannelClient extends Disposable implements IExtensionHostDebugService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(private channel: IChannel) { super(); @@ -101,7 +101,7 @@ export class ExtensionHostDebugChannelClient extends Disposable implements IExte return this.channel.listen('terminate'); } - openExtensionDevelopmentHostWindow(args: string[], env: IProcessEnvironment): Promise { - return this.channel.call('openExtensionDevelopmentHostWindow', [args, env]); + openExtensionDevelopmentHostWindow(args: string[], env: IProcessEnvironment, debugRenderer: boolean): Promise { + return this.channel.call('openExtensionDevelopmentHostWindow', [args, env, debugRenderer]); } } diff --git a/src/vs/platform/diagnostics/node/diagnosticsIpc.ts b/src/vs/platform/diagnostics/node/diagnosticsIpc.ts index 4315b7f8b6a..f43ae1f4ca3 100644 --- a/src/vs/platform/diagnostics/node/diagnosticsIpc.ts +++ b/src/vs/platform/diagnostics/node/diagnosticsIpc.ts @@ -36,7 +36,7 @@ export class DiagnosticsChannel implements IServerChannel { export class DiagnosticsService implements IDiagnosticsService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(private channel: IChannel) { } diff --git a/src/vs/platform/diagnostics/node/diagnosticsService.ts b/src/vs/platform/diagnostics/node/diagnosticsService.ts index 8ee870e3297..a858b96f5b7 100644 --- a/src/vs/platform/diagnostics/node/diagnosticsService.ts +++ b/src/vs/platform/diagnostics/node/diagnosticsService.ts @@ -23,7 +23,7 @@ export const ID = 'diagnosticsService'; export const IDiagnosticsService = createDecorator(ID); export interface IDiagnosticsService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; getPerformanceInfo(mainProcessInfo: IMainProcessInfo, remoteInfo: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise; getSystemInfo(mainProcessInfo: IMainProcessInfo, remoteInfo: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise; @@ -223,7 +223,7 @@ export function collectLaunchConfigs(folder: string): Promise('fileDialo */ export interface IFileDialogService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * The default path for a new file based on previously used files. @@ -272,3 +272,12 @@ export function getFileNamesMessage(fileNamesOrResources: readonly (string | URI message.push(''); return message.join('\n'); } + +export interface INativeOpenDialogOptions { + forceNewWindow?: boolean; + + defaultPath?: string; + + telemetryEventName?: string; + telemetryExtraData?: ITelemetryData; +} diff --git a/src/vs/platform/dialogs/electron-main/dialogs.ts b/src/vs/platform/dialogs/electron-main/dialogs.ts index c694c003e9b..809e2558724 100644 --- a/src/vs/platform/dialogs/electron-main/dialogs.ts +++ b/src/vs/platform/dialogs/electron-main/dialogs.ts @@ -11,7 +11,7 @@ import { isMacintosh } from 'vs/base/common/platform'; import { dirname } from 'vs/base/common/path'; import { normalizeNFC } from 'vs/base/common/normalization'; import { exists } from 'vs/base/node/pfs'; -import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs'; +import { INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { withNullAsUndefined } from 'vs/base/common/types'; import { localize } from 'vs/nls'; import { WORKSPACE_FILTER } from 'vs/platform/workspaces/common/workspaces'; @@ -21,7 +21,7 @@ export const IDialogMainService = createDecorator('dialogMai export interface IDialogMainService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; pickFileFolder(options: INativeOpenDialogOptions, window?: BrowserWindow): Promise; pickFolder(options: INativeOpenDialogOptions, window?: BrowserWindow): Promise; @@ -44,7 +44,7 @@ interface IInternalNativeOpenDialogOptions extends INativeOpenDialogOptions { export class DialogMainService implements IDialogMainService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private static readonly workingDirPickerStorageKey = 'pickerWorkingDir'; diff --git a/src/vs/platform/dialogs/test/common/testDialogService.ts b/src/vs/platform/dialogs/test/common/testDialogService.ts index 0f1cbc0f992..c25590cb6e0 100644 --- a/src/vs/platform/dialogs/test/common/testDialogService.ts +++ b/src/vs/platform/dialogs/test/common/testDialogService.ts @@ -8,7 +8,7 @@ import { IConfirmation, IConfirmationResult, IDialogService, IDialogOptions, ISh export class TestDialogService implements IDialogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; confirm(_confirmation: IConfirmation): Promise { return Promise.resolve({ confirmed: false }); } show(_severity: Severity, _message: string, _buttons: string[], _options?: IDialogOptions): Promise { return Promise.resolve({ choice: 0 }); } diff --git a/src/vs/platform/download/common/download.ts b/src/vs/platform/download/common/download.ts index 95f1d0ad66e..6eccb9da75b 100644 --- a/src/vs/platform/download/common/download.ts +++ b/src/vs/platform/download/common/download.ts @@ -11,7 +11,7 @@ export const IDownloadService = createDecorator('downloadServi export interface IDownloadService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; download(uri: URI, to: URI, cancellationToken?: CancellationToken): Promise; diff --git a/src/vs/platform/download/common/downloadIpc.ts b/src/vs/platform/download/common/downloadIpc.ts index 0876dfbc3d7..6cb88c42cee 100644 --- a/src/vs/platform/download/common/downloadIpc.ts +++ b/src/vs/platform/download/common/downloadIpc.ts @@ -27,7 +27,7 @@ export class DownloadServiceChannel implements IServerChannel { export class DownloadServiceChannelClient implements IDownloadService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(private channel: IChannel, private getUriTransformer: () => IURITransformer | null) { } @@ -39,4 +39,4 @@ export class DownloadServiceChannelClient implements IDownloadService { } await this.channel.call('download', [from, to]); } -} \ No newline at end of file +} diff --git a/src/vs/platform/download/common/downloadService.ts b/src/vs/platform/download/common/downloadService.ts index 7de9b02fc3d..6442bbd301c 100644 --- a/src/vs/platform/download/common/downloadService.ts +++ b/src/vs/platform/download/common/downloadService.ts @@ -12,7 +12,7 @@ import { Schemas } from 'vs/base/common/network'; export class DownloadService implements IDownloadService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @IRequestService private readonly requestService: IRequestService, diff --git a/src/vs/platform/driver/common/driver.ts b/src/vs/platform/driver/common/driver.ts index c93106ce99f..bae76735466 100644 --- a/src/vs/platform/driver/common/driver.ts +++ b/src/vs/platform/driver/common/driver.ts @@ -19,7 +19,7 @@ export interface IElement { } export interface IDriver { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; getWindowIds(): Promise; capturePage(windowId: number): Promise; diff --git a/src/vs/platform/driver/electron-browser/driver.ts b/src/vs/platform/driver/electron-browser/driver.ts index 2594eef1361..f0d350d392e 100644 --- a/src/vs/platform/driver/electron-browser/driver.ts +++ b/src/vs/platform/driver/electron-browser/driver.ts @@ -6,11 +6,10 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { WindowDriverChannel, WindowDriverRegistryChannelClient } from 'vs/platform/driver/node/driver'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; -import * as electron from 'electron'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; import { timeout } from 'vs/base/common/async'; import { BaseWindowDriver } from 'vs/platform/driver/browser/baseDriver'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; class WindowDriver extends BaseWindowDriver { @@ -32,11 +31,10 @@ class WindowDriver extends BaseWindowDriver { private async _click(selector: string, clickCount: number, offset?: { x: number, y: number }): Promise { const { x, y } = await this._getElementXY(selector, offset); - const webContents: electron.WebContents = (electron as any).remote.getCurrentWebContents(); - webContents.sendInputEvent({ type: 'mouseDown', x, y, button: 'left', clickCount } as any); + await this.electronService.sendInputEvent({ type: 'mouseDown', x, y, button: 'left', clickCount } as any); await timeout(10); - webContents.sendInputEvent({ type: 'mouseUp', x, y, button: 'left', clickCount } as any); + await this.electronService.sendInputEvent({ type: 'mouseUp', x, y, button: 'left', clickCount } as any); await timeout(100); } diff --git a/src/vs/platform/driver/electron-main/driver.ts b/src/vs/platform/driver/electron-main/driver.ts index b5a041bf44c..b8b703f05ff 100644 --- a/src/vs/platform/driver/electron-main/driver.ts +++ b/src/vs/platform/driver/electron-main/driver.ts @@ -27,7 +27,7 @@ function isSilentKeyCode(keyCode: KeyCode) { export class Driver implements IDriver, IWindowDriverRegistry { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private registeredWindowIds = new Set(); private reloadingWindowIds = new Set(); diff --git a/src/vs/platform/driver/node/driver.ts b/src/vs/platform/driver/node/driver.ts index f0713a37181..185100ef2b9 100644 --- a/src/vs/platform/driver/node/driver.ts +++ b/src/vs/platform/driver/node/driver.ts @@ -42,7 +42,7 @@ export class DriverChannel implements IServerChannel { export class DriverChannelClient implements IDriver { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(private channel: IChannel) { } @@ -136,7 +136,7 @@ export class WindowDriverRegistryChannel implements IServerChannel { export class WindowDriverRegistryChannelClient implements IWindowDriverRegistry { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(private channel: IChannel) { } @@ -177,7 +177,7 @@ export class WindowDriverChannel implements IServerChannel { export class WindowDriverChannelClient implements IWindowDriver { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(private channel: IChannel) { } diff --git a/src/vs/platform/editor/common/editor.ts b/src/vs/platform/editor/common/editor.ts index 0ab59f42389..ff00b046dad 100644 --- a/src/vs/platform/editor/common/editor.ts +++ b/src/vs/platform/editor/common/editor.ts @@ -197,9 +197,12 @@ export interface IEditorOptions { readonly ignoreError?: boolean; /** - * Does not use editor overrides while opening the editor + * Allows to override the editor that should be used to display the input: + * - `undefined`: let the editor decide for itself + * - `false`: disable overrides + * - `string`: specific override by id */ - readonly ignoreOverrides?: boolean; + readonly override?: false | string; /** * A optional hint to signal in which context the editor opens. diff --git a/src/vs/platform/electron/node/electron.ts b/src/vs/platform/electron/common/electron.ts similarity index 67% rename from src/vs/platform/electron/node/electron.ts rename to src/vs/platform/electron/common/electron.ts index 43203239b81..1d65ceea6c6 100644 --- a/src/vs/platform/electron/node/electron.ts +++ b/src/vs/platform/electron/common/electron.ts @@ -4,18 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { MessageBoxOptions, MessageBoxReturnValue, OpenDevToolsOptions, SaveDialogOptions, OpenDialogOptions, OpenDialogReturnValue, SaveDialogReturnValue, CrashReporterStartOptions } from 'electron'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; -import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs'; +import { MessageBoxOptions, MessageBoxReturnValue, OpenDevToolsOptions, SaveDialogOptions, OpenDialogOptions, OpenDialogReturnValue, SaveDialogReturnValue, CrashReporterStartOptions, MouseInputEvent } from 'vs/base/parts/sandbox/common/electronTypes'; +import { IOpenedWindow, IWindowOpenable, IOpenEmptyWindowOptions, IOpenWindowOptions } from 'vs/platform/windows/common/windows'; +import { INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; -import { INativeOpenWindowOptions, IOpenedWindow } from 'vs/platform/windows/node/window'; -export const IElectronService = createDecorator('electronService'); +export interface ICommonElectronService { -export interface IElectronService { + readonly _serviceBrand: undefined; - _serviceBrand: undefined; + // Properties + readonly windowId: number; // Events readonly onWindowOpen: Event; @@ -26,13 +25,15 @@ export interface IElectronService { readonly onWindowFocus: Event; readonly onWindowBlur: Event; + readonly onOSResume: Event; + // Window getWindows(): Promise; getWindowCount(): Promise; getActiveWindowId(): Promise; openWindow(options?: IOpenEmptyWindowOptions): Promise; - openWindow(toOpen: IWindowOpenable[], options?: INativeOpenWindowOptions): Promise; + openWindow(toOpen: IWindowOpenable[], options?: IOpenWindowOptions): Promise; toggleFullScreen(): Promise; @@ -61,6 +62,21 @@ export interface IElectronService { setDocumentEdited(edited: boolean): Promise; openExternal(url: string): Promise; updateTouchBar(items: ISerializableCommandAction[][]): Promise; + moveItemToTrash(fullPath: string, deleteOnFail?: boolean): Promise; + isAdmin(): Promise; + getTotalMem(): Promise; + + // Process + killProcess(pid: number, code: string): Promise; + + // clipboard + readClipboardText(type?: 'selection' | 'clipboard'): Promise; + writeClipboardText(text: string, type?: 'selection' | 'clipboard'): Promise; + readClipboardFindText(): Promise; + writeClipboardFindText(text: string): Promise; + writeClipboardBuffer(format: string, buffer: Uint8Array, type?: 'selection' | 'clipboard'): Promise; + readClipboardBuffer(format: string): Promise; + hasClipboard(format: string, type?: 'selection' | 'clipboard'): Promise; // macOS Touchbar newWindowTab(): Promise; @@ -71,16 +87,19 @@ export interface IElectronService { toggleWindowTabsBar(): Promise; // Lifecycle + notifyReady(): Promise relaunch(options?: { addArgs?: string[], removeArgs?: string[] }): Promise; reload(options?: { disableExtensions?: boolean }): Promise; closeWindow(): Promise; closeWindowById(windowId: number): Promise; quit(): Promise; + exit(code: number): Promise; // Development openDevTools(options?: OpenDevToolsOptions): Promise; toggleDevTools(): Promise; startCrashReporter(options: CrashReporterStartOptions): Promise; + sendInputEvent(event: MouseInputEvent): Promise; // Connectivity resolveProxy(url: string): Promise; diff --git a/src/vs/platform/electron/electron-main/electronMainService.ts b/src/vs/platform/electron/electron-main/electronMainService.ts index c21fb966278..0976846791f 100644 --- a/src/vs/platform/electron/electron-main/electronMainService.ts +++ b/src/vs/platform/electron/electron-main/electronMainService.ts @@ -5,13 +5,13 @@ import { Event } from 'vs/base/common/event'; import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; -import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, CrashReporterStartOptions, crashReporter, Menu, BrowserWindow, app } from 'electron'; -import { INativeOpenWindowOptions, IOpenedWindow, OpenContext } from 'vs/platform/windows/node/window'; +import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, CrashReporterStartOptions, crashReporter, Menu, BrowserWindow, app, clipboard, powerMonitor } from 'electron'; +import { OpenContext } from 'vs/platform/windows/node/window'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; -import { IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; -import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs'; -import { isMacintosh } from 'vs/base/common/platform'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IOpenedWindow, IOpenWindowOptions, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; +import { INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; +import { isMacintosh, isWindows, isRootUser } from 'vs/base/common/platform'; +import { ICommonElectronService } from 'vs/platform/electron/common/electron'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { AddFirstParameterToFunctions } from 'vs/base/common/types'; @@ -22,14 +22,16 @@ import { ITelemetryData, ITelemetryService } from 'vs/platform/telemetry/common/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { MouseInputEvent } from 'vs/base/parts/sandbox/common/electronTypes'; +import { totalmem } from 'os'; -export interface IElectronMainService extends AddFirstParameterToFunctions /* only methods, not events */, number | undefined /* window ID */> { } +export interface IElectronMainService extends AddFirstParameterToFunctions /* only methods, not events */, number | undefined /* window ID */> { } -export const IElectronMainService = createDecorator('electronMainService'); +export const IElectronMainService = createDecorator('electronMainService'); export class ElectronMainService implements IElectronMainService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @IWindowsMainService private readonly windowsMainService: IWindowsMainService, @@ -41,19 +43,27 @@ export class ElectronMainService implements IElectronMainService { ) { } + //#region Properties + + get windowId(): never { throw new Error('Not implemented in electron-main'); } + + //#endregion + //#region Events - readonly onWindowOpen: Event = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-created', (_, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); + readonly onWindowOpen = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-created', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); - readonly onWindowMaximize: Event = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-maximize', (_, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); - readonly onWindowUnmaximize: Event = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-unmaximize', (_, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); + readonly onWindowMaximize = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-maximize', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); + readonly onWindowUnmaximize = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-unmaximize', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); - readonly onWindowBlur: Event = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-blur', (_, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); - readonly onWindowFocus: Event = Event.any( + readonly onWindowBlur = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-blur', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); + readonly onWindowFocus = Event.any( Event.map(Event.filter(Event.map(this.windowsMainService.onWindowsCountChanged, () => this.windowsMainService.getLastActiveWindow()), window => !!window), window => window!.id), - Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-focus', (_, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)) + Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-focus', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)) ); + readonly onOSResume = Event.fromNodeEventEmitter(powerMonitor, 'resume'); + //#endregion //#region Window @@ -85,8 +95,8 @@ export class ElectronMainService implements IElectronMainService { } openWindow(windowId: number | undefined, options?: IOpenEmptyWindowOptions): Promise; - openWindow(windowId: number | undefined, toOpen: IWindowOpenable[], options?: INativeOpenWindowOptions): Promise; - openWindow(windowId: number | undefined, arg1?: IOpenEmptyWindowOptions | IWindowOpenable[], arg2?: INativeOpenWindowOptions): Promise { + openWindow(windowId: number | undefined, toOpen: IWindowOpenable[], options?: IOpenWindowOptions): Promise; + openWindow(windowId: number | undefined, arg1?: IOpenEmptyWindowOptions | IWindowOpenable[], arg2?: IOpenWindowOptions): Promise { if (Array.isArray(arg1)) { return this.doOpenWindow(windowId, arg1, arg2); } @@ -94,7 +104,7 @@ export class ElectronMainService implements IElectronMainService { return this.doOpenEmptyWindow(windowId, arg1); } - private async doOpenWindow(windowId: number | undefined, toOpen: IWindowOpenable[], options: INativeOpenWindowOptions = Object.create(null)): Promise { + private async doOpenWindow(windowId: number | undefined, toOpen: IWindowOpenable[], options: IOpenWindowOptions = Object.create(null)): Promise { if (toOpen.length > 0) { this.windowsMainService.open({ context: OpenContext.API, @@ -171,11 +181,7 @@ export class ElectronMainService implements IElectronMainService { const window = this.windowById(windowId); if (window) { - if (isMacintosh) { - window.win.show(); - } else { - window.win.focus(); - } + window.focus(); } } @@ -293,6 +299,67 @@ export class ElectronMainService implements IElectronMainService { } } + async moveItemToTrash(windowId: number | undefined, fullPath: string): Promise { + return shell.moveItemToTrash(fullPath); + } + + async isAdmin(): Promise { + let isAdmin: boolean; + if (isWindows) { + isAdmin = (await import('native-is-elevated'))(); + } else { + isAdmin = isRootUser(); + } + + return isAdmin; + } + + async getTotalMem(): Promise { + return totalmem(); + } + + //#endregion + + + //#region Process + + async killProcess(windowId: number | undefined, pid: number, code: string): Promise { + process.kill(pid, code); + } + + //#endregion + + + //#region clipboard + + async readClipboardText(windowId: number | undefined, type?: 'selection' | 'clipboard'): Promise { + return clipboard.readText(type); + } + + async writeClipboardText(windowId: number | undefined, text: string, type?: 'selection' | 'clipboard'): Promise { + return clipboard.writeText(text, type); + } + + async readClipboardFindText(windowId: number | undefined,): Promise { + return clipboard.readFindText(); + } + + async writeClipboardFindText(windowId: number | undefined, text: string): Promise { + return clipboard.writeFindText(text); + } + + async writeClipboardBuffer(windowId: number | undefined, format: string, buffer: Uint8Array, type?: 'selection' | 'clipboard'): Promise { + return clipboard.writeBuffer(format, Buffer.from(buffer), type); + } + + async readClipboardBuffer(windowId: number | undefined, format: string): Promise { + return clipboard.readBuffer(format); + } + + async hasClipboard(windowId: number | undefined, format: string, type?: 'selection' | 'clipboard'): Promise { + return clipboard.has(format, type); + } + //#endregion //#region macOS Touchbar @@ -325,6 +392,13 @@ export class ElectronMainService implements IElectronMainService { //#region Lifecycle + async notifyReady(windowId: number | undefined): Promise { + const window = this.windowById(windowId); + if (window) { + window.setReady(); + } + } + async relaunch(windowId: number | undefined, options?: { addArgs?: string[], removeArgs?: string[] }): Promise { return this.lifecycleMainService.relaunch(options); } @@ -364,6 +438,10 @@ export class ElectronMainService implements IElectronMainService { } } + async exit(windowId: number | undefined, code: number): Promise { + await this.lifecycleMainService.kill(code); + } + //#endregion //#region Connectivity @@ -402,8 +480,16 @@ export class ElectronMainService implements IElectronMainService { } async startCrashReporter(windowId: number | undefined, options: CrashReporterStartOptions): Promise { - crashReporter.start(options); this.logService.trace('ElectronMainService#crashReporter', JSON.stringify(options)); + + crashReporter.start(options); + } + + async sendInputEvent(windowId: number | undefined, event: MouseInputEvent): Promise { + const window = this.windowById(windowId); + if (window && (event.type === 'mouseDown' || event.type === 'mouseUp')) { + window.win.webContents.sendInputEvent(event); + } } //#endregion diff --git a/src/vs/platform/electron/electron-sandbox/electron.ts b/src/vs/platform/electron/electron-sandbox/electron.ts new file mode 100644 index 00000000000..25f193f372d --- /dev/null +++ b/src/vs/platform/electron/electron-sandbox/electron.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 { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ICommonElectronService } from 'vs/platform/electron/common/electron'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; + +export const IElectronService = createDecorator('electronService'); + +export interface IElectronService extends ICommonElectronService { } + +export class ElectronService { + + declare readonly _serviceBrand: undefined; + + constructor( + readonly windowId: number, + @IMainProcessService mainProcessService: IMainProcessService + ) { + return createChannelSender(mainProcessService.getChannel('electron'), { + context: windowId, + properties: (() => { + const properties = new Map(); + properties.set('windowId', windowId); + + return properties; + })() + }); + } +} diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 3627ab28552..6c660f87ed8 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -26,7 +26,7 @@ export interface IEnvironmentService { // UNLESS THIS PROPERTY IS SUPPORTED BOTH IN WEB AND DESKTOP!!!! // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - _serviceBrand: undefined; + readonly _serviceBrand: undefined; // --- user roaming data userRoamingDataHome: URI; @@ -40,6 +40,9 @@ export interface IEnvironmentService { backupHome: URI; untitledWorkspacesHome: URI; + globalStorageHome: URI; + workspaceStorageHome: URI; + // --- settings sync userDataSyncLogResource: URI; userDataSyncHome: URI; diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 61c2b16ad7a..2379b626c81 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as minimist from 'minimist'; -import * as os from 'os'; import { localize } from 'vs/nls'; +import { isWindows } from 'vs/base/common/platform'; export interface ParsedArgs { _: string[]; @@ -33,6 +33,7 @@ export interface ParsedArgs { trace?: boolean; 'trace-category-filter'?: string; 'trace-options'?: string; + 'open-devtools'?: boolean; log?: string; logExtensionHostCommunication?: boolean; 'extensions-dir'?: string; @@ -69,11 +70,13 @@ export interface ParsedArgs { 'file-chmod'?: boolean; 'driver'?: string; 'driver-verbose'?: boolean; - remote?: string; + 'remote'?: string; 'disable-user-env-probe'?: boolean; 'force'?: boolean; + 'do-not-sync'?: boolean; 'force-user-env'?: boolean; 'sync'?: 'on' | 'off'; + '__sandbox'?: boolean; // chromium command line args: https://electronjs.org/docs/all#supported-chrome-command-line-switches 'no-proxy-server'?: boolean; @@ -141,7 +144,7 @@ export const OPTIONS: OptionDescriptions> = { 'list-extensions': { type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") }, 'show-versions': { type: 'boolean', cat: 'e', description: localize('showVersions', "Show versions of installed extensions, when using --list-extension.") }, 'category': { type: 'string', cat: 'e', description: localize('category', "Filters installed extensions by provided category, when using --list-extension.") }, - 'install-extension': { type: 'string[]', cat: 'e', args: 'extension-id | path-to-vsix', description: localize('installExtension', "Installs or updates the extension. Use `--force` argument to avoid prompts.") }, + 'install-extension': { type: 'string[]', cat: 'e', args: 'extension-id[@version] | path-to-vsix', description: localize('installExtension', "Installs or updates the extension. Use `--force` argument to avoid prompts. The identifier of an extension is always `${publisher}.${name}`. To install a specific version provide `@${version}`. For example: 'vscode.csharp@1.2.3'.") }, 'uninstall-extension': { type: 'string[]', cat: 'e', args: 'extension-id', description: localize('uninstallExtension', "Uninstalls an extension.") }, 'enable-proposed-api': { type: 'string[]', cat: 'e', args: 'extension-id', description: localize('experimentalApis', "Enables proposed API features for extensions. Can receive one or more extension IDs to enable individually.") }, @@ -187,10 +190,13 @@ export const OPTIONS: OptionDescriptions> = { 'file-chmod': { type: 'boolean' }, 'driver-verbose': { type: 'boolean' }, 'force': { type: 'boolean' }, + 'do-not-sync': { type: 'boolean' }, 'trace': { type: 'boolean' }, 'trace-category-filter': { type: 'string' }, 'trace-options': { type: 'string' }, 'force-user-env': { type: 'boolean' }, + 'open-devtools': { type: 'boolean' }, + '__sandbox': { type: 'boolean' }, // chromium flags 'no-proxy-server': { type: 'boolean' }, @@ -254,7 +260,7 @@ export function parseArgs(args: string[], options: OptionDescriptions, err const remainingArgs: any = parsedArgs; // https://github.com/microsoft/vscode/issues/58177 - cleanedArgs._ = parsedArgs._.filter(arg => arg.length > 0); + cleanedArgs._ = parsedArgs._.filter(arg => String(arg).length > 0); delete remainingArgs._; @@ -362,7 +368,7 @@ export function buildHelpMessage(productName: string, executableName: string, ve help.push(`${localize('usage', "Usage")}: ${executableName} [${localize('options', "options")}][${localize('paths', 'paths')}...]`); help.push(''); if (isPipeSupported) { - if (os.platform() === 'win32') { + if (isWindows) { help.push(localize('stdinWindows', "To read output from another program, append '-' (e.g. 'echo Hello World | {0} -')", executableName)); } else { help.push(localize('stdinUnix', "To read from stdin, append '-' (e.g. 'ps aux | grep code | {0} -')", executableName)); diff --git a/src/vs/platform/environment/node/argvHelper.ts b/src/vs/platform/environment/node/argvHelper.ts index 11e54c68b8a..eda11907549 100644 --- a/src/vs/platform/environment/node/argvHelper.ts +++ b/src/vs/platform/environment/node/argvHelper.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { firstIndex } from 'vs/base/common/arrays'; import { localize } from 'vs/nls'; -import { MIN_MAX_MEMORY_SIZE_MB } from 'vs/platform/files/node/files'; +import { MIN_MAX_MEMORY_SIZE_MB } from 'vs/platform/files/common/files'; import { parseArgs, ErrorReporter, OPTIONS, ParsedArgs } from 'vs/platform/environment/node/argv'; function parseAndValidate(cmdLineArgs: string[], reportWarnings: boolean): ParsedArgs { diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 283b4b325b9..5c0dc4ad4ae 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -36,21 +36,20 @@ export interface INativeEnvironmentService extends IEnvironmentService { installSourcePath: string; extensionsPath?: string; - extensionsDownloadPath?: string; + extensionsDownloadPath: string; builtinExtensionsPath: string; - globalStorageHome: string; - workspaceStorageHome: string; - driverHandle?: string; driverVerbose: boolean; disableUpdates: boolean; + + sandbox: boolean; } export class EnvironmentService implements INativeEnvironmentService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; get args(): ParsedArgs { return this._args; } @@ -102,10 +101,10 @@ export class EnvironmentService implements INativeEnvironmentService { get machineSettingsResource(): URI { return resources.joinPath(URI.file(path.join(this.userDataPath, 'Machine')), 'settings.json'); } @memoize - get globalStorageHome(): string { return path.join(this.appSettingsHome.fsPath, 'globalStorage'); } + get globalStorageHome(): URI { return URI.joinPath(this.appSettingsHome, 'globalStorage'); } @memoize - get workspaceStorageHome(): string { return path.join(this.appSettingsHome.fsPath, 'workspaceStorage'); } + get workspaceStorageHome(): URI { return URI.joinPath(this.appSettingsHome, 'workspaceStorage'); } @memoize get keybindingsResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'keybindings.json'); } @@ -151,8 +150,13 @@ export class EnvironmentService implements INativeEnvironmentService { } } - get extensionsDownloadPath(): string | undefined { - return parsePathArg(this._args['extensions-download-dir'], process); + get extensionsDownloadPath(): string { + const fromArgs = parsePathArg(this._args['extensions-download-dir'], process); + if (fromArgs) { + return fromArgs; + } else { + return path.join(this.userDataPath, 'CachedExtensionVSIXs'); + } } @memoize @@ -260,6 +264,8 @@ export class EnvironmentService implements INativeEnvironmentService { get disableTelemetry(): boolean { return !!this._args['disable-telemetry']; } + get sandbox(): boolean { return !!this._args['__sandbox']; } + constructor(private _args: ParsedArgs, private _execPath: string) { if (!process.env['VSCODE_LOGS']) { const key = toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, ''); diff --git a/src/vs/platform/environment/node/stdin.ts b/src/vs/platform/environment/node/stdin.ts index e870ac6e704..a6b660643c6 100644 --- a/src/vs/platform/environment/node/stdin.ts +++ b/src/vs/platform/environment/node/stdin.ts @@ -46,13 +46,22 @@ export async function readFromStdin(targetPath: string, verbose: boolean): Promi let encoding = await resolveTerminalEncoding(verbose); - const iconv = await import('iconv-lite'); + const iconv = await import('iconv-lite-umd'); if (!iconv.encodingExists(encoding)) { console.log(`Unsupported terminal encoding: ${encoding}, falling back to UTF-8.`); encoding = 'utf8'; } // Pipe into tmp file using terminals encoding - const converterStream = iconv.decodeStream(encoding); - process.stdin.pipe(converterStream).pipe(stdinFileStream); + const decoder = iconv.getDecoder(encoding); + process.stdin.on('data', chunk => stdinFileStream.write(decoder.write(chunk))); + process.stdin.on('end', () => { + const end = decoder.end(); + if (typeof end === 'string') { + stdinFileStream.write(end); + } + stdinFileStream.end(); + }); + process.stdin.on('error', error => stdinFileStream.destroy(error)); + process.stdin.on('close', () => stdinFileStream.close()); } diff --git a/src/vs/platform/extensionManagement/common/configRemotes.ts b/src/vs/platform/extensionManagement/common/configRemotes.ts index dece01ab35a..b4d631be3f0 100644 --- a/src/vs/platform/extensionManagement/common/configRemotes.ts +++ b/src/vs/platform/extensionManagement/common/configRemotes.ts @@ -13,7 +13,7 @@ const SecondLevelDomainMatcher = /([^@:.]+\.[^@:.]+)(:\d+)?$/; const RemoteMatcher = /^\s*url\s*=\s*(.+\S)\s*$/mg; const AnyButDot = /[^.]/g; -export const SecondLevelDomainWhitelist = [ +export const AllowedSecondLevelDomains = [ 'github.com', 'bitbucket.org', 'visualstudio.com', @@ -54,7 +54,7 @@ function extractDomain(url: string): string | null { return null; } -export function getDomainsOfRemotes(text: string, whitelist: string[]): string[] { +export function getDomainsOfRemotes(text: string, allowedDomains: readonly string[]): string[] { const domains = new Set(); let match: RegExpExecArray | null; while (match = RemoteMatcher.exec(text)) { @@ -64,16 +64,9 @@ export function getDomainsOfRemotes(text: string, whitelist: string[]): string[] } } - const whitemap = whitelist.reduce((map, key) => { - map[key] = true; - return map; - }, Object.create(null)); - - const elements: string[] = []; - domains.forEach(e => elements.push(e)); - - return elements - .map(key => whitemap[key] ? key : key.replace(AnyButDot, 'a')); + const allowedDomainsSet = new Set(allowedDomains); + return Array.from(domains) + .map(key => allowedDomainsSet.has(key) ? key : key.replace(AnyButDot, 'a')); } function stripPort(authority: string): string | null { diff --git a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts index b3e6041730c..afb3cf6e68e 100644 --- a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts @@ -12,7 +12,7 @@ import { isUndefinedOrNull } from 'vs/base/common/types'; export class GlobalExtensionEnablementService extends Disposable implements IGlobalExtensionEnablementService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _onDidChangeEnablement = new Emitter<{ readonly extensions: IExtensionIdentifier[], readonly source?: string }>(); readonly onDidChangeEnablement: Event<{ readonly extensions: IExtensionIdentifier[], readonly source?: string }> = this._onDidChangeEnablement.event; diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index 25c274ca05f..52949788147 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -13,7 +13,6 @@ import { IRequestService, asJson, asText } from 'vs/platform/request/common/requ import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request'; import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { values } from 'vs/base/common/map'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ILogService } from 'vs/platform/log/common/log'; import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; @@ -163,7 +162,7 @@ class Query { withFilter(filterType: FilterType, ...values: string[]): Query { const criteria = [ ...this.state.criteria, - ...values.map(value => ({ filterType, value })) + ...values.length ? values.map(value => ({ filterType, value })) : [{ filterType }] ]; return new Query(assign({}, this.state, { criteria })); @@ -217,7 +216,7 @@ function getCoreTranslationAssets(version: IRawGalleryExtensionVersion): [string function getRepositoryAsset(version: IRawGalleryExtensionVersion): IGalleryExtensionAsset | null { if (version.properties) { const results = version.properties.filter(p => p.key === AssetType.Repository); - const gitRegExp = new RegExp('((git|ssh|http(s)?)|(git@[\w\.]+))(:(//)?)([\w\.@\:/\-~]+)(\.git)(/)?'); + const gitRegExp = new RegExp('((git|ssh|http(s)?)|(git@[\w.]+))(:(//)?)([\w.@\:/\-~]+)(.git)(/)?'); const uri = results.filter(r => gitRegExp.test(r.value))[0]; return uri ? { uri: uri.value, fallbackUri: uri.value } : null; @@ -295,6 +294,8 @@ function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGaller installCount: getStatistic(galleryExtension.statistics, 'install'), rating: getStatistic(galleryExtension.statistics, 'averagerating'), ratingCount: getStatistic(galleryExtension.statistics, 'ratingcount'), + assetUri: URI.parse(version.assetUri), + assetTypes: version.files.map(({ assetType }) => assetType), assets, properties: { dependencies: getExtensions(version, PropertyType.Dependency), @@ -325,7 +326,7 @@ interface IRawExtensionsReport { export class ExtensionGalleryService implements IExtensionGalleryService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private extensionsGalleryUrl: string | undefined; private extensionsControlUrl: string | undefined; @@ -362,10 +363,9 @@ export class ExtensionGalleryService implements IExtensionGalleryService { } const { id, uuid } = extension ? extension.identifier : arg1; let query = new Query() - .withFlags(Flags.IncludeAssetUri, Flags.IncludeStatistics, Flags.IncludeFiles, Flags.IncludeVersionProperties, Flags.ExcludeNonValidated) + .withFlags(Flags.IncludeAssetUri, Flags.IncludeStatistics, Flags.IncludeFiles, Flags.IncludeVersionProperties) .withPage(1, 1) - .withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code') - .withFilter(FilterType.ExcludeWithFlags, flagsToString(Flags.Unpublished)); + .withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code'); if (uuid) { query = query.withFilter(FilterType.ExtensionId, uuid); @@ -426,8 +426,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { let query = new Query() .withFlags(Flags.IncludeLatestVersionOnly, Flags.IncludeAssetUri, Flags.IncludeStatistics, Flags.IncludeFiles, Flags.IncludeVersionProperties) .withPage(1, pageSize) - .withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code') - .withFilter(FilterType.ExcludeWithFlags, flagsToString(Flags.Unpublished)); + .withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code'); if (text) { // Use category filter instead of "category:themes" @@ -442,6 +441,12 @@ export class ExtensionGalleryService implements IExtensionGalleryService { return ''; }); + // Use featured filter + text = text.replace(/\bfeatured(\s+|\b|$)/g, () => { + query = query.withFilter(FilterType.Featured); + return ''; + }); + text = text.trim(); if (text) { @@ -484,6 +489,11 @@ export class ExtensionGalleryService implements IExtensionGalleryService { } private queryGallery(query: Query, token: CancellationToken): Promise<{ galleryExtensions: IRawGalleryExtension[], total: number; }> { + // Always exclude non validated and unpublished extensions + query = query + .withFlags(query.flags, Flags.ExcludeNonValidated) + .withFilter(FilterType.ExcludeWithFlags, flagsToString(Flags.Unpublished)); + if (!this.isEnabled()) { return Promise.reject(new Error('No extension gallery service configured.')); } @@ -600,12 +610,11 @@ export class ExtensionGalleryService implements IExtensionGalleryService { return Promise.resolve(''); } - getAllVersions(extension: IGalleryExtension, compatible: boolean): Promise { + async getAllVersions(extension: IGalleryExtension, compatible: boolean): Promise { let query = new Query() - .withFlags(Flags.IncludeVersions, Flags.IncludeFiles, Flags.IncludeVersionProperties, Flags.ExcludeNonValidated) + .withFlags(Flags.IncludeVersions, Flags.IncludeFiles, Flags.IncludeVersionProperties) .withPage(1, 1) - .withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code') - .withFilter(FilterType.ExcludeWithFlags, flagsToString(Flags.Unpublished)); + .withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code'); if (extension.identifier.uuid) { query = query.withFilter(FilterType.ExtensionId, extension.identifier.uuid); @@ -613,19 +622,24 @@ export class ExtensionGalleryService implements IExtensionGalleryService { query = query.withFilter(FilterType.ExtensionName, extension.identifier.id); } - return this.queryGallery(query, CancellationToken.None).then(({ galleryExtensions }) => { - if (galleryExtensions.length) { - if (compatible) { - return Promise.all(galleryExtensions[0].versions.map(v => this.getEngine(v).then(engine => isEngineValid(engine, this.productService.version) ? v : null))) - .then(versions => versions - .filter(v => !!v) - .map(v => ({ version: v!.version, date: v!.lastUpdated }))); - } else { - return galleryExtensions[0].versions.map(v => ({ version: v.version, date: v.lastUpdated })); - } + const result: IGalleryExtensionVersion[] = []; + const { galleryExtensions } = await this.queryGallery(query, CancellationToken.None); + if (galleryExtensions.length) { + if (compatible) { + await Promise.all(galleryExtensions[0].versions.map(async v => { + let engine: string | undefined; + try { + engine = await this.getEngine(v); + } catch (error) { /* Ignore error and skip version */ } + if (engine && isEngineValid(engine, this.productService.version)) { + result.push({ version: v!.version, date: v!.lastUpdated }); + } + })); + } else { + result.push(...galleryExtensions[0].versions.map(v => ({ version: v.version, date: v.lastUpdated }))); } - return []; - }); + } + return result; } private getAsset(asset: IGalleryExtensionAsset, options: IRequestOptions = {}, token: CancellationToken = CancellationToken.None): Promise { @@ -750,7 +764,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { } } - return Promise.resolve(values(map)); + return [...map.values()]; }); }); } diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index f26af4def98..34811829b09 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -10,9 +10,8 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { URI } from 'vs/base/common/uri'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IExtensionManifest, IExtension, ExtensionType } from 'vs/platform/extensions/common/extensions'; -import { IExeBasedExtensionTip } from 'vs/platform/product/common/productService'; -export const EXTENSION_IDENTIFIER_PATTERN = '^([a-z0-9A-Z][a-z0-9\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\-A-Z]*)$'; +export const EXTENSION_IDENTIFIER_PATTERN = '^([a-z0-9A-Z][a-z0-9-A-Z]*)\\.([a-z0-9A-Z][a-z0-9-A-Z]*)$'; export const EXTENSION_IDENTIFIER_REGEX = new RegExp(EXTENSION_IDENTIFIER_PATTERN); export interface IGalleryExtensionProperties { @@ -78,6 +77,8 @@ export interface IGalleryExtension { installCount: number; rating: number; ratingCount: number; + assetUri: URI; + assetTypes: string[]; assets: IGalleryExtensionAssets; properties: IGalleryExtensionProperties; telemetryData: any; @@ -91,15 +92,11 @@ export interface IGalleryMetadata { } export interface ILocalExtension extends IExtension { - readonly manifest: IExtensionManifest; - metadata: IGalleryMetadata; - readmeUrl: URI | null; - changelogUrl: URI | null; + isMachineScoped: boolean; + publisherId: string | null; + publisherDisplayName: string | null; } -export const IExtensionManagementService = createDecorator('extensionManagementService'); -export const IExtensionGalleryService = createDecorator('extensionGalleryService'); - export const enum SortBy { NoneOrRelevance = 0, LastUpdatedDate = 1, @@ -146,8 +143,9 @@ export interface ITranslation { contents: { [key: string]: {} }; } +export const IExtensionGalleryService = createDecorator('extensionGalleryService'); export interface IExtensionGalleryService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; isEnabled(): boolean; query(token: CancellationToken): Promise>; query(options: IQueryOptions, token: CancellationToken): Promise>; @@ -187,8 +185,15 @@ export const INSTALL_ERROR_NOT_SUPPORTED = 'notsupported'; export const INSTALL_ERROR_MALICIOUS = 'malicious'; export const INSTALL_ERROR_INCOMPATIBLE = 'incompatible'; +export class ExtensionManagementError extends Error { + constructor(message: string, readonly code: string) { + super(message); + } +} + +export const IExtensionManagementService = createDecorator('extensionManagementService'); export interface IExtensionManagementService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; onInstallExtension: Event; onDidInstallExtension: Event; @@ -196,10 +201,10 @@ export interface IExtensionManagementService { onDidUninstallExtension: Event; zip(extension: ILocalExtension): Promise; - unzip(zipLocation: URI, type: ExtensionType): Promise; + unzip(zipLocation: URI): Promise; getManifest(vsix: URI): Promise; - install(vsix: URI): Promise; - installFromGallery(extension: IGalleryExtension): Promise; + install(vsix: URI, isMachineScoped?: boolean): Promise; + installFromGallery(extension: IGalleryExtension, isMachineScoped?: boolean): Promise; uninstall(extension: ILocalExtension, force?: boolean): Promise; reinstallFromGallery(extension: ILocalExtension): Promise; getInstalled(type?: ExtensionType): Promise; @@ -213,7 +218,7 @@ export const ENABLED_EXTENSIONS_STORAGE_PATH = 'extensionsIdentifiers/enabled'; export const IGlobalExtensionEnablementService = createDecorator('IGlobalExtensionEnablementService'); export interface IGlobalExtensionEnablementService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly onDidChangeEnablement: Event<{ readonly extensions: IExtensionIdentifier[], readonly source?: string }>; getDisabledExtensions(): IExtensionIdentifier[]; @@ -229,12 +234,20 @@ export type IConfigBasedExtensionTip = { readonly configName: string, readonly important: boolean, }; -export type IExecutableBasedExtensionTip = { extensionId: string } & Omit, 'important'>; + +export type IExecutableBasedExtensionTip = { + readonly extensionId: string, + readonly extensionName: string, + readonly isExtensionPack: boolean, + readonly exeFriendlyName: string, + readonly windowsPath?: string, +}; + export type IWorkspaceTips = { readonly remoteSet: string[]; readonly recommendations: string[]; }; export const IExtensionTipsService = createDecorator('IExtensionTipsService'); export interface IExtensionTipsService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; getConfigBasedTips(folder: URI): Promise; getImportantExecutableBasedTips(): Promise; diff --git a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts index 23efae34eef..d497780449a 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts @@ -60,7 +60,7 @@ export class ExtensionManagementChannel implements IServerChannel { const uriTransformer: IURITransformer | null = this.getUriTransformer(context); switch (command) { case 'zip': return this.service.zip(transformIncomingExtension(args[0], uriTransformer)).then(uri => transformOutgoingURI(uri, uriTransformer)); - case 'unzip': return this.service.unzip(transformIncomingURI(args[0], uriTransformer), args[1]); + case 'unzip': return this.service.unzip(transformIncomingURI(args[0], uriTransformer)); case 'install': return this.service.install(transformIncomingURI(args[0], uriTransformer)); case 'getManifest': return this.service.getManifest(transformIncomingURI(args[0], uriTransformer)); case 'installFromGallery': return this.service.installFromGallery(args[0]); @@ -77,7 +77,7 @@ export class ExtensionManagementChannel implements IServerChannel { export class ExtensionManagementChannelClient implements IExtensionManagementService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( private readonly channel: IChannel, @@ -92,8 +92,8 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer return Promise.resolve(this.channel.call('zip', [extension]).then(result => URI.revive(result))); } - unzip(zipLocation: URI, type: ExtensionType): Promise { - return Promise.resolve(this.channel.call('unzip', [zipLocation, type])); + unzip(zipLocation: URI): Promise { + return Promise.resolve(this.channel.call('unzip', [zipLocation])); } install(vsix: URI): Promise { diff --git a/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts b/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts index bad92e00651..fef60bf20b8 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts @@ -69,9 +69,9 @@ export function getLocalExtensionTelemetryData(extension: ILocalExtension): any id: extension.identifier.id, name: extension.manifest.name, galleryId: null, - publisherId: extension.metadata ? extension.metadata.publisherId : null, + publisherId: extension.publisherId, publisherName: extension.manifest.publisher, - publisherDisplayName: extension.metadata ? extension.metadata.publisherDisplayName : null, + publisherDisplayName: extension.publisherDisplayName, dependencies: extension.manifest.extensionDependencies && extension.manifest.extensionDependencies.length > 0 }; } @@ -116,4 +116,4 @@ export function getMaliciousExtensionsSet(report: IReportedExtension[]): Set result.push(recommendationByRemote.get(remote)!)); + const domains = getDomainsOfRemotes(content.value.toString(), [...recommendationByRemote.keys()]); + for (const domain of domains) { + const remote = recommendationByRemote.get(domain); + if (remote) { + result.push(remote); + } + } } catch (error) { /* Ignore */ } } return result; diff --git a/src/vs/platform/extensionManagement/node/extensionDownloader.ts b/src/vs/platform/extensionManagement/node/extensionDownloader.ts index bde78e8d80b..62f1a290444 100644 --- a/src/vs/platform/extensionManagement/node/extensionDownloader.ts +++ b/src/vs/platform/extensionManagement/node/extensionDownloader.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { tmpdir } from 'os'; import { Disposable } from 'vs/base/common/lifecycle'; import { IFileService, IFileStatWithMetadata } from 'vs/platform/files/common/files'; import { IExtensionGalleryService, IGalleryExtension, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -20,9 +19,9 @@ const ExtensionIdVersionRegex = /^([^.]+\..+)-(\d+\.\d+\.\d+)$/; export class ExtensionsDownloader extends Disposable { - private readonly extensionsDownloadDir: URI = URI.file(tmpdir()); - private readonly cache: number = 0; - private readonly cleanUpPromise: Promise = Promise.resolve(); + private readonly extensionsDownloadDir: URI; + private readonly cache: number; + private readonly cleanUpPromise: Promise; constructor( @IEnvironmentService environmentService: INativeEnvironmentService, @@ -31,11 +30,9 @@ export class ExtensionsDownloader extends Disposable { @ILogService private readonly logService: ILogService, ) { super(); - if (environmentService.extensionsDownloadPath) { - this.extensionsDownloadDir = URI.file(environmentService.extensionsDownloadPath); - this.cache = 20; // Cache 20 downloads - this.cleanUpPromise = this.cleanUp(); - } + this.extensionsDownloadDir = URI.file(environmentService.extensionsDownloadPath); + this.cache = 20; // Cache 20 downloads + this.cleanUpPromise = this.cleanUp(); } async downloadExtension(extension: IGalleryExtension, operation: InstallOperation): Promise { @@ -46,10 +43,7 @@ export class ExtensionsDownloader extends Disposable { } async delete(location: URI): Promise { - // Delete immediately if caching is disabled - if (!this.cache) { - await this.fileService.del(location); - } + // noop as caching is enabled always } private async download(extension: IGalleryExtension, location: URI, operation: InstallOperation): Promise { diff --git a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts index 9b561c030f2..4a9e5f8d4af 100644 --- a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts +++ b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts @@ -14,14 +14,15 @@ import { Event } from 'vs/base/common/event'; import { Schemas } from 'vs/base/common/network'; import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { rimraf } from 'vs/base/node/pfs'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; export class ExtensionsLifecycle extends Disposable { private processesLimiter: Limiter = new Limiter(5); // Run max 5 processes in parallel constructor( - private environmentService: INativeEnvironmentService, - private logService: ILogService + @IEnvironmentService private environmentService: INativeEnvironmentService, + @ILogService private readonly logService: ILogService ) { super(); } @@ -130,6 +131,6 @@ export class ExtensionsLifecycle extends Disposable { } private getExtensionStoragePath(extension: ILocalExtension): string { - return join(this.environmentService.globalStorageHome, extension.identifier.id.toLowerCase()); + return join(this.environmentService.globalStorageHome.fsPath, extension.identifier.id.toLowerCase()); } } diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 7d466be263d..bfd0fbd7877 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -8,8 +8,8 @@ import * as path from 'vs/base/common/path'; import * as pfs from 'vs/base/node/pfs'; import { assign } from 'vs/base/common/objects'; import { toDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { flatten, isNonEmptyArray } from 'vs/base/common/arrays'; -import { extract, ExtractError, zip, IFile } from 'vs/base/node/zip'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; +import { zip, IFile } from 'vs/base/node/zip'; import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IGalleryMetadata, @@ -19,21 +19,20 @@ import { IReportedExtension, InstallOperation, INSTALL_ERROR_MALICIOUS, - INSTALL_ERROR_INCOMPATIBLE + INSTALL_ERROR_INCOMPATIBLE, + ExtensionManagementError } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, ExtensionIdentifierWithVersion } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { localizeManifest } from '../common/extensionNls'; +import { areSameExtensions, getGalleryExtensionId, getMaliciousExtensionsSet, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, ExtensionIdentifierWithVersion } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { Limiter, createCancelablePromise, CancelablePromise, Queue } from 'vs/base/common/async'; +import { createCancelablePromise, CancelablePromise } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; import * as semver from 'semver-umd'; import { URI } from 'vs/base/common/uri'; import product from 'vs/platform/product/common/product'; -import { isMacintosh, isWindows } from 'vs/base/common/platform'; +import { isMacintosh } from 'vs/base/common/platform'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtensionsManifestCache } from 'vs/platform/extensionManagement/node/extensionsManifestCache'; -import { ExtensionsLifecycle } from 'vs/platform/extensionManagement/node/extensionLifecycle'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator'; @@ -43,79 +42,35 @@ import { IDownloadService } from 'vs/platform/download/common/download'; import { optional, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Schemas } from 'vs/base/common/network'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; import { IExtensionManifest, ExtensionType } from 'vs/platform/extensions/common/extensions'; import { ExtensionsDownloader } from 'vs/platform/extensionManagement/node/extensionDownloader'; +import { ExtensionsScanner, IMetadata } from 'vs/platform/extensionManagement/node/extensionsScanner'; +import { ExtensionsLifecycle } from 'vs/platform/extensionManagement/node/extensionLifecycle'; -const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem'; -const ERROR_SCANNING_USER_EXTENSIONS = 'scanningUser'; const INSTALL_ERROR_UNSET_UNINSTALLED = 'unsetUninstalled'; const INSTALL_ERROR_DOWNLOADING = 'downloading'; const INSTALL_ERROR_VALIDATING = 'validating'; const INSTALL_ERROR_LOCAL = 'local'; -const INSTALL_ERROR_EXTRACTING = 'extracting'; -const INSTALL_ERROR_RENAMING = 'renaming'; -const INSTALL_ERROR_DELETING = 'deleting'; const ERROR_UNKNOWN = 'unknown'; -export class ExtensionManagementError extends Error { - constructor(message: string, readonly code: string) { - super(message); - } -} - -function parseManifest(raw: string): Promise<{ manifest: IExtensionManifest; metadata: IGalleryMetadata; }> { - return new Promise((c, e) => { - try { - const manifest = JSON.parse(raw); - const metadata = manifest.__metadata || null; - delete manifest.__metadata; - c({ manifest, metadata }); - } catch (err) { - e(new Error(nls.localize('invalidManifest', "Extension invalid: package.json is not a JSON file."))); - } - }); -} - -function readManifest(extensionPath: string): Promise<{ manifest: IExtensionManifest; metadata: IGalleryMetadata; }> { - const promises = [ - pfs.readFile(path.join(extensionPath, 'package.json'), 'utf8') - .then(raw => parseManifest(raw)), - pfs.readFile(path.join(extensionPath, 'package.nls.json'), 'utf8') - .then(undefined, err => err.code !== 'ENOENT' ? Promise.reject(err) : '{}') - .then(raw => JSON.parse(raw)) - ]; - - return Promise.all(promises).then(([{ manifest, metadata }, translations]) => { - return { - manifest: localizeManifest(manifest, translations), - metadata - }; - }); -} - interface InstallableExtension { zipPath: string; identifierWithVersion: ExtensionIdentifierWithVersion; - metadata: IGalleryMetadata | null; + metadata?: IMetadata; } export class ExtensionManagementService extends Disposable implements IExtensionManagementService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; - private systemExtensionsPath: string; - private extensionsPath: string; - private uninstalledPath: string; - private uninstalledFileLimiter: Queue; + private readonly extensionsScanner: ExtensionsScanner; private reportedExtensions: Promise | undefined; private lastReportTimestamp = 0; private readonly installingExtensions: Map> = new Map>(); private readonly uninstallingExtensions: Map> = new Map>(); private readonly manifestCache: ExtensionsManifestCache; private readonly extensionsDownloader: ExtensionsDownloader; - private readonly extensionLifecycle: ExtensionsLifecycle; private readonly _onInstallExtension = this._register(new Emitter()); readonly onInstallExtension: Event = this._onInstallExtension.event; @@ -130,7 +85,7 @@ export class ExtensionManagementService extends Disposable implements IExtension onDidUninstallExtension: Event = this._onDidUninstallExtension.event; constructor( - @IEnvironmentService private readonly environmentService: INativeEnvironmentService, + @IEnvironmentService environmentService: INativeEnvironmentService, @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, @ILogService private readonly logService: ILogService, @optional(IDownloadService) private downloadService: IDownloadService, @@ -138,13 +93,10 @@ export class ExtensionManagementService extends Disposable implements IExtension @IInstantiationService instantiationService: IInstantiationService, ) { super(); - this.systemExtensionsPath = environmentService.builtinExtensionsPath; - this.extensionsPath = environmentService.extensionsPath!; - this.uninstalledPath = path.join(this.extensionsPath, '.obsolete'); - this.uninstalledFileLimiter = new Queue(); + const extensionLifecycle = this._register(instantiationService.createInstance(ExtensionsLifecycle)); + this.extensionsScanner = this._register(instantiationService.createInstance(ExtensionsScanner, extension => extensionLifecycle.postUninstall(extension))); this.manifestCache = this._register(new ExtensionsManifestCache(environmentService, this)); this.extensionsDownloader = this._register(instantiationService.createInstance(ExtensionsDownloader)); - this.extensionLifecycle = this._register(new ExtensionsLifecycle(environmentService, this.logService)); this._register(toDisposable(() => { this.installingExtensions.forEach(promise => promise.cancel()); @@ -161,9 +113,9 @@ export class ExtensionManagementService extends Disposable implements IExtension .then(path => URI.file(path)); } - unzip(zipLocation: URI, type: ExtensionType): Promise { + unzip(zipLocation: URI): Promise { this.logService.trace('ExtensionManagementService#unzip', zipLocation.toString()); - return this.install(zipLocation, type).then(local => local.identifier); + return this.install(zipLocation).then(local => local.identifier); } async getManifest(vsix: URI): Promise { @@ -198,7 +150,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } - install(vsix: URI, type: ExtensionType = ExtensionType.User): Promise { + install(vsix: URI, isMachineScoped?: boolean): Promise { this.logService.trace('ExtensionManagementService#install', vsix.toString()); return createCancelablePromise(token => { return this.downloadVsix(vsix).then(downloadLocation => { @@ -216,11 +168,12 @@ export class ExtensionManagementService extends Disposable implements IExtension .then(installedExtensions => { const existing = installedExtensions.filter(i => areSameExtensions(identifier, i.identifier))[0]; if (existing) { + isMachineScoped = isMachineScoped || existing.isMachineScoped; operation = InstallOperation.Update; if (identifierWithVersion.equals(new ExtensionIdentifierWithVersion(existing.identifier, existing.manifest.version))) { - return this.removeExtension(existing, 'existing').then(null, e => Promise.reject(new Error(nls.localize('restartCode', "Please restart VS Code before reinstalling {0}.", manifest.displayName || manifest.name)))); + return this.extensionsScanner.removeExtension(existing, 'existing').then(null, e => Promise.reject(new Error(nls.localize('restartCode', "Please restart VS Code before reinstalling {0}.", manifest.displayName || manifest.name)))); } else if (semver.gt(existing.manifest.version, manifest.version)) { - return this.uninstall(existing, true); + return this.uninstallExtension(existing); } } else { // Remove the extension with same version if it is already uninstalled. @@ -228,7 +181,7 @@ export class ExtensionManagementService extends Disposable implements IExtension return this.unsetUninstalledAndGetLocal(identifierWithVersion) .then(existing => { if (existing) { - return this.removeExtension(existing, 'existing').then(null, e => Promise.reject(new Error(nls.localize('restartCode', "Please restart VS Code before reinstalling {0}.", manifest.displayName || manifest.name)))); + return this.extensionsScanner.removeExtension(existing, 'existing').then(null, e => Promise.reject(new Error(nls.localize('restartCode', "Please restart VS Code before reinstalling {0}.", manifest.displayName || manifest.name)))); } return undefined; }); @@ -238,10 +191,10 @@ export class ExtensionManagementService extends Disposable implements IExtension .then(() => { this.logService.info('Installing the extension:', identifier.id); this._onInstallExtension.fire({ identifier, zipPath }); - return this.getMetadata(getGalleryExtensionId(manifest.publisher, manifest.name)) + return this.getGalleryMetadata(getGalleryExtensionId(manifest.publisher, manifest.name)) .then( - metadata => this.installFromZipPath(identifierWithVersion, zipPath, metadata, type, operation, token), - () => this.installFromZipPath(identifierWithVersion, zipPath, null, type, operation, token)) + metadata => this.installFromZipPath(identifierWithVersion, zipPath, { ...metadata, isMachineScoped }, operation, token), + () => this.installFromZipPath(identifierWithVersion, zipPath, isMachineScoped ? { isMachineScoped } : undefined, operation, token)) .then( local => { this.logService.info('Successfully installed the extension:', identifier.id); return local; }, e => { @@ -265,9 +218,9 @@ export class ExtensionManagementService extends Disposable implements IExtension return this.downloadService.download(vsix, URI.file(downloadedLocation)).then(() => URI.file(downloadedLocation)); } - private installFromZipPath(identifierWithVersion: ExtensionIdentifierWithVersion, zipPath: string, metadata: IGalleryMetadata | null, type: ExtensionType, operation: InstallOperation, token: CancellationToken): Promise { - return this.toNonCancellablePromise(this.installExtension({ zipPath, identifierWithVersion, metadata }, type, token) - .then(local => this.installDependenciesAndPackExtensions(local, null) + private installFromZipPath(identifierWithVersion: ExtensionIdentifierWithVersion, zipPath: string, metadata: IMetadata | undefined, operation: InstallOperation, token: CancellationToken): Promise { + return this.toNonCancellablePromise(this.installExtension({ zipPath, identifierWithVersion, metadata }, token) + .then(local => this.installDependenciesAndPackExtensions(local, undefined) .then( () => local, error => { @@ -285,7 +238,7 @@ export class ExtensionManagementService extends Disposable implements IExtension )); } - async installFromGallery(extension: IGalleryExtension): Promise { + async installFromGallery(extension: IGalleryExtension, isMachineScoped?: boolean): Promise { if (!this.galleryService.isEnabled()) { return Promise.reject(new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled"))); } @@ -327,16 +280,19 @@ export class ExtensionManagementService extends Disposable implements IExtension this.installingExtensions.set(key, cancellablePromise); try { const installed = await this.getInstalled(ExtensionType.User); - const existingExtension = installed.filter(i => areSameExtensions(i.identifier, extension.identifier))[0]; + const existingExtension = installed.find(i => areSameExtensions(i.identifier, extension.identifier)); if (existingExtension) { operation = InstallOperation.Update; } this.downloadInstallableExtension(extension, operation) - .then(installableExtension => this.installExtension(installableExtension, ExtensionType.User, cancellationToken) - .then(local => this.extensionsDownloader.delete(URI.file(installableExtension.zipPath)).finally(() => { }).then(() => local))) + .then(installableExtension => { + installableExtension.metadata.isMachineScoped = isMachineScoped || existingExtension?.isMachineScoped; + return this.installExtension(installableExtension, cancellationToken) + .then(local => this.extensionsDownloader.delete(URI.file(installableExtension.zipPath)).finally(() => { }).then(() => local)); + }) .then(local => this.installDependenciesAndPackExtensions(local, existingExtension) - .then(() => local, error => this.uninstall(local, true).then(() => Promise.reject(error), () => Promise.reject(error)))) + .then(() => local, error => this.uninstall(local).then(() => Promise.reject(error), () => Promise.reject(error)))) .then( async local => { if (existingExtension && semver.neq(existingExtension.manifest.version, extension.version)) { @@ -386,7 +342,7 @@ export class ExtensionManagementService extends Disposable implements IExtension .then(galleryExtension => { if (galleryExtension) { return this.setUninstalled(extension) - .then(() => this.removeUninstalledExtension(extension) + .then(() => this.extensionsScanner.removeUninstalledExtension(extension) .then( () => this.installFromGallery(galleryExtension).then(), e => Promise.reject(new Error(nls.localize('removeError', "Error while removing the extension: {0}. Please Quit and Start VS Code before trying again.", toErrorMessage(e)))))); @@ -404,7 +360,7 @@ export class ExtensionManagementService extends Disposable implements IExtension .then(report => getMaliciousExtensionsSet(report).has(extension.identifier.id)); } - private downloadInstallableExtension(extension: IGalleryExtension, operation: InstallOperation): Promise { + private downloadInstallableExtension(extension: IGalleryExtension, operation: InstallOperation): Promise> { const metadata = { id: extension.identifier.uuid, publisherId: extension.publisherId, @@ -419,21 +375,21 @@ export class ExtensionManagementService extends Disposable implements IExtension this.logService.info('Downloaded extension:', extension.identifier.id, zipPath); return getManifest(zipPath) .then( - manifest => ({ zipPath, identifierWithVersion: new ExtensionIdentifierWithVersion(extension.identifier, manifest.version), metadata }), + manifest => (>{ zipPath, identifierWithVersion: new ExtensionIdentifierWithVersion(extension.identifier, manifest.version), metadata }), error => Promise.reject(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_VALIDATING)) ); }, error => Promise.reject(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_DOWNLOADING))); } - private installExtension(installableExtension: InstallableExtension, type: ExtensionType, token: CancellationToken): Promise { + private installExtension(installableExtension: InstallableExtension, token: CancellationToken): Promise { return this.unsetUninstalledAndGetLocal(installableExtension.identifierWithVersion) .then( local => { if (local) { return local; } - return this.extractAndInstall(installableExtension, type, token); + return this.extractAndInstall(installableExtension, token); }, e => { if (isMacintosh) { @@ -460,63 +416,17 @@ export class ExtensionManagementService extends Disposable implements IExtension }); } - private extractAndInstall({ zipPath, identifierWithVersion, metadata }: InstallableExtension, type: ExtensionType, token: CancellationToken): Promise { + private async extractAndInstall({ zipPath, identifierWithVersion, metadata }: InstallableExtension, token: CancellationToken): Promise { const { identifier } = identifierWithVersion; - const location = type === ExtensionType.User ? this.extensionsPath : this.systemExtensionsPath; - const folderName = identifierWithVersion.key(); - const tempPath = path.join(location, `.${folderName}`); - const extensionPath = path.join(location, folderName); - return pfs.rimraf(extensionPath) - .then(() => this.extractAndRename(identifier, zipPath, tempPath, extensionPath, token), e => Promise.reject(new ExtensionManagementError(nls.localize('errorDeleting', "Unable to delete the existing folder '{0}' while installing the extension '{1}'. Please delete the folder manually and try again", extensionPath, identifier.id), INSTALL_ERROR_DELETING))) - .then(() => this.scanExtension(folderName, location, type)) - .then(local => { - if (!local) { - return Promise.reject(nls.localize('cannot read', "Cannot read the extension from {0}", location)); - } - this.logService.info('Installation completed.', identifier.id); - if (metadata) { - this.setMetadata(local, metadata); - return this.saveMetadataForLocalExtension(local); - } - return local; - }, error => pfs.rimraf(extensionPath).then(() => Promise.reject(error), () => Promise.reject(error))); + let local = await this.extensionsScanner.extractUserExtension(identifierWithVersion, zipPath, token); + this.logService.info('Installation completed.', identifier.id); + if (metadata) { + local = await this.extensionsScanner.saveMetadataForLocalExtension(local, metadata); + } + return local; } - private extractAndRename(identifier: IExtensionIdentifier, zipPath: string, extractPath: string, renamePath: string, token: CancellationToken): Promise { - return this.extract(identifier, zipPath, extractPath, token) - .then(() => this.rename(identifier, extractPath, renamePath, Date.now() + (2 * 60 * 1000) /* Retry for 2 minutes */) - .then( - () => this.logService.info('Renamed to', renamePath), - e => { - this.logService.info('Rename failed. Deleting from extracted location', extractPath); - return pfs.rimraf(extractPath).finally(() => { }).then(() => Promise.reject(e)); - })); - } - - private extract(identifier: IExtensionIdentifier, zipPath: string, extractPath: string, token: CancellationToken): Promise { - this.logService.trace(`Started extracting the extension from ${zipPath} to ${extractPath}`); - return pfs.rimraf(extractPath) - .then( - () => extract(zipPath, extractPath, { sourcePath: 'extension', overwrite: true }, token) - .then( - () => this.logService.info(`Extracted extension to ${extractPath}:`, identifier.id), - e => pfs.rimraf(extractPath).finally(() => { }) - .then(() => Promise.reject(new ExtensionManagementError(e.message, e instanceof ExtractError && e.type ? e.type : INSTALL_ERROR_EXTRACTING)))), - e => Promise.reject(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_DELETING))); - } - - private rename(identifier: IExtensionIdentifier, extractPath: string, renamePath: string, retryUntil: number): Promise { - return pfs.rename(extractPath, renamePath) - .then(undefined, error => { - if (isWindows && error && error.code === 'EPERM' && Date.now() < retryUntil) { - this.logService.info(`Failed renaming ${extractPath} to ${renamePath} with 'EPERM' error. Trying again...`, identifier.id); - return this.rename(identifier, extractPath, renamePath, retryUntil); - } - return Promise.reject(new ExtensionManagementError(error.message || nls.localize('renameError', "Unknown error while renaming {0} to {1}", extractPath, renamePath), error.code || INSTALL_ERROR_RENAMING)); - }); - } - - private async installDependenciesAndPackExtensions(installed: ILocalExtension, existing: ILocalExtension | null): Promise { + private async installDependenciesAndPackExtensions(installed: ILocalExtension, existing: ILocalExtension | undefined): Promise { if (this.galleryService.isEnabled()) { const dependenciesAndPackExtensions: string[] = installed.manifest.extensionDependencies || []; if (installed.manifest.extensionPack) { @@ -553,11 +463,11 @@ export class ExtensionManagementService extends Disposable implements IExtension return this.getInstalled(ExtensionType.User) .then(installed => Promise.all(installed.filter(local => extensions.some(galleryExtension => new ExtensionIdentifierWithVersion(local.identifier, local.manifest.version).equals(new ExtensionIdentifierWithVersion(galleryExtension.identifier, galleryExtension.version)))) // Check with version because we want to rollback the exact version - .map(local => this.uninstall(local, true)))) + .map(local => this.uninstall(local)))) .then(() => undefined, () => undefined); } - uninstall(extension: ILocalExtension, force = false): Promise { + uninstall(extension: ILocalExtension): Promise { this.logService.trace('ExtensionManagementService#uninstall', extension.identifier.id); return this.toNonCancellablePromise(this.getInstalled(ExtensionType.User) .then(installed => { @@ -570,31 +480,16 @@ export class ExtensionManagementService extends Disposable implements IExtension })); } - updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise { + async updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise { this.logService.trace('ExtensionManagementService#updateMetadata', local.identifier.id); - local.metadata = metadata; - return this.saveMetadataForLocalExtension(local) - .then(localExtension => { - this.manifestCache.invalidate(); - return localExtension; - }); + local = await this.extensionsScanner.saveMetadataForLocalExtension(local, { ...metadata, isMachineScoped: local.isMachineScoped }); + this.manifestCache.invalidate(); + return local; } - private saveMetadataForLocalExtension(local: ILocalExtension): Promise { - if (!local.metadata) { - return Promise.resolve(local); - } - const manifestPath = path.join(local.location.fsPath, 'package.json'); - return pfs.readFile(manifestPath, 'utf8') - .then(raw => parseManifest(raw)) - .then(({ manifest }) => assign(manifest, { __metadata: local.metadata })) - .then(manifest => pfs.writeFile(manifestPath, JSON.stringify(manifest, null, '\t'))) - .then(() => local); - } - - private getMetadata(extensionName: string): Promise { + private getGalleryMetadata(extensionName: string): Promise { return this.findGalleryExtensionByName(extensionName) - .then(galleryExtension => galleryExtension ? { id: galleryExtension.identifier.uuid, publisherDisplayName: galleryExtension.publisherDisplayName, publisherId: galleryExtension.publisherId } : null); + .then(galleryExtension => galleryExtension ? { id: galleryExtension.identifier.uuid, publisherDisplayName: galleryExtension.publisherDisplayName, publisherId: galleryExtension.publisherId } : undefined); } private findGalleryExtension(local: ILocalExtension): Promise { @@ -707,7 +602,7 @@ export class ExtensionManagementService extends Disposable implements IExtension let promise = this.uninstallingExtensions.get(local.identifier.id); if (!promise) { // Set all versions of the extension as uninstalled - promise = createCancelablePromise(token => this.scanUserExtensions(false) + promise = createCancelablePromise(token => this.extensionsScanner.scanUserExtensions(false) .then(userExtensions => this.setUninstalled(...userExtensions.filter(u => areSameExtensions(u.identifier, local.identifier)))) .then(() => { this.uninstallingExtensions.delete(local.identifier.id); })); this.uninstallingExtensions.set(local.identifier.id, promise); @@ -731,142 +626,11 @@ export class ExtensionManagementService extends Disposable implements IExtension } getInstalled(type: ExtensionType | null = null): Promise { - const promises: Promise[] = []; - - if (type === null || type === ExtensionType.System) { - promises.push(this.scanSystemExtensions().then(null, e => Promise.reject(new ExtensionManagementError(this.joinErrors(e).message, ERROR_SCANNING_SYS_EXTENSIONS)))); - } - - if (type === null || type === ExtensionType.User) { - promises.push(this.scanUserExtensions(true).then(null, e => Promise.reject(new ExtensionManagementError(this.joinErrors(e).message, ERROR_SCANNING_USER_EXTENSIONS)))); - } - - return Promise.all(promises).then(flatten, errors => Promise.reject(this.joinErrors(errors))); + return this.extensionsScanner.scanExtensions(type); } - private scanSystemExtensions(): Promise { - this.logService.trace('Started scanning system extensions'); - const systemExtensionsPromise = this.scanExtensions(this.systemExtensionsPath, ExtensionType.System) - .then(result => { - this.logService.trace('Scanned system extensions:', result.length); - return result; - }); - if (this.environmentService.isBuilt) { - return systemExtensionsPromise; - } - - // Scan other system extensions during development - const devSystemExtensionsPromise = this.getDevSystemExtensionsList() - .then(devSystemExtensionsList => { - if (devSystemExtensionsList.length) { - return this.scanExtensions(this.devSystemExtensionsPath, ExtensionType.System) - .then(result => { - this.logService.trace('Scanned dev system extensions:', result.length); - return result.filter(r => devSystemExtensionsList.some(id => areSameExtensions(r.identifier, { id }))); - }); - } else { - return []; - } - }); - return Promise.all([systemExtensionsPromise, devSystemExtensionsPromise]) - .then(([systemExtensions, devSystemExtensions]) => [...systemExtensions, ...devSystemExtensions]); - } - - private scanUserExtensions(excludeOutdated: boolean): Promise { - this.logService.trace('Started scanning user extensions'); - return Promise.all([this.getUninstalledExtensions(), this.scanExtensions(this.extensionsPath, ExtensionType.User)]) - .then(([uninstalled, extensions]) => { - extensions = extensions.filter(e => !uninstalled[new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version).key()]); - if (excludeOutdated) { - const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => e.identifier); - extensions = byExtension.map(p => p.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version))[0]); - } - this.logService.trace('Scanned user extensions:', extensions.length); - return extensions; - }); - } - - private scanExtensions(root: string, type: ExtensionType): Promise { - const limiter = new Limiter(10); - return pfs.readdir(root) - .then(extensionsFolders => Promise.all(extensionsFolders.map(extensionFolder => limiter.queue(() => this.scanExtension(extensionFolder, root, type))))) - .then(extensions => extensions.filter(e => e && e.identifier)); - } - - private scanExtension(folderName: string, root: string, type: ExtensionType): Promise { - if (type === ExtensionType.User && folderName.indexOf('.') === 0) { // Do not consider user extension folder starting with `.` - return Promise.resolve(null); - } - const extensionPath = path.join(root, folderName); - return pfs.readdir(extensionPath) - .then(children => readManifest(extensionPath) - .then(({ manifest, metadata }) => { - const readme = children.filter(child => /^readme(\.txt|\.md|)$/i.test(child))[0]; - const readmeUrl = readme ? URI.file(path.join(extensionPath, readme)) : null; - const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0]; - const changelogUrl = changelog ? URI.file(path.join(extensionPath, changelog)) : null; - const identifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) }; - const local = { type, identifier, manifest, metadata, location: URI.file(extensionPath), readmeUrl, changelogUrl }; - if (metadata) { - this.setMetadata(local, metadata); - } - return local; - })) - .then(undefined, () => null); - } - - private setMetadata(local: ILocalExtension, metadata: IGalleryMetadata): void { - local.metadata = metadata; - local.identifier.uuid = metadata.id; - } - - async removeDeprecatedExtensions(): Promise { - await this.removeUninstalledExtensions(); - await this.removeOutdatedExtensions(); - } - - private async removeUninstalledExtensions(): Promise { - const uninstalled = await this.getUninstalledExtensions(); - const extensions = await this.scanExtensions(this.extensionsPath, ExtensionType.User); // All user extensions - const installed: Set = new Set(); - for (const e of extensions) { - if (!uninstalled[new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version).key()]) { - installed.add(e.identifier.id.toLowerCase()); - } - } - const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => e.identifier); - await Promise.all(byExtension.map(async e => { - const latest = e.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version))[0]; - if (!installed.has(latest.identifier.id.toLowerCase())) { - await this.extensionLifecycle.postUninstall(latest); - } - })); - const toRemove: ILocalExtension[] = extensions.filter(e => uninstalled[new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version).key()]); - await Promise.all(toRemove.map(e => this.removeUninstalledExtension(e))); - } - - private removeOutdatedExtensions(): Promise { - return this.scanExtensions(this.extensionsPath, ExtensionType.User) // All user extensions - .then(extensions => { - const toRemove: ILocalExtension[] = []; - - // Outdated extensions - const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => e.identifier); - toRemove.push(...flatten(byExtension.map(p => p.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version)).slice(1)))); - - return Promise.all(toRemove.map(extension => this.removeExtension(extension, 'outdated'))); - }).then(() => undefined); - } - - private removeUninstalledExtension(extension: ILocalExtension): Promise { - return this.removeExtension(extension, 'uninstalled') - .then(() => this.withUninstalledExtensions(uninstalled => delete uninstalled[new ExtensionIdentifierWithVersion(extension.identifier, extension.manifest.version).key()])) - .then(() => undefined); - } - - private removeExtension(extension: ILocalExtension, type: string): Promise { - this.logService.trace(`Deleting ${type} extension from disk`, extension.identifier.id, extension.location.fsPath); - return pfs.rimraf(extension.location.fsPath).then(() => this.logService.info('Deleted from disk', extension.identifier.id, extension.location.fsPath)); + removeDeprecatedExtensions(): Promise { + return this.extensionsScanner.cleanUp(); } private isUninstalled(identifier: ExtensionIdentifierWithVersion): Promise { @@ -874,7 +638,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } private filterUninstalled(...identifiers: ExtensionIdentifierWithVersion[]): Promise { - return this.withUninstalledExtensions(allUninstalled => { + return this.extensionsScanner.withUninstalledExtensions(allUninstalled => { const uninstalled: string[] = []; for (const identifier of identifiers) { if (!!allUninstalled[identifier.key()]) { @@ -887,34 +651,11 @@ export class ExtensionManagementService extends Disposable implements IExtension private setUninstalled(...extensions: ILocalExtension[]): Promise<{ [id: string]: boolean }> { const ids: ExtensionIdentifierWithVersion[] = extensions.map(e => new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version)); - return this.withUninstalledExtensions(uninstalled => assign(uninstalled, ids.reduce((result, id) => { result[id.key()] = true; return result; }, {} as { [id: string]: boolean }))); + return this.extensionsScanner.withUninstalledExtensions(uninstalled => assign(uninstalled, ids.reduce((result, id) => { result[id.key()] = true; return result; }, {} as { [id: string]: boolean }))); } private unsetUninstalled(extensionIdentifier: ExtensionIdentifierWithVersion): Promise { - return this.withUninstalledExtensions(uninstalled => delete uninstalled[extensionIdentifier.key()]); - } - - private getUninstalledExtensions(): Promise<{ [id: string]: boolean; }> { - return this.withUninstalledExtensions(uninstalled => uninstalled); - } - - private async withUninstalledExtensions(fn: (uninstalled: { [id: string]: boolean; }) => T): Promise { - return await this.uninstalledFileLimiter.queue(() => { - let result: T | null = null; - return pfs.readFile(this.uninstalledPath, 'utf8') - .then(undefined, err => err.code === 'ENOENT' ? Promise.resolve('{}') : Promise.reject(err)) - .then<{ [id: string]: boolean }>(raw => { try { return JSON.parse(raw); } catch (e) { return {}; } }) - .then(uninstalled => { result = fn(uninstalled); return uninstalled; }) - .then(uninstalled => { - if (Object.keys(uninstalled).length === 0) { - return pfs.rimraf(this.uninstalledPath); - } else { - const raw = JSON.stringify(uninstalled); - return pfs.writeFile(this.uninstalledPath, raw); - } - }) - .then(() => result); - }); + return this.extensionsScanner.withUninstalledExtensions(uninstalled => delete uninstalled[extensionIdentifier.key()]); } getExtensionsReport(): Promise { @@ -941,18 +682,6 @@ export class ExtensionManagementService extends Disposable implements IExtension }); } - private _devSystemExtensionsPath: string | null = null; - private get devSystemExtensionsPath(): string { - if (!this._devSystemExtensionsPath) { - this._devSystemExtensionsPath = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', '.build', 'builtInExtensions')); - } - return this._devSystemExtensionsPath; - } - - private getDevSystemExtensionsList(): Promise { - return Promise.resolve(product.builtInExtensions ? product.builtInExtensions.map(e => e.name) : []); - } - private toNonCancellablePromise(promise: Promise): Promise { return new Promise((c, e) => promise.then(result => c(result), error => e(error))); } diff --git a/src/vs/platform/extensionManagement/node/extensionTipsService.ts b/src/vs/platform/extensionManagement/node/extensionTipsService.ts index 5a9a09a5e7b..e1ad6092e0b 100644 --- a/src/vs/platform/extensionManagement/node/extensionTipsService.ts +++ b/src/vs/platform/extensionManagement/node/extensionTipsService.ts @@ -5,7 +5,7 @@ import { URI } from 'vs/base/common/uri'; import { join, } from 'vs/base/common/path'; -import { IProductService, IExeBasedExtensionTip } from 'vs/platform/product/common/productService'; +import { IProductService } from 'vs/platform/product/common/productService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { env as processEnv } from 'vs/base/common/process'; import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; @@ -13,17 +13,23 @@ import { IFileService } from 'vs/platform/files/common/files'; import { isWindows } from 'vs/base/common/platform'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import { IExecutableBasedExtensionTip } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IStringDictionary, forEach } from 'vs/base/common/collections'; +import { forEach } from 'vs/base/common/collections'; import { IRequestService } from 'vs/platform/request/common/request'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtensionTipsService as BaseExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionTipsService'; +type IExeBasedExtensionTips = { + readonly exeFriendlyName: string, + readonly windowsPath?: string, + readonly recommendations: { extensionId: string, extensionName: string, isExtensionPack: boolean }[]; +}; + export class ExtensionTipsService extends BaseExtensionTipsService { _serviceBrand: any; - private readonly allImportantExecutableTips: IStringDictionary = {}; - private readonly allOtherExecutableTips: IStringDictionary = {}; + private readonly allImportantExecutableTips: Map = new Map(); + private readonly allOtherExecutableTips: Map = new Map(); constructor( @IEnvironmentService private readonly environmentService: INativeEnvironmentService, @@ -35,10 +41,20 @@ export class ExtensionTipsService extends BaseExtensionTipsService { super(fileService, productService, requestService, logService); if (productService.exeBasedExtensionTips) { forEach(productService.exeBasedExtensionTips, ({ key, value }) => { - if (value.important) { - this.allImportantExecutableTips[key] = value; - } else { - this.allOtherExecutableTips[key] = value; + const importantRecommendations: { extensionId: string, extensionName: string, isExtensionPack: boolean }[] = []; + const otherRecommendations: { extensionId: string, extensionName: string, isExtensionPack: boolean }[] = []; + forEach(value.recommendations, ({ key: extensionId, value }) => { + if (value.important) { + importantRecommendations.push({ extensionId, extensionName: value.name, isExtensionPack: !!value.isExtensionPack }); + } else { + otherRecommendations.push({ extensionId, extensionName: value.name, isExtensionPack: !!value.isExtensionPack }); + } + }); + if (importantRecommendations.length) { + this.allImportantExecutableTips.set(key, { exeFriendlyName: value.friendlyName, windowsPath: value.windowsPath, recommendations: importantRecommendations }); + } + if (otherRecommendations.length) { + this.allOtherExecutableTips.set(key, { exeFriendlyName: value.friendlyName, windowsPath: value.windowsPath, recommendations: otherRecommendations }); } }); } @@ -52,13 +68,13 @@ export class ExtensionTipsService extends BaseExtensionTipsService { return this.getValidExecutableBasedExtensionTips(this.allOtherExecutableTips); } - private async getValidExecutableBasedExtensionTips(executableTips: IStringDictionary): Promise { + private async getValidExecutableBasedExtensionTips(executableTips: Map): Promise { const result: IExecutableBasedExtensionTip[] = []; const checkedExecutables: Map = new Map(); - for (const exeName of Object.keys(executableTips)) { - const extensionTip = executableTips[exeName]; - if (!isNonEmptyArray(extensionTip?.recommendations)) { + for (const exeName of executableTips.keys()) { + const extensionTip = executableTips.get(exeName); + if (!extensionTip || !isNonEmptyArray(extensionTip.recommendations)) { continue; } @@ -83,12 +99,15 @@ export class ExtensionTipsService extends BaseExtensionTipsService { checkedExecutables.set(exePath, exists); } if (exists) { - extensionTip.recommendations.forEach(recommendation => result.push({ - extensionId: recommendation, - friendlyName: extensionTip.friendlyName, - exeFriendlyName: extensionTip.exeFriendlyName, - windowsPath: extensionTip.windowsPath, - })); + for (const { extensionId, extensionName, isExtensionPack } of extensionTip.recommendations) { + result.push({ + extensionId, + extensionName, + isExtensionPack, + exeFriendlyName: extensionTip.exeFriendlyName, + windowsPath: extensionTip.windowsPath, + }); + } } } } diff --git a/src/vs/platform/extensionManagement/node/extensionsScanner.ts b/src/vs/platform/extensionManagement/node/extensionsScanner.ts new file mode 100644 index 00000000000..575b2aafc38 --- /dev/null +++ b/src/vs/platform/extensionManagement/node/extensionsScanner.ts @@ -0,0 +1,348 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as semver from 'semver-umd'; +import { Disposable } from 'vs/base/common/lifecycle'; +import * as pfs from 'vs/base/node/pfs'; +import * as path from 'vs/base/common/path'; +import { ILogService } from 'vs/platform/log/common/log'; +import { ILocalExtension, IGalleryMetadata, ExtensionManagementError } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ExtensionType, IExtensionManifest, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { areSameExtensions, ExtensionIdentifierWithVersion, groupByExtension, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { Limiter, Queue } from 'vs/base/common/async'; +import { URI } from 'vs/base/common/uri'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { localizeManifest } from 'vs/platform/extensionManagement/common/extensionNls'; +import { localize } from 'vs/nls'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { CancellationToken } from 'vscode'; +import { extract, ExtractError } from 'vs/base/node/zip'; +import { isWindows } from 'vs/base/common/platform'; +import { flatten } from 'vs/base/common/arrays'; +import { assign } from 'vs/base/common/objects'; + +const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem'; +const ERROR_SCANNING_USER_EXTENSIONS = 'scanningUser'; +const INSTALL_ERROR_EXTRACTING = 'extracting'; +const INSTALL_ERROR_DELETING = 'deleting'; +const INSTALL_ERROR_RENAMING = 'renaming'; + +export type IMetadata = Partial; + +export class ExtensionsScanner extends Disposable { + + private readonly systemExtensionsPath: string; + private readonly extensionsPath: string; + private readonly uninstalledPath: string; + private readonly uninstalledFileLimiter: Queue; + + constructor( + private readonly beforeRemovingExtension: (e: ILocalExtension) => Promise, + @ILogService private readonly logService: ILogService, + @IEnvironmentService private readonly environmentService: INativeEnvironmentService, + @IProductService private readonly productService: IProductService, + ) { + super(); + this.systemExtensionsPath = environmentService.builtinExtensionsPath; + this.extensionsPath = environmentService.extensionsPath!; + this.uninstalledPath = path.join(this.extensionsPath, '.obsolete'); + this.uninstalledFileLimiter = new Queue(); + } + + async cleanUp(): Promise { + await this.removeUninstalledExtensions(); + await this.removeOutdatedExtensions(); + } + + async scanExtensions(type: ExtensionType | null): Promise { + const promises: Promise[] = []; + + if (type === null || type === ExtensionType.System) { + promises.push(this.scanSystemExtensions().then(null, e => Promise.reject(new ExtensionManagementError(this.joinErrors(e).message, ERROR_SCANNING_SYS_EXTENSIONS)))); + } + + if (type === null || type === ExtensionType.User) { + promises.push(this.scanUserExtensions(true).then(null, e => Promise.reject(new ExtensionManagementError(this.joinErrors(e).message, ERROR_SCANNING_USER_EXTENSIONS)))); + } + + return Promise.all(promises).then(flatten, errors => Promise.reject(this.joinErrors(errors))); + } + + async scanUserExtensions(excludeOutdated: boolean): Promise { + this.logService.trace('Started scanning user extensions'); + let [uninstalled, extensions] = await Promise.all([this.getUninstalledExtensions(), this.scanAllUserExtensions()]); + extensions = extensions.filter(e => !uninstalled[new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version).key()]); + if (excludeOutdated) { + const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => e.identifier); + extensions = byExtension.map(p => p.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version))[0]); + } + this.logService.trace('Scanned user extensions:', extensions.length); + return extensions; + } + + async scanAllUserExtensions(): Promise { + return this.scanExtensionsInDir(this.extensionsPath, ExtensionType.User); + } + + async extractUserExtension(identifierWithVersion: ExtensionIdentifierWithVersion, zipPath: string, token: CancellationToken): Promise { + const { identifier } = identifierWithVersion; + const folderName = identifierWithVersion.key(); + const tempPath = path.join(this.extensionsPath, `.${folderName}`); + const extensionPath = path.join(this.extensionsPath, folderName); + + try { + await pfs.rimraf(extensionPath); + } catch (error) { + try { + await pfs.rimraf(extensionPath); + } catch (e) { /* ignore */ } + throw new ExtensionManagementError(localize('errorDeleting', "Unable to delete the existing folder '{0}' while installing the extension '{1}'. Please delete the folder manually and try again", extensionPath, identifier.id), INSTALL_ERROR_DELETING); + } + + await this.extractAtLocation(identifier, zipPath, tempPath, token); + try { + await this.rename(identifier, tempPath, extensionPath, Date.now() + (2 * 60 * 1000) /* Retry for 2 minutes */); + this.logService.info('Renamed to', extensionPath); + } catch (error) { + this.logService.info('Rename failed. Deleting from extracted location', tempPath); + try { + pfs.rimraf(tempPath); + } catch (e) { /* ignore */ } + throw error; + } + + let local: ILocalExtension | null = null; + try { + local = await this.scanExtension(folderName, this.extensionsPath, ExtensionType.User); + } catch (e) { /*ignore */ } + + if (local) { + return local; + } + throw new Error(localize('cannot read', "Cannot read the extension from {0}", this.extensionsPath)); + } + + async saveMetadataForLocalExtension(local: ILocalExtension, metadata: IMetadata): Promise { + this.setMetadata(local, metadata); + + // unset if false + metadata.isMachineScoped = metadata.isMachineScoped || undefined; + const manifestPath = path.join(local.location.fsPath, 'package.json'); + const raw = await pfs.readFile(manifestPath, 'utf8'); + const { manifest } = await this.parseManifest(raw); + assign(manifest, { __metadata: metadata }); + await pfs.writeFile(manifestPath, JSON.stringify(manifest, null, '\t')); + return local; + } + + getUninstalledExtensions(): Promise<{ [id: string]: boolean; }> { + return this.withUninstalledExtensions(uninstalled => uninstalled); + } + + async withUninstalledExtensions(fn: (uninstalled: { [id: string]: boolean; }) => T): Promise { + return this.uninstalledFileLimiter.queue(async () => { + let result: T | null = null; + return pfs.readFile(this.uninstalledPath, 'utf8') + .then(undefined, err => err.code === 'ENOENT' ? Promise.resolve('{}') : Promise.reject(err)) + .then<{ [id: string]: boolean }>(raw => { try { return JSON.parse(raw); } catch (e) { return {}; } }) + .then(uninstalled => { result = fn(uninstalled); return uninstalled; }) + .then(uninstalled => { + if (Object.keys(uninstalled).length === 0) { + return pfs.rimraf(this.uninstalledPath); + } else { + const raw = JSON.stringify(uninstalled); + return pfs.writeFile(this.uninstalledPath, raw); + } + }) + .then(() => result); + }); + } + + async removeExtension(extension: ILocalExtension, type: string): Promise { + this.logService.trace(`Deleting ${type} extension from disk`, extension.identifier.id, extension.location.fsPath); + await pfs.rimraf(extension.location.fsPath); + this.logService.info('Deleted from disk', extension.identifier.id, extension.location.fsPath); + } + + async removeUninstalledExtension(extension: ILocalExtension): Promise { + await this.removeExtension(extension, 'uninstalled'); + await this.withUninstalledExtensions(uninstalled => delete uninstalled[new ExtensionIdentifierWithVersion(extension.identifier, extension.manifest.version).key()]); + } + + private extractAtLocation(identifier: IExtensionIdentifier, zipPath: string, location: string, token: CancellationToken): Promise { + this.logService.trace(`Started extracting the extension from ${zipPath} to ${location}`); + return pfs.rimraf(location) + .then( + () => extract(zipPath, location, { sourcePath: 'extension', overwrite: true }, token) + .then( + () => this.logService.info(`Extracted extension to ${location}:`, identifier.id), + e => pfs.rimraf(location).finally(() => { }) + .then(() => Promise.reject(new ExtensionManagementError(e.message, e instanceof ExtractError && e.type ? e.type : INSTALL_ERROR_EXTRACTING)))), + e => Promise.reject(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_DELETING))); + } + + private rename(identifier: IExtensionIdentifier, extractPath: string, renamePath: string, retryUntil: number): Promise { + return pfs.rename(extractPath, renamePath) + .then(undefined, error => { + if (isWindows && error && error.code === 'EPERM' && Date.now() < retryUntil) { + this.logService.info(`Failed renaming ${extractPath} to ${renamePath} with 'EPERM' error. Trying again...`, identifier.id); + return this.rename(identifier, extractPath, renamePath, retryUntil); + } + return Promise.reject(new ExtensionManagementError(error.message || localize('renameError', "Unknown error while renaming {0} to {1}", extractPath, renamePath), error.code || INSTALL_ERROR_RENAMING)); + }); + } + + private async scanSystemExtensions(): Promise { + this.logService.trace('Started scanning system extensions'); + const systemExtensionsPromise = this.scanDefaultSystemExtensions(); + if (this.environmentService.isBuilt) { + return systemExtensionsPromise; + } + + // Scan other system extensions during development + const devSystemExtensionsPromise = this.scanDevSystemExtensions(); + const [systemExtensions, devSystemExtensions] = await Promise.all([systemExtensionsPromise, devSystemExtensionsPromise]); + return [...systemExtensions, ...devSystemExtensions]; + } + + private async scanExtensionsInDir(dir: string, type: ExtensionType): Promise { + const limiter = new Limiter(10); + const extensionsFolders = await pfs.readdir(dir); + const extensions = await Promise.all(extensionsFolders.map(extensionFolder => limiter.queue(() => this.scanExtension(extensionFolder, dir, type)))); + return extensions.filter(e => e && e.identifier); + } + + private async scanExtension(folderName: string, root: string, type: ExtensionType): Promise { + if (type === ExtensionType.User && folderName.indexOf('.') === 0) { // Do not consider user extension folder starting with `.` + return null; + } + const extensionPath = path.join(root, folderName); + try { + const children = await pfs.readdir(extensionPath); + const { manifest, metadata } = await this.readManifest(extensionPath); + const readme = children.filter(child => /^readme(\.txt|\.md|)$/i.test(child))[0]; + const readmeUrl = readme ? URI.file(path.join(extensionPath, readme)) : undefined; + const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0]; + const changelogUrl = changelog ? URI.file(path.join(extensionPath, changelog)) : undefined; + const identifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) }; + const local = { type, identifier, manifest, location: URI.file(extensionPath), readmeUrl, changelogUrl, publisherDisplayName: null, publisherId: null, isMachineScoped: false }; + if (metadata) { + this.setMetadata(local, metadata); + } + return local; + } catch (e) { + this.logService.trace(e); + return null; + } + } + + private async scanDefaultSystemExtensions(): Promise { + const result = await this.scanExtensionsInDir(this.systemExtensionsPath, ExtensionType.System); + this.logService.trace('Scanned system extensions:', result.length); + return result; + } + + private async scanDevSystemExtensions(): Promise { + const devSystemExtensionsList = this.getDevSystemExtensionsList(); + if (devSystemExtensionsList.length) { + const result = await this.scanExtensionsInDir(this.devSystemExtensionsPath, ExtensionType.System); + this.logService.trace('Scanned dev system extensions:', result.length); + return result.filter(r => devSystemExtensionsList.some(id => areSameExtensions(r.identifier, { id }))); + } else { + return []; + } + } + + private setMetadata(local: ILocalExtension, metadata: IMetadata): void { + local.publisherDisplayName = metadata.publisherDisplayName || null; + local.publisherId = metadata.publisherId || null; + local.identifier.uuid = metadata.id; + local.isMachineScoped = !!metadata.isMachineScoped; + } + + private async removeUninstalledExtensions(): Promise { + const uninstalled = await this.getUninstalledExtensions(); + const extensions = await this.scanAllUserExtensions(); // All user extensions + const installed: Set = new Set(); + for (const e of extensions) { + if (!uninstalled[new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version).key()]) { + installed.add(e.identifier.id.toLowerCase()); + } + } + const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => e.identifier); + await Promise.all(byExtension.map(async e => { + const latest = e.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version))[0]; + if (!installed.has(latest.identifier.id.toLowerCase())) { + await this.beforeRemovingExtension(latest); + } + })); + const toRemove: ILocalExtension[] = extensions.filter(e => uninstalled[new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version).key()]); + await Promise.all(toRemove.map(e => this.removeUninstalledExtension(e))); + } + + private async removeOutdatedExtensions(): Promise { + const extensions = await this.scanAllUserExtensions(); + const toRemove: ILocalExtension[] = []; + + // Outdated extensions + const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => e.identifier); + toRemove.push(...flatten(byExtension.map(p => p.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version)).slice(1)))); + + await Promise.all(toRemove.map(extension => this.removeExtension(extension, 'outdated'))); + } + + private getDevSystemExtensionsList(): string[] { + return (this.productService.builtInExtensions || []).map(e => e.name); + } + + private joinErrors(errorOrErrors: (Error | string) | (Array)): Error { + const errors = Array.isArray(errorOrErrors) ? errorOrErrors : [errorOrErrors]; + if (errors.length === 1) { + return errors[0] instanceof Error ? errors[0] : new Error(errors[0]); + } + return errors.reduce((previousValue: Error, currentValue: Error | string) => { + return new Error(`${previousValue.message}${previousValue.message ? ',' : ''}${currentValue instanceof Error ? currentValue.message : currentValue}`); + }, new Error('')); + } + + private _devSystemExtensionsPath: string | null = null; + private get devSystemExtensionsPath(): string { + if (!this._devSystemExtensionsPath) { + this._devSystemExtensionsPath = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', '.build', 'builtInExtensions')); + } + return this._devSystemExtensionsPath; + } + + private async readManifest(extensionPath: string): Promise<{ manifest: IExtensionManifest; metadata: IMetadata | null; }> { + const promises = [ + pfs.readFile(path.join(extensionPath, 'package.json'), 'utf8') + .then(raw => this.parseManifest(raw)), + pfs.readFile(path.join(extensionPath, 'package.nls.json'), 'utf8') + .then(undefined, err => err.code !== 'ENOENT' ? Promise.reject(err) : '{}') + .then(raw => JSON.parse(raw)) + ]; + + const [{ manifest, metadata }, translations] = await Promise.all(promises); + return { + manifest: localizeManifest(manifest, translations), + metadata + }; + } + + private parseManifest(raw: string): Promise<{ manifest: IExtensionManifest; metadata: IMetadata | null; }> { + return new Promise((c, e) => { + try { + const manifest = JSON.parse(raw); + const metadata = manifest.__metadata || null; + delete manifest.__metadata; + c({ manifest, metadata }); + } catch (err) { + e(new Error(localize('invalidManifest', "Extension invalid: package.json is not a JSON file."))); + } + }); + } +} diff --git a/src/vs/platform/extensionManagement/test/common/configRemotes.test.ts b/src/vs/platform/extensionManagement/test/common/configRemotes.test.ts index cc9a2262d29..67b5beae330 100644 --- a/src/vs/platform/extensionManagement/test/common/configRemotes.test.ts +++ b/src/vs/platform/extensionManagement/test/common/configRemotes.test.ts @@ -8,7 +8,7 @@ import { getDomainsOfRemotes, getRemotes } from 'vs/platform/extensionManagement suite('Config Remotes', () => { - const whitelist = [ + const allowedDomains = [ 'github.com', 'github2.com', 'github3.com', @@ -20,37 +20,37 @@ suite('Config Remotes', () => { ]; test('HTTPS remotes', function () { - assert.deepStrictEqual(getDomainsOfRemotes(remote('https://github.com/Microsoft/vscode.git'), whitelist), ['github.com']); - assert.deepStrictEqual(getDomainsOfRemotes(remote('https://git.example.com/gitproject.git'), whitelist), ['example.com']); - assert.deepStrictEqual(getDomainsOfRemotes(remote('https://username@github2.com/username/repository.git'), whitelist), ['github2.com']); - assert.deepStrictEqual(getDomainsOfRemotes(remote('https://username:password@github3.com/username/repository.git'), whitelist), ['github3.com']); - assert.deepStrictEqual(getDomainsOfRemotes(remote('https://username:password@example2.com:1234/username/repository.git'), whitelist), ['example2.com']); - assert.deepStrictEqual(getDomainsOfRemotes(remote('https://example3.com:1234/username/repository.git'), whitelist), ['example3.com']); + assert.deepStrictEqual(getDomainsOfRemotes(remote('https://github.com/Microsoft/vscode.git'), allowedDomains), ['github.com']); + assert.deepStrictEqual(getDomainsOfRemotes(remote('https://git.example.com/gitproject.git'), allowedDomains), ['example.com']); + assert.deepStrictEqual(getDomainsOfRemotes(remote('https://username@github2.com/username/repository.git'), allowedDomains), ['github2.com']); + assert.deepStrictEqual(getDomainsOfRemotes(remote('https://username:password@github3.com/username/repository.git'), allowedDomains), ['github3.com']); + assert.deepStrictEqual(getDomainsOfRemotes(remote('https://username:password@example2.com:1234/username/repository.git'), allowedDomains), ['example2.com']); + assert.deepStrictEqual(getDomainsOfRemotes(remote('https://example3.com:1234/username/repository.git'), allowedDomains), ['example3.com']); }); test('SSH remotes', function () { - assert.deepStrictEqual(getDomainsOfRemotes(remote('ssh://user@git.server.org/project.git'), whitelist), ['server.org']); + assert.deepStrictEqual(getDomainsOfRemotes(remote('ssh://user@git.server.org/project.git'), allowedDomains), ['server.org']); }); test('SCP-like remotes', function () { - assert.deepStrictEqual(getDomainsOfRemotes(remote('git@github.com:Microsoft/vscode.git'), whitelist), ['github.com']); - assert.deepStrictEqual(getDomainsOfRemotes(remote('user@git.server.org:project.git'), whitelist), ['server.org']); - assert.deepStrictEqual(getDomainsOfRemotes(remote('git.server2.org:project.git'), whitelist), ['server2.org']); + assert.deepStrictEqual(getDomainsOfRemotes(remote('git@github.com:Microsoft/vscode.git'), allowedDomains), ['github.com']); + assert.deepStrictEqual(getDomainsOfRemotes(remote('user@git.server.org:project.git'), allowedDomains), ['server.org']); + assert.deepStrictEqual(getDomainsOfRemotes(remote('git.server2.org:project.git'), allowedDomains), ['server2.org']); }); test('Local remotes', function () { - assert.deepStrictEqual(getDomainsOfRemotes(remote('/opt/git/project.git'), whitelist), []); - assert.deepStrictEqual(getDomainsOfRemotes(remote('file:///opt/git/project.git'), whitelist), []); + assert.deepStrictEqual(getDomainsOfRemotes(remote('/opt/git/project.git'), allowedDomains), []); + assert.deepStrictEqual(getDomainsOfRemotes(remote('file:///opt/git/project.git'), allowedDomains), []); }); test('Multiple remotes', function () { const config = ['https://github.com/Microsoft/vscode.git', 'https://git.example.com/gitproject.git'].map(remote).join(''); - assert.deepStrictEqual(getDomainsOfRemotes(config, whitelist).sort(), ['example.com', 'github.com']); + assert.deepStrictEqual(getDomainsOfRemotes(config, allowedDomains).sort(), ['example.com', 'github.com']); }); - test('Whitelisting', () => { + test('Non allowed domains are anonymized', () => { const config = ['https://github.com/Microsoft/vscode.git', 'https://git.foobar.com/gitproject.git'].map(remote).join(''); - assert.deepStrictEqual(getDomainsOfRemotes(config, whitelist).sort(), ['aaaaaa.aaa', 'github.com']); + assert.deepStrictEqual(getDomainsOfRemotes(config, allowedDomains).sort(), ['aaaaaa.aaa', 'github.com']); }); test('HTTPS remotes to be hashed', function () { diff --git a/src/vs/platform/extensionManagement/test/common/extensionManagement.test.ts b/src/vs/platform/extensionManagement/test/common/extensionManagement.test.ts index 32c514601c7..a9652639529 100644 --- a/src/vs/platform/extensionManagement/test/common/extensionManagement.test.ts +++ b/src/vs/platform/extensionManagement/test/common/extensionManagement.test.ts @@ -15,6 +15,8 @@ suite('Extension Identifier Pattern', () => { assert.equal(true, regEx.test('PUBLISHER.NAME')); assert.equal(true, regEx.test('PUBLISHEr.NAMe')); assert.equal(true, regEx.test('PUBLISHEr.N-AMe')); + assert.equal(true, regEx.test('PUB-LISHEr.NAMe')); + assert.equal(true, regEx.test('PUB-LISHEr.N-AMe')); assert.equal(true, regEx.test('PUBLISH12Er90.N-A54Me123')); assert.equal(true, regEx.test('111PUBLISH12Er90.N-1111A54Me123')); assert.equal(false, regEx.test('publishername')); diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index ac6ec196a03..6024a71fb10 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -6,6 +6,7 @@ import * as strings from 'vs/base/common/strings'; import { ILocalization } from 'vs/platform/localizations/common/localizations'; import { URI } from 'vs/base/common/uri'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const MANIFEST_CACHE_FOLDER = 'CachedExtensions'; export const USER_MANIFEST_CACHE_FILE = 'user'; @@ -141,6 +142,26 @@ export interface IExtensionIdentifier { uuid?: string; } +export const EXTENSION_CATEGORIES = [ + 'Azure', + 'Data Science', + 'Debuggers', + 'Extension Packs', + 'Formatters', + 'Keymaps', + 'Language Packs', + 'Linters', + 'Machine Learning', + 'Notebooks', + 'Programming Languages', + 'SCM Providers', + 'Snippets', + 'Themes', + 'Testing', + 'Visualization', + 'Other', +]; + export interface IExtensionManifest { readonly name: string; readonly displayName?: string; @@ -149,6 +170,7 @@ export interface IExtensionManifest { readonly engines: { vscode: string }; readonly description?: string; readonly main?: string; + readonly browser?: string; readonly icon?: string; readonly categories?: string[]; readonly keywords?: string[]; @@ -174,6 +196,8 @@ export interface IExtension { readonly identifier: IExtensionIdentifier; readonly manifest: IExtensionManifest; readonly location: URI; + readonly readmeUrl?: URI; + readonly changelogUrl?: URI; } /** @@ -243,3 +267,29 @@ export interface IExtensionDescription extends IExtensionManifest { export function isLanguagePackExtension(manifest: IExtensionManifest): boolean { return manifest.contributes && manifest.contributes.localizations ? manifest.contributes.localizations.length > 0 : false; } + +export interface IScannedExtension { + readonly identifier: IExtensionIdentifier; + readonly location: URI; + readonly type: ExtensionType; + readonly packageJSON: IExtensionManifest; + readonly packageNLS?: any; + readonly packageNLSUrl?: URI; + readonly readmeUrl?: URI; + readonly changelogUrl?: URI; +} + +export interface ITranslatedScannedExtension { + readonly identifier: IExtensionIdentifier; + readonly location: URI; + readonly type: ExtensionType; + readonly packageJSON: IExtensionManifest; + readonly readmeUrl?: URI; + readonly changelogUrl?: URI; +} + +export const IBuiltinExtensionsScannerService = createDecorator('IBuiltinExtensionsScannerService'); +export interface IBuiltinExtensionsScannerService { + readonly _serviceBrand: undefined; + scanBuiltinExtensions(): Promise; +} diff --git a/src/vs/platform/files/browser/indexedDBFileSystemProvider.ts b/src/vs/platform/files/browser/indexedDBFileSystemProvider.ts new file mode 100644 index 00000000000..059bffe8744 --- /dev/null +++ b/src/vs/platform/files/browser/indexedDBFileSystemProvider.ts @@ -0,0 +1,123 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyValueFileSystemProvider } from 'vs/platform/files/common/keyValueFileSystemProvider'; +import * as browser from 'vs/base/browser/browser'; +import { IFileSystemProvider } from 'vs/platform/files/common/files'; + +const INDEXEDDB_VSCODE_DB = 'vscode-web-db'; +export const INDEXEDDB_USERDATA_OBJECT_STORE = 'vscode-userdata-store'; +export const INDEXEDDB_LOGS_OBJECT_STORE = 'vscode-logs-store'; + +export class IndexedDB { + + private indexedDBPromise: Promise; + + constructor() { + this.indexedDBPromise = this.openIndexedDB(INDEXEDDB_VSCODE_DB, 2, [INDEXEDDB_USERDATA_OBJECT_STORE, INDEXEDDB_LOGS_OBJECT_STORE]); + } + + async createFileSystemProvider(scheme: string, store: string): Promise { + let fsp: IFileSystemProvider | null = null; + const indexedDB = await this.indexedDBPromise; + if (indexedDB) { + if (indexedDB.objectStoreNames.contains(store)) { + fsp = new IndexedDBFileSystemProvider(scheme, indexedDB, store); + } else { + console.error(`Error while creating indexedDB filesystem provider. Could not find ${store} object store`); + } + } + return fsp; + } + + private openIndexedDB(name: string, version: number, stores: string[]): Promise { + if (browser.isEdge) { + return Promise.resolve(null); + } + return new Promise((c, e) => { + const request = window.indexedDB.open(name, version); + request.onerror = (err) => e(request.error); + request.onsuccess = () => { + const db = request.result; + for (const store of stores) { + if (!db.objectStoreNames.contains(store)) { + console.error(`Error while creating indexedDB. Could not create ${store} object store`); + c(null); + return; + } + } + c(db); + }; + request.onupgradeneeded = () => { + const db = request.result; + for (const store of stores) { + if (!db.objectStoreNames.contains(store)) { + db.createObjectStore(store); + } + } + }; + }); + } + +} + +class IndexedDBFileSystemProvider extends KeyValueFileSystemProvider { + + constructor(scheme: string, private readonly database: IDBDatabase, private readonly store: string) { + super(scheme); + } + + protected async getAllKeys(): Promise { + return new Promise(async (c, e) => { + const transaction = this.database.transaction([this.store]); + const objectStore = transaction.objectStore(this.store); + const request = objectStore.getAllKeys(); + request.onerror = () => e(request.error); + request.onsuccess = () => c(request.result); + }); + } + + protected hasKey(key: string): Promise { + return new Promise(async (c, e) => { + const transaction = this.database.transaction([this.store]); + const objectStore = transaction.objectStore(this.store); + const request = objectStore.getKey(key); + request.onerror = () => e(request.error); + request.onsuccess = () => { + c(!!request.result); + }; + }); + } + + protected getValue(key: string): Promise { + return new Promise(async (c, e) => { + const transaction = this.database.transaction([this.store]); + const objectStore = transaction.objectStore(this.store); + const request = objectStore.get(key); + request.onerror = () => e(request.error); + request.onsuccess = () => c(request.result || ''); + }); + } + + protected setValue(key: string, value: string): Promise { + return new Promise(async (c, e) => { + const transaction = this.database.transaction([this.store], 'readwrite'); + const objectStore = transaction.objectStore(this.store); + const request = objectStore.put(value, key); + request.onerror = () => e(request.error); + request.onsuccess = () => c(); + }); + } + + protected deleteKey(key: string): Promise { + return new Promise(async (c, e) => { + const transaction = this.database.transaction([this.store], 'readwrite'); + const objectStore = transaction.objectStore(this.store); + const request = objectStore.delete(key); + request.onerror = () => e(request.error); + request.onsuccess = () => c(); + }); + } +} diff --git a/src/vs/platform/files/common/fileService.ts b/src/vs/platform/files/common/fileService.ts index 073855d2890..fc4e543c263 100644 --- a/src/vs/platform/files/common/fileService.ts +++ b/src/vs/platform/files/common/fileService.ts @@ -7,38 +7,38 @@ import { Disposable, IDisposable, toDisposable, dispose, DisposableStore } from import { IFileService, IResolveFileOptions, FileChangesEvent, FileOperationEvent, IFileSystemProviderRegistrationEvent, IFileSystemProvider, IFileStat, IResolveFileResult, ICreateFileOptions, IFileSystemProviderActivationEvent, FileOperationError, FileOperationResult, FileOperation, FileSystemProviderCapabilities, FileType, toFileSystemProviderErrorCode, FileSystemProviderErrorCode, IStat, IFileStatWithMetadata, IResolveMetadataFileOptions, etag, hasReadWriteCapability, hasFileFolderCopyCapability, hasOpenReadWriteCloseCapability, toFileOperationResult, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadWriteCapability, IResolveFileResultWithMetadata, IWatchOptions, IWriteFileOptions, IReadFileOptions, IFileStreamContent, IFileContent, ETAG_DISABLED, hasFileReadStreamCapability, IFileSystemProviderWithFileReadStreamCapability, ensureFileSystemProviderError, IFileSystemProviderCapabilitiesChangeEvent } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; -import { isAbsolutePath, dirname, basename, joinPath, isEqual, isEqualOrParent } from 'vs/base/common/resources'; +import { isAbsolutePath, dirname, basename, joinPath, IExtUri, extUri, extUriIgnorePathCase } from 'vs/base/common/resources'; import { localize } from 'vs/nls'; import { TernarySearchTree } from 'vs/base/common/map'; import { isNonEmptyArray, coalesce } from 'vs/base/common/arrays'; import { getBaseLabel } from 'vs/base/common/labels'; import { ILogService } from 'vs/platform/log/common/log'; -import { VSBuffer, VSBufferReadable, readableToBuffer, bufferToReadable, streamToBuffer, bufferToStream, VSBufferReadableStream } from 'vs/base/common/buffer'; -import { isReadableStream, transform, ReadableStreamEvents, consumeReadableWithLimit, consumeStreamWithLimit } from 'vs/base/common/stream'; +import { VSBuffer, VSBufferReadable, readableToBuffer, bufferToReadable, streamToBuffer, bufferToStream, VSBufferReadableStream, VSBufferReadableBufferedStream, bufferedStreamToBuffer, newWriteableBufferStream } from 'vs/base/common/buffer'; +import { isReadableStream, transform, peekReadable, peekStream, isReadableBufferedStream } from 'vs/base/common/stream'; import { Queue } from 'vs/base/common/async'; import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; import { Schemas } from 'vs/base/common/network'; -import { createReadStream } from 'vs/platform/files/common/io'; +import { readFileIntoStream } from 'vs/platform/files/common/io'; export class FileService extends Disposable implements IFileService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly BUFFER_SIZE = 64 * 1024; - constructor(@ILogService private logService: ILogService) { + constructor(@ILogService private readonly logService: ILogService) { super(); } //#region File System Provider - private _onDidChangeFileSystemProviderRegistrations = this._register(new Emitter()); + private readonly _onDidChangeFileSystemProviderRegistrations = this._register(new Emitter()); readonly onDidChangeFileSystemProviderRegistrations = this._onDidChangeFileSystemProviderRegistrations.event; - private _onWillActivateFileSystemProvider = this._register(new Emitter()); + private readonly _onWillActivateFileSystemProvider = this._register(new Emitter()); readonly onWillActivateFileSystemProvider = this._onWillActivateFileSystemProvider.event; - private _onDidChangeFileSystemProviderCapabilities = this._register(new Emitter()); + private readonly _onDidChangeFileSystemProviderCapabilities = this._register(new Emitter()); readonly onDidChangeFileSystemProviderCapabilities = this._onDidChangeFileSystemProviderCapabilities.event; private readonly provider = new Map(); @@ -54,7 +54,7 @@ export class FileService extends Disposable implements IFileService { // Forward events from provider const providerDisposables = new DisposableStore(); - providerDisposables.add(provider.onDidChangeFile(changes => this._onDidFilesChange.fire(new FileChangesEvent(changes)))); + providerDisposables.add(provider.onDidChangeFile(changes => this._onDidFilesChange.fire(new FileChangesEvent(changes, this.getExtUri(provider).extUri)))); providerDisposables.add(provider.onDidChangeCapabilities(() => this._onDidChangeFileSystemProviderCapabilities.fire({ provider, scheme }))); if (typeof provider.onDidErrorOccur === 'function') { providerDisposables.add(provider.onDidErrorOccur(error => this._onError.fire(new Error(error)))); @@ -146,10 +146,10 @@ export class FileService extends Disposable implements IFileService { //#endregion - private _onDidRunOperation = this._register(new Emitter()); + private readonly _onDidRunOperation = this._register(new Emitter()); readonly onDidRunOperation = this._onDidRunOperation.event; - private _onError = this._register(new Emitter()); + private readonly _onError = this._register(new Emitter()); readonly onError = this._onError.event; //#region File Metadata Resolving @@ -287,12 +287,28 @@ export class FileService extends Disposable implements IFileService { //#region File Reading/Writing - async createFile(resource: URI, bufferOrReadableOrStream: VSBuffer | VSBufferReadable | VSBufferReadableStream = VSBuffer.fromString(''), options?: ICreateFileOptions): Promise { + async canCreateFile(resource: URI, options?: ICreateFileOptions): Promise { + try { + await this.doValidateCreateFile(resource, options); + } catch (error) { + return error; + } + + return true; + } + + private async doValidateCreateFile(resource: URI, options?: ICreateFileOptions): Promise { // validate overwrite if (!options?.overwrite && await this.exists(resource)) { throw new FileOperationError(localize('fileExists', "Unable to create file '{0}' that already exists when overwrite flag is not set", this.resourceForError(resource)), FileOperationResult.FILE_MODIFIED_SINCE, options); } + } + + async createFile(resource: URI, bufferOrReadableOrStream: VSBuffer | VSBufferReadable | VSBufferReadableStream = VSBuffer.fromString(''), options?: ICreateFileOptions): Promise { + + // validate + await this.doValidateCreateFile(resource, options); // do write into file (this will create it too) const fileStat = await this.writeFile(resource, bufferOrReadableOrStream); @@ -320,22 +336,30 @@ export class FileService extends Disposable implements IFileService { // to write is a Readable, we consume up to 3 chunks and try to write the data // unbuffered to reduce the overhead. If the Readable has more data to provide // we continue to write buffered. + let bufferOrReadableOrStreamOrBufferedStream: VSBuffer | VSBufferReadable | VSBufferReadableStream | VSBufferReadableBufferedStream; if (hasReadWriteCapability(provider) && !(bufferOrReadableOrStream instanceof VSBuffer)) { if (isReadableStream(bufferOrReadableOrStream)) { - bufferOrReadableOrStream = await consumeStreamWithLimit(bufferOrReadableOrStream, data => VSBuffer.concat(data), 3); + const bufferedStream = await peekStream(bufferOrReadableOrStream, 3); + if (bufferedStream.ended) { + bufferOrReadableOrStreamOrBufferedStream = VSBuffer.concat(bufferedStream.buffer); + } else { + bufferOrReadableOrStreamOrBufferedStream = bufferedStream; + } } else { - bufferOrReadableOrStream = consumeReadableWithLimit(bufferOrReadableOrStream, data => VSBuffer.concat(data), 3); + bufferOrReadableOrStreamOrBufferedStream = peekReadable(bufferOrReadableOrStream, data => VSBuffer.concat(data), 3); } + } else { + bufferOrReadableOrStreamOrBufferedStream = bufferOrReadableOrStream; } // write file: unbuffered (only if data to write is a buffer, or the provider has no buffered write capability) - if (!hasOpenReadWriteCloseCapability(provider) || (hasReadWriteCapability(provider) && bufferOrReadableOrStream instanceof VSBuffer)) { - await this.doWriteUnbuffered(provider, resource, bufferOrReadableOrStream); + if (!hasOpenReadWriteCloseCapability(provider) || (hasReadWriteCapability(provider) && bufferOrReadableOrStreamOrBufferedStream instanceof VSBuffer)) { + await this.doWriteUnbuffered(provider, resource, bufferOrReadableOrStreamOrBufferedStream); } // write file: buffered else { - await this.doWriteBuffered(provider, resource, bufferOrReadableOrStream instanceof VSBuffer ? bufferToReadable(bufferOrReadableOrStream) : bufferOrReadableOrStream); + await this.doWriteBuffered(provider, resource, bufferOrReadableOrStreamOrBufferedStream instanceof VSBuffer ? bufferToReadable(bufferOrReadableOrStreamOrBufferedStream) : bufferOrReadableOrStreamOrBufferedStream); } } catch (error) { throw new FileOperationError(localize('err.write', "Unable to write file '{0}' ({1})", this.resourceForError(resource), ensureFileSystemProviderError(error).toString()), toFileOperationResult(error), options); @@ -461,25 +485,24 @@ export class FileService extends Disposable implements IFileService { private readFileStreamed(provider: IFileSystemProviderWithFileReadStreamCapability, resource: URI, token: CancellationToken, options: IReadFileOptions = Object.create(null)): VSBufferReadableStream { const fileStream = provider.readFileStream(resource, options, token); - return this.transformFileReadStream(resource, fileStream, options); - } - - private readFileBuffered(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, resource: URI, token: CancellationToken, options: IReadFileOptions = Object.create(null)): VSBufferReadableStream { - const fileStream = createReadStream(provider, resource, { - ...options, - bufferSize: this.BUFFER_SIZE - }, token); - - return this.transformFileReadStream(resource, fileStream, options); - } - - private transformFileReadStream(resource: URI, stream: ReadableStreamEvents, options: IReadFileOptions): VSBufferReadableStream { - return transform(stream, { + return transform(fileStream, { data: data => data instanceof VSBuffer ? data : VSBuffer.wrap(data), error: error => new FileOperationError(localize('err.read', "Unable to read file '{0}' ({1})", this.resourceForError(resource), ensureFileSystemProviderError(error).toString()), toFileOperationResult(error), options) }, data => VSBuffer.concat(data)); } + private readFileBuffered(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, resource: URI, token: CancellationToken, options: IReadFileOptions = Object.create(null)): VSBufferReadableStream { + const stream = newWriteableBufferStream(); + + readFileIntoStream(provider, resource, stream, data => data, { + ...options, + bufferSize: this.BUFFER_SIZE, + errorTransformer: error => new FileOperationError(localize('err.read', "Unable to read file '{0}' ({1})", this.resourceForError(resource), ensureFileSystemProviderError(error).toString()), toFileOperationResult(error), options) + }, token); + + return stream; + } + private async readFileUnbuffered(provider: IFileSystemProviderWithFileReadWriteCapability, resource: URI, options?: IReadFileOptions): Promise { let buffer = await provider.readFile(resource); @@ -540,6 +563,29 @@ export class FileService extends Disposable implements IFileService { //#region Move/Copy/Delete/Create Folder + async canMove(source: URI, target: URI, overwrite?: boolean): Promise { + return this.doCanMoveCopy(source, target, 'move', overwrite); + } + + async canCopy(source: URI, target: URI, overwrite?: boolean): Promise { + return this.doCanMoveCopy(source, target, 'copy', overwrite); + } + + private async doCanMoveCopy(source: URI, target: URI, mode: 'move' | 'copy', overwrite?: boolean): Promise { + if (source.toString() !== target.toString()) { + try { + const sourceProvider = mode === 'move' ? this.throwIfFileSystemIsReadonly(await this.withWriteProvider(source), source) : await this.withReadProvider(source); + const targetProvider = this.throwIfFileSystemIsReadonly(await this.withWriteProvider(target), target); + + await this.doValidateMoveCopy(sourceProvider, source, targetProvider, target, mode, overwrite); + } catch (error) { + return error; + } + } + + return true; + } + async move(source: URI, target: URI, overwrite?: boolean): Promise { const sourceProvider = this.throwIfFileSystemIsReadonly(await this.withWriteProvider(source), source); const targetProvider = this.throwIfFileSystemIsReadonly(await this.withWriteProvider(target), target); @@ -673,16 +719,16 @@ export class FileService extends Disposable implements IFileService { // Check if source is equal or parent to target (requires providers to be the same) if (sourceProvider === targetProvider) { - const isPathCaseSensitive = !!(sourceProvider.capabilities & FileSystemProviderCapabilities.PathCaseSensitive); + const { extUri, isPathCaseSensitive } = this.getExtUri(sourceProvider); if (!isPathCaseSensitive) { - isSameResourceWithDifferentPathCase = isEqual(source, target, true /* ignore case */); + isSameResourceWithDifferentPathCase = extUri.isEqual(source, target); } if (isSameResourceWithDifferentPathCase && mode === 'copy') { throw new Error(localize('unableToMoveCopyError1', "Unable to copy when source '{0}' is same as target '{1}' with different path case on a case insensitive file system", this.resourceForError(source), this.resourceForError(target))); } - if (!isSameResourceWithDifferentPathCase && isEqualOrParent(target, source, !isPathCaseSensitive)) { + if (!isSameResourceWithDifferentPathCase && extUri.isEqualOrParent(target, source)) { throw new Error(localize('unableToMoveCopyError2', "Unable to move/copy when source '{0}' is parent of target '{1}'.", this.resourceForError(source), this.resourceForError(target))); } } @@ -699,8 +745,8 @@ export class FileService extends Disposable implements IFileService { // Special case: if the target is a parent of the source, we cannot delete // it as it would delete the source as well. In this case we have to throw if (sourceProvider === targetProvider) { - const isPathCaseSensitive = !!(sourceProvider.capabilities & FileSystemProviderCapabilities.PathCaseSensitive); - if (isEqualOrParent(source, target, !isPathCaseSensitive)) { + const { extUri } = this.getExtUri(sourceProvider); + if (extUri.isEqualOrParent(source, target)) { throw new Error(localize('unableToMoveCopyError4', "Unable to move/copy '{0}' into '{1}' since a file would replace the folder it is contained in.", this.resourceForError(source), this.resourceForError(target))); } } @@ -709,6 +755,15 @@ export class FileService extends Disposable implements IFileService { return { exists, isSameResourceWithDifferentPathCase }; } + private getExtUri(provider: IFileSystemProvider): { extUri: IExtUri, isPathCaseSensitive: boolean } { + const isPathCaseSensitive = !!(provider.capabilities & FileSystemProviderCapabilities.PathCaseSensitive); + + return { + extUri: isPathCaseSensitive ? extUri : extUriIgnorePathCase, + isPathCaseSensitive + }; + } + async createFolder(resource: URI): Promise { const provider = this.throwIfFileSystemIsReadonly(await this.withProvider(resource), resource); @@ -726,7 +781,8 @@ export class FileService extends Disposable implements IFileService { const directoriesToCreate: string[] = []; // mkdir until we reach root - while (!isEqual(directory, dirname(directory))) { + const { extUri } = this.getExtUri(provider); + while (!extUri.isEqual(directory, dirname(directory))) { try { const stat = await provider.stat(directory); if ((stat.type & FileType.Directory) === 0) { @@ -771,7 +827,17 @@ export class FileService extends Disposable implements IFileService { } } - async del(resource: URI, options?: { useTrash?: boolean; recursive?: boolean; }): Promise { + async canDelete(resource: URI, options?: { useTrash?: boolean; recursive?: boolean; }): Promise { + try { + await this.doValidateDelete(resource, options); + } catch (error) { + return error; + } + + return true; + } + + private async doValidateDelete(resource: URI, options?: { useTrash?: boolean; recursive?: boolean; }): Promise { const provider = this.throwIfFileSystemIsReadonly(await this.withProvider(resource), resource); // Validate trash support @@ -795,6 +861,15 @@ export class FileService extends Disposable implements IFileService { } } + return provider; + } + + async del(resource: URI, options?: { useTrash?: boolean; recursive?: boolean; }): Promise { + const provider = await this.doValidateDelete(resource, options); + + const useTrash = !!options?.useTrash; + const recursive = !!options?.recursive; + // Delete through provider await provider.delete(resource, { recursive, useTrash }); @@ -806,10 +881,10 @@ export class FileService extends Disposable implements IFileService { //#region File Watching - private _onDidFilesChange = this._register(new Emitter()); + private readonly _onDidFilesChange = this._register(new Emitter()); readonly onDidFilesChange = this._onDidFilesChange.event; - private activeWatchers = new Map(); + private readonly activeWatchers = new Map(); watch(resource: URI, options: IWatchOptions = { recursive: false, excludes: [] }): IDisposable { let watchDisposed = false; @@ -855,8 +930,10 @@ export class FileService extends Disposable implements IFileService { } private toWatchKey(provider: IFileSystemProvider, resource: URI, options: IWatchOptions): string { + const { extUri } = this.getExtUri(provider); + return [ - this.toMapKey(provider, resource), // lowercase path if the provider is case insensitive + extUri.getComparisonKey(resource), // lowercase path if the provider is case insensitive String(options.recursive), // use recursive: true | false as part of the key options.excludes.join() // use excludes as part of the key ].join(); @@ -873,13 +950,15 @@ export class FileService extends Disposable implements IFileService { //#region Helpers - private writeQueues: Map> = new Map(); + private readonly writeQueues: Map> = new Map(); private ensureWriteQueue(provider: IFileSystemProvider, resource: URI): Queue { + const { extUri } = this.getExtUri(provider); + const queueKey = extUri.getComparisonKey(resource); + // ensure to never write to the same resource without finishing // the one write. this ensures a write finishes consistently // (even with error) before another write is done. - const queueKey = this.toMapKey(provider, resource); let writeQueue = this.writeQueues.get(queueKey); if (!writeQueue) { writeQueue = new Queue(); @@ -895,13 +974,7 @@ export class FileService extends Disposable implements IFileService { return writeQueue; } - private toMapKey(provider: IFileSystemProvider, resource: URI): string { - const isPathCaseSensitive = !!(provider.capabilities & FileSystemProviderCapabilities.PathCaseSensitive); - - return isPathCaseSensitive ? resource.toString() : resource.toString().toLowerCase(); - } - - private async doWriteBuffered(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, resource: URI, readableOrStream: VSBufferReadable | VSBufferReadableStream): Promise { + private async doWriteBuffered(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, resource: URI, readableOrStreamOrBufferedStream: VSBufferReadable | VSBufferReadableStream | VSBufferReadableBufferedStream): Promise { return this.ensureWriteQueue(provider, resource).queue(async () => { // open handle @@ -909,10 +982,10 @@ export class FileService extends Disposable implements IFileService { // write into handle until all bytes from buffer have been written try { - if (isReadableStream(readableOrStream)) { - await this.doWriteStreamBufferedQueued(provider, handle, readableOrStream); + if (isReadableStream(readableOrStreamOrBufferedStream) || isReadableBufferedStream(readableOrStreamOrBufferedStream)) { + await this.doWriteStreamBufferedQueued(provider, handle, readableOrStreamOrBufferedStream); } else { - await this.doWriteReadableBufferedQueued(provider, handle, readableOrStream); + await this.doWriteReadableBufferedQueued(provider, handle, readableOrStreamOrBufferedStream); } } catch (error) { throw ensureFileSystemProviderError(error); @@ -924,9 +997,34 @@ export class FileService extends Disposable implements IFileService { }); } - private doWriteStreamBufferedQueued(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, handle: number, stream: VSBufferReadableStream): Promise { - return new Promise((resolve, reject) => { - let posInFile = 0; + private async doWriteStreamBufferedQueued(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, handle: number, streamOrBufferedStream: VSBufferReadableStream | VSBufferReadableBufferedStream): Promise { + let posInFile = 0; + let stream: VSBufferReadableStream; + + // Buffered stream: consume the buffer first by writing + // it to the target before reading from the stream. + if (isReadableBufferedStream(streamOrBufferedStream)) { + if (streamOrBufferedStream.buffer.length > 0) { + const chunk = VSBuffer.concat(streamOrBufferedStream.buffer); + await this.doWriteBuffer(provider, handle, chunk, chunk.byteLength, posInFile, 0); + + posInFile += chunk.byteLength; + } + + // If the stream has been consumed, return early + if (streamOrBufferedStream.ended) { + return; + } + + stream = streamOrBufferedStream.stream; + } + + // Unbuffered stream - just take as is + else { + stream = streamOrBufferedStream; + } + + return new Promise(async (resolve, reject) => { stream.on('data', async chunk => { @@ -967,26 +1065,31 @@ export class FileService extends Disposable implements IFileService { private async doWriteBuffer(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, handle: number, buffer: VSBuffer, length: number, posInFile: number, posInBuffer: number): Promise { let totalBytesWritten = 0; while (totalBytesWritten < length) { + + // Write through the provider const bytesWritten = await provider.write(handle, posInFile + totalBytesWritten, buffer.buffer, posInBuffer + totalBytesWritten, length - totalBytesWritten); totalBytesWritten += bytesWritten; } } - private async doWriteUnbuffered(provider: IFileSystemProviderWithFileReadWriteCapability, resource: URI, bufferOrReadableOrStream: VSBuffer | VSBufferReadable | VSBufferReadableStream): Promise { - return this.ensureWriteQueue(provider, resource).queue(() => this.doWriteUnbufferedQueued(provider, resource, bufferOrReadableOrStream)); + private async doWriteUnbuffered(provider: IFileSystemProviderWithFileReadWriteCapability, resource: URI, bufferOrReadableOrStreamOrBufferedStream: VSBuffer | VSBufferReadable | VSBufferReadableStream | VSBufferReadableBufferedStream): Promise { + return this.ensureWriteQueue(provider, resource).queue(() => this.doWriteUnbufferedQueued(provider, resource, bufferOrReadableOrStreamOrBufferedStream)); } - private async doWriteUnbufferedQueued(provider: IFileSystemProviderWithFileReadWriteCapability, resource: URI, bufferOrReadableOrStream: VSBuffer | VSBufferReadable | VSBufferReadableStream): Promise { + private async doWriteUnbufferedQueued(provider: IFileSystemProviderWithFileReadWriteCapability, resource: URI, bufferOrReadableOrStreamOrBufferedStream: VSBuffer | VSBufferReadable | VSBufferReadableStream | VSBufferReadableBufferedStream): Promise { let buffer: VSBuffer; - if (bufferOrReadableOrStream instanceof VSBuffer) { - buffer = bufferOrReadableOrStream; - } else if (isReadableStream(bufferOrReadableOrStream)) { - buffer = await streamToBuffer(bufferOrReadableOrStream); + if (bufferOrReadableOrStreamOrBufferedStream instanceof VSBuffer) { + buffer = bufferOrReadableOrStreamOrBufferedStream; + } else if (isReadableStream(bufferOrReadableOrStreamOrBufferedStream)) { + buffer = await streamToBuffer(bufferOrReadableOrStreamOrBufferedStream); + } else if (isReadableBufferedStream(bufferOrReadableOrStreamOrBufferedStream)) { + buffer = await bufferedStreamToBuffer(bufferOrReadableOrStreamOrBufferedStream); } else { - buffer = readableToBuffer(bufferOrReadableOrStream); + buffer = readableToBuffer(bufferOrReadableOrStreamOrBufferedStream); } - return provider.writeFile(resource, buffer.buffer, { create: true, overwrite: true }); + // Write through the provider + await provider.writeFile(resource, buffer.buffer, { create: true, overwrite: true }); } private async doPipeBuffered(sourceProvider: IFileSystemProviderWithOpenReadWriteCloseCapability, source: URI, targetProvider: IFileSystemProviderWithOpenReadWriteCloseCapability, target: URI): Promise { diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index 7438ec84efe..39b57391424 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -11,7 +11,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { Event } from 'vs/base/common/event'; import { startsWithIgnoreCase } from 'vs/base/common/strings'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { isEqualOrParent, isEqual } from 'vs/base/common/resources'; +import { IExtUri } from 'vs/base/common/resources'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { VSBuffer, VSBufferReadable, VSBufferReadableStream } from 'vs/base/common/buffer'; import { ReadableStreamEvents } from 'vs/base/common/stream'; @@ -21,7 +21,7 @@ export const IFileService = createDecorator('fileService'); export interface IFileService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * An event that is fired when a file system provider is added or removed @@ -29,7 +29,7 @@ export interface IFileService { readonly onDidChangeFileSystemProviderRegistrations: Event; /** - * An even that is fired when a registered file system provider changes it's capabilities. + * An event that is fired when a registered file system provider changes it's capabilities. */ readonly onDidChangeFileSystemProviderCapabilities: Event; @@ -121,6 +121,12 @@ export interface IFileService { */ move(source: URI, target: URI, overwrite?: boolean): Promise; + /** + * Find out if a move operation is possible given the arguments. No changes on disk will + * be performed. Returns an Error if the operation cannot be done. + */ + canMove(source: URI, target: URI, overwrite?: boolean): Promise; + /** * Copies the file/folder to a path identified by the resource. * @@ -128,6 +134,18 @@ export interface IFileService { */ copy(source: URI, target: URI, overwrite?: boolean): Promise; + /** + * Find out if a copy operation is possible given the arguments. No changes on disk will + * be performed. Returns an Error if the operation cannot be done. + */ + canCopy(source: URI, target: URI, overwrite?: boolean): Promise; + + /** + * Find out if a file create operation is possible given the arguments. No changes on disk will + * be performed. Returns an Error if the operation cannot be done. + */ + canCreateFile(resource: URI, options?: ICreateFileOptions): Promise; + /** * Creates a new file with the given path and optional contents. The returned promise * will have the stat model object as a result. @@ -149,6 +167,12 @@ export interface IFileService { */ del(resource: URI, options?: { useTrash?: boolean, recursive?: boolean }): Promise; + /** + * Find out if a delete operation is possible given the arguments. No changes on disk will + * be performed. Returns an Error if the operation cannot be done. + */ + canDelete(resource: URI, options?: { useTrash?: boolean, recursive?: boolean }): Promise; + /** * Allows to start a watcher that reports file/folder change events on the provided resource. * @@ -265,7 +289,7 @@ export interface IFileSystemProvider { readFile?(resource: URI): Promise; writeFile?(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise; - readFileStream?(resource: URI, opts: FileReadStreamOptions, token?: CancellationToken): ReadableStreamEvents; + readFileStream?(resource: URI, opts: FileReadStreamOptions, token: CancellationToken): ReadableStreamEvents; open?(resource: URI, opts: FileOpenOptions): Promise; close?(fd: number): Promise; @@ -302,7 +326,7 @@ export function hasOpenReadWriteCloseCapability(provider: IFileSystemProvider): } export interface IFileSystemProviderWithFileReadStreamCapability extends IFileSystemProvider { - readFileStream(resource: URI, opts: FileReadStreamOptions, token?: CancellationToken): ReadableStreamEvents; + readFileStream(resource: URI, opts: FileReadStreamOptions, token: CancellationToken): ReadableStreamEvents; } export function hasFileReadStreamCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithFileReadStreamCapability { @@ -473,7 +497,7 @@ export interface IFileChange { export class FileChangesEvent { - constructor(public readonly changes: readonly IFileChange[]) { } + constructor(public readonly changes: readonly IFileChange[], private readonly extUri: IExtUri) { } /** * Returns true if this change event contains the provided file with the given change type (if provided). In case of @@ -494,10 +518,10 @@ export class FileChangesEvent { // For deleted also return true when deleted folder is parent of target path if (change.type === FileChangeType.DELETED) { - return isEqualOrParent(resource, change.resource); + return this.extUri.isEqualOrParent(resource, change.resource); } - return isEqual(resource, change.resource); + return this.extUri.isEqual(resource, change.resource); }); } @@ -552,6 +576,10 @@ export class FileChangesEvent { return change.type === type; }); } + + filter(filterFn: (change: IFileChange) => boolean): FileChangesEvent { + return new FileChangesEvent(this.changes.filter(change => filterFn(change)), this.extUri); + } } export function isParent(path: string, candidate: string, ignoreCase?: boolean): boolean { @@ -824,7 +852,6 @@ export function etag(stat: { mtime: number | undefined, size: number | undefined return stat.mtime.toString(29) + stat.size.toString(31); } - export function whenProviderRegistered(file: URI, fileService: IFileService): Promise { if (fileService.canHandleResource(URI.from({ scheme: file.scheme }))) { return Promise.resolve(); @@ -838,3 +865,39 @@ export function whenProviderRegistered(file: URI, fileService: IFileService): Pr }); }); } + +/** + * Desktop only: limits for memory sizes + */ +export const MIN_MAX_MEMORY_SIZE_MB = 2048; +export const FALLBACK_MAX_MEMORY_SIZE_MB = 4096; + +/** + * Helper to format a raw byte size into a human readable label. + */ +export 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)); + } +} diff --git a/src/vs/platform/files/common/inMemoryFilesystemProvider.ts b/src/vs/platform/files/common/inMemoryFilesystemProvider.ts index ad689bb57fc..e5708db8bed 100644 --- a/src/vs/platform/files/common/inMemoryFilesystemProvider.ts +++ b/src/vs/platform/files/common/inMemoryFilesystemProvider.ts @@ -11,7 +11,7 @@ import { URI } from 'vs/base/common/uri'; class File implements IStat { - type: FileType; + type: FileType.File; ctime: number; mtime: number; size: number; @@ -30,7 +30,7 @@ class File implements IStat { class Directory implements IStat { - type: FileType; + type: FileType.Directory; ctime: number; mtime: number; size: number; @@ -52,7 +52,9 @@ export type Entry = File | Directory; export class InMemoryFileSystemProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability { - readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite; + readonly capabilities: FileSystemProviderCapabilities = + FileSystemProviderCapabilities.FileReadWrite + | FileSystemProviderCapabilities.PathCaseSensitive; readonly onDidChangeCapabilities: Event = Event.None; root = new Directory(''); diff --git a/src/vs/platform/files/common/io.ts b/src/vs/platform/files/common/io.ts index 1fb8ff0b285..1412411593a 100644 --- a/src/vs/platform/files/common/io.ts +++ b/src/vs/platform/files/common/io.ts @@ -5,10 +5,11 @@ import { localize } from 'vs/nls'; import { URI } from 'vs/base/common/uri'; -import { VSBuffer, VSBufferWriteableStream, newWriteableBufferStream, VSBufferReadableStream } from 'vs/base/common/buffer'; +import { VSBuffer } from 'vs/base/common/buffer'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IFileSystemProviderWithOpenReadWriteCloseCapability, FileReadStreamOptions, createFileSystemProviderError, FileSystemProviderErrorCode, ensureFileSystemProviderError } from 'vs/platform/files/common/files'; import { canceled } from 'vs/base/common/errors'; +import { IErrorTransformer, IDataTransformer, WriteableStream } from 'vs/base/common/stream'; export interface ICreateReadStreamOptions extends FileReadStreamOptions { @@ -16,21 +17,40 @@ export interface ICreateReadStreamOptions extends FileReadStreamOptions { * The size of the buffer to use before sending to the stream. */ bufferSize: number; + + /** + * Allows to massage any possibly error that happens during reading. + */ + errorTransformer?: IErrorTransformer; } -export function createReadStream(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, resource: URI, options: ICreateReadStreamOptions, token?: CancellationToken): VSBufferReadableStream { - const stream = newWriteableBufferStream(); - - // do not await reading but simply return the stream directly since it operates - // via events. finally end the stream and send through the possible error +/** + * A helper to read a file from a provider with open/read/close capability into a stream. + */ +export async function readFileIntoStream( + provider: IFileSystemProviderWithOpenReadWriteCloseCapability, + resource: URI, + target: WriteableStream, + transformer: IDataTransformer, + options: ICreateReadStreamOptions, + token: CancellationToken +): Promise { let error: Error | undefined = undefined; - doReadFileIntoStream(provider, resource, stream, options, token).then(undefined, err => error = err).finally(() => stream.end(error)); + try { + await doReadFileIntoStream(provider, resource, target, transformer, options, token); + } catch (err) { + error = err; + } finally { + if (error && options.errorTransformer) { + error = options.errorTransformer(error); + } - return stream; + target.end(error); + } } -async function doReadFileIntoStream(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, resource: URI, stream: VSBufferWriteableStream, options: ICreateReadStreamOptions, token?: CancellationToken): Promise { +async function doReadFileIntoStream(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, resource: URI, target: WriteableStream, transformer: IDataTransformer, options: ICreateReadStreamOptions, token: CancellationToken): Promise { // Check for cancellation throwIfCancelled(token); @@ -65,7 +85,7 @@ async function doReadFileIntoStream(provider: IFileSystemProviderWithOpenReadWri // when buffer full, create a new one and emit it through stream if (posInBuffer === buffer.byteLength) { - stream.write(buffer); + await target.write(transformer(buffer)); buffer = VSBuffer.alloc(Math.min(options.bufferSize, typeof allowedRemainingBytes === 'number' ? allowedRemainingBytes : options.bufferSize)); @@ -80,7 +100,7 @@ async function doReadFileIntoStream(provider: IFileSystemProviderWithOpenReadWri lastChunkLength = Math.min(posInBuffer, allowedRemainingBytes); } - stream.write(buffer.slice(0, lastChunkLength)); + target.write(transformer(buffer.slice(0, lastChunkLength))); } } catch (error) { throw ensureFileSystemProviderError(error); @@ -89,8 +109,8 @@ async function doReadFileIntoStream(provider: IFileSystemProviderWithOpenReadWri } } -function throwIfCancelled(token?: CancellationToken): boolean { - if (token && token.isCancellationRequested) { +function throwIfCancelled(token: CancellationToken): boolean { + if (token.isCancellationRequested) { throw canceled(); } diff --git a/src/vs/workbench/services/log/common/keyValueLogProvider.ts b/src/vs/platform/files/common/keyValueFileSystemProvider.ts similarity index 80% rename from src/vs/workbench/services/log/common/keyValueLogProvider.ts rename to src/vs/platform/files/common/keyValueFileSystemProvider.ts index da0fdb3f167..15742e9b9ec 100644 --- a/src/vs/workbench/services/log/common/keyValueLogProvider.ts +++ b/src/vs/platform/files/common/keyValueFileSystemProvider.ts @@ -8,22 +8,26 @@ import { IFileSystemProviderWithFileReadWriteCapability, FileSystemProviderCapab import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { VSBuffer } from 'vs/base/common/buffer'; -import { isEqualOrParent, joinPath, relativePath } from 'vs/base/common/resources'; -import { values } from 'vs/base/common/map'; +import { joinPath, extUri, dirname } from 'vs/base/common/resources'; import { localize } from 'vs/nls'; -export abstract class KeyValueLogProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability { +export abstract class KeyValueFileSystemProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability { - readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite; + readonly capabilities: FileSystemProviderCapabilities = + FileSystemProviderCapabilities.FileReadWrite + | FileSystemProviderCapabilities.PathCaseSensitive; readonly onDidChangeCapabilities: Event = Event.None; private readonly _onDidChangeFile = this._register(new Emitter()); readonly onDidChangeFile: Event = this._onDidChangeFile.event; private readonly versions: Map = new Map(); + private readonly dirs: Set = new Set(); constructor(private readonly scheme: string) { super(); + // Add root directory by default + this.dirs.add('/'); } watch(resource: URI, opts: IWatchOptions): IDisposable { @@ -31,6 +35,17 @@ export abstract class KeyValueLogProvider extends Disposable implements IFileSys } async mkdir(resource: URI): Promise { + try { + const resourceStat = await this.stat(resource); + if (resourceStat.type === FileType.File) { + throw createFileSystemProviderError(localize('fileNotDirectory', "File is not a directory"), FileSystemProviderErrorCode.FileNotADirectory); + } + } catch (error) { /* Ignore */ } + + // Make sure parent dir exists + await this.stat(dirname(resource)); + + this.dirs.add(resource.path); } async stat(resource: URI): Promise { @@ -53,6 +68,14 @@ export abstract class KeyValueLogProvider extends Disposable implements IFileSys size: 0 }; } + if (this.dirs.has(resource.path)) { + return { + type: FileType.Directory, + ctime: 0, + mtime: 0, + size: 0 + }; + } throw createFileSystemProviderError(localize('fileNotExists', "File does not exist"), FileSystemProviderErrorCode.FileNotFound); } @@ -65,15 +88,15 @@ export abstract class KeyValueLogProvider extends Disposable implements IFileSys const files: Map = new Map(); for (const key of keys) { const keyResource = this.toResource(key); - if (isEqualOrParent(keyResource, resource, false)) { - const path = relativePath(resource, keyResource, false); + if (extUri.isEqualOrParent(keyResource, resource)) { + const path = extUri.relativePath(resource, keyResource); if (path) { const keySegments = path.split('/'); files.set(keySegments[0], [keySegments[0], keySegments.length === 1 ? FileType.File : FileType.Directory]); } } } - return values(files); + return [...files.values()]; } async readFile(resource: URI): Promise { diff --git a/src/vs/platform/files/electron-browser/diskFileSystemProvider.ts b/src/vs/platform/files/electron-browser/diskFileSystemProvider.ts index 2c23af873f1..1ba8c20f3e4 100644 --- a/src/vs/platform/files/electron-browser/diskFileSystemProvider.ts +++ b/src/vs/platform/files/electron-browser/diskFileSystemProvider.ts @@ -3,15 +3,24 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { shell } from 'electron'; -import { DiskFileSystemProvider as NodeDiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; +import { DiskFileSystemProvider as NodeDiskFileSystemProvider, IDiskFileSystemProviderOptions } from 'vs/platform/files/node/diskFileSystemProvider'; import { FileDeleteOptions, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; import { isWindows } from 'vs/base/common/platform'; import { localize } from 'vs/nls'; import { basename } from 'vs/base/common/path'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; export class DiskFileSystemProvider extends NodeDiskFileSystemProvider { + constructor( + logService: ILogService, + private readonly electronService: IElectronService, + options?: IDiskFileSystemProviderOptions + ) { + super(logService, options); + } + get capabilities(): FileSystemProviderCapabilities { if (!this._capabilities) { this._capabilities = super.capabilities | FileSystemProviderCapabilities.Trash; @@ -25,9 +34,9 @@ export class DiskFileSystemProvider extends NodeDiskFileSystemProvider { return super.doDelete(filePath, opts); } - const result = shell.moveItemToTrash(filePath); + const result = await this.electronService.moveItemToTrash(filePath); if (!result) { throw new Error(isWindows ? localize('binFailed', "Failed to move '{0}' to the recycle bin", basename(filePath)) : localize('trashFailed', "Failed to move '{0}' to the trash", basename(filePath))); } } -} \ No newline at end of file +} diff --git a/src/vs/platform/files/node/diskFileSystemProvider.ts b/src/vs/platform/files/node/diskFileSystemProvider.ts index 1524e074b99..c551bcac9c2 100644 --- a/src/vs/platform/files/node/diskFileSystemProvider.ts +++ b/src/vs/platform/files/node/diskFileSystemProvider.ts @@ -22,11 +22,11 @@ import { FileWatcher as UnixWatcherService } from 'vs/platform/files/node/watche import { FileWatcher as WindowsWatcherService } from 'vs/platform/files/node/watcher/win32/watcherService'; import { FileWatcher as NsfwWatcherService } from 'vs/platform/files/node/watcher/nsfw/watcherService'; import { FileWatcher as NodeJSWatcherService } from 'vs/platform/files/node/watcher/nodejs/watcherService'; -import { VSBuffer } from 'vs/base/common/buffer'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { ReadableStreamEvents, transform } from 'vs/base/common/stream'; -import { createReadStream } from 'vs/platform/files/common/io'; +import { ReadableStreamEvents, newWriteableStream } from 'vs/base/common/stream'; +import { readFileIntoStream } from 'vs/platform/files/common/io'; import { insert } from 'vs/base/common/arrays'; +import { VSBuffer } from 'vs/base/common/buffer'; export interface IWatcherOptions { pollingInterval?: number; @@ -46,7 +46,10 @@ export class DiskFileSystemProvider extends Disposable implements private readonly BUFFER_SIZE = this.options?.bufferSize || 64 * 1024; - constructor(private logService: ILogService, private options?: IDiskFileSystemProviderOptions) { + constructor( + private readonly logService: ILogService, + private readonly options?: IDiskFileSystemProviderOptions + ) { super(); } @@ -154,13 +157,15 @@ export class DiskFileSystemProvider extends Disposable implements } } - readFileStream(resource: URI, opts: FileReadStreamOptions, token?: CancellationToken): ReadableStreamEvents { - const fileStream = createReadStream(this, resource, { + readFileStream(resource: URI, opts: FileReadStreamOptions, token: CancellationToken): ReadableStreamEvents { + const stream = newWriteableStream(data => VSBuffer.concat(data.map(data => VSBuffer.wrap(data))).buffer); + + readFileIntoStream(this, resource, stream, data => data.buffer, { ...opts, bufferSize: this.BUFFER_SIZE }, token); - return transform(fileStream, { data: data => data.buffer }, data => VSBuffer.concat(data.map(data => VSBuffer.wrap(data))).buffer); + return stream; } async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { @@ -196,9 +201,9 @@ export class DiskFileSystemProvider extends Disposable implements } } - private mapHandleToPos: Map = new Map(); + private readonly mapHandleToPos: Map = new Map(); - private writeHandles: Set = new Set(); + private readonly writeHandles: Set = new Set(); private canFlush: boolean = true; async open(resource: URI, opts: FileOpenOptions): Promise { @@ -472,12 +477,11 @@ export class DiskFileSystemProvider extends Disposable implements } private async validateTargetDeleted(from: URI, to: URI, mode: 'move' | 'copy', overwrite?: boolean): Promise { - const isPathCaseSensitive = !!(this.capabilities & FileSystemProviderCapabilities.PathCaseSensitive); - const fromFilePath = this.toFilePath(from); const toFilePath = this.toFilePath(to); let isSameResourceWithDifferentPathCase = false; + const isPathCaseSensitive = !!(this.capabilities & FileSystemProviderCapabilities.PathCaseSensitive); if (!isPathCaseSensitive) { isSameResourceWithDifferentPathCase = isEqual(fromFilePath, toFilePath, true /* ignore case */); } @@ -501,14 +505,14 @@ export class DiskFileSystemProvider extends Disposable implements //#region File Watching - private _onDidWatchErrorOccur = this._register(new Emitter()); + private readonly _onDidWatchErrorOccur = this._register(new Emitter()); readonly onDidErrorOccur = this._onDidWatchErrorOccur.event; - private _onDidChangeFile = this._register(new Emitter()); + private readonly _onDidChangeFile = this._register(new Emitter()); readonly onDidChangeFile = this._onDidChangeFile.event; private recursiveWatcher: WindowsWatcherService | UnixWatcherService | NsfwWatcherService | undefined; - private recursiveFoldersToWatch: { path: string, excludes: string[] }[] = []; + private readonly recursiveFoldersToWatch: { path: string, excludes: string[] }[] = []; private recursiveWatchRequestDelayer = this._register(new ThrottledDelayer(0)); private recursiveWatcherLogLevelListener: IDisposable | undefined; diff --git a/src/vs/platform/files/test/common/files.test.ts b/src/vs/platform/files/test/common/files.test.ts index 5956d293b4c..41cd8b4ccb6 100644 --- a/src/vs/platform/files/test/common/files.test.ts +++ b/src/vs/platform/files/test/common/files.test.ts @@ -8,8 +8,8 @@ import { URI } from 'vs/base/common/uri'; import { isEqual, isEqualOrParent } from 'vs/base/common/extpath'; import { FileChangeType, FileChangesEvent, isParent } from 'vs/platform/files/common/files'; import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; -// eslint-disable-next-line code-import-patterns import { toResource } from 'vs/base/test/common/utils'; +import { extUri } from 'vs/base/common/resources'; suite('Files', () => { @@ -22,7 +22,7 @@ suite('Files', () => { { resource: toResource.call(this, '/bar/folder'), type: FileChangeType.DELETED } ]; - let r1 = new FileChangesEvent(changes); + let r1 = new FileChangesEvent(changes, extUri); assert(!r1.contains(toResource.call(this, '/foo'), FileChangeType.UPDATED)); assert(r1.contains(toResource.call(this, '/foo/updated.txt'), FileChangeType.UPDATED)); diff --git a/src/vs/platform/files/test/electron-browser/diskFileService.test.ts b/src/vs/platform/files/test/electron-browser/diskFileService.test.ts index 803ff185ab9..3a290e0c0c1 100644 --- a/src/vs/platform/files/test/electron-browser/diskFileService.test.ts +++ b/src/vs/platform/files/test/electron-browser/diskFileService.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { tmpdir } from 'os'; import { FileService } from 'vs/platform/files/common/fileService'; import { Schemas } from 'vs/base/common/network'; -import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider'; +import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { generateUuid } from 'vs/base/common/uuid'; import { join, basename, dirname, posix } from 'vs/base/common/path'; @@ -463,6 +463,7 @@ suite('Disk File Service', function () { const resource = URI.file(join(testDir, 'deep', 'conway.js')); const source = await service.resolve(resource); + assert.equal(await service.canDelete(source.resource, { useTrash }), true); await service.del(source.resource, { useTrash }); assert.equal(existsSync(source.resource.fsPath), false); @@ -492,6 +493,7 @@ suite('Disk File Service', function () { let event: FileOperationEvent; disposables.add(service.onDidRunOperation(e => event = e)); + assert.equal(await service.canDelete(source.resource), true); await service.del(source.resource); assert.equal(existsSync(source.resource.fsPath), false); @@ -511,6 +513,7 @@ suite('Disk File Service', function () { let event: FileOperationEvent; disposables.add(service.onDidRunOperation(e => event = e)); + assert.equal(await service.canDelete(link), true); await service.del(link); assert.equal(existsSync(link.fsPath), false); @@ -535,6 +538,7 @@ suite('Disk File Service', function () { const resource = URI.file(join(testDir, 'deep')); const source = await service.resolve(resource); + assert.equal(await service.canDelete(source.resource, { recursive: true, useTrash }), true); await service.del(source.resource, { recursive: true, useTrash }); assert.equal(existsSync(source.resource.fsPath), false); @@ -547,6 +551,8 @@ suite('Disk File Service', function () { const resource = URI.file(join(testDir, 'deep')); const source = await service.resolve(resource); + assert.ok((await service.canDelete(source.resource)) instanceof Error); + let error; try { await service.del(source.resource); @@ -566,6 +572,7 @@ suite('Disk File Service', function () { const target = URI.file(join(dirname(source.fsPath), 'other.html')); + assert.equal(await service.canMove(source, target), true); const renamed = await service.move(source, target); assert.equal(existsSync(renamed.resource.fsPath), true); @@ -646,6 +653,7 @@ suite('Disk File Service', function () { const target = URI.file(join(dirname(source.fsPath), 'other.html')).with({ scheme: testSchema }); + assert.equal(await service.canMove(source, target), true); const renamed = await service.move(source, target); assert.equal(existsSync(renamed.resource.fsPath), true); @@ -670,6 +678,7 @@ suite('Disk File Service', function () { const source = URI.file(join(testDir, 'index.html')); + assert.equal(await service.canMove(source, URI.file(join(dirname(source.fsPath), renameToPath))), true); const renamed = await service.move(source, URI.file(join(dirname(source.fsPath), renameToPath))); assert.equal(existsSync(renamed.resource.fsPath), true); @@ -686,6 +695,7 @@ suite('Disk File Service', function () { const source = URI.file(join(testDir, 'deep')); + assert.equal(await service.canMove(source, URI.file(join(dirname(source.fsPath), 'deeper'))), true); const renamed = await service.move(source, URI.file(join(dirname(source.fsPath), 'deeper'))); assert.equal(existsSync(renamed.resource.fsPath), true); @@ -733,6 +743,7 @@ suite('Disk File Service', function () { const target = URI.file(join(dirname(source.fsPath), 'deeper')).with({ scheme: testSchema }); + assert.equal(await service.canMove(source, target), true); const renamed = await service.move(source, target); assert.equal(existsSync(renamed.resource.fsPath), true); @@ -757,6 +768,7 @@ suite('Disk File Service', function () { assert.ok(source.size > 0); const renamedResource = URI.file(join(dirname(source.resource.fsPath), 'INDEX.html')); + assert.equal(await service.canMove(source.resource, renamedResource), true); let renamed = await service.move(source.resource, renamedResource); assert.equal(existsSync(renamedResource.fsPath), true); @@ -777,6 +789,7 @@ suite('Disk File Service', function () { const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true }); assert.ok(source.size > 0); + assert.equal(await service.canMove(source.resource, URI.file(source.resource.fsPath)), true); let renamed = await service.move(source.resource, URI.file(source.resource.fsPath)); assert.equal(existsSync(renamed.resource.fsPath), true); @@ -800,6 +813,7 @@ suite('Disk File Service', function () { const targetParent = URI.file(testDir); const target = targetParent.with({ path: posix.join(targetParent.path, posix.basename(source.resource.path)) }); + assert.equal(await service.canMove(source.resource, target), true); let renamed = await service.move(source.resource, target); assert.equal(existsSync(renamed.resource.fsPath), true); @@ -821,6 +835,8 @@ suite('Disk File Service', function () { const originalSize = source.size; assert.ok(originalSize > 0); + assert.ok((await service.canMove(URI.file(testDir), URI.file(join(testDir, 'binary.txt'))) instanceof Error)); + let error; try { await service.move(URI.file(testDir), URI.file(join(testDir, 'binary.txt'))); @@ -843,6 +859,8 @@ suite('Disk File Service', function () { const originalSize = source.size; assert.ok(originalSize > 0); + assert.ok((await service.canMove(source.resource, URI.file(join(testDir, 'binary.txt'))) instanceof Error)); + let error; try { await service.move(source.resource, URI.file(join(testDir, 'binary.txt'))); @@ -876,6 +894,7 @@ suite('Disk File Service', function () { const f = await service.createFolder(folderResource); const source = URI.file(join(testDir, 'deep', 'conway.js')); + assert.equal(await service.canMove(source, f.resource, true), true); const moved = await service.move(source, f.resource, true); assert.equal(existsSync(moved.resource.fsPath), true); @@ -930,6 +949,7 @@ suite('Disk File Service', function () { const source = await service.resolve(URI.file(join(testDir, sourceName))); const target = URI.file(join(testDir, 'other.html')); + assert.equal(await service.canCopy(source.resource, target), true); const copied = await service.copy(source.resource, target); assert.equal(existsSync(copied.resource.fsPath), true); @@ -965,6 +985,7 @@ suite('Disk File Service', function () { const f = await service.createFolder(folderResource); const source = URI.file(join(testDir, 'deep', 'conway.js')); + assert.equal(await service.canCopy(source, f.resource, true), true); const copied = await service.copy(source, f.resource, true); assert.equal(existsSync(copied.resource.fsPath), true); @@ -984,6 +1005,8 @@ suite('Disk File Service', function () { const target = URI.file(join(dirname(source.resource.fsPath), 'INDEX.html')); + const canCopy = await service.canCopy(source.resource, target); + let error; let copied: IFileStatWithMetadata; try { @@ -994,12 +1017,14 @@ suite('Disk File Service', function () { if (isLinux) { assert.ok(!error); + assert.equal(canCopy, true); assert.equal(existsSync(copied!.resource.fsPath), true); assert.ok(readdirSync(testDir).some(f => f === 'INDEX.html')); assert.equal(source.size, copied!.size); } else { assert.ok(error); + assert.ok(canCopy instanceof Error); source = await service.resolve(source.resource, { resolveMetadata: true }); assert.equal(originalSize, source.size); @@ -1013,6 +1038,8 @@ suite('Disk File Service', function () { const target = URI.file(join(dirname(source.resource.fsPath), 'INDEX.html')); + const canCopy = await service.canCopy(source.resource, target, true); + let error; let copied: IFileStatWithMetadata; try { @@ -1023,12 +1050,14 @@ suite('Disk File Service', function () { if (isLinux) { assert.ok(!error); + assert.equal(canCopy, true); assert.equal(existsSync(copied!.resource.fsPath), true); assert.ok(readdirSync(testDir).some(f => f === 'INDEX.html')); assert.equal(source.size, copied!.size); } else { assert.ok(error); + assert.ok(canCopy instanceof Error); source = await service.resolve(source.resource, { resolveMetadata: true }); assert.equal(originalSize, source.size); @@ -1036,21 +1065,22 @@ suite('Disk File Service', function () { }); test('copy - MIX CASE different taget - overwrite', async () => { - const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true }); - assert.ok(source.size > 0); + const source1 = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true }); + assert.ok(source1.size > 0); - const renamed = await service.move(source.resource, URI.file(join(dirname(source.resource.fsPath), 'CONWAY.js'))); + const renamed = await service.move(source1.resource, URI.file(join(dirname(source1.resource.fsPath), 'CONWAY.js'))); assert.equal(existsSync(renamed.resource.fsPath), true); assert.ok(readdirSync(testDir).some(f => f === 'CONWAY.js')); - assert.equal(source.size, renamed.size); + assert.equal(source1.size, renamed.size); - const source_1 = await service.resolve(URI.file(join(testDir, 'deep', 'conway.js')), { resolveMetadata: true }); - const target = URI.file(join(testDir, basename(source_1.resource.path))); + const source2 = await service.resolve(URI.file(join(testDir, 'deep', 'conway.js')), { resolveMetadata: true }); + const target = URI.file(join(testDir, basename(source2.resource.path))); - const res = await service.copy(source_1.resource, target, true); + assert.equal(await service.canCopy(source2.resource, target, true), true); + const res = await service.copy(source2.resource, target, true); assert.equal(existsSync(res.resource.fsPath), true); assert.ok(readdirSync(testDir).some(f => f === 'conway.js')); - assert.equal(source_1.size, res.size); + assert.equal(source2.size, res.size); }); test('copy - same file', async () => { @@ -1060,6 +1090,7 @@ suite('Disk File Service', function () { const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true }); assert.ok(source.size > 0); + assert.equal(await service.canCopy(source.resource, URI.file(source.resource.fsPath)), true); let copied = await service.copy(source.resource, URI.file(source.resource.fsPath)); assert.equal(existsSync(copied.resource.fsPath), true); @@ -1083,6 +1114,7 @@ suite('Disk File Service', function () { const targetParent = URI.file(testDir); const target = targetParent.with({ path: posix.join(targetParent.path, posix.basename(source.resource.path)) }); + assert.equal(await service.canCopy(source.resource, URI.file(target.fsPath)), true); let copied = await service.copy(source.resource, URI.file(target.fsPath)); assert.equal(existsSync(copied.resource.fsPath), true); @@ -1517,23 +1549,23 @@ suite('Disk File Service', function () { assert.equal(error!.fileOperationResult, FileOperationResult.FILE_EXCEEDS_MEMORY_LIMIT); } - test('readFile - FILE_TOO_LARGE - default', async () => { + (isWindows ? test.skip /* flaky test */ : test)('readFile - FILE_TOO_LARGE - default', async () => { return testFileTooLarge(); }); - test('readFile - FILE_TOO_LARGE - buffered', async () => { + (isWindows ? test.skip /* flaky test */ : test)('readFile - FILE_TOO_LARGE - buffered', async () => { setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose); return testFileTooLarge(); }); - test('readFile - FILE_TOO_LARGE - unbuffered', async () => { + (isWindows ? test.skip /* flaky test */ : test)('readFile - FILE_TOO_LARGE - unbuffered', async () => { setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite); return testFileTooLarge(); }); - test('readFile - FILE_TOO_LARGE - streamed', async () => { + (isWindows ? test.skip /* flaky test */ : test)('readFile - FILE_TOO_LARGE - streamed', async () => { setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadStream); return testFileTooLarge(); @@ -1579,6 +1611,8 @@ suite('Disk File Service', function () { const contents = 'Hello World'; const resource = URI.file(join(testDir, 'test.txt')); + + assert.equal(await service.canCreateFile(resource), true); const fileStat = await service.createFile(resource, converter(contents)); assert.equal(fileStat.name, 'test.txt'); assert.equal(existsSync(fileStat.resource.fsPath), true); @@ -1596,6 +1630,8 @@ suite('Disk File Service', function () { writeFileSync(resource.fsPath, ''); // create file + assert.ok((await service.canCreateFile(resource)) instanceof Error); + let error; try { await service.createFile(resource, VSBuffer.fromString(contents)); @@ -1615,6 +1651,7 @@ suite('Disk File Service', function () { writeFileSync(resource.fsPath, ''); // create file + assert.equal(await service.canCreateFile(resource, { overwrite: true }), true); const fileStat = await service.createFile(resource, VSBuffer.fromString(contents), { overwrite: true }); assert.equal(fileStat.name, 'test.txt'); assert.equal(existsSync(fileStat.resource.fsPath), true); @@ -1806,7 +1843,8 @@ suite('Disk File Service', function () { const fileStat = await service.writeFile(target, streamToBufferReadableStream(createReadStream(source.fsPath))); assert.equal(fileStat.name, 'small-copy.txt'); - assert.equal(readFileSync(source.fsPath).toString(), readFileSync(target.fsPath).toString()); + const targetContents = readFileSync(target.fsPath).toString(); + assert.equal(readFileSync(source.fsPath).toString(), targetContents); } test('writeFile (large file - stream) - default', async () => { @@ -1832,7 +1870,8 @@ suite('Disk File Service', function () { const fileStat = await service.writeFile(target, streamToBufferReadableStream(createReadStream(source.fsPath))); assert.equal(fileStat.name, 'lorem-copy.txt'); - assert.equal(readFileSync(source.fsPath).toString(), readFileSync(target.fsPath).toString()); + const targetContents = readFileSync(target.fsPath).toString(); + assert.equal(readFileSync(source.fsPath).toString(), targetContents); } test('writeFile (file is created including parents)', async () => { diff --git a/src/vs/platform/files/test/electron-browser/normalizer.test.ts b/src/vs/platform/files/test/electron-browser/normalizer.test.ts index aade9f0879e..0aa61f7ce03 100644 --- a/src/vs/platform/files/test/electron-browser/normalizer.test.ts +++ b/src/vs/platform/files/test/electron-browser/normalizer.test.ts @@ -9,9 +9,10 @@ import { FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files import { URI as uri } from 'vs/base/common/uri'; import { IDiskFileChange, normalizeFileChanges, toFileChanges } from 'vs/platform/files/node/watcher/watcher'; import { Event, Emitter } from 'vs/base/common/event'; +import { ExtUri } from 'vs/base/common/resources'; function toFileChangesEvent(changes: IDiskFileChange[]): FileChangesEvent { - return new FileChangesEvent(toFileChanges(changes)); + return new FileChangesEvent(toFileChanges(changes), new ExtUri(() => !platform.isLinux)); } class TestFileWatcher { diff --git a/src/vs/platform/instantiation/common/instantiation.ts b/src/vs/platform/instantiation/common/instantiation.ts index 6d72a93db53..dbde5f6332c 100644 --- a/src/vs/platform/instantiation/common/instantiation.ts +++ b/src/vs/platform/instantiation/common/instantiation.ts @@ -85,7 +85,7 @@ type GetLeadingNonServiceArgs = export interface IInstantiationService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Synchronously creates an instance that is denoted by diff --git a/src/vs/platform/instantiation/common/instantiationService.ts b/src/vs/platform/instantiation/common/instantiationService.ts index c16ed7b4e9d..30ad4283515 100644 --- a/src/vs/platform/instantiation/common/instantiationService.ts +++ b/src/vs/platform/instantiation/common/instantiationService.ts @@ -22,7 +22,7 @@ class CyclicDependencyError extends Error { export class InstantiationService implements IInstantiationService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _services: ServiceCollection; private readonly _strict: boolean; @@ -96,8 +96,7 @@ export class InstantiationService implements IInstantiationService { // check for argument mismatches, adjust static args if needed if (args.length !== firstServiceArgPos) { - console.warn(`[createInstance] First service dependency of ${ctor.name} at position ${ - firstServiceArgPos + 1} conflicts with ${args.length} static arguments`); + console.warn(`[createInstance] First service dependency of ${ctor.name} at position ${firstServiceArgPos + 1} conflicts with ${args.length} static arguments`); let delta = firstServiceArgPos - args.length; if (delta > 0) { diff --git a/src/vs/platform/instantiation/common/serviceCollection.ts b/src/vs/platform/instantiation/common/serviceCollection.ts index d1003c1a095..3d7650139f2 100644 --- a/src/vs/platform/instantiation/common/serviceCollection.ts +++ b/src/vs/platform/instantiation/common/serviceCollection.ts @@ -22,10 +22,6 @@ export class ServiceCollection { return result; } - forEach(callback: (id: ServiceIdentifier, instanceOrDescriptor: any) => any): void { - this._entries.forEach((value, key) => callback(key, value)); - } - has(id: ServiceIdentifier): boolean { return this._entries.has(id); } diff --git a/src/vs/platform/instantiation/test/common/instantiationService.test.ts b/src/vs/platform/instantiation/test/common/instantiationService.test.ts index 2b30f7d3ee1..353b66615b9 100644 --- a/src/vs/platform/instantiation/test/common/instantiationService.test.ts +++ b/src/vs/platform/instantiation/test/common/instantiationService.test.ts @@ -12,48 +12,48 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; let IService1 = createDecorator('service1'); interface IService1 { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; c: number; } class Service1 implements IService1 { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; c = 1; } let IService2 = createDecorator('service2'); interface IService2 { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; d: boolean; } class Service2 implements IService2 { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; d = true; } let IService3 = createDecorator('service3'); interface IService3 { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; s: string; } class Service3 implements IService3 { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; s = 'farboo'; } let IDependentService = createDecorator('dependentService'); interface IDependentService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; name: string; } class DependentService implements IDependentService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(@IService1 service: IService1) { assert.equal(service.c, 1); } @@ -116,7 +116,7 @@ class DependentServiceTarget2 { class ServiceLoop1 implements IService1 { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; c = 1; constructor(@IService2 s: IService2) { @@ -125,7 +125,7 @@ class ServiceLoop1 implements IService1 { } class ServiceLoop2 implements IService2 { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; d = true; constructor(@IService1 s: IService1) { @@ -364,7 +364,7 @@ suite('Instantiation Service', () => { let serviceInstanceCount = 0; const CtorCounter = class implements Service1 { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; c = 1; constructor() { serviceInstanceCount += 1; diff --git a/src/vs/platform/ipc/electron-browser/sharedProcessService.ts b/src/vs/platform/ipc/electron-browser/sharedProcessService.ts index d02e7d8e005..59fb5a6bd43 100644 --- a/src/vs/platform/ipc/electron-browser/sharedProcessService.ts +++ b/src/vs/platform/ipc/electron-browser/sharedProcessService.ts @@ -10,7 +10,7 @@ export const ISharedProcessService = createDecorator('sha export interface ISharedProcessService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; getChannel(channelName: string): IChannel; registerChannel(channelName: string, channel: IServerChannel): void; diff --git a/src/vs/platform/ipc/electron-main/sharedProcessMainService.ts b/src/vs/platform/ipc/electron-main/sharedProcessMainService.ts index 4fb5318cc2b..97decd56a1a 100644 --- a/src/vs/platform/ipc/electron-main/sharedProcessMainService.ts +++ b/src/vs/platform/ipc/electron-main/sharedProcessMainService.ts @@ -9,7 +9,7 @@ export const ISharedProcessMainService = createDecorator; toggleSharedProcessWindow(): Promise; @@ -22,7 +22,7 @@ export interface ISharedProcess { export class SharedProcessMainService implements ISharedProcessMainService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(private sharedProcess: ISharedProcess) { } diff --git a/src/vs/platform/ipc/electron-browser/mainProcessService.ts b/src/vs/platform/ipc/electron-sandbox/mainProcessService.ts similarity index 89% rename from src/vs/platform/ipc/electron-browser/mainProcessService.ts rename to src/vs/platform/ipc/electron-sandbox/mainProcessService.ts index c72b1c703d3..b2c78a33279 100644 --- a/src/vs/platform/ipc/electron-browser/mainProcessService.ts +++ b/src/vs/platform/ipc/electron-sandbox/mainProcessService.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { Client } from 'vs/base/parts/ipc/electron-browser/ipc.electron-browser'; +import { Client } from 'vs/base/parts/ipc/electron-sandbox/ipc.electron-sandbox'; import { Disposable } from 'vs/base/common/lifecycle'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const IMainProcessService = createDecorator('mainProcessService'); export interface IMainProcessService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; getChannel(channelName: string): IChannel; @@ -21,7 +21,7 @@ export interface IMainProcessService { export class MainProcessService extends Disposable implements IMainProcessService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private mainProcessConnection: Client; diff --git a/src/vs/platform/issue/node/issue.ts b/src/vs/platform/issue/common/issue.ts similarity index 92% rename from src/vs/platform/issue/node/issue.ts rename to src/vs/platform/issue/common/issue.ts index 08a9c5d456f..d82ba0948de 100644 --- a/src/vs/platform/issue/node/issue.ts +++ b/src/vs/platform/issue/common/issue.ts @@ -3,10 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; - -export const IIssueService = createDecorator('issueService'); - // Since data sent through the service is serialized to JSON, functions will be lost, so Color objects // should not be sent as their 'toString' method will be stripped. Instead convert to strings before sending. export interface WindowStyles { @@ -89,10 +85,12 @@ export interface ProcessExplorerStyles extends WindowStyles { export interface ProcessExplorerData extends WindowData { pid: number; styles: ProcessExplorerStyles; + platform: string; + applicationName: string; } -export interface IIssueService { - _serviceBrand: undefined; +export interface ICommonIssueService { + readonly _serviceBrand: undefined; openReporter(data: IssueReporterData): Promise; openProcessExplorer(data: ProcessExplorerData): Promise; getSystemStatus(): Promise; diff --git a/src/vs/platform/issue/electron-main/issueMainService.ts b/src/vs/platform/issue/electron-main/issueMainService.ts index 193ab70b12f..48fae538c08 100644 --- a/src/vs/platform/issue/electron-main/issueMainService.ts +++ b/src/vs/platform/issue/electron-main/issueMainService.ts @@ -4,9 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; +import * as os from 'os'; +import product from 'vs/platform/product/common/product'; import * as objects from 'vs/base/common/objects'; import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; -import { IIssueService, IssueReporterData, IssueReporterFeatures, ProcessExplorerData } from 'vs/platform/issue/node/issue'; +import { ICommonIssueService, IssueReporterData, IssueReporterFeatures, ProcessExplorerData } from 'vs/platform/issue/common/issue'; import { BrowserWindow, ipcMain, screen, IpcMainEvent, Display, shell } from 'electron'; import { ILaunchMainService } from 'vs/platform/launch/electron-main/launchMainService'; import { PerformanceInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; @@ -18,11 +20,18 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IWindowState } from 'vs/platform/windows/electron-main/windows'; import { listProcesses } from 'vs/base/node/ps'; import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs'; +import { URI } from 'vs/base/common/uri'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { zoomLevelToZoomFactor } from 'vs/platform/windows/common/windows'; const DEFAULT_BACKGROUND_COLOR = '#1E1E1E'; -export class IssueMainService implements IIssueService { - _serviceBrand: undefined; +export const IIssueMainService = createDecorator('issueMainService'); + +export interface IIssueMainService extends ICommonIssueService { } + +export class IssueMainService implements ICommonIssueService { + declare readonly _serviceBrand: undefined; _issueWindow: BrowserWindow | null = null; _issueParentWindow: BrowserWindow | null = null; _processExplorerWindow: BrowserWindow | null = null; @@ -163,12 +172,11 @@ export class IssueMainService implements IIssueService { } }); - ipcMain.on('windowsInfoRequest', (event: IpcMainEvent) => { + ipcMain.on('vscode:windowsInfoRequest', (event: IpcMainEvent) => { this.launchMainService.getMainProcessInfo().then(info => { event.sender.send('vscode:windowsInfoResponse', info.windows); }); }); - } openReporter(data: IssueReporterData): Promise { @@ -189,7 +197,23 @@ export class IssueMainService implements IIssueService { title: localize('issueReporter', "Issue Reporter"), backgroundColor: data.styles.backgroundColor || DEFAULT_BACKGROUND_COLOR, webPreferences: { - nodeIntegration: true + preload: URI.parse(require.toUrl('vs/base/parts/sandbox/electron-browser/preload.js')).fsPath, + enableWebSQL: false, + enableRemoteModule: false, + nativeWindowOpen: true, + zoomFactor: zoomLevelToZoomFactor(data.zoomLevel), + ...this.environmentService.sandbox ? + + // Sandbox + { + sandbox: true, + contextIsolation: true + } : + + // No Sandbox + { + nodeIntegration: true + } } }); @@ -224,7 +248,7 @@ export class IssueMainService implements IIssueService { if (!this._processExplorerWindow) { this._processExplorerParentWindow = BrowserWindow.getFocusedWindow(); if (this._processExplorerParentWindow) { - const position = this.getWindowPosition(this._processExplorerParentWindow, 800, 300); + const position = this.getWindowPosition(this._processExplorerParentWindow, 800, 500); this._processExplorerWindow = new BrowserWindow({ skipTaskbar: true, resizable: true, @@ -238,7 +262,23 @@ export class IssueMainService implements IIssueService { backgroundColor: data.styles.backgroundColor, title: localize('processExplorer', "Process Explorer"), webPreferences: { - nodeIntegration: true + preload: URI.parse(require.toUrl('vs/base/parts/sandbox/electron-browser/preload.js')).fsPath, + enableWebSQL: false, + enableRemoteModule: false, + nativeWindowOpen: true, + zoomFactor: zoomLevelToZoomFactor(data.zoomLevel), + ...this.environmentService.sandbox ? + + // Sandbox + { + sandbox: true, + contextIsolation: true + } : + + // No Sandbox + { + nodeIntegration: true + } } }); @@ -254,7 +294,7 @@ export class IssueMainService implements IIssueService { }; this._processExplorerWindow.loadURL( - toLauchUrl('vs/code/electron-browser/processExplorer/processExplorer.html', windowConfiguration)); + toLauchUrl('vs/code/electron-sandbox/processExplorer/processExplorer.html', windowConfiguration)); this._processExplorerWindow.on('close', () => this._processExplorerWindow = null); @@ -379,10 +419,23 @@ export class IssueMainService implements IIssueService { machineId: this.machineId, userEnv: this.userEnv, data, - features + features, + disableExtensions: this.environmentService.disableExtensions, + os: { + type: os.type(), + arch: os.arch(), + release: os.release(), + }, + product: { + nameShort: product.nameShort, + version: product.version, + commit: product.commit, + date: product.date, + reportIssueUrl: product.reportIssueUrl + } }; - return toLauchUrl('vs/code/electron-browser/issue/issueReporter.html', windowConfiguration); + return toLauchUrl('vs/code/electron-sandbox/issue/issueReporter.html', windowConfiguration); } } diff --git a/src/vs/platform/issue/electron-sandbox/issue.ts b/src/vs/platform/issue/electron-sandbox/issue.ts new file mode 100644 index 00000000000..4e84d42806e --- /dev/null +++ b/src/vs/platform/issue/electron-sandbox/issue.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 { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ICommonIssueService } from 'vs/platform/issue/common/issue'; + +export const IIssueService = createDecorator('issueService'); + +export interface IIssueService extends ICommonIssueService { } diff --git a/src/vs/platform/keybinding/common/keybinding.ts b/src/vs/platform/keybinding/common/keybinding.ts index 17ab82b8184..a24bfa45410 100644 --- a/src/vs/platform/keybinding/common/keybinding.ts +++ b/src/vs/platform/keybinding/common/keybinding.ts @@ -48,7 +48,7 @@ export interface KeybindingsSchemaContribution { export const IKeybindingService = createDecorator('keybindingService'); export interface IKeybindingService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly inChordMode: boolean; diff --git a/src/vs/platform/keybinding/common/keybindingResolver.ts b/src/vs/platform/keybinding/common/keybindingResolver.ts index 951d01f4c16..796d4a49873 100644 --- a/src/vs/platform/keybinding/common/keybindingResolver.ts +++ b/src/vs/platform/keybinding/common/keybindingResolver.ts @@ -8,7 +8,6 @@ import { MenuRegistry } from 'vs/platform/actions/common/actions'; import { CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; import { IContext, ContextKeyExpression, ContextKeyExprType } from 'vs/platform/contextkey/common/contextkey'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; -import { keys } from 'vs/base/common/map'; export interface IResolveResult { /** Whether the resolved keybinding is entering a chord */ @@ -353,10 +352,10 @@ export class KeybindingResolver { } unboundCommands.push(id); }; - for (const id of keys(MenuRegistry.getCommands())) { + for (const id of MenuRegistry.getCommands().keys()) { addCommand(id, true); } - for (const id of keys(CommandsRegistry.getCommands())) { + for (const id of CommandsRegistry.getCommands().keys()) { addCommand(id, false); } diff --git a/src/vs/platform/keybinding/common/keybindingsRegistry.ts b/src/vs/platform/keybinding/common/keybindingsRegistry.ts index 6f249867f8e..b3901f68cd4 100644 --- a/src/vs/platform/keybinding/common/keybindingsRegistry.ts +++ b/src/vs/platform/keybinding/common/keybindingsRegistry.ts @@ -39,7 +39,7 @@ export interface IKeybindingRule extends IKeybindings { id: string; weight: number; args?: any; - when: ContextKeyExpression | null | undefined; + when?: ContextKeyExpression | null | undefined; } export interface IKeybindingRule2 { diff --git a/src/vs/platform/keybinding/test/common/keybindingLabels.test.ts b/src/vs/platform/keybinding/test/common/keybindingLabels.test.ts index 16e096e564b..a321d9b38bb 100644 --- a/src/vs/platform/keybinding/test/common/keybindingLabels.test.ts +++ b/src/vs/platform/keybinding/test/common/keybindingLabels.test.ts @@ -167,4 +167,7 @@ suite('KeybindingLabels', () => { assertElectronAcceleratorLabel(OperatingSystem.Macintosh, KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_A, KeyMod.CtrlCmd | KeyCode.KEY_B), 'cmd+a cmd+b'); }); + test('issue #91235: Do not end with a +', () => { + assertUSLabel(OperatingSystem.Windows, KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Alt, 'Ctrl+Alt'); + }); }); diff --git a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts index 998169968bd..a147dd652cc 100644 --- a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts +++ b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts @@ -53,7 +53,7 @@ export class MockContextKeyService implements IContextKeyService { public get onDidChangeContext(): Event { return Event.None; } - public bufferChangeEvents() { } + public bufferChangeEvents(callback: () => void) { callback(); } public getContextKeyValue(key: string) { const value = this._keys.get(key); if (value) { diff --git a/src/vs/platform/label/common/label.ts b/src/vs/platform/label/common/label.ts index 080f1ff0994..d1739d0a6d6 100644 --- a/src/vs/platform/label/common/label.ts +++ b/src/vs/platform/label/common/label.ts @@ -8,12 +8,14 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; import { IWorkspace } from 'vs/platform/workspace/common/workspace'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces'; -import { localize } from 'vs/nls'; -import { isEqualOrParent, basename } from 'vs/base/common/resources'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; + +export const ILabelService = createDecorator('labelService'); export interface ILabelService { - _serviceBrand: undefined; + + readonly _serviceBrand: undefined; + /** * Gets the human readable label for a uri. * If relative is passed returns a label relative to the workspace root that the uri belongs to. @@ -24,6 +26,7 @@ export interface ILabelService { getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWorkspace), options?: { verbose: boolean }): string; getHostLabel(scheme: string, authority?: string): string; getSeparator(scheme: string, authority?: string): '/' | '\\'; + registerFormatter(formatter: ResourceLabelFormatter): IDisposable; onDidChangeFormatters: Event; } @@ -46,25 +49,5 @@ export interface ResourceLabelFormatting { normalizeDriveLetter?: boolean; workspaceSuffix?: string; authorityPrefix?: string; + stripPathStartingSeparator?: boolean; } - -const LABEL_SERVICE_ID = 'label'; - -export function getSimpleWorkspaceLabel(workspace: IWorkspaceIdentifier | URI, workspaceHome: URI): string { - if (isSingleFolderWorkspaceIdentifier(workspace)) { - return basename(workspace); - } - // Workspace: Untitled - if (isEqualOrParent(workspace.configPath, workspaceHome)) { - return localize('untitledWorkspace', "Untitled (Workspace)"); - } - - let filename = basename(workspace.configPath); - if (filename.endsWith(WORKSPACE_EXTENSION)) { - filename = filename.substr(0, filename.length - WORKSPACE_EXTENSION.length - 1); - } - return localize('workspaceName', "{0} (Workspace)", filename); -} - - -export const ILabelService = createDecorator(LABEL_SERVICE_ID); diff --git a/src/vs/platform/launch/electron-main/launchMainService.ts b/src/vs/platform/launch/electron-main/launchMainService.ts index 8775dd82ad2..233f1690c98 100644 --- a/src/vs/platform/launch/electron-main/launchMainService.ts +++ b/src/vs/platform/launch/electron-main/launchMainService.ts @@ -51,7 +51,7 @@ function parseOpenUrl(args: ParsedArgs): URI[] { } export interface ILaunchMainService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; start(args: ParsedArgs, userEnv: IProcessEnvironment): Promise; getMainProcessId(): Promise; getMainProcessInfo(): Promise; @@ -60,7 +60,7 @@ export interface ILaunchMainService { export class LaunchMainService implements ILaunchMainService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @ILogService private readonly logService: ILogService, diff --git a/src/vs/platform/layout/browser/layoutService.ts b/src/vs/platform/layout/browser/layoutService.ts index 39f3f1da8eb..44a00b53a4b 100644 --- a/src/vs/platform/layout/browser/layoutService.ts +++ b/src/vs/platform/layout/browser/layoutService.ts @@ -11,7 +11,7 @@ export const ILayoutService = createDecorator('layoutService'); export interface ILayoutService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * The dimensions of the container. diff --git a/src/vs/platform/lifecycle/common/lifecycle.ts b/src/vs/platform/lifecycle/common/lifecycle.ts index cae9c857fb3..5d047b97813 100644 --- a/src/vs/platform/lifecycle/common/lifecycle.ts +++ b/src/vs/platform/lifecycle/common/lifecycle.ts @@ -123,7 +123,7 @@ export function LifecyclePhaseToString(phase: LifecyclePhase) { */ export interface ILifecycleService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Value indicates how this window got loaded. diff --git a/src/vs/platform/lifecycle/common/lifecycleService.ts b/src/vs/platform/lifecycle/common/lifecycleService.ts index d0fad7f28f4..fed0b6580e5 100644 --- a/src/vs/platform/lifecycle/common/lifecycleService.ts +++ b/src/vs/platform/lifecycle/common/lifecycleService.ts @@ -12,7 +12,7 @@ import { mark } from 'vs/base/common/performance'; export abstract class AbstractLifecycleService extends Disposable implements ILifecycleService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; protected readonly _onBeforeShutdown = this._register(new Emitter()); readonly onBeforeShutdown = this._onBeforeShutdown.event; diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts index a2ae8cd766b..1b8e3852c50 100644 --- a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts +++ b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts @@ -41,7 +41,7 @@ export interface ShutdownEvent { export interface ILifecycleMainService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Will be true if the program was restarted (e.g. due to explicit request or update). @@ -137,7 +137,7 @@ export const enum LifecycleMainPhase { export class LifecycleMainService extends Disposable implements ILifecycleMainService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private static readonly QUIT_FROM_RESTART_MARKER = 'quit.from.restart'; // use a marker to find out if the session was restarted diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index f81f8534749..ea3a652aa7e 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -6,11 +6,11 @@ import { createStyleSheet } from 'vs/base/browser/dom'; import { IListMouseEvent, IListTouchEvent, IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IPagedRenderer, PagedList, IPagedListOptions } from 'vs/base/browser/ui/list/listPaging'; -import { DefaultStyleController, IListOptions, IMultipleSelectionController, IOpenController, isSelectionRangeChangeEvent, isSelectionSingleChangeEvent, List, IListAccessibilityProvider, IListOptionsUpdate } from 'vs/base/browser/ui/list/listWidget'; +import { DefaultStyleController, IListOptions, IMultipleSelectionController, isSelectionRangeChangeEvent, isSelectionSingleChangeEvent, List, IListAccessibilityProvider, IListOptionsUpdate } from 'vs/base/browser/ui/list/listWidget'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, dispose, IDisposable, toDisposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; -import { IConfigurationService, getMigratedSettingValue } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; @@ -21,48 +21,49 @@ import { attachListStyler, computeStyles, defaultListStyles, IColorMapping } fro import { IThemeService } from 'vs/platform/theme/common/themeService'; import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys'; import { ObjectTree, IObjectTreeOptions, ICompressibleTreeRenderer, CompressibleObjectTree, ICompressibleObjectTreeOptions, ICompressibleObjectTreeOptionsUpdate } from 'vs/base/browser/ui/tree/objectTree'; -import { ITreeRenderer, IAsyncDataSource, IDataSource } from 'vs/base/browser/ui/tree/tree'; +import { ITreeRenderer, IAsyncDataSource, IDataSource, ITreeEvent } from 'vs/base/browser/ui/tree/tree'; import { AsyncDataTree, IAsyncDataTreeOptions, CompressibleAsyncDataTree, ITreeCompressionDelegate, ICompressibleAsyncDataTreeOptions, IAsyncDataTreeOptionsUpdate } from 'vs/base/browser/ui/tree/asyncDataTree'; import { DataTree, IDataTreeOptions } from 'vs/base/browser/ui/tree/dataTree'; import { IKeyboardNavigationEventFilter, IAbstractTreeOptions, RenderIndentGuides, IAbstractTreeOptionsUpdate } from 'vs/base/browser/ui/tree/abstractTree'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; export type ListWidget = List | PagedList | ObjectTree | DataTree | AsyncDataTree; +export type WorkbenchListWidget = WorkbenchList | WorkbenchPagedList | WorkbenchObjectTree | WorkbenchCompressibleObjectTree | WorkbenchDataTree | WorkbenchAsyncDataTree | WorkbenchCompressibleAsyncDataTree; export const IListService = createDecorator('listService'); export interface IListService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Returns the currently focused list widget if any. */ - readonly lastFocusedList: ListWidget | undefined; + readonly lastFocusedList: WorkbenchListWidget | undefined; } interface IRegisteredList { - widget: ListWidget; + widget: WorkbenchListWidget; extraContextKeys?: (IContextKey)[]; } export class ListService implements IListService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private disposables = new DisposableStore(); private lists: IRegisteredList[] = []; - private _lastFocusedWidget: ListWidget | undefined = undefined; + private _lastFocusedWidget: WorkbenchListWidget | undefined = undefined; private _hasCreatedStyleController: boolean = false; - get lastFocusedList(): ListWidget | undefined { + get lastFocusedList(): WorkbenchListWidget | undefined { return this._lastFocusedWidget; } constructor(@IThemeService private readonly _themeService: IThemeService) { } - register(widget: ListWidget, extraContextKeys?: (IContextKey)[]): IDisposable { + register(widget: WorkbenchListWidget, extraContextKeys?: (IContextKey)[]): IDisposable { if (!this._hasCreatedStyleController) { this._hasCreatedStyleController = true; // create a shared default tree style sheet for performance reasons @@ -124,19 +125,12 @@ export const keyboardNavigationSettingKey = 'workbench.list.keyboardNavigation'; export const automaticKeyboardNavigationSettingKey = 'workbench.list.automaticKeyboardNavigation'; const treeIndentKey = 'workbench.tree.indent'; const treeRenderIndentGuidesKey = 'workbench.tree.renderIndentGuides'; - -function getHorizontalScrollingSetting(configurationService: IConfigurationService): boolean { - return getMigratedSettingValue(configurationService, horizontalScrollingKey, 'workbench.tree.horizontalScrolling'); -} +const listSmoothScrolling = 'workbench.list.smoothScrolling'; function useAltAsMultipleSelectionModifier(configurationService: IConfigurationService): boolean { return configurationService.getValue(multiSelectModifierSettingKey) === 'alt'; } -function useSingleClickToOpen(configurationService: IConfigurationService): boolean { - return configurationService.getValue(openModeSettingKey) !== 'doubleClick'; -} - class MultipleSelectionController extends Disposable implements IMultipleSelectionController { private useAltAsMultipleSelectionModifier: boolean; @@ -169,44 +163,6 @@ class MultipleSelectionController extends Disposable implements IMultipleSele } } -class WorkbenchOpenController extends Disposable implements IOpenController { - private openOnSingleClick: boolean; - - constructor(private configurationService: IConfigurationService, private existingOpenController?: IOpenController) { - super(); - - this.openOnSingleClick = useSingleClickToOpen(configurationService); - - this.registerListeners(); - } - - private registerListeners(): void { - this._register(this.configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(openModeSettingKey)) { - this.openOnSingleClick = useSingleClickToOpen(this.configurationService); - } - })); - } - - shouldOpen(event: UIEvent): boolean { - if (event instanceof MouseEvent) { - const isLeftButton = event.button === 0; - const isDoubleClick = event.detail === 2; - if (isLeftButton && !this.openOnSingleClick && !isDoubleClick) { - return false; - } - - if (isLeftButton /* left mouse button */ || event.button === 1 /* middle mouse button */) { - return this.existingOpenController ? this.existingOpenController.shouldOpen(event) : true; - } - - return false; - } - - return this.existingOpenController ? this.existingOpenController.shouldOpen(event) : true; - } -} - function toWorkbenchListOptions(options: IListOptions, configurationService: IConfigurationService, keybindingService: IKeybindingService): [IListOptions, IDisposable] { const disposables = new DisposableStore(); const result = { ...options }; @@ -217,16 +173,14 @@ function toWorkbenchListOptions(options: IListOptions, configurationServic disposables.add(multipleSelectionController); } - const openController = new WorkbenchOpenController(configurationService, options.openController); - result.openController = openController; - disposables.add(openController); - result.keyboardNavigationDelegate = { mightProducePrintableCharacter(e) { return keybindingService.mightProducePrintableCharacter(e); } }; + result.smoothScrolling = configurationService.getValue(listSmoothScrolling); + return [result, disposables]; } @@ -241,12 +195,12 @@ export interface IWorkbenchListOptions extends IWorkbenchListOptionsUpdate, I export class WorkbenchList extends List { readonly contextKeyService: IContextKeyService; - private readonly configurationService: IConfigurationService; private readonly themeService: IThemeService; private listHasSelectionOrFocus: IContextKey; private listDoubleSelection: IContextKey; private listMultiSelection: IContextKey; + private horizontalScrolling: boolean | undefined; private _styler: IDisposable | undefined; private _useAltAsMultipleSelectionModifier: boolean; @@ -263,7 +217,7 @@ export class WorkbenchList extends List { @IConfigurationService configurationService: IConfigurationService, @IKeybindingService keybindingService: IKeybindingService ) { - const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : getHorizontalScrollingSetting(configurationService); + const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : configurationService.getValue(horizontalScrollingKey); const [workbenchListOptions, workbenchListOptionsDisposable] = toWorkbenchListOptions(options, configurationService, keybindingService); super(user, container, delegate, renderers, @@ -278,7 +232,6 @@ export class WorkbenchList extends List { this.disposables.add(workbenchListOptionsDisposable); this.contextKeyService = createScopedContextKeyService(contextKeyService, this); - this.configurationService = configurationService; this.themeService = themeService; const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService); @@ -287,6 +240,7 @@ export class WorkbenchList extends List { this.listHasSelectionOrFocus = WorkbenchListHasSelectionOrFocus.bindTo(this.contextKeyService); this.listDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService); this.listMultiSelection = WorkbenchListMultiSelection.bindTo(this.contextKeyService); + this.horizontalScrolling = options.horizontalScrolling; this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); @@ -301,9 +255,11 @@ export class WorkbenchList extends List { const selection = this.getSelection(); const focus = this.getFocus(); - this.listHasSelectionOrFocus.set(selection.length > 0 || focus.length > 0); - this.listMultiSelection.set(selection.length > 1); - this.listDoubleSelection.set(selection.length === 2); + this.contextKeyService.bufferChangeEvents(() => { + this.listHasSelectionOrFocus.set(selection.length > 0 || focus.length > 0); + this.listMultiSelection.set(selection.length > 1); + this.listDoubleSelection.set(selection.length === 2); + }); })); this.disposables.add(this.onDidChangeFocus(() => { const selection = this.getSelection(); @@ -311,8 +267,25 @@ export class WorkbenchList extends List { this.listHasSelectionOrFocus.set(selection.length > 0 || focus.length > 0); })); + this.disposables.add(configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(multiSelectModifierSettingKey)) { + this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); + } - this.registerListeners(); + let options: IListOptionsUpdate = {}; + + if (e.affectsConfiguration(horizontalScrollingKey) && this.horizontalScrolling === undefined) { + const horizontalScrolling = configurationService.getValue(horizontalScrollingKey); + options = { ...options, horizontalScrolling }; + } + if (e.affectsConfiguration(listSmoothScrolling)) { + const smoothScrolling = configurationService.getValue(listSmoothScrolling); + options = { ...options, smoothScrolling }; + } + if (Object.keys(options).length > 0) { + this.updateOptions(options); + } + })); } updateOptions(options: IWorkbenchListOptionsUpdate): void { @@ -338,14 +311,6 @@ export class WorkbenchList extends List { this._styler = attachListStyler(this, this.themeService, styles); } - private registerListeners(): void { - this.disposables.add(this.configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(multiSelectModifierSettingKey)) { - this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(this.configurationService); - } - })); - } - get useAltAsMultipleSelectionModifier(): boolean { return this._useAltAsMultipleSelectionModifier; } @@ -358,11 +323,11 @@ export interface IWorkbenchPagedListOptions extends IWorkbenchListOptionsUpda export class WorkbenchPagedList extends PagedList { readonly contextKeyService: IContextKeyService; - private readonly configurationService: IConfigurationService; private readonly disposables: DisposableStore; private _useAltAsMultipleSelectionModifier: boolean; + private horizontalScrolling: boolean | undefined; constructor( user: string, @@ -376,7 +341,7 @@ export class WorkbenchPagedList extends PagedList { @IConfigurationService configurationService: IConfigurationService, @IKeybindingService keybindingService: IKeybindingService ) { - const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : getHorizontalScrollingSetting(configurationService); + const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : configurationService.getValue(horizontalScrollingKey); const [workbenchListOptions, workbenchListOptionsDisposable] = toWorkbenchListOptions(options, configurationService, keybindingService); super(user, container, delegate, renderers, { @@ -391,7 +356,7 @@ export class WorkbenchPagedList extends PagedList { this.disposables.add(workbenchListOptionsDisposable); this.contextKeyService = createScopedContextKeyService(contextKeyService, this); - this.configurationService = configurationService; + this.horizontalScrolling = options.horizontalScrolling; const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService); listSupportsMultiSelect.set(!(options.multipleSelectionSupport === false)); @@ -405,13 +370,23 @@ export class WorkbenchPagedList extends PagedList { this.disposables.add(attachListStyler(this, themeService, options.overrideStyles)); } - this.registerListeners(); - } - - private registerListeners(): void { - this.disposables.add(this.configurationService.onDidChangeConfiguration(e => { + this.disposables.add(configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(multiSelectModifierSettingKey)) { - this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(this.configurationService); + this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); + } + + let options: IListOptionsUpdate = {}; + + if (e.affectsConfiguration(horizontalScrollingKey) && this.horizontalScrolling === undefined) { + const horizontalScrolling = configurationService.getValue(horizontalScrollingKey); + options = { ...options, horizontalScrolling }; + } + if (e.affectsConfiguration(listSmoothScrolling)) { + const smoothScrolling = configurationService.getValue(listSmoothScrolling); + options = { ...options, smoothScrolling }; + } + if (Object.keys(options).length > 0) { + this.updateOptions(options); } })); } @@ -446,8 +421,8 @@ export interface IOpenEvent { } export interface IResourceNavigatorOptions { + readonly configurationService?: IConfigurationService; readonly openOnFocus?: boolean; - readonly openOnSelection?: boolean; readonly openOnSingleClick?: boolean; } @@ -464,101 +439,119 @@ export function getSelectionKeyboardEvent(typeArg = 'keydown', preserveFocus?: b abstract class ResourceNavigator extends Disposable { - private readonly options: IResourceNavigatorOptions; + private readonly openOnFocus: boolean; + private openOnSingleClick: boolean; - private readonly _onDidOpenResource = new Emitter>(); - readonly onDidOpenResource: Event> = this._onDidOpenResource.event; + private readonly _onDidOpen = new Emitter>(); + readonly onDidOpen: Event> = this._onDidOpen.event; constructor( - private readonly treeOrList: { - getFocus(): (T | null)[], - getSelection(): (T | null)[], - setSelection(elements: (T | null)[], browserEvent?: UIEvent): void, - onDidChangeFocus: Event<{ browserEvent?: UIEvent }>, - onDidChangeSelection: Event<{ browserEvent?: UIEvent }>, - onDidOpen: Event<{ browserEvent?: UIEvent }>, - readonly openOnSingleClick?: boolean - }, + private readonly widget: ListWidget, options?: IResourceNavigatorOptions ) { super(); - this.options = { openOnSelection: true, ...(options || {}) }; + this.openOnFocus = options?.openOnFocus ?? false; + this.openOnSingleClick = options?.openOnSingleClick ?? true; - this.registerListeners(); - } + this._register(Event.filter(this.widget.onDidChangeSelection, e => e.browserEvent instanceof KeyboardEvent)(e => this.onSelectionFromKeyboard(e))); + this._register(this.widget.onPointer((e: { browserEvent: MouseEvent }) => this.onPointer(e.browserEvent))); + this._register(this.widget.onMouseDblClick((e: { browserEvent: MouseEvent }) => this.onMouseDblClick(e.browserEvent))); - private registerListeners(): void { - if (this.options && this.options.openOnFocus) { - this._register(this.treeOrList.onDidChangeFocus(e => this.onFocus(e.browserEvent))); + if (this.openOnFocus) { + this._register(Event.filter(this.widget.onDidChangeFocus, e => e.browserEvent instanceof KeyboardEvent)(e => this.onFocusFromKeyboard(e))); } - if (this.options && this.options.openOnSelection) { - this._register(this.treeOrList.onDidChangeSelection(e => this.onSelection(e.browserEvent))); + if (typeof options?.openOnSingleClick !== 'boolean' && options?.configurationService) { + this._register(options?.configurationService.onDidChangeConfiguration(() => { + this.openOnSingleClick = options?.configurationService!.getValue(openModeSettingKey) !== 'doubleClick'; + })); } - - this._register(this.treeOrList.onDidOpen(e => this.onSelection(e.browserEvent))); } - private onFocus(browserEvent?: UIEvent): void { - const focus = this.treeOrList.getFocus(); - this.treeOrList.setSelection(focus, browserEvent); + private onFocusFromKeyboard(event: ITreeEvent): void { + const focus = this.widget.getFocus(); + this.widget.setSelection(focus, event.browserEvent); + const preserveFocus = typeof (event.browserEvent as SelectionKeyboardEvent).preserveFocus === 'boolean' ? (event.browserEvent as SelectionKeyboardEvent).preserveFocus! : true; + const pinned = false; + const sideBySide = false; + + this._open(preserveFocus, pinned, sideBySide, event.browserEvent); + } + + private onSelectionFromKeyboard(event: ITreeEvent): void { + if (event.elements.length !== 1) { + return; + } + + const preserveFocus = typeof (event.browserEvent as SelectionKeyboardEvent).preserveFocus === 'boolean' ? (event.browserEvent as SelectionKeyboardEvent).preserveFocus! : true; + const pinned = false; + const sideBySide = false; + + this._open(preserveFocus, pinned, sideBySide, event.browserEvent); + } + + private onPointer(browserEvent: MouseEvent): void { + const isDoubleClick = browserEvent.detail === 2; + + if (!this.openOnSingleClick && !isDoubleClick) { + return; + } + + const isMiddleClick = browserEvent.button === 1; + const preserveFocus = !isDoubleClick; + const pinned = isDoubleClick || isMiddleClick; + const sideBySide = browserEvent.ctrlKey || browserEvent.metaKey || browserEvent.altKey; + + this._open(preserveFocus, pinned, sideBySide, browserEvent); + } + + private onMouseDblClick(browserEvent?: MouseEvent): void { if (!browserEvent) { return; } - const isMouseEvent = browserEvent && browserEvent instanceof MouseEvent; + const preserveFocus = false; + const pinned = true; + const sideBySide = (browserEvent.ctrlKey || browserEvent.metaKey || browserEvent.altKey); - if (!isMouseEvent) { - const preserveFocus = (browserEvent instanceof KeyboardEvent && typeof (browserEvent).preserveFocus === 'boolean') ? - !!(browserEvent).preserveFocus : - true; - - this.open(preserveFocus, false, false, browserEvent); - } + this._open(preserveFocus, pinned, sideBySide, browserEvent); } - private onSelection(browserEvent?: MouseEvent | UIEvent): void { - if (!browserEvent || browserEvent.type === 'contextmenu') { - return; - } - - const isKeyboardEvent = browserEvent instanceof KeyboardEvent; - const isMiddleClick = browserEvent instanceof MouseEvent ? browserEvent.button === 1 : false; - const isDoubleClick = browserEvent.detail === 2; - const preserveFocus = (browserEvent instanceof KeyboardEvent && typeof (browserEvent).preserveFocus === 'boolean') ? - !!(browserEvent).preserveFocus : - !isDoubleClick; - - if (this.options.openOnSingleClick || this.treeOrList.openOnSingleClick || isDoubleClick || isKeyboardEvent) { - const sideBySide = browserEvent instanceof MouseEvent && (browserEvent.ctrlKey || browserEvent.metaKey || browserEvent.altKey); - this.open(preserveFocus, isDoubleClick || isMiddleClick, sideBySide, browserEvent); - } - } - - private open(preserveFocus: boolean, pinned: boolean, sideBySide: boolean, browserEvent?: UIEvent): void { - this._onDidOpenResource.fire({ + private _open(preserveFocus: boolean, pinned: boolean, sideBySide: boolean, browserEvent?: UIEvent): void { + this._onDidOpen.fire({ editorOptions: { preserveFocus, pinned, revealIfVisible: true }, sideBySide, - element: this.treeOrList.getSelection()[0], + element: this.widget.getSelection()[0], browserEvent }); } + + // hack for References Widget: pressing Enter on already selected tree element + open(browserEvent?: UIEvent): void { + this._open((browserEvent as any)?.preserveFocus || false, true, false, browserEvent); + } } export class ListResourceNavigator extends ResourceNavigator { - constructor(list: WorkbenchList | WorkbenchPagedList, options?: IResourceNavigatorOptions) { + constructor( + list: List | PagedList, + options?: IResourceNavigatorOptions + ) { super(list, options); } } -export class TreeResourceNavigator extends ResourceNavigator { - constructor(tree: WorkbenchObjectTree | WorkbenchCompressibleObjectTree | WorkbenchDataTree | WorkbenchAsyncDataTree | WorkbenchCompressibleAsyncDataTree, options: IResourceNavigatorOptions = {}) { +class TreeResourceNavigator extends ResourceNavigator { + constructor( + tree: ObjectTree | CompressibleObjectTree | DataTree | AsyncDataTree | CompressibleAsyncDataTree, + options: IResourceNavigatorOptions + ) { super(tree, options); } } @@ -584,7 +577,7 @@ function createKeyboardNavigationEventFilter(container: HTMLElement, keybindingS }; } -export interface IWorkbenchObjectTreeOptions extends IObjectTreeOptions { +export interface IWorkbenchObjectTreeOptions extends IObjectTreeOptions, IResourceNavigatorOptions { readonly accessibilityProvider: IListAccessibilityProvider; readonly overrideStyles?: IColorMapping; } @@ -594,6 +587,7 @@ export class WorkbenchObjectTree, TFilterData = void> private internals: WorkbenchTreeInternals; get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; } get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; } + get onDidOpen(): Event> { return this.internals.onDidOpen; } constructor( user: string, @@ -611,16 +605,20 @@ export class WorkbenchObjectTree, TFilterData = void> const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble>(container, options, contextKeyService, configurationService, keybindingService, accessibilityService); super(user, container, delegate, renderers, treeOptions); this.disposables.add(disposable); - this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService); + this.internals = new WorkbenchTreeInternals(this, options, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService); this.disposables.add(this.internals); } + + open(browserEvent?: UIEvent): void { + this.internals.open(browserEvent); + } } export interface IWorkbenchCompressibleObjectTreeOptionsUpdate extends ICompressibleObjectTreeOptionsUpdate { readonly overrideStyles?: IColorMapping; } -export interface IWorkbenchCompressibleObjectTreeOptions extends IWorkbenchCompressibleObjectTreeOptionsUpdate, ICompressibleObjectTreeOptions { +export interface IWorkbenchCompressibleObjectTreeOptions extends IWorkbenchCompressibleObjectTreeOptionsUpdate, ICompressibleObjectTreeOptions, IResourceNavigatorOptions { readonly accessibilityProvider: IListAccessibilityProvider; } @@ -629,6 +627,7 @@ export class WorkbenchCompressibleObjectTree, TFilter private internals: WorkbenchTreeInternals; get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; } get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; } + get onDidOpen(): Event> { return this.internals.onDidOpen; } constructor( user: string, @@ -646,7 +645,7 @@ export class WorkbenchCompressibleObjectTree, TFilter const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble>(container, options, contextKeyService, configurationService, keybindingService, accessibilityService); super(user, container, delegate, renderers, treeOptions); this.disposables.add(disposable); - this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService); + this.internals = new WorkbenchTreeInternals(this, options, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService); this.disposables.add(this.internals); } @@ -657,13 +656,17 @@ export class WorkbenchCompressibleObjectTree, TFilter this.internals.updateStyleOverrides(options.overrideStyles); } } + + open(browserEvent?: UIEvent): void { + this.internals.open(browserEvent); + } } export interface IWorkbenchDataTreeOptionsUpdate extends IAbstractTreeOptionsUpdate { readonly overrideStyles?: IColorMapping; } -export interface IWorkbenchDataTreeOptions extends IWorkbenchDataTreeOptionsUpdate, IDataTreeOptions { +export interface IWorkbenchDataTreeOptions extends IWorkbenchDataTreeOptionsUpdate, IDataTreeOptions, IResourceNavigatorOptions { readonly accessibilityProvider: IListAccessibilityProvider; } @@ -672,6 +675,7 @@ export class WorkbenchDataTree extends DataTree; get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; } get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; } + get onDidOpen(): Event> { return this.internals.onDidOpen; } constructor( user: string, @@ -690,7 +694,7 @@ export class WorkbenchDataTree extends DataTree>(container, options, contextKeyService, configurationService, keybindingService, accessibilityService); super(user, container, delegate, renderers, dataSource, treeOptions); this.disposables.add(disposable); - this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService); + this.internals = new WorkbenchTreeInternals(this, options, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService); this.disposables.add(this.internals); } @@ -701,13 +705,17 @@ export class WorkbenchDataTree extends DataTree extends IWorkbenchAsyncDataTreeOptionsUpdate, IAsyncDataTreeOptions { +export interface IWorkbenchAsyncDataTreeOptions extends IWorkbenchAsyncDataTreeOptionsUpdate, IAsyncDataTreeOptions, IResourceNavigatorOptions { readonly accessibilityProvider: IListAccessibilityProvider; } @@ -716,6 +724,7 @@ export class WorkbenchAsyncDataTree extends Async private internals: WorkbenchTreeInternals; get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; } get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; } + get onDidOpen(): Event> { return this.internals.onDidOpen; } constructor( user: string, @@ -734,7 +743,7 @@ export class WorkbenchAsyncDataTree extends Async const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble>(container, options, contextKeyService, configurationService, keybindingService, accessibilityService); super(user, container, delegate, renderers, dataSource, treeOptions); this.disposables.add(disposable); - this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService); + this.internals = new WorkbenchTreeInternals(this, options, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService); this.disposables.add(this.internals); } @@ -745,9 +754,13 @@ export class WorkbenchAsyncDataTree extends Async this.internals.updateStyleOverrides(options.overrideStyles); } } + + open(browserEvent?: UIEvent): void { + this.internals.open(browserEvent); + } } -export interface IWorkbenchCompressibleAsyncDataTreeOptions extends ICompressibleAsyncDataTreeOptions { +export interface IWorkbenchCompressibleAsyncDataTreeOptions extends ICompressibleAsyncDataTreeOptions, IResourceNavigatorOptions { readonly accessibilityProvider: IListAccessibilityProvider; readonly overrideStyles?: IColorMapping; } @@ -757,6 +770,7 @@ export class WorkbenchCompressibleAsyncDataTree e private internals: WorkbenchTreeInternals; get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; } get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; } + get onDidOpen(): Event> { return this.internals.onDidOpen; } constructor( user: string, @@ -776,9 +790,13 @@ export class WorkbenchCompressibleAsyncDataTree e const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble>(container, options, contextKeyService, configurationService, keybindingService, accessibilityService); super(user, container, virtualDelegate, compressionDelegate, renderers, dataSource, treeOptions); this.disposables.add(disposable); - this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService); + this.internals = new WorkbenchTreeInternals(this, options, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService); this.disposables.add(this.internals); } + + open(browserEvent?: UIEvent): void { + this.internals.open(browserEvent); + } } function workbenchTreeDataPreamble | IAsyncDataTreeOptions>( @@ -809,8 +827,7 @@ function workbenchTreeDataPreamble(keyboardNavigationSettingKey); - const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : getHorizontalScrollingSetting(configurationService); - const openOnSingleClick = useSingleClickToOpen(configurationService); + const horizontalScrolling = options.horizontalScrolling !== undefined ? options.horizontalScrolling : configurationService.getValue(horizontalScrollingKey); const [workbenchListOptions, disposable] = toWorkbenchListOptions(options, configurationService, keybindingService); const additionalScrollHeight = options.additionalScrollHeight; @@ -823,14 +840,15 @@ function workbenchTreeDataPreamble(treeIndentKey), renderIndentGuides: configurationService.getValue(treeRenderIndentGuidesKey), + smoothScrolling: configurationService.getValue(listSmoothScrolling), automaticKeyboardNavigation: getAutomaticKeyboardNavigation(), simpleKeyboardNavigation: keyboardNavigation === 'simple', filterOnType: keyboardNavigation === 'filter', horizontalScrolling, - openOnSingleClick, keyboardNavigationEventFilter: createKeyboardNavigationEventFilter(container, keybindingService), additionalScrollHeight, - hideTwistiesOfChildlessElements: options.hideTwistiesOfChildlessElements + hideTwistiesOfChildlessElements: options.hideTwistiesOfChildlessElements, + expandOnlyOnDoubleClick: configurationService.getValue(openModeSettingKey) === 'doubleClick' } as TOptions }; } @@ -844,10 +862,13 @@ class WorkbenchTreeInternals { private _useAltAsMultipleSelectionModifier: boolean; private disposables: IDisposable[] = []; private styler: IDisposable | undefined; + private navigator: TreeResourceNavigator; + + get onDidOpen(): Event> { return this.navigator.onDidOpen; } constructor( - private tree: WorkbenchObjectTree | CompressibleObjectTree | WorkbenchDataTree | WorkbenchAsyncDataTree | WorkbenchCompressibleAsyncDataTree, - options: IAbstractTreeOptions | IAsyncDataTreeOptions, + private tree: WorkbenchObjectTree | WorkbenchCompressibleObjectTree | WorkbenchDataTree | WorkbenchAsyncDataTree | WorkbenchCompressibleAsyncDataTree, + options: IWorkbenchObjectTreeOptions | IWorkbenchCompressibleObjectTreeOptions | IWorkbenchDataTreeOptions | IWorkbenchAsyncDataTreeOptions | IWorkbenchCompressibleAsyncDataTreeOptions, getAutomaticKeyboardNavigation: () => boolean | undefined, overrideStyles: IColorMapping | undefined, @IContextKeyService contextKeyService: IContextKeyService, @@ -887,9 +908,11 @@ class WorkbenchTreeInternals { const selection = tree.getSelection(); const focus = tree.getFocus(); - this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0); - this.hasMultiSelection.set(selection.length > 1); - this.hasDoubleSelection.set(selection.length === 2); + this.contextKeyService.bufferChangeEvents(() => { + this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0); + this.hasMultiSelection.set(selection.length > 1); + this.hasDoubleSelection.set(selection.length === 2); + }); }), tree.onDidChangeFocus(() => { const selection = tree.getSelection(); @@ -898,25 +921,37 @@ class WorkbenchTreeInternals { this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0); }), configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(openModeSettingKey)) { - tree.updateOptions({ openOnSingleClick: useSingleClickToOpen(configurationService) }); - } + let newOptions: any = {}; if (e.affectsConfiguration(multiSelectModifierSettingKey)) { this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); } if (e.affectsConfiguration(treeIndentKey)) { const indent = configurationService.getValue(treeIndentKey); - tree.updateOptions({ indent }); + newOptions = { ...newOptions, indent }; } if (e.affectsConfiguration(treeRenderIndentGuidesKey)) { const renderIndentGuides = configurationService.getValue(treeRenderIndentGuidesKey); - tree.updateOptions({ renderIndentGuides }); + newOptions = { ...newOptions, renderIndentGuides }; + } + if (e.affectsConfiguration(listSmoothScrolling)) { + const smoothScrolling = configurationService.getValue(listSmoothScrolling); + newOptions = { ...newOptions, smoothScrolling }; } if (e.affectsConfiguration(keyboardNavigationSettingKey)) { updateKeyboardNavigation(); } if (e.affectsConfiguration(automaticKeyboardNavigationSettingKey)) { - tree.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() }); + newOptions = { ...newOptions, automaticKeyboardNavigation: getAutomaticKeyboardNavigation() }; + } + if (e.affectsConfiguration(horizontalScrollingKey) && options.horizontalScrolling === undefined) { + const horizontalScrolling = configurationService.getValue(horizontalScrollingKey); + newOptions = { ...newOptions, horizontalScrolling }; + } + if (e.affectsConfiguration(openModeSettingKey)) { + newOptions = { ...newOptions, expandOnlyOnDoubleClick: configurationService.getValue(openModeSettingKey) === 'doubleClick' }; + } + if (Object.keys(newOptions).length > 0) { + tree.updateOptions(newOptions); } }), this.contextKeyService.onDidChangeContext(e => { @@ -926,6 +961,9 @@ class WorkbenchTreeInternals { }), accessibilityService.onDidChangeScreenReaderOptimized(() => updateKeyboardNavigation()) ); + + this.navigator = new TreeResourceNavigator(tree, { configurationService, ...options }); + this.disposables.push(this.navigator); } get useAltAsMultipleSelectionModifier(): boolean { @@ -937,6 +975,10 @@ class WorkbenchTreeInternals { this.styler = overrideStyles ? attachListStyler(this.tree, this.themeService, overrideStyles) : Disposable.None; } + open(browserEvent?: UIEvent): void { + this.navigator.open(browserEvent); + } + dispose(): void { this.disposables = dispose(this.disposables); dispose(this.styler); @@ -982,12 +1024,6 @@ configurationRegistry.registerConfiguration({ 'default': false, 'description': localize('horizontalScrolling setting', "Controls whether lists and trees support horizontal scrolling in the workbench. Warning: turning on this setting has a performance implication.") }, - 'workbench.tree.horizontalScrolling': { - 'type': 'boolean', - 'default': false, - 'description': localize('tree horizontalScrolling setting', "Controls whether trees support horizontal scrolling in the workbench."), - 'deprecationMessage': localize('deprecated', "This setting is deprecated, please use '{0}' instead.", horizontalScrollingKey) - }, [treeIndentKey]: { 'type': 'number', 'default': 8, @@ -1001,6 +1037,11 @@ configurationRegistry.registerConfiguration({ default: 'onHover', description: localize('render tree indent guides', "Controls whether the tree should render indent guides.") }, + [listSmoothScrolling]: { + type: 'boolean', + default: false, + description: localize('list smoothScrolling setting', "Controls whether lists and trees have smooth scrolling."), + }, [keyboardNavigationSettingKey]: { 'type': 'string', 'enum': ['simple', 'highlight', 'filter'], diff --git a/src/vs/platform/localizations/common/localizations.ts b/src/vs/platform/localizations/common/localizations.ts index 4323324d681..509c4ada830 100644 --- a/src/vs/platform/localizations/common/localizations.ts +++ b/src/vs/platform/localizations/common/localizations.ts @@ -19,17 +19,12 @@ export interface ITranslation { path: string; } -export const enum LanguageType { - Core = 1, - Contributed -} - export const ILocalizationsService = createDecorator('localizationsService'); export interface ILocalizationsService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly onDidLanguagesChange: Event; - getLanguageIds(type?: LanguageType): Promise; + getLanguageIds(): Promise; } export function isValidLocalization(localization: ILocalization): boolean { @@ -54,4 +49,4 @@ export function isValidLocalization(localization: ILocalization): boolean { return false; } return true; -} \ No newline at end of file +} diff --git a/src/vs/platform/localizations/node/localizations.ts b/src/vs/platform/localizations/node/localizations.ts index 6a3b96f8214..ea3d80192f1 100644 --- a/src/vs/platform/localizations/node/localizations.ts +++ b/src/vs/platform/localizations/node/localizations.ts @@ -12,8 +12,7 @@ import { INativeEnvironmentService } from 'vs/platform/environment/node/environm import { Queue } from 'vs/base/common/async'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ILogService } from 'vs/platform/log/common/log'; -import { isValidLocalization, ILocalizationsService, LanguageType } from 'vs/platform/localizations/common/localizations'; -import product from 'vs/platform/product/common/product'; +import { isValidLocalization, ILocalizationsService } from 'vs/platform/localizations/common/localizations'; import { distinct, equals } from 'vs/base/common/arrays'; import { Event, Emitter } from 'vs/base/common/event'; import { Schemas } from 'vs/base/common/network'; @@ -28,14 +27,9 @@ interface ILanguagePack { translations: { [id: string]: string }; } -const systemLanguages: string[] = ['de', 'en', 'en-US', 'es', 'fr', 'it', 'ja', 'ko', 'ru', 'zh-CN', 'zh-TW']; -if (product.quality !== 'stable') { - systemLanguages.push('hu'); -} - export class LocalizationsService extends Disposable implements ILocalizationsService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly cache: LanguagePacksCache; @@ -54,13 +48,11 @@ export class LocalizationsService extends Disposable implements ILocalizationsSe this._register(extensionManagementService.onDidUninstallExtension(({ identifier }) => this.onDidUninstallExtension(identifier))); } - getLanguageIds(type: LanguageType): Promise { - if (type === LanguageType.Core) { - return Promise.resolve([...systemLanguages]); - } + getLanguageIds(): Promise { return this.cache.getLanguagePacks() .then(languagePacks => { - const languages = type === LanguageType.Contributed ? Object.keys(languagePacks) : [...systemLanguages, ...Object.keys(languagePacks)]; + // Contributed languages are those installed via extension packs, so does not include English + const languages = ['en', ...Object.keys(languagePacks)]; return distinct(languages); }); } diff --git a/src/vs/platform/log/browser/log.ts b/src/vs/platform/log/browser/log.ts index 466ba9dbaba..918f37c25d4 100644 --- a/src/vs/platform/log/browser/log.ts +++ b/src/vs/platform/log/browser/log.ts @@ -18,7 +18,7 @@ export class ConsoleLogInAutomationService extends LogServiceAdapter implements declare codeAutomationLog: any; - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(logLevel: LogLevel = DEFAULT_LOG_LEVEL) { super({ consoleLog: (type, args) => this.consoleLog(type, args) }, logLevel); diff --git a/src/vs/platform/log/common/bufferLog.ts b/src/vs/platform/log/common/bufferLog.ts index aa34008245a..2b1e69edd37 100644 --- a/src/vs/platform/log/common/bufferLog.ts +++ b/src/vs/platform/log/common/bufferLog.ts @@ -24,7 +24,7 @@ function getLogFunction(logger: ILogService, level: LogLevel): Function { export class BufferLogService extends AbstractLogService implements ILogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private buffer: ILog[] = []; private _logger: ILogService | undefined = undefined; diff --git a/src/vs/platform/log/common/fileLogService.ts b/src/vs/platform/log/common/fileLogService.ts index 20716554d04..b182280dcdf 100644 --- a/src/vs/platform/log/common/fileLogService.ts +++ b/src/vs/platform/log/common/fileLogService.ts @@ -17,7 +17,7 @@ const MAX_FILE_SIZE = 1024 * 1024 * 5; export class FileLogService extends AbstractLogService implements ILogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly initializePromise: Promise; private readonly queue: Queue; @@ -157,7 +157,7 @@ export class FileLogService extends AbstractLogService implements ILogService { export class FileLoggerService extends Disposable implements ILoggerService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly loggers = new Map(); diff --git a/src/vs/platform/log/common/log.ts b/src/vs/platform/log/common/log.ts index 59ffc8e9ccd..add6b7666af 100644 --- a/src/vs/platform/log/common/log.ts +++ b/src/vs/platform/log/common/log.ts @@ -50,11 +50,11 @@ export interface ILogger extends IDisposable { } export interface ILogService extends ILogger { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; } export interface ILoggerService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; getLogger(file: URI): ILogger; } @@ -80,7 +80,7 @@ export abstract class AbstractLogService extends Disposable { export class ConsoleLogMainService extends AbstractLogService implements ILogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private useColors: boolean; constructor(logLevel: LogLevel = DEFAULT_LOG_LEVEL) { @@ -161,7 +161,7 @@ export class ConsoleLogMainService extends AbstractLogService implements ILogSer export class ConsoleLogService extends AbstractLogService implements ILogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(logLevel: LogLevel = DEFAULT_LOG_LEVEL) { super(); @@ -215,7 +215,7 @@ export class ConsoleLogService extends AbstractLogService implements ILogService export class LogServiceAdapter extends AbstractLogService implements ILogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(private readonly adapter: { consoleLog: (type: string, args: any[]) => void }, logLevel: LogLevel = DEFAULT_LOG_LEVEL) { super(); @@ -277,7 +277,7 @@ export class LogServiceAdapter extends AbstractLogService implements ILogService export class ConsoleLogInMainService extends LogServiceAdapter implements ILogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(client: LoggerChannelClient, logLevel: LogLevel = DEFAULT_LOG_LEVEL) { super({ consoleLog: (type, args) => client.consoleLog(type, args) }, logLevel); @@ -285,7 +285,7 @@ export class ConsoleLogInMainService extends LogServiceAdapter implements ILogSe } export class MultiplexLogService extends AbstractLogService implements ILogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(private readonly logServices: ReadonlyArray) { super(); @@ -351,7 +351,7 @@ export class MultiplexLogService extends AbstractLogService implements ILogServi } export class DelegatedLogService extends Disposable implements ILogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(private logService: ILogService) { super(); @@ -400,7 +400,7 @@ export class DelegatedLogService extends Disposable implements ILogService { } export class NullLogService implements ILogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; readonly onDidChangeLogLevel: Event = new Emitter().event; setLevel(level: LogLevel): void { } getLevel(): LogLevel { return LogLevel.Info; } diff --git a/src/vs/platform/log/common/logIpc.ts b/src/vs/platform/log/common/logIpc.ts index 80cb6fcd883..4c92e8d88af 100644 --- a/src/vs/platform/log/common/logIpc.ts +++ b/src/vs/platform/log/common/logIpc.ts @@ -73,16 +73,16 @@ export class LoggerChannelClient { } export class FollowerLogService extends DelegatedLogService implements ILogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; - constructor(private master: LoggerChannelClient, logService: ILogService) { + constructor(private parent: LoggerChannelClient, logService: ILogService) { super(logService); - this._register(master.onDidChangeLogLevel(level => logService.setLevel(level))); + this._register(parent.onDidChangeLogLevel(level => logService.setLevel(level))); } setLevel(level: LogLevel): void { super.setLevel(level); - this.master.setLevel(level); + this.parent.setLevel(level); } } diff --git a/src/vs/platform/log/node/loggerService.ts b/src/vs/platform/log/node/loggerService.ts index fa5c621c264..7120d99d213 100644 --- a/src/vs/platform/log/node/loggerService.ts +++ b/src/vs/platform/log/node/loggerService.ts @@ -14,7 +14,7 @@ import { SpdLogService } from 'vs/platform/log/node/spdlogService'; export class LoggerService extends Disposable implements ILoggerService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly loggers = new Map(); diff --git a/src/vs/platform/log/node/spdlogService.ts b/src/vs/platform/log/node/spdlogService.ts index ac8c8dc0a6f..c32ab07af39 100644 --- a/src/vs/platform/log/node/spdlogService.ts +++ b/src/vs/platform/log/node/spdlogService.ts @@ -44,7 +44,7 @@ function log(logger: spdlog.RotatingLogger, level: LogLevel, message: string): v export class SpdLogService extends AbstractLogService implements ILogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private buffer: ILog[] = []; private _loggerCreationPromise: Promise | undefined = undefined; diff --git a/src/vs/platform/markers/common/markerService.ts b/src/vs/platform/markers/common/markerService.ts index 1ee846eb8fd..537ade06001 100644 --- a/src/vs/platform/markers/common/markerService.ts +++ b/src/vs/platform/markers/common/markerService.ts @@ -139,7 +139,7 @@ class MarkerStats implements MarkerStatistics { export class MarkerService implements IMarkerService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _onMarkerChanged = new Emitter(); readonly onMarkerChanged: Event = Event.debounce(this._onMarkerChanged.event, MarkerService._debouncer, 0); diff --git a/src/vs/platform/markers/common/markers.ts b/src/vs/platform/markers/common/markers.ts index e89a52153d2..244d9ce7786 100644 --- a/src/vs/platform/markers/common/markers.ts +++ b/src/vs/platform/markers/common/markers.ts @@ -10,7 +10,7 @@ import { localize } from 'vs/nls'; import Severity from 'vs/base/common/severity'; export interface IMarkerService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; getStatistics(): MarkerStatistics; diff --git a/src/vs/platform/menubar/node/menubar.ts b/src/vs/platform/menubar/common/menubar.ts similarity index 90% rename from src/vs/platform/menubar/node/menubar.ts rename to src/vs/platform/menubar/common/menubar.ts index db246d1b487..69b74dfbe33 100644 --- a/src/vs/platform/menubar/node/menubar.ts +++ b/src/vs/platform/menubar/common/menubar.ts @@ -3,14 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; -export const IMenubarService = createDecorator('menubarService'); - -export interface IMenubarService { - _serviceBrand: undefined; - +export interface ICommonMenubarService { updateMenubar(windowId: number, menuData: IMenubarData): Promise; } diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index ac66913edbf..e4cd33ab6e8 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -18,7 +18,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { mnemonicMenuLabel as baseMnemonicLabel } from 'vs/base/common/labels'; import { IWindowsMainService, IWindowsCountChangedEvent } from 'vs/platform/windows/electron-main/windows'; import { IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService'; -import { IMenubarData, IMenubarKeybinding, MenubarMenuItem, isMenubarMenuItemSeparator, isMenubarMenuItemSubmenu, isMenubarMenuItemAction, IMenubarMenu, isMenubarMenuItemUriAction } from 'vs/platform/menubar/node/menubar'; +import { IMenubarData, IMenubarKeybinding, MenubarMenuItem, isMenubarMenuItemSeparator, isMenubarMenuItemSubmenu, isMenubarMenuItemAction, IMenubarMenu, isMenubarMenuItemUriAction } from 'vs/platform/menubar/common/menubar'; import { URI } from 'vs/base/common/uri'; import { IStateService } from 'vs/platform/state/node/state'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; @@ -49,6 +49,7 @@ export class Menubar { private willShutdown: boolean | undefined; private appMenuInstalled: boolean | undefined; private closedLastWindow: boolean; + private noActiveWindow: boolean; private menuUpdater: RunOnceScheduler; private menuGC: RunOnceScheduler; @@ -61,7 +62,7 @@ export class Menubar { private keybindings: { [commandId: string]: IMenubarKeybinding }; - private readonly fallbackMenuHandlers: { [id: string]: (menuItem: MenuItem, browserWindow: BrowserWindow, event: Event) => void } = Object.create(null); + private readonly fallbackMenuHandlers: { [id: string]: (menuItem: MenuItem, browserWindow: BrowserWindow | undefined, event: Event) => void } = Object.create(null); constructor( @IUpdateService private readonly updateService: IUpdateService, @@ -89,6 +90,7 @@ export class Menubar { this.addFallbackHandlers(); this.closedLastWindow = false; + this.noActiveWindow = false; this.oldMenus = []; @@ -113,8 +115,8 @@ export class Menubar { private addFallbackHandlers(): void { // File Menu Items - this.fallbackMenuHandlers['workbench.action.files.newUntitledFile'] = (menuItem, win, event) => this.windowsMainService.openEmptyWindow({ context: OpenContext.MENU, contextWindowId: win.id }); - this.fallbackMenuHandlers['workbench.action.newWindow'] = (menuItem, win, event) => this.windowsMainService.openEmptyWindow({ context: OpenContext.MENU, contextWindowId: win.id }); + this.fallbackMenuHandlers['workbench.action.files.newUntitledFile'] = (menuItem, win, event) => this.windowsMainService.openEmptyWindow({ context: OpenContext.MENU, contextWindowId: win?.id }); + this.fallbackMenuHandlers['workbench.action.newWindow'] = (menuItem, win, event) => this.windowsMainService.openEmptyWindow({ context: OpenContext.MENU, contextWindowId: win?.id }); this.fallbackMenuHandlers['workbench.action.files.openFileFolder'] = (menuItem, win, event) => this.electronMainService.pickFileFolderAndOpen(undefined, { forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }); this.fallbackMenuHandlers['workbench.action.openWorkspace'] = (menuItem, win, event) => this.electronMainService.pickWorkspaceAndOpen(undefined, { forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }); @@ -168,6 +170,8 @@ export class Menubar { // // Listen to some events from window service to update menu this.windowsMainService.onWindowsCountChanged(e => this.onWindowsCountChanged(e)); + this.electronMainService.onWindowBlur(() => this.onWindowFocusChange()); + this.electronMainService.onWindowFocus(() => this.onWindowFocusChange()); } private get currentEnableMenuBarMnemonics(): boolean { @@ -234,6 +238,15 @@ export class Menubar { } } + private onWindowFocusChange(): void { + if (!isMacintosh) { + return; + } + + this.noActiveWindow = !BrowserWindow.getFocusedWindow(); + this.scheduleUpdateMenu(); + } + private install(): void { // Store old menu in our array to avoid GC to collect the menu and crash. See #55347 // TODO@sbatten Remove this when fixed upstream by Electron @@ -412,12 +425,12 @@ export class Menubar { case 'File': case 'Help': if (isMacintosh) { - return (this.windowsMainService.getWindowCount() === 0 && this.closedLastWindow) || (!!this.menubarMenus && !!this.menubarMenus[menuId]); + return (this.windowsMainService.getWindowCount() === 0 && this.closedLastWindow) || (this.windowsMainService.getWindowCount() > 0 && this.noActiveWindow) || (!!this.menubarMenus && !!this.menubarMenus[menuId]); } case 'Window': if (isMacintosh) { - return (this.windowsMainService.getWindowCount() === 0 && this.closedLastWindow) || !!this.menubarMenus; + return (this.windowsMainService.getWindowCount() === 0 && this.closedLastWindow) || (this.windowsMainService.getWindowCount() > 0 && this.noActiveWindow) || !!this.menubarMenus; } default: @@ -432,7 +445,7 @@ export class Menubar { menu.append(__separator__()); } else if (isMenubarMenuItemSubmenu(item)) { const submenu = new Menu(); - const submenuItem = new MenuItem({ label: this.mnemonicLabel(item.label), submenu: submenu }); + const submenuItem = new MenuItem({ label: this.mnemonicLabel(item.label), submenu }); this.setMenu(submenu, item.submenu.items); menu.append(submenuItem); } else if (isMenubarMenuItemUriAction(item)) { @@ -443,7 +456,8 @@ export class Menubar { } if (isMacintosh) { - if (this.windowsMainService.getWindowCount() === 0 && this.closedLastWindow) { + if ((this.windowsMainService.getWindowCount() === 0 && this.closedLastWindow) || + (this.windowsMainService.getWindowCount() > 0 && this.noActiveWindow)) { // In the fallback scenario, we are either disabled or using a fallback handler if (this.fallbackMenuHandlers[item.id]) { menu.append(new MenuItem(this.likeAction(item.id, { label: this.mnemonicLabel(item.label), click: this.fallbackMenuHandlers[item.id] }))); diff --git a/src/vs/platform/menubar/electron-main/menubarMainService.ts b/src/vs/platform/menubar/electron-main/menubarMainService.ts index 7cadd57ecdb..836db31e73f 100644 --- a/src/vs/platform/menubar/electron-main/menubarMainService.ts +++ b/src/vs/platform/menubar/electron-main/menubarMainService.ts @@ -3,15 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IMenubarService, IMenubarData } from 'vs/platform/menubar/node/menubar'; +import { ICommonMenubarService, IMenubarData } from 'vs/platform/menubar/common/menubar'; import { Menubar } from 'vs/platform/menubar/electron-main/menubar'; import { ILogService } from 'vs/platform/log/common/log'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; -export class MenubarMainService implements IMenubarService { +export const IMenubarMainService = createDecorator('menubarMainService'); - _serviceBrand: undefined; +export interface IMenubarMainService extends ICommonMenubarService { + readonly _serviceBrand: undefined; +} + +export class MenubarMainService implements IMenubarMainService { + + declare readonly _serviceBrand: undefined; private menubar: Promise; diff --git a/src/vs/platform/menubar/electron-sandbox/menubar.ts b/src/vs/platform/menubar/electron-sandbox/menubar.ts new file mode 100644 index 00000000000..77f60e85e9a --- /dev/null +++ b/src/vs/platform/menubar/electron-sandbox/menubar.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ICommonMenubarService } from 'vs/platform/menubar/common/menubar'; + +export const IMenubarService = createDecorator('menubarService'); + +export interface IMenubarService extends ICommonMenubarService { + readonly _serviceBrand: undefined; +} diff --git a/src/vs/platform/notification/common/notification.ts b/src/vs/platform/notification/common/notification.ts index 34e33e31894..cc3cb107774 100644 --- a/src/vs/platform/notification/common/notification.ts +++ b/src/vs/platform/notification/common/notification.ts @@ -284,7 +284,7 @@ export enum NotificationsFilter { */ export interface INotificationService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Show the provided notification to the user. The returned `INotificationHandle` diff --git a/src/vs/platform/notification/test/common/testNotificationService.ts b/src/vs/platform/notification/test/common/testNotificationService.ts index 671339e55e9..82d9960eaeb 100644 --- a/src/vs/platform/notification/test/common/testNotificationService.ts +++ b/src/vs/platform/notification/test/common/testNotificationService.ts @@ -8,7 +8,7 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; export class TestNotificationService implements INotificationService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private static readonly NO_OP: INotificationHandle = new NoOpNotification(); diff --git a/src/vs/platform/opener/common/opener.ts b/src/vs/platform/opener/common/opener.ts index 8f7921d1581..977cb79b80d 100644 --- a/src/vs/platform/opener/common/opener.ts +++ b/src/vs/platform/opener/common/opener.ts @@ -53,7 +53,7 @@ export interface IExternalUriResolver { export interface IOpenerService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Register a participant that can handle the open() call. diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index 87a3b99c703..3370a608b4b 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -20,10 +20,14 @@ if (isWeb) { // Running out of sources if (Object.keys(product).length === 0) { Object.assign(product, { - version: '1.45.0-dev', + version: '1.48.0-dev', nameLong: 'Visual Studio Code Web Dev', nameShort: 'VSCode Web Dev', - urlProtocol: 'code-oss' + urlProtocol: 'code-oss', + extensionAllowedProposedApi: [ + 'ms-vscode.references-view', + 'ms-vscode.github-browser' + ], }); } } diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts index 266aa69fc65..040c869d94c 100644 --- a/src/vs/platform/product/common/productService.ts +++ b/src/vs/platform/product/common/productService.ts @@ -11,7 +11,7 @@ export const IProductService = createDecorator('productService' export interface IProductService extends Readonly { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; } @@ -22,7 +22,12 @@ export interface IBuiltInExtension { readonly metadata: any; } -export type ConfigurationSyncStore = { url: string, authenticationProviders: IStringDictionary<{ scopes: string[] }> }; +export type ConfigurationSyncStore = { + url: string, + insidersUrl?: string, + stableUrl?: string, + authenticationProviders: IStringDictionary<{ scopes: string[] }> +}; export interface IProductConfiguration { readonly version: string; @@ -49,6 +54,13 @@ export interface IProductConfiguration { readonly settingsSearchBuildId?: number; readonly settingsSearchUrl?: string; + readonly tasConfig?: { + endpoint: string; + telemetryEventName: string; + featuresTelemetryPropertyName: string; + assignmentContextTelemetryPropertyName: string; + }; + readonly experimentsUrl?: string; readonly extensionsGallery?: { @@ -102,12 +114,7 @@ export interface IProductConfiguration { readonly checksums?: { [path: string]: string; }; readonly checksumFailMoreInfoUrl?: string; - readonly appCenter?: { - readonly 'win32-ia32': string; - readonly 'win32-x64': string; - readonly 'linux-x64': string; - readonly 'darwin': string; - }; + readonly appCenter?: IAppCenterConfiguration; readonly portable?: string; @@ -120,6 +127,13 @@ export interface IProductConfiguration { readonly 'configurationSync.store'?: ConfigurationSyncStore; } +export interface IAppCenterConfiguration { + readonly 'win32-ia32': string; + readonly 'win32-x64': string; + readonly 'linux-x64': string; + readonly 'darwin': string; +} + export interface IConfigBasedExtensionTip { configPath: string; configName: string; @@ -129,9 +143,7 @@ export interface IConfigBasedExtensionTip { export interface IExeBasedExtensionTip { friendlyName: string; windowsPath?: string; - recommendations: readonly string[]; - important?: boolean; - exeFriendlyName?: string; + recommendations: IStringDictionary<{ name: string, important?: boolean, isExtensionPack?: boolean }>; } export interface IRemoteExtensionTip { diff --git a/src/vs/platform/progress/common/progress.ts b/src/vs/platform/progress/common/progress.ts index 2a5cfda1b32..34214b8a324 100644 --- a/src/vs/platform/progress/common/progress.ts +++ b/src/vs/platform/progress/common/progress.ts @@ -15,7 +15,7 @@ export const IProgressService = createDecorator('progressServi */ export interface IProgressService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; withProgress( options: IProgressOptions | IProgressNotificationOptions | IProgressWindowOptions | IProgressCompositeOptions, @@ -179,5 +179,5 @@ export const IEditorProgressService = createDecorator('e */ export interface IEditorProgressService extends IProgressIndicator { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; } diff --git a/src/vs/platform/quickinput/browser/pickerQuickAccess.ts b/src/vs/platform/quickinput/browser/pickerQuickAccess.ts index 204ed7c4305..8286520c756 100644 --- a/src/vs/platform/quickinput/browser/pickerQuickAccess.ts +++ b/src/vs/platform/quickinput/browser/pickerQuickAccess.ts @@ -76,7 +76,6 @@ export type Pick = T | IQuickPickSeparator; export type PicksWithActive = { items: ReadonlyArray>, active?: T }; export type Picks = ReadonlyArray> | PicksWithActive; export type FastAndSlowPicks = { picks: Picks, additionalPicks: Promise> }; -export type FastAndSlowPicksWithActive = { picks: PicksWithActive, additionalPicks: PicksWithActive> }; function isPicksWithActive(obj: unknown): obj is PicksWithActive { const candidate = obj as PicksWithActive; @@ -211,9 +210,23 @@ export abstract class PickerQuickAccessProvider 0 || !fastPicksApplied) { + // If we do not have any activePick or additionalActivePick + // we try to preserve the currently active pick from the + // fast results. This fixes an issue where the user might + // have made a pick active before the additional results + // kick in. + // See https://github.com/microsoft/vscode/issues/102480 + let fallbackActivePick: Pick | undefined = undefined; + if (!activePick && !additionalActivePick) { + const fallbackActivePickCandidate = picker.activeItems[0]; + if (fallbackActivePickCandidate && picks.indexOf(fallbackActivePickCandidate) !== -1) { + fallbackActivePick = fallbackActivePickCandidate; + } + } + applyPicks({ items: [...picks, ...additionalPicks], - active: activePick || additionalActivePick + active: activePick || additionalActivePick || fallbackActivePick }); } } finally { diff --git a/src/vs/platform/quickinput/browser/quickInput.ts b/src/vs/platform/quickinput/browser/quickInput.ts index 1ea7ca600a1..86f5ddaf348 100644 --- a/src/vs/platform/quickinput/browser/quickInput.ts +++ b/src/vs/platform/quickinput/browser/quickInput.ts @@ -23,7 +23,7 @@ export interface IQuickInputControllerHost extends ILayoutService { } export class QuickInputService extends Themable implements IQuickInputService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; get backButton(): IQuickInputButton { return this.controller.backButton; } diff --git a/src/vs/platform/quickinput/common/quickAccess.ts b/src/vs/platform/quickinput/common/quickAccess.ts index b817188c26c..3b682f00add 100644 --- a/src/vs/platform/quickinput/common/quickAccess.ts +++ b/src/vs/platform/quickinput/common/quickAccess.ts @@ -24,7 +24,7 @@ export interface IQuickAccessOptions { itemActivation?: ItemActivation; /** - * Wether to take the input value as is and not restore it + * Whether to take the input value as is and not restore it * from any existing value if quick access is visible. */ preserveValue?: boolean; diff --git a/src/vs/platform/quickinput/common/quickInput.ts b/src/vs/platform/quickinput/common/quickInput.ts index 6b6009967b5..8f2a9f8ae28 100644 --- a/src/vs/platform/quickinput/common/quickInput.ts +++ b/src/vs/platform/quickinput/common/quickInput.ts @@ -17,7 +17,7 @@ export type Omit = Pick>; export interface IQuickInputService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Provides access to the back button in quick input. diff --git a/src/vs/platform/remote/browser/browserSocketFactory.ts b/src/vs/platform/remote/browser/browserSocketFactory.ts index d0f6e6b18a6..3715cbb8e6e 100644 --- a/src/vs/platform/remote/browser/browserSocketFactory.ts +++ b/src/vs/platform/remote/browser/browserSocketFactory.ts @@ -194,6 +194,9 @@ class BrowserSocket implements ISocket { this.socket.close(); } + public drain(): Promise { + return Promise.resolve(); + } } diff --git a/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts b/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts index ed6281a5dde..cfe1929acff 100644 --- a/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts +++ b/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts @@ -3,41 +3,74 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ResolvedAuthority, IRemoteAuthorityResolverService, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { ResolvedAuthority, IRemoteAuthorityResolverService, ResolverResult, IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { RemoteAuthorities } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; -export class RemoteAuthorityResolverService implements IRemoteAuthorityResolverService { +export class RemoteAuthorityResolverService extends Disposable implements IRemoteAuthorityResolverService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; - constructor( - resourceUriProvider: ((uri: URI) => URI) | undefined - ) { + private readonly _onDidChangeConnectionData = this._register(new Emitter()); + public readonly onDidChangeConnectionData = this._onDidChangeConnectionData.event; + + private readonly _cache: Map; + private readonly _connectionTokens: Map; + + constructor(resourceUriProvider: ((uri: URI) => URI) | undefined) { + super(); + this._cache = new Map(); + this._connectionTokens = new Map(); if (resourceUriProvider) { RemoteAuthorities.setDelegate(resourceUriProvider); } } - resolveAuthority(authority: string): Promise { + async resolveAuthority(authority: string): Promise { + if (!this._cache.has(authority)) { + const result = this._doResolveAuthority(authority); + RemoteAuthorities.set(authority, result.authority.host, result.authority.port); + this._cache.set(authority, result); + this._onDidChangeConnectionData.fire(); + } + return this._cache.get(authority)!; + } + + getConnectionData(authority: string): IRemoteConnectionData | null { + if (!this._cache.has(authority)) { + return null; + } + const resolverResult = this._cache.get(authority)!; + const connectionToken = this._connectionTokens.get(authority); + return { + host: resolverResult.authority.host, + port: resolverResult.authority.port, + connectionToken: connectionToken + }; + } + + private _doResolveAuthority(authority: string): ResolverResult { if (authority.indexOf(':') >= 0) { const pieces = authority.split(':'); - return Promise.resolve(this._createResolvedAuthority(authority, pieces[0], parseInt(pieces[1], 10))); + return { authority: { authority, host: pieces[0], port: parseInt(pieces[1], 10) } }; } - return Promise.resolve(this._createResolvedAuthority(authority, authority, 80)); + return { authority: { authority, host: authority, port: 80 } }; } - private _createResolvedAuthority(authority: string, host: string, port: number): ResolverResult { - RemoteAuthorities.set(authority, host, port); - return { authority: { authority, host, port } }; + _clearResolvedAuthority(authority: string): void { } - clearResolvedAuthority(authority: string): void { + _setResolvedAuthority(resolvedAuthority: ResolvedAuthority) { } - setResolvedAuthority(resolvedAuthority: ResolvedAuthority) { + _setResolvedAuthorityError(authority: string, err: any): void { } - setResolvedAuthorityError(authority: string, err: any): void { + _setAuthorityConnectionToken(authority: string, connectionToken: string): void { + this._connectionTokens.set(authority, connectionToken); + RemoteAuthorities.setConnectionToken(authority, connectionToken); + this._onDidChangeConnectionData.fire(); } } diff --git a/src/vs/platform/remote/common/remoteAgentConnection.ts b/src/vs/platform/remote/common/remoteAgentConnection.ts index eab85914921..2185bb5228c 100644 --- a/src/vs/platform/remote/common/remoteAgentConnection.ts +++ b/src/vs/platform/remote/common/remoteAgentConnection.ts @@ -14,6 +14,7 @@ import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors import { ISignService } from 'vs/platform/sign/common/sign'; import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { ILogService } from 'vs/platform/log/common/log'; +import { IIPCLogger } from 'vs/base/parts/ipc/common/ipc'; export const enum ConnectionType { Management = 1, @@ -246,6 +247,7 @@ export interface IConnectionOptions { addressProvider: IAddressProvider; signService: ISignService; logService: ILogService; + ipcLogger: IIPCLogger | null; } async function resolveConnectionOptions(options: IConnectionOptions, reconnectionToken: string, reconnectionProtocol: PersistentProtocol | null): Promise { @@ -493,7 +495,7 @@ export class ManagementPersistentConnection extends PersistentConnection { this.client = this._register(new Client(protocol, { remoteAuthority: remoteAuthority, clientId: clientId - })); + }, options.ipcLogger)); } protected async _reconnect(options: ISimpleConnectionOptions): Promise { diff --git a/src/vs/platform/remote/common/remoteAgentEnvironment.ts b/src/vs/platform/remote/common/remoteAgentEnvironment.ts index 839a6cb5c19..3052141ff01 100644 --- a/src/vs/platform/remote/common/remoteAgentEnvironment.ts +++ b/src/vs/platform/remote/common/remoteAgentEnvironment.ts @@ -5,20 +5,18 @@ import { URI } from 'vs/base/common/uri'; import { OperatingSystem } from 'vs/base/common/platform'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; export interface IRemoteAgentEnvironment { pid: number; connectionToken: string; appRoot: URI; - appSettingsHome: URI; settingsPath: URI; logsPath: URI; extensionsPath: URI; extensionHostLogsPath: URI; globalStorageHome: URI; + workspaceStorageHome: URI; userHome: URI; - extensions: IExtensionDescription[]; os: OperatingSystem; } diff --git a/src/vs/platform/remote/common/remoteAuthorityResolver.ts b/src/vs/platform/remote/common/remoteAuthorityResolver.ts index 10d818bd949..075d086fd52 100644 --- a/src/vs/platform/remote/common/remoteAuthorityResolver.ts +++ b/src/vs/platform/remote/common/remoteAuthorityResolver.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { Event } from 'vs/base/common/event'; export const IRemoteAuthorityResolverService = createDecorator('remoteAuthorityResolverService'); @@ -31,6 +32,12 @@ export interface ResolverResult { tunnelInformation?: TunnelInformation; } +export interface IRemoteConnectionData { + host: string; + port: number; + connectionToken: string | undefined; +} + export enum RemoteAuthorityResolverErrorCode { Unknown = 'Unknown', NotAvailable = 'NotAvailable', @@ -77,11 +84,15 @@ export class RemoteAuthorityResolverError extends Error { export interface IRemoteAuthorityResolverService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; + + readonly onDidChangeConnectionData: Event; resolveAuthority(authority: string): Promise; + getConnectionData(authority: string): IRemoteConnectionData | null; - clearResolvedAuthority(authority: string): void; - setResolvedAuthority(resolvedAuthority: ResolvedAuthority, resolvedOptions?: ResolvedOptions): void; - setResolvedAuthorityError(authority: string, err: any): void; + _clearResolvedAuthority(authority: string): void; + _setResolvedAuthority(resolvedAuthority: ResolvedAuthority, resolvedOptions?: ResolvedOptions): void; + _setResolvedAuthorityError(authority: string, err: any): void; + _setAuthorityConnectionToken(authority: string, connectionToken: string): void; } diff --git a/src/vs/platform/remote/common/tunnel.ts b/src/vs/platform/remote/common/tunnel.ts index 0cdceb96358..c2275c2948c 100644 --- a/src/vs/platform/remote/common/tunnel.ts +++ b/src/vs/platform/remote/common/tunnel.ts @@ -3,10 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { URI } from 'vs/base/common/uri'; -import { Event } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IAddressProvider } from 'vs/platform/remote/common/remoteAgentConnection'; export const ITunnelService = createDecorator('tunnelService'); @@ -29,13 +31,13 @@ export interface ITunnelProvider { } export interface ITunnelService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly tunnels: Promise; readonly onTunnelOpened: Event; readonly onTunnelClosed: Event<{ host: string, port: number }>; - openTunnel(remoteHost: string | undefined, remotePort: number, localPort?: number): Promise | undefined; + openTunnel(addressProvider: IAddressProvider | undefined, remoteHost: string | undefined, remotePort: number, localPort?: number): Promise | undefined; closeTunnel(remoteHost: string, remotePort: number): Promise; setTunnelProvider(provider: ITunnelProvider | undefined): IDisposable; } @@ -53,3 +55,144 @@ export function extractLocalHostUriMetaDataForPortMapping(uri: URI): { address: port: +localhostMatch[2], }; } + + + +export abstract class AbstractTunnelService implements ITunnelService { + declare readonly _serviceBrand: undefined; + + private _onTunnelOpened: Emitter = new Emitter(); + public onTunnelOpened: Event = this._onTunnelOpened.event; + private _onTunnelClosed: Emitter<{ host: string, port: number }> = new Emitter(); + public onTunnelClosed: Event<{ host: string, port: number }> = this._onTunnelClosed.event; + protected readonly _tunnels = new Map }>>(); + protected _tunnelProvider: ITunnelProvider | undefined; + + public constructor( + @ILogService protected readonly logService: ILogService + ) { } + + setTunnelProvider(provider: ITunnelProvider | undefined): IDisposable { + if (!provider) { + return { + dispose: () => { } + }; + } + this._tunnelProvider = provider; + return { + dispose: () => { + this._tunnelProvider = undefined; + } + }; + } + + public get tunnels(): Promise { + const promises: Promise[] = []; + Array.from(this._tunnels.values()).forEach(portMap => Array.from(portMap.values()).forEach(x => promises.push(x.value))); + return Promise.all(promises); + } + + dispose(): void { + for (const portMap of this._tunnels.values()) { + for (const { value } of portMap.values()) { + value.then(tunnel => tunnel.dispose()); + } + portMap.clear(); + } + this._tunnels.clear(); + } + + openTunnel(addressProvider: IAddressProvider | undefined, remoteHost: string | undefined, remotePort: number, localPort: number): Promise | undefined { + if (!addressProvider) { + return undefined; + } + + if (!remoteHost || (remoteHost === '127.0.0.1')) { + remoteHost = 'localhost'; + } + + const resolvedTunnel = this.retainOrCreateTunnel(addressProvider, remoteHost, remotePort, localPort); + if (!resolvedTunnel) { + return resolvedTunnel; + } + + return resolvedTunnel.then(tunnel => { + const newTunnel = this.makeTunnel(tunnel); + if (tunnel.tunnelRemoteHost !== remoteHost || tunnel.tunnelRemotePort !== remotePort) { + this.logService.warn('Created tunnel does not match requirements of requested tunnel. Host or port mismatch.'); + } + this._onTunnelOpened.fire(newTunnel); + return newTunnel; + }); + } + + private makeTunnel(tunnel: RemoteTunnel): RemoteTunnel { + return { + tunnelRemotePort: tunnel.tunnelRemotePort, + tunnelRemoteHost: tunnel.tunnelRemoteHost, + tunnelLocalPort: tunnel.tunnelLocalPort, + localAddress: tunnel.localAddress, + dispose: () => { + const existingHost = this._tunnels.get(tunnel.tunnelRemoteHost); + if (existingHost) { + const existing = existingHost.get(tunnel.tunnelRemotePort); + if (existing) { + existing.refcount--; + this.tryDisposeTunnel(tunnel.tunnelRemoteHost, tunnel.tunnelRemotePort, existing); + } + } + } + }; + } + + private async tryDisposeTunnel(remoteHost: string, remotePort: number, tunnel: { refcount: number, readonly value: Promise }): Promise { + if (tunnel.refcount <= 0) { + const disposePromise: Promise = tunnel.value.then(tunnel => { + tunnel.dispose(true); + this._onTunnelClosed.fire({ host: tunnel.tunnelRemoteHost, port: tunnel.tunnelRemotePort }); + }); + if (this._tunnels.has(remoteHost)) { + this._tunnels.get(remoteHost)!.delete(remotePort); + } + return disposePromise; + } + } + + async closeTunnel(remoteHost: string, remotePort: number): Promise { + const portMap = this._tunnels.get(remoteHost); + if (portMap && portMap.has(remotePort)) { + const value = portMap.get(remotePort)!; + value.refcount = 0; + await this.tryDisposeTunnel(remoteHost, remotePort, value); + } + } + + protected addTunnelToMap(remoteHost: string, remotePort: number, tunnel: Promise) { + if (!this._tunnels.has(remoteHost)) { + this._tunnels.set(remoteHost, new Map()); + } + this._tunnels.get(remoteHost)!.set(remotePort, { refcount: 1, value: tunnel }); + } + + protected abstract retainOrCreateTunnel(addressProvider: IAddressProvider, remoteHost: string, remotePort: number, localPort?: number): Promise | undefined; +} + +export class TunnelService extends AbstractTunnelService { + protected retainOrCreateTunnel(_addressProvider: IAddressProvider, remoteHost: string, remotePort: number, localPort?: number | undefined): Promise | undefined { + const portMap = this._tunnels.get(remoteHost); + const existing = portMap ? portMap.get(remotePort) : undefined; + if (existing) { + ++existing.refcount; + return existing.value; + } + + if (this._tunnelProvider) { + const tunnel = this._tunnelProvider.forwardPort({ remoteAddress: { host: remoteHost, port: remotePort } }); + if (tunnel) { + this.addTunnelToMap(remoteHost, remotePort, tunnel); + } + return tunnel; + } + return undefined; + } +} diff --git a/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts b/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts index 006ce16ca33..4dd702b53ec 100644 --- a/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts +++ b/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts @@ -3,61 +3,105 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ResolvedAuthority, IRemoteAuthorityResolverService, ResolverResult, ResolvedOptions } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { ResolvedAuthority, IRemoteAuthorityResolverService, ResolverResult, ResolvedOptions, IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver'; import * as errors from 'vs/base/common/errors'; import { RemoteAuthorities } from 'vs/base/common/network'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Emitter } from 'vs/base/common/event'; class PendingResolveAuthorityRequest { + + public value: ResolverResult | null; + constructor( - public readonly resolve: (value: ResolverResult) => void, - public readonly reject: (err: any) => void, + private readonly _resolve: (value: ResolverResult) => void, + private readonly _reject: (err: any) => void, public readonly promise: Promise, ) { + this.value = null; + } + + resolve(value: ResolverResult): void { + this.value = value; + this._resolve(this.value); + } + + reject(err: any): void { + this._reject(err); } } -export class RemoteAuthorityResolverService implements IRemoteAuthorityResolverService { +export class RemoteAuthorityResolverService extends Disposable implements IRemoteAuthorityResolverService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; - private _resolveAuthorityRequests: { [authority: string]: PendingResolveAuthorityRequest; }; + private readonly _onDidChangeConnectionData = this._register(new Emitter()); + public readonly onDidChangeConnectionData = this._onDidChangeConnectionData.event; + + private readonly _resolveAuthorityRequests: Map; + private readonly _connectionTokens: Map; constructor() { - this._resolveAuthorityRequests = Object.create(null); + super(); + this._resolveAuthorityRequests = new Map(); + this._connectionTokens = new Map(); } resolveAuthority(authority: string): Promise { - if (!this._resolveAuthorityRequests[authority]) { + if (!this._resolveAuthorityRequests.has(authority)) { let resolve: (value: ResolverResult) => void; let reject: (err: any) => void; - let promise = new Promise((_resolve, _reject) => { + const promise = new Promise((_resolve, _reject) => { resolve = _resolve; reject = _reject; }); - this._resolveAuthorityRequests[authority] = new PendingResolveAuthorityRequest(resolve!, reject!, promise); + this._resolveAuthorityRequests.set(authority, new PendingResolveAuthorityRequest(resolve!, reject!, promise)); } - return this._resolveAuthorityRequests[authority].promise; + return this._resolveAuthorityRequests.get(authority)!.promise; } - clearResolvedAuthority(authority: string): void { - if (this._resolveAuthorityRequests[authority]) { - this._resolveAuthorityRequests[authority].reject(errors.canceled()); - delete this._resolveAuthorityRequests[authority]; + getConnectionData(authority: string): IRemoteConnectionData | null { + if (!this._resolveAuthorityRequests.has(authority)) { + return null; + } + const request = this._resolveAuthorityRequests.get(authority)!; + if (!request.value) { + return null; + } + const connectionToken = this._connectionTokens.get(authority); + return { + host: request.value.authority.host, + port: request.value.authority.port, + connectionToken: connectionToken + }; + } + + _clearResolvedAuthority(authority: string): void { + if (this._resolveAuthorityRequests.has(authority)) { + this._resolveAuthorityRequests.get(authority)!.reject(errors.canceled()); + this._resolveAuthorityRequests.delete(authority); } } - setResolvedAuthority(resolvedAuthority: ResolvedAuthority, options?: ResolvedOptions) { - if (this._resolveAuthorityRequests[resolvedAuthority.authority]) { - let request = this._resolveAuthorityRequests[resolvedAuthority.authority]; + _setResolvedAuthority(resolvedAuthority: ResolvedAuthority, options?: ResolvedOptions): void { + if (this._resolveAuthorityRequests.has(resolvedAuthority.authority)) { + const request = this._resolveAuthorityRequests.get(resolvedAuthority.authority)!; RemoteAuthorities.set(resolvedAuthority.authority, resolvedAuthority.host, resolvedAuthority.port); request.resolve({ authority: resolvedAuthority, options }); + this._onDidChangeConnectionData.fire(); } } - setResolvedAuthorityError(authority: string, err: any): void { - if (this._resolveAuthorityRequests[authority]) { - let request = this._resolveAuthorityRequests[authority]; + _setResolvedAuthorityError(authority: string, err: any): void { + if (this._resolveAuthorityRequests.has(authority)) { + const request = this._resolveAuthorityRequests.get(authority)!; request.reject(err); } } + + _setAuthorityConnectionToken(authority: string, connectionToken: string): void { + this._connectionTokens.set(authority, connectionToken); + RemoteAuthorities.setConnectionToken(authority, connectionToken); + this._onDidChangeConnectionData.fire(); + } } diff --git a/src/vs/workbench/services/remote/node/tunnelService.ts b/src/vs/platform/remote/node/tunnelService.ts similarity index 80% rename from src/vs/workbench/services/remote/node/tunnelService.ts rename to src/vs/platform/remote/node/tunnelService.ts index e43fb3762a6..983401be417 100644 --- a/src/vs/workbench/services/remote/node/tunnelService.ts +++ b/src/vs/platform/remote/node/tunnelService.ts @@ -6,18 +6,14 @@ import * as net from 'net'; import { Barrier } from 'vs/base/common/async'; import { Disposable } from 'vs/base/common/lifecycle'; +import { findFreePortFaster } from 'vs/base/node/ports'; import { NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import product from 'vs/platform/product/common/product'; -import { connectRemoteAgentTunnel, IConnectionOptions } from 'vs/platform/remote/common/remoteAgentConnection'; -import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { connectRemoteAgentTunnel, IConnectionOptions, IAddressProvider } from 'vs/platform/remote/common/remoteAgentConnection'; +import { AbstractTunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory'; import { ISignService } from 'vs/platform/sign/common/sign'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { ILogService } from 'vs/platform/log/common/log'; -import { findFreePortFaster } from 'vs/base/node/ports'; -import { AbstractTunnelService } from 'vs/workbench/services/remote/common/tunnelService'; async function createRemoteTunnel(options: IConnectionOptions, tunnelRemoteHost: string, tunnelRemotePort: number, tunnelLocalPort?: number): Promise { const tunnel = new NodeRemoteTunnel(options, tunnelRemoteHost, tunnelRemotePort, tunnelLocalPort); @@ -128,15 +124,14 @@ class NodeRemoteTunnel extends Disposable implements RemoteTunnel { export class TunnelService extends AbstractTunnelService { public constructor( - @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @ILogService logService: ILogService, - @IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService, @ISignService private readonly signService: ISignService, + @IProductService private readonly productService: IProductService ) { - super(environmentService, logService); + super(logService); } - protected retainOrCreateTunnel(remoteAuthority: string, remoteHost: string, remotePort: number, localPort?: number): Promise | undefined { + protected retainOrCreateTunnel(addressProvider: IAddressProvider, remoteHost: string, remotePort: number, localPort?: number): Promise | undefined { const portMap = this._tunnels.get(remoteHost); const existing = portMap ? portMap.get(remotePort) : undefined; if (existing) { @@ -152,16 +147,12 @@ export class TunnelService extends AbstractTunnelService { return tunnel; } else { const options: IConnectionOptions = { - commit: product.commit, + commit: this.productService.commit, socketFactory: nodeSocketFactory, - addressProvider: { - getAddress: async () => { - const { authority } = await this.remoteAuthorityResolverService.resolveAuthority(remoteAuthority); - return { host: authority.host, port: authority.port }; - } - }, + addressProvider, signService: this.signService, - logService: this.logService + logService: this.logService, + ipcLogger: null }; const tunnel = createRemoteTunnel(options, remoteHost, remotePort, localPort); @@ -170,5 +161,3 @@ export class TunnelService extends AbstractTunnelService { } } } - -registerSingleton(ITunnelService, TunnelService, true); diff --git a/src/vs/platform/request/browser/requestService.ts b/src/vs/platform/request/browser/requestService.ts index 76c608ad819..133e8347434 100644 --- a/src/vs/platform/request/browser/requestService.ts +++ b/src/vs/platform/request/browser/requestService.ts @@ -16,7 +16,7 @@ import { IRequestService } from 'vs/platform/request/common/request'; */ export class RequestService implements IRequestService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @IConfigurationService private readonly configurationService: IConfigurationService, diff --git a/src/vs/platform/request/common/request.ts b/src/vs/platform/request/common/request.ts index 5ddf08de7bf..d14d210e430 100644 --- a/src/vs/platform/request/common/request.ts +++ b/src/vs/platform/request/common/request.ts @@ -14,7 +14,7 @@ import { IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/r export const IRequestService = createDecorator('requestService'); export interface IRequestService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; request(options: IRequestOptions, token: CancellationToken): Promise; diff --git a/src/vs/platform/request/common/requestIpc.ts b/src/vs/platform/request/common/requestIpc.ts index 4fbcdec40da..5c96f0b4890 100644 --- a/src/vs/platform/request/common/requestIpc.ts +++ b/src/vs/platform/request/common/requestIpc.ts @@ -40,7 +40,7 @@ export class RequestChannel implements IServerChannel { export class RequestChannelClient { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(private readonly channel: IChannel) { } diff --git a/src/vs/platform/request/node/requestService.ts b/src/vs/platform/request/node/requestService.ts index ad44dcbc336..377929d065d 100644 --- a/src/vs/platform/request/node/requestService.ts +++ b/src/vs/platform/request/node/requestService.ts @@ -36,7 +36,7 @@ export interface NodeRequestOptions extends IRequestOptions { */ export class RequestService extends Disposable implements IRequestService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private proxyUrl?: string; private strictSSL: boolean | undefined; @@ -112,7 +112,7 @@ export class RequestService extends Disposable implements IRequestService { followRedirects: followRedirects - 1 }), token).then(c, e); } else { - let stream: streams.ReadableStream = res; + let stream: streams.ReadableStreamEvents = res; if (res.headers['content-encoding'] === 'gzip') { stream = res.pipe(createGunzip()); diff --git a/src/vs/platform/resource/common/resourceIdentityService.ts b/src/vs/platform/resource/common/resourceIdentityService.ts index d81e3dc8e2f..6c9d00a4f3a 100644 --- a/src/vs/platform/resource/common/resourceIdentityService.ts +++ b/src/vs/platform/resource/common/resourceIdentityService.ts @@ -10,12 +10,12 @@ import { Disposable } from 'vs/base/common/lifecycle'; export const IResourceIdentityService = createDecorator('IResourceIdentityService'); export interface IResourceIdentityService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; resolveResourceIdentity(resource: URI): Promise; } export class WebResourceIdentityService extends Disposable implements IResourceIdentityService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; async resolveResourceIdentity(resource: URI): Promise { return hash(resource.toString()).toString(16); } diff --git a/src/vs/platform/resource/node/resourceIdentityServiceImpl.ts b/src/vs/platform/resource/node/resourceIdentityServiceImpl.ts index 71cb292b6ec..fe7cd857a5f 100644 --- a/src/vs/platform/resource/node/resourceIdentityServiceImpl.ts +++ b/src/vs/platform/resource/node/resourceIdentityServiceImpl.ts @@ -14,7 +14,7 @@ import { ResourceMap } from 'vs/base/common/map'; export class NativeResourceIdentityService extends Disposable implements IResourceIdentityService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly cache: ResourceMap> = new ResourceMap>(); diff --git a/src/vs/platform/sign/browser/signService.ts b/src/vs/platform/sign/browser/signService.ts index 7de2c3d6a11..a8605ecbd82 100644 --- a/src/vs/platform/sign/browser/signService.ts +++ b/src/vs/platform/sign/browser/signService.ts @@ -7,7 +7,7 @@ import { ISignService } from 'vs/platform/sign/common/sign'; export class SignService implements ISignService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _tkn: string | null; diff --git a/src/vs/platform/sign/common/sign.ts b/src/vs/platform/sign/common/sign.ts index f3a766e760b..57d3b1e4d6f 100644 --- a/src/vs/platform/sign/common/sign.ts +++ b/src/vs/platform/sign/common/sign.ts @@ -9,7 +9,7 @@ export const SIGN_SERVICE_ID = 'signService'; export const ISignService = createDecorator(SIGN_SERVICE_ID); export interface ISignService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; sign(value: string): Promise; } diff --git a/src/vs/platform/sign/node/signService.ts b/src/vs/platform/sign/node/signService.ts index b6d0afd467c..7109d3fa70d 100644 --- a/src/vs/platform/sign/node/signService.ts +++ b/src/vs/platform/sign/node/signService.ts @@ -6,14 +6,15 @@ import { ISignService } from 'vs/platform/sign/common/sign'; declare module vsda { - // eslint-disable-next-line @typescript-eslint/class-name-casing + // the signer is a native module that for historical reasons uses a lower case class name + // eslint-disable-next-line @typescript-eslint/naming-convention export class signer { sign(arg: any): any; } } export class SignService implements ISignService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private vsda(): Promise { return new Promise((resolve, reject) => require(['vsda'], resolve, reject)); diff --git a/src/vs/platform/state/node/state.ts b/src/vs/platform/state/node/state.ts index 356ffffdf58..b07335f956f 100644 --- a/src/vs/platform/state/node/state.ts +++ b/src/vs/platform/state/node/state.ts @@ -8,10 +8,10 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' export const IStateService = createDecorator('stateService'); export interface IStateService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; getItem(key: string, defaultValue: T): T; getItem(key: string, defaultValue?: T): T | undefined; setItem(key: string, data?: object | string | number | boolean | undefined | null): void; removeItem(key: string): void; -} \ No newline at end of file +} diff --git a/src/vs/platform/state/node/stateService.ts b/src/vs/platform/state/node/stateService.ts index 4932ef70231..a30d0c6d8e6 100644 --- a/src/vs/platform/state/node/stateService.ts +++ b/src/vs/platform/state/node/stateService.ts @@ -126,7 +126,7 @@ export class FileStorage { export class StateService implements IStateService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private static readonly STATE_FILE = 'storage.json'; diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index cbf002a0605..59b1baf9120 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -18,7 +18,9 @@ import { assertIsDefined, assertAllDefined } from 'vs/base/common/types'; export class BrowserStorageService extends Disposable implements IStorageService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; + + private static readonly WORKSPACE_IS_NEW_KEY = '__$__isNewStorageMarker'; private readonly _onDidChangeStorage = this._register(new Emitter()); readonly onDidChangeStorage = this._onDidChangeStorage.event; @@ -63,6 +65,7 @@ export class BrowserStorageService extends Disposable implements IStorageService // Workspace Storage this.workspaceStorageFile = joinPath(stateRoot, `${payload.id}.json`); + this.workspaceStorageDatabase = this._register(new FileStorageDatabase(this.workspaceStorageFile, false /* do not watch for external changes */, this.fileService)); this.workspaceStorage = this._register(new Storage(this.workspaceStorageDatabase)); this._register(this.workspaceStorage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key, scope: StorageScope.WORKSPACE }))); @@ -79,6 +82,14 @@ export class BrowserStorageService extends Disposable implements IStorageService this.globalStorage.init() ]); + // Check to see if this is the first time we are "opening" this workspace + const firstOpen = this.workspaceStorage.getBoolean(BrowserStorageService.WORKSPACE_IS_NEW_KEY); + if (firstOpen === undefined) { + this.workspaceStorage.set(BrowserStorageService.WORKSPACE_IS_NEW_KEY, true); + } else if (firstOpen) { + this.workspaceStorage.set(BrowserStorageService.WORKSPACE_IS_NEW_KEY, false); + } + // In the browser we do not have support for long running unload sequences. As such, // we cannot ask for saving state in that moment, because that would result in a // long running operation. @@ -178,6 +189,10 @@ export class BrowserStorageService extends Disposable implements IStorageService this.dispose(); } + isNew(scope: StorageScope.WORKSPACE): boolean { + return this.getBoolean(BrowserStorageService.WORKSPACE_IS_NEW_KEY, scope) === true; + } + dispose(): void { dispose(this.runWhenIdleDisposable); this.runWhenIdleDisposable = undefined; diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index a2ee988de63..1623957cb18 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -22,7 +22,7 @@ export interface IWillSaveStateEvent { export interface IStorageService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Emitted whenever data is updated or deleted. @@ -103,6 +103,14 @@ export interface IStorageService { */ migrate(toWorkspace: IWorkspaceInitializationPayload): Promise; + /** + * Wether the storage for the given scope was created during this session or + * existed before. + * + * Note: currently only implemented for `WORKSPACE` scope. + */ + isNew(scope: StorageScope.WORKSPACE): boolean; + /** * Allows to flush state, e.g. in cases where a shutdown is * imminent. This will send out the onWillSaveState to ask @@ -131,7 +139,7 @@ export interface IWorkspaceStorageChangeEvent { export class InMemoryStorageService extends Disposable implements IStorageService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _onDidChangeStorage = this._register(new Emitter()); readonly onDidChangeStorage = this._onDidChangeStorage.event; @@ -227,6 +235,10 @@ export class InMemoryStorageService extends Disposable implements IStorageServic flush(): void { this._onWillSaveState.fire({ reason: WillSaveStateReason.NONE }); } + + isNew(): boolean { + return true; // always new when in-memory + } } export async function logStorage(global: Map, workspace: Map, globalPath: string, workspacePath: string): Promise { diff --git a/src/vs/platform/storage/node/storageIpc.ts b/src/vs/platform/storage/node/storageIpc.ts index 5b1d824e58b..9db4e9afcda 100644 --- a/src/vs/platform/storage/node/storageIpc.ts +++ b/src/vs/platform/storage/node/storageIpc.ts @@ -164,7 +164,7 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC export class GlobalStorageDatabaseChannelClient extends Disposable implements IStorageDatabase { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _onDidChangeItemsExternal = this._register(new Emitter()); readonly onDidChangeItemsExternal = this._onDidChangeItemsExternal.event; diff --git a/src/vs/platform/storage/node/storageMainService.ts b/src/vs/platform/storage/node/storageMainService.ts index e90d77b5cb1..1abfd328ce6 100644 --- a/src/vs/platform/storage/node/storageMainService.ts +++ b/src/vs/platform/storage/node/storageMainService.ts @@ -17,7 +17,7 @@ export const IStorageMainService = createDecorator('storage export interface IStorageMainService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Emitted whenever data is updated or deleted. @@ -86,7 +86,7 @@ export interface IStorageChangeEvent { export class StorageMainService extends Disposable implements IStorageMainService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private static readonly STORAGE_NAME = 'state.vscdb'; @@ -117,7 +117,7 @@ export class StorageMainService extends Disposable implements IStorageMainServic return SQLiteStorageDatabase.IN_MEMORY_PATH; // no storage during extension tests! } - return join(this.environmentService.globalStorageHome, StorageMainService.STORAGE_NAME); + return join(this.environmentService.globalStorageHome.fsPath, StorageMainService.STORAGE_NAME); } private createLogginOptions(): ISQLiteStorageDatabaseLoggingOptions { diff --git a/src/vs/platform/storage/node/storageService.ts b/src/vs/platform/storage/node/storageService.ts index 1c111c4d8cd..75514fe5a4f 100644 --- a/src/vs/platform/storage/node/storageService.ts +++ b/src/vs/platform/storage/node/storageService.ts @@ -20,11 +20,13 @@ import { RunOnceScheduler, runWhenIdle } from 'vs/base/common/async'; export class NativeStorageService extends Disposable implements IStorageService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private static readonly WORKSPACE_STORAGE_NAME = 'state.vscdb'; private static readonly WORKSPACE_META_NAME = 'workspace.json'; + private static readonly WORKSPACE_IS_NEW_KEY = '__$__isNewStorageMarker'; + private readonly _onDidChangeStorage = this._register(new Emitter()); readonly onDidChangeStorage = this._onDidChangeStorage.event; @@ -104,6 +106,14 @@ export class NativeStorageService extends Disposable implements IStorageService result.wasCreated ? StorageHint.STORAGE_DOES_NOT_EXIST : undefined ); await workspaceStorage.init(); + + // Check to see if this is the first time we are "opening" this workspace + const firstOpen = workspaceStorage.getBoolean(NativeStorageService.WORKSPACE_IS_NEW_KEY); + if (firstOpen === undefined) { + workspaceStorage.set(NativeStorageService.WORKSPACE_IS_NEW_KEY, result.wasCreated); + } else if (firstOpen) { + workspaceStorage.set(NativeStorageService.WORKSPACE_IS_NEW_KEY, false); + } } finally { mark('didInitWorkspaceStorage'); } @@ -133,7 +143,7 @@ export class NativeStorageService extends Disposable implements IStorageService } private getWorkspaceStorageFolderPath(payload: IWorkspaceInitializationPayload): string { - return join(this.environmentService.workspaceStorageHome, payload.id); // workspace home + workspace id; + return join(this.environmentService.workspaceStorageHome.fsPath, payload.id); // workspace home + workspace id; } private async prepareWorkspaceStorageFolder(payload: IWorkspaceInitializationPayload): Promise<{ path: string, wasCreated: boolean }> { @@ -247,13 +257,13 @@ export class NativeStorageService extends Disposable implements IStorageService return logStorage( this.globalStorage.items, this.workspaceStorage ? this.workspaceStorage.items : new Map(), // Shared process storage does not has workspace storage - this.environmentService.globalStorageHome, + this.environmentService.globalStorageHome.fsPath, this.workspaceStoragePath || ''); } async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise { if (this.workspaceStoragePath === SQLiteStorageDatabase.IN_MEMORY_PATH) { - return Promise.resolve(); // no migration needed if running in memory + return; // no migration needed if running in memory } // Close workspace DB to be able to copy @@ -270,4 +280,8 @@ export class NativeStorageService extends Disposable implements IStorageService // Recreate and init workspace storage return this.createWorkspaceStorage(newWorkspaceStoragePath).init(); } + + isNew(scope: StorageScope.WORKSPACE): boolean { + return this.getBoolean(NativeStorageService.WORKSPACE_IS_NEW_KEY, scope) === true; + } } diff --git a/src/vs/platform/storage/test/node/storageService.test.ts b/src/vs/platform/storage/test/node/storageService.test.ts index 67eabfb6b66..454f34f01ef 100644 --- a/src/vs/platform/storage/test/node/storageService.test.ts +++ b/src/vs/platform/storage/test/node/storageService.test.ts @@ -14,6 +14,7 @@ import { NullLogService } from 'vs/platform/log/common/log'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; import { InMemoryStorageDatabase } from 'vs/base/parts/storage/common/storage'; +import { URI } from 'vs/base/common/uri'; suite('StorageService', () => { @@ -85,11 +86,11 @@ suite('StorageService', () => { test('Migrate Data', async () => { class StorageTestEnvironmentService extends EnvironmentService { - constructor(private workspaceStorageFolderPath: string, private _extensionsPath: string) { + constructor(private workspaceStorageFolderPath: URI, private _extensionsPath: string) { super(parseArgs(process.argv, OPTIONS), process.execPath); } - get workspaceStorageHome(): string { + get workspaceStorageHome(): URI { return this.workspaceStorageFolderPath; } @@ -101,7 +102,7 @@ suite('StorageService', () => { const storageDir = uniqueStorageDir(); await mkdirp(storageDir); - const storage = new NativeStorageService(new InMemoryStorageDatabase(), new NullLogService(), new StorageTestEnvironmentService(storageDir, storageDir)); + const storage = new NativeStorageService(new InMemoryStorageDatabase(), new NullLogService(), new StorageTestEnvironmentService(URI.file(storageDir), storageDir)); await storage.initialize({ id: String(Date.now()) }); storage.store('bar', 'foo', StorageScope.WORKSPACE); diff --git a/src/vs/platform/telemetry/common/telemetry.ts b/src/vs/platform/telemetry/common/telemetry.ts index b3dac3f61a7..523f81e8d0b 100644 --- a/src/vs/platform/telemetry/common/telemetry.ts +++ b/src/vs/platform/telemetry/common/telemetry.ts @@ -23,7 +23,12 @@ export interface ITelemetryData { export interface ITelemetryService { - _serviceBrand: undefined; + /** + * Whether error telemetry will get sent. If false, `publicLogError` will no-op. + */ + readonly sendErrorTelemetry: boolean; + + readonly _serviceBrand: undefined; /** * Sends a telemetry event that has been privacy approved. @@ -41,6 +46,8 @@ export interface ITelemetryService { getTelemetryInfo(): Promise; + setExperimentProperty(name: string, value: string): void; + isOptedIn: boolean; } @@ -50,5 +57,4 @@ export const currentSessionDateStorageKey = 'telemetry.currentSessionDate'; export const firstSessionDateStorageKey = 'telemetry.firstSessionDate'; export const lastSessionDateStorageKey = 'telemetry.lastSessionDate'; export const machineIdKey = 'telemetry.machineId'; -export const trueMachineIdKey = 'telemetry.trueMachineId'; export const crashReporterIdStorageKey = 'crashReporter.guid'; diff --git a/src/vs/platform/telemetry/common/telemetryService.ts b/src/vs/platform/telemetry/common/telemetryService.ts index 1eb3e5603c4..1e1c6fcb583 100644 --- a/src/vs/platform/telemetry/common/telemetryService.ts +++ b/src/vs/platform/telemetry/common/telemetryService.ts @@ -20,7 +20,6 @@ export interface ITelemetryServiceConfig { sendErrorTelemetry?: boolean; commonProperties?: Promise<{ [name: string]: any }>; piiPaths?: string[]; - trueMachineId?: string; } export class TelemetryService implements ITelemetryService { @@ -28,14 +27,15 @@ export class TelemetryService implements ITelemetryService { static readonly IDLE_START_EVENT_NAME = 'UserIdleStart'; static readonly IDLE_STOP_EVENT_NAME = 'UserIdleStop'; - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _appender: ITelemetryAppender; private _commonProperties: Promise<{ [name: string]: any; }>; + private _experimentProperties: { [name: string]: string } = {}; private _piiPaths: string[]; private _userOptIn: boolean; private _enabled: boolean; - private _sendErrorTelemetry: boolean; + public readonly sendErrorTelemetry: boolean; private readonly _disposables = new DisposableStore(); private _cleanupPatterns: RegExp[] = []; @@ -49,7 +49,7 @@ export class TelemetryService implements ITelemetryService { this._piiPaths = config.piiPaths || []; this._userOptIn = true; this._enabled = true; - this._sendErrorTelemetry = !!config.sendErrorTelemetry; + this.sendErrorTelemetry = !!config.sendErrorTelemetry; // static cleanup pattern for: `file:///DANGEROUS/PATH/resources/app/Useful/Information` this._cleanupPatterns = [/file:\/\/\/.*?\/resources\/app\//gi]; @@ -76,17 +76,14 @@ export class TelemetryService implements ITelemetryService { usingFallbackGuid: { classification: 'SystemMetaData', purpose: 'BusinessInsight', isMeasurement: true }; }; this.publicLog2<{ usingFallbackGuid: boolean }, MachineIdFallbackClassification>('machineIdFallback', { usingFallbackGuid: !isHashedId }); - - if (config.trueMachineId) { - type MachineIdDisambiguationClassification = { - correctedMachineId: { endPoint: 'MacAddressHash', classification: 'EndUserPseudonymizedInformation', purpose: 'FeatureInsight' }; - }; - this.publicLog2<{ correctedMachineId: string }, MachineIdDisambiguationClassification>('machineIdDisambiguation', { correctedMachineId: config.trueMachineId }); - } }); } } + setExperimentProperty(name: string, value: string): void { + this._experimentProperties[name] = value; + } + setEnabled(value: boolean): void { this._enabled = value; } @@ -127,6 +124,9 @@ export class TelemetryService implements ITelemetryService { // (first) add common properties data = mixin(data, values); + // (next) add experiment properties + data = mixin(data, this._experimentProperties); + // (last) remove all PII from data data = cloneAndChange(data, value => { if (typeof value === 'string') { @@ -148,7 +148,7 @@ export class TelemetryService implements ITelemetryService { } publicLogError(errorEventName: string, data?: ITelemetryData): Promise { - if (!this._sendErrorTelemetry) { + if (!this.sendErrorTelemetry) { return Promise.resolve(undefined); } diff --git a/src/vs/platform/telemetry/common/telemetryUtils.ts b/src/vs/platform/telemetry/common/telemetryUtils.ts index ead9f92b06b..392c3ad8ea8 100644 --- a/src/vs/platform/telemetry/common/telemetryUtils.ts +++ b/src/vs/platform/telemetry/common/telemetryUtils.ts @@ -12,7 +12,9 @@ import { safeStringify } from 'vs/base/common/objects'; import { isObject } from 'vs/base/common/types'; export const NullTelemetryService = new class implements ITelemetryService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; + readonly sendErrorTelemetry = false; + publicLog(eventName: string, data?: ITelemetryData) { return Promise.resolve(undefined); } @@ -26,6 +28,7 @@ export const NullTelemetryService = new class implements ITelemetryService { return this.publicLogError(eventName, data as ITelemetryData); } + setExperimentProperty() { } setEnabled() { } isOptedIn = true; getTelemetryInfo(): Promise { @@ -154,8 +157,8 @@ export function cleanRemoteAuthority(remoteAuthority?: string): string { } let ret = 'other'; - // Whitelisted remote authorities - ['ssh-remote', 'dev-container', 'attached-container', 'wsl'].forEach((res: string) => { + const allowedAuthorities = ['ssh-remote', 'dev-container', 'attached-container', 'wsl']; + allowedAuthorities.forEach((res: string) => { if (remoteAuthority!.indexOf(`${res}+`) === 0) { ret = res; } diff --git a/src/vs/platform/telemetry/test/electron-browser/appInsightsAppender.test.ts b/src/vs/platform/telemetry/test/electron-browser/appInsightsAppender.test.ts index fba7d60af8b..fc12d532073 100644 --- a/src/vs/platform/telemetry/test/electron-browser/appInsightsAppender.test.ts +++ b/src/vs/platform/telemetry/test/electron-browser/appInsightsAppender.test.ts @@ -28,7 +28,7 @@ class AppInsightsMock extends TelemetryClient { } class TestableLogService extends AbstractLogService implements ILogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; public logs: string[] = []; diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 6ea71ed04e1..673f3fb3d93 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -178,7 +178,7 @@ export const errorForeground = registerColor('errorForeground', { dark: '#F48771 export const descriptionForeground = registerColor('descriptionForeground', { light: '#717171', dark: transparent(foreground, 0.7), hc: transparent(foreground, 0.7) }, nls.localize('descriptionForeground', "Foreground color for description text providing additional information, for example for a label.")); export const iconForeground = registerColor('icon.foreground', { dark: '#C5C5C5', light: '#424242', hc: '#FFFFFF' }, nls.localize('iconForeground', "The default color for icons in the workbench.")); -export const focusBorder = registerColor('focusBorder', { dark: Color.fromHex('#0E639C').transparent(0.8), light: Color.fromHex('#007ACC').transparent(0.4), hc: '#F38518' }, nls.localize('focusBorder', "Overall border color for focused elements. This color is only used if not overridden by a component.")); +export const focusBorder = registerColor('focusBorder', { dark: '#007FD4', light: '#0090F1', hc: '#F38518' }, nls.localize('focusBorder', "Overall border color for focused elements. This color is only used if not overridden by a component.")); export const contrastBorder = registerColor('contrastBorder', { light: null, dark: null, hc: '#6FC3DF' }, nls.localize('contrastBorder', "An extra border around elements to separate them from others for greater contrast.")); export const activeContrastBorder = registerColor('contrastActiveBorder', { light: null, dark: null, hc: focusBorder }, nls.localize('activeContrastBorder', "An extra border around active elements to separate them from others for greater contrast.")); @@ -202,7 +202,8 @@ export const inputBackground = registerColor('input.background', { dark: '#3C3C3 export const inputForeground = registerColor('input.foreground', { dark: foreground, light: foreground, hc: foreground }, nls.localize('inputBoxForeground', "Input box foreground.")); export const inputBorder = registerColor('input.border', { dark: null, light: null, hc: contrastBorder }, nls.localize('inputBoxBorder', "Input box border.")); export const inputActiveOptionBorder = registerColor('inputOption.activeBorder', { dark: '#007ACC00', light: '#007ACC00', hc: contrastBorder }, nls.localize('inputBoxActiveOptionBorder', "Border color of activated options in input fields.")); -export const inputActiveOptionBackground = registerColor('inputOption.activeBackground', { dark: transparent(focusBorder, 0.5), light: transparent(focusBorder, 0.3), hc: null }, nls.localize('inputOption.activeBackground', "Background color of activated options in input fields.")); +export const inputActiveOptionBackground = registerColor('inputOption.activeBackground', { dark: transparent(focusBorder, 0.4), light: transparent(focusBorder, 0.2), hc: Color.transparent }, nls.localize('inputOption.activeBackground', "Background color of activated options in input fields.")); +export const inputActiveOptionForeground = registerColor('inputOption.activeForeground', { dark: Color.white, light: Color.black, hc: null }, nls.localize('inputOption.activeForeground', "Foreground color of activated options in input fields.")); export const inputPlaceholderForeground = registerColor('input.placeholderForeground', { light: transparent(foreground, 0.5), dark: transparent(foreground, 0.5), hc: transparent(foreground, 0.7) }, nls.localize('inputPlaceholderForeground', "Input box foreground color for placeholder text.")); export const inputValidationInfoBackground = registerColor('inputValidation.infoBackground', { dark: '#063B49', light: '#D6ECF2', hc: Color.black }, nls.localize('inputValidationInfoBackground', "Input validation background color for information severity.")); @@ -228,6 +229,10 @@ export const buttonForeground = registerColor('button.foreground', { dark: Color export const buttonBackground = registerColor('button.background', { dark: '#0E639C', light: '#007ACC', hc: null }, nls.localize('buttonBackground', "Button background color.")); export const buttonHoverBackground = registerColor('button.hoverBackground', { dark: lighten(buttonBackground, 0.2), light: darken(buttonBackground, 0.2), hc: null }, nls.localize('buttonHoverBackground', "Button background color when hovering.")); +export const buttonSecondaryForeground = registerColor('button.secondaryForeground', { dark: Color.white, light: Color.white, hc: Color.white }, nls.localize('buttonSecondaryForeground', "Secondary button foreground color.")); +export const buttonSecondaryBackground = registerColor('button.secondaryBackground', { dark: '#3A3D41', light: '#5F6A79', hc: null }, nls.localize('buttonSecondaryBackground', "Secondary button background color.")); +export const buttonSecondaryHoverBackground = registerColor('button.secondaryHoverBackground', { dark: lighten(buttonSecondaryBackground, 0.2), light: darken(buttonSecondaryBackground, 0.2), hc: null }, nls.localize('buttonSecondaryHoverBackground', "Secondary button background color when hovering.")); + export const badgeBackground = registerColor('badge.background', { dark: '#4D4D4D', light: '#C4C4C4', hc: Color.black }, nls.localize('badgeBackground', "Badge background color. Badges are small information labels, e.g. for search results count.")); export const badgeForeground = registerColor('badge.foreground', { dark: Color.white, light: '#333', hc: Color.white }, nls.localize('badgeForeground', "Badge foreground color. Badges are small information labels, e.g. for search results count.")); @@ -385,7 +390,7 @@ export const menuSeparatorBackground = registerColor('menu.separatorBackground', export const snippetTabstopHighlightBackground = registerColor('editor.snippetTabstopHighlightBackground', { dark: new Color(new RGBA(124, 124, 124, 0.3)), light: new Color(new RGBA(10, 50, 100, 0.2)), hc: new Color(new RGBA(124, 124, 124, 0.3)) }, nls.localize('snippetTabstopHighlightBackground', "Highlight background color of a snippet tabstop.")); export const snippetTabstopHighlightBorder = registerColor('editor.snippetTabstopHighlightBorder', { dark: null, light: null, hc: null }, nls.localize('snippetTabstopHighlightBorder', "Highlight border color of a snippet tabstop.")); export const snippetFinalTabstopHighlightBackground = registerColor('editor.snippetFinalTabstopHighlightBackground', { dark: null, light: null, hc: null }, nls.localize('snippetFinalTabstopHighlightBackground', "Highlight background color of the final tabstop of a snippet.")); -export const snippetFinalTabstopHighlightBorder = registerColor('editor.snippetFinalTabstopHighlightBorder', { dark: '#525252', light: new Color(new RGBA(10, 50, 100, 0.5)), hc: '#525252' }, nls.localize('snippetFinalTabstopHighlightBorder', "Highlight border color of the final stabstop of a snippet.")); +export const snippetFinalTabstopHighlightBorder = registerColor('editor.snippetFinalTabstopHighlightBorder', { dark: '#525252', light: new Color(new RGBA(10, 50, 100, 0.5)), hc: '#525252' }, nls.localize('snippetFinalTabstopHighlightBorder', "Highlight border color of the final tabstop of a snippet.")); /** * Breadcrumb colors diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index 0810ef07116..c8317017afe 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService'; -import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, ColorValue, resolveColorValue, textLinkForeground, problemsWarningIconForeground, problemsErrorIconForeground, problemsInfoIconForeground } from 'vs/platform/theme/common/colorRegistry'; +import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, ColorValue, resolveColorValue, textLinkForeground, problemsWarningIconForeground, problemsErrorIconForeground, problemsInfoIconForeground, buttonSecondaryBackground, buttonSecondaryForeground, buttonSecondaryHoverBackground } from 'vs/platform/theme/common/colorRegistry'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { IThemable, styleFn } from 'vs/base/common/styler'; @@ -51,12 +51,14 @@ export function attachStyler(themeService: IThemeServic export interface ICheckboxStyleOverrides extends IStyleOverrides { inputActiveOptionBorderColor?: ColorIdentifier; + inputActiveOptionForegroundColor?: ColorIdentifier; inputActiveOptionBackgroundColor?: ColorIdentifier; } export function attachCheckboxStyler(widget: IThemable, themeService: IThemeService, style?: ICheckboxStyleOverrides): IDisposable { return attachStyler(themeService, { inputActiveOptionBorder: (style && style.inputActiveOptionBorderColor) || inputActiveOptionBorder, + inputActiveOptionForeground: (style && style.inputActiveOptionForegroundColor) || inputActiveOptionForeground, inputActiveOptionBackground: (style && style.inputActiveOptionBackgroundColor) || inputActiveOptionBackground } as ICheckboxStyleOverrides, widget); } @@ -79,6 +81,7 @@ export interface IInputBoxStyleOverrides extends IStyleOverrides { inputForeground?: ColorIdentifier; inputBorder?: ColorIdentifier; inputActiveOptionBorder?: ColorIdentifier; + inputActiveOptionForeground?: ColorIdentifier; inputActiveOptionBackground?: ColorIdentifier; inputValidationInfoBorder?: ColorIdentifier; inputValidationInfoBackground?: ColorIdentifier; @@ -141,6 +144,7 @@ export function attachFindReplaceInputBoxStyler(widget: IThemable, themeService: inputForeground: (style && style.inputForeground) || inputForeground, inputBorder: (style && style.inputBorder) || inputBorder, inputActiveOptionBorder: (style && style.inputActiveOptionBorder) || inputActiveOptionBorder, + inputActiveOptionForeground: (style && style.inputActiveOptionForeground) || inputActiveOptionForeground, inputActiveOptionBackground: (style && style.inputActiveOptionBackground) || inputActiveOptionBackground, inputValidationInfoBorder: (style && style.inputValidationInfoBorder) || inputValidationInfoBorder, inputValidationInfoBackground: (style && style.inputValidationInfoBackground) || inputValidationInfoBackground, @@ -258,6 +262,9 @@ export interface IButtonStyleOverrides extends IStyleOverrides { buttonForeground?: ColorIdentifier; buttonBackground?: ColorIdentifier; buttonHoverBackground?: ColorIdentifier; + buttonSecondaryForeground?: ColorIdentifier; + buttonSecondaryBackground?: ColorIdentifier; + buttonSecondaryHoverBackground?: ColorIdentifier; } export function attachButtonStyler(widget: IThemable, themeService: IThemeService, style?: IButtonStyleOverrides): IDisposable { @@ -265,6 +272,9 @@ export function attachButtonStyler(widget: IThemable, themeService: IThemeServic buttonForeground: (style && style.buttonForeground) || buttonForeground, buttonBackground: (style && style.buttonBackground) || buttonBackground, buttonHoverBackground: (style && style.buttonHoverBackground) || buttonHoverBackground, + buttonSecondaryForeground: (style && style.buttonSecondaryForeground) || buttonSecondaryForeground, + buttonSecondaryBackground: (style && style.buttonSecondaryBackground) || buttonSecondaryBackground, + buttonSecondaryHoverBackground: (style && style.buttonSecondaryHoverBackground) || buttonSecondaryHoverBackground, buttonBorder: contrastBorder } as IButtonStyleOverrides, widget); } diff --git a/src/vs/platform/theme/common/themeService.ts b/src/vs/platform/theme/common/themeService.ts index 77f4795576f..e33a3e9cbd4 100644 --- a/src/vs/platform/theme/common/themeService.ts +++ b/src/vs/platform/theme/common/themeService.ts @@ -87,8 +87,11 @@ export interface ITokenStyle { } export interface IColorTheme { + readonly type: ThemeType; + readonly label: string; + /** * Resolves the color of the given color identifier. If the theme does not * specify the color, the default color is returned unless useDefault is set to false. @@ -134,7 +137,7 @@ export interface IThemingParticipant { } export interface IThemeService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; getColorTheme(): IColorTheme; diff --git a/src/vs/platform/theme/common/tokenClassificationRegistry.ts b/src/vs/platform/theme/common/tokenClassificationRegistry.ts index 754c93d5bd7..f2fd8eb4264 100644 --- a/src/vs/platform/theme/common/tokenClassificationRegistry.ts +++ b/src/vs/platform/theme/common/tokenClassificationRegistry.ts @@ -162,7 +162,7 @@ export namespace SemanticTokenRule { && TokenStyle.equals(r1.style, r2.style); } export function is(r: any): r is SemanticTokenRule { - return r && r.selector && typeof r.selector.selectorString === 'string' && TokenStyle.is(r.style); + return r && r.selector && typeof r.selector.id === 'string' && TokenStyle.is(r.style); } } diff --git a/src/vs/platform/theme/electron-main/themeMainService.ts b/src/vs/platform/theme/electron-main/themeMainService.ts index 6787d57bc27..0d16ddb9002 100644 --- a/src/vs/platform/theme/electron-main/themeMainService.ts +++ b/src/vs/platform/theme/electron-main/themeMainService.ts @@ -18,14 +18,14 @@ const THEME_BG_STORAGE_KEY = 'themeBackground'; export const IThemeMainService = createDecorator('themeMainService'); export interface IThemeMainService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; getBackgroundColor(): string; } export class ThemeMainService implements IThemeMainService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(@IStateService private stateService: IStateService) { ipc.on('vscode:changeColorTheme', (e: Event, windowId: number, broadcast: string) => { diff --git a/src/vs/platform/theme/test/common/testThemeService.ts b/src/vs/platform/theme/test/common/testThemeService.ts index 82dbe518768..bc5fc2403f1 100644 --- a/src/vs/platform/theme/test/common/testThemeService.ts +++ b/src/vs/platform/theme/test/common/testThemeService.ts @@ -9,6 +9,8 @@ import { Color } from 'vs/base/common/color'; export class TestColorTheme implements IColorTheme { + public readonly label = 'test'; + constructor(private colors: { [id: string]: string; } = {}, public type = DARK) { } @@ -43,7 +45,7 @@ export class TestFileIconTheme implements IFileIconTheme { export class TestThemeService implements IThemeService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; _colorTheme: IColorTheme; _fileIconTheme: IFileIconTheme; _onThemeChange = new Emitter(); diff --git a/src/vs/platform/undoRedo/common/undoRedo.ts b/src/vs/platform/undoRedo/common/undoRedo.ts index 935e3ffcb22..ae05184da63 100644 --- a/src/vs/platform/undoRedo/common/undoRedo.ts +++ b/src/vs/platform/undoRedo/common/undoRedo.ts @@ -5,6 +5,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; +import { IDisposable } from 'vs/base/common/lifecycle'; export const IUndoRedoService = createDecorator('undoRedoService'); @@ -27,7 +28,18 @@ export interface IWorkspaceUndoRedoElement { readonly label: string; undo(): Promise | void; redo(): Promise | void; - split(): IResourceUndoRedoElement[]; + + /** + * If implemented, indicates that this undo/redo element can be split into multiple per resource elements. + */ + split?(): IResourceUndoRedoElement[]; + + /** + * If implemented, will be invoked before calling `undo()` or `redo()`. + * This is a good place to prepare everything such that the calls to `undo()` or `redo()` are synchronous. + * If a disposable is returned, it will be invoked to clean things up. + */ + prepareUndoRedo?(): Promise | IDisposable | void; } export type IUndoRedoElement = IResourceUndoRedoElement | IWorkspaceUndoRedoElement; @@ -37,8 +49,31 @@ export interface IPastFutureElements { future: IUndoRedoElement[]; } +export interface UriComparisonKeyComputer { + getComparisonKey(uri: URI): string; +} + +export class ResourceEditStackSnapshot { + constructor( + public readonly resource: URI, + public readonly elements: number[] + ) { } +} + export interface IUndoRedoService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; + + /** + * Register an URI -> string hasher. + * This is useful for making multiple URIs share the same undo-redo stack. + */ + registerUriComparisonKeyComputer(scheme: string, uriComparisonKeyComputer: UriComparisonKeyComputer): IDisposable; + + /** + * Get the hash used internally for a certain URI. + * This uses any registered `UriComparisonKeyComputer`. + */ + getUriComparisonKey(resource: URI): string; /** * Add a new element to the `undo` stack. @@ -47,21 +82,36 @@ export interface IUndoRedoService { pushElement(element: IUndoRedoElement): void; /** - * Get the last pushed element. If the last pushed element has been undone, returns null. + * Get the last pushed element for a resource. + * If the last pushed element has been undone, returns null. */ getLastElement(resource: URI): IUndoRedoElement | null; + /** + * Get all the elements associated with a resource. + * This includes the past and the future. + */ getElements(resource: URI): IPastFutureElements; - hasElements(resource: URI): boolean; - - setElementsIsValid(resource: URI, isValid: boolean): void; + /** + * Validate or invalidate stack elements associated with a resource. + */ + setElementsValidFlag(resource: URI, isValid: boolean, filter: (element: IUndoRedoElement) => boolean): void; /** * Remove elements that target `resource`. */ removeElements(resource: URI): void; + /** + * Create a snapshot of the current elements on the undo-redo stack for a resource. + */ + createSnapshot(resource: URI): ResourceEditStackSnapshot; + /** + * Attempt (as best as possible) to restore a certain snapshot previously created with `createSnapshot` for a resource. + */ + restoreSnapshot(snapshot: ResourceEditStackSnapshot): void; + canUndo(resource: URI): boolean; undo(resource: URI): Promise | void; diff --git a/src/vs/platform/undoRedo/common/undoRedoService.ts b/src/vs/platform/undoRedo/common/undoRedoService.ts index 9858c9eb6af..a42d889cc8b 100644 --- a/src/vs/platform/undoRedo/common/undoRedoService.ts +++ b/src/vs/platform/undoRedo/common/undoRedoService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IUndoRedoService, IResourceUndoRedoElement, IWorkspaceUndoRedoElement, UndoRedoElementType, IUndoRedoElement, IPastFutureElements } from 'vs/platform/undoRedo/common/undoRedo'; +import { IUndoRedoService, IWorkspaceUndoRedoElement, UndoRedoElementType, IUndoRedoElement, IPastFutureElements, ResourceEditStackSnapshot, UriComparisonKeyComputer, IResourceUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo'; import { URI } from 'vs/base/common/uri'; import { onUnexpectedError } from 'vs/base/common/errors'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -12,28 +12,34 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; import { Schemas } from 'vs/base/common/network'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IDisposable, Disposable, isDisposable } from 'vs/base/common/lifecycle'; -function uriGetComparisonKey(resource: URI): string { - return resource.toString(); +const DEBUG = false; + +function getResourceLabel(resource: URI): string { + return resource.scheme === Schemas.file ? resource.fsPath : resource.path; } +let stackElementCounter = 0; + class ResourceStackElement { + public readonly id = (++stackElementCounter); public readonly type = UndoRedoElementType.Resource; - public readonly actual: IResourceUndoRedoElement; + public readonly actual: IUndoRedoElement; public readonly label: string; - public readonly resource: URI; + public readonly resourceLabel: string; public readonly strResource: string; - public readonly resources: URI[]; + public readonly resourceLabels: string[]; public readonly strResources: string[]; public isValid: boolean; - constructor(actual: IResourceUndoRedoElement) { + constructor(actual: IUndoRedoElement, resourceLabel: string, strResource: string) { this.actual = actual; this.label = actual.label; - this.resource = actual.resource; - this.strResource = uriGetComparisonKey(this.resource); - this.resources = [this.resource]; + this.resourceLabel = resourceLabel; + this.strResource = strResource; + this.resourceLabels = [this.resourceLabel]; this.strResources = [this.strResource]; this.isValid = true; } @@ -41,6 +47,10 @@ class ResourceStackElement { public setValid(isValid: boolean): void { this.isValid = isValid; } + + public toString(): string { + return `[${this.id}] [${this.isValid ? 'VALID' : 'INVALID'}] ${this.actual}`; + } } const enum RemovedResourceReason { @@ -50,7 +60,7 @@ const enum RemovedResourceReason { class ResourceReasonPair { constructor( - public readonly resource: URI, + public readonly resourceLabel: string, public readonly reason: RemovedResourceReason ) { } } @@ -58,10 +68,6 @@ class ResourceReasonPair { class RemovedResources { private readonly elements = new Map(); - private _getPath(resource: URI): string { - return resource.scheme === Schemas.file ? resource.fsPath : resource.path; - } - public createMessage(): string { const externalRemoval: string[] = []; const noParallelUniverses: string[] = []; @@ -71,15 +77,24 @@ class RemovedResources { ? externalRemoval : noParallelUniverses ); - dest.push(this._getPath(element.resource)); + dest.push(element.resourceLabel); } let messages: string[] = []; if (externalRemoval.length > 0) { - messages.push(nls.localize('externalRemoval', "The following files have been closed: {0}.", externalRemoval.join(', '))); + messages.push( + nls.localize( + { key: 'externalRemoval', comment: ['{0} is a list of filenames'] }, + "The following files have been closed and modified on disk: {0}.", externalRemoval.join(', ') + ) + ); } if (noParallelUniverses.length > 0) { - messages.push(nls.localize('noParallelUniverses', "The following files have been modified in an incompatible way: {0}.", noParallelUniverses.join(', '))); + messages.push( + nls.localize( + { key: 'noParallelUniverses', comment: ['{0} is a list of filenames'] }, + "The following files have been modified in an incompatible way: {0}.", noParallelUniverses.join(', ') + )); } return messages.join('\n'); } @@ -102,34 +117,39 @@ class RemovedResources { } class WorkspaceStackElement { + public readonly id = (++stackElementCounter); public readonly type = UndoRedoElementType.Workspace; public readonly actual: IWorkspaceUndoRedoElement; public readonly label: string; - public readonly resources: URI[]; + public readonly resourceLabels: string[]; public readonly strResources: string[]; public removedResources: RemovedResources | null; public invalidatedResources: RemovedResources | null; - constructor(actual: IWorkspaceUndoRedoElement) { + constructor(actual: IWorkspaceUndoRedoElement, resourceLabels: string[], strResources: string[]) { this.actual = actual; this.label = actual.label; - this.resources = actual.resources.slice(0); - this.strResources = this.resources.map(resource => uriGetComparisonKey(resource)); + this.resourceLabels = resourceLabels; + this.strResources = strResources; this.removedResources = null; this.invalidatedResources = null; } - public removeResource(resource: URI, strResource: string, reason: RemovedResourceReason): void { + public canSplit(): this is WorkspaceStackElement & { actual: { split(): IResourceUndoRedoElement[]; } } { + return (typeof this.actual.split === 'function'); + } + + public removeResource(resourceLabel: string, strResource: string, reason: RemovedResourceReason): void { if (!this.removedResources) { this.removedResources = new RemovedResources(); } if (!this.removedResources.has(strResource)) { - this.removedResources.set(strResource, new ResourceReasonPair(resource, reason)); + this.removedResources.set(strResource, new ResourceReasonPair(resourceLabel, reason)); } } - public setValid(resource: URI, strResource: string, isValid: boolean): void { + public setValid(resourceLabel: string, strResource: string, isValid: boolean): void { if (isValid) { if (this.invalidatedResources) { this.invalidatedResources.delete(strResource); @@ -142,90 +162,395 @@ class WorkspaceStackElement { this.invalidatedResources = new RemovedResources(); } if (!this.invalidatedResources.has(strResource)) { - this.invalidatedResources.set(strResource, new ResourceReasonPair(resource, RemovedResourceReason.ExternalRemoval)); + this.invalidatedResources.set(strResource, new ResourceReasonPair(resourceLabel, RemovedResourceReason.ExternalRemoval)); } } } + + public toString(): string { + return `[${this.id}] [${this.invalidatedResources ? 'INVALID' : 'VALID'}] ${this.actual}`; + } } type StackElement = ResourceStackElement | WorkspaceStackElement; class ResourceEditStack { - public resource: URI; - public past: StackElement[]; - public future: StackElement[]; + public readonly resourceLabel: string; + private readonly strResource: string; + private _past: StackElement[]; + private _future: StackElement[]; + public locked: boolean; + public versionId: number; - constructor(resource: URI) { - this.resource = resource; - this.past = []; - this.future = []; + constructor(resourceLabel: string, strResource: string) { + this.resourceLabel = resourceLabel; + this.strResource = strResource; + this._past = []; + this._future = []; + this.locked = false; + this.versionId = 1; + } + + public dispose(): void { + for (const element of this._past) { + if (element.type === UndoRedoElementType.Workspace) { + element.removeResource(this.resourceLabel, this.strResource, RemovedResourceReason.ExternalRemoval); + } + } + for (const element of this._future) { + if (element.type === UndoRedoElementType.Workspace) { + element.removeResource(this.resourceLabel, this.strResource, RemovedResourceReason.ExternalRemoval); + } + } + this.versionId++; + } + + public toString(): string { + let result: string[] = []; + result.push(`* ${this.strResource}:`); + for (let i = 0; i < this._past.length; i++) { + result.push(` * [UNDO] ${this._past[i]}`); + } + for (let i = this._future.length - 1; i >= 0; i--) { + result.push(` * [REDO] ${this._future[i]}`); + } + return result.join('\n'); + } + + public flushAllElements(): void { + this._past = []; + this._future = []; + this.versionId++; + } + + public setElementsIsValid(isValid: boolean): void { + for (const element of this._past) { + if (element.type === UndoRedoElementType.Workspace) { + element.setValid(this.resourceLabel, this.strResource, isValid); + } else { + element.setValid(isValid); + } + } + for (const element of this._future) { + if (element.type === UndoRedoElementType.Workspace) { + element.setValid(this.resourceLabel, this.strResource, isValid); + } else { + element.setValid(isValid); + } + } + } + + private _setElementValidFlag(element: StackElement, isValid: boolean): void { + if (element.type === UndoRedoElementType.Workspace) { + element.setValid(this.resourceLabel, this.strResource, isValid); + } else { + element.setValid(isValid); + } + } + + public setElementsValidFlag(isValid: boolean, filter: (element: IUndoRedoElement) => boolean): void { + for (const element of this._past) { + if (filter(element.actual)) { + this._setElementValidFlag(element, isValid); + } + } + for (const element of this._future) { + if (filter(element.actual)) { + this._setElementValidFlag(element, isValid); + } + } + } + + public pushElement(element: StackElement): void { + // remove the future + for (const futureElement of this._future) { + if (futureElement.type === UndoRedoElementType.Workspace) { + futureElement.removeResource(this.resourceLabel, this.strResource, RemovedResourceReason.NoParallelUniverses); + } + } + this._future = []; + if (this._past.length > 0) { + const lastElement = this._past[this._past.length - 1]; + if (lastElement.type === UndoRedoElementType.Resource && !lastElement.isValid) { + // clear undo stack + this._past = []; + } + } + this._past.push(element); + this.versionId++; + } + + public createSnapshot(resource: URI): ResourceEditStackSnapshot { + const elements: number[] = []; + + for (let i = 0, len = this._past.length; i < len; i++) { + elements.push(this._past[i].id); + } + for (let i = this._future.length - 1; i >= 0; i--) { + elements.push(this._future[i].id); + } + + return new ResourceEditStackSnapshot(resource, elements); + } + + public restoreSnapshot(snapshot: ResourceEditStackSnapshot): void { + const snapshotLength = snapshot.elements.length; + let isOK = true; + let snapshotIndex = 0; + let removePastAfter = -1; + for (let i = 0, len = this._past.length; i < len; i++, snapshotIndex++) { + const element = this._past[i]; + if (isOK && (snapshotIndex >= snapshotLength || element.id !== snapshot.elements[snapshotIndex])) { + isOK = false; + removePastAfter = 0; + } + if (!isOK && element.type === UndoRedoElementType.Workspace) { + element.removeResource(this.resourceLabel, this.strResource, RemovedResourceReason.ExternalRemoval); + } + } + let removeFutureBefore = -1; + for (let i = this._future.length - 1; i >= 0; i--, snapshotIndex++) { + const element = this._future[i]; + if (isOK && (snapshotIndex >= snapshotLength || element.id !== snapshot.elements[snapshotIndex])) { + isOK = false; + removeFutureBefore = i; + } + if (!isOK && element.type === UndoRedoElementType.Workspace) { + element.removeResource(this.resourceLabel, this.strResource, RemovedResourceReason.ExternalRemoval); + } + } + if (removePastAfter !== -1) { + this._past = this._past.slice(0, removePastAfter); + } + if (removeFutureBefore !== -1) { + this._future = this._future.slice(removeFutureBefore + 1); + } + this.versionId++; + } + + public getElements(): IPastFutureElements { + const past: IUndoRedoElement[] = []; + const future: IUndoRedoElement[] = []; + + for (const element of this._past) { + past.push(element.actual); + } + for (const element of this._future) { + future.push(element.actual); + } + + return { past, future }; + } + + public getClosestPastElement(): StackElement | null { + if (this._past.length === 0) { + return null; + } + return this._past[this._past.length - 1]; + } + + public getClosestFutureElement(): StackElement | null { + if (this._future.length === 0) { + return null; + } + return this._future[this._future.length - 1]; + } + + public hasPastElements(): boolean { + return (this._past.length > 0); + } + + public hasFutureElements(): boolean { + return (this._future.length > 0); + } + + public splitPastWorkspaceElement(toRemove: WorkspaceStackElement, individualMap: Map): void { + for (let j = this._past.length - 1; j >= 0; j--) { + if (this._past[j] === toRemove) { + if (individualMap.has(this.strResource)) { + // gets replaced + this._past[j] = individualMap.get(this.strResource)!; + } else { + // gets deleted + this._past.splice(j, 1); + } + break; + } + } + this.versionId++; + } + + public splitFutureWorkspaceElement(toRemove: WorkspaceStackElement, individualMap: Map): void { + for (let j = this._future.length - 1; j >= 0; j--) { + if (this._future[j] === toRemove) { + if (individualMap.has(this.strResource)) { + // gets replaced + this._future[j] = individualMap.get(this.strResource)!; + } else { + // gets deleted + this._future.splice(j, 1); + } + break; + } + } + this.versionId++; + } + + public moveBackward(element: StackElement): void { + this._past.pop(); + this._future.push(element); + this.versionId++; + } + + public moveForward(element: StackElement): void { + this._future.pop(); + this._past.push(element); + this.versionId++; } } +class EditStackSnapshot { + + public readonly editStacks: ResourceEditStack[]; + private readonly _versionIds: number[]; + + constructor(editStacks: ResourceEditStack[]) { + this.editStacks = editStacks; + this._versionIds = []; + for (let i = 0, len = this.editStacks.length; i < len; i++) { + this._versionIds[i] = this.editStacks[i].versionId; + } + } + + public isValid(): boolean { + for (let i = 0, len = this.editStacks.length; i < len; i++) { + if (this._versionIds[i] !== this.editStacks[i].versionId) { + return false; + } + } + return true; + } +} + +const missingEditStack = new ResourceEditStack('', ''); +missingEditStack.locked = true; + export class UndoRedoService implements IUndoRedoService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _editStacks: Map; + private readonly _uriComparisonKeyComputers: [string, UriComparisonKeyComputer][]; constructor( @IDialogService private readonly _dialogService: IDialogService, @INotificationService private readonly _notificationService: INotificationService, ) { this._editStacks = new Map(); + this._uriComparisonKeyComputers = []; } - public pushElement(_element: IUndoRedoElement): void { - const element: StackElement = (_element.type === UndoRedoElementType.Resource ? new ResourceStackElement(_element) : new WorkspaceStackElement(_element)); - for (let i = 0, len = element.resources.length; i < len; i++) { - const resource = element.resources[i]; + public registerUriComparisonKeyComputer(scheme: string, uriComparisonKeyComputer: UriComparisonKeyComputer): IDisposable { + this._uriComparisonKeyComputers.push([scheme, uriComparisonKeyComputer]); + return { + dispose: () => { + for (let i = 0, len = this._uriComparisonKeyComputers.length; i < len; i++) { + if (this._uriComparisonKeyComputers[i][1] === uriComparisonKeyComputer) { + this._uriComparisonKeyComputers.splice(i, 1); + return; + } + } + } + }; + } + + public getUriComparisonKey(resource: URI): string { + for (const uriComparisonKeyComputer of this._uriComparisonKeyComputers) { + if (uriComparisonKeyComputer[0] === resource.scheme) { + return uriComparisonKeyComputer[1].getComparisonKey(resource); + } + } + return resource.toString(); + } + + private _print(label: string): void { + console.log(`------------------------------------`); + console.log(`AFTER ${label}: `); + let str: string[] = []; + for (const element of this._editStacks) { + str.push(element[1].toString()); + } + console.log(str.join('\n')); + } + + public pushElement(element: IUndoRedoElement): void { + if (element.type === UndoRedoElementType.Resource) { + const resourceLabel = getResourceLabel(element.resource); + const strResource = this.getUriComparisonKey(element.resource); + this._pushElement(new ResourceStackElement(element, resourceLabel, strResource)); + } else { + const seen = new Set(); + const resourceLabels: string[] = []; + const strResources: string[] = []; + for (const resource of element.resources) { + const resourceLabel = getResourceLabel(resource); + const strResource = this.getUriComparisonKey(resource); + + if (seen.has(strResource)) { + continue; + } + seen.add(strResource); + resourceLabels.push(resourceLabel); + strResources.push(strResource); + } + + if (resourceLabels.length === 1) { + this._pushElement(new ResourceStackElement(element, resourceLabels[0], strResources[0])); + } else { + this._pushElement(new WorkspaceStackElement(element, resourceLabels, strResources)); + } + } + if (DEBUG) { + this._print('pushElement'); + } + } + + private _pushElement(element: StackElement): void { + for (let i = 0, len = element.strResources.length; i < len; i++) { + const resourceLabel = element.resourceLabels[i]; const strResource = element.strResources[i]; let editStack: ResourceEditStack; if (this._editStacks.has(strResource)) { editStack = this._editStacks.get(strResource)!; } else { - editStack = new ResourceEditStack(resource); + editStack = new ResourceEditStack(resourceLabel, strResource); this._editStacks.set(strResource, editStack); } - // remove the future - for (const futureElement of editStack.future) { - if (futureElement.type === UndoRedoElementType.Workspace) { - futureElement.removeResource(resource, strResource, RemovedResourceReason.NoParallelUniverses); - } - } - editStack.future = []; - if (editStack.past.length > 0) { - const lastElement = editStack.past[editStack.past.length - 1]; - if (lastElement.type === UndoRedoElementType.Resource && !lastElement.isValid) { - // clear undo stack - editStack.past = []; - } - } - editStack.past.push(element); + editStack.pushElement(element); } } public getLastElement(resource: URI): IUndoRedoElement | null { - const strResource = uriGetComparisonKey(resource); + const strResource = this.getUriComparisonKey(resource); if (this._editStacks.has(strResource)) { const editStack = this._editStacks.get(strResource)!; - if (editStack.future.length > 0) { + if (editStack.hasFutureElements()) { return null; } - if (editStack.past.length === 0) { - return null; - } - return editStack.past[editStack.past.length - 1].actual; + const closestPastElement = editStack.getClosestPastElement(); + return closestPastElement ? closestPastElement.actual : null; } return null; } - private _splitPastWorkspaceElement(toRemove: WorkspaceStackElement, ignoreResources: RemovedResources | null): void { + private _splitPastWorkspaceElement(toRemove: WorkspaceStackElement & { actual: { split(): IResourceUndoRedoElement[]; } }, ignoreResources: RemovedResources | null): void { const individualArr = toRemove.actual.split(); const individualMap = new Map(); for (const _element of individualArr) { - const element = new ResourceStackElement(_element); + const resourceLabel = getResourceLabel(_element.resource); + const strResource = this.getUriComparisonKey(_element.resource); + const element = new ResourceStackElement(_element, resourceLabel, strResource); individualMap.set(element.strResource, element); } @@ -234,26 +559,17 @@ export class UndoRedoService implements IUndoRedoService { continue; } const editStack = this._editStacks.get(strResource)!; - for (let j = editStack.past.length - 1; j >= 0; j--) { - if (editStack.past[j] === toRemove) { - if (individualMap.has(strResource)) { - // gets replaced - editStack.past[j] = individualMap.get(strResource)!; - } else { - // gets deleted - editStack.past.splice(j, 1); - } - break; - } - } + editStack.splitPastWorkspaceElement(toRemove, individualMap); } } - private _splitFutureWorkspaceElement(toRemove: WorkspaceStackElement, ignoreResources: RemovedResources | null): void { + private _splitFutureWorkspaceElement(toRemove: WorkspaceStackElement & { actual: { split(): IResourceUndoRedoElement[]; } }, ignoreResources: RemovedResources | null): void { const individualArr = toRemove.actual.split(); const individualMap = new Map(); for (const _element of individualArr) { - const element = new ResourceStackElement(_element); + const resourceLabel = getResourceLabel(_element.resource); + const strResource = this.getUriComparisonKey(_element.resource); + const element = new ResourceStackElement(_element, resourceLabel, strResource); individualMap.set(element.strResource, element); } @@ -262,94 +578,82 @@ export class UndoRedoService implements IUndoRedoService { continue; } const editStack = this._editStacks.get(strResource)!; - for (let j = editStack.future.length - 1; j >= 0; j--) { - if (editStack.future[j] === toRemove) { - if (individualMap.has(strResource)) { - // gets replaced - editStack.future[j] = individualMap.get(strResource)!; - } else { - // gets deleted - editStack.future.splice(j, 1); - } - break; - } - } + editStack.splitFutureWorkspaceElement(toRemove, individualMap); } } - public removeElements(resource: URI): void { - const strResource = uriGetComparisonKey(resource); + public removeElements(resource: URI | string): void { + const strResource = typeof resource === 'string' ? resource : this.getUriComparisonKey(resource); if (this._editStacks.has(strResource)) { const editStack = this._editStacks.get(strResource)!; - for (const element of editStack.past) { - if (element.type === UndoRedoElementType.Workspace) { - element.removeResource(resource, strResource, RemovedResourceReason.ExternalRemoval); - } - } - for (const element of editStack.future) { - if (element.type === UndoRedoElementType.Workspace) { - element.removeResource(resource, strResource, RemovedResourceReason.ExternalRemoval); - } - } + editStack.dispose(); this._editStacks.delete(strResource); } - } - - public setElementsIsValid(resource: URI, isValid: boolean): void { - const strResource = uriGetComparisonKey(resource); - if (this._editStacks.has(strResource)) { - const editStack = this._editStacks.get(strResource)!; - for (const element of editStack.past) { - if (element.type === UndoRedoElementType.Workspace) { - element.setValid(resource, strResource, isValid); - } else { - element.setValid(isValid); - } - } - for (const element of editStack.future) { - if (element.type === UndoRedoElementType.Workspace) { - element.setValid(resource, strResource, isValid); - } else { - element.setValid(isValid); - } - } + if (DEBUG) { + this._print('removeElements'); } } - // resource - - public hasElements(resource: URI): boolean { - const strResource = uriGetComparisonKey(resource); + public setElementsValidFlag(resource: URI, isValid: boolean, filter: (element: IUndoRedoElement) => boolean): void { + const strResource = this.getUriComparisonKey(resource); if (this._editStacks.has(strResource)) { const editStack = this._editStacks.get(strResource)!; - return (editStack.past.length > 0 || editStack.future.length > 0); + editStack.setElementsValidFlag(isValid, filter); + } + if (DEBUG) { + this._print('setElementsValidFlag'); + } + } + + public hasElements(resource: URI): boolean { + const strResource = this.getUriComparisonKey(resource); + if (this._editStacks.has(strResource)) { + const editStack = this._editStacks.get(strResource)!; + return (editStack.hasPastElements() || editStack.hasFutureElements()); } return false; } - public getElements(resource: URI): IPastFutureElements { - const past: IUndoRedoElement[] = []; - const future: IUndoRedoElement[] = []; - - const strResource = uriGetComparisonKey(resource); + public createSnapshot(resource: URI): ResourceEditStackSnapshot { + const strResource = this.getUriComparisonKey(resource); if (this._editStacks.has(strResource)) { const editStack = this._editStacks.get(strResource)!; - for (const element of editStack.past) { - past.push(element.actual); - } - for (const element of editStack.future) { - future.push(element.actual); + return editStack.createSnapshot(resource); + } + return new ResourceEditStackSnapshot(resource, []); + } + + public restoreSnapshot(snapshot: ResourceEditStackSnapshot): void { + const strResource = this.getUriComparisonKey(snapshot.resource); + if (this._editStacks.has(strResource)) { + const editStack = this._editStacks.get(strResource)!; + editStack.restoreSnapshot(snapshot); + + if (!editStack.hasPastElements() && !editStack.hasFutureElements()) { + // the edit stack is now empty, just remove it entirely + editStack.dispose(); + this._editStacks.delete(strResource); } } + if (DEBUG) { + this._print('restoreSnapshot'); + } + } - return { past, future }; + public getElements(resource: URI): IPastFutureElements { + const strResource = this.getUriComparisonKey(resource); + if (this._editStacks.has(strResource)) { + const editStack = this._editStacks.get(strResource)!; + return editStack.getElements(); + } + return { past: [], future: [] }; } public canUndo(resource: URI): boolean { - const strResource = uriGetComparisonKey(resource); + const strResource = this.getUriComparisonKey(resource); if (this._editStacks.has(strResource)) { const editStack = this._editStacks.get(strResource)!; - return (editStack.past.length > 0); + return editStack.hasPastElements(); } return false; } @@ -357,200 +661,494 @@ export class UndoRedoService implements IUndoRedoService { private _onError(err: Error, element: StackElement): void { onUnexpectedError(err); // An error occured while undoing or redoing => drop the undo/redo stack for all affected resources - for (const resource of element.resources) { - this.removeElements(resource); + for (const strResource of element.strResources) { + this.removeElements(strResource); } this._notificationService.error(err); } - private _safeInvoke(element: StackElement, invoke: () => Promise | void): Promise | void { + private _acquireLocks(editStackSnapshot: EditStackSnapshot): () => void { + // first, check if all locks can be acquired + for (const editStack of editStackSnapshot.editStacks) { + if (editStack.locked) { + throw new Error('Cannot acquire edit stack lock'); + } + } + + // can acquire all locks + for (const editStack of editStackSnapshot.editStacks) { + editStack.locked = true; + } + + return () => { + // release all locks + for (const editStack of editStackSnapshot.editStacks) { + editStack.locked = false; + } + }; + } + + private _safeInvokeWithLocks(element: StackElement, invoke: () => Promise | void, editStackSnapshot: EditStackSnapshot, cleanup: IDisposable = Disposable.None): Promise | void { + const releaseLocks = this._acquireLocks(editStackSnapshot); + let result: Promise | void; try { result = invoke(); } catch (err) { + releaseLocks(); + cleanup.dispose(); return this._onError(err, element); } if (result) { - return result.then(undefined, (err) => this._onError(err, element)); + // result is Promise + return result.then( + () => { + releaseLocks(); + cleanup.dispose(); + }, + (err) => { + releaseLocks(); + cleanup.dispose(); + return this._onError(err, element); + } + ); + } else { + // result is void + releaseLocks(); + cleanup.dispose(); } } - private _workspaceUndo(resource: URI, element: WorkspaceStackElement): Promise | void { - if (element.removedResources) { - this._splitPastWorkspaceElement(element, element.removedResources); - const message = nls.localize('cannotWorkspaceUndo', "Could not undo '{0}' across all files. {1}", element.label, element.removedResources.createMessage()); - this._notificationService.info(message); - return this.undo(resource); + private async _invokeWorkspacePrepare(element: WorkspaceStackElement): Promise { + if (typeof element.actual.prepareUndoRedo === 'undefined') { + return Disposable.None; } - if (element.invalidatedResources) { - this._splitPastWorkspaceElement(element, element.invalidatedResources); - const message = nls.localize('cannotWorkspaceUndo', "Could not undo '{0}' across all files. {1}", element.label, element.invalidatedResources.createMessage()); + const result = element.actual.prepareUndoRedo(); + if (typeof result === 'undefined') { + return Disposable.None; + } + return result; + } + + private _invokeResourcePrepare(element: ResourceStackElement, callback: (disposable: IDisposable) => void): void | Promise { + if (element.actual.type !== UndoRedoElementType.Workspace || typeof element.actual.prepareUndoRedo === 'undefined') { + // no preparation needed + callback(Disposable.None); + return; + } + + const r = element.actual.prepareUndoRedo(); + if (!r) { + // nothing to clean up + callback(Disposable.None); + return; + } + + if (isDisposable(r)) { + callback(r); + return; + } + + return r.then((disposable) => { + callback(disposable); + }); + } + + private _getAffectedEditStacks(element: WorkspaceStackElement): EditStackSnapshot { + const affectedEditStacks: ResourceEditStack[] = []; + for (const strResource of element.strResources) { + affectedEditStacks.push(this._editStacks.get(strResource) || missingEditStack); + } + return new EditStackSnapshot(affectedEditStacks); + } + + private _tryToSplitAndUndo(strResource: string, element: WorkspaceStackElement, ignoreResources: RemovedResources | null, message: string): WorkspaceVerificationError { + if (element.canSplit()) { + this._splitPastWorkspaceElement(element, ignoreResources); this._notificationService.info(message); - return this.undo(resource); + return new WorkspaceVerificationError(this.undo(strResource)); + } else { + // Cannot safely split this workspace element => flush all undo/redo stacks + for (const strResource of element.strResources) { + this.removeElements(strResource); + } + this._notificationService.info(message); + return new WorkspaceVerificationError(); + } + } + + private _checkWorkspaceUndo(strResource: string, element: WorkspaceStackElement, editStackSnapshot: EditStackSnapshot, checkInvalidatedResources: boolean): WorkspaceVerificationError | null { + if (element.removedResources) { + return this._tryToSplitAndUndo( + strResource, + element, + element.removedResources, + nls.localize( + { key: 'cannotWorkspaceUndo', comment: ['{0} is a label for an operation. {1} is another message.'] }, + "Could not undo '{0}' across all files. {1}", element.label, element.removedResources.createMessage() + ) + ); + } + if (checkInvalidatedResources && element.invalidatedResources) { + return this._tryToSplitAndUndo( + strResource, + element, + element.invalidatedResources, + nls.localize( + { key: 'cannotWorkspaceUndo', comment: ['{0} is a label for an operation. {1} is another message.'] }, + "Could not undo '{0}' across all files. {1}", element.label, element.invalidatedResources.createMessage() + ) + ); } // this must be the last past element in all the impacted resources! - let affectedEditStacks: ResourceEditStack[] = []; - for (const strResource of element.strResources) { - affectedEditStacks.push(this._editStacks.get(strResource)!); - } - - let cannotUndoDueToResources: URI[] = []; - for (const editStack of affectedEditStacks) { - if (editStack.past.length === 0 || editStack.past[editStack.past.length - 1] !== element) { - cannotUndoDueToResources.push(editStack.resource); + const cannotUndoDueToResources: string[] = []; + for (const editStack of editStackSnapshot.editStacks) { + if (editStack.getClosestPastElement() !== element) { + cannotUndoDueToResources.push(editStack.resourceLabel); } } - if (cannotUndoDueToResources.length > 0) { - this._splitPastWorkspaceElement(element, null); - const paths = cannotUndoDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path); - const message = nls.localize('cannotWorkspaceUndoDueToChanges', "Could not undo '{0}' across all files because changes were made to {1}", element.label, paths.join(', ')); - this._notificationService.info(message); - return this.undo(resource); + return this._tryToSplitAndUndo( + strResource, + element, + null, + nls.localize( + { key: 'cannotWorkspaceUndoDueToChanges', comment: ['{0} is a label for an operation. {1} is a list of filenames.'] }, + "Could not undo '{0}' across all files because changes were made to {1}", element.label, cannotUndoDueToResources.join(', ') + ) + ); } - return this._dialogService.show( - Severity.Info, - nls.localize('confirmWorkspace', "Would you like to undo '{0}' across all files?", element.label), - [ - nls.localize('ok', "Undo in {0} Files", affectedEditStacks.length), - nls.localize('nok', "Undo this File"), - nls.localize('cancel', "Cancel"), - ], - { - cancelId: 2 + const cannotLockDueToResources: string[] = []; + for (const editStack of editStackSnapshot.editStacks) { + if (editStack.locked) { + cannotLockDueToResources.push(editStack.resourceLabel); } - ).then((result) => { - if (result.choice === 2) { - // cancel - return; - } else if (result.choice === 0) { - for (const editStack of affectedEditStacks) { - editStack.past.pop(); - editStack.future.push(element); + } + if (cannotLockDueToResources.length > 0) { + return this._tryToSplitAndUndo( + strResource, + element, + null, + nls.localize( + { key: 'cannotWorkspaceUndoDueToInProgressUndoRedo', comment: ['{0} is a label for an operation. {1} is a list of filenames.'] }, + "Could not undo '{0}' across all files because there is already an undo or redo operation running on {1}", element.label, cannotLockDueToResources.join(', ') + ) + ); + } + + // check if new stack elements were added in the meantime... + if (!editStackSnapshot.isValid()) { + return this._tryToSplitAndUndo( + strResource, + element, + null, + nls.localize( + { key: 'cannotWorkspaceUndoDueToInMeantimeUndoRedo', comment: ['{0} is a label for an operation. {1} is a list of filenames.'] }, + "Could not undo '{0}' across all files because an undo or redo operation occurred in the meantime", element.label + ) + ); + } + + return null; + } + + private _workspaceUndo(strResource: string, element: WorkspaceStackElement): Promise | void { + const affectedEditStacks = this._getAffectedEditStacks(element); + const verificationError = this._checkWorkspaceUndo(strResource, element, affectedEditStacks, /*invalidated resources will be checked after the prepare call*/false); + if (verificationError) { + return verificationError.returnValue; + } + return this._confirmAndExecuteWorkspaceUndo(strResource, element, affectedEditStacks); + } + + private async _confirmAndExecuteWorkspaceUndo(strResource: string, element: WorkspaceStackElement, editStackSnapshot: EditStackSnapshot): Promise { + + if (element.canSplit()) { + // this element can be split + + const result = await this._dialogService.show( + Severity.Info, + nls.localize('confirmWorkspace', "Would you like to undo '{0}' across all files?", element.label), + [ + nls.localize({ key: 'ok', comment: ['{0} denotes a number that is > 1'] }, "Undo in {0} Files", editStackSnapshot.editStacks.length), + nls.localize('nok', "Undo this File"), + nls.localize('cancel', "Cancel"), + ], + { + cancelId: 2 } - return this._safeInvoke(element, () => element.actual.undo()); - } else { - this._splitPastWorkspaceElement(element, null); - return this.undo(resource); + ); + + if (result.choice === 2) { + // choice: cancel + return; } - }); + + if (result.choice === 1) { + // choice: undo this file + this._splitPastWorkspaceElement(element, null); + return this.undo(strResource); + } + + // choice: undo in all files + + // At this point, it is possible that the element has been made invalid in the meantime (due to the confirmation await) + const verificationError1 = this._checkWorkspaceUndo(strResource, element, editStackSnapshot, /*invalidated resources will be checked after the prepare call*/false); + if (verificationError1) { + return verificationError1.returnValue; + } + } + + // prepare + let cleanup: IDisposable; + try { + cleanup = await this._invokeWorkspacePrepare(element); + } catch (err) { + return this._onError(err, element); + } + + // At this point, it is possible that the element has been made invalid in the meantime (due to the prepare await) + const verificationError2 = this._checkWorkspaceUndo(strResource, element, editStackSnapshot, /*now also check that there are no more invalidated resources*/true); + if (verificationError2) { + cleanup.dispose(); + return verificationError2.returnValue; + } + + for (const editStack of editStackSnapshot.editStacks) { + editStack.moveBackward(element); + } + return this._safeInvokeWithLocks(element, () => element.actual.undo(), editStackSnapshot, cleanup); } private _resourceUndo(editStack: ResourceEditStack, element: ResourceStackElement): Promise | void { if (!element.isValid) { // invalid element => immediately flush edit stack! - editStack.past = []; - editStack.future = []; + editStack.flushAllElements(); return; } - editStack.past.pop(); - editStack.future.push(element); - return this._safeInvoke(element, () => element.actual.undo()); + if (editStack.locked) { + const message = nls.localize( + { key: 'cannotResourceUndoDueToInProgressUndoRedo', comment: ['{0} is a label for an operation.'] }, + "Could not undo '{0}' because there is already an undo or redo operation running.", element.label + ); + this._notificationService.info(message); + return; + } + return this._invokeResourcePrepare(element, (cleanup) => { + editStack.moveBackward(element); + return this._safeInvokeWithLocks(element, () => element.actual.undo(), new EditStackSnapshot([editStack]), cleanup); + }); } - public undo(resource: URI): Promise | void { - const strResource = uriGetComparisonKey(resource); + public undo(resource: URI | string): Promise | void { + const strResource = typeof resource === 'string' ? resource : this.getUriComparisonKey(resource); if (!this._editStacks.has(strResource)) { return; } const editStack = this._editStacks.get(strResource)!; - if (editStack.past.length === 0) { + const element = editStack.getClosestPastElement(); + if (!element) { return; } - const element = editStack.past[editStack.past.length - 1]; - if (element.type === UndoRedoElementType.Workspace) { - return this._workspaceUndo(resource, element); - } else { - return this._resourceUndo(editStack, element); + try { + if (element.type === UndoRedoElementType.Workspace) { + return this._workspaceUndo(strResource, element); + } else { + return this._resourceUndo(editStack, element); + } + } finally { + if (DEBUG) { + this._print('undo'); + } } } public canRedo(resource: URI): boolean { - const strResource = uriGetComparisonKey(resource); + const strResource = this.getUriComparisonKey(resource); if (this._editStacks.has(strResource)) { const editStack = this._editStacks.get(strResource)!; - return (editStack.future.length > 0); + return editStack.hasFutureElements(); } return false; } - private _workspaceRedo(resource: URI, element: WorkspaceStackElement): Promise | void { - if (element.removedResources) { - this._splitFutureWorkspaceElement(element, element.removedResources); - const message = nls.localize('cannotWorkspaceRedo', "Could not redo '{0}' across all files. {1}", element.label, element.removedResources.createMessage()); + private _tryToSplitAndRedo(strResource: string, element: WorkspaceStackElement, ignoreResources: RemovedResources | null, message: string): WorkspaceVerificationError { + if (element.canSplit()) { + this._splitFutureWorkspaceElement(element, ignoreResources); this._notificationService.info(message); - return this.redo(resource); + return new WorkspaceVerificationError(this.redo(strResource)); + } else { + // Cannot safely split this workspace element => flush all undo/redo stacks + for (const strResource of element.strResources) { + this.removeElements(strResource); + } + this._notificationService.info(message); + return new WorkspaceVerificationError(); } - if (element.invalidatedResources) { - this._splitFutureWorkspaceElement(element, element.invalidatedResources); - const message = nls.localize('cannotWorkspaceRedo', "Could not redo '{0}' across all files. {1}", element.label, element.invalidatedResources.createMessage()); - this._notificationService.info(message); - return this.redo(resource); + } + + private _checkWorkspaceRedo(strResource: string, element: WorkspaceStackElement, editStackSnapshot: EditStackSnapshot, checkInvalidatedResources: boolean): WorkspaceVerificationError | null { + if (element.removedResources) { + return this._tryToSplitAndRedo( + strResource, + element, + element.removedResources, + nls.localize( + { key: 'cannotWorkspaceRedo', comment: ['{0} is a label for an operation. {1} is another message.'] }, + "Could not redo '{0}' across all files. {1}", element.label, element.removedResources.createMessage() + ) + ); + } + if (checkInvalidatedResources && element.invalidatedResources) { + return this._tryToSplitAndRedo( + strResource, + element, + element.invalidatedResources, + nls.localize( + { key: 'cannotWorkspaceRedo', comment: ['{0} is a label for an operation. {1} is another message.'] }, + "Could not redo '{0}' across all files. {1}", element.label, element.invalidatedResources.createMessage() + ) + ); } // this must be the last future element in all the impacted resources! - let affectedEditStacks: ResourceEditStack[] = []; - for (const strResource of element.strResources) { - affectedEditStacks.push(this._editStacks.get(strResource)!); - } - - let cannotRedoDueToResources: URI[] = []; - for (const editStack of affectedEditStacks) { - if (editStack.future.length === 0 || editStack.future[editStack.future.length - 1] !== element) { - cannotRedoDueToResources.push(editStack.resource); + const cannotRedoDueToResources: string[] = []; + for (const editStack of editStackSnapshot.editStacks) { + if (editStack.getClosestFutureElement() !== element) { + cannotRedoDueToResources.push(editStack.resourceLabel); } } - if (cannotRedoDueToResources.length > 0) { - this._splitFutureWorkspaceElement(element, null); - const paths = cannotRedoDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path); - const message = nls.localize('cannotWorkspaceRedoDueToChanges', "Could not redo '{0}' across all files because changes were made to {1}", element.label, paths.join(', ')); - this._notificationService.info(message); - return this.redo(resource); + return this._tryToSplitAndRedo( + strResource, + element, + null, + nls.localize( + { key: 'cannotWorkspaceRedoDueToChanges', comment: ['{0} is a label for an operation. {1} is a list of filenames.'] }, + "Could not redo '{0}' across all files because changes were made to {1}", element.label, cannotRedoDueToResources.join(', ') + ) + ); } - for (const editStack of affectedEditStacks) { - editStack.future.pop(); - editStack.past.push(element); + const cannotLockDueToResources: string[] = []; + for (const editStack of editStackSnapshot.editStacks) { + if (editStack.locked) { + cannotLockDueToResources.push(editStack.resourceLabel); + } } - return this._safeInvoke(element, () => element.actual.redo()); + if (cannotLockDueToResources.length > 0) { + return this._tryToSplitAndRedo( + strResource, + element, + null, + nls.localize( + { key: 'cannotWorkspaceRedoDueToInProgressUndoRedo', comment: ['{0} is a label for an operation. {1} is a list of filenames.'] }, + "Could not redo '{0}' across all files because there is already an undo or redo operation running on {1}", element.label, cannotLockDueToResources.join(', ') + ) + ); + } + + // check if new stack elements were added in the meantime... + if (!editStackSnapshot.isValid()) { + return this._tryToSplitAndRedo( + strResource, + element, + null, + nls.localize( + { key: 'cannotWorkspaceRedoDueToInMeantimeUndoRedo', comment: ['{0} is a label for an operation. {1} is a list of filenames.'] }, + "Could not redo '{0}' across all files because an undo or redo operation occurred in the meantime", element.label + ) + ); + } + + return null; + } + + private _workspaceRedo(strResource: string, element: WorkspaceStackElement): Promise | void { + const affectedEditStacks = this._getAffectedEditStacks(element); + const verificationError = this._checkWorkspaceRedo(strResource, element, affectedEditStacks, /*invalidated resources will be checked after the prepare call*/false); + if (verificationError) { + return verificationError.returnValue; + } + return this._executeWorkspaceRedo(strResource, element, affectedEditStacks); + } + + private async _executeWorkspaceRedo(strResource: string, element: WorkspaceStackElement, editStackSnapshot: EditStackSnapshot): Promise { + // prepare + let cleanup: IDisposable; + try { + cleanup = await this._invokeWorkspacePrepare(element); + } catch (err) { + return this._onError(err, element); + } + + // At this point, it is possible that the element has been made invalid in the meantime (due to the prepare await) + const verificationError = this._checkWorkspaceRedo(strResource, element, editStackSnapshot, /*now also check that there are no more invalidated resources*/true); + if (verificationError) { + cleanup.dispose(); + return verificationError.returnValue; + } + + for (const editStack of editStackSnapshot.editStacks) { + editStack.moveForward(element); + } + return this._safeInvokeWithLocks(element, () => element.actual.redo(), editStackSnapshot, cleanup); } private _resourceRedo(editStack: ResourceEditStack, element: ResourceStackElement): Promise | void { if (!element.isValid) { // invalid element => immediately flush edit stack! - editStack.past = []; - editStack.future = []; + editStack.flushAllElements(); return; } - editStack.future.pop(); - editStack.past.push(element); - return this._safeInvoke(element, () => element.actual.redo()); + if (editStack.locked) { + const message = nls.localize( + { key: 'cannotResourceRedoDueToInProgressUndoRedo', comment: ['{0} is a label for an operation.'] }, + "Could not redo '{0}' because there is already an undo or redo operation running.", element.label + ); + this._notificationService.info(message); + return; + } + + return this._invokeResourcePrepare(element, (cleanup) => { + editStack.moveForward(element); + return this._safeInvokeWithLocks(element, () => element.actual.redo(), new EditStackSnapshot([editStack]), cleanup); + }); } - public redo(resource: URI): Promise | void { - const strResource = uriGetComparisonKey(resource); + public redo(resource: URI | string): Promise | void { + const strResource = typeof resource === 'string' ? resource : this.getUriComparisonKey(resource); if (!this._editStacks.has(strResource)) { return; } const editStack = this._editStacks.get(strResource)!; - if (editStack.future.length === 0) { + const element = editStack.getClosestFutureElement(); + if (!element) { return; } - const element = editStack.future[editStack.future.length - 1]; - if (element.type === UndoRedoElementType.Workspace) { - return this._workspaceRedo(resource, element); - } else { - return this._resourceRedo(editStack, element); + try { + if (element.type === UndoRedoElementType.Workspace) { + return this._workspaceRedo(strResource, element); + } else { + return this._resourceRedo(editStack, element); + } + } finally { + if (DEBUG) { + this._print('redo'); + } } } } +class WorkspaceVerificationError { + constructor(public readonly returnValue: Promise | void) { } +} + registerSingleton(IUndoRedoService, UndoRedoService); diff --git a/src/vs/platform/undoRedo/test/common/undoRedoService.test.ts b/src/vs/platform/undoRedo/test/common/undoRedoService.test.ts new file mode 100644 index 00000000000..86714f8d471 --- /dev/null +++ b/src/vs/platform/undoRedo/test/common/undoRedoService.test.ts @@ -0,0 +1,211 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { 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'; +import { UndoRedoElementType, IUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo'; +import { URI } from 'vs/base/common/uri'; +import { mock } from 'vs/base/test/common/mock'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; + +suite('UndoRedoService', () => { + + function createUndoRedoService(dialogService: IDialogService = new TestDialogService()): UndoRedoService { + const notificationService = new TestNotificationService(); + return new UndoRedoService(dialogService, notificationService); + } + + test('simple single resource elements', () => { + const resource = URI.file('test.txt'); + const service = createUndoRedoService(); + + assert.equal(service.canUndo(resource), false); + assert.equal(service.canRedo(resource), false); + assert.equal(service.hasElements(resource), false); + assert.ok(service.getLastElement(resource) === null); + + let undoCall1 = 0; + let redoCall1 = 0; + const element1: IUndoRedoElement = { + type: UndoRedoElementType.Resource, + resource: resource, + label: 'typing 1', + undo: () => { undoCall1++; }, + redo: () => { redoCall1++; } + }; + service.pushElement(element1); + + assert.equal(undoCall1, 0); + assert.equal(redoCall1, 0); + assert.equal(service.canUndo(resource), true); + assert.equal(service.canRedo(resource), false); + assert.equal(service.hasElements(resource), true); + assert.ok(service.getLastElement(resource) === element1); + + service.undo(resource); + assert.equal(undoCall1, 1); + assert.equal(redoCall1, 0); + assert.equal(service.canUndo(resource), false); + assert.equal(service.canRedo(resource), true); + assert.equal(service.hasElements(resource), true); + assert.ok(service.getLastElement(resource) === null); + + service.redo(resource); + assert.equal(undoCall1, 1); + assert.equal(redoCall1, 1); + assert.equal(service.canUndo(resource), true); + assert.equal(service.canRedo(resource), false); + assert.equal(service.hasElements(resource), true); + assert.ok(service.getLastElement(resource) === element1); + + let undoCall2 = 0; + let redoCall2 = 0; + const element2: IUndoRedoElement = { + type: UndoRedoElementType.Resource, + resource: resource, + label: 'typing 2', + undo: () => { undoCall2++; }, + redo: () => { redoCall2++; } + }; + service.pushElement(element2); + + assert.equal(undoCall1, 1); + assert.equal(redoCall1, 1); + assert.equal(undoCall2, 0); + assert.equal(redoCall2, 0); + assert.equal(service.canUndo(resource), true); + assert.equal(service.canRedo(resource), false); + assert.equal(service.hasElements(resource), true); + assert.ok(service.getLastElement(resource) === element2); + + service.undo(resource); + + assert.equal(undoCall1, 1); + assert.equal(redoCall1, 1); + assert.equal(undoCall2, 1); + assert.equal(redoCall2, 0); + assert.equal(service.canUndo(resource), true); + assert.equal(service.canRedo(resource), true); + assert.equal(service.hasElements(resource), true); + assert.ok(service.getLastElement(resource) === null); + + let undoCall3 = 0; + let redoCall3 = 0; + const element3: IUndoRedoElement = { + type: UndoRedoElementType.Resource, + resource: resource, + label: 'typing 2', + undo: () => { undoCall3++; }, + redo: () => { redoCall3++; } + }; + service.pushElement(element3); + + assert.equal(undoCall1, 1); + assert.equal(redoCall1, 1); + assert.equal(undoCall2, 1); + assert.equal(redoCall2, 0); + assert.equal(undoCall3, 0); + assert.equal(redoCall3, 0); + assert.equal(service.canUndo(resource), true); + assert.equal(service.canRedo(resource), false); + assert.equal(service.hasElements(resource), true); + assert.ok(service.getLastElement(resource) === element3); + + service.undo(resource); + + assert.equal(undoCall1, 1); + assert.equal(redoCall1, 1); + assert.equal(undoCall2, 1); + assert.equal(redoCall2, 0); + assert.equal(undoCall3, 1); + assert.equal(redoCall3, 0); + assert.equal(service.canUndo(resource), true); + assert.equal(service.canRedo(resource), true); + assert.equal(service.hasElements(resource), true); + assert.ok(service.getLastElement(resource) === null); + }); + + test('multi resource elements', async () => { + const resource1 = URI.file('test1.txt'); + const resource2 = URI.file('test2.txt'); + const service = createUndoRedoService(new class extends mock() { + async show() { + return { + choice: 0 // confirm! + }; + } + }); + + let undoCall1 = 0, undoCall11 = 0, undoCall12 = 0; + let redoCall1 = 0, redoCall11 = 0, redoCall12 = 0; + const element1: IUndoRedoElement = { + type: UndoRedoElementType.Workspace, + resources: [resource1, resource2], + label: 'typing 1', + undo: () => { undoCall1++; }, + redo: () => { redoCall1++; }, + split: () => { + return [ + { + type: UndoRedoElementType.Resource, + resource: resource1, + label: 'typing 1.1', + undo: () => { undoCall11++; }, + redo: () => { redoCall11++; } + }, + { + type: UndoRedoElementType.Resource, + resource: resource2, + label: 'typing 1.2', + undo: () => { undoCall12++; }, + redo: () => { redoCall12++; } + } + ]; + } + }; + service.pushElement(element1); + + assert.equal(service.canUndo(resource1), true); + assert.equal(service.canRedo(resource1), false); + assert.equal(service.hasElements(resource1), true); + assert.ok(service.getLastElement(resource1) === element1); + assert.equal(service.canUndo(resource2), true); + assert.equal(service.canRedo(resource2), false); + assert.equal(service.hasElements(resource2), true); + assert.ok(service.getLastElement(resource2) === element1); + + await service.undo(resource1); + + assert.equal(undoCall1, 1); + assert.equal(redoCall1, 0); + assert.equal(service.canUndo(resource1), false); + assert.equal(service.canRedo(resource1), true); + assert.equal(service.hasElements(resource1), true); + assert.ok(service.getLastElement(resource1) === null); + assert.equal(service.canUndo(resource2), false); + assert.equal(service.canRedo(resource2), true); + assert.equal(service.hasElements(resource2), true); + assert.ok(service.getLastElement(resource2) === null); + + await service.redo(resource2); + assert.equal(undoCall1, 1); + assert.equal(redoCall1, 1); + assert.equal(undoCall11, 0); + assert.equal(redoCall11, 0); + assert.equal(undoCall12, 0); + assert.equal(redoCall12, 0); + assert.equal(service.canUndo(resource1), true); + assert.equal(service.canRedo(resource1), false); + assert.equal(service.hasElements(resource1), true); + assert.ok(service.getLastElement(resource1) === element1); + assert.equal(service.canUndo(resource2), true); + assert.equal(service.canRedo(resource2), false); + assert.equal(service.hasElements(resource2), true); + assert.ok(service.getLastElement(resource2) === element1); + + }); +}); diff --git a/src/vs/platform/update/common/update.ts b/src/vs/platform/update/common/update.ts index d05b111a89f..13e322a9115 100644 --- a/src/vs/platform/update/common/update.ts +++ b/src/vs/platform/update/common/update.ts @@ -81,7 +81,7 @@ export interface IAutoUpdater extends Event.NodeEventEmitter { export const IUpdateService = createDecorator('updateService'); export interface IUpdateService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly onStateChange: Event; readonly state: State; diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.ts index 65d1f28c961..dd67aaf2070 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts @@ -25,7 +25,7 @@ export type UpdateNotAvailableClassification = { export abstract class AbstractUpdateService implements IUpdateService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; protected url: string | undefined; diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts index f8fae801ca9..ff83e9c23a1 100644 --- a/src/vs/platform/update/electron-main/updateService.darwin.ts +++ b/src/vs/platform/update/electron-main/updateService.darwin.ts @@ -19,7 +19,7 @@ import { IRequestService } from 'vs/platform/request/common/request'; export class DarwinUpdateService extends AbstractUpdateService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly disposables = new DisposableStore(); diff --git a/src/vs/platform/update/electron-main/updateService.linux.ts b/src/vs/platform/update/electron-main/updateService.linux.ts index 4b34dafeab8..243b64b686c 100644 --- a/src/vs/platform/update/electron-main/updateService.linux.ts +++ b/src/vs/platform/update/electron-main/updateService.linux.ts @@ -18,7 +18,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; export class LinuxUpdateService extends AbstractUpdateService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @ILifecycleMainService lifecycleMainService: ILifecycleMainService, diff --git a/src/vs/platform/update/electron-main/updateService.snap.ts b/src/vs/platform/update/electron-main/updateService.snap.ts index a5720f4f045..9e36e24aefa 100644 --- a/src/vs/platform/update/electron-main/updateService.snap.ts +++ b/src/vs/platform/update/electron-main/updateService.snap.ts @@ -18,7 +18,7 @@ import { UpdateNotAvailableClassification } from 'vs/platform/update/electron-ma abstract class AbstractUpdateService2 implements IUpdateService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _state: State = State.Uninitialized; @@ -135,7 +135,7 @@ abstract class AbstractUpdateService2 implements IUpdateService { export class SnapUpdateService extends AbstractUpdateService2 { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( private snap: string, diff --git a/src/vs/platform/update/electron-main/updateService.win32.ts b/src/vs/platform/update/electron-main/updateService.win32.ts index d8ff2bb11e5..5ff2be09f91 100644 --- a/src/vs/platform/update/electron-main/updateService.win32.ts +++ b/src/vs/platform/update/electron-main/updateService.win32.ts @@ -50,7 +50,7 @@ function getUpdateType(): UpdateType { export class Win32UpdateService extends AbstractUpdateService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private availableUpdate: IAvailableUpdate | undefined; @@ -93,8 +93,8 @@ export class Win32UpdateService extends AbstractUpdateService { protected buildUpdateFeedUrl(quality: string): string | undefined { let platform = 'win32'; - if (process.arch === 'x64') { - platform += '-x64'; + if (process.arch !== 'ia32') { + platform += `-${process.arch}`; } if (getUpdateType() === UpdateType.Archive) { diff --git a/src/vs/platform/url/common/url.ts b/src/vs/platform/url/common/url.ts index 097a2398e9b..2069fc48e51 100644 --- a/src/vs/platform/url/common/url.ts +++ b/src/vs/platform/url/common/url.ts @@ -26,7 +26,7 @@ export interface IURLHandler { export interface IURLService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Create a URL that can be called to trigger IURLhandlers. diff --git a/src/vs/platform/url/common/urlService.ts b/src/vs/platform/url/common/urlService.ts index 030d52b587e..0c1e354c073 100644 --- a/src/vs/platform/url/common/urlService.ts +++ b/src/vs/platform/url/common/urlService.ts @@ -5,20 +5,20 @@ import { IURLService, IURLHandler, IOpenURLOptions } from 'vs/platform/url/common/url'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { values } from 'vs/base/common/map'; import { first } from 'vs/base/common/async'; import { toDisposable, IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import product from 'vs/platform/product/common/product'; export abstract class AbstractURLService extends Disposable implements IURLService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private handlers = new Set(); abstract create(options?: Partial): URI; open(uri: URI, options?: IOpenURLOptions): Promise { - const handlers = values(this.handlers); + const handlers = [...this.handlers.values()]; return first(handlers.map(h => () => h.handleURL(uri, options)), undefined, false).then(val => val || false); } @@ -27,3 +27,16 @@ export abstract class AbstractURLService extends Disposable implements IURLServi return toDisposable(() => this.handlers.delete(handler)); } } + +export class NativeURLService extends AbstractURLService { + + create(options?: Partial): URI { + let { authority, path, query, fragment } = options ? options : { authority: undefined, path: undefined, query: undefined, fragment: undefined }; + + if (authority && path && path.indexOf('/') !== 0) { + path = `/${path}`; // URI validation requires a path if there is an authority + } + + return URI.from({ scheme: product.urlProtocol, authority, path, query, fragment }); + } +} diff --git a/src/vs/platform/url/node/urlService.ts b/src/vs/platform/url/node/urlService.ts deleted file mode 100644 index a0c9249dd7d..00000000000 --- a/src/vs/platform/url/node/urlService.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 { URI, UriComponents } from 'vs/base/common/uri'; -import product from 'vs/platform/product/common/product'; -import { AbstractURLService } from 'vs/platform/url/common/urlService'; - -export class URLService extends AbstractURLService { - - create(options?: Partial): URI { - let { authority, path, query, fragment } = options ? options : { authority: undefined, path: undefined, query: undefined, fragment: undefined }; - - if (authority && path && path.indexOf('/') !== 0) { - path = `/${path}`; // URI validation requires a path if there is an authority - } - - return URI.from({ scheme: product.urlProtocol, authority, path, query, fragment }); - } -} diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index e52d759f8e4..2657d958fd1 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -7,10 +7,14 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IFileService, IFileContent, FileChangesEvent, FileOperationResult, FileOperationError } from 'vs/platform/files/common/files'; import { VSBuffer } from 'vs/base/common/buffer'; import { URI } from 'vs/base/common/uri'; -import { SyncResource, SyncStatus, IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, UserDataSyncError, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, Conflict, ISyncResourceHandle, USER_DATA_SYNC_SCHEME, ISyncPreviewResult } from 'vs/platform/userDataSync/common/userDataSync'; +import { + SyncResource, SyncStatus, IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, UserDataSyncError, IUserDataSyncLogService, IUserDataSyncUtilService, + IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, ISyncResourceHandle, USER_DATA_SYNC_SCHEME, ISyncResourcePreview as IBaseSyncResourcePreview, + IUserDataManifest, ISyncData, IRemoteUserData, PREVIEW_DIR_NAME, IResourcePreview as IBaseResourcePreview, Change, MergeState +} from 'vs/platform/userDataSync/common/userDataSync'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { joinPath, dirname, isEqual, basename } from 'vs/base/common/resources'; -import { CancelablePromise } from 'vs/base/common/async'; +import { CancelablePromise, RunOnceScheduler, createCancelablePromise } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ParseError, parse } from 'vs/base/common/json'; @@ -21,70 +25,151 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { isString } from 'vs/base/common/types'; import { uppercaseFirstLetter } from 'vs/base/common/strings'; import { equals } from 'vs/base/common/arrays'; +import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IHeaders } from 'vs/base/parts/request/common/request'; type SyncSourceClassification = { source?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; }; -export interface IRemoteUserData { - ref: string; - syncData: ISyncData | null; -} - -export interface ISyncData { - version: number; - content: string; -} - function isSyncData(thing: any): thing is ISyncData { - return thing - && (thing.version && typeof thing.version === 'number') - && (thing.content && typeof thing.content === 'string') - && Object.keys(thing).length === 2; + if (thing + && (thing.version !== undefined && typeof thing.version === 'number') + && (thing.content !== undefined && typeof thing.content === 'string')) { + + // backward compatibility + if (Object.keys(thing).length === 2) { + return true; + } + + if (Object.keys(thing).length === 3 + && (thing.machineId !== undefined && typeof thing.machineId === 'string')) { + return true; + } + } + + return false; +} + +export interface IResourcePreview { + + readonly remoteResource: URI; + readonly remoteContent: string | null; + readonly remoteChange: Change; + + readonly localResource: URI; + readonly localContent: string | null; + readonly localChange: Change; + + readonly previewResource: URI; + readonly acceptedResource: URI; +} + +export interface IAcceptResult { + readonly content: string | null; + readonly localChange: Change; + readonly remoteChange: Change; +} + +export interface IMergeResult extends IAcceptResult { + readonly hasConflicts: boolean; +} + +interface IEditableResourcePreview extends IBaseResourcePreview, IResourcePreview { + localChange: Change; + remoteChange: Change; + mergeState: MergeState; + acceptResult?: IAcceptResult; +} + +interface ISyncResourcePreview extends IBaseSyncResourcePreview { + readonly remoteUserData: IRemoteUserData; + readonly lastSyncUserData: IRemoteUserData | null; + readonly resourcePreviews: IEditableResourcePreview[]; } export abstract class AbstractSynchroniser extends Disposable { + private syncPreviewPromise: CancelablePromise | null = null; + protected readonly syncFolder: URI; + protected readonly syncPreviewFolder: URI; + private readonly currentMachineIdPromise: Promise; private _status: SyncStatus = SyncStatus.Idle; get status(): SyncStatus { return this._status; } private _onDidChangStatus: Emitter = this._register(new Emitter()); readonly onDidChangeStatus: Event = this._onDidChangStatus.event; - private _conflicts: Conflict[] = []; - get conflicts(): Conflict[] { return this._conflicts; } - private _onDidChangeConflicts: Emitter = this._register(new Emitter()); - readonly onDidChangeConflicts: Event = this._onDidChangeConflicts.event; + private _conflicts: IBaseResourcePreview[] = []; + get conflicts(): IBaseResourcePreview[] { return this._conflicts; } + private _onDidChangeConflicts: Emitter = this._register(new Emitter()); + readonly onDidChangeConflicts: Event = this._onDidChangeConflicts.event; - protected readonly _onDidChangeLocal: Emitter = this._register(new Emitter()); + private readonly localChangeTriggerScheduler = new RunOnceScheduler(() => this.doTriggerLocalChange(), 50); + private readonly _onDidChangeLocal: Emitter = this._register(new Emitter()); readonly onDidChangeLocal: Event = this._onDidChangeLocal.event; protected readonly lastSyncResource: URI; protected readonly syncResourceLogLabel: string; + private syncHeaders: IHeaders = {}; + constructor( readonly resource: SyncResource, @IFileService protected readonly fileService: IFileService, - @IEnvironmentService environmentService: IEnvironmentService, + @IEnvironmentService protected readonly environmentService: IEnvironmentService, + @IStorageService storageService: IStorageService, @IUserDataSyncStoreService protected readonly userDataSyncStoreService: IUserDataSyncStoreService, @IUserDataSyncBackupStoreService protected readonly userDataSyncBackupStoreService: IUserDataSyncBackupStoreService, - @IUserDataSyncEnablementService protected readonly userDataSyncEnablementService: IUserDataSyncEnablementService, - @ITelemetryService private readonly telemetryService: ITelemetryService, + @IUserDataSyncResourceEnablementService protected readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, + @ITelemetryService protected readonly telemetryService: ITelemetryService, @IUserDataSyncLogService protected readonly logService: IUserDataSyncLogService, @IConfigurationService protected readonly configurationService: IConfigurationService, ) { super(); this.syncResourceLogLabel = uppercaseFirstLetter(this.resource); this.syncFolder = joinPath(environmentService.userDataSyncHome, resource); + this.syncPreviewFolder = joinPath(this.syncFolder, PREVIEW_DIR_NAME); this.lastSyncResource = joinPath(this.syncFolder, `lastSync${this.resource}.json`); + this.currentMachineIdPromise = getServiceMachineId(environmentService, fileService, storageService); + } + + protected isEnabled(): boolean { return this.userDataSyncResourceEnablementService.isResourceEnabled(this.resource); } + + protected async triggerLocalChange(): Promise { + if (this.isEnabled()) { + this.localChangeTriggerScheduler.schedule(); + } + } + + protected async doTriggerLocalChange(): Promise { + + // Sync again if current status is in conflicts + if (this.status === SyncStatus.HasConflicts) { + this.logService.info(`${this.syncResourceLogLabel}: In conflicts state and local change detected. Syncing again...`); + const preview = await this.syncPreviewPromise!; + this.syncPreviewPromise = null; + const status = await this.performSync(preview.remoteUserData, preview.lastSyncUserData, true); + this.setStatus(status); + } + + // Check if local change causes remote change + else { + this.logService.trace(`${this.syncResourceLogLabel}: Checking for local changes...`); + const lastSyncUserData = await this.getLastSyncUserData(); + const hasRemoteChanged = lastSyncUserData ? (await this.doGenerateSyncResourcePreview(lastSyncUserData, lastSyncUserData, true, CancellationToken.None)).resourcePreviews.some(({ remoteChange }) => remoteChange !== Change.None) : true; + if (hasRemoteChanged) { + this._onDidChangeLocal.fire(); + } + } } protected setStatus(status: SyncStatus): void { if (this._status !== status) { const oldStatus = this._status; - this._status = status; - this._onDidChangStatus.fire(status); if (status === SyncStatus.HasConflicts) { // Log to telemetry when there is a sync conflict this.telemetryService.publicLog2<{ source: string }, SyncSourceClassification>('sync/conflictsDetected', { source: this.resource }); @@ -93,80 +178,150 @@ export abstract class AbstractSynchroniser extends Disposable { // Log to telemetry when conflicts are resolved this.telemetryService.publicLog2<{ source: string }, SyncSourceClassification>('sync/conflictsResolved', { source: this.resource }); } - if (this.status !== SyncStatus.HasConflicts) { - this.setConflicts([]); - } + this._status = status; + this._onDidChangStatus.fire(status); } } - protected setConflicts(conflicts: Conflict[]) { - if (!equals(this._conflicts, conflicts, (a, b) => isEqual(a.local, b.local) && isEqual(a.remote, b.remote))) { - this._conflicts = conflicts; - this._onDidChangeConflicts.fire(this._conflicts); - } + async sync(manifest: IUserDataManifest | null, headers: IHeaders = {}): Promise { + await this._sync(manifest, true, headers); } - protected isEnabled(): boolean { return this.userDataSyncEnablementService.isResourceEnabled(this.resource); } + async preview(manifest: IUserDataManifest | null, headers: IHeaders = {}): Promise { + return this._sync(manifest, false, headers); + } - async sync(ref?: string): Promise { - if (!this.isEnabled()) { - if (this.status !== SyncStatus.Idle) { - await this.stop(); - } - this.logService.info(`${this.syncResourceLogLabel}: Skipped synchronizing ${this.resource.toLowerCase()} as it is disabled.`); - return; - } - if (this.status === SyncStatus.HasConflicts) { - this.logService.info(`${this.syncResourceLogLabel}: Skipped synchronizing ${this.resource.toLowerCase()} as there are conflicts.`); - return; - } - if (this.status === SyncStatus.Syncing) { - this.logService.info(`${this.syncResourceLogLabel}: Skipped synchronizing ${this.resource.toLowerCase()} as it is running already.`); - return; - } - - this.logService.trace(`${this.syncResourceLogLabel}: Started synchronizing ${this.resource.toLowerCase()}...`); - this.setStatus(SyncStatus.Syncing); - - const lastSyncUserData = await this.getLastSyncUserData(); - const remoteUserData = ref && lastSyncUserData && lastSyncUserData.ref === ref ? lastSyncUserData : await this.getRemoteUserData(lastSyncUserData); - - let status: SyncStatus = SyncStatus.Idle; + async apply(force: boolean, headers: IHeaders = {}): Promise { try { - status = await this.doSync(remoteUserData, lastSyncUserData); - if (status === SyncStatus.HasConflicts) { - this.logService.info(`${this.syncResourceLogLabel}: Detected conflicts while synchronizing ${this.resource.toLowerCase()}.`); - } else if (status === SyncStatus.Idle) { - this.logService.trace(`${this.syncResourceLogLabel}: Finished synchronizing ${this.resource.toLowerCase()}.`); + this.syncHeaders = { ...headers }; + + const status = await this.doApply(force); + this.setStatus(status); + + return this.syncPreviewPromise; + } finally { + this.syncHeaders = {}; + } + } + + private async _sync(manifest: IUserDataManifest | null, apply: boolean, headers: IHeaders): Promise { + try { + this.syncHeaders = { ...headers }; + + if (!this.isEnabled()) { + if (this.status !== SyncStatus.Idle) { + await this.stop(); + } + this.logService.info(`${this.syncResourceLogLabel}: Skipped synchronizing ${this.resource.toLowerCase()} as it is disabled.`); + return null; + } + + if (this.status === SyncStatus.HasConflicts) { + this.logService.info(`${this.syncResourceLogLabel}: Skipped synchronizing ${this.resource.toLowerCase()} as there are conflicts.`); + return this.syncPreviewPromise; + } + + if (this.status === SyncStatus.Syncing) { + this.logService.info(`${this.syncResourceLogLabel}: Skipped synchronizing ${this.resource.toLowerCase()} as it is running already.`); + return this.syncPreviewPromise; + } + + this.logService.trace(`${this.syncResourceLogLabel}: Started synchronizing ${this.resource.toLowerCase()}...`); + this.setStatus(SyncStatus.Syncing); + + let status: SyncStatus = SyncStatus.Idle; + try { + const lastSyncUserData = await this.getLastSyncUserData(); + const remoteUserData = await this.getLatestRemoteUserData(manifest, lastSyncUserData); + status = await this.performSync(remoteUserData, lastSyncUserData, apply); + if (status === SyncStatus.HasConflicts) { + this.logService.info(`${this.syncResourceLogLabel}: Detected conflicts while synchronizing ${this.resource.toLowerCase()}.`); + } else if (status === SyncStatus.Idle) { + this.logService.trace(`${this.syncResourceLogLabel}: Finished synchronizing ${this.resource.toLowerCase()}.`); + } + return this.syncPreviewPromise || null; + } finally { + this.setStatus(status); } } finally { - this.setStatus(status); + this.syncHeaders = {}; } } - async getSyncPreview(): Promise { - if (!this.isEnabled()) { - return { hasLocalChanged: false, hasRemoteChanged: false }; + async replace(uri: URI): Promise { + const content = await this.resolveContent(uri); + if (!content) { + return false; } - const lastSyncUserData = await this.getLastSyncUserData(); - const remoteUserData = await this.getRemoteUserData(lastSyncUserData); - return this.generatePreview(remoteUserData, lastSyncUserData); + const syncData = this.parseSyncData(content); + if (!syncData) { + return false; + } + + await this.stop(); + + try { + this.logService.trace(`${this.syncResourceLogLabel}: Started resetting ${this.resource.toLowerCase()}...`); + this.setStatus(SyncStatus.Syncing); + const lastSyncUserData = await this.getLastSyncUserData(); + const remoteUserData = await this.getLatestRemoteUserData(null, lastSyncUserData); + + const resourcePreviewResults = await this.generateSyncPreview(remoteUserData, lastSyncUserData, CancellationToken.None); + + const resourcePreviews: [IResourcePreview, IAcceptResult][] = []; + for (const resourcePreviewResult of resourcePreviewResults) { + /* Accept remote resource */ + const acceptResult: IAcceptResult = await this.getAcceptResult(resourcePreviewResult, resourcePreviewResult.remoteResource, resourcePreviewResult.remoteContent, CancellationToken.None); + resourcePreviews.push([resourcePreviewResult, acceptResult]); + } + + await this.applyResult(remoteUserData, lastSyncUserData, resourcePreviews, false); + this.logService.info(`${this.syncResourceLogLabel}: Finished resetting ${this.resource.toLowerCase()}.`); + } finally { + this.setStatus(SyncStatus.Idle); + } + + return true; } - protected async doSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise { + protected async getLatestRemoteUserData(manifest: IUserDataManifest | null, lastSyncUserData: IRemoteUserData | null): Promise { + if (lastSyncUserData) { + + const latestRef = manifest && manifest.latest ? manifest.latest[this.resource] : undefined; + + // Last time synced resource and latest resource on server are same + if (lastSyncUserData.ref === latestRef) { + return lastSyncUserData; + } + + // There is no resource on server and last time it was synced with no resource + if (latestRef === undefined && lastSyncUserData.syncData === null) { + return lastSyncUserData; + } + } + return this.getRemoteUserData(lastSyncUserData); + } + + private async performSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, apply: boolean): Promise { if (remoteUserData.syncData && remoteUserData.syncData.version > this.version) { // current version is not compatible with cloud version this.telemetryService.publicLog2<{ source: string }, SyncSourceClassification>('sync/incompatible', { source: this.resource }); - throw new UserDataSyncError(localize('incompatible', "Cannot sync {0} as its version {1} is not compatible with cloud {2}", this.resource, this.version, remoteUserData.syncData.version), UserDataSyncErrorCode.Incompatible, this.resource); + throw new UserDataSyncError(localize({ key: 'incompatible', comment: ['This is an error while syncing a resource that its local version is not compatible with its remote version.'] }, "Cannot sync {0} as its local version {1} is not compatible with its remote version {2}", this.resource, this.version, remoteUserData.syncData.version), UserDataSyncErrorCode.IncompatibleLocalContent, this.resource); } + try { - const status = await this.performSync(remoteUserData, lastSyncUserData); - return status; + return await this.doSync(remoteUserData, lastSyncUserData, apply); } catch (e) { if (e instanceof UserDataSyncError) { switch (e.code) { - case UserDataSyncErrorCode.RemotePreconditionFailed: + + case UserDataSyncErrorCode.LocalPreconditionFailed: + // Rejected as there is a new local version. Syncing again... + this.logService.info(`${this.syncResourceLogLabel}: Failed to synchronize ${this.syncResourceLogLabel} as there is a new local version available. Synchronizing again...`); + return this.performSync(remoteUserData, lastSyncUserData, apply); + + case UserDataSyncErrorCode.PreconditionFailed: // Rejected as there is a new remote version. Syncing again... this.logService.info(`${this.syncResourceLogLabel}: Failed to synchronize as there is a new remote version available. Synchronizing again...`); @@ -177,13 +332,155 @@ export abstract class AbstractSynchroniser extends Disposable { // and one of them successfully updated remote and last sync state. lastSyncUserData = await this.getLastSyncUserData(); - return this.doSync(remoteUserData, lastSyncUserData); + return this.performSync(remoteUserData, lastSyncUserData, apply); } } throw e; } } + protected async doSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, apply: boolean): Promise { + try { + // generate or use existing preview + if (!this.syncPreviewPromise) { + this.syncPreviewPromise = createCancelablePromise(token => this.doGenerateSyncResourcePreview(remoteUserData, lastSyncUserData, apply, token)); + } + + const preview = await this.syncPreviewPromise; + this.updateConflicts(preview.resourcePreviews); + if (preview.resourcePreviews.some(({ mergeState }) => mergeState === MergeState.Conflict)) { + return SyncStatus.HasConflicts; + } + + if (apply) { + return await this.doApply(false); + } + + return SyncStatus.Syncing; + + } catch (error) { + + // reset preview on error + this.syncPreviewPromise = null; + + throw error; + } + } + + async merge(resource: URI): Promise { + await this.updateSyncResourcePreview(resource, async (resourcePreview) => { + const mergeResult = await this.getMergeResult(resourcePreview, CancellationToken.None); + await this.fileService.writeFile(resourcePreview.previewResource, VSBuffer.fromString(mergeResult?.content || '')); + const acceptResult: IAcceptResult | undefined = mergeResult && !mergeResult.hasConflicts + ? await this.getAcceptResult(resourcePreview, resourcePreview.previewResource, undefined, CancellationToken.None) + : undefined; + resourcePreview.acceptResult = acceptResult; + resourcePreview.mergeState = mergeResult.hasConflicts ? MergeState.Conflict : acceptResult ? MergeState.Accepted : MergeState.Preview; + resourcePreview.localChange = acceptResult ? acceptResult.localChange : mergeResult.localChange; + resourcePreview.remoteChange = acceptResult ? acceptResult.remoteChange : mergeResult.remoteChange; + return resourcePreview; + }); + return this.syncPreviewPromise; + } + + async accept(resource: URI, content?: string | null): Promise { + await this.updateSyncResourcePreview(resource, async (resourcePreview) => { + const acceptResult = await this.getAcceptResult(resourcePreview, resource, content, CancellationToken.None); + resourcePreview.acceptResult = acceptResult; + resourcePreview.mergeState = MergeState.Accepted; + resourcePreview.localChange = acceptResult.localChange; + resourcePreview.remoteChange = acceptResult.remoteChange; + return resourcePreview; + }); + return this.syncPreviewPromise; + } + + async discard(resource: URI): Promise { + await this.updateSyncResourcePreview(resource, async (resourcePreview) => { + const mergeResult = await this.getMergeResult(resourcePreview, CancellationToken.None); + await this.fileService.writeFile(resourcePreview.previewResource, VSBuffer.fromString(mergeResult.content || '')); + resourcePreview.acceptResult = undefined; + resourcePreview.mergeState = MergeState.Preview; + resourcePreview.localChange = mergeResult.localChange; + resourcePreview.remoteChange = mergeResult.remoteChange; + return resourcePreview; + }); + return this.syncPreviewPromise; + } + + private async updateSyncResourcePreview(resource: URI, updateResourcePreview: (resourcePreview: IEditableResourcePreview) => Promise): Promise { + if (!this.syncPreviewPromise) { + return; + } + + let preview = await this.syncPreviewPromise; + const index = preview.resourcePreviews.findIndex(({ localResource, remoteResource, previewResource }) => + isEqual(localResource, resource) || isEqual(remoteResource, resource) || isEqual(previewResource, resource)); + if (index === -1) { + return; + } + + this.syncPreviewPromise = createCancelablePromise(async token => { + const resourcePreviews = [...preview.resourcePreviews]; + resourcePreviews[index] = await updateResourcePreview(resourcePreviews[index]); + return { + ...preview, + resourcePreviews + }; + }); + + preview = await this.syncPreviewPromise; + this.updateConflicts(preview.resourcePreviews); + if (preview.resourcePreviews.some(({ mergeState }) => mergeState === MergeState.Conflict)) { + this.setStatus(SyncStatus.HasConflicts); + } else { + this.setStatus(SyncStatus.Syncing); + } + } + + private async doApply(force: boolean): Promise { + if (!this.syncPreviewPromise) { + return SyncStatus.Idle; + } + + const preview = await this.syncPreviewPromise; + + // check for conflicts + if (preview.resourcePreviews.some(({ mergeState }) => mergeState === MergeState.Conflict)) { + return SyncStatus.HasConflicts; + } + + // check if all are accepted + if (preview.resourcePreviews.some(({ mergeState }) => mergeState !== MergeState.Accepted)) { + return SyncStatus.Syncing; + } + + // apply preview + await this.applyResult(preview.remoteUserData, preview.lastSyncUserData, preview.resourcePreviews.map(resourcePreview => ([resourcePreview, resourcePreview.acceptResult!])), force); + + // reset preview + this.syncPreviewPromise = null; + + // reset preview folder + await this.clearPreviewFolder(); + + return SyncStatus.Idle; + } + + private async clearPreviewFolder(): Promise { + try { + await this.fileService.del(this.syncPreviewFolder, { recursive: true }); + } catch (error) { /* Ignore */ } + } + + private updateConflicts(resourcePreviews: IEditableResourcePreview[]): void { + const conflicts = resourcePreviews.filter(({ mergeState }) => mergeState === MergeState.Conflict); + if (!equals(this._conflicts, conflicts, (a, b) => isEqual(a.previewResource, b.previewResource))) { + this._conflicts = conflicts; + this._onDidChangeConflicts.fire(conflicts); + } + } + async hasPreviouslySynced(): Promise { const lastSyncData = await this.getLastSyncUserData(); return !!lastSyncData; @@ -207,6 +504,18 @@ export abstract class AbstractSynchroniser extends Disposable { return URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local-backup', path: `/${this.resource}/${ref}` }); } + async getMachineId({ uri }: ISyncResourceHandle): Promise { + const ref = basename(uri); + if (isEqual(uri, this.toRemoteBackupResource(ref))) { + const { content } = await this.getUserData(ref); + if (content) { + const syncData = this.parseSyncData(content); + return syncData?.machineId; + } + } + return undefined; + } + async resolveContent(uri: URI): Promise { const ref = basename(uri); if (isEqual(uri, this.toRemoteBackupResource(ref))) { @@ -219,24 +528,95 @@ export abstract class AbstractSynchroniser extends Disposable { return null; } + protected async resolvePreviewContent(uri: URI): Promise { + const syncPreview = this.syncPreviewPromise ? await this.syncPreviewPromise : null; + if (syncPreview) { + for (const resourcePreview of syncPreview.resourcePreviews) { + if (isEqual(resourcePreview.acceptedResource, uri)) { + return resourcePreview.acceptResult ? resourcePreview.acceptResult.content : null; + } + if (isEqual(resourcePreview.remoteResource, uri)) { + return resourcePreview.remoteContent; + } + if (isEqual(resourcePreview.localResource, uri)) { + return resourcePreview.localContent; + } + } + } + return null; + } + async resetLocal(): Promise { try { await this.fileService.del(this.lastSyncResource); } catch (e) { /* ignore */ } } - protected async getLastSyncUserData(): Promise { + private async doGenerateSyncResourcePreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, apply: boolean, token: CancellationToken): Promise { + const machineId = await this.currentMachineIdPromise; + const isLastSyncFromCurrentMachine = !!remoteUserData.syncData?.machineId && remoteUserData.syncData.machineId === machineId; + + // For preview, use remoteUserData if lastSyncUserData does not exists and last sync is from current machine + const lastSyncUserDataForPreview = lastSyncUserData === null && isLastSyncFromCurrentMachine ? remoteUserData : lastSyncUserData; + const resourcePreviewResults = await this.generateSyncPreview(remoteUserData, lastSyncUserDataForPreview, token); + + const resourcePreviews: IEditableResourcePreview[] = []; + for (const resourcePreviewResult of resourcePreviewResults) { + const acceptedResource = resourcePreviewResult.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }); + + /* No change -> Accept */ + if (resourcePreviewResult.localChange === Change.None && resourcePreviewResult.remoteChange === Change.None) { + resourcePreviews.push({ + ...resourcePreviewResult, + acceptedResource, + acceptResult: { content: null, localChange: Change.None, remoteChange: Change.None }, + mergeState: MergeState.Accepted + }); + } + + /* Changed -> Apply ? (Merge ? Conflict | Accept) : Preview */ + else { + /* Merge */ + const mergeResult = apply ? await this.getMergeResult(resourcePreviewResult, token) : undefined; + if (token.isCancellationRequested) { + break; + } + await this.fileService.writeFile(resourcePreviewResult.previewResource, VSBuffer.fromString(mergeResult?.content || '')); + + /* Conflict | Accept */ + const acceptResult = mergeResult && !mergeResult.hasConflicts + /* Accept if merged and there are no conflicts */ + ? await this.getAcceptResult(resourcePreviewResult, resourcePreviewResult.previewResource, undefined, token) + : undefined; + + resourcePreviews.push({ + ...resourcePreviewResult, + acceptResult, + mergeState: mergeResult?.hasConflicts ? MergeState.Conflict : acceptResult ? MergeState.Accepted : MergeState.Preview, + localChange: acceptResult ? acceptResult.localChange : mergeResult ? mergeResult.localChange : resourcePreviewResult.localChange, + remoteChange: acceptResult ? acceptResult.remoteChange : mergeResult ? mergeResult.remoteChange : resourcePreviewResult.remoteChange + }); + } + } + + return { remoteUserData, lastSyncUserData, resourcePreviews, isLastSyncFromCurrentMachine }; + } + + async getLastSyncUserData(): Promise { try { const content = await this.fileService.readFile(this.lastSyncResource); const parsed = JSON.parse(content.value.toString()); - let syncData: ISyncData = JSON.parse(parsed.content); + const userData: IUserData = parsed as IUserData; + if (userData.content === null) { + return { ref: parsed.ref, syncData: null } as T; + } + const syncData: ISyncData = JSON.parse(userData.content); - // Migration from old content to sync data - if (!isSyncData(syncData)) { - syncData = { version: this.version, content: parsed.content }; + /* Check if syncData is of expected type. Return only if matches */ + if (isSyncData(syncData)) { + return { ...parsed, ...{ syncData, content: undefined } }; } - return { ...parsed, ...{ syncData, content: undefined } }; } catch (error) { if (!(error instanceof FileOperationError && error.fileOperationResult === FileOperationResult.FILE_NOT_FOUND)) { // log error always except when file does not exist @@ -247,11 +627,11 @@ export abstract class AbstractSynchroniser extends Disposable { } protected async updateLastSyncUserData(lastSyncRemoteUserData: IRemoteUserData, additionalProps: IStringDictionary = {}): Promise { - const lastSyncUserData: IUserData = { ref: lastSyncRemoteUserData.ref, content: JSON.stringify(lastSyncRemoteUserData.syncData), ...additionalProps }; + const lastSyncUserData: IUserData = { ref: lastSyncRemoteUserData.ref, content: lastSyncRemoteUserData.syncData ? JSON.stringify(lastSyncRemoteUserData.syncData) : null, ...additionalProps }; await this.fileService.writeFile(this.lastSyncResource, VSBuffer.fromString(JSON.stringify(lastSyncUserData))); } - protected async getRemoteUserData(lastSyncData: IRemoteUserData | null): Promise { + async getRemoteUserData(lastSyncData: IRemoteUserData | null): Promise { const { ref, content } = await this.getUserData(lastSyncData); let syncData: ISyncData | null = null; if (content !== null) { @@ -260,20 +640,16 @@ export abstract class AbstractSynchroniser extends Disposable { return { ref, syncData }; } - protected parseSyncData(content: string): ISyncData | null { - let syncData: ISyncData | null = null; + protected parseSyncData(content: string): ISyncData { try { - syncData = JSON.parse(content); - - // Migration from old content to sync data - if (!isSyncData(syncData)) { - syncData = { version: this.version, content }; + const syncData: ISyncData = JSON.parse(content); + if (isSyncData(syncData)) { + return syncData; } - - } catch (e) { - this.logService.error(e); + } catch (error) { + this.logService.error(error); } - return syncData; + throw new UserDataSyncError(localize('incompatible sync data', "Cannot parse sync data as it is not compatible with current version."), UserDataSyncErrorCode.IncompatibleRemoteContent, this.resource); } private async getUserData(refOrLastSyncData: string | IRemoteUserData | null): Promise { @@ -282,13 +658,14 @@ export abstract class AbstractSynchroniser extends Disposable { return { ref: refOrLastSyncData, content }; } else { const lastSyncUserData: IUserData | null = refOrLastSyncData ? { ref: refOrLastSyncData.ref, content: refOrLastSyncData.syncData ? JSON.stringify(refOrLastSyncData.syncData) : null } : null; - return this.userDataSyncStoreService.read(this.resource, lastSyncUserData); + return this.userDataSyncStoreService.read(this.resource, lastSyncUserData, this.syncHeaders); } } protected async updateRemoteUserData(content: string, ref: string | null): Promise { - const syncData: ISyncData = { version: this.version, content }; - ref = await this.userDataSyncStoreService.write(this.resource, JSON.stringify(syncData), ref); + const machineId = await this.currentMachineIdPromise; + const syncData: ISyncData = { version: this.version, machineId, content }; + ref = await this.userDataSyncStoreService.write(this.resource, JSON.stringify(syncData), ref, this.syncHeaders); return { ref, syncData }; } @@ -297,66 +674,55 @@ export abstract class AbstractSynchroniser extends Disposable { return this.userDataSyncBackupStoreService.backup(this.resource, JSON.stringify(syncData)); } - abstract stop(): Promise; + async stop(): Promise { + if (this.status === SyncStatus.Idle) { + return; + } + + this.logService.trace(`${this.syncResourceLogLabel}: Stopping synchronizing ${this.resource.toLowerCase()}.`); + if (this.syncPreviewPromise) { + this.syncPreviewPromise.cancel(); + this.syncPreviewPromise = null; + } + + this.updateConflicts([]); + await this.clearPreviewFolder(); + + this.setStatus(SyncStatus.Idle); + this.logService.info(`${this.syncResourceLogLabel}: Stopped synchronizing ${this.resource.toLowerCase()}.`); + } protected abstract readonly version: number; - protected abstract performSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise; - protected abstract generatePreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise; + protected abstract generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise; + protected abstract getMergeResult(resourcePreview: IResourcePreview, token: CancellationToken): Promise; + protected abstract getAcceptResult(resourcePreview: IResourcePreview, resource: URI, content: string | null | undefined, token: CancellationToken): Promise; + protected abstract applyResult(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, result: [IResourcePreview, IAcceptResult][], force: boolean): Promise; } -export interface IFileSyncPreviewResult extends ISyncPreviewResult { +export interface IFileResourcePreview extends IResourcePreview { readonly fileContent: IFileContent | null; - readonly remoteUserData: IRemoteUserData; - readonly lastSyncUserData: IRemoteUserData | null; - readonly content: string | null; - readonly hasConflicts: boolean; } export abstract class AbstractFileSynchroniser extends AbstractSynchroniser { - protected syncPreviewResultPromise: CancelablePromise | null = null; - constructor( protected readonly file: URI, resource: SyncResource, @IFileService fileService: IFileService, @IEnvironmentService environmentService: IEnvironmentService, + @IStorageService storageService: IStorageService, @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, @IUserDataSyncBackupStoreService userDataSyncBackupStoreService: IUserDataSyncBackupStoreService, - @IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService, + @IUserDataSyncResourceEnablementService userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, @ITelemetryService telemetryService: ITelemetryService, @IUserDataSyncLogService logService: IUserDataSyncLogService, @IConfigurationService configurationService: IConfigurationService, ) { - super(resource, fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService); + super(resource, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncResourceEnablementService, telemetryService, logService, configurationService); this._register(this.fileService.watch(dirname(file))); this._register(this.fileService.onDidFilesChange(e => this.onFileChanges(e))); } - async stop(): Promise { - this.cancel(); - this.logService.info(`${this.syncResourceLogLabel}: Stopped synchronizing ${this.resource.toLowerCase()}.`); - try { - await this.fileService.del(this.localPreviewResource); - } catch (e) { /* ignore */ } - this.setStatus(SyncStatus.Idle); - } - - protected async getConflictContent(conflictResource: URI): Promise { - if (isEqual(this.remotePreviewResource, conflictResource) || isEqual(this.localPreviewResource, conflictResource)) { - if (this.syncPreviewResultPromise) { - const result = await this.syncPreviewResultPromise; - if (isEqual(this.remotePreviewResource, conflictResource)) { - return result.remoteUserData && result.remoteUserData.syncData ? result.remoteUserData.syncData.content : null; - } - if (isEqual(this.localPreviewResource, conflictResource)) { - return result.fileContent ? result.fileContent.value.toString() : null; - } - } - } - return null; - } - protected async getLocalFileContent(): Promise { try { return await this.fileService.readFile(this.file); @@ -365,14 +731,14 @@ export abstract class AbstractFileSynchroniser extends AbstractSynchroniser { } } - protected async updateLocalFileContent(newContent: string, oldContent: IFileContent | null): Promise { + protected async updateLocalFileContent(newContent: string, oldContent: IFileContent | null, force: boolean): Promise { try { if (oldContent) { // file exists already - await this.fileService.writeFile(this.file, VSBuffer.fromString(newContent), oldContent); + await this.fileService.writeFile(this.file, VSBuffer.fromString(newContent), force ? undefined : oldContent); } else { // file does not exist - await this.fileService.createFile(this.file, VSBuffer.fromString(newContent), { overwrite: false }); + await this.fileService.createFile(this.file, VSBuffer.fromString(newContent), { overwrite: force }); } } catch (e) { if ((e instanceof FileOperationError && e.fileOperationResult === FileOperationResult.FILE_NOT_FOUND) || @@ -388,35 +754,9 @@ export abstract class AbstractFileSynchroniser extends AbstractSynchroniser { if (!e.contains(this.file)) { return; } - - if (!this.isEnabled()) { - return; - } - - // Sync again if local file has changed and current status is in conflicts - if (this.status === SyncStatus.HasConflicts) { - this.syncPreviewResultPromise?.then(result => { - this.cancel(); - this.doSync(result.remoteUserData, result.lastSyncUserData).then(status => this.setStatus(status)); - }); - } - - // Otherwise fire change event - else { - this._onDidChangeLocal.fire(); - } - + this.triggerLocalChange(); } - protected cancel(): void { - if (this.syncPreviewResultPromise) { - this.syncPreviewResultPromise.cancel(); - this.syncPreviewResultPromise = null; - } - } - - protected abstract readonly localPreviewResource: URI; - protected abstract readonly remotePreviewResource: URI; } export abstract class AbstractJsonFileSynchroniser extends AbstractFileSynchroniser { @@ -426,15 +766,16 @@ export abstract class AbstractJsonFileSynchroniser extends AbstractFileSynchroni resource: SyncResource, @IFileService fileService: IFileService, @IEnvironmentService environmentService: IEnvironmentService, + @IStorageService storageService: IStorageService, @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, @IUserDataSyncBackupStoreService userDataSyncBackupStoreService: IUserDataSyncBackupStoreService, - @IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService, + @IUserDataSyncResourceEnablementService userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, @ITelemetryService telemetryService: ITelemetryService, @IUserDataSyncLogService logService: IUserDataSyncLogService, @IUserDataSyncUtilService protected readonly userDataSyncUtilService: IUserDataSyncUtilService, @IConfigurationService configurationService: IConfigurationService, ) { - super(file, resource, fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService); + super(file, resource, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncResourceEnablementService, telemetryService, logService, configurationService); } protected hasErrors(content: string): boolean { diff --git a/src/vs/platform/userDataSync/common/extensionsMerge.ts b/src/vs/platform/userDataSync/common/extensionsMerge.ts index bb02ff61d84..381047368e0 100644 --- a/src/vs/platform/userDataSync/common/extensionsMerge.ts +++ b/src/vs/platform/userDataSync/common/extensionsMerge.ts @@ -3,10 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { values, keys } from 'vs/base/common/map'; import { ISyncExtension } from 'vs/platform/userDataSync/common/userDataSync'; import { IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { startsWith } from 'vs/base/common/strings'; +import { deepClone } from 'vs/base/common/objects'; +import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { distinct } from 'vs/base/common/arrays'; export interface IMergeResult { added: ISyncExtension[]; @@ -21,16 +23,15 @@ export function merge(localExtensions: ISyncExtension[], remoteExtensions: ISync const updated: ISyncExtension[] = []; if (!remoteExtensions) { + const remote = localExtensions.filter(({ identifier }) => ignoredExtensions.every(id => id.toLowerCase() !== identifier.id.toLowerCase())); return { added, removed, updated, - remote: localExtensions.filter(({ identifier }) => ignoredExtensions.every(id => id.toLowerCase() !== identifier.id.toLowerCase())) + remote: remote.length > 0 ? remote : null }; } - // massage incoming extension - add disabled property - const massageIncomingExtension = (extension: ISyncExtension): ISyncExtension => ({ ...extension, ...{ disabled: !!extension.disabled } }); localExtensions = localExtensions.map(massageIncomingExtension); remoteExtensions = remoteExtensions.map(massageIncomingExtension); lastSyncExtensions = lastSyncExtensions ? lastSyncExtensions.map(massageIncomingExtension) : null; @@ -53,7 +54,14 @@ export function merge(localExtensions: ISyncExtension[], remoteExtensions: ISync }; const localExtensionsMap = localExtensions.reduce(addExtensionToMap, new Map()); const remoteExtensionsMap = remoteExtensions.reduce(addExtensionToMap, new Map()); - const newRemoteExtensionsMap = remoteExtensions.reduce(addExtensionToMap, new Map()); + const newRemoteExtensionsMap = remoteExtensions.reduce((map: Map, extension: ISyncExtension) => { + const key = getKey(extension); + extension = deepClone(extension); + if (localExtensionsMap.get(key)?.installed) { + extension.installed = true; + } + return addExtensionToMap(map, extension); + }, new Map()); const lastSyncExtensionsMap = lastSyncExtensions ? lastSyncExtensions.reduce(addExtensionToMap, new Map()) : null; const skippedExtensionsMap = skippedExtensions.reduce(addExtensionToMap, new Map()); const ignoredExtensionsSet = ignoredExtensions.reduce((set, id) => { @@ -62,90 +70,82 @@ export function merge(localExtensions: ISyncExtension[], remoteExtensions: ISync }, new Set()); const localToRemote = compare(localExtensionsMap, remoteExtensionsMap, ignoredExtensionsSet); - if (localToRemote.added.size === 0 && localToRemote.removed.size === 0 && localToRemote.updated.size === 0) { - // No changes found between local and remote. - return { added: [], removed: [], updated: [], remote: null }; - } + if (localToRemote.added.size > 0 || localToRemote.removed.size > 0 || localToRemote.updated.size > 0) { - const baseToLocal = compare(lastSyncExtensionsMap, localExtensionsMap, ignoredExtensionsSet); - const baseToRemote = compare(lastSyncExtensionsMap, remoteExtensionsMap, ignoredExtensionsSet); + const baseToLocal = compare(lastSyncExtensionsMap, localExtensionsMap, ignoredExtensionsSet); + const baseToRemote = compare(lastSyncExtensionsMap, remoteExtensionsMap, ignoredExtensionsSet); - // massage outgoing extension - remove disabled property - const massageOutgoingExtension = (extension: ISyncExtension, key: string): ISyncExtension => { - const massagedExtension: ISyncExtension = { - identifier: { - id: extension.identifier.id, - uuid: startsWith(key, 'uuid:') ? key.substring('uuid:'.length) : undefined - }, - }; - if (extension.disabled) { - massagedExtension.disabled = true; - } - if (extension.version) { - massagedExtension.version = extension.version; - } - return massagedExtension; - }; - - // Remotely removed extension. - for (const key of values(baseToRemote.removed)) { - const e = localExtensionsMap.get(key); - if (e) { - removed.push(e.identifier); - } - } - - // Remotely added extension - for (const key of values(baseToRemote.added)) { - // Got added in local - if (baseToLocal.added.has(key)) { - // Is different from local to remote - if (localToRemote.updated.has(key)) { - updated.push(massageOutgoingExtension(remoteExtensionsMap.get(key)!, key)); + // Remotely removed extension. + for (const key of baseToRemote.removed.values()) { + const e = localExtensionsMap.get(key); + if (e) { + removed.push(e.identifier); } - } else { - // Add to local - added.push(massageOutgoingExtension(remoteExtensionsMap.get(key)!, key)); - } - } - - // Remotely updated extensions - for (const key of values(baseToRemote.updated)) { - // Update in local always - updated.push(massageOutgoingExtension(remoteExtensionsMap.get(key)!, key)); - } - - // Locally added extensions - for (const key of values(baseToLocal.added)) { - // Not there in remote - if (!baseToRemote.added.has(key)) { - newRemoteExtensionsMap.set(key, localExtensionsMap.get(key)!); - } - } - - // Locally updated extensions - for (const key of values(baseToLocal.updated)) { - // If removed in remote - if (baseToRemote.removed.has(key)) { - continue; } - // If not updated in remote - if (!baseToRemote.updated.has(key)) { - newRemoteExtensionsMap.set(key, localExtensionsMap.get(key)!); + // Remotely added extension + for (const key of baseToRemote.added.values()) { + // Got added in local + if (baseToLocal.added.has(key)) { + // Is different from local to remote + if (localToRemote.updated.has(key)) { + updated.push(massageOutgoingExtension(remoteExtensionsMap.get(key)!, key)); + } + } else { + // Add only installed extension to local + const remoteExtension = remoteExtensionsMap.get(key)!; + if (remoteExtension.installed) { + added.push(massageOutgoingExtension(remoteExtension, key)); + } + } } - } - // Locally removed extensions - for (const key of values(baseToLocal.removed)) { - // If not skipped and not updated in remote - if (!skippedExtensionsMap.has(key) && !baseToRemote.updated.has(key)) { - newRemoteExtensionsMap.delete(key); + // Remotely updated extensions + for (const key of baseToRemote.updated.values()) { + // Update in local always + updated.push(massageOutgoingExtension(remoteExtensionsMap.get(key)!, key)); + } + + // Locally added extensions + for (const key of baseToLocal.added.values()) { + // Not there in remote + if (!baseToRemote.added.has(key)) { + newRemoteExtensionsMap.set(key, localExtensionsMap.get(key)!); + } + } + + // Locally updated extensions + for (const key of baseToLocal.updated.values()) { + // If removed in remote + if (baseToRemote.removed.has(key)) { + continue; + } + + // If not updated in remote + if (!baseToRemote.updated.has(key)) { + const extension = deepClone(localExtensionsMap.get(key)!); + // Retain installed property + if (newRemoteExtensionsMap.get(key)?.installed) { + extension.installed = true; + } + newRemoteExtensionsMap.set(key, extension); + } + } + + // Locally removed extensions + for (const key of baseToLocal.removed.values()) { + // If not skipped and not updated in remote + if (!skippedExtensionsMap.has(key) && !baseToRemote.updated.has(key)) { + // Remove only if it is an installed extension + if (lastSyncExtensionsMap?.get(key)?.installed) { + newRemoteExtensionsMap.delete(key); + } + } } } const remote: ISyncExtension[] = []; - const remoteChanges = compare(remoteExtensionsMap, newRemoteExtensionsMap, new Set()); + const remoteChanges = compare(remoteExtensionsMap, newRemoteExtensionsMap, new Set(), { checkInstalledProperty: true }); if (remoteChanges.added.size > 0 || remoteChanges.updated.size > 0 || remoteChanges.removed.size > 0) { newRemoteExtensionsMap.forEach((value, key) => remote.push(massageOutgoingExtension(value, key))); } @@ -153,9 +153,9 @@ export function merge(localExtensions: ISyncExtension[], remoteExtensions: ISync return { added, removed, updated, remote: remote.length ? remote : null }; } -function compare(from: Map | null, to: Map, ignoredExtensions: Set): { added: Set, removed: Set, updated: Set } { - const fromKeys = from ? keys(from).filter(key => !ignoredExtensions.has(key)) : []; - const toKeys = keys(to).filter(key => !ignoredExtensions.has(key)); +function compare(from: Map | null, to: Map, ignoredExtensions: Set, { checkInstalledProperty }: { checkInstalledProperty: boolean } = { checkInstalledProperty: false }): { added: Set, removed: Set, updated: Set } { + const fromKeys = from ? [...from.keys()].filter(key => !ignoredExtensions.has(key)) : []; + const toKeys = [...to.keys()].filter(key => !ignoredExtensions.has(key)); const added = toKeys.filter(key => fromKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set()); const removed = fromKeys.filter(key => toKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set()); const updated: Set = new Set(); @@ -169,6 +169,7 @@ function compare(from: Map | null, to: Map | null, to: Map i.isMachineScoped).map(i => i.identifier.id.toLowerCase()); + const value = (configurationService.getValue('sync.ignoredExtensions') || []).map(id => id.toLowerCase()); + const added: string[] = [], removed: string[] = []; + if (Array.isArray(value)) { + for (const key of value) { + if (key.startsWith('-')) { + removed.push(key.substring(1)); + } else { + added.push(key); + } + } + } + return distinct([...defaultIgnoredExtensions, ...added,].filter(setting => removed.indexOf(setting) === -1)); +} diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index ae3e0ba4da8..31ed99aaf13 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -3,49 +3,62 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SyncStatus, IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, ISyncResourceHandle, ISyncPreviewResult, USER_DATA_SYNC_SCHEME } from 'vs/platform/userDataSync/common/userDataSync'; +import { + IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncResource, IUserDataSyncResourceEnablementService, + IUserDataSyncBackupStoreService, ISyncResourceHandle, USER_DATA_SYNC_SCHEME, IRemoteUserData, ISyncData, Change +} from 'vs/platform/userDataSync/common/userDataSync'; import { Event } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionType, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IFileService } from 'vs/platform/files/common/files'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { merge } from 'vs/platform/userDataSync/common/extensionsMerge'; -import { isNonEmptyArray } from 'vs/base/common/arrays'; -import { AbstractSynchroniser, IRemoteUserData, ISyncData } from 'vs/platform/userDataSync/common/abstractSynchronizer'; +import { merge, getIgnoredExtensions } from 'vs/platform/userDataSync/common/extensionsMerge'; +import { AbstractSynchroniser, IAcceptResult, IMergeResult, IResourcePreview } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { URI } from 'vs/base/common/uri'; import { joinPath, dirname, basename, isEqual } from 'vs/base/common/resources'; import { format } from 'vs/base/common/jsonFormatter'; import { applyEdits } from 'vs/base/common/jsonEdit'; import { compare } from 'vs/base/common/strings'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { CancellationToken } from 'vs/base/common/cancellation'; -interface IExtensionsSyncPreviewResult extends ISyncPreviewResult { - readonly localExtensions: ISyncExtension[]; - readonly remoteUserData: IRemoteUserData; - readonly lastSyncUserData: ILastSyncUserData | null; +interface IExtensionResourceMergeResult extends IAcceptResult { readonly added: ISyncExtension[]; readonly removed: IExtensionIdentifier[]; readonly updated: ISyncExtension[]; readonly remote: ISyncExtension[] | null; +} + +interface IExtensionResourcePreview extends IResourcePreview { + readonly localExtensions: ISyncExtension[]; readonly skippedExtensions: ISyncExtension[]; + readonly previewResult: IExtensionResourceMergeResult; } interface ILastSyncUserData extends IRemoteUserData { skippedExtensions: ISyncExtension[] | undefined; } - export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUserDataSynchroniser { - private static readonly EXTENSIONS_DATA_URI = URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'extensions', path: `/current.json` }); - protected readonly version: number = 2; + private static readonly EXTENSIONS_DATA_URI = URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'extensions', path: `/extensions.json` }); + /* + Version 3 - Introduce installed property to skip installing built in extensions + */ + protected readonly version: number = 3; protected isEnabled(): boolean { return super.isEnabled() && this.extensionGalleryService.isEnabled(); } + private readonly previewResource: URI = joinPath(this.syncPreviewFolder, 'extensions.json'); + private readonly localResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }); + private readonly remoteResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }); + private readonly acceptedResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }); constructor( @IEnvironmentService environmentService: IEnvironmentService, @IFileService fileService: IFileService, + @IStorageService storageService: IStorageService, @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, @IUserDataSyncBackupStoreService userDataSyncBackupStoreService: IUserDataSyncBackupStoreService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @@ -53,95 +66,186 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse @IUserDataSyncLogService logService: IUserDataSyncLogService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, @IConfigurationService configurationService: IConfigurationService, - @IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService, + @IUserDataSyncResourceEnablementService userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, @ITelemetryService telemetryService: ITelemetryService, ) { - super(SyncResource.Extensions, fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService); + super(SyncResource.Extensions, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncResourceEnablementService, telemetryService, logService, configurationService); this._register( Event.debounce( Event.any( Event.filter(this.extensionManagementService.onDidInstallExtension, (e => !!e.gallery)), Event.filter(this.extensionManagementService.onDidUninstallExtension, (e => !e.error)), this.extensionEnablementService.onDidChangeEnablement), - () => undefined, 500)(() => this._onDidChangeLocal.fire())); + () => undefined, 500)(() => this.triggerLocalChange())); } - async pull(): Promise { - if (!this.isEnabled()) { - this.logService.info(`${this.syncResourceLogLabel}: Skipped pulling extensions as it is disabled.`); - return; + protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null): Promise { + const remoteExtensions: ISyncExtension[] | null = remoteUserData.syncData ? await this.parseAndMigrateExtensions(remoteUserData.syncData) : null; + const skippedExtensions: ISyncExtension[] = lastSyncUserData ? lastSyncUserData.skippedExtensions || [] : []; + const lastSyncExtensions: ISyncExtension[] | null = lastSyncUserData ? await this.parseAndMigrateExtensions(lastSyncUserData.syncData!) : null; + + const installedExtensions = await this.extensionManagementService.getInstalled(); + const localExtensions = this.getLocalExtensions(installedExtensions); + const ignoredExtensions = getIgnoredExtensions(installedExtensions, this.configurationService); + + if (remoteExtensions) { + this.logService.trace(`${this.syncResourceLogLabel}: Merging remote extensions with local extensions...`); + } else { + this.logService.trace(`${this.syncResourceLogLabel}: Remote extensions does not exist. Synchronizing extensions for the first time.`); } - this.stop(); + const { added, removed, updated, remote } = merge(localExtensions, remoteExtensions, lastSyncExtensions, skippedExtensions, ignoredExtensions); + const previewResult: IExtensionResourceMergeResult = { + added, + removed, + updated, + remote, + content: this.getPreviewContent(localExtensions, added, updated, removed), + localChange: added.length > 0 || removed.length > 0 || updated.length > 0 ? Change.Modified : Change.None, + remoteChange: remote !== null ? Change.Modified : Change.None, + }; - try { - this.logService.info(`${this.syncResourceLogLabel}: Started pulling extensions...`); - this.setStatus(SyncStatus.Syncing); + return [{ + skippedExtensions, + localResource: this.localResource, + localContent: this.format(localExtensions), + localExtensions, + remoteResource: this.remoteResource, + remoteContent: remoteExtensions ? this.format(remoteExtensions) : null, + previewResource: this.previewResource, + previewResult, + localChange: previewResult.localChange, + remoteChange: previewResult.remoteChange, + acceptedResource: this.acceptedResource, + }]; + } - const lastSyncUserData = await this.getLastSyncUserData(); - const remoteUserData = await this.getRemoteUserData(lastSyncUserData); + private getPreviewContent(localExtensions: ISyncExtension[], added: ISyncExtension[], updated: ISyncExtension[], removed: IExtensionIdentifier[]): string { + const preview: ISyncExtension[] = [...added, ...updated]; - if (remoteUserData.syncData !== null) { - const localExtensions = await this.getLocalExtensions(); - const remoteExtensions = this.parseExtensions(remoteUserData.syncData); - const { added, updated, remote, removed } = merge(localExtensions, remoteExtensions, localExtensions, [], this.getIgnoredExtensions()); - await this.apply({ - added, removed, updated, remote, remoteUserData, localExtensions, skippedExtensions: [], lastSyncUserData, - hasLocalChanged: added.length > 0 || removed.length > 0 || updated.length > 0, - hasRemoteChanged: remote !== null - }); + const idsOrUUIDs: Set = new Set(); + const addIdentifier = (identifier: IExtensionIdentifier) => { + idsOrUUIDs.add(identifier.id.toLowerCase()); + if (identifier.uuid) { + idsOrUUIDs.add(identifier.uuid); } + }; + preview.forEach(({ identifier }) => addIdentifier(identifier)); + removed.forEach(addIdentifier); - // No remote exists to pull - else { - this.logService.info(`${this.syncResourceLogLabel}: Remote extensions does not exist.`); + for (const localExtension of localExtensions) { + if (idsOrUUIDs.has(localExtension.identifier.id.toLowerCase()) || (localExtension.identifier.uuid && idsOrUUIDs.has(localExtension.identifier.uuid))) { + // skip + continue; } + preview.push(localExtension); + } - this.logService.info(`${this.syncResourceLogLabel}: Finished pulling extensions.`); - } finally { - this.setStatus(SyncStatus.Idle); + return this.format(preview); + } + + protected async getMergeResult(resourcePreview: IExtensionResourcePreview, token: CancellationToken): Promise { + return { ...resourcePreview.previewResult, hasConflicts: false }; + } + + protected async getAcceptResult(resourcePreview: IExtensionResourcePreview, resource: URI, content: string | null | undefined, token: CancellationToken): Promise { + + /* Accept local resource */ + if (isEqual(resource, this.localResource)) { + return this.acceptLocal(resourcePreview); + } + + /* Accept remote resource */ + if (isEqual(resource, this.remoteResource)) { + return this.acceptRemote(resourcePreview); + } + + /* Accept preview resource */ + if (isEqual(resource, this.previewResource)) { + return resourcePreview.previewResult; + } + + throw new Error(`Invalid Resource: ${resource.toString()}`); + } + + private async acceptLocal(resourcePreview: IExtensionResourcePreview): Promise { + const installedExtensions = await this.extensionManagementService.getInstalled(); + const ignoredExtensions = getIgnoredExtensions(installedExtensions, this.configurationService); + const mergeResult = merge(resourcePreview.localExtensions, null, null, resourcePreview.skippedExtensions, ignoredExtensions); + const { added, removed, updated, remote } = mergeResult; + return { + content: resourcePreview.localContent, + added, + removed, + updated, + remote, + localChange: added.length > 0 || removed.length > 0 || updated.length > 0 ? Change.Modified : Change.None, + remoteChange: remote !== null ? Change.Modified : Change.None, + }; + } + + private async acceptRemote(resourcePreview: IExtensionResourcePreview): Promise { + const installedExtensions = await this.extensionManagementService.getInstalled(); + const ignoredExtensions = getIgnoredExtensions(installedExtensions, this.configurationService); + const remoteExtensions = resourcePreview.remoteContent ? JSON.parse(resourcePreview.remoteContent) : null; + if (remoteExtensions !== null) { + const mergeResult = merge(resourcePreview.localExtensions, remoteExtensions, resourcePreview.localExtensions, [], ignoredExtensions); + const { added, removed, updated, remote } = mergeResult; + return { + content: resourcePreview.remoteContent, + added, + removed, + updated, + remote, + localChange: added.length > 0 || removed.length > 0 || updated.length > 0 ? Change.Modified : Change.None, + remoteChange: remote !== null ? Change.Modified : Change.None, + }; + } else { + return { + content: resourcePreview.remoteContent, + added: [], removed: [], updated: [], remote: null, + localChange: Change.None, + remoteChange: Change.None, + }; } } - async push(): Promise { - if (!this.isEnabled()) { - this.logService.info(`${this.syncResourceLogLabel}: Skipped pushing extensions as it is disabled.`); - return; + protected async applyResult(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, resourcePreviews: [IExtensionResourcePreview, IExtensionResourceMergeResult][], force: boolean): Promise { + let { skippedExtensions, localExtensions } = resourcePreviews[0][0]; + let { added, removed, updated, remote, localChange, remoteChange } = resourcePreviews[0][1]; + + if (localChange === Change.None && remoteChange === Change.None) { + this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing extensions.`); } - this.stop(); - - try { - this.logService.info(`${this.syncResourceLogLabel}: Started pushing extensions...`); - this.setStatus(SyncStatus.Syncing); - - const localExtensions = await this.getLocalExtensions(); - const { added, removed, updated, remote } = merge(localExtensions, null, null, [], this.getIgnoredExtensions()); - const lastSyncUserData = await this.getLastSyncUserData(); - const remoteUserData = await this.getRemoteUserData(lastSyncUserData); - await this.apply({ - added, removed, updated, remote, remoteUserData, localExtensions, skippedExtensions: [], lastSyncUserData, - hasLocalChanged: added.length > 0 || removed.length > 0 || updated.length > 0, - hasRemoteChanged: remote !== null - }, true); - - this.logService.info(`${this.syncResourceLogLabel}: Finished pushing extensions.`); - } finally { - this.setStatus(SyncStatus.Idle); + if (localChange !== Change.None) { + await this.backupLocal(JSON.stringify(localExtensions)); + skippedExtensions = await this.updateLocalExtensions(added, removed, updated, skippedExtensions); } + if (remote) { + // update remote + this.logService.trace(`${this.syncResourceLogLabel}: Updating remote extensions...`); + const content = JSON.stringify(remote); + remoteUserData = await this.updateRemoteUserData(content, force ? null : remoteUserData.ref); + this.logService.info(`${this.syncResourceLogLabel}: Updated remote extensions`); + } + + if (lastSyncUserData?.ref !== remoteUserData.ref) { + // update last sync + this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized extensions...`); + await this.updateLastSyncUserData(remoteUserData, { skippedExtensions }); + this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized extensions`); + } } - async stop(): Promise { } - async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> { return [{ resource: joinPath(uri, 'extensions.json'), comparableResource: ExtensionsSynchroniser.EXTENSIONS_DATA_URI }]; } async resolveContent(uri: URI): Promise { - if (isEqual(uri, ExtensionsSynchroniser.EXTENSIONS_DATA_URI)) { - const localExtensions = await this.getLocalExtensions(); - return this.format(localExtensions); + if (isEqual(this.remoteResource, uri) || isEqual(this.localResource, uri) || isEqual(this.acceptedResource, uri)) { + return this.resolvePreviewContent(uri); } let content = await super.resolveContent(uri); @@ -178,14 +282,11 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse return applyEdits(content, edits); } - async acceptConflict(conflict: URI, content: string): Promise { - throw new Error(`${this.syncResourceLogLabel}: Conflicts should not occur`); - } - async hasLocalData(): Promise { try { - const localExtensions = await this.getLocalExtensions(); - if (isNonEmptyArray(localExtensions)) { + const installedExtensions = await this.extensionManagementService.getInstalled(); + const localExtensions = this.getLocalExtensions(installedExtensions); + if (localExtensions.some(e => e.installed || e.disabled)) { return true; } } catch (error) { @@ -194,72 +295,6 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse return false; } - protected async performSync(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null): Promise { - const previewResult = await this.generatePreview(remoteUserData, lastSyncUserData); - await this.apply(previewResult); - return SyncStatus.Idle; - } - - protected async generatePreview(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null): Promise { - const remoteExtensions: ISyncExtension[] | null = remoteUserData.syncData ? this.parseExtensions(remoteUserData.syncData) : null; - const lastSyncExtensions: ISyncExtension[] | null = lastSyncUserData ? this.parseExtensions(lastSyncUserData.syncData!) : null; - const skippedExtensions: ISyncExtension[] = lastSyncUserData ? lastSyncUserData.skippedExtensions || [] : []; - - const localExtensions = await this.getLocalExtensions(); - - if (remoteExtensions) { - this.logService.trace(`${this.syncResourceLogLabel}: Merging remote extensions with local extensions...`); - } else { - this.logService.trace(`${this.syncResourceLogLabel}: Remote extensions does not exist. Synchronizing extensions for the first time.`); - } - - const { added, removed, updated, remote } = merge(localExtensions, remoteExtensions, lastSyncExtensions, skippedExtensions, this.getIgnoredExtensions()); - - return { - added, - removed, - updated, - remote, - skippedExtensions, - remoteUserData, - localExtensions, - lastSyncUserData, - hasLocalChanged: added.length > 0 || removed.length > 0 || updated.length > 0, - hasRemoteChanged: remote !== null - }; - } - - private getIgnoredExtensions() { - return this.configurationService.getValue('sync.ignoredExtensions') || []; - } - - private async apply({ added, removed, updated, remote, remoteUserData, skippedExtensions, lastSyncUserData, localExtensions, hasLocalChanged, hasRemoteChanged }: IExtensionsSyncPreviewResult, forcePush?: boolean): Promise { - - if (!hasLocalChanged && !hasRemoteChanged) { - this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing extensions.`); - } - - if (hasLocalChanged) { - await this.backupLocal(JSON.stringify(localExtensions)); - skippedExtensions = await this.updateLocalExtensions(added, removed, updated, skippedExtensions); - } - - if (remote) { - // update remote - this.logService.trace(`${this.syncResourceLogLabel}: Updating remote extensions...`); - const content = JSON.stringify(remote); - remoteUserData = await this.updateRemoteUserData(content, forcePush ? null : remoteUserData.ref); - this.logService.info(`${this.syncResourceLogLabel}: Updated remote extensions`); - } - - if (lastSyncUserData?.ref !== remoteUserData.ref) { - // update last sync - this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized extensions...`); - await this.updateLastSyncUserData(remoteUserData, { skippedExtensions }); - this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized extensions`); - } - } - private async updateLocalExtensions(added: ISyncExtension[], removed: IExtensionIdentifier[], updated: ISyncExtension[], skippedExtensions: ISyncExtension[]): Promise { const removeFromSkipped: IExtensionIdentifier[] = []; const addToSkipped: ISyncExtension[] = []; @@ -339,31 +374,49 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse return newSkippedExtensions; } - private parseExtensions(syncData: ISyncData): ISyncExtension[] { - let extensions: ISyncExtension[] = JSON.parse(syncData.content); - if (syncData.version !== this.version) { - extensions = extensions.map(e => { + private async parseAndMigrateExtensions(syncData: ISyncData): Promise { + const extensions = this.parseExtensions(syncData); + if (syncData.version === 1 + || syncData.version === 2 + ) { + const systemExtensions = await this.extensionManagementService.getInstalled(ExtensionType.System); + for (const extension of extensions) { // #region Migration from v1 (enabled -> disabled) - if (!(e).enabled) { - e.disabled = true; + if (syncData.version === 1) { + if ((extension).enabled === false) { + extension.disabled = true; + } + delete (extension).enabled; } - delete (e).enabled; // #endregion - return e; - }); + + // #region Migration from v2 (set installed property on extension) + if (syncData.version === 2) { + if (systemExtensions.every(installed => !areSameExtensions(installed.identifier, extension.identifier))) { + extension.installed = true; + } + } + // #endregion + } } return extensions; } - private async getLocalExtensions(): Promise { - const installedExtensions = await this.extensionManagementService.getInstalled(); + private parseExtensions(syncData: ISyncData): ISyncExtension[] { + return JSON.parse(syncData.content); + } + + private getLocalExtensions(installedExtensions: ILocalExtension[]): ISyncExtension[] { const disabledExtensions = this.extensionEnablementService.getDisabledExtensions(); return installedExtensions - .map(({ identifier }) => { + .map(({ identifier, type }) => { const syncExntesion: ISyncExtension = { identifier }; if (disabledExtensions.some(disabledExtension => areSameExtensions(disabledExtension, identifier))) { syncExntesion.disabled = true; } + if (type === ExtensionType.User) { + syncExntesion.installed = true; + } return syncExntesion; }); } diff --git a/src/vs/platform/userDataSync/common/globalStateMerge.ts b/src/vs/platform/userDataSync/common/globalStateMerge.ts index 4d4755fb245..8af4c4de7de 100644 --- a/src/vs/platform/userDataSync/common/globalStateMerge.ts +++ b/src/vs/platform/userDataSync/common/globalStateMerge.ts @@ -6,7 +6,6 @@ import * as objects from 'vs/base/common/objects'; import { IStorageValue } from 'vs/platform/userDataSync/common/userDataSync'; import { IStringDictionary } from 'vs/base/common/collections'; -import { values } from 'vs/base/common/map'; import { IStorageKey } from 'vs/platform/userDataSync/common/storageKeys'; import { ILogService } from 'vs/platform/log/common/log'; @@ -18,7 +17,7 @@ export interface IMergeResult { export function merge(localStorage: IStringDictionary, remoteStorage: IStringDictionary | null, baseStorage: IStringDictionary | null, storageKeys: ReadonlyArray, previouslySkipped: string[], logService: ILogService): IMergeResult { if (!remoteStorage) { - return { remote: localStorage, local: { added: {}, removed: [], updated: {} }, skipped: [] }; + return { remote: Object.keys(localStorage).length > 0 ? localStorage : null, local: { added: {}, removed: [], updated: {} }, skipped: [] }; } const localToRemote = compare(localStorage, remoteStorage); @@ -35,12 +34,12 @@ export function merge(localStorage: IStringDictionary, remoteStor const skipped: string[] = []; // Added in remote - for (const key of values(baseToRemote.added)) { + for (const key of baseToRemote.added.values()) { const remoteValue = remoteStorage[key]; const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0]; if (!storageKey) { skipped.push(key); - logService.info(`GlobalState: Skipped adding ${key} in local storage as it is not registered.`); + logService.trace(`GlobalState: Skipped adding ${key} in local storage as it is not registered.`); continue; } if (storageKey.version !== remoteValue.version) { @@ -59,12 +58,12 @@ export function merge(localStorage: IStringDictionary, remoteStor } // Updated in Remote - for (const key of values(baseToRemote.updated)) { + for (const key of baseToRemote.updated.values()) { const remoteValue = remoteStorage[key]; const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0]; if (!storageKey) { skipped.push(key); - logService.info(`GlobalState: Skipped updating ${key} in local storage as is not registered.`); + logService.trace(`GlobalState: Skipped updating ${key} in local storage as is not registered.`); continue; } if (storageKey.version !== remoteValue.version) { @@ -79,24 +78,24 @@ export function merge(localStorage: IStringDictionary, remoteStor } // Removed in remote - for (const key of values(baseToRemote.removed)) { + for (const key of baseToRemote.removed.values()) { const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0]; if (!storageKey) { - logService.info(`GlobalState: Skipped removing ${key} in local storage. It is not registered to sync.`); + logService.trace(`GlobalState: Skipped removing ${key} in local storage. It is not registered to sync.`); continue; } local.removed.push(key); } // Added in local - for (const key of values(baseToLocal.added)) { + for (const key of baseToLocal.added.values()) { if (!baseToRemote.added.has(key)) { remote[key] = localStorage[key]; } } // Updated in local - for (const key of values(baseToLocal.updated)) { + for (const key of baseToLocal.updated.values()) { if (baseToRemote.updated.has(key) || baseToRemote.removed.has(key)) { continue; } @@ -110,7 +109,7 @@ export function merge(localStorage: IStringDictionary, remoteStor } // Removed in local - for (const key of values(baseToLocal.removed)) { + for (const key of baseToLocal.removed.values()) { // do not remove from remote if it is updated in remote if (baseToRemote.updated.has(key)) { continue; @@ -120,7 +119,7 @@ export function merge(localStorage: IStringDictionary, remoteStor // do not remove from remote if storage key is not found if (!storageKey) { skipped.push(key); - logService.info(`GlobalState: Skipped removing ${key} in remote storage. It is not registered to sync.`); + logService.trace(`GlobalState: Skipped removing ${key} in remote storage. It is not registered to sync.`); continue; } diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index ca00b220b5b..df914b99f3d 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -3,7 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncResource, IUserDataSynchroniser, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, ISyncResourceHandle, IStorageValue, ISyncPreviewResult, USER_DATA_SYNC_SCHEME } from 'vs/platform/userDataSync/common/userDataSync'; +import { + IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncResource, IUserDataSynchroniser, IUserDataSyncResourceEnablementService, + IUserDataSyncBackupStoreService, ISyncResourceHandle, IStorageValue, USER_DATA_SYNC_SCHEME, IRemoteUserData, Change +} from 'vs/platform/userDataSync/common/userDataSync'; import { VSBuffer } from 'vs/base/common/buffer'; import { Event } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -13,7 +16,7 @@ import { IStringDictionary } from 'vs/base/common/collections'; import { edit } from 'vs/platform/userDataSync/common/content'; import { merge } from 'vs/platform/userDataSync/common/globalStateMerge'; import { parse } from 'vs/base/common/json'; -import { AbstractSynchroniser, IRemoteUserData } from 'vs/platform/userDataSync/common/abstractSynchronizer'; +import { AbstractSynchroniser, IAcceptResult, IMergeResult, IResourcePreview } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { URI } from 'vs/base/common/uri'; @@ -22,17 +25,20 @@ import { applyEdits } from 'vs/base/common/jsonEdit'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IStorageKeysSyncRegistryService, IStorageKey } from 'vs/platform/userDataSync/common/storageKeys'; import { equals } from 'vs/base/common/arrays'; +import { CancellationToken } from 'vs/base/common/cancellation'; const argvStoragePrefx = 'globalState.argv.'; const argvProperties: string[] = ['locale']; -interface IGlobalSyncPreviewResult extends ISyncPreviewResult { +interface IGlobalStateResourceMergeResult extends IAcceptResult { readonly local: { added: IStringDictionary, removed: string[], updated: IStringDictionary }; readonly remote: IStringDictionary | null; +} + +export interface IGlobalStateResourcePreview extends IResourcePreview { readonly skippedStorageKeys: string[]; readonly localUserData: IGlobalState; - readonly remoteUserData: IRemoteUserData; - readonly lastSyncUserData: ILastSyncUserData | null; + readonly previewResult: IGlobalStateResourceMergeResult; } interface ILastSyncUserData extends IRemoteUserData { @@ -41,22 +47,26 @@ interface ILastSyncUserData extends IRemoteUserData { export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUserDataSynchroniser { - private static readonly GLOBAL_STATE_DATA_URI = URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'globalState', path: `/current.json` }); + private static readonly GLOBAL_STATE_DATA_URI = URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'globalState', path: `/globalState.json` }); protected readonly version: number = 1; + private readonly previewResource: URI = joinPath(this.syncPreviewFolder, 'globalState.json'); + private readonly localResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }); + private readonly remoteResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }); + private readonly acceptedResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }); constructor( @IFileService fileService: IFileService, @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, @IUserDataSyncBackupStoreService userDataSyncBackupStoreService: IUserDataSyncBackupStoreService, @IUserDataSyncLogService logService: IUserDataSyncLogService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, - @IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService, + @IEnvironmentService readonly environmentService: IEnvironmentService, + @IUserDataSyncResourceEnablementService userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, @ITelemetryService telemetryService: ITelemetryService, @IConfigurationService configurationService: IConfigurationService, @IStorageService private readonly storageService: IStorageService, @IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, ) { - super(SyncResource.GlobalState, fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService); + super(SyncResource.GlobalState, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncResourceEnablementService, telemetryService, logService, configurationService); this._register(this.fileService.watch(dirname(this.environmentService.argvResource))); this._register( Event.any( @@ -66,87 +76,141 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs Event.filter(this.storageService.onDidChangeStorage, e => storageKeysSyncRegistryService.storageKeys.some(({ key }) => e.key === key)), /* Storage key registered */ this.storageKeysSyncRegistryService.onDidChangeStorageKeys - )((() => this._onDidChangeLocal.fire())) + )((() => this.triggerLocalChange())) ); } - async pull(): Promise { - if (!this.isEnabled()) { - this.logService.info(`${this.syncResourceLogLabel}: Skipped pulling ui state as it is disabled.`); - return; + protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null, token: CancellationToken): Promise { + const remoteGlobalState: IGlobalState = remoteUserData.syncData ? JSON.parse(remoteUserData.syncData.content) : null; + const lastSyncGlobalState: IGlobalState | null = lastSyncUserData && lastSyncUserData.syncData ? JSON.parse(lastSyncUserData.syncData.content) : null; + + const localGloablState = await this.getLocalGlobalState(); + + if (remoteGlobalState) { + this.logService.trace(`${this.syncResourceLogLabel}: Merging remote ui state with local ui state...`); + } else { + this.logService.trace(`${this.syncResourceLogLabel}: Remote ui state does not exist. Synchronizing ui state for the first time.`); } - this.stop(); + const { local, remote, skipped } = merge(localGloablState.storage, remoteGlobalState ? remoteGlobalState.storage : null, lastSyncGlobalState ? lastSyncGlobalState.storage : null, this.getSyncStorageKeys(), lastSyncUserData?.skippedStorageKeys || [], this.logService); + const previewResult: IGlobalStateResourceMergeResult = { + content: null, + local, + remote, + localChange: Object.keys(local.added).length > 0 || Object.keys(local.updated).length > 0 || local.removed.length > 0 ? Change.Modified : Change.None, + remoteChange: remote !== null ? Change.Modified : Change.None, + }; - try { - this.logService.info(`${this.syncResourceLogLabel}: Started pulling ui state...`); - this.setStatus(SyncStatus.Syncing); + return [{ + skippedStorageKeys: skipped, + localResource: this.localResource, + localContent: this.format(localGloablState), + localUserData: localGloablState, + remoteResource: this.remoteResource, + remoteContent: remoteGlobalState ? this.format(remoteGlobalState) : null, + previewResource: this.previewResource, + previewResult, + localChange: previewResult.localChange, + remoteChange: previewResult.remoteChange, + acceptedResource: this.acceptedResource, + }]; + } - const lastSyncUserData = await this.getLastSyncUserData(); - const remoteUserData = await this.getRemoteUserData(lastSyncUserData); + protected async getMergeResult(resourcePreview: IGlobalStateResourcePreview, token: CancellationToken): Promise { + return { ...resourcePreview.previewResult, hasConflicts: false }; + } - if (remoteUserData.syncData !== null) { - const localGlobalState = await this.getLocalGlobalState(); - const remoteGlobalState: IGlobalState = JSON.parse(remoteUserData.syncData.content); - const { local, remote, skipped } = merge(localGlobalState.storage, remoteGlobalState.storage, null, this.getSyncStorageKeys(), lastSyncUserData?.skippedStorageKeys || [], this.logService); - await this.apply({ - local, remote, remoteUserData, localUserData: localGlobalState, lastSyncUserData, - skippedStorageKeys: skipped, - hasLocalChanged: Object.keys(local.added).length > 0 || Object.keys(local.updated).length > 0 || local.removed.length > 0, - hasRemoteChanged: remote !== null - }); - } + protected async getAcceptResult(resourcePreview: IGlobalStateResourcePreview, resource: URI, content: string | null | undefined, token: CancellationToken): Promise { - // No remote exists to pull - else { - this.logService.info(`${this.syncResourceLogLabel}: Remote UI state does not exist.`); - } + /* Accept local resource */ + if (isEqual(resource, this.localResource)) { + return this.acceptLocal(resourcePreview); + } - this.logService.info(`${this.syncResourceLogLabel}: Finished pulling UI state.`); - } finally { - this.setStatus(SyncStatus.Idle); + /* Accept remote resource */ + if (isEqual(resource, this.remoteResource)) { + return this.acceptRemote(resourcePreview); + } + + /* Accept preview resource */ + if (isEqual(resource, this.previewResource)) { + return resourcePreview.previewResult; + } + + throw new Error(`Invalid Resource: ${resource.toString()}`); + } + + private async acceptLocal(resourcePreview: IGlobalStateResourcePreview): Promise { + return { + content: resourcePreview.localContent, + local: { added: {}, removed: [], updated: {} }, + remote: resourcePreview.localUserData.storage, + localChange: Change.None, + remoteChange: Change.Modified, + }; + } + + private async acceptRemote(resourcePreview: IGlobalStateResourcePreview): Promise { + if (resourcePreview.remoteContent !== null) { + const remoteGlobalState: IGlobalState = JSON.parse(resourcePreview.remoteContent); + const { local, remote } = merge(resourcePreview.localUserData.storage, remoteGlobalState.storage, null, this.getSyncStorageKeys(), resourcePreview.skippedStorageKeys, this.logService); + return { + content: resourcePreview.remoteContent, + local, + remote, + localChange: Object.keys(local.added).length > 0 || Object.keys(local.updated).length > 0 || local.removed.length > 0 ? Change.Modified : Change.None, + remoteChange: remote !== null ? Change.Modified : Change.None, + }; + } else { + return { + content: resourcePreview.remoteContent, + local: { added: {}, removed: [], updated: {} }, + remote: null, + localChange: Change.None, + remoteChange: Change.None, + }; } } - async push(): Promise { - if (!this.isEnabled()) { - this.logService.info(`${this.syncResourceLogLabel}: Skipped pushing UI State as it is disabled.`); - return; + protected async applyResult(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null, resourcePreviews: [IGlobalStateResourcePreview, IGlobalStateResourceMergeResult][], force: boolean): Promise { + let { localUserData, skippedStorageKeys } = resourcePreviews[0][0]; + let { local, remote, localChange, remoteChange } = resourcePreviews[0][1]; + + if (localChange === Change.None && remoteChange === Change.None) { + this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing ui state.`); } - this.stop(); - - try { - this.logService.info(`${this.syncResourceLogLabel}: Started pushing UI State...`); - this.setStatus(SyncStatus.Syncing); - - const localUserData = await this.getLocalGlobalState(); - const lastSyncUserData = await this.getLastSyncUserData(); - const remoteUserData = await this.getRemoteUserData(lastSyncUserData); - await this.apply({ - local: { added: {}, removed: [], updated: {} }, remote: localUserData.storage, remoteUserData, localUserData, lastSyncUserData, - skippedStorageKeys: [], - hasLocalChanged: false, - hasRemoteChanged: true - }, true); - - this.logService.info(`${this.syncResourceLogLabel}: Finished pushing UI State.`); - } finally { - this.setStatus(SyncStatus.Idle); + if (localChange !== Change.None) { + // update local + this.logService.trace(`${this.syncResourceLogLabel}: Updating local ui state...`); + await this.backupLocal(JSON.stringify(localUserData)); + await this.writeLocalGlobalState(local); + this.logService.info(`${this.syncResourceLogLabel}: Updated local ui state`); } + if (remoteChange !== Change.None) { + // update remote + this.logService.trace(`${this.syncResourceLogLabel}: Updating remote ui state...`); + const content = JSON.stringify({ storage: remote }); + remoteUserData = await this.updateRemoteUserData(content, force ? null : remoteUserData.ref); + this.logService.info(`${this.syncResourceLogLabel}: Updated remote ui state`); + } + + if (lastSyncUserData?.ref !== remoteUserData.ref || !equals(lastSyncUserData.skippedStorageKeys, skippedStorageKeys)) { + // update last sync + this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized ui state...`); + await this.updateLastSyncUserData(remoteUserData, { skippedStorageKeys }); + this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized ui state`); + } } - async stop(): Promise { } - async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> { return [{ resource: joinPath(uri, 'globalState.json'), comparableResource: GlobalStateSynchroniser.GLOBAL_STATE_DATA_URI }]; } async resolveContent(uri: URI): Promise { - if (isEqual(uri, GlobalStateSynchroniser.GLOBAL_STATE_DATA_URI)) { - const localGlobalState = await this.getLocalGlobalState(); - return this.format(localGlobalState); + if (isEqual(this.remoteResource, uri) || isEqual(this.localResource, uri) || isEqual(this.acceptedResource, uri)) { + return this.resolvePreviewContent(uri); } let content = await super.resolveContent(uri); @@ -178,10 +242,6 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs return applyEdits(content, edits); } - async acceptConflict(conflict: URI, content: string): Promise { - throw new Error(`${this.syncResourceLogLabel}: Conflicts should not occur`); - } - async hasLocalData(): Promise { try { const { storage } = await this.getLocalGlobalState(); @@ -194,64 +254,6 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs return false; } - protected async performSync(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null): Promise { - const result = await this.generatePreview(remoteUserData, lastSyncUserData); - await this.apply(result); - return SyncStatus.Idle; - } - - protected async generatePreview(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null): Promise { - const remoteGlobalState: IGlobalState = remoteUserData.syncData ? JSON.parse(remoteUserData.syncData.content) : null; - const lastSyncGlobalState: IGlobalState = lastSyncUserData && lastSyncUserData.syncData ? JSON.parse(lastSyncUserData.syncData.content) : null; - - const localGloablState = await this.getLocalGlobalState(); - - if (remoteGlobalState) { - this.logService.trace(`${this.syncResourceLogLabel}: Merging remote ui state with local ui state...`); - } else { - this.logService.trace(`${this.syncResourceLogLabel}: Remote ui state does not exist. Synchronizing ui state for the first time.`); - } - - const { local, remote, skipped } = merge(localGloablState.storage, remoteGlobalState ? remoteGlobalState.storage : null, lastSyncGlobalState ? lastSyncGlobalState.storage : null, this.getSyncStorageKeys(), lastSyncUserData?.skippedStorageKeys || [], this.logService); - - return { - local, remote, remoteUserData, localUserData: localGloablState, lastSyncUserData, - skippedStorageKeys: skipped, - hasLocalChanged: Object.keys(local.added).length > 0 || Object.keys(local.updated).length > 0 || local.removed.length > 0, - hasRemoteChanged: remote !== null - }; - } - - private async apply({ local, remote, remoteUserData, lastSyncUserData, localUserData, hasLocalChanged, hasRemoteChanged, skippedStorageKeys }: IGlobalSyncPreviewResult, forcePush?: boolean): Promise { - - if (!hasLocalChanged && !hasRemoteChanged) { - this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing ui state.`); - } - - if (hasLocalChanged) { - // update local - this.logService.trace(`${this.syncResourceLogLabel}: Updating local ui state...`); - await this.backupLocal(JSON.stringify(localUserData)); - await this.writeLocalGlobalState(local); - this.logService.info(`${this.syncResourceLogLabel}: Updated local ui state`); - } - - if (hasRemoteChanged) { - // update remote - this.logService.trace(`${this.syncResourceLogLabel}: Updating remote ui state...`); - const content = JSON.stringify({ storage: remote }); - remoteUserData = await this.updateRemoteUserData(content, forcePush ? null : remoteUserData.ref); - this.logService.info(`${this.syncResourceLogLabel}: Updated remote ui state`); - } - - if (lastSyncUserData?.ref !== remoteUserData.ref || !equals(lastSyncUserData.skippedStorageKeys, skippedStorageKeys)) { - // update last sync - this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized ui state...`); - await this.updateLastSyncUserData(remoteUserData, { skippedStorageKeys }); - this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized ui state`); - } - } - private async getLocalGlobalState(): Promise { const storage: IStringDictionary = {}; const argvContent: string = await this.getLocalArgvContent(); diff --git a/src/vs/platform/userDataSync/common/keybindingsMerge.ts b/src/vs/platform/userDataSync/common/keybindingsMerge.ts index 4416ceb235c..3f994050bc7 100644 --- a/src/vs/platform/userDataSync/common/keybindingsMerge.ts +++ b/src/vs/platform/userDataSync/common/keybindingsMerge.ts @@ -5,7 +5,6 @@ import * as objects from 'vs/base/common/objects'; import { parse } from 'vs/base/common/json'; -import { values, keys } from 'vs/base/common/map'; import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; import { firstIndex as findFirstIndex, equals } from 'vs/base/common/arrays'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -57,14 +56,14 @@ export async function merge(localContent: string, remoteContent: string, baseCon const remoteByCommand = byCommand(remote); const baseByCommand = base ? byCommand(base) : null; const localToRemoteByCommand = compareByCommand(localByCommand, remoteByCommand, normalizedKeys); - const baseToLocalByCommand = baseByCommand ? compareByCommand(baseByCommand, localByCommand, normalizedKeys) : { added: keys(localByCommand).reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; - const baseToRemoteByCommand = baseByCommand ? compareByCommand(baseByCommand, remoteByCommand, normalizedKeys) : { added: keys(remoteByCommand).reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; + const baseToLocalByCommand = baseByCommand ? compareByCommand(baseByCommand, localByCommand, normalizedKeys) : { added: [...localByCommand.keys()].reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; + const baseToRemoteByCommand = baseByCommand ? compareByCommand(baseByCommand, remoteByCommand, normalizedKeys) : { added: [...remoteByCommand.keys()].reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; const commandsMergeResult = computeMergeResult(localToRemoteByCommand, baseToLocalByCommand, baseToRemoteByCommand); let mergeContent = localContent; // Removed commands in Remote - for (const command of values(commandsMergeResult.removed)) { + for (const command of commandsMergeResult.removed.values()) { if (commandsMergeResult.conflicts.has(command)) { continue; } @@ -72,7 +71,7 @@ export async function merge(localContent: string, remoteContent: string, baseCon } // Added commands in remote - for (const command of values(commandsMergeResult.added)) { + for (const command of commandsMergeResult.added.values()) { if (commandsMergeResult.conflicts.has(command)) { continue; } @@ -86,7 +85,7 @@ export async function merge(localContent: string, remoteContent: string, baseCon } // Updated commands in Remote - for (const command of values(commandsMergeResult.updated)) { + for (const command of commandsMergeResult.updated.values()) { if (commandsMergeResult.conflicts.has(command)) { continue; } @@ -109,7 +108,7 @@ function computeMergeResult(localToRemote: ICompareResult, baseToLocal: ICompare const conflicts: Set = new Set(); // Removed keys in Local - for (const key of values(baseToLocal.removed)) { + for (const key of baseToLocal.removed.values()) { // Got updated in remote if (baseToRemote.updated.has(key)) { conflicts.add(key); @@ -117,7 +116,7 @@ function computeMergeResult(localToRemote: ICompareResult, baseToLocal: ICompare } // Removed keys in Remote - for (const key of values(baseToRemote.removed)) { + for (const key of baseToRemote.removed.values()) { if (conflicts.has(key)) { continue; } @@ -131,7 +130,7 @@ function computeMergeResult(localToRemote: ICompareResult, baseToLocal: ICompare } // Added keys in Local - for (const key of values(baseToLocal.added)) { + for (const key of baseToLocal.added.values()) { if (conflicts.has(key)) { continue; } @@ -145,7 +144,7 @@ function computeMergeResult(localToRemote: ICompareResult, baseToLocal: ICompare } // Added keys in remote - for (const key of values(baseToRemote.added)) { + for (const key of baseToRemote.added.values()) { if (conflicts.has(key)) { continue; } @@ -161,7 +160,7 @@ function computeMergeResult(localToRemote: ICompareResult, baseToLocal: ICompare } // Updated keys in Local - for (const key of values(baseToLocal.updated)) { + for (const key of baseToLocal.updated.values()) { if (conflicts.has(key)) { continue; } @@ -175,7 +174,7 @@ function computeMergeResult(localToRemote: ICompareResult, baseToLocal: ICompare } // Updated keys in Remote - for (const key of values(baseToRemote.updated)) { + for (const key of baseToRemote.updated.values()) { if (conflicts.has(key)) { continue; } @@ -204,13 +203,13 @@ function computeMergeResultByKeybinding(local: IUserFriendlyKeybinding[], remote return { hasLocalForwarded: false, hasRemoteForwarded: false, added: empty, removed: empty, updated: empty, conflicts: empty }; } - const baseToLocalByKeybinding = baseByKeybinding ? compareByKeybinding(baseByKeybinding, localByKeybinding) : { added: keys(localByKeybinding).reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; + const baseToLocalByKeybinding = baseByKeybinding ? compareByKeybinding(baseByKeybinding, localByKeybinding) : { added: [...localByKeybinding.keys()].reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; if (baseToLocalByKeybinding.added.size === 0 && baseToLocalByKeybinding.removed.size === 0 && baseToLocalByKeybinding.updated.size === 0) { // Remote has moved forward and local has not. return { hasLocalForwarded: false, hasRemoteForwarded: true, added: empty, removed: empty, updated: empty, conflicts: empty }; } - const baseToRemoteByKeybinding = baseByKeybinding ? compareByKeybinding(baseByKeybinding, remoteByKeybinding) : { added: keys(remoteByKeybinding).reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; + const baseToRemoteByKeybinding = baseByKeybinding ? compareByKeybinding(baseByKeybinding, remoteByKeybinding) : { added: [...remoteByKeybinding.keys()].reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; if (baseToRemoteByKeybinding.added.size === 0 && baseToRemoteByKeybinding.removed.size === 0 && baseToRemoteByKeybinding.updated.size === 0) { return { hasLocalForwarded: true, hasRemoteForwarded: false, added: empty, removed: empty, updated: empty, conflicts: empty }; } @@ -250,8 +249,8 @@ function byCommand(keybindings: IUserFriendlyKeybinding[]): Map, to: Map): ICompareResult { - const fromKeys = keys(from); - const toKeys = keys(to); + const fromKeys = [...from.keys()]; + const toKeys = [...to.keys()]; const added = toKeys.filter(key => fromKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set()); const removed = fromKeys.filter(key => toKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set()); const updated: Set = new Set(); @@ -271,8 +270,8 @@ function compareByKeybinding(from: Map, to: M } function compareByCommand(from: Map, to: Map, normalizedKeys: IStringDictionary): ICompareResult { - const fromKeys = keys(from); - const toKeys = keys(to); + const fromKeys = [...from.keys()]; + const toKeys = [...to.keys()]; const added = toKeys.filter(key => fromKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set()); const removed = fromKeys.filter(key => toKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set()); const updated: Set = new Set(); diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts index ed6943af053..4aa6c402f65 100644 --- a/src/vs/platform/userDataSync/common/keybindingsSync.ts +++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts @@ -4,22 +4,25 @@ *--------------------------------------------------------------------------------------------*/ import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; -import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, SyncResource, IUserDataSynchroniser, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, USER_DATA_SYNC_SCHEME, PREVIEW_DIR_NAME, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync'; +import { + UserDataSyncError, UserDataSyncErrorCode, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, SyncResource, + IUserDataSynchroniser, IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, USER_DATA_SYNC_SCHEME, ISyncResourceHandle, + IRemoteUserData, Change +} from 'vs/platform/userDataSync/common/userDataSync'; import { merge } from 'vs/platform/userDataSync/common/keybindingsMerge'; -import { VSBuffer } from 'vs/base/common/buffer'; import { parse } from 'vs/base/common/json'; import { localize } from 'vs/nls'; -import { createCancelablePromise } from 'vs/base/common/async'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CancellationToken } from 'vs/base/common/cancellation'; import { OS, OperatingSystem } from 'vs/base/common/platform'; import { isUndefined } from 'vs/base/common/types'; import { isNonEmptyArray } from 'vs/base/common/arrays'; -import { IFileSyncPreviewResult, AbstractJsonFileSynchroniser, IRemoteUserData } from 'vs/platform/userDataSync/common/abstractSynchronizer'; +import { AbstractJsonFileSynchroniser, IAcceptResult, IFileResourcePreview, IMergeResult } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { URI } from 'vs/base/common/uri'; import { joinPath, isEqual, dirname, basename } from 'vs/base/common/resources'; +import { IStorageService } from 'vs/platform/storage/common/storage'; interface ISyncContent { mac?: string; @@ -28,121 +31,192 @@ interface ISyncContent { all?: string; } +interface IKeybindingsResourcePreview extends IFileResourcePreview { + previewResult: IMergeResult; +} + export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implements IUserDataSynchroniser { protected readonly version: number = 1; - protected readonly localPreviewResource: URI = joinPath(this.syncFolder, PREVIEW_DIR_NAME, 'keybindings.json'); - protected readonly remotePreviewResource: URI = this.localPreviewResource.with({ scheme: USER_DATA_SYNC_SCHEME }); + private readonly previewResource: URI = joinPath(this.syncPreviewFolder, 'keybindings.json'); + private readonly localResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }); + private readonly remoteResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }); + private readonly acceptedResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }); constructor( @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, @IUserDataSyncBackupStoreService userDataSyncBackupStoreService: IUserDataSyncBackupStoreService, @IUserDataSyncLogService logService: IUserDataSyncLogService, @IConfigurationService configurationService: IConfigurationService, - @IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService, + @IUserDataSyncResourceEnablementService userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, @IFileService fileService: IFileService, @IEnvironmentService environmentService: IEnvironmentService, + @IStorageService storageService: IStorageService, @IUserDataSyncUtilService userDataSyncUtilService: IUserDataSyncUtilService, @ITelemetryService telemetryService: ITelemetryService, ) { - super(environmentService.keybindingsResource, SyncResource.Keybindings, fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService); + super(environmentService.keybindingsResource, SyncResource.Keybindings, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncResourceEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService); } - async pull(): Promise { - if (!this.isEnabled()) { - this.logService.info(`${this.syncResourceLogLabel}: Skipped pulling keybindings as it is disabled.`); - return; + protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise { + const remoteContent = remoteUserData.syncData ? this.getKeybindingsContentFromSyncContent(remoteUserData.syncData.content) : null; + const lastSyncContent: string | null = lastSyncUserData && lastSyncUserData.syncData ? this.getKeybindingsContentFromSyncContent(lastSyncUserData.syncData.content) : null; + + // Get file content last to get the latest + const fileContent = await this.getLocalFileContent(); + const formattingOptions = await this.getFormattingOptions(); + + let mergedContent: string | null = null; + let hasLocalChanged: boolean = false; + let hasRemoteChanged: boolean = false; + let hasConflicts: boolean = false; + + if (remoteContent) { + const localContent: string = fileContent ? fileContent.value.toString() : '[]'; + if (!localContent.trim() || this.hasErrors(localContent)) { + throw new UserDataSyncError(localize('errorInvalidSettings', "Unable to sync keybindings because the content in the file is not valid. Please open the file and correct it."), UserDataSyncErrorCode.LocalInvalidContent, this.resource); + } + + if (!lastSyncContent // First time sync + || lastSyncContent !== localContent // Local has forwarded + || lastSyncContent !== remoteContent // Remote has forwarded + ) { + this.logService.trace(`${this.syncResourceLogLabel}: Merging remote keybindings with local keybindings...`); + const result = await merge(localContent, remoteContent, lastSyncContent, formattingOptions, this.userDataSyncUtilService); + // Sync only if there are changes + if (result.hasChanges) { + mergedContent = result.mergeContent; + hasConflicts = result.hasConflicts; + hasLocalChanged = hasConflicts || result.mergeContent !== localContent; + hasRemoteChanged = hasConflicts || result.mergeContent !== remoteContent; + } + } } - this.stop(); + // First time syncing to remote + else if (fileContent) { + this.logService.trace(`${this.syncResourceLogLabel}: Remote keybindings does not exist. Synchronizing keybindings for the first time.`); + mergedContent = fileContent.value.toString(); + hasRemoteChanged = true; + } - try { - this.logService.info(`${this.syncResourceLogLabel}: Started pulling keybindings...`); - this.setStatus(SyncStatus.Syncing); + const previewResult: IMergeResult = { + content: mergedContent, + localChange: hasLocalChanged ? fileContent ? Change.Modified : Change.Added : Change.None, + remoteChange: hasRemoteChanged ? Change.Modified : Change.None, + hasConflicts + }; - const lastSyncUserData = await this.getLastSyncUserData(); - const remoteUserData = await this.getRemoteUserData(lastSyncUserData); - const content = remoteUserData.syncData !== null ? this.getKeybindingsContentFromSyncContent(remoteUserData.syncData.content) : null; + return [{ + fileContent, + localResource: this.localResource, + localContent: fileContent ? fileContent.value.toString() : null, + localChange: previewResult.localChange, - if (content !== null) { - const fileContent = await this.getLocalFileContent(); - this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve({ - fileContent, - remoteUserData, - lastSyncUserData, + remoteResource: this.remoteResource, + remoteContent, + remoteChange: previewResult.remoteChange, + + previewResource: this.previewResource, + previewResult, + acceptedResource: this.acceptedResource, + }]; + + } + + protected async getMergeResult(resourcePreview: IKeybindingsResourcePreview, token: CancellationToken): Promise { + return resourcePreview.previewResult; + } + + protected async getAcceptResult(resourcePreview: IKeybindingsResourcePreview, resource: URI, content: string | null | undefined, token: CancellationToken): Promise { + + /* Accept local resource */ + if (isEqual(resource, this.localResource)) { + return { + content: resourcePreview.fileContent ? resourcePreview.fileContent.value.toString() : null, + localChange: Change.None, + remoteChange: Change.Modified, + }; + } + + /* Accept remote resource */ + if (isEqual(resource, this.remoteResource)) { + return { + content: resourcePreview.remoteContent !== null ? this.getKeybindingsContentFromSyncContent(resourcePreview.remoteContent) : null, + localChange: Change.Modified, + remoteChange: Change.None, + }; + } + + /* Accept preview resource */ + if (isEqual(resource, this.previewResource)) { + if (content === undefined) { + return { + content: resourcePreview.previewResult.content, + localChange: resourcePreview.previewResult.localChange, + remoteChange: resourcePreview.previewResult.remoteChange, + }; + } else { + return { content, - hasConflicts: false, - hasLocalChanged: true, - hasRemoteChanged: false, - })); - await this.apply(); + localChange: Change.Modified, + remoteChange: Change.Modified, + }; } - - // No remote exists to pull - else { - this.logService.info(`${this.syncResourceLogLabel}: Remote keybindings does not exist.`); - } - - this.logService.info(`${this.syncResourceLogLabel}: Finished pulling keybindings.`); - } finally { - this.setStatus(SyncStatus.Idle); } + throw new Error(`Invalid Resource: ${resource.toString()}`); } - async push(): Promise { - if (!this.isEnabled()) { - this.logService.info(`${this.syncResourceLogLabel}: Skipped pushing keybindings as it is disabled.`); - return; + protected async applyResult(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, resourcePreviews: [IKeybindingsResourcePreview, IAcceptResult][], force: boolean): Promise { + const { fileContent } = resourcePreviews[0][0]; + let { content, localChange, remoteChange } = resourcePreviews[0][1]; + + if (localChange === Change.None && remoteChange === Change.None) { + this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing keybindings.`); } - this.stop(); + if (content !== null && this.hasErrors(content)) { + throw new UserDataSyncError(localize('errorInvalidSettings', "Unable to sync keybindings because the content in the file is not valid. Please open the file and correct it."), UserDataSyncErrorCode.LocalInvalidContent, this.resource); + } + if (localChange !== Change.None) { + this.logService.trace(`${this.syncResourceLogLabel}: Updating local keybindings...`); + if (fileContent) { + await this.backupLocal(this.toSyncContent(fileContent.value.toString(), null)); + } + await this.updateLocalFileContent(content || '[]', fileContent, force); + this.logService.info(`${this.syncResourceLogLabel}: Updated local keybindings`); + } + + if (remoteChange !== Change.None) { + this.logService.trace(`${this.syncResourceLogLabel}: Updating remote keybindings...`); + const remoteContents = this.toSyncContent(content || '[]', remoteUserData.syncData ? remoteUserData.syncData.content : null); + remoteUserData = await this.updateRemoteUserData(remoteContents, force ? null : remoteUserData.ref); + this.logService.info(`${this.syncResourceLogLabel}: Updated remote keybindings`); + } + + // Delete the preview try { - this.logService.info(`${this.syncResourceLogLabel}: Started pushing keybindings...`); - this.setStatus(SyncStatus.Syncing); + await this.fileService.del(this.previewResource); + } catch (e) { /* ignore */ } - const fileContent = await this.getLocalFileContent(); - - if (fileContent !== null) { - const lastSyncUserData = await this.getLastSyncUserData(); - const remoteUserData = await this.getRemoteUserData(lastSyncUserData); - this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve({ - fileContent, - remoteUserData, - lastSyncUserData, - content: fileContent.value.toString(), - hasLocalChanged: false, - hasRemoteChanged: true, - hasConflicts: false, - })); - await this.apply(true); - } - - // No local exists to push - else { - this.logService.info(`${this.syncResourceLogLabel}: Local keybindings does not exist.`); - } - - this.logService.info(`${this.syncResourceLogLabel}: Finished pushing keybindings.`); - } finally { - this.setStatus(SyncStatus.Idle); + if (lastSyncUserData?.ref !== remoteUserData.ref) { + this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized keybindings...`); + const lastSyncContent = content !== null ? this.toSyncContent(content, null) : null; + await this.updateLastSyncUserData({ + ref: remoteUserData.ref, + syncData: lastSyncContent ? { + version: remoteUserData.syncData ? remoteUserData.syncData.version : this.version, + machineId: remoteUserData.syncData!.machineId, + content: lastSyncContent + } : null + }); + this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized keybindings`); } } - async acceptConflict(conflict: URI, content: string): Promise { - if (this.status === SyncStatus.HasConflicts - && (isEqual(this.localPreviewResource, conflict) || isEqual(this.remotePreviewResource, conflict)) - ) { - const preview = await this.syncPreviewResultPromise!; - this.cancel(); - this.syncPreviewResultPromise = createCancelablePromise(async () => ({ ...preview, content })); - await this.apply(true); - this.setStatus(SyncStatus.Idle); - } - } - async hasLocalData(): Promise { try { const localFileContent = await this.getLocalFileContent(); @@ -165,8 +239,8 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem } async resolveContent(uri: URI): Promise { - if (isEqual(this.remotePreviewResource, uri)) { - return this.getConflictContent(uri); + if (isEqual(this.remoteResource, uri) || isEqual(this.localResource, uri) || isEqual(this.acceptedResource, uri)) { + return this.resolvePreviewContent(uri); } let content = await super.resolveContent(uri); if (content) { @@ -185,137 +259,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem return null; } - protected async getConflictContent(conflictResource: URI): Promise { - const content = await super.getConflictContent(conflictResource); - return content !== null ? this.getKeybindingsContentFromSyncContent(content) : null; - } - - protected async performSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise { - try { - const result = await this.getPreview(remoteUserData, lastSyncUserData); - if (result.hasConflicts) { - return SyncStatus.HasConflicts; - } - await this.apply(); - return SyncStatus.Idle; - } catch (e) { - this.syncPreviewResultPromise = null; - if (e instanceof UserDataSyncError) { - switch (e.code) { - case UserDataSyncErrorCode.LocalPreconditionFailed: - // Rejected as there is a new local version. Syncing again. - this.logService.info(`${this.syncResourceLogLabel}: Failed to synchronize keybindings as there is a new local version available. Synchronizing again...`); - return this.performSync(remoteUserData, lastSyncUserData); - } - } - throw e; - } - } - - private async apply(forcePush?: boolean): Promise { - if (!this.syncPreviewResultPromise) { - return; - } - - let { fileContent, remoteUserData, lastSyncUserData, content, hasLocalChanged, hasRemoteChanged } = await this.syncPreviewResultPromise; - - if (content !== null) { - if (this.hasErrors(content)) { - throw new UserDataSyncError(localize('errorInvalidSettings', "Unable to sync keybindings as there are errors/warning in keybindings file."), UserDataSyncErrorCode.LocalInvalidContent, this.resource); - } - - if (hasLocalChanged) { - this.logService.trace(`${this.syncResourceLogLabel}: Updating local keybindings...`); - if (fileContent) { - await this.backupLocal(this.toSyncContent(fileContent.value.toString(), null)); - } - await this.updateLocalFileContent(content, fileContent); - this.logService.info(`${this.syncResourceLogLabel}: Updated local keybindings`); - } - - if (hasRemoteChanged) { - this.logService.trace(`${this.syncResourceLogLabel}: Updating remote keybindings...`); - const remoteContents = this.toSyncContent(content, remoteUserData.syncData ? remoteUserData.syncData.content : null); - remoteUserData = await this.updateRemoteUserData(remoteContents, forcePush ? null : remoteUserData.ref); - this.logService.info(`${this.syncResourceLogLabel}: Updated remote keybindings`); - } - - // Delete the preview - try { - await this.fileService.del(this.localPreviewResource); - } catch (e) { /* ignore */ } - } else { - this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing keybindings.`); - } - - if (lastSyncUserData?.ref !== remoteUserData.ref && (content !== null || fileContent !== null)) { - this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized keybindings...`); - const lastSyncContent = this.toSyncContent(content !== null ? content : fileContent!.value.toString(), null); - await this.updateLastSyncUserData({ ref: remoteUserData.ref, syncData: { version: remoteUserData.syncData!.version, content: lastSyncContent } }); - this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized keybindings`); - } - - this.syncPreviewResultPromise = null; - } - - private getPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise { - if (!this.syncPreviewResultPromise) { - this.syncPreviewResultPromise = createCancelablePromise(token => this.generatePreview(remoteUserData, lastSyncUserData, token)); - } - return this.syncPreviewResultPromise; - } - - protected async generatePreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken = CancellationToken.None): Promise { - const remoteContent = remoteUserData.syncData ? this.getKeybindingsContentFromSyncContent(remoteUserData.syncData.content) : null; - const lastSyncContent = lastSyncUserData && lastSyncUserData.syncData ? this.getKeybindingsContentFromSyncContent(lastSyncUserData.syncData.content) : null; - // Get file content last to get the latest - const fileContent = await this.getLocalFileContent(); - const formattingOptions = await this.getFormattingOptions(); - - let content: string | null = null; - let hasLocalChanged: boolean = false; - let hasRemoteChanged: boolean = false; - let hasConflicts: boolean = false; - - if (remoteContent) { - const localContent: string = fileContent ? fileContent.value.toString() : '[]'; - if (this.hasErrors(localContent)) { - throw new UserDataSyncError(localize('errorInvalidSettings', "Unable to sync keybindings as there are errors/warning in keybindings file."), UserDataSyncErrorCode.LocalInvalidContent, this.resource); - } - - if (!lastSyncContent // First time sync - || lastSyncContent !== localContent // Local has forwarded - || lastSyncContent !== remoteContent // Remote has forwarded - ) { - this.logService.trace(`${this.syncResourceLogLabel}: Merging remote keybindings with local keybindings...`); - const result = await merge(localContent, remoteContent, lastSyncContent, formattingOptions, this.userDataSyncUtilService); - // Sync only if there are changes - if (result.hasChanges) { - content = result.mergeContent; - hasConflicts = result.hasConflicts; - hasLocalChanged = hasConflicts || result.mergeContent !== localContent; - hasRemoteChanged = hasConflicts || result.mergeContent !== remoteContent; - } - } - } - - // First time syncing to remote - else if (fileContent) { - this.logService.trace(`${this.syncResourceLogLabel}: Remote keybindings does not exist. Synchronizing keybindings for the first time.`); - content = fileContent.value.toString(); - hasRemoteChanged = true; - } - - if (content && !token.isCancellationRequested) { - await this.fileService.writeFile(this.localPreviewResource, VSBuffer.fromString(content)); - } - - this.setConflicts(hasConflicts && !token.isCancellationRequested ? [{ local: this.localPreviewResource, remote: this.remotePreviewResource }] : []); - - return { fileContent, remoteUserData, lastSyncUserData, content, hasLocalChanged, hasRemoteChanged, hasConflicts }; - } - - private getKeybindingsContentFromSyncContent(syncContent: string): string | null { + getKeybindingsContentFromSyncContent(syncContent: string): string | null { try { const parsed = JSON.parse(syncContent); if (!this.configurationService.getValue('sync.keybindingsPerPlatform')) { diff --git a/src/vs/platform/userDataSync/common/settingsMerge.ts b/src/vs/platform/userDataSync/common/settingsMerge.ts index 184a9a38fa8..d05975d3c94 100644 --- a/src/vs/platform/userDataSync/common/settingsMerge.ts +++ b/src/vs/platform/userDataSync/common/settingsMerge.ts @@ -6,14 +6,12 @@ import * as objects from 'vs/base/common/objects'; import { parse, JSONVisitor, visit } from 'vs/base/common/json'; import { setProperty, withFormatting, applyEdits } from 'vs/base/common/jsonEdit'; -import { values } from 'vs/base/common/map'; import { IStringDictionary } from 'vs/base/common/collections'; import { FormattingOptions, Edit, getEOL } from 'vs/base/common/jsonFormatter'; import * as contentUtil from 'vs/platform/userDataSync/common/content'; import { IConflictSetting, getDisallowedIgnoredSettings } from 'vs/platform/userDataSync/common/userDataSync'; import { firstIndex, distinct } from 'vs/base/common/arrays'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { startsWith } from 'vs/base/common/strings'; export interface IMergeResult { localContent: string | null; @@ -35,7 +33,7 @@ export function getIgnoredSettings(defaultIgnoredSettings: string[], configurati const added: string[] = [], removed: string[] = [...getDisallowedIgnoredSettings()]; if (Array.isArray(value)) { for (const key of value) { - if (startsWith(key, '-')) { + if (key.startsWith('-')) { removed.push(key.substring(1)); } else { added.push(key); @@ -130,7 +128,7 @@ export function merge(originalLocalContent: string, originalRemoteContent: strin }; // Removed settings in Local - for (const key of values(baseToLocal.removed)) { + for (const key of baseToLocal.removed.values()) { // Conflict - Got updated in remote. if (baseToRemote.updated.has(key)) { handleConflict(key); @@ -142,7 +140,7 @@ export function merge(originalLocalContent: string, originalRemoteContent: strin } // Removed settings in Remote - for (const key of values(baseToRemote.removed)) { + for (const key of baseToRemote.removed.values()) { if (handledConflicts.has(key)) { continue; } @@ -157,7 +155,7 @@ export function merge(originalLocalContent: string, originalRemoteContent: strin } // Updated settings in Local - for (const key of values(baseToLocal.updated)) { + for (const key of baseToLocal.updated.values()) { if (handledConflicts.has(key)) { continue; } @@ -173,7 +171,7 @@ export function merge(originalLocalContent: string, originalRemoteContent: strin } // Updated settings in Remote - for (const key of values(baseToRemote.updated)) { + for (const key of baseToRemote.updated.values()) { if (handledConflicts.has(key)) { continue; } @@ -189,7 +187,7 @@ export function merge(originalLocalContent: string, originalRemoteContent: strin } // Added settings in Local - for (const key of values(baseToLocal.added)) { + for (const key of baseToLocal.added.values()) { if (handledConflicts.has(key)) { continue; } @@ -205,7 +203,7 @@ export function merge(originalLocalContent: string, originalRemoteContent: strin } // Added settings in remote - for (const key of values(baseToRemote.added)) { + for (const key of baseToRemote.added.values()) { if (handledConflicts.has(key)) { continue; } @@ -223,7 +221,7 @@ export function merge(originalLocalContent: string, originalRemoteContent: strin const hasConflicts = conflicts.size > 0 || !areSame(localContent, remoteContent, ignoredSettings); const hasLocalChanged = hasConflicts || !areSame(localContent, originalLocalContent, []); const hasRemoteChanged = hasConflicts || !areSame(remoteContent, originalRemoteContent, []); - return { localContent: hasLocalChanged ? localContent : null, remoteContent: hasRemoteChanged ? remoteContent : null, conflictsSettings: values(conflicts), hasConflicts }; + return { localContent: hasLocalChanged ? localContent : null, remoteContent: hasRemoteChanged ? remoteContent : null, conflictsSettings: [...conflicts.values()], hasConflicts }; } export function areSame(localContent: string, remoteContent: string, ignoredSettings: string[]): boolean { @@ -429,26 +427,34 @@ function getEditToInsertAtLocation(content: string, key: string, value: any, loc if (location.insertAfter) { + const edits: Edit[] = []; + /* Insert after a setting */ if (node.setting) { - return [{ offset: node.endOffset, length: 0, content: ',' + newProperty }]; + edits.push({ offset: node.endOffset, length: 0, content: ',' + newProperty }); } - /* - Insert after a comment and before a setting (or) - Insert between comments and there is a setting after - */ - if (tree[location.index + 1] && - (tree[location.index + 1].setting || findNextSettingNode(location.index, tree))) { - return [{ offset: node.endOffset, length: 0, content: eol + newProperty + ',' }]; + /* Insert after a comment */ + else { + + const nextSettingNode = findNextSettingNode(location.index, tree); + const previousSettingNode = findPreviousSettingNode(location.index, tree); + const previousSettingCommaOffset = previousSettingNode?.setting?.commaOffset; + + /* If there is a previous setting and it does not has comma then add it */ + if (previousSettingNode && previousSettingCommaOffset === undefined) { + edits.push({ offset: previousSettingNode.endOffset, length: 0, content: ',' }); + } + + const isPreviouisSettingIncludesComment = previousSettingCommaOffset !== undefined && previousSettingCommaOffset > node.endOffset; + edits.push({ + offset: isPreviouisSettingIncludesComment ? previousSettingCommaOffset! + 1 : node.endOffset, + length: 0, + content: nextSettingNode ? eol + newProperty + ',' : eol + newProperty + }); } - /* Insert after the comment at the end */ - const edits = [{ offset: node.endOffset, length: 0, content: eol + newProperty }]; - const previousSettingNode = findPreviousSettingNode(location.index, tree); - if (previousSettingNode && !previousSettingNode.setting!.hasCommaSeparator) { - edits.splice(0, 0, { offset: previousSettingNode.endOffset, length: 0, content: ',' }); - } + return edits; } @@ -516,7 +522,7 @@ interface INode { readonly value: string; readonly setting?: { readonly key: string; - readonly hasCommaSeparator: boolean; + readonly commaOffset: number | undefined; }; readonly comment?: string; } @@ -547,7 +553,7 @@ function parseSettings(content: string): INode[] { value: content.substring(startOffset, offset + length), setting: { key, - hasCommaSeparator: false + commaOffset: undefined } }); } @@ -564,7 +570,7 @@ function parseSettings(content: string): INode[] { value: content.substring(startOffset, offset + length), setting: { key, - hasCommaSeparator: false + commaOffset: undefined } }); } @@ -577,7 +583,7 @@ function parseSettings(content: string): INode[] { value: content.substring(startOffset, offset + length), setting: { key, - hasCommaSeparator: false + commaOffset: undefined } }); } @@ -585,15 +591,21 @@ function parseSettings(content: string): INode[] { onSeparator: (sep: string, offset: number, length: number) => { if (hierarchyLevel === 0) { if (sep === ',') { - const node = nodes.pop(); + let index = nodes.length - 1; + for (; index >= 0; index--) { + if (nodes[index].setting) { + break; + } + } + const node = nodes[index]; if (node) { - nodes.push({ + nodes.splice(index, 1, { startOffset: node.startOffset, endOffset: node.endOffset, value: node.value, setting: { key: node.setting!.key, - hasCommaSeparator: true + commaOffset: offset } }); } diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index ce6c56c87b5..62911b4053b 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -4,21 +4,31 @@ *--------------------------------------------------------------------------------------------*/ import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; -import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, CONFIGURATION_SYNC_STORE_KEY, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, USER_DATA_SYNC_SCHEME, PREVIEW_DIR_NAME, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync'; +import { + UserDataSyncError, UserDataSyncErrorCode, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, CONFIGURATION_SYNC_STORE_KEY, + SyncResource, IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, USER_DATA_SYNC_SCHEME, ISyncResourceHandle, IUserDataSynchroniser, + IRemoteUserData, ISyncData, Change +} from 'vs/platform/userDataSync/common/userDataSync'; import { VSBuffer } from 'vs/base/common/buffer'; import { localize } from 'vs/nls'; import { Event } from 'vs/base/common/event'; -import { createCancelablePromise } from 'vs/base/common/async'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CancellationToken } from 'vs/base/common/cancellation'; import { updateIgnoredSettings, merge, getIgnoredSettings, isEmpty } from 'vs/platform/userDataSync/common/settingsMerge'; import { edit } from 'vs/platform/userDataSync/common/content'; -import { IFileSyncPreviewResult, AbstractJsonFileSynchroniser, IRemoteUserData } from 'vs/platform/userDataSync/common/abstractSynchronizer'; +import { AbstractJsonFileSynchroniser, IAcceptResult, IFileResourcePreview, IMergeResult } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { URI } from 'vs/base/common/uri'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { joinPath, isEqual, dirname, basename } from 'vs/base/common/resources'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { Edit } from 'vs/base/common/jsonFormatter'; +import { setProperty, applyEdits } from 'vs/base/common/jsonEdit'; + +interface ISettingsResourcePreview extends IFileResourcePreview { + previewResult: IMergeResult; +} export interface ISettingsSyncContent { settings: string; @@ -30,126 +40,182 @@ function isSettingsSyncContent(thing: any): thing is ISettingsSyncContent { && Object.keys(thing).length === 1; } -export class SettingsSynchroniser extends AbstractJsonFileSynchroniser { - - _serviceBrand: any; +export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implements IUserDataSynchroniser { protected readonly version: number = 1; - protected readonly localPreviewResource: URI = joinPath(this.syncFolder, PREVIEW_DIR_NAME, 'settings.json'); - protected readonly remotePreviewResource: URI = this.localPreviewResource.with({ scheme: USER_DATA_SYNC_SCHEME }); + readonly previewResource: URI = joinPath(this.syncPreviewFolder, 'settings.json'); + readonly localResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }); + readonly remoteResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }); + readonly acceptedResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }); constructor( @IFileService fileService: IFileService, @IEnvironmentService environmentService: IEnvironmentService, + @IStorageService storageService: IStorageService, @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, @IUserDataSyncBackupStoreService userDataSyncBackupStoreService: IUserDataSyncBackupStoreService, @IUserDataSyncLogService logService: IUserDataSyncLogService, @IUserDataSyncUtilService userDataSyncUtilService: IUserDataSyncUtilService, @IConfigurationService configurationService: IConfigurationService, - @IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService, + @IUserDataSyncResourceEnablementService userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, @ITelemetryService telemetryService: ITelemetryService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, ) { - super(environmentService.settingsResource, SyncResource.Settings, fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService); + super(environmentService.settingsResource, SyncResource.Settings, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncResourceEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService); } - protected setStatus(status: SyncStatus): void { - super.setStatus(status); - if (this.status !== SyncStatus.HasConflicts) { - this.setConflicts([]); + protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise { + const fileContent = await this.getLocalFileContent(); + const formattingOptions = await this.getFormattingOptions(); + const remoteSettingsSyncContent = this.getSettingsSyncContent(remoteUserData); + const lastSettingsSyncContent: ISettingsSyncContent | null = lastSyncUserData ? this.getSettingsSyncContent(lastSyncUserData) : null; + const ignoredSettings = await this.getIgnoredSettings(); + + let mergedContent: string | null = null; + let hasLocalChanged: boolean = false; + let hasRemoteChanged: boolean = false; + let hasConflicts: boolean = false; + + if (remoteSettingsSyncContent) { + const localContent: string = fileContent ? fileContent.value.toString() : '{}'; + this.validateContent(localContent); + this.logService.trace(`${this.syncResourceLogLabel}: Merging remote settings with local settings...`); + const result = merge(localContent, remoteSettingsSyncContent.settings, lastSettingsSyncContent ? lastSettingsSyncContent.settings : null, ignoredSettings, [], formattingOptions); + mergedContent = result.localContent || result.remoteContent; + hasLocalChanged = result.localContent !== null; + hasRemoteChanged = result.remoteContent !== null; + hasConflicts = result.hasConflicts; } + + // First time syncing to remote + else if (fileContent) { + this.logService.trace(`${this.syncResourceLogLabel}: Remote settings does not exist. Synchronizing settings for the first time.`); + mergedContent = fileContent.value.toString(); + hasRemoteChanged = true; + } + + const previewResult = { + content: mergedContent, + localChange: hasLocalChanged ? Change.Modified : Change.None, + remoteChange: hasRemoteChanged ? Change.Modified : Change.None, + hasConflicts + }; + + return [{ + fileContent, + localResource: this.localResource, + localContent: fileContent ? fileContent.value.toString() : null, + localChange: previewResult.localChange, + + remoteResource: this.remoteResource, + remoteContent: remoteSettingsSyncContent ? remoteSettingsSyncContent.settings : null, + remoteChange: previewResult.remoteChange, + + previewResource: this.previewResource, + previewResult, + acceptedResource: this.acceptedResource, + }]; } - async pull(): Promise { - if (!this.isEnabled()) { - this.logService.info(`${this.syncResourceLogLabel}: Skipped pulling settings as it is disabled.`); - return; + protected async getMergeResult(resourcePreview: ISettingsResourcePreview, token: CancellationToken): Promise { + const formatUtils = await this.getFormattingOptions(); + const ignoredSettings = await this.getIgnoredSettings(); + return { + ...resourcePreview.previewResult, + + // remove ignored settings from the preview content + content: resourcePreview.previewResult.content ? updateIgnoredSettings(resourcePreview.previewResult.content, '{}', ignoredSettings, formatUtils) : null + }; + } + + protected async getAcceptResult(resourcePreview: ISettingsResourcePreview, resource: URI, content: string | null | undefined, token: CancellationToken): Promise { + + const formattingOptions = await this.getFormattingOptions(); + const ignoredSettings = await this.getIgnoredSettings(); + + /* Accept local resource */ + if (isEqual(resource, this.localResource)) { + return { + /* Remove ignored settings */ + content: resourcePreview.fileContent ? updateIgnoredSettings(resourcePreview.fileContent.value.toString(), '{}', ignoredSettings, formattingOptions) : null, + localChange: Change.None, + remoteChange: Change.Modified, + }; } - this.stop(); + /* Accept remote resource */ + if (isEqual(resource, this.remoteResource)) { + return { + /* Update ignored settings from local file content */ + content: resourcePreview.remoteContent !== null ? updateIgnoredSettings(resourcePreview.remoteContent, resourcePreview.fileContent ? resourcePreview.fileContent.value.toString() : '{}', ignoredSettings, formattingOptions) : null, + localChange: Change.Modified, + remoteChange: Change.None, + }; + } - try { - this.logService.info(`${this.syncResourceLogLabel}: Started pulling settings...`); - this.setStatus(SyncStatus.Syncing); + /* Accept preview resource */ + if (isEqual(resource, this.previewResource)) { + if (content === undefined) { + return { + content: resourcePreview.previewResult.content, + localChange: resourcePreview.previewResult.localChange, + remoteChange: resourcePreview.previewResult.remoteChange, + }; + } else { + return { + /* Add ignored settings from local file content */ + content: content !== null ? updateIgnoredSettings(content, resourcePreview.fileContent ? resourcePreview.fileContent.value.toString() : '{}', ignoredSettings, formattingOptions) : null, + localChange: Change.Modified, + remoteChange: Change.Modified, + }; + } + } - const lastSyncUserData = await this.getLastSyncUserData(); - const remoteUserData = await this.getRemoteUserData(lastSyncUserData); + throw new Error(`Invalid Resource: ${resource.toString()}`); + } + + protected async applyResult(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, resourcePreviews: [ISettingsResourcePreview, IAcceptResult][], force: boolean): Promise { + const { fileContent } = resourcePreviews[0][0]; + let { content, localChange, remoteChange } = resourcePreviews[0][1]; + + if (localChange === Change.None && remoteChange === Change.None) { + this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing settings.`); + } + + content = content !== null ? content : '{}'; + this.validateContent(content); + + if (localChange !== Change.None) { + this.logService.trace(`${this.syncResourceLogLabel}: Updating local settings...`); + if (fileContent) { + await this.backupLocal(JSON.stringify(this.toSettingsSyncContent(fileContent.value.toString()))); + } + await this.updateLocalFileContent(content, fileContent, force); + this.logService.info(`${this.syncResourceLogLabel}: Updated local settings`); + } + + if (remoteChange !== Change.None) { + const formatUtils = await this.getFormattingOptions(); + // Update ignored settings from remote const remoteSettingsSyncContent = this.getSettingsSyncContent(remoteUserData); - - if (remoteSettingsSyncContent !== null) { - const fileContent = await this.getLocalFileContent(); - const formatUtils = await this.getFormattingOptions(); - // Update ignored settings from local file content - const ignoredSettings = await this.getIgnoredSettings(); - const content = updateIgnoredSettings(remoteSettingsSyncContent.settings, fileContent ? fileContent.value.toString() : '{}', ignoredSettings, formatUtils); - this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve({ - fileContent, - remoteUserData, - lastSyncUserData, - content, - hasLocalChanged: true, - hasRemoteChanged: false, - hasConflicts: false, - })); - - await this.apply(); - } - - // No remote exists to pull - else { - this.logService.info(`${this.syncResourceLogLabel}: Remote settings does not exist.`); - } - - this.logService.info(`${this.syncResourceLogLabel}: Finished pulling settings.`); - } finally { - this.setStatus(SyncStatus.Idle); - } - } - - async push(): Promise { - if (!this.isEnabled()) { - this.logService.info(`${this.syncResourceLogLabel}: Skipped pushing settings as it is disabled.`); - return; + const ignoredSettings = await this.getIgnoredSettings(content); + content = updateIgnoredSettings(content, remoteSettingsSyncContent ? remoteSettingsSyncContent.settings : '{}', ignoredSettings, formatUtils); + this.logService.trace(`${this.syncResourceLogLabel}: Updating remote settings...`); + remoteUserData = await this.updateRemoteUserData(JSON.stringify(this.toSettingsSyncContent(content)), force ? null : remoteUserData.ref); + this.logService.info(`${this.syncResourceLogLabel}: Updated remote settings`); } - this.stop(); - + // Delete the preview try { - this.logService.info(`${this.syncResourceLogLabel}: Started pushing settings...`); - this.setStatus(SyncStatus.Syncing); + await this.fileService.del(this.previewResource); + } catch (e) { /* ignore */ } - const fileContent = await this.getLocalFileContent(); - - if (fileContent !== null) { - const formatUtils = await this.getFormattingOptions(); - // Remove ignored settings - const ignoredSettings = await this.getIgnoredSettings(); - const content = updateIgnoredSettings(fileContent.value.toString(), '{}', ignoredSettings, formatUtils); - const lastSyncUserData = await this.getLastSyncUserData(); - const remoteUserData = await this.getRemoteUserData(lastSyncUserData); - - this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve({ - fileContent, - remoteUserData, - lastSyncUserData, - content, - hasRemoteChanged: true, - hasLocalChanged: false, - hasConflicts: false, - })); - - await this.apply(true); - } - - // No local exists to push - else { - this.logService.info(`${this.syncResourceLogLabel}: Local settings does not exist.`); - } - - this.logService.info(`${this.syncResourceLogLabel}: Finished pushing settings.`); - } finally { - this.setStatus(SyncStatus.Idle); + if (lastSyncUserData?.ref !== remoteUserData.ref) { + this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized settings...`); + await this.updateLastSyncUserData(remoteUserData); + this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized settings`); } + } async hasLocalData(): Promise { @@ -173,8 +239,8 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser { } async resolveContent(uri: URI): Promise { - if (isEqual(this.remotePreviewResource, uri)) { - return this.getConflictContent(uri); + if (isEqual(this.remoteResource, uri) || isEqual(this.localResource, uri) || isEqual(this.acceptedResource, uri)) { + return this.resolvePreviewContent(uri); } let content = await super.resolveContent(uri); if (content) { @@ -196,168 +262,22 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser { return null; } - protected async getConflictContent(conflictResource: URI): Promise { - let content = await super.getConflictContent(conflictResource); - if (content !== null) { - const settingsSyncContent = this.parseSettingsSyncContent(content); - content = settingsSyncContent ? settingsSyncContent.settings : null; - } - if (content !== null) { + protected async resolvePreviewContent(resource: URI): Promise { + let content = await super.resolvePreviewContent(resource); + if (content) { const formatUtils = await this.getFormattingOptions(); - // remove ignored settings from the remote content for preview + // remove ignored settings from the preview content const ignoredSettings = await this.getIgnoredSettings(); content = updateIgnoredSettings(content, '{}', ignoredSettings, formatUtils); } return content; } - async acceptConflict(conflict: URI, content: string): Promise { - if (this.status === SyncStatus.HasConflicts - && (isEqual(this.localPreviewResource, conflict) || isEqual(this.remotePreviewResource, conflict)) - ) { - const preview = await this.syncPreviewResultPromise!; - this.cancel(); - const formatUtils = await this.getFormattingOptions(); - // Add ignored settings from local file content - const ignoredSettings = await this.getIgnoredSettings(); - content = updateIgnoredSettings(content, preview.fileContent ? preview.fileContent.value.toString() : '{}', ignoredSettings, formatUtils); - this.syncPreviewResultPromise = createCancelablePromise(async () => ({ ...preview, content })); - await this.apply(true); - this.setStatus(SyncStatus.Idle); - } - } - - async resolveSettingsConflicts(resolvedConflicts: { key: string, value: any | undefined }[]): Promise { - if (this.status === SyncStatus.HasConflicts) { - const preview = await this.syncPreviewResultPromise!; - this.cancel(); - await this.performSync(preview.remoteUserData, preview.lastSyncUserData, resolvedConflicts); - } - } - - protected async performSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, resolvedConflicts: { key: string, value: any | undefined }[] = []): Promise { - try { - const result = await this.getPreview(remoteUserData, lastSyncUserData, resolvedConflicts); - if (result.hasConflicts) { - return SyncStatus.HasConflicts; - } - await this.apply(); - return SyncStatus.Idle; - } catch (e) { - this.syncPreviewResultPromise = null; - if (e instanceof UserDataSyncError) { - switch (e.code) { - case UserDataSyncErrorCode.LocalPreconditionFailed: - // Rejected as there is a new local version. Syncing again. - this.logService.info(`${this.syncResourceLogLabel}: Failed to synchronize settings as there is a new local version available. Synchronizing again...`); - return this.performSync(remoteUserData, lastSyncUserData, resolvedConflicts); - } - } - throw e; - } - } - - private async apply(forcePush?: boolean): Promise { - if (!this.syncPreviewResultPromise) { - return; - } - - let { fileContent, remoteUserData, lastSyncUserData, content, hasLocalChanged, hasRemoteChanged } = await this.syncPreviewResultPromise; - - if (content !== null) { - - this.validateContent(content); - - if (hasLocalChanged) { - this.logService.trace(`${this.syncResourceLogLabel}: Updating local settings...`); - if (fileContent) { - await this.backupLocal(JSON.stringify(this.toSettingsSyncContent(fileContent.value.toString()))); - } - await this.updateLocalFileContent(content, fileContent); - this.logService.info(`${this.syncResourceLogLabel}: Updated local settings`); - } - if (hasRemoteChanged) { - const formatUtils = await this.getFormattingOptions(); - // Update ignored settings from remote - const remoteSettingsSyncContent = this.getSettingsSyncContent(remoteUserData); - const ignoredSettings = await this.getIgnoredSettings(content); - content = updateIgnoredSettings(content, remoteSettingsSyncContent ? remoteSettingsSyncContent.settings : '{}', ignoredSettings, formatUtils); - this.logService.trace(`${this.syncResourceLogLabel}: Updating remote settings...`); - remoteUserData = await this.updateRemoteUserData(JSON.stringify(this.toSettingsSyncContent(content)), forcePush ? null : remoteUserData.ref); - this.logService.info(`${this.syncResourceLogLabel}: Updated remote settings`); - } - - // Delete the preview - try { - await this.fileService.del(this.localPreviewResource); - } catch (e) { /* ignore */ } - } else { - this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing settings.`); - } - - if (lastSyncUserData?.ref !== remoteUserData.ref) { - this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized settings...`); - await this.updateLastSyncUserData(remoteUserData); - this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized settings`); - } - - this.syncPreviewResultPromise = null; - } - - private getPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, resolvedConflicts: { key: string, value: any }[] = []): Promise { - if (!this.syncPreviewResultPromise) { - this.syncPreviewResultPromise = createCancelablePromise(token => this.generatePreview(remoteUserData, lastSyncUserData, resolvedConflicts, token)); - } - return this.syncPreviewResultPromise; - } - - protected async generatePreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, resolvedConflicts: { key: string, value: any }[] = [], token: CancellationToken = CancellationToken.None): Promise { - const fileContent = await this.getLocalFileContent(); - const formattingOptions = await this.getFormattingOptions(); - const remoteSettingsSyncContent = this.getSettingsSyncContent(remoteUserData); - const lastSettingsSyncContent = lastSyncUserData ? this.getSettingsSyncContent(lastSyncUserData) : null; - - let content: string | null = null; - let hasLocalChanged: boolean = false; - let hasRemoteChanged: boolean = false; - let hasConflicts: boolean = false; - - if (remoteSettingsSyncContent) { - const localContent: string = fileContent ? fileContent.value.toString() : '{}'; - this.validateContent(localContent); - this.logService.trace(`${this.syncResourceLogLabel}: Merging remote settings with local settings...`); - const ignoredSettings = await this.getIgnoredSettings(); - const result = merge(localContent, remoteSettingsSyncContent.settings, lastSettingsSyncContent ? lastSettingsSyncContent.settings : null, ignoredSettings, resolvedConflicts, formattingOptions); - content = result.localContent || result.remoteContent; - hasLocalChanged = result.localContent !== null; - hasRemoteChanged = result.remoteContent !== null; - hasConflicts = result.hasConflicts; - } - - // First time syncing to remote - else if (fileContent) { - this.logService.trace(`${this.syncResourceLogLabel}: Remote settings does not exist. Synchronizing settings for the first time.`); - content = fileContent.value.toString(); - hasRemoteChanged = true; - } - - if (content && !token.isCancellationRequested) { - // Remove the ignored settings from the preview. - const ignoredSettings = await this.getIgnoredSettings(); - const previewContent = updateIgnoredSettings(content, '{}', ignoredSettings, formattingOptions); - await this.fileService.writeFile(this.localPreviewResource, VSBuffer.fromString(previewContent)); - } - - this.setConflicts(hasConflicts && !token.isCancellationRequested ? [{ local: this.localPreviewResource, remote: this.remotePreviewResource }] : []); - - return { fileContent, remoteUserData, lastSyncUserData, content, hasLocalChanged, hasRemoteChanged, hasConflicts }; - } - private getSettingsSyncContent(remoteUserData: IRemoteUserData): ISettingsSyncContent | null { return remoteUserData.syncData ? this.parseSettingsSyncContent(remoteUserData.syncData.content) : null; } - private parseSettingsSyncContent(syncContent: string): ISettingsSyncContent | null { + parseSettingsSyncContent(syncContent: string): ISettingsSyncContent | null { try { const parsed = JSON.parse(syncContent); return isSettingsSyncContent(parsed) ? parsed : /* migrate */ { settings: syncContent }; @@ -372,7 +292,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser { } private _defaultIgnoredSettings: Promise | undefined = undefined; - protected async getIgnoredSettings(content?: string): Promise { + private async getIgnoredSettings(content?: string): Promise { if (!this._defaultIgnoredSettings) { this._defaultIgnoredSettings = this.userDataSyncUtilService.resolveDefaultIgnoredSettings(); const disposable = Event.any( @@ -391,4 +311,49 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser { throw new UserDataSyncError(localize('errorInvalidSettings', "Unable to sync settings as there are errors/warning in settings file."), UserDataSyncErrorCode.LocalInvalidContent, this.resource); } } + + async recoverSettings(): Promise { + try { + const fileContent = await this.getLocalFileContent(); + if (!fileContent) { + return; + } + + const syncData: ISyncData = JSON.parse(fileContent.value.toString()); + if (!isSyncData(syncData)) { + return; + } + + this.telemetryService.publicLog2('sync/settingsCorrupted'); + const settingsSyncContent = this.parseSettingsSyncContent(syncData.content); + if (!settingsSyncContent || !settingsSyncContent.settings) { + return; + } + + let settings = settingsSyncContent.settings; + const formattingOptions = await this.getFormattingOptions(); + for (const key in syncData) { + if (['version', 'content', 'machineId'].indexOf(key) === -1 && (syncData as any)[key] !== undefined) { + const edits: Edit[] = setProperty(settings, [key], (syncData as any)[key], formattingOptions); + if (edits.length) { + settings = applyEdits(settings, edits); + } + } + } + + await this.fileService.writeFile(this.file, VSBuffer.fromString(settings)); + } catch (e) {/* ignore */ } + } +} + +function isSyncData(thing: any): thing is ISyncData { + if (thing + && (thing.version !== undefined && typeof thing.version === 'number') + && (thing.content !== undefined && typeof thing.content === 'string') + && (thing.machineId !== undefined && typeof thing.machineId === 'string') + ) { + return true; + } + + return false; } diff --git a/src/vs/platform/userDataSync/common/snippetsMerge.ts b/src/vs/platform/userDataSync/common/snippetsMerge.ts index 42a9dfaae05..7f8e3e486d2 100644 --- a/src/vs/platform/userDataSync/common/snippetsMerge.ts +++ b/src/vs/platform/userDataSync/common/snippetsMerge.ts @@ -3,30 +3,32 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { values } from 'vs/base/common/map'; import { IStringDictionary } from 'vs/base/common/collections'; -import { deepClone } from 'vs/base/common/objects'; export interface IMergeResult { - added: IStringDictionary; - updated: IStringDictionary; - removed: string[]; + local: { + added: IStringDictionary; + updated: IStringDictionary; + removed: string[]; + }; + remote: { + added: IStringDictionary; + updated: IStringDictionary; + removed: string[]; + }; conflicts: string[]; - remote: IStringDictionary | null; } -export function merge(local: IStringDictionary, remote: IStringDictionary | null, base: IStringDictionary | null, resolvedConflicts: IStringDictionary = {}): IMergeResult { - const added: IStringDictionary = {}; - const updated: IStringDictionary = {}; - const removed: Set = new Set(); +export function merge(local: IStringDictionary, remote: IStringDictionary | null, base: IStringDictionary | null): IMergeResult { + const localAdded: IStringDictionary = {}; + const localUpdated: IStringDictionary = {}; + const localRemoved: Set = new Set(); if (!remote) { return { - added, - removed: values(removed), - updated, - conflicts: [], - remote: local + local: { added: localAdded, updated: localUpdated, removed: [...localRemoved.values()] }, + remote: { added: local, updated: {}, removed: [] }, + conflicts: [] }; } @@ -34,145 +36,118 @@ export function merge(local: IStringDictionary, remote: IStringDictionar if (localToRemote.added.size === 0 && localToRemote.removed.size === 0 && localToRemote.updated.size === 0) { // No changes found between local and remote. return { - added, - removed: values(removed), - updated, - conflicts: [], - remote: null + local: { added: localAdded, updated: localUpdated, removed: [...localRemoved.values()] }, + remote: { added: {}, updated: {}, removed: [] }, + conflicts: [] }; } const baseToLocal = compare(base, local); const baseToRemote = compare(base, remote); - const remoteContent: IStringDictionary = deepClone(remote); + + const remoteAdded: IStringDictionary = {}; + const remoteUpdated: IStringDictionary = {}; + const remoteRemoved: Set = new Set(); + const conflicts: Set = new Set(); - const handledConflicts: Set = new Set(); - const handleConflict = (key: string): void => { - if (handledConflicts.has(key)) { - return; - } - handledConflicts.add(key); - const conflictContent = resolvedConflicts[key]; - - // add to conflicts - if (conflictContent === undefined) { - conflicts.add(key); - } - - // remove the snippet - else if (conflictContent === null) { - delete remote[key]; - if (local[key]) { - removed.add(key); - } - } - - // add/update the snippet - else { - if (local[key]) { - if (local[key] !== conflictContent) { - updated[key] = conflictContent; - } - } else { - added[key] = conflictContent; - } - remoteContent[key] = conflictContent; - } - }; // Removed snippets in Local - for (const key of values(baseToLocal.removed)) { + for (const key of baseToLocal.removed.values()) { // Conflict - Got updated in remote. if (baseToRemote.updated.has(key)) { // Add to local - added[key] = remote[key]; + localAdded[key] = remote[key]; } // Remove it in remote else { - delete remoteContent[key]; + remoteRemoved.add(key); } } // Removed snippets in Remote - for (const key of values(baseToRemote.removed)) { - if (handledConflicts.has(key)) { + for (const key of baseToRemote.removed.values()) { + if (conflicts.has(key)) { continue; } // Conflict - Got updated in local if (baseToLocal.updated.has(key)) { - handleConflict(key); + conflicts.add(key); } // Also remove in Local else { - removed.add(key); + localRemoved.add(key); } } // Updated snippets in Local - for (const key of values(baseToLocal.updated)) { - if (handledConflicts.has(key)) { + for (const key of baseToLocal.updated.values()) { + if (conflicts.has(key)) { continue; } // Got updated in remote if (baseToRemote.updated.has(key)) { // Has different value if (localToRemote.updated.has(key)) { - handleConflict(key); + conflicts.add(key); } } else { - remoteContent[key] = local[key]; + remoteUpdated[key] = local[key]; } } // Updated snippets in Remote - for (const key of values(baseToRemote.updated)) { - if (handledConflicts.has(key)) { + for (const key of baseToRemote.updated.values()) { + if (conflicts.has(key)) { continue; } // Got updated in local if (baseToLocal.updated.has(key)) { // Has different value if (localToRemote.updated.has(key)) { - handleConflict(key); + conflicts.add(key); } } else if (local[key] !== undefined) { - updated[key] = remote[key]; + localUpdated[key] = remote[key]; } } // Added snippets in Local - for (const key of values(baseToLocal.added)) { - if (handledConflicts.has(key)) { + for (const key of baseToLocal.added.values()) { + if (conflicts.has(key)) { continue; } // Got added in remote if (baseToRemote.added.has(key)) { // Has different value if (localToRemote.updated.has(key)) { - handleConflict(key); + conflicts.add(key); } } else { - remoteContent[key] = local[key]; + remoteAdded[key] = local[key]; } } // Added snippets in remote - for (const key of values(baseToRemote.added)) { - if (handledConflicts.has(key)) { + for (const key of baseToRemote.added.values()) { + if (conflicts.has(key)) { continue; } // Got added in local if (baseToLocal.added.has(key)) { // Has different value if (localToRemote.updated.has(key)) { - handleConflict(key); + conflicts.add(key); } } else { - added[key] = remote[key]; + localAdded[key] = remote[key]; } } - return { added, removed: values(removed), updated, conflicts: values(conflicts), remote: areSame(remote, remoteContent) ? null : remoteContent }; + return { + local: { added: localAdded, removed: [...localRemoved.values()], updated: localUpdated }, + remote: { added: remoteAdded, removed: [...remoteRemoved.values()], updated: remoteUpdated }, + conflicts: [...conflicts.values()], + }; } function compare(from: IStringDictionary | null, to: IStringDictionary | null): { added: Set, removed: Set, updated: Set } { @@ -196,7 +171,7 @@ function compare(from: IStringDictionary | null, to: IStringDictionary, b: IStringDictionary): boolean { +export function areSame(a: IStringDictionary, b: IStringDictionary): boolean { const { added, removed, updated } = compare(a, b); return added.size === 0 && removed.size === 0 && updated.size === 0; } diff --git a/src/vs/platform/userDataSync/common/snippetsSync.ts b/src/vs/platform/userDataSync/common/snippetsSync.ts index d802e543cf4..a40a457b535 100644 --- a/src/vs/platform/userDataSync/common/snippetsSync.ts +++ b/src/vs/platform/userDataSync/common/snippetsSync.ts @@ -3,52 +3,50 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSynchroniser, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, Conflict, USER_DATA_SYNC_SCHEME, PREVIEW_DIR_NAME, UserDataSyncError, UserDataSyncErrorCode, ISyncResourceHandle, ISyncPreviewResult } from 'vs/platform/userDataSync/common/userDataSync'; +import { + IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSynchroniser, SyncResource, IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, + USER_DATA_SYNC_SCHEME, ISyncResourceHandle, IRemoteUserData, ISyncData, Change +} from 'vs/platform/userDataSync/common/userDataSync'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService, FileChangesEvent, IFileStat, IFileContent, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { AbstractSynchroniser, IRemoteUserData, ISyncData } from 'vs/platform/userDataSync/common/abstractSynchronizer'; +import { AbstractSynchroniser, IAcceptResult, IFileResourcePreview, IMergeResult } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStringDictionary } from 'vs/base/common/collections'; import { URI } from 'vs/base/common/uri'; -import { joinPath, extname, relativePath, isEqualOrParent, isEqual, basename, dirname } from 'vs/base/common/resources'; +import { joinPath, extname, relativePath, isEqualOrParent, basename, dirname } from 'vs/base/common/resources'; import { VSBuffer } from 'vs/base/common/buffer'; -import { merge } from 'vs/platform/userDataSync/common/snippetsMerge'; -import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; +import { merge, IMergeResult as ISnippetsMergeResult, areSame } from 'vs/platform/userDataSync/common/snippetsMerge'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { deepClone } from 'vs/base/common/objects'; -interface ISinppetsSyncPreviewResult extends ISyncPreviewResult { - readonly local: IStringDictionary; - readonly remoteUserData: IRemoteUserData; - readonly lastSyncUserData: IRemoteUserData | null; - readonly added: IStringDictionary; - readonly updated: IStringDictionary; - readonly removed: string[]; - readonly conflicts: Conflict[]; - readonly resolvedConflicts: IStringDictionary; - readonly remote: IStringDictionary | null; +interface ISnippetsResourcePreview extends IFileResourcePreview { + previewResult: IMergeResult; +} + +interface ISnippetsAcceptedResourcePreview extends IFileResourcePreview { + acceptResult: IAcceptResult; } export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserDataSynchroniser { protected readonly version: number = 1; private readonly snippetsFolder: URI; - private readonly snippetsPreviewFolder: URI; - private syncPreviewResultPromise: CancelablePromise | null = null; constructor( @IEnvironmentService environmentService: IEnvironmentService, @IFileService fileService: IFileService, + @IStorageService storageService: IStorageService, @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, @IUserDataSyncBackupStoreService userDataSyncBackupStoreService: IUserDataSyncBackupStoreService, @IUserDataSyncLogService logService: IUserDataSyncLogService, @IConfigurationService configurationService: IConfigurationService, - @IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService, + @IUserDataSyncResourceEnablementService userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, @ITelemetryService telemetryService: ITelemetryService, ) { - super(SyncResource.Snippets, fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService); + super(SyncResource.Snippets, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncResourceEnablementService, telemetryService, logService, configurationService); this.snippetsFolder = environmentService.snippetsHome; - this.snippetsPreviewFolder = joinPath(this.syncFolder, PREVIEW_DIR_NAME); this._register(this.fileService.watch(environmentService.userRoamingDataHome)); this._register(this.fileService.watch(this.snippetsFolder)); this._register(this.fileService.onDidFilesChange(e => this.onFileChanges(e))); @@ -58,98 +56,291 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD if (!e.changes.some(change => isEqualOrParent(change.resource, this.snippetsFolder))) { return; } - if (!this.isEnabled()) { - return; + this.triggerLocalChange(); + } + + protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise { + const local = await this.getSnippetsFileContents(); + const localSnippets = this.toSnippetsContents(local); + const remoteSnippets: IStringDictionary | null = remoteUserData.syncData ? this.parseSnippets(remoteUserData.syncData) : null; + const lastSyncSnippets: IStringDictionary | null = lastSyncUserData && lastSyncUserData.syncData ? this.parseSnippets(lastSyncUserData.syncData) : null; + + if (remoteSnippets) { + this.logService.trace(`${this.syncResourceLogLabel}: Merging remote snippets with local snippets...`); + } else { + this.logService.trace(`${this.syncResourceLogLabel}: Remote snippets does not exist. Synchronizing snippets for the first time.`); } - // Sync again if local file has changed and current status is in conflicts - if (this.status === SyncStatus.HasConflicts) { - this.syncPreviewResultPromise!.then(result => { - this.cancel(); - this.doSync(result.remoteUserData, result.lastSyncUserData).then(status => this.setStatus(status)); + + const mergeResult = merge(localSnippets, remoteSnippets, lastSyncSnippets); + return this.getResourcePreviews(mergeResult, local, remoteSnippets || {}); + } + + protected async getMergeResult(resourcePreview: ISnippetsResourcePreview, token: CancellationToken): Promise { + return resourcePreview.previewResult; + } + + protected async getAcceptResult(resourcePreview: ISnippetsResourcePreview, resource: URI, content: string | null | undefined, token: CancellationToken): Promise { + + /* Accept local resource */ + if (isEqualOrParent(resource, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }))) { + return { + content: resourcePreview.fileContent ? resourcePreview.fileContent.value.toString() : null, + localChange: Change.None, + remoteChange: resourcePreview.fileContent + ? resourcePreview.remoteContent !== null ? Change.Modified : Change.Added + : Change.Deleted + }; + } + + /* Accept remote resource */ + if (isEqualOrParent(resource, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }))) { + return { + content: resourcePreview.remoteContent, + localChange: resourcePreview.remoteContent !== null + ? resourcePreview.fileContent ? Change.Modified : Change.Added + : Change.Deleted, + remoteChange: Change.None, + }; + } + + /* Accept preview resource */ + if (isEqualOrParent(resource, this.syncPreviewFolder)) { + if (content === undefined) { + return { + content: resourcePreview.previewResult.content, + localChange: resourcePreview.previewResult.localChange, + remoteChange: resourcePreview.previewResult.remoteChange, + }; + } else { + return { + content, + localChange: content === null + ? resourcePreview.fileContent !== null ? Change.Deleted : Change.None + : Change.Modified, + remoteChange: content === null + ? resourcePreview.remoteContent !== null ? Change.Deleted : Change.None + : Change.Modified + }; + } + } + + throw new Error(`Invalid Resource: ${resource.toString()}`); + } + + protected async applyResult(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, resourcePreviews: [ISnippetsResourcePreview, IAcceptResult][], force: boolean): Promise { + const accptedResourcePreviews: ISnippetsAcceptedResourcePreview[] = resourcePreviews.map(([resourcePreview, acceptResult]) => ({ ...resourcePreview, acceptResult })); + if (accptedResourcePreviews.every(({ localChange, remoteChange }) => localChange === Change.None && remoteChange === Change.None)) { + this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing snippets.`); + } + + if (accptedResourcePreviews.some(({ localChange }) => localChange !== Change.None)) { + // back up all snippets + await this.updateLocalBackup(accptedResourcePreviews); + await this.updateLocalSnippets(accptedResourcePreviews, force); + } + + if (accptedResourcePreviews.some(({ remoteChange }) => remoteChange !== Change.None)) { + remoteUserData = await this.updateRemoteSnippets(accptedResourcePreviews, remoteUserData, force); + } + + if (lastSyncUserData?.ref !== remoteUserData.ref) { + // update last sync + this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized snippets...`); + await this.updateLastSyncUserData(remoteUserData); + this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized snippets`); + } + + for (const { previewResource } of accptedResourcePreviews) { + // Delete the preview + try { + await this.fileService.del(previewResource); + } catch (e) { /* ignore */ } + } + + } + + private getResourcePreviews(snippetsMergeResult: ISnippetsMergeResult, localFileContent: IStringDictionary, remoteSnippets: IStringDictionary): ISnippetsResourcePreview[] { + const resourcePreviews: Map = new Map(); + + /* Snippets added remotely -> add locally */ + for (const key of Object.keys(snippetsMergeResult.local.added)) { + const previewResult: IMergeResult = { + content: snippetsMergeResult.local.added[key], + hasConflicts: false, + localChange: Change.Added, + remoteChange: Change.None, + }; + resourcePreviews.set(key, { + fileContent: null, + localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), + localContent: null, + remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), + remoteContent: remoteSnippets[key], + previewResource: joinPath(this.syncPreviewFolder, key), + previewResult, + localChange: previewResult.localChange, + remoteChange: previewResult.remoteChange, + acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) }); } - // Otherwise fire change event - else { - this._onDidChangeLocal.fire(); - } - } - async pull(): Promise { - if (!this.isEnabled()) { - this.logService.info(`${this.syncResourceLogLabel}: Skipped pulling snippets as it is disabled.`); - return; + /* Snippets updated remotely -> update locally */ + for (const key of Object.keys(snippetsMergeResult.local.updated)) { + const previewResult: IMergeResult = { + content: snippetsMergeResult.local.updated[key], + hasConflicts: false, + localChange: Change.Modified, + remoteChange: Change.None, + }; + resourcePreviews.set(key, { + localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), + fileContent: localFileContent[key], + localContent: localFileContent[key].value.toString(), + remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), + remoteContent: remoteSnippets[key], + previewResource: joinPath(this.syncPreviewFolder, key), + previewResult, + localChange: previewResult.localChange, + remoteChange: previewResult.remoteChange, + acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) + }); } - this.stop(); + /* Snippets removed remotely -> remove locally */ + for (const key of snippetsMergeResult.local.removed) { + const previewResult: IMergeResult = { + content: null, + hasConflicts: false, + localChange: Change.Deleted, + remoteChange: Change.None, + }; + resourcePreviews.set(key, { + localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), + fileContent: localFileContent[key], + localContent: localFileContent[key].value.toString(), + remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), + remoteContent: null, + previewResource: joinPath(this.syncPreviewFolder, key), + previewResult, + localChange: previewResult.localChange, + remoteChange: previewResult.remoteChange, + acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) + }); + } - try { - this.logService.info(`${this.syncResourceLogLabel}: Started pulling snippets...`); - this.setStatus(SyncStatus.Syncing); + /* Snippets added locally -> add remotely */ + for (const key of Object.keys(snippetsMergeResult.remote.added)) { + const previewResult: IMergeResult = { + content: snippetsMergeResult.remote.added[key], + hasConflicts: false, + localChange: Change.None, + remoteChange: Change.Added, + }; + resourcePreviews.set(key, { + localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), + fileContent: localFileContent[key], + localContent: localFileContent[key].value.toString(), + remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), + remoteContent: null, + previewResource: joinPath(this.syncPreviewFolder, key), + previewResult, + localChange: previewResult.localChange, + remoteChange: previewResult.remoteChange, + acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) + }); + } - const lastSyncUserData = await this.getLastSyncUserData(); - const remoteUserData = await this.getRemoteUserData(lastSyncUserData); + /* Snippets updated locally -> update remotely */ + for (const key of Object.keys(snippetsMergeResult.remote.updated)) { + const previewResult: IMergeResult = { + content: snippetsMergeResult.remote.updated[key], + hasConflicts: false, + localChange: Change.None, + remoteChange: Change.Modified, + }; + resourcePreviews.set(key, { + localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), + fileContent: localFileContent[key], + localContent: localFileContent[key].value.toString(), + remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), + remoteContent: remoteSnippets[key], + previewResource: joinPath(this.syncPreviewFolder, key), + previewResult, + localChange: previewResult.localChange, + remoteChange: previewResult.remoteChange, + acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) + }); + } - if (remoteUserData.syncData !== null) { - const local = await this.getSnippetsFileContents(); - const localSnippets = this.toSnippetsContents(local); - const remoteSnippets = this.parseSnippets(remoteUserData.syncData); - const { added, updated, remote, removed } = merge(localSnippets, remoteSnippets, localSnippets); - this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve({ - added, removed, updated, remote, remoteUserData, local, lastSyncUserData, conflicts: [], resolvedConflicts: {}, - hasLocalChanged: Object.keys(added).length > 0 || removed.length > 0 || Object.keys(updated).length > 0, - hasRemoteChanged: remote !== null - })); - await this.apply(); + /* Snippets removed locally -> remove remotely */ + for (const key of snippetsMergeResult.remote.removed) { + const previewResult: IMergeResult = { + content: null, + hasConflicts: false, + localChange: Change.None, + remoteChange: Change.Deleted, + }; + resourcePreviews.set(key, { + localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), + fileContent: null, + localContent: null, + remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), + remoteContent: remoteSnippets[key], + previewResource: joinPath(this.syncPreviewFolder, key), + previewResult, + localChange: previewResult.localChange, + remoteChange: previewResult.remoteChange, + acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) + }); + } + + /* Snippets with conflicts */ + for (const key of snippetsMergeResult.conflicts) { + const previewResult: IMergeResult = { + content: localFileContent[key] ? localFileContent[key].value.toString() : null, + hasConflicts: true, + localChange: localFileContent[key] ? Change.Modified : Change.Added, + remoteChange: remoteSnippets[key] ? Change.Modified : Change.Added + }; + resourcePreviews.set(key, { + localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), + fileContent: localFileContent[key] || null, + localContent: localFileContent[key] ? localFileContent[key].value.toString() : null, + remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), + remoteContent: remoteSnippets[key] || null, + previewResource: joinPath(this.syncPreviewFolder, key), + previewResult, + localChange: previewResult.localChange, + remoteChange: previewResult.remoteChange, + acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) + }); + } + + /* Unmodified Snippets */ + for (const key of Object.keys(localFileContent)) { + if (!resourcePreviews.has(key)) { + const previewResult: IMergeResult = { + content: localFileContent[key] ? localFileContent[key].value.toString() : null, + hasConflicts: false, + localChange: Change.None, + remoteChange: Change.None + }; + resourcePreviews.set(key, { + localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }), + fileContent: localFileContent[key] || null, + localContent: localFileContent[key] ? localFileContent[key].value.toString() : null, + remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }), + remoteContent: remoteSnippets[key] || null, + previewResource: joinPath(this.syncPreviewFolder, key), + previewResult, + localChange: previewResult.localChange, + remoteChange: previewResult.remoteChange, + acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }) + }); } - - // No remote exists to pull - else { - this.logService.info(`${this.syncResourceLogLabel}: Remote snippets does not exist.`); - } - - this.logService.info(`${this.syncResourceLogLabel}: Finished pulling snippets.`); - } finally { - this.setStatus(SyncStatus.Idle); - } - } - - async push(): Promise { - if (!this.isEnabled()) { - this.logService.info(`${this.syncResourceLogLabel}: Skipped pushing snippets as it is disabled.`); - return; } - this.stop(); - - try { - this.logService.info(`${this.syncResourceLogLabel}: Started pushing snippets...`); - this.setStatus(SyncStatus.Syncing); - - const local = await this.getSnippetsFileContents(); - const localSnippets = this.toSnippetsContents(local); - const { added, removed, updated, remote } = merge(localSnippets, null, null); - const lastSyncUserData = await this.getLastSyncUserData(); - const remoteUserData = await this.getRemoteUserData(lastSyncUserData); - this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve({ - added, removed, updated, remote, remoteUserData, local, lastSyncUserData, conflicts: [], resolvedConflicts: {}, - hasLocalChanged: Object.keys(added).length > 0 || removed.length > 0 || Object.keys(updated).length > 0, - hasRemoteChanged: remote !== null - })); - - await this.apply(true); - - this.logService.info(`${this.syncResourceLogLabel}: Finished pushing snippets.`); - } finally { - this.setStatus(SyncStatus.Idle); - } - - } - - async stop(): Promise { - await this.clearConflicts(); - this.cancel(); - this.logService.info(`${this.syncResourceLogLabel}: Stopped synchronizing ${this.syncResourceLogLabel}.`); - this.setStatus(SyncStatus.Idle); + return [...resourcePreviews.values()]; } async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> { @@ -172,13 +363,17 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD } async resolveContent(uri: URI): Promise { - if (isEqualOrParent(uri.with({ scheme: this.syncFolder.scheme }), this.snippetsPreviewFolder)) { - return this.getConflictContent(uri); + if (isEqualOrParent(uri, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' })) + || isEqualOrParent(uri, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' })) + || isEqualOrParent(uri, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }))) { + return this.resolvePreviewContent(uri); } + let content = await super.resolveContent(uri); if (content) { return content; } + content = await super.resolveContent(dirname(uri)); if (content) { const syncData = this.parseSyncData(content); @@ -187,40 +382,10 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD return snippets[basename(uri)] || null; } } - return null; - } - protected async getConflictContent(conflictResource: URI): Promise { - if (this.syncPreviewResultPromise) { - const result = await this.syncPreviewResultPromise; - const key = relativePath(this.snippetsPreviewFolder, conflictResource.with({ scheme: this.snippetsPreviewFolder.scheme }))!; - if (conflictResource.scheme === this.snippetsPreviewFolder.scheme) { - return result.local[key] ? result.local[key].value.toString() : null; - } else if (result.remoteUserData && result.remoteUserData.syncData) { - const snippets = this.parseSnippets(result.remoteUserData.syncData); - return snippets[key] || null; - } - } return null; } - async acceptConflict(conflictResource: URI, content: string): Promise { - const conflict = this.conflicts.filter(({ local, remote }) => isEqual(local, conflictResource) || isEqual(remote, conflictResource))[0]; - if (this.status === SyncStatus.HasConflicts && conflict) { - const key = relativePath(this.snippetsPreviewFolder, conflict.local)!; - let previewResult = await this.syncPreviewResultPromise!; - this.cancel(); - previewResult.resolvedConflicts[key] = content || null; - this.syncPreviewResultPromise = createCancelablePromise(token => this.doGeneratePreview(previewResult.local, previewResult.remoteUserData, previewResult.lastSyncUserData, previewResult.resolvedConflicts, token)); - previewResult = await this.syncPreviewResultPromise; - this.setConflicts(previewResult.conflicts); - if (!this.conflicts.length) { - await this.apply(); - this.setStatus(SyncStatus.Idle); - } - } - } - async hasLocalData(): Promise { try { const localSnippets = await this.getSnippetsFileContents(); @@ -233,160 +398,68 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD return false; } - protected async performSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise { - try { - const previewResult = await this.getPreview(remoteUserData, lastSyncUserData); - this.setConflicts(previewResult.conflicts); - if (this.conflicts.length) { - return SyncStatus.HasConflicts; + private async updateLocalBackup(resourcePreviews: IFileResourcePreview[]): Promise { + const local: IStringDictionary = {}; + for (const resourcePreview of resourcePreviews) { + if (resourcePreview.fileContent) { + local[basename(resourcePreview.localResource!)] = resourcePreview.fileContent; } - await this.apply(); - return SyncStatus.Idle; - } catch (e) { - this.syncPreviewResultPromise = null; - if (e instanceof UserDataSyncError) { - switch (e.code) { - case UserDataSyncErrorCode.LocalPreconditionFailed: - // Rejected as there is a new local version. Syncing again. - this.logService.info(`${this.syncResourceLogLabel}: Failed to synchronize snippets as there is a new local version available. Synchronizing again...`); - return this.performSync(remoteUserData, lastSyncUserData); + } + await this.backupLocal(JSON.stringify(this.toSnippetsContents(local))); + } + + private async updateLocalSnippets(resourcePreviews: ISnippetsAcceptedResourcePreview[], force: boolean): Promise { + for (const { fileContent, acceptResult, localResource, remoteResource, localChange } of resourcePreviews) { + if (localChange !== Change.None) { + const key = remoteResource ? basename(remoteResource) : basename(localResource!); + const resource = joinPath(this.snippetsFolder, key); + + // Removed + if (localChange === Change.Deleted) { + this.logService.trace(`${this.syncResourceLogLabel}: Deleting snippet...`, basename(resource)); + await this.fileService.del(resource); + this.logService.info(`${this.syncResourceLogLabel}: Deleted snippet`, basename(resource)); + } + + // Added + else if (localChange === Change.Added) { + this.logService.trace(`${this.syncResourceLogLabel}: Creating snippet...`, basename(resource)); + await this.fileService.createFile(resource, VSBuffer.fromString(acceptResult.content!), { overwrite: force }); + this.logService.info(`${this.syncResourceLogLabel}: Created snippet`, basename(resource)); + } + + // Updated + else { + this.logService.trace(`${this.syncResourceLogLabel}: Updating snippet...`, basename(resource)); + await this.fileService.writeFile(resource, VSBuffer.fromString(acceptResult.content!), force ? undefined : fileContent!); + this.logService.info(`${this.syncResourceLogLabel}: Updated snippet`, basename(resource)); } } - throw e; } } - protected getPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise { - if (!this.syncPreviewResultPromise) { - this.syncPreviewResultPromise = createCancelablePromise(token => this.generatePreview(remoteUserData, lastSyncUserData, token)); - } - return this.syncPreviewResultPromise; - } + private async updateRemoteSnippets(resourcePreviews: ISnippetsAcceptedResourcePreview[], remoteUserData: IRemoteUserData, forcePush: boolean): Promise { + const currentSnippets: IStringDictionary = remoteUserData.syncData ? this.parseSnippets(remoteUserData.syncData) : {}; + const newSnippets: IStringDictionary = deepClone(currentSnippets); - protected cancel(): void { - if (this.syncPreviewResultPromise) { - this.syncPreviewResultPromise.cancel(); - this.syncPreviewResultPromise = null; - } - } - - private async clearConflicts(): Promise { - if (this.conflicts.length) { - await Promise.all(this.conflicts.map(({ local }) => this.fileService.del(local))); - this.setConflicts([]); - } - } - - protected async generatePreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken = CancellationToken.None): Promise { - return this.getSnippetsFileContents() - .then(local => this.doGeneratePreview(local, remoteUserData, lastSyncUserData, {}, token)); - } - - private async doGeneratePreview(local: IStringDictionary, remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, resolvedConflicts: IStringDictionary = {}, token: CancellationToken = CancellationToken.None): Promise { - const localSnippets = this.toSnippetsContents(local); - const remoteSnippets: IStringDictionary | null = remoteUserData.syncData ? this.parseSnippets(remoteUserData.syncData) : null; - const lastSyncSnippets: IStringDictionary | null = lastSyncUserData ? this.parseSnippets(lastSyncUserData.syncData!) : null; - - if (remoteSnippets) { - this.logService.trace(`${this.syncResourceLogLabel}: Merging remote snippets with local snippets...`); - } else { - this.logService.trace(`${this.syncResourceLogLabel}: Remote snippets does not exist. Synchronizing snippets for the first time.`); - } - - const mergeResult = merge(localSnippets, remoteSnippets, lastSyncSnippets, resolvedConflicts); - - const conflicts: Conflict[] = []; - for (const key of mergeResult.conflicts) { - const localPreview = joinPath(this.snippetsPreviewFolder, key); - conflicts.push({ local: localPreview, remote: localPreview.with({ scheme: USER_DATA_SYNC_SCHEME }) }); - const content = local[key]; - if (!token.isCancellationRequested) { - await this.fileService.writeFile(localPreview, content ? content.value : VSBuffer.fromString('')); - } - } - - for (const conflict of this.conflicts) { - // clear obsolete conflicts - if (!conflicts.some(({ local }) => isEqual(local, conflict.local))) { - try { - await this.fileService.del(conflict.local); - } catch (error) { - // Ignore & log - this.logService.error(error); + for (const { acceptResult, localResource, remoteResource, remoteChange } of resourcePreviews) { + if (remoteChange !== Change.None) { + const key = localResource ? basename(localResource) : basename(remoteResource!); + if (remoteChange === Change.Deleted) { + delete newSnippets[key]; + } else { + newSnippets[key] = acceptResult.content!; } } } - return { - remoteUserData, local, - lastSyncUserData, - added: mergeResult.added, - removed: mergeResult.removed, - updated: mergeResult.updated, - conflicts, - remote: mergeResult.remote, - resolvedConflicts, - hasLocalChanged: Object.keys(mergeResult.added).length > 0 || mergeResult.removed.length > 0 || Object.keys(mergeResult.updated).length > 0, - hasRemoteChanged: mergeResult.remote !== null - }; - } - - private async apply(forcePush?: boolean): Promise { - if (!this.syncPreviewResultPromise) { - return; - } - - let { added, removed, updated, local, remote, remoteUserData, lastSyncUserData, hasLocalChanged, hasRemoteChanged } = await this.syncPreviewResultPromise; - - if (!hasLocalChanged && !hasRemoteChanged) { - this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing snippets.`); - } - - if (hasLocalChanged) { - // back up all snippets - await this.backupLocal(JSON.stringify(this.toSnippetsContents(local))); - await this.updateLocalSnippets(added, removed, updated, local); - } - - if (remote) { + if (!areSame(currentSnippets, newSnippets)) { // update remote this.logService.trace(`${this.syncResourceLogLabel}: Updating remote snippets...`); - const content = JSON.stringify(remote); - remoteUserData = await this.updateRemoteUserData(content, forcePush ? null : remoteUserData.ref); + remoteUserData = await this.updateRemoteUserData(JSON.stringify(newSnippets), forcePush ? null : remoteUserData.ref); this.logService.info(`${this.syncResourceLogLabel}: Updated remote snippets`); } - - if (lastSyncUserData?.ref !== remoteUserData.ref) { - // update last sync - this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized snippets...`); - await this.updateLastSyncUserData(remoteUserData); - this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized snippets`); - } - - this.syncPreviewResultPromise = null; - } - - private async updateLocalSnippets(added: IStringDictionary, removed: string[], updated: IStringDictionary, local: IStringDictionary): Promise { - for (const key of removed) { - const resource = joinPath(this.snippetsFolder, key); - this.logService.trace(`${this.syncResourceLogLabel}: Deleting snippet...`, basename(resource)); - await this.fileService.del(resource); - this.logService.info(`${this.syncResourceLogLabel}: Deleted snippet`, basename(resource)); - } - - for (const key of Object.keys(added)) { - const resource = joinPath(this.snippetsFolder, key); - this.logService.trace(`${this.syncResourceLogLabel}: Creating snippet...`, basename(resource)); - await this.fileService.createFile(resource, VSBuffer.fromString(added[key]), { overwrite: false }); - this.logService.info(`${this.syncResourceLogLabel}: Created snippet`, basename(resource)); - } - - for (const key of Object.keys(updated)) { - const resource = joinPath(this.snippetsFolder, key); - this.logService.trace(`${this.syncResourceLogLabel}: Updating snippet...`, basename(resource)); - await this.fileService.writeFile(resource, VSBuffer.fromString(updated[key]), local[key]); - this.logService.info(`${this.syncResourceLogLabel}: Updated snippet`, basename(resource)); - } + return remoteUserData; } private parseSnippets(syncData: ISyncData): IStringDictionary { diff --git a/src/vs/platform/userDataSync/common/storageKeys.ts b/src/vs/platform/userDataSync/common/storageKeys.ts index 43c150d4b3a..10c2db84ee6 100644 --- a/src/vs/platform/userDataSync/common/storageKeys.ts +++ b/src/vs/platform/userDataSync/common/storageKeys.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; -import { values } from 'vs/base/common/map'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -43,7 +42,7 @@ export class StorageKeysSyncRegistryService extends Disposable implements IStora _serviceBrand: any; private readonly _storageKeys = new Map(); - get storageKeys(): ReadonlyArray { return values(this._storageKeys); } + get storageKeys(): ReadonlyArray { return [...this._storageKeys.values()]; } private readonly _onDidChangeStorageKeys: Emitter> = this._register(new Emitter>()); readonly onDidChangeStorageKeys = this._onDidChangeStorageKeys.event; diff --git a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts index 5c5b984a302..2ca29d000b0 100644 --- a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts @@ -3,128 +3,522 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { timeout, Delayer } from 'vs/base/common/async'; +import { Delayer, disposableTimeout, CancelablePromise, createCancelablePromise, timeout } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { IUserDataSyncLogService, IUserDataSyncService, SyncStatus, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; -import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication'; +import { Disposable, toDisposable, MutableDisposable, IDisposable } from 'vs/base/common/lifecycle'; +import { IUserDataSyncLogService, IUserDataSyncService, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, IUserDataSyncResourceEnablementService, IUserDataSyncStoreService, UserDataAutoSyncError, ISyncTask, IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { isPromiseCanceledError } from 'vs/base/common/errors'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; +import { localize } from 'vs/nls'; +import { toLocalISOString } from 'vs/base/common/date'; +import { URI } from 'vs/base/common/uri'; +import { isEqual } from 'vs/base/common/resources'; -type AutoSyncTriggerClassification = { - source: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; +type AutoSyncClassification = { + sources: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; }; -export class UserDataAutoSyncService extends Disposable implements IUserDataAutoSyncService { +type AutoSyncEnablementClassification = { + enabled?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; +}; - _serviceBrand: any; +type AutoSyncErrorClassification = { + code: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; + service: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; +}; - private enabled: boolean = false; - private successiveFailures: number = 0; - private readonly syncDelayer: Delayer; +const enablementKey = 'sync.enable'; +const disableMachineEventuallyKey = 'sync.disableMachineEventually'; +const sessionIdKey = 'sync.sessionId'; +const storeUrlKey = 'sync.storeUrl'; - private readonly _onError: Emitter = this._register(new Emitter()); - readonly onError: Event = this._onError.event; +export class UserDataAutoSyncEnablementService extends Disposable { + + private _onDidChangeEnablement = new Emitter(); + readonly onDidChangeEnablement: Event = this._onDidChangeEnablement.event; constructor( - @IUserDataSyncEnablementService private readonly userDataSyncEnablementService: IUserDataSyncEnablementService, - @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, - @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, - @IAuthenticationTokenService private readonly authTokenService: IAuthenticationTokenService, - @ITelemetryService private readonly telemetryService: ITelemetryService, + @IStorageService protected readonly storageService: IStorageService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IUserDataSyncStoreManagementService protected readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService ) { super(); - this.updateEnablement(false, true); - this.syncDelayer = this._register(new Delayer(0)); - this._register(Event.any(authTokenService.onDidChangeToken)(() => this.updateEnablement(true, true))); - this._register(Event.any(userDataSyncService.onDidChangeStatus)(() => this.updateEnablement(true, true))); - this._register(this.userDataSyncEnablementService.onDidChangeEnablement(() => this.updateEnablement(true, false))); - this._register(this.userDataSyncEnablementService.onDidChangeResourceEnablement(() => this.triggerAutoSync(['resourceEnablement']))); + this._register(storageService.onDidChangeStorage(e => this.onDidStorageChange(e))); } - private async updateEnablement(stopIfDisabled: boolean, auto: boolean): Promise { - const { enabled, reason } = await this.isAutoSyncEnabled(); - if (this.enabled === enabled) { - return; + isEnabled(): boolean { + switch (this.environmentService.sync) { + case 'on': + return true; + case 'off': + return false; } + return this.storageService.getBoolean(enablementKey, StorageScope.GLOBAL, this.environmentService.enableSyncByDefault); + } - this.enabled = enabled; - if (this.enabled) { - this.logService.info('Auto Sync: Started'); - this.sync(true, auto); - return; - } else { - this.resetFailures(); - if (stopIfDisabled) { - this.userDataSyncService.stop(); - this.logService.info('Auto Sync: stopped because', reason); + canToggleEnablement(): boolean { + return this.userDataSyncStoreManagementService.userDataSyncStore !== undefined && this.environmentService.sync === undefined; + } + + private onDidStorageChange(workspaceStorageChangeEvent: IWorkspaceStorageChangeEvent): void { + if (workspaceStorageChangeEvent.scope === StorageScope.GLOBAL) { + if (enablementKey === workspaceStorageChangeEvent.key) { + this._onDidChangeEnablement.fire(this.isEnabled()); } } - - } - - private async sync(loop: boolean, auto: boolean): Promise { - if (this.enabled) { - try { - await this.userDataSyncService.sync(); - this.resetFailures(); - } catch (e) { - const error = UserDataSyncError.toUserDataSyncError(e); - if (error.code === UserDataSyncErrorCode.TurnedOff || error.code === UserDataSyncErrorCode.SessionExpired) { - this.logService.info('Auto Sync: Sync is turned off in the cloud.'); - this.logService.info('Auto Sync: Resetting the local sync state.'); - await this.userDataSyncService.resetLocal(); - this.logService.info('Auto Sync: Completed resetting the local sync state.'); - if (auto) { - this.userDataSyncEnablementService.setEnablement(false); - this._onError.fire(error); - return; - } else { - return this.sync(loop, auto); - } - } - this.logService.error(error); - this.successiveFailures++; - this._onError.fire(error); - } - if (loop) { - await timeout(1000 * 60 * 5); - this.sync(loop, true); - } - } else { - this.logService.trace('Auto Sync: Not syncing as it is disabled.'); - } - } - - private async isAutoSyncEnabled(): Promise<{ enabled: boolean, reason?: string }> { - if (!this.userDataSyncEnablementService.isEnabled()) { - return { enabled: false, reason: 'sync is disabled' }; - } - if (this.userDataSyncService.status === SyncStatus.Uninitialized) { - return { enabled: false, reason: 'sync is not initialized' }; - } - const token = await this.authTokenService.getToken(); - if (!token) { - return { enabled: false, reason: 'token is not avaialable' }; - } - return { enabled: true }; - } - - private resetFailures(): void { - this.successiveFailures = 0; - } - - async triggerAutoSync(sources: string[]): Promise { - sources.forEach(source => this.telemetryService.publicLog2<{ source: string }, AutoSyncTriggerClassification>('sync/triggerAutoSync', { source })); - if (this.enabled) { - return this.syncDelayer.trigger(() => { - this.logService.info('Auto Sync: Triggered.'); - return this.sync(false, true); - }, this.successiveFailures - ? 1000 * 1 * Math.min(this.successiveFailures, 60) /* Delay by number of seconds as number of failures up to 1 minute */ - : 1000); - } else { - this.syncDelayer.cancel(); - } + } + +} + +export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService implements IUserDataAutoSyncService { + + _serviceBrand: any; + + private readonly autoSync = this._register(new MutableDisposable()); + private successiveFailures: number = 0; + private lastSyncTriggerTime: number | undefined = undefined; + private readonly syncTriggerDelayer: Delayer; + + private readonly _onError: Emitter = this._register(new Emitter()); + readonly onError: Event = this._onError.event; + + private lastSyncUrl: URI | undefined; + private get syncUrl(): URI | undefined { + const value = this.storageService.get(storeUrlKey, StorageScope.GLOBAL); + return value ? URI.parse(value) : undefined; + } + private set syncUrl(syncUrl: URI | undefined) { + if (syncUrl) { + this.storageService.store(storeUrlKey, syncUrl.toString(), StorageScope.GLOBAL); + } else { + this.storageService.remove(storeUrlKey, StorageScope.GLOBAL); + } + } + + constructor( + @IUserDataSyncStoreManagementService userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, + @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService, + @IUserDataSyncResourceEnablementService private readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, + @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, + @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, + @IUserDataSyncAccountService private readonly userDataSyncAccountService: IUserDataSyncAccountService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IUserDataSyncMachinesService private readonly userDataSyncMachinesService: IUserDataSyncMachinesService, + @IStorageService storageService: IStorageService, + @IEnvironmentService environmentService: IEnvironmentService + ) { + super(storageService, environmentService, userDataSyncStoreManagementService); + this.syncTriggerDelayer = this._register(new Delayer(0)); + this.lastSyncUrl = this.syncUrl; + this.syncUrl = userDataSyncStoreManagementService.userDataSyncStore?.url; + + if (userDataSyncStoreManagementService.userDataSyncStore) { + if (this.isEnabled()) { + this.logService.info('Auto Sync is enabled.'); + } else { + this.logService.info('Auto Sync is disabled.'); + } + this.updateAutoSync(); + + if (this.hasToDisableMachineEventually()) { + this.disableMachineEventually(); + } + + this._register(userDataSyncAccountService.onDidChangeAccount(() => this.updateAutoSync())); + this._register(userDataSyncStoreService.onDidChangeDonotMakeRequestsUntil(() => this.updateAutoSync())); + this._register(Event.debounce(userDataSyncService.onDidChangeLocal, (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, false))); + this._register(Event.filter(this.userDataSyncResourceEnablementService.onDidChangeResourceEnablement, ([, enabled]) => enabled)(() => this.triggerSync(['resourceEnablement'], false))); + } + } + + private updateAutoSync(): void { + const { enabled, message } = this.isAutoSyncEnabled(); + if (enabled) { + if (this.autoSync.value === undefined) { + this.autoSync.value = new AutoSync(this.lastSyncUrl, 1000 * 60 * 5 /* 5 miutes */, this.userDataSyncStoreManagementService, this.userDataSyncStoreService, this.userDataSyncService, this.userDataSyncMachinesService, this.logService, this.storageService); + this.autoSync.value.register(this.autoSync.value.onDidStartSync(() => this.lastSyncTriggerTime = new Date().getTime())); + this.autoSync.value.register(this.autoSync.value.onDidFinishSync(e => this.onDidFinishSync(e))); + if (this.startAutoSync()) { + this.autoSync.value.start(); + } + } + } else { + this.syncTriggerDelayer.cancel(); + if (this.autoSync.value !== undefined) { + if (message) { + this.logService.info(message); + } + this.autoSync.clear(); + } + + /* log message when auto sync is not disabled by user */ + else if (message && this.isEnabled()) { + this.logService.info(message); + } + } + } + + // For tests purpose only + protected startAutoSync(): boolean { return true; } + + private isAutoSyncEnabled(): { enabled: boolean, message?: string } { + if (!this.isEnabled()) { + return { enabled: false, message: 'Auto Sync: Disabled.' }; + } + if (!this.userDataSyncAccountService.account) { + return { enabled: false, message: 'Auto Sync: Suspended until auth token is available.' }; + } + if (this.userDataSyncStoreService.donotMakeRequestsUntil) { + return { enabled: false, message: `Auto Sync: Suspended until ${toLocalISOString(this.userDataSyncStoreService.donotMakeRequestsUntil)} because server is not accepting requests until then.` }; + } + return { enabled: true }; + } + + async turnOn(): Promise { + this.stopDisableMachineEventually(); + this.lastSyncUrl = this.syncUrl; + this.setEnablement(true); + } + + async turnOff(everywhere: boolean, softTurnOffOnError?: boolean, donotRemoveMachine?: boolean): Promise { + try { + + // Remove machine + if (this.userDataSyncAccountService.account && !donotRemoveMachine) { + await this.userDataSyncMachinesService.removeCurrentMachine(); + } + + // Disable Auto Sync + this.setEnablement(false); + + // Reset Session + this.storageService.remove(sessionIdKey, StorageScope.GLOBAL); + + // Reset + if (everywhere) { + this.telemetryService.publicLog2('sync/turnOffEveryWhere'); + await this.userDataSyncService.reset(); + } else { + await this.userDataSyncService.resetLocal(); + } + } catch (error) { + if (softTurnOffOnError) { + this.logService.error(error); + this.setEnablement(false); + } else { + throw error; + } + } + } + + private setEnablement(enabled: boolean): void { + if (this.isEnabled() !== enabled) { + this.telemetryService.publicLog2<{ enabled: boolean }, AutoSyncEnablementClassification>(enablementKey, { enabled }); + this.storageService.store(enablementKey, enabled, StorageScope.GLOBAL); + this.updateAutoSync(); + } + } + + private async onDidFinishSync(error: Error | undefined): Promise { + if (!error) { + // Sync finished without errors + this.successiveFailures = 0; + return; + } + + // Error while syncing + const userDataSyncError = UserDataSyncError.toUserDataSyncError(error); + + // Log to telemetry + if (userDataSyncError instanceof UserDataAutoSyncError) { + this.telemetryService.publicLog2<{ code: string, service: string }, AutoSyncErrorClassification>(`autosync/error`, { code: userDataSyncError.code, service: this.userDataSyncStoreManagementService.userDataSyncStore!.url.toString() }); + } + + // Session got expired + if (userDataSyncError.code === UserDataSyncErrorCode.SessionExpired) { + await this.turnOff(false, true /* force soft turnoff on error */); + this.logService.info('Auto Sync: Turned off sync because current session is expired'); + } + + // Turned off from another device + else if (userDataSyncError.code === UserDataSyncErrorCode.TurnedOff) { + await this.turnOff(false, true /* force soft turnoff on error */); + this.logService.info('Auto Sync: Turned off sync because sync is turned off in the cloud'); + } + + // Exceeded Rate Limit + else if (userDataSyncError.code === UserDataSyncErrorCode.LocalTooManyRequests || userDataSyncError.code === UserDataSyncErrorCode.TooManyRequests) { + await this.turnOff(false, true /* force soft turnoff on error */, + true /* do not disable machine because disabling a machine makes request to server and can fail with TooManyRequests */); + this.disableMachineEventually(); + this.logService.info('Auto Sync: Turned off sync because of making too many requests to server'); + } + + // Upgrade Required or Gone + else if (userDataSyncError.code === UserDataSyncErrorCode.UpgradeRequired || userDataSyncError.code === UserDataSyncErrorCode.Gone) { + await this.turnOff(false, true /* force soft turnoff on error */, + true /* do not disable machine because disabling a machine makes request to server and can fail with upgrade required or gone */); + this.disableMachineEventually(); + this.logService.info('Auto Sync: Turned off sync because current client is not compatible with server. Requires client upgrade.'); + } + + // Incompatible Local Content + else if (userDataSyncError.code === UserDataSyncErrorCode.IncompatibleLocalContent) { + await this.turnOff(false, true /* force soft turnoff on error */); + this.logService.info('Auto Sync: Turned off sync because server has {0} content with newer version than of client. Requires client upgrade.', userDataSyncError.resource); + } + + // Incompatible Remote Content + else if (userDataSyncError.code === UserDataSyncErrorCode.IncompatibleRemoteContent) { + await this.turnOff(false, true /* force soft turnoff on error */); + this.logService.info('Auto Sync: Turned off sync because server has {0} content with older version than of client. Requires server reset.', userDataSyncError.resource); + } + + // Service changed + else if (userDataSyncError.code === UserDataSyncErrorCode.ServiceChanged || userDataSyncError.code === UserDataSyncErrorCode.DefaultServiceChanged) { + await this.turnOff(false, true /* force soft turnoff on error */, true /* do not disable machine */); + await this.turnOn(); + this.logService.info('Auto Sync: Sync Service changed. Turned off auto sync, reset local state and turned on auto sync.'); + } + + else { + this.logService.error(userDataSyncError); + this.successiveFailures++; + } + + this._onError.fire(userDataSyncError); + } + + private async disableMachineEventually(): Promise { + this.storageService.store(disableMachineEventuallyKey, true, StorageScope.GLOBAL); + await timeout(1000 * 60 * 10); + + // Return if got stopped meanwhile. + if (!this.hasToDisableMachineEventually()) { + return; + } + + this.stopDisableMachineEventually(); + + // disable only if sync is disabled + if (!this.isEnabled() && this.userDataSyncAccountService.account) { + await this.userDataSyncMachinesService.removeCurrentMachine(); + } + } + + private hasToDisableMachineEventually(): boolean { + return this.storageService.getBoolean(disableMachineEventuallyKey, StorageScope.GLOBAL, false); + } + + private stopDisableMachineEventually(): void { + this.storageService.remove(disableMachineEventuallyKey, StorageScope.GLOBAL); + } + + private sources: string[] = []; + async triggerSync(sources: string[], skipIfSyncedRecently: boolean): Promise { + if (this.autoSync.value === undefined) { + return this.syncTriggerDelayer.cancel(); + } + + if (skipIfSyncedRecently && this.lastSyncTriggerTime + && Math.round((new Date().getTime() - this.lastSyncTriggerTime) / 1000) < 10) { + this.logService.debug('Auto Sync: Skipped. Limited to once per 10 seconds.'); + return; + } + + this.sources.push(...sources); + return this.syncTriggerDelayer.trigger(async () => { + this.logService.trace('activity sources', ...this.sources); + this.telemetryService.publicLog2<{ sources: string[] }, AutoSyncClassification>('sync/triggered', { sources: this.sources }); + this.sources = []; + if (this.autoSync.value) { + await this.autoSync.value.sync('Activity'); + } + }, this.successiveFailures + ? this.getSyncTriggerDelayTime() * 1 * Math.min(Math.pow(2, this.successiveFailures), 60) /* Delay exponentially until max 1 minute */ + : this.getSyncTriggerDelayTime()); + + } + + protected getSyncTriggerDelayTime(): number { + return 1000; /* Debounce for a second if there are no failures */ + } + +} + +class AutoSync extends Disposable { + + private static readonly INTERVAL_SYNCING = 'Interval'; + + private readonly intervalHandler = this._register(new MutableDisposable()); + + private readonly _onDidStartSync = this._register(new Emitter()); + readonly onDidStartSync = this._onDidStartSync.event; + + private readonly _onDidFinishSync = this._register(new Emitter()); + readonly onDidFinishSync = this._onDidFinishSync.event; + + private syncTask: ISyncTask | undefined; + private syncPromise: CancelablePromise | undefined; + + constructor( + private readonly lastSyncUrl: URI | undefined, + private readonly interval: number /* in milliseconds */, + private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, + private readonly userDataSyncStoreService: IUserDataSyncStoreService, + private readonly userDataSyncService: IUserDataSyncService, + private readonly userDataSyncMachinesService: IUserDataSyncMachinesService, + private readonly logService: IUserDataSyncLogService, + private readonly storageService: IStorageService, + ) { + super(); + } + + start(): void { + this._register(this.onDidFinishSync(() => this.waitUntilNextIntervalAndSync())); + this._register(toDisposable(() => { + if (this.syncPromise) { + this.syncPromise.cancel(); + this.logService.info('Auto sync: Cancelled sync that is in progress'); + this.syncPromise = undefined; + } + if (this.syncTask) { + this.syncTask.stop(); + } + this.logService.info('Auto Sync: Stopped'); + })); + this.logService.info('Auto Sync: Started'); + this.sync(AutoSync.INTERVAL_SYNCING); + } + + private waitUntilNextIntervalAndSync(): void { + this.intervalHandler.value = disposableTimeout(() => this.sync(AutoSync.INTERVAL_SYNCING), this.interval); + } + + sync(reason: string): Promise { + const syncPromise = createCancelablePromise(async token => { + if (this.syncPromise) { + try { + // Wait until existing sync is finished + this.logService.debug('Auto Sync: Waiting until sync is finished.'); + await this.syncPromise; + } catch (error) { + if (isPromiseCanceledError(error)) { + // Cancelled => Disposed. Donot continue sync. + return; + } + } + } + return this.doSync(reason, token); + }); + this.syncPromise = syncPromise; + this.syncPromise.finally(() => this.syncPromise = undefined); + return this.syncPromise; + } + + private hasSyncServiceChanged(): boolean { + return this.lastSyncUrl !== undefined && !isEqual(this.lastSyncUrl, this.userDataSyncStoreManagementService.userDataSyncStore?.url); + } + + private async hasDefaultServiceChanged(): Promise { + const previous = await this.userDataSyncStoreManagementService.getPreviousUserDataSyncStore(); + const current = this.userDataSyncStoreManagementService.userDataSyncStore; + // check if defaults changed + return !!current && !!previous && + (!isEqual(current.defaultUrl, previous.defaultUrl) || + !isEqual(current.insidersUrl, previous.insidersUrl) || + !isEqual(current.stableUrl, previous.stableUrl)); + } + + private async doSync(reason: string, token: CancellationToken): Promise { + this.logService.info(`Auto Sync: Triggered by ${reason}`); + this._onDidStartSync.fire(); + let error: Error | undefined; + try { + this.syncTask = await this.userDataSyncService.createSyncTask(); + if (token.isCancellationRequested) { + return; + } + let manifest = this.syncTask.manifest; + + // Server has no data but this machine was synced before + if (manifest === null && await this.userDataSyncService.hasPreviouslySynced()) { + if (this.hasSyncServiceChanged()) { + if (await this.hasDefaultServiceChanged()) { + throw new UserDataAutoSyncError(localize('default service changed', "Cannot sync because sync default servuce has changed"), UserDataSyncErrorCode.DefaultServiceChanged); + } else { + throw new UserDataAutoSyncError(localize('service changed', "Cannot sync because sync service has changed"), UserDataSyncErrorCode.ServiceChanged); + } + } else { + // Sync was turned off in the cloud + throw new UserDataAutoSyncError(localize('turned off', "Cannot sync because syncing is turned off in the cloud"), UserDataSyncErrorCode.TurnedOff); + } + } + + const sessionId = this.storageService.get(sessionIdKey, StorageScope.GLOBAL); + // Server session is different from client session + if (sessionId && manifest && sessionId !== manifest.session) { + if (this.hasSyncServiceChanged()) { + if (await this.hasDefaultServiceChanged()) { + throw new UserDataAutoSyncError(localize('default service changed', "Cannot sync because sync default servuce has changed"), UserDataSyncErrorCode.DefaultServiceChanged); + } else { + throw new UserDataAutoSyncError(localize('service changed', "Cannot sync because sync service has changed"), UserDataSyncErrorCode.ServiceChanged); + } + } else { + throw new UserDataAutoSyncError(localize('session expired', "Cannot sync because current session is expired"), UserDataSyncErrorCode.SessionExpired); + } + } + + const machines = await this.userDataSyncMachinesService.getMachines(manifest || undefined); + // Return if cancellation is requested + if (token.isCancellationRequested) { + return; + } + + const currentMachine = machines.find(machine => machine.isCurrent); + // Check if sync was turned off from other machine + if (currentMachine?.disabled) { + // Throw TurnedOff error + throw new UserDataAutoSyncError(localize('turned off machine', "Cannot sync because syncing is turned off on this machine from another machine."), UserDataSyncErrorCode.TurnedOff); + } + + await this.syncTask.run(); + + // After syncing, get the manifest if it was not available before + if (manifest === null) { + manifest = await this.userDataSyncStoreService.manifest(); + } + + // Update local session id + if (manifest && manifest.session !== sessionId) { + this.storageService.store(sessionIdKey, manifest.session, StorageScope.GLOBAL); + } + + // Return if cancellation is requested + if (token.isCancellationRequested) { + return; + } + + // Add current machine + if (!currentMachine) { + await this.userDataSyncMachinesService.addCurrentMachine(manifest || undefined); + } + + } catch (e) { + this.logService.error(e); + error = e; + } + + this._onDidFinishSync.fire(error); + } + + register(t: T): T { + return super._register(t); } } diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index eec0c4502a1..80d901977a4 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -6,7 +6,6 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; import { IExtensionIdentifier, EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope, allSettings } from 'vs/platform/configuration/common/configurationRegistry'; import { localize } from 'vs/nls'; @@ -14,15 +13,14 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { ILogService } from 'vs/platform/log/common/log'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStringDictionary } from 'vs/base/common/collections'; import { FormattingOptions } from 'vs/base/common/jsonFormatter'; import { URI } from 'vs/base/common/uri'; import { joinPath, isEqualOrParent } from 'vs/base/common/resources'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IProductService, ConfigurationSyncStore } from 'vs/platform/product/common/productService'; import { distinct } from 'vs/base/common/arrays'; import { isArray, isString, isObject } from 'vs/base/common/types'; +import { IHeaders } from 'vs/base/parts/request/common/request'; export const CONFIGURATION_SYNC_STORE_KEY = 'configurationSync.store'; @@ -110,8 +108,11 @@ export interface IUserData { export type IAuthenticationProvider = { id: string, scopes: string[] }; export interface IUserDataSyncStore { - url: URI; - authenticationProviders: IAuthenticationProvider[]; + readonly url: URI; + readonly defaultUrl: URI; + readonly stableUrl: URI | undefined; + readonly insidersUrl: URI | undefined; + readonly authenticationProviders: IAuthenticationProvider[]; } export function isAuthenticationProvider(thing: any): thing is IAuthenticationProvider { @@ -121,24 +122,6 @@ export function isAuthenticationProvider(thing: any): thing is IAuthenticationPr && isArray(thing.scopes); } -export function getUserDataSyncStore(productService: IProductService, configurationService: IConfigurationService): IUserDataSyncStore | undefined { - const value = configurationService.getValue(CONFIGURATION_SYNC_STORE_KEY) || productService[CONFIGURATION_SYNC_STORE_KEY]; - if (value - && isString(value.url) - && isObject(value.authenticationProviders) - && Object.keys(value.authenticationProviders).every(authenticationProviderId => isArray(value.authenticationProviders[authenticationProviderId].scopes)) - ) { - return { - url: joinPath(URI.parse(value.url), 'v1'), - authenticationProviders: Object.keys(value.authenticationProviders).reduce((result, id) => { - result.push({ id, scopes: value.authenticationProviders[id].scopes }); - return result; - }, []) - }; - } - return undefined; -} - export const enum SyncResource { Settings = 'settings', Keybindings = 'keybindings', @@ -149,7 +132,7 @@ export const enum SyncResource { export const ALL_SYNC_RESOURCES: SyncResource[] = [SyncResource.Settings, SyncResource.Keybindings, SyncResource.Snippets, SyncResource.Extensions, SyncResource.GlobalState]; export interface IUserDataManifest { - latest?: Record + latest?: Record session: string; } @@ -158,22 +141,42 @@ export interface IResourceRefHandle { created: number; } +export type ServerResource = SyncResource | 'machines'; +export type UserDataSyncStoreType = 'insiders' | 'stable'; + +export const IUserDataSyncStoreManagementService = createDecorator('IUserDataSyncStoreManagementService'); +export interface IUserDataSyncStoreManagementService { + readonly _serviceBrand: undefined; + readonly userDataSyncStore: IUserDataSyncStore | undefined; + switch(type: UserDataSyncStoreType): Promise; + getPreviousUserDataSyncStore(): Promise; +} + export const IUserDataSyncStoreService = createDecorator('IUserDataSyncStoreService'); export interface IUserDataSyncStoreService { - _serviceBrand: undefined; - readonly userDataSyncStore: IUserDataSyncStore | undefined; - read(resource: SyncResource, oldValue: IUserData | null): Promise; - write(resource: SyncResource, content: string, ref: string | null): Promise; - manifest(): Promise; + readonly _serviceBrand: undefined; + + readonly onDidChangeDonotMakeRequestsUntil: Event; + readonly donotMakeRequestsUntil: Date | undefined; + + readonly onTokenFailed: Event; + readonly onTokenSucceed: Event; + setAuthToken(token: string, type: string): void; + + // Sync requests + manifest(headers?: IHeaders): Promise; + read(resource: ServerResource, oldValue: IUserData | null, headers?: IHeaders): Promise; + write(resource: ServerResource, content: string, ref: string | null, headers?: IHeaders): Promise; clear(): Promise; - getAllRefs(resource: SyncResource): Promise; - resolveContent(resource: SyncResource, ref: string): Promise; - delete(resource: SyncResource): Promise; + delete(resource: ServerResource): Promise; + + getAllRefs(resource: ServerResource): Promise; + resolveContent(resource: ServerResource, ref: string): Promise; } export const IUserDataSyncBackupStoreService = createDecorator('IUserDataSyncBackupStoreService'); export interface IUserDataSyncBackupStoreService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; backup(resource: SyncResource, content: string): Promise; getAllRefs(resource: SyncResource): Promise; resolveContent(resource: SyncResource, ref?: string): Promise; @@ -181,50 +184,87 @@ export interface IUserDataSyncBackupStoreService { //#endregion +// #region User Data Sync Headers + +export const HEADER_OPERATION_ID = 'x-operation-id'; +export const HEADER_EXECUTION_ID = 'X-Execution-Id'; + +//#endregion + // #region User Data Sync Error export enum UserDataSyncErrorCode { - // Server Errors - Unauthorized = 'Unauthorized', - Forbidden = 'Forbidden', + // Client Errors (>= 400 ) + Unauthorized = 'Unauthorized', /* 401 */ + Gone = 'Gone', /* 410 */ + PreconditionFailed = 'PreconditionFailed', /* 412 */ + TooLarge = 'TooLarge', /* 413 */ + UpgradeRequired = 'UpgradeRequired', /* 426 */ + PreconditionRequired = 'PreconditionRequired', /* 428 */ + TooManyRequests = 'RemoteTooManyRequests', /* 429 */ + TooManyRequestsAndRetryAfter = 'TooManyRequestsAndRetryAfter', /* 429 + Retry-After */ + + // Local Errors ConnectionRefused = 'ConnectionRefused', - RemotePreconditionFailed = 'RemotePreconditionFailed', - TooLarge = 'TooLarge', NoRef = 'NoRef', TurnedOff = 'TurnedOff', SessionExpired = 'SessionExpired', - - // Local Errors + ServiceChanged = 'ServiceChanged', + DefaultServiceChanged = 'DefaultServiceChanged', + LocalTooManyRequests = 'LocalTooManyRequests', LocalPreconditionFailed = 'LocalPreconditionFailed', LocalInvalidContent = 'LocalInvalidContent', LocalError = 'LocalError', - Incompatible = 'Incompatible', + IncompatibleLocalContent = 'IncompatibleLocalContent', + IncompatibleRemoteContent = 'IncompatibleRemoteContent', + UnresolvedConflicts = 'UnresolvedConflicts', Unknown = 'Unknown', } export class UserDataSyncError extends Error { - constructor(message: string, public readonly code: UserDataSyncErrorCode, public readonly resource?: SyncResource) { + constructor( + message: string, + readonly code: UserDataSyncErrorCode, + readonly resource?: SyncResource, + readonly operationId?: string + ) { super(message); - this.name = `${this.code} (UserDataSyncError) ${this.resource}`; + this.name = `${this.code} (UserDataSyncError) syncResource:${this.resource || 'unknown'} operationId:${this.operationId || 'unknown'}`; } - static toUserDataSyncError(error: Error): UserDataSyncError { - if (error instanceof UserDataSyncStoreError) { +} + +export class UserDataSyncStoreError extends UserDataSyncError { + constructor(message: string, code: UserDataSyncErrorCode, readonly operationId: string | undefined) { + super(message, code, undefined, operationId); + } +} + +export class UserDataAutoSyncError extends UserDataSyncError { + constructor(message: string, code: UserDataSyncErrorCode) { + super(message, code); + } +} + +export namespace UserDataSyncError { + + export function toUserDataSyncError(error: Error): UserDataSyncError { + if (error instanceof UserDataSyncError) { return error; } - const match = /^(.+) \(UserDataSyncError\) (.+)?$/.exec(error.name); + const match = /^(.+) \(UserDataSyncError\) syncResource:(.+) operationId:(.+)$/.exec(error.name); if (match && match[1]) { - return new UserDataSyncError(error.message, match[1], match[2]); + const syncResource = match[2] === 'unknown' ? undefined : match[2] as SyncResource; + const operationId = match[3] === 'unknown' ? undefined : match[3]; + return new UserDataSyncError(error.message, match[1], syncResource, operationId); } return new UserDataSyncError(error.message, UserDataSyncErrorCode.Unknown); } } -export class UserDataSyncStoreError extends UserDataSyncError { } - //#endregion // #region User Data Synchroniser @@ -233,6 +273,7 @@ export interface ISyncExtension { identifier: IExtensionIdentifier; version?: string; disabled?: boolean; + installed?: boolean; } export interface IStorageValue { @@ -256,11 +297,43 @@ export interface ISyncResourceHandle { uri: URI; } -export type Conflict = { remote: URI, local: URI }; +export interface IRemoteUserData { + ref: string; + syncData: ISyncData | null; +} -export interface ISyncPreviewResult { - readonly hasLocalChanged: boolean; - readonly hasRemoteChanged: boolean; +export interface ISyncData { + version: number; + machineId?: string; + content: string; +} + +export const enum Change { + None, + Added, + Modified, + Deleted, +} + +export const enum MergeState { + Preview = 'preview', + Conflict = 'conflict', + Accepted = 'accepted', +} + +export interface IResourcePreview { + readonly remoteResource: URI; + readonly localResource: URI; + readonly previewResource: URI; + readonly acceptedResource: URI; + readonly localChange: Change; + readonly remoteChange: Change; + readonly mergeState: MergeState; +} + +export interface ISyncResourcePreview { + readonly isLastSyncFromCurrentMachine: boolean; + readonly resourcePreviews: IResourcePreview[]; } export interface IUserDataSynchroniser { @@ -268,48 +341,65 @@ export interface IUserDataSynchroniser { readonly resource: SyncResource; readonly status: SyncStatus; readonly onDidChangeStatus: Event; - readonly conflicts: Conflict[]; - readonly onDidChangeConflicts: Event; + + readonly conflicts: IResourcePreview[]; + readonly onDidChangeConflicts: Event; + readonly onDidChangeLocal: Event; - pull(): Promise; - push(): Promise; - sync(ref?: string): Promise; + sync(manifest: IUserDataManifest | null, headers: IHeaders): Promise; + replace(uri: URI): Promise; stop(): Promise; - getSyncPreview(): Promise - hasPreviouslySynced(): Promise + preview(manifest: IUserDataManifest | null, headers: IHeaders): Promise; + accept(resource: URI, content?: string | null): Promise; + merge(resource: URI): Promise; + discard(resource: URI): Promise; + apply(force: boolean, headers: IHeaders): Promise; + + hasPreviouslySynced(): Promise; hasLocalData(): Promise; resetLocal(): Promise; resolveContent(resource: URI): Promise; - acceptConflict(conflictResource: URI, content: string): Promise; - getRemoteSyncResourceHandles(): Promise; getLocalSyncResourceHandles(): Promise; getAssociatedResources(syncResourceHandle: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]>; + getMachineId(syncResourceHandle: ISyncResourceHandle): Promise; } //#endregion // #region User Data Sync Services -export const IUserDataSyncEnablementService = createDecorator('IUserDataSyncEnablementService'); -export interface IUserDataSyncEnablementService { +export const IUserDataSyncResourceEnablementService = createDecorator('IUserDataSyncResourceEnablementService'); +export interface IUserDataSyncResourceEnablementService { _serviceBrand: any; - readonly onDidChangeEnablement: Event; readonly onDidChangeResourceEnablement: Event<[SyncResource, boolean]>; - - isEnabled(): boolean; - setEnablement(enabled: boolean): void; - canToggleEnablement(): boolean; - isResourceEnabled(resource: SyncResource): boolean; setResourceEnablement(resource: SyncResource, enabled: boolean): void; } -export type SyncResourceConflicts = { syncResource: SyncResource, conflicts: Conflict[] }; +export interface ISyncTask { + readonly manifest: IUserDataManifest | null; + run(): Promise; + stop(): Promise; +} + +export interface IManualSyncTask extends IDisposable { + readonly id: string; + readonly manifest: IUserDataManifest | null; + readonly onSynchronizeResources: Event<[SyncResource, URI[]][]>; + preview(): Promise<[SyncResource, ISyncResourcePreview][]>; + accept(resource: URI, content?: string | null): Promise<[SyncResource, ISyncResourcePreview][]>; + merge(resource: URI): Promise<[SyncResource, ISyncResourcePreview][]>; + discard(resource: URI): Promise<[SyncResource, ISyncResourcePreview][]>; + apply(): Promise<[SyncResource, ISyncResourcePreview][]>; + pull(): Promise; + push(): Promise; + stop(): Promise; +} export const IUserDataSyncService = createDecorator('IUserDataSyncService'); export interface IUserDataSyncService { @@ -318,8 +408,8 @@ export interface IUserDataSyncService { readonly status: SyncStatus; readonly onDidChangeStatus: Event; - readonly conflicts: SyncResourceConflicts[]; - readonly onDidChangeConflicts: Event; + readonly conflicts: [SyncResource, IResourcePreview[]][]; + readonly onDidChangeConflicts: Event<[SyncResource, IResourcePreview[]][]>; readonly onDidChangeLocal: Event; readonly onSyncErrors: Event<[SyncResource, UserDataSyncError][]>; @@ -327,31 +417,40 @@ export interface IUserDataSyncService { readonly lastSyncTime: number | undefined; readonly onDidChangeLastSyncTime: Event; - pull(): Promise; - sync(): Promise; - stop(): Promise; + createSyncTask(): Promise; + createManualSyncTask(): Promise; + + replace(uri: URI): Promise; reset(): Promise; + resetRemote(): Promise; resetLocal(): Promise; - isFirstTimeSyncWithMerge(): Promise; + hasLocalData(): Promise; + hasPreviouslySynced(): Promise; resolveContent(resource: URI): Promise; - acceptConflict(conflictResource: URI, content: string): Promise; + accept(resource: SyncResource, conflictResource: URI, content: string | null | undefined, apply: boolean): Promise; getLocalSyncResourceHandles(resource: SyncResource): Promise; getRemoteSyncResourceHandles(resource: SyncResource): Promise; getAssociatedResources(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]>; + getMachineId(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise; } export const IUserDataAutoSyncService = createDecorator('IUserDataAutoSyncService'); export interface IUserDataAutoSyncService { _serviceBrand: any; readonly onError: Event; - triggerAutoSync(sources: string[]): Promise; + readonly onDidChangeEnablement: Event; + isEnabled(): boolean; + canToggleEnablement(): boolean; + turnOn(): Promise; + turnOff(everywhere: boolean): Promise; + triggerSync(sources: string[], hasToLimitSync: boolean): Promise; } export const IUserDataSyncUtilService = createDecorator('IUserDataSyncUtilService'); export interface IUserDataSyncUtilService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; resolveUserBindings(userbindings: string[]): Promise>; resolveFormattingOptions(resource: URI): Promise; resolveDefaultIgnoredSettings(): Promise; @@ -369,9 +468,6 @@ export interface IConflictSetting { //#endregion export const USER_DATA_SYNC_SCHEME = 'vscode-userdata-sync'; -export const CONTEXT_SYNC_STATE = new RawContextKey('syncStatus', SyncStatus.Uninitialized); -export const CONTEXT_SYNC_ENABLEMENT = new RawContextKey('syncEnabled', false); - export const PREVIEW_DIR_NAME = 'preview'; export function getSyncResourceFromLocalPreview(localPreview: URI, environmentService: IEnvironmentService): SyncResource | undefined { if (localPreview.scheme === USER_DATA_SYNC_SCHEME) { diff --git a/src/vs/platform/userDataSync/common/userDataSyncAccount.ts b/src/vs/platform/userDataSync/common/userDataSyncAccount.ts new file mode 100644 index 00000000000..eade8a601b4 --- /dev/null +++ b/src/vs/platform/userDataSync/common/userDataSyncAccount.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IUserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSync'; + +export interface IUserDataSyncAccount { + readonly authenticationProviderId: string; + readonly token: string; +} + +export const IUserDataSyncAccountService = createDecorator('IUserDataSyncAccountService'); +export interface IUserDataSyncAccountService { + readonly _serviceBrand: undefined; + + readonly onTokenFailed: Event; + readonly account: IUserDataSyncAccount | undefined; + readonly onDidChangeAccount: Event; + updateAccount(account: IUserDataSyncAccount | undefined): Promise; + +} + +export class UserDataSyncAccountService extends Disposable implements IUserDataSyncAccountService { + + _serviceBrand: any; + + private _account: IUserDataSyncAccount | undefined; + get account(): IUserDataSyncAccount | undefined { return this._account; } + private _onDidChangeAccount = this._register(new Emitter()); + readonly onDidChangeAccount = this._onDidChangeAccount.event; + + private _onTokenFailed: Emitter = this._register(new Emitter()); + readonly onTokenFailed: Event = this._onTokenFailed.event; + + private wasTokenFailed: boolean = false; + + constructor( + @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService + ) { + super(); + this._register(userDataSyncStoreService.onTokenFailed(() => { + this.updateAccount(undefined); + this._onTokenFailed.fire(this.wasTokenFailed); + this.wasTokenFailed = true; + })); + this._register(userDataSyncStoreService.onTokenSucceed(() => this.wasTokenFailed = false)); + } + + async updateAccount(account: IUserDataSyncAccount | undefined): Promise { + if (account && this._account ? account.token !== this._account.token || account.authenticationProviderId !== this._account.authenticationProviderId : account !== this._account) { + this._account = account; + if (this._account) { + this.userDataSyncStoreService.setAuthToken(this._account.token, this._account.authenticationProviderId); + } + this._onDidChangeAccount.fire(account); + } + } + +} + diff --git a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts index 1aeeecf920f..934b64ec000 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts @@ -3,18 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IServerChannel, IChannel, IPCServer } from 'vs/base/parts/ipc/common/ipc'; import { Event, Emitter } from 'vs/base/common/event'; -import { IUserDataSyncService, IUserDataSyncUtilService, IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, IUserDataSyncUtilService, IUserDataAutoSyncService, IManualSyncTask, IUserDataManifest, IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync'; import { URI } from 'vs/base/common/uri'; import { IStringDictionary } from 'vs/base/common/collections'; import { FormattingOptions } from 'vs/base/common/jsonFormatter'; import { IStorageKeysSyncRegistryService, IStorageKey } from 'vs/platform/userDataSync/common/storageKeys'; import { Disposable } from 'vs/base/common/lifecycle'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; +import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; export class UserDataSyncChannel implements IServerChannel { - constructor(private readonly service: IUserDataSyncService) { } + constructor(private server: IPCServer, private readonly service: IUserDataSyncService, private readonly logService: ILogService) { } listen(_: unknown, event: string): Event { switch (event) { @@ -27,23 +30,85 @@ export class UserDataSyncChannel implements IServerChannel { throw new Error(`Event not found: ${event}`); } - call(context: any, command: string, args?: any): Promise { + async call(context: any, command: string, args?: any): Promise { + try { + const result = await this._call(context, command, args); + return result; + } catch (e) { + this.logService.error(e); + throw e; + } + } + + private _call(context: any, command: string, args?: any): Promise { switch (command) { case '_getInitialData': return Promise.resolve([this.service.status, this.service.conflicts, this.service.lastSyncTime]); - case 'pull': return this.service.pull(); - case 'sync': return this.service.sync(); - case 'stop': this.service.stop(); return Promise.resolve(); + + case 'createManualSyncTask': return this.createManualSyncTask(); + + case 'replace': return this.service.replace(URI.revive(args[0])); case 'reset': return this.service.reset(); + case 'resetRemote': return this.service.resetRemote(); case 'resetLocal': return this.service.resetLocal(); - case 'isFirstTimeSyncWithMerge': return this.service.isFirstTimeSyncWithMerge(); - case 'acceptConflict': return this.service.acceptConflict(URI.revive(args[0]), args[1]); + case 'hasPreviouslySynced': return this.service.hasPreviouslySynced(); + case 'hasLocalData': return this.service.hasLocalData(); + case 'accept': return this.service.accept(args[0], URI.revive(args[1]), args[2], args[3]); case 'resolveContent': return this.service.resolveContent(URI.revive(args[0])); case 'getLocalSyncResourceHandles': return this.service.getLocalSyncResourceHandles(args[0]); case 'getRemoteSyncResourceHandles': return this.service.getRemoteSyncResourceHandles(args[0]); case 'getAssociatedResources': return this.service.getAssociatedResources(args[0], { created: args[1].created, uri: URI.revive(args[1].uri) }); + case 'getMachineId': return this.service.getMachineId(args[0], { created: args[1].created, uri: URI.revive(args[1].uri) }); } throw new Error('Invalid call'); } + + private async createManualSyncTask(): Promise<{ id: string, manifest: IUserDataManifest | null }> { + const manualSyncTask = await this.service.createManualSyncTask(); + const manualSyncTaskChannel = new ManualSyncTaskChannel(manualSyncTask, this.logService); + this.server.registerChannel(`manualSyncTask-${manualSyncTask.id}`, manualSyncTaskChannel); + return { id: manualSyncTask.id, manifest: manualSyncTask.manifest }; + } +} + +class ManualSyncTaskChannel implements IServerChannel { + + constructor( + private readonly manualSyncTask: IManualSyncTask, + private readonly logService: ILogService + ) { } + + listen(_: unknown, event: string): Event { + switch (event) { + case 'onSynchronizeResources': return this.manualSyncTask.onSynchronizeResources; + } + throw new Error(`Event not found: ${event}`); + } + + async call(context: any, command: string, args?: any): Promise { + try { + const result = await this._call(context, command, args); + return result; + } catch (e) { + this.logService.error(e); + throw e; + } + } + + private async _call(context: any, command: string, args?: any): Promise { + switch (command) { + case 'preview': return this.manualSyncTask.preview(); + case 'accept': return this.manualSyncTask.accept(URI.revive(args[0]), args[1]); + case 'merge': return this.manualSyncTask.merge(URI.revive(args[0])); + case 'discard': return this.manualSyncTask.discard(URI.revive(args[0])); + case 'apply': return this.manualSyncTask.apply(); + case 'pull': return this.manualSyncTask.pull(); + case 'push': return this.manualSyncTask.push(); + case 'stop': return this.manualSyncTask.stop(); + case 'dispose': return this.manualSyncTask.dispose(); + } + throw new Error('Invalid call'); + } + } export class UserDataAutoSyncChannel implements IServerChannel { @@ -59,7 +124,9 @@ export class UserDataAutoSyncChannel implements IServerChannel { call(context: any, command: string, args?: any): Promise { switch (command) { - case 'triggerAutoSync': return this.service.triggerAutoSync(args[0]); + case 'triggerSync': return this.service.triggerSync(args[0], args[1]); + case 'turnOn': return this.service.turnOn(); + case 'turnOff': return this.service.turnOff(args[0]); } throw new Error('Invalid call'); } @@ -85,7 +152,7 @@ export class UserDataSycnUtilServiceChannel implements IServerChannel { export class UserDataSyncUtilServiceClient implements IUserDataSyncUtilService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(private readonly channel: IChannel) { } @@ -126,7 +193,7 @@ export class StorageKeysSyncRegistryChannel implements IServerChannel { export class StorageKeysSyncRegistryChannelClient extends Disposable implements IStorageKeysSyncRegistryService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _storageKeys: ReadonlyArray = []; get storageKeys(): ReadonlyArray { return this._storageKeys; } @@ -151,3 +218,60 @@ export class StorageKeysSyncRegistryChannelClient extends Disposable implements } } + +export class UserDataSyncMachinesServiceChannel implements IServerChannel { + + constructor(private readonly service: IUserDataSyncMachinesService) { } + + listen(_: unknown, event: string): Event { + throw new Error(`Event not found: ${event}`); + } + + async call(context: any, command: string, args?: any): Promise { + switch (command) { + case 'getMachines': return this.service.getMachines(); + case 'addCurrentMachine': return this.service.addCurrentMachine(); + case 'removeCurrentMachine': return this.service.removeCurrentMachine(); + case 'renameMachine': return this.service.renameMachine(args[0], args[1]); + case 'setEnablement': return this.service.setEnablement(args[0], args[1]); + } + throw new Error('Invalid call'); + } + +} + +export class UserDataSyncAccountServiceChannel implements IServerChannel { + constructor(private readonly service: IUserDataSyncAccountService) { } + + listen(_: unknown, event: string): Event { + switch (event) { + case 'onDidChangeAccount': return this.service.onDidChangeAccount; + case 'onTokenFailed': return this.service.onTokenFailed; + } + throw new Error(`Event not found: ${event}`); + } + + call(context: any, command: string, args?: any): Promise { + switch (command) { + case '_getInitialData': return Promise.resolve(this.service.account); + case 'updateAccount': return this.service.updateAccount(args); + } + throw new Error('Invalid call'); + } +} + +export class UserDataSyncStoreManagementServiceChannel implements IServerChannel { + constructor(private readonly service: IUserDataSyncStoreManagementService) { } + + listen(_: unknown, event: string): Event { + throw new Error(`Event not found: ${event}`); + } + + call(context: any, command: string, args?: any): Promise { + switch (command) { + case 'switch': return this.service.switch(args[0]); + case 'getPreviousUserDataSyncStore': return this.service.getPreviousUserDataSyncStore(); + } + throw new Error('Invalid call'); + } +} diff --git a/src/vs/platform/userDataSync/common/userDataSyncLog.ts b/src/vs/platform/userDataSync/common/userDataSyncLog.ts index df1251ca3af..095ccda8462 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncLog.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncLog.ts @@ -9,7 +9,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' export class UserDataSyncLogService extends AbstractLogService implements IUserDataSyncLogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly logger: ILogger; constructor( diff --git a/src/vs/platform/userDataSync/common/userDataSyncMachines.ts b/src/vs/platform/userDataSync/common/userDataSyncMachines.ts new file mode 100644 index 00000000000..0bd7cd93992 --- /dev/null +++ b/src/vs/platform/userDataSync/common/userDataSyncMachines.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 { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IUserDataSyncStoreService, IUserData, IUserDataSyncLogService, IUserDataManifest } from 'vs/platform/userDataSync/common/userDataSync'; +import { localize } from 'vs/nls'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { PlatformToString, isWeb, Platform, platform } from 'vs/base/common/platform'; +import { escapeRegExpCharacters } from 'vs/base/common/strings'; + +interface IMachineData { + id: string; + name: string; + disabled?: boolean; +} + +interface IMachinesData { + version: number; + machines: IMachineData[]; +} + +export type IUserDataSyncMachine = Readonly & { readonly isCurrent: boolean }; + +export const IUserDataSyncMachinesService = createDecorator('IUserDataSyncMachinesService'); +export interface IUserDataSyncMachinesService { + _serviceBrand: any; + + getMachines(manifest?: IUserDataManifest): Promise; + + addCurrentMachine(manifest?: IUserDataManifest): Promise; + removeCurrentMachine(manifest?: IUserDataManifest): Promise; + renameMachine(machineId: string, name: string): Promise; + setEnablement(machineId: string, enabled: boolean): Promise; +} + +const currentMachineNameKey = 'sync.currentMachineName'; + +export class UserDataSyncMachinesService extends Disposable implements IUserDataSyncMachinesService { + + private static readonly VERSION = 1; + private static readonly RESOURCE = 'machines'; + + _serviceBrand: any; + + private readonly currentMachineIdPromise: Promise; + private userData: IUserData | null = null; + + constructor( + @IEnvironmentService environmentService: IEnvironmentService, + @IFileService fileService: IFileService, + @IStorageService private readonly storageService: IStorageService, + @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService, + @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, + @IProductService private readonly productService: IProductService, + ) { + super(); + this.currentMachineIdPromise = getServiceMachineId(environmentService, fileService, storageService); + } + + async getMachines(manifest?: IUserDataManifest): Promise { + const currentMachineId = await this.currentMachineIdPromise; + const machineData = await this.readMachinesData(manifest); + return machineData.machines.map(machine => ({ ...machine, ...{ isCurrent: machine.id === currentMachineId } })); + } + + async addCurrentMachine(manifest?: IUserDataManifest): Promise { + const currentMachineId = await this.currentMachineIdPromise; + const machineData = await this.readMachinesData(manifest); + if (!machineData.machines.some(({ id }) => id === currentMachineId)) { + machineData.machines.push({ id: currentMachineId, name: this.computeCurrentMachineName(machineData.machines) }); + await this.writeMachinesData(machineData); + } + } + + async removeCurrentMachine(manifest?: IUserDataManifest): Promise { + const currentMachineId = await this.currentMachineIdPromise; + const machineData = await this.readMachinesData(manifest); + const updatedMachines = machineData.machines.filter(({ id }) => id !== currentMachineId); + if (updatedMachines.length !== machineData.machines.length) { + machineData.machines = updatedMachines; + await this.writeMachinesData(machineData); + } + } + + async renameMachine(machineId: string, name: string, manifest?: IUserDataManifest): Promise { + const currentMachineId = await this.currentMachineIdPromise; + const machineData = await this.readMachinesData(manifest); + const machine = machineData.machines.find(({ id }) => id === machineId); + if (machine) { + machine.name = name; + await this.writeMachinesData(machineData); + if (machineData.machines.some(({ id }) => id === currentMachineId)) { + this.storageService.store(currentMachineNameKey, name, StorageScope.GLOBAL); + } + } + } + + async setEnablement(machineId: string, enabled: boolean): Promise { + const machineData = await this.readMachinesData(); + const machine = machineData.machines.find(({ id }) => id === machineId); + if (machine) { + machine.disabled = enabled ? undefined : true; + await this.writeMachinesData(machineData); + } + } + + private computeCurrentMachineName(machines: IMachineData[]): string { + const previousName = this.storageService.get(currentMachineNameKey, StorageScope.GLOBAL); + if (previousName) { + return previousName; + } + + const namePrefix = `${this.productService.nameLong} (${PlatformToString(isWeb ? Platform.Web : platform)})`; + const nameRegEx = new RegExp(`${escapeRegExpCharacters(namePrefix)}\\s#(\\d)`); + let nameIndex = 0; + for (const machine of machines) { + const matches = nameRegEx.exec(machine.name); + const index = matches ? parseInt(matches[1]) : 0; + nameIndex = index > nameIndex ? index : nameIndex; + } + return `${namePrefix} #${nameIndex + 1}`; + } + + private async readMachinesData(manifest?: IUserDataManifest): Promise { + this.userData = await this.readUserData(manifest); + const machinesData = this.parse(this.userData); + if (machinesData.version !== UserDataSyncMachinesService.VERSION) { + throw new Error(localize('error incompatible', "Cannot read machines data as the current version is incompatible. Please update {0} and try again.", this.productService.nameLong)); + } + return machinesData; + } + + private async writeMachinesData(machinesData: IMachinesData): Promise { + const content = JSON.stringify(machinesData); + const ref = await this.userDataSyncStoreService.write(UserDataSyncMachinesService.RESOURCE, content, this.userData?.ref || null); + this.userData = { ref, content }; + } + + private async readUserData(manifest?: IUserDataManifest): Promise { + if (this.userData) { + + const latestRef = manifest && manifest.latest ? manifest.latest[UserDataSyncMachinesService.RESOURCE] : undefined; + + // Last time synced resource and latest resource on server are same + if (this.userData.ref === latestRef) { + return this.userData; + } + + // There is no resource on server and last time it was synced with no resource + if (latestRef === undefined && this.userData.content === null) { + return this.userData; + } + } + + return this.userDataSyncStoreService.read(UserDataSyncMachinesService.RESOURCE, this.userData); + } + + private parse(userData: IUserData): IMachinesData { + if (userData.content !== null) { + try { + return JSON.parse(userData.content); + } catch (e) { + this.logService.error(e); + } + } + return { + version: UserDataSyncMachinesService.VERSION, + machines: [] + }; + } +} diff --git a/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts b/src/vs/platform/userDataSync/common/userDataSyncResourceEnablementService.ts similarity index 64% rename from src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts rename to src/vs/platform/userDataSync/common/userDataSyncResourceEnablementService.ts index 28b4b68e882..0ca0decce1c 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncResourceEnablementService.ts @@ -3,12 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IUserDataSyncEnablementService, ALL_SYNC_RESOURCES, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncResourceEnablementService, ALL_SYNC_RESOURCES, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; import { Disposable } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; import { IStorageService, IWorkspaceStorageChangeEvent, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; type SyncEnablementClassification = { enabled?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; @@ -17,46 +16,21 @@ type SyncEnablementClassification = { const enablementKey = 'sync.enable'; function getEnablementKey(resource: SyncResource) { return `${enablementKey}.${resource}`; } -export class UserDataSyncEnablementService extends Disposable implements IUserDataSyncEnablementService { +export class UserDataSyncResourceEnablementService extends Disposable implements IUserDataSyncResourceEnablementService { _serviceBrand: any; - private _onDidChangeEnablement = new Emitter(); - readonly onDidChangeEnablement: Event = this._onDidChangeEnablement.event; - private _onDidChangeResourceEnablement = new Emitter<[SyncResource, boolean]>(); readonly onDidChangeResourceEnablement: Event<[SyncResource, boolean]> = this._onDidChangeResourceEnablement.event; constructor( @IStorageService private readonly storageService: IStorageService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, ) { super(); this._register(storageService.onDidChangeStorage(e => this.onDidStorageChange(e))); } - canToggleEnablement(): boolean { - return this.environmentService.sync === undefined; - } - - isEnabled(): boolean { - switch (this.environmentService.sync) { - case 'on': - return true; - case 'off': - return false; - } - return this.storageService.getBoolean(enablementKey, StorageScope.GLOBAL, this.environmentService.enableSyncByDefault); - } - - setEnablement(enabled: boolean): void { - if (this.isEnabled() !== enabled) { - this.telemetryService.publicLog2<{ enabled: boolean }, SyncEnablementClassification>(enablementKey, { enabled }); - this.storageService.store(enablementKey, enabled, StorageScope.GLOBAL); - } - } - isResourceEnabled(resource: SyncResource): boolean { return this.storageService.getBoolean(getEnablementKey(resource), StorageScope.GLOBAL, true); } @@ -71,13 +45,9 @@ export class UserDataSyncEnablementService extends Disposable implements IUserDa private onDidStorageChange(workspaceStorageChangeEvent: IWorkspaceStorageChangeEvent): void { if (workspaceStorageChangeEvent.scope === StorageScope.GLOBAL) { - if (enablementKey === workspaceStorageChangeEvent.key) { - this._onDidChangeEnablement.fire(this.isEnabled()); - return; - } const resourceKey = ALL_SYNC_RESOURCES.filter(resourceKey => getEnablementKey(resourceKey) === workspaceStorageChangeEvent.key)[0]; if (resourceKey) { - this._onDidChangeResourceEnablement.fire([resourceKey, this.isEnabled()]); + this._onDidChangeResourceEnablement.fire([resourceKey, this.isResourceEnabled(resourceKey)]); return; } } diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 1c1bd056006..fbc100b733a 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -3,7 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncResource, IUserDataSyncLogService, IUserDataSynchroniser, UserDataSyncStoreError, UserDataSyncErrorCode, UserDataSyncError, SyncResourceConflicts, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync'; +import { + IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncResource, IUserDataSyncLogService, IUserDataSynchroniser, UserDataSyncErrorCode, + UserDataSyncError, ISyncResourceHandle, IUserDataManifest, ISyncTask, IResourcePreview, IManualSyncTask, ISyncResourcePreview, HEADER_EXECUTION_ID, MergeState, Change, IUserDataSyncStoreManagementService +} from 'vs/platform/userDataSync/common/userDataSync'; import { Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Emitter, Event } from 'vs/base/common/event'; @@ -13,20 +16,32 @@ import { GlobalStateSynchroniser } from 'vs/platform/userDataSync/common/globalS import { toErrorMessage } from 'vs/base/common/errorMessage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { equals } from 'vs/base/common/arrays'; -import { localize } from 'vs/nls'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { URI } from 'vs/base/common/uri'; import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync'; import { isEqual } from 'vs/base/common/resources'; import { SnippetsSynchroniser } from 'vs/platform/userDataSync/common/snippetsSync'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IHeaders } from 'vs/base/parts/request/common/request'; +import { generateUuid } from 'vs/base/common/uuid'; +import { createCancelablePromise, CancelablePromise } from 'vs/base/common/async'; +import { isPromiseCanceledError } from 'vs/base/common/errors'; type SyncErrorClassification = { - source: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; + code: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; + service: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; + resource?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; + executionId?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; }; -const SESSION_ID_KEY = 'sync.sessionId'; const LAST_SYNC_TIME_KEY = 'sync.lastSyncTime'; +function createSyncHeaders(executionId: string): IHeaders { + const headers: IHeaders = {}; + headers[HEADER_EXECUTION_ID] = executionId; + return headers; +} + export class UserDataSyncService extends Disposable implements IUserDataSyncService { _serviceBrand: any; @@ -40,10 +55,10 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ readonly onDidChangeLocal: Event; - private _conflicts: SyncResourceConflicts[] = []; - get conflicts(): SyncResourceConflicts[] { return this._conflicts; } - private _onDidChangeConflicts: Emitter = this._register(new Emitter()); - readonly onDidChangeConflicts: Event = this._onDidChangeConflicts.event; + private _conflicts: [SyncResource, IResourcePreview[]][] = []; + get conflicts(): [SyncResource, IResourcePreview[]][] { return this._conflicts; } + private _onDidChangeConflicts: Emitter<[SyncResource, IResourcePreview[]][]> = this._register(new Emitter<[SyncResource, IResourcePreview[]][]>()); + readonly onDidChangeConflicts: Event<[SyncResource, IResourcePreview[]][]> = this._onDidChangeConflicts.event; private _syncErrors: [SyncResource, UserDataSyncError][] = []; private _onSyncErrors: Emitter<[SyncResource, UserDataSyncError][]> = this._register(new Emitter<[SyncResource, UserDataSyncError][]>()); @@ -62,10 +77,11 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ constructor( @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService, + @IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IStorageService private readonly storageService: IStorageService + @IStorageService private readonly storageService: IStorageService, ) { super(); this.settingsSynchroniser = this._register(this.instantiationService.createInstance(SettingsSynchroniser)); @@ -76,7 +92,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ this.synchronisers = [this.settingsSynchroniser, this.keybindingsSynchroniser, this.snippetsSynchroniser, this.globalStateSynchroniser, this.extensionsSynchroniser]; this.updateStatus(); - if (this.userDataSyncStoreService.userDataSyncStore) { + if (this.userDataSyncStoreManagementService.userDataSyncStore) { this._register(Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeStatus, () => undefined)))(() => this.updateStatus())); this._register(Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeConflicts, () => undefined)))(() => this.updateConflicts())); } @@ -85,32 +101,69 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeLocal, () => s.resource))); } - async pull(): Promise { + async createSyncTask(): Promise { await this.checkEnablement(); - for (const synchroniser of this.synchronisers) { - try { - await synchroniser.pull(); - } catch (e) { - this.handleSyncError(e, synchroniser.resource); - } + + const executionId = generateUuid(); + let manifest: IUserDataManifest | null; + try { + manifest = await this.userDataSyncStoreService.manifest(createSyncHeaders(executionId)); + } catch (error) { + error = UserDataSyncError.toUserDataSyncError(error); + this.telemetryService.publicLog2<{ code: string, service: string, resource?: string, executionId?: string }, SyncErrorClassification>('sync/error', { code: error.code, resource: error.resource, executionId, service: this.userDataSyncStoreManagementService.userDataSyncStore!.url.toString() }); + throw error; } - this.updateLastSyncTime(); + + let executed = false; + const that = this; + let cancellablePromise: CancelablePromise | undefined; + return { + manifest, + run(): Promise { + if (executed) { + throw new Error('Can run a task only once'); + } + cancellablePromise = createCancelablePromise(token => that.sync(manifest, executionId, token)); + return cancellablePromise.finally(() => cancellablePromise = undefined); + }, + async stop(): Promise { + if (cancellablePromise) { + cancellablePromise.cancel(); + return that.stop(); + } + } + }; } - async push(): Promise { + async createManualSyncTask(): Promise { await this.checkEnablement(); - for (const synchroniser of this.synchronisers) { - try { - await synchroniser.push(); - } catch (e) { - this.handleSyncError(e, synchroniser.resource); - } + + const executionId = generateUuid(); + const syncHeaders = createSyncHeaders(executionId); + + let manifest: IUserDataManifest | null; + try { + manifest = await this.userDataSyncStoreService.manifest(syncHeaders); + } catch (error) { + error = UserDataSyncError.toUserDataSyncError(error); + this.telemetryService.publicLog2<{ code: string, service: string, resource?: string, executionId?: string }, SyncErrorClassification>('sync/error', { code: error.code, resource: error.resource, executionId, service: this.userDataSyncStoreManagementService.userDataSyncStore!.url.toString() }); + throw error; } - this.updateLastSyncTime(); + + return new ManualSyncTask(executionId, manifest, syncHeaders, this.synchronisers, this.logService); } - async sync(): Promise { - await this.checkEnablement(); + private recoveredSettings: boolean = false; + private async sync(manifest: IUserDataManifest | null, executionId: string, token: CancellationToken): Promise { + if (!this.recoveredSettings) { + await this.settingsSynchroniser.recoverSettings(); + this.recoveredSettings = true; + } + + // Return if cancellation is requested + if (token.isCancellationRequested) { + return; + } const startTime = new Date().getTime(); this._syncErrors = []; @@ -120,53 +173,38 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ this.setStatus(SyncStatus.Syncing); } - let manifest = await this.userDataSyncStoreService.manifest(); - - // Server has no data but this machine was synced before - if (manifest === null && await this.hasPreviouslySynced()) { - // Sync was turned off from other machine - throw new UserDataSyncError(localize('turned off', "Cannot sync because syncing is turned off in the cloud"), UserDataSyncErrorCode.TurnedOff); - } - - const sessionId = this.storageService.get(SESSION_ID_KEY, StorageScope.GLOBAL); - // Server session is different from client session - if (sessionId && manifest && sessionId !== manifest.session) { - throw new UserDataSyncError(localize('session expired', "Cannot sync because current session is expired"), UserDataSyncErrorCode.SessionExpired); - } + const syncHeaders = createSyncHeaders(executionId); for (const synchroniser of this.synchronisers) { + // Return if cancellation is requested + if (token.isCancellationRequested) { + return; + } try { - await synchroniser.sync(manifest && manifest.latest ? manifest.latest[synchroniser.resource] : undefined); + await synchroniser.sync(manifest, syncHeaders); } catch (e) { - this.handleSyncError(e, synchroniser.resource); + this.handleSynchronizerError(e, synchroniser.resource); this._syncErrors.push([synchroniser.resource, UserDataSyncError.toUserDataSyncError(e)]); } } - // After syncing, get the manifest if it was not available before - if (manifest === null) { - manifest = await this.userDataSyncStoreService.manifest(); - } - - // Update local session id - if (manifest && manifest.session !== sessionId) { - this.storageService.store(SESSION_ID_KEY, manifest.session, StorageScope.GLOBAL); - } - this.logService.info(`Sync done. Took ${new Date().getTime() - startTime}ms`); this.updateLastSyncTime(); - + } catch (error) { + error = UserDataSyncError.toUserDataSyncError(error); + this.telemetryService.publicLog2<{ code: string, service: string, resource?: string, executionId?: string }, SyncErrorClassification>('sync/error', { code: error.code, resource: error.resource, executionId, service: this.userDataSyncStoreManagementService.userDataSyncStore!.url.toString() }); + throw error; } finally { this.updateStatus(); this._onSyncErrors.fire(this._syncErrors); } } - async stop(): Promise { - await this.checkEnablement(); + private async stop(): Promise { if (this.status === SyncStatus.Idle) { return; } + for (const synchroniser of this.synchronisers) { try { if (synchroniser.status !== SyncStatus.Idle) { @@ -176,14 +214,24 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ this.logService.error(e); } } + } - async acceptConflict(conflict: URI, content: string): Promise { + async replace(uri: URI): Promise { await this.checkEnablement(); - const syncResourceConflict = this.conflicts.filter(({ conflicts }) => conflicts.some(({ local, remote }) => isEqual(conflict, local) || isEqual(conflict, remote)))[0]; - if (syncResourceConflict) { - const synchroniser = this.getSynchroniser(syncResourceConflict.syncResource); - await synchroniser.acceptConflict(conflict, content); + for (const synchroniser of this.synchronisers) { + if (await synchroniser.replace(uri)) { + return; + } + } + } + + async accept(syncResource: SyncResource, resource: URI, content: string | null | undefined, apply: boolean): Promise { + await this.checkEnablement(); + const synchroniser = this.getSynchroniser(syncResource); + await synchroniser.accept(resource, content); + if (apply) { + await synchroniser.apply(false, createSyncHeaders(generateUuid())); } } @@ -209,20 +257,15 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ return this.getSynchroniser(resource).getAssociatedResources(syncResourceHandle); } - async isFirstTimeSyncWithMerge(): Promise { - await this.checkEnablement(); - if (!await this.userDataSyncStoreService.manifest()) { - return false; - } - if (await this.hasPreviouslySynced()) { - return false; - } - if (!(await this.hasLocalData())) { - return false; - } - for (const synchroniser of [this.settingsSynchroniser, this.keybindingsSynchroniser, this.snippetsSynchroniser, this.extensionsSynchroniser]) { - const preview = await synchroniser.getSyncPreview(); - if (preview.hasLocalChanged || preview.hasRemoteChanged) { + getMachineId(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise { + return this.getSynchroniser(resource).getMachineId(syncResourceHandle); + } + + async hasLocalData(): Promise { + // skip global state synchronizer + const synchronizers = [this.settingsSynchroniser, this.keybindingsSynchroniser, this.snippetsSynchroniser, this.extensionsSynchroniser]; + for (const synchroniser of synchronizers) { + if (await synchroniser.hasLocalData()) { return true; } } @@ -235,21 +278,31 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ await this.resetLocal(); } + async resetRemote(): Promise { + await this.checkEnablement(); + try { + await this.userDataSyncStoreService.clear(); + this.logService.info('Cleared data on server'); + } catch (e) { + this.logService.error(e); + } + } + async resetLocal(): Promise { await this.checkEnablement(); - this.storageService.remove(SESSION_ID_KEY, StorageScope.GLOBAL); this.storageService.remove(LAST_SYNC_TIME_KEY, StorageScope.GLOBAL); for (const synchroniser of this.synchronisers) { try { - synchroniser.resetLocal(); + await synchroniser.resetLocal(); } catch (e) { this.logService.error(`${synchroniser.resource}: ${toErrorMessage(e)}`); this.logService.error(e); } } + this.logService.info('Did reset the local sync state.'); } - private async hasPreviouslySynced(): Promise { + async hasPreviouslySynced(): Promise { for (const synchroniser of this.synchronisers) { if (await synchroniser.hasPreviouslySynced()) { return true; @@ -258,24 +311,6 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ return false; } - private async hasLocalData(): Promise { - for (const synchroniser of this.synchronisers) { - if (await synchroniser.hasLocalData()) { - return true; - } - } - return false; - } - - private async resetRemote(): Promise { - await this.checkEnablement(); - try { - await this.userDataSyncStoreService.clear(); - } catch (e) { - this.logService.error(e); - } - } - private setStatus(status: SyncStatus): void { const oldStatus = this._status; if (this._status !== status) { @@ -295,14 +330,14 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ private updateConflicts(): void { const conflicts = this.computeConflicts(); - if (!equals(this._conflicts, conflicts, (a, b) => a.syncResource === b.syncResource && equals(a.conflicts, b.conflicts, (a, b) => isEqual(a.local, b.local) && isEqual(a.remote, b.remote)))) { + if (!equals(this._conflicts, conflicts, ([syncResourceA, conflictsA], [syncResourceB, conflictsB]) => syncResourceA === syncResourceA && equals(conflictsA, conflictsB, (a, b) => isEqual(a.previewResource, b.previewResource)))) { this._conflicts = this.computeConflicts(); this._onDidChangeConflicts.fire(conflicts); } } private computeStatus(): SyncStatus { - if (!this.userDataSyncStoreService.userDataSyncStore) { + if (!this.userDataSyncStoreManagementService.userDataSyncStore) { return SyncStatus.Uninitialized; } if (this.synchronisers.some(s => s.status === SyncStatus.HasConflicts)) { @@ -322,31 +357,265 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ } } - private handleSyncError(e: Error, source: SyncResource): void { - if (e instanceof UserDataSyncStoreError) { + private handleSynchronizerError(e: Error, source: SyncResource): void { + if (e instanceof UserDataSyncError) { switch (e.code) { case UserDataSyncErrorCode.TooLarge: - this.telemetryService.publicLog2<{ source: string }, SyncErrorClassification>('sync/errorTooLarge', { source }); + throw new UserDataSyncError(e.message, e.code, source); + + case UserDataSyncErrorCode.TooManyRequests: + case UserDataSyncErrorCode.TooManyRequestsAndRetryAfter: + case UserDataSyncErrorCode.LocalTooManyRequests: + case UserDataSyncErrorCode.Gone: + case UserDataSyncErrorCode.UpgradeRequired: + case UserDataSyncErrorCode.IncompatibleRemoteContent: + case UserDataSyncErrorCode.IncompatibleLocalContent: + throw e; } - throw e; } this.logService.error(e); this.logService.error(`${source}: ${toErrorMessage(e)}`); } - private computeConflicts(): SyncResourceConflicts[] { + private computeConflicts(): [SyncResource, IResourcePreview[]][] { return this.synchronisers.filter(s => s.status === SyncStatus.HasConflicts) - .map(s => ({ syncResource: s.resource, conflicts: s.conflicts })); + .map(s => ([s.resource, s.conflicts.map(toStrictResourcePreview)])); } getSynchroniser(source: SyncResource): IUserDataSynchroniser { - return this.synchronisers.filter(s => s.resource === source)[0]; + return this.synchronisers.find(s => s.resource === source)!; } private async checkEnablement(): Promise { - if (!this.userDataSyncStoreService.userDataSyncStore) { + if (!this.userDataSyncStoreManagementService.userDataSyncStore) { throw new Error('Not enabled'); } } } + +class ManualSyncTask extends Disposable implements IManualSyncTask { + + private previewsPromise: CancelablePromise<[SyncResource, ISyncResourcePreview][]> | undefined; + private previews: [SyncResource, ISyncResourcePreview][] | undefined; + + private synchronizingResources: [SyncResource, URI[]][] = []; + private _onSynchronizeResources = this._register(new Emitter<[SyncResource, URI[]][]>()); + readonly onSynchronizeResources = this._onSynchronizeResources.event; + + private isDisposed: boolean = false; + + constructor( + readonly id: string, + readonly manifest: IUserDataManifest | null, + private readonly syncHeaders: IHeaders, + private readonly synchronisers: IUserDataSynchroniser[], + private readonly logService: IUserDataSyncLogService, + ) { + super(); + } + + async preview(): Promise<[SyncResource, ISyncResourcePreview][]> { + if (this.isDisposed) { + throw new Error('Disposed'); + } + if (!this.previewsPromise) { + this.previewsPromise = createCancelablePromise(token => this.getPreviews(token)); + } + this.previews = await this.previewsPromise; + return this.previews; + } + + async accept(resource: URI, content?: string | null): Promise<[SyncResource, ISyncResourcePreview][]> { + return this.performAction(resource, sychronizer => sychronizer.accept(resource, content)); + } + + async merge(resource: URI): Promise<[SyncResource, ISyncResourcePreview][]> { + return this.performAction(resource, sychronizer => sychronizer.merge(resource)); + } + + async discard(resource: URI): Promise<[SyncResource, ISyncResourcePreview][]> { + return this.performAction(resource, sychronizer => sychronizer.discard(resource)); + } + + private async performAction(resource: URI, action: (synchroniser: IUserDataSynchroniser) => Promise): Promise<[SyncResource, ISyncResourcePreview][]> { + if (!this.previews) { + throw new Error('Missing preview. Create preview and try again.'); + } + + const index = this.previews.findIndex(([, preview]) => preview.resourcePreviews.some(({ localResource, previewResource, remoteResource }) => + isEqual(resource, localResource) || isEqual(resource, previewResource) || isEqual(resource, remoteResource))); + if (index === -1) { + return this.previews; + } + + const [syncResource, previews] = this.previews[index]; + const resourcePreview = previews.resourcePreviews.find(({ localResource, remoteResource, previewResource }) => isEqual(localResource, resource) || isEqual(remoteResource, resource) || isEqual(previewResource, resource)); + if (!resourcePreview) { + return this.previews; + } + + let synchronizingResources = this.synchronizingResources.find(s => s[0] === syncResource); + if (!synchronizingResources) { + synchronizingResources = [syncResource, []]; + this.synchronizingResources.push(synchronizingResources); + } + if (!synchronizingResources[1].some(s => isEqual(s, resourcePreview.localResource))) { + synchronizingResources[1].push(resourcePreview.localResource); + this._onSynchronizeResources.fire(this.synchronizingResources); + } + + const synchroniser = this.synchronisers.find(s => s.resource === this.previews![index][0])!; + const preview = await action(synchroniser); + preview ? this.previews.splice(index, 1, this.toSyncResourcePreview(synchroniser.resource, preview)) : this.previews.splice(index, 1); + + const i = this.synchronizingResources.findIndex(s => s[0] === syncResource); + this.synchronizingResources[i][1].splice(synchronizingResources[1].findIndex(r => isEqual(r, resourcePreview.localResource)), 1); + if (!synchronizingResources[1].length) { + this.synchronizingResources.splice(i, 1); + this._onSynchronizeResources.fire(this.synchronizingResources); + } + + return this.previews; + } + + async apply(): Promise<[SyncResource, ISyncResourcePreview][]> { + if (!this.previews) { + throw new Error('You need to create preview before applying'); + } + if (this.synchronizingResources.length) { + throw new Error('Cannot pull while synchronizing resources'); + } + const previews: [SyncResource, ISyncResourcePreview][] = []; + for (const [syncResource, preview] of this.previews) { + this.synchronizingResources.push([syncResource, preview.resourcePreviews.map(r => r.localResource)]); + this._onSynchronizeResources.fire(this.synchronizingResources); + + const synchroniser = this.synchronisers.find(s => s.resource === syncResource)!; + + /* merge those which are not yet merged */ + for (const resourcePreview of preview.resourcePreviews) { + if ((resourcePreview.localChange !== Change.None || resourcePreview.remoteChange !== Change.None) && resourcePreview.mergeState === MergeState.Preview) { + await synchroniser.merge(resourcePreview.previewResource); + } + } + + /* apply */ + const newPreview = await synchroniser.apply(false, this.syncHeaders); + if (newPreview) { + previews.push(this.toSyncResourcePreview(synchroniser.resource, newPreview)); + } + + this.synchronizingResources.splice(this.synchronizingResources.findIndex(s => s[0] === syncResource), 1); + this._onSynchronizeResources.fire(this.synchronizingResources); + } + this.previews = previews; + return this.previews; + } + + async pull(): Promise { + if (!this.previews) { + throw new Error('You need to create preview before applying'); + } + if (this.synchronizingResources.length) { + throw new Error('Cannot pull while synchronizing resources'); + } + for (const [syncResource, preview] of this.previews) { + this.synchronizingResources.push([syncResource, preview.resourcePreviews.map(r => r.localResource)]); + this._onSynchronizeResources.fire(this.synchronizingResources); + const synchroniser = this.synchronisers.find(s => s.resource === syncResource)!; + for (const resourcePreview of preview.resourcePreviews) { + await synchroniser.accept(resourcePreview.remoteResource); + } + await synchroniser.apply(true, this.syncHeaders); + this.synchronizingResources.splice(this.synchronizingResources.findIndex(s => s[0] === syncResource), 1); + this._onSynchronizeResources.fire(this.synchronizingResources); + } + this.previews = []; + } + + async push(): Promise { + if (!this.previews) { + throw new Error('You need to create preview before applying'); + } + if (this.synchronizingResources.length) { + throw new Error('Cannot pull while synchronizing resources'); + } + for (const [syncResource, preview] of this.previews) { + this.synchronizingResources.push([syncResource, preview.resourcePreviews.map(r => r.localResource)]); + this._onSynchronizeResources.fire(this.synchronizingResources); + const synchroniser = this.synchronisers.find(s => s.resource === syncResource)!; + for (const resourcePreview of preview.resourcePreviews) { + await synchroniser.accept(resourcePreview.localResource); + } + await synchroniser.apply(true, this.syncHeaders); + this.synchronizingResources.splice(this.synchronizingResources.findIndex(s => s[0] === syncResource), 1); + this._onSynchronizeResources.fire(this.synchronizingResources); + } + this.previews = []; + } + + async stop(): Promise { + for (const synchroniser of this.synchronisers) { + try { + await synchroniser.stop(); + } catch (error) { + if (!isPromiseCanceledError(error)) { + this.logService.error(error); + } + } + } + this.reset(); + } + + private async getPreviews(token: CancellationToken): Promise<[SyncResource, ISyncResourcePreview][]> { + const result: [SyncResource, ISyncResourcePreview][] = []; + for (const synchroniser of this.synchronisers) { + if (token.isCancellationRequested) { + return []; + } + const preview = await synchroniser.preview(this.manifest, this.syncHeaders); + if (preview) { + result.push(this.toSyncResourcePreview(synchroniser.resource, preview)); + } + } + return result; + } + + private toSyncResourcePreview(syncResource: SyncResource, preview: ISyncResourcePreview): [SyncResource, ISyncResourcePreview] { + return [ + syncResource, + { + isLastSyncFromCurrentMachine: preview.isLastSyncFromCurrentMachine, + resourcePreviews: preview.resourcePreviews.map(toStrictResourcePreview) + } + ]; + } + + private reset(): void { + if (this.previewsPromise) { + this.previewsPromise.cancel(); + this.previewsPromise = undefined; + } + this.previews = undefined; + this.synchronizingResources = []; + } + + dispose(): void { + this.reset(); + this.isDisposed = true; + } + +} + +function toStrictResourcePreview(resourcePreview: IResourcePreview): IResourcePreview { + return { + localResource: resourcePreview.localResource, + previewResource: resourcePreview.previewResource, + remoteResource: resourcePreview.remoteResource, + acceptedResource: resourcePreview.acceptedResource, + localChange: resourcePreview.localChange, + remoteChange: resourcePreview.remoteChange, + mergeState: resourcePreview.mergeState, + }; +} diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index 7d0ef006ea8..087369b95db 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -4,116 +4,270 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable, } from 'vs/base/common/lifecycle'; -import { IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, IUserDataSyncStore, getUserDataSyncStore, SyncResource, UserDataSyncStoreError, IUserDataSyncLogService, IUserDataManifest, IResourceRefHandle } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, IUserDataSyncStore, ServerResource, UserDataSyncStoreError, IUserDataSyncLogService, IUserDataManifest, IResourceRefHandle, HEADER_OPERATION_ID, HEADER_EXECUTION_ID, CONFIGURATION_SYNC_STORE_KEY, IAuthenticationProvider, IUserDataSyncStoreManagementService, UserDataSyncStoreType } from 'vs/platform/userDataSync/common/userDataSync'; import { IRequestService, asText, isSuccess, asJson } from 'vs/platform/request/common/request'; import { joinPath, relativePath } from 'vs/base/common/resources'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IHeaders, IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication'; -import { IProductService } from 'vs/platform/product/common/productService'; +import { IProductService, ConfigurationSyncStore } from 'vs/platform/product/common/productService'; import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; -import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { assign } from 'vs/base/common/objects'; +import { generateUuid } from 'vs/base/common/uuid'; +import { isWeb } from 'vs/base/common/platform'; +import { Emitter, Event } from 'vs/base/common/event'; +import { createCancelablePromise, timeout, CancelablePromise } from 'vs/base/common/async'; +import { isString, isObject, isArray } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; +const SYNC_SERVICE_URL_TYPE = 'sync.store.url.type'; +const SYNC_PREVIOUS_STORE = 'sync.previous.store'; +const DONOT_MAKE_REQUESTS_UNTIL_KEY = 'sync.donot-make-requests-until'; +const USER_SESSION_ID_KEY = 'sync.user-session-id'; +const MACHINE_SESSION_ID_KEY = 'sync.machine-session-id'; +const REQUEST_SESSION_LIMIT = 100; +const REQUEST_SESSION_INTERVAL = 1000 * 60 * 5; /* 5 minutes */ + +type UserDataSyncStore = IUserDataSyncStore & { defaultType?: UserDataSyncStoreType; type?: UserDataSyncStoreType }; + +export abstract class AbstractUserDataSyncStoreManagementService extends Disposable implements IUserDataSyncStoreManagementService { + + _serviceBrand: any; + + readonly userDataSyncStore: UserDataSyncStore | undefined; + + constructor( + @IProductService protected readonly productService: IProductService, + @IConfigurationService protected readonly configurationService: IConfigurationService, + @IStorageService protected readonly storageService: IStorageService, + ) { + super(); + this.userDataSyncStore = this.toUserDataSyncStore(this.productService[CONFIGURATION_SYNC_STORE_KEY]); + } + + protected toUserDataSyncStore(syncStore: ConfigurationSyncStore | undefined): UserDataSyncStore | undefined { + if (syncStore) { + if (syncStore + && isString(syncStore.url) + && isObject(syncStore.authenticationProviders) + && Object.keys(syncStore.authenticationProviders).every(authenticationProviderId => isArray(syncStore.authenticationProviders[authenticationProviderId].scopes)) + ) { + const type: UserDataSyncStoreType | undefined = this.storageService.get(SYNC_SERVICE_URL_TYPE, StorageScope.GLOBAL) as UserDataSyncStoreType | undefined; + const url = this.configurationService.getValue(CONFIGURATION_SYNC_STORE_KEY)?.url + || (type === 'insiders' ? syncStore.insidersUrl : type === 'stable' ? syncStore.stableUrl : undefined) + || syncStore.url; + return { + url: URI.parse(url), + type, + defaultType: syncStore.url === syncStore.insidersUrl ? 'insiders' : syncStore.url === syncStore.stableUrl ? 'stable' : undefined, + defaultUrl: URI.parse(syncStore.url), + stableUrl: syncStore.stableUrl ? URI.parse(syncStore.stableUrl) : undefined, + insidersUrl: syncStore.insidersUrl ? URI.parse(syncStore.insidersUrl) : undefined, + authenticationProviders: Object.keys(syncStore.authenticationProviders).reduce((result, id) => { + result.push({ id, scopes: syncStore.authenticationProviders[id].scopes }); + return result; + }, []) + }; + } + } + return undefined; + } + + abstract switch(type: UserDataSyncStoreType): Promise; + abstract getPreviousUserDataSyncStore(): Promise; + +} + +export class UserDataSyncStoreManagementService extends AbstractUserDataSyncStoreManagementService implements IUserDataSyncStoreManagementService { + + private readonly previousConfigurationSyncStore: ConfigurationSyncStore | undefined; + + constructor( + @IProductService productService: IProductService, + @IConfigurationService configurationService: IConfigurationService, + @IStorageService storageService: IStorageService, + ) { + super(productService, configurationService, storageService); + + const previousConfigurationSyncStore = this.storageService.get(SYNC_PREVIOUS_STORE, StorageScope.GLOBAL); + if (previousConfigurationSyncStore) { + this.previousConfigurationSyncStore = JSON.parse(previousConfigurationSyncStore); + } + + const syncStore = this.productService[CONFIGURATION_SYNC_STORE_KEY]; + if (syncStore) { + this.storageService.store(SYNC_PREVIOUS_STORE, JSON.stringify(syncStore), StorageScope.GLOBAL); + } else { + this.storageService.remove(SYNC_PREVIOUS_STORE, StorageScope.GLOBAL); + } + } + + async switch(type: UserDataSyncStoreType): Promise { + if (type !== this.userDataSyncStore?.type) { + if (type === this.userDataSyncStore?.defaultType) { + this.storageService.remove(SYNC_SERVICE_URL_TYPE, StorageScope.GLOBAL); + } else { + this.storageService.store(SYNC_SERVICE_URL_TYPE, type, StorageScope.GLOBAL); + } + } + } + + async getPreviousUserDataSyncStore(): Promise { + return this.toUserDataSyncStore(this.previousConfigurationSyncStore); + } +} export class UserDataSyncStoreService extends Disposable implements IUserDataSyncStoreService { _serviceBrand: any; - readonly userDataSyncStore: IUserDataSyncStore | undefined; + private readonly userDataSyncStoreUrl: URI | undefined; + + private authToken: { token: string, type: string } | undefined; private readonly commonHeadersPromise: Promise<{ [key: string]: string; }>; + private readonly session: RequestsSession; + + private _onTokenFailed: Emitter = this._register(new Emitter()); + readonly onTokenFailed: Event = this._onTokenFailed.event; + + private _onTokenSucceed: Emitter = this._register(new Emitter()); + readonly onTokenSucceed: Event = this._onTokenSucceed.event; + + private _donotMakeRequestsUntil: Date | undefined = undefined; + get donotMakeRequestsUntil() { return this._donotMakeRequestsUntil; } + private _onDidChangeDonotMakeRequestsUntil = this._register(new Emitter()); + readonly onDidChangeDonotMakeRequestsUntil = this._onDidChangeDonotMakeRequestsUntil.event; constructor( @IProductService productService: IProductService, - @IConfigurationService configurationService: IConfigurationService, @IRequestService private readonly requestService: IRequestService, - @IAuthenticationTokenService private readonly authTokenService: IAuthenticationTokenService, + @IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, @IEnvironmentService environmentService: IEnvironmentService, @IFileService fileService: IFileService, - @IStorageService storageService: IStorageService, + @IStorageService private readonly storageService: IStorageService, ) { super(); - this.userDataSyncStore = getUserDataSyncStore(productService, configurationService); + this.userDataSyncStoreUrl = this.userDataSyncStoreManagementService.userDataSyncStore ? joinPath(this.userDataSyncStoreManagementService.userDataSyncStore.url, 'v1') : undefined; this.commonHeadersPromise = getServiceMachineId(environmentService, fileService, storageService) .then(uuid => { const headers: IHeaders = { - 'X-Sync-Client-Id': productService.version, + 'X-Client-Name': `${productService.applicationName}${isWeb ? '-web' : ''}`, + 'X-Client-Version': productService.version, + 'X-Machine-Id': uuid }; - headers['X-Sync-Machine-Id'] = uuid; + if (productService.commit) { + headers['X-Client-Commit'] = productService.commit; + } return headers; }); + + /* A requests session that limits requests per sessions */ + this.session = new RequestsSession(REQUEST_SESSION_LIMIT, REQUEST_SESSION_INTERVAL, this.requestService, this.logService); + this.initDonotMakeRequestsUntil(); } - async getAllRefs(resource: SyncResource): Promise { - if (!this.userDataSyncStore) { + setAuthToken(token: string, type: string): void { + this.authToken = { token, type }; + } + + private initDonotMakeRequestsUntil(): void { + const donotMakeRequestsUntil = this.storageService.getNumber(DONOT_MAKE_REQUESTS_UNTIL_KEY, StorageScope.GLOBAL); + if (donotMakeRequestsUntil && Date.now() < donotMakeRequestsUntil) { + this.setDonotMakeRequestsUntil(new Date(donotMakeRequestsUntil)); + } + } + + private resetDonotMakeRequestsUntilPromise: CancelablePromise | undefined = undefined; + private setDonotMakeRequestsUntil(donotMakeRequestsUntil: Date | undefined): void { + if (this._donotMakeRequestsUntil?.getTime() !== donotMakeRequestsUntil?.getTime()) { + this._donotMakeRequestsUntil = donotMakeRequestsUntil; + + if (this.resetDonotMakeRequestsUntilPromise) { + this.resetDonotMakeRequestsUntilPromise.cancel(); + this.resetDonotMakeRequestsUntilPromise = undefined; + } + + if (this._donotMakeRequestsUntil) { + this.storageService.store(DONOT_MAKE_REQUESTS_UNTIL_KEY, this._donotMakeRequestsUntil.getTime(), StorageScope.GLOBAL); + this.resetDonotMakeRequestsUntilPromise = createCancelablePromise(token => timeout(this._donotMakeRequestsUntil!.getTime() - Date.now(), token).then(() => this.setDonotMakeRequestsUntil(undefined))); + } else { + this.storageService.remove(DONOT_MAKE_REQUESTS_UNTIL_KEY, StorageScope.GLOBAL); + } + + this._onDidChangeDonotMakeRequestsUntil.fire(); + } + } + + async getAllRefs(resource: ServerResource): Promise { + if (!this.userDataSyncStoreUrl) { throw new Error('No settings sync store url configured.'); } - const uri = joinPath(this.userDataSyncStore.url, 'resource', resource); + const uri = joinPath(this.userDataSyncStoreUrl, 'resource', resource); const headers: IHeaders = {}; - const context = await this.request({ type: 'GET', url: uri.toString(), headers }, undefined, CancellationToken.None); + const context = await this.request({ type: 'GET', url: uri.toString(), headers }, CancellationToken.None); if (!isSuccess(context)) { - throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, UserDataSyncErrorCode.Unknown, undefined); + throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, UserDataSyncErrorCode.Unknown, context.res.headers[HEADER_OPERATION_ID]); } const result = await asJson<{ url: string, created: number }[]>(context) || []; return result.map(({ url, created }) => ({ ref: relativePath(uri, uri.with({ path: url }))!, created: created * 1000 /* Server returns in seconds */ })); } - async resolveContent(resource: SyncResource, ref: string): Promise { - if (!this.userDataSyncStore) { + async resolveContent(resource: ServerResource, ref: string): Promise { + if (!this.userDataSyncStoreUrl) { throw new Error('No settings sync store url configured.'); } - const url = joinPath(this.userDataSyncStore.url, 'resource', resource, ref).toString(); + const url = joinPath(this.userDataSyncStoreUrl, 'resource', resource, ref).toString(); const headers: IHeaders = {}; headers['Cache-Control'] = 'no-cache'; - const context = await this.request({ type: 'GET', url, headers }, undefined, CancellationToken.None); + const context = await this.request({ type: 'GET', url, headers }, CancellationToken.None); if (!isSuccess(context)) { - throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, UserDataSyncErrorCode.Unknown, undefined); + throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, UserDataSyncErrorCode.Unknown, context.res.headers[HEADER_OPERATION_ID]); } const content = await asText(context); return content; } - async delete(resource: SyncResource): Promise { - if (!this.userDataSyncStore) { + async delete(resource: ServerResource): Promise { + if (!this.userDataSyncStoreUrl) { throw new Error('No settings sync store url configured.'); } - const url = joinPath(this.userDataSyncStore.url, 'resource', resource).toString(); + const url = joinPath(this.userDataSyncStoreUrl, 'resource', resource).toString(); const headers: IHeaders = {}; - const context = await this.request({ type: 'DELETE', url, headers }, undefined, CancellationToken.None); + const context = await this.request({ type: 'DELETE', url, headers }, CancellationToken.None); if (!isSuccess(context)) { - throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, UserDataSyncErrorCode.Unknown, undefined); + throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, UserDataSyncErrorCode.Unknown, context.res.headers[HEADER_OPERATION_ID]); } } - async read(resource: SyncResource, oldValue: IUserData | null): Promise { - if (!this.userDataSyncStore) { + async read(resource: ServerResource, oldValue: IUserData | null, headers: IHeaders = {}): Promise { + if (!this.userDataSyncStoreUrl) { throw new Error('No settings sync store url configured.'); } - const url = joinPath(this.userDataSyncStore.url, 'resource', resource, 'latest').toString(); - const headers: IHeaders = {}; + const url = joinPath(this.userDataSyncStoreUrl, 'resource', resource, 'latest').toString(); + headers = { ...headers }; // Disable caching as they are cached by synchronisers headers['Cache-Control'] = 'no-cache'; if (oldValue) { headers['If-None-Match'] = oldValue.ref; } - const context = await this.request({ type: 'GET', url, headers }, resource, CancellationToken.None); + const context = await this.request({ type: 'GET', url, headers }, CancellationToken.None); if (context.res.statusCode === 304) { // There is no new value. Hence return the old value. @@ -121,112 +275,228 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn } if (!isSuccess(context)) { - throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, UserDataSyncErrorCode.Unknown, resource); + throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, UserDataSyncErrorCode.Unknown, context.res.headers[HEADER_OPERATION_ID]); } const ref = context.res.headers['etag']; if (!ref) { - throw new UserDataSyncStoreError('Server did not return the ref', UserDataSyncErrorCode.NoRef, resource); + throw new UserDataSyncStoreError('Server did not return the ref', UserDataSyncErrorCode.NoRef, context.res.headers[HEADER_OPERATION_ID]); } const content = await asText(context); return { ref, content }; } - async write(resource: SyncResource, data: string, ref: string | null): Promise { - if (!this.userDataSyncStore) { + async write(resource: ServerResource, data: string, ref: string | null, headers: IHeaders = {}): Promise { + if (!this.userDataSyncStoreUrl) { throw new Error('No settings sync store url configured.'); } - const url = joinPath(this.userDataSyncStore.url, 'resource', resource).toString(); - const headers: IHeaders = { 'Content-Type': 'text/plain' }; + const url = joinPath(this.userDataSyncStoreUrl, 'resource', resource).toString(); + headers = { ...headers }; + headers['Content-Type'] = 'text/plain'; if (ref) { headers['If-Match'] = ref; } - const context = await this.request({ type: 'POST', url, data, headers }, resource, CancellationToken.None); + const context = await this.request({ type: 'POST', url, data, headers }, CancellationToken.None); if (!isSuccess(context)) { - throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, UserDataSyncErrorCode.Unknown, resource); + throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, UserDataSyncErrorCode.Unknown, context.res.headers[HEADER_OPERATION_ID]); } const newRef = context.res.headers['etag']; if (!newRef) { - throw new UserDataSyncStoreError('Server did not return the ref', UserDataSyncErrorCode.NoRef, resource); + throw new UserDataSyncStoreError('Server did not return the ref', UserDataSyncErrorCode.NoRef, context.res.headers[HEADER_OPERATION_ID]); } return newRef; } - async manifest(): Promise { - if (!this.userDataSyncStore) { + async manifest(headers: IHeaders = {}): Promise { + if (!this.userDataSyncStoreUrl) { throw new Error('No settings sync store url configured.'); } - const url = joinPath(this.userDataSyncStore.url, 'manifest').toString(); - const headers: IHeaders = { 'Content-Type': 'application/json' }; + const url = joinPath(this.userDataSyncStoreUrl, 'manifest').toString(); + headers = { ...headers }; + headers['Content-Type'] = 'application/json'; - const context = await this.request({ type: 'GET', url, headers }, undefined, CancellationToken.None); + const context = await this.request({ type: 'GET', url, headers }, CancellationToken.None); if (!isSuccess(context)) { - throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, UserDataSyncErrorCode.Unknown); + throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, UserDataSyncErrorCode.Unknown, context.res.headers[HEADER_OPERATION_ID]); } - return asJson(context); + const manifest = await asJson(context); + const currentSessionId = this.storageService.get(USER_SESSION_ID_KEY, StorageScope.GLOBAL); + + if (currentSessionId && manifest && currentSessionId !== manifest.session) { + // Server session is different from client session so clear cached session. + this.clearSession(); + } + + if (manifest === null && currentSessionId) { + // server session is cleared so clear cached session. + this.clearSession(); + } + + if (manifest) { + // update session + this.storageService.store(USER_SESSION_ID_KEY, manifest.session, StorageScope.GLOBAL); + } + + return manifest; } async clear(): Promise { - if (!this.userDataSyncStore) { + if (!this.userDataSyncStoreUrl) { throw new Error('No settings sync store url configured.'); } - const url = joinPath(this.userDataSyncStore.url, 'resource').toString(); + const url = joinPath(this.userDataSyncStoreUrl, 'resource').toString(); const headers: IHeaders = { 'Content-Type': 'text/plain' }; - const context = await this.request({ type: 'DELETE', url, headers }, undefined, CancellationToken.None); + const context = await this.request({ type: 'DELETE', url, headers }, CancellationToken.None); if (!isSuccess(context)) { - throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, UserDataSyncErrorCode.Unknown); + throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, UserDataSyncErrorCode.Unknown, context.res.headers[HEADER_OPERATION_ID]); } + + // clear cached session. + this.clearSession(); } - private async request(options: IRequestOptions, source: SyncResource | undefined, token: CancellationToken): Promise { - const authToken = await this.authTokenService.getToken(); - if (!authToken) { - throw new UserDataSyncStoreError('No Auth Token Available', UserDataSyncErrorCode.Unauthorized, source); + private clearSession(): void { + this.storageService.remove(USER_SESSION_ID_KEY, StorageScope.GLOBAL); + this.storageService.remove(MACHINE_SESSION_ID_KEY, StorageScope.GLOBAL); + } + + private async request(options: IRequestOptions, token: CancellationToken): Promise { + if (!this.authToken) { + throw new UserDataSyncStoreError('No Auth Token Available', UserDataSyncErrorCode.Unauthorized, undefined); } + if (this._donotMakeRequestsUntil && Date.now() < this._donotMakeRequestsUntil.getTime()) { + throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of too many requests (429).`, UserDataSyncErrorCode.TooManyRequestsAndRetryAfter, undefined); + } + this.setDonotMakeRequestsUntil(undefined); + const commonHeaders = await this.commonHeadersPromise; options.headers = assign(options.headers || {}, commonHeaders, { - 'X-Account-Type': authToken.authenticationProviderId, - 'authorization': `Bearer ${authToken.token}`, + 'X-Account-Type': this.authToken.type, + 'authorization': `Bearer ${this.authToken.token}`, }); + // Add session headers + this.addSessionHeaders(options.headers); + this.logService.trace('Sending request to server', { url: options.url, type: options.type, headers: { ...options.headers, ...{ authorization: undefined } } }); let context; try { - context = await this.requestService.request(options, token); - this.logService.trace('Request finished', { url: options.url, status: context.res.statusCode }); + context = await this.session.request(options, token); } catch (e) { - throw new UserDataSyncStoreError(`Connection refused for the request '${options.url?.toString()}'.`, UserDataSyncErrorCode.ConnectionRefused, source); + if (!(e instanceof UserDataSyncStoreError)) { + e = new UserDataSyncStoreError(`Connection refused for the request '${options.url?.toString()}'.`, UserDataSyncErrorCode.ConnectionRefused, undefined); + } + this.logService.info('Request failed', options.url); + throw e; + } + + const operationId = context.res.headers[HEADER_OPERATION_ID]; + const requestInfo = { url: options.url, status: context.res.statusCode, 'execution-id': options.headers[HEADER_EXECUTION_ID], 'operation-id': operationId }; + if (isSuccess(context)) { + this.logService.trace('Request succeeded', requestInfo); + } else { + this.logService.info('Request failed', requestInfo); } if (context.res.statusCode === 401) { - this.authTokenService.sendTokenFailed(); - throw new UserDataSyncStoreError(`Request '${options.url?.toString()}' failed because of Unauthorized (401).`, UserDataSyncErrorCode.Unauthorized, source); + this.authToken = undefined; + this._onTokenFailed.fire(); + throw new UserDataSyncStoreError(`Request '${options.url?.toString()}' failed because of Unauthorized (401).`, UserDataSyncErrorCode.Unauthorized, operationId); } - if (context.res.statusCode === 403) { - throw new UserDataSyncStoreError(`Request '${options.url?.toString()}' is Forbidden (403).`, UserDataSyncErrorCode.Forbidden, source); + this._onTokenSucceed.fire(); + + if (context.res.statusCode === 410) { + throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because the requested resource is not longer available (410).`, UserDataSyncErrorCode.Gone, operationId); } if (context.res.statusCode === 412) { - throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of Precondition Failed (412). There is new data exists for this resource. Make the request again with latest data.`, UserDataSyncErrorCode.RemotePreconditionFailed, source); + throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of Precondition Failed (412). There is new data exists for this resource. Make the request again with latest data.`, UserDataSyncErrorCode.PreconditionFailed, operationId); } if (context.res.statusCode === 413) { - throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of too large payload (413).`, UserDataSyncErrorCode.TooLarge, source); + throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of too large payload (413).`, UserDataSyncErrorCode.TooLarge, operationId); + } + + if (context.res.statusCode === 426) { + throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed with status Upgrade Required (426). Please upgrade the client and try again.`, UserDataSyncErrorCode.UpgradeRequired, operationId); + } + + if (context.res.statusCode === 429) { + const retryAfter = context.res.headers['retry-after']; + if (retryAfter) { + this.setDonotMakeRequestsUntil(new Date(Date.now() + (parseInt(retryAfter) * 1000))); + throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of too many requests (429).`, UserDataSyncErrorCode.TooManyRequestsAndRetryAfter, operationId); + } else { + throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of too many requests (429).`, UserDataSyncErrorCode.TooManyRequests, operationId); + } } return context; } + private addSessionHeaders(headers: IHeaders): void { + let machineSessionId = this.storageService.get(MACHINE_SESSION_ID_KEY, StorageScope.GLOBAL); + if (machineSessionId === undefined) { + machineSessionId = generateUuid(); + this.storageService.store(MACHINE_SESSION_ID_KEY, machineSessionId, StorageScope.GLOBAL); + } + headers['X-Machine-Session-Id'] = machineSessionId; + + const userSessionId = this.storageService.get(USER_SESSION_ID_KEY, StorageScope.GLOBAL); + if (userSessionId !== undefined) { + headers['X-User-Session-Id'] = userSessionId; + } + } + +} + +export class RequestsSession { + + private requests: string[] = []; + private startTime: Date | undefined = undefined; + + constructor( + private readonly limit: number, + private readonly interval: number, /* in ms */ + private readonly requestService: IRequestService, + private readonly logService: IUserDataSyncLogService, + ) { } + + request(options: IRequestOptions, token: CancellationToken): Promise { + if (this.isExpired()) { + this.reset(); + } + + if (this.requests.length >= this.limit) { + this.logService.info('Too many requests', ...this.requests); + throw new UserDataSyncStoreError(`Too many requests. Allowed only ${this.limit} requests in ${this.interval / (1000 * 60)} minutes.`, UserDataSyncErrorCode.LocalTooManyRequests, undefined); + } + + this.startTime = this.startTime || new Date(); + this.requests.push(options.url!); + + return this.requestService.request(options, token); + } + + private isExpired(): boolean { + return this.startTime !== undefined && new Date().getTime() - this.startTime.getTime() > this.interval; + } + + private reset(): void { + this.requests = []; + this.startTime = undefined; + } + } diff --git a/src/vs/platform/userDataSync/electron-browser/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/electron-browser/userDataAutoSyncService.ts index 6fa694e1d27..81483659e9b 100644 --- a/src/vs/platform/userDataSync/electron-browser/userDataAutoSyncService.ts +++ b/src/vs/platform/userDataSync/electron-browser/userDataAutoSyncService.ts @@ -3,30 +3,37 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IUserDataSyncService, IUserDataSyncLogService, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, IUserDataSyncLogService, IUserDataSyncResourceEnablementService, IUserDataSyncStoreService, IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync'; import { Event } from 'vs/base/common/event'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { UserDataAutoSyncService as BaseUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; -import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication'; +import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; export class UserDataAutoSyncService extends BaseUserDataAutoSyncService { constructor( - @IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService, + @IUserDataSyncStoreManagementService userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, + @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, + @IUserDataSyncResourceEnablementService userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, @IUserDataSyncService userDataSyncService: IUserDataSyncService, @IElectronService electronService: IElectronService, @IUserDataSyncLogService logService: IUserDataSyncLogService, - @IAuthenticationTokenService authTokenService: IAuthenticationTokenService, + @IUserDataSyncAccountService authTokenService: IUserDataSyncAccountService, @ITelemetryService telemetryService: ITelemetryService, + @IUserDataSyncMachinesService userDataSyncMachinesService: IUserDataSyncMachinesService, + @IStorageService storageService: IStorageService, + @IEnvironmentService environmentService: IEnvironmentService, ) { - super(userDataSyncEnablementService, userDataSyncService, logService, authTokenService, telemetryService); + super(userDataSyncStoreManagementService, userDataSyncStoreService, userDataSyncResourceEnablementService, userDataSyncService, logService, authTokenService, telemetryService, userDataSyncMachinesService, storageService, environmentService); this._register(Event.debounce(Event.any( Event.map(electronService.onWindowFocus, () => 'windowFocus'), Event.map(electronService.onWindowOpen, () => 'windowOpen'), - userDataSyncService.onDidChangeLocal, - ), (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerAutoSync(sources))); + ), (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, true))); } } diff --git a/src/vs/platform/userDataSync/test/common/extensionsMerge.test.ts b/src/vs/platform/userDataSync/test/common/extensionsMerge.test.ts index 3bd7057806f..a66b9d45f8c 100644 --- a/src/vs/platform/userDataSync/test/common/extensionsMerge.test.ts +++ b/src/vs/platform/userDataSync/test/common/extensionsMerge.test.ts @@ -7,13 +7,13 @@ import * as assert from 'assert'; import { ISyncExtension } from 'vs/platform/userDataSync/common/userDataSync'; import { merge } from 'vs/platform/userDataSync/common/extensionsMerge'; -suite('ExtensionsMerge - No Conflicts', () => { +suite('ExtensionsMerge', () => { - test('merge returns local extension if remote does not exist', async () => { + test('merge returns local extension if remote does not exist', () => { const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const actual = merge(localExtensions, null, null, [], []); @@ -24,15 +24,15 @@ suite('ExtensionsMerge - No Conflicts', () => { assert.deepEqual(actual.remote, localExtensions); }); - test('merge returns local extension if remote does not exist with ignored extensions', async () => { + test('merge returns local extension if remote does not exist with ignored extensions', () => { const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const expected: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const actual = merge(localExtensions, null, null, [], ['a']); @@ -43,15 +43,15 @@ suite('ExtensionsMerge - No Conflicts', () => { assert.deepEqual(actual.remote, expected); }); - test('merge returns local extension if remote does not exist with ignored extensions (ignore case)', async () => { + test('merge returns local extension if remote does not exist with ignored extensions (ignore case)', () => { const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const expected: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const actual = merge(localExtensions, null, null, [], ['A']); @@ -62,19 +62,19 @@ suite('ExtensionsMerge - No Conflicts', () => { assert.deepEqual(actual.remote, expected); }); - test('merge returns local extension if remote does not exist with skipped extensions', async () => { + test('merge returns local extension if remote does not exist with skipped extensions', () => { const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const skippedExtension: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, ]; const expected: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const actual = merge(localExtensions, null, null, skippedExtension, []); @@ -85,18 +85,18 @@ suite('ExtensionsMerge - No Conflicts', () => { assert.deepEqual(actual.remote, expected); }); - test('merge returns local extension if remote does not exist with skipped and ignored extensions', async () => { + test('merge returns local extension if remote does not exist with skipped and ignored extensions', () => { const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const skippedExtension: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, ]; const expected: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const actual = merge(localExtensions, null, null, skippedExtension, ['a']); @@ -107,180 +107,180 @@ suite('ExtensionsMerge - No Conflicts', () => { assert.deepEqual(actual.remote, expected); }); - test('merge local and remote extensions when there is no base', async () => { + test('merge local and remote extensions when there is no base', () => { const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const remoteExtensions: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const expected: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const actual = merge(localExtensions, remoteExtensions, null, [], []); - assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' } }, { identifier: { id: 'c', uuid: 'c' } }]); + assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' }, installed: true }, { identifier: { id: 'c', uuid: 'c' }, installed: true }]); assert.deepEqual(actual.removed, []); assert.deepEqual(actual.updated, []); assert.deepEqual(actual.remote, expected); }); - test('merge local and remote extensions when there is no base and with ignored extensions', async () => { + test('merge local and remote extensions when there is no base and with ignored extensions', () => { const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const remoteExtensions: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const expected: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const actual = merge(localExtensions, remoteExtensions, null, [], ['a']); - assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' } }, { identifier: { id: 'c', uuid: 'c' } }]); + assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' }, installed: true }, { identifier: { id: 'c', uuid: 'c' }, installed: true }]); assert.deepEqual(actual.removed, []); assert.deepEqual(actual.updated, []); assert.deepEqual(actual.remote, expected); }); - test('merge local and remote extensions when remote is moved forwarded', async () => { + test('merge local and remote extensions when remote is moved forwarded', () => { const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const remoteExtensions: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], []); - assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' } }, { identifier: { id: 'c', uuid: 'c' } }]); + assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' }, installed: true }, { identifier: { id: 'c', uuid: 'c' }, installed: true }]); assert.deepEqual(actual.removed, [{ id: 'a', uuid: 'a' }, { id: 'd', uuid: 'd' }]); assert.deepEqual(actual.updated, []); assert.equal(actual.remote, null); }); - test('merge local and remote extensions when remote is moved forwarded with disabled extension', async () => { + test('merge local and remote extensions when remote is moved forwarded with disabled extension', () => { const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const remoteExtensions: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, - { identifier: { id: 'd', uuid: 'd' }, disabled: true }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, disabled: true, installed: true }, ]; const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], []); - assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' } }, { identifier: { id: 'c', uuid: 'c' } }]); + assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' }, installed: true }, { identifier: { id: 'c', uuid: 'c' }, installed: true }]); assert.deepEqual(actual.removed, [{ id: 'a', uuid: 'a' }]); - assert.deepEqual(actual.updated, [{ identifier: { id: 'd', uuid: 'd' }, disabled: true }]); + assert.deepEqual(actual.updated, [{ identifier: { id: 'd', uuid: 'd' }, disabled: true, installed: true }]); assert.equal(actual.remote, null); }); - test('merge local and remote extensions when remote moved forwarded with ignored extensions', async () => { + test('merge local and remote extensions when remote moved forwarded with ignored extensions', () => { const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const remoteExtensions: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], ['a']); - assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' } }, { identifier: { id: 'c', uuid: 'c' } }]); + assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' }, installed: true }, { identifier: { id: 'c', uuid: 'c' }, installed: true }]); assert.deepEqual(actual.removed, [{ id: 'd', uuid: 'd' }]); assert.deepEqual(actual.updated, []); assert.equal(actual.remote, null); }); - test('merge local and remote extensions when remote is moved forwarded with skipped extensions', async () => { + test('merge local and remote extensions when remote is moved forwarded with skipped extensions', () => { const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const skippedExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, ]; const remoteExtensions: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, []); - assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' } }, { identifier: { id: 'c', uuid: 'c' } }]); + assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' }, installed: true }, { identifier: { id: 'c', uuid: 'c' }, installed: true }]); assert.deepEqual(actual.removed, [{ id: 'd', uuid: 'd' }]); assert.deepEqual(actual.updated, []); assert.equal(actual.remote, null); }); - test('merge local and remote extensions when remote is moved forwarded with skipped and ignored extensions', async () => { + test('merge local and remote extensions when remote is moved forwarded with skipped and ignored extensions', () => { const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const skippedExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, ]; const remoteExtensions: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, ['b']); - assert.deepEqual(actual.added, [{ identifier: { id: 'c', uuid: 'c' } }]); + assert.deepEqual(actual.added, [{ identifier: { id: 'c', uuid: 'c' }, installed: true }]); assert.deepEqual(actual.removed, [{ id: 'd', uuid: 'd' }]); assert.deepEqual(actual.updated, []); assert.equal(actual.remote, null); }); - test('merge local and remote extensions when local is moved forwarded', async () => { + test('merge local and remote extensions when local is moved forwarded', () => { const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const remoteExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], []); @@ -291,19 +291,19 @@ suite('ExtensionsMerge - No Conflicts', () => { assert.deepEqual(actual.remote, localExtensions); }); - test('merge local and remote extensions when local is moved forwarded with disabled extensions', async () => { + test('merge local and remote extensions when local is moved forwarded with disabled extensions', () => { const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' }, disabled: true }, - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'a', uuid: 'a' }, disabled: true, installed: true }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const remoteExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], []); @@ -314,18 +314,18 @@ suite('ExtensionsMerge - No Conflicts', () => { assert.deepEqual(actual.remote, localExtensions); }); - test('merge local and remote extensions when local is moved forwarded with ignored settings', async () => { + test('merge local and remote extensions when local is moved forwarded with ignored settings', () => { const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const remoteExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], ['b']); @@ -334,30 +334,30 @@ suite('ExtensionsMerge - No Conflicts', () => { assert.deepEqual(actual.removed, []); assert.deepEqual(actual.updated, []); assert.deepEqual(actual.remote, [ - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]); }); - test('merge local and remote extensions when local is moved forwarded with skipped extensions', async () => { + test('merge local and remote extensions when local is moved forwarded with skipped extensions', () => { const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const skippedExtensions: ISyncExtension[] = [ - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const remoteExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const expected: ISyncExtension[] = [ - { identifier: { id: 'd', uuid: 'd' } }, - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, []); @@ -368,25 +368,25 @@ suite('ExtensionsMerge - No Conflicts', () => { assert.deepEqual(actual.remote, expected); }); - test('merge local and remote extensions when local is moved forwarded with skipped and ignored extensions', async () => { + test('merge local and remote extensions when local is moved forwarded with skipped and ignored extensions', () => { const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const skippedExtensions: ISyncExtension[] = [ - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const remoteExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const expected: ISyncExtension[] = [ - { identifier: { id: 'd', uuid: 'd' } }, - { identifier: { id: 'b', uuid: 'b' } }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, ]; const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, ['c']); @@ -397,54 +397,54 @@ suite('ExtensionsMerge - No Conflicts', () => { assert.deepEqual(actual.remote, expected); }); - test('merge local and remote extensions when both moved forwarded', async () => { + test('merge local and remote extensions when both moved forwarded', () => { const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const remoteExtensions: ISyncExtension[] = [ - { identifier: { id: 'd', uuid: 'd' } }, - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'e', uuid: 'e' } }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'e', uuid: 'e' }, installed: true }, ]; const expected: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'e', uuid: 'e' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'e', uuid: 'e' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], []); - assert.deepEqual(actual.added, [{ identifier: { id: 'e', uuid: 'e' } }]); + assert.deepEqual(actual.added, [{ identifier: { id: 'e', uuid: 'e' }, installed: true }]); assert.deepEqual(actual.removed, [{ id: 'a', uuid: 'a' }]); assert.deepEqual(actual.updated, []); assert.deepEqual(actual.remote, expected); }); - test('merge local and remote extensions when both moved forwarded with ignored extensions', async () => { + test('merge local and remote extensions when both moved forwarded with ignored extensions', () => { const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const remoteExtensions: ISyncExtension[] = [ - { identifier: { id: 'd', uuid: 'd' } }, - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'e', uuid: 'e' } }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'e', uuid: 'e' }, installed: true }, ]; const expected: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'e', uuid: 'e' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'e', uuid: 'e' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], ['a', 'e']); @@ -455,58 +455,58 @@ suite('ExtensionsMerge - No Conflicts', () => { assert.deepEqual(actual.remote, expected); }); - test('merge local and remote extensions when both moved forwarded with skipped extensions', async () => { + test('merge local and remote extensions when both moved forwarded with skipped extensions', () => { const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const skippedExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, ]; const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const remoteExtensions: ISyncExtension[] = [ - { identifier: { id: 'd', uuid: 'd' } }, - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'e', uuid: 'e' } }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'e', uuid: 'e' }, installed: true }, ]; const expected: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'e', uuid: 'e' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'e', uuid: 'e' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, []); - assert.deepEqual(actual.added, [{ identifier: { id: 'e', uuid: 'e' } }]); + assert.deepEqual(actual.added, [{ identifier: { id: 'e', uuid: 'e' }, installed: true }]); assert.deepEqual(actual.removed, []); assert.deepEqual(actual.updated, []); assert.deepEqual(actual.remote, expected); }); - test('merge local and remote extensions when both moved forwarded with skipped and ignoredextensions', async () => { + test('merge local and remote extensions when both moved forwarded with skipped and ignoredextensions', () => { const baseExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const skippedExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, ]; const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const remoteExtensions: ISyncExtension[] = [ - { identifier: { id: 'd', uuid: 'd' } }, - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'e', uuid: 'e' } }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'e', uuid: 'e' }, installed: true }, ]; const expected: ISyncExtension[] = [ - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'e', uuid: 'e' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'e', uuid: 'e' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, ['e']); @@ -517,30 +517,134 @@ suite('ExtensionsMerge - No Conflicts', () => { assert.deepEqual(actual.remote, expected); }); - test('merge when remote extension has no uuid and different extension id case', async () => { + test('merge when remote extension has no uuid and different extension id case', () => { const localExtensions: ISyncExtension[] = [ - { identifier: { id: 'a', uuid: 'a' } }, - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const remoteExtensions: ISyncExtension[] = [ - { identifier: { id: 'A' } }, - { identifier: { id: 'd', uuid: 'd' } }, + { identifier: { id: 'A' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, ]; const expected: ISyncExtension[] = [ - { identifier: { id: 'A', uuid: 'a' } }, - { identifier: { id: 'd', uuid: 'd' } }, - { identifier: { id: 'b', uuid: 'b' } }, - { identifier: { id: 'c', uuid: 'c' } }, + { identifier: { id: 'A', uuid: 'a' }, installed: true }, + { identifier: { id: 'd', uuid: 'd' }, installed: true }, + { identifier: { id: 'b', uuid: 'b' }, installed: true }, + { identifier: { id: 'c', uuid: 'c' }, installed: true }, ]; const actual = merge(localExtensions, remoteExtensions, null, [], []); - assert.deepEqual(actual.added, [{ identifier: { id: 'd', uuid: 'd' } }]); + assert.deepEqual(actual.added, [{ identifier: { id: 'd', uuid: 'd' }, installed: true }]); assert.deepEqual(actual.removed, []); assert.deepEqual(actual.updated, []); assert.deepEqual(actual.remote, expected); }); + test('merge when remote extension is not an installed extension', () => { + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + { identifier: { id: 'b', uuid: 'b' } }, + ]; + + const actual = merge(localExtensions, remoteExtensions, null, [], []); + + assert.deepEqual(actual.added, []); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, null); + }); + + test('merge when remote extension is not an installed extension but is an installed extension locally', () => { + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' } }, + ]; + + const actual = merge(localExtensions, remoteExtensions, null, [], []); + + assert.deepEqual(actual.added, []); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, localExtensions); + }); + + test('merge when an extension is not an installed extension remotely and does not exist locally', () => { + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' } }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' } }, + { identifier: { id: 'b', uuid: 'b' } }, + ]; + + const actual = merge(localExtensions, remoteExtensions, remoteExtensions, [], []); + + assert.deepEqual(actual.added, []); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, null); + }); + + test('merge when an extension is an installed extension remotely but not locally and updated locally', () => { + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, disabled: true }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, installed: true }, + ]; + const expected: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, installed: true, disabled: true }, + ]; + + const actual = merge(localExtensions, remoteExtensions, remoteExtensions, [], []); + + assert.deepEqual(actual.added, []); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, expected); + }); + + test('merge when an extension is an installed extension remotely but not locally and updated remotely', () => { + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' } }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, installed: true, disabled: true }, + ]; + + const actual = merge(localExtensions, remoteExtensions, localExtensions, [], []); + + assert.deepEqual(actual.added, []); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, remoteExtensions); + assert.deepEqual(actual.remote, null); + }); + + test('merge not installed extensions', () => { + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' } }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' } }, + ]; + const expected: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' } }, + { identifier: { id: 'a', uuid: 'a' } }, + ]; + + const actual = merge(localExtensions, remoteExtensions, null, [], []); + + assert.deepEqual(actual.added, []); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, expected); + }); }); diff --git a/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts b/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts index 9f86b9354a5..2d5594c01d7 100644 --- a/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts @@ -4,13 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IUserDataSyncStoreService, IUserDataSyncService, SyncResource, SyncStatus, IGlobalState } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncStoreService, IUserDataSyncService, SyncResource, SyncStatus, IGlobalState, ISyncData } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient'; import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; import { IFileService } from 'vs/platform/files/common/files'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ISyncData } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { GlobalStateSynchroniser } from 'vs/platform/userDataSync/common/globalStateSync'; import { VSBuffer } from 'vs/base/common/buffer'; @@ -44,11 +43,58 @@ suite('GlobalStateSync', () => { teardown(() => disposableStore.clear()); + test('when global state does not exist', async () => { + assert.deepEqual(await testObject.getLastSyncUserData(), null); + let manifest = await testClient.manifest(); + server.reset(); + await testObject.sync(manifest); + + assert.deepEqual(server.requests, [ + { type: 'GET', url: `${server.url}/v1/resource/${testObject.resource}/latest`, headers: {} }, + ]); + + const lastSyncUserData = await testObject.getLastSyncUserData(); + const remoteUserData = await testObject.getRemoteUserData(null); + assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref); + assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData); + assert.equal(lastSyncUserData!.syncData, null); + + manifest = await testClient.manifest(); + server.reset(); + await testObject.sync(manifest); + assert.deepEqual(server.requests, []); + + manifest = await testClient.manifest(); + server.reset(); + await testObject.sync(manifest); + assert.deepEqual(server.requests, []); + }); + + test('when global state is created after first sync', async () => { + await testObject.sync(await testClient.manifest()); + updateStorage('a', 'value1', testClient); + + let lastSyncUserData = await testObject.getLastSyncUserData(); + const manifest = await testClient.manifest(); + server.reset(); + await testObject.sync(manifest); + + assert.deepEqual(server.requests, [ + { type: 'POST', url: `${server.url}/v1/resource/${testObject.resource}`, headers: { 'If-Match': lastSyncUserData?.ref } }, + ]); + + lastSyncUserData = await testObject.getLastSyncUserData(); + const remoteUserData = await testObject.getRemoteUserData(null); + assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref); + assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData); + assert.deepEqual(JSON.parse(lastSyncUserData!.syncData!.content).storage, { 'a': { version: 1, value: 'value1' } }); + }); + test('first time sync - outgoing to server (no state)', async () => { updateStorage('a', 'value1', testClient); await updateLocale(testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -63,7 +109,7 @@ suite('GlobalStateSync', () => { await updateLocale(client2); await client2.sync(); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -76,7 +122,7 @@ suite('GlobalStateSync', () => { await client2.sync(); updateStorage('b', 'value2', testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -94,7 +140,7 @@ suite('GlobalStateSync', () => { await client2.sync(); updateStorage('a', 'value2', client2); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -109,10 +155,10 @@ suite('GlobalStateSync', () => { test('sync adding a storage value', async () => { updateStorage('a', 'value1', testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); updateStorage('b', 'value2', testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -127,10 +173,10 @@ suite('GlobalStateSync', () => { test('sync updating a storage value', async () => { updateStorage('a', 'value1', testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); updateStorage('a', 'value2', testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -145,10 +191,10 @@ suite('GlobalStateSync', () => { test('sync removing a storage value', async () => { updateStorage('a', 'value1', testClient); updateStorage('b', 'value2', testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); removeStorage('b', testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -161,33 +207,6 @@ suite('GlobalStateSync', () => { assert.deepEqual(actual.storage, { 'a': { version: 1, value: 'value1' } }); }); - test('first time sync - push', async () => { - updateStorage('a', 'value1', testClient); - updateStorage('b', 'value2', testClient); - - await testObject.push(); - assert.equal(testObject.status, SyncStatus.Idle); - assert.deepEqual(testObject.conflicts, []); - - const { content } = await testClient.read(testObject.resource); - assert.ok(content !== null); - const actual = parseGlobalState(content!); - assert.deepEqual(actual.storage, { 'a': { version: 1, value: 'value1' }, 'b': { version: 1, value: 'value2' } }); - }); - - test('first time sync - pull', async () => { - updateStorage('a', 'value1', client2); - updateStorage('b', 'value2', client2); - await client2.sync(); - - await testObject.pull(); - assert.equal(testObject.status, SyncStatus.Idle); - assert.deepEqual(testObject.conflicts, []); - - assert.equal(readStorage('a', testClient), 'value1'); - assert.equal(readStorage('b', testClient), 'value2'); - }); - function parseGlobalState(content: string): IGlobalState { const syncData: ISyncData = JSON.parse(content); return JSON.parse(syncData.content); diff --git a/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts b/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts new file mode 100644 index 00000000000..49d439bfd60 --- /dev/null +++ b/src/vs/platform/userDataSync/test/common/keybindingsSync.test.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 assert from 'assert'; +import { IUserDataSyncStoreService, IUserDataSyncService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; +import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient'; +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { KeybindingsSynchroniser } from 'vs/platform/userDataSync/common/keybindingsSync'; +import { VSBuffer } from 'vs/base/common/buffer'; + +suite('KeybindingsSync', () => { + + const disposableStore = new DisposableStore(); + const server = new UserDataSyncTestServer(); + let client: UserDataSyncClient; + + let testObject: KeybindingsSynchroniser; + + setup(async () => { + client = disposableStore.add(new UserDataSyncClient(server)); + await client.setUp(true); + testObject = (client.instantiationService.get(IUserDataSyncService) as UserDataSyncService).getSynchroniser(SyncResource.Keybindings) as KeybindingsSynchroniser; + disposableStore.add(toDisposable(() => client.instantiationService.get(IUserDataSyncStoreService).clear())); + }); + + teardown(() => disposableStore.clear()); + + test('when keybindings file does not exist', async () => { + const fileService = client.instantiationService.get(IFileService); + const keybindingsResource = client.instantiationService.get(IEnvironmentService).keybindingsResource; + + assert.deepEqual(await testObject.getLastSyncUserData(), null); + let manifest = await client.manifest(); + server.reset(); + await testObject.sync(manifest); + + assert.deepEqual(server.requests, [ + { type: 'GET', url: `${server.url}/v1/resource/${testObject.resource}/latest`, headers: {} }, + ]); + assert.ok(!await fileService.exists(keybindingsResource)); + + const lastSyncUserData = await testObject.getLastSyncUserData(); + const remoteUserData = await testObject.getRemoteUserData(null); + assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref); + assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData); + assert.equal(lastSyncUserData!.syncData, null); + + manifest = await client.manifest(); + server.reset(); + await testObject.sync(manifest); + assert.deepEqual(server.requests, []); + + manifest = await client.manifest(); + server.reset(); + await testObject.sync(manifest); + assert.deepEqual(server.requests, []); + }); + + test('when keybindings file is created after first sync', async () => { + const fileService = client.instantiationService.get(IFileService); + const keybindingsResource = client.instantiationService.get(IEnvironmentService).keybindingsResource; + await testObject.sync(await client.manifest()); + await fileService.createFile(keybindingsResource, VSBuffer.fromString('[]')); + + let lastSyncUserData = await testObject.getLastSyncUserData(); + const manifest = await client.manifest(); + server.reset(); + await testObject.sync(manifest); + + assert.deepEqual(server.requests, [ + { type: 'POST', url: `${server.url}/v1/resource/${testObject.resource}`, headers: { 'If-Match': lastSyncUserData?.ref } }, + ]); + + lastSyncUserData = await testObject.getLastSyncUserData(); + const remoteUserData = await testObject.getRemoteUserData(null); + assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref); + assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData); + assert.equal(testObject.getKeybindingsContentFromSyncContent(lastSyncUserData!.syncData!.content!), '[]'); + }); + + test('test apply remote when keybindings file does not exist', async () => { + const fileService = client.instantiationService.get(IFileService); + const keybindingsResource = client.instantiationService.get(IEnvironmentService).keybindingsResource; + if (await fileService.exists(keybindingsResource)) { + await fileService.del(keybindingsResource); + } + + const preview = (await testObject.preview(await client.manifest()))!; + + server.reset(); + const content = await testObject.resolveContent(preview.resourcePreviews[0].remoteResource); + await testObject.accept(preview.resourcePreviews[0].remoteResource, content); + await testObject.apply(false); + assert.deepEqual(server.requests, []); + }); + +}); diff --git a/src/vs/platform/userDataSync/test/common/settingsMerge.test.ts b/src/vs/platform/userDataSync/test/common/settingsMerge.test.ts index c620e9ba876..644011b982d 100644 --- a/src/vs/platform/userDataSync/test/common/settingsMerge.test.ts +++ b/src/vs/platform/userDataSync/test/common/settingsMerge.test.ts @@ -1495,8 +1495,102 @@ suite('SettingsMerge - Add Setting', () => { assert.equal(actual, sourceContent); }); + + test('Insert after a comment with comma separator of previous setting and no next nodes ', () => { + + const sourceContent = ` +{ + "a": 1 + // this is comment for a + , + "b": 2 +}`; + const targetContent = ` +{ + "a": 1 + // this is comment for a + , +}`; + + const expected = ` +{ + "a": 1 + // this is comment for a + , + "b": 2 +}`; + + const actual = addSetting('b', sourceContent, targetContent, formattingOptions); + + assert.equal(actual, expected); + }); + + test('Insert after a comment with comma separator of previous setting and there is a setting after ', () => { + + const sourceContent = ` +{ + "a": 1 + // this is comment for a + , + "b": 2, + "c": 3 +}`; + const targetContent = ` +{ + "a": 1 + // this is comment for a + , + "c": 3 +}`; + + const expected = ` +{ + "a": 1 + // this is comment for a + , + "b": 2, + "c": 3 +}`; + + const actual = addSetting('b', sourceContent, targetContent, formattingOptions); + + assert.equal(actual, expected); + }); + + test('Insert after a comment with comma separator of previous setting and there is a comment after ', () => { + + const sourceContent = ` +{ + "a": 1 + // this is comment for a + , + "b": 2 + // this is a comment +}`; + const targetContent = ` +{ + "a": 1 + // this is comment for a + , + // this is a comment +}`; + + const expected = ` +{ + "a": 1 + // this is comment for a + , + "b": 2 + // this is a comment +}`; + + const actual = addSetting('b', sourceContent, targetContent, formattingOptions); + + assert.equal(actual, expected); + }); }); + function stringify(value: any): string { return JSON.stringify(value, null, '\t'); } diff --git a/src/vs/platform/userDataSync/test/common/settingsSync.test.ts b/src/vs/platform/userDataSync/test/common/settingsSync.test.ts index c0f87712cf1..6b1511f0bde 100644 --- a/src/vs/platform/userDataSync/test/common/settingsSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/settingsSync.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IUserDataSyncStoreService, IUserDataSyncService, SyncResource, UserDataSyncError, UserDataSyncErrorCode } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncStoreService, IUserDataSyncService, SyncResource, UserDataSyncError, UserDataSyncErrorCode, ISyncData, SyncStatus } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient'; import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { SettingsSynchroniser, ISettingsSyncContent } from 'vs/platform/userDataSync/common/settingsSync'; @@ -12,44 +12,96 @@ import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyn import { IFileService } from 'vs/platform/files/common/files'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { VSBuffer } from 'vs/base/common/buffer'; -import { ISyncData } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { Event } from 'vs/base/common/event'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -suite('SettingsSync', () => { +Registry.as(Extensions.Configuration).registerConfiguration({ + 'id': 'settingsSync', + 'type': 'object', + 'properties': { + 'settingsSync.machine': { + 'type': 'string', + 'scope': ConfigurationScope.MACHINE + }, + 'settingsSync.machineOverridable': { + 'type': 'string', + 'scope': ConfigurationScope.MACHINE_OVERRIDABLE + } + } +}); + +suite('SettingsSync - Auto', () => { const disposableStore = new DisposableStore(); const server = new UserDataSyncTestServer(); let client: UserDataSyncClient; - let testObject: SettingsSynchroniser; - suiteSetup(() => { - Registry.as(Extensions.Configuration).registerConfiguration({ - 'id': 'settingsSync', - 'type': 'object', - 'properties': { - 'settingsSync.machine': { - 'type': 'string', - 'scope': ConfigurationScope.MACHINE - }, - 'settingsSync.machineOverridable': { - 'type': 'string', - 'scope': ConfigurationScope.MACHINE_OVERRIDABLE - } - } - }); - }); - setup(async () => { client = disposableStore.add(new UserDataSyncClient(server)); - await client.setUp(); + await client.setUp(true); testObject = (client.instantiationService.get(IUserDataSyncService) as UserDataSyncService).getSynchroniser(SyncResource.Settings) as SettingsSynchroniser; disposableStore.add(toDisposable(() => client.instantiationService.get(IUserDataSyncStoreService).clear())); }); teardown(() => disposableStore.clear()); + test('when settings file does not exist', async () => { + const fileService = client.instantiationService.get(IFileService); + const settingResource = client.instantiationService.get(IEnvironmentService).settingsResource; + + assert.deepEqual(await testObject.getLastSyncUserData(), null); + let manifest = await client.manifest(); + server.reset(); + await testObject.sync(manifest); + + assert.deepEqual(server.requests, [ + { type: 'GET', url: `${server.url}/v1/resource/${testObject.resource}/latest`, headers: {} }, + ]); + assert.ok(!await fileService.exists(settingResource)); + + const lastSyncUserData = await testObject.getLastSyncUserData(); + const remoteUserData = await testObject.getRemoteUserData(null); + assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref); + assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData); + assert.equal(lastSyncUserData!.syncData, null); + + manifest = await client.manifest(); + server.reset(); + await testObject.sync(manifest); + assert.deepEqual(server.requests, []); + + manifest = await client.manifest(); + server.reset(); + await testObject.sync(manifest); + assert.deepEqual(server.requests, []); + }); + + test('when settings file is created after first sync', async () => { + const fileService = client.instantiationService.get(IFileService); + + const settingsResource = client.instantiationService.get(IEnvironmentService).settingsResource; + await testObject.sync(await client.manifest()); + await fileService.createFile(settingsResource, VSBuffer.fromString('{}')); + + let lastSyncUserData = await testObject.getLastSyncUserData(); + const manifest = await client.manifest(); + server.reset(); + await testObject.sync(manifest); + + assert.deepEqual(server.requests, [ + { type: 'POST', url: `${server.url}/v1/resource/${testObject.resource}`, headers: { 'If-Match': lastSyncUserData?.ref } }, + ]); + + lastSyncUserData = await testObject.getLastSyncUserData(); + const remoteUserData = await testObject.getRemoteUserData(null); + assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref); + assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData); + assert.equal(testObject.parseSettingsSyncContent(lastSyncUserData!.syncData!.content!)?.settings, '{}'); + }); + test('sync for first time to the server', async () => { const expected = `{ @@ -74,8 +126,8 @@ suite('SettingsSync', () => { "workbench.view.experimental.allowMovingToNewContainer": true, }`; - await updateSettings(expected); - await testObject.sync(); + await updateSettings(expected, client); + await testObject.sync(await client.manifest()); const { content } = await client.read(testObject.resource); assert.ok(content !== null); @@ -97,9 +149,9 @@ suite('SettingsSync', () => { "settingsSync.machine": "someValue", "settingsSync.machineOverridable": "someValue" }`; - await updateSettings(settingsContent); + await updateSettings(settingsContent, client); - await testObject.sync(); + await testObject.sync(await client.manifest()); const { content } = await client.read(testObject.resource); assert.ok(content !== null); @@ -128,9 +180,9 @@ suite('SettingsSync', () => { // Machine "settingsSync.machineOverridable": "someValue" }`; - await updateSettings(settingsContent); + await updateSettings(settingsContent, client); - await testObject.sync(); + await testObject.sync(await client.manifest()); const { content } = await client.read(testObject.resource); assert.ok(content !== null); @@ -159,9 +211,9 @@ suite('SettingsSync', () => { "settingsSync.machineOverridable": "someValue", "files.simpleDialog.enable": true, }`; - await updateSettings(settingsContent); + await updateSettings(settingsContent, client); - await testObject.sync(); + await testObject.sync(await client.manifest()); const { content } = await client.read(testObject.resource); assert.ok(content !== null); @@ -183,9 +235,9 @@ suite('SettingsSync', () => { "settingsSync.machine": "someValue", "settingsSync.machineOverridable": "someValue" }`; - await updateSettings(settingsContent); + await updateSettings(settingsContent, client); - await testObject.sync(); + await testObject.sync(await client.manifest()); const { content } = await client.read(testObject.resource); assert.ok(content !== null); @@ -201,9 +253,9 @@ suite('SettingsSync', () => { "settingsSync.machine": "someValue", "settingsSync.machineOverridable": "someValue", }`; - await updateSettings(settingsContent); + await updateSettings(settingsContent, client); - await testObject.sync(); + await testObject.sync(await client.manifest()); const { content } = await client.read(testObject.resource); assert.ok(content !== null); @@ -213,6 +265,24 @@ suite('SettingsSync', () => { }`); }); + test('local change event is triggered when settings are changed', async () => { + const content = + `{ + "files.autoSave": "afterDelay", + "files.simpleDialog.enable": true, +}`; + + await updateSettings(content, client); + await testObject.sync(await client.manifest()); + + const promise = Event.toPromise(testObject.onDidChangeLocal); + await updateSettings(`{ + "files.autoSave": "off", + "files.simpleDialog.enable": true, +}`, client); + await promise; + }); + test('do not sync ignored settings', async () => { const settingsContent = `{ @@ -235,9 +305,9 @@ suite('SettingsSync', () => { "terminal.integrated.shell.osx" ] }`; - await updateSettings(settingsContent); + await updateSettings(settingsContent, client); - await testObject.sync(); + await testObject.sync(await client.manifest()); const { content } = await client.read(testObject.resource); assert.ok(content !== null); @@ -283,9 +353,9 @@ suite('SettingsSync', () => { // Machine "settingsSync.machine": "someValue", }`; - await updateSettings(settingsContent); + await updateSettings(settingsContent, client); - await testObject.sync(); + await testObject.sync(await client.manifest()); const { content } = await client.read(testObject.resource); assert.ok(content !== null); @@ -330,10 +400,10 @@ suite('SettingsSync', () => { "workbench.view.experimental.allowMovingToNewContainer": true, }`; - await updateSettings(expected); + await updateSettings(expected, client); try { - await testObject.sync(); + await testObject.sync(await client.manifest()); assert.fail('should fail with invalid content error'); } catch (e) { assert.ok(e instanceof UserDataSyncError); @@ -341,15 +411,109 @@ suite('SettingsSync', () => { } }); - function parseSettings(content: string): string { - const syncData: ISyncData = JSON.parse(content); - const settingsSyncContent: ISettingsSyncContent = JSON.parse(syncData.content); - return settingsSyncContent.settings; - } + test('sync when there are conflicts', async () => { + const client2 = disposableStore.add(new UserDataSyncClient(server)); + await client2.setUp(true); + await updateSettings(JSON.stringify({ + 'a': 1, + 'b': 2, + 'sync.ignoredSettings': ['a'] + }), client2); + await client2.sync(); - async function updateSettings(content: string): Promise { - await client.instantiationService.get(IFileService).writeFile(client.instantiationService.get(IEnvironmentService).settingsResource, VSBuffer.fromString(content)); - } + await updateSettings(JSON.stringify({ + 'a': 2, + 'b': 1, + 'sync.ignoredSettings': ['a'] + }), client); + await testObject.sync(await client.manifest()); + assert.equal(testObject.status, SyncStatus.HasConflicts); + assert.equal(testObject.conflicts[0].localResource.toString(), testObject.localResource); + + const fileService = client.instantiationService.get(IFileService); + const mergeContent = (await fileService.readFile(testObject.conflicts[0].previewResource)).value.toString(); + assert.deepEqual(JSON.parse(mergeContent), { + 'b': 1, + 'sync.ignoredSettings': ['a'] + }); + }); }); + +suite('SettingsSync - Manual', () => { + + const disposableStore = new DisposableStore(); + const server = new UserDataSyncTestServer(); + let client: UserDataSyncClient; + let testObject: SettingsSynchroniser; + + setup(async () => { + client = disposableStore.add(new UserDataSyncClient(server)); + await client.setUp(true); + testObject = (client.instantiationService.get(IUserDataSyncService) as UserDataSyncService).getSynchroniser(SyncResource.Settings) as SettingsSynchroniser; + disposableStore.add(toDisposable(() => client.instantiationService.get(IUserDataSyncStoreService).clear())); + }); + + teardown(() => disposableStore.clear()); + + test('do not sync ignored settings', async () => { + const settingsContent = + `{ + // Always + "files.autoSave": "afterDelay", + "files.simpleDialog.enable": true, + + // Editor + "editor.fontFamily": "Fira Code", + + // Terminal + "terminal.integrated.shell.osx": "some path", + + // Workbench + "workbench.colorTheme": "GitHub Sharp", + + // Ignored + "sync.ignoredSettings": [ + "editor.fontFamily", + "terminal.integrated.shell.osx" + ] +}`; + await updateSettings(settingsContent, client); + + let preview = await testObject.preview(await client.manifest()); + assert.equal(testObject.status, SyncStatus.Syncing); + preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); + preview = await testObject.apply(false); + + const { content } = await client.read(testObject.resource); + assert.ok(content !== null); + const actual = parseSettings(content!); + assert.deepEqual(actual, `{ + // Always + "files.autoSave": "afterDelay", + "files.simpleDialog.enable": true, + + // Workbench + "workbench.colorTheme": "GitHub Sharp", + + // Ignored + "sync.ignoredSettings": [ + "editor.fontFamily", + "terminal.integrated.shell.osx" + ] +}`); + }); + +}); + +function parseSettings(content: string): string { + const syncData: ISyncData = JSON.parse(content); + const settingsSyncContent: ISettingsSyncContent = JSON.parse(syncData.content); + return settingsSyncContent.settings; +} + +async function updateSettings(content: string, client: UserDataSyncClient): Promise { + await client.instantiationService.get(IFileService).writeFile(client.instantiationService.get(IEnvironmentService).settingsResource, VSBuffer.fromString(content)); + await client.instantiationService.get(IConfigurationService).reloadConfiguration(); +} diff --git a/src/vs/platform/userDataSync/test/common/snippetsMerge.test.ts b/src/vs/platform/userDataSync/test/common/snippetsMerge.test.ts index 5a396c4a6da..55b33d2d8ce 100644 --- a/src/vs/platform/userDataSync/test/common/snippetsMerge.test.ts +++ b/src/vs/platform/userDataSync/test/common/snippetsMerge.test.ts @@ -116,11 +116,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, null); - assert.deepEqual(actual.added, {}); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.local.added, {}); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); assert.deepEqual(actual.conflicts, []); - assert.equal(actual.remote, null); + assert.deepEqual(actual.remote.added, {}); + assert.deepEqual(actual.remote.updated, {}); + assert.deepEqual(actual.remote.removed, []); }); test('merge when local and remote are same with multiple entries', async () => { @@ -129,11 +131,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, null); - assert.deepEqual(actual.added, {}); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.local.added, {}); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); assert.deepEqual(actual.conflicts, []); - assert.equal(actual.remote, null); + assert.deepEqual(actual.remote.added, {}); + assert.deepEqual(actual.remote.updated, {}); + assert.deepEqual(actual.remote.removed, []); }); test('merge when local and remote are same with multiple entries in different order', async () => { @@ -142,11 +146,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, null); - assert.deepEqual(actual.added, {}); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.local.added, {}); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); assert.deepEqual(actual.conflicts, []); - assert.equal(actual.remote, null); + assert.deepEqual(actual.remote.added, {}); + assert.deepEqual(actual.remote.updated, {}); + assert.deepEqual(actual.remote.removed, []); }); test('merge when local and remote are same with different base content', async () => { @@ -156,11 +162,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, base); - assert.deepEqual(actual.added, {}); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.local.added, {}); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); assert.deepEqual(actual.conflicts, []); - assert.equal(actual.remote, null); + assert.deepEqual(actual.remote.added, {}); + assert.deepEqual(actual.remote.updated, {}); + assert.deepEqual(actual.remote.removed, []); }); test('merge when a new entry is added to remote', async () => { @@ -169,11 +177,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, null); - assert.deepEqual(actual.added, { 'typescript.json': tsSnippet1 }); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.local.added, { 'typescript.json': tsSnippet1 }); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); assert.deepEqual(actual.conflicts, []); - assert.equal(actual.remote, null); + assert.deepEqual(actual.remote.added, {}); + assert.deepEqual(actual.remote.updated, {}); + assert.deepEqual(actual.remote.removed, []); }); test('merge when multiple new entries are added to remote', async () => { @@ -182,11 +192,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, null); - assert.deepEqual(actual.added, remote); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.local.added, remote); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); assert.deepEqual(actual.conflicts, []); - assert.equal(actual.remote, null); + assert.deepEqual(actual.remote.added, {}); + assert.deepEqual(actual.remote.updated, {}); + assert.deepEqual(actual.remote.removed, []); }); test('merge when new entry is added to remote from base and local has not changed', async () => { @@ -195,11 +207,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, local); - assert.deepEqual(actual.added, { 'typescript.json': tsSnippet1 }); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.local.added, { 'typescript.json': tsSnippet1 }); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); assert.deepEqual(actual.conflicts, []); - assert.equal(actual.remote, null); + assert.deepEqual(actual.remote.added, {}); + assert.deepEqual(actual.remote.updated, {}); + assert.deepEqual(actual.remote.removed, []); }); test('merge when an entry is removed from remote from base and local has not changed', async () => { @@ -208,11 +222,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, local); - assert.deepEqual(actual.added, {}); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, ['typescript.json']); + assert.deepEqual(actual.local.added, {}); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, ['typescript.json']); assert.deepEqual(actual.conflicts, []); - assert.equal(actual.remote, null); + assert.deepEqual(actual.remote.added, {}); + assert.deepEqual(actual.remote.updated, {}); + assert.deepEqual(actual.remote.removed, []); }); test('merge when all entries are removed from base and local has not changed', async () => { @@ -221,11 +237,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, local); - assert.deepEqual(actual.added, {}); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, ['html.json', 'typescript.json']); + assert.deepEqual(actual.local.added, {}); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, ['html.json', 'typescript.json']); assert.deepEqual(actual.conflicts, []); - assert.equal(actual.remote, null); + assert.deepEqual(actual.remote.added, {}); + assert.deepEqual(actual.remote.updated, {}); + assert.deepEqual(actual.remote.removed, []); }); test('merge when an entry is updated in remote from base and local has not changed', async () => { @@ -234,11 +252,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, local); - assert.deepEqual(actual.added, {}); - assert.deepEqual(actual.updated, { 'html.json': htmlSnippet2 }); - assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.local.added, {}); + assert.deepEqual(actual.local.updated, { 'html.json': htmlSnippet2 }); + assert.deepEqual(actual.local.removed, []); assert.deepEqual(actual.conflicts, []); - assert.equal(actual.remote, null); + assert.deepEqual(actual.remote.added, {}); + assert.deepEqual(actual.remote.updated, {}); + assert.deepEqual(actual.remote.removed, []); }); test('merge when remote has moved forwarded with multiple changes and local stays with base', async () => { @@ -247,11 +267,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, local); - assert.deepEqual(actual.added, { 'c.json': cSnippet }); - assert.deepEqual(actual.updated, { 'html.json': htmlSnippet2 }); - assert.deepEqual(actual.removed, ['typescript.json']); + assert.deepEqual(actual.local.added, { 'c.json': cSnippet }); + assert.deepEqual(actual.local.updated, { 'html.json': htmlSnippet2 }); + assert.deepEqual(actual.local.removed, ['typescript.json']); assert.deepEqual(actual.conflicts, []); - assert.deepEqual(actual.remote, null); + assert.deepEqual(actual.remote.added, {}); + assert.deepEqual(actual.remote.updated, {}); + assert.deepEqual(actual.remote.removed, []); }); test('merge when a new entries are added to local', async () => { @@ -260,11 +282,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, null); - assert.deepEqual(actual.added, {}); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.local.added, {}); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); assert.deepEqual(actual.conflicts, []); - assert.deepEqual(actual.remote, local); + assert.deepEqual(actual.remote.added, { 'c.json': cSnippet }); + assert.deepEqual(actual.remote.updated, {}); + assert.deepEqual(actual.remote.removed, []); }); test('merge when multiple new entries are added to local from base and remote is not changed', async () => { @@ -273,11 +297,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, remote); - assert.deepEqual(actual.added, {}); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.local.added, {}); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); assert.deepEqual(actual.conflicts, []); - assert.deepEqual(actual.remote, { 'typescript.json': tsSnippet1, 'html.json': htmlSnippet1, 'c.json': cSnippet }); + assert.deepEqual(actual.remote.added, { 'html.json': htmlSnippet1, 'c.json': cSnippet }); + assert.deepEqual(actual.remote.updated, {}); + assert.deepEqual(actual.remote.removed, []); }); test('merge when an entry is removed from local from base and remote has not changed', async () => { @@ -286,11 +312,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, remote); - assert.deepEqual(actual.added, {}); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.local.added, {}); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); assert.deepEqual(actual.conflicts, []); - assert.deepEqual(actual.remote, local); + assert.deepEqual(actual.remote.added, {}); + assert.deepEqual(actual.remote.updated, {}); + assert.deepEqual(actual.remote.removed, ['typescript.json']); }); test('merge when an entry is updated in local from base and remote has not changed', async () => { @@ -299,11 +327,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, remote); - assert.deepEqual(actual.added, {}); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.local.added, {}); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); assert.deepEqual(actual.conflicts, []); - assert.deepEqual(actual.remote, local); + assert.deepEqual(actual.remote.added, {}); + assert.deepEqual(actual.remote.updated, { 'html.json': htmlSnippet2 }); + assert.deepEqual(actual.remote.removed, []); }); test('merge when local has moved forwarded with multiple changes and remote stays with base', async () => { @@ -312,11 +342,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, remote); - assert.deepEqual(actual.added, {}); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.local.added, {}); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); assert.deepEqual(actual.conflicts, []); - assert.deepEqual(actual.remote, local); + assert.deepEqual(actual.remote.added, { 'c.json': cSnippet }); + assert.deepEqual(actual.remote.updated, { 'html.json': htmlSnippet2 }); + assert.deepEqual(actual.remote.removed, ['typescript.json']); }); test('merge when local and remote with one entry but different value', async () => { @@ -325,11 +357,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, null); - assert.deepEqual(actual.added, {}); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.local.added, {}); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); assert.deepEqual(actual.conflicts, ['html.json']); - assert.deepEqual(actual.remote, null); + assert.deepEqual(actual.remote.added, {}); + assert.deepEqual(actual.remote.updated, {}); + assert.deepEqual(actual.remote.removed, []); }); test('merge when the entry is removed in remote but updated in local and a new entry is added in remote', async () => { @@ -339,11 +373,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, base); - assert.deepEqual(actual.added, { 'typescript.json': tsSnippet1 }); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.local.added, { 'typescript.json': tsSnippet1 }); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); assert.deepEqual(actual.conflicts, ['html.json']); - assert.deepEqual(actual.remote, null); + assert.deepEqual(actual.remote.added, {}); + assert.deepEqual(actual.remote.updated, {}); + assert.deepEqual(actual.remote.removed, []); }); test('merge with single entry and local is empty', async () => { @@ -353,11 +389,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, base); - assert.deepEqual(actual.added, { 'html.json': htmlSnippet2 }); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.local.added, { 'html.json': htmlSnippet2 }); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); assert.deepEqual(actual.conflicts, []); - assert.deepEqual(actual.remote, null); + assert.deepEqual(actual.remote.added, {}); + assert.deepEqual(actual.remote.updated, {}); + assert.deepEqual(actual.remote.removed, []); }); test('merge when local and remote has moved forwareded with conflicts', async () => { @@ -367,41 +405,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, base); - assert.deepEqual(actual.added, { 'typescript.json': tsSnippet2 }); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.local.added, { 'typescript.json': tsSnippet2 }); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); assert.deepEqual(actual.conflicts, ['html.json']); - assert.deepEqual(actual.remote, { 'typescript.json': tsSnippet2, 'c.json': cSnippet }); - }); - - test('merge when local and remote has moved forwareded with resolved conflicts - update', async () => { - const base = { 'html.json': htmlSnippet1, 'typescript.json': tsSnippet1 }; - const local = { 'html.json': htmlSnippet2, 'c.json': cSnippet }; - const remote = { 'typescript.json': tsSnippet2 }; - const resolvedConflicts = { 'html.json': htmlSnippet2 }; - - const actual = merge(local, remote, base, resolvedConflicts); - - assert.deepEqual(actual.added, { 'typescript.json': tsSnippet2 }); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, []); - assert.deepEqual(actual.conflicts, []); - assert.deepEqual(actual.remote, { 'typescript.json': tsSnippet2, 'html.json': htmlSnippet2, 'c.json': cSnippet }); - }); - - test('merge when local and remote has moved forwareded with resolved conflicts - remove', async () => { - const base = { 'html.json': htmlSnippet1, 'typescript.json': tsSnippet1 }; - const local = { 'html.json': htmlSnippet2, 'c.json': cSnippet }; - const remote = { 'typescript.json': tsSnippet2 }; - const resolvedConflicts = { 'html.json': null }; - - const actual = merge(local, remote, base, resolvedConflicts); - - assert.deepEqual(actual.added, { 'typescript.json': tsSnippet2 }); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, ['html.json']); - assert.deepEqual(actual.conflicts, []); - assert.deepEqual(actual.remote, { 'typescript.json': tsSnippet2, 'c.json': cSnippet }); + assert.deepEqual(actual.remote.added, { 'c.json': cSnippet }); + assert.deepEqual(actual.remote.updated, {}); + assert.deepEqual(actual.remote.removed, []); }); test('merge when local and remote has moved forwareded with multiple conflicts', async () => { @@ -411,26 +421,13 @@ suite('SnippetsMerge', () => { const actual = merge(local, remote, base); - assert.deepEqual(actual.added, {}); - assert.deepEqual(actual.updated, {}); - assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.local.added, {}); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); assert.deepEqual(actual.conflicts, ['html.json', 'typescript.json']); - assert.deepEqual(actual.remote, null); - }); - - test('merge when local and remote has moved forwareded with multiple conflicts and resolving one conflict', async () => { - const base = { 'html.json': htmlSnippet1, 'typescript.json': tsSnippet1 }; - const local = { 'html.json': htmlSnippet2, 'typescript.json': tsSnippet2, 'c.json': cSnippet }; - const remote = { 'c.json': cSnippet }; - const resolvedConflicts = { 'html.json': htmlSnippet1 }; - - const actual = merge(local, remote, base, resolvedConflicts); - - assert.deepEqual(actual.added, {}); - assert.deepEqual(actual.updated, { 'html.json': htmlSnippet1 }); - assert.deepEqual(actual.removed, []); - assert.deepEqual(actual.conflicts, ['typescript.json']); - assert.deepEqual(actual.remote, { 'c.json': cSnippet, 'html.json': htmlSnippet1 }); + assert.deepEqual(actual.remote.added, {}); + assert.deepEqual(actual.remote.updated, {}); + assert.deepEqual(actual.remote.removed, []); }); }); diff --git a/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts b/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts index 7943e35359f..262fcf95f63 100644 --- a/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts @@ -4,17 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IUserDataSyncStoreService, IUserDataSyncService, SyncResource, SyncStatus, Conflict, USER_DATA_SYNC_SCHEME, PREVIEW_DIR_NAME } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncStoreService, IUserDataSyncService, SyncResource, SyncStatus, PREVIEW_DIR_NAME, ISyncData, IResourcePreview } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient'; import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; import { IFileService } from 'vs/platform/files/common/files'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { VSBuffer } from 'vs/base/common/buffer'; -import { ISyncData } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { SnippetsSynchroniser } from 'vs/platform/userDataSync/common/snippetsSync'; -import { joinPath } from 'vs/base/common/resources'; +import { joinPath, dirname } from 'vs/base/common/resources'; import { IStringDictionary } from 'vs/base/common/collections'; +import { URI } from 'vs/base/common/uri'; const tsSnippet1 = `{ @@ -167,11 +167,62 @@ suite('SnippetsSync', () => { teardown(() => disposableStore.clear()); + test('when snippets does not exist', async () => { + const fileService = testClient.instantiationService.get(IFileService); + const snippetsResource = testClient.instantiationService.get(IEnvironmentService).snippetsHome; + + assert.deepEqual(await testObject.getLastSyncUserData(), null); + let manifest = await testClient.manifest(); + server.reset(); + await testObject.sync(manifest); + + assert.deepEqual(server.requests, [ + { type: 'GET', url: `${server.url}/v1/resource/${testObject.resource}/latest`, headers: {} }, + ]); + assert.ok(!await fileService.exists(snippetsResource)); + + const lastSyncUserData = await testObject.getLastSyncUserData(); + const remoteUserData = await testObject.getRemoteUserData(null); + assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref); + assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData); + assert.equal(lastSyncUserData!.syncData, null); + + manifest = await testClient.manifest(); + server.reset(); + await testObject.sync(manifest); + assert.deepEqual(server.requests, []); + + manifest = await testClient.manifest(); + server.reset(); + await testObject.sync(manifest); + assert.deepEqual(server.requests, []); + }); + + test('when snippet is created after first sync', async () => { + await testObject.sync(await testClient.manifest()); + await updateSnippet('html.json', htmlSnippet1, testClient); + + let lastSyncUserData = await testObject.getLastSyncUserData(); + const manifest = await testClient.manifest(); + server.reset(); + await testObject.sync(manifest); + + assert.deepEqual(server.requests, [ + { type: 'POST', url: `${server.url}/v1/resource/${testObject.resource}`, headers: { 'If-Match': lastSyncUserData?.ref } }, + ]); + + lastSyncUserData = await testObject.getLastSyncUserData(); + const remoteUserData = await testObject.getRemoteUserData(null); + assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref); + assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData); + assert.deepEqual(lastSyncUserData!.syncData!.content, JSON.stringify({ 'html.json': htmlSnippet1 })); + }); + test('first time sync - outgoing to server (no snippets)', async () => { await updateSnippet('html.json', htmlSnippet1, testClient); await updateSnippet('typescript.json', tsSnippet1, testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -186,7 +237,7 @@ suite('SnippetsSync', () => { await updateSnippet('typescript.json', tsSnippet1, client2); await client2.sync(); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -201,7 +252,7 @@ suite('SnippetsSync', () => { await client2.sync(); await updateSnippet('typescript.json', tsSnippet1, testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -221,12 +272,12 @@ suite('SnippetsSync', () => { await client2.sync(); await updateSnippet('html.json', htmlSnippet2, testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.HasConflicts); const environmentService = testClient.instantiationService.get(IEnvironmentService); const local = joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'); - assertConflicts(testObject.conflicts, [{ local, remote: local.with({ scheme: USER_DATA_SYNC_SCHEME }) }]); + assertPreviews(testObject.conflicts, [local]); }); test('first time sync when snippets exists - has conflicts and accept conflicts', async () => { @@ -234,14 +285,13 @@ suite('SnippetsSync', () => { await client2.sync(); await updateSnippet('html.json', htmlSnippet2, testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); const conflicts = testObject.conflicts; - await testObject.acceptConflict(conflicts[0].local, htmlSnippet1); + await testObject.accept(conflicts[0].previewResource, htmlSnippet1); + await testObject.apply(false); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); - const fileService = testClient.instantiationService.get(IFileService); - assert.ok(!await fileService.exists(conflicts[0].local)); const actual1 = await readSnippet('html.json', testClient); assert.equal(actual1, htmlSnippet1); @@ -259,16 +309,13 @@ suite('SnippetsSync', () => { await updateSnippet('html.json', htmlSnippet2, testClient); await updateSnippet('typescript.json', tsSnippet2, testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.HasConflicts); const environmentService = testClient.instantiationService.get(IEnvironmentService); const local1 = joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'); const local2 = joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'); - assertConflicts(testObject.conflicts, [ - { local: local1, remote: local1.with({ scheme: USER_DATA_SYNC_SCHEME }) }, - { local: local2, remote: local2.with({ scheme: USER_DATA_SYNC_SCHEME }) } - ]); + assertPreviews(testObject.conflicts, [local1, local2]); }); test('first time sync when snippets exists - has multiple conflicts and accept one conflict', async () => { @@ -278,18 +325,16 @@ suite('SnippetsSync', () => { await updateSnippet('html.json', htmlSnippet2, testClient); await updateSnippet('typescript.json', tsSnippet2, testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); let conflicts = testObject.conflicts; - await testObject.acceptConflict(conflicts[0].local, htmlSnippet2); - const fileService = testClient.instantiationService.get(IFileService); - assert.ok(!await fileService.exists(conflicts[0].local)); + await testObject.accept(conflicts[0].previewResource, htmlSnippet2); conflicts = testObject.conflicts; assert.equal(testObject.status, SyncStatus.HasConflicts); const environmentService = testClient.instantiationService.get(IEnvironmentService); const local = joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'); - assertConflicts(testObject.conflicts, [{ local, remote: local.with({ scheme: USER_DATA_SYNC_SCHEME }) }]); + assertPreviews(testObject.conflicts, [local]); }); test('first time sync when snippets exists - has multiple conflicts and accept all conflicts', async () => { @@ -299,17 +344,15 @@ suite('SnippetsSync', () => { await updateSnippet('html.json', htmlSnippet2, testClient); await updateSnippet('typescript.json', tsSnippet2, testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); const conflicts = testObject.conflicts; - await testObject.acceptConflict(conflicts[0].local, htmlSnippet2); - await testObject.acceptConflict(conflicts[1].local, tsSnippet1); + await testObject.accept(conflicts[0].previewResource, htmlSnippet2); + await testObject.accept(conflicts[1].previewResource, tsSnippet1); + await testObject.apply(false); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); - const fileService = testClient.instantiationService.get(IFileService); - assert.ok(!await fileService.exists(conflicts[0].local)); - assert.ok(!await fileService.exists(conflicts[1].local)); const actual1 = await readSnippet('html.json', testClient); assert.equal(actual1, htmlSnippet2); @@ -324,10 +367,10 @@ suite('SnippetsSync', () => { test('sync adding a snippet', async () => { await updateSnippet('html.json', htmlSnippet1, testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); await updateSnippet('typescript.json', tsSnippet1, testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -345,12 +388,12 @@ suite('SnippetsSync', () => { test('sync adding a snippet - accept', async () => { await updateSnippet('html.json', htmlSnippet1, client2); await client2.sync(); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); await updateSnippet('typescript.json', tsSnippet1, client2); await client2.sync(); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -362,10 +405,10 @@ suite('SnippetsSync', () => { test('sync updating a snippet', async () => { await updateSnippet('html.json', htmlSnippet1, testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); await updateSnippet('html.json', htmlSnippet2, testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -381,12 +424,12 @@ suite('SnippetsSync', () => { test('sync updating a snippet - accept', async () => { await updateSnippet('html.json', htmlSnippet1, client2); await client2.sync(); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); await updateSnippet('html.json', htmlSnippet2, client2); await client2.sync(); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -397,30 +440,31 @@ suite('SnippetsSync', () => { test('sync updating a snippet - conflict', async () => { await updateSnippet('html.json', htmlSnippet1, client2); await client2.sync(); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); await updateSnippet('html.json', htmlSnippet2, client2); await client2.sync(); await updateSnippet('html.json', htmlSnippet3, testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.HasConflicts); const environmentService = testClient.instantiationService.get(IEnvironmentService); const local = joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'); - assertConflicts(testObject.conflicts, [{ local, remote: local.with({ scheme: USER_DATA_SYNC_SCHEME }) }]); + assertPreviews(testObject.conflicts, [local]); }); test('sync updating a snippet - resolve conflict', async () => { await updateSnippet('html.json', htmlSnippet1, client2); await client2.sync(); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); await updateSnippet('html.json', htmlSnippet2, client2); await client2.sync(); await updateSnippet('html.json', htmlSnippet3, testClient); - await testObject.sync(); - await testObject.acceptConflict(testObject.conflicts[0].local, htmlSnippet2); + await testObject.sync(await testClient.manifest()); + await testObject.accept(testObject.conflicts[0].previewResource, htmlSnippet2); + await testObject.apply(false); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -437,10 +481,10 @@ suite('SnippetsSync', () => { test('sync removing a snippet', async () => { await updateSnippet('html.json', htmlSnippet1, testClient); await updateSnippet('typescript.json', tsSnippet1, testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); await removeSnippet('html.json', testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -459,12 +503,12 @@ suite('SnippetsSync', () => { await updateSnippet('html.json', htmlSnippet1, client2); await updateSnippet('typescript.json', tsSnippet1, client2); await client2.sync(); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); await removeSnippet('html.json', client2); await client2.sync(); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -478,13 +522,13 @@ suite('SnippetsSync', () => { await updateSnippet('html.json', htmlSnippet1, client2); await updateSnippet('typescript.json', tsSnippet1, client2); await client2.sync(); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); await updateSnippet('html.json', htmlSnippet2, client2); await client2.sync(); await removeSnippet('html.json', testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -499,32 +543,33 @@ suite('SnippetsSync', () => { await updateSnippet('html.json', htmlSnippet1, client2); await updateSnippet('typescript.json', tsSnippet1, client2); await client2.sync(); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); await removeSnippet('html.json', client2); await client2.sync(); await updateSnippet('html.json', htmlSnippet2, testClient); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.HasConflicts); const environmentService = testClient.instantiationService.get(IEnvironmentService); const local = joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'); - assertConflicts(testObject.conflicts, [{ local, remote: local.with({ scheme: USER_DATA_SYNC_SCHEME }) }]); + assertPreviews(testObject.conflicts, [local]); }); test('sync removing a snippet - resolve conflict', async () => { await updateSnippet('html.json', htmlSnippet1, client2); await updateSnippet('typescript.json', tsSnippet1, client2); await client2.sync(); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); await removeSnippet('html.json', client2); await client2.sync(); await updateSnippet('html.json', htmlSnippet2, testClient); - await testObject.sync(); - await testObject.acceptConflict(testObject.conflicts[0].local, htmlSnippet3); + await testObject.sync(await testClient.manifest()); + await testObject.accept(testObject.conflicts[0].previewResource, htmlSnippet3); + await testObject.apply(false); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -544,14 +589,15 @@ suite('SnippetsSync', () => { await updateSnippet('html.json', htmlSnippet1, client2); await updateSnippet('typescript.json', tsSnippet1, client2); await client2.sync(); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); await removeSnippet('html.json', client2); await client2.sync(); await updateSnippet('html.json', htmlSnippet2, testClient); - await testObject.sync(); - await testObject.acceptConflict(testObject.conflicts[0].local, ''); + await testObject.sync(await testClient.manifest()); + await testObject.accept(testObject.conflicts[0].previewResource, null); + await testObject.apply(false); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -567,41 +613,12 @@ suite('SnippetsSync', () => { assert.deepEqual(actual, { 'typescript.json': tsSnippet1 }); }); - test('first time sync - push', async () => { - await updateSnippet('html.json', htmlSnippet1, testClient); - await updateSnippet('typescript.json', tsSnippet1, testClient); - - await testObject.push(); - assert.equal(testObject.status, SyncStatus.Idle); - assert.deepEqual(testObject.conflicts, []); - - const { content } = await testClient.read(testObject.resource); - assert.ok(content !== null); - const actual = parseSnippets(content!); - assert.deepEqual(actual, { 'html.json': htmlSnippet1, 'typescript.json': tsSnippet1 }); - }); - - test('first time sync - pull', async () => { - await updateSnippet('html.json', htmlSnippet1, client2); - await updateSnippet('typescript.json', tsSnippet1, client2); - await client2.sync(); - - await testObject.pull(); - assert.equal(testObject.status, SyncStatus.Idle); - assert.deepEqual(testObject.conflicts, []); - - const actual1 = await readSnippet('html.json', testClient); - assert.equal(actual1, htmlSnippet1); - const actual2 = await readSnippet('typescript.json', testClient); - assert.equal(actual2, tsSnippet1); - }); - test('sync global and language snippet', async () => { await updateSnippet('global.code-snippets', globalSnippet, client2); await updateSnippet('html.json', htmlSnippet1, client2); await client2.sync(); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -622,7 +639,7 @@ suite('SnippetsSync', () => { await updateSnippet('typescript.json', tsSnippet1, client2); await client2.sync(); - await testObject.sync(); + await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -639,6 +656,311 @@ suite('SnippetsSync', () => { assert.deepEqual(actual, { 'typescript.json': tsSnippet1, 'global.code-snippets': globalSnippet }); }); + test('previews are reset after all conflicts resolved', async () => { + await updateSnippet('html.json', htmlSnippet1, client2); + await updateSnippet('typescript.json', tsSnippet1, client2); + await client2.sync(); + + await updateSnippet('html.json', htmlSnippet2, testClient); + await testObject.sync(await testClient.manifest()); + + let conflicts = testObject.conflicts; + await testObject.accept(conflicts[0].previewResource, htmlSnippet2); + await testObject.apply(false); + + const fileService = testClient.instantiationService.get(IFileService); + assert.ok(!await fileService.exists(dirname(conflicts[0].previewResource))); + }); + + test('merge when there are multiple snippets and only one snippet is merged', async () => { + const environmentService = testClient.instantiationService.get(IEnvironmentService); + + await updateSnippet('html.json', htmlSnippet2, testClient); + await updateSnippet('typescript.json', tsSnippet2, testClient); + let preview = await testObject.preview(await testClient.manifest()); + + assert.equal(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), + ]); + assert.deepEqual(testObject.conflicts, []); + + preview = await testObject.merge(preview!.resourcePreviews[0].localResource); + + assert.equal(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), + ]); + assert.deepEqual(testObject.conflicts, []); + }); + + test('merge when there are multiple snippets and all snippets are merged', async () => { + const environmentService = testClient.instantiationService.get(IEnvironmentService); + + await updateSnippet('html.json', htmlSnippet2, testClient); + await updateSnippet('typescript.json', tsSnippet2, testClient); + let preview = await testObject.preview(await testClient.manifest()); + + assert.equal(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), + ]); + assert.deepEqual(testObject.conflicts, []); + + preview = await testObject.merge(preview!.resourcePreviews[0].localResource); + preview = await testObject.merge(preview!.resourcePreviews[1].localResource); + + assert.equal(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), + ]); + assert.deepEqual(testObject.conflicts, []); + }); + + test('merge when there are multiple snippets and all snippets are merged and applied', async () => { + const environmentService = testClient.instantiationService.get(IEnvironmentService); + + await updateSnippet('html.json', htmlSnippet2, testClient); + await updateSnippet('typescript.json', tsSnippet2, testClient); + let preview = await testObject.preview(await testClient.manifest()); + + assert.equal(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), + ]); + assert.deepEqual(testObject.conflicts, []); + + preview = await testObject.merge(preview!.resourcePreviews[0].localResource); + preview = await testObject.merge(preview!.resourcePreviews[1].localResource); + preview = await testObject.apply(false); + + assert.equal(testObject.status, SyncStatus.Idle); + assert.equal(preview, null); + assert.deepEqual(testObject.conflicts, []); + }); + + test('merge when there are multiple snippets and one snippet has no changes and one snippet is merged', async () => { + const environmentService = testClient.instantiationService.get(IEnvironmentService); + + await updateSnippet('html.json', htmlSnippet1, client2); + await client2.sync(); + + await updateSnippet('html.json', htmlSnippet1, testClient); + await updateSnippet('typescript.json', tsSnippet2, testClient); + let preview = await testObject.preview(await testClient.manifest()); + + assert.equal(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + ]); + assert.deepEqual(testObject.conflicts, []); + + preview = await testObject.merge(preview!.resourcePreviews[0].localResource); + + assert.equal(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + ]); + assert.deepEqual(testObject.conflicts, []); + }); + + test('merge when there are multiple snippets and one snippet has no changes and one snippet is merged and applied', async () => { + const environmentService = testClient.instantiationService.get(IEnvironmentService); + + await updateSnippet('html.json', htmlSnippet1, client2); + await client2.sync(); + + await updateSnippet('html.json', htmlSnippet1, testClient); + await updateSnippet('typescript.json', tsSnippet2, testClient); + let preview = await testObject.preview(await testClient.manifest()); + + assert.equal(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + ]); + assert.deepEqual(testObject.conflicts, []); + + preview = await testObject.merge(preview!.resourcePreviews[0].localResource); + preview = await testObject.apply(false); + + assert.equal(testObject.status, SyncStatus.Idle); + assert.equal(preview, null); + assert.deepEqual(testObject.conflicts, []); + }); + + test('merge when there are multiple snippets with conflicts and only one snippet is merged', async () => { + const environmentService = testClient.instantiationService.get(IEnvironmentService); + + await updateSnippet('html.json', htmlSnippet1, client2); + await updateSnippet('typescript.json', tsSnippet1, client2); + await client2.sync(); + + await updateSnippet('html.json', htmlSnippet2, testClient); + await updateSnippet('typescript.json', tsSnippet2, testClient); + let preview = await testObject.preview(await testClient.manifest()); + + assert.equal(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), + ]); + assert.deepEqual(testObject.conflicts, []); + + preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + + assert.equal(testObject.status, SyncStatus.HasConflicts); + assertPreviews(preview!.resourcePreviews, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), + ]); + assertPreviews(testObject.conflicts, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + ]); + }); + + test('merge when there are multiple snippets with conflicts and all snippets are merged', async () => { + const environmentService = testClient.instantiationService.get(IEnvironmentService); + + await updateSnippet('html.json', htmlSnippet1, client2); + await updateSnippet('typescript.json', tsSnippet1, client2); + await client2.sync(); + + await updateSnippet('html.json', htmlSnippet2, testClient); + await updateSnippet('typescript.json', tsSnippet2, testClient); + let preview = await testObject.preview(await testClient.manifest()); + + assert.equal(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), + ]); + assert.deepEqual(testObject.conflicts, []); + + preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + preview = await testObject.merge(preview!.resourcePreviews[1].previewResource); + + assert.equal(testObject.status, SyncStatus.HasConflicts); + assertPreviews(preview!.resourcePreviews, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), + ]); + assertPreviews(testObject.conflicts, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), + ]); + }); + + test('accept when there are multiple snippets with conflicts and only one snippet is accepted', async () => { + const environmentService = testClient.instantiationService.get(IEnvironmentService); + + await updateSnippet('html.json', htmlSnippet1, client2); + await updateSnippet('typescript.json', tsSnippet1, client2); + await client2.sync(); + + await updateSnippet('html.json', htmlSnippet2, testClient); + await updateSnippet('typescript.json', tsSnippet2, testClient); + let preview = await testObject.preview(await testClient.manifest()); + + assert.equal(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), + ]); + assert.deepEqual(testObject.conflicts, []); + + preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, htmlSnippet2); + + assert.equal(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), + ]); + assert.deepEqual(testObject.conflicts, []); + }); + + test('accept when there are multiple snippets with conflicts and all snippets are accepted', async () => { + const environmentService = testClient.instantiationService.get(IEnvironmentService); + + await updateSnippet('html.json', htmlSnippet1, client2); + await updateSnippet('typescript.json', tsSnippet1, client2); + await client2.sync(); + + await updateSnippet('html.json', htmlSnippet2, testClient); + await updateSnippet('typescript.json', tsSnippet2, testClient); + let preview = await testObject.preview(await testClient.manifest()); + + assert.equal(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), + ]); + assert.deepEqual(testObject.conflicts, []); + + preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, htmlSnippet2); + preview = await testObject.accept(preview!.resourcePreviews[1].previewResource, tsSnippet2); + + assert.equal(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), + ]); + assert.deepEqual(testObject.conflicts, []); + }); + + test('accept when there are multiple snippets with conflicts and all snippets are accepted and applied', async () => { + const environmentService = testClient.instantiationService.get(IEnvironmentService); + + await updateSnippet('html.json', htmlSnippet1, client2); + await updateSnippet('typescript.json', tsSnippet1, client2); + await client2.sync(); + + await updateSnippet('html.json', htmlSnippet2, testClient); + await updateSnippet('typescript.json', tsSnippet2, testClient); + let preview = await testObject.preview(await testClient.manifest()); + + assert.equal(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), + ]); + assert.deepEqual(testObject.conflicts, []); + + preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, htmlSnippet2); + preview = await testObject.accept(preview!.resourcePreviews[1].previewResource, tsSnippet2); + preview = await testObject.apply(false); + + assert.equal(testObject.status, SyncStatus.Idle); + assert.equal(preview, null); + assert.deepEqual(testObject.conflicts, []); + }); + function parseSnippets(content: string): IStringDictionary { const syncData: ISyncData = JSON.parse(content); return JSON.parse(syncData.content); @@ -669,8 +991,8 @@ suite('SnippetsSync', () => { return null; } - function assertConflicts(actual: Conflict[], expected: Conflict[]) { - assert.deepEqual(actual.map(({ local, remote }) => ({ local: local.toString(), remote: remote.toString() })), expected.map(({ local, remote }) => ({ local: local.toString(), remote: remote.toString() }))); + function assertPreviews(actual: IResourcePreview[], expected: URI[]) { + assert.deepEqual(actual.map(({ previewResource }) => previewResource.toString()), expected.map(uri => uri.toString())); } }); diff --git a/src/vs/platform/userDataSync/test/common/synchronizer.test.ts b/src/vs/platform/userDataSync/test/common/synchronizer.test.ts index 3c2583e4626..5d2731d5e13 100644 --- a/src/vs/platform/userDataSync/test/common/synchronizer.test.ts +++ b/src/vs/platform/userDataSync/test/common/synchronizer.test.ts @@ -4,25 +4,44 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IUserDataSyncStoreService, SyncResource, SyncStatus, IUserDataSyncEnablementService, ISyncPreviewResult } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncStoreService, SyncResource, SyncStatus, IUserDataSyncResourceEnablementService, IRemoteUserData, Change, USER_DATA_SYNC_SCHEME, IUserDataManifest, MergeState, IResourcePreview as IBaseResourcePreview } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient'; import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; -import { AbstractSynchroniser, IRemoteUserData } from 'vs/platform/userDataSync/common/abstractSynchronizer'; +import { AbstractSynchroniser, IAcceptResult, IMergeResult, IResourcePreview } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { Barrier } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { URI } from 'vs/base/common/uri'; +import { IFileService } from 'vs/platform/files/common/files'; +import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { isEqual, joinPath } from 'vs/base/common/resources'; + +interface ITestResourcePreview extends IResourcePreview { + ref: string; +} class TestSynchroniser extends AbstractSynchroniser { syncBarrier: Barrier = new Barrier(); - syncResult: { status?: SyncStatus, error?: boolean } = {}; + syncResult: { hasConflicts: boolean, hasError: boolean } = { hasConflicts: false, hasError: false }; onDoSyncCall: Emitter = this._register(new Emitter()); + failWhenGettingLatestRemoteUserData: boolean = false; readonly resource: SyncResource = SyncResource.Settings; protected readonly version: number = 1; private cancelled: boolean = false; + readonly localResource = joinPath(this.environmentService.userRoamingDataHome, 'testResource.json'); - protected async performSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise { + protected getLatestRemoteUserData(manifest: IUserDataManifest | null, lastSyncUserData: IRemoteUserData | null): Promise { + if (this.failWhenGettingLatestRemoteUserData) { + throw new Error(); + } + return super.getLatestRemoteUserData(manifest, lastSyncUserData); + } + + protected async doSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, apply: boolean): Promise { this.cancelled = false; this.onDoSyncCall.fire(); await this.syncBarrier.wait(); @@ -31,31 +50,120 @@ class TestSynchroniser extends AbstractSynchroniser { return SyncStatus.Idle; } - if (this.syncResult.error) { + return super.doSync(remoteUserData, lastSyncUserData, apply); + } + + protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise { + if (this.syncResult.hasError) { throw new Error('failed'); } - await this.apply(remoteUserData.ref); - return this.syncResult.status || SyncStatus.Idle; + let fileContent = null; + try { + fileContent = await this.fileService.readFile(this.localResource); + } catch (error) { } + + return [{ + localResource: this.localResource, + localContent: fileContent ? fileContent.value.toString() : null, + remoteResource: this.localResource.with(({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' })), + remoteContent: remoteUserData.syncData ? remoteUserData.syncData.content : null, + previewResource: this.localResource.with(({ scheme: USER_DATA_SYNC_SCHEME, authority: 'preview' })), + ref: remoteUserData.ref, + localChange: Change.Modified, + remoteChange: Change.Modified, + acceptedResource: this.localResource.with(({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' })), + }]; } - async apply(ref: string): Promise { - ref = await this.userDataSyncStoreService.write(this.resource, '', ref); - await this.updateLastSyncUserData({ ref, syncData: { content: '', version: this.version } }); + protected async getMergeResult(resourcePreview: ITestResourcePreview, token: CancellationToken): Promise { + return { + content: resourcePreview.ref, + localChange: Change.Modified, + remoteChange: Change.Modified, + hasConflicts: this.syncResult.hasConflicts, + }; + } + + protected async getAcceptResult(resourcePreview: ITestResourcePreview, resource: URI, content: string | null | undefined, token: CancellationToken): Promise { + + if (isEqual(resource, resourcePreview.localResource)) { + return { + content: resourcePreview.localContent, + localChange: Change.None, + remoteChange: resourcePreview.localContent === null ? Change.Deleted : Change.Modified, + }; + } + + if (isEqual(resource, resourcePreview.remoteResource)) { + return { + content: resourcePreview.remoteContent, + localChange: resourcePreview.remoteContent === null ? Change.Deleted : Change.Modified, + remoteChange: Change.None, + }; + } + + if (isEqual(resource, resourcePreview.previewResource)) { + if (content === undefined) { + return { + content: resourcePreview.ref, + localChange: Change.Modified, + remoteChange: Change.Modified, + }; + } else { + return { + content, + localChange: content === null ? resourcePreview.localContent !== null ? Change.Deleted : Change.None : Change.Modified, + remoteChange: content === null ? resourcePreview.remoteContent !== null ? Change.Deleted : Change.None : Change.Modified, + }; + } + } + + throw new Error(`Invalid Resource: ${resource.toString()}`); + } + + protected async applyResult(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, resourcePreviews: [IResourcePreview, IAcceptResult][], force: boolean): Promise { + if (resourcePreviews[0][1].localChange === Change.Deleted) { + await this.fileService.del(this.localResource); + } + + if (resourcePreviews[0][1].localChange === Change.Added || resourcePreviews[0][1].localChange === Change.Modified) { + await this.fileService.writeFile(this.localResource, VSBuffer.fromString(resourcePreviews[0][1].content!)); + } + + if (resourcePreviews[0][1].remoteChange === Change.Deleted) { + await this.applyRef(null, remoteUserData.ref); + } + + if (resourcePreviews[0][1].remoteChange === Change.Added || resourcePreviews[0][1].remoteChange === Change.Modified) { + await this.applyRef(resourcePreviews[0][1].content, remoteUserData.ref); + } + } + + async applyRef(content: string | null, ref: string): Promise { + const remoteUserData = await this.updateRemoteUserData(content === null ? '' : content, ref); + await this.updateLastSyncUserData(remoteUserData); } async stop(): Promise { this.cancelled = true; this.syncBarrier.open(); + super.stop(); } - protected async generatePreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise { - return { hasLocalChanged: false, hasRemoteChanged: false }; + async triggerLocalChange(): Promise { + super.triggerLocalChange(); + } + + onDidTriggerLocalChangeCall: Emitter = this._register(new Emitter()); + protected async doTriggerLocalChange(): Promise { + await super.doTriggerLocalChange(); + this.onDidTriggerLocalChangeCall.fire(); } } -suite('TestSynchronizer', () => { +suite('TestSynchronizer - Auto Sync', () => { const disposableStore = new DisposableStore(); const server = new UserDataSyncTestServer(); @@ -67,6 +175,7 @@ suite('TestSynchronizer', () => { await client.setUp(); userDataSyncStoreService = client.instantiationService.get(IUserDataSyncStoreService); disposableStore.add(toDisposable(() => userDataSyncStoreService.clear())); + client.instantiationService.get(IFileService).registerProvider(USER_DATA_SYNC_SCHEME, new InMemoryFileSystemProvider()); }); teardown(() => disposableStore.clear()); @@ -79,7 +188,7 @@ suite('TestSynchronizer', () => { const promise = Event.toPromise(testObject.onDoSyncCall.event); - testObject.sync(); + testObject.sync(await client.manifest()); await promise; assert.deepEqual(actual, [SyncStatus.Syncing]); @@ -94,35 +203,22 @@ suite('TestSynchronizer', () => { const actual: SyncStatus[] = []; disposableStore.add(testObject.onDidChangeStatus(status => actual.push(status))); - await testObject.sync(); + await testObject.sync(await client.manifest()); assert.deepEqual(actual, [SyncStatus.Syncing, SyncStatus.Idle]); assert.deepEqual(testObject.status, SyncStatus.Idle); }); - test('status is set correctly when sync has conflicts', async () => { - const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); - testObject.syncResult = { status: SyncStatus.HasConflicts }; - testObject.syncBarrier.open(); - - const actual: SyncStatus[] = []; - disposableStore.add(testObject.onDidChangeStatus(status => actual.push(status))); - await testObject.sync(); - - assert.deepEqual(actual, [SyncStatus.Syncing, SyncStatus.HasConflicts]); - assert.deepEqual(testObject.status, SyncStatus.HasConflicts); - }); - test('status is set correctly when sync has errors', async () => { const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); - testObject.syncResult = { error: true }; + testObject.syncResult = { hasError: true, hasConflicts: false }; testObject.syncBarrier.open(); const actual: SyncStatus[] = []; disposableStore.add(testObject.onDidChangeStatus(status => actual.push(status))); try { - await testObject.sync(); + await testObject.sync(await client.manifest()); assert.fail('Should fail'); } catch (e) { assert.deepEqual(actual, [SyncStatus.Syncing, SyncStatus.Idle]); @@ -130,31 +226,42 @@ suite('TestSynchronizer', () => { } }); + test('status is set to hasConflicts when asked to sync if there are conflicts', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: true, hasError: false }; + testObject.syncBarrier.open(); + + await testObject.sync(await client.manifest()); + + assert.deepEqual(testObject.status, SyncStatus.HasConflicts); + assertConflicts(testObject.conflicts, [testObject.localResource]); + }); + test('sync should not run if syncing already', async () => { const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); const promise = Event.toPromise(testObject.onDoSyncCall.event); - testObject.sync(); + testObject.sync(await client.manifest()); await promise; const actual: SyncStatus[] = []; disposableStore.add(testObject.onDidChangeStatus(status => actual.push(status))); - await testObject.sync(); + await testObject.sync(await client.manifest()); assert.deepEqual(actual, []); assert.deepEqual(testObject.status, SyncStatus.Syncing); - testObject.stop(); + await testObject.stop(); }); test('sync should not run if disabled', async () => { const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); - client.instantiationService.get(IUserDataSyncEnablementService).setResourceEnablement(testObject.resource, false); + client.instantiationService.get(IUserDataSyncResourceEnablementService).setResourceEnablement(testObject.resource, false); const actual: SyncStatus[] = []; disposableStore.add(testObject.onDidChangeStatus(status => actual.push(status))); - await testObject.sync(); + await testObject.sync(await client.manifest()); assert.deepEqual(actual, []); assert.deepEqual(testObject.status, SyncStatus.Idle); @@ -162,36 +269,186 @@ suite('TestSynchronizer', () => { test('sync should not run if there are conflicts', async () => { const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); - testObject.syncResult = { status: SyncStatus.HasConflicts }; + testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); - await testObject.sync(); + await testObject.sync(await client.manifest()); const actual: SyncStatus[] = []; disposableStore.add(testObject.onDidChangeStatus(status => actual.push(status))); - await testObject.sync(); + await testObject.sync(await client.manifest()); assert.deepEqual(actual, []); assert.deepEqual(testObject.status, SyncStatus.HasConflicts); }); + test('accept preview during conflicts', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: true, hasError: false }; + testObject.syncBarrier.open(); + + await testObject.sync(await client.manifest()); + assert.deepEqual(testObject.status, SyncStatus.HasConflicts); + + await testObject.accept(testObject.conflicts[0].previewResource); + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertConflicts(testObject.conflicts, []); + + await testObject.apply(false); + assert.deepEqual(testObject.status, SyncStatus.Idle); + const fileService = client.instantiationService.get(IFileService); + assert.equal((await testObject.getRemoteUserData(null)).syncData?.content, (await fileService.readFile(testObject.localResource)).value.toString()); + }); + + test('accept remote during conflicts', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncBarrier.open(); + await testObject.sync(await client.manifest()); + const fileService = client.instantiationService.get(IFileService); + const currentRemoteContent = (await testObject.getRemoteUserData(null)).syncData?.content; + const newLocalContent = 'conflict'; + await fileService.writeFile(testObject.localResource, VSBuffer.fromString(newLocalContent)); + + testObject.syncResult = { hasConflicts: true, hasError: false }; + await testObject.sync(await client.manifest()); + assert.deepEqual(testObject.status, SyncStatus.HasConflicts); + + await testObject.accept(testObject.conflicts[0].remoteResource); + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertConflicts(testObject.conflicts, []); + + await testObject.apply(false); + assert.deepEqual(testObject.status, SyncStatus.Idle); + assert.equal((await testObject.getRemoteUserData(null)).syncData?.content, currentRemoteContent); + assert.equal((await fileService.readFile(testObject.localResource)).value.toString(), currentRemoteContent); + }); + + test('accept local during conflicts', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncBarrier.open(); + await testObject.sync(await client.manifest()); + const fileService = client.instantiationService.get(IFileService); + const newLocalContent = 'conflict'; + await fileService.writeFile(testObject.localResource, VSBuffer.fromString(newLocalContent)); + + testObject.syncResult = { hasConflicts: true, hasError: false }; + await testObject.sync(await client.manifest()); + assert.deepEqual(testObject.status, SyncStatus.HasConflicts); + + await testObject.accept(testObject.conflicts[0].localResource); + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertConflicts(testObject.conflicts, []); + + await testObject.apply(false); + assert.deepEqual(testObject.status, SyncStatus.Idle); + assert.equal((await testObject.getRemoteUserData(null)).syncData?.content, newLocalContent); + assert.equal((await fileService.readFile(testObject.localResource)).value.toString(), newLocalContent); + }); + + test('accept new content during conflicts', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncBarrier.open(); + await testObject.sync(await client.manifest()); + const fileService = client.instantiationService.get(IFileService); + const newLocalContent = 'conflict'; + await fileService.writeFile(testObject.localResource, VSBuffer.fromString(newLocalContent)); + + testObject.syncResult = { hasConflicts: true, hasError: false }; + await testObject.sync(await client.manifest()); + assert.deepEqual(testObject.status, SyncStatus.HasConflicts); + + const mergeContent = 'newContent'; + await testObject.accept(testObject.conflicts[0].previewResource, mergeContent); + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertConflicts(testObject.conflicts, []); + + await testObject.apply(false); + assert.deepEqual(testObject.status, SyncStatus.Idle); + assert.equal((await testObject.getRemoteUserData(null)).syncData?.content, mergeContent); + assert.equal((await fileService.readFile(testObject.localResource)).value.toString(), mergeContent); + }); + + test('accept delete during conflicts', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncBarrier.open(); + await testObject.sync(await client.manifest()); + const fileService = client.instantiationService.get(IFileService); + const newLocalContent = 'conflict'; + await fileService.writeFile(testObject.localResource, VSBuffer.fromString(newLocalContent)); + + testObject.syncResult = { hasConflicts: true, hasError: false }; + await testObject.sync(await client.manifest()); + assert.deepEqual(testObject.status, SyncStatus.HasConflicts); + + await testObject.accept(testObject.conflicts[0].previewResource, null); + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertConflicts(testObject.conflicts, []); + + await testObject.apply(false); + assert.deepEqual(testObject.status, SyncStatus.Idle); + assert.equal((await testObject.getRemoteUserData(null)).syncData?.content, ''); + assert.ok(!(await fileService.exists(testObject.localResource))); + }); + + test('accept deleted local during conflicts', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncBarrier.open(); + await testObject.sync(await client.manifest()); + const fileService = client.instantiationService.get(IFileService); + await fileService.del(testObject.localResource); + + testObject.syncResult = { hasConflicts: true, hasError: false }; + await testObject.sync(await client.manifest()); + assert.deepEqual(testObject.status, SyncStatus.HasConflicts); + + await testObject.accept(testObject.conflicts[0].localResource); + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertConflicts(testObject.conflicts, []); + + await testObject.apply(false); + assert.deepEqual(testObject.status, SyncStatus.Idle); + assert.equal((await testObject.getRemoteUserData(null)).syncData?.content, ''); + assert.ok(!(await fileService.exists(testObject.localResource))); + }); + + test('accept deleted remote during conflicts', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncBarrier.open(); + const fileService = client.instantiationService.get(IFileService); + await fileService.writeFile(testObject.localResource, VSBuffer.fromString('some content')); + testObject.syncResult = { hasConflicts: true, hasError: false }; + + await testObject.sync(await client.manifest()); + assert.deepEqual(testObject.status, SyncStatus.HasConflicts); + + await testObject.accept(testObject.conflicts[0].remoteResource); + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertConflicts(testObject.conflicts, []); + + await testObject.apply(false); + assert.deepEqual(testObject.status, SyncStatus.Idle); + assert.equal((await testObject.getRemoteUserData(null)).syncData, null); + assert.ok(!(await fileService.exists(testObject.localResource))); + }); + test('request latest data on precondition failure', async () => { const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); // Sync once testObject.syncBarrier.open(); - await testObject.sync(); + await testObject.sync(await client.manifest()); testObject.syncBarrier = new Barrier(); // update remote data before syncing so that 412 is thrown by server const disposable = testObject.onDoSyncCall.event(async () => { disposable.dispose(); - await testObject.apply(ref); + await testObject.applyRef(ref, ref); server.reset(); testObject.syncBarrier.open(); }); // Start sycing - const { ref } = await userDataSyncStoreService.read(testObject.resource, null); - await testObject.sync(ref); + const manifest = await client.manifest(); + const ref = manifest!.latest![testObject.resource]; + await testObject.sync(await client.manifest()); assert.deepEqual(server.requests, [ { type: 'POST', url: `${server.url}/v1/resource/${testObject.resource}`, headers: { 'If-Match': ref } }, @@ -200,5 +457,628 @@ suite('TestSynchronizer', () => { ]); }); + test('no requests are made to server when local change is triggered', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncBarrier.open(); + await testObject.sync(await client.manifest()); + + server.reset(); + const promise = Event.toPromise(testObject.onDidTriggerLocalChangeCall.event); + await testObject.triggerLocalChange(); + + await promise; + assert.deepEqual(server.requests, []); + }); + + test('status is reset when getting latest remote data fails', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.failWhenGettingLatestRemoteUserData = true; + + try { + await testObject.sync(await client.manifest()); + assert.fail('Should throw an error'); + } catch (error) { + } + + assert.equal(testObject.status, SyncStatus.Idle); + }); +}); + +suite('TestSynchronizer - Manual Sync', () => { + + const disposableStore = new DisposableStore(); + const server = new UserDataSyncTestServer(); + let client: UserDataSyncClient; + let userDataSyncStoreService: IUserDataSyncStoreService; + + setup(async () => { + client = disposableStore.add(new UserDataSyncClient(server)); + await client.setUp(); + userDataSyncStoreService = client.instantiationService.get(IUserDataSyncStoreService); + disposableStore.add(toDisposable(() => userDataSyncStoreService.clear())); + client.instantiationService.get(IFileService).registerProvider(USER_DATA_SYNC_SCHEME, new InMemoryFileSystemProvider()); + }); + + teardown(() => disposableStore.clear()); + + test('preview', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + + const preview = await testObject.preview(await client.manifest()); + + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assertConflicts(testObject.conflicts, []); + }); + + test('preview -> merge', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assert.equal(preview!.resourcePreviews[0].mergeState, MergeState.Accepted); + assertConflicts(testObject.conflicts, []); + }); + + test('preview -> accept', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); + + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assert.equal(preview!.resourcePreviews[0].mergeState, MergeState.Accepted); + assertConflicts(testObject.conflicts, []); + }); + + test('preview -> merge -> accept', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + preview = await testObject.accept(preview!.resourcePreviews[0].localResource); + + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assert.equal(preview!.resourcePreviews[0].mergeState, MergeState.Accepted); + assertConflicts(testObject.conflicts, []); + }); + + test('preview -> merge -> apply', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + await testObject.sync(await client.manifest()); + + const manifest = await client.manifest(); + let preview = await testObject.preview(manifest); + preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + preview = await testObject.apply(false); + + assert.deepEqual(testObject.status, SyncStatus.Idle); + assert.equal(preview, null); + assertConflicts(testObject.conflicts, []); + + const expectedContent = manifest!.latest![testObject.resource]; + assert.equal((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); + assert.equal((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); + }); + + test('preview -> accept -> apply', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + await testObject.sync(await client.manifest()); + + const manifest = await client.manifest(); + const expectedContent = manifest!.latest![testObject.resource]; + let preview = await testObject.preview(manifest); + preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); + preview = await testObject.apply(false); + + assert.deepEqual(testObject.status, SyncStatus.Idle); + assert.equal(preview, null); + assertConflicts(testObject.conflicts, []); + + assert.equal((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); + assert.equal((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); + }); + + test('preview -> merge -> accept -> apply', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + await testObject.sync(await client.manifest()); + + const expectedContent = (await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(); + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + preview = await testObject.accept(preview!.resourcePreviews[0].localResource); + preview = await testObject.apply(false); + + assert.deepEqual(testObject.status, SyncStatus.Idle); + assert.equal(preview, null); + assertConflicts(testObject.conflicts, []); + + assert.equal((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); + assert.equal(!(await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); + }); + + test('preview -> accept', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); + + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assertConflicts(testObject.conflicts, []); + }); + + test('preview -> accept -> apply', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + await testObject.sync(await client.manifest()); + + const manifest = await client.manifest(); + const expectedContent = manifest!.latest![testObject.resource]; + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); + preview = await testObject.apply(false); + + assert.deepEqual(testObject.status, SyncStatus.Idle); + assert.equal(preview, null); + assertConflicts(testObject.conflicts, []); + + assert.equal((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); + assert.equal((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); + }); + + test('preivew -> merge -> discard', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); + + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assert.equal(preview!.resourcePreviews[0].mergeState, MergeState.Preview); + assertConflicts(testObject.conflicts, []); + }); + + test('preivew -> merge -> discard -> accept', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); + preview = await testObject.accept(preview!.resourcePreviews[0].remoteResource); + + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assert.equal(preview!.resourcePreviews[0].mergeState, MergeState.Accepted); + assertConflicts(testObject.conflicts, []); + }); + + test('preivew -> accept -> discard', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); + preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); + + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assert.equal(preview!.resourcePreviews[0].mergeState, MergeState.Preview); + assertConflicts(testObject.conflicts, []); + }); + + test('preivew -> accept -> discard -> accept', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); + preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); + preview = await testObject.accept(preview!.resourcePreviews[0].remoteResource); + + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assert.equal(preview!.resourcePreviews[0].mergeState, MergeState.Accepted); + assertConflicts(testObject.conflicts, []); + }); + + test('preivew -> accept -> discard -> merge', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); + preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); + preview = await testObject.merge(preview!.resourcePreviews[0].remoteResource); + + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assert.equal(preview!.resourcePreviews[0].mergeState, MergeState.Accepted); + assertConflicts(testObject.conflicts, []); + }); + + test('preivew -> merge -> accept -> discard', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + preview = await testObject.accept(preview!.resourcePreviews[0].remoteResource); + preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); + + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assert.equal(preview!.resourcePreviews[0].mergeState, MergeState.Preview); + assertConflicts(testObject.conflicts, []); + }); + + test('preivew -> merge -> discard -> accept -> apply', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + await testObject.sync(await client.manifest()); + + const expectedContent = (await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(); + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); + preview = await testObject.accept(preview!.resourcePreviews[0].localResource); + preview = await testObject.apply(false); + + assert.deepEqual(testObject.status, SyncStatus.Idle); + assert.equal(preview, null); + assertConflicts(testObject.conflicts, []); + assert.equal((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); + assert.equal(!(await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); + }); + + test('preivew -> accept -> discard -> accept -> apply', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + await testObject.sync(await client.manifest()); + + const expectedContent = (await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(); + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + preview = await testObject.accept(preview!.resourcePreviews[0].remoteResource); + preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); + preview = await testObject.accept(preview!.resourcePreviews[0].localResource); + preview = await testObject.apply(false); + + assert.deepEqual(testObject.status, SyncStatus.Idle); + assert.equal(preview, null); + assertConflicts(testObject.conflicts, []); + assert.equal((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); + assert.equal(!(await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); + }); + + test('preivew -> accept -> discard -> merge -> apply', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + await testObject.sync(await client.manifest()); + + const manifest = await client.manifest(); + const expectedContent = manifest!.latest![testObject.resource]; + let preview = await testObject.preview(manifest); + preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + preview = await testObject.accept(preview!.resourcePreviews[0].remoteResource); + preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); + preview = await testObject.merge(preview!.resourcePreviews[0].localResource); + preview = await testObject.apply(false); + + assert.deepEqual(testObject.status, SyncStatus.Idle); + assert.equal(preview, null); + assertConflicts(testObject.conflicts, []); + + assert.equal((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); + assert.equal((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); + }); + + test('conflicts: preview', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: true, hasError: false }; + testObject.syncBarrier.open(); + + const preview = await testObject.preview(await client.manifest()); + + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assertConflicts(testObject.conflicts, []); + }); + + test('conflicts: preview -> merge', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: true, hasError: false }; + testObject.syncBarrier.open(); + + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + + assert.deepEqual(testObject.status, SyncStatus.HasConflicts); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assert.equal(preview!.resourcePreviews[0].mergeState, MergeState.Conflict); + assertConflicts(testObject.conflicts, [preview!.resourcePreviews[0].localResource]); + }); + + test('conflicts: preview -> merge -> discard', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: true, hasError: false }; + testObject.syncBarrier.open(); + + const preview = await testObject.preview(await client.manifest()); + await testObject.merge(preview!.resourcePreviews[0].previewResource); + await testObject.discard(preview!.resourcePreviews[0].previewResource); + + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assert.equal(preview!.resourcePreviews[0].mergeState, MergeState.Preview); + assertConflicts(testObject.conflicts, []); + }); + + test('conflicts: preview -> accept', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: true, hasError: false }; + testObject.syncBarrier.open(); + + let preview = await testObject.preview(await client.manifest()); + await testObject.merge(preview!.resourcePreviews[0].previewResource); + const content = await testObject.resolveContent(preview!.resourcePreviews[0].previewResource); + preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, content); + + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assert.deepEqual(testObject.conflicts, []); + }); + + test('conflicts: preview -> merge -> accept -> apply', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + await testObject.sync(await client.manifest()); + + testObject.syncResult = { hasConflicts: true, hasError: false }; + const manifest = await client.manifest(); + const expectedContent = manifest!.latest![testObject.resource]; + let preview = await testObject.preview(manifest); + + await testObject.merge(preview!.resourcePreviews[0].previewResource); + preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); + preview = await testObject.apply(false); + + assert.deepEqual(testObject.status, SyncStatus.Idle); + assert.equal(preview, null); + assertConflicts(testObject.conflicts, []); + + assert.equal((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); + assert.equal((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); + }); + + test('conflicts: preview -> accept', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: true, hasError: false }; + testObject.syncBarrier.open(); + + let preview = await testObject.preview(await client.manifest()); + const content = await testObject.resolveContent(preview!.resourcePreviews[0].previewResource); + preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, content); + + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assertConflicts(testObject.conflicts, []); + }); + + test('conflicts: preview -> accept -> apply', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + await testObject.sync(await client.manifest()); + + testObject.syncResult = { hasConflicts: true, hasError: false }; + const manifest = await client.manifest(); + const expectedContent = manifest!.latest![testObject.resource]; + let preview = await testObject.preview(manifest); + + preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); + preview = await testObject.apply(false); + + assert.deepEqual(testObject.status, SyncStatus.Idle); + assert.equal(preview, null); + assertConflicts(testObject.conflicts, []); + + assert.equal((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); + assert.equal((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); + }); + + test('conflicts: preivew -> merge -> discard', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: true, hasError: false }; + testObject.syncBarrier.open(); + + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); + + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assert.equal(preview!.resourcePreviews[0].mergeState, MergeState.Preview); + assertConflicts(testObject.conflicts, []); + }); + + test('conflicts: preivew -> merge -> discard -> accept', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: true, hasError: false }; + testObject.syncBarrier.open(); + + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); + preview = await testObject.accept(preview!.resourcePreviews[0].remoteResource); + + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assert.equal(preview!.resourcePreviews[0].mergeState, MergeState.Accepted); + assertConflicts(testObject.conflicts, []); + }); + + test('conflicts: preivew -> accept -> discard', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: true, hasError: false }; + testObject.syncBarrier.open(); + + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); + preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); + + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assert.equal(preview!.resourcePreviews[0].mergeState, MergeState.Preview); + assertConflicts(testObject.conflicts, []); + }); + + test('conflicts: preivew -> accept -> discard -> accept', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: true, hasError: false }; + testObject.syncBarrier.open(); + + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); + preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); + preview = await testObject.accept(preview!.resourcePreviews[0].remoteResource); + + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assert.equal(preview!.resourcePreviews[0].mergeState, MergeState.Accepted); + assertConflicts(testObject.conflicts, []); + }); + + test('conflicts: preivew -> accept -> discard -> merge', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: true, hasError: false }; + testObject.syncBarrier.open(); + + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); + preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); + preview = await testObject.merge(preview!.resourcePreviews[0].remoteResource); + + assert.deepEqual(testObject.status, SyncStatus.HasConflicts); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assert.equal(preview!.resourcePreviews[0].mergeState, MergeState.Conflict); + assertConflicts(testObject.conflicts, [preview!.resourcePreviews[0].localResource]); + }); + + test('conflicts: preivew -> merge -> discard -> merge', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: true, hasError: false }; + testObject.syncBarrier.open(); + + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); + preview = await testObject.merge(preview!.resourcePreviews[0].remoteResource); + + assert.deepEqual(testObject.status, SyncStatus.HasConflicts); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assert.equal(preview!.resourcePreviews[0].mergeState, MergeState.Conflict); + assertConflicts(testObject.conflicts, [preview!.resourcePreviews[0].localResource]); + }); + + test('conflicts: preivew -> merge -> accept -> discard', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + preview = await testObject.accept(preview!.resourcePreviews[0].remoteResource); + preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); + + assert.deepEqual(testObject.status, SyncStatus.Syncing); + assertPreviews(preview!.resourcePreviews, [testObject.localResource]); + assert.equal(preview!.resourcePreviews[0].mergeState, MergeState.Preview); + assertConflicts(testObject.conflicts, []); + }); + + test('conflicts: preivew -> merge -> discard -> accept -> apply', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + await testObject.sync(await client.manifest()); + + const expectedContent = (await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(); + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); + preview = await testObject.accept(preview!.resourcePreviews[0].localResource); + preview = await testObject.apply(false); + + assert.deepEqual(testObject.status, SyncStatus.Idle); + assert.equal(preview, null); + assertConflicts(testObject.conflicts, []); + assert.equal((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); + assert.equal(!(await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); + }); + + test('conflicts: preivew -> accept -> discard -> accept -> apply', async () => { + const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings); + testObject.syncResult = { hasConflicts: false, hasError: false }; + testObject.syncBarrier.open(); + await testObject.sync(await client.manifest()); + + const expectedContent = (await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(); + let preview = await testObject.preview(await client.manifest()); + preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + preview = await testObject.accept(preview!.resourcePreviews[0].remoteResource); + preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); + preview = await testObject.accept(preview!.resourcePreviews[0].localResource); + preview = await testObject.apply(false); + + assert.deepEqual(testObject.status, SyncStatus.Idle); + assert.equal(preview, null); + assertConflicts(testObject.conflicts, []); + assert.equal((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); + assert.equal(!(await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); + }); }); + +function assertConflicts(actual: IBaseResourcePreview[], expected: URI[]) { + assert.deepEqual(actual.map(({ localResource }) => localResource.toString()), expected.map(uri => uri.toString())); +} + +function assertPreviews(actual: IBaseResourcePreview[], expected: URI[]) { + assert.deepEqual(actual.map(({ localResource }) => localResource.toString()), expected.map(uri => uri.toString())); +} diff --git a/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts b/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts new file mode 100644 index 00000000000..310c69dd6cf --- /dev/null +++ b/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts @@ -0,0 +1,404 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { UserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; +import { IUserDataSyncService, SyncResource, UserDataAutoSyncError, UserDataSyncErrorCode, UserDataSyncStoreError } from 'vs/platform/userDataSync/common/userDataSync'; +import { Event } from 'vs/base/common/event'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { joinPath } from 'vs/base/common/resources'; +import { IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; + +class TestUserDataAutoSyncService extends UserDataAutoSyncService { + protected startAutoSync(): boolean { return false; } + protected getSyncTriggerDelayTime(): number { return 50; } + + sync(): Promise { + return this.triggerSync(['sync'], false); + } +} + +suite('UserDataAutoSyncService', () => { + + const disposableStore = new DisposableStore(); + + teardown(() => disposableStore.clear()); + + test('test auto sync with sync resource change triggers sync', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + + // Sync once and reset requests + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask()).run(); + target.reset(); + + const testObject: UserDataAutoSyncService = client.instantiationService.createInstance(TestUserDataAutoSyncService); + + // Trigger auto sync with settings change + await testObject.triggerSync([SyncResource.Settings], false); + + // Filter out machine requests + const actual = target.requests.filter(request => !request.url.startsWith(`${target.url}/v1/resource/machines`)); + + // Make sure only one manifest request is made + assert.deepEqual(actual, [{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }]); + }); + + test('test auto sync with sync resource change triggers sync for every change', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + + // Sync once and reset requests + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask()).run(); + target.reset(); + + const testObject: UserDataAutoSyncService = client.instantiationService.createInstance(TestUserDataAutoSyncService); + + // Trigger auto sync with settings change multiple times + for (let counter = 0; counter < 2; counter++) { + await testObject.triggerSync([SyncResource.Settings], false); + } + + // Filter out machine requests + const actual = target.requests.filter(request => !request.url.startsWith(`${target.url}/v1/resource/machines`)); + + assert.deepEqual(actual, [ + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} } + ]); + }); + + test('test auto sync with non sync resource change triggers sync', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + + // Sync once and reset requests + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask()).run(); + target.reset(); + + const testObject: UserDataAutoSyncService = client.instantiationService.createInstance(TestUserDataAutoSyncService); + + // Trigger auto sync with window focus once + await testObject.triggerSync(['windowFocus'], true); + + // Filter out machine requests + const actual = target.requests.filter(request => !request.url.startsWith(`${target.url}/v1/resource/machines`)); + + // Make sure only one manifest request is made + assert.deepEqual(actual, [{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }]); + }); + + test('test auto sync with non sync resource change does not trigger continuous syncs', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + + // Sync once and reset requests + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask()).run(); + target.reset(); + + const testObject: UserDataAutoSyncService = client.instantiationService.createInstance(TestUserDataAutoSyncService); + + // Trigger auto sync with window focus multiple times + for (let counter = 0; counter < 2; counter++) { + await testObject.triggerSync(['windowFocus'], true); + } + + // Filter out machine requests + const actual = target.requests.filter(request => !request.url.startsWith(`${target.url}/v1/resource/machines`)); + + // Make sure only one manifest request is made + assert.deepEqual(actual, [{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }]); + }); + + test('test first auto sync requests', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject: TestUserDataAutoSyncService = client.instantiationService.createInstance(TestUserDataAutoSyncService); + + await testObject.sync(); + + assert.deepEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + // Machines + { type: 'GET', url: `${target.url}/v1/resource/machines/latest`, headers: {} }, + // Settings + { type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '0' } }, + // Keybindings + { type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/keybindings`, headers: { 'If-Match': '0' } }, + // Snippets + { type: 'GET', url: `${target.url}/v1/resource/snippets/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/snippets`, headers: { 'If-Match': '0' } }, + // Global state + { type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } }, + // Extensions + { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + // Machines + { type: 'POST', url: `${target.url}/v1/resource/machines`, headers: { 'If-Match': '0' } } + ]); + + }); + + test('test further auto sync requests without changes', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject: TestUserDataAutoSyncService = client.instantiationService.createInstance(TestUserDataAutoSyncService); + + // Sync once and reset requests + await testObject.sync(); + target.reset(); + + await testObject.sync(); + + assert.deepEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} } + ]); + + }); + + test('test further auto sync requests with changes', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject: TestUserDataAutoSyncService = client.instantiationService.createInstance(TestUserDataAutoSyncService); + + // Sync once and reset requests + await testObject.sync(); + target.reset(); + + // Do changes in the client + const fileService = client.instantiationService.get(IFileService); + const environmentService = client.instantiationService.get(IEnvironmentService); + await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); + await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); + await fileService.writeFile(joinPath(environmentService.snippetsHome, 'html.json'), VSBuffer.fromString(`{}`)); + await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'de' }))); + await testObject.sync(); + + assert.deepEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + // Settings + { type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '1' } }, + // Keybindings + { type: 'POST', url: `${target.url}/v1/resource/keybindings`, headers: { 'If-Match': '1' } }, + // Snippets + { type: 'POST', url: `${target.url}/v1/resource/snippets`, headers: { 'If-Match': '1' } }, + // Global state + { type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '1' } }, + ]); + + }); + + test('test auto sync send execution id header', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject: TestUserDataAutoSyncService = client.instantiationService.createInstance(TestUserDataAutoSyncService); + + // Sync once and reset requests + await testObject.sync(); + target.reset(); + + await testObject.sync(); + + for (const request of target.requestsWithAllHeaders) { + const hasExecutionIdHeader = request.headers && request.headers['X-Execution-Id'] && request.headers['X-Execution-Id'].length > 0; + if (request.url.startsWith(`${target.url}/v1/resource/machines`)) { + assert.ok(!hasExecutionIdHeader, `Should not have execution header: ${request.url}`); + } else { + assert.ok(hasExecutionIdHeader, `Should have execution header: ${request.url}`); + } + } + + }); + + test('test delete on one client throws turned off error on other client while syncing', async () => { + const target = new UserDataSyncTestServer(); + + // Set up and sync from the client + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask()).run(); + + // Set up and sync from the test client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject: TestUserDataAutoSyncService = testClient.instantiationService.createInstance(TestUserDataAutoSyncService); + await testObject.sync(); + + // Reset from the first client + await client.instantiationService.get(IUserDataSyncService).reset(); + + // Sync from the test client + target.reset(); + + const errorPromise = Event.toPromise(testObject.onError); + await testObject.sync(); + + const e = await errorPromise; + assert.ok(e instanceof UserDataAutoSyncError); + assert.deepEqual((e).code, UserDataSyncErrorCode.TurnedOff); + assert.deepEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + // Machine + { type: 'GET', url: `${target.url}/v1/resource/machines/latest`, headers: { 'If-None-Match': '1' } }, + ]); + }); + + test('test disabling the machine turns off sync', async () => { + const target = new UserDataSyncTestServer(); + + // Set up and sync from the test client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject: TestUserDataAutoSyncService = testClient.instantiationService.createInstance(TestUserDataAutoSyncService); + await testObject.sync(); + + // Disable current machine + const userDataSyncMachinesService = testClient.instantiationService.get(IUserDataSyncMachinesService); + const machines = await userDataSyncMachinesService.getMachines(); + const currentMachine = machines.find(m => m.isCurrent)!; + await userDataSyncMachinesService.setEnablement(currentMachine.id, false); + + target.reset(); + + const errorPromise = Event.toPromise(testObject.onError); + await testObject.sync(); + + const e = await errorPromise; + assert.ok(e instanceof UserDataAutoSyncError); + assert.deepEqual((e).code, UserDataSyncErrorCode.TurnedOff); + assert.deepEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + // Machine + { type: 'GET', url: `${target.url}/v1/resource/machines/latest`, headers: { 'If-None-Match': '2' } }, + { type: 'POST', url: `${target.url}/v1/resource/machines`, headers: { 'If-Match': '2' } }, + ]); + }); + + test('test removing the machine adds machine back', async () => { + const target = new UserDataSyncTestServer(); + + // Set up and sync from the test client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject: TestUserDataAutoSyncService = testClient.instantiationService.createInstance(TestUserDataAutoSyncService); + await testObject.sync(); + + // Remove current machine + await testClient.instantiationService.get(IUserDataSyncMachinesService).removeCurrentMachine(); + + target.reset(); + + await testObject.sync(); + assert.deepEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + // Machine + { type: 'POST', url: `${target.url}/v1/resource/machines`, headers: { 'If-Match': '2' } }, + ]); + }); + + test('test creating new session from one client throws session expired error on another client while syncing', async () => { + const target = new UserDataSyncTestServer(); + + // Set up and sync from the client + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask()).run(); + + // Set up and sync from the test client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject: TestUserDataAutoSyncService = testClient.instantiationService.createInstance(TestUserDataAutoSyncService); + await testObject.sync(); + + // Reset from the first client + await client.instantiationService.get(IUserDataSyncService).reset(); + + // Sync again from the first client to create new session + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask()).run(); + + // Sync from the test client + target.reset(); + + const errorPromise = Event.toPromise(testObject.onError); + await testObject.sync(); + + const e = await errorPromise; + assert.ok(e instanceof UserDataAutoSyncError); + assert.deepEqual((e).code, UserDataSyncErrorCode.SessionExpired); + assert.deepEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + // Machine + { type: 'GET', url: `${target.url}/v1/resource/machines/latest`, headers: { 'If-None-Match': '1' } }, + ]); + }); + + test('test rate limit on server', async () => { + const target = new UserDataSyncTestServer(5); + + // Set up and sync from the test client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject: TestUserDataAutoSyncService = testClient.instantiationService.createInstance(TestUserDataAutoSyncService); + + const errorPromise = Event.toPromise(testObject.onError); + while (target.requests.length < 5) { + await testObject.sync(); + } + + const e = await errorPromise; + assert.ok(e instanceof UserDataSyncStoreError); + assert.deepEqual((e).code, UserDataSyncErrorCode.TooManyRequests); + }); + + test('test auto sync is suspended when server donot accepts requests', async () => { + const target = new UserDataSyncTestServer(5, 1); + + // Set up and sync from the test client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject: TestUserDataAutoSyncService = testClient.instantiationService.createInstance(TestUserDataAutoSyncService); + + while (target.requests.length < 5) { + await testObject.sync(); + } + + target.reset(); + await testObject.sync(); + + assert.deepEqual(target.requests, []); + }); + +}); diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index d9168c084cc..767fbbf6cb5 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -6,14 +6,14 @@ import { IRequestService } from 'vs/platform/request/common/request'; import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IUserData, IUserDataManifest, ALL_SYNC_RESOURCES, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncService, getDefaultIgnoredSettings, IUserDataSyncBackupStoreService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserData, IUserDataManifest, ALL_SYNC_RESOURCES, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncResourceEnablementService, IUserDataSyncService, getDefaultIgnoredSettings, IUserDataSyncBackupStoreService, SyncResource, ServerResource, IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync'; import { bufferToStream, VSBuffer } from 'vs/base/common/buffer'; import { generateUuid } from 'vs/base/common/uuid'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { NullLogService, ILogService } from 'vs/platform/log/common/log'; -import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; +import { UserDataSyncStoreService, UserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IFileService } from 'vs/platform/files/common/files'; @@ -25,18 +25,19 @@ import { URI } from 'vs/base/common/uri'; import { joinPath } from 'vs/base/common/resources'; import { IStringDictionary } from 'vs/base/common/collections'; import { FormattingOptions } from 'vs/base/common/jsonFormatter'; -import { UserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSyncEnablementService'; +import { UserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSyncResourceEnablementService'; import { IGlobalExtensionEnablementService, IExtensionManagementService, IExtensionGalleryService, DidInstallExtensionEvent, DidUninstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement'; import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; import { Disposable } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; -import { IAuthenticationTokenService, IUserDataSyncAuthToken } from 'vs/platform/authentication/common/authentication'; +import { IUserDataSyncAccountService, UserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSyncBackupStoreService'; import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { IUserDataSyncMachinesService, UserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; export class UserDataSyncClient extends Disposable { @@ -48,14 +49,16 @@ export class UserDataSyncClient extends Disposable { } async setUp(empty: boolean = false): Promise { - const userDataDirectory = URI.file('userdata').with({ scheme: Schemas.inMemory }); - const userDataSyncHome = joinPath(userDataDirectory, '.sync'); + const userRoamingDataHome = URI.file('userdata').with({ scheme: Schemas.inMemory }); + const userDataSyncHome = joinPath(userRoamingDataHome, '.sync'); const environmentService = this.instantiationService.stub(IEnvironmentService, >{ userDataSyncHome, - settingsResource: joinPath(userDataDirectory, 'settings.json'), - keybindingsResource: joinPath(userDataDirectory, 'keybindings.json'), - snippetsHome: joinPath(userDataDirectory, 'snippets'), - argvResource: joinPath(userDataDirectory, 'argv.json') + userRoamingDataHome, + settingsResource: joinPath(userRoamingDataHome, 'settings.json'), + keybindingsResource: joinPath(userRoamingDataHome, 'keybindings.json'), + snippetsHome: joinPath(userRoamingDataHome, 'snippets'), + argvResource: joinPath(userRoamingDataHome, 'argv.json'), + sync: 'on', }); const logService = new NullLogService(); @@ -81,17 +84,20 @@ export class UserDataSyncClient extends Disposable { this.instantiationService.stub(IConfigurationService, configurationService); this.instantiationService.stub(IRequestService, this.testServer); - this.instantiationService.stub(IAuthenticationTokenService, >{ - onDidChangeToken: new Emitter().event, - async getToken() { return { authenticationProviderId: 'id', token: 'token' }; } - }); this.instantiationService.stub(IUserDataSyncLogService, logService); this.instantiationService.stub(ITelemetryService, NullTelemetryService); + this.instantiationService.stub(IUserDataSyncStoreManagementService, this.instantiationService.createInstance(UserDataSyncStoreManagementService)); this.instantiationService.stub(IUserDataSyncStoreService, this.instantiationService.createInstance(UserDataSyncStoreService)); + + const userDataSyncAccountService: IUserDataSyncAccountService = this.instantiationService.createInstance(UserDataSyncAccountService); + await userDataSyncAccountService.updateAccount({ authenticationProviderId: 'authenticationProviderId', token: 'token' }); + this.instantiationService.stub(IUserDataSyncAccountService, userDataSyncAccountService); + + this.instantiationService.stub(IUserDataSyncMachinesService, this.instantiationService.createInstance(UserDataSyncMachinesService)); this.instantiationService.stub(IUserDataSyncBackupStoreService, this.instantiationService.createInstance(UserDataSyncBackupStoreService)); this.instantiationService.stub(IUserDataSyncUtilService, new TestUserDataSyncUtilService()); - this.instantiationService.stub(IUserDataSyncEnablementService, this.instantiationService.createInstance(UserDataSyncEnablementService)); + this.instantiationService.stub(IUserDataSyncResourceEnablementService, this.instantiationService.createInstance(UserDataSyncResourceEnablementService)); this.instantiationService.stub(IStorageKeysSyncRegistryService, this.instantiationService.createInstance(StorageKeysSyncRegistryService)); this.instantiationService.stub(IGlobalExtensionEnablementService, this.instantiationService.createInstance(GlobalExtensionEnablementService)); @@ -116,34 +122,48 @@ export class UserDataSyncClient extends Disposable { await configurationService.reloadConfiguration(); } - sync(): Promise { - return this.instantiationService.get(IUserDataSyncService).sync(); + async sync(): Promise { + await (await this.instantiationService.get(IUserDataSyncService).createSyncTask()).run(); } read(resource: SyncResource): Promise { return this.instantiationService.get(IUserDataSyncStoreService).read(resource, null); } + manifest(): Promise { + return this.instantiationService.get(IUserDataSyncStoreService).manifest(); + } + } +const ALL_SERVER_RESOURCES: ServerResource[] = [...ALL_SYNC_RESOURCES, 'machines']; + export class UserDataSyncTestServer implements IRequestService { _serviceBrand: any; readonly url: string = 'http://host:3000'; private session: string | null = null; - private readonly data: Map = new Map(); + private readonly data: Map = new Map(); private _requests: { url: string, type: string, headers?: IHeaders }[] = []; get requests(): { url: string, type: string, headers?: IHeaders }[] { return this._requests; } + private _requestsWithAllHeaders: { url: string, type: string, headers?: IHeaders }[] = []; + get requestsWithAllHeaders(): { url: string, type: string, headers?: IHeaders }[] { return this._requestsWithAllHeaders; } + private _responses: { status: number }[] = []; get responses(): { status: number }[] { return this._responses; } - reset(): void { this._requests = []; this._responses = []; } + reset(): void { this._requests = []; this._responses = []; this._requestsWithAllHeaders = []; } + + constructor(private readonly rateLimit = Number.MAX_SAFE_INTEGER, private readonly retryAfter?: number) { } async resolveProxy(url: string): Promise { return url; } async request(options: IRequestOptions, token: CancellationToken): Promise { + if (this._requests.length === this.rateLimit) { + return this.toResponse(429, this.retryAfter ? { 'retry-after': `${this.retryAfter}` } : undefined); + } const headers: IHeaders = {}; if (options.headers) { if (options.headers['If-None-Match']) { @@ -154,6 +174,7 @@ export class UserDataSyncTestServer implements IRequestService { } } this._requests.push({ url: options.url!, type: options.type!, headers }); + this._requestsWithAllHeaders.push({ url: options.url!, type: options.type!, headers: options.headers }); const requestContext = await this.doRequest(options); this._responses.push({ status: requestContext.res.statusCode! }); return requestContext; @@ -180,7 +201,7 @@ export class UserDataSyncTestServer implements IRequestService { private async getManifest(headers?: IHeaders): Promise { if (this.session) { - const latest: Record = Object.create({}); + const latest: Record = Object.create({}); const manifest: IUserDataManifest = { session: this.session, latest }; this.data.forEach((value, key) => latest[key] = value.ref); return this.toResponse(200, { 'Content-Type': 'application/json' }, JSON.stringify(manifest)); @@ -189,7 +210,7 @@ export class UserDataSyncTestServer implements IRequestService { } private async getLatestData(resource: string, headers: IHeaders = {}): Promise { - const resourceKey = ALL_SYNC_RESOURCES.find(key => key === resource); + const resourceKey = ALL_SERVER_RESOURCES.find(key => key === resource); if (resourceKey) { const data = this.data.get(resourceKey); if (!data) { @@ -207,7 +228,7 @@ export class UserDataSyncTestServer implements IRequestService { if (!this.session) { this.session = generateUuid(); } - const resourceKey = ALL_SYNC_RESOURCES.find(key => key === resource); + const resourceKey = ALL_SERVER_RESOURCES.find(key => key === resource); if (resourceKey) { const data = this.data.get(resourceKey); if (headers['If-Match'] !== undefined && headers['If-Match'] !== (data ? data.ref : '0')) { @@ -220,7 +241,7 @@ export class UserDataSyncTestServer implements IRequestService { return this.toResponse(204); } - private async clear(headers?: IHeaders): Promise { + async clear(headers?: IHeaders): Promise { this.data.clear(); this.session = null; return this.toResponse(204); diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts b/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts index 5c2b51c2840..768592a3ec5 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IUserDataSyncService, UserDataSyncError, UserDataSyncErrorCode, SyncStatus, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, SyncStatus, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IFileService } from 'vs/platform/files/common/files'; @@ -26,7 +26,7 @@ suite('UserDataSyncService', () => { const testObject = client.instantiationService.get(IUserDataSyncService); // Sync for first time - await testObject.sync(); + await (await testObject.createSyncTask()).run(); assert.deepEqual(target.requests, [ // Manifest @@ -45,9 +45,6 @@ suite('UserDataSyncService', () => { { type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } }, // Extensions { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, - { type: 'POST', url: `${target.url}/v1/resource/extensions`, headers: { 'If-Match': '0' } }, - // Manifest - { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, ]); }); @@ -60,7 +57,7 @@ suite('UserDataSyncService', () => { const testObject = client.instantiationService.get(IUserDataSyncService); // Sync for first time - await testObject.sync(); + await (await testObject.createSyncTask()).run(); assert.deepEqual(target.requests, [ // Manifest @@ -71,87 +68,10 @@ suite('UserDataSyncService', () => { { type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} }, // Snippets { type: 'GET', url: `${target.url}/v1/resource/snippets/latest`, headers: {} }, - { type: 'POST', url: `${target.url}/v1/resource/snippets`, headers: { 'If-Match': '0' } }, // Global state { type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} }, - { type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } }, // Extensions { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, - { type: 'POST', url: `${target.url}/v1/resource/extensions`, headers: { 'If-Match': '0' } }, - // Manifest - { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, - ]); - - }); - - test('test first time sync from the client with no changes - pull', async () => { - const target = new UserDataSyncTestServer(); - - // Setup and sync from the first client - const client = disposableStore.add(new UserDataSyncClient(target)); - await client.setUp(); - await client.instantiationService.get(IUserDataSyncService).sync(); - - // Setup the test client - const testClient = disposableStore.add(new UserDataSyncClient(target)); - await testClient.setUp(); - const testObject = testClient.instantiationService.get(IUserDataSyncService); - - // Sync (pull) from the test client - target.reset(); - await testObject.isFirstTimeSyncWithMerge(); - await testObject.pull(); - - assert.deepEqual(target.requests, [ - /* first time sync */ - { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/resource/snippets/latest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, - /* pull */ - { type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/resource/snippets/latest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, - ]); - - }); - - test('test first time sync from the client with changes - pull', async () => { - const target = new UserDataSyncTestServer(); - - // Setup and sync from the first client - const client = disposableStore.add(new UserDataSyncClient(target)); - await client.setUp(); - await client.instantiationService.get(IUserDataSyncService).sync(); - - // Setup the test client with changes - const testClient = disposableStore.add(new UserDataSyncClient(target)); - await testClient.setUp(); - const testObject = testClient.instantiationService.get(IUserDataSyncService); - const fileService = testClient.instantiationService.get(IFileService); - const environmentService = testClient.instantiationService.get(IEnvironmentService); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); - await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); - await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'de' }))); - - // Sync (pull) from the test client - target.reset(); - await testObject.isFirstTimeSyncWithMerge(); - await testObject.pull(); - - assert.deepEqual(target.requests, [ - /* first time sync */ - { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} }, - /* pull */ - { type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/resource/snippets/latest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, ]); }); @@ -162,7 +82,7 @@ suite('UserDataSyncService', () => { // Setup and sync from the first client const client = disposableStore.add(new UserDataSyncClient(target)); await client.setUp(); - await client.instantiationService.get(IUserDataSyncService).sync(); + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask()).run(); // Setup the test client const testClient = disposableStore.add(new UserDataSyncClient(target)); @@ -171,17 +91,9 @@ suite('UserDataSyncService', () => { // Sync (merge) from the test client target.reset(); - await testObject.isFirstTimeSyncWithMerge(); - await testObject.sync(); + await (await testObject.createSyncTask()).run(); assert.deepEqual(target.requests, [ - /* first time sync */ - { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/resource/snippets/latest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, - /* sync */ { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} }, @@ -198,7 +110,7 @@ suite('UserDataSyncService', () => { // Setup and sync from the first client const client = disposableStore.add(new UserDataSyncClient(target)); await client.setUp(); - await client.instantiationService.get(IUserDataSyncService).sync(); + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask()).run(); // Setup the test client with changes const testClient = disposableStore.add(new UserDataSyncClient(target)); @@ -213,15 +125,9 @@ suite('UserDataSyncService', () => { // Sync (merge) from the test client target.reset(); - await testObject.isFirstTimeSyncWithMerge(); - await testObject.sync(); + await (await testObject.createSyncTask()).run(); assert.deepEqual(target.requests, [ - /* first time sync */ - { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} }, - - /* first time sync */ { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} }, { type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '1' } }, @@ -242,11 +148,11 @@ suite('UserDataSyncService', () => { const client = disposableStore.add(new UserDataSyncClient(target)); await client.setUp(); const testObject = client.instantiationService.get(IUserDataSyncService); - await testObject.sync(); + await (await testObject.createSyncTask()).run(); // sync from the client again target.reset(); - await testObject.sync(); + await (await testObject.createSyncTask()).run(); assert.deepEqual(target.requests, [ // Manifest @@ -261,7 +167,7 @@ suite('UserDataSyncService', () => { const client = disposableStore.add(new UserDataSyncClient(target)); await client.setUp(); const testObject = client.instantiationService.get(IUserDataSyncService); - await testObject.sync(); + await (await testObject.createSyncTask()).run(); target.reset(); // Do changes in the client @@ -273,7 +179,7 @@ suite('UserDataSyncService', () => { await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'de' }))); // Sync from the client - await testObject.sync(); + await (await testObject.createSyncTask()).run(); assert.deepEqual(target.requests, [ // Manifest @@ -295,13 +201,13 @@ suite('UserDataSyncService', () => { // Sync from first client const client = disposableStore.add(new UserDataSyncClient(target)); await client.setUp(); - await client.instantiationService.get(IUserDataSyncService).sync(); + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask()).run(); // Sync from test client const testClient = disposableStore.add(new UserDataSyncClient(target)); await testClient.setUp(); const testObject = testClient.instantiationService.get(IUserDataSyncService); - await testObject.sync(); + await (await testObject.createSyncTask()).run(); // Do changes in first client and sync const fileService = client.instantiationService.get(IFileService); @@ -310,11 +216,11 @@ suite('UserDataSyncService', () => { await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); await fileService.writeFile(joinPath(environmentService.snippetsHome, 'html.json'), VSBuffer.fromString(`{ "a": "changed" }`)); await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'de' }))); - await client.instantiationService.get(IUserDataSyncService).sync(); + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask()).run(); // Sync from test client target.reset(); - await testObject.sync(); + await (await testObject.createSyncTask()).run(); assert.deepEqual(target.requests, [ // Manifest @@ -338,7 +244,7 @@ suite('UserDataSyncService', () => { const testClient = disposableStore.add(new UserDataSyncClient(target)); await testClient.setUp(); const testObject = testClient.instantiationService.get(IUserDataSyncService); - await testObject.sync(); + await (await testObject.createSyncTask()).run(); // Reset from the client target.reset(); @@ -358,14 +264,14 @@ suite('UserDataSyncService', () => { const testClient = disposableStore.add(new UserDataSyncClient(target)); await testClient.setUp(); const testObject = testClient.instantiationService.get(IUserDataSyncService); - await testObject.sync(); + await (await testObject.createSyncTask()).run(); // Reset from the client await testObject.reset(); // Sync again target.reset(); - await testObject.sync(); + await (await testObject.createSyncTask()).run(); assert.deepEqual(target.requests, [ // Manifest @@ -384,82 +290,10 @@ suite('UserDataSyncService', () => { { type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } }, // Extensions { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, - { type: 'POST', url: `${target.url}/v1/resource/extensions`, headers: { 'If-Match': '0' } }, - // Manifest - { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, ]); }); - test('test delete on one client throws turned off error on other client while syncing', async () => { - const target = new UserDataSyncTestServer(); - - // Set up and sync from the client - const client = disposableStore.add(new UserDataSyncClient(target)); - await client.setUp(); - await client.instantiationService.get(IUserDataSyncService).sync(); - - // Set up and sync from the test client - const testClient = disposableStore.add(new UserDataSyncClient(target)); - await testClient.setUp(); - const testObject = testClient.instantiationService.get(IUserDataSyncService); - await testObject.sync(); - - // Reset from the first client - await client.instantiationService.get(IUserDataSyncService).reset(); - - // Sync from the test client - target.reset(); - try { - await testObject.sync(); - } catch (e) { - assert.ok(e instanceof UserDataSyncError); - assert.deepEqual((e).code, UserDataSyncErrorCode.TurnedOff); - assert.deepEqual(target.requests, [ - // Manifest - { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, - ]); - return; - } - throw assert.fail('Should fail with turned off error'); - }); - - test('test creating new session from one client throws session expired error on another client while syncing', async () => { - const target = new UserDataSyncTestServer(); - - // Set up and sync from the client - const client = disposableStore.add(new UserDataSyncClient(target)); - await client.setUp(); - await client.instantiationService.get(IUserDataSyncService).sync(); - - // Set up and sync from the test client - const testClient = disposableStore.add(new UserDataSyncClient(target)); - await testClient.setUp(); - const testObject = testClient.instantiationService.get(IUserDataSyncService); - await testObject.sync(); - - // Reset from the first client - await client.instantiationService.get(IUserDataSyncService).reset(); - - // Sync again from the first client to create new session - await client.instantiationService.get(IUserDataSyncService).sync(); - - // Sync from the test client - target.reset(); - try { - await testObject.sync(); - } catch (e) { - assert.ok(e instanceof UserDataSyncError); - assert.deepEqual((e).code, UserDataSyncErrorCode.SessionExpired); - assert.deepEqual(target.requests, [ - // Manifest - { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, - ]); - return; - } - throw assert.fail('Should fail with turned off error'); - }); - test('test sync status', async () => { const target = new UserDataSyncTestServer(); @@ -471,7 +305,7 @@ suite('UserDataSyncService', () => { // sync from the client const actualStatuses: SyncStatus[] = []; const disposable = testObject.onDidChangeStatus(status => actualStatuses.push(status)); - await testObject.sync(); + await (await testObject.createSyncTask()).run(); disposable.dispose(); assert.deepEqual(actualStatuses, [SyncStatus.Syncing, SyncStatus.Idle, SyncStatus.Syncing, SyncStatus.Idle, SyncStatus.Syncing, SyncStatus.Idle, SyncStatus.Syncing, SyncStatus.Idle, SyncStatus.Syncing, SyncStatus.Idle]); @@ -486,7 +320,7 @@ suite('UserDataSyncService', () => { let fileService = client.instantiationService.get(IFileService); let environmentService = client.instantiationService.get(IEnvironmentService); await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); - await client.instantiationService.get(IUserDataSyncService).sync(); + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask()).run(); // Setup the test client const testClient = disposableStore.add(new UserDataSyncClient(target)); @@ -497,10 +331,10 @@ suite('UserDataSyncService', () => { const testObject = testClient.instantiationService.get(IUserDataSyncService); // sync from the client - await testObject.sync(); + await (await testObject.createSyncTask()).run(); assert.deepEqual(testObject.status, SyncStatus.HasConflicts); - assert.deepEqual(testObject.conflicts.map(({ syncResource }) => syncResource), [SyncResource.Settings]); + assert.deepEqual(testObject.conflicts.map(([syncResource]) => syncResource), [SyncResource.Settings]); }); test('test sync will sync other non conflicted areas', async () => { @@ -512,7 +346,7 @@ suite('UserDataSyncService', () => { let fileService = client.instantiationService.get(IFileService); let environmentService = client.instantiationService.get(IEnvironmentService); await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); - await client.instantiationService.get(IUserDataSyncService).sync(); + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask()).run(); // Setup the test client and get conflicts in settings const testClient = disposableStore.add(new UserDataSyncClient(target)); @@ -521,17 +355,17 @@ suite('UserDataSyncService', () => { let testEnvironmentService = testClient.instantiationService.get(IEnvironmentService); await testFileService.writeFile(testEnvironmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 16 }))); const testObject = testClient.instantiationService.get(IUserDataSyncService); - await testObject.sync(); + await (await testObject.createSyncTask()).run(); // sync from the first client with changes in keybindings await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); - await client.instantiationService.get(IUserDataSyncService).sync(); + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask()).run(); // sync from the test client target.reset(); const actualStatuses: SyncStatus[] = []; const disposable = testObject.onDidChangeStatus(status => actualStatuses.push(status)); - await testObject.sync(); + await (await testObject.createSyncTask()).run(); disposable.dispose(); assert.deepEqual(actualStatuses, []); @@ -554,7 +388,7 @@ suite('UserDataSyncService', () => { let fileService = client.instantiationService.get(IFileService); let environmentService = client.instantiationService.get(IEnvironmentService); await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); - await client.instantiationService.get(IUserDataSyncService).sync(); + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask()).run(); // Setup the test client const testClient = disposableStore.add(new UserDataSyncClient(target)); @@ -563,13 +397,48 @@ suite('UserDataSyncService', () => { environmentService = testClient.instantiationService.get(IEnvironmentService); await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 16 }))); const testObject = testClient.instantiationService.get(IUserDataSyncService); - await testObject.sync(); - // sync from the client - await testObject.stop(); + + const syncTask = (await testObject.createSyncTask()); + syncTask.run(); + await syncTask.stop(); assert.deepEqual(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); }); + test('test sync send execution id header', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncService); + + await (await testObject.createSyncTask()).run(); + + for (const request of target.requestsWithAllHeaders) { + const hasExecutionIdHeader = request.headers && request.headers['X-Execution-Id'] && request.headers['X-Execution-Id'].length > 0; + assert.ok(hasExecutionIdHeader, `Should have execution header: ${request.url}`); + } + + }); + + test('test can run sync taks only once', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncService); + + const syncTask = await testObject.createSyncTask(); + await syncTask.run(); + + try { + await syncTask.run(); + assert.fail('Should fail running the task again'); + } catch (error) { + /* expected */ + } + }); + }); diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts b/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts new file mode 100644 index 00000000000..f732c47f0b2 --- /dev/null +++ b/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts @@ -0,0 +1,438 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { IUserDataSyncStoreService, SyncResource, UserDataSyncErrorCode, UserDataSyncStoreError } from 'vs/platform/userDataSync/common/userDataSync'; +import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { isWeb } from 'vs/base/common/platform'; +import { RequestsSession, UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IRequestService } from 'vs/platform/request/common/request'; +import { newWriteableBufferStream } from 'vs/base/common/buffer'; +import { timeout } from 'vs/base/common/async'; +import { NullLogService } from 'vs/platform/log/common/log'; +import { Event } from 'vs/base/common/event'; + +suite('UserDataSyncStoreService', () => { + + const disposableStore = new DisposableStore(); + + teardown(() => disposableStore.clear()); + + test('test read manifest for the first time', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncStoreService); + const productService = client.instantiationService.get(IProductService); + + await testObject.manifest(); + + assert.equal(target.requestsWithAllHeaders.length, 1); + assert.equal(target.requestsWithAllHeaders[0].headers!['X-Client-Name'], `${productService.applicationName}${isWeb ? '-web' : ''}`); + assert.equal(target.requestsWithAllHeaders[0].headers!['X-Client-Version'], productService.version); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-Machine-Id'], undefined); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id'], undefined); + assert.equal(target.requestsWithAllHeaders[0].headers!['X-User-Session-Id'], undefined); + }); + + test('test read manifest for the second time when session is not yet created', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncStoreService); + + await testObject.manifest(); + const machineSessionId = target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id']; + + target.reset(); + await testObject.manifest(); + + assert.equal(target.requestsWithAllHeaders.length, 1); + assert.equal(target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id'], machineSessionId); + assert.equal(target.requestsWithAllHeaders[0].headers!['X-User-Session-Id'], undefined); + }); + + test('test session id header is not set in the first manifest request after session is created', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncStoreService); + + await testObject.manifest(); + const machineSessionId = target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id']; + await testObject.write(SyncResource.Settings, 'some content', null); + + target.reset(); + await testObject.manifest(); + + assert.equal(target.requestsWithAllHeaders.length, 1); + assert.equal(target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id'], machineSessionId); + assert.equal(target.requestsWithAllHeaders[0].headers!['X-User-Session-Id'], undefined); + }); + + test('test session id header is set from the second manifest request after session is created', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncStoreService); + + await testObject.manifest(); + const machineSessionId = target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id']; + await testObject.write(SyncResource.Settings, 'some content', null); + await testObject.manifest(); + + target.reset(); + await testObject.manifest(); + + assert.equal(target.requestsWithAllHeaders.length, 1); + assert.equal(target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id'], machineSessionId); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-User-Session-Id'], undefined); + }); + + test('test headers are send for write request', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncStoreService); + + await testObject.manifest(); + const machineSessionId = target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id']; + await testObject.write(SyncResource.Settings, 'some content', null); + await testObject.manifest(); + await testObject.manifest(); + + target.reset(); + await testObject.write(SyncResource.Settings, 'some content', null); + + assert.equal(target.requestsWithAllHeaders.length, 1); + assert.equal(target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id'], machineSessionId); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-User-Session-Id'], undefined); + }); + + test('test headers are send for read request', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncStoreService); + + await testObject.manifest(); + const machineSessionId = target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id']; + await testObject.write(SyncResource.Settings, 'some content', null); + await testObject.manifest(); + await testObject.manifest(); + + target.reset(); + await testObject.read(SyncResource.Settings, null); + + assert.equal(target.requestsWithAllHeaders.length, 1); + assert.equal(target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id'], machineSessionId); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-User-Session-Id'], undefined); + }); + + test('test headers are reset after session is cleared ', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncStoreService); + + await testObject.manifest(); + const machineSessionId = target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id']; + await testObject.write(SyncResource.Settings, 'some content', null); + await testObject.manifest(); + await testObject.manifest(); + await testObject.clear(); + + target.reset(); + await testObject.manifest(); + + assert.equal(target.requestsWithAllHeaders.length, 1); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id'], undefined); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id'], machineSessionId); + assert.equal(target.requestsWithAllHeaders[0].headers!['X-User-Session-Id'], undefined); + }); + + test('test old headers are sent after session is changed on server ', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncStoreService); + + await testObject.manifest(); + await testObject.write(SyncResource.Settings, 'some content', null); + await testObject.manifest(); + target.reset(); + await testObject.manifest(); + const machineSessionId = target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id']; + const userSessionId = target.requestsWithAllHeaders[0].headers!['X-User-Session-Id']; + await target.clear(); + + // client 2 + const client2 = disposableStore.add(new UserDataSyncClient(target)); + await client2.setUp(); + const testObject2 = client2.instantiationService.get(IUserDataSyncStoreService); + await testObject2.write(SyncResource.Settings, 'some content', null); + + target.reset(); + await testObject.manifest(); + + assert.equal(target.requestsWithAllHeaders.length, 1); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id'], undefined); + assert.equal(target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id'], machineSessionId); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-User-Session-Id'], undefined); + assert.equal(target.requestsWithAllHeaders[0].headers!['X-User-Session-Id'], userSessionId); + }); + + test('test old headers are reset from second request after session is changed on server ', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncStoreService); + + await testObject.manifest(); + await testObject.write(SyncResource.Settings, 'some content', null); + await testObject.manifest(); + target.reset(); + await testObject.manifest(); + const machineSessionId = target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id']; + const userSessionId = target.requestsWithAllHeaders[0].headers!['X-User-Session-Id']; + await target.clear(); + + // client 2 + const client2 = disposableStore.add(new UserDataSyncClient(target)); + await client2.setUp(); + const testObject2 = client2.instantiationService.get(IUserDataSyncStoreService); + await testObject2.write(SyncResource.Settings, 'some content', null); + + await testObject.manifest(); + target.reset(); + await testObject.manifest(); + + assert.equal(target.requestsWithAllHeaders.length, 1); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id'], undefined); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id'], machineSessionId); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-User-Session-Id'], undefined); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-User-Session-Id'], userSessionId); + }); + + test('test old headers are sent after session is cleared from another server ', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncStoreService); + + await testObject.manifest(); + await testObject.write(SyncResource.Settings, 'some content', null); + await testObject.manifest(); + target.reset(); + await testObject.manifest(); + const machineSessionId = target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id']; + const userSessionId = target.requestsWithAllHeaders[0].headers!['X-User-Session-Id']; + + // client 2 + const client2 = disposableStore.add(new UserDataSyncClient(target)); + await client2.setUp(); + const testObject2 = client2.instantiationService.get(IUserDataSyncStoreService); + await testObject2.clear(); + + target.reset(); + await testObject.manifest(); + + assert.equal(target.requestsWithAllHeaders.length, 1); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id'], undefined); + assert.equal(target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id'], machineSessionId); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-User-Session-Id'], undefined); + assert.equal(target.requestsWithAllHeaders[0].headers!['X-User-Session-Id'], userSessionId); + }); + + test('test headers are reset after session is cleared from another server ', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncStoreService); + + await testObject.manifest(); + await testObject.write(SyncResource.Settings, 'some content', null); + await testObject.manifest(); + target.reset(); + await testObject.manifest(); + const machineSessionId = target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id']; + + // client 2 + const client2 = disposableStore.add(new UserDataSyncClient(target)); + await client2.setUp(); + const testObject2 = client2.instantiationService.get(IUserDataSyncStoreService); + await testObject2.clear(); + + await testObject.manifest(); + target.reset(); + await testObject.manifest(); + + assert.equal(target.requestsWithAllHeaders.length, 1); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id'], undefined); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id'], machineSessionId); + assert.equal(target.requestsWithAllHeaders[0].headers!['X-User-Session-Id'], undefined); + }); + + test('test headers are reset after session is cleared from another server - started syncing again', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncStoreService); + + await testObject.manifest(); + await testObject.write(SyncResource.Settings, 'some content', null); + await testObject.manifest(); + target.reset(); + await testObject.manifest(); + const machineSessionId = target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id']; + const userSessionId = target.requestsWithAllHeaders[0].headers!['X-User-Session-Id']; + + // client 2 + const client2 = disposableStore.add(new UserDataSyncClient(target)); + await client2.setUp(); + const testObject2 = client2.instantiationService.get(IUserDataSyncStoreService); + await testObject2.clear(); + + await testObject.manifest(); + await testObject.write(SyncResource.Settings, 'some content', null); + await testObject.manifest(); + target.reset(); + await testObject.manifest(); + + assert.equal(target.requestsWithAllHeaders.length, 1); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id'], undefined); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-Machine-Session-Id'], machineSessionId); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-User-Session-Id'], userSessionId); + assert.notEqual(target.requestsWithAllHeaders[0].headers!['X-User-Session-Id'], undefined); + }); + + test('test rate limit on server with retry after', async () => { + const target = new UserDataSyncTestServer(1, 1); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncStoreService); + + await testObject.manifest(); + + const promise = Event.toPromise(testObject.onDidChangeDonotMakeRequestsUntil); + try { + await testObject.manifest(); + assert.fail('should fail'); + } catch (e) { + assert.ok(e instanceof UserDataSyncStoreError); + assert.deepEqual((e).code, UserDataSyncErrorCode.TooManyRequestsAndRetryAfter); + await promise; + assert.ok(!!testObject.donotMakeRequestsUntil); + } + }); + + test('test donotMakeRequestsUntil is reset after retry time is finished', async () => { + const client = disposableStore.add(new UserDataSyncClient(new UserDataSyncTestServer(1, 0.25))); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncStoreService); + + await testObject.manifest(); + try { + await testObject.manifest(); + } catch (e) { } + + const promise = Event.toPromise(testObject.onDidChangeDonotMakeRequestsUntil); + await timeout(300); + await promise; + assert.ok(!testObject.donotMakeRequestsUntil); + }); + + test('test donotMakeRequestsUntil is retrieved', async () => { + const client = disposableStore.add(new UserDataSyncClient(new UserDataSyncTestServer(1, 1))); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncStoreService); + + await testObject.manifest(); + try { + await testObject.manifest(); + } catch (e) { } + + const target = client.instantiationService.createInstance(UserDataSyncStoreService); + assert.equal(target.donotMakeRequestsUntil?.getTime(), testObject.donotMakeRequestsUntil?.getTime()); + }); + + test('test donotMakeRequestsUntil is checked and reset after retreived', async () => { + const client = disposableStore.add(new UserDataSyncClient(new UserDataSyncTestServer(1, 0.25))); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncStoreService); + + await testObject.manifest(); + try { + await testObject.manifest(); + } catch (e) { } + + await timeout(300); + const target = client.instantiationService.createInstance(UserDataSyncStoreService); + assert.ok(!target.donotMakeRequestsUntil); + }); + +}); + +suite('UserDataSyncRequestsSession', () => { + + const requestService: IRequestService = { + _serviceBrand: undefined, + async request() { return { res: { headers: {} }, stream: newWriteableBufferStream() }; }, + async resolveProxy() { return undefined; } + }; + + test('too many requests are thrown when limit exceeded', async () => { + const testObject = new RequestsSession(1, 500, requestService, new NullLogService()); + await testObject.request({}, CancellationToken.None); + + try { + await testObject.request({}, CancellationToken.None); + } catch (error) { + assert.ok(error instanceof UserDataSyncStoreError); + assert.equal((error).code, UserDataSyncErrorCode.LocalTooManyRequests); + return; + } + assert.fail('Should fail with limit exceeded'); + }); + + test('requests are handled after session is expired', async () => { + const testObject = new RequestsSession(1, 500, requestService, new NullLogService()); + await testObject.request({}, CancellationToken.None); + await timeout(600); + await testObject.request({}, CancellationToken.None); + }); + + test('too many requests are thrown after session is expired', async () => { + const testObject = new RequestsSession(1, 500, requestService, new NullLogService()); + await testObject.request({}, CancellationToken.None); + await timeout(600); + await testObject.request({}, CancellationToken.None); + + try { + await testObject.request({}, CancellationToken.None); + } catch (error) { + assert.ok(error instanceof UserDataSyncStoreError); + assert.equal((error).code, UserDataSyncErrorCode.LocalTooManyRequests); + return; + } + assert.fail('Should fail with limit exceeded'); + }); + +}); diff --git a/src/vs/workbench/contrib/webview/common/mimeTypes.ts b/src/vs/platform/webview/common/mimeTypes.ts similarity index 78% rename from src/vs/workbench/contrib/webview/common/mimeTypes.ts rename to src/vs/platform/webview/common/mimeTypes.ts index 0f1f583d451..f9a54488d19 100644 --- a/src/vs/workbench/contrib/webview/common/mimeTypes.ts +++ b/src/vs/platform/webview/common/mimeTypes.ts @@ -20,7 +20,7 @@ const webviewMimeTypes = new Map([ ['.xml', 'application/xml'], ]); -export function getWebviewContentMimeType(normalizedPath: URI): string { - const ext = extname(normalizedPath.fsPath).toLowerCase(); - return webviewMimeTypes.get(ext) || getMediaMime(normalizedPath.fsPath) || MIME_UNKNOWN; +export function getWebviewContentMimeType(resource: URI): string { + const ext = extname(resource.fsPath).toLowerCase(); + return webviewMimeTypes.get(ext) || getMediaMime(resource.fsPath) || MIME_UNKNOWN; } diff --git a/src/vs/platform/webview/common/resourceLoader.ts b/src/vs/platform/webview/common/resourceLoader.ts new file mode 100644 index 00000000000..ee64c8ef4ff --- /dev/null +++ b/src/vs/platform/webview/common/resourceLoader.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 { VSBufferReadableStream } from 'vs/base/common/buffer'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { isUNC } from 'vs/base/common/extpath'; +import { Schemas } from 'vs/base/common/network'; +import { sep } from 'vs/base/common/path'; +import { URI } from 'vs/base/common/uri'; +import { IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { IRequestService } from 'vs/platform/request/common/request'; +import { getWebviewContentMimeType } from 'vs/platform/webview/common/mimeTypes'; + + +export const webviewPartitionId = 'webview'; + +export namespace WebviewResourceResponse { + export enum Type { Success, Failed, AccessDenied } + + export class StreamSuccess { + readonly type = Type.Success; + + constructor( + public readonly stream: VSBufferReadableStream, + public readonly mimeType: string + ) { } + } + + export const Failed = { type: Type.Failed } as const; + export const AccessDenied = { type: Type.AccessDenied } as const; + + export type StreamResponse = StreamSuccess | typeof Failed | typeof AccessDenied; +} + +interface FileReader { + readFileStream(resource: URI): Promise; +} + +export async function loadLocalResource( + requestUri: URI, + options: { + extensionLocation: URI | undefined; + roots: ReadonlyArray; + remoteConnectionData?: IRemoteConnectionData | null; + rewriteUri?: (uri: URI) => URI, + }, + fileReader: FileReader, + requestService: IRequestService, +): Promise { + let resourceToLoad = getResourceToLoad(requestUri, options.roots); + if (!resourceToLoad) { + return WebviewResourceResponse.AccessDenied; + } + + const mime = getWebviewContentMimeType(requestUri); // Use the original path for the mime + + // Perform extra normalization if needed + if (options.rewriteUri) { + resourceToLoad = options.rewriteUri(resourceToLoad); + } + + if (resourceToLoad.scheme === Schemas.http || resourceToLoad.scheme === Schemas.https) { + const response = await requestService.request({ url: resourceToLoad.toString(true) }, CancellationToken.None); + if (response.res.statusCode === 200) { + return new WebviewResourceResponse.StreamSuccess(response.stream, mime); + } + return WebviewResourceResponse.Failed; + } + + try { + const contents = await fileReader.readFileStream(resourceToLoad); + return new WebviewResourceResponse.StreamSuccess(contents, mime); + } catch (err) { + console.log(err); + return WebviewResourceResponse.Failed; + } +} + +function getResourceToLoad( + requestUri: URI, + roots: ReadonlyArray +): URI | undefined { + const normalizedPath = normalizeRequestPath(requestUri); + + for (const root of roots) { + if (containsResource(root, normalizedPath)) { + return normalizedPath; + } + } + + return undefined; +} + +function normalizeRequestPath(requestUri: URI) { + if (requestUri.scheme === Schemas.vscodeWebviewResource) { + // The `vscode-webview-resource` scheme has the following format: + // + // vscode-webview-resource://id/scheme//authority?/path + // + + // Encode requestUri.path so that URI.parse can properly parse special characters like '#', '?', etc. + const resourceUri = URI.parse(encodeURIComponent(requestUri.path).replace(/%2F/gi, '/').replace(/^\/([a-z0-9\-]+)(\/{1,2})/i, (_: string, scheme: string, sep: string) => { + if (sep.length === 1) { + return `${scheme}:///`; // Add empty authority. + } else { + return `${scheme}://`; // Url has own authority. + } + })); + return resourceUri.with({ + query: requestUri.query, + fragment: requestUri.fragment + }); + } else { + return requestUri; + } +} + +function containsResource(root: URI, resource: URI): boolean { + let rootPath = root.fsPath + (root.fsPath.endsWith(sep) ? '' : sep); + let resourceFsPath = resource.fsPath; + + if (isUNC(root.fsPath) && isUNC(resource.fsPath)) { + rootPath = rootPath.toLowerCase(); + resourceFsPath = resourceFsPath.toLowerCase(); + } + + return resourceFsPath.startsWith(rootPath); +} diff --git a/src/vs/platform/webview/common/webviewManagerService.ts b/src/vs/platform/webview/common/webviewManagerService.ts new file mode 100644 index 00000000000..56171e56fc0 --- /dev/null +++ b/src/vs/platform/webview/common/webviewManagerService.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 { VSBuffer } from 'vs/base/common/buffer'; +import { UriComponents } from 'vs/base/common/uri'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { IWebviewPortMapping } from 'vs/platform/webview/common/webviewPortMapping'; + +export const IWebviewManagerService = createDecorator('webviewManagerService'); + +export interface IWebviewManagerService { + _serviceBrand: unknown; + + registerWebview(id: string, webContentsId: number | undefined, windowId: number, metadata: RegisterWebviewMetadata): Promise; + unregisterWebview(id: string): Promise; + updateWebviewMetadata(id: string, metadataDelta: Partial): Promise; + + didLoadResource(requestId: number, content: VSBuffer | undefined): void; + + setIgnoreMenuShortcuts(webContentsId: number, enabled: boolean): Promise; +} + +export interface RegisterWebviewMetadata { + readonly extensionLocation: UriComponents | undefined; + readonly localResourceRoots: readonly UriComponents[]; + readonly remoteConnectionData: IRemoteConnectionData | null; + readonly portMappings: readonly IWebviewPortMapping[]; +} diff --git a/src/vs/workbench/contrib/webview/common/portMapping.ts b/src/vs/platform/webview/common/webviewPortMapping.ts similarity index 59% rename from src/vs/workbench/contrib/webview/common/portMapping.ts rename to src/vs/platform/webview/common/webviewPortMapping.ts index fe5a6d21962..dbd8b65a9cd 100644 --- a/src/vs/workbench/contrib/webview/common/portMapping.ts +++ b/src/vs/platform/webview/common/webviewPortMapping.ts @@ -3,36 +3,42 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import * as modes from 'vs/editor/common/modes'; +import { IAddress } from 'vs/platform/remote/common/remoteAgentConnection'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; -import { ITunnelService, RemoteTunnel, extractLocalHostUriMetaDataForPortMapping } from 'vs/platform/remote/common/tunnel'; +import { extractLocalHostUriMetaDataForPortMapping, ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; -export class WebviewPortMappingManager extends Disposable { +export interface IWebviewPortMapping { + webviewPort: number; + extensionHostPort: number; +} + +/** + * Manages port mappings for a single webview. + */ +export class WebviewPortMappingManager implements IDisposable { private readonly _tunnels = new Map>(); constructor( - private readonly getExtensionLocation: () => URI | undefined, - private readonly mappings: () => ReadonlyArray, + private readonly _getExtensionLocation: () => URI | undefined, + private readonly _getMappings: () => readonly IWebviewPortMapping[], private readonly tunnelService: ITunnelService - ) { - super(); - } + ) { } - public async getRedirect(url: string): Promise { + public async getRedirect(resolveAuthority: IAddress | null | undefined, url: string): Promise { const uri = URI.parse(url); const requestLocalHostInfo = extractLocalHostUriMetaDataForPortMapping(uri); if (!requestLocalHostInfo) { return undefined; } - for (const mapping of this.mappings()) { + for (const mapping of this._getMappings()) { if (mapping.webviewPort === requestLocalHostInfo.port) { - const extensionLocation = this.getExtensionLocation(); + const extensionLocation = this._getExtensionLocation(); if (extensionLocation && extensionLocation.scheme === REMOTE_HOST_SCHEME) { - const tunnel = await this.getOrCreateTunnel(mapping.extensionHostPort); + const tunnel = resolveAuthority && await this.getOrCreateTunnel(resolveAuthority, mapping.extensionHostPort); if (tunnel) { if (tunnel.tunnelLocalPort === mapping.webviewPort) { return undefined; @@ -55,20 +61,18 @@ export class WebviewPortMappingManager extends Disposable { } dispose() { - super.dispose(); - for (const tunnel of this._tunnels.values()) { tunnel.then(tunnel => tunnel.dispose()); } this._tunnels.clear(); } - private getOrCreateTunnel(remotePort: number): Promise | undefined { + private getOrCreateTunnel(remoteAuthority: IAddress, remotePort: number): Promise | undefined { const existing = this._tunnels.get(remotePort); if (existing) { return existing; } - const tunnel = this.tunnelService.openTunnel(undefined, remotePort); + const tunnel = this.tunnelService.openTunnel({ getAddress: async () => remoteAuthority }, undefined, remotePort); if (tunnel) { this._tunnels.set(remotePort, tunnel); } diff --git a/src/vs/platform/webview/electron-main/webviewMainService.ts b/src/vs/platform/webview/electron-main/webviewMainService.ts new file mode 100644 index 00000000000..0e29b1cbaff --- /dev/null +++ b/src/vs/platform/webview/electron-main/webviewMainService.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 { webContents } from 'electron'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ITunnelService } from 'vs/platform/remote/common/tunnel'; +import { IRequestService } from 'vs/platform/request/common/request'; +import { IWebviewManagerService, RegisterWebviewMetadata } from 'vs/platform/webview/common/webviewManagerService'; +import { WebviewPortMappingProvider } from 'vs/platform/webview/electron-main/webviewPortMappingProvider'; +import { WebviewProtocolProvider } from 'vs/platform/webview/electron-main/webviewProtocolProvider'; +import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; + +export class WebviewMainService extends Disposable implements IWebviewManagerService { + + declare readonly _serviceBrand: undefined; + + private readonly protocolProvider: WebviewProtocolProvider; + private readonly portMappingProvider: WebviewPortMappingProvider; + + constructor( + @IFileService fileService: IFileService, + @IRequestService requestService: IRequestService, + @ITunnelService tunnelService: ITunnelService, + @IWindowsMainService windowsMainService: IWindowsMainService, + ) { + super(); + this.protocolProvider = this._register(new WebviewProtocolProvider(fileService, requestService, windowsMainService)); + this.portMappingProvider = this._register(new WebviewPortMappingProvider(tunnelService)); + } + + public async registerWebview(id: string, webContentsId: number | undefined, windowId: number, metadata: RegisterWebviewMetadata): Promise { + const extensionLocation = metadata.extensionLocation ? URI.from(metadata.extensionLocation) : undefined; + + this.protocolProvider.registerWebview(id, { + ...metadata, + windowId: windowId, + extensionLocation, + localResourceRoots: metadata.localResourceRoots.map(x => URI.from(x)) + }); + + this.portMappingProvider.registerWebview(id, webContentsId, { + extensionLocation, + mappings: metadata.portMappings, + resolvedAuthority: metadata.remoteConnectionData, + }); + } + + public async unregisterWebview(id: string): Promise { + this.protocolProvider.unregisterWebview(id); + this.portMappingProvider.unregisterWebview(id); + } + + public async updateWebviewMetadata(id: string, metaDataDelta: Partial): Promise { + const extensionLocation = metaDataDelta.extensionLocation ? URI.from(metaDataDelta.extensionLocation) : undefined; + + this.protocolProvider.updateWebviewMetadata(id, { + ...metaDataDelta, + extensionLocation, + localResourceRoots: metaDataDelta.localResourceRoots?.map(x => URI.from(x)), + }); + + this.portMappingProvider.updateWebviewMetadata(id, { + ...metaDataDelta, + extensionLocation, + }); + } + + public async setIgnoreMenuShortcuts(webContentsId: number, enabled: boolean): Promise { + const contents = webContents.fromId(webContentsId); + if (!contents) { + throw new Error(`Invalid webContentsId: ${webContentsId}`); + } + if (!contents.isDestroyed()) { + contents.setIgnoreMenuShortcuts(enabled); + } + } + + public async didLoadResource(requestId: number, content: VSBuffer | undefined): Promise { + this.protocolProvider.didLoadResource(requestId, content); + } +} diff --git a/src/vs/platform/webview/electron-main/webviewPortMappingProvider.ts b/src/vs/platform/webview/electron-main/webviewPortMappingProvider.ts new file mode 100644 index 00000000000..94dc3036ebc --- /dev/null +++ b/src/vs/platform/webview/electron-main/webviewPortMappingProvider.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 { session } from 'electron'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { IAddress } from 'vs/platform/remote/common/remoteAgentConnection'; +import { ITunnelService } from 'vs/platform/remote/common/tunnel'; +import { webviewPartitionId } from 'vs/platform/webview/common/resourceLoader'; +import { IWebviewPortMapping, WebviewPortMappingManager } from 'vs/platform/webview/common/webviewPortMapping'; + +interface PortMappingData { + readonly extensionLocation: URI | undefined; + readonly mappings: readonly IWebviewPortMapping[]; + readonly resolvedAuthority: IAddress | null | undefined; +} + +export class WebviewPortMappingProvider extends Disposable { + + private readonly _webviewData = new Map(); + + private _webContentsIdsToWebviewIds = new Map(); + + constructor( + @ITunnelService private readonly _tunnelService: ITunnelService, + ) { + super(); + + const sess = session.fromPartition(webviewPartitionId); + + sess.webRequest.onBeforeRequest({ + urls: [ + '*://localhost:*/*', + '*://127.0.0.1:*/*', + '*://0.0.0.0:*/*', + ] + }, async (details, callback) => { + const webviewId = details.webContentsId && this._webContentsIdsToWebviewIds.get(details.webContentsId); + if (!webviewId) { + return callback({}); + } + + const entry = this._webviewData.get(webviewId); + if (!entry) { + return callback({}); + } + + const redirect = await entry.manager.getRedirect(entry.metadata.resolvedAuthority, details.url); + return callback(redirect ? { redirectURL: redirect } : {}); + }); + } + + public async registerWebview(id: string, webContentsId: number | undefined, metadata: PortMappingData): Promise { + const manager = new WebviewPortMappingManager( + () => this._webviewData.get(id)?.metadata.extensionLocation, + () => this._webviewData.get(id)?.metadata.mappings || [], + this._tunnelService); + + this._webviewData.set(id, { webContentsId, metadata, manager }); + if (typeof webContentsId === 'number') { + this._webContentsIdsToWebviewIds.set(webContentsId, id); + } + } + + public unregisterWebview(id: string): void { + const existing = this._webviewData.get(id); + if (existing) { + existing.manager.dispose(); + this._webviewData.delete(id); + if (typeof existing.webContentsId === 'number') { + this._webContentsIdsToWebviewIds.delete(existing.webContentsId); + } + } + } + + public async updateWebviewMetadata(id: string, metadataDelta: Partial): Promise { + const entry = this._webviewData.get(id); + if (entry) { + this._webviewData.set(id, { + ...entry, + ...metadataDelta, + }); + } + } +} diff --git a/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts b/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts new file mode 100644 index 00000000000..7c158fa3895 --- /dev/null +++ b/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts @@ -0,0 +1,243 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { protocol, session } from 'electron'; +import { Readable } from 'stream'; +import { bufferToStream, VSBuffer, VSBufferReadableStream } from 'vs/base/common/buffer'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; +import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; +import { IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; +import { IRequestService } from 'vs/platform/request/common/request'; +import { loadLocalResource, webviewPartitionId, WebviewResourceResponse } from 'vs/platform/webview/common/resourceLoader'; +import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; + +interface WebviewMetadata { + readonly windowId: number; + readonly extensionLocation: URI | undefined; + readonly localResourceRoots: readonly URI[]; + readonly remoteConnectionData: IRemoteConnectionData | null; +} + +export class WebviewProtocolProvider extends Disposable { + + private static validWebviewFilePaths = new Map([ + ['/index.html', 'index.html'], + ['/electron-browser/index.html', 'index.html'], + ['/main.js', 'main.js'], + ['/host.js', 'host.js'], + ]); + + private readonly webviewMetadata = new Map(); + + private requestIdPool = 1; + private readonly pendingResourceReads = new Map void }>(); + + constructor( + @IFileService private readonly fileService: IFileService, + @IRequestService private readonly requestService: IRequestService, + @IWindowsMainService readonly windowsMainService: IWindowsMainService, + ) { + super(); + + const sess = session.fromPartition(webviewPartitionId); + + // Register the protocol loading webview html + const webviewHandler = this.handleWebviewRequest.bind(this); + protocol.registerFileProtocol(Schemas.vscodeWebview, webviewHandler); + sess.protocol.registerFileProtocol(Schemas.vscodeWebview, webviewHandler); + + // Register the protocol loading webview resources both inside the webview and at the top level + const webviewResourceHandler = this.handleWebviewResourceRequest.bind(this); + protocol.registerStreamProtocol(Schemas.vscodeWebviewResource, webviewResourceHandler); + sess.protocol.registerStreamProtocol(Schemas.vscodeWebviewResource, webviewResourceHandler); + + this._register(toDisposable(() => { + protocol.unregisterProtocol(Schemas.vscodeWebviewResource); + sess.protocol.unregisterProtocol(Schemas.vscodeWebviewResource); + protocol.unregisterProtocol(Schemas.vscodeWebview); + sess.protocol.unregisterProtocol(Schemas.vscodeWebview); + })); + } + + private 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); + } + }; + } + + public async registerWebview(id: string, metadata: WebviewMetadata): Promise { + this.webviewMetadata.set(id, metadata); + } + + public unregisterWebview(id: string): void { + this.webviewMetadata.delete(id); + } + + public async updateWebviewMetadata(id: string, metadataDelta: Partial): Promise { + const entry = this.webviewMetadata.get(id); + if (entry) { + this.webviewMetadata.set(id, { + ...entry, + ...metadataDelta, + }); + } + } + + private async handleWebviewRequest(request: Electron.Request, callback: any) { + try { + const uri = URI.parse(request.url); + const entry = WebviewProtocolProvider.validWebviewFilePaths.get(uri.path); + if (typeof entry === 'string') { + let url: string; + if (uri.path.startsWith('/electron-browser')) { + url = require.toUrl(`vs/workbench/contrib/webview/electron-browser/pre/${entry}`); + } else { + url = require.toUrl(`vs/workbench/contrib/webview/browser/pre/${entry}`); + } + return callback(decodeURIComponent(url.replace('file://', ''))); + } + } catch { + // noop + } + callback({ error: -10 /* ACCESS_DENIED - https://cs.chromium.org/chromium/src/net/base/net_error_list.h?l=32 */ }); + } + + private async handleWebviewResourceRequest( + request: Electron.Request, + callback: (stream?: NodeJS.ReadableStream | Electron.StreamProtocolResponse | undefined) => void + ) { + try { + const uri = URI.parse(request.url); + + const id = uri.authority; + const metadata = this.webviewMetadata.get(id); + if (metadata) { + + // Try to further rewrite remote uris so that they go to the resolved server on the main thread + let rewriteUri: undefined | ((uri: URI) => URI); + if (metadata.remoteConnectionData) { + rewriteUri = (uri) => { + if (metadata.remoteConnectionData) { + if (uri.scheme === Schemas.vscodeRemote || (metadata.extensionLocation?.scheme === REMOTE_HOST_SCHEME)) { + return URI.parse(`http://${metadata.remoteConnectionData.host}:${metadata.remoteConnectionData.port}`).with({ + path: '/vscode-remote-resource', + query: `tkn=${metadata.remoteConnectionData.connectionToken}&path=${encodeURIComponent(uri.path)}`, + }); + } + } + return uri; + }; + } + + const fileService = { + readFileStream: async (resource: URI): Promise => { + if (resource.scheme === Schemas.file) { + return (await this.fileService.readFileStream(resource)).value; + } + + // Unknown uri scheme. Try delegating the file read back to the renderer + // process which should have a file system provider registered for the uri. + + const window = this.windowsMainService.getWindowById(metadata.windowId); + if (!window) { + throw new FileOperationError('Could not find window for resource', FileOperationResult.FILE_NOT_FOUND); + } + + const requestId = this.requestIdPool++; + const p = new Promise(resolve => { + this.pendingResourceReads.set(requestId, { resolve }); + }); + + window.send(`vscode:loadWebviewResource-${id}`, requestId, uri); + + const result = await p; + if (!result) { + throw new FileOperationError('Could not read file', FileOperationResult.FILE_NOT_FOUND); + } + + return bufferToStream(result); + } + }; + + const result = await loadLocalResource(uri, { + extensionLocation: metadata.extensionLocation, + roots: metadata.localResourceRoots, + remoteConnectionData: metadata.remoteConnectionData, + rewriteUri, + }, fileService, this.requestService); + + if (result.type === WebviewResourceResponse.Type.Success) { + return callback({ + statusCode: 200, + data: this.streamToNodeReadable(result.stream), + headers: { + 'Content-Type': result.mimeType, + 'Access-Control-Allow-Origin': '*', + } + }); + } + + if (result.type === WebviewResourceResponse.Type.AccessDenied) { + console.error('Webview: Cannot load resource outside of protocol root'); + return callback({ data: null, statusCode: 401 }); + } + } + } catch { + // noop + } + + return callback({ data: null, statusCode: 404 }); + } + + public didLoadResource(requestId: number, content: VSBuffer | undefined) { + const pendingRead = this.pendingResourceReads.get(requestId); + if (!pendingRead) { + throw new Error('Unknown request'); + } + this.pendingResourceReads.delete(requestId); + pendingRead.resolve(content); + } +} diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 60fc99708df..4933ec468bb 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -7,6 +7,8 @@ import { isMacintosh, isLinux, isWeb } from 'vs/base/common/platform'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ThemeType } from 'vs/platform/theme/common/themeService'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; export interface IBaseOpenWindowsOptions { forceReuseWindow?: boolean; @@ -17,6 +19,26 @@ export interface IOpenWindowOptions extends IBaseOpenWindowsOptions { preferNewWindow?: boolean; noRecentEntry?: boolean; + + addMode?: boolean; + + diffMode?: boolean; + gotoLineMode?: boolean; + + waitMarkerFileURI?: URI; +} + +export interface IAddFoldersRequest { + foldersToAdd: UriComponents[]; +} + +export interface IOpenedWindow { + id: number; + workspace?: IWorkspaceIdentifier; + folderUri?: ISingleFolderWorkspaceIdentifier; + title: string; + filename?: string; + dirty: boolean; } export interface IOpenEmptyWindowOptions extends IBaseOpenWindowsOptions { @@ -144,6 +166,9 @@ export interface IPathData { // Specifies if the file should be only be opened if it exists openOnlyIfExists?: boolean; + + // Specifies an optional id to override the editor used to edit the resource, e.g. custom editor. + overrideId?: string; } export interface IOpenFileRequest { @@ -157,7 +182,16 @@ export interface IWindowConfiguration { remoteAuthority?: string; highContrast?: boolean; + defaultThemeType?: ThemeType; filesToOpenOrCreate?: IPath[]; filesToDiff?: IPath[]; } + +/** + * According to Electron docs: `scale := 1.2 ^ level`. + * https://github.com/electron/electron/blob/master/docs/api/web-contents.md#contentssetzoomlevellevel + */ +export function zoomLevelToZoomFactor(zoomLevel = 0): number { + return Math.pow(1.2, zoomLevel); +} diff --git a/src/vs/code/node/activeWindowTracker.ts b/src/vs/platform/windows/electron-main/windowTracker.ts similarity index 86% rename from src/vs/code/node/activeWindowTracker.ts rename to src/vs/platform/windows/electron-main/windowTracker.ts index a7dbeb98bf2..f9c5c9fe055 100644 --- a/src/vs/code/node/activeWindowTracker.ts +++ b/src/vs/platform/windows/electron-main/windowTracker.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { DisposableStore, Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronMainService } from 'vs/platform/electron/electron-main/electronMainService'; export class ActiveWindowManager extends Disposable { @@ -15,7 +15,7 @@ export class ActiveWindowManager extends Disposable { private activeWindowId: number | undefined; - constructor(@IElectronService electronService: IElectronService) { + constructor(@IElectronMainService electronService: IElectronMainService) { super(); // remember last active window id upon events @@ -23,7 +23,7 @@ export class ActiveWindowManager extends Disposable { onActiveWindowChange(this.setActiveWindow, this, this.disposables); // resolve current active window - this.firstActiveWindowIdPromise = createCancelablePromise(() => electronService.getActiveWindowId()); + this.firstActiveWindowIdPromise = createCancelablePromise(() => electronService.getActiveWindowId(-1)); (async () => { try { const windowId = await this.firstActiveWindowIdPromise; diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 5c6facab0a9..8df91e318a8 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -33,9 +33,6 @@ export const enum WindowMode { export interface ICodeWindow extends IDisposable { - readonly onClose: Event; - readonly onDestroy: Event; - readonly whenClosedOrLoaded: Promise; readonly id: number; @@ -99,7 +96,7 @@ export interface IWindowsCountChangedEvent { export interface IWindowsMainService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly onWindowReady: Event; readonly onWindowsCountChanged: Event; diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index cdd6731748d..e441c8c4bb0 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -15,13 +15,12 @@ import { ParsedArgs } from 'vs/platform/environment/node/argv'; import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IStateService } from 'vs/platform/state/node/state'; import { CodeWindow, defaultWindowState } from 'vs/code/electron-main/window'; -import { ipcMain as ipc, screen, BrowserWindow, MessageBoxOptions, Display, app, nativeTheme } from 'electron'; -import { parseLineAndColumnAware } from 'vs/code/node/paths'; +import { screen, BrowserWindow, MessageBoxOptions, Display, app, nativeTheme } from 'electron'; import { ILifecycleMainService, UnloadReason, LifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; -import { IWindowSettings, IPath, isFileToOpen, isWorkspaceToOpen, isFolderToOpen, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; -import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderUri, INativeWindowConfiguration, OpenContext, IAddFoldersRequest, IPathsToWaitFor } from 'vs/platform/windows/node/window'; +import { IWindowSettings, IPath, isFileToOpen, isWorkspaceToOpen, isFolderToOpen, IWindowOpenable, IOpenEmptyWindowOptions, IAddFoldersRequest } from 'vs/platform/windows/common/windows'; +import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderUri, INativeWindowConfiguration, OpenContext, IPathsToWaitFor } from 'vs/platform/windows/node/window'; import { Emitter } from 'vs/base/common/event'; import product from 'vs/platform/product/common/product'; import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent, ICodeWindow, IWindowState as ISingleWindowState, WindowMode, IOpenEmptyConfiguration } from 'vs/platform/windows/electron-main/windows'; @@ -31,7 +30,7 @@ import { IWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, hasWorkspaceFi import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; -import { getComparisonKey, isEqual, normalizePath, originalFSPath, removeTrailingPathSeparator } from 'vs/base/common/resources'; +import { normalizePath, originalFSPath, removeTrailingPathSeparator, extUriBiasedIgnorePathCase } from 'vs/base/common/resources'; import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; import { restoreWindowsState, WindowsStateStorageData, getWindowsStateStoreData } from 'vs/platform/windows/electron-main/windowsStateStorage'; import { getWorkspaceIdentifier, IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; @@ -39,7 +38,7 @@ import { once } from 'vs/base/common/functional'; import { Disposable } from 'vs/base/common/lifecycle'; import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs'; import { withNullAsUndefined } from 'vs/base/common/types'; -import { isWindowsDriveLetter, toSlashes } from 'vs/base/common/extpath'; +import { isWindowsDriveLetter, toSlashes, parseLineAndColumnAware } from 'vs/base/common/extpath'; import { CharCode } from 'vs/base/common/charCode'; export interface IWindowState { @@ -153,7 +152,7 @@ interface IWorkspacePathToOpen { export class WindowsMainService extends Disposable implements IWindowsMainService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private static readonly windowsStateStorageKey = 'windowsState'; @@ -213,19 +212,6 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private registerListeners(): void { - // React to workbench ready events from windows - ipc.on('vscode:workbenchReady', (event: Event, windowId: number) => { - this.logService.trace('IPC#vscode-workbenchReady'); - - const win = this.getWindowById(windowId); - if (win) { - win.setReady(); - - // Event - this._onWindowReady.fire(win); - } - }); - // React to HC color scheme changes (Windows) if (isWindows) { nativeTheme.on('updated', () => { @@ -266,13 +252,13 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // Note that onBeforeShutdown() and onBeforeWindowClose() are fired in different order depending on the OS: // - macOS: since the app will not quit when closing the last window, you will always first get - // the onBeforeShutdown() event followed by N onbeforeWindowClose() events for each window + // the onBeforeShutdown() event followed by N onBeforeWindowClose() events for each window // - other: on other OS, closing the last window will quit the app so the order depends on the // user interaction: closing the last window will first trigger onBeforeWindowClose() // and then onBeforeShutdown(). Using the quit action however will first issue onBeforeShutdown() // and then onBeforeWindowClose(). // - // Here is the behaviour on different OS dependig on action taken (Electron 1.7.x): + // Here is the behavior on different OS depending on action taken (Electron 1.7.x): // // Legend // - quit(N): quit application with N windows opened @@ -334,7 +320,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // 3.) All windows (except extension host) for N >= 2 to support restoreWindows: all or for auto update // - // Carefull here: asking a window for its window state after it has been closed returns bogus values (width: 0, height: 0) + // Careful here: asking a window for its window state after it has been closed returns bogus values (width: 0, height: 0) // so if we ever want to persist the UI state of the last closed window (window count === 1), it has // to come from the stored lastClosedWindowState on Win/Linux at least if (this.getWindowCount() > 1) { @@ -366,7 +352,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic else if (!win.isExtensionDevelopmentHost && (!!win.openedWorkspace || !!win.openedFolderUri)) { this.windowsState.openedWindows.forEach(o => { const sameWorkspace = win.openedWorkspace && o.workspace && o.workspace.id === win.openedWorkspace.id; - const sameFolder = win.openedFolderUri && o.folderUri && isEqual(o.folderUri, win.openedFolderUri); + const sameFolder = win.openedFolderUri && o.folderUri && extUriBiasedIgnorePathCase.isEqual(o.folderUri, win.openedFolderUri); if (sameWorkspace || sameFolder) { o.uiState = state.uiState; @@ -674,7 +660,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } // Handle folders to open (instructed and to restore) - const allFoldersToOpen = arrays.distinct(foldersToOpen, folder => getComparisonKey(folder.folderUri)); // prevent duplicates + const allFoldersToOpen = arrays.distinct(foldersToOpen, folder => extUriBiasedIgnorePathCase.getComparisonKey(folder.folderUri)); // prevent duplicates if (allFoldersToOpen.length > 0) { // Check for existing instances @@ -697,7 +683,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // Open remaining ones allFoldersToOpen.forEach(folderToOpen => { - if (windowsOnFolderPath.some(win => isEqual(win.openedFolderUri, folderToOpen.folderUri))) { + if (windowsOnFolderPath.some(win => extUriBiasedIgnorePathCase.isEqual(win.openedFolderUri, folderToOpen.folderUri))) { return; // ignore folders that are already open } @@ -1023,7 +1009,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic const windowConfig = this.configurationService.getValue('window'); restoreWindows = windowConfig?.restoreWindows || 'all'; // by default restore all windows - if (['all', 'folders', 'one', 'none'].indexOf(restoreWindows) === -1) { + if (!['all', 'folders', 'one', 'none'].includes(restoreWindows)) { restoreWindows = 'all'; // by default restore all windows } } @@ -1131,6 +1117,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic if (remoteAuthority) { const first = anyPath.charCodeAt(0); + // make absolute if (first !== CharCode.Slash) { if (isWindowsDriveLetter(first) && anyPath.charCodeAt(anyPath.charCodeAt(1)) === CharCode.Colon) { @@ -1435,24 +1422,25 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic if (options.forceNewTabbedWindow) { const activeWindow = this.getLastActiveWindow(); if (activeWindow) { - activeWindow.addTabbedWindow(window); + activeWindow.addTabbedWindow(createdWindow); } } // Add to our list of windows - WindowsMainService.WINDOWS.push(window); + WindowsMainService.WINDOWS.push(createdWindow); // Indicate number change via event this._onWindowsCountChanged.fire({ oldCount: WindowsMainService.WINDOWS.length - 1, newCount: WindowsMainService.WINDOWS.length }); // Window Events - once(window.onClose)(() => this.onWindowClosed(createdWindow)); - once(window.onDestroy)(() => this.onBeforeWindowClose(createdWindow)); // try to save state before destroy because close will not fire - window.win.webContents.removeAllListeners('devtools-reload-page'); // remove built in listener so we can handle this on our own - window.win.webContents.on('devtools-reload-page', () => this.lifecycleMainService.reload(createdWindow)); + once(createdWindow.onReady)(() => this._onWindowReady.fire(createdWindow)); + once(createdWindow.onClose)(() => this.onWindowClosed(createdWindow)); + once(createdWindow.onDestroy)(() => this.onBeforeWindowClose(createdWindow)); // try to save state before destroy because close will not fire + createdWindow.win.webContents.removeAllListeners('devtools-reload-page'); // remove built in listener so we can handle this on our own + createdWindow.win.webContents.on('devtools-reload-page', () => this.lifecycleMainService.reload(createdWindow)); // Lifecycle - (this.lifecycleMainService as LifecycleMainService).registerWindow(window); + (this.lifecycleMainService as LifecycleMainService).registerWindow(createdWindow); } // Existing window @@ -1527,7 +1515,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // Known Folder - load from stored settings if (configuration.folderUri) { - const stateForFolder = this.windowsState.openedWindows.filter(o => o.folderUri && isEqual(o.folderUri, configuration.folderUri)).map(o => o.uiState); + const stateForFolder = this.windowsState.openedWindows.filter(o => o.folderUri && extUriBiasedIgnorePathCase.isEqual(o.folderUri, configuration.folderUri)).map(o => o.uiState); if (stateForFolder.length) { return stateForFolder[0]; } diff --git a/src/vs/platform/windows/electron-sandbox/window.ts b/src/vs/platform/windows/electron-sandbox/window.ts new file mode 100644 index 00000000000..5659fa20d7d --- /dev/null +++ b/src/vs/platform/windows/electron-sandbox/window.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 { webFrame } from 'vs/base/parts/sandbox/electron-sandbox/globals'; +import { zoomLevelToZoomFactor } from 'vs/platform/windows/common/windows'; +import { setZoomFactor, setZoomLevel, getZoomLevel } from 'vs/base/browser/browser'; + +/** + * Apply a zoom level to the window. Also sets it in our in-memory + * browser helper so that it can be accessed in non-electron layers. + */ +export function applyZoom(zoomLevel: number): void { + webFrame.setZoomLevel(zoomLevel); + setZoomFactor(zoomLevelToZoomFactor(zoomLevel)); + // Cannot be trusted because the webFrame might take some time + // until it really applies the new zoom level + // See https://github.com/Microsoft/vscode/issues/26151 + setZoomLevel(zoomLevel, false /* isTrusted */); +} + +export function zoomIn(): void { + applyZoom(getZoomLevel() + 1); +} + +export function zoomOut(): void { + applyZoom(getZoomLevel() - 1); +} diff --git a/src/vs/platform/windows/node/window.ts b/src/vs/platform/windows/node/window.ts index cf737ec23b4..7d643a5963c 100644 --- a/src/vs/platform/windows/node/window.ts +++ b/src/vs/platform/windows/node/window.ts @@ -3,25 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IOpenWindowOptions, IWindowConfiguration, IPath, IOpenFileRequest, IPathData } from 'vs/platform/windows/common/windows'; +import { IWindowConfiguration, IPath, IOpenFileRequest, IPathData } from 'vs/platform/windows/common/windows'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as platform from 'vs/base/common/platform'; import * as extpath from 'vs/base/common/extpath'; import { IWorkspaceIdentifier, IResolvedWorkspace, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { isEqual, isEqualOrParent } from 'vs/base/common/resources'; +import { extUriBiasedIgnorePathCase } from 'vs/base/common/resources'; import { LogLevel } from 'vs/platform/log/common/log'; import { ExportData } from 'vs/base/common/performance'; import { ParsedArgs } from 'vs/platform/environment/node/argv'; -export interface IOpenedWindow { - id: number; - workspace?: IWorkspaceIdentifier; - folderUri?: ISingleFolderWorkspaceIdentifier; - title: string; - filename?: string; - dirty: boolean; -} - export const enum OpenContext { // opening when running from the command line @@ -53,10 +44,6 @@ export interface IRunKeybindingInWindowRequest { userSettingsLabel: string; } -export interface IAddFoldersRequest { - foldersToAdd: UriComponents[]; -} - export interface INativeWindowConfiguration extends IWindowConfiguration, ParsedArgs { mainPid: number; @@ -100,13 +87,6 @@ export interface IPathsToWaitForData { waitMarkerFileUri: UriComponents; } -export interface INativeOpenWindowOptions extends IOpenWindowOptions { - diffMode?: boolean; - addMode?: boolean; - gotoLineMode?: boolean; - waitMarkerFileURI?: URI; -} - export interface IWindowContext { openedWorkspace?: IWorkspaceIdentifier; openedFolderUri?: URI; @@ -144,12 +124,12 @@ function findWindowOnFilePath(windows: W[], fileUri: U const resolvedWorkspace = localWorkspaceResolver(workspace); if (resolvedWorkspace) { // workspace could be resolved: It's in the local file system - if (resolvedWorkspace.folders.some(folder => isEqualOrParent(fileUri, folder.uri))) { + if (resolvedWorkspace.folders.some(folder => extUriBiasedIgnorePathCase.isEqualOrParent(fileUri, folder.uri))) { return window; } } else { // use the config path instead - if (isEqualOrParent(fileUri, workspace.configPath)) { + if (extUriBiasedIgnorePathCase.isEqualOrParent(fileUri, workspace.configPath)) { return window; } } @@ -157,7 +137,7 @@ function findWindowOnFilePath(windows: W[], fileUri: U } // Then go with single folder windows that are parent of the provided file path - const singleFolderWindowsOnFilePath = windows.filter(window => window.openedFolderUri && isEqualOrParent(fileUri, window.openedFolderUri)); + const singleFolderWindowsOnFilePath = windows.filter(window => window.openedFolderUri && extUriBiasedIgnorePathCase.isEqualOrParent(fileUri, window.openedFolderUri)); if (singleFolderWindowsOnFilePath.length) { return singleFolderWindowsOnFilePath.sort((a, b) => -(a.openedFolderUri!.path.length - b.openedFolderUri!.path.length))[0]; } @@ -176,7 +156,7 @@ export function findWindowOnWorkspace(windows: W[], wo for (const window of windows) { // match on folder if (isSingleFolderWorkspaceIdentifier(workspace)) { - if (window.openedFolderUri && isEqual(window.openedFolderUri, workspace)) { + if (window.openedFolderUri && extUriBiasedIgnorePathCase.isEqual(window.openedFolderUri, workspace)) { return window; } } @@ -215,12 +195,12 @@ export function findWindowOnWorkspaceOrFolderUri(windo } for (const window of windows) { // check for workspace config path - if (window.openedWorkspace && isEqual(window.openedWorkspace.configPath, uri)) { + if (window.openedWorkspace && extUriBiasedIgnorePathCase.isEqual(window.openedWorkspace.configPath, uri)) { return window; } // check for folder path - if (window.openedFolderUri && isEqual(window.openedFolderUri, uri)) { + if (window.openedFolderUri && extUriBiasedIgnorePathCase.isEqual(window.openedFolderUri, uri)) { return window; } } diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index 911d5a77fc4..0f2e82d0e85 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -14,7 +14,7 @@ import { IWorkspaceFolderProvider } from 'vs/base/common/labels'; export const IWorkspaceContextService = createDecorator('contextService'); export interface IWorkspaceContextService extends IWorkspaceFolderProvider { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * An event which fires on workbench state changes. diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index dc4ef3687f1..d6643801ab6 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -9,7 +9,7 @@ import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/works import { URI, UriComponents } from 'vs/base/common/uri'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; import { extname, isAbsolute } from 'vs/base/common/path'; -import { dirname, resolvePath, isEqualAuthority, isEqualOrParent, relativePath, extname as resourceExtname } from 'vs/base/common/resources'; +import { dirname, resolvePath, isEqualAuthority, relativePath, extname as resourceExtname, extUriBiasedIgnorePathCase } from 'vs/base/common/resources'; import * as jsonEdit from 'vs/base/common/jsonEdit'; import * as json from 'vs/base/common/json'; import { Schemas } from 'vs/base/common/network'; @@ -29,7 +29,7 @@ export const IWorkspacesService = createDecorator('workspace export interface IWorkspacesService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; // Workspaces Management enterWorkspace(path: URI): Promise; @@ -168,6 +168,7 @@ export function toWorkspaceIdentifier(workspace: IWorkspace): IWorkspaceIdentifi id: workspace.id }; } + if (workspace.folders.length === 1) { return workspace.folders[0].uri; } @@ -177,7 +178,7 @@ export function toWorkspaceIdentifier(workspace: IWorkspace): IWorkspaceIdentifi } export function isUntitledWorkspace(path: URI, environmentService: IEnvironmentService): boolean { - return isEqualOrParent(path, environmentService.untitledWorkspacesHome); + return extUriBiasedIgnorePathCase.isEqualOrParent(path, environmentService.untitledWorkspacesHome); } export type IMultiFolderWorkspaceInitializationPayload = IWorkspaceIdentifier; @@ -212,7 +213,6 @@ const SLASH = '/'; * @param useSlashForPath if set, use forward slashes for file paths on windows */ export function getStoredWorkspaceFolder(folderURI: URI, forceAbsolute: boolean, folderName: string | undefined, targetConfigFolderURI: URI, useSlashForPath = !isWindows): IStoredWorkspaceFolder { - if (folderURI.scheme !== targetConfigFolderURI.scheme) { return { name: folderName, uri: folderURI.toString(true) }; } @@ -227,6 +227,7 @@ export function getStoredWorkspaceFolder(folderURI: URI, forceAbsolute: boolean, folderPath = folderPath.replace(/\//g, '\\'); } } else { + // use absolute path if (folderURI.scheme === Schemas.file) { folderPath = folderURI.fsPath; @@ -246,6 +247,7 @@ export function getStoredWorkspaceFolder(folderURI: URI, forceAbsolute: boolean, folderPath = folderURI.path; } } + return { name: folderName, path: folderPath }; } @@ -285,6 +287,7 @@ export function rewriteWorkspaceFileForNewLocation(rawWorkspaceContents: string, // unsaved remote workspaces have the remoteAuthority set. Remove it when no longer nexessary. newContent = jsonEdit.applyEdits(newContent, jsonEdit.removeProperty(newContent, ['remoteAuthority'], formattingOptions)); } + return newContent; } @@ -307,6 +310,7 @@ export function useSlashForPath(storedFolders: IStoredWorkspaceFolder[]): boolea if (isWindows) { return storedFolders.some(folder => isRawFileWorkspaceFolder(folder) && folder.path.indexOf(SLASH) >= 0); } + return true; } @@ -319,22 +323,7 @@ interface ISerializedRecentlyOpened { fileLabels?: Array; // added in 1.33 } -interface ILegacySerializedRecentlyOpened { - workspaces2: Array; // legacy, configPath as file path - workspaces: Array; // legacy (UriComponents was also supported for a few insider builds) - files: string[]; // files as paths -} - interface ISerializedWorkspace { id: string; configURIPath: string; } -interface ILegacySerializedWorkspace { id: string; configPath: string; } - -function isLegacySerializedWorkspace(curr: any): curr is ILegacySerializedWorkspace { - return typeof curr === 'object' && typeof curr['id'] === 'string' && typeof curr['configPath'] === 'string'; -} - -function isUriComponents(curr: any): curr is UriComponents { - return curr && typeof curr['path'] === 'string' && typeof curr['scheme'] === 'string'; -} export type RecentlyOpenedStorageData = object; @@ -351,7 +340,7 @@ export function restoreRecentlyOpened(data: RecentlyOpenedStorageData | undefine } }; - const storedRecents = data as ISerializedRecentlyOpened & ILegacySerializedRecentlyOpened; + const storedRecents = data as ISerializedRecentlyOpened; if (Array.isArray(storedRecents.workspaces3)) { restoreGracefully(storedRecents.workspaces3, (workspace, i) => { const label: string | undefined = (Array.isArray(storedRecents.workspaceLabels) && storedRecents.workspaceLabels[i]) || undefined; @@ -361,27 +350,6 @@ export function restoreRecentlyOpened(data: RecentlyOpenedStorageData | undefine result.workspaces.push({ label, folderUri: URI.parse(workspace) }); } }); - } else if (Array.isArray(storedRecents.workspaces2)) { - restoreGracefully(storedRecents.workspaces2, workspace => { - if (typeof workspace === 'object' && typeof workspace.id === 'string' && typeof workspace.configPath === 'string') { - result.workspaces.push({ workspace: { id: workspace.id, configPath: URI.file(workspace.configPath) } }); - } else if (typeof workspace === 'string') { - result.workspaces.push({ folderUri: URI.parse(workspace) }); - } - }); - } else if (Array.isArray(storedRecents.workspaces)) { - // TODO@martin legacy support can be removed at some point (6 month?) - // format of 1.25 and before - restoreGracefully(storedRecents.workspaces, workspace => { - if (typeof workspace === 'string') { - result.workspaces.push({ folderUri: URI.file(workspace) }); - } else if (isLegacySerializedWorkspace(workspace)) { - result.workspaces.push({ workspace: { id: workspace.id, configPath: URI.file(workspace.configPath) } }); - } else if (isUriComponents(workspace)) { - // added by 1.26-insiders - result.workspaces.push({ folderUri: URI.revive(workspace) }); - } - }); } if (Array.isArray(storedRecents.files2)) { restoreGracefully(storedRecents.files2, (file, i) => { @@ -390,12 +358,6 @@ export function restoreRecentlyOpened(data: RecentlyOpenedStorageData | undefine result.files.push({ label, fileUri: URI.parse(file) }); } }); - } else if (Array.isArray(storedRecents.files)) { - restoreGracefully(storedRecents.files, file => { - if (typeof file === 'string') { - result.files.push({ fileUri: URI.file(file) }); - } - }); } } @@ -416,17 +378,20 @@ export function toStoreData(recents: IRecentlyOpened): RecentlyOpenedStorageData workspaceLabels.push(recent.label || null); hasLabel = hasLabel || !!recent.label; } + if (hasLabel) { serialized.workspaceLabels = workspaceLabels; } hasLabel = false; + const fileLabels: (string | null)[] = []; for (const recent of recents.files) { serialized.files2.push(recent.fileUri.toString()); fileLabels.push(recent.label || null); hasLabel = hasLabel || !!recent.label; } + if (hasLabel) { serialized.fileLabels = fileLabels; } diff --git a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts index 25dcc3d140a..d0c4ea932b3 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts @@ -11,15 +11,14 @@ import { ILogService } from 'vs/platform/log/common/log'; import { getBaseLabel, getPathLabel, splitName } from 'vs/base/common/labels'; import { Event as CommonEvent, Emitter } from 'vs/base/common/event'; import { isWindows, isMacintosh } from 'vs/base/common/platform'; -import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IRecentlyOpened, isRecentWorkspace, isRecentFolder, IRecent, isRecentFile, IRecentFolder, IRecentWorkspace, IRecentFile, toStoreData, restoreRecentlyOpened, RecentlyOpenedStorageData } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IRecentlyOpened, isRecentWorkspace, isRecentFolder, IRecent, isRecentFile, IRecentFolder, IRecentWorkspace, IRecentFile, toStoreData, restoreRecentlyOpened, RecentlyOpenedStorageData, WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; import { ThrottledDelayer } from 'vs/base/common/async'; -import { isEqual as areResourcesEqual, dirname, originalFSPath, basename } from 'vs/base/common/resources'; +import { isEqual, dirname, originalFSPath, basename, extUriBiasedIgnorePathCase } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { getSimpleWorkspaceLabel } from 'vs/platform/label/common/label'; import { exists } from 'vs/base/node/pfs'; import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -30,7 +29,7 @@ export const IWorkspacesHistoryMainService = createDecorator; @@ -57,7 +56,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa private static readonly recentlyOpenedStorageKey = 'openedPathsList'; - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _onRecentlyOpenedChange = new Emitter(); readonly onRecentlyOpenedChange: CommonEvent = this._onRecentlyOpenedChange.event; @@ -152,8 +151,8 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa removeRecentlyOpened(toRemove: URI[]): void { const keep = (recent: IRecent) => { const uri = location(recent); - for (const r of toRemove) { - if (areResourcesEqual(r, uri)) { + for (const resource of toRemove) { + if (isEqual(resource, uri)) { return false; } } @@ -207,8 +206,8 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa if (loc.scheme === Schemas.file) { const filePath = originalFSPath(loc); if ( - WorkspacesHistoryMainService.COMMON_FILES_FILTER.indexOf(basename(loc)) !== -1 || // skip some well known file entries - workspaceEntries.indexOf(filePath) !== -1 // prefer a workspace entry over a file entry (e.g. for .code-workspace) + WorkspacesHistoryMainService.COMMON_FILES_FILTER.includes(basename(loc)) || // skip some well known file entries + workspaceEntries.includes(filePath) // prefer a workspace entry over a file entry (e.g. for .code-workspace) ) { continue; } @@ -356,7 +355,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa name: nls.localize('recentFolders', "Recent Workspaces"), items: arrays.coalesce(this.getRecentlyOpened().workspaces.slice(0, 7 /* limit number of entries here */).map(recent => { const workspace = isRecentWorkspace(recent) ? recent.workspace : recent.folderUri; - const title = recent.label ? splitName(recent.label).name : getSimpleWorkspaceLabel(workspace, this.environmentService.untitledWorkspacesHome); + const title = recent.label ? splitName(recent.label).name : this.getSimpleWorkspaceLabel(workspace, this.environmentService.untitledWorkspacesHome); let description; let args; @@ -392,6 +391,24 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa this.logService.warn('#setJumpList', error); // since setJumpList is relatively new API, make sure to guard for errors } } + + private getSimpleWorkspaceLabel(workspace: IWorkspaceIdentifier | URI, workspaceHome: URI): string { + if (isSingleFolderWorkspaceIdentifier(workspace)) { + return basename(workspace); + } + + // Workspace: Untitled + if (extUriBiasedIgnorePathCase.isEqualOrParent(workspace.configPath, workspaceHome)) { + return nls.localize('untitledWorkspace', "Untitled (Workspace)"); + } + + let filename = basename(workspace.configPath); + if (filename.endsWith(WORKSPACE_EXTENSION)) { + filename = filename.substr(0, filename.length - WORKSPACE_EXTENSION.length - 1); + } + + return nls.localize('workspaceName', "{0} (Workspace)", filename); + } } function location(recent: IRecent): URI { @@ -411,9 +428,9 @@ function indexOfWorkspace(arr: IRecent[], candidate: IWorkspaceIdentifier): numb } function indexOfFolder(arr: IRecent[], candidate: ISingleFolderWorkspaceIdentifier): number { - return arr.findIndex(folder => isRecentFolder(folder) && areResourcesEqual(folder.folderUri, candidate)); + return arr.findIndex(folder => isRecentFolder(folder) && isEqual(folder.folderUri, candidate)); } function indexOfFile(arr: IRecentFile[], candidate: URI): number { - return arr.findIndex(file => areResourcesEqual(file.fileUri, candidate)); + return arr.findIndex(file => isEqual(file.fileUri, candidate)); } diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts index a742475e6d4..18151ec9c96 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts @@ -18,7 +18,7 @@ import { toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { Disposable } from 'vs/base/common/lifecycle'; -import { originalFSPath, joinPath, isEqual, basename } from 'vs/base/common/resources'; +import { originalFSPath, joinPath, basename, extUriBiasedIgnorePathCase } from 'vs/base/common/resources'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { localize } from 'vs/nls'; @@ -38,7 +38,7 @@ export interface IWorkspaceEnteredEvent { export interface IWorkspacesMainService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly onUntitledWorkspaceDeleted: Event; readonly onWorkspaceEntered: Event; @@ -65,7 +65,7 @@ export interface IStoredWorkspace { export class WorkspacesMainService extends Disposable implements IWorkspacesMainService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly untitledWorkspacesHome: URI; // local URI that contains all untitled workspaces @@ -134,7 +134,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain if (storedWorkspace && Array.isArray(storedWorkspace.folders)) { storedWorkspace.folders = storedWorkspace.folders.filter(folder => isStoredWorkspaceFolder(folder)); } else { - throw new Error(`${path.toString()} looks like an invalid workspace file.`); + throw new Error(`${path.toString(true)} looks like an invalid workspace file.`); } return storedWorkspace; @@ -185,8 +185,8 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain }; } - getWorkspaceIdentifier(configPath: URI): Promise { - return Promise.resolve(getWorkspaceIdentifier(configPath)); + async getWorkspaceIdentifier(configPath: URI): Promise { + return getWorkspaceIdentifier(configPath); } isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean { @@ -205,9 +205,8 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain this._onUntitledWorkspaceDeleted.fire(workspace); } - deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise { + async deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise { this.deleteUntitledWorkspaceSync(workspace); - return Promise.resolve(); } private doDeleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void { @@ -218,7 +217,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain rimrafSync(dirname(configPath)); // Mark Workspace Storage to be deleted - const workspaceStoragePath = join(this.environmentService.workspaceStorageHome, workspace.id); + const workspaceStoragePath = join(this.environmentService.workspaceStorageHome.fsPath, workspace.id); if (existsSync(workspaceStoragePath)) { writeFileSync(join(workspaceStoragePath, 'obsolete'), ''); } @@ -274,7 +273,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain return true; } - if (window.openedWorkspace && isEqual(window.openedWorkspace.configPath, path)) { + if (window.openedWorkspace && extUriBiasedIgnorePathCase.isEqual(window.openedWorkspace.configPath, path)) { return false; // window is already opened on a workspace with that path } diff --git a/src/vs/platform/workspaces/electron-main/workspacesService.ts b/src/vs/platform/workspaces/electron-main/workspacesService.ts index c5a12d23e58..8d1d22d9e02 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesService.ts @@ -13,7 +13,7 @@ import { IBackupMainService } from 'vs/platform/backup/electron-main/backup'; export class WorkspacesService implements AddFirstParameterToFunctions /* only methods, not events */, number /* window ID */> { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService, diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesHistoryStorage.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesHistoryStorage.test.ts index ecdc561c986..c1637320630 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesHistoryStorage.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesHistoryStorage.test.ts @@ -98,92 +98,6 @@ suite('History Storage', () => { assertRestoring(ro, 'labels'); }); - test('open 1_25', () => { - const v1_25_win = `{ - "workspaces": [ - { - "id": "2fa677dbdf5f771e775af84dea9feaea", - "configPath": "C:\\\\workspaces\\\\testing\\\\test.code-workspace" - }, - "C:\\\\workspaces\\\\testing\\\\test-ext", - { - "id": "d87a0241f8abc86b95c4e5481ebcbf56", - "configPath": "C:\\\\workspaces\\\\test.code-workspace" - } - ], - "files": [ - "C:\\\\workspaces\\\\test.code-workspace", - "C:\\\\workspaces\\\\testing\\\\test-ext\\\\.gitignore" - ] - }`; - - let actual = restoreRecentlyOpened(JSON.parse(v1_25_win), new NullLogService()); - let expected: IRecentlyOpened = { - files: [{ fileUri: URI.file('C:\\workspaces\\test.code-workspace') }, { fileUri: URI.file('C:\\workspaces\\testing\\test-ext\\.gitignore') }], - workspaces: [ - { workspace: { id: '2fa677dbdf5f771e775af84dea9feaea', configPath: URI.file('C:\\workspaces\\testing\\test.code-workspace') } }, - { folderUri: URI.file('C:\\workspaces\\testing\\test-ext') }, - { workspace: { id: 'd87a0241f8abc86b95c4e5481ebcbf56', configPath: URI.file('C:\\workspaces\\test.code-workspace') } } - ] - }; - - assertEqualRecentlyOpened(actual, expected, 'v1_31_win'); - }); - - test('open 1_31', () => { - const v1_31_win = `{ - "workspaces2": [ - "file:///c%3A/workspaces/testing/test-ext", - "file:///c%3A/WINDOWS/system32", - { - "id": "d87a0241f8abc86b95c4e5481ebcbf56", - "configPath": "c:\\\\workspaces\\\\test.code-workspace" - } - ], - "files2": [ - "file:///c%3A/workspaces/vscode/.yarnrc" - ] - }`; - - let actual = restoreRecentlyOpened(JSON.parse(v1_31_win), new NullLogService()); - let expected: IRecentlyOpened = { - files: [{ fileUri: URI.parse('file:///c%3A/workspaces/vscode/.yarnrc') }], - workspaces: [ - { folderUri: URI.parse('file:///c%3A/workspaces/testing/test-ext') }, - { folderUri: URI.parse('file:///c%3A/WINDOWS/system32') }, - { workspace: { id: 'd87a0241f8abc86b95c4e5481ebcbf56', configPath: URI.file('c:\\workspaces\\test.code-workspace') } } - ] - }; - - assertEqualRecentlyOpened(actual, expected, 'v1_31_win'); - }); - - test('open 1_32', () => { - const v1_32 = `{ - "workspaces3": [ - { - "id": "53b714b46ef1a2d4346568b4f591028c", - "configURIPath": "file:///home/user/workspaces/testing/custom.code-workspace" - }, - "file:///home/user/workspaces/testing/folding" - ], - "files2": [ - "file:///home/user/.config/code-oss-dev/storage.json" - ] - }`; - - let windowsState = restoreRecentlyOpened(JSON.parse(v1_32), new NullLogService()); - let expected: IRecentlyOpened = { - files: [{ fileUri: URI.parse('file:///home/user/.config/code-oss-dev/storage.json') }], - workspaces: [ - { workspace: { id: '53b714b46ef1a2d4346568b4f591028c', configPath: URI.parse('file:///home/user/workspaces/testing/custom.code-workspace') } }, - { folderUri: URI.parse('file:///home/user/workspaces/testing/folding') } - ] - }; - - assertEqualRecentlyOpened(windowsState, expected, 'v1_32'); - }); - test('open 1_33', () => { const v1_33 = `{ "workspaces3": [ diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts index 2356a099f18..ba4ea5b5268 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts @@ -19,12 +19,12 @@ import { isWindows } from 'vs/base/common/platform'; import { normalizeDriveLetter } from 'vs/base/common/labels'; import { dirname, joinPath } from 'vs/base/common/resources'; import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs'; -import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs'; +import { INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { IBackupMainService, IWorkspaceBackupInfo } from 'vs/platform/backup/electron-main/backup'; import { IEmptyWindowBackupInfo } from 'vs/platform/backup/node/backup'; export class TestDialogMainService implements IDialogMainService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; pickFileFolder(options: INativeOpenDialogOptions, window?: Electron.BrowserWindow | undefined): Promise { throw new Error('Method not implemented.'); @@ -57,7 +57,7 @@ export class TestDialogMainService implements IDialogMainService { export class TestBackupMainService implements IBackupMainService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; isHotExitEnabled(): boolean { throw new Error('Method not implemented.'); diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index d412b4e8a56..60fe1cf9379 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -1290,7 +1290,7 @@ declare module 'vscode' { * The path segments are normalized in the following ways: * - sequences of path separators (`/` or `\`) are replaced with a single separator * - for `file`-uris on windows, the backslash-character (`\`) is considered a path-separator - * - the `..`-segment denotes the parent segment, the `.` denotes the current segement + * - the `..`-segment denotes the parent segment, the `.` denotes the current segment * - paths have a root which always remains, for instance on windows drive-letters are roots * so that is true: `joinPath(Uri.file('file:///c:/root'), '../../other').fsPath === 'c:/other'` * @@ -1730,6 +1730,14 @@ declare module 'vscode' { * ``` */ filters?: { [name: string]: string[] }; + + /** + * Dialog title. + * + * This parameter might be ignored, as not all operating systems display a title on open dialogs + * (for example, macOS). + */ + title?: string; } /** @@ -1757,6 +1765,14 @@ declare module 'vscode' { * ``` */ filters?: { [name: string]: string[] }; + + /** + * Dialog title. + * + * This parameter might be ignored, as not all operating systems display a title on save dialogs + * (for example, macOS). + */ + title?: string; } /** @@ -2216,7 +2232,7 @@ declare module 'vscode' { } /** - * Metadata about the type of code actions that a [CodeActionProvider](#CodeActionProvider) providers. + * Metadata about the type of code actions that a [CodeActionProvider](#CodeActionProvider) provides. */ export interface CodeActionProviderMetadata { /** @@ -2228,6 +2244,40 @@ declare module 'vscode' { * such as `[CodeActionKind.Refactor.Extract.append('function'), CodeActionKind.Refactor.Extract.append('constant'), ...]`. */ readonly providedCodeActionKinds?: ReadonlyArray; + + /** + * Static documentation for a class of code actions. + * + * Documentation from the provider is shown in the code actions menu if either: + * + * - Code actions of `kind` are requested by VS Code. In this case, VS Code will show the documentation that + * most closely matches the requested code action kind. For example, if a provider has documentation for + * both `Refactor` and `RefactorExtract`, when the user requests code actions for `RefactorExtract`, + * VS Code will use the documentation for `RefactorExtract` instead of the documentation for `Refactor`. + * + * - Any code actions of `kind` are returned by the provider. + * + * At most one documentation entry will be shown per provider. + */ + readonly documentation?: ReadonlyArray<{ + /** + * The kind of the code action being documented. + * + * If the kind is generic, such as `CodeActionKind.Refactor`, the documentation will be shown whenever any + * refactorings are returned. If the kind if more specific, such as `CodeActionKind.RefactorExtract`, the + * documentation will only be shown when extract refactoring code actions are returned. + */ + readonly kind: CodeActionKind; + + /** + * Command that displays the documentation to the user. + * + * This can display the documentation directly in VS Code or open a website using [`env.openExternal`](#env.openExternal); + * + * The title of this documentation code action is taken from [`Command.title`](#Command.title) + */ + readonly command: Command; + }>; } /** @@ -2270,7 +2320,7 @@ declare module 'vscode' { * A code lens provider adds [commands](#Command) to source text. The commands will be shown * as dedicated horizontal lines in between the source text. */ - export interface CodeLensProvider { + export interface CodeLensProvider { /** * An optional event to signal that the code lenses from this provider have changed. @@ -2414,6 +2464,11 @@ declare module 'vscode' { */ isTrusted?: boolean; + /** + * Indicates that this markdown string can contain [ThemeIcons](#ThemeIcon), e.g. `$(zap)`. + */ + readonly supportThemeIcons?: boolean; + /** * Creates a new markdown string with the given value. * @@ -2645,7 +2700,6 @@ declare module 'vscode' { TypeParameter = 25 } - /** * Symbol tags are extra annotations that tweak the rendering of a symbol. */ @@ -2798,7 +2852,7 @@ declare module 'vscode' { * The workspace symbol provider interface defines the contract between extensions and * the [symbol search](https://code.visualstudio.com/docs/editor/editingevolved#_open-symbol-by-name)-feature. */ - export interface WorkspaceSymbolProvider { + export interface WorkspaceSymbolProvider { /** * Project-wide search for a symbol matching the given query string. @@ -3055,7 +3109,6 @@ declare module 'vscode' { */ renameFile(oldUri: Uri, newUri: Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean }, metadata?: WorkspaceEditEntryMetadata): void; - /** * Get all text edits grouped by resource. * @@ -3858,7 +3911,7 @@ declare module 'vscode' { * Represents a collection of [completion items](#CompletionItem) to be presented * in the editor. */ - export class CompletionList { + export class CompletionList { /** * This list is not complete. Further typing should result in recomputing @@ -3931,7 +3984,7 @@ declare module 'vscode' { * Providers are asked for completions either explicitly by a user gesture or -depending on the configuration- * implicitly when typing words or trigger characters. */ - export interface CompletionItemProvider { + export interface CompletionItemProvider { /** * Provide completion items for the given position and document. @@ -3952,9 +4005,13 @@ declare module 'vscode' { * * The editor will only resolve a completion item once. * - * *Note* that accepting a completion item will not wait for it to be resolved. Because of that [`insertText`](#CompletionItem.insertText), - * [`additionalTextEdits`](#CompletionItem.additionalTextEdits), and [`command`](#CompletionItem.command) should not - * be changed when resolving an item. + * *Note* that this function is called when completion items are already showing in the UI or when an item has been + * selected for insertion. Because of that, no property that changes the presentation (label, sorting, filtering etc) + * or the (primary) insert behaviour ([insertText](#CompletionItem.insertText)) can be changed. + * + * This function may fill in [additionalTextEdits](#CompletionItem.additionalTextEdits). However, that means an item might be + * inserted *before* resolving is done and in that case the editor will do a best effort to still apply those additional + * text edits. * * @param item A completion item currently active in the UI. * @param token A cancellation token. @@ -3964,7 +4021,6 @@ declare module 'vscode' { resolveCompletionItem?(item: T, token: CancellationToken): ProviderResult; } - /** * A document link is a range in a text document that links to an internal or external resource, like another * text document or a web site. @@ -4003,7 +4059,7 @@ declare module 'vscode' { * The document link provider defines the contract between extensions and feature of showing * links in the editor. */ - export interface DocumentLinkProvider { + export interface DocumentLinkProvider { /** * Provide links for the given document. Note that the editor ships with a default provider that detects @@ -4375,7 +4431,7 @@ declare module 'vscode' { } /** - * The call hierarchy provider interface describes the constract between extensions + * The call hierarchy provider interface describes the contract between extensions * and the call hierarchy feature which allows to browse calls and caller of function, * methods, constructor etc. */ @@ -5182,6 +5238,24 @@ declare module 'vscode' { dispose(): void; } + /** + * Accessibility information which controls screen reader behavior. + */ + export interface AccessibilityInformation { + /** + * Label to be read out by a screen reader once the item has focus. + */ + label: string; + + /** + * Role of the widget which defines how a screen reader interacts with it. + * The role should be set in special cases when for example a tree-like element behaves like a checkbox. + * If role is not specified VS Code will pick the appropriate role automatically. + * More about aria roles can be found here https://w3c.github.io/aria/#widget_roles + */ + role?: string; + } + /** * Represents the alignment of status bar items. */ @@ -5245,6 +5319,11 @@ declare module 'vscode' { */ command: string | Command | undefined; + /** + * Accessibility information used when screen reader interacts with this StatusBar item + */ + accessibilityInformation?: AccessibilityInformation; + /** * Shows the entry in the status bar. */ @@ -5414,6 +5493,30 @@ declare module 'vscode' { activate(): Thenable; } + /** + * The ExtensionMode is provided on the `ExtensionContext` and indicates the + * mode the specific extension is running in. + */ + export enum ExtensionMode { + /** + * The extension is installed normally (for example, from the marketplace + * or VSIX) in VS Code. + */ + Production = 1, + + /** + * The extension is running from an `--extensionDevelopmentPath` provided + * when launching VS Code. + */ + Development = 2, + + /** + * The extension is running from an `--extensionTestsPath` and + * the extension host is running unit tests. + */ + Test = 3, + } + /** * An extension context is a collection of utilities private to an * extension. @@ -5491,6 +5594,13 @@ declare module 'vscode' { * the parent directory is guaranteed to be existent. */ readonly logPath: string; + + /** + * The mode the extension is running in. This is specific to the current + * extension. One extension may be in `ExtensionMode.Development` while + * other extensions in the host run in `ExtensionMode.Release`. + */ + readonly extensionMode: ExtensionMode; } /** @@ -5657,7 +5767,6 @@ declare module 'vscode' { private constructor(id: string, label: string); } - /** * A structure that defines a task kind in the system. * The value must be JSON-stringifyable. @@ -5922,7 +6031,7 @@ declare module 'vscode' { */ export enum TaskScope { /** - * The task is a global task. Global tasks are currrently not supported. + * The task is a global task. Global tasks are currently not supported. */ Global = 1, @@ -6036,7 +6145,7 @@ declare module 'vscode' { * A task provider allows to add tasks to the task service. * A task provider is registered via #tasks.registerTaskProvider. */ - export interface TaskProvider { + export interface TaskProvider { /** * Provides tasks. * @param token A cancellation token. @@ -6176,6 +6285,10 @@ declare module 'vscode' { * Executes a task that is managed by VS Code. The returned * task execution can be used to terminate the task. * + * @throws When running a ShellExecution or a ProcessExecution + * task in an environment where a new process cannot be started. + * In such an environment, only CustomExecution tasks can be run. + * * @param task the task to execute */ export function executeTask(task: Task): Thenable; @@ -6610,7 +6723,7 @@ declare module 'vscode' { readonly enableCommandUris?: boolean; /** - * Root paths from which the webview can load local (filesystem) resources using the `vscode-resource:` scheme. + * Root paths from which the webview can load local (filesystem) resources using uris from `asWebviewUri` * * Default to the root folders of the current workspace plus the extension's install directory. * @@ -6896,12 +7009,15 @@ declare module 'vscode' { * This is called when a user first opens a resource for a `CustomTextEditorProvider`, or if they reopen an * existing editor using this `CustomTextEditorProvider`. * - * To resolve a custom editor, the provider must fill in its initial html content and hook up all - * the event listeners it is interested it. The provider can also hold onto the `WebviewPanel` to use later, - * for example in a command. See [`WebviewPanel`](#WebviewPanel) for additional details. * * @param document Document for the resource to resolve. - * @param webviewPanel Webview to resolve. + * + * @param webviewPanel The webview panel used to display the editor UI for this resource. + * + * During resolve, the provider must fill in the initial html for the content webview panel and hook up all + * the event listeners on it that it is interested in. The provider can also hold onto the `WebviewPanel` to + * use later for example in a command. See [`WebviewPanel`](#WebviewPanel) for additional details. + * * @param token A cancellation token that indicates the result is no longer needed. * * @return Thenable indicating that the custom editor has been resolved. @@ -6909,6 +7025,285 @@ declare module 'vscode' { resolveCustomTextEditor(document: TextDocument, webviewPanel: WebviewPanel, token: CancellationToken): Thenable | void; } + /** + * Represents a custom document used by a [`CustomEditorProvider`](#CustomEditorProvider). + * + * Custom documents are only used within a given `CustomEditorProvider`. The lifecycle of a `CustomDocument` is + * managed by VS Code. When no more references remain to a `CustomDocument`, it is disposed of. + */ + interface CustomDocument { + /** + * The associated uri for this document. + */ + readonly uri: Uri; + + /** + * Dispose of the custom document. + * + * This is invoked by VS Code when there are no more references to a given `CustomDocument` (for example when + * all editors associated with the document have been closed.) + */ + dispose(): void; + } + + /** + * Event triggered by extensions to signal to VS Code that an edit has occurred on an [`CustomDocument`](#CustomDocument). + * + * @see [`CustomDocumentProvider.onDidChangeCustomDocument`](#CustomDocumentProvider.onDidChangeCustomDocument). + */ + interface CustomDocumentEditEvent { + + /** + * The document that the edit is for. + */ + readonly document: T; + + /** + * Undo the edit operation. + * + * This is invoked by VS Code when the user undoes this edit. To implement `undo`, your + * extension should restore the document and editor to the state they were in just before this + * edit was added to VS Code's internal edit stack by `onDidChangeCustomDocument`. + */ + undo(): Thenable | void; + + /** + * Redo the edit operation. + * + * This is invoked by VS Code when the user redoes this edit. To implement `redo`, your + * extension should restore the document and editor to the state they were in just after this + * edit was added to VS Code's internal edit stack by `onDidChangeCustomDocument`. + */ + redo(): Thenable | void; + + /** + * Display name describing the edit. + * + * This will be shown to users in the UI for undo/redo operations. + */ + readonly label?: string; + } + + /** + * Event triggered by extensions to signal to VS Code that the content of a [`CustomDocument`](#CustomDocument) + * has changed. + * + * @see [`CustomDocumentProvider.onDidChangeCustomDocument`](#CustomDocumentProvider.onDidChangeCustomDocument). + */ + interface CustomDocumentContentChangeEvent { + /** + * The document that the change is for. + */ + readonly document: T; + } + + /** + * A backup for an [`CustomDocument`](#CustomDocument). + */ + interface CustomDocumentBackup { + /** + * Unique identifier for the backup. + * + * This id is passed back to your extension in `openCustomDocument` when opening a custom editor from a backup. + */ + readonly id: string; + + /** + * Delete the current backup. + * + * This is called by VS Code when it is clear the current backup is no longer needed, such as when a new backup + * is made or when the file is saved. + */ + delete(): void; + } + + /** + * Additional information used to implement [`CustomEditableDocument.backup`](#CustomEditableDocument.backup). + */ + interface CustomDocumentBackupContext { + /** + * Suggested file location to write the new backup. + * + * Note that your extension is free to ignore this and use its own strategy for backup. + * + * If the editor is for a resource from the current workspace, `destination` will point to a file inside + * `ExtensionContext.storagePath`. The parent folder of `destination` may not exist, so make sure to created it + * before writing the backup to this location. + */ + readonly destination: Uri; + } + + /** + * Additional information about the opening custom document. + */ + interface CustomDocumentOpenContext { + /** + * The id of the backup to restore the document from or `undefined` if there is no backup. + * + * If this is provided, your extension should restore the editor from the backup instead of reading the file + * from the user's workspace. + */ + readonly backupId?: string; + } + + /** + * Provider for readonly custom editors that use a custom document model. + * + * Custom editors use [`CustomDocument`](#CustomDocument) as their document model instead of a [`TextDocument`](#TextDocument). + * + * You should use this type of custom editor when dealing with binary files or more complex scenarios. For simple + * text based documents, use [`CustomTextEditorProvider`](#CustomTextEditorProvider) instead. + * + * @param T Type of the custom document returned by this provider. + */ + export interface CustomReadonlyEditorProvider { + + /** + * Create a new document for a given resource. + * + * `openCustomDocument` is called when the first time an editor for a given resource is opened. The opened + * document is then passed to `resolveCustomEditor` so that the editor can be shown to the user. + * + * Already opened `CustomDocument` are re-used if the user opened additional editors. When all editors for a + * given resource are closed, the `CustomDocument` is disposed of. Opening an editor at this point will + * trigger another call to `openCustomDocument`. + * + * @param uri Uri of the document to open. + * @param openContext Additional information about the opening custom document. + * @param token A cancellation token that indicates the result is no longer needed. + * + * @return The custom document. + */ + openCustomDocument(uri: Uri, openContext: CustomDocumentOpenContext, token: CancellationToken): Thenable | T; + + /** + * Resolve a custom editor for a given resource. + * + * This is called whenever the user opens a new editor for this `CustomEditorProvider`. + * + * @param document Document for the resource being resolved. + * + * @param webviewPanel The webview panel used to display the editor UI for this resource. + * + * During resolve, the provider must fill in the initial html for the content webview panel and hook up all + * the event listeners on it that it is interested in. The provider can also hold onto the `WebviewPanel` to + * use later for example in a command. See [`WebviewPanel`](#WebviewPanel) for additional details. + * + * @param token A cancellation token that indicates the result is no longer needed. + * + * @return Optional thenable indicating that the custom editor has been resolved. + */ + resolveCustomEditor(document: T, webviewPanel: WebviewPanel, token: CancellationToken): Thenable | void; + } + + /** + * Provider for editable custom editors that use a custom document model. + * + * Custom editors use [`CustomDocument`](#CustomDocument) as their document model instead of a [`TextDocument`](#TextDocument). + * This gives extensions full control over actions such as edit, save, and backup. + * + * You should use this type of custom editor when dealing with binary files or more complex scenarios. For simple + * text based documents, use [`CustomTextEditorProvider`](#CustomTextEditorProvider) instead. + * + * @param T Type of the custom document returned by this provider. + */ + export interface CustomEditorProvider extends CustomReadonlyEditorProvider { + /** + * Signal that an edit has occurred inside a custom editor. + * + * This event must be fired by your extension whenever an edit happens in a custom editor. An edit can be + * anything from changing some text, to cropping an image, to reordering a list. Your extension is free to + * define what an edit is and what data is stored on each edit. + * + * Firing `onDidChange` causes VS Code to mark the editors as being dirty. This is cleared when the user either + * saves or reverts the file. + * + * Editors that support undo/redo must fire a `CustomDocumentEditEvent` whenever an edit happens. This allows + * users to undo and redo the edit using VS Code's standard VS Code keyboard shortcuts. VS Code will also mark + * the editor as no longer being dirty if the user undoes all edits to the last saved state. + * + * Editors that support editing but cannot use VS Code's standard undo/redo mechanism must fire a `CustomDocumentContentChangeEvent`. + * The only way for a user to clear the dirty state of an editor that does not support undo/redo is to either + * `save` or `revert` the file. + * + * An editor should only ever fire `CustomDocumentEditEvent` events, or only ever fire `CustomDocumentContentChangeEvent` events. + */ + readonly onDidChangeCustomDocument: Event> | Event>; + + /** + * Save a custom document. + * + * This method is invoked by VS Code when the user saves a custom editor. This can happen when the user + * triggers save while the custom editor is active, by commands such as `save all`, or by auto save if enabled. + * + * To implement `save`, the implementer must persist the custom editor. This usually means writing the + * file data for the custom document to disk. After `save` completes, any associated editor instances will + * no longer be marked as dirty. + * + * @param document Document to save. + * @param cancellation Token that signals the save is no longer required (for example, if another save was triggered). + * + * @return Thenable signaling that saving has completed. + */ + saveCustomDocument(document: T, cancellation: CancellationToken): Thenable; + + /** + * Save a custom document to a different location. + * + * This method is invoked by VS Code when the user triggers 'save as' on a custom editor. The implementer must + * persist the custom editor to `destination`. + * + * When the user accepts save as, the current editor is be replaced by an non-dirty editor for the newly saved file. + * + * @param document Document to save. + * @param destination Location to save to. + * @param cancellation Token that signals the save is no longer required. + * + * @return Thenable signaling that saving has completed. + */ + saveCustomDocumentAs(document: T, destination: Uri, cancellation: CancellationToken): Thenable; + + /** + * Revert a custom document to its last saved state. + * + * This method is invoked by VS Code when the user triggers `File: Revert File` in a custom editor. (Note that + * this is only used using VS Code's `File: Revert File` command and not on a `git revert` of the file). + * + * To implement `revert`, the implementer must make sure all editor instances (webviews) for `document` + * are displaying the document in the same state is saved in. This usually means reloading the file from the + * workspace. + * + * @param document Document to revert. + * @param cancellation Token that signals the revert is no longer required. + * + * @return Thenable signaling that the change has completed. + */ + revertCustomDocument(document: T, cancellation: CancellationToken): Thenable; + + /** + * Back up a dirty custom document. + * + * Backups are used for hot exit and to prevent data loss. Your `backup` method should persist the resource in + * its current state, i.e. with the edits applied. Most commonly this means saving the resource to disk in + * the `ExtensionContext.storagePath`. When VS Code reloads and your custom editor is opened for a resource, + * your extension should first check to see if any backups exist for the resource. If there is a backup, your + * extension should load the file contents from there instead of from the resource in the workspace. + * + * `backup` is triggered approximately one second after the the user stops editing the document. If the user + * rapidly edits the document, `backup` will not be invoked until the editing stops. + * + * `backup` is not invoked when `auto save` is enabled (since auto save already persists the resource). + * + * @param document Document to backup. + * @param context Information that can be used to backup the document. + * @param cancellation Token that signals the current backup since a new backup is coming in. It is up to your + * extension to decided how to respond to cancellation. If for example your extension is backing up a large file + * in an operation that takes time to complete, your extension may decide to finish the ongoing backup rather + * than cancelling it to ensure that VS Code has some valid backup. + */ + backupCustomDocument(document: T, context: CustomDocumentBackupContext, cancellation: CancellationToken): Thenable; + } + /** * The clipboard provides read and write access to the system's clipboard. */ @@ -6955,6 +7350,9 @@ declare module 'vscode' { /** * The application root folder from which the editor is running. + * + * *Note* that the value is the empty string when running in an + * environment that has no representation of an application root folder. */ export const appRoot: string; @@ -6997,7 +7395,8 @@ declare module 'vscode' { /** * The detected default shell for the extension host, this is overridden by the - * `terminal.integrated.shell` setting for the extension host's platform. + * `terminal.integrated.shell` setting for the extension host's platform. Note that in + * environments that do not support a shell the value is the empty string. */ export const shell: string; @@ -7036,7 +7435,7 @@ declare module 'vscode' { * * If the extension is running remotely, this function automatically establishes a port forwarding tunnel * from the local machine to `target` on the remote and returns a local uri to the tunnel. The lifetime of - * the port fowarding tunnel is managed by VS Code and the tunnel can be closed by the user. + * the port forwarding tunnel is managed by VS Code and the tunnel can be closed by the user. * * *Note* that uris passed through `openExternal` are automatically resolved and you should not call `asExternalUri` on them. * @@ -7521,7 +7920,7 @@ declare module 'vscode' { * @param options Options that control the dialog. * @returns A promise that resolves to the selected resources or `undefined`. */ - export function showOpenDialog(options: OpenDialogOptions): Thenable; + export function showOpenDialog(options?: OpenDialogOptions): Thenable; /** * Shows a file save dialog to the user which allows to select a file @@ -7530,7 +7929,7 @@ declare module 'vscode' { * @param options Options that control the dialog. * @returns A promise that resolves to the selected resource or `undefined`. */ - export function showSaveDialog(options: SaveDialogOptions): Thenable; + export function showSaveDialog(options?: SaveDialogOptions): Thenable; /** * Opens an input box to ask the user for input. @@ -7671,6 +8070,7 @@ declare module 'vscode' { * allows specifying shell args in * [command-line format](https://msdn.microsoft.com/en-au/08dfcab2-eb6e-49a4-80eb-87d4076c98c6). * @return A new Terminal. + * @throws When running in an environment where a new process cannot be started. */ export function createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): Terminal; @@ -7679,6 +8079,7 @@ declare module 'vscode' { * * @param options A TerminalOptions object describing the characteristics of the new terminal. * @return A new Terminal. + * @throws When running in an environment where a new process cannot be started. */ export function createTerminal(options: TerminalOptions): Terminal; @@ -7750,7 +8151,8 @@ declare module 'vscode' { * Register a provider for custom editors for the `viewType` contributed by the `customEditors` extension point. * * When a custom editor is opened, VS Code fires an `onCustomEditor:viewType` activation event. Your extension - * must register a [`CustomTextEditorProvider`](#CustomTextEditorProvider) for `viewType` as part of activation. + * must register a [`CustomTextEditorProvider`](#CustomTextEditorProvider), [`CustomReadonlyEditorProvider`](#CustomReadonlyEditorProvider), + * [`CustomEditorProvider`](#CustomEditorProvider)for `viewType` as part of activation. * * @param viewType Unique identifier for the custom editor provider. This should match the `viewType` from the * `customEditors` contribution point. @@ -7759,7 +8161,28 @@ declare module 'vscode' { * * @return Disposable that unregisters the provider. */ - export function registerCustomEditorProvider(viewType: string, provider: CustomTextEditorProvider, options?: { readonly webviewOptions?: WebviewPanelOptions; }): Disposable; + export function registerCustomEditorProvider(viewType: string, provider: CustomTextEditorProvider | CustomReadonlyEditorProvider | CustomEditorProvider, options?: { + /** + * Content settings for the webview panels created for this custom editor. + */ + readonly webviewOptions?: WebviewPanelOptions; + + /** + * Only applies to `CustomReadonlyEditorProvider | CustomEditorProvider`. + * + * Indicates that the provider allows multiple editor instances to be open at the same time for + * the same resource. + * + * By default, VS Code only allows one editor instance to be open at a time for each resource. If the + * user tries to open a second editor instance for the resource, the first one is instead moved to where + * the second one was to be opened. + * + * When `supportsMultipleEditorsPerDocument` is enabled, users can split and create copies of the custom + * editor. In this case, the custom editor must make sure it can properly synchronize the states of all + * editor instances for a resource so that they are consistent. + */ + readonly supportsMultipleEditorsPerDocument?: boolean; + }): Disposable; /** * The currently active color theme as configured in the settings. The active @@ -7889,7 +8312,7 @@ declare module 'vscode' { * In order to expand the revealed element, set the option `expand` to `true`. To expand recursively set `expand` to the number of levels to expand. * **NOTE:** You can expand only to 3 levels maximum. * - * **NOTE:** [TreeDataProvider](#TreeDataProvider) is required to implement [getParent](#TreeDataProvider.getParent) method to access this API. + * **NOTE:** The [TreeDataProvider](#TreeDataProvider) that the `TreeView` [is registered with](#window.createTreeView) with must implement [getParent](#TreeDataProvider.getParent) method to access this API. */ reveal(element: T, options?: { select?: boolean, focus?: boolean, expand?: boolean | number }): Thenable; } @@ -8002,6 +8425,13 @@ declare module 'vscode' { */ contextValue?: string; + /** + * Accessibility information used when screen reader interacts with this tree item. + * Generally, a TreeItem has no need to set the `role` of the accessibilityInformation; + * however, there are cases where a TreeItem is not displayed in a tree-like way where setting the `role` may make sense. + */ + accessibilityInformation?: AccessibilityInformation; + /** * @param label A human-readable string describing this item * @param collapsibleState [TreeItemCollapsibleState](#TreeItemCollapsibleState) of the tree item. Default is [TreeItemCollapsibleState.None](#TreeItemCollapsibleState.None) @@ -8104,8 +8534,9 @@ declare module 'vscode' { interface Pseudoterminal { /** * An event that when fired will write data to the terminal. Unlike - * [Terminal.sendText](#Terminal.sendText) which sends text to the underlying _process_ - * (the pty "slave"), this will write the text to the terminal itself (the pty "master"). + * [Terminal.sendText](#Terminal.sendText) which sends text to the underlying child + * pseudo-device (the child), this will write the text to parent pseudo-device (the + * _terminal_ itself). * * Note writing `\n` will just move the cursor down 1 row, you need to write `\r` as well * to move the cursor to the left-most cell. @@ -8957,7 +9388,6 @@ declare module 'vscode' { readonly files: ReadonlyArray<{ oldUri: Uri, newUri: Uri }>; } - /** * An event describing a change to the set of [workspace folders](#workspace.workspaceFolders). */ @@ -8975,7 +9405,7 @@ declare module 'vscode' { /** * A workspace folder is one of potentially many roots opened by the editor. All workspace folders - * are equal which means there is no notion of an active or master workspace folder. + * are equal which means there is no notion of an active or primary workspace folder. */ export interface WorkspaceFolder { @@ -9274,9 +9704,11 @@ declare module 'vscode' { * An event that is emitted when a [text document](#TextDocument) is disposed or when the language id * of a text document [has been changed](#languages.setTextDocumentLanguage). * - * To add an event listener when a visible text document is closed, use the [TextEditor](#TextEditor) events in the - * [window](#window) namespace. Note that this event is not emitted when a [TextEditor](#TextEditor) is closed - * but the document remains open in another [visible text editor](#window.visibleTextEditors). + * *Note 1:* There is no guarantee that this event fires when an editor tab is closed, use the + * [`onDidChangeVisibleTextEditors`](#window.onDidChangeVisibleTextEditors)-event to know when editors change. + * + * *Note 2:* A document can be open but not shown in an editor which means this event can fire + * for a document that has not been shown in an editor. */ export const onDidCloseTextDocument: Event; @@ -9315,7 +9747,7 @@ declare module 'vscode' { * files change on disk, e.g triggered by another application, or when using the * [`workspace.fs`](#FileSystem)-api. * - * *Note 2:* When this event is fired, edits to files thare are being created cannot be applied. + * *Note 2:* When this event is fired, edits to files that are are being created cannot be applied. */ export const onWillCreateFiles: Event; @@ -9384,7 +9816,7 @@ declare module 'vscode' { * is returned. Dots in the section-identifier are interpreted as child-access, * like `{ myExt: { setting: { doIt: true }}}` and `getConfiguration('myExt.setting').get('doIt') === true`. * - * When a scope is provided configuraiton confined to that scope is returned. Scope can be a resource or a language identifier or both. + * When a scope is provided configuration confined to that scope is returned. Scope can be a resource or a language identifier or both. * * @param section A dot-separated identifier. * @param scope A scope for which the configuration is asked for. @@ -9436,11 +9868,12 @@ declare module 'vscode' { export interface ConfigurationChangeEvent { /** - * Returns `true` if the given section is affected in the provided scope. + * Checks if the given section has changed. + * If scope is provided, checks if the section has changed for resources under the given scope. * * @param section Configuration name, supports _dotted_ names. * @param scope A scope in which to check. - * @return `true` if the given section is affected in the provided scope. + * @return `true` if the given section has changed. */ affectsConfiguration(section: string, scope?: ConfigurationScope): boolean; } @@ -9592,7 +10025,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A code action provider. - * @param metadata Metadata about the kind of code actions the provider providers. + * @param metadata Metadata about the kind of code actions the provider provides. * @return A [disposable](#Disposable) that unregisters this provider when being disposed. */ export function registerCodeActionsProvider(selector: DocumentSelector, provider: CodeActionProvider, metadata?: CodeActionProviderMetadata): Disposable; @@ -9743,8 +10176,8 @@ declare module 'vscode' { * Register a rename provider. * * Multiple providers can be registered for a language. In that case providers are sorted - * by their [score](#languages.match) and the best-matching provider is used. Failure - * of the selected provider will cause a failure of the whole operation. + * by their [score](#languages.match) and asked in sequence. The first provider producing a result + * defines the result of the whole operation. * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A rename provider. @@ -10639,7 +11072,6 @@ declare module 'vscode' { */ export let breakpoints: Breakpoint[]; - /** * An [event](#Event) which fires when the [active debug session](#debug.activeDebugSession) * has changed. *Note* that the event also fires when the active debug session changes @@ -10711,7 +11143,7 @@ declare module 'vscode' { * Folder specific variables used in the configuration (e.g. '${workspaceFolder}') are resolved against the given folder. * @param folder The [workspace folder](#WorkspaceFolder) for looking up named configurations and resolving variables or `undefined` for a non-folder setup. * @param nameOrConfiguration Either the name of a debug or compound configuration or a [DebugConfiguration](#DebugConfiguration) object. - * @param parentSessionOrOptions Debug sesison options. When passed a parent [debug session](#DebugSession), assumes options with just this parent session. + * @param parentSessionOrOptions Debug session options. When passed a parent [debug session](#DebugSession), assumes options with just this parent session. * @return A thenable that resolves when debugging could be successfully started. */ export function startDebugging(folder: WorkspaceFolder | undefined, nameOrConfiguration: string | DebugConfiguration, parentSessionOrOptions?: DebugSession | DebugSessionOptions): Thenable; @@ -10890,7 +11322,7 @@ declare module 'vscode' { /** * Dispose this comment thread. * - * Once disposed, this comment thread will be removed from visible editors and Comment Panel when approriate. + * Once disposed, this comment thread will be removed from visible editors and Comment Panel when appropriate. */ dispose(): void; } @@ -11089,6 +11521,131 @@ declare module 'vscode' { } //#endregion + + /** + * Represents a session of a currently logged in user. + */ + export interface AuthenticationSession { + /** + * The identifier of the authentication session. + */ + readonly id: string; + + /** + * The access token. + */ + readonly accessToken: string; + + /** + * The account associated with the session. + */ + readonly account: AuthenticationSessionAccountInformation; + + /** + * The permissions granted by the session's access token. Available scopes + * are defined by the [AuthenticationProvider](#AuthenticationProvider). + */ + readonly scopes: ReadonlyArray; + } + + /** + * The information of an account associated with an [AuthenticationSession](#AuthenticationSession). + */ + export interface AuthenticationSessionAccountInformation { + /** + * The unique identifier of the account. + */ + readonly id: string; + + /** + * The human-readable name of the account. + */ + readonly label: string; + } + + + /** + * Options to be used when getting an [AuthenticationSession](#AuthenticationSession) from an [AuthenticationProvider](#AuthenticationProvider). + */ + export interface AuthenticationGetSessionOptions { + /** + * Whether login should be performed if there is no matching session. Defaults to false. + */ + createIfNone?: boolean; + + /** + * Whether the existing user session preference should be cleared. Set to allow the user to switch accounts. + * Defaults to false. + */ + clearSessionPreference?: boolean; + } + + /** + * Basic information about an[authenticationProvider](#AuthenticationProvider) + */ + export interface AuthenticationProviderInformation { + /** + * The unique identifier of the authentication provider. + */ + readonly id: string; + + /** + * The human-readable name of the authentication provider. + */ + readonly label: string; + } + + /** + * An [event](#Event) which fires when an [AuthenticationSession](#AuthenticationSession) is added, removed, or changed. + */ + export interface AuthenticationSessionsChangeEvent { + /** + * The [authenticationProvider](#AuthenticationProvider) that has had its sessions change. + */ + readonly provider: AuthenticationProviderInformation; + } + + /** + * Namespace for authentication. + */ + export namespace authentication { + /** + * Get an authentication session matching the desired scopes. Rejects if a provider with providerId is not + * registered, or if the user does not consent to sharing authentication information with + * the extension. If there are multiple sessions with the same scopes, the user will be shown a + * quickpick to select which account they would like to use. + * + * Currently, there are only two authentication providers that are contributed from built in extensions + * to VS Code that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'. + * @param providerId The id of the provider to use + * @param scopes A list of scopes representing the permissions requested. These are dependent on the authentication provider + * @param options The [getSessionOptions](#GetSessionOptions) to use + * @returns A thenable that resolves to an authentication session + */ + export function getSession(providerId: string, scopes: string[], options: AuthenticationGetSessionOptions & { createIfNone: true }): Thenable; + + /** + * Get an authentication session matching the desired scopes. Rejects if a provider with providerId is not + * registered, or if the user does not consent to sharing authentication information with + * the extension. If there are multiple sessions with the same scopes, the user will be shown a + * quickpick to select which account they would like to use. + * + * Currently, there are only two authentication providers that are contributed from built in extensions + * to VS Code that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'. + * @param providerId The id of the provider to use + * @param scopes A list of scopes representing the permissions requested. These are dependent on the authentication provider + * @param options The [getSessionOptions](#GetSessionOptions) to use + * @returns A thenable that resolves to an authentication session if available, or undefined if there are no sessions + */ + export function getSession(providerId: string, scopes: string[], options: AuthenticationGetSessionOptions): Thenable; + + /** + * An [event](#Event) which fires when the array of sessions has changed, or data + * within a session has changed for a provider. Fires with the ids of the providers + * that have had session data change. + */ + export const onDidChangeSessions: Event; + } } /** diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 530e3fc19c4..0fb9a73bc65 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -18,16 +18,6 @@ declare module 'vscode' { // #region auth provider: https://github.com/microsoft/vscode/issues/88309 - export interface AuthenticationSession { - id: string; - getAccessToken(): Thenable; - account: { - displayName: string; - id: string; - }; - scopes: string[]; - } - /** * An [event](#Event) which fires when an [AuthenticationProvider](#AuthenticationProvider) is added or removed. */ @@ -35,32 +25,32 @@ declare module 'vscode' { /** * The ids of the [authenticationProvider](#AuthenticationProvider)s that have been added. */ - readonly added: string[]; + readonly added: ReadonlyArray; /** * The ids of the [authenticationProvider](#AuthenticationProvider)s that have been removed. */ - readonly removed: string[]; + readonly removed: ReadonlyArray; } /** * An [event](#Event) which fires when an [AuthenticationSession](#AuthenticationSession) is added, removed, or changed. */ - export interface AuthenticationSessionsChangeEvent { + export interface AuthenticationProviderAuthenticationSessionsChangeEvent { /** * The ids of the [AuthenticationSession](#AuthenticationSession)s that have been added. */ - readonly added: string[]; + readonly added: ReadonlyArray; /** * The ids of the [AuthenticationSession](#AuthenticationSession)s that have been removed. */ - readonly removed: string[]; + readonly removed: ReadonlyArray; /** * The ids of the [AuthenticationSession](#AuthenticationSession)s that have been changed. */ - readonly changed: string[]; + readonly changed: ReadonlyArray; } /** @@ -75,13 +65,22 @@ declare module 'vscode' { * another provider with the same id will fail. */ readonly id: string; - readonly displayName: string; + + /** + * The human-readable name of the provider. + */ + readonly label: string; + + /** + * Whether it is possible to be signed into multiple accounts at once with this provider + */ + readonly supportsMultipleAccounts: boolean; /** * An [event](#Event) which fires when the array of sessions has changed, or data * within a session has changed. */ - readonly onDidChangeSessions: Event; + readonly onDidChangeSessions: Event; /** * Returns an array of current sessions. @@ -92,10 +91,24 @@ declare module 'vscode' { * Prompts a user to login. */ login(scopes: string[]): Thenable; + + /** + * Removes the session corresponding to session id. + * @param sessionId The session id to log out of + */ logout(sessionId: string): Thenable; } export namespace authentication { + /** + * Register an authentication provider. + * + * There can only be one provider per id and an error is being thrown when an id + * has already been used by another provider. + * + * @param provider The authentication provider provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ export function registerAuthenticationProvider(provider: AuthenticationProvider): Disposable; /** @@ -104,44 +117,31 @@ declare module 'vscode' { export const onDidChangeAuthenticationProviders: Event; /** + * @deprecated + * The ids of the currently registered authentication providers. + * @returns An array of the ids of authentication providers that are currently registered. + */ + export function getProviderIds(): Thenable>; + + /** + * @deprecated * An array of the ids of authentication providers that are currently registered. */ - export const providerIds: string[]; + export const providerIds: ReadonlyArray; /** - * Get existing authentication sessions. Rejects if a provider with providerId is not - * registered, or if the user does not consent to sharing authentication information with - * the extension. - * @param providerId The id of the provider to use - * @param scopes A list of scopes representing the permissions requested. These are dependent on the authentication - * provider + * An array of the information of authentication providers that are currently registered. */ - export function getSessions(providerId: string, scopes: string[]): Thenable; - - /** - * Prompt a user to login to create a new authenticaiton session. Rejects if a provider with - * providerId is not registered, or if the user does not consent to sharing authentication - * information with the extension. - * @param providerId The id of the provider to use - * @param scopes A list of scopes representing the permissions requested. These are dependent on the authentication - * provider - */ - export function login(providerId: string, scopes: string[]): Thenable; + export const providers: ReadonlyArray; /** + * @deprecated * Logout of a specific session. * @param providerId The id of the provider to use * @param sessionId The session id to remove * provider */ export function logout(providerId: string, sessionId: string): Thenable; - - /** - * An [event](#Event) which fires when the array of sessions has changed, or data - * within a session has changed for a provider. Fires with the ids of the providers - * that have had session data change. - */ - export const onDidChangeSessions: Event<{ [providerId: string]: AuthenticationSessionsChangeEvent; }>; } //#endregion @@ -224,6 +224,9 @@ declare module 'vscode' { /** * Forwards a port. If the current resolver implements RemoteAuthorityResolver:forwardPort then that will be used to make the tunnel. * By default, openTunnel only support localhost; however, RemoteAuthorityResolver:tunnelFactory can be used to support other ips. + * + * @throws When run in an environment without a remote. + * * @param tunnelOptions The `localPort` is a suggestion only. If that port is not available another will be chosen. */ export function openTunnel(tunnelOptions: TunnelOptions): Thenable; @@ -248,13 +251,14 @@ declare module 'vscode' { export interface ResourceLabelFormatting { label: string; // myLabel:/${path} - // TODO@isidorn + // For historic reasons we use an or string here. Once we finalize this API we should start using enums instead and adopt it in extensions. // eslint-disable-next-line vscode-dts-literal-or-types separator: '/' | '\\' | ''; tildify?: boolean; normalizeDriveLetter?: boolean; workspaceSuffix?: string; authorityPrefix?: string; + stripPathStartingSeparator?: boolean; } export namespace workspace { @@ -733,6 +737,21 @@ declare module 'vscode' { //#region debug + export interface DebugSessionOptions { + /** + * Controls whether this session should run without debugging, thus ignoring breakpoints. + * When this property is not specified, the value from the parent session (if there is one) is used. + */ + noDebug?: boolean; + + /** + * Controls if the debug session's parent session is shown in the CALL STACK view even if it has only a single child. + * By default, the debug session will never hide its parent. + * If compact is true, debug sessions with a single child are hidden in the CALL STACK view to make the tree more compact. + */ + compact?: boolean; + } + // deprecated debug API export interface DebugConfigurationProvider { @@ -743,6 +762,16 @@ declare module 'vscode' { debugAdapterExecutable?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult; } + export namespace debug { + + /** + * Stop the given debug session or stop all debug sessions if no session is specified. + * @param session The [debug session](#DebugSession) to stop or `undefined` for stopping all sessions. + * @return A thenable that resolves when the sessions could be stopped successfully. + */ + export function stopDebugging(session: DebugSession | undefined): Thenable; + } + //#endregion //#region LogLevel: https://github.com/microsoft/vscode/issues/85992 @@ -856,9 +885,9 @@ declare module 'vscode' { namespace window { /** - * An event which fires when the terminal's pty slave pseudo-device is written to. In other - * words, this provides access to the raw data stream from the process running within the - * terminal, including VT sequences. + * An event which fires when the terminal's child pseudo-device is written to (the shell). + * In other words, this provides access to the raw data stream from the process running + * within the terminal, including VT sequences. */ export const onDidWriteTerminalData: Event; } @@ -899,8 +928,6 @@ declare module 'vscode' { //#endregion - - //#region Terminal link handlers https://github.com/microsoft/vscode/issues/91606 export namespace window { @@ -930,6 +957,64 @@ declare module 'vscode' { //#endregion + //#region Terminal link provider https://github.com/microsoft/vscode/issues/91606 + + export namespace window { + export function registerTerminalLinkProvider(provider: TerminalLinkProvider): Disposable; + } + + export interface TerminalLinkContext { + /** + * This is the text from the unwrapped line in the terminal. + */ + line: string; + + /** + * The terminal the link belongs to. + */ + terminal: Terminal; + } + + export interface TerminalLinkProvider { + /** + * Provide terminal links for the given context. Note that this can be called multiple times + * even before previous calls resolve, make sure to not share global objects (eg. `RegExp`) + * that could have problems when asynchronous usage may overlap. + * @param context Information about what links are being provided for. + * @param token A cancellation token. + * @return A list of terminal links for the given line. + */ + provideTerminalLinks(context: TerminalLinkContext, token: CancellationToken): ProviderResult + + /** + * Handle an activated terminal link. + */ + handleTerminalLink(link: T): ProviderResult; + } + + export interface TerminalLink { + /** + * The start index of the link on [TerminalLinkContext.line](#TerminalLinkContext.line]. + */ + startIndex: number; + + /** + * The length of the link on [TerminalLinkContext.line](#TerminalLinkContext.line] + */ + length: number; + + /** + * The tooltip text when you hover over this link. + * + * If a tooltip is provided, is will be displayed in a string that includes instructions on + * how to trigger the link, such as `{0} (ctrl + click)`. The specific instructions vary + * depending on OS, user settings, and localization. + */ + tooltip?: string; + } + + //#endregion + //#region @jrieken -> exclusive document filters export interface DocumentFilter { @@ -966,12 +1051,22 @@ declare module 'vscode' { } + // https://github.com/microsoft/vscode/issues/100741 + export interface TreeDataProvider { + resolveTreeItem?(element: T, item: TreeItem2): TreeItem2 | Thenable; + } + export class TreeItem2 extends TreeItem { /** * Label describing this item. When `falsy`, it is derived from [resourceUri](#TreeItem.resourceUri). */ label?: string | TreeItemLabel | /* for compilation */ any; + /** + * Content to be shown when you hover over the tree item. + */ + tooltip?: string | MarkdownString | /* for compilation */ any; + /** * @param label Label describing this item * @param collapsibleState [TreeItemCollapsibleState](#TreeItemCollapsibleState) of the tree item. Default is [TreeItemCollapsibleState.None](#TreeItemCollapsibleState.None) @@ -1033,6 +1128,11 @@ declare module 'vscode' { */ name: string; + /** + * Accessibility information used when screen reader interacts with this status bar item. + */ + accessibilityInformation?: AccessibilityInformation; + /** * The alignment of the status bar item. */ @@ -1095,317 +1195,12 @@ declare module 'vscode' { //#endregion - //#region Custom editor https://github.com/microsoft/vscode/issues/77131 - - /** - * Represents a custom document used by a [`CustomEditorProvider`](#CustomEditorProvider). - * - * Custom documents are only used within a given `CustomEditorProvider`. The lifecycle of a `CustomDocument` is - * managed by VS Code. When no more references remain to a `CustomDocument`, it is disposed of. - */ - interface CustomDocument { - /** - * The associated uri for this document. - */ - readonly uri: Uri; - - /** - * Dispose of the custom document. - * - * This is invoked by VS Code when there are no more references to a given `CustomDocument` (for example when - * all editors associated with the document have been closed.) - */ - dispose(): void; - } - - /** - * Event triggered by extensions to signal to VS Code that an edit has occurred on an [`CustomDocument`](#CustomDocument). - * - * @see [`CustomDocumentProvider.onDidChangeCustomDocument`](#CustomDocumentProvider.onDidChangeCustomDocument). - */ - interface CustomDocumentEditEvent { - - /** - * The document that the edit is for. - */ - readonly document: T; - - /** - * Undo the edit operation. - * - * This is invoked by VS Code when the user undoes this edit. To implement `undo`, your - * extension should restore the document and editor to the state they were in just before this - * edit was added to VS Code's internal edit stack by `onDidChangeCustomDocument`. - */ - undo(): Thenable | void; - - /** - * Redo the edit operation. - * - * This is invoked by VS Code when the user redoes this edit. To implement `redo`, your - * extension should restore the document and editor to the state they were in just after this - * edit was added to VS Code's internal edit stack by `onDidChangeCustomDocument`. - */ - redo(): Thenable | void; - - /** - * Display name describing the edit. - * - * This is shown in the UI to users. - */ - readonly label?: string; - } - - /** - * Event triggered by extensions to signal to VS Code that the content of a [`CustomDocument`](#CustomDocument) - * has changed. - * - * @see [`CustomDocumentProvider.onDidChangeCustomDocument`](#CustomDocumentProvider.onDidChangeCustomDocument). - */ - interface CustomDocumentContentChangeEvent { - /** - * The document that the change is for. - */ - readonly document: T; - } - - /** - * A backup for an [`CustomDocument`](#CustomDocument). - */ - interface CustomDocumentBackup { - /** - * Unique identifier for the backup. - * - * This id is passed back to your extension in `openCustomDocument` when opening a custom editor from a backup. - */ - readonly id: string; - - /** - * Delete the current backup. - * - * This is called by VS Code when it is clear the current backup is no longer needed, such as when a new backup - * is made or when the file is saved. - */ - delete(): void; - } - - /** - * Additional information used to implement [`CustomEditableDocument.backup`](#CustomEditableDocument.backup). - */ - interface CustomDocumentBackupContext { - /** - * Suggested file location to write the new backup. - * - * Note that your extension is free to ignore this and use its own strategy for backup. - * - * For editors for workspace resource, this destination will be in the workspace storage. The path may not - */ - readonly destination: Uri; - } - - /** - * Additional information about the opening custom document. - */ - interface CustomDocumentOpenContext { - /** - * The id of the backup to restore the document from or `undefined` if there is no backup. - * - * If this is provided, your extension should restore the editor from the backup instead of reading the file - * the user's workspace. - */ - readonly backupId?: string; - } - - /** - * Provider for readonly custom editors that use a custom document model. - * - * Custom editors use [`CustomDocument`](#CustomDocument) as their document model instead of a [`TextDocument`](#TextDocument). - * - * You should use this type of custom editor when dealing with binary files or more complex scenarios. For simple - * text based documents, use [`CustomTextEditorProvider`](#CustomTextEditorProvider) instead. - * - * @param T Type of the custom document returned by this provider. - */ - export interface CustomReadonlyEditorProvider { - - /** - * Create a new document for a given resource. - * - * `openCustomDocument` is called when the first editor for a given resource is opened, and the resolve document - * is passed to `resolveCustomEditor`. The resolved `CustomDocument` is re-used for subsequent editor opens. - * If all editors for a given resource are closed, the `CustomDocument` is disposed of. Opening an editor at - * this point will trigger another call to `openCustomDocument`. - * - * @param uri Uri of the document to open. - * @param openContext Additional information about the opening custom document. - * @param token A cancellation token that indicates the result is no longer needed. - * - * @return The custom document. - */ - openCustomDocument(uri: Uri, openContext: CustomDocumentOpenContext, token: CancellationToken): Thenable | T; - - /** - * Resolve a custom editor for a given resource. - * - * This is called whenever the user opens a new editor for this `CustomEditorProvider`. - * - * To resolve a custom editor, the provider must fill in its initial html content and hook up all - * the event listeners it is interested it. The provider can also hold onto the `WebviewPanel` to use later, - * for example in a command. See [`WebviewPanel`](#WebviewPanel) for additional details. - * - * @param document Document for the resource being resolved. - * @param webviewPanel Webview to resolve. - * @param token A cancellation token that indicates the result is no longer needed. - * - * @return Optional thenable indicating that the custom editor has been resolved. - */ - resolveCustomEditor(document: T, webviewPanel: WebviewPanel, token: CancellationToken): Thenable | void; - } - - /** - * Provider for editiable custom editors that use a custom document model. - * - * Custom editors use [`CustomDocument`](#CustomDocument) as their document model instead of a [`TextDocument`](#TextDocument). - * This gives extensions full control over actions such as edit, save, and backup. - * - * You should use this type of custom editor when dealing with binary files or more complex scenarios. For simple - * text based documents, use [`CustomTextEditorProvider`](#CustomTextEditorProvider) instead. - * - * @param T Type of the custom document returned by this provider. - */ - export interface CustomEditorProvider extends CustomReadonlyEditorProvider { - /** - * Signal that an edit has occurred inside a custom editor. - * - * This event must be fired by your extension whenever an edit happens in a custom editor. An edit can be - * anything from changing some text, to cropping an image, to reordering a list. Your extension is free to - * define what an edit is and what data is stored on each edit. - * - * Firing `onDidChange` causes VS Code to mark the editors as being dirty. This is cleared when the user either - * saves or reverts the file. - * - * Editors that support undo/redo must fire a `CustomDocumentEditEvent` whenever an edit happens. This allows - * users to undo and redo the edit using VS Code's standard VS Code keyboard shortcuts. VS Code will also mark - * the editor as no longer being dirty if the user undoes all edits to the last saved state. - * - * Editors that support editing but cannot use VS Code's standard undo/redo mechanism must fire a `CustomDocumentContentChangeEvent`. - * The only way for a user to clear the dirty state of an editor that does not support undo/redo is to either - * `save` or `revert` the file. - * - * An editor should only ever fire `CustomDocumentEditEvent` events, or only ever fire `CustomDocumentContentChangeEvent` events. - */ - readonly onDidChangeCustomDocument: Event> | Event>; - - /** - * Save a custom document. - * - * This method is invoked by VS Code when the user saves a custom editor. This can happen when the user - * triggers save while the custom editor is active, by commands such as `save all`, or by auto save if enabled. - * - * To implement `save`, the implementer must persist the custom editor. This usually means writing the - * file data for the custom document to disk. After `save` completes, any associated editor instances will - * no longer be marked as dirty. - * - * @param document Document to save. - * @param cancellation Token that signals the save is no longer required (for example, if another save was triggered). - * - * @return Thenable signaling that saving has completed. - */ - saveCustomDocument(document: T, cancellation: CancellationToken): Thenable; - - /** - * Save a custom document to a different location. - * - * This method is invoked by VS Code when the user triggers 'save as' on a custom editor. The implementer must - * persist the custom editor to `destination`. - * - * When the user accepts save as, the current editor is be replaced by an non-dirty editor for the newly saved file. - * - * @param document Document to save. - * @param destination Location to save to. - * @param cancellation Token that signals the save is no longer required. - * - * @return Thenable signaling that saving has completed. - */ - saveCustomDocumentAs(document: T, destination: Uri, cancellation: CancellationToken): Thenable; - - /** - * Revert a custom document to its last saved state. - * - * This method is invoked by VS Code when the user triggers `File: Revert File` in a custom editor. (Note that - * this is only used using VS Code's `File: Revert File` command and not on a `git revert` of the file). - * - * To implement `revert`, the implementer must make sure all editor instances (webviews) for `document` - * are displaying the document in the same state is saved in. This usually means reloading the file from the - * workspace. - * - * @param document Document to revert. - * @param cancellation Token that signals the revert is no longer required. - * - * @return Thenable signaling that the change has completed. - */ - revertCustomDocument(document: T, cancellation: CancellationToken): Thenable; - - /** - * Back up a dirty custom document. - * - * Backups are used for hot exit and to prevent data loss. Your `backup` method should persist the resource in - * its current state, i.e. with the edits applied. Most commonly this means saving the resource to disk in - * the `ExtensionContext.storagePath`. When VS Code reloads and your custom editor is opened for a resource, - * your extension should first check to see if any backups exist for the resource. If there is a backup, your - * extension should load the file contents from there instead of from the resource in the workspace. - * - * `backup` is triggered whenever an edit it made. Calls to `backup` are debounced so that if multiple edits are - * made in quick succession, `backup` is only triggered after the last one. `backup` is not invoked when - * `auto save` is enabled (since auto save already persists resource ). - * - * @param document Document to backup. - * @param context Information that can be used to backup the document. - * @param cancellation Token that signals the current backup since a new backup is coming in. It is up to your - * extension to decided how to respond to cancellation. If for example your extension is backing up a large file - * in an operation that takes time to complete, your extension may decide to finish the ongoing backup rather - * than cancelling it to ensure that VS Code has some valid backup. - */ - backupCustomDocument(document: T, context: CustomDocumentBackupContext, cancellation: CancellationToken): Thenable; - } - - namespace window { - /** - * Temporary overload for `registerCustomEditorProvider` that takes a `CustomEditorProvider`. - */ - export function registerCustomEditorProvider2( - viewType: string, - provider: CustomReadonlyEditorProvider | CustomEditorProvider, - options?: { - readonly webviewOptions?: WebviewPanelOptions; - - /** - * Only applies to `CustomReadonlyEditorProvider | CustomEditorProvider`. - * - * Indicates that the provider allows multiple editor instances to be open at the same time for - * the same resource. - * - * If not set, VS Code only allows one editor instance to be open at a time for each resource. If the - * user tries to open a second editor instance for the resource, the first one is instead moved to where - * the second one was to be opened. - * - * When set, users can split and create copies of the custom editor. The custom editor must make sure it - * can properly synchronize the states of all editor instances for a resource so that they are consistent. - */ - readonly supportsMultipleEditorsPerDocument?: boolean; - } - ): Disposable; - } - - // #endregion - //#region Custom editor move https://github.com/microsoft/vscode/issues/86146 // TODO: Also for custom editor export interface CustomTextEditorProvider { - /** * Handle when the underlying resource for a custom editor is renamed. * @@ -1423,7 +1218,6 @@ declare module 'vscode' { //#endregion - //#region allow QuickPicks to skip sorting: https://github.com/microsoft/vscode/issues/73904 export interface QuickPick extends QuickInput { @@ -1435,18 +1229,6 @@ declare module 'vscode' { //#endregion - //#region Allow theme icons in hovers: https://github.com/microsoft/vscode/issues/84695 - - export interface MarkdownString { - - /** - * Indicates that this markdown string can contain [ThemeIcons](#ThemeIcon), e.g. `$(zap)`. - */ - readonly supportThemeIcons?: boolean; - } - - //#endregion - //#region @rebornix: Notebook export enum CellKind { @@ -1481,6 +1263,13 @@ declare module 'vscode' { traceback: string[]; } + export interface NotebookCellOutputMetadata { + /** + * Additional attributes of a cell metadata. + */ + custom?: { [key: string]: any }; + } + export interface CellDisplayOutput { outputKind: CellOutputKind.Rich; /** @@ -1501,6 +1290,8 @@ declare module 'vscode' { * } */ data: { [key: string]: any; }; + + readonly metadata?: NotebookCellOutputMetadata; } export type CellOutput = CellStreamOutput | CellErrorOutput | CellDisplayOutput; @@ -1512,6 +1303,11 @@ declare module 'vscode' { Error = 4 } + export enum NotebookRunState { + Running = 1, + Idle = 2 + } + export interface NotebookCellMetadata { /** * Controls if the content of a cell is editable or not. @@ -1524,6 +1320,18 @@ declare module 'vscode' { */ runnable?: boolean; + /** + * Controls if the cell has a margin to support the breakpoint UI. + * This metadata is ignored for markdown cell. + */ + breakpointMargin?: boolean; + + /** + * Whether the [execution order](#NotebookCellMetadata.executionOrder) indicator will be displayed. + * Defaults to true. + */ + hasExecutionOrder?: boolean; + /** * The order in which this cell was executed. */ @@ -1538,14 +1346,38 @@ declare module 'vscode' { * The cell's current run state */ runState?: NotebookCellRunState; + + /** + * If the cell is running, the time at which the cell started running + */ + runStartTime?: number; + + /** + * The total duration of the cell's last run + */ + lastRunDuration?: number; + + /** + * Whether a code cell's editor is collapsed + */ + inputCollapsed?: boolean; + + /** + * Whether a code cell's outputs are collapsed + */ + outputCollapsed?: boolean; + + /** + * Additional attributes of a cell metadata. + */ + custom?: { [key: string]: any }; } export interface NotebookCell { + readonly notebook: NotebookDocument; readonly uri: Uri; readonly cellKind: CellKind; readonly document: TextDocument; - // API remove `source` or doc it as shorthand for document.getText() - readonly source: string; language: string; outputs: CellOutput[]; metadata: NotebookCellMetadata; @@ -1577,18 +1409,30 @@ declare module 'vscode' { cellRunnable?: boolean; /** - * Whether the [execution order](#NotebookCellMetadata.executionOrder) indicator will be displayed. + * Default value for [cell hasExecutionOrder metadata](#NotebookCellMetadata.hasExecutionOrder). * Defaults to true. */ - hasExecutionOrder?: boolean; + cellHasExecutionOrder?: boolean; displayOrder?: GlobPattern[]; + + /** + * Additional attributes of the document metadata. + */ + custom?: { [key: string]: any }; + + /** + * The document's current run state + */ + runState?: NotebookRunState; } export interface NotebookDocument { readonly uri: Uri; readonly fileName: string; + readonly viewType: string; readonly isDirty: boolean; + readonly isUntitled: boolean; readonly cells: NotebookCell[]; languages: string[]; displayOrder?: GlobPattern[]; @@ -1596,16 +1440,22 @@ declare module 'vscode' { } export interface NotebookConcatTextDocument { + uri: Uri; isClosed: boolean; dispose(): void; onDidChange: Event; version: number; getText(): string; getText(range: Range): string; + offsetAt(position: Position): number; positionAt(offset: number): Position; + validateRange(range: Range): Range; + validatePosition(position: Position): Position; + locationAt(positionOrRange: Position | Range): Location; positionAt(location: Location): Position; + contains(uri: Uri): boolean } export interface NotebookEditorCellEdit { @@ -1623,7 +1473,32 @@ declare module 'vscode' { * The primary selected cell on this notebook editor. */ readonly selection?: NotebookCell; + + /** + * The column in which this editor shows. + */ viewColumn?: ViewColumn; + + /** + * Whether the panel is active (focused by the user). + */ + readonly active: boolean; + + /** + * Whether the panel is visible. + */ + readonly visible: boolean; + + /** + * Fired when the panel is disposed. + */ + readonly onDidDispose: Event; + + /** + * Active kernel used in the editor + */ + readonly kernel?: NotebookKernel; + /** * Fired when the output hosting webview posts a message. */ @@ -1637,12 +1512,22 @@ declare module 'vscode' { */ postMessage(message: any): Thenable; + /** + * Convert a uri for the local file system to one that can be used inside outputs webview. + */ + asWebviewUri(localResource: Uri): Uri; + edit(callback: (editBuilder: NotebookEditorCellEdit) => void): Thenable; } export interface NotebookOutputSelector { - type: string; - subTypes?: string[]; + mimeTypes?: string[]; + } + + export interface NotebookRenderRequest { + output: CellDisplayOutput; + mimeType: string; + outputId: string; } export interface NotebookOutputRenderer { @@ -1651,21 +1536,73 @@ declare module 'vscode' { * @returns HTML fragment. We can probably return `CellOutput` instead of string ? * */ - render(document: NotebookDocument, output: CellDisplayOutput, mimeType: string): string; - preloads?: Uri[]; + render(document: NotebookDocument, request: NotebookRenderRequest): string; + + /** + * Call before HTML from the renderer is executed, and will be called for + * every editor associated with notebook documents where the renderer + * is or was used. + * + * The communication object will only send and receive messages to the + * render API, retrieved via `acquireNotebookRendererApi`, acquired with + * this specific renderer's ID. + * + * If you need to keep an association between the communication object + * and the document for use in the `render()` method, you can use a WeakMap. + */ + resolveNotebook?(document: NotebookDocument, communication: NotebookCommunication): void; + + readonly preloads?: Uri[]; } - export interface NotebookDocumentChangeEvent { + export interface NotebookCellsChangeData { + readonly start: number; + readonly deletedCount: number; + readonly deletedItems: NotebookCell[]; + readonly items: NotebookCell[]; + } + + export interface NotebookCellsChangeEvent { /** * The affected document. */ readonly document: NotebookDocument; + readonly changes: ReadonlyArray; + } + + export interface NotebookCellMoveEvent { /** - * An array of content changes. + * The affected document. */ - // readonly contentChanges: ReadonlyArray; + readonly document: NotebookDocument; + readonly index: number; + readonly newIndex: number; + } + + export interface NotebookCellOutputsChangeEvent { + + /** + * The affected document. + */ + readonly document: NotebookDocument; + readonly cells: NotebookCell[]; + } + + export interface NotebookCellLanguageChangeEvent { + + /** + * The affected document. + */ + readonly document: NotebookDocument; + readonly cell: NotebookCell; + readonly language: string; + } + + export interface NotebookCellMetadataChangeEvent { + readonly document: NotebookDocument; + readonly cell: NotebookCell; } export interface NotebookCellData { @@ -1682,7 +1619,7 @@ declare module 'vscode' { readonly metadata: NotebookDocumentMetadata; } - interface NotebookDocumentEditEvent { + interface NotebookDocumentContentChangeEvent { /** * The document that the edit is for. @@ -1690,19 +1627,133 @@ declare module 'vscode' { readonly document: NotebookDocument; } - export interface NotebookContentProvider { - openNotebook(uri: Uri): NotebookData | Promise; - saveNotebook(document: NotebookDocument, cancellation: CancellationToken): Promise; - saveNotebookAs(targetResource: Uri, document: NotebookDocument, cancellation: CancellationToken): Promise; - readonly onDidChangeNotebook: Event; - - // revert?(document: NotebookDocument, cancellation: CancellationToken): Thenable; - // backup?(document: NotebookDocument, cancellation: CancellationToken): Thenable; + interface NotebookDocumentEditEvent { /** - * Responsible for filling in outputs for the cell + * The document that the edit is for. */ - executeCell(document: NotebookDocument, cell: NotebookCell | undefined, token: CancellationToken): Promise; + readonly document: NotebookDocument; + + /** + * Undo the edit operation. + * + * This is invoked by VS Code when the user undoes this edit. To implement `undo`, your + * extension should restore the document and editor to the state they were in just before this + * edit was added to VS Code's internal edit stack by `onDidChangeCustomDocument`. + */ + undo(): Thenable | void; + + /** + * Redo the edit operation. + * + * This is invoked by VS Code when the user redoes this edit. To implement `redo`, your + * extension should restore the document and editor to the state they were in just after this + * edit was added to VS Code's internal edit stack by `onDidChangeCustomDocument`. + */ + redo(): Thenable | void; + + /** + * Display name describing the edit. + * + * This will be shown to users in the UI for undo/redo operations. + */ + readonly label?: string; + } + + interface NotebookDocumentBackup { + /** + * Unique identifier for the backup. + * + * This id is passed back to your extension in `openCustomDocument` when opening a notebook editor from a backup. + */ + readonly id: string; + + /** + * Delete the current backup. + * + * This is called by VS Code when it is clear the current backup is no longer needed, such as when a new backup + * is made or when the file is saved. + */ + delete(): void; + } + + interface NotebookDocumentBackupContext { + readonly destination: Uri; + } + + interface NotebookDocumentOpenContext { + readonly backupId?: string; + } + + /** + * Communication object passed to the {@link NotebookContentProvider} and + * {@link NotebookOutputRenderer} to communicate with the webview. + */ + export interface NotebookCommunication { + /** + * ID of the editor this object communicates with. A single notebook + * document can have multiple attached webviews and editors, when the + * notebook is split for instance. The editor ID lets you differentiate + * between them. + */ + readonly editorId: string; + + /** + * Fired when the output hosting webview posts a message. + */ + readonly onDidReceiveMessage: Event; + /** + * Post a message to the output hosting webview. + * + * Messages are only delivered if the editor is live. + * + * @param message Body of the message. This must be a string or other json serilizable object. + */ + postMessage(message: any): Thenable; + + /** + * Convert a uri for the local file system to one that can be used inside outputs webview. + */ + asWebviewUri(localResource: Uri): Uri; + } + + export interface NotebookContentProvider { + /** + * Content providers should always use [file system providers](#FileSystemProvider) to + * resolve the raw content for `uri` as the resouce is not necessarily a file on disk. + */ + openNotebook(uri: Uri, openContext: NotebookDocumentOpenContext): NotebookData | Promise; + resolveNotebook(document: NotebookDocument, webview: NotebookCommunication): Promise; + saveNotebook(document: NotebookDocument, cancellation: CancellationToken): Promise; + saveNotebookAs(targetResource: Uri, document: NotebookDocument, cancellation: CancellationToken): Promise; + readonly onDidChangeNotebook: Event; + backupNotebook(document: NotebookDocument, context: NotebookDocumentBackupContext, cancellation: CancellationToken): Promise; + + kernel?: NotebookKernel; + } + + export interface NotebookKernel { + readonly id?: string; + label: string; + description?: string; + isPreferred?: boolean; + preloads?: Uri[]; + executeCell(document: NotebookDocument, cell: NotebookCell): void; + cancelCellExecution(document: NotebookDocument, cell: NotebookCell): void; + executeAllCells(document: NotebookDocument): void; + cancelAllCellsExecution(document: NotebookDocument): void; + } + + export interface NotebookDocumentFilter { + viewType?: string; + filenamePattern?: GlobPattern; + excludeFileNamePattern?: GlobPattern; + } + + export interface NotebookKernelProvider { + onDidChangeKernels?: Event; + provideKernels(document: NotebookDocument, token: CancellationToken): ProviderResult; + resolveKernel?(kernel: T, document: NotebookDocument, webview: NotebookCommunication, token: CancellationToken): ProviderResult; } export namespace notebook { @@ -1711,23 +1762,41 @@ declare module 'vscode' { provider: NotebookContentProvider ): Disposable; + export function registerNotebookKernelProvider( + selector: NotebookDocumentFilter, + provider: NotebookKernelProvider + ): Disposable; + + export function registerNotebookKernel( + id: string, + selectors: GlobPattern[], + kernel: NotebookKernel + ): Disposable; + export function registerNotebookOutputRenderer( - type: string, + id: string, outputSelector: NotebookOutputSelector, renderer: NotebookOutputRenderer ): Disposable; export const onDidOpenNotebookDocument: Event; export const onDidCloseNotebookDocument: Event; - // export const onDidChangeVisibleNotebookEditors: Event; + export const onDidSaveNotebookDocument: Event; - // remove activeNotebookDocument, now that there is activeNotebookEditor.document - export let activeNotebookDocument: NotebookDocument | undefined; + /** + * All currently known notebook documents. + */ + export const notebookDocuments: ReadonlyArray; + + export let visibleNotebookEditors: NotebookEditor[]; + export const onDidChangeVisibleNotebookEditors: Event; export let activeNotebookEditor: NotebookEditor | undefined; - - export const onDidChangeNotebookDocument: Event; - + export const onDidChangeActiveNotebookEditor: Event; + export const onDidChangeNotebookCells: Event; + export const onDidChangeCellOutputs: Event; + export const onDidChangeCellLanguage: Event; + export const onDidChangeCellMetadata: Event; /** * Create a document that is the concatenation of all notebook cells. By default all code-cells are included * but a selector can be provided to narrow to down the set of cells. @@ -1736,43 +1805,8 @@ declare module 'vscode' { * @param selector */ export function createConcatTextDocument(notebook: NotebookDocument, selector?: DocumentSelector): NotebookConcatTextDocument; - } - //#endregion - - //#region @connor4312 extension mode: https://github.com/microsoft/vscode/issues/95926 - - /** - * The ExtensionMode is provided on the `ExtensionContext` and indicates the - * mode the specific extension is running in. - */ - export enum ExtensionMode { - /** - * The extension is installed normally (for example, from the marketplace - * or VSIX) in VS Code. - */ - Release = 1, - - /** - * The extension is running from an `--extensionDevelopmentPath` provided - * when launching VS Code. - */ - Development = 2, - - /** - * The extension is running from an `--extensionDevelopmentPath` and - * the extension host is running unit tests. - */ - Test = 3, - } - - export interface ExtensionContext { - /** - * The mode the extension is running in. This is specific to the current - * extension. One extension may be in `ExtensionMode.Development` while - * other extensions in the host run in `ExtensionMode.Release`. - */ - readonly extensionMode: ExtensionMode; + export const onDidChangeActiveNotebookKernel: Event<{ document: NotebookDocument, kernel: NotebookKernel | undefined }>; } //#endregion @@ -1810,7 +1844,6 @@ declare module 'vscode' { //#endregion - //#region @eamodio - timeline: https://github.com/microsoft/vscode/issues/84297 export class TimelineItem { @@ -1871,6 +1904,11 @@ declare module 'vscode' { */ contextValue?: string; + /** + * Accessibility information used when screen reader interacts with this timeline item. + */ + accessibilityInformation?: AccessibilityInformation; + /** * @param label A human-readable string describing the timeline item * @param timestamp A timestamp (in milliseconds since 1 January 1970 00:00:00) for when the timeline item occurred @@ -1964,22 +2002,22 @@ declare module 'vscode' { //#endregion - //#region https://github.com/microsoft/vscode/issues/86788 + //#region https://github.com/microsoft/vscode/issues/91555 - export interface CodeActionProviderMetadata { - /** - * Static documentation for a class of code actions. - * - * The documentation is shown in the code actions menu if either: - * - * - Code actions of `kind` are requested by VS Code. In this case, VS Code will show the documentation that - * most closely matches the requested code action kind. For example, if a provider has documentation for - * both `Refactor` and `RefactorExtract`, when the user requests code actions for `RefactorExtract`, - * VS Code will use the documentation for `RefactorExtract` intead of the documentation for `Refactor`. - * - * - Any code actions of `kind` are returned by the provider. - */ - readonly documentation?: ReadonlyArray<{ readonly kind: CodeActionKind, readonly command: Command; }>; + export enum StandardTokenType { + Other = 0, + Comment = 1, + String = 2, + RegEx = 4 + } + + export interface TokenInformation { + type: StandardTokenType; + range: Range; + } + + export namespace languages { + export function getTokenInformationAtPosition(document: TextDocument, position: Position): Promise; } //#endregion @@ -2009,38 +2047,58 @@ declare module 'vscode' { } //#endregion + //#region https://github.com/microsoft/vscode/issues/101857 - //#region Dialog title: https://github.com/microsoft/vscode/issues/82871 + export interface ExtensionContext { - /** - * Options to configure the behaviour of a file open dialog. - * - * * Note 1: A dialog can select files, folders, or both. This is not true for Windows - * which enforces to open either files or folder, but *not both*. - * * Note 2: Explicitly setting `canSelectFiles` and `canSelectFolders` to `false` is futile - * and the editor then silently adjusts the options to select files. - */ - export interface OpenDialogOptions { /** - * Dialog title. + * The uri of a directory in which the extension can create log files. + * The directory might not exist on disk and creation is up to the extension. However, + * the parent directory is guaranteed to be existent. * - * Depending on the underlying operating system this parameter might be ignored, since some - * systems do not present title on open dialogs. + * @see [`workspace.fs`](#FileSystem) for how to read and write files and folders from + * an uri. */ - title?: string; - } + readonly logUri: Uri; - /** - * Options to configure the behaviour of a file save dialog. - */ - export interface SaveDialogOptions { /** - * Dialog title. + * The uri of a workspace specific directory in which the extension + * can store private state. The directory might not exist and creation is + * up to the extension. However, the parent directory is guaranteed to be existent. + * The value is `undefined` when no workspace nor folder has been opened. * - * Depending on the underlying operating system this parameter might be ignored, since some - * systems do not present title on save dialogs. + * Use [`workspaceState`](#ExtensionContext.workspaceState) or + * [`globalState`](#ExtensionContext.globalState) to store key value data. + * + * @see [`workspace.fs`](#FileSystem) for how to read and write files and folders from + * an uri. */ - title?: string; + readonly storageUri: Uri | undefined; + + /** + * The uri of a directory in which the extension can store global state. + * The directory might not exist on disk and creation is + * up to the extension. However, the parent directory is guaranteed to be existent. + * + * Use [`globalState`](#ExtensionContext.globalState) to store key value data. + * + * @see [`workspace.fs`](#FileSystem) for how to read and write files and folders from + * an uri. + */ + readonly globalStorageUri: Uri; + + /** + * @deprecated Use [logUri](#ExtensionContext.logUri) instead. + */ + readonly logPath: string; + /** + * @deprecated Use [storagePath](#ExtensionContent.storageUri) instead. + */ + readonly storagePath: string | undefined; + /** + * @deprecated Use [globalStoragePath](#ExtensionContent.globalStorageUri) instead. + */ + readonly globalStoragePath: string; } //#endregion diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index 3f2de2c7380..3d77009b908 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -41,6 +41,7 @@ import './mainThreadMessageService'; import './mainThreadOutputService'; import './mainThreadProgress'; import './mainThreadQuickOpen'; +import './mainThreadRemoteConnectionData'; import './mainThreadSaveParticipant'; import './mainThreadSCM'; import './mainThreadSearch'; diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts index ccabaa3af36..7a9e0fc6a64 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -3,71 +3,87 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import * as modes from 'vs/editor/common/modes'; import * as nls from 'vs/nls'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; -import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; +import { IAuthenticationService, AllowedExtension, readAllowedExtensions, getAuthenticationProviderActivationEvent } from 'vs/workbench/services/authentication/browser/authenticationService'; import { ExtHostAuthenticationShape, ExtHostContext, IExtHostContext, MainContext, MainThreadAuthenticationShape } from '../common/extHost.protocol'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import Severity from 'vs/base/common/severity'; -import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { fromNow } from 'vs/base/common/date'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { Platform, platform } from 'vs/base/common/platform'; -interface AllowedExtension { - id: string; - name: string; +const VSO_ALLOWED_EXTENSIONS = ['github.vscode-pull-request-github', 'github.vscode-pull-request-github-insiders', 'vscode.git', 'ms-vsonline.vsonline', 'vscode.github-browser']; + +interface IAccountUsage { + extensionId: string; + extensionName: string; + lastUsed: number; } -const accountUsages = new Map(); - -const VSO_ALLOWED_EXTENSIONS = ['github.vscode-pull-request-github', 'github.vscode-pull-request-github-insiders', 'vscode.git']; - -function addAccountUsage(providerId: string, accountName: string, extensionOrFeatureName: string) { - const providerAccountUsage = accountUsages.get(providerId); - if (!providerAccountUsage) { - accountUsages.set(providerId, { [accountName]: [extensionOrFeatureName] }); - } else { - if (providerAccountUsage[accountName]) { - if (!providerAccountUsage[accountName].includes(extensionOrFeatureName)) { - providerAccountUsage[accountName].push(extensionOrFeatureName); - } - } else { - providerAccountUsage[accountName] = [extensionOrFeatureName]; +function readAccountUsages(storageService: IStorageService, providerId: string, accountName: string,): IAccountUsage[] { + const accountKey = `${providerId}-${accountName}-usages`; + const storedUsages = storageService.get(accountKey, StorageScope.GLOBAL); + let usages: IAccountUsage[] = []; + if (storedUsages) { + try { + usages = JSON.parse(storedUsages); + } catch (e) { + // ignore } - - accountUsages.set(providerId, providerAccountUsage); } + + return usages; } -function readAllowedExtensions(storageService: IStorageService, providerId: string, accountName: string): AllowedExtension[] { - let trustedExtensions: AllowedExtension[] = []; - try { - const trustedExtensionSrc = storageService.get(`${providerId}-${accountName}`, StorageScope.GLOBAL); - if (trustedExtensionSrc) { - trustedExtensions = JSON.parse(trustedExtensionSrc); - } - } catch (err) { } +function removeAccountUsage(storageService: IStorageService, providerId: string, accountName: string): void { + const accountKey = `${providerId}-${accountName}-usages`; + storageService.remove(accountKey, StorageScope.GLOBAL); +} - return trustedExtensions; +function addAccountUsage(storageService: IStorageService, providerId: string, accountName: string, extensionId: string, extensionName: string) { + const accountKey = `${providerId}-${accountName}-usages`; + const usages = readAccountUsages(storageService, providerId, accountName); + + const existingUsageIndex = usages.findIndex(usage => usage.extensionId === extensionId); + if (existingUsageIndex > -1) { + usages.splice(existingUsageIndex, 1, { + extensionId, + extensionName, + lastUsed: Date.now() + }); + } else { + usages.push({ + extensionId, + extensionName, + lastUsed: Date.now() + }); + } + + storageService.store(accountKey, JSON.stringify(usages), StorageScope.GLOBAL); } export class MainThreadAuthenticationProvider extends Disposable { - private _sessionMenuItems = new Map(); private _accounts = new Map(); // Map account name to session ids private _sessions = new Map(); // Map account id to name constructor( private readonly _proxy: ExtHostAuthenticationShape, public readonly id: string, - public readonly displayName: string, + public readonly label: string, + public readonly supportsMultipleAccounts: boolean, private readonly notificationService: INotificationService, - private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService + private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, + private readonly storageService: IStorageService, + private readonly quickInputService: IQuickInputService, + private readonly dialogService: IDialogService ) { super(); } @@ -80,13 +96,18 @@ export class MainThreadAuthenticationProvider extends Disposable { return !!this._sessions.size; } - private manageTrustedExtensions(quickInputService: IQuickInputService, storageService: IStorageService, accountName: string) { - const quickPick = quickInputService.createQuickPick<{ label: string, extension: AllowedExtension }>(); + public manageTrustedExtensions(accountName: string) { + const quickPick = this.quickInputService.createQuickPick<{ label: string, description: string, extension: AllowedExtension }>(); quickPick.canSelectMany = true; - const allowedExtensions = readAllowedExtensions(storageService, this.id, accountName); + const allowedExtensions = readAllowedExtensions(this.storageService, this.id, accountName); + const usages = readAccountUsages(this.storageService, this.id, accountName); const items = allowedExtensions.map(extension => { + const usage = usages.find(usage => extension.id === usage.extensionId); return { label: extension.name, + description: usage + ? nls.localize({ key: 'accountLastUsedDate', comment: ['The placeholder {0} is a string with time information, such as "3 days ago"'] }, "Last used this account {0}", fromNow(usage.lastUsed, true)) + : nls.localize('notUsed', "Has not used this account"), extension }; }); @@ -98,7 +119,7 @@ export class MainThreadAuthenticationProvider extends Disposable { quickPick.onDidAccept(() => { const updatedAllowedList = quickPick.selectedItems.map(item => item.extension); - storageService.store(`${this.id}-${accountName}`, JSON.stringify(updatedAllowedList), StorageScope.GLOBAL); + this.storageService.store(`${this.id}-${accountName}`, JSON.stringify(updatedAllowedList), StorageScope.GLOBAL); quickPick.dispose(); }); @@ -110,128 +131,44 @@ export class MainThreadAuthenticationProvider extends Disposable { quickPick.show(); } - private showUsage(quickInputService: IQuickInputService, accountName: string) { - const quickPick = quickInputService.createQuickPick(); - const providerUsage = accountUsages.get(this.id); - const accountUsage = (providerUsage || {})[accountName] || []; - - quickPick.items = accountUsage.map(extensionOrFeature => { - return { - label: extensionOrFeature - }; - }); - - quickPick.onDidHide(() => { - quickPick.dispose(); - }); - - quickPick.show(); - } - private async registerCommandsAndContextMenuItems(): Promise { const sessions = await this._proxy.$getSessions(this.id); sessions.forEach(session => this.registerSession(session)); } private registerSession(session: modes.AuthenticationSession) { - this._sessions.set(session.id, session.account.displayName); + this._sessions.set(session.id, session.account.label); - const existingSessionsForAccount = this._accounts.get(session.account.displayName); + const existingSessionsForAccount = this._accounts.get(session.account.label); if (existingSessionsForAccount) { - this._accounts.set(session.account.displayName, existingSessionsForAccount.concat(session.id)); + this._accounts.set(session.account.label, existingSessionsForAccount.concat(session.id)); return; } else { - this._accounts.set(session.account.displayName, [session.id]); + this._accounts.set(session.account.label, [session.id]); } - const menuItem = MenuRegistry.appendMenuItem(MenuId.AccountsContext, { - group: '1_accounts', - command: { - id: `configureSessions${session.id}`, - title: `${session.account.displayName} (${this.displayName})` - }, - order: 3 - }); - - this.storageKeysSyncRegistryService.registerStorageKey({ key: `${this.id}-${session.account.displayName}`, version: 1 }); - - const manageCommand = CommandsRegistry.registerCommand({ - id: `configureSessions${session.id}`, - handler: (accessor, args) => { - const quickInputService = accessor.get(IQuickInputService); - const storageService = accessor.get(IStorageService); - const dialogService = accessor.get(IDialogService); - - const quickPick = quickInputService.createQuickPick(); - const showUsage = nls.localize('showUsage', "Show Extensions and Features Using This Account"); - const manage = nls.localize('manageTrustedExtensions', "Manage Trusted Extensions"); - const signOut = nls.localize('signOut', "Sign Out"); - const items = ([{ label: showUsage }, { label: manage }, { label: signOut }]); - - quickPick.items = items; - - quickPick.onDidAccept(e => { - const selected = quickPick.selectedItems[0]; - if (selected.label === signOut) { - this.signOut(dialogService, session); - } - - if (selected.label === manage) { - this.manageTrustedExtensions(quickInputService, storageService, session.account.displayName); - } - - if (selected.label === showUsage) { - this.showUsage(quickInputService, session.account.displayName); - } - - quickPick.dispose(); - }); - - quickPick.onDidHide(_ => { - quickPick.dispose(); - }); - - quickPick.show(); - }, - }); - - this._sessionMenuItems.set(session.account.displayName, [menuItem, manageCommand]); + this.storageKeysSyncRegistryService.registerStorageKey({ key: `${this.id}-${session.account.label}`, version: 1 }); } - async signOut(dialogService: IDialogService, session: modes.AuthenticationSession): Promise { - const providerUsage = accountUsages.get(this.id); - const accountUsage = (providerUsage || {})[session.account.displayName] || []; - const sessionsForAccount = this._accounts.get(session.account.displayName); + async signOut(accountName: string): Promise { + const accountUsages = readAccountUsages(this.storageService, this.id, accountName); + const sessionsForAccount = this._accounts.get(accountName); - // Skip dialog if nothing is using the account - if (!accountUsage.length) { - accountUsages.set(this.id, { [session.account.displayName]: [] }); - sessionsForAccount?.forEach(sessionId => this.logout(sessionId)); - return; - } - - const result = await dialogService.confirm({ - title: nls.localize('signOutConfirm', "Sign out of {0}", session.account.displayName), - message: nls.localize('signOutMessage', "The account {0} is currently used by: \n\n{1}\n\n Sign out of these features?", session.account.displayName, accountUsage.join('\n')) + const result = await this.dialogService.confirm({ + title: nls.localize('signOutConfirm', "Sign out of {0}", accountName), + message: accountUsages.length + ? nls.localize('signOutMessagve', "The account {0} has been used by: \n\n{1}\n\n Sign out of these features?", accountName, accountUsages.map(usage => usage.extensionName).join('\n')) + : nls.localize('signOutMessageSimple', "Sign out of {0}?", accountName) }); if (result.confirmed) { - accountUsages.set(this.id, { [session.account.displayName]: [] }); sessionsForAccount?.forEach(sessionId => this.logout(sessionId)); + removeAccountUsage(this.storageService, this.id, accountName); } } async getSessions(): Promise> { - return (await this._proxy.$getSessions(this.id)).map(session => { - return { - id: session.id, - account: session.account, - getAccessToken: () => { - addAccountUsage(this.id, session.account.displayName, nls.localize('sync', "Preferences Sync")); - return this._proxy.$getSessionAccessToken(this.id, session.id); - } - }; - }); + return this._proxy.$getSessions(this.id); } async updateSessionItems(event: modes.AuthenticationSessionsChangeEvent): Promise { @@ -248,11 +185,6 @@ export class MainThreadAuthenticationProvider extends Disposable { sessionsForAccount.splice(sessionIndex); if (!sessionsForAccount.length) { - const disposeables = this._sessionMenuItems.get(accountName); - if (disposeables) { - disposeables.forEach(disposeable => disposeable.dispose()); - this._sessionMenuItems.delete(accountName); - } this._accounts.delete(accountName); } } @@ -262,25 +194,13 @@ export class MainThreadAuthenticationProvider extends Disposable { } login(scopes: string[]): Promise { - return this._proxy.$login(this.id, scopes).then(session => { - return { - id: session.id, - account: session.account, - getAccessToken: () => this._proxy.$getSessionAccessToken(this.id, session.id) - }; - }); + return this._proxy.$login(this.id, scopes); } async logout(sessionId: string): Promise { await this._proxy.$logout(this.id, sessionId); this.notificationService.info(nls.localize('signedOut', "Successfully signed out.")); } - - dispose(): void { - super.dispose(); - this._sessionMenuItems.forEach(item => item.forEach(d => d.dispose())); - this._sessionMenuItems.clear(); - } } @extHostNamedCustomer(MainContext.MainThreadAuthentication) @@ -294,14 +214,32 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu @IStorageService private readonly storageService: IStorageService, @INotificationService private readonly notificationService: INotificationService, @IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, - @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService + @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @IExtensionService private readonly extensionService: IExtensionService ) { super(); this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostAuthentication); + + this._register(this.authenticationService.onDidChangeSessions(e => { + this._proxy.$onDidChangeAuthenticationSessions(e.providerId, e.label, e.event); + })); + + this._register(this.authenticationService.onDidRegisterAuthenticationProvider(info => { + this._proxy.$onDidChangeAuthenticationProviders([info], []); + })); + + this._register(this.authenticationService.onDidUnregisterAuthenticationProvider(info => { + this._proxy.$onDidChangeAuthenticationProviders([], [info]); + })); } - async $registerAuthenticationProvider(id: string, displayName: string): Promise { - const provider = new MainThreadAuthenticationProvider(this._proxy, id, displayName, this.notificationService, this.storageKeysSyncRegistryService); + $getProviderIds(): Promise { + return Promise.resolve(this.authenticationService.getProviderIds()); + } + + async $registerAuthenticationProvider(id: string, label: string, supportsMultipleAccounts: boolean): Promise { + const provider = new MainThreadAuthenticationProvider(this._proxy, id, label, supportsMultipleAccounts, this.notificationService, this.storageKeysSyncRegistryService, this.storageService, this.quickInputService, this.dialogService); await provider.initialize(); this.authenticationService.registerAuthenticationProvider(id, provider); } @@ -310,21 +248,157 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu this.authenticationService.unregisterAuthenticationProvider(id); } - $onDidChangeSessions(id: string, event: modes.AuthenticationSessionsChangeEvent): void { + $ensureProvider(id: string): Promise { + return this.extensionService.activateByEvent(getAuthenticationProviderActivationEvent(id)); + } + + $sendDidChangeSessions(id: string, event: modes.AuthenticationSessionsChangeEvent): void { this.authenticationService.sessionsUpdate(id, event); } - async $getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise { - addAccountUsage(providerId, accountName, extensionName); + $getSessions(id: string): Promise> { + return this.authenticationService.getSessions(id); + } + $login(providerId: string, scopes: string[]): Promise { + return this.authenticationService.login(providerId, scopes); + } + + $logout(providerId: string, sessionId: string): Promise { + return this.authenticationService.logout(providerId, sessionId); + } + + async $requestNewSession(providerId: string, scopes: string[], extensionId: string, extensionName: string): Promise { + return this.authenticationService.requestNewSession(providerId, scopes, extensionId, extensionName); + } + + async $getSession(providerId: string, scopes: string[], extensionId: string, extensionName: string, options: { createIfNone: boolean, clearSessionPreference: boolean }): Promise { + const orderedScopes = scopes.sort().join(' '); + const sessions = (await this.$getSessions(providerId)).filter(session => session.scopes.slice().sort().join(' ') === orderedScopes); + const label = this.authenticationService.getLabel(providerId); + + if (sessions.length) { + if (!this.authenticationService.supportsMultipleAccounts(providerId)) { + const session = sessions[0]; + const allowed = await this.$getSessionsPrompt(providerId, session.account.label, label, extensionId, extensionName); + if (allowed) { + return session; + } else { + throw new Error('User did not consent to login.'); + } + } + + // On renderer side, confirm consent, ask user to choose between accounts if multiple sessions are valid + const selected = await this.$selectSession(providerId, label, extensionId, extensionName, sessions, scopes, !!options.clearSessionPreference); + return sessions.find(session => session.id === selected.id); + } else { + if (options.createIfNone) { + const isAllowed = await this.$loginPrompt(label, extensionName); + if (!isAllowed) { + throw new Error('User did not consent to login.'); + } + + const session = await this.authenticationService.login(providerId, scopes); + await this.$setTrustedExtensionAndAccountPreference(providerId, session.account.label, extensionId, extensionName, session.id); + return session; + } else { + await this.$requestNewSession(providerId, scopes, extensionId, extensionName); + return undefined; + } + } + } + + async $selectSession(providerId: string, providerName: string, extensionId: string, extensionName: string, potentialSessions: modes.AuthenticationSession[], scopes: string[], clearSessionPreference: boolean): Promise { + if (!potentialSessions.length) { + throw new Error('No potential sessions found'); + } + + if (clearSessionPreference) { + this.storageService.remove(`${extensionName}-${providerId}`, StorageScope.GLOBAL); + } else { + const existingSessionPreference = this.storageService.get(`${extensionName}-${providerId}`, StorageScope.GLOBAL); + if (existingSessionPreference) { + const matchingSession = potentialSessions.find(session => session.id === existingSessionPreference); + if (matchingSession) { + const allowed = await this.$getSessionsPrompt(providerId, matchingSession.account.label, providerName, extensionId, extensionName); + if (allowed) { + return matchingSession; + } + } + } + } + + return new Promise((resolve, reject) => { + const quickPick = this.quickInputService.createQuickPick<{ label: string, session?: modes.AuthenticationSession }>(); + quickPick.ignoreFocusOut = true; + const items: { label: string, session?: modes.AuthenticationSession }[] = potentialSessions.map(session => { + return { + label: session.account.label, + session + }; + }); + + items.push({ + label: nls.localize('useOtherAccount', "Sign in to another account") + }); + + quickPick.items = items; + quickPick.title = nls.localize( + { + key: 'selectAccount', + comment: ['The placeholder {0} is the name of an extension. {1} is the name of the type of account, such as Microsoft or GitHub.'] + }, + "The extension '{0}' wants to access a {1} account", + extensionName, + providerName); + quickPick.placeholder = nls.localize('getSessionPlateholder', "Select an account for '{0}' to use or Esc to cancel", extensionName); + + quickPick.onDidAccept(async _ => { + const selected = quickPick.selectedItems[0]; + + const session = selected.session ?? await this.authenticationService.login(providerId, scopes); + + const accountName = session.account.label; + + const allowList = readAllowedExtensions(this.storageService, providerId, accountName); + if (!allowList.find(allowed => allowed.id === extensionId)) { + allowList.push({ id: extensionId, name: extensionName }); + this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL); + } + + this.storageService.store(`${extensionName}-${providerId}`, session.id, StorageScope.GLOBAL); + + quickPick.dispose(); + resolve(session); + }); + + quickPick.onDidHide(_ => { + if (!quickPick.selectedItems[0]) { + reject('User did not consent to account access'); + } + + quickPick.dispose(); + }); + + quickPick.show(); + }); + } + + async $getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise { const allowList = readAllowedExtensions(this.storageService, providerId, accountName); const extensionData = allowList.find(extension => extension.id === extensionId); if (extensionData) { + addAccountUsage(this.storageService, providerId, accountName, extensionId, extensionName); return true; } const remoteConnection = this.remoteAgentService.getConnection(); - if (remoteConnection && remoteConnection.remoteAuthority && remoteConnection.remoteAuthority.startsWith('vsonline') && VSO_ALLOWED_EXTENSIONS.includes(extensionId)) { + const isVSO = remoteConnection !== null + ? remoteConnection.remoteAuthority.startsWith('vsonline') + : platform === Platform.Web; + + if (isVSO && VSO_ALLOWED_EXTENSIONS.includes(extensionId)) { + addAccountUsage(this.storageService, providerId, accountName, extensionId, extensionName); return true; } @@ -339,6 +413,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu const allow = choice === 0; if (allow) { + addAccountUsage(this.storageService, providerId, accountName, extensionId, extensionName); allowList.push({ id: extensionId, name: extensionName }); this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL); } @@ -359,11 +434,13 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu return choice === 0; } - async $setTrustedExtension(providerId: string, accountName: string, extensionId: string, extensionName: string): Promise { + async $setTrustedExtensionAndAccountPreference(providerId: string, accountName: string, extensionId: string, extensionName: string, sessionId: string): Promise { const allowList = readAllowedExtensions(this.storageService, providerId, accountName); if (!allowList.find(allowed => allowed.id === extensionId)) { allowList.push({ id: extensionId, name: extensionName }); this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL); } + + this.storageService.store(`${extensionName}-${providerId}`, sessionId, StorageScope.GLOBAL); } } diff --git a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts index 8568dedd56b..8ea57336612 100644 --- a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts +++ b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts @@ -94,8 +94,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape { }, { allowScripts: options.enableScripts, localResourceRoots: options.localResourceRoots ? options.localResourceRoots.map(uri => URI.revive(uri)) : undefined - }); - webview.extension = { id: extensionId, location: URI.revive(extensionLocation) }; + }, { id: extensionId, location: URI.revive(extensionLocation) }); const webviewZone = new EditorWebviewZone(editor, line, height, webview); @@ -128,12 +127,15 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape { $setOptions(handle: number, options: modes.IWebviewOptions): void { const inset = this.getInset(handle); - inset.webview.contentOptions = options; + inset.webview.contentOptions = { + ...options, + localResourceRoots: options.localResourceRoots?.map(components => URI.from(components)), + }; } async $postMessage(handle: number, value: any): Promise { const inset = this.getInset(handle); - inset.webview.sendMessage(value); + inset.webview.postMessage(value); return true; } diff --git a/src/vs/workbench/api/browser/mainThreadCommands.ts b/src/vs/workbench/api/browser/mainThreadCommands.ts index 9e0e591c640..ff817619ff7 100644 --- a/src/vs/workbench/api/browser/mainThreadCommands.ts +++ b/src/vs/workbench/api/browser/mainThreadCommands.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ICommandService, CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ExtHostContext, MainThreadCommandsShape, ExtHostCommandsShape, MainContext, IExtHostContext } from '../common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { revive } from 'vs/base/common/marshalling'; @@ -28,7 +28,7 @@ export class MainThreadCommands implements MainThreadCommandsShape { } dispose() { - this._commandRegistrations.forEach(value => value.dispose()); + dispose(this._commandRegistrations.values()); this._commandRegistrations.clear(); this._generateCommandsDocumentationRegistration.dispose(); diff --git a/src/vs/workbench/api/browser/mainThreadComments.ts b/src/vs/workbench/api/browser/mainThreadComments.ts index 31dafb2a31f..fad1a18c266 100644 --- a/src/vs/workbench/api/browser/mainThreadComments.ts +++ b/src/vs/workbench/api/browser/mainThreadComments.ts @@ -6,7 +6,6 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { keys } from 'vs/base/common/map'; import { URI, UriComponents } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { IRange } from 'vs/editor/common/core/range'; @@ -21,6 +20,7 @@ import { COMMENTS_VIEW_ID, COMMENTS_VIEW_TITLE } from 'vs/workbench/contrib/comm import { ViewContainer, IViewContainersRegistry, Extensions as ViewExtensions, ViewContainerLocation, IViewsRegistry, IViewsService, IViewDescriptorService } from 'vs/workbench/common/views'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { Codicon } from 'vs/base/common/codicons'; export class MainThreadCommentThread implements modes.CommentThread { @@ -283,7 +283,7 @@ export class MainThreadCommentController { async getDocumentComments(resource: URI, token: CancellationToken) { let ret: modes.CommentThread[] = []; - for (let thread of keys(this._threads)) { + for (let thread of [...this._threads.keys()]) { const commentThread = this._threads.get(thread)!; if (commentThread.resource === resource.toString()) { ret.push(commentThread); @@ -314,7 +314,7 @@ export class MainThreadCommentController { getAllComments(): MainThreadCommentThread[] { let ret: MainThreadCommentThread[] = []; - for (let thread of keys(this._threads)) { + for (let thread of [...this._threads.keys()]) { ret.push(this._threads.get(thread)!); } @@ -458,6 +458,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [COMMENTS_VIEW_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), storageId: COMMENTS_VIEW_TITLE, hideIfEmpty: true, + icon: Codicon.commentDiscussion.classNames, order: 10, }, ViewContainerLocation.Panel); @@ -467,6 +468,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments canToggleVisibility: false, ctorDescriptor: new SyncDescriptor(CommentsPanel), canMoveView: true, + containerIcon: Codicon.commentDiscussion.classNames, focusCommand: { id: 'workbench.action.focusCommentsPanel' } @@ -483,7 +485,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments if (!commentsPanelAlreadyConstructed && !this._openViewListener) { this._openViewListener = this._viewsService.onDidChangeViewVisibility(e => { if (e.id === COMMENTS_VIEW_ID && e.visible) { - keys(this._commentControllers).forEach(handle => { + [...this._commentControllers.keys()].forEach(handle => { let threads = this._commentControllers.get(handle)!.getAllComments(); if (threads.length) { diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index 8477fb16a29..907f2886311 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -228,10 +228,13 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb public $startDebugging(folder: UriComponents | undefined, nameOrConfig: string | IDebugConfiguration, options: IStartDebuggingOptions): Promise { const folderUri = folder ? uri.revive(folder) : undefined; const launch = this.debugService.getConfigurationManager().getLaunch(folderUri); + const parentSession = this.getSession(options.parentSessionID); const debugOptions: IDebugSessionOptions = { - noDebug: false, - parentSession: this.getSession(options.parentSessionID), - repl: options.repl + noDebug: options.noDebug, + parentSession, + repl: options.repl, + compact: options.compact, + compoundRoot: parentSession?.compoundRoot }; return this.debugService.startDebugging(launch, nameOrConfig, debugOptions).then(success => { return success; @@ -261,6 +264,18 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb return Promise.reject(new Error('debug session not found')); } + public $stopDebugging(sessionId: DebugSessionUUID | undefined): Promise { + if (sessionId) { + const session = this.debugService.getModel().getSession(sessionId, true); + if (session) { + return this.debugService.stopSession(session); + } + } else { // stop all + return this.debugService.stopSession(undefined); + } + return Promise.reject(new Error('debug session not found')); + } + public $appendDebugConsole(value: string): void { // Use warning as severity to get the orange color for messages coming from the debug extension const session = this.debugService.getViewModel().focusedSession; diff --git a/src/vs/workbench/api/browser/mainThreadDecorations.ts b/src/vs/workbench/api/browser/mainThreadDecorations.ts index 08d46a98405..e2f865dfa4e 100644 --- a/src/vs/workbench/api/browser/mainThreadDecorations.ts +++ b/src/vs/workbench/api/browser/mainThreadDecorations.ts @@ -9,33 +9,33 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ExtHostContext, MainContext, IExtHostContext, MainThreadDecorationsShape, ExtHostDecorationsShape, DecorationData, DecorationRequest } from '../common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { IDecorationsService, IDecorationData } from 'vs/workbench/services/decorations/browser/decorations'; -import { values } from 'vs/base/common/collections'; import { CancellationToken } from 'vs/base/common/cancellation'; class DecorationRequestsQueue { private _idPool = 0; - private _requests: { [id: number]: DecorationRequest } = Object.create(null); - private _resolver: { [id: number]: (data: DecorationData) => any } = Object.create(null); + private _requests = new Map(); + private _resolver = new Map any>(); private _timer: any; constructor( - private readonly _proxy: ExtHostDecorationsShape + private readonly _proxy: ExtHostDecorationsShape, + private readonly _handle: number ) { // } - enqueue(handle: number, uri: URI, token: CancellationToken): Promise { + enqueue(uri: URI, token: CancellationToken): Promise { const id = ++this._idPool; const result = new Promise(resolve => { - this._requests[id] = { id, handle, uri }; - this._resolver[id] = resolve; + this._requests.set(id, { id, uri }); + this._resolver.set(id, resolve); this._processQueue(); }); token.onCancellationRequested(() => { - delete this._requests[id]; - delete this._resolver[id]; + this._requests.delete(id); + this._resolver.delete(id); }); return result; } @@ -49,15 +49,15 @@ class DecorationRequestsQueue { // make request const requests = this._requests; const resolver = this._resolver; - this._proxy.$provideDecorations(values(requests), CancellationToken.None).then(data => { - for (const id in resolver) { - resolver[id](data[id]); + this._proxy.$provideDecorations(this._handle, [...requests.values()], CancellationToken.None).then(data => { + for (let [id, resolve] of resolver) { + resolve(data[id]); } }); // reset - this._requests = []; - this._resolver = []; + this._requests = new Map(); + this._resolver = new Map(); this._timer = undefined; }, 0); } @@ -68,14 +68,12 @@ export class MainThreadDecorations implements MainThreadDecorationsShape { private readonly _provider = new Map, IDisposable]>(); private readonly _proxy: ExtHostDecorationsShape; - private readonly _requestQueue: DecorationRequestsQueue; constructor( context: IExtHostContext, @IDecorationsService private readonly _decorationsService: IDecorationsService ) { this._proxy = context.getProxy(ExtHostContext.ExtHostDecorations); - this._requestQueue = new DecorationRequestsQueue(this._proxy); } dispose() { @@ -85,23 +83,23 @@ export class MainThreadDecorations implements MainThreadDecorationsShape { $registerDecorationProvider(handle: number, label: string): void { const emitter = new Emitter(); + const queue = new DecorationRequestsQueue(this._proxy, handle); const registration = this._decorationsService.registerDecorationsProvider({ label, onDidChange: emitter.event, - provideDecorations: (uri, token) => { - return this._requestQueue.enqueue(handle, uri, token).then(data => { - if (!data) { - return undefined; - } - const [weight, bubble, tooltip, letter, themeColor] = data; - return { - weight: weight || 0, - bubble: bubble || false, - color: themeColor && themeColor.id, - tooltip, - letter - }; - }); + provideDecorations: async (uri, token) => { + const data = await queue.enqueue(uri, token); + if (!data) { + return undefined; + } + const [weight, bubble, tooltip, letter, themeColor] = data; + return { + weight: weight ?? 0, + bubble: bubble ?? false, + color: themeColor?.id, + tooltip, + letter + }; } }); this._provider.set(handle, [emitter, registration]); diff --git a/src/vs/workbench/api/browser/mainThreadDiagnostics.ts b/src/vs/workbench/api/browser/mainThreadDiagnostics.ts index d8a3e129097..f4606720aa1 100644 --- a/src/vs/workbench/api/browser/mainThreadDiagnostics.ts +++ b/src/vs/workbench/api/browser/mainThreadDiagnostics.ts @@ -8,6 +8,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { MainThreadDiagnosticsShape, MainContext, IExtHostContext, ExtHostDiagnosticsShape, ExtHostContext } from '../common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; @extHostNamedCustomer(MainContext.MainThreadDiagnostics) export class MainThreadDiagnostics implements MainThreadDiagnosticsShape { @@ -15,15 +16,15 @@ export class MainThreadDiagnostics implements MainThreadDiagnosticsShape { private readonly _activeOwners = new Set(); private readonly _proxy: ExtHostDiagnosticsShape; - private readonly _markerService: IMarkerService; private readonly _markerListener: IDisposable; constructor( extHostContext: IExtHostContext, - @IMarkerService markerService: IMarkerService + @IMarkerService private readonly _markerService: IMarkerService, + @IUriIdentityService private readonly _uriIdentService: IUriIdentityService, ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDiagnostics); - this._markerService = markerService; + this._markerListener = this._markerService.onMarkerChanged(this._forwardMarkers, this); } @@ -59,7 +60,7 @@ export class MainThreadDiagnostics implements MainThreadDiagnosticsShape { } } } - this._markerService.changeOne(owner, URI.revive(uri), markers); + this._markerService.changeOne(owner, this._uriIdentService.asCanonicalUri(URI.revive(uri)), markers); } this._activeOwners.add(owner); } diff --git a/src/vs/workbench/api/browser/mainThreadDialogs.ts b/src/vs/workbench/api/browser/mainThreadDialogs.ts index 79c26c34570..ff3b66e935d 100644 --- a/src/vs/workbench/api/browser/mainThreadDialogs.ts +++ b/src/vs/workbench/api/browser/mainThreadDialogs.ts @@ -23,37 +23,37 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape { // } - $showOpenDialog(options: MainThreadDialogOpenOptions): Promise { + $showOpenDialog(options?: MainThreadDialogOpenOptions): Promise { return Promise.resolve(this._fileDialogService.showOpenDialog(MainThreadDialogs._convertOpenOptions(options))); } - $showSaveDialog(options: MainThreadDialogSaveOptions): Promise { + $showSaveDialog(options?: MainThreadDialogSaveOptions): Promise { return Promise.resolve(this._fileDialogService.showSaveDialog(MainThreadDialogs._convertSaveOptions(options))); } - private static _convertOpenOptions(options: MainThreadDialogOpenOptions): IOpenDialogOptions { + private static _convertOpenOptions(options?: MainThreadDialogOpenOptions): IOpenDialogOptions { const result: IOpenDialogOptions = { - openLabel: options.openLabel || undefined, - canSelectFiles: options.canSelectFiles || (!options.canSelectFiles && !options.canSelectFolders), - canSelectFolders: options.canSelectFolders, - canSelectMany: options.canSelectMany, - defaultUri: options.defaultUri ? URI.revive(options.defaultUri) : undefined, - title: options.title || undefined + openLabel: options?.openLabel || undefined, + canSelectFiles: options?.canSelectFiles || (!options?.canSelectFiles && !options?.canSelectFolders), + canSelectFolders: options?.canSelectFolders, + canSelectMany: options?.canSelectMany, + defaultUri: options?.defaultUri ? URI.revive(options.defaultUri) : undefined, + title: options?.title || undefined }; - if (options.filters) { + if (options?.filters) { result.filters = []; forEach(options.filters, entry => result.filters!.push({ name: entry.key, extensions: entry.value })); } return result; } - private static _convertSaveOptions(options: MainThreadDialogSaveOptions): ISaveDialogOptions { + private static _convertSaveOptions(options?: MainThreadDialogSaveOptions): ISaveDialogOptions { const result: ISaveDialogOptions = { - defaultUri: options.defaultUri ? URI.revive(options.defaultUri) : undefined, - saveLabel: options.saveLabel || undefined, - title: options.title || undefined + defaultUri: options?.defaultUri ? URI.revive(options.defaultUri) : undefined, + saveLabel: options?.saveLabel || undefined, + title: options?.title || undefined }; - if (options.filters) { + if (options?.filters) { result.filters = []; forEach(options.filters, entry => result.filters!.push({ name: entry.key, extensions: entry.value })); } diff --git a/src/vs/workbench/api/browser/mainThreadDocumentContentProviders.ts b/src/vs/workbench/api/browser/mainThreadDocumentContentProviders.ts index 11de21cb607..c8fb41198bf 100644 --- a/src/vs/workbench/api/browser/mainThreadDocumentContentProviders.ts +++ b/src/vs/workbench/api/browser/mainThreadDocumentContentProviders.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { onUnexpectedError } from 'vs/base/common/errors'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; @@ -35,8 +35,8 @@ export class MainThreadDocumentContentProviders implements MainThreadDocumentCon } dispose(): void { - this._resourceContentProvider.forEach(p => p.dispose()); - this._pendingUpdate.forEach(source => source.dispose()); + dispose(this._resourceContentProvider.values()); + dispose(this._pendingUpdate.values()); } $registerTextContentProvider(handle: number, scheme: string): void { diff --git a/src/vs/workbench/api/browser/mainThreadDocuments.ts b/src/vs/workbench/api/browser/mainThreadDocuments.ts index 68454e67aca..05e5e7c8089 100644 --- a/src/vs/workbench/api/browser/mainThreadDocuments.ts +++ b/src/vs/workbench/api/browser/mainThreadDocuments.ts @@ -4,28 +4,32 @@ *--------------------------------------------------------------------------------------------*/ import { toErrorMessage } from 'vs/base/common/errorMessage'; -import { IDisposable, IReference, dispose, DisposableStore } from 'vs/base/common/lifecycle'; +import { IReference, dispose, Disposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ITextModel } from 'vs/editor/common/model'; import { IModelService, shouldSynchronizeModel } from 'vs/editor/common/services/modelService'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { IFileService } from 'vs/platform/files/common/files'; +import { IFileService, FileOperation } from 'vs/platform/files/common/files'; import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/browser/mainThreadDocumentsAndEditors'; import { ExtHostContext, ExtHostDocumentsShape, IExtHostContext, MainThreadDocumentsShape } from 'vs/workbench/api/common/extHost.protocol'; import { ITextEditorModel } from 'vs/workbench/common/editor'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { toLocalResource } from 'vs/base/common/resources'; +import { toLocalResource, extUri, IExtUri } from 'vs/base/common/resources'; +import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { Emitter } from 'vs/base/common/event'; export class BoundModelReferenceCollection { - private _data = new Array<{ length: number, dispose(): void }>(); + private _data = new Array<{ uri: URI, length: number, dispose(): void }>(); private _length = 0; constructor( + private readonly _extUri: IExtUri, private readonly _maxAge: number = 1000 * 60 * 3, - private readonly _maxLength: number = 1024 * 1024 * 80 + private readonly _maxLength: number = 1024 * 1024 * 80, ) { // } @@ -34,10 +38,18 @@ export class BoundModelReferenceCollection { this._data = dispose(this._data); } - add(ref: IReference): void { + remove(uri: URI): void { + for (const entry of [...this._data] /* copy array because dispose will modify it */) { + if (this._extUri.isEqualOrParent(entry.uri, uri)) { + entry.dispose(); + } + } + } + + add(uri: URI, ref: IReference): void { const length = ref.object.textEditorModel.getValueLength(); let handle: any; - let entry: { length: number, dispose(): void }; + let entry: { uri: URI, length: number, dispose(): void }; const dispose = () => { const idx = this._data.indexOf(entry); if (idx >= 0) { @@ -48,7 +60,7 @@ export class BoundModelReferenceCollection { } }; handle = setTimeout(dispose, this._maxAge); - entry = { length, dispose }; + entry = { uri, length, dispose }; this._data.push(entry); this._length += length; @@ -62,19 +74,48 @@ export class BoundModelReferenceCollection { } } -export class MainThreadDocuments implements MainThreadDocumentsShape { +class ModelTracker extends Disposable { + + private _knownVersionId: number; + + constructor( + private readonly _model: ITextModel, + private readonly _onIsCaughtUpWithContentChanges: Emitter, + private readonly _proxy: ExtHostDocumentsShape, + private readonly _textFileService: ITextFileService, + ) { + super(); + this._knownVersionId = this._model.getVersionId(); + this._register(this._model.onDidChangeContent((e) => { + this._knownVersionId = e.versionId; + this._proxy.$acceptModelChanged(this._model.uri, e, this._textFileService.isDirty(this._model.uri)); + if (this.isCaughtUpWithContentChanges()) { + this._onIsCaughtUpWithContentChanges.fire(this._model.uri); + } + })); + } + + public isCaughtUpWithContentChanges(): boolean { + return (this._model.getVersionId() === this._knownVersionId); + } +} + +export class MainThreadDocuments extends Disposable implements MainThreadDocumentsShape { + + private _onIsCaughtUpWithContentChanges = this._register(new Emitter()); + public readonly onIsCaughtUpWithContentChanges = this._onIsCaughtUpWithContentChanges.event; private readonly _modelService: IModelService; private readonly _textModelResolverService: ITextModelService; private readonly _textFileService: ITextFileService; private readonly _fileService: IFileService; private readonly _environmentService: IWorkbenchEnvironmentService; + private readonly _uriIdentityService: IUriIdentityService; - private readonly _toDispose = new DisposableStore(); - private _modelToDisposeMap: { [modelUrl: string]: IDisposable; }; + private _modelTrackers: { [modelUrl: string]: ModelTracker; }; private readonly _proxy: ExtHostDocumentsShape; private readonly _modelIsSynced = new Set(); - private _modelReferenceCollection = new BoundModelReferenceCollection(); + private readonly _modelReferenceCollection: BoundModelReferenceCollection; constructor( documentsAndEditors: MainThreadDocumentsAndEditors, @@ -83,41 +124,64 @@ export class MainThreadDocuments implements MainThreadDocumentsShape { @ITextFileService textFileService: ITextFileService, @IFileService fileService: IFileService, @ITextModelService textModelResolverService: ITextModelService, - @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IUriIdentityService uriIdentityService: IUriIdentityService, + @IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService ) { + super(); this._modelService = modelService; this._textModelResolverService = textModelResolverService; this._textFileService = textFileService; this._fileService = fileService; this._environmentService = environmentService; + this._uriIdentityService = uriIdentityService; + + this._modelReferenceCollection = this._register(new BoundModelReferenceCollection(uriIdentityService.extUri)); this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocuments); - this._toDispose.add(documentsAndEditors.onDocumentAdd(models => models.forEach(this._onModelAdded, this))); - this._toDispose.add(documentsAndEditors.onDocumentRemove(urls => urls.forEach(this._onModelRemoved, this))); - this._toDispose.add(this._modelReferenceCollection); - this._toDispose.add(modelService.onModelModeChanged(this._onModelModeChanged, this)); + this._register(documentsAndEditors.onDocumentAdd(models => models.forEach(this._onModelAdded, this))); + this._register(documentsAndEditors.onDocumentRemove(urls => urls.forEach(this._onModelRemoved, this))); + this._register(modelService.onModelModeChanged(this._onModelModeChanged, this)); - this._toDispose.add(textFileService.files.onDidSave(e => { + this._register(textFileService.files.onDidSave(e => { if (this._shouldHandleFileEvent(e.model.resource)) { this._proxy.$acceptModelSaved(e.model.resource); } })); - this._toDispose.add(textFileService.files.onDidChangeDirty(m => { + this._register(textFileService.files.onDidChangeDirty(m => { if (this._shouldHandleFileEvent(m.resource)) { this._proxy.$acceptDirtyStateChanged(m.resource, m.isDirty()); } })); - this._modelToDisposeMap = Object.create(null); + this._register(workingCopyFileService.onDidRunWorkingCopyFileOperation(e => { + if (e.operation === FileOperation.MOVE || e.operation === FileOperation.DELETE) { + for (const { source } of e.files) { + if (source) { + this._modelReferenceCollection.remove(source); + } + } + } + })); + + this._modelTrackers = Object.create(null); } public dispose(): void { - Object.keys(this._modelToDisposeMap).forEach((modelUrl) => { - this._modelToDisposeMap[modelUrl].dispose(); + Object.keys(this._modelTrackers).forEach((modelUrl) => { + this._modelTrackers[modelUrl].dispose(); }); - this._modelToDisposeMap = Object.create(null); - this._toDispose.dispose(); + this._modelTrackers = Object.create(null); + super.dispose(); + } + + public isCaughtUpWithContentChanges(resource: URI): boolean { + const modelUrl = resource.toString(); + if (this._modelTrackers[modelUrl]) { + return this._modelTrackers[modelUrl].isCaughtUpWithContentChanges(); + } + return true; } private _shouldHandleFileEvent(resource: URI): boolean { @@ -133,9 +197,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape { } const modelUrl = model.uri; this._modelIsSynced.add(modelUrl.toString()); - this._modelToDisposeMap[modelUrl.toString()] = model.onDidChangeContent((e) => { - this._proxy.$acceptModelChanged(modelUrl, e, this._textFileService.isDirty(modelUrl)); - }); + this._modelTrackers[modelUrl.toString()] = new ModelTracker(model, this._onIsCaughtUpWithContentChanges, this._proxy, this._textFileService); } private _onModelModeChanged(event: { model: ITextModel; oldModeId: string; }): void { @@ -153,8 +215,8 @@ export class MainThreadDocuments implements MainThreadDocumentsShape { return; } this._modelIsSynced.delete(strModelUrl); - this._modelToDisposeMap[strModelUrl].dispose(); - delete this._modelToDisposeMap[strModelUrl]; + this._modelTrackers[strModelUrl].dispose(); + delete this._modelTrackers[strModelUrl]; } // --- from extension host process @@ -163,33 +225,37 @@ export class MainThreadDocuments implements MainThreadDocumentsShape { return this._textFileService.save(URI.revive(uri)).then(target => !!target); } - $tryOpenDocument(_uri: UriComponents): Promise { - const uri = URI.revive(_uri); - if (!uri.scheme || !(uri.fsPath || uri.authority)) { + $tryOpenDocument(uriData: UriComponents): Promise { + const inputUri = URI.revive(uriData); + if (!inputUri.scheme || !(inputUri.fsPath || inputUri.authority)) { return Promise.reject(new Error(`Invalid uri. Scheme and authority or path must be set.`)); } - let promise: Promise; - switch (uri.scheme) { + const canonicalUri = this._uriIdentityService.asCanonicalUri(inputUri); + + let promise: Promise; + switch (canonicalUri.scheme) { case Schemas.untitled: - promise = this._handleUntitledScheme(uri); + promise = this._handleUntitledScheme(canonicalUri); break; case Schemas.file: default: - promise = this._handleAsResourceInput(uri); + promise = this._handleAsResourceInput(canonicalUri); break; } - return promise.then(success => { - if (!success) { - return Promise.reject(new Error('cannot open ' + uri.toString())); - } else if (!this._modelIsSynced.has(uri.toString())) { - return Promise.reject(new Error('cannot open ' + uri.toString() + '. Detail: Files above 50MB cannot be synchronized with extensions.')); + return promise.then(documentUri => { + if (!documentUri) { + return Promise.reject(new Error(`cannot open ${canonicalUri.toString()}`)); + } else if (!extUri.isEqual(documentUri, canonicalUri)) { + return Promise.reject(new Error(`cannot open ${canonicalUri.toString()}. Detail: Actual document opened as ${documentUri.toString()}`)); + } else if (!this._modelIsSynced.has(canonicalUri.toString())) { + return Promise.reject(new Error(`cannot open ${canonicalUri.toString()}. Detail: Files above 50MB cannot be synchronized with extensions.`)); } else { - return undefined; + return canonicalUri; } }, err => { - return Promise.reject(new Error('cannot open ' + uri.toString() + '. Detail: ' + toErrorMessage(err))); + return Promise.reject(new Error(`cannot open ${canonicalUri.toString()}. Detail: ${toErrorMessage(err)}`)); }); } @@ -197,21 +263,20 @@ export class MainThreadDocuments implements MainThreadDocumentsShape { return this._doCreateUntitled(undefined, options ? options.language : undefined, options ? options.content : undefined); } - private _handleAsResourceInput(uri: URI): Promise { + private _handleAsResourceInput(uri: URI): Promise { return this._textModelResolverService.createModelReference(uri).then(ref => { - this._modelReferenceCollection.add(ref); - const result = !!ref.object; - return result; + this._modelReferenceCollection.add(uri, ref); + return ref.object.textEditorModel.uri; }); } - private _handleUntitledScheme(uri: URI): Promise { + private _handleUntitledScheme(uri: URI): Promise { const asLocalUri = toLocalResource(uri, this._environmentService.configuration.remoteAuthority); return this._fileService.resolve(asLocalUri).then(stats => { // don't create a new file ontop of an existing file return Promise.reject(new Error('file already exists')); }, err => { - return this._doCreateUntitled(Boolean(uri.path) ? uri : undefined).then(resource => !!resource); + return this._doCreateUntitled(Boolean(uri.path) ? uri : undefined); }); } diff --git a/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts b/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts index 78da88e8955..5f69d9ee9bc 100644 --- a/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts @@ -27,38 +27,41 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; namespace delta { export function ofSets(before: Set, after: Set): { removed: T[], added: T[] } { const removed: T[] = []; const added: T[] = []; - before.forEach(element => { + for (let element of before) { if (!after.has(element)) { removed.push(element); } - }); - after.forEach(element => { + } + for (let element of after) { if (!before.has(element)) { added.push(element); } - }); + } return { removed, added }; } export function ofMaps(before: Map, after: Map): { removed: V[], added: V[] } { const removed: V[] = []; const added: V[] = []; - before.forEach((value, index) => { + for (let [index, value] of before) { if (!after.has(index)) { removed.push(value); } - }); - after.forEach((value, index) => { + } + for (let [index, value] of after) { if (!before.has(index)) { added.push(value); } - }); + } return { removed, added }; } } @@ -263,11 +266,11 @@ class MainThreadDocumentAndEditorStateComputer { } if (candidate) { - editors.forEach(snapshot => { + for (const snapshot of editors.values()) { if (candidate === snapshot.editor) { activeEditor = snapshot.id; } - }); + } } } @@ -303,6 +306,7 @@ export class MainThreadDocumentsAndEditors { private readonly _toDispose = new DisposableStore(); private readonly _proxy: ExtHostDocumentsAndEditorsShape; + private readonly _mainThreadDocuments: MainThreadDocuments; private readonly _textEditors = new Map(); private readonly _onTextEditorAdd = new Emitter(); @@ -326,12 +330,15 @@ export class MainThreadDocumentsAndEditors { @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, @IBulkEditService bulkEditService: IBulkEditService, @IPanelService panelService: IPanelService, - @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService, + @IUriIdentityService uriIdentityService: IUriIdentityService, + @IClipboardService private readonly _clipboardService: IClipboardService, ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocumentsAndEditors); - const mainThreadDocuments = this._toDispose.add(new MainThreadDocuments(this, extHostContext, this._modelService, this._textFileService, fileService, textModelResolverService, environmentService)); - extHostContext.set(MainContext.MainThreadDocuments, mainThreadDocuments); + this._mainThreadDocuments = this._toDispose.add(new MainThreadDocuments(this, extHostContext, this._modelService, this._textFileService, fileService, textModelResolverService, environmentService, uriIdentityService, workingCopyFileService)); + extHostContext.set(MainContext.MainThreadDocuments, this._mainThreadDocuments); const mainThreadTextEditors = this._toDispose.add(new MainThreadTextEditors(this, extHostContext, codeEditorService, bulkEditService, this._editorService, this._editorGroupService)); extHostContext.set(MainContext.MainThreadTextEditors, mainThreadTextEditors); @@ -361,7 +368,7 @@ export class MainThreadDocumentsAndEditors { // added editors for (const apiEditor of delta.addedEditors) { const mainThreadEditor = new MainThreadTextEditor(apiEditor.id, apiEditor.editor.getModel(), - apiEditor.editor, { onGainedFocus() { }, onLostFocus() { } }, this._modelService); + apiEditor.editor, { onGainedFocus() { }, onLostFocus() { } }, this._mainThreadDocuments, this._modelService, this._clipboardService); this._textEditors.set(apiEditor.id, mainThreadEditor); addedEditors.push(mainThreadEditor); diff --git a/src/vs/workbench/api/browser/mainThreadEditor.ts b/src/vs/workbench/api/browser/mainThreadEditor.ts index 1cb9c614eb4..f805b7f9cbe 100644 --- a/src/vs/workbench/api/browser/mainThreadEditor.ts +++ b/src/vs/workbench/api/browser/mainThreadEditor.ts @@ -17,6 +17,10 @@ import { IApplyEditsOptions, IEditorPropertiesChangeData, IResolvedTextEditorCon import { IEditorPane } from 'vs/workbench/common/editor'; import { withNullAsUndefined } from 'vs/base/common/types'; import { equals } from 'vs/base/common/arrays'; +import { CodeEditorStateFlag, EditorState } from 'vs/editor/browser/core/editorState'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser'; +import { MainThreadDocuments } from 'vs/workbench/api/browser/mainThreadDocuments'; export interface IFocusTracker { onGainedFocus(): void; @@ -157,8 +161,10 @@ export class MainThreadTextEditorProperties { export class MainThreadTextEditor { private readonly _id: string; - private _model: ITextModel; + private readonly _model: ITextModel; + private readonly _mainThreadDocuments: MainThreadDocuments; private readonly _modelService: IModelService; + private readonly _clipboardService: IClipboardService; private readonly _modelListeners = new DisposableStore(); private _codeEditor: ICodeEditor | null; private readonly _focusTracker: IFocusTracker; @@ -172,14 +178,18 @@ export class MainThreadTextEditor { model: ITextModel, codeEditor: ICodeEditor, focusTracker: IFocusTracker, - modelService: IModelService + mainThreadDocuments: MainThreadDocuments, + modelService: IModelService, + clipboardService: IClipboardService, ) { this._id = id; this._model = model; this._codeEditor = null; this._properties = null; this._focusTracker = focusTracker; + this._mainThreadDocuments = mainThreadDocuments; this._modelService = modelService; + this._clipboardService = clipboardService; this._onPropertiesChanged = new Emitter(); @@ -192,7 +202,6 @@ export class MainThreadTextEditor { } public dispose(): void { - this._model = null!; this._modelListeners.dispose(); this._codeEditor = null; this._codeEditorListeners.dispose(); @@ -251,21 +260,66 @@ export class MainThreadTextEditor { this._focusTracker.onLostFocus(); })); + let nextSelectionChangeSource: string | null = null; + this._codeEditorListeners.add(this._mainThreadDocuments.onIsCaughtUpWithContentChanges((uri) => { + if (uri.toString() === this._model.uri.toString()) { + const selectionChangeSource = nextSelectionChangeSource; + nextSelectionChangeSource = null; + this._updatePropertiesNow(selectionChangeSource); + } + })); + + const isValidCodeEditor = () => { + // Due to event timings, it is possible that there is a model change event not yet delivered to us. + // > e.g. a model change event is emitted to a listener which then decides to update editor options + // > In this case the editor configuration change event reaches us first. + // So simply check that the model is still attached to this code editor + return (this._codeEditor && this._codeEditor.getModel() === this._model); + }; + + const updateProperties = (selectionChangeSource: string | null) => { + // Some editor events get delivered faster than model content changes. This is + // problematic, as this leads to editor properties reaching the extension host + // too soon, before the model content change that was the root cause. + // + // If this case is identified, then let's update editor properties on the next model + // content change instead. + if (this._mainThreadDocuments.isCaughtUpWithContentChanges(this._model.uri)) { + nextSelectionChangeSource = null; + this._updatePropertiesNow(selectionChangeSource); + } else { + // update editor properties on the next model content change + nextSelectionChangeSource = selectionChangeSource; + } + }; + this._codeEditorListeners.add(this._codeEditor.onDidChangeCursorSelection((e) => { // selection - this._updatePropertiesNow(e.source); + if (!isValidCodeEditor()) { + return; + } + updateProperties(e.source); })); - this._codeEditorListeners.add(this._codeEditor.onDidChangeConfiguration(() => { + this._codeEditorListeners.add(this._codeEditor.onDidChangeConfiguration((e) => { // options - this._updatePropertiesNow(null); + if (!isValidCodeEditor()) { + return; + } + updateProperties(null); })); this._codeEditorListeners.add(this._codeEditor.onDidLayoutChange(() => { // visibleRanges - this._updatePropertiesNow(null); + if (!isValidCodeEditor()) { + return; + } + updateProperties(null); })); this._codeEditorListeners.add(this._codeEditor.onDidScrollChange(() => { // visibleRanges - this._updatePropertiesNow(null); + if (!isValidCodeEditor()) { + return; + } + updateProperties(null); })); this._updatePropertiesNow(null); } @@ -454,12 +508,23 @@ export class MainThreadTextEditor { return true; } - insertSnippet(template: string, ranges: readonly IRange[], opts: IUndoStopOptions) { + async insertSnippet(template: string, ranges: readonly IRange[], opts: IUndoStopOptions) { - if (!this._codeEditor) { + if (!this._codeEditor || !this._codeEditor.hasModel()) { return false; } + // check if clipboard is required and only iff read it (async) + let clipboardText: string | undefined; + const needsTemplate = SnippetParser.guessNeedsClipboard(template); + if (needsTemplate) { + const state = new EditorState(this._codeEditor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position); + clipboardText = await this._clipboardService.readText(); + if (!state.validate(this._codeEditor)) { + return false; + } + } + const snippetController = SnippetController2.get(this._codeEditor); // // cancel previous snippet mode @@ -471,7 +536,11 @@ export class MainThreadTextEditor { this._codeEditor.focus(); // make modifications - snippetController.insert(template, { overwriteBefore: 0, overwriteAfter: 0, undoStopBefore: opts.undoStopBefore, undoStopAfter: opts.undoStopAfter }); + snippetController.insert(template, { + overwriteBefore: 0, overwriteAfter: 0, + undoStopBefore: opts.undoStopBefore, undoStopAfter: opts.undoStopAfter, + clipboardText + }); return true; } diff --git a/src/vs/workbench/api/browser/mainThreadEditors.ts b/src/vs/workbench/api/browser/mainThreadEditors.ts index dc825512734..ed1f0d0efc4 100644 --- a/src/vs/workbench/api/browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadEditors.ts @@ -24,9 +24,11 @@ import { ExtHostContext, ExtHostEditorsShape, IApplyEditsOptions, IExtHostContex import { EditorViewColumn, editorGroupToViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { openEditorWith } from 'vs/workbench/contrib/files/common/openWith'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; export class MainThreadTextEditors implements MainThreadTextEditorsShape { @@ -125,7 +127,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { // preserve pre 1.38 behaviour to not make group active when preserveFocus: true // but make sure to restore the editor to fix https://github.com/microsoft/vscode/issues/79633 activation: options.preserveFocus ? EditorActivation.RESTORE : undefined, - ignoreOverrides: true + override: false }; const input: IResourceEditorInput = { @@ -276,7 +278,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { // --- commands -CommandsRegistry.registerCommand('_workbench.open', function (accessor: ServicesAccessor, args: [URI, IEditorOptions, EditorViewColumn, string?]) { +CommandsRegistry.registerCommand('_workbench.open', async function (accessor: ServicesAccessor, args: [URI, IEditorOptions, EditorViewColumn, string?]) { const editorService = accessor.get(IEditorService); const editorGroupService = accessor.get(IEditorGroupsService); const openerService = accessor.get(IOpenerService); @@ -285,16 +287,17 @@ CommandsRegistry.registerCommand('_workbench.open', function (accessor: Services if (options || typeof position === 'number') { // use editor options or editor view column as a hint to use the editor service for opening - return editorService.openEditor({ resource, options, label }, viewColumnToEditorGroup(editorGroupService, position)).then(_ => undefined); + await editorService.openEditor({ resource, options, label }, viewColumnToEditorGroup(editorGroupService, position)); + return; } if (resource && resource.scheme === 'command') { // do not allow to execute commands from here - return Promise.resolve(undefined); + return; } // finally, delegate to opener service - return openerService.open(resource).then(_ => undefined); + await openerService.open(resource); }); CommandsRegistry.registerCommand('_workbench.openWith', (accessor: ServicesAccessor, args: [URI, string, ITextEditorOptions | undefined, EditorViewColumn | undefined]) => { @@ -306,14 +309,14 @@ CommandsRegistry.registerCommand('_workbench.openWith', (accessor: ServicesAcces const [resource, id, options, position] = args; const group = editorGroupsService.getGroup(viewColumnToEditorGroup(editorGroupsService, position)) ?? editorGroupsService.activeGroup; - const textOptions = options ? { ...options, ignoreOverrides: true } : { ignoreOverrides: true }; + const textOptions: ITextEditorOptions = options ? { ...options, override: false } : { override: false }; const input = editorService.createEditorInput({ resource }); return openEditorWith(input, id, textOptions, group, editorService, configurationService, quickInputService); }); -CommandsRegistry.registerCommand('_workbench.diff', function (accessor: ServicesAccessor, args: [URI, URI, string, string, IEditorOptions, EditorViewColumn]) { +CommandsRegistry.registerCommand('_workbench.diff', async function (accessor: ServicesAccessor, args: [URI, URI, string, string, IEditorOptions, EditorViewColumn]) { const editorService = accessor.get(IEditorService); const editorGroupService = accessor.get(IEditorGroupsService); @@ -329,5 +332,17 @@ CommandsRegistry.registerCommand('_workbench.diff', function (accessor: Services label = localize('diffLeftRightLabel', "{0} ⟷ {1}", leftResource.toString(true), rightResource.toString(true)); } - return editorService.openEditor({ leftResource, rightResource, label, description, options }, viewColumnToEditorGroup(editorGroupService, position)).then(() => undefined); + await editorService.openEditor({ leftResource, rightResource, label, description, options }, viewColumnToEditorGroup(editorGroupService, position)); +}); + +CommandsRegistry.registerCommand('_workbench.revertAllDirty', async function (accessor: ServicesAccessor) { + const environmentService = accessor.get(IEnvironmentService); + if (!environmentService.extensionTestsLocationURI) { + throw new Error('Command is only available when running extension tests.'); + } + + const workingCopyService = accessor.get(IWorkingCopyService); + for (const workingCopy of workingCopyService.dirtyWorkingCopies) { + await workingCopy.revert({ soft: true }); + } }); diff --git a/src/vs/workbench/api/browser/mainThreadExtensionService.ts b/src/vs/workbench/api/browser/mainThreadExtensionService.ts index ced7611d1e4..e0b3986200d 100644 --- a/src/vs/workbench/api/browser/mainThreadExtensionService.ts +++ b/src/vs/workbench/api/browser/mainThreadExtensionService.ts @@ -128,7 +128,7 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha } } - $onExtensionHostExit(code: number): void { + async $onExtensionHostExit(code: number): Promise { this._extensionService._onExtensionHostExit(code); } } diff --git a/src/vs/workbench/api/browser/mainThreadFileSystem.ts b/src/vs/workbench/api/browser/mainThreadFileSystem.ts index 4a26a90cd76..c68894ca077 100644 --- a/src/vs/workbench/api/browser/mainThreadFileSystem.ts +++ b/src/vs/workbench/api/browser/mainThreadFileSystem.ts @@ -25,7 +25,7 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape { } dispose(): void { - this._fileProvider.forEach(value => value.dispose()); + dispose(this._fileProvider.values()); this._fileProvider.clear(); } diff --git a/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts b/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts index d88424433fb..ca2c751ed56 100644 --- a/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts +++ b/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts @@ -4,16 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { DisposableStore } from 'vs/base/common/lifecycle'; -import { FileChangeType, IFileService, FileOperation } from 'vs/platform/files/common/files'; +import { FileChangeType, IFileService } from 'vs/platform/files/common/files'; import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { ExtHostContext, FileSystemEvents, IExtHostContext } from '../common/extHost.protocol'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IProgressService } from 'vs/platform/progress/common/progress'; import { localize } from 'vs/nls'; import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ILogService } from 'vs/platform/log/common/log'; import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; @extHostCustomer @@ -24,10 +20,6 @@ export class MainThreadFileSystemEventService { constructor( extHostContext: IExtHostContext, @IFileService fileService: IFileService, - @ITextFileService textFileService: ITextFileService, - @IProgressService progressService: IProgressService, - @IConfigurationService configService: IConfigurationService, - @ILogService logService: ILogService, @IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService ) { @@ -63,14 +55,13 @@ export class MainThreadFileSystemEventService { // BEFORE file operation workingCopyFileService.addFileOperationParticipant({ - participate: (target, source, operation, progress, timeout, token) => { - return proxy.$onWillRunFileOperation(operation, target, source, timeout, token); + participate: (files, operation, progress, timeout, token) => { + return proxy.$onWillRunFileOperation(operation, files, timeout, token); } }); // AFTER file operation - this._listener.add(textFileService.onDidCreateTextFile(e => proxy.$onDidRunFileOperation(FileOperation.CREATE, e.resource, undefined))); - this._listener.add(workingCopyFileService.onDidRunWorkingCopyFileOperation(e => proxy.$onDidRunFileOperation(e.operation, e.target, e.source))); + this._listener.add(workingCopyFileService.onDidRunWorkingCopyFileOperation(e => proxy.$onDidRunFileOperation(e.operation, e.files))); } dispose(): void { diff --git a/src/vs/workbench/api/browser/mainThreadLanguages.ts b/src/vs/workbench/api/browser/mainThreadLanguages.ts index 19bdbd3f9e2..b979dad2a2e 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguages.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguages.ts @@ -8,6 +8,9 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { MainThreadLanguagesShape, MainContext, IExtHostContext } from '../common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; +import { IPosition } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { StandardTokenType } from 'vs/editor/common/modes'; @extHostNamedCustomer(MainContext.MainThreadLanguages) export class MainThreadLanguages implements MainThreadLanguagesShape { @@ -40,4 +43,19 @@ export class MainThreadLanguages implements MainThreadLanguagesShape { this._modelService.setMode(model, this._modeService.create(languageId)); return Promise.resolve(undefined); } + + async $tokensAtPosition(resource: UriComponents, position: IPosition): Promise { + const uri = URI.revive(resource); + const model = this._modelService.getModel(uri); + if (!model) { + return undefined; + } + model.tokenizeIfCheap(position.lineNumber); + const tokens = model.getLineTokens(position.lineNumber); + const idx = tokens.findTokenIndexAtOffset(position.column - 1); + return { + type: tokens.getStandardTokenType(idx), + range: new Range(position.lineNumber, 1 + tokens.getStartOffset(idx), position.lineNumber, 1 + tokens.getEndOffset(idx)) + }; + } } diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index efdef129e3f..9f627eb2cf4 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -3,18 +3,24 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as DOM from 'vs/base/browser/dom'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; -import { MainContext, MainThreadNotebookShape, NotebookExtensionDescription, IExtHostContext, ExtHostNotebookShape, ExtHostContext } from '../common/extHost.protocol'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { MainContext, MainThreadNotebookShape, NotebookExtensionDescription, IExtHostContext, ExtHostNotebookShape, ExtHostContext, INotebookDocumentsAndEditorsDelta } from '../common/extHost.protocol'; +import { Disposable, IDisposable, combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/notebook/common/notebookService'; -import { INotebookTextModel, INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, INotebookKernelInfo, INotebookKernelInfoDto, IEditor, INotebookRendererInfo, IOutputRenderRequest, IOutputRenderResponse, INotebookDocumentFilter } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { IRelativePattern } from 'vs/base/common/glob'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { Emitter } from 'vs/base/common/event'; export class MainThreadNotebookDocument extends Disposable { private _textModel: NotebookTextModel; @@ -27,37 +33,148 @@ export class MainThreadNotebookDocument extends Disposable { private readonly _proxy: ExtHostNotebookShape, public handle: number, public viewType: string, - public uri: URI + public supportBackup: boolean, + public uri: URI, + @INotebookService readonly notebookService: INotebookService, + @IUndoRedoService readonly undoRedoService: IUndoRedoService, + @ITextModelService modelService: ITextModelService + ) { super(); - this._textModel = new NotebookTextModel(handle, viewType, uri); - this._register(this._textModel.onDidModelChange(e => { + + this._textModel = new NotebookTextModel(handle, viewType, supportBackup, uri, undoRedoService, modelService); + this._register(this._textModel.onDidModelChangeProxy(e => { this._proxy.$acceptModelChanged(this.uri, e); + this._proxy.$acceptEditorPropertiesChanged(uri, { selections: { selections: this._textModel.selections }, metadata: null }); })); this._register(this._textModel.onDidSelectionChange(e => { const selectionsChange = e ? { selections: e } : null; - this._proxy.$acceptEditorPropertiesChanged(uri, { selections: selectionsChange }); + this._proxy.$acceptEditorPropertiesChanged(uri, { selections: selectionsChange, metadata: null }); })); } - applyEdit(modelVersionId: number, edits: ICellEditOperation[]): boolean { - return this._textModel.applyEdit(modelVersionId, edits); - } - - updateRenderers(renderers: number[]) { - this._textModel.updateRenderers(renderers); - } - dispose() { - this._textModel.dispose(); + // this._textModel.dispose(); super.dispose(); } } +class DocumentAndEditorState { + static ofSets(before: Set, after: Set): { removed: T[], added: T[] } { + const removed: T[] = []; + const added: T[] = []; + before.forEach(element => { + if (!after.has(element)) { + removed.push(element); + } + }); + after.forEach(element => { + if (!before.has(element)) { + added.push(element); + } + }); + return { removed, added }; + } + + static ofMaps(before: Map, after: Map): { removed: V[], added: V[] } { + const removed: V[] = []; + const added: V[] = []; + before.forEach((value, index) => { + if (!after.has(index)) { + removed.push(value); + } + }); + after.forEach((value, index) => { + if (!before.has(index)) { + added.push(value); + } + }); + return { removed, added }; + } + + static compute(before: DocumentAndEditorState | undefined, after: DocumentAndEditorState): INotebookDocumentsAndEditorsDelta { + if (!before) { + const apiEditors = []; + for (let id in after.textEditors) { + const editor = after.textEditors.get(id)!; + apiEditors.push({ id, documentUri: editor.uri!, selections: editor!.textModel!.selections }); + } + + return { + addedDocuments: [], + addedEditors: apiEditors, + visibleEditors: [...after.visibleEditors].map(editor => editor[0]) + }; + } + const documentDelta = DocumentAndEditorState.ofSets(before.documents, after.documents); + const editorDelta = DocumentAndEditorState.ofMaps(before.textEditors, after.textEditors); + const addedAPIEditors = editorDelta.added.map(add => ({ + id: add.getId(), + documentUri: add.uri!, + selections: add.textModel!.selections || [] + })); + + const removedAPIEditors = editorDelta.removed.map(removed => removed.getId()); + + // const oldActiveEditor = before.activeEditor !== after.activeEditor ? before.activeEditor : undefined; + const newActiveEditor = before.activeEditor !== after.activeEditor ? after.activeEditor : undefined; + + const visibleEditorDelta = DocumentAndEditorState.ofMaps(before.visibleEditors, after.visibleEditors); + + return { + addedDocuments: documentDelta.added.map(e => { + return { + viewType: e.viewType, + handle: e.handle, + uri: e.uri, + metadata: e.metadata, + versionId: e.versionId, + cells: e.cells.map(cell => ({ + handle: cell.handle, + uri: cell.uri, + source: cell.textBuffer.getLinesContent(), + eol: cell.textBuffer.getEOL(), + language: cell.language, + cellKind: cell.cellKind, + outputs: cell.outputs, + metadata: cell.metadata + })), + // attachedEditor: editorId ? { + // id: editorId, + // selections: document.textModel.selections + // } : undefined + }; + }), + removedDocuments: documentDelta.removed.map(e => e.uri), + addedEditors: addedAPIEditors, + removedEditors: removedAPIEditors, + newActiveEditor: newActiveEditor, + visibleEditors: visibleEditorDelta.added.length === 0 && visibleEditorDelta.removed.length === 0 + ? undefined + : [...after.visibleEditors].map(editor => editor[0]) + }; + } + + constructor( + readonly documents: Set, + readonly textEditors: Map, + readonly activeEditor: string | null | undefined, + readonly visibleEditors: Map + ) { + // + } +} + @extHostNamedCustomer(MainContext.MainThreadNotebook) export class MainThreadNotebooks extends Disposable implements MainThreadNotebookShape { - private readonly _notebookProviders = new Map(); + private readonly _notebookProviders = new Map(); + private readonly _notebookKernels = new Map(); + private readonly _notebookKernelProviders = new Map, provider: IDisposable }>(); + private readonly _notebookRenderers = new Map(); private readonly _proxy: ExtHostNotebookShape; + private _toDisposeOnEditorRemove = new Map(); + private _currentState?: DocumentAndEditorState; + private _editorEventListenersMapping: Map = new Map(); constructor( extHostContext: IExtHostContext, @@ -73,20 +190,123 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo } async $tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], renderers: number[]): Promise { - let controller = this._notebookProviders.get(viewType); - - if (controller) { - return controller.tryApplyEdits(resource, modelVersionId, edits, renderers); + const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); + if (textModel) { + await this._notebookService.transformEditsOutputs(textModel, edits); + return textModel.$applyEdit(modelVersionId, edits, true); } return false; } + async removeNotebookTextModel(uri: URI): Promise { + // TODO@rebornix, remove cell should use emitDelta as well to ensure document/editor events are sent together + await this._proxy.$acceptDocumentAndEditorsDelta({ removedDocuments: [uri] }); + let textModelDisposableStore = this._editorEventListenersMapping.get(uri.toString()); + textModelDisposableStore?.dispose(); + this._editorEventListenersMapping.delete(URI.from(uri).toString()); + } + + private _isDeltaEmpty(delta: INotebookDocumentsAndEditorsDelta) { + if (delta.addedDocuments !== undefined && delta.addedDocuments.length > 0) { + return false; + } + + if (delta.removedDocuments !== undefined && delta.removedDocuments.length > 0) { + return false; + } + + if (delta.addedEditors !== undefined && delta.addedEditors.length > 0) { + return false; + } + + if (delta.removedEditors !== undefined && delta.removedEditors.length > 0) { + return false; + } + + if (delta.visibleEditors !== undefined && delta.visibleEditors.length > 0) { + return false; + } + + if (delta.newActiveEditor !== undefined) { + return false; + } + + return true; + } + + private _emitDelta(delta: INotebookDocumentsAndEditorsDelta) { + if (this._isDeltaEmpty(delta)) { + return; + } + + return this._proxy.$acceptDocumentAndEditorsDelta(delta); + } + registerListeners() { + this._notebookService.listNotebookEditors().forEach((e) => { + this._addNotebookEditor(e); + }); + this._register(this._notebookService.onDidChangeActiveEditor(e => { - this._proxy.$acceptDocumentAndEditorsDelta({ - newActiveEditor: e.uri + this._updateState(); + })); + + this._register(this._notebookService.onDidChangeVisibleEditors(e => { + if (this._notebookProviders.size > 0) { + if (!this._currentState) { + // no current state means we didn't even create editors in ext host yet. + return; + } + + // we can't simply update visibleEditors as we need to check if we should create editors first. + this._updateState(); + } + })); + + this._register(this._notebookService.onNotebookEditorAdd(editor => { + this._addNotebookEditor(editor); + })); + + this._register(this._notebookService.onNotebookEditorsRemove(editors => { + this._removeNotebookEditor(editors); + })); + + this._register(this._notebookService.onNotebookDocumentAdd((documents) => { + documents.forEach(doc => { + if (!this._editorEventListenersMapping.has(doc.toString())) { + const disposableStore = new DisposableStore(); + const textModel = this._notebookService.getNotebookTextModel(doc); + disposableStore.add(textModel!.onDidModelChangeProxy(e => { + this._proxy.$acceptModelChanged(textModel!.uri, e); + this._proxy.$acceptEditorPropertiesChanged(doc, { selections: { selections: textModel!.selections }, metadata: null }); + })); + disposableStore.add(textModel!.onDidSelectionChange(e => { + const selectionsChange = e ? { selections: e } : null; + this._proxy.$acceptEditorPropertiesChanged(doc, { selections: selectionsChange, metadata: null }); + })); + + this._editorEventListenersMapping.set(textModel!.uri.toString(), disposableStore); + } }); + this._updateState(); + })); + + this._register(this._notebookService.onNotebookDocumentRemove((documents) => { + documents.forEach(doc => { + this._editorEventListenersMapping.get(doc.toString())?.dispose(); + this._editorEventListenersMapping.delete(doc.toString()); + }); + + this._updateState(); + })); + + this._register(this._notebookService.onDidChangeNotebookActiveKernel(e => { + this._proxy.$acceptNotebookActiveKernelChange(e); + })); + + this._register(this._notebookService.onNotebookDocumentSaved(e => { + this._proxy.$acceptModelSaved(e); })); const updateOrder = () => { @@ -108,210 +328,353 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => { updateOrder(); })); + + const activeEditorPane = this.editorService.activeEditorPane as any | undefined; + const notebookEditor = activeEditorPane?.isNotebookEditor ? activeEditorPane.getControl() : undefined; + this._updateState(notebookEditor); } - async $registerNotebookRenderer(extension: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, handle: number, preloads: UriComponents[]): Promise { - this._notebookService.registerNotebookRenderer(handle, extension, type, selectors, preloads.map(uri => URI.revive(uri))); + private _addNotebookEditor(e: IEditor) { + this._toDisposeOnEditorRemove.set(e.getId(), combinedDisposable( + e.onDidChangeModel(() => this._updateState()), + e.onDidFocusEditorWidget(() => { + this._updateState(e); + }), + )); + + const activeEditorPane = this.editorService.activeEditorPane as any | undefined; + const notebookEditor = activeEditorPane?.isNotebookEditor ? activeEditorPane.getControl() : undefined; + this._updateState(notebookEditor); } - async $unregisterNotebookRenderer(handle: number): Promise { - this._notebookService.unregisterNotebookRenderer(handle); + private _removeNotebookEditor(editors: IEditor[]) { + editors.forEach(e => { + const sub = this._toDisposeOnEditorRemove.get(e.getId()); + if (sub) { + this._toDisposeOnEditorRemove.delete(e.getId()); + sub.dispose(); + } + }); + + this._updateState(); } - async $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string): Promise { - let controller = new MainThreadNotebookController(this._proxy, this, viewType); - this._notebookProviders.set(viewType, controller); - this._notebookService.registerNotebookController(viewType, extension, controller); + private async _updateState(focusedNotebookEditor?: IEditor) { + let activeEditor: string | null = null; + + const activeEditorPane = this.editorService.activeEditorPane as any | undefined; + if (activeEditorPane?.isNotebookEditor) { + const notebookEditor = (activeEditorPane.getControl() as INotebookEditor); + activeEditor = notebookEditor && notebookEditor.hasModel() ? notebookEditor!.getId() : null; + } + + const documentEditorsMap = new Map(); + + const editors = new Map(); + this._notebookService.listNotebookEditors().forEach(editor => { + if (editor.hasModel()) { + editors.set(editor.getId(), editor); + documentEditorsMap.set(editor.textModel!.uri.toString(), editor); + } + }); + + const visibleEditorsMap = new Map(); + this.editorService.visibleEditorPanes.forEach(editor => { + if ((editor as any).isNotebookEditor) { + const nbEditorWidget = (editor as any).getControl() as INotebookEditor; + if (nbEditorWidget && editors.has(nbEditorWidget.getId())) { + visibleEditorsMap.set(nbEditorWidget.getId(), nbEditorWidget); + } + } + }); + + const documents = new Set(); + this._notebookService.listNotebookDocuments().forEach(document => { + documents.add(document); + }); + + if (!activeEditor && focusedNotebookEditor && focusedNotebookEditor.hasModel()) { + activeEditor = focusedNotebookEditor.getId(); + } + + // editors always have view model attached, which means there is already a document in exthost. + const newState = new DocumentAndEditorState(documents, editors, activeEditor, visibleEditorsMap); + const delta = DocumentAndEditorState.compute(this._currentState, newState); + // const isEmptyChange = (!delta.addedDocuments || delta.addedDocuments.length === 0) + // && (!delta.removedDocuments || delta.removedDocuments.length === 0) + // && (!delta.addedEditors || delta.addedEditors.length === 0) + // && (!delta.removedEditors || delta.removedEditors.length === 0) + // && (delta.newActiveEditor === undefined) + + // if (!isEmptyChange) { + this._currentState = newState; + await this._emitDelta(delta); + // } + } + + async $registerNotebookRenderer(extension: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, preloads: UriComponents[]): Promise { + const renderer = new MainThreadNotebookRenderer(this._proxy, type, extension.id, URI.revive(extension.location), selectors, preloads.map(uri => URI.revive(uri))); + this._notebookRenderers.set(type, renderer); + this._notebookService.registerNotebookRenderer(type, renderer); + } + + async $unregisterNotebookRenderer(id: string): Promise { + this._notebookService.unregisterNotebookRenderer(id); + } + + async $registerNotebookProvider(_extension: NotebookExtensionDescription, _viewType: string, _supportBackup: boolean, _kernel: INotebookKernelInfoDto | undefined): Promise { + const controller: IMainNotebookController = { + kernel: _kernel, + supportBackup: _supportBackup, + reloadNotebook: async (mainthreadTextModel: NotebookTextModel) => { + const data = await this._proxy.$resolveNotebookData(_viewType, mainthreadTextModel.uri); + if (!data) { + return; + } + + mainthreadTextModel.languages = data.languages; + mainthreadTextModel.metadata = data.metadata; + + const edits: ICellEditOperation[] = [ + { editType: CellEditType.Delete, count: mainthreadTextModel.cells.length, index: 0 }, + { editType: CellEditType.Insert, index: 0, cells: data.cells } + ]; + + await this._notebookService.transformEditsOutputs(mainthreadTextModel, edits); + await new Promise(resolve => { + DOM.scheduleAtNextAnimationFrame(() => { + const ret = mainthreadTextModel!.$applyEdit(mainthreadTextModel!.versionId, edits, true); + resolve(ret); + }); + }); + }, + createNotebook: async (textModel: NotebookTextModel, backupId?: string) => { + // open notebook document + const data = await this._proxy.$resolveNotebookData(textModel.viewType, textModel.uri, backupId); + if (!data) { + return; + } + + textModel.languages = data.languages; + textModel.metadata = data.metadata; + + if (data.cells.length) { + textModel.initialize(data!.cells); + } else { + const mainCell = textModel.createCellTextModel([''], textModel.languages.length ? textModel.languages[0] : '', CellKind.Code, [], undefined); + textModel.insertTemplateCell(mainCell); + } + + this._proxy.$acceptEditorPropertiesChanged(textModel.uri, { selections: null, metadata: textModel.metadata }); + return; + }, + resolveNotebookEditor: async (viewType: string, uri: URI, editorId: string) => { + await this._proxy.$resolveNotebookEditor(viewType, uri, editorId); + }, + executeNotebookByAttachedKernel: async (viewType: string, uri: URI) => { + return this.executeNotebookByAttachedKernel(viewType, uri); + }, + cancelNotebookByAttachedKernel: async (viewType: string, uri: URI) => { + return this.cancelNotebookByAttachedKernel(viewType, uri); + }, + onDidReceiveMessage: (editorId: string, rendererType: string | undefined, message: unknown) => { + this._proxy.$onDidReceiveMessage(editorId, rendererType, message); + }, + removeNotebookDocument: async (uri: URI) => { + return this.removeNotebookTextModel(uri); + }, + executeNotebookCell: async (uri: URI, handle: number) => { + return this._proxy.$executeNotebookByAttachedKernel(_viewType, uri, handle); + }, + cancelNotebookCell: async (uri: URI, handle: number) => { + return this._proxy.$cancelNotebookByAttachedKernel(_viewType, uri, handle); + }, + save: async (uri: URI, token: CancellationToken) => { + return this._proxy.$saveNotebook(_viewType, uri, token); + }, + saveAs: async (uri: URI, target: URI, token: CancellationToken) => { + return this._proxy.$saveNotebookAs(_viewType, uri, target, token); + }, + backup: async (uri: URI, token: CancellationToken) => { + return this._proxy.$backup(_viewType, uri, token); + } + }; + + this._notebookProviders.set(_viewType, controller); + this._notebookService.registerNotebookController(_viewType, _extension, controller); return; } + async $onNotebookChange(viewType: string, uri: UriComponents): Promise { + const textModel = this._notebookService.getNotebookTextModel(URI.from(uri)); + textModel?.handleUnknownChange(); + } + async $unregisterNotebookProvider(viewType: string): Promise { this._notebookProviders.delete(viewType); this._notebookService.unregisterNotebookProvider(viewType); return; } - async $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise { - let controller = this._notebookProviders.get(viewType); + async $registerNotebookKernel(extension: NotebookExtensionDescription, id: string, label: string, selectors: (string | IRelativePattern)[], preloads: UriComponents[]): Promise { + const kernel = new MainThreadNotebookKernel(this._proxy, id, label, selectors, extension.id, URI.revive(extension.location), preloads.map(preload => URI.revive(preload))); + this._notebookKernels.set(id, kernel); + this._notebookService.registerNotebookKernel(kernel); + return; + } - if (controller) { - controller.updateLanguages(resource, languages); + async $unregisterNotebookKernel(id: string): Promise { + this._notebookKernels.delete(id); + this._notebookService.unregisterNotebookKernel(id); + return; + } + + async $registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise { + const emitter = new Emitter(); + const that = this; + const provider = this._notebookService.registerNotebookKernelProvider({ + providerExtensionId: extension.id.value, + providerDescription: extension.description, + onDidChangeKernels: emitter.event, + selector: documentFilter, + provideKernels: async (uri: URI, token: CancellationToken) => { + const kernels = await that._proxy.$provideNotebookKernels(handle, uri, token); + return kernels.map(kernel => { + return { + ...kernel, + providerHandle: handle + }; + }); + }, + resolveKernel: (editorId: string, uri: URI, kernelId: string, token: CancellationToken) => { + return that._proxy.$resolveNotebookKernel(handle, editorId, uri, kernelId, token); + }, + executeNotebook: (uri: URI, kernelId: string, cellHandle: number | undefined) => { + return that._proxy.$executeNotebookKernelFromProvider(handle, uri, kernelId, cellHandle); + }, + cancelNotebook: (uri: URI, kernelId: string, cellHandle: number | undefined) => { + return that._proxy.$cancelNotebookKernelFromProvider(handle, uri, kernelId, cellHandle); + }, + }); + this._notebookKernelProviders.set(handle, { + extension, + emitter, + provider + }); + + return; + } + + async $unregisterNotebookKernelProvider(handle: number): Promise { + const entry = this._notebookKernelProviders.get(handle); + + if (entry) { + entry.emitter.dispose(); + entry.provider.dispose(); + this._notebookKernelProviders.delete(handle); } } + $onNotebookKernelChange(handle: number): void { + const entry = this._notebookKernelProviders.get(handle); + + entry?.emitter.fire(); + } + + async $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise { + const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); + textModel?.updateLanguages(languages); + } + async $updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise { - let controller = this._notebookProviders.get(viewType); - - if (controller) { - controller.updateNotebookMetadata(resource, metadata); - } + const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); + textModel?.updateNotebookMetadata(metadata); } async $updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata): Promise { - let controller = this._notebookProviders.get(viewType); - - if (controller) { - controller.updateNotebookCellMetadata(resource, handle, metadata); - } + const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); + textModel?.updateNotebookCellMetadata(handle, metadata); } async $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise { - let controller = this._notebookProviders.get(viewType); - controller?.spliceNotebookCellOutputs(resource, cellHandle, splices, renderers); + const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); + + if (textModel) { + await this._notebookService.transformSpliceOutputs(textModel, splices); + textModel.$spliceNotebookCellOutputs(cellHandle, splices); + } } - async executeNotebook(viewType: string, uri: URI, token: CancellationToken): Promise { - return this._proxy.$executeNotebook(viewType, uri, undefined, token); + async executeNotebookByAttachedKernel(viewType: string, uri: URI): Promise { + return this._proxy.$executeNotebookByAttachedKernel(viewType, uri, undefined); } - async $postMessage(handle: number, value: any): Promise { + async cancelNotebookByAttachedKernel(viewType: string, uri: URI): Promise { + return this._proxy.$cancelNotebookByAttachedKernel(viewType, uri, undefined); + } - const activeEditorPane = this.editorService.activeEditorPane as any | undefined; - if (activeEditorPane?.isNotebookEditor) { - const notebookEditor = (activeEditorPane as INotebookEditor); - - if (notebookEditor.viewModel?.handle === handle) { - notebookEditor.postMessage(value); - return true; - } + async $postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise { + const editor = this._notebookService.getNotebookEditor(editorId) as INotebookEditor | undefined; + if (editor?.isNotebookEditor) { + editor.postMessage(forRendererId, value); + return true; } return false; } + + $onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void { + const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); + + if (textModel) { + textModel.$handleEdit(label, () => { + return this._proxy.$undoNotebook(textModel.viewType, textModel.uri, editId, textModel.isDirty); + }, () => { + return this._proxy.$redoNotebook(textModel.viewType, textModel.uri, editId, textModel.isDirty); + }); + } + } + + $onContentChange(resource: UriComponents, viewType: string): void { + const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); + textModel?.handleUnknownChange(); + } } -export class MainThreadNotebookController implements IMainNotebookController { - private _mapping: Map = new Map(); - static documentHandle: number = 0; - +export class MainThreadNotebookKernel implements INotebookKernelInfo { constructor( private readonly _proxy: ExtHostNotebookShape, - private _mainThreadNotebook: MainThreadNotebooks, - private _viewType: string + readonly id: string, + readonly label: string, + readonly selectors: (string | IRelativePattern)[], + readonly extension: ExtensionIdentifier, + readonly extensionLocation: URI, + readonly preloads: URI[] ) { } - async createNotebook(viewType: string, uri: URI, forBackup: boolean, forceReload: boolean): Promise { - let mainthreadNotebook = this._mapping.get(URI.from(uri).toString()); - - if (mainthreadNotebook) { - if (forceReload) { - const data = await this._proxy.$resolveNotebookData(viewType, uri); - if (!data) { - return; - } - - mainthreadNotebook.textModel.languages = data.languages; - mainthreadNotebook.textModel.metadata = data.metadata; - mainthreadNotebook.textModel.applyEdit(mainthreadNotebook.textModel.versionId, [ - { editType: CellEditType.Delete, count: mainthreadNotebook.textModel.cells.length, index: 0 }, - { editType: CellEditType.Insert, index: 0, cells: data.cells } - ]); - } - return mainthreadNotebook.textModel; - } - - let document = new MainThreadNotebookDocument(this._proxy, MainThreadNotebookController.documentHandle++, viewType, uri); - await this.createNotebookDocument(document); - - if (forBackup) { - return document.textModel; - } - - // open notebook document - const data = await this._proxy.$resolveNotebookData(viewType, uri); - if (!data) { - return; - } - - document.textModel.languages = data.languages; - document.textModel.metadata = data.metadata; - document.textModel.initialize(data!.cells); - - return document.textModel; - } - - async tryApplyEdits(resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], renderers: number[]): Promise { - let mainthreadNotebook = this._mapping.get(URI.from(resource).toString()); - - if (mainthreadNotebook) { - mainthreadNotebook.updateRenderers(renderers); - return mainthreadNotebook.applyEdit(modelVersionId, edits); - } - - return false; - } - - spliceNotebookCellOutputs(resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): void { - let mainthreadNotebook = this._mapping.get(URI.from(resource).toString()); - mainthreadNotebook?.textModel.updateRenderers(renderers); - mainthreadNotebook?.textModel.$spliceNotebookCellOutputs(cellHandle, splices); - } - - async executeNotebook(viewType: string, uri: URI, token: CancellationToken): Promise { - this._mainThreadNotebook.executeNotebook(viewType, uri, token); - } - - onDidReceiveMessage(uri: UriComponents, message: any): void { - this._proxy.$onDidReceiveMessage(uri, message); - } - - async createNotebookDocument(document: MainThreadNotebookDocument): Promise { - this._mapping.set(document.uri.toString(), document); - - await this._proxy.$acceptDocumentAndEditorsDelta({ - addedDocuments: [{ - viewType: document.viewType, - handle: document.handle, - uri: document.uri - }] - }); - } - - async removeNotebookDocument(notebook: INotebookTextModel): Promise { - let document = this._mapping.get(URI.from(notebook.uri).toString()); - - if (!document) { - return; - } - - await this._proxy.$acceptDocumentAndEditorsDelta({ removedDocuments: [notebook.uri] }); - document.dispose(); - this._mapping.delete(URI.from(notebook.uri).toString()); - } - - // Methods for ExtHost - - updateLanguages(resource: UriComponents, languages: string[]) { - let document = this._mapping.get(URI.from(resource).toString()); - document?.textModel.updateLanguages(languages); - } - - updateNotebookMetadata(resource: UriComponents, metadata: NotebookDocumentMetadata) { - let document = this._mapping.get(URI.from(resource).toString()); - document?.textModel.updateNotebookMetadata(metadata); - } - - updateNotebookCellMetadata(resource: UriComponents, handle: number, metadata: NotebookCellMetadata) { - let document = this._mapping.get(URI.from(resource).toString()); - document?.textModel.updateNotebookCellMetadata(handle, metadata); - } - - updateNotebookRenderers(resource: UriComponents, renderers: number[]): void { - let document = this._mapping.get(URI.from(resource).toString()); - document?.textModel.updateRenderers(renderers); - } - - async executeNotebookCell(uri: URI, handle: number, token: CancellationToken): Promise { - return this._proxy.$executeNotebook(this._viewType, uri, handle, token); - } - - async save(uri: URI, token: CancellationToken): Promise { - return this._proxy.$saveNotebook(this._viewType, uri, token); - } - - async saveAs(uri: URI, target: URI, token: CancellationToken): Promise { - return this._proxy.$saveNotebookAs(this._viewType, uri, target, token); - + async executeNotebook(viewType: string, uri: URI, handle: number | undefined): Promise { + return this._proxy.$executeNotebook2(this.id, viewType, uri, handle); + } +} + +export class MainThreadNotebookRenderer implements INotebookRendererInfo { + constructor( + private readonly _proxy: ExtHostNotebookShape, + readonly id: string, + readonly extensionId: ExtensionIdentifier, + readonly extensionLocation: URI, + readonly selectors: INotebookMimeTypeSelector, + readonly preloads: URI[] + ) { + + } + + render(uri: URI, request: IOutputRenderRequest): Promise | undefined> { + return this._proxy.$renderOutputs(uri, this.id, request); + } + + render2(uri: URI, request: IOutputRenderRequest): Promise | undefined> { + return this._proxy.$renderOutputs2(uri, this.id, request); } } diff --git a/src/vs/workbench/api/browser/mainThreadRemoteConnectionData.ts b/src/vs/workbench/api/browser/mainThreadRemoteConnectionData.ts new file mode 100644 index 00000000000..bf40ca23528 --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadRemoteConnectionData.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 { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers'; +import { ExtHostContext, IExtHostContext, ExtHostExtensionServiceShape } from '../common/extHost.protocol'; +import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; + +@extHostCustomer +export class MainThreadRemoteConnectionData extends Disposable { + + private readonly _proxy: ExtHostExtensionServiceShape; + + constructor( + extHostContext: IExtHostContext, + @IWorkbenchEnvironmentService protected readonly _environmentService: IWorkbenchEnvironmentService, + @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService + ) { + super(); + this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostExtensionService); + + const remoteAuthority = this._environmentService.configuration.remoteAuthority; + if (remoteAuthority) { + this._register(remoteAuthorityResolverService.onDidChangeConnectionData(() => { + const connectionData = remoteAuthorityResolverService.getConnectionData(remoteAuthority); + if (connectionData) { + this._proxy.$updateRemoteConnectionData(connectionData); + } + })); + } + } +} diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index e0f0c5f7189..54f6150d2f9 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -6,7 +6,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; import { assign } from 'vs/base/common/objects'; -import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle'; import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, IInputValidation } from 'vs/workbench/contrib/scm/common/scm'; import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResourceSplices, SCMGroupFeatures, MainContext, IExtHostContext } from '../common/extHost.protocol'; import { Command } from 'vs/editor/common/modes'; @@ -266,7 +266,7 @@ export class MainThreadSCM implements MainThreadSCMShape { private readonly _proxy: ExtHostSCMShape; private _repositories = new Map(); - private _inputDisposables = new Map(); + private _repositoryDisposables = new Map(); private readonly _disposables = new DisposableStore(); constructor( @@ -274,17 +274,14 @@ export class MainThreadSCM implements MainThreadSCMShape { @ISCMService private readonly scmService: ISCMService ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostSCM); - - Event.debounce(scmService.onDidChangeSelectedRepositories, (_, e) => e, 100) - (this.onDidChangeSelectedRepositories, this, this._disposables); } dispose(): void { this._repositories.forEach(r => r.dispose()); this._repositories.clear(); - this._inputDisposables.forEach(d => d.dispose()); - this._inputDisposables.clear(); + this._repositoryDisposables.forEach(d => d.dispose()); + this._repositoryDisposables.clear(); this._disposables.dispose(); } @@ -294,8 +291,16 @@ export class MainThreadSCM implements MainThreadSCMShape { const repository = this.scmService.registerSCMProvider(provider); this._repositories.set(handle, repository); - const inputDisposable = repository.input.onDidChange(value => this._proxy.$onInputBoxValueChange(handle, value)); - this._inputDisposables.set(handle, inputDisposable); + const disposable = combinedDisposable( + Event.filter(repository.onDidChangeSelection, selected => selected)(_ => this._proxy.$setSelectedSourceControl(handle)), + repository.input.onDidChange(value => this._proxy.$onInputBoxValueChange(handle, value)) + ); + + if (repository.selected) { + setTimeout(() => this._proxy.$setSelectedSourceControl(handle), 0); + } + + this._repositoryDisposables.set(handle, disposable); } $updateSourceControl(handle: number, features: SCMProviderFeatures): void { @@ -316,8 +321,8 @@ export class MainThreadSCM implements MainThreadSCMShape { return; } - this._inputDisposables.get(handle)!.dispose(); - this._inputDisposables.delete(handle); + this._repositoryDisposables.get(handle)!.dispose(); + this._repositoryDisposables.delete(handle); repository.dispose(); this._repositories.delete(handle); @@ -424,12 +429,4 @@ export class MainThreadSCM implements MainThreadSCMShape { repository.input.validateInput = async () => undefined; } } - - private onDidChangeSelectedRepositories(repositories: ISCMRepository[]): void { - const handles = repositories - .filter(r => r.provider instanceof MainThreadSCMProvider) - .map(r => (r.provider as MainThreadSCMProvider).handle); - - this._proxy.$setSelectedSourceControls(handles); - } } diff --git a/src/vs/workbench/api/browser/mainThreadSearch.ts b/src/vs/workbench/api/browser/mainThreadSearch.ts index 9d2fc19ab3a..9e9ef51ca3d 100644 --- a/src/vs/workbench/api/browser/mainThreadSearch.ts +++ b/src/vs/workbench/api/browser/mainThreadSearch.ts @@ -5,7 +5,6 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { dispose, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { values } from 'vs/base/common/map'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; @@ -139,7 +138,7 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable { return Promise.resolve(searchP).then((result: ISearchCompleteStats) => { this._searches.delete(search.id); - return { results: values(search.matches), stats: result.stats, limitHit: result.limitHit }; + return { results: Array.from(search.matches.values()), stats: result.stats, limitHit: result.limitHit }; }, err => { this._searches.delete(search.id); return Promise.reject(err); diff --git a/src/vs/workbench/api/browser/mainThreadStatusBar.ts b/src/vs/workbench/api/browser/mainThreadStatusBar.ts index d2846ff106e..d50bc8f08eb 100644 --- a/src/vs/workbench/api/browser/mainThreadStatusBar.ts +++ b/src/vs/workbench/api/browser/mainThreadStatusBar.ts @@ -9,11 +9,13 @@ import { ThemeColor } from 'vs/platform/theme/common/themeService'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { dispose } from 'vs/base/common/lifecycle'; import { Command } from 'vs/editor/common/modes'; +import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility'; @extHostNamedCustomer(MainContext.MainThreadStatusBar) export class MainThreadStatusBar implements MainThreadStatusBarShape { private readonly entries: Map = new Map(); + static readonly CODICON_REGEXP = /\$\((.*?)\)/g; constructor( _extHostContext: IExtHostContext, @@ -25,10 +27,17 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape { this.entries.clear(); } - $setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, alignment: MainThreadStatusBarAlignment, priority: number | undefined): void { + $setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, alignment: MainThreadStatusBarAlignment, priority: number | undefined, accessibilityInformation: IAccessibilityInformation): void { // if there are icons in the text use the tooltip for the aria label - const ariaLabel = text.indexOf('$(') === -1 ? text : tooltip || text; - const entry: IStatusbarEntry = { text, tooltip, command, color, ariaLabel }; + let ariaLabel: string; + let role: string | undefined = undefined; + if (accessibilityInformation) { + ariaLabel = accessibilityInformation.label; + role = accessibilityInformation.role; + } else { + ariaLabel = text ? text.replace(MainThreadStatusBar.CODICON_REGEXP, (_match, codiconName) => codiconName) : ''; + } + const entry: IStatusbarEntry = { text, tooltip, command, color, ariaLabel, role }; if (typeof priority === 'undefined') { priority = 0; diff --git a/src/vs/workbench/api/browser/mainThreadTask.ts b/src/vs/workbench/api/browser/mainThreadTask.ts index 30fd88fcb13..536fd02dbd3 100644 --- a/src/vs/workbench/api/browser/mainThreadTask.ts +++ b/src/vs/workbench/api/browser/mainThreadTask.ts @@ -7,7 +7,6 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; -import * as Objects from 'vs/base/common/objects'; import * as Types from 'vs/base/common/types'; import * as Platform from 'vs/base/common/platform'; import { IStringDictionary, forEach } from 'vs/base/common/collections'; @@ -64,7 +63,7 @@ namespace TaskProcessEndedDTO { namespace TaskDefinitionDTO { export function from(value: KeyedTaskIdentifier): TaskDefinitionDTO { - const result = Objects.assign(Object.create(null), value); + const result = Object.assign(Object.create(null), value); delete result._key; return result; } @@ -85,13 +84,13 @@ namespace TaskPresentationOptionsDTO { if (value === undefined || value === null) { return undefined; } - return Objects.assign(Object.create(null), value); + return Object.assign(Object.create(null), value); } export function to(value: TaskPresentationOptionsDTO | undefined): PresentationOptions { if (value === undefined || value === null) { return PresentationOptions.defaults; } - return Objects.assign(Object.create(null), PresentationOptions.defaults, value); + return Object.assign(Object.create(null), PresentationOptions.defaults, value); } } @@ -100,13 +99,13 @@ namespace RunOptionsDTO { if (value === undefined || value === null) { return undefined; } - return Objects.assign(Object.create(null), value); + return Object.assign(Object.create(null), value); } export function to(value: RunOptionsDTO | undefined): RunOptions { if (value === undefined || value === null) { return RunOptions.defaults; } - return Objects.assign(Object.create(null), RunOptions.defaults, value); + return Object.assign(Object.create(null), RunOptions.defaults, value); } } @@ -328,10 +327,10 @@ namespace TaskDTO { result.detail = task.configurationProperties.detail; } if (!ConfiguringTask.is(task) && task.command) { - if (task.command.runtime === RuntimeType.Process) { - result.execution = ProcessExecutionDTO.from(task.command); - } else if (task.command.runtime === RuntimeType.Shell) { - result.execution = ShellExecutionDTO.from(task.command); + switch (task.command.runtime) { + case RuntimeType.Process: result.execution = ProcessExecutionDTO.from(task.command); break; + case RuntimeType.Shell: result.execution = ShellExecutionDTO.from(task.command); break; + case RuntimeType.CustomExecution: result.execution = CustomExecutionDTO.from(task.command); break; } } if (task.configurationProperties.problemMatchers) { @@ -368,7 +367,7 @@ namespace TaskDTO { const label = nls.localize('task.label', '{0}: {1}', source.label, task.name); const definition = TaskDefinitionDTO.to(task.definition, executeOnly)!; - const id = `${task.source.extensionId}.${definition._key}`; + const id = (CustomExecutionDTO.is(task.execution!) && task._id) ? task._id : `${task.source.extensionId}.${definition._key}`; const result: ContributedTask = new ContributedTask( id, // uuidMap.getUUID(identifier) source, @@ -510,6 +509,32 @@ export class MainThreadTask implements MainThreadTaskShape { }); } + public async $getTaskExecution(value: TaskHandleDTO | TaskDTO): Promise { + if (TaskHandleDTO.is(value)) { + const workspaceFolder = typeof value.workspaceFolder === 'string' ? value.workspaceFolder : this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder)); + if (workspaceFolder) { + const task = await this._taskService.getTask(workspaceFolder, value.id, true); + if (task) { + return { + id: task._id, + task: TaskDTO.from(task) + }; + } + throw new Error('Task not found'); + } else { + throw new Error('No workspace folder'); + } + } else { + const task = TaskDTO.to(value, this._workspaceContextServer, true)!; + return { + id: task._id, + task: TaskDTO.from(task) + }; + } + } + + // Passing in a TaskHandleDTO will cause the task to get re-resolved, which is important for tasks are coming from the core, + // such as those gotten from a fetchTasks, since they can have missing configuration properties. public $executeTask(value: TaskHandleDTO | TaskDTO): Promise { return new Promise((resolve, reject) => { if (TaskHandleDTO.is(value)) { @@ -548,6 +573,7 @@ export class MainThreadTask implements MainThreadTaskShape { }); } + public $customExecutionComplete(id: string, result?: number): Promise { return new Promise((resolve, reject) => { this._taskService.getActiveTasks().then((tasks) => { @@ -587,6 +613,9 @@ export class MainThreadTask implements MainThreadTaskShape { public $registerTaskSystem(key: string, info: TaskSystemInfoDTO): void { let platform: Platform.Platform; switch (info.platform) { + case 'Web': + platform = Platform.Platform.Web; + break; case 'win32': platform = Platform.Platform.Windows; break; @@ -605,7 +634,7 @@ export class MainThreadTask implements MainThreadTaskShape { return URI.parse(`${info.scheme}://${info.authority}${path}`); }, context: this._extHostContext, - resolveVariables: (workspaceFolder: IWorkspaceFolder, toResolve: ResolveSet, target: ConfigurationTarget): Promise => { + resolveVariables: (workspaceFolder: IWorkspaceFolder, toResolve: ResolveSet, target: ConfigurationTarget): Promise => { const vars: string[] = []; toResolve.variables.forEach(item => vars.push(item)); return Promise.resolve(this._proxy.$resolveVariables(workspaceFolder.uri, { process: toResolve.process, variables: vars })).then(values => { @@ -613,8 +642,12 @@ export class MainThreadTask implements MainThreadTaskShape { forEach(values.variables, (entry) => { partiallyResolvedVars.push(entry.value); }); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { this._configurationResolverService.resolveWithInteraction(workspaceFolder, partiallyResolvedVars, 'tasks', undefined, target).then(resolvedVars => { + if (!resolvedVars) { + resolve(undefined); + } + const result: ResolvedVariables = { process: undefined, variables: new Map() @@ -642,7 +675,15 @@ export class MainThreadTask implements MainThreadTaskShape { }, getDefaultShellAndArgs: (): Promise<{ shell: string, args: string[] | string | undefined }> => { return Promise.resolve(this._proxy.$getDefaultShellAndArgs()); + }, + findExecutable: (command: string, cwd?: string, paths?: string[]): Promise => { + return this._proxy.$findExecutable(command, cwd, paths); } }); } + + async $registerSupportedExecutions(custom?: boolean, shell?: boolean, process?: boolean): Promise { + return this._taskService.registerSupportedExecutions(custom, shell, process); + } + } diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index 54b299991ec..ffd7fb0faee 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -9,12 +9,13 @@ import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceS import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { URI } from 'vs/base/common/uri'; import { StopWatch } from 'vs/base/common/stopwatch'; -import { ITerminalInstanceService, ITerminalService, ITerminalInstance, ITerminalBeforeHandleLinkEvent } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalInstanceService, ITerminalService, ITerminalInstance, ITerminalBeforeHandleLinkEvent, ITerminalExternalLinkProvider, ITerminalLink } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering'; import { IEnvironmentVariableService, ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { deserializeEnvironmentVariableCollection, serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared'; +import { ILogService } from 'vs/platform/log/common/log'; @extHostNamedCustomer(MainContext.MainThreadTerminalService) export class MainThreadTerminalService implements MainThreadTerminalServiceShape { @@ -22,10 +23,16 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape private _proxy: ExtHostTerminalServiceShape; private _remoteAuthority: string | null; private readonly _toDispose = new DisposableStore(); - private readonly _terminalProcesses = new Map>(); - private readonly _terminalProcessesReady = new Map void>(); + private readonly _terminalProcessProxies = new Map(); private _dataEventTracker: TerminalDataEventTracker | undefined; private _linkHandler: IDisposable | undefined; + /** + * A single shared terminal link provider for the exthost. When an ext registers a link + * provider, this is registered with the terminal on the renderer side and all links are + * provided through this, even from multiple ext link providers. Xterm should remove lower + * priority intersecting links itself. + */ + private _linkProvider: IDisposable | undefined; constructor( extHostContext: IExtHostContext, @@ -34,6 +41,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IEnvironmentVariableService private readonly _environmentVariableService: IEnvironmentVariableService, + @ILogService private readonly _logService: ILogService, ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTerminalService); this._remoteAuthority = extHostContext.remoteAuthority; @@ -82,11 +90,13 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._proxy.$initEnvironmentVariableCollections(serializedCollections); } - this._terminalService.extHostReady(extHostContext.remoteAuthority); + this._terminalService.extHostReady(extHostContext.remoteAuthority!); // TODO@Tyriar: remove null assertion } public dispose(): void { this._toDispose.dispose(); + this._linkHandler?.dispose(); + this._linkProvider?.dispose(); // TODO@Daniel: Should all the previously created terminals be disposed // when the extension host process goes down ? @@ -106,7 +116,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape isExtensionTerminal: launchConfig.isExtensionTerminal }; const terminal = this._terminalService.createTerminal(shellLaunchConfig); - this._terminalProcesses.set(terminal.id, new Promise(r => this._terminalProcessesReady.set(terminal.id, r))); return Promise.resolve({ id: terminal.id, name: terminal.title @@ -164,6 +173,17 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape public $stopHandlingLinks(): void { this._linkHandler?.dispose(); + this._linkHandler = undefined; + } + + public $startLinkProvider(): void { + this._linkProvider?.dispose(); + this._linkProvider = this._terminalService.registerLinkProvider(new ExtensionTerminalLinkProvider(this._proxy)); + } + + public $stopLinkProvider(): void { + this._linkProvider?.dispose(); + this._linkProvider = undefined; } private async _handleLink(e: ITerminalBeforeHandleLinkEvent): Promise { @@ -199,7 +219,8 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape executable: terminalInstance.shellLaunchConfig.executable, args: terminalInstance.shellLaunchConfig.args, cwd: terminalInstance.shellLaunchConfig.cwd, - env: terminalInstance.shellLaunchConfig.env + env: terminalInstance.shellLaunchConfig.env, + hideFromUser: terminalInstance.shellLaunchConfig.hideFromUser }; if (terminalInstance.title) { this._proxy.$acceptTerminalOpened(terminalInstance.id, terminalInstance.title, shellLaunchConfigDto); @@ -232,13 +253,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape } const proxy = request.proxy; - const ready = this._terminalProcessesReady.get(proxy.terminalId); - if (ready) { - ready(proxy); - this._terminalProcessesReady.delete(proxy.terminalId); - } else { - this._terminalProcesses.set(proxy.terminalId, Promise.resolve(proxy)); - } + this._terminalProcessProxies.set(proxy.terminalId, proxy); const shellLaunchConfigDto: IShellLaunchConfigDto = { name: request.shellLaunchConfig.name, executable: request.shellLaunchConfig.executable, @@ -246,7 +261,17 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape cwd: request.shellLaunchConfig.cwd, env: request.shellLaunchConfig.env }; - this._proxy.$spawnExtHostProcess(proxy.terminalId, shellLaunchConfigDto, request.activeWorkspaceRootUri, request.cols, request.rows, request.isWorkspaceShellAllowed); + + this._logService.trace('Spawning ext host process', { terminalId: proxy.terminalId, shellLaunchConfigDto, request }); + this._proxy.$spawnExtHostProcess( + proxy.terminalId, + shellLaunchConfigDto, + request.activeWorkspaceRootUri, + request.cols, + request.rows, + request.isWorkspaceShellAllowed + ).then(request.callback); + proxy.onInput(data => this._proxy.$acceptProcessInput(proxy.terminalId, data)); proxy.onResize(dimensions => this._proxy.$acceptProcessResize(proxy.terminalId, dimensions.cols, dimensions.rows)); proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(proxy.terminalId, immediate)); @@ -257,13 +282,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape private _onRequestStartExtensionTerminal(request: IStartExtensionTerminalRequest): void { const proxy = request.proxy; - const ready = this._terminalProcessesReady.get(proxy.terminalId); - if (!ready) { - this._terminalProcesses.set(proxy.terminalId, Promise.resolve(proxy)); - } else { - ready(proxy); - this._terminalProcessesReady.delete(proxy.terminalId); - } + this._terminalProcessProxies.set(proxy.terminalId, proxy); // Note that onReisze is not being listened to here as it needs to fire when max dimensions // change, excluding the dimension override @@ -271,7 +290,12 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape columns: request.cols, rows: request.rows } : undefined; - this._proxy.$startExtensionTerminal(proxy.terminalId, initialDimensions); + + this._proxy.$startExtensionTerminal( + proxy.terminalId, + initialDimensions + ).then(request.callback); + proxy.onInput(data => this._proxy.$acceptProcessInput(proxy.terminalId, data)); proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(proxy.terminalId, immediate)); proxy.onRequestCwd(() => this._proxy.$acceptProcessRequestCwd(proxy.terminalId)); @@ -280,38 +304,38 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape } public $sendProcessTitle(terminalId: number, title: string): void { - this._getTerminalProcess(terminalId).then(e => e.emitTitle(title)); + this._getTerminalProcess(terminalId).emitTitle(title); } public $sendProcessData(terminalId: number, data: string): void { - this._getTerminalProcess(terminalId).then(e => e.emitData(data)); + this._getTerminalProcess(terminalId).emitData(data); } public $sendProcessReady(terminalId: number, pid: number, cwd: string): void { - this._getTerminalProcess(terminalId).then(e => e.emitReady(pid, cwd)); + this._getTerminalProcess(terminalId).emitReady(pid, cwd); } public $sendProcessExit(terminalId: number, exitCode: number | undefined): void { - this._getTerminalProcess(terminalId).then(e => e.emitExit(exitCode)); - this._terminalProcesses.delete(terminalId); + this._getTerminalProcess(terminalId).emitExit(exitCode); + this._terminalProcessProxies.delete(terminalId); } public $sendOverrideDimensions(terminalId: number, dimensions: ITerminalDimensions | undefined): void { - this._getTerminalProcess(terminalId).then(e => e.emitOverrideDimensions(dimensions)); + this._getTerminalProcess(terminalId).emitOverrideDimensions(dimensions); } public $sendProcessInitialCwd(terminalId: number, initialCwd: string): void { - this._getTerminalProcess(terminalId).then(e => e.emitInitialCwd(initialCwd)); + this._getTerminalProcess(terminalId).emitInitialCwd(initialCwd); } public $sendProcessCwd(terminalId: number, cwd: string): void { - this._getTerminalProcess(terminalId).then(e => e.emitCwd(cwd)); + this._getTerminalProcess(terminalId).emitCwd(cwd); } public $sendResolvedLaunchConfig(terminalId: number, shellLaunchConfig: IShellLaunchConfig): void { const instance = this._terminalService.getInstanceFromId(terminalId); if (instance) { - this._getTerminalProcess(terminalId).then(e => e.emitResolvedShellLaunchConfig(shellLaunchConfig)); + this._getTerminalProcess(terminalId).emitResolvedShellLaunchConfig(shellLaunchConfig); } } @@ -324,7 +348,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape sw.stop(); sum += sw.elapsed(); } - this._getTerminalProcess(terminalId).then(e => e.emitLatency(sum / COUNT)); + this._getTerminalProcess(terminalId).emitLatency(sum / COUNT); } private _isPrimaryExtHost(): boolean { @@ -349,8 +373,8 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape } } - private _getTerminalProcess(terminalId: number): Promise { - const terminal = this._terminalProcesses.get(terminalId); + private _getTerminalProcess(terminalId: number): ITerminalProcessExtHostProxy { + const terminal = this._terminalProcessProxies.get(terminalId); if (!terminal) { throw new Error(`Unknown terminal: ${terminalId}`); } @@ -395,3 +419,22 @@ class TerminalDataEventTracker extends Disposable { this._register(this._bufferer.startBuffering(instance.id, instance.onData)); } } + +class ExtensionTerminalLinkProvider implements ITerminalExternalLinkProvider { + constructor( + private readonly _proxy: ExtHostTerminalServiceShape + ) { + } + + async provideLinks(instance: ITerminalInstance, line: string): Promise { + const proxy = this._proxy; + const extHostLinks = await proxy.$provideLinks(instance.id, line); + return extHostLinks.map(dto => ({ + id: dto.id, + startIndex: dto.startIndex, + length: dto.length, + label: dto.label, + activate: () => proxy.$activateLink(instance.id, dto.id) + })); + } +} diff --git a/src/vs/workbench/api/browser/mainThreadTheming.ts b/src/vs/workbench/api/browser/mainThreadTheming.ts index 4f0fa417240..d810863f852 100644 --- a/src/vs/workbench/api/browser/mainThreadTheming.ts +++ b/src/vs/workbench/api/browser/mainThreadTheming.ts @@ -25,6 +25,7 @@ export class MainThreadTheming implements MainThreadThemingShape { this._themeChangeListener = this._themeService.onDidColorThemeChange(e => { this._proxy.$onColorThemeChange(this._themeService.getColorTheme().type); }); + this._proxy.$onColorThemeChange(this._themeService.getColorTheme().type); } dispose(): void { diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index 58796f182f6..3d62a4a9e89 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -5,7 +5,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { ExtHostContext, MainThreadTreeViewsShape, ExtHostTreeViewsShape, MainContext, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; -import { ITreeViewDataProvider, ITreeItem, IViewsService, ITreeView, IViewsRegistry, ITreeViewDescriptor, IRevealOptions, Extensions } from 'vs/workbench/common/views'; +import { ITreeViewDataProvider, ITreeItem, IViewsService, ITreeView, IViewsRegistry, ITreeViewDescriptor, IRevealOptions, Extensions, ResolvableTreeItem } from 'vs/workbench/common/views'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { distinct } from 'vs/base/common/arrays'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -163,11 +163,13 @@ type TreeItemHandle = string; class TreeViewDataProvider implements ITreeViewDataProvider { private readonly itemsMap: Map = new Map(); + private hasResolve: Promise; constructor(private readonly treeViewId: string, private readonly _proxy: ExtHostTreeViewsShape, private readonly notificationService: INotificationService ) { + this.hasResolve = this._proxy.$hasResolve(this.treeViewId); } getChildren(treeItem?: ITreeItem): Promise { @@ -214,12 +216,16 @@ class TreeViewDataProvider implements ITreeViewDataProvider { return this.itemsMap.size === 0; } - private postGetChildren(elements: ITreeItem[]): ITreeItem[] { - const result: ITreeItem[] = []; + private async postGetChildren(elements: ITreeItem[]): Promise { + const result: ResolvableTreeItem[] = []; + const hasResolve = await this.hasResolve; if (elements) { for (const element of elements) { - this.itemsMap.set(element.handle, element); - result.push(element); + const resolvable = new ResolvableTreeItem(element, hasResolve ? () => { + return this._proxy.$resolve(this.treeViewId, element.handle); + } : undefined); + this.itemsMap.set(element.handle, resolvable); + result.push(resolvable); } } return result; diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 9e4d58a2cd8..0d21c18efe8 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -5,17 +5,18 @@ import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { onUnexpectedError } from 'vs/base/common/errors'; +import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore, dispose, IDisposable, IReference } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { basename } from 'vs/base/common/path'; import { isWeb } from 'vs/base/common/platform'; -import { isEqual, isEqualOrParent } from 'vs/base/common/resources'; +import { isEqual, isEqualOrParent, toLocalResource } from 'vs/base/common/resources'; import { escape } from 'vs/base/common/strings'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as modes from 'vs/editor/common/modes'; import { localize } from 'vs/nls'; +import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -38,6 +39,7 @@ import { ICreateWebViewShowOptions, IWebviewWorkbenchService, WebviewInputOption import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { IWorkingCopy, IWorkingCopyBackup, IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; @@ -141,7 +143,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this._register(_editorService.onDidActiveEditorChange(() => { const activeInput = this._editorService.activeEditor; - if (activeInput instanceof DiffEditorInput && activeInput.master instanceof WebviewInput && activeInput.details instanceof WebviewInput) { + if (activeInput instanceof DiffEditorInput && activeInput.primary instanceof WebviewInput && activeInput.secondary instanceof WebviewInput) { this.registerWebviewFromDiffEditorListeners(activeInput); } @@ -185,6 +187,15 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma }); } + dispose() { + super.dispose(); + + for (const disposable of this._editorProviders.values()) { + disposable.dispose(); + } + this._editorProviders.clear(); + } + public $createWebviewPanel( extensionData: extHostProtocol.WebviewExtensionDescription, handle: extHostProtocol.WebviewPanelHandle, @@ -252,7 +263,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma public async $postMessage(handle: extHostProtocol.WebviewPanelHandle, message: any): Promise { const webview = this.getWebviewInput(handle); - webview.webview.sendMessage(message); + webview.webview.postMessage(message); return true; } @@ -280,8 +291,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma if (webviewInput.webview.state) { try { state = JSON.parse(webviewInput.webview.state); - } catch { - // noop + } catch (e) { + console.error('Could not load webview state', e, webviewInput.webview.state); } } @@ -320,7 +331,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma options: modes.IWebviewPanelOptions, capabilities: extHostProtocol.CustomTextEditorCapabilities, supportsMultipleEditorsPerDocument: boolean, - ): DisposableStore { + ): void { if (this._editorProviders.has(viewType)) { throw new Error(`Provider for ${viewType} already registered`); } @@ -361,6 +372,17 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma } webviewInput.webview.onDispose(() => { + // If the model is still dirty, make sure we have time to save it + if (modelRef.object.isDirty()) { + const sub = modelRef.object.onDidChangeDirty(() => { + if (!modelRef.object.isDirty()) { + sub.dispose(); + modelRef.dispose(); + } + }); + return; + } + modelRef.dispose(); }); @@ -385,8 +407,6 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma })); this._editorProviders.set(viewType, disposables); - - return disposables; } public $unregisterEditorProvider(viewType: string): void { @@ -457,22 +477,22 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma } private registerWebviewFromDiffEditorListeners(diffEditorInput: DiffEditorInput): void { - const master = diffEditorInput.master as WebviewInput; - const details = diffEditorInput.details as WebviewInput; + const primary = diffEditorInput.primary as WebviewInput; + const secondary = diffEditorInput.secondary as WebviewInput; - if (this._webviewFromDiffEditorHandles.has(master.id) || this._webviewFromDiffEditorHandles.has(details.id)) { + if (this._webviewFromDiffEditorHandles.has(primary.id) || this._webviewFromDiffEditorHandles.has(secondary.id)) { return; } - this._webviewFromDiffEditorHandles.add(master.id); - this._webviewFromDiffEditorHandles.add(details.id); + this._webviewFromDiffEditorHandles.add(primary.id); + this._webviewFromDiffEditorHandles.add(secondary.id); const disposables = new DisposableStore(); - disposables.add(master.webview.onDidFocus(() => this.updateWebviewViewStates(master))); - disposables.add(details.webview.onDidFocus(() => this.updateWebviewViewStates(details))); + disposables.add(primary.webview.onDidFocus(() => this.updateWebviewViewStates(primary))); + disposables.add(secondary.webview.onDidFocus(() => this.updateWebviewViewStates(secondary))); disposables.add(diffEditorInput.onDispose(() => { - this._webviewFromDiffEditorHandles.delete(master.id); - this._webviewFromDiffEditorHandles.delete(details.id); + this._webviewFromDiffEditorHandles.delete(primary.id); + this._webviewFromDiffEditorHandles.delete(secondary.id); dispose(disposables); })); } @@ -504,8 +524,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma for (const group of this._editorGroupService.groups) { for (const input of group.editors) { if (input instanceof DiffEditorInput) { - updateViewStatesForInput(group, input, input.master); - updateViewStatesForInput(group, input, input.details); + updateViewStatesForInput(group, input, input.primary); + updateViewStatesForInput(group, input, input.secondary); } else { updateViewStatesForInput(group, input, input); } @@ -643,17 +663,20 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod fromBackup: boolean, private readonly _editable: boolean, private readonly _getEditors: () => CustomEditorInput[], - @IWorkingCopyService workingCopyService: IWorkingCopyService, - @ILabelService private readonly _labelService: ILabelService, + @IFileDialogService private readonly _fileDialogService: IFileDialogService, @IFileService private readonly _fileService: IFileService, + @ILabelService private readonly _labelService: ILabelService, @IUndoRedoService private readonly _undoService: IUndoRedoService, + @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, + @IWorkingCopyService workingCopyService: IWorkingCopyService, ) { super(); + this._fromBackup = fromBackup; + if (_editable) { this._register(workingCopyService.registerWorkingCopy(this)); } - this._fromBackup = fromBackup; } get editorResource() { @@ -711,7 +734,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod //#endregion public isReadonly() { - return this._editable; + return !this._editable; } public get viewType() { @@ -826,11 +849,20 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod return !!await this.saveCustomEditor(options); } - public async saveCustomEditor(_options?: ISaveOptions): Promise { + public async saveCustomEditor(options?: ISaveOptions): Promise { if (!this._editable) { return undefined; } - // TODO: handle save untitled case + + if (this._editorResource.scheme === Schemas.untitled) { + const targetUri = await this.suggestUntitledSavePath(options); + if (!targetUri) { + return undefined; + } + + await this.saveCustomEditorAs(this._editorResource, targetUri, options); + return targetUri; + } const savePromise = createCancelablePromise(token => this._proxy.$onSave(this._editorResource, this.viewType, token)); this._ongoingSave?.cancel(); @@ -852,6 +884,18 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod return this._editorResource; } + private suggestUntitledSavePath(options: ISaveOptions | undefined): Promise { + if (this._editorResource.scheme !== Schemas.untitled) { + throw new Error('Resource is not untitled'); + } + + const remoteAuthority = this._environmentService.configuration.remoteAuthority; + const localResrouce = toLocalResource(this._editorResource, remoteAuthority); + + + return this._fileDialogService.pickFileToSave(localResrouce, options?.availableFileSystems); + } + public async saveCustomEditorAs(resource: URI, targetResource: URI, _options?: ISaveOptions): Promise { if (this._editable) { // TODO: handle cancellation @@ -913,7 +957,12 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod this._backupId = backupId; } } catch (e) { - // Make sure state has not changed in the meantime + if (isPromiseCanceledError(e)) { + // This is expected + throw e; + } + + // Otherwise it could be a real error. Make sure state has not changed in the meantime. if (this._hotExitState === pendingState) { this._hotExitState = HotExitState.NotAllowed; } diff --git a/src/vs/workbench/api/browser/mainThreadWorkspace.ts b/src/vs/workbench/api/browser/mainThreadWorkspace.ts index bf9dc7b1088..252fa8e426e 100644 --- a/src/vs/workbench/api/browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/browser/mainThreadWorkspace.ts @@ -24,6 +24,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { withNullAsUndefined } from 'vs/base/common/types'; import { IFileService } from 'vs/platform/files/common/files'; import { IRequestService } from 'vs/platform/request/common/request'; +import { checkGlobFileExists } from 'vs/workbench/api/common/shared/workspaceContains'; @extHostNamedCustomer(MainContext.MainThreadWorkspace) export class MainThreadWorkspace implements MainThreadWorkspaceShape { @@ -188,26 +189,8 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { return search; } - $checkExists(folders: UriComponents[], includes: string[], token: CancellationToken): Promise { - const queryBuilder = this._instantiationService.createInstance(QueryBuilder); - const query = queryBuilder.file(folders.map(folder => toWorkspaceFolder(URI.revive(folder))), { - _reason: 'checkExists', - includePattern: includes.join(', '), - expandPatterns: true, - exists: true - }); - - return this._searchService.fileSearch(query, token).then( - result => { - return !!result.limitHit; - }, - err => { - if (!isPromiseCanceledError(err)) { - return Promise.reject(err); - } - - return false; - }); + $checkExists(folders: readonly UriComponents[], includes: string[], token: CancellationToken): Promise { + return this._instantiationService.invokeFunction((accessor) => checkGlobFileExists(accessor, folders, includes, token)); } // --- save & edit resources --- diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index ce84fd5028b..0762abe8c4d 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -9,7 +9,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; import * as resources from 'vs/base/common/resources'; import { ExtensionMessageCollector, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { ViewContainer, IViewsRegistry, ITreeViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID, IViewDescriptor, ViewContainerLocation } from 'vs/workbench/common/views'; -import { TreeViewPane, CustomTreeView } from 'vs/workbench/browser/parts/views/treeView'; +import { TreeViewPane } from 'vs/workbench/browser/parts/views/treeView'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { coalesce, } from 'vs/base/common/arrays'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -31,6 +31,7 @@ import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { Codicon } from 'vs/base/common/codicons'; +import { CustomTreeView } from 'vs/workbench/contrib/views/browser/treeView'; export interface IUserFriendlyViewsContainerDescriptor { id: string; @@ -80,11 +81,21 @@ interface IUserFriendlyViewDescriptor { name: string; when?: string; + icon?: string; + contextualTitle?: string; + visibility?: string; + // From 'remoteViewDescriptor' type group?: string; remoteName?: string | string[]; } +enum InitialVisibility { + Visible = 'visible', + Hidden = 'hidden', + Collapsed = 'collapsed' +} + const viewDescriptor: IJSONSchema = { type: 'object', properties: { @@ -100,6 +111,28 @@ const viewDescriptor: IJSONSchema = { description: localize('vscode.extension.contributes.view.when', 'Condition which must be true to show this view'), type: 'string' }, + icon: { + description: localize('vscode.extension.contributes.view.icon', "Path to the view icon. View icons are displayed when the name of the view cannot be shown. It is recommended that icons be in SVG, though any image file type is accepted."), + type: 'string' + }, + contextualTitle: { + description: localize('vscode.extension.contributes.view.contextualTitle', "Human-readable context for when the view is moved out of its original location. By default, the view's container name will be used. Will be shown"), + type: 'string' + }, + visibility: { + description: localize('vscode.extension.contributes.view.initialState', "Initial state of the view when the extension is first installed. Once the user has changed the view state by collapsing, moving, or hiding the view, the initial state will not be used again."), + type: 'string', + enum: [ + 'visible', + 'hidden', + 'collapsed' + ], + enumDescriptions: [ + localize('vscode.extension.contributes.view.initialState.visible', "The default initial state for view. The view will be expanded. This may have different behavior when the view container that the view is in is built in."), + localize('vscode.extension.contributes.view.initialState.hidden', "The view will not be shown in the view container, but will be discoverable through the views menu and other view entry points and can be un-hidden by the user."), + localize('vscode.extension.contributes.view.initialState.collapsed', "The view will show in the view container, but will be collapsed.") + ] + } } }; @@ -406,22 +439,25 @@ class ViewsExtensionHandler implements IWorkbenchContribution { ? container.viewOrderDelegate.getOrder(item.group) : undefined; + const icon = item.icon ? resources.joinPath(extension.description.extensionLocation, item.icon) : undefined; + const initialVisibility = this.convertInitialVisibility(item.visibility); const viewDescriptor = { id: item.id, name: item.name, ctorDescriptor: new SyncDescriptor(TreeViewPane), when: ContextKeyExpr.deserialize(item.when), - containerIcon: viewContainer?.icon, - containerTitle: viewContainer?.name, + containerIcon: icon || viewContainer?.icon, + containerTitle: item.contextualTitle || viewContainer?.name, canToggleVisibility: true, canMoveView: true, treeView: this.instantiationService.createInstance(CustomTreeView, item.id, item.name), - collapsed: this.showCollapsed(container), + collapsed: this.showCollapsed(container) || initialVisibility === InitialVisibility.Collapsed, order: order, extensionId: extension.description.identifier, originalContainerId: entry.key, group: item.group, - remoteAuthority: item.remoteName || (item).remoteAuthority // TODO@roblou - delete after remote extensions are updated + remoteAuthority: item.remoteName || (item).remoteAuthority, // TODO@roblou - delete after remote extensions are updated + hideByDefault: initialVisibility === InitialVisibility.Hidden }; viewIds.add(viewDescriptor.id); @@ -450,6 +486,13 @@ class ViewsExtensionHandler implements IWorkbenchContribution { } } + private convertInitialVisibility(value: any): InitialVisibility | undefined { + if (Object.values(InitialVisibility).includes(value)) { + return value; + } + return undefined; + } + private isValidViewDescriptors(viewDescriptors: IUserFriendlyViewDescriptor[], collector: ExtensionMessageCollector): boolean { if (!Array.isArray(viewDescriptors)) { collector.error(localize('requirearray', "views must be an array")); @@ -469,6 +512,18 @@ class ViewsExtensionHandler implements IWorkbenchContribution { collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'when')); return false; } + if (descriptor.icon && typeof descriptor.icon !== 'string') { + collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'icon')); + return false; + } + if (descriptor.contextualTitle && typeof descriptor.contextualTitle !== 'string') { + collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'contextualTitle')); + return false; + } + if (descriptor.visibility && !this.convertInitialVisibility(descriptor.visibility)) { + collector.error(localize('optenum', "property `{0}` can be omitted or must be one of {1}", 'visibility', Object.values(InitialVisibility).join(', '))); + return false; + } } return true; diff --git a/src/vs/workbench/api/common/apiCommands.ts b/src/vs/workbench/api/common/apiCommands.ts index ea4e3428172..5e55c18f426 100644 --- a/src/vs/workbench/api/common/apiCommands.ts +++ b/src/vs/workbench/api/common/apiCommands.ts @@ -97,12 +97,12 @@ CommandsRegistry.registerCommand({ export class DiffAPICommand { public static readonly ID = 'vscode.diff'; - public static execute(executor: ICommandsExecutor, left: URI, right: URI, label: string, options?: vscode.TextDocumentShowOptions): Promise { + public static execute(executor: ICommandsExecutor, left: URI, right: URI, label: string, options?: typeConverters.TextEditorOpenOptions): Promise { return executor.executeCommand('_workbench.diff', [ left, right, label, undefined, - typeConverters.TextEditorOptions.from(options), + typeConverters.TextEditorOpenOptions.from(options), options ? typeConverters.ViewColumn.from(options.viewColumn) : undefined ]); } @@ -111,7 +111,7 @@ CommandsRegistry.registerCommand(DiffAPICommand.ID, adjustHandler(DiffAPICommand export class OpenAPICommand { public static readonly ID = 'vscode.open'; - public static execute(executor: ICommandsExecutor, resource: URI, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, label?: string): Promise { + public static execute(executor: ICommandsExecutor, resource: URI, columnOrOptions?: vscode.ViewColumn | typeConverters.TextEditorOpenOptions, label?: string): Promise { let options: ITextEditorOptions | undefined; let position: EditorViewColumn | undefined; @@ -119,7 +119,7 @@ export class OpenAPICommand { if (typeof columnOrOptions === 'number') { position = typeConverters.ViewColumn.from(columnOrOptions); } else { - options = typeConverters.TextEditorOptions.from(columnOrOptions); + options = typeConverters.TextEditorOpenOptions.from(columnOrOptions); position = typeConverters.ViewColumn.from(columnOrOptions.viewColumn); } } @@ -136,14 +136,14 @@ CommandsRegistry.registerCommand(OpenAPICommand.ID, adjustHandler(OpenAPICommand export class OpenWithAPICommand { public static readonly ID = 'vscode.openWith'; - public static execute(executor: ICommandsExecutor, resource: URI, viewType: string, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions): Promise { + public static execute(executor: ICommandsExecutor, resource: URI, viewType: string, columnOrOptions?: vscode.ViewColumn | typeConverters.TextEditorOpenOptions): Promise { let options: ITextEditorOptions | undefined; let position: EditorViewColumn | undefined; if (typeof columnOrOptions === 'number') { position = typeConverters.ViewColumn.from(columnOrOptions); } else if (typeof columnOrOptions !== 'undefined') { - options = typeConverters.TextEditorOptions.from(columnOrOptions); + options = typeConverters.TextEditorOpenOptions.from(columnOrOptions); } return executor.executeCommand('_workbench.openWith', [ diff --git a/src/vs/workbench/api/common/configurationExtensionPoint.ts b/src/vs/workbench/api/common/configurationExtensionPoint.ts index 17516f5f860..83c0ab29ec7 100644 --- a/src/vs/workbench/api/common/configurationExtensionPoint.ts +++ b/src/vs/workbench/api/common/configurationExtensionPoint.ts @@ -8,11 +8,12 @@ import * as objects from 'vs/base/common/objects'; import { Registry } from 'vs/platform/registry/common/platform'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { IConfigurationNode, IConfigurationRegistry, Extensions, resourceLanguageSettingsSchemaId, IDefaultConfigurationExtension, validateProperty, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationNode, IConfigurationRegistry, Extensions, resourceLanguageSettingsSchemaId, validateProperty, ConfigurationScope, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { workspaceSettingsSchemaId, launchSchemaId, tasksSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { isObject } from 'vs/base/common/types'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { IStringDictionary } from 'vs/base/common/collections'; const configurationRegistry = Registry.as(Extensions.Configuration); @@ -92,34 +93,31 @@ const defaultConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint { if (removed.length) { - const removedDefaultConfigurations: IDefaultConfigurationExtension[] = removed.map(extension => { - const id = extension.description.identifier; - const name = extension.description.name; - const defaults = objects.deepClone(extension.value); - return { - id, name, defaults - }; - }); + const removedDefaultConfigurations = removed.map>(extension => objects.deepClone(extension.value)); configurationRegistry.deregisterDefaultConfigurations(removedDefaultConfigurations); } if (added.length) { - const addedDefaultConfigurations = added.map(extension => { - const id = extension.description.identifier; - const name = extension.description.name; - const defaults = objects.deepClone(extension.value); - return { - id, name, defaults - }; + const addedDefaultConfigurations = added.map>(extension => { + const defaults: IStringDictionary = objects.deepClone(extension.value); + for (const key of Object.keys(defaults)) { + if (!OVERRIDE_PROPERTY_PATTERN.test(key) || typeof defaults[key] !== 'object') { + extension.collector.warn(nls.localize('config.property.defaultConfiguration.warning', "Cannot register configuration defaults for '{0}'. Only defaults for language specific settings are supported.", key)); + delete defaults[key]; + } + } + return defaults; }); configurationRegistry.registerDefaultConfigurations(addedDefaultConfigurations); } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index eed6c1f9d12..10285872216 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -46,7 +46,7 @@ import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { ExtHostUrls } from 'vs/workbench/api/common/extHostUrls'; import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview'; -import { ExtHostWindow } from 'vs/workbench/api/common/extHostWindow'; +import { IExtHostWindow } from 'vs/workbench/api/common/extHostWindow'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { throwProposedApiError, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier'; @@ -75,6 +75,7 @@ import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentica import { ExtHostTimeline } from 'vs/workbench/api/common/extHostTimeline'; import { ExtHostNotebookConcatDocument } from 'vs/workbench/api/common/extHostNotebookConcatDocument'; import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; +import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer'; export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; @@ -87,6 +88,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I // services const initData = accessor.get(IExtHostInitDataService); + const extHostConsumerFileSystem = accessor.get(IExtHostConsumerFileSystem); const extensionService = accessor.get(IExtHostExtensionService); const extHostWorkspace = accessor.get(IExtHostWorkspace); const extHostConfiguration = accessor.get(IExtHostConfiguration); @@ -97,6 +99,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostLogService = accessor.get(ILogService); const extHostTunnelService = accessor.get(IExtHostTunnelService); const extHostApiDeprecation = accessor.get(IExtHostApiDeprecationService); + const extHostWindow = accessor.get(IExtHostWindow); // register addressable instances rpcProtocol.set(ExtHostContext.ExtHostLogService, extHostLogService); @@ -105,6 +108,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService); rpcProtocol.set(ExtHostContext.ExtHostStorage, extHostStorage); rpcProtocol.set(ExtHostContext.ExtHostTunnelService, extHostTunnelService); + rpcProtocol.set(ExtHostContext.ExtHostWindow, extHostWindow); // automatically create and register addressable instances const extHostDecorations = rpcProtocol.set(ExtHostContext.ExtHostDecorations, accessor.get(IExtHostDecorations)); @@ -131,10 +135,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands)); const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService)); const extHostComment = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands, extHostDocuments)); - const extHostWindow = rpcProtocol.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol)); const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress))); const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHosLabelService, new ExtHostLabelService(rpcProtocol)); - const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors)); + const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, initData.uiKind === UIKind.Web ? new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, initData.environment) : new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, initData.environment, extensionStoragePaths)); const extHostTheming = rpcProtocol.set(ExtHostContext.ExtHostTheming, new ExtHostTheming(rpcProtocol)); const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol)); const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands)); @@ -193,19 +196,22 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I get onDidChangeAuthenticationProviders(): Event { return extHostAuthentication.onDidChangeAuthenticationProviders; }, + getProviderIds(): Thenable> { + return extHostAuthentication.getProviderIds(); + }, get providerIds(): string[] { return extHostAuthentication.providerIds; }, - getSessions(providerId: string, scopes: string[]): Thenable { - return extHostAuthentication.getSessions(extension, providerId, scopes); + get providers(): ReadonlyArray { + return extHostAuthentication.providers; }, - login(providerId: string, scopes: string[]): Thenable { - return extHostAuthentication.login(extension, providerId, scopes); + getSession(providerId: string, scopes: string[], options: vscode.AuthenticationGetSessionOptions) { + return extHostAuthentication.getSession(extension, providerId, scopes, options as any); }, logout(providerId: string, sessionId: string): Thenable { return extHostAuthentication.logout(providerId, sessionId); }, - get onDidChangeSessions(): Event<{ [providerId: string]: vscode.AuthenticationSessionsChangeEvent }> { + get onDidChangeSessions(): Event { return extHostAuthentication.onDidChangeSessions; }, }; @@ -262,7 +268,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I get sessionId() { return initData.telemetryInfo.sessionId; }, get language() { return initData.environment.appLanguage; }, get appName() { return initData.environment.appName; }, - get appRoot() { return initData.environment.appRoot!.fsPath; }, + get appRoot() { return initData.environment.appRoot?.fsPath ?? ''; }, get uriScheme() { return initData.environment.appUriScheme; }, get logLevel() { checkProposedApiEnabled(extension); @@ -425,6 +431,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration): vscode.Disposable => { return extHostLanguageFeatures.setLanguageConfiguration(extension, language, configuration); + }, + getTokenInformationAtPosition(doc: vscode.TextDocument, pos: vscode.Position) { + checkProposedApiEnabled(extension); + return extHostLanguages.tokenAtPosition(doc, pos); } }; @@ -442,16 +452,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I get terminals() { return extHostTerminalService.terminals; }, - showTextDocument(documentOrUri: vscode.TextDocument | vscode.Uri, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): Thenable { - let documentPromise: Promise; - if (URI.isUri(documentOrUri)) { - documentPromise = Promise.resolve(workspace.openTextDocument(documentOrUri)); - } else { - documentPromise = Promise.resolve(documentOrUri); - } - return documentPromise.then(document => { - return extHostEditors.showTextDocument(document, columnOrOptions, preserveFocus); - }); + async showTextDocument(documentOrUri: vscode.TextDocument | vscode.Uri, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): Promise { + const document = await (URI.isUri(documentOrUri) + ? Promise.resolve(workspace.openTextDocument(documentOrUri)) + : Promise.resolve(documentOrUri)); + + return extHostEditors.showTextDocument(document, columnOrOptions, preserveFocus); }, createTextEditorDecorationType(options: vscode.DecorationRenderOptions): vscode.TextEditorDecorationType { return extHostEditors.createTextEditorDecorationType(options); @@ -525,12 +531,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I let id: string; let name: string; let alignment: number | undefined; + let accessibilityInformation: vscode.AccessibilityInformation | undefined = undefined; if (alignmentOrOptions && typeof alignmentOrOptions !== 'number') { id = alignmentOrOptions.id; name = alignmentOrOptions.name; alignment = alignmentOrOptions.alignment; priority = alignmentOrOptions.priority; + accessibilityInformation = alignmentOrOptions.accessibilityInformation; } else { id = extension.identifier.value; name = nls.localize('extensionLabel', "{0} (Extension)", extension.displayName || extension.name); @@ -538,7 +546,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I priority = priority; } - return extHostStatusBar.createStatusBarEntry(id, name, alignment, priority); + return extHostStatusBar.createStatusBarEntry(id, name, alignment, priority, accessibilityInformation); }, setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable): vscode.Disposable { return extHostStatusBar.setStatusBarMessage(text, timeoutOrThenable); @@ -575,6 +583,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension); return extHostTerminalService.registerLinkHandler(handler); }, + registerTerminalLinkProvider(handler: vscode.TerminalLinkProvider): vscode.Disposable { + checkProposedApiEnabled(extension); + return extHostTerminalService.registerLinkProvider(handler); + }, registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider): vscode.Disposable { return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider, extension); }, @@ -584,11 +596,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => { return extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializer); }, - registerCustomEditorProvider: (viewType: string, provider: vscode.CustomTextEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions } = {}) => { - return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options); - }, - registerCustomEditorProvider2: (viewType: string, provider: vscode.CustomReadonlyEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean } = {}) => { - checkProposedApiEnabled(extension); + registerCustomEditorProvider: (viewType: string, provider: vscode.CustomTextEditorProvider | vscode.CustomReadonlyEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean } = {}) => { return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options); }, registerDecorationProvider(provider: vscode.DecorationProvider) { @@ -700,8 +708,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I } return uriPromise.then(uri => { - return extHostDocuments.ensureDocumentData(uri).then(() => { - return extHostDocuments.getDocument(uri); + return extHostDocuments.ensureDocumentData(uri).then(documentData => { + return documentData.document; }); }); }, @@ -740,7 +748,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostFileSystem.registerFileSystemProvider(scheme, provider, options); }, get fs() { - return extHostFileSystem.fileSystem; + return extHostConsumerFileSystem; }, registerFileSearchProvider: (scheme: string, provider: vscode.FileSearchProvider) => { checkProposedApiEnabled(extension); @@ -862,6 +870,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I } return extHostDebugService.startDebugging(folder, nameOrConfig, parentSessionOrOptions || {}); }, + stopDebugging(session: vscode.DebugSession | undefined) { + return extHostDebugService.stopDebugging(session); + }, addBreakpoints(breakpoints: vscode.Breakpoint[]) { return extHostDebugService.addBreakpoints(breakpoints); }, @@ -910,25 +921,65 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension); return extHostNotebook.onDidCloseNotebookDocument; }, + get onDidSaveNotebookDocument(): Event { + checkProposedApiEnabled(extension); + return extHostNotebook.onDidSaveNotebookDocument; + }, + get notebookDocuments(): vscode.NotebookDocument[] { + checkProposedApiEnabled(extension); + return extHostNotebook.notebookDocuments; + }, + get visibleNotebookEditors() { + checkProposedApiEnabled(extension); + return extHostNotebook.visibleNotebookEditors; + }, + get onDidChangeVisibleNotebookEditors() { + checkProposedApiEnabled(extension); + return extHostNotebook.onDidChangeVisibleNotebookEditors; + }, + get onDidChangeActiveNotebookKernel() { + checkProposedApiEnabled(extension); + return extHostNotebook.onDidChangeActiveNotebookKernel; + }, registerNotebookContentProvider: (viewType: string, provider: vscode.NotebookContentProvider) => { checkProposedApiEnabled(extension); return extHostNotebook.registerNotebookContentProvider(extension, viewType, provider); }, + registerNotebookKernel: (id: string, selector: vscode.GlobPattern[], kernel: vscode.NotebookKernel) => { + checkProposedApiEnabled(extension); + return extHostNotebook.registerNotebookKernel(extension, id, selector, kernel); + }, + registerNotebookKernelProvider: (selector: vscode.NotebookDocumentFilter, provider: vscode.NotebookKernelProvider) => { + checkProposedApiEnabled(extension); + return extHostNotebook.registerNotebookKernelProvider(extension, selector, provider); + }, registerNotebookOutputRenderer: (type: string, outputFilter: vscode.NotebookOutputSelector, renderer: vscode.NotebookOutputRenderer) => { checkProposedApiEnabled(extension); return extHostNotebook.registerNotebookOutputRenderer(type, extension, outputFilter, renderer); }, - get activeNotebookDocument(): vscode.NotebookDocument | undefined { - checkProposedApiEnabled(extension); - return extHostNotebook.activeNotebookDocument; - }, get activeNotebookEditor(): vscode.NotebookEditor | undefined { checkProposedApiEnabled(extension); return extHostNotebook.activeNotebookEditor; }, - onDidChangeNotebookDocument(listener, thisArgs?, disposables?) { + onDidChangeActiveNotebookEditor(listener, thisArgs?, disposables?) { checkProposedApiEnabled(extension); - return extHostNotebook.onDidChangeNotebookDocument(listener, thisArgs, disposables); + return extHostNotebook.onDidChangeActiveNotebookEditor(listener, thisArgs, disposables); + }, + onDidChangeNotebookCells(listener, thisArgs?, disposables?) { + checkProposedApiEnabled(extension); + return extHostNotebook.onDidChangeNotebookCells(listener, thisArgs, disposables); + }, + onDidChangeCellOutputs(listener, thisArgs?, disposables?) { + checkProposedApiEnabled(extension); + return extHostNotebook.onDidChangeCellOutputs(listener, thisArgs, disposables); + }, + onDidChangeCellLanguage(listener, thisArgs?, disposables?) { + checkProposedApiEnabled(extension); + return extHostNotebook.onDidChangeCellLanguage(listener, thisArgs, disposables); + }, + onDidChangeCellMetadata(listener, thisArgs?, disposables?) { + checkProposedApiEnabled(extension); + return extHostNotebook.onDidChangeCellMetadata(listener, thisArgs, disposables); }, createConcatTextDocument(notebook, selector) { checkProposedApiEnabled(extension); @@ -1028,6 +1079,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I SnippetString: extHostTypes.SnippetString, SourceBreakpoint: extHostTypes.SourceBreakpoint, SourceControlInputBoxValidationType: extHostTypes.SourceControlInputBoxValidationType, + StandardTokenType: extHostTypes.StandardTokenType, StatusBarAlignment: extHostTypes.StatusBarAlignment, SymbolInformation: extHostTypes.SymbolInformation, SymbolKind: extHostTypes.SymbolKind, @@ -1064,7 +1116,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I TimelineItem: extHostTypes.TimelineItem, CellKind: extHostTypes.CellKind, CellOutputKind: extHostTypes.CellOutputKind, - NotebookCellRunState: extHostTypes.NotebookCellRunState + NotebookCellRunState: extHostTypes.NotebookCellRunState, + NotebookRunState: extHostTypes.NotebookRunState }; }; } diff --git a/src/vs/workbench/services/extensions/worker/extHost.services.ts b/src/vs/workbench/api/common/extHost.common.services.ts similarity index 76% rename from src/vs/workbench/services/extensions/worker/extHost.services.ts rename to src/vs/workbench/api/common/extHost.common.services.ts index 100864519dc..7d25f421282 100644 --- a/src/vs/workbench/services/extensions/worker/extHost.services.ts +++ b/src/vs/workbench/api/common/extHost.common.services.ts @@ -14,31 +14,26 @@ import { IExtHostTerminalService, WorkerExtHostTerminalService } from 'vs/workbe import { IExtHostTask, WorkerExtHostTask } from 'vs/workbench/api/common/extHostTask'; import { IExtHostDebugService, WorkerExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService'; import { IExtHostSearch, ExtHostSearch } from 'vs/workbench/api/common/extHostSearch'; -import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; -import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; +import { IExtensionStoragePaths, ExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; import { IExtHostStorage, ExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; -import { ExtHostExtensionService } from 'vs/workbench/api/worker/extHostExtensionService'; -import { ILogService } from 'vs/platform/log/common/log'; -import { ExtHostLogService } from 'vs/workbench/api/worker/extHostLogService'; import { IExtHostTunnelService, ExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; import { IExtHostApiDeprecationService, ExtHostApiDeprecationService, } from 'vs/workbench/api/common/extHostApiDeprecationService'; -import { NotImplementedProxy } from 'vs/base/common/types'; +import { IExtHostWindow, ExtHostWindow } from 'vs/workbench/api/common/extHostWindow'; +import { ExtHostConsumerFileSystem, IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer'; -// register singleton services -registerSingleton(ILogService, ExtHostLogService); +registerSingleton(IExtensionStoragePaths, ExtensionStoragePaths); registerSingleton(IExtHostApiDeprecationService, ExtHostApiDeprecationService); -registerSingleton(IExtHostOutputService, ExtHostOutputService); -registerSingleton(IExtHostWorkspace, ExtHostWorkspace); -registerSingleton(IExtHostDecorations, ExtHostDecorations); -registerSingleton(IExtHostConfiguration, ExtHostConfiguration); registerSingleton(IExtHostCommands, ExtHostCommands); -registerSingleton(IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors); -registerSingleton(IExtHostStorage, ExtHostStorage); -registerSingleton(IExtHostExtensionService, ExtHostExtensionService); -registerSingleton(IExtHostSearch, ExtHostSearch); -registerSingleton(IExtHostTunnelService, ExtHostTunnelService); - -registerSingleton(IExtHostTerminalService, WorkerExtHostTerminalService); -registerSingleton(IExtHostTask, WorkerExtHostTask); +registerSingleton(IExtHostConfiguration, ExtHostConfiguration); +registerSingleton(IExtHostConsumerFileSystem, ExtHostConsumerFileSystem); registerSingleton(IExtHostDebugService, WorkerExtHostDebugService); -registerSingleton(IExtensionStoragePaths, class extends NotImplementedProxy(String(IExtensionStoragePaths)) { whenReady = Promise.resolve(); }); +registerSingleton(IExtHostDecorations, ExtHostDecorations); +registerSingleton(IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors); +registerSingleton(IExtHostOutputService, ExtHostOutputService); +registerSingleton(IExtHostSearch, ExtHostSearch); +registerSingleton(IExtHostStorage, ExtHostStorage); +registerSingleton(IExtHostTask, WorkerExtHostTask); +registerSingleton(IExtHostTerminalService, WorkerExtHostTerminalService); +registerSingleton(IExtHostTunnelService, ExtHostTunnelService); +registerSingleton(IExtHostWindow, ExtHostWindow); +registerSingleton(IExtHostWorkspace, ExtHostWorkspace); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index f5a630f645d..ee24e5b3495 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -31,7 +31,7 @@ import { LogLevel } from 'vs/platform/log/common/log'; import { IMarkerData } from 'vs/platform/markers/common/markers'; import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress'; import * as quickInput from 'vs/platform/quickinput/common/quickInput'; -import { RemoteAuthorityResolverErrorCode, ResolverResult, TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { RemoteAuthorityResolverErrorCode, ResolverResult, TunnelDescription, IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver'; import * as statusbar from 'vs/workbench/services/statusbar/common/statusbar'; import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings'; import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; @@ -41,7 +41,7 @@ import * as tasks from 'vs/workbench/api/common/shared/tasks'; import { IRevealOptions, ITreeItem } from 'vs/workbench/common/views'; import { IAdapterDescriptor, IConfig, IDebugSessionReplMode } from 'vs/workbench/contrib/debug/common/debug'; import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder'; -import { ITerminalDimensions, IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalDimensions, IShellLaunchConfig, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions'; import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import * as search from 'vs/workbench/services/search/common/search'; @@ -51,11 +51,12 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; import { TunnelOptions } from 'vs/platform/remote/common/tunnel'; import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline'; import { revive } from 'vs/base/common/marshalling'; -import { INotebookMimeTypeSelector, IOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEvent, NotebookDataDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookMimeTypeSelector, IProcessedOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEvent, NotebookDataDto, INotebookKernelInfoDto, IMainCellDto, IOutputRenderRequest, IOutputRenderResponse, INotebookDocumentFilter, INotebookKernelInfoDto2 } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import { Dto } from 'vs/base/common/types'; import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes'; +import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility'; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; @@ -63,11 +64,10 @@ export interface IEnvironment { appRoot?: URI; appLanguage: string; appUriScheme: string; - appSettingsHome?: URI; extensionDevelopmentLocationURI?: URI[]; extensionTestsLocationURI?: URI; globalStorageHome: URI; - userHome: URI; + workspaceStorageHome: URI; webviewResourceRoot: string; webviewCspSource: string; useHostProxy?: boolean; @@ -98,7 +98,7 @@ export interface IInitData { logsLocation: URI; logFile: URI; autoStart: boolean; - remote: { isRemote: boolean; authority: string | undefined; }; + remote: { isRemote: boolean; authority: string | undefined; connectionData: IRemoteConnectionData | null; }; uiKind: UIKind; } @@ -107,7 +107,7 @@ export interface IConfigurationInitData extends IConfigurationData { } export interface IExtHostContext extends IRPCProtocol { - remoteAuthority: string; + remoteAuthority: string | null; } export interface IMainContext extends IRPCProtocol { @@ -157,12 +157,21 @@ export interface MainThreadCommentsShape extends IDisposable { } export interface MainThreadAuthenticationShape extends IDisposable { - $registerAuthenticationProvider(id: string, displayName: string): void; + $registerAuthenticationProvider(id: string, label: string, supportsMultipleAccounts: boolean): void; $unregisterAuthenticationProvider(id: string): void; - $onDidChangeSessions(providerId: string, event: modes.AuthenticationSessionsChangeEvent): void; + $ensureProvider(id: string): Promise; + $getProviderIds(): Promise; + $sendDidChangeSessions(providerId: string, event: modes.AuthenticationSessionsChangeEvent): void; + $getSession(providerId: string, scopes: string[], extensionId: string, extensionName: string, options: { createIfNone?: boolean, clearSessionPreference?: boolean }): Promise; + $selectSession(providerId: string, providerName: string, extensionId: string, extensionName: string, potentialSessions: modes.AuthenticationSession[], scopes: string[], clearSessionPreference: boolean): Promise; $getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise; $loginPrompt(providerName: string, extensionName: string): Promise; - $setTrustedExtension(providerId: string, accountName: string, extensionId: string, extensionName: string): Promise; + $setTrustedExtensionAndAccountPreference(providerId: string, accountName: string, extensionId: string, extensionName: string, sessionId: string): Promise; + $requestNewSession(providerId: string, scopes: string[], extensionId: string, extensionName: string): Promise; + + $getSessions(providerId: string): Promise>; + $login(providerId: string, scopes: string[]): Promise; + $logout(providerId: string, sessionId: string): Promise; } export interface MainThreadConfigurationShape extends IDisposable { @@ -193,8 +202,8 @@ export interface MainThreadDialogSaveOptions { } export interface MainThreadDiaglogsShape extends IDisposable { - $showOpenDialog(options: MainThreadDialogOpenOptions): Promise; - $showSaveDialog(options: MainThreadDialogSaveOptions): Promise; + $showOpenDialog(options?: MainThreadDialogOpenOptions): Promise; + $showSaveDialog(options?: MainThreadDialogSaveOptions): Promise; } export interface MainThreadDecorationsShape extends IDisposable { @@ -211,7 +220,7 @@ export interface MainThreadDocumentContentProvidersShape extends IDisposable { export interface MainThreadDocumentsShape extends IDisposable { $tryCreateDocument(options?: { language?: string; content?: string; }): Promise; - $tryOpenDocument(uri: UriComponents): Promise; + $tryOpenDocument(uri: UriComponents): Promise; $trySaveDocument(uri: UriComponents): Promise; } @@ -390,6 +399,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { export interface MainThreadLanguagesShape extends IDisposable { $getLanguages(): Promise; $changeLanguage(resource: UriComponents, languageId: string): Promise; + $tokensAtPosition(resource: UriComponents, position: IPosition): Promise; } export interface MainThreadMessageOptions { @@ -440,6 +450,8 @@ export interface MainThreadTerminalServiceShape extends IDisposable { $stopSendingDataEvents(): void; $startHandlingLinks(): void; $stopHandlingLinks(): void; + $startLinkProvider(): void; + $stopLinkProvider(): void; $setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: ISerializableEnvironmentVariableCollection | undefined): void; // Process @@ -543,7 +555,7 @@ export interface MainThreadQuickOpenShape extends IDisposable { } export interface MainThreadStatusBarShape extends IDisposable { - $setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: ICommandDto | undefined, color: string | ThemeColor | undefined, alignment: statusbar.StatusbarAlignment, priority: number | undefined): void; + $setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: ICommandDto | undefined, color: string | ThemeColor | undefined, alignment: statusbar.StatusbarAlignment, priority: number | undefined, accessibilityInformation: IAccessibilityInformation | undefined): void; $dispose(id: number): void; } @@ -586,6 +598,7 @@ export interface WebviewExtensionDescription { export interface NotebookExtensionDescription { readonly id: ExtensionIdentifier; readonly location: UriComponents; + readonly description?: string; } export enum WebviewEditorCapabilities { @@ -670,7 +683,7 @@ export interface ICellDto { source: string[]; language: string; cellKind: CellKind; - outputs: IOutput[]; + outputs: IProcessedOutput[]; metadata?: NotebookCellMetadata; } @@ -683,20 +696,29 @@ export type NotebookCellsSplice = [ export type NotebookCellOutputsSplice = [ number /* start */, number /* delete count */, - IOutput[] + IProcessedOutput[] ]; export interface MainThreadNotebookShape extends IDisposable { - $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string): Promise; + $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, supportBackup: boolean, kernelInfoDto: INotebookKernelInfoDto | undefined): Promise; + $onNotebookChange(viewType: string, resource: UriComponents): Promise; $unregisterNotebookProvider(viewType: string): Promise; - $registerNotebookRenderer(extension: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, handle: number, preloads: UriComponents[]): Promise; - $unregisterNotebookRenderer(handle: number): Promise; + $registerNotebookRenderer(extension: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, preloads: UriComponents[]): Promise; + $unregisterNotebookRenderer(id: string): Promise; + $registerNotebookKernel(extension: NotebookExtensionDescription, id: string, label: string, selectors: (string | IRelativePattern)[], preloads: UriComponents[]): Promise; + $registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise; + $unregisterNotebookKernelProvider(handle: number): Promise; + $onNotebookKernelChange(handle: number): void; + $unregisterNotebookKernel(id: string): Promise; $tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], renderers: number[]): Promise; $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise; $updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise; $updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata | undefined): Promise; $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise; - $postMessage(handle: number, value: any): Promise; + $postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise; + + $onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void; + $onContentChange(resource: UriComponents, viewType: string): void; } export interface MainThreadUrlsShape extends IDisposable { @@ -716,7 +738,7 @@ export interface ITextSearchComplete { export interface MainThreadWorkspaceShape extends IDisposable { $startFileSearch(includePattern: string | null, includeFolder: UriComponents | null, excludePatternOrDisregardExcludes: string | false | null, maxResults: number | null, token: CancellationToken): Promise; $startTextSearch(query: search.IPatternInfo, folder: UriComponents | null, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise; - $checkExists(folders: UriComponents[], includes: string[], token: CancellationToken): Promise; + $checkExists(folders: readonly UriComponents[], includes: string[], token: CancellationToken): Promise; $saveAll(includeUntitled?: boolean): Promise; $updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string; }[]): Promise; $resolveProxy(url: string): Promise; @@ -761,10 +783,12 @@ export interface MainThreadTaskShape extends IDisposable { $registerTaskProvider(handle: number, type: string): Promise; $unregisterTaskProvider(handle: number): Promise; $fetchTasks(filter?: tasks.TaskFilterDTO): Promise; + $getTaskExecution(value: tasks.TaskHandleDTO | tasks.TaskDTO): Promise; $executeTask(task: tasks.TaskHandleDTO | tasks.TaskDTO): Promise; $terminateTask(id: string): Promise; $registerTaskSystem(scheme: string, info: tasks.TaskSystemInfoDTO): void; $customExecutionComplete(id: string, result?: number): Promise; + $registerSupportedExecutions(custom?: boolean, shell?: boolean, process?: boolean): Promise; } export interface MainThreadExtensionServiceShape extends IDisposable { @@ -773,7 +797,7 @@ export interface MainThreadExtensionServiceShape extends IDisposable { $onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void; $onExtensionActivationError(extensionId: ExtensionIdentifier, error: ExtensionActivationError): Promise; $onExtensionRuntimeError(extensionId: ExtensionIdentifier, error: SerializedError): void; - $onExtensionHostExit(code: number): void; + $onExtensionHostExit(code: number): Promise; } export interface SCMProviderFeatures { @@ -839,6 +863,8 @@ export interface IDebugConfiguration { export interface IStartDebuggingOptions { parentSessionID?: DebugSessionUUID; repl?: IDebugSessionReplMode; + noDebug?: boolean; + compact?: boolean; } export interface MainThreadDebugServiceShape extends IDisposable { @@ -852,6 +878,7 @@ export interface MainThreadDebugServiceShape extends IDisposable { $unregisterDebugConfigurationProvider(handle: number): void; $unregisterDebugAdapterDescriptorFactory(handle: number): void; $startDebugging(folder: UriComponents | undefined, nameOrConfig: string | IDebugConfiguration, options: IStartDebuggingOptions): Promise; + $stopDebugging(sessionId: DebugSessionUUID | undefined): Promise; $setDebugSessionName(id: DebugSessionUUID, name: string): void; $customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Promise; $appendDebugConsole(value: string): void; @@ -968,6 +995,8 @@ export interface ExtHostTreeViewsShape { $setExpanded(treeViewId: string, treeItemHandle: string, expanded: boolean): void; $setSelection(treeViewId: string, treeItemHandles: string[]): void; $setVisible(treeViewId: string, visible: boolean): void; + $hasResolve(treeViewId: string): Promise; + $resolve(treeViewId: string, treeItemHandle: string): Promise; } export interface ExtHostWorkspaceShape { @@ -1002,6 +1031,8 @@ export interface ExtHostAuthenticationShape { $getSessionAccessToken(id: string, sessionId: string): Promise; $login(id: string, scopes: string[]): Promise; $logout(id: string, sessionId: string): Promise; + $onDidChangeAuthenticationSessions(id: string, label: string, event: modes.AuthenticationSessionsChangeEvent): Promise; + $onDidChangeAuthenticationProviders(added: modes.AuthenticationProviderInformation[], removed: modes.AuthenticationProviderInformation[]): Promise; } export interface ExtHostSearchShape { @@ -1032,6 +1063,7 @@ export interface ExtHostExtensionServiceShape { $activateByEvent(activationEvent: string): Promise; $activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise; $setRemoteEnvironment(env: { [key: string]: string | null; }): Promise; + $updateRemoteConnectionData(connectionData: IRemoteConnectionData): Promise; $deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise; @@ -1046,10 +1078,15 @@ export interface FileSystemEvents { deleted: UriComponents[]; } +export interface SourceTargetPair { + source?: UriComponents; + target: UriComponents; +} + export interface ExtHostFileSystemEventServiceShape { $onFileEvent(events: FileSystemEvents): void; - $onWillRunFileOperation(operation: files.FileOperation, target: UriComponents, source: UriComponents | undefined, timeout: number, token: CancellationToken): Promise; - $onDidRunFileOperation(operation: files.FileOperation, target: UriComponents, source: UriComponents | undefined): void; + $onWillRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[], timeout: number, token: CancellationToken): Promise; + $onDidRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[]): void; } export interface ObjectIdentifier { @@ -1348,6 +1385,7 @@ export interface IShellLaunchConfigDto { args?: string[] | string; cwd?: string | UriComponents; env?: { [key: string]: string | null; }; + hideFromUser?: boolean; } export interface IShellDefinitionDto { @@ -1360,6 +1398,17 @@ export interface IShellAndArgsDto { args: string[] | string | undefined; } +export interface ITerminalLinkDto { + /** The ID of the link to enable activation and disposal. */ + id: number; + /** The startIndex of the link in the line. */ + startIndex: number; + /** The length of the link in the line. */ + length: number; + /** The descriptive label for what the link does when activated. */ + label?: string; +} + export interface ITerminalDimensionsDto { columns: number; rows: number; @@ -1374,8 +1423,8 @@ export interface ExtHostTerminalServiceShape { $acceptTerminalTitleChange(id: number, name: string): void; $acceptTerminalDimensions(id: number, cols: number, rows: number): void; $acceptTerminalMaximumDimensions(id: number, cols: number, rows: number): void; - $spawnExtHostProcess(id: number, shellLaunchConfig: IShellLaunchConfigDto, activeWorkspaceRootUri: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void; - $startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): void; + $spawnExtHostProcess(id: number, shellLaunchConfig: IShellLaunchConfigDto, activeWorkspaceRootUri: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise; + $startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): Promise; $acceptProcessInput(id: number, data: string): void; $acceptProcessResize(id: number, cols: number, rows: number): void; $acceptProcessShutdown(id: number, immediate: boolean): void; @@ -1386,6 +1435,8 @@ export interface ExtHostTerminalServiceShape { $getAvailableShells(): Promise; $getDefaultShellAndArgs(useAutomationShell: boolean): Promise; $handleLink(id: number, link: string): Promise; + $provideLinks(id: number, line: string): Promise; + $activateLink(id: number, linkId: number): void; $initEnvironmentVariableCollections(collections: [string, ISerializableEnvironmentVariableCollection][]): void; } @@ -1394,7 +1445,7 @@ export interface ExtHostSCMShape { $onInputBoxValueChange(sourceControlHandle: number, value: string): void; $executeResourceCommand(sourceControlHandle: number, groupHandle: number, handle: number, preserveFocus: boolean): Promise; $validateInput(sourceControlHandle: number, value: string, cursorPosition: number): Promise<[string, number] | undefined>; - $setSelectedSourceControls(selectedSourceControlHandles: number[]): Promise; + $setSelectedSourceControl(selectedSourceControlHandle: number | undefined): Promise; } export interface ExtHostTaskShape { @@ -1407,6 +1458,7 @@ export interface ExtHostTaskShape { $resolveVariables(workspaceFolder: UriComponents, toResolve: { process?: { name: string; cwd?: string; }, variables: string[]; }): Promise<{ process?: string; variables: { [key: string]: string; }; }>; $getDefaultShellAndArgs(): Thenable<{ shell: string, args: string[] | string | undefined; }>; $jsonTasksSupported(): Thenable; + $findExecutable(command: string, cwd?: string, paths?: string[]): Promise; } export interface IBreakpointDto { @@ -1490,7 +1542,6 @@ export interface ExtHostDebugServiceShape { export interface DecorationRequest { readonly id: number; - readonly handle: number; readonly uri: UriComponents; } @@ -1498,7 +1549,7 @@ export type DecorationData = [number, boolean, string, string, ThemeColor]; export type DecorationReply = { [id: number]: DecorationData; }; export interface ExtHostDecorationsShape { - $provideDecorations(requests: DecorationRequest[], token: CancellationToken): Promise; + $provideDecorations(handle: number, requests: DecorationRequest[], token: CancellationToken): Promise; } export interface ExtHostWindowShape { @@ -1536,33 +1587,59 @@ export interface INotebookSelectionChangeEvent { export interface INotebookEditorPropertiesChangeData { selections: INotebookSelectionChangeEvent | null; + metadata: NotebookDocumentMetadata | null; } export interface INotebookModelAddedData { uri: UriComponents; handle: number; - // versionId: number; + versionId: number; + cells: IMainCellDto[], viewType: string; + metadata?: NotebookDocumentMetadata; + attachedEditor?: { id: string; selections: number[]; } +} + +export interface INotebookEditorAddData { + id: string; + documentUri: UriComponents; + selections: number[]; } export interface INotebookDocumentsAndEditorsDelta { removedDocuments?: UriComponents[]; addedDocuments?: INotebookModelAddedData[]; - // removedEditors?: string[]; - // addedEditors?: ITextEditorAddData[]; - newActiveEditor?: UriComponents | null; + removedEditors?: string[]; + addedEditors?: INotebookEditorAddData[]; + newActiveEditor?: string | null; + visibleEditors?: string[]; } export interface ExtHostNotebookShape { - $resolveNotebookData(viewType: string, uri: UriComponents): Promise; - $executeNotebook(viewType: string, uri: UriComponents, cellHandle: number | undefined, token: CancellationToken): Promise; + $resolveNotebookData(viewType: string, uri: UriComponents, backupId?: string): Promise; + $resolveNotebookEditor(viewType: string, uri: UriComponents, editorId: string): Promise; + $provideNotebookKernels(handle: number, uri: UriComponents, token: CancellationToken): Promise; + $resolveNotebookKernel(handle: number, editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise; + $executeNotebookByAttachedKernel(viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise; + $cancelNotebookByAttachedKernel(viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise; + $executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise; + $cancelNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise; + $executeNotebook2(kernelId: string, viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise; $saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise; $saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise; + $backup(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise; $acceptDisplayOrder(displayOrder: INotebookDisplayOrder): void; - $onDidReceiveMessage(uri: UriComponents, message: any): void; + $acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelId: string | undefined }): void; + $renderOutputs(uriComponents: UriComponents, id: string, request: IOutputRenderRequest): Promise | undefined>; + $renderOutputs2(uriComponents: UriComponents, id: string, request: IOutputRenderRequest): Promise | undefined>; + $onDidReceiveMessage(editorId: string, rendererId: string | undefined, message: unknown): void; $acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent): void; + $acceptModelSaved(uriComponents: UriComponents): void; $acceptEditorPropertiesChanged(uriComponents: UriComponents, data: INotebookEditorPropertiesChangeData): void; $acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta): Promise; + $undoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise; + $redoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise; + } export interface ExtHostStorageShape { diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 91fc7bbcc9b..1f60c7f5ad0 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -215,6 +215,20 @@ const newCommands: ApiCommand[] = [ [ApiCommandArgument.CallHierarchyItem], new ApiCommandResult('A CallHierarchyItem or undefined', v => v.map(typeConverters.CallHierarchyOutgoingCall.to)) ), + // --- rename + new ApiCommand( + 'vscode.executeDocumentRenameProvider', '_executeDocumentRenameProvider', 'Execute rename provider.', + [ApiCommandArgument.Uri, ApiCommandArgument.Position, new ApiCommandArgument('newName', 'The new symbol name', v => typeof v === 'string', v => v)], + new ApiCommandResult('A promise that resolves to a WorkspaceEdit.', value => { + if (!value) { + return undefined; + } + if (value.rejectReason) { + throw new Error(value.rejectReason); + } + return typeConverters.WorkspaceEdit.to(value); + }) + ) ]; @@ -238,15 +252,6 @@ export class ExtHostApiCommands { } registerCommands() { - this._register('vscode.executeDocumentRenameProvider', this._executeDocumentRenameProvider, { - description: 'Execute rename provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'position', description: 'Position in a text document', constraint: types.Position }, - { name: 'newName', description: 'The new symbol name', constraint: String } - ], - returns: 'A promise that resolves to a WorkspaceEdit.' - }); this._register('vscode.executeSignatureHelpProvider', this._executeSignatureHelpProvider, { description: 'Execute signature help provider.', args: [ @@ -376,23 +381,6 @@ export class ExtHostApiCommands { this._disposables.add(disposable); } - private _executeDocumentRenameProvider(resource: URI, position: types.Position, newName: string): Promise { - const args = { - resource, - position: position && typeConverters.Position.from(position), - newName - }; - return this._commands.executeCommand('_executeDocumentRenameProvider', args).then(value => { - if (!value) { - return undefined; - } - if (value.rejectReason) { - return Promise.reject(new Error(value.rejectReason)); - } - return typeConverters.WorkspaceEdit.to(value); - }); - } - private _executeSignatureHelpProvider(resource: URI, position: types.Position, triggerCharacter: string): Promise { const args = { resource, diff --git a/src/vs/workbench/api/common/extHostApiDeprecationService.ts b/src/vs/workbench/api/common/extHostApiDeprecationService.ts index 010458267f0..44cce5ba2ef 100644 --- a/src/vs/workbench/api/common/extHostApiDeprecationService.ts +++ b/src/vs/workbench/api/common/extHostApiDeprecationService.ts @@ -10,7 +10,7 @@ import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; export interface IExtHostApiDeprecationService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; report(apiId: string, extension: IExtensionDescription, migrationSuggestion: string): void; } @@ -19,7 +19,7 @@ export const IExtHostApiDeprecationService = createDecorator(); private readonly _telemetryShape: extHostProtocol.MainThreadTelemetryShape; @@ -63,7 +63,7 @@ export class ExtHostApiDeprecationService implements IExtHostApiDeprecationServi export const NullApiDeprecationService = Object.freeze(new class implements IExtHostApiDeprecationService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; public report(_apiId: string, _extension: IExtensionDescription, _warningMessage: string): void { // noop diff --git a/src/vs/workbench/api/common/extHostAuthentication.ts b/src/vs/workbench/api/common/extHostAuthentication.ts index 888aefea967..b4f005ab1d8 100644 --- a/src/vs/workbench/api/common/extHostAuthentication.ts +++ b/src/vs/workbench/api/common/extHostAuthentication.ts @@ -14,98 +14,81 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape { private _proxy: MainThreadAuthenticationShape; private _authenticationProviders: Map = new Map(); + private _providerIds: string[] = []; + + private _providers: vscode.AuthenticationProviderInformation[] = []; + private _onDidChangeAuthenticationProviders = new Emitter(); readonly onDidChangeAuthenticationProviders: Event = this._onDidChangeAuthenticationProviders.event; - private _onDidChangeSessions = new Emitter<{ [providerId: string]: vscode.AuthenticationSessionsChangeEvent }>(); - readonly onDidChangeSessions: Event<{ [providerId: string]: vscode.AuthenticationSessionsChangeEvent }> = this._onDidChangeSessions.event; + private _onDidChangeSessions = new Emitter(); + readonly onDidChangeSessions: Event = this._onDidChangeSessions.event; constructor(mainContext: IMainContext) { this._proxy = mainContext.getProxy(MainContext.MainThreadAuthentication); } + getProviderIds(): Promise> { + return this._proxy.$getProviderIds(); + } + get providerIds(): string[] { - const ids: string[] = []; - this._authenticationProviders.forEach(provider => { - ids.push(provider.id); - }); - - return ids; + return this._providerIds; } - async getSessions(requestingExtension: IExtensionDescription, providerId: string, scopes: string[]): Promise { - const provider = this._authenticationProviders.get(providerId); - if (!provider) { - throw new Error(`No authentication provider with id '${providerId}' is currently registered.`); - } - - const extensionId = ExtensionIdentifier.toKey(requestingExtension.identifier); - const orderedScopes = scopes.sort().join(' '); - - return (await provider.getSessions()) - .filter(session => session.scopes.sort().join(' ') === orderedScopes) - .map(session => { - return { - id: session.id, - account: session.account, - scopes: session.scopes, - getAccessToken: async () => { - const isAllowed = await this._proxy.$getSessionsPrompt( - provider.id, - session.account.displayName, - provider.displayName, - extensionId, - requestingExtension.displayName || requestingExtension.name); - - if (!isAllowed) { - throw new Error('User did not consent to token access.'); - } - - return session.getAccessToken(); - } - }; - }); + get providers(): ReadonlyArray { + return Object.freeze(this._providers.slice()); } - async login(requestingExtension: IExtensionDescription, providerId: string, scopes: string[]): Promise { + async getSession(requestingExtension: IExtensionDescription, providerId: string, scopes: string[], options: vscode.AuthenticationGetSessionOptions & { createIfNone: true }): Promise; + async getSession(requestingExtension: IExtensionDescription, providerId: string, scopes: string[], options: vscode.AuthenticationGetSessionOptions): Promise { + await this._proxy.$ensureProvider(providerId); const provider = this._authenticationProviders.get(providerId); - if (!provider) { - throw new Error(`No authentication provider with id '${providerId}' is currently registered.`); - } - const extensionName = requestingExtension.displayName || requestingExtension.name; - const isAllowed = await this._proxy.$loginPrompt(provider.displayName, extensionName); - if (!isAllowed) { - throw new Error('User did not consent to login.'); + const extensionId = ExtensionIdentifier.toKey(requestingExtension.identifier); + + if (!provider) { + return this._proxy.$getSession(providerId, scopes, extensionId, extensionName, options); } - const session = await provider.login(scopes); - await this._proxy.$setTrustedExtension(provider.id, session.account.displayName, ExtensionIdentifier.toKey(requestingExtension.identifier), extensionName); - return { - id: session.id, - account: session.account, - scopes: session.scopes, - getAccessToken: async () => { - const isAllowed = await this._proxy.$getSessionsPrompt( - provider.id, - session.account.displayName, - provider.displayName, - ExtensionIdentifier.toKey(requestingExtension.identifier), - requestingExtension.displayName || requestingExtension.name); + const orderedScopes = scopes.sort().join(' '); + const sessions = (await provider.getSessions()).filter(session => session.scopes.slice().sort().join(' ') === orderedScopes); + if (sessions.length) { + if (!provider.supportsMultipleAccounts) { + const session = sessions[0]; + const allowed = await this._proxy.$getSessionsPrompt(providerId, session.account.label, provider.label, extensionId, extensionName); + if (allowed) { + return session; + } else { + throw new Error('User did not consent to login.'); + } + } + + // On renderer side, confirm consent, ask user to choose between accounts if multiple sessions are valid + const selected = await this._proxy.$selectSession(providerId, provider.label, extensionId, extensionName, sessions, scopes, !!options.clearSessionPreference); + return sessions.find(session => session.id === selected.id); + } else { + if (options.createIfNone) { + const isAllowed = await this._proxy.$loginPrompt(provider.label, extensionName); if (!isAllowed) { - throw new Error('User did not consent to token access.'); + throw new Error('User did not consent to login.'); } - return session.getAccessToken(); + const session = await provider.login(scopes); + await this._proxy.$setTrustedExtensionAndAccountPreference(providerId, session.account.label, extensionId, extensionName, session.id); + return session; + } else { + await this._proxy.$requestNewSession(providerId, scopes, extensionId, extensionName); + return undefined; } - }; + } } async logout(providerId: string, sessionId: string): Promise { const provider = this._authenticationProviders.get(providerId); if (!provider) { - throw new Error(`No authentication provider with id '${providerId}' is currently registered.`); + return this._proxy.$logout(providerId, sessionId); } return provider.logout(sessionId); @@ -117,20 +100,37 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape { } this._authenticationProviders.set(provider.id, provider); + if (!this._providerIds.includes(provider.id)) { + this._providerIds.push(provider.id); + } + + if (!this._providers.find(p => p.id === provider.id)) { + this._providers.push({ + id: provider.id, + label: provider.label + }); + } const listener = provider.onDidChangeSessions(e => { - this._proxy.$onDidChangeSessions(provider.id, e); - this._onDidChangeSessions.fire({ [provider.id]: e }); + this._proxy.$sendDidChangeSessions(provider.id, e); }); - this._proxy.$registerAuthenticationProvider(provider.id, provider.displayName); - this._onDidChangeAuthenticationProviders.fire({ added: [provider.id], removed: [] }); + this._proxy.$registerAuthenticationProvider(provider.id, provider.label, provider.supportsMultipleAccounts); return new Disposable(() => { listener.dispose(); this._authenticationProviders.delete(provider.id); + const index = this._providerIds.findIndex(id => id === provider.id); + if (index > -1) { + this._providerIds.splice(index); + } + + const i = this._providers.findIndex(p => p.id === provider.id); + if (i > -1) { + this._providers.splice(i); + } + this._proxy.$unregisterAuthenticationProvider(provider.id); - this._onDidChangeAuthenticationProviders.fire({ added: [], removed: [provider.id] }); }); } @@ -167,7 +167,7 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape { const sessions = await authProvider.getSessions(); const session = sessions.find(session => session.id === sessionId); if (session) { - return session.getAccessToken(); + return session.accessToken; } throw new Error(`Unable to find session with id: ${sessionId}`); @@ -175,4 +175,27 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape { throw new Error(`Unable to find authentication provider with handle: ${providerId}`); } + + $onDidChangeAuthenticationSessions(id: string, label: string, event: modes.AuthenticationSessionsChangeEvent) { + this._onDidChangeSessions.fire({ provider: { id, label }, ...event }); + return Promise.resolve(); + } + + $onDidChangeAuthenticationProviders(added: modes.AuthenticationProviderInformation[], removed: modes.AuthenticationProviderInformation[]) { + added.forEach(id => { + if (!this._providers.includes(id)) { + this._providers.push(id); + } + }); + + removed.forEach(p => { + const index = this._providers.findIndex(provider => provider.id === p.id); + if (index > -1) { + this._providers.splice(index); + } + }); + + this._onDidChangeAuthenticationProviders.fire({ added, removed }); + return Promise.resolve(); + } } diff --git a/src/vs/workbench/api/common/extHostCodeInsets.ts b/src/vs/workbench/api/common/extHostCodeInsets.ts index 8eabb9d74ab..138e4950ae2 100644 --- a/src/vs/workbench/api/common/extHostCodeInsets.ts +++ b/src/vs/workbench/api/common/extHostCodeInsets.ts @@ -28,11 +28,11 @@ export class ExtHostEditorInsets implements ExtHostEditorInsetsShape { // dispose editor inset whenever the hosting editor goes away this._disposables.add(_editors.onDidChangeVisibleTextEditors(() => { const visibleEditor = _editors.getVisibleTextEditors(); - this._insets.forEach(value => { + for (const value of this._insets.values()) { if (visibleEditor.indexOf(value.editor) < 0) { value.inset.dispose(); // will remove from `this._insets` } - }); + } })); } diff --git a/src/vs/workbench/api/common/extHostCommands.ts b/src/vs/workbench/api/common/extHostCommands.ts index 090d0892cb1..83fc7bc31ac 100644 --- a/src/vs/workbench/api/common/extHostCommands.ts +++ b/src/vs/workbench/api/common/extHostCommands.ts @@ -204,12 +204,12 @@ export class ExtHostCommands implements ExtHostCommandsShape { $getContributedCommandHandlerDescriptions(): Promise<{ [id: string]: string | ICommandHandlerDescription }> { const result: { [id: string]: string | ICommandHandlerDescription } = Object.create(null); - this._commands.forEach((command, id) => { + for (let [id, command] of this._commands) { let { description } = command; if (description) { result[id] = description; } - }); + } return Promise.resolve(result); } } diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index 94132fed5b8..f9dcbecccef 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -51,13 +51,14 @@ export interface IExtHostDebugService extends ExtHostDebugServiceShape { addBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise; removeBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise; startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, options: vscode.DebugSessionOptions): Promise; + stopDebugging(session: vscode.DebugSession | undefined): Promise; registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider, trigger: vscode.DebugConfigurationProviderTriggerKind): vscode.Disposable; registerDebugAdapterDescriptorFactory(extension: IExtensionDescription, type: string, factory: vscode.DebugAdapterDescriptorFactory): vscode.Disposable; registerDebugAdapterTrackerFactory(type: string, factory: vscode.DebugAdapterTrackerFactory): vscode.Disposable; asDebugSourceUri(source: vscode.DebugProtocolSource, session?: vscode.DebugSession): vscode.Uri; } -export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDebugServiceShape { +export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDebugServiceShape { readonly _serviceBrand: undefined; @@ -295,10 +296,16 @@ export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDeb public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, options: vscode.DebugSessionOptions): Promise { return this._debugServiceProxy.$startDebugging(folder ? folder.uri : undefined, nameOrConfig, { parentSessionID: options.parentSession ? options.parentSession.id : undefined, - repl: options.consoleMode === DebugConsoleMode.MergeWithParent ? 'mergeWithParent' : 'separate' + repl: options.consoleMode === DebugConsoleMode.MergeWithParent ? 'mergeWithParent' : 'separate', + noDebug: options.noDebug, + compact: options.compact }); } + public stopDebugging(session: vscode.DebugSession | undefined): Promise { + return this._debugServiceProxy.$stopDebugging(session ? session.id : undefined); + } + public registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider, trigger: vscode.DebugConfigurationProviderTriggerKind): vscode.Disposable { if (!provider) { @@ -372,9 +379,7 @@ export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDeb return Promise.resolve(undefined); } - protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService { - return new ExtHostVariableResolverService(folders, editorService, configurationService); - } + protected abstract createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService; public async $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): Promise { if (!this._variableResolver) { @@ -973,7 +978,7 @@ export class ExtHostDebugConsole implements vscode.DebugConsole { export class ExtHostVariableResolverService extends AbstractVariableResolverService { - constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider, env?: IProcessEnvironment) { + constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors | undefined, configurationService: ExtHostConfigProvider, env?: IProcessEnvironment) { super({ getFolderUri: (folderName: string): URI | undefined => { const found = folders.filter(f => f.name === folderName); @@ -992,27 +997,33 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ return env ? env['VSCODE_EXEC_PATH'] : undefined; }, getFilePath: (): string | undefined => { - const activeEditor = editorService.activeEditor(); - if (activeEditor) { - return path.normalize(activeEditor.document.uri.fsPath); + if (editorService) { + const activeEditor = editorService.activeEditor(); + if (activeEditor) { + return path.normalize(activeEditor.document.uri.fsPath); + } } return undefined; }, getSelectedText: (): string | undefined => { - const activeEditor = editorService.activeEditor(); - if (activeEditor && !activeEditor.selection.isEmpty) { - return activeEditor.document.getText(activeEditor.selection); + if (editorService) { + const activeEditor = editorService.activeEditor(); + if (activeEditor && !activeEditor.selection.isEmpty) { + return activeEditor.document.getText(activeEditor.selection); + } } return undefined; }, getLineNumber: (): string | undefined => { - const activeEditor = editorService.activeEditor(); - if (activeEditor) { - return String(activeEditor.selection.end.line + 1); + if (editorService) { + const activeEditor = editorService.activeEditor(); + if (activeEditor) { + return String(activeEditor.selection.end.line + 1); + } } return undefined; } - }, env); + }, env, !editorService); } } @@ -1103,4 +1114,8 @@ export class WorkerExtHostDebugService extends ExtHostDebugServiceBase { ) { super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService, commandService); } + + protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService { + return new ExtHostVariableResolverService(folders, editorService, configurationService); + } } diff --git a/src/vs/workbench/api/common/extHostDecorations.ts b/src/vs/workbench/api/common/extHostDecorations.ts index cccb18f5fa9..d50d8f15dd2 100644 --- a/src/vs/workbench/api/common/extHostDecorations.ts +++ b/src/vs/workbench/api/common/extHostDecorations.ts @@ -9,10 +9,10 @@ import { MainContext, ExtHostDecorationsShape, MainThreadDecorationsShape, Decor import { Disposable, Decoration } from 'vs/workbench/api/common/extHostTypes'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { asArray } from 'vs/base/common/arrays'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { ILogService } from 'vs/platform/log/common/log'; +import { asArray } from 'vs/base/common/arrays'; interface ProviderData { provider: vscode.DecorationProvider; @@ -40,7 +40,9 @@ export class ExtHostDecorations implements IExtHostDecorations { this._proxy.$registerDecorationProvider(handle, extensionId.value); const listener = provider.onDidChangeDecorations(e => { - this._proxy.$onDidChange(handle, !e ? null : asArray(e)); + this._proxy.$onDidChange(handle, !e || (Array.isArray(e) && e.length > 250) + ? null + : asArray(e)); }); return new Disposable(() => { @@ -50,17 +52,20 @@ export class ExtHostDecorations implements IExtHostDecorations { }); } - $provideDecorations(requests: DecorationRequest[], token: CancellationToken): Promise { + async $provideDecorations(handle: number, requests: DecorationRequest[], token: CancellationToken): Promise { + + if (!this._provider.has(handle)) { + // might have been unregistered in the meantime + return Object.create(null); + } + const result: DecorationReply = Object.create(null); - return Promise.all(requests.map(request => { - const { handle, uri, id } = request; - const entry = this._provider.get(handle); - if (!entry) { - // might have been unregistered in the meantime - return undefined; - } - const { provider, extensionId } = entry; - return Promise.resolve(provider.provideDecoration(URI.revive(uri), token)).then(data => { + const { provider, extensionId } = this._provider.get(handle)!; + + await Promise.all(requests.map(async request => { + try { + const { uri, id } = request; + const data = await Promise.resolve(provider.provideDecoration(URI.revive(uri), token)); if (!data) { return; } @@ -70,13 +75,12 @@ export class ExtHostDecorations implements IExtHostDecorations { } catch (e) { this._logService.warn(`INVALID decoration from extension '${extensionId.value}': ${e}`); } - }, err => { + } catch (err) { this._logService.error(err); - }); + } + })); - })).then(() => { - return result; - }); + return result; } } diff --git a/src/vs/workbench/api/common/extHostDiagnostics.ts b/src/vs/workbench/api/common/extHostDiagnostics.ts index 0612d41754f..0ae5595173d 100644 --- a/src/vs/workbench/api/common/extHostDiagnostics.ts +++ b/src/vs/workbench/api/common/extHostDiagnostics.ts @@ -173,9 +173,9 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { forEach(callback: (uri: URI, diagnostics: ReadonlyArray, collection: DiagnosticCollection) => any, thisArg?: any): void { this._checkDisposed(); - this._data.forEach((value, uri) => { + for (let uri of this._data.keys()) { callback.apply(thisArg, [uri, this.get(uri), this]); - }); + } } get(uri: URI): ReadonlyArray { @@ -307,7 +307,7 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape { } else { const index = new Map(); const res: [vscode.Uri, vscode.Diagnostic[]][] = []; - this._collections.forEach(collection => { + for (const collection of this._collections.values()) { collection.forEach((uri, diagnostics) => { let idx = index.get(uri.toString()); if (typeof idx === 'undefined') { @@ -317,18 +317,18 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape { } res[idx][1] = res[idx][1].concat(...diagnostics); }); - }); + } return res; } } private _getDiagnostics(resource: vscode.Uri): ReadonlyArray { let res: vscode.Diagnostic[] = []; - this._collections.forEach(collection => { + for (let collection of this._collections.values()) { if (collection.has(resource)) { res = res.concat(collection.get(resource)); } - }); + } return res; } diff --git a/src/vs/workbench/api/common/extHostDialogs.ts b/src/vs/workbench/api/common/extHostDialogs.ts index 393db6ff77a..69de2a3e7f8 100644 --- a/src/vs/workbench/api/common/extHostDialogs.ts +++ b/src/vs/workbench/api/common/extHostDialogs.ts @@ -15,13 +15,13 @@ export class ExtHostDialogs { this._proxy = mainContext.getProxy(MainContext.MainThreadDialogs); } - showOpenDialog(options: vscode.OpenDialogOptions): Promise { + showOpenDialog(options?: vscode.OpenDialogOptions): Promise { return this._proxy.$showOpenDialog(options).then(filepaths => { return filepaths ? filepaths.map(p => URI.revive(p)) : undefined; }); } - showSaveDialog(options: vscode.SaveDialogOptions): Promise { + showSaveDialog(options?: vscode.SaveDialogOptions): Promise { return this._proxy.$showSaveDialog(options).then(filepath => { return filepath ? URI.revive(filepath) : undefined; }); diff --git a/src/vs/workbench/api/common/extHostDocuments.ts b/src/vs/workbench/api/common/extHostDocuments.ts index 4e8bb4d1252..af4a3f9de52 100644 --- a/src/vs/workbench/api/common/extHostDocuments.ts +++ b/src/vs/workbench/api/common/extHostDocuments.ts @@ -84,9 +84,10 @@ export class ExtHostDocuments implements ExtHostDocumentsShape { let promise = this._documentLoader.get(uri.toString()); if (!promise) { - promise = this._proxy.$tryOpenDocument(uri).then(() => { + promise = this._proxy.$tryOpenDocument(uri).then(uriData => { this._documentLoader.delete(uri.toString()); - return assertIsDefined(this._documentsAndEditors.getDocument(uri)); + const canonicalUri = URI.revive(uriData); + return assertIsDefined(this._documentsAndEditors.getDocument(canonicalUri)); }, err => { this._documentLoader.delete(uri.toString()); return Promise.reject(err); diff --git a/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts b/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts index 533f7c4c68a..bf3919a0841 100644 --- a/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts +++ b/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts @@ -14,6 +14,7 @@ import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { ExtHostTextEditor } from 'vs/workbench/api/common/extHostTextEditor'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { ILogService } from 'vs/platform/log/common/log'; +import { ResourceMap } from 'vs/base/common/map'; export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsShape { @@ -22,7 +23,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha private _activeEditorId: string | null = null; private readonly _editors = new Map(); - private readonly _documents = new Map(); + private readonly _documents = new ResourceMap(); private readonly _onDidAddDocuments = new Emitter(); private readonly _onDidRemoveDocuments = new Emitter(); @@ -48,9 +49,8 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha if (delta.removedDocuments) { for (const uriComponent of delta.removedDocuments) { const uri = URI.revive(uriComponent); - const id = uri.toString(); - const data = this._documents.get(id); - this._documents.delete(id); + const data = this._documents.get(uri); + this._documents.delete(uri); if (data) { removedDocuments.push(data); } @@ -60,7 +60,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha if (delta.addedDocuments) { for (const data of delta.addedDocuments) { const resource = URI.revive(data.uri); - assert.ok(!this._documents.has(resource.toString()), `document '${resource} already exists!'`); + assert.ok(!this._documents.has(resource), `document '${resource} already exists!'`); const documentData = new ExtHostDocumentData( this._extHostRpc.getProxy(MainContext.MainThreadDocuments), @@ -71,7 +71,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha data.versionId, data.isDirty ); - this._documents.set(resource.toString(), documentData); + this._documents.set(resource, documentData); addedDocuments.push(documentData); } } @@ -89,10 +89,10 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha if (delta.addedEditors) { for (const data of delta.addedEditors) { const resource = URI.revive(data.documentUri); - assert.ok(this._documents.has(resource.toString()), `document '${resource}' does not exist`); + assert.ok(this._documents.has(resource), `document '${resource}' does not exist`); assert.ok(!this._editors.has(data.id), `editor '${data.id}' already exists!`); - const documentData = this._documents.get(resource.toString())!; + const documentData = this._documents.get(resource)!; const editor = new ExtHostTextEditor( data.id, this._extHostRpc.getProxy(MainContext.MainThreadTextEditors), @@ -132,13 +132,11 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha } getDocument(uri: URI): ExtHostDocumentData | undefined { - return this._documents.get(uri.toString()); + return this._documents.get(uri); } allDocuments(): ExtHostDocumentData[] { - const result: ExtHostDocumentData[] = []; - this._documents.forEach(data => result.push(data)); - return result; + return [...this._documents.values()]; } getEditor(id: string): ExtHostTextEditor | undefined { @@ -154,9 +152,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha } allEditors(): ExtHostTextEditor[] { - const result: ExtHostTextEditor[] = []; - this._editors.forEach(data => result.push(data)); - return result; + return [...this._editors.values()]; } } diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index d4ab9840abf..34639e18b6f 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -5,9 +5,10 @@ import * as nls from 'vs/nls'; import * as path from 'vs/base/common/path'; +import * as platform from 'vs/base/common/platform'; import { originalFSPath, joinPath } from 'vs/base/common/resources'; -import { Barrier } from 'vs/base/common/async'; -import { dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { Barrier, timeout } from 'vs/base/common/async'; +import { dispose, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; import { TernarySearchTree } from 'vs/base/common/map'; import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; @@ -18,7 +19,6 @@ import { ExtHostStorage, IExtHostStorage } from 'vs/workbench/api/common/extHost import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { ExtensionActivationError, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; import type * as vscode from 'vscode'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; @@ -26,7 +26,7 @@ import { Schemas } from 'vs/base/common/network'; import { VSBuffer } from 'vs/base/common/buffer'; import { ExtensionMemento } from 'vs/workbench/api/common/extHostMemento'; import { RemoteAuthorityResolverError, ExtensionMode } from 'vs/workbench/api/common/extHostTypes'; -import { ResolvedAuthority, ResolvedOptions, RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { ResolvedAuthority, ResolvedOptions, RemoteAuthorityResolverErrorCode, IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; @@ -34,6 +34,8 @@ import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService'; +import { Emitter, Event } from 'vs/base/common/event'; +import { IExtensionActivationHost, checkActivateWorkspaceContainsExtension } from 'vs/workbench/api/common/shared/workspaceContains'; interface ITestRunner { /** Old test runner API, as exported from `vscode/lib/testrunner` */ @@ -48,7 +50,7 @@ interface INewTestRunner { export const IHostUtils = createDecorator('IHostUtils'); export interface IHostUtils { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; exit(code?: number): void; exists(path: string): Promise; realpath(path: string): Promise; @@ -65,11 +67,13 @@ type TelemetryActivationEventFragment = { reasonId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; }; -export abstract class AbstractExtHostExtensionService implements ExtHostExtensionServiceShape { +export abstract class AbstractExtHostExtensionService extends Disposable implements ExtHostExtensionServiceShape { readonly _serviceBrand: undefined; - private static readonly WORKSPACE_CONTAINS_TIMEOUT = 7000; + + private readonly _onDidChangeRemoteConnectionData = this._register(new Emitter()); + public readonly onDidChangeRemoteConnectionData = this._onDidChangeRemoteConnectionData.event; protected readonly _hostUtils: IHostUtils; protected readonly _initData: IInitData; @@ -97,6 +101,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio private readonly _resolvers: { [authorityPrefix: string]: vscode.RemoteAuthorityResolver; }; private _started: boolean; + private _remoteConnectionData: IRemoteConnectionData | null; private readonly _disposables: DisposableStore; @@ -112,6 +117,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio @IExtHostTunnelService extHostTunnelService: IExtHostTunnelService, @IExtHostTerminalService extHostTerminalService: IExtHostTerminalService ) { + super(); this._hostUtils = hostUtils; this._extHostContext = extHostContext; this._initData = initData; @@ -164,6 +170,11 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio this._extensionPathIndex = null; this._resolvers = Object.create(null); this._started = false; + this._remoteConnectionData = this._initData.remote.connectionData; + } + + public getRemoteConnectionData(): IRemoteConnectionData | null { + return this._remoteConnectionData; } public async initialize(): Promise { @@ -183,8 +194,6 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio } } - protected abstract _beforeAlmostReadyToRunExtensions(): Promise; - public async deactivateAll(): Promise { let allPromises: Promise[] = []; try { @@ -244,7 +253,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio if (!this._extensionPathIndex) { const tree = TernarySearchTree.forPaths(); const extensions = this._registry.getAllExtensionDescriptions().map(ext => { - if (!ext.main) { + if (!this._getEntryPoint(ext)) { return undefined; } return this._hostUtils.realpath(ext.extensionLocation.fsPath).then(value => tree.set(URI.file(value).fsPath, ext)); @@ -335,7 +344,8 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio const event = getTelemetryActivationEvent(extensionDescription, reason); type ActivatePluginClassification = {} & TelemetryActivationEventFragment; this._mainThreadTelemetryProxy.$publicLog2('activatePlugin', event); - if (!extensionDescription.main) { + const entryPoint = this._getEntryPoint(extensionDescription); + if (!entryPoint) { // Treat the extension as being empty => NOT AN ERROR CASE return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE)); } @@ -345,22 +355,20 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup); return Promise.all([ - this._loadCommonJSModule(joinPath(extensionDescription.extensionLocation, extensionDescription.main), activationTimesBuilder), + this._loadCommonJSModule(joinPath(extensionDescription.extensionLocation, entryPoint), activationTimesBuilder), this._loadExtensionContext(extensionDescription) ]).then(values => { return AbstractExtHostExtensionService._callActivate(this._logService, extensionDescription.identifier, values[0], values[1], activationTimesBuilder); }); } - protected abstract _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise; - private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise { const globalState = new ExtensionMemento(extensionDescription.identifier.value, true, this._storage); const workspaceState = new ExtensionMemento(extensionDescription.identifier.value, false, this._storage); const extensionMode = extensionDescription.isUnderDevelopment ? (this._initData.environment.extensionTestsLocationURI ? ExtensionMode.Test : ExtensionMode.Development) - : ExtensionMode.Release; + : ExtensionMode.Production; this._logService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`); @@ -376,14 +384,30 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio subscriptions: [], get extensionUri() { return extensionDescription.extensionLocation; }, get extensionPath() { return extensionDescription.extensionLocation.fsPath; }, - get storagePath() { return that._storagePath.workspaceValue(extensionDescription); }, - get globalStoragePath() { return that._storagePath.globalValue(extensionDescription); }, - asAbsolutePath(relativePath: string) { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); }, - get logPath() { return path.join(that._initData.logsLocation.fsPath, extensionDescription.identifier.value); }, - get extensionMode() { - checkProposedApiEnabled(extensionDescription); - return extensionMode; + asAbsolutePath(relativePath: string) { + if (platform.isWeb) { + // web worker + return URI.joinPath(extensionDescription.extensionLocation, relativePath).toString(); + } else { + return path.join(extensionDescription.extensionLocation.fsPath, relativePath); + } }, + get storagePath() { return that._storagePath.workspaceValue(extensionDescription)?.fsPath; }, + get globalStoragePath() { return that._storagePath.globalValue(extensionDescription).fsPath; }, + get logPath() { return path.join(that._initData.logsLocation.fsPath, extensionDescription.identifier.value); }, + get logUri() { + checkProposedApiEnabled(extensionDescription); + return URI.joinPath(that._initData.logsLocation, extensionDescription.identifier.value); + }, + get storageUri() { + checkProposedApiEnabled(extensionDescription); + return that._storagePath.workspaceValue(extensionDescription); + }, + get globalStorageUri() { + checkProposedApiEnabled(extensionDescription); + return that._storagePath.globalValue(extensionDescription); + }, + get extensionMode() { return extensionMode; }, get environmentVariableCollection() { return that._extHostTerminalService.getEnvironmentVariableCollection(extensionDescription); } }); }); @@ -426,15 +450,44 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio // -- eager activation + private _activateOneStartupFinished(desc: IExtensionDescription, activationEvent: string): void { + this._activateById(desc.identifier, { + startup: false, + extensionId: desc.identifier, + activationEvent: activationEvent + }).then(undefined, (err) => { + this._logService.error(err); + }); + } + + private _activateAllStartupFinished(): void { + for (const desc of this._registry.getAllExtensionDescriptions()) { + if (desc.activationEvents) { + for (const activationEvent of desc.activationEvents) { + if (activationEvent === 'onStartupFinished') { + this._activateOneStartupFinished(desc, activationEvent); + } + } + } + } + } + // Handle "eager" activation extensions private _handleEagerExtensions(): Promise { - this._activateByEvent('*', true).then(undefined, (err) => { + const starActivation = this._activateByEvent('*', true).then(undefined, (err) => { this._logService.error(err); }); this._disposables.add(this._extHostWorkspace.onDidChangeWorkspace((e) => this._handleWorkspaceContainsEagerExtensions(e.added))); const folders = this._extHostWorkspace.workspace ? this._extHostWorkspace.workspace.folders : []; - return this._handleWorkspaceContainsEagerExtensions(folders); + const workspaceContainsActivation = this._handleWorkspaceContainsEagerExtensions(folders); + const eagerExtensionsActivation = Promise.all([starActivation, workspaceContainsActivation]).then(() => { }); + + Promise.race([eagerExtensionsActivation, timeout(10000)]).then(() => { + this._activateAllStartupFinished(); + }); + + return eagerExtensionsActivation; } private _handleWorkspaceContainsEagerExtensions(folders: ReadonlyArray): Promise { @@ -449,94 +502,28 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio ).then(() => { }); } - private _handleWorkspaceContainsEagerExtension(folders: ReadonlyArray, desc: IExtensionDescription): Promise { - const activationEvents = desc.activationEvents; - if (!activationEvents) { - return Promise.resolve(undefined); - } - + private async _handleWorkspaceContainsEagerExtension(folders: ReadonlyArray, desc: IExtensionDescription): Promise { if (this.isActivated(desc.identifier)) { - return Promise.resolve(undefined); + return; } - const fileNames: string[] = []; - const globPatterns: string[] = []; - const localWithRemote = !this._initData.remote.isRemote && !!this._initData.remote.authority; - for (const activationEvent of activationEvents) { - if (/^workspaceContains:/.test(activationEvent)) { - const fileNameOrGlob = activationEvent.substr('workspaceContains:'.length); - if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0 || localWithRemote) { - globPatterns.push(fileNameOrGlob); - } else { - fileNames.push(fileNameOrGlob); - } - } + const host: IExtensionActivationHost = { + folders: folders.map(folder => folder.uri), + forceUsingSearch: localWithRemote, + exists: (path) => this._hostUtils.exists(path), + checkExists: (folders, includes, token) => this._mainThreadWorkspaceProxy.$checkExists(folders, includes, token) + }; + + const result = await checkActivateWorkspaceContainsExtension(host, desc); + if (!result) { + return; } - if (fileNames.length === 0 && globPatterns.length === 0) { - return Promise.resolve(undefined); - } - - const fileNamePromise = Promise.all(fileNames.map((fileName) => this._activateIfFileName(folders, desc.identifier, fileName))).then(() => { }); - const globPatternPromise = this._activateIfGlobPatterns(folders, desc.identifier, globPatterns); - - return Promise.all([fileNamePromise, globPatternPromise]).then(() => { }); - } - - private async _activateIfFileName(folders: ReadonlyArray, extensionId: ExtensionIdentifier, fileName: string): Promise { - - // find exact path - for (const { uri } of folders) { - if (await this._hostUtils.exists(path.join(URI.revive(uri).fsPath, fileName))) { - // the file was found - return ( - this._activateById(extensionId, { startup: true, extensionId, activationEvent: `workspaceContains:${fileName}` }) - .then(undefined, err => this._logService.error(err)) - ); - } - } - - return undefined; - } - - private async _activateIfGlobPatterns(folders: ReadonlyArray, extensionId: ExtensionIdentifier, globPatterns: string[]): Promise { - this._logService.trace(`extensionHostMain#activateIfGlobPatterns: fileSearch, extension: ${extensionId.value}, entryPoint: workspaceContains`); - - if (globPatterns.length === 0) { - return Promise.resolve(undefined); - } - - const tokenSource = new CancellationTokenSource(); - const searchP = this._mainThreadWorkspaceProxy.$checkExists(folders.map(folder => folder.uri), globPatterns, tokenSource.token); - - const timer = setTimeout(async () => { - tokenSource.cancel(); - this._activateById(extensionId, { startup: true, extensionId, activationEvent: `workspaceContainsTimeout:${globPatterns.join(',')}` }) - .then(undefined, err => this._logService.error(err)); - }, AbstractExtHostExtensionService.WORKSPACE_CONTAINS_TIMEOUT); - - let exists: boolean = false; - try { - exists = await searchP; - } catch (err) { - if (!errors.isPromiseCanceledError(err)) { - this._logService.error(err); - } - } - - tokenSource.dispose(); - clearTimeout(timer); - - if (exists) { - // a file was found matching one of the glob patterns - return ( - this._activateById(extensionId, { startup: true, extensionId, activationEvent: `workspaceContains:${globPatterns.join(',')}` }) - .then(undefined, err => this._logService.error(err)) - ); - } - - return Promise.resolve(undefined); + return ( + this._activateById(desc.identifier, { startup: true, extensionId: desc.identifier, activationEvent: result.activationEvent }) + .then(undefined, err => this._logService.error(err)) + ); } private _handleExtensionTests(): Promise { @@ -575,7 +562,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio } // after tests have run, we shutdown the host - this._gracefulExit(error || (typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */); + this._testRunnerExit(error || (typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */); }; const runResult = testRunner!.run(extensionTestsPath, oldTestRunnerCallback); @@ -585,11 +572,11 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio runResult .then(() => { c(); - this._gracefulExit(0); + this._testRunnerExit(0); }) .catch((err: Error) => { e(err.toString()); - this._gracefulExit(1); + this._testRunnerExit(1); }); } }); @@ -597,24 +584,20 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio // Otherwise make sure to shutdown anyway even in case of an error else { - this._gracefulExit(1 /* ERROR */); + this._testRunnerExit(1 /* ERROR */); } return Promise.reject(new Error(requireError ? requireError.toString() : nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsPath))); } - private _gracefulExit(code: number): void { - // to give the PH process a chance to flush any outstanding console - // messages to the main process, we delay the exit() by some time - setTimeout(() => { - // If extension tests are running, give the exit code to the renderer - if (this._initData.remote.isRemote && !!this._initData.environment.extensionTestsLocationURI) { - this._mainThreadExtensionsProxy.$onExtensionHostExit(code); - return; - } - + private _testRunnerExit(code: number): void { + // wait at most 5000ms for the renderer to confirm our exit request and for the renderer socket to drain + // (this is to ensure all outstanding messages reach the renderer) + const exitPromise = this._mainThreadExtensionsProxy.$onExtensionHostExit(code); + const drainPromise = this._extHostContext.drain(); + Promise.race([Promise.all([exitPromise, drainPromise]), timeout(5000)]).then(() => { this._hostUtils.exit(code); - }, 500); + }); } private _startExtensionHost(): Promise { @@ -764,6 +747,14 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio return buff; } + public async $updateRemoteConnectionData(connectionData: IRemoteConnectionData): Promise { + this._remoteConnectionData = connectionData; + this._onDidChangeRemoteConnectionData.fire(); + } + + protected abstract _beforeAlmostReadyToRunExtensions(): Promise; + protected abstract _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined; + protected abstract _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise; public abstract async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise; } @@ -798,7 +789,7 @@ function getTelemetryActivationEvent(extensionDescription: IExtensionDescription export const IExtHostExtensionService = createDecorator('IExtHostExtensionService'); export interface IExtHostExtensionService extends AbstractExtHostExtensionService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; initialize(): Promise; isActivated(extensionId: ExtensionIdentifier): boolean; activateByIdWithErrors(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise; @@ -807,4 +798,7 @@ export interface IExtHostExtensionService extends AbstractExtHostExtensionServic getExtensionRegistry(): Promise; getExtensionPathIndex(): Promise>; registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable; + + onDidChangeRemoteConnectionData: Event; + getRemoteConnectionData(): IRemoteConnectionData | null; } diff --git a/src/vs/workbench/api/common/extHostFileSystem.ts b/src/vs/workbench/api/common/extHostFileSystem.ts index ca30ef0d660..9c0ee143a8b 100644 --- a/src/vs/workbench/api/common/extHostFileSystem.ts +++ b/src/vs/workbench/api/common/extHostFileSystem.ts @@ -8,7 +8,7 @@ import { MainContext, IMainContext, ExtHostFileSystemShape, MainThreadFileSystem import type * as vscode from 'vscode'; import * as files from 'vs/platform/files/common/files'; import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle'; -import { FileChangeType, FileSystemError } from 'vs/workbench/api/common/extHostTypes'; +import { FileChangeType } from 'vs/workbench/api/common/extHostTypes'; import * as typeConverter from 'vs/workbench/api/common/extHostTypeConverters'; import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguageFeatures'; import { Schemas } from 'vs/base/common/network'; @@ -108,59 +108,6 @@ class FsLinkProvider { } } -class ConsumerFileSystem implements vscode.FileSystem { - - constructor(private _proxy: MainThreadFileSystemShape) { } - - stat(uri: vscode.Uri): Promise { - return this._proxy.$stat(uri).catch(ConsumerFileSystem._handleError); - } - readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> { - return this._proxy.$readdir(uri).catch(ConsumerFileSystem._handleError); - } - createDirectory(uri: vscode.Uri): Promise { - return this._proxy.$mkdir(uri).catch(ConsumerFileSystem._handleError); - } - async readFile(uri: vscode.Uri): Promise { - return this._proxy.$readFile(uri).then(buff => buff.buffer).catch(ConsumerFileSystem._handleError); - } - writeFile(uri: vscode.Uri, content: Uint8Array): Promise { - return this._proxy.$writeFile(uri, VSBuffer.wrap(content)).catch(ConsumerFileSystem._handleError); - } - delete(uri: vscode.Uri, options?: { recursive?: boolean; useTrash?: boolean; }): Promise { - return this._proxy.$delete(uri, { ...{ recursive: false, useTrash: false }, ...options }).catch(ConsumerFileSystem._handleError); - } - rename(oldUri: vscode.Uri, newUri: vscode.Uri, options?: { overwrite?: boolean; }): Promise { - return this._proxy.$rename(oldUri, newUri, { ...{ overwrite: false }, ...options }).catch(ConsumerFileSystem._handleError); - } - copy(source: vscode.Uri, destination: vscode.Uri, options?: { overwrite?: boolean }): Promise { - return this._proxy.$copy(source, destination, { ...{ overwrite: false }, ...options }).catch(ConsumerFileSystem._handleError); - } - private static _handleError(err: any): never { - // generic error - if (!(err instanceof Error)) { - throw new FileSystemError(String(err)); - } - - // no provider (unknown scheme) error - if (err.name === 'ENOPRO') { - throw FileSystemError.Unavailable(err.message); - } - - // file system error - switch (err.name) { - case files.FileSystemProviderErrorCode.FileExists: throw FileSystemError.FileExists(err.message); - case files.FileSystemProviderErrorCode.FileNotFound: throw FileSystemError.FileNotFound(err.message); - case files.FileSystemProviderErrorCode.FileNotADirectory: throw FileSystemError.FileNotADirectory(err.message); - case files.FileSystemProviderErrorCode.FileIsADirectory: throw FileSystemError.FileIsADirectory(err.message); - case files.FileSystemProviderErrorCode.NoPermissions: throw FileSystemError.NoPermissions(err.message); - case files.FileSystemProviderErrorCode.Unavailable: throw FileSystemError.Unavailable(err.message); - - default: throw new FileSystemError(err.message, err.name as files.FileSystemProviderErrorCode); - } - } -} - export class ExtHostFileSystem implements ExtHostFileSystemShape { private readonly _proxy: MainThreadFileSystemShape; @@ -172,11 +119,8 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { private _linkProviderRegistration?: IDisposable; private _handlePool: number = 0; - readonly fileSystem: vscode.FileSystem; - constructor(mainContext: IMainContext, private _extHostLanguageFeatures: ExtHostLanguageFeatures) { this._proxy = mainContext.getProxy(MainContext.MainThreadFileSystem); - this.fileSystem = new ConsumerFileSystem(this._proxy); // register used schemes Object.keys(Schemas).forEach(scheme => this._usedSchemes.add(scheme)); diff --git a/src/vs/workbench/api/common/extHostFileSystemConsumer.ts b/src/vs/workbench/api/common/extHostFileSystemConsumer.ts new file mode 100644 index 00000000000..67c8bb77d5e --- /dev/null +++ b/src/vs/workbench/api/common/extHostFileSystemConsumer.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +import { MainThreadFileSystemShape, MainContext } from './extHost.protocol'; +import * as vscode from 'vscode'; +import * as files from 'vs/platform/files/common/files'; +import { FileSystemError } from 'vs/workbench/api/common/extHostTypes'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; + +export class ExtHostConsumerFileSystem implements vscode.FileSystem { + + readonly _serviceBrand: undefined; + + private readonly _proxy: MainThreadFileSystemShape; + + constructor(@IExtHostRpcService extHostRpc: IExtHostRpcService) { + this._proxy = extHostRpc.getProxy(MainContext.MainThreadFileSystem); + } + + stat(uri: vscode.Uri): Promise { + return this._proxy.$stat(uri).catch(ExtHostConsumerFileSystem._handleError); + } + readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> { + return this._proxy.$readdir(uri).catch(ExtHostConsumerFileSystem._handleError); + } + createDirectory(uri: vscode.Uri): Promise { + return this._proxy.$mkdir(uri).catch(ExtHostConsumerFileSystem._handleError); + } + async readFile(uri: vscode.Uri): Promise { + return this._proxy.$readFile(uri).then(buff => buff.buffer).catch(ExtHostConsumerFileSystem._handleError); + } + writeFile(uri: vscode.Uri, content: Uint8Array): Promise { + return this._proxy.$writeFile(uri, VSBuffer.wrap(content)).catch(ExtHostConsumerFileSystem._handleError); + } + delete(uri: vscode.Uri, options?: { recursive?: boolean; useTrash?: boolean; }): Promise { + return this._proxy.$delete(uri, { ...{ recursive: false, useTrash: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError); + } + rename(oldUri: vscode.Uri, newUri: vscode.Uri, options?: { overwrite?: boolean; }): Promise { + return this._proxy.$rename(oldUri, newUri, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError); + } + copy(source: vscode.Uri, destination: vscode.Uri, options?: { overwrite?: boolean; }): Promise { + return this._proxy.$copy(source, destination, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError); + } + private static _handleError(err: any): never { + // generic error + if (!(err instanceof Error)) { + throw new FileSystemError(String(err)); + } + + // no provider (unknown scheme) error + if (err.name === 'ENOPRO') { + throw FileSystemError.Unavailable(err.message); + } + + // file system error + switch (err.name) { + case files.FileSystemProviderErrorCode.FileExists: throw FileSystemError.FileExists(err.message); + case files.FileSystemProviderErrorCode.FileNotFound: throw FileSystemError.FileNotFound(err.message); + case files.FileSystemProviderErrorCode.FileNotADirectory: throw FileSystemError.FileNotADirectory(err.message); + case files.FileSystemProviderErrorCode.FileIsADirectory: throw FileSystemError.FileIsADirectory(err.message); + case files.FileSystemProviderErrorCode.NoPermissions: throw FileSystemError.NoPermissions(err.message); + case files.FileSystemProviderErrorCode.Unavailable: throw FileSystemError.Unavailable(err.message); + + default: throw new FileSystemError(err.message, err.name as files.FileSystemProviderErrorCode); + } + } +} + +export interface IExtHostConsumerFileSystem extends ExtHostConsumerFileSystem { } +export const IExtHostConsumerFileSystem = createDecorator('IExtHostConsumerFileSystem'); diff --git a/src/vs/workbench/api/common/extHostFileSystemEventService.ts b/src/vs/workbench/api/common/extHostFileSystemEventService.ts index f8114192102..41606d40393 100644 --- a/src/vs/workbench/api/common/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/common/extHostFileSystemEventService.ts @@ -5,10 +5,10 @@ import { AsyncEmitter, Emitter, Event, IWaitUntil } from 'vs/base/common/event'; import { IRelativePattern, parse } from 'vs/base/common/glob'; -import { URI, UriComponents } from 'vs/base/common/uri'; +import { URI } from 'vs/base/common/uri'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import type * as vscode from 'vscode'; -import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, MainThreadTextEditorsShape, IWorkspaceFileEditDto, IWorkspaceTextEditDto } from './extHost.protocol'; +import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, MainThreadTextEditorsShape, IWorkspaceFileEditDto, IWorkspaceTextEditDto, SourceTargetPair } from './extHost.protocol'; import * as typeConverter from './extHostTypeConverters'; import { Disposable, WorkspaceEdit } from './extHostTypes'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; @@ -142,16 +142,16 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ //--- file operations - $onDidRunFileOperation(operation: FileOperation, target: UriComponents, source: UriComponents | undefined): void { + $onDidRunFileOperation(operation: FileOperation, files: SourceTargetPair[]): void { switch (operation) { case FileOperation.MOVE: - this._onDidRenameFile.fire(Object.freeze({ files: [{ oldUri: URI.revive(source!), newUri: URI.revive(target) }] })); + this._onDidRenameFile.fire(Object.freeze({ files: files.map(f => ({ oldUri: URI.revive(f.source!), newUri: URI.revive(f.target) })) })); break; case FileOperation.DELETE: - this._onDidDeleteFile.fire(Object.freeze({ files: [URI.revive(target)] })); + this._onDidDeleteFile.fire(Object.freeze({ files: files.map(f => URI.revive(f.target)) })); break; case FileOperation.CREATE: - this._onDidCreateFile.fire(Object.freeze({ files: [URI.revive(target)] })); + this._onDidCreateFile.fire(Object.freeze({ files: files.map(f => URI.revive(f.target)) })); break; default: //ignore, dont send @@ -179,16 +179,16 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ }; } - async $onWillRunFileOperation(operation: FileOperation, target: UriComponents, source: UriComponents | undefined, timeout: number, token: CancellationToken): Promise { + async $onWillRunFileOperation(operation: FileOperation, files: SourceTargetPair[], timeout: number, token: CancellationToken): Promise { switch (operation) { case FileOperation.MOVE: - await this._fireWillEvent(this._onWillRenameFile, { files: [{ oldUri: URI.revive(source!), newUri: URI.revive(target) }] }, timeout, token); + await this._fireWillEvent(this._onWillRenameFile, { files: files.map(f => ({ oldUri: URI.revive(f.source!), newUri: URI.revive(f.target) })) }, timeout, token); break; case FileOperation.DELETE: - await this._fireWillEvent(this._onWillDeleteFile, { files: [URI.revive(target)] }, timeout, token); + await this._fireWillEvent(this._onWillDeleteFile, { files: files.map(f => URI.revive(f.target)) }, timeout, token); break; case FileOperation.CREATE: - await this._fireWillEvent(this._onWillCreateFile, { files: [URI.revive(target)] }, timeout, token); + await this._fireWillEvent(this._onWillCreateFile, { files: files.map(f => URI.revive(f.target)) }, timeout, token); break; default: //ignore, dont send diff --git a/src/vs/workbench/api/common/extHostInitDataService.ts b/src/vs/workbench/api/common/extHostInitDataService.ts index 2f0acd57a3c..c318d2694d0 100644 --- a/src/vs/workbench/api/common/extHostInitDataService.ts +++ b/src/vs/workbench/api/common/extHostInitDataService.ts @@ -9,6 +9,6 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' export const IExtHostInitDataService = createDecorator('IExtHostInitDataService'); export interface IExtHostInitDataService extends Readonly { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; } diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 13697b1e1ed..cbe85a2675e 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -73,7 +73,7 @@ class DocumentSymbolAdapter { const element: modes.DocumentSymbol = { name: info.name || '!!MISSING: name!!', kind: typeConvert.SymbolKind.from(info.kind), - tags: info.tags ? info.tags.map(typeConvert.SymbolTag.from) : [], + tags: info.tags?.map(typeConvert.SymbolTag.from) || [], detail: '', containerName: info.containerName, range: typeConvert.Range.from(info.location.range), @@ -857,16 +857,11 @@ class SuggestAdapter { private _cache = new Cache('CompletionItem'); private _disposables = new Map(); - private _didWarnMust: boolean = false; - private _didWarnShould: boolean = false; - constructor( private readonly _documents: ExtHostDocuments, private readonly _commands: CommandsConverter, private readonly _provider: vscode.CompletionItemProvider, - private readonly _logService: ILogService, private readonly _apiDeprecation: IExtHostApiDeprecationService, - private readonly _telemetry: extHostProtocol.MainThreadTelemetryShape, private readonly _extension: IExtensionDescription, ) { } @@ -930,41 +925,12 @@ class SuggestAdapter { return undefined; } - const _mustNotChange = SuggestAdapter._mustNotChangeHash(item); - const _mayNotChange = SuggestAdapter._mayNotChangeHash(item); - const resolvedItem = await asPromise(() => this._provider.resolveCompletionItem!(item, token)); if (!resolvedItem) { return undefined; } - type BlameExtension = { - extensionId: string; - kind: string; - index: string; - }; - - type BlameExtensionMeta = { - extensionId: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; - kind: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; - index: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; - }; - - let _mustNotChangeIndex = !this._didWarnMust && SuggestAdapter._mustNotChangeDiff(_mustNotChange, resolvedItem); - if (typeof _mustNotChangeIndex === 'string') { - this._logService.warn(`[${this._extension.identifier.value}] INVALID result from 'resolveCompletionItem', extension MUST NOT change any of: label, sortText, filterText, insertText, or textEdit`); - this._telemetry.$publicLog2('badresolvecompletion', { extensionId: this._extension.identifier.value, kind: 'must', index: _mustNotChangeIndex }); - this._didWarnMust = true; - } - - let _mayNotChangeIndex = !this._didWarnShould && SuggestAdapter._mayNotChangeDiff(_mayNotChange, resolvedItem); - if (typeof _mayNotChangeIndex === 'string') { - this._logService.info(`[${this._extension.identifier.value}] UNSAVE result from 'resolveCompletionItem', extension SHOULD NOT change any of: additionalTextEdits, or command`); - this._telemetry.$publicLog2('badresolvecompletion', { extensionId: this._extension.identifier.value, kind: 'should', index: _mayNotChangeIndex }); - this._didWarnShould = true; - } - return this._convertCompletionItem(resolvedItem, id); } @@ -1035,45 +1001,6 @@ class SuggestAdapter { return result; } - - private static _mustNotChangeHash(item: vscode.CompletionItem) { - const res = JSON.stringify([item.label, item.sortText, item.filterText, item.insertText, item.range]); - return res; - } - - private static _mustNotChangeDiff(hash: string, item: vscode.CompletionItem): string | void { - const thisArr = [item.label, item.sortText, item.filterText, item.insertText, item.range]; - const thisHash = JSON.stringify(thisArr); - if (hash === thisHash) { - return; - } - const arr = JSON.parse(hash); - for (let i = 0; i < 6; i++) { - if (JSON.stringify(arr[i] !== JSON.stringify(thisArr[i]))) { - return i.toString(); - } - } - return 'unknown'; - } - - private static _mayNotChangeHash(item: vscode.CompletionItem) { - return JSON.stringify([item.additionalTextEdits, item.command]); - } - - private static _mayNotChangeDiff(hash: string, item: vscode.CompletionItem): string | void { - const thisArr = [item.additionalTextEdits, item.command]; - const thisHash = JSON.stringify(thisArr); - if (hash === thisHash) { - return; - } - const arr = JSON.parse(hash); - for (let i = 0; i < 6; i++) { - if (JSON.stringify(arr[i] !== JSON.stringify(thisArr[i]))) { - return i.toString(); - } - } - return 'unknown'; - } } class SignatureHelpAdapter { @@ -1360,6 +1287,7 @@ class CallHierarchyAdapter { uri: item.uri, range: typeConvert.Range.from(item.range), selectionRange: typeConvert.Range.from(item.selectionRange), + tags: item.tags?.map(typeConvert.SymbolTag.from) }; map.set(dto._itemId, item); return dto; @@ -1392,7 +1320,6 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF private readonly _uriTransformer: IURITransformer | null; private readonly _proxy: extHostProtocol.MainThreadLanguageFeaturesShape; - private readonly _telemetryShape: extHostProtocol.MainThreadTelemetryShape; private _documents: ExtHostDocuments; private _commands: ExtHostCommands; private _diagnostics: ExtHostDiagnostics; @@ -1411,7 +1338,6 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF ) { this._uriTransformer = uriTransformer; this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadLanguageFeatures); - this._telemetryShape = mainContext.getProxy(extHostProtocol.MainContext.MainThreadTelemetry); this._documents = documents; this._commands = commands; this._diagnostics = diagnostics; @@ -1780,7 +1706,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF // --- suggestion registerCompletionItemProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, triggerCharacters: string[]): vscode.Disposable { - const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider, this._logService, this._apiDeprecation, this._telemetryShape, extension), extension); + const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider, this._apiDeprecation, extension), extension); this._proxy.$registerSuggestSupport(handle, this._transformDocumentSelector(selector), triggerCharacters, SuggestAdapter.supportsResolving(provider), extension.identifier); return this._createDisposable(handle); } diff --git a/src/vs/workbench/api/common/extHostLanguages.ts b/src/vs/workbench/api/common/extHostLanguages.ts index 2d9e1af6550..035e22148dd 100644 --- a/src/vs/workbench/api/common/extHostLanguages.ts +++ b/src/vs/workbench/api/common/extHostLanguages.ts @@ -6,6 +6,8 @@ import { MainContext, MainThreadLanguagesShape, IMainContext } from './extHost.protocol'; import type * as vscode from 'vscode'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; +import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters'; +import { StandardTokenType, Range, Position } from 'vs/workbench/api/common/extHostTypes'; export class ExtHostLanguages { @@ -32,4 +34,31 @@ export class ExtHostLanguages { } return data.document; } + + async tokenAtPosition(document: vscode.TextDocument, position: vscode.Position): Promise { + const versionNow = document.version; + const pos = typeConvert.Position.from(position); + const info = await this._proxy.$tokensAtPosition(document.uri, pos); + const defaultRange = { + type: StandardTokenType.Other, + range: document.getWordRangeAtPosition(position) ?? new Range(position.line, position.character, position.line, position.character) + }; + if (!info) { + // no result + return defaultRange; + } + const result = { + range: typeConvert.Range.to(info.range), + type: typeConvert.TokenType.to(info.type) + }; + if (!result.range.contains(position)) { + // bogous result + return defaultRange; + } + if (versionNow !== document.version) { + // concurrent change + return defaultRange; + } + return result; + } } diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 66dba087614..9d9c567c468 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -3,21 +3,29 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { readonly } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; +import { hash } from 'vs/base/common/hash'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; +import { joinPath } from 'vs/base/common/resources'; import { ISplice } from 'vs/base/common/sequence'; -import { URI, UriComponents } from 'vs/base/common/uri'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { CellKind, CellOutputKind, ExtHostNotebookShape, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice, MainThreadDocumentsShape, INotebookEditorPropertiesChangeData, INotebookDocumentsAndEditorsDelta } from 'vs/workbench/api/common/extHost.protocol'; -import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; -import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; -import { CellEditType, CellUri, diff, ICellEditOperation, ICellInsertEdit, IErrorOutput, INotebookDisplayOrder, INotebookEditData, IOrderedMimeType, IStreamOutput, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellsChangedEvent, NotebookCellsSplice2, sortMimeTypes, ICellDeleteEdit, notebookDocumentMetadataDefaults, NotebookCellsChangeType, NotebookDataDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { Disposable as VSCodeDisposable } from './extHostTypes'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData'; import { NotImplementedProxy } from 'vs/base/common/types'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import * as UUID from 'vs/base/common/uuid'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { CellKind, ExtHostNotebookShape, IMainContext, INotebookDocumentsAndEditorsDelta, INotebookEditorPropertiesChangeData, MainContext, MainThreadDocumentsShape, MainThreadNotebookShape, NotebookCellOutputsSplice } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; +import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData'; +import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; +import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; +import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; +import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; +import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; +import { CellEditType, CellOutputKind, diff, ICellDeleteEdit, ICellEditOperation, ICellInsertEdit, IMainCellDto, INotebookDisplayOrder, INotebookEditData, INotebookKernelInfoDto2, IOutputRenderRequest, IOutputRenderResponse, IOutputRenderResponseCellInfo, IOutputRenderResponseOutputInfo, IProcessedOutput, IRawOutput, NotebookCellMetadata, NotebookCellsChangedEvent, NotebookCellsChangeType, NotebookCellsSplice2, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import * as vscode from 'vscode'; +import { Cache } from './cache'; interface IObservable { proxy: T; @@ -40,80 +48,124 @@ function getObservable(obj: T): IObservable { }; } +interface INotebookEventEmitter { + emitModelChange(events: vscode.NotebookCellsChangeEvent): void; + emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void; + emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void; + emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void; +} + +const addIdToOutput = (output: IRawOutput, id = UUID.generateUuid()): IProcessedOutput => output.outputKind === CellOutputKind.Rich + ? ({ ...output, outputId: id }) : output; + +class DettachedCellDocumentData extends ExtHostDocumentData { + + private static readonly _fakeProxy = new class extends NotImplementedProxy('document') { + $trySaveDocument() { + return Promise.reject('Cell-document cannot be saved'); + } + }; + + constructor(cell: IMainCellDto) { + super(DettachedCellDocumentData._fakeProxy, + URI.revive(cell.uri), + cell.source, + cell.eol, + cell.language, + 0, + false + ); + } +} + export class ExtHostCell extends Disposable implements vscode.NotebookCell { - // private originalSource: string[]; - private _outputs: any[]; - private _onDidChangeOutputs = new Emitter[]>(); - onDidChangeOutputs: Event[]> = this._onDidChangeOutputs.event; - // private _textDocument: vscode.TextDocument | undefined; - // private _initalVersion: number = -1; - private _outputMapping = new Set(); - private _metadata: vscode.NotebookCellMetadata; + private _onDidChangeOutputs = new Emitter[]>(); + readonly onDidChangeOutputs: Event[]> = this._onDidChangeOutputs.event; + private _outputs: any[]; + private _outputMapping = new WeakMap(); + + private _metadata: vscode.NotebookCellMetadata; private _metadataChangeListener: IDisposable; - private _documentData: ExtHostDocumentData; + readonly handle: number; + readonly uri: URI; + readonly cellKind: CellKind; - get document(): vscode.TextDocument { - return this._documentData.document; - } - - get source() { - // todo@jrieken remove this - return this._documentData.getText(); - } + // todo@jrieken this is a little fish because we have + // vscode.TextDocument for which we never fired an onDidOpen + // event and which doesn't appear in the list of documents. + // this will change once the "real" document comes along. We + // should come up with a better approach here... + readonly defaultDocument: DettachedCellDocumentData; constructor( - private readonly viewType: string, - private readonly documentUri: URI, - readonly handle: number, - readonly uri: URI, - content: string, - public readonly cellKind: CellKind, - public language: string, - outputs: any[], - _metadata: vscode.NotebookCellMetadata | undefined, private _proxy: MainThreadNotebookShape, + readonly notebook: ExtHostNotebookDocument, + private _extHostDocument: ExtHostDocumentsAndEditors, + cell: IMainCellDto, ) { super(); - this._documentData = new ExtHostDocumentData( - new class extends NotImplementedProxy('document') { }, - uri, - content.split(/\r|\n|\r\n/g), '\n', - language, 0, false - ); - this._outputs = outputs; + this.handle = cell.handle; + this.uri = URI.revive(cell.uri); + this.cellKind = cell.cellKind; + this.defaultDocument = new DettachedCellDocumentData(cell); - const observableMetadata = getObservable(_metadata || {}); + this._outputs = cell.outputs; + for (const output of this._outputs) { + this._outputMapping.set(output, output.outputId); + delete output.outputId; + } + + const observableMetadata = getObservable(cell.metadata ?? {}); this._metadata = observableMetadata.proxy; this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => { - this.updateMetadata(); + this._updateMetadata(); })); } + get document(): vscode.TextDocument { + return this._extHostDocument.getDocument(this.uri)?.document ?? this.defaultDocument.document; + } + + get language(): string { + return this.document.languageId; + } + get outputs() { return this._outputs; } set outputs(newOutputs: vscode.CellOutput[]) { - let diffs = diff(this._outputs || [], newOutputs || [], (a) => { + const rawDiffs = diff(this._outputs || [], newOutputs || [], (a) => { return this._outputMapping.has(a); }); - diffs.forEach(diff => { + const transformedDiffs: ISplice[] = rawDiffs.map(diff => { for (let i = diff.start; i < diff.start + diff.deleteCount; i++) { this._outputMapping.delete(this._outputs[i]); } - diff.toInsert.forEach(output => { - this._outputMapping.add(output); - }); + return { + deleteCount: diff.deleteCount, + start: diff.start, + toInsert: diff.toInsert.map((output): IProcessedOutput => { + if (output.outputKind === CellOutputKind.Rich) { + const uuid = UUID.generateUuid(); + this._outputMapping.set(output, uuid); + return { ...output, outputId: uuid }; + } + + this._outputMapping.set(output, undefined); + return output; + }) + }; }); this._outputs = newOutputs; - this._onDidChangeOutputs.fire(diffs); + this._onDidChangeOutputs.fire(transformedDiffs); } get metadata() { @@ -121,34 +173,22 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell { } set metadata(newMetadata: vscode.NotebookCellMetadata) { + this.setMetadata(newMetadata); + this._updateMetadata(); + } + + setMetadata(newMetadata: vscode.NotebookCellMetadata): void { // Don't apply metadata defaults here, 'undefined' means 'inherit from document metadata' this._metadataChangeListener.dispose(); const observableMetadata = getObservable(newMetadata); this._metadata = observableMetadata.proxy; this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => { - this.updateMetadata(); + this._updateMetadata(); })); - - this.updateMetadata(); } - private updateMetadata(): Promise { - return this._proxy.$updateNotebookCellMetadata(this.viewType, this.documentUri, this.handle, this._metadata); - } - - attachTextDocument(document: ExtHostDocumentData) { - this._documentData = document; - // this._initalVersion = this._documentData.version; - } - - detachTextDocument() { - // no-op? keep stale document until new comes along? - - // if (this._textDocument && this._textDocument.version !== this._initalVersion) { - // this.originalSource = this._textDocument.getText().split(/\r|\n|\r\n/g); - // } - // this._textDocument = undefined; - // this._initalVersion = -1; + private _updateMetadata(): Promise { + return this._proxy.$updateNotebookCellMetadata(this.notebook.viewType, this.notebook.uri, this.handle, this._metadata); } } @@ -175,6 +215,10 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo this._proxy.$updateNotebookLanguages(this.viewType, this.uri, this._languages); } + get isUntitled() { + return this.uri.scheme === Schemas.untitled; + } + private _metadata: Required = notebookDocumentMetadataDefaults; private _metadataChangeListener: IDisposable; @@ -217,12 +261,57 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo return this._versionId; } + private _backupCounter = 1; + + private _backup?: vscode.NotebookDocumentBackup; + + + private readonly _edits = new Cache('notebook documents'); + + + addEdit(item: vscode.NotebookDocumentEditEvent): number { + return this._edits.add([item]); + } + + async undo(editId: number, isDirty: boolean): Promise { + await this.getEdit(editId).undo(); + // if (!isDirty) { + // this.disposeBackup(); + // } + } + + async redo(editId: number, isDirty: boolean): Promise { + await this.getEdit(editId).redo(); + // if (!isDirty) { + // this.disposeBackup(); + // } + } + + private getEdit(editId: number): vscode.NotebookDocumentEditEvent { + const edit = this._edits.get(editId, 0); + if (!edit) { + throw new Error('No edit found'); + } + + return edit; + } + + disposeEdits(editIds: number[]): void { + for (const id of editIds) { + this._edits.delete(id); + } + } + + private _disposed = false; + constructor( private readonly _proxy: MainThreadNotebookShape, private _documentsAndEditors: ExtHostDocumentsAndEditors, + private _emitter: INotebookEventEmitter, public viewType: string, public uri: URI, - public renderingHandler: ExtHostNotebookOutputRenderingHandler + public renderingHandler: ExtHostNotebookOutputRenderingHandler, + private readonly _storagePath: URI | undefined ) { super(); @@ -237,7 +326,26 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo this._proxy.$updateNotebookMetadata(this.viewType, this.uri, this._metadata); } + getNewBackupUri(): URI { + if (!this._storagePath) { + throw new Error('Backup requires a valid storage path'); + } + const fileName = hashPath(this.uri) + (this._backupCounter++); + return joinPath(this._storagePath, fileName); + } + + updateBackup(backup: vscode.NotebookDocumentBackup): void { + this._backup?.delete(); + this._backup = backup; + } + + disposeBackup(): void { + this._backup?.delete(); + this._backup = undefined; + } + dispose() { + this._disposed = true; super.dispose(); this._cellDisposableMapping.forEach(cell => cell.dispose()); } @@ -246,9 +354,12 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo get isDirty() { return false; } - accpetModelChanged(event: NotebookCellsChangedEvent) { - if (event.kind === NotebookCellsChangeType.ModelChange) { - this.$spliceNotebookCells(event.changes); + acceptModelChanged(event: NotebookCellsChangedEvent): void { + this._versionId = event.versionId; + if (event.kind === NotebookCellsChangeType.Initialize) { + this.$spliceNotebookCells(event.changes, true); + } if (event.kind === NotebookCellsChangeType.ModelChange) { + this.$spliceNotebookCells(event.changes, false); } else if (event.kind === NotebookCellsChangeType.Move) { this.$moveCell(event.index, event.newIdx); } else if (event.kind === NotebookCellsChangeType.CellClearOutput) { @@ -257,31 +368,29 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo this.$clearAllCellOutputs(); } else if (event.kind === NotebookCellsChangeType.ChangeLanguage) { this.$changeCellLanguage(event.index, event.language); + } else if (event.kind === NotebookCellsChangeType.ChangeMetadata) { + this.$changeCellMetadata(event.index, event.metadata); } - - this._versionId = event.versionId; } - private $spliceNotebookCells(splices: NotebookCellsSplice2[]): void { - if (!splices.length) { + private $spliceNotebookCells(splices: NotebookCellsSplice2[], initialization: boolean): void { + if (this._disposed) { return; } - splices.reverse().forEach(splice => { - let cellDtos = splice[2]; - let newCells = cellDtos.map(cell => { - const extCell = new ExtHostCell(this.viewType, this.uri, cell.handle, URI.revive(cell.uri), cell.source.join('\n'), cell.cellKind, cell.language, cell.outputs, cell.metadata, this._proxy); - const documentData = this._documentsAndEditors.getDocument(URI.revive(cell.uri)); + const contentChangeEvents: vscode.NotebookCellsChangeData[] = []; - if (documentData) { - extCell.attachTextDocument(documentData); - } + splices.reverse().forEach(splice => { + const cellDtos = splice[2]; + const newCells = cellDtos.map(cell => { + + const extCell = new ExtHostCell(this._proxy, this, this._documentsAndEditors, cell); if (!this._cellDisposableMapping.has(extCell.handle)) { this._cellDisposableMapping.set(extCell.handle, new DisposableStore()); } - let store = this._cellDisposableMapping.get(extCell.handle)!; + const store = this._cellDisposableMapping.get(extCell.handle)!; store.add(extCell.onDidChangeOutputs((diffs) => { this.eventuallyUpdateCellOutputs(extCell, diffs); @@ -296,120 +405,99 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo } - this.cells.splice(splice[0], splice[1], ...newCells); + const deletedItems = this.cells.splice(splice[0], splice[1], ...newCells); + + const event: vscode.NotebookCellsChangeData = { + start: splice[0], + deletedCount: splice[1], + deletedItems, + items: newCells + }; + + contentChangeEvents.push(event); }); + + if (!initialization) { + this._emitter.emitModelChange({ + document: this, + changes: contentChangeEvents + }); + } } - private $moveCell(index: number, newIdx: number) { + private $moveCell(index: number, newIdx: number): void { const cells = this.cells.splice(index, 1); this.cells.splice(newIdx, 0, ...cells); + const changes: vscode.NotebookCellsChangeData[] = [{ + start: index, + deletedCount: 1, + deletedItems: cells, + items: [] + }, { + start: newIdx, + deletedCount: 0, + deletedItems: [], + items: cells + }]; + this._emitter.emitModelChange({ + document: this, + changes + }); } - private $clearCellOutputs(index: number) { + private $clearCellOutputs(index: number): void { const cell = this.cells[index]; cell.outputs = []; + const event: vscode.NotebookCellOutputsChangeEvent = { document: this, cells: [cell] }; + this._emitter.emitCellOutputsChange(event); } - private $clearAllCellOutputs() { - this.cells.forEach(cell => cell.outputs = []); - } - - private $changeCellLanguage(index: number, language: string) { - const cell = this.cells[index]; - cell.language = language; - } - - eventuallyUpdateCellOutputs(cell: ExtHostCell, diffs: ISplice[]) { - let renderers = new Set(); - let outputDtos: NotebookCellOutputsSplice[] = diffs.map(diff => { - let outputs = diff.toInsert; - - let transformedOutputs = outputs.map(output => { - if (output.outputKind === CellOutputKind.Rich) { - const ret = this.transformMimeTypes(output); - - if (ret.orderedMimeTypes[ret.pickedMimeTypeIndex].isResolved) { - renderers.add(ret.orderedMimeTypes[ret.pickedMimeTypeIndex].rendererId!); - } - return ret; - } else { - return output as IStreamOutput | IErrorOutput; - } - }); - - return [diff.start, diff.deleteCount, transformedOutputs]; - }); - - this._proxy.$spliceNotebookCellOutputs(this.viewType, this.uri, cell.handle, outputDtos, Array.from(renderers)); - } - - transformMimeTypes(output: vscode.CellDisplayOutput): ITransformedDisplayOutputDto { - let mimeTypes = Object.keys(output.data); - let coreDisplayOrder = this.renderingHandler.outputDisplayOrder; - const sorted = sortMimeTypes(mimeTypes, coreDisplayOrder?.userOrder || [], this._displayOrder, coreDisplayOrder?.defaultOrder || []); - - let orderMimeTypes: IOrderedMimeType[] = []; - - sorted.forEach(mimeType => { - let handlers = this.renderingHandler.findBestMatchedRenderer(mimeType); - - if (handlers.length) { - let renderedOutput = handlers[0].render(this, output, mimeType); - - orderMimeTypes.push({ - mimeType: mimeType, - isResolved: true, - rendererId: handlers[0].handle, - output: renderedOutput - }); - - for (let i = 1; i < handlers.length; i++) { - orderMimeTypes.push({ - mimeType: mimeType, - isResolved: false, - rendererId: handlers[i].handle - }); - } - - if (mimeTypeSupportedByCore(mimeType)) { - orderMimeTypes.push({ - mimeType: mimeType, - isResolved: false, - rendererId: -1 - }); - } - } else { - orderMimeTypes.push({ - mimeType: mimeType, - isResolved: false - }); + private $clearAllCellOutputs(): void { + const modifedCells: vscode.NotebookCell[] = []; + this.cells.forEach(cell => { + if (cell.outputs.length !== 0) { + cell.outputs = []; + modifedCells.push(cell); } }); + const event: vscode.NotebookCellOutputsChangeEvent = { document: this, cells: modifedCells }; + this._emitter.emitCellOutputsChange(event); + } - return { - outputKind: output.outputKind, - data: output.data, - orderedMimeTypes: orderMimeTypes, - pickedMimeTypeIndex: 0 - }; + private $changeCellLanguage(index: number, language: string): void { + const cell = this.cells[index]; + cell.defaultDocument._acceptLanguageId(language); + const event: vscode.NotebookCellLanguageChangeEvent = { document: this, cell, language }; + this._emitter.emitCellLanguageChange(event); + } + + private $changeCellMetadata(index: number, newMetadata: NotebookCellMetadata): void { + const cell = this.cells[index]; + cell.setMetadata(newMetadata); + const event: vscode.NotebookCellMetadataChangeEvent = { document: this, cell }; + this._emitter.emitCellMetadataChange(event); + } + + async eventuallyUpdateCellOutputs(cell: ExtHostCell, diffs: ISplice[]) { + const renderers = new Set(); + const outputDtos: NotebookCellOutputsSplice[] = diffs.map(diff => { + const outputs = diff.toInsert; + return [diff.start, diff.deleteCount, outputs]; + }); + + await this._proxy.$spliceNotebookCellOutputs(this.viewType, this.uri, cell.handle, outputDtos, Array.from(renderers)); + this._emitter.emitCellOutputsChange({ + document: this, + cells: [cell] + }); } getCell(cellHandle: number) { return this.cells.find(cell => cell.handle === cellHandle); } - attachCellTextDocument(textDocument: ExtHostDocumentData) { - let cell = this.cells.find(cell => cell.uri.toString() === textDocument.document.uri.toString()); - if (cell) { - cell.attachTextDocument(textDocument); - } - } - - detachCellTextDocument(textDocument: ExtHostDocumentData) { - let cell = this.cells.find(cell => cell.uri.toString() === textDocument.document.uri.toString()); - if (cell) { - cell.detachTextDocument(); - } + getCell2(cellUri: UriComponents) { + return this.cells.find(cell => cell.uri.fragment === cellUri.fragment); } } @@ -444,29 +532,14 @@ export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellE this._throwIfFinalized(); const sourceArr = Array.isArray(content) ? content : content.split(/\r|\n|\r\n/g); - let cell = { + const cell = { source: sourceArr, language, cellKind: type, - outputs: (outputs as any[]), // TODO@rebornix - metadata + outputs: outputs.map(o => addIdToOutput(o)), + metadata, }; - const transformedOutputs = outputs.map(output => { - if (output.outputKind === CellOutputKind.Rich) { - const ret = this.editor.document.transformMimeTypes(output); - - if (ret.orderedMimeTypes[ret.pickedMimeTypeIndex].isResolved) { - this._renderers.add(ret.orderedMimeTypes[ret.pickedMimeTypeIndex].rendererId!); - } - return ret; - } else { - return output as IStreamOutput | IErrorOutput; - } - }); - - cell.outputs = transformedOutputs; - this._collectedEdits.push({ editType: CellEditType.Insert, index, @@ -485,10 +558,95 @@ export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellE } } +class ExtHostWebviewCommWrapper extends Disposable { + private readonly _onDidReceiveDocumentMessage = new Emitter(); + private readonly _rendererIdToEmitters = new Map>(); + + constructor( + private _editorId: string, + public uri: URI, + private _proxy: MainThreadNotebookShape, + private _webviewInitData: WebviewInitData, + public document: ExtHostNotebookDocument, + ) { + super(); + } + + public onDidReceiveMessage(forRendererId: string | undefined, message: any) { + this._onDidReceiveDocumentMessage.fire(message); + if (forRendererId !== undefined) { + this._rendererIdToEmitters.get(forRendererId)?.fire(message); + } + } + + public readonly contentProviderComm: vscode.NotebookCommunication = { + editorId: this._editorId, + onDidReceiveMessage: this._onDidReceiveDocumentMessage.event, + postMessage: (message: any) => this._proxy.$postMessage(this._editorId, undefined, message), + asWebviewUri: (uri: vscode.Uri) => this._asWebviewUri(uri), + }; + + public getRendererComm(rendererId: string): vscode.NotebookCommunication { + const emitter = new Emitter(); + this._rendererIdToEmitters.set(rendererId, emitter); + return { + editorId: this._editorId, + onDidReceiveMessage: emitter.event, + postMessage: (message: any) => this._proxy.$postMessage(this._editorId, rendererId, message), + asWebviewUri: (uri: vscode.Uri) => this._asWebviewUri(uri), + }; + } + + + private _asWebviewUri(localResource: vscode.Uri): vscode.Uri { + return asWebviewUri(this._webviewInitData, this._editorId, localResource); + } +} + export class ExtHostNotebookEditor extends Disposable implements vscode.NotebookEditor { private _viewColumn: vscode.ViewColumn | undefined; selection?: ExtHostCell = undefined; + + private _active: boolean = false; + get active(): boolean { + return this._active; + } + + set active(_state: boolean) { + throw readonly('active'); + } + + private _visible: boolean = false; + get visible(): boolean { + return this._visible; + } + + set visible(_state: boolean) { + throw readonly('visible'); + } + + _acceptVisibility(value: boolean) { + this._visible = value; + } + + _acceptActive(value: boolean) { + this._active = value; + } + + private _kernel?: vscode.NotebookKernel; + + get kernel() { + return this._kernel; + } + + set kernel(_kernel: vscode.NotebookKernel | undefined) { + throw readonly('kernel'); + } + + private _onDidDispose = new Emitter(); + readonly onDidDispose: Event = this._onDidDispose.event; + private _onDidReceiveMessage = new Emitter(); onDidReceiveMessage: vscode.Event = this._onDidReceiveMessage.event; constructor( @@ -496,31 +654,12 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook readonly id: string, public uri: URI, private _proxy: MainThreadNotebookShape, - private _onDidReceiveMessage: Emitter, + private _webComm: vscode.NotebookCommunication, public document: ExtHostNotebookDocument, - private _documentsAndEditors: ExtHostDocumentsAndEditors ) { super(); - this._register(this._documentsAndEditors.onDidAddDocuments(documents => { - for (const documentData of documents) { - let data = CellUri.parse(documentData.document.uri); - if (data) { - if (this.document.uri.toString() === data.notebook.toString()) { - document.attachCellTextDocument(documentData); - } - } - } - })); - - this._register(this._documentsAndEditors.onDidRemoveDocuments(documents => { - for (const documentData of documents) { - let data = CellUri.parse(documentData.document.uri); - if (data) { - if (this.document.uri.toString() === data.notebook.toString()) { - document.detachCellTextDocument(documentData); - } - } - } + this._register(this._webComm.onDidReceiveMessage(e => { + this._onDidReceiveMessage.fire(e); })); } @@ -538,7 +677,7 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook return Promise.resolve(true); } - let compressedEdits: ICellEditOperation[] = []; + const compressedEdits: ICellEditOperation[] = []; let compressedEditsIndex = -1; for (let i = 0; i < editData.edits.length; i++) { @@ -548,8 +687,8 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook continue; } - let prevIndex = compressedEditsIndex; - let prev = compressedEdits[prevIndex]; + const prevIndex = compressedEditsIndex; + const prev = compressedEdits[prevIndex]; if (prev.editType === CellEditType.Insert && editData.edits[i].editType === CellEditType.Insert) { if (prev.index === editData.edits[i].index) { @@ -580,14 +719,25 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook throw readonly('viewColumn'); } + updateActiveKernel(kernel?: vscode.NotebookKernel) { + this._kernel = kernel; + } async postMessage(message: any): Promise { - return this._proxy.$postMessage(this.document.handle, message); + return this._webComm.postMessage(message); } + asWebviewUri(localResource: vscode.Uri): vscode.Uri { + return this._webComm.asWebviewUri(localResource); + } + dispose() { + this._onDidDispose.fire(); + super.dispose(); + } } export class ExtHostNotebookOutputRenderer { private static _handlePool: number = 0; + private resolvedComms = new WeakSet(); readonly handle = ExtHostNotebookOutputRenderer._handlePool++; constructor( @@ -599,37 +749,166 @@ export class ExtHostNotebookOutputRenderer { } matches(mimeType: string): boolean { - if (this.filter.subTypes) { - if (this.filter.subTypes.indexOf(mimeType) >= 0) { + if (this.filter.mimeTypes) { + if (this.filter.mimeTypes.indexOf(mimeType) >= 0) { return true; } } return false; } - render(document: ExtHostNotebookDocument, output: vscode.CellDisplayOutput, mimeType: string): string { - let html = this.renderer.render(document, output, mimeType); + resolveNotebook(document: ExtHostNotebookDocument, comm: ExtHostWebviewCommWrapper) { + if (!this.resolvedComms.has(comm) && this.renderer.resolveNotebook) { + this.renderer.resolveNotebook(document, comm.getRendererComm(this.type)); + this.resolvedComms.add(comm); + } + } + + render(document: ExtHostNotebookDocument, output: vscode.CellDisplayOutput, outputId: string, mimeType: string): string { + const html = this.renderer.render(document, { output, outputId, mimeType }); return html; } } - export interface ExtHostNotebookOutputRenderingHandler { outputDisplayOrder: INotebookDisplayOrder | undefined; findBestMatchedRenderer(mimeType: string): ExtHostNotebookOutputRenderer[]; } +export class ExtHostNotebookKernelProviderAdapter extends Disposable { + private _kernelToId = new Map(); + private _idToKernel = new Map(); + constructor( + private readonly _proxy: MainThreadNotebookShape, + private readonly _handle: number, + private readonly _extension: IExtensionDescription, + private readonly _provider: vscode.NotebookKernelProvider + ) { + super(); + + if (this._provider.onDidChangeKernels) { + this._register(this._provider.onDidChangeKernels(() => { + this._proxy.$onNotebookKernelChange(this._handle); + })); + } + } + + async provideKernels(document: ExtHostNotebookDocument, token: vscode.CancellationToken): Promise { + const data = await this._provider.provideKernels(document, token) || []; + + const newMap = new Map(); + let kernel_unique_pool = 0; + const kernelIdCache = new Set(); + + const transformedData: INotebookKernelInfoDto2[] = data.map(kernel => { + let id = this._kernelToId.get(kernel); + if (id === undefined) { + if (kernel.id && kernelIdCache.has(kernel.id)) { + id = `${this._extension.identifier.value}_${kernel.id}_${kernel_unique_pool++}`; + } else { + id = `${this._extension.identifier.value}_${kernel.id || UUID.generateUuid()}`; + } + + this._kernelToId.set(kernel, id); + } + + newMap.set(kernel, id); + + return { + id, + label: kernel.label, + extension: this._extension.identifier, + extensionLocation: this._extension.extensionLocation, + description: kernel.description, + isPreferred: kernel.isPreferred, + preloads: kernel.preloads + }; + }); + + this._kernelToId = newMap; + + this._idToKernel.clear(); + this._kernelToId.forEach((value, key) => { + this._idToKernel.set(value, key); + }); + + return transformedData; + } + + getKernel(kernelId: string) { + return this._idToKernel.get(kernelId); + } + + async resolveNotebook(kernelId: string, document: ExtHostNotebookDocument, webview: vscode.NotebookCommunication, token: CancellationToken) { + const kernel = this._idToKernel.get(kernelId); + + if (kernel && this._provider.resolveKernel) { + return this._provider.resolveKernel(kernel, document, webview, token); + } + } + + async executeNotebook(kernelId: string, document: ExtHostNotebookDocument, cell: ExtHostCell | undefined) { + const kernel = this._idToKernel.get(kernelId); + + if (!kernel) { + return; + } + + if (cell) { + return withToken(token => (kernel.executeCell as any)(document, cell, token)); + } else { + return withToken(token => (kernel.executeAllCells as any)(document, token)); + } + } + + async cancelNotebook(kernelId: string, document: ExtHostNotebookDocument, cell: ExtHostCell | undefined) { + const kernel = this._idToKernel.get(kernelId); + + if (!kernel) { + return; + } + + if (cell) { + return kernel.cancelCellExecution(document, cell); + } else { + return kernel.cancelAllCellsExecution(document); + } + } +} + +// TODO@roblou remove 'token' passed to all execute APIs once extensions are updated +async function withToken(cb: (token: CancellationToken) => any) { + const source = new CancellationTokenSource(); + try { + await cb(source.token); + } finally { + source.dispose(); + } +} + export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostNotebookOutputRenderingHandler { - private static _handlePool: number = 0; + private static _notebookKernelProviderHandlePool: number = 0; private readonly _proxy: MainThreadNotebookShape; private readonly _notebookContentProviders = new Map(); + private readonly _notebookKernels = new Map(); + private readonly _notebookKernelProviders = new Map(); private readonly _documents = new Map(); - private readonly _editors = new Map; }>(); - private readonly _notebookOutputRenderers = new Map(); - - private readonly _onDidChangeNotebookDocument = new Emitter<{ document: ExtHostNotebookDocument, changes: NotebookCellsChangedEvent[]; }>(); - readonly onDidChangeNotebookDocument: Event<{ document: ExtHostNotebookDocument, changes: NotebookCellsChangedEvent[]; }> = this._onDidChangeNotebookDocument.event; + private readonly _unInitializedDocuments = new Map(); + private readonly _editors = new Map(); + private readonly _webviewComm = new Map(); + private readonly _notebookOutputRenderers = new Map(); + private readonly _renderersUsedInNotebooks = new WeakMap>(); + private readonly _onDidChangeNotebookCells = new Emitter(); + readonly onDidChangeNotebookCells = this._onDidChangeNotebookCells.event; + private readonly _onDidChangeCellOutputs = new Emitter(); + readonly onDidChangeCellOutputs = this._onDidChangeCellOutputs.event; + private readonly _onDidChangeCellLanguage = new Emitter(); + readonly onDidChangeCellLanguage = this._onDidChangeCellLanguage.event; + private readonly _onDidChangeCellMetadata = new Emitter(); + readonly onDidChangeCellMetadata = this._onDidChangeCellMetadata.event; + private readonly _onDidChangeActiveNotebookEditor = new Emitter(); + readonly onDidChangeActiveNotebookEditor = this._onDidChangeActiveNotebookEditor.event; private _outputDisplayOrder: INotebookDisplayOrder | undefined; @@ -637,24 +916,35 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN return this._outputDisplayOrder; } - private _activeNotebookDocument: ExtHostNotebookDocument | undefined; - - get activeNotebookDocument() { - return this._activeNotebookDocument; - } - private _activeNotebookEditor: ExtHostNotebookEditor | undefined; get activeNotebookEditor() { return this._activeNotebookEditor; } + get notebookDocuments() { + return [...this._documents.values()]; + } + private _onDidOpenNotebookDocument = new Emitter(); onDidOpenNotebookDocument: Event = this._onDidOpenNotebookDocument.event; private _onDidCloseNotebookDocument = new Emitter(); onDidCloseNotebookDocument: Event = this._onDidCloseNotebookDocument.event; + private _onDidSaveNotebookDocument = new Emitter(); + onDidSaveNotebookDocument: Event = this._onDidCloseNotebookDocument.event; + visibleNotebookEditors: ExtHostNotebookEditor[] = []; + private _onDidChangeActiveNotebookKernel = new Emitter<{ document: ExtHostNotebookDocument, kernel: vscode.NotebookKernel | undefined; }>(); + onDidChangeActiveNotebookKernel = this._onDidChangeActiveNotebookKernel.event; + private _onDidChangeVisibleNotebookEditors = new Emitter(); + onDidChangeVisibleNotebookEditors = this._onDidChangeVisibleNotebookEditors.event; - constructor(mainContext: IMainContext, commands: ExtHostCommands, private _documentsAndEditors: ExtHostDocumentsAndEditors) { + constructor( + mainContext: IMainContext, + commands: ExtHostCommands, + private _documentsAndEditors: ExtHostDocumentsAndEditors, + private readonly _webviewInitData: WebviewInitData, + private readonly _extensionStoragePaths?: IExtensionStoragePaths, + ) { this._proxy = mainContext.getProxy(MainContext.MainThreadNotebook); commands.registerArgumentProcessor({ @@ -663,7 +953,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const documentHandle = arg.notebookEditor?.notebookHandle; const cellHandle = arg.cell.handle; - for (let value of this._editors) { + for (const value of this._editors) { if (value[1].editor.document.handle === documentHandle) { const cell = value[1].editor.document.getCell(cellHandle); if (cell) { @@ -683,18 +973,94 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN filter: vscode.NotebookOutputSelector, renderer: vscode.NotebookOutputRenderer ): vscode.Disposable { - let extHostRenderer = new ExtHostNotebookOutputRenderer(type, filter, renderer); - this._notebookOutputRenderers.set(extHostRenderer.handle, extHostRenderer); - this._proxy.$registerNotebookRenderer({ id: extension.identifier, location: extension.extensionLocation }, type, filter, extHostRenderer.handle, renderer.preloads || []); - return new VSCodeDisposable(() => { - this._notebookOutputRenderers.delete(extHostRenderer.handle); - this._proxy.$unregisterNotebookRenderer(extHostRenderer.handle); + if (this._notebookKernels.has(type)) { + throw new Error(`Notebook renderer for '${type}' already registered`); + } + + const extHostRenderer = new ExtHostNotebookOutputRenderer(type, filter, renderer); + this._notebookOutputRenderers.set(extHostRenderer.type, extHostRenderer); + this._proxy.$registerNotebookRenderer({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, type, filter, renderer.preloads || []); + return new extHostTypes.Disposable(() => { + this._notebookOutputRenderers.delete(extHostRenderer.type); + this._proxy.$unregisterNotebookRenderer(extHostRenderer.type); }); } + async $renderOutputs(uriComponents: UriComponents, id: string, request: IOutputRenderRequest): Promise | undefined> { + if (!this._notebookOutputRenderers.has(id)) { + throw new Error(`Notebook renderer for '${id}' is not registered`); + } + + const document = this._documents.get(URI.revive(uriComponents).toString()); + + if (!document) { + return; + } + + const renderer = this._notebookOutputRenderers.get(id)!; + this.provideCommToNotebookRenderers(document, renderer); + + const cellsResponse: IOutputRenderResponseCellInfo[] = request.items.map(cellInfo => { + const cell = document.getCell2(cellInfo.key)!; + const outputResponse: IOutputRenderResponseOutputInfo[] = cellInfo.outputs.map(output => { + return { + index: output.index, + outputId: output.outputId, + mimeType: output.mimeType, + handlerId: id, + transformedOutput: renderer.render(document, cell.outputs[output.index] as vscode.CellDisplayOutput, output.outputId, output.mimeType) + }; + }); + + return { + key: cellInfo.key, + outputs: outputResponse + }; + }); + + return { items: cellsResponse }; + } + + /** + * The request carry the raw data for outputs so we don't look up in the existing document + */ + async $renderOutputs2(uriComponents: UriComponents, id: string, request: IOutputRenderRequest): Promise | undefined> { + if (!this._notebookOutputRenderers.has(id)) { + throw new Error(`Notebook renderer for '${id}' is not registered`); + } + + const document = this._documents.get(URI.revive(uriComponents).toString()); + + if (!document) { + return; + } + + const renderer = this._notebookOutputRenderers.get(id)!; + this.provideCommToNotebookRenderers(document, renderer); + + const cellsResponse: IOutputRenderResponseCellInfo[] = request.items.map(cellInfo => { + const outputResponse: IOutputRenderResponseOutputInfo[] = cellInfo.outputs.map(output => { + return { + index: output.index, + outputId: output.outputId, + mimeType: output.mimeType, + handlerId: id, + transformedOutput: renderer.render(document, output.output! as vscode.CellDisplayOutput, output.outputId, output.mimeType) + }; + }); + + return { + key: cellInfo.key, + outputs: outputResponse + }; + }); + + return { items: cellsResponse }; + } + findBestMatchedRenderer(mimeType: string): ExtHostNotebookOutputRenderer[] { - let matches: ExtHostNotebookOutputRenderer[] = []; - for (let renderer of this._notebookOutputRenderers) { + const matches: ExtHostNotebookOutputRenderer[] = []; + for (const renderer of this._notebookOutputRenderers) { if (renderer[1].matches(mimeType)) { matches.push(renderer[1]); } @@ -713,50 +1079,146 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN throw new Error(`Notebook provider for '${viewType}' already registered`); } + // if ((provider).executeCell) { + // throw new Error('NotebookContentKernel.executeCell is removed, please use vscode.notebook.registerNotebookKernel instead.'); + // } + this._notebookContentProviders.set(viewType, { extension, provider }); - this._proxy.$registerNotebookProvider({ id: extension.identifier, location: extension.extensionLocation }, viewType); - return new VSCodeDisposable(() => { + + const listener = provider.onDidChangeNotebook + ? provider.onDidChangeNotebook(e => { + const document = this._documents.get(URI.revive(e.document.uri).toString()); + + if (!document) { + throw new Error(`Notebook document ${e.document.uri.toString()} not found`); + } + + if (isEditEvent(e)) { + const editId = document.addEdit(e); + this._proxy.$onDidEdit(e.document.uri, viewType, editId, e.label); + } else { + this._proxy.$onContentChange(e.document.uri, viewType); + } + }) + : Disposable.None; + + const supportBackup = !!provider.backupNotebook; + + this._proxy.$registerNotebookProvider({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, viewType, supportBackup, provider.kernel ? { id: viewType, label: provider.kernel.label, extensionLocation: extension.extensionLocation, preloads: provider.kernel.preloads } : undefined); + + return new extHostTypes.Disposable(() => { + listener.dispose(); this._notebookContentProviders.delete(viewType); this._proxy.$unregisterNotebookProvider(viewType); }); } - async $resolveNotebookData(viewType: string, uri: UriComponents): Promise { - let provider = this._notebookContentProviders.get(viewType); - let document = this._documents.get(URI.revive(uri).toString()); + registerNotebookKernelProvider(extension: IExtensionDescription, selector: vscode.NotebookDocumentFilter, provider: vscode.NotebookKernelProvider) { + const handle = ExtHostNotebookController._notebookKernelProviderHandlePool++; + const adapter = new ExtHostNotebookKernelProviderAdapter(this._proxy, handle, extension, provider); + this._notebookKernelProviders.set(handle, adapter); + this._proxy.$registerNotebookKernelProvider({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, handle, { + viewType: selector.viewType, + filenamePattern: selector.filenamePattern ? typeConverters.GlobPattern.from(selector.filenamePattern) : undefined, + excludeFileNamePattern: selector.excludeFileNamePattern ? typeConverters.GlobPattern.from(selector.excludeFileNamePattern) : undefined, + }); - if (provider && document) { - const rawCells = await provider.provider.openNotebook(URI.revive(uri)); - const renderers = new Set(); + return new extHostTypes.Disposable(() => { + adapter.dispose(); + this._notebookKernelProviders.delete(handle); + this._proxy.$unregisterNotebookKernelProvider(handle); + }); + } + + private _withAdapter(handle: number, uri: UriComponents, callback: (adapter: ExtHostNotebookKernelProviderAdapter, document: ExtHostNotebookDocument) => Promise) { + const document = this._documents.get(URI.revive(uri).toString()); + + if (!document) { + return []; + } + + const provider = this._notebookKernelProviders.get(handle); + + if (!provider) { + return []; + } + + return callback(provider, document); + } + + async $provideNotebookKernels(handle: number, uri: UriComponents, token: CancellationToken): Promise { + return this._withAdapter(handle, uri, (adapter, document) => { + return adapter.provideKernels(document, token); + }); + } + + async $resolveNotebookKernel(handle: number, editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise { + await this._withAdapter(handle, uri, async (adapter, document) => { + const webComm = this._webviewComm.get(editorId); + + if (webComm) { + await adapter.resolveNotebook(kernelId, document, webComm.contentProviderComm, token); + } + }); + } + + registerNotebookKernel(extension: IExtensionDescription, id: string, selectors: vscode.GlobPattern[], kernel: vscode.NotebookKernel): vscode.Disposable { + if (this._notebookKernels.has(id)) { + throw new Error(`Notebook kernel for '${id}' already registered`); + } + + this._notebookKernels.set(id, { kernel, extension }); + const transformedSelectors = selectors.map(selector => typeConverters.GlobPattern.from(selector)); + + this._proxy.$registerNotebookKernel({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, id, kernel.label, transformedSelectors, kernel.preloads || []); + return new extHostTypes.Disposable(() => { + this._notebookKernels.delete(id); + this._proxy.$unregisterNotebookKernel(id); + }); + } + + async $resolveNotebookData(viewType: string, uri: UriComponents, backupId?: string): Promise { + const provider = this._notebookContentProviders.get(viewType); + const revivedUri = URI.revive(uri); + + if (provider) { + let storageRoot: URI | undefined; + if (this._extensionStoragePaths) { + storageRoot = this._extensionStoragePaths.workspaceValue(provider.extension) ?? this._extensionStoragePaths.globalValue(provider.extension); + } + + let document = this._documents.get(URI.revive(uri).toString()); + + if (!document) { + const that = this; + document = this._unInitializedDocuments.get(revivedUri.toString()) ?? new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, { + emitModelChange(event: vscode.NotebookCellsChangeEvent): void { + that._onDidChangeNotebookCells.fire(event); + }, + emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void { + that._onDidChangeCellOutputs.fire(event); + }, + emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void { + that._onDidChangeCellLanguage.fire(event); + }, + emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void { + that._onDidChangeCellMetadata.fire(event); + }, + }, viewType, revivedUri, this, storageRoot); + this._unInitializedDocuments.set(revivedUri.toString(), document); + } + + const rawCells = await provider.provider.openNotebook(URI.revive(uri), { backupId }); const dto = { metadata: { ...notebookDocumentMetadataDefaults, ...rawCells.metadata }, languages: rawCells.languages, - cells: rawCells.cells.map(cell => { - let transformedOutputs = cell.outputs.map(output => { - if (output.outputKind === CellOutputKind.Rich) { - // TODO display string[] - const ret = this._transformMimeTypes(document!, (rawCells.metadata.displayOrder as string[]) || [], output); - - if (ret.orderedMimeTypes[ret.pickedMimeTypeIndex].isResolved) { - renderers.add(ret.orderedMimeTypes[ret.pickedMimeTypeIndex].rendererId!); - } - return ret; - } else { - return output as IStreamOutput | IErrorOutput; - } - }); - - return { - language: cell.language, - cellKind: cell.cellKind, - metadata: cell.metadata, - source: cell.source, - outputs: transformedOutputs - }; - }) + cells: rawCells.cells.map(cell => ({ + ...cell, + outputs: cell.outputs.map(o => addIdToOutput(o)) + })), }; return dto; @@ -765,84 +1227,135 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN return; } - private _transformMimeTypes(document: ExtHostNotebookDocument, displayOrder: string[], output: vscode.CellDisplayOutput): ITransformedDisplayOutputDto { - let mimeTypes = Object.keys(output.data); - let coreDisplayOrder = this.outputDisplayOrder; - const sorted = sortMimeTypes(mimeTypes, coreDisplayOrder?.userOrder || [], displayOrder, coreDisplayOrder?.defaultOrder || []); + async $resolveNotebookEditor(viewType: string, uri: UriComponents, editorId: string): Promise { + const provider = this._notebookContentProviders.get(viewType); + const revivedUri = URI.revive(uri); + const document = this._documents.get(revivedUri.toString()); + if (!document || !provider) { + return; + } - let orderMimeTypes: IOrderedMimeType[] = []; + let webComm = this._webviewComm.get(editorId); + if (!webComm) { + webComm = new ExtHostWebviewCommWrapper(editorId, revivedUri, this._proxy, this._webviewInitData, document); + this._webviewComm.set(editorId, webComm); + } - sorted.forEach(mimeType => { - let handlers = this.findBestMatchedRenderer(mimeType); + if (!provider.provider.resolveNotebook) { + return; + } - if (handlers.length) { - let renderedOutput = handlers[0].render(document, output, mimeType); - - orderMimeTypes.push({ - mimeType: mimeType, - isResolved: true, - rendererId: handlers[0].handle, - output: renderedOutput - }); - - for (let i = 1; i < handlers.length; i++) { - orderMimeTypes.push({ - mimeType: mimeType, - isResolved: false, - rendererId: handlers[i].handle - }); - } - - if (mimeTypeSupportedByCore(mimeType)) { - orderMimeTypes.push({ - mimeType: mimeType, - isResolved: false, - rendererId: -1 - }); - } - } else { - orderMimeTypes.push({ - mimeType: mimeType, - isResolved: false - }); - } - }); - - return { - outputKind: output.outputKind, - data: output.data, - orderedMimeTypes: orderMimeTypes, - pickedMimeTypeIndex: 0 - }; + await provider.provider.resolveNotebook(document, webComm.contentProviderComm); } - async $executeNotebook(viewType: string, uri: UriComponents, cellHandle: number | undefined, token: CancellationToken): Promise { - let document = this._documents.get(URI.revive(uri).toString()); + private provideCommToNotebookRenderers(document: ExtHostNotebookDocument, renderer: ExtHostNotebookOutputRenderer) { + let alreadyRegistered = this._renderersUsedInNotebooks.get(document); + if (!alreadyRegistered) { + alreadyRegistered = new Set(); + this._renderersUsedInNotebooks.set(document, alreadyRegistered); + } + + if (alreadyRegistered.has(renderer)) { + return; + } + + alreadyRegistered.add(renderer); + for (const editorId of this._editors.keys()) { + const comm = this._webviewComm.get(editorId); + if (comm) { + renderer.resolveNotebook(document, comm); + } + } + } + + async $executeNotebookByAttachedKernel(viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise { + const document = this._documents.get(URI.revive(uri).toString()); if (!document) { return; } if (this._notebookContentProviders.has(viewType)) { - let cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined; + const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined; + const provider = this._notebookContentProviders.get(viewType)!.provider; - return this._notebookContentProviders.get(viewType)!.provider.executeCell(document, cell, token); + if (provider.kernel) { + if (cell) { + return withToken(token => (provider.kernel!.executeCell as any)(document, cell, token)); + } else { + return withToken(token => (provider.kernel!.executeAllCells as any)(document, token)); + } + } + } + } + + async $cancelNotebookByAttachedKernel(viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise { + const document = this._documents.get(URI.revive(uri).toString()); + + if (!document) { + return; + } + + if (this._notebookContentProviders.has(viewType)) { + const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined; + const provider = this._notebookContentProviders.get(viewType)!.provider; + + if (provider.kernel) { + if (cell) { + return provider.kernel.cancelCellExecution(document, cell); + } else { + return provider.kernel.cancelAllCellsExecution(document); + } + } + } + } + + async $executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise { + await this._withAdapter(handle, uri, async (adapter, document) => { + const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined; + + return adapter.executeNotebook(kernelId, document, cell); + }); + } + + async $cancelNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise { + await this._withAdapter(handle, uri, async (adapter, document) => { + const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined; + + return adapter.cancelNotebook(kernelId, document, cell); + }); + } + + async $executeNotebook2(kernelId: string, viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise { + const document = this._documents.get(URI.revive(uri).toString()); + + if (!document || document.viewType !== viewType) { + return; + } + + const kernelInfo = this._notebookKernels.get(kernelId); + + if (!kernelInfo) { + return; + } + + const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined; + + if (cell) { + return withToken(token => (kernelInfo!.kernel.executeCell as any)(document, cell, token)); + } else { + return withToken(token => (kernelInfo!.kernel.executeAllCells as any)(document, token)); } } async $saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise { - let document = this._documents.get(URI.revive(uri).toString()); + const document = this._documents.get(URI.revive(uri).toString()); if (!document) { return false; } if (this._notebookContentProviders.has(viewType)) { - try { - await this._notebookContentProviders.get(viewType)!.provider.saveNotebook(document, token); - } catch (e) { - return false; - } - + await this._notebookContentProviders.get(viewType)!.provider.saveNotebook(document, token); return true; } @@ -850,51 +1363,105 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } async $saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise { - let document = this._documents.get(URI.revive(uri).toString()); + const document = this._documents.get(URI.revive(uri).toString()); if (!document) { return false; } if (this._notebookContentProviders.has(viewType)) { - try { - await this._notebookContentProviders.get(viewType)!.provider.saveNotebookAs(URI.revive(target), document, token); - } catch (e) { - return false; - } - + await this._notebookContentProviders.get(viewType)!.provider.saveNotebookAs(URI.revive(target), document, token); return true; } return false; } + async $undoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise { + const document = this._documents.get(URI.revive(uri).toString()); + if (!document) { + return; + } + + document.undo(editId, isDirty); + + } + + async $redoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise { + const document = this._documents.get(URI.revive(uri).toString()); + if (!document) { + return; + } + + document.redo(editId, isDirty); + } + + + async $backup(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise { + const document = this._documents.get(URI.revive(uri).toString()); + const provider = this._notebookContentProviders.get(viewType); + + if (document && provider && provider.provider.backupNotebook) { + const backup = await provider.provider.backupNotebook(document, { destination: document.getNewBackupUri() }, cancellation); + document.updateBackup(backup); + return backup.id; + } + + return; + } + $acceptDisplayOrder(displayOrder: INotebookDisplayOrder): void { this._outputDisplayOrder = displayOrder; } - $onDidReceiveMessage(uri: UriComponents, message: any): void { - let editor = this._editors.get(URI.revive(uri).toString()); - - if (editor) { - editor.onDidReceiveMessage.fire(message); + $acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelId: string | undefined; }) { + if (event.providerHandle !== undefined) { + this._withAdapter(event.providerHandle, event.uri, async (adapter, document) => { + const kernel = event.kernelId ? adapter.getKernel(event.kernelId) : undefined; + this._editors.forEach(editor => { + if (editor.editor.document === document) { + editor.editor.updateActiveKernel(kernel); + } + }); + this._onDidChangeActiveNotebookKernel.fire({ document, kernel }); + }); } } + // TODO: remove document - editor one on one mapping + private _getEditorFromURI(uriComponents: UriComponents) { + const uriStr = URI.revive(uriComponents).toString(); + let editor: { editor: ExtHostNotebookEditor; } | undefined; + this._editors.forEach(e => { + if (e.editor.uri.toString() === uriStr) { + editor = e; + } + }); + + return editor; + } + + $onDidReceiveMessage(editorId: string, forRendererType: string | undefined, message: any): void { + this._webviewComm.get(editorId)?.onDidReceiveMessage(forRendererType, message); + } + $acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent): void { - let editor = this._editors.get(URI.revive(uriComponents).toString()); + const document = this._documents.get(URI.revive(uriComponents).toString()); - if (editor) { - editor.editor.document.accpetModelChanged(event); - this._onDidChangeNotebookDocument.fire({ - document: editor.editor.document, - changes: [event] - }); + if (document) { + document.acceptModelChanged(event); } + } + public $acceptModelSaved(uriComponents: UriComponents): void { + const document = this._documents.get(URI.revive(uriComponents).toString()); + if (document) { + // this.$acceptDirtyStateChanged(uriComponents, false); + this._onDidSaveNotebookDocument.fire(document); + } } $acceptEditorPropertiesChanged(uriComponents: UriComponents, data: INotebookEditorPropertiesChangeData): void { - let editor = this._editors.get(URI.revive(uriComponents).toString()); + const editor = this._getEditorFromURI(uriComponents); if (!editor) { return; @@ -910,61 +1477,223 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN editor.editor.selection = undefined; } } + + if (data.metadata) { + editor.editor.document.metadata = { + ...notebookDocumentMetadataDefaults, + ...data.metadata + }; + } + } + + private _createExtHostEditor(document: ExtHostNotebookDocument, editorId: string, selections: number[]) { + const revivedUri = document.uri; + let webComm = this._webviewComm.get(editorId); + + if (!webComm) { + webComm = new ExtHostWebviewCommWrapper(editorId, revivedUri, this._proxy, this._webviewInitData, document); + this._webviewComm.set(editorId, webComm); + } + + const editor = new ExtHostNotebookEditor( + document.viewType, + editorId, + revivedUri, + this._proxy, + webComm.contentProviderComm, + document + ); + + const cells = editor.document.cells; + + if (selections.length) { + const firstCell = selections[0]; + editor.selection = cells.find(cell => cell.handle === firstCell); + } else { + editor.selection = undefined; + } + + this._editors.get(editorId)?.editor.dispose(); + + for (const renderer of this._renderersUsedInNotebooks.get(document) ?? []) { + renderer.resolveNotebook(document, webComm); + } + + this._editors.set(editorId, { editor }); } async $acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta) { + let editorChanged = false; + if (delta.removedDocuments) { delta.removedDocuments.forEach((uri) => { - let document = this._documents.get(URI.revive(uri).toString()); + const revivedUri = URI.revive(uri); + const revivedUriStr = revivedUri.toString(); + const document = this._documents.get(revivedUriStr); if (document) { document.dispose(); - this._documents.delete(URI.revive(uri).toString()); + this._documents.delete(revivedUriStr); this._onDidCloseNotebookDocument.fire(document); } - let editor = this._editors.get(URI.revive(uri).toString()); - - if (editor) { - editor.editor.dispose(); - editor.onDidReceiveMessage.dispose(); - this._editors.delete(URI.revive(uri).toString()); - } + [...this._editors.values()].forEach((e) => { + if (e.editor.uri.toString() === revivedUriStr) { + e.editor.dispose(); + this._editors.delete(e.editor.id); + editorChanged = true; + } + }); }); } if (delta.addedDocuments) { delta.addedDocuments.forEach(modelData => { const revivedUri = URI.revive(modelData.uri); + const revivedUriStr = revivedUri.toString(); const viewType = modelData.viewType; - if (!this._documents.has(revivedUri.toString())) { - let document = new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, viewType, revivedUri, this); - this._documents.set(revivedUri.toString(), document); + const entry = this._notebookContentProviders.get(viewType); + let storageRoot: URI | undefined; + if (entry && this._extensionStoragePaths) { + storageRoot = this._extensionStoragePaths.workspaceValue(entry.extension) ?? this._extensionStoragePaths.globalValue(entry.extension); } - const onDidReceiveMessage = new Emitter(); - const document = this._documents.get(revivedUri.toString())!; + if (!this._documents.has(revivedUriStr)) { + const that = this; - let editor = new ExtHostNotebookEditor( - viewType, - `${ExtHostNotebookController._handlePool++}`, - revivedUri, - this._proxy, - onDidReceiveMessage, - document, - this._documentsAndEditors - ); + const document = this._unInitializedDocuments.get(revivedUriStr) ?? new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, { + emitModelChange(event: vscode.NotebookCellsChangeEvent): void { + that._onDidChangeNotebookCells.fire(event); + }, + emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void { + that._onDidChangeCellOutputs.fire(event); + }, + emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void { + that._onDidChangeCellLanguage.fire(event); + }, + emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void { + that._onDidChangeCellMetadata.fire(event); + } + }, viewType, revivedUri, this, storageRoot); + this._unInitializedDocuments.delete(revivedUriStr); + if (modelData.metadata) { + document.metadata = { + ...notebookDocumentMetadataDefaults, + ...modelData.metadata + }; + } + + document.acceptModelChanged({ + kind: NotebookCellsChangeType.Initialize, + versionId: modelData.versionId, + changes: [[ + 0, + 0, + modelData.cells + ]] + }); + + this._documents.get(revivedUriStr)?.dispose(); + this._documents.set(revivedUriStr, document); + + // create editor if populated + if (modelData.attachedEditor) { + this._createExtHostEditor(document, modelData.attachedEditor.id, modelData.attachedEditor.selections); + editorChanged = true; + } + } + + const document = this._documents.get(revivedUriStr)!; this._onDidOpenNotebookDocument.fire(document); - - // TODO, does it already exist? - this._editors.set(revivedUri.toString(), { editor, onDidReceiveMessage }); }); } - if (delta.newActiveEditor) { - this._activeNotebookDocument = this._documents.get(URI.revive(delta.newActiveEditor).toString()); - this._activeNotebookEditor = this._editors.get(URI.revive(delta.newActiveEditor).toString())?.editor; + if (delta.addedEditors) { + delta.addedEditors.forEach(editorModelData => { + if (this._editors.has(editorModelData.id)) { + return; + } + + const revivedUri = URI.revive(editorModelData.documentUri); + const document = this._documents.get(revivedUri.toString()); + + if (document) { + this._createExtHostEditor(document, editorModelData.id, editorModelData.selections); + editorChanged = true; + } + }); + } + + const removedEditors: { editor: ExtHostNotebookEditor; }[] = []; + + if (delta.removedEditors) { + delta.removedEditors.forEach(editorid => { + const editor = this._editors.get(editorid); + + if (editor) { + editorChanged = true; + this._editors.delete(editorid); + + if (this.activeNotebookEditor?.id === editor.editor.id) { + this._activeNotebookEditor = undefined; + } + + removedEditors.push(editor); + } + }); + } + + if (editorChanged) { + removedEditors.forEach(e => { + e.editor.dispose(); + }); + } + + if (delta.visibleEditors) { + this.visibleNotebookEditors = delta.visibleEditors.map(id => this._editors.get(id)!.editor).filter(editor => !!editor) as ExtHostNotebookEditor[]; + const visibleEditorsSet = new Set(); + this.visibleNotebookEditors.forEach(editor => visibleEditorsSet.add(editor.id)); + + [...this._editors.values()].forEach((e) => { + const newValue = visibleEditorsSet.has(e.editor.id); + e.editor._acceptVisibility(newValue); + }); + + this.visibleNotebookEditors = [...this._editors.values()].map(e => e.editor).filter(e => e.visible); + this._onDidChangeVisibleNotebookEditors.fire(this.visibleNotebookEditors); + } + + if (delta.newActiveEditor !== undefined) { + if (delta.newActiveEditor) { + this._activeNotebookEditor = this._editors.get(delta.newActiveEditor)?.editor; + this._activeNotebookEditor?._acceptActive(true); + [...this._editors.values()].forEach((e) => { + if (e.editor !== this.activeNotebookEditor) { + e.editor._acceptActive(false); + } + }); + } else { + // clear active notebook as current active editor is non-notebook editor + this._activeNotebookEditor = undefined; + + [...this._editors.values()].forEach((e) => { + e.editor._acceptActive(false); + }); + + } + + this._onDidChangeActiveNotebookEditor.fire(this._activeNotebookEditor); } } } + +function hashPath(resource: URI): string { + const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString(); + return hash(str) + ''; +} + +function isEditEvent(e: vscode.NotebookDocumentEditEvent | vscode.NotebookDocumentContentChangeEvent): e is vscode.NotebookDocumentEditEvent { + return typeof (e as vscode.NotebookDocumentEditEvent).undo === 'function' + && typeof (e as vscode.NotebookDocumentEditEvent).redo === 'function'; +} diff --git a/src/vs/workbench/api/common/extHostNotebookConcatDocument.ts b/src/vs/workbench/api/common/extHostNotebookConcatDocument.ts index 887ac671ccf..8ffeda97be3 100644 --- a/src/vs/workbench/api/common/extHostNotebookConcatDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookConcatDocument.ts @@ -12,7 +12,9 @@ import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer' import { DisposableStore } from 'vs/base/common/lifecycle'; import { score } from 'vs/editor/common/modes/languageSelector'; import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { isEqual } from 'vs/base/common/resources'; +import { ResourceMap } from 'vs/base/common/map'; +import { URI } from 'vs/base/common/uri'; +import { generateUuid } from 'vs/base/common/uuid'; export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextDocument { @@ -20,6 +22,7 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD private _isClosed = false; private _cells!: ExtHostCell[]; + private _cellUris!: ResourceMap; private _cellLengths!: PrefixSumComputer; private _cellLines!: PrefixSumComputer; private _versionId = 0; @@ -27,6 +30,8 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD private readonly _onDidChange = new Emitter(); readonly onDidChange: Event = this._onDidChange.event; + readonly uri = URI.from({ scheme: 'vscode-concat-doc', path: generateUuid() }); + constructor( extHostNotebooks: ExtHostNotebookController, extHostDocuments: ExtHostDocuments, @@ -36,21 +41,24 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD this._init(); this._disposables.add(extHostDocuments.onDidChangeDocument(e => { - let cellIdx = this._cells.findIndex(cell => isEqual(cell.uri, e.document.uri)); - if (cellIdx >= 0) { + const cellIdx = this._cellUris.get(e.document.uri); + if (cellIdx !== undefined) { this._cellLengths.changeValue(cellIdx, this._cells[cellIdx].document.getText().length + 1); this._cellLines.changeValue(cellIdx, this._cells[cellIdx].document.lineCount); this._versionId += 1; this._onDidChange.fire(undefined); } })); - this._disposables.add(extHostNotebooks.onDidChangeNotebookDocument(e => { - if (e.document === this._notebook) { + const documentChange = (document: vscode.NotebookDocument) => { + if (document === this._notebook) { this._init(); this._versionId += 1; this._onDidChange.fire(undefined); } - })); + }; + + this._disposables.add(extHostNotebooks.onDidChangeCellLanguage(e => documentChange(e.document))); + this._disposables.add(extHostNotebooks.onDidChangeNotebookCells(e => documentChange(e.document))); } dispose(): void { @@ -64,10 +72,12 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD private _init() { this._cells = []; + this._cellUris = new ResourceMap(); const cellLengths: number[] = []; const cellLineCounts: number[] = []; - for (let cell of this._notebook.cells) { + for (const cell of this._notebook.cells) { if (cell.cellKind === CellKind.Code && (!this._selector || score(this._selector, cell.uri, cell.language, true))) { + this._cellUris.set(cell.uri, this._cells.length); this._cells.push(cell); cellLengths.push(cell.document.getText().length + 1); cellLineCounts.push(cell.document.lineCount); @@ -84,7 +94,7 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD getText(range?: vscode.Range): string { if (!range) { let result = ''; - for (let cell of this._cells) { + for (const cell of this._cells) { result += cell.document.getText() + '\n'; } // remove last newline again @@ -99,16 +109,16 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD // get start and end locations and create substrings const start = this.locationAt(range.start); const end = this.locationAt(range.end); - const startCell = this._cells.find(cell => isEqual(cell.uri, start.uri)); - const endCell = this._cells.find(cell => isEqual(cell.uri, end.uri)); + const startCell = this._cells[this._cellUris.get(start.uri) ?? -1]; + const endCell = this._cells[this._cellUris.get(end.uri) ?? -1]; if (!startCell || !endCell) { return ''; } else if (startCell === endCell) { return startCell.document.getText(new types.Range(start.range.start, end.range.end)); } else { - let a = startCell.document.getText(new types.Range(start.range.start, new types.Position(startCell.document.lineCount, 0))); - let b = endCell.document.getText(new types.Range(new types.Position(0, 0), end.range.end)); + const a = startCell.document.getText(new types.Range(start.range.start, new types.Position(startCell.document.lineCount, 0))); + const b = endCell.document.getText(new types.Range(new types.Position(0, 0), end.range.end)); return a + '\n' + b; } } @@ -127,9 +137,9 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD return this._cells[idx.index].document.positionAt(idx.remainder).translate(lineCount); } - const idx = this._cells.findIndex(cell => isEqual(cell.uri, locationOrOffset.uri)); - if (idx >= 0) { - let line = this._cellLines.getAccumulatedValue(idx - 1); + const idx = this._cellUris.get(locationOrOffset.uri); + if (idx !== undefined) { + const line = this._cellLines.getAccumulatedValue(idx - 1); return new types.Position(line + locationOrOffset.range.start.line, locationOrOffset.range.start.character); } // do better? @@ -148,11 +158,31 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD endIdx = this._cellLines.getIndexOf(positionOrRange.end.line); } - let startPos = new types.Position(startIdx.remainder, positionOrRange.start.character); - let endPos = new types.Position(endIdx.remainder, positionOrRange.end.character); - let range = new types.Range(startPos, endPos); + const startPos = new types.Position(startIdx.remainder, positionOrRange.start.character); + const endPos = new types.Position(endIdx.remainder, positionOrRange.end.character); + const range = new types.Range(startPos, endPos); const startCell = this._cells[startIdx.index]; return new types.Location(startCell.uri, startCell.document.validateRange(range)); } + + contains(uri: vscode.Uri): boolean { + return this._cellUris.has(uri); + } + + validateRange(range: vscode.Range): vscode.Range { + const start = this.validatePosition(range.start); + const end = this.validatePosition(range.end); + return range.with(start, end); + } + + validatePosition(position: vscode.Position): vscode.Position { + const startIdx = this._cellLines.getIndexOf(position.line); + + const cellPosition = new types.Position(startIdx.remainder, position.character); + const validCellPosition = this._cells[startIdx.index].document.validatePosition(cellPosition); + + const line = this._cellLines.getAccumulatedValue(startIdx.index - 1); + return new types.Position(line + validCellPosition.line, validCellPosition.character); + } } diff --git a/src/vs/workbench/api/common/extHostRpcService.ts b/src/vs/workbench/api/common/extHostRpcService.ts index 3cbdcf31165..6582ef5fb3f 100644 --- a/src/vs/workbench/api/common/extHostRpcService.ts +++ b/src/vs/workbench/api/common/extHostRpcService.ts @@ -9,7 +9,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' export const IExtHostRpcService = createDecorator('IExtHostRpcService'); export interface IExtHostRpcService extends IRPCProtocol { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; } export class ExtHostRpcService implements IExtHostRpcService { @@ -18,12 +18,12 @@ export class ExtHostRpcService implements IExtHostRpcService { readonly getProxy: (identifier: ProxyIdentifier) => T; readonly set: (identifier: ProxyIdentifier, instance: R) => R; readonly assertRegistered: (identifiers: ProxyIdentifier[]) => void; + readonly drain: () => Promise; constructor(rpcProtocol: IRPCProtocol) { this.getProxy = rpcProtocol.getProxy.bind(rpcProtocol); this.set = rpcProtocol.set.bind(rpcProtocol); this.assertRegistered = rpcProtocol.assertRegistered.bind(rpcProtocol); - + this.drain = rpcProtocol.drain.bind(rpcProtocol); } - } diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index 64930929c1c..5ae7b998a51 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -537,7 +537,7 @@ export class ExtHostSCM implements ExtHostSCMShape { private readonly _onDidChangeActiveProvider = new Emitter(); get onDidChangeActiveProvider(): Event { return this._onDidChangeActiveProvider.event; } - private _selectedSourceControlHandles = new Set(); + private _selectedSourceControlHandle: number | undefined; constructor( mainContext: IMainContext, @@ -682,40 +682,18 @@ export class ExtHostSCM implements ExtHostSCMShape { }); } - $setSelectedSourceControls(selectedSourceControlHandles: number[]): Promise { - this.logService.trace('ExtHostSCM#$setSelectedSourceControls', selectedSourceControlHandles); + $setSelectedSourceControl(selectedSourceControlHandle: number | undefined): Promise { + this.logService.trace('ExtHostSCM#$setSelectedSourceControl', selectedSourceControlHandle); - const set = new Set(); - - for (const handle of selectedSourceControlHandles) { - set.add(handle); + if (selectedSourceControlHandle !== undefined) { + this._sourceControls.get(selectedSourceControlHandle)?.setSelectionState(true); } - set.forEach(handle => { - if (!this._selectedSourceControlHandles.has(handle)) { - const sourceControl = this._sourceControls.get(handle); + if (this._selectedSourceControlHandle !== undefined) { + this._sourceControls.get(this._selectedSourceControlHandle)?.setSelectionState(false); + } - if (!sourceControl) { - return; - } - - sourceControl.setSelectionState(true); - } - }); - - this._selectedSourceControlHandles.forEach(handle => { - if (!set.has(handle)) { - const sourceControl = this._sourceControls.get(handle); - - if (!sourceControl) { - return; - } - - sourceControl.setSelectionState(false); - } - }); - - this._selectedSourceControlHandles = set; + this._selectedSourceControlHandle = selectedSourceControlHandle; return Promise.resolve(undefined); } } diff --git a/src/vs/workbench/api/common/extHostStatusBar.ts b/src/vs/workbench/api/common/extHostStatusBar.ts index d26ee3d7c86..edbaec0f60a 100644 --- a/src/vs/workbench/api/common/extHostStatusBar.ts +++ b/src/vs/workbench/api/common/extHostStatusBar.ts @@ -35,8 +35,9 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { private _timeoutHandle: any; private _proxy: MainThreadStatusBarShape; private _commands: CommandsConverter; + private _accessibilityInformation?: vscode.AccessibilityInformation; - constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, id: string, name: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number) { + constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, id: string, name: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number, accessibilityInformation?: vscode.AccessibilityInformation) { this._id = ExtHostStatusBarEntry.ID_GEN++; this._proxy = proxy; this._commands = commands; @@ -44,6 +45,7 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { this._statusName = name; this._alignment = alignment; this._priority = priority; + this._accessibilityInformation = accessibilityInformation; } public get id(): number { @@ -74,6 +76,10 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { return this._command?.fromApi; } + public get accessibilityInformation(): vscode.AccessibilityInformation | undefined { + return this._accessibilityInformation; + } + public set text(text: string) { this._text = text; this.update(); @@ -111,6 +117,11 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { this.update(); } + public set accessibilityInformation(accessibilityInformation: vscode.AccessibilityInformation | undefined) { + this._accessibilityInformation = accessibilityInformation; + this.update(); + } + public show(): void { this._visible = true; this.update(); @@ -136,7 +147,7 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { // Set to status bar this._proxy.$setEntry(this.id, this._statusId, this._statusName, this.text, this.tooltip, this._command?.internal, this.color, this._alignment === ExtHostStatusBarAlignment.Left ? MainThreadStatusBarAlignment.LEFT : MainThreadStatusBarAlignment.RIGHT, - this._priority); + this._priority, this._accessibilityInformation); }, 0); } @@ -196,8 +207,8 @@ export class ExtHostStatusBar { this._statusMessage = new StatusBarMessage(this); } - createStatusBarEntry(id: string, name: string, alignment?: ExtHostStatusBarAlignment, priority?: number): vscode.StatusBarItem { - return new ExtHostStatusBarEntry(this._proxy, this._commands, id, name, alignment, priority); + createStatusBarEntry(id: string, name: string, alignment?: ExtHostStatusBarAlignment, priority?: number, accessibilityInformation?: vscode.AccessibilityInformation): vscode.StatusBarItem { + return new ExtHostStatusBarEntry(this._proxy, this._commands, id, name, alignment, priority, accessibilityInformation); } setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable): Disposable { diff --git a/src/vs/workbench/api/common/extHostStoragePaths.ts b/src/vs/workbench/api/common/extHostStoragePaths.ts index cd73bf5563b..34dc47ea635 100644 --- a/src/vs/workbench/api/common/extHostStoragePaths.ts +++ b/src/vs/workbench/api/common/extHostStoragePaths.ts @@ -5,12 +5,83 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IEnvironment, IStaticWorkspaceData } from 'vs/workbench/api/common/extHost.protocol'; +import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer'; +import { URI } from 'vs/base/common/uri'; export const IExtensionStoragePaths = createDecorator('IExtensionStoragePaths'); export interface IExtensionStoragePaths { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; whenReady: Promise; - workspaceValue(extension: IExtensionDescription): string | undefined; - globalValue(extension: IExtensionDescription): string; + workspaceValue(extension: IExtensionDescription): URI | undefined; + globalValue(extension: IExtensionDescription): URI; +} + +export class ExtensionStoragePaths implements IExtensionStoragePaths { + + readonly _serviceBrand: undefined; + + private readonly _workspace?: IStaticWorkspaceData; + private readonly _environment: IEnvironment; + + readonly whenReady: Promise; + private _value?: URI; + + constructor( + @IExtHostInitDataService initData: IExtHostInitDataService, + @ILogService private readonly _logService: ILogService, + @IExtHostConsumerFileSystem private readonly _extHostFileSystem: IExtHostConsumerFileSystem + ) { + this._workspace = initData.workspace ?? undefined; + this._environment = initData.environment; + this.whenReady = this._getOrCreateWorkspaceStoragePath().then(value => this._value = value); + } + + private async _getOrCreateWorkspaceStoragePath(): Promise { + if (!this._workspace) { + return Promise.resolve(undefined); + } + const storageName = this._workspace.id; + const storageUri = URI.joinPath(this._environment.workspaceStorageHome, storageName); + + try { + await this._extHostFileSystem.stat(storageUri); + this._logService.trace('[ExtHostStorage] storage dir already exists', storageUri); + return storageUri; + } catch { + // doesn't exist, that's OK + } + + try { + this._logService.trace('[ExtHostStorage] creating dir and metadata-file', storageUri); + await this._extHostFileSystem.createDirectory(storageUri); + await this._extHostFileSystem.writeFile( + URI.joinPath(storageUri, 'meta.json'), + new TextEncoder().encode(JSON.stringify({ + id: this._workspace.id, + configuration: URI.revive(this._workspace.configuration)?.toString(), + name: this._workspace.name + }, undefined, 2)) + ); + return storageUri; + + } catch (e) { + this._logService.error('[ExtHostStorage]', e); + return undefined; + } + } + + workspaceValue(extension: IExtensionDescription): URI | undefined { + if (this._value) { + return URI.joinPath(this._value, extension.identifier.value); + } + return undefined; + } + + globalValue(extension: IExtensionDescription): URI { + return URI.joinPath(this._environment.globalStorageHome, extension.identifier.value.toLowerCase()); + } } diff --git a/src/vs/workbench/api/common/extHostTask.ts b/src/vs/workbench/api/common/extHostTask.ts index 71430153a3d..ee6423408fd 100644 --- a/src/vs/workbench/api/common/extHostTask.ts +++ b/src/vs/workbench/api/common/extHostTask.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { URI, UriComponents } from 'vs/base/common/uri'; -import * as Objects from 'vs/base/common/objects'; import { asPromise } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; @@ -27,6 +26,7 @@ import * as Platform from 'vs/base/common/platform'; import { ILogService } from 'vs/platform/log/common/log'; import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; import { USER_TASKS_GROUP_KEY } from 'vs/workbench/contrib/tasks/common/taskService'; +import { NotSupportedError } from 'vs/base/common/errors'; export interface IExtHostTask extends ExtHostTaskShape { @@ -325,7 +325,7 @@ export namespace TaskFilterDTO { if (!value) { return undefined; } - return Objects.assign(Object.create(null), value); + return Object.assign(Object.create(null), value); } } @@ -371,7 +371,7 @@ export interface HandlerData { extension: IExtensionDescription; } -export abstract class ExtHostTaskBase implements ExtHostTaskShape { +export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask { readonly _serviceBrand: undefined; protected readonly _proxy: MainThreadTaskShape; @@ -384,6 +384,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape { protected _handleCounter: number; protected _handlers: Map; protected _taskExecutions: Map; + protected _taskExecutionPromises: Map>; protected _providedCustomExecutions2: Map; private _notProvidedCustomExecutions: Set; // Used for custom executions tasks that are created and run through executeTask. protected _activeCustomExecutions2: Map; @@ -412,11 +413,13 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape { this._handleCounter = 0; this._handlers = new Map(); this._taskExecutions = new Map(); + this._taskExecutionPromises = new Map>(); this._providedCustomExecutions2 = new Map(); this._notProvidedCustomExecutions = new Set(); this._activeCustomExecutions2 = new Map(); this._logService = logService; this._deprecationService = deprecationService; + this._proxy.$registerSupportedExecutions(true); } public registerTaskProvider(extension: IExtensionDescription, type: string, provider: vscode.TaskProvider): vscode.Disposable { @@ -496,6 +499,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape { public async $OnDidEndTask(execution: tasks.TaskExecutionDTO): Promise { const _execution = await this.getTaskExecution(execution); + this._taskExecutionPromises.delete(execution.id); this._taskExecutions.delete(execution.id); this.customExecutionComplete(execution); this._onDidTerminateTask.fire({ @@ -509,12 +513,10 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape { public async $onDidStartTaskProcess(value: tasks.TaskProcessStartedDTO): Promise { const execution = await this.getTaskExecution(value.id); - if (execution) { - this._onDidTaskProcessStarted.fire({ - execution: execution, - processId: value.processId - }); - } + this._onDidTaskProcessStarted.fire({ + execution: execution, + processId: value.processId + }); } public get onDidEndTaskProcess(): Event { @@ -523,12 +525,10 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape { public async $onDidEndTaskProcess(value: tasks.TaskProcessEndedDTO): Promise { const execution = await this.getTaskExecution(value.id); - if (execution) { - this._onDidTaskProcessEnded.fire({ - execution: execution, - exitCode: value.exitCode - }); - } + this._onDidTaskProcessEnded.fire({ + execution: execution, + exitCode: value.exitCode + }); } protected abstract provideTasksInternal(validTypes: { [key: string]: boolean; }, taskIdPromises: Promise[], handler: HandlerData, value: vscode.Task[] | null | undefined): { tasks: tasks.TaskDTO[], extension: IExtensionDescription }; @@ -619,24 +619,33 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape { protected async getTaskExecution(execution: tasks.TaskExecutionDTO | string, task?: vscode.Task): Promise { if (typeof execution === 'string') { - const taskExecution = this._taskExecutions.get(execution); + const taskExecution = this._taskExecutionPromises.get(execution); if (!taskExecution) { throw new Error('Unexpected: The specified task is missing an execution'); } return taskExecution; } - let result: TaskExecutionImpl | undefined = this._taskExecutions.get(execution.id); + let result: Promise | undefined = this._taskExecutionPromises.get(execution.id); if (result) { return result; } - const taskToCreate = task ? task : await TaskDTO.to(execution.task, this._workspaceProvider); - if (!taskToCreate) { - throw new Error('Unexpected: Task does not exist.'); - } - const createdResult: TaskExecutionImpl = new TaskExecutionImpl(this, execution.id, taskToCreate); - this._taskExecutions.set(execution.id, createdResult); - return createdResult; + const createdResult: Promise = new Promise(async (resolve, reject) => { + const taskToCreate = task ? task : await TaskDTO.to(execution.task, this._workspaceProvider); + if (!taskToCreate) { + reject('Unexpected: Task does not exist.'); + } else { + resolve(new TaskExecutionImpl(this, execution.id, taskToCreate)); + } + }); + + this._taskExecutionPromises.set(execution.id, createdResult); + return createdResult.then(executionCreatedResult => { + this._taskExecutions.set(execution.id, executionCreatedResult); + return executionCreatedResult; + }, rejected => { + return Promise.reject(rejected); + }); } protected checkDeprecation(task: vscode.Task, handler: HandlerData) { @@ -671,7 +680,9 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape { } } - public abstract async $jsonTasksSupported(): Promise; + public abstract $jsonTasksSupported(): Promise; + + public abstract $findExecutable(command: string, cwd?: string | undefined, paths?: string[] | undefined): Promise; } export class WorkerExtHostTask extends ExtHostTaskBase { @@ -686,19 +697,17 @@ export class WorkerExtHostTask extends ExtHostTaskBase { @IExtHostApiDeprecationService deprecationService: IExtHostApiDeprecationService ) { super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService, logService, deprecationService); - if (initData.remote.isRemote && initData.remote.authority) { - this.registerTaskSystem(Schemas.vscodeRemote, { - scheme: Schemas.vscodeRemote, - authority: initData.remote.authority, - platform: Platform.PlatformToString(Platform.Platform.Web) - }); - } + this.registerTaskSystem(Schemas.vscodeRemote, { + scheme: Schemas.vscodeRemote, + authority: '', + platform: Platform.PlatformToString(Platform.Platform.Web) + }); } public async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise { const dto = TaskDTO.from(task, extension); if (dto === undefined) { - return Promise.reject(new Error('Task is not valid')); + throw new Error('Task is not valid'); } // If this task is a custom execution, then we need to save it away @@ -707,10 +716,13 @@ export class WorkerExtHostTask extends ExtHostTaskBase { if (CustomExecutionDTO.is(dto.execution)) { await this.addCustomExecution(dto, task, false); } else { - throw new Error('Not implemented'); + throw new NotSupportedError(); } - return this._proxy.$executeTask(dto).then(value => this.getTaskExecution(value, task)); + // Always get the task execution first to prevent timing issues when retrieving it later + const execution = await this.getTaskExecution(await this._proxy.$getTaskExecution(dto), task); + this._proxy.$executeTask(dto).catch(error => { throw new Error(error); }); + return execution; } protected provideTasksInternal(validTypes: { [key: string]: boolean; }, taskIdPromises: Promise[], handler: HandlerData, value: vscode.Task[] | null | undefined): { tasks: tasks.TaskDTO[], extension: IExtensionDescription } { @@ -764,6 +776,10 @@ export class WorkerExtHostTask extends ExtHostTaskBase { public async $jsonTasksSupported(): Promise { return false; } + + public async $findExecutable(command: string, cwd?: string | undefined, paths?: string[] | undefined): Promise { + return undefined; + } } export const IExtHostTask = createDecorator('IExtHostTask'); diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 834116c615f..ed8fd570cdd 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -5,11 +5,11 @@ import type * as vscode from 'vscode'; import { Event, Emitter } from 'vs/base/common/event'; -import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto, ITerminalDimensionsDto } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto, ITerminalDimensionsDto, ITerminalLinkDto } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { ITerminalChildProcess, ITerminalDimensions, EXT_HOST_CREATION_DELAY } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalChildProcess, ITerminalDimensions, EXT_HOST_CREATION_DELAY, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; import { timeout } from 'vs/base/common/async'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering'; @@ -17,10 +17,14 @@ import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Disposable as VSCodeDisposable, EnvironmentVariableMutatorType } from './extHostTypes'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; +import { localize } from 'vs/nls'; +import { NotSupportedError } from 'vs/base/common/errors'; +import { serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; export interface IExtHostTerminalService extends ExtHostTerminalServiceShape { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; activeTerminal: vscode.Terminal | undefined; terminals: vscode.Terminal[]; @@ -38,6 +42,7 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape { getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string; getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string; registerLinkHandler(handler: vscode.TerminalLinkHandler): vscode.Disposable; + registerLinkProvider(provider: vscode.TerminalLinkProvider): vscode.Disposable; getEnvironmentVariableCollection(extension: IExtensionDescription, persistent?: boolean): vscode.EnvironmentVariableCollection; } @@ -243,6 +248,10 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess { constructor(private readonly _pty: vscode.Pseudoterminal) { } + async start(): Promise { + return undefined; + } + shutdown(): void { this._pty.close(); } @@ -288,6 +297,13 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess { } } +let nextLinkId = 1; + +interface ICachedLinkEntry { + provider: vscode.TerminalLinkProvider; + link: vscode.TerminalLink; +} + export abstract class BaseExtHostTerminalService implements IExtHostTerminalService, ExtHostTerminalServiceShape { readonly _serviceBrand: undefined; @@ -299,9 +315,13 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ protected _terminalProcessDisposables: { [id: number]: IDisposable } = {}; protected _extensionTerminalAwaitingStart: { [id: number]: { initialDimensions: ITerminalDimensionsDto | undefined } | undefined } = {}; protected _getTerminalPromises: { [id: number]: Promise } = {}; + protected _environmentVariableCollections: Map = new Map(); private readonly _bufferer: TerminalDataBufferer; private readonly _linkHandlers: Set = new Set(); + private readonly _linkProviders: Set = new Set(); + private readonly _terminalLinkCache: Map> = new Map(); + private readonly _terminalLinkCancellationSource: Map = new Map(); public get activeTerminal(): ExtHostTerminal | undefined { return this._activeTerminal; } public get terminals(): ExtHostTerminal[] { return this._terminals; } @@ -332,12 +352,10 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ public abstract createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal; public abstract getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string; public abstract getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string; - public abstract $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise; + public abstract $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise; public abstract $getAvailableShells(): Promise; public abstract $getDefaultShellAndArgs(useAutomationShell: boolean): Promise; public abstract $acceptWorkspacePermissionsChanged(isAllowed: boolean): void; - public abstract getEnvironmentVariableCollection(extension: IExtensionDescription, persistent?: boolean): vscode.EnvironmentVariableCollection; - public abstract $initEnvironmentVariableCollections(collections: [string, ISerializableEnvironmentVariableCollection][]): void; public createExtensionTerminal(options: vscode.ExtensionTerminalOptions): vscode.Terminal { const terminal = new ExtHostTerminal(this._proxy, options, options.name); @@ -439,7 +457,8 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ shellPath: shellLaunchConfigDto.executable, shellArgs: shellLaunchConfigDto.args, cwd: typeof shellLaunchConfigDto.cwd === 'string' ? shellLaunchConfigDto.cwd : URI.revive(shellLaunchConfigDto.cwd), - env: shellLaunchConfigDto.env + env: shellLaunchConfigDto.env, + hideFromUser: shellLaunchConfigDto.hideFromUser }; const terminal = new ExtHostTerminal(this._proxy, creationOptions, name, id); this._terminals.push(terminal); @@ -454,20 +473,17 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ } } - public async $startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): Promise { + public async $startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): Promise { // Make sure the ExtHostTerminal exists so onDidOpenTerminal has fired before we call // Pseudoterminal.start const terminal = await this._getTerminalByIdEventually(id); if (!terminal) { - return; + return { message: localize('launchFail.idMissingOnExtHost', "Could not find the terminal with id {0} on the extension host", id) }; } // Wait for onDidOpenTerminal to fire - let openPromise: Promise; - if (terminal.isOpen) { - openPromise = Promise.resolve(); - } else { - openPromise = new Promise(r => { + if (!terminal.isOpen) { + await new Promise(r => { // Ensure open is called after onDidOpenTerminal const listener = this.onDidOpenTerminal(async e => { if (e === terminal) { @@ -477,7 +493,6 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ }); }); } - await openPromise; if (this._terminalProcesses[id]) { (this._terminalProcesses[id] as ExtHostPseudoterminal).startSendingEvents(initialDimensions); @@ -486,6 +501,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ this._extensionTerminalAwaitingStart[id] = { initialDimensions }; } + return undefined; } protected _setupExtHostProcessListeners(id: number, p: ITerminalChildProcess): IDisposable { @@ -545,17 +561,30 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ public registerLinkHandler(handler: vscode.TerminalLinkHandler): vscode.Disposable { this._linkHandlers.add(handler); - if (this._linkHandlers.size === 1) { + if (this._linkHandlers.size === 1 && this._linkProviders.size === 0) { this._proxy.$startHandlingLinks(); } return new VSCodeDisposable(() => { this._linkHandlers.delete(handler); - if (this._linkHandlers.size === 0) { + if (this._linkHandlers.size === 0 && this._linkProviders.size === 0) { this._proxy.$stopHandlingLinks(); } }); } + public registerLinkProvider(provider: vscode.TerminalLinkProvider): vscode.Disposable { + this._linkProviders.add(provider); + if (this._linkProviders.size === 1) { + this._proxy.$startLinkProvider(); + } + return new VSCodeDisposable(() => { + this._linkProviders.delete(provider); + if (this._linkProviders.size === 0) { + this._proxy.$stopLinkProvider(); + } + }); + } + public async $handleLink(id: number, link: string): Promise { const terminal = this._getTerminalById(id); if (!terminal) { @@ -575,6 +604,75 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ return false; } + public async $provideLinks(terminalId: number, line: string): Promise { + const terminal = this._getTerminalById(terminalId); + if (!terminal) { + return []; + } + + // Discard any cached links the terminal has been holding, currently all links are released + // when new links are provided. + this._terminalLinkCache.delete(terminalId); + + const oldToken = this._terminalLinkCancellationSource.get(terminalId); + if (oldToken) { + oldToken.dispose(true); + } + const cancellationSource = new CancellationTokenSource(); + this._terminalLinkCancellationSource.set(terminalId, cancellationSource); + + const result: ITerminalLinkDto[] = []; + const context: vscode.TerminalLinkContext = { terminal, line }; + const promises: vscode.ProviderResult<{ provider: vscode.TerminalLinkProvider, links: vscode.TerminalLink[] }>[] = []; + + for (const provider of this._linkProviders) { + promises.push(new Promise(async r => { + cancellationSource.token.onCancellationRequested(() => r({ provider, links: [] })); + const links = (await provider.provideTerminalLinks(context, cancellationSource.token)) || []; + if (!cancellationSource.token.isCancellationRequested) { + r({ provider, links }); + } + })); + } + + const provideResults = await Promise.all(promises); + + if (cancellationSource.token.isCancellationRequested) { + return []; + } + + const cacheLinkMap = new Map(); + for (const provideResult of provideResults) { + if (provideResult && provideResult.links.length > 0) { + result.push(...provideResult.links.map(providerLink => { + const link = { + id: nextLinkId++, + startIndex: providerLink.startIndex, + length: providerLink.length, + label: providerLink.tooltip + }; + cacheLinkMap.set(link.id, { + provider: provideResult.provider, + link: providerLink + }); + return link; + })); + } + } + + this._terminalLinkCache.set(terminalId, cacheLinkMap); + + return result; + } + + $activateLink(terminalId: number, linkId: number): void { + const cachedLink = this._terminalLinkCache.get(terminalId)?.get(linkId); + if (!cachedLink) { + return; + } + cachedLink.provider.handleTerminalLink(cachedLink.link); + } + private _onProcessExit(id: number, exitCode: number | undefined): void { this._bufferer.stopBuffering(id); @@ -640,6 +738,39 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ }); return index; } + + public getEnvironmentVariableCollection(extension: IExtensionDescription): vscode.EnvironmentVariableCollection { + let collection = this._environmentVariableCollections.get(extension.identifier.value); + if (!collection) { + collection = new EnvironmentVariableCollection(); + this._setEnvironmentVariableCollection(extension.identifier.value, collection); + } + return collection; + } + + private _syncEnvironmentVariableCollection(extensionIdentifier: string, collection: EnvironmentVariableCollection): void { + const serialized = serializeEnvironmentVariableCollection(collection.map); + this._proxy.$setEnvironmentVariableCollection(extensionIdentifier, collection.persistent, serialized.length === 0 ? undefined : serialized); + } + + public $initEnvironmentVariableCollections(collections: [string, ISerializableEnvironmentVariableCollection][]): void { + collections.forEach(entry => { + const extensionIdentifier = entry[0]; + const collection = new EnvironmentVariableCollection(entry[1]); + this._setEnvironmentVariableCollection(extensionIdentifier, collection); + }); + } + + private _setEnvironmentVariableCollection(extensionIdentifier: string, collection: EnvironmentVariableCollection): void { + this._environmentVariableCollections.set(extensionIdentifier, collection); + collection.onDidChangeCollection(() => { + // When any collection value changes send this immediately, this is done to ensure + // following calls to createTerminal will be created with the new environment. It will + // result in more noise by sending multiple updates when called but collections are + // expected to be small. + this._syncEnvironmentVariableCollection(extensionIdentifier, collection!); + }); + } } export class EnvironmentVariableCollection implements vscode.EnvironmentVariableCollection { @@ -706,43 +837,35 @@ export class EnvironmentVariableCollection implements vscode.EnvironmentVariable export class WorkerExtHostTerminalService extends BaseExtHostTerminalService { public createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal { - throw new Error('Not implemented'); + throw new NotSupportedError(); } public createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal { - throw new Error('Not implemented'); + throw new NotSupportedError(); } public getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string { - throw new Error('Not implemented'); + // Return the empty string to avoid throwing + return ''; } public getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string { - throw new Error('Not implemented'); + throw new NotSupportedError(); } - public $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise { - throw new Error('Not implemented'); + public $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise { + throw new NotSupportedError(); } public $getAvailableShells(): Promise { - throw new Error('Not implemented'); + throw new NotSupportedError(); } public async $getDefaultShellAndArgs(useAutomationShell: boolean): Promise { - throw new Error('Not implemented'); + throw new NotSupportedError(); } public $acceptWorkspacePermissionsChanged(isAllowed: boolean): void { // No-op for web worker ext host as workspace permissions aren't used } - - public getEnvironmentVariableCollection(extension: IExtensionDescription, persistent?: boolean): vscode.EnvironmentVariableCollection { - // This is not implemented so worker ext host extensions cannot influence terminal envs - throw new Error('Not implemented'); - } - - public $initEnvironmentVariableCollections(collections: [string, ISerializableEnvironmentVariableCollection][]): void { - // No-op for web worker ext host as collections aren't used - } } diff --git a/src/vs/workbench/api/common/extHostTextEditors.ts b/src/vs/workbench/api/common/extHostTextEditors.ts index 4a61bf07536..e99f513e940 100644 --- a/src/vs/workbench/api/common/extHostTextEditors.ts +++ b/src/vs/workbench/api/common/extHostTextEditors.ts @@ -54,7 +54,7 @@ export class ExtHostEditors implements ExtHostEditorsShape { showTextDocument(document: vscode.TextDocument, column: vscode.ViewColumn, preserveFocus: boolean): Promise; showTextDocument(document: vscode.TextDocument, options: { column: vscode.ViewColumn, preserveFocus: boolean, pinned: boolean }): Promise; showTextDocument(document: vscode.TextDocument, columnOrOptions: vscode.ViewColumn | vscode.TextDocumentShowOptions | undefined, preserveFocus?: boolean): Promise; - showTextDocument(document: vscode.TextDocument, columnOrOptions: vscode.ViewColumn | vscode.TextDocumentShowOptions | undefined, preserveFocus?: boolean): Promise { + async showTextDocument(document: vscode.TextDocument, columnOrOptions: vscode.ViewColumn | vscode.TextDocumentShowOptions | undefined, preserveFocus?: boolean): Promise { let options: ITextDocumentShowOptions; if (typeof columnOrOptions === 'number') { options = { @@ -74,14 +74,18 @@ export class ExtHostEditors implements ExtHostEditorsShape { }; } - return this._proxy.$tryShowTextDocument(document.uri, options).then(id => { - const editor = id && this._extHostDocumentsAndEditors.getEditor(id); - if (editor) { - return editor; - } else { - throw new Error(`Failed to show text document ${document.uri.toString()}, should show in editor #${id}`); - } - }); + const editorId = await this._proxy.$tryShowTextDocument(document.uri, options); + const editor = editorId && this._extHostDocumentsAndEditors.getEditor(editorId); + if (editor) { + return editor; + } + // we have no editor... having an id means that we had an editor + // on the main side and that it isn't the current editor anymore... + if (editorId) { + throw new Error(`Could NOT open editor for "${document.uri.toString()}" because another editor opened in the meantime.`); + } else { + throw new Error(`Could NOT open editor for "${document.uri.toString()}".`); + } } createTextEditorDecorationType(options: vscode.DecorationRenderOptions): vscode.TextEditorDecorationType { diff --git a/src/vs/workbench/api/common/extHostTimeline.ts b/src/vs/workbench/api/common/extHostTimeline.ts index c15a9e73617..3d3cf981856 100644 --- a/src/vs/workbench/api/common/extHostTimeline.ts +++ b/src/vs/workbench/api/common/extHostTimeline.ts @@ -22,7 +22,7 @@ export interface IExtHostTimeline extends ExtHostTimelineShape { export const IExtHostTimeline = createDecorator('IExtHostTimeline'); export class ExtHostTimeline implements IExtHostTimeline { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _proxy: MainThreadTimelineShape; @@ -78,9 +78,7 @@ export class ExtHostTimeline implements IExtHostTimeline { } const result = await provider.provideTimeline(uri, options, token); - // Intentional == we don't know how a provider will respond - // eslint-disable-next-line eqeqeq - if (result == null) { + if (result === undefined || result === null) { return undefined; } @@ -152,7 +150,8 @@ export class ExtHostTimeline implements IExtHostTimeline { command: item.command ? commandConverter.toInternal(item.command, disposables) : undefined, icon: icon, iconDark: iconDark, - themeIcon: themeIcon + themeIcon: themeIcon, + accessibilityInformation: item.accessibilityInformation }; }; }; @@ -188,4 +187,3 @@ export class ExtHostTimeline implements IExtHostTimeline { function getUriKey(uri: URI | undefined): string | undefined { return uri?.toString(); } - diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index a2e117ad7e5..8621743e706 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -13,12 +13,14 @@ import { ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.proto import { ITreeItem, TreeViewItemHandleArg, ITreeItemLabel, IRevealOptions } from 'vs/workbench/common/views'; import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/common/extHostCommands'; import { asPromise } from 'vs/base/common/async'; -import { TreeItemCollapsibleState, ThemeIcon } from 'vs/workbench/api/common/extHostTypes'; +import { TreeItemCollapsibleState, ThemeIcon, MarkdownString as MarkdownStringType } from 'vs/workbench/api/common/extHostTypes'; import { isUndefinedOrNull, isString } from 'vs/base/common/types'; import { equals, coalesce } from 'vs/base/common/arrays'; import { ILogService } from 'vs/platform/log/common/log'; import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { MarkdownString } from 'vs/workbench/api/common/extHostTypeConverters'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; type TreeItemHandle = string; @@ -117,6 +119,22 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { return treeView.getChildren(treeItemHandle); } + async $hasResolve(treeViewId: string): Promise { + const treeView = this.treeViews.get(treeViewId); + if (!treeView) { + throw new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId)); + } + return treeView.hasResolve; + } + + $resolve(treeViewId: string, treeItemHandle: string): Promise { + const treeView = this.treeViews.get(treeViewId); + if (!treeView) { + throw new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId)); + } + return treeView.resolveTreeItem(treeItemHandle); + } + $setExpanded(treeViewId: string, treeItemHandle: string, expanded: boolean): void { const treeView = this.treeViews.get(treeViewId); if (!treeView) { @@ -158,6 +176,7 @@ type TreeData = { message: boolean, element: T | Root | false }; interface TreeNode extends IDisposable { item: ITreeItem; + extensionItem: vscode.TreeItem2; parent: TreeNode | Root; children?: TreeNode[]; } @@ -327,6 +346,27 @@ class ExtHostTreeView extends Disposable { } } + get hasResolve(): boolean { + return !!this.dataProvider.resolveTreeItem; + } + + async resolveTreeItem(treeItemHandle: string): Promise { + if (!this.dataProvider.resolveTreeItem) { + return; + } + const element = this.elements.get(treeItemHandle); + if (element) { + const node = this.nodes.get(element); + if (node) { + const resolve = await this.dataProvider.resolveTreeItem(element, node.extensionItem); + // Resolvable elements. Currently only tooltip. + node.item.tooltip = this.getTooltip(resolve.tooltip); + return node.item; + } + } + return; + } + private resolveUnknownParentChain(element: T): Promise { return this.resolveParent(element) .then((parent) => { @@ -486,7 +526,15 @@ class ExtHostTreeView extends Disposable { return node; } - private createTreeNode(element: T, extensionTreeItem: vscode.TreeItem, parent: TreeNode | Root): TreeNode { + private getTooltip(tooltip?: string | vscode.MarkdownString): string | IMarkdownString | undefined { + if (MarkdownStringType.isMarkdownString(tooltip)) { + checkProposedApiEnabled(this.extension); + return MarkdownString.from(tooltip); + } + return tooltip; + } + + private createTreeNode(element: T, extensionTreeItem: vscode.TreeItem2, parent: TreeNode | Root): TreeNode { const disposable = new DisposableStore(); const handle = this.createHandle(element, extensionTreeItem, parent); const icon = this.getLightIconPath(extensionTreeItem); @@ -496,17 +544,19 @@ class ExtHostTreeView extends Disposable { label: toTreeItemLabel(extensionTreeItem.label, this.extension), description: extensionTreeItem.description, resourceUri: extensionTreeItem.resourceUri, - tooltip: typeof extensionTreeItem.tooltip === 'string' ? extensionTreeItem.tooltip : undefined, + tooltip: this.getTooltip(extensionTreeItem.tooltip), command: extensionTreeItem.command ? this.commands.toInternal(extensionTreeItem.command, disposable) : undefined, contextValue: extensionTreeItem.contextValue, icon, iconDark: this.getDarkIconPath(extensionTreeItem) || icon, themeIcon: extensionTreeItem.iconPath instanceof ThemeIcon ? { id: extensionTreeItem.iconPath.id } : undefined, - collapsibleState: isUndefinedOrNull(extensionTreeItem.collapsibleState) ? TreeItemCollapsibleState.None : extensionTreeItem.collapsibleState + collapsibleState: isUndefinedOrNull(extensionTreeItem.collapsibleState) ? TreeItemCollapsibleState.None : extensionTreeItem.collapsibleState, + accessibilityInformation: extensionTreeItem.accessibilityInformation }; return { item, + extensionItem: extensionTreeItem, parent, children: undefined, dispose(): void { disposable.dispose(); } diff --git a/src/vs/workbench/api/common/extHostTunnelService.ts b/src/vs/workbench/api/common/extHostTunnelService.ts index 60aec092a05..ca249267d38 100644 --- a/src/vs/workbench/api/common/extHostTunnelService.ts +++ b/src/vs/workbench/api/common/extHostTunnelService.ts @@ -41,7 +41,7 @@ export interface IExtHostTunnelService extends ExtHostTunnelServiceShape { export const IExtHostTunnelService = createDecorator('IExtHostTunnelService'); export class ExtHostTunnelService implements IExtHostTunnelService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; onDidChangeTunnels: vscode.Event = (new Emitter()).event; private readonly _proxy: MainThreadTunnelServiceShape; diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index b24be8d20a0..6a7993f9ae7 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -95,11 +95,22 @@ export namespace Range { } } +export namespace TokenType { + export function to(type: modes.StandardTokenType): types.StandardTokenType { + switch (type) { + case modes.StandardTokenType.Comment: return types.StandardTokenType.Comment; + case modes.StandardTokenType.Other: return types.StandardTokenType.Other; + case modes.StandardTokenType.RegEx: return types.StandardTokenType.RegEx; + case modes.StandardTokenType.String: return types.StandardTokenType.String; + } + } +} + export namespace Position { export function to(position: IPosition): types.Position { return new types.Position(position.lineNumber - 1, position.column - 1); } - export function from(position: types.Position): IPosition { + export function from(position: types.Position | vscode.Position): IPosition { return { lineNumber: position.line + 1, column: position.character + 1 }; } } @@ -635,7 +646,7 @@ export namespace DocumentSymbol { range: Range.from(info.range), selectionRange: Range.from(info.selectionRange), kind: SymbolKind.from(info.kind), - tags: info.tags ? info.tags.map(SymbolTag.from) : [] + tags: info.tags?.map(SymbolTag.from) ?? [] }; if (info.children) { result.children = info.children.map(from); @@ -900,7 +911,7 @@ export namespace CompletionItem { result.insertText = suggestion.insertText; result.kind = CompletionItemKind.to(suggestion.kind); - result.tags = suggestion.tags && suggestion.tags.map(CompletionItemTag.to); + result.tags = suggestion.tags?.map(CompletionItemTag.to); result.detail = suggestion.detail; result.documentation = htmlContent.isMarkdownString(suggestion.documentation) ? MarkdownString.to(suggestion.documentation) : suggestion.documentation; result.sortText = suggestion.sortText; @@ -1153,15 +1164,20 @@ export namespace FoldingRangeKind { } } -export namespace TextEditorOptions { +export interface TextEditorOpenOptions extends vscode.TextDocumentShowOptions { + background?: boolean; +} - export function from(options?: vscode.TextDocumentShowOptions): ITextEditorOptions | undefined { +export namespace TextEditorOpenOptions { + + export function from(options?: TextEditorOpenOptions): ITextEditorOptions | undefined { if (options) { return { pinned: typeof options.preview === 'boolean' ? !options.preview : undefined, + inactive: options.background, preserveFocus: options.preserveFocus, - selection: typeof options.selection === 'object' ? Range.from(options.selection) : undefined - } as ITextEditorOptions; + selection: typeof options.selection === 'object' ? Range.from(options.selection) : undefined, + }; } return undefined; diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 4894d276e46..447fa8092c6 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1252,7 +1252,7 @@ export class MarkdownString { // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash this.value += (this.supportThemeIcons ? escapeCodicons(value) : value) .replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&') - .replace('\n', '\n\n'); + .replace(/\n/, '\n\n'); return this; } @@ -1271,6 +1271,13 @@ export class MarkdownString { this.value += '\n```\n'; return this; } + + static isMarkdownString(thing: any): thing is vscode.MarkdownString { + if (thing instanceof MarkdownString) { + return true; + } + return thing && thing.appendCodeblock && thing.appendMarkdown && thing.appendText && (thing.value !== undefined); + } } @es5ClassCompat @@ -2101,7 +2108,7 @@ export class TreeItem { iconPath?: string | URI | { light: string | URI; dark: string | URI; }; command?: vscode.Command; contextValue?: string; - tooltip?: string; + tooltip?: string | vscode.MarkdownString; constructor(label: string | vscode.TreeItemLabel, collapsibleState?: vscode.TreeItemCollapsibleState); constructor(resourceUri: URI, collapsibleState?: vscode.TreeItemCollapsibleState); @@ -2732,6 +2739,11 @@ export enum NotebookCellRunState { Error = 4 } +export enum NotebookRunState { + Running = 1, + Idle = 2 +} + //#endregion //#region Timeline @@ -2750,7 +2762,7 @@ export enum ExtensionMode { * The extension is installed normally (for example, from the marketplace * or VSIX) in VS Code. */ - Release = 1, + Production = 1, /** * The extension is running from an `--extensionDevelopmentPath` provided @@ -2766,3 +2778,10 @@ export enum ExtensionMode { } //#endregion ExtensionContext + +export enum StandardTokenType { + Other = 0, + Comment = 1, + String = 2, + RegEx = 4 +} diff --git a/src/vs/workbench/api/common/extHostUriTransformerService.ts b/src/vs/workbench/api/common/extHostUriTransformerService.ts index 97b00f23806..4d86037b020 100644 --- a/src/vs/workbench/api/common/extHostUriTransformerService.ts +++ b/src/vs/workbench/api/common/extHostUriTransformerService.ts @@ -8,13 +8,13 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { URI, UriComponents } from 'vs/base/common/uri'; export interface IURITransformerService extends IURITransformer { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; } export const IURITransformerService = createDecorator('IURITransformerService'); export class URITransformerService implements IURITransformerService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; transformIncoming: (uri: UriComponents) => UriComponents; transformOutgoing: (uri: UriComponents) => UriComponents; diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index ee06aec4d66..f005de99781 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -273,7 +273,7 @@ class CustomDocumentStoreEntry { constructor( public readonly document: vscode.CustomDocument, - private readonly _storagePath: string, + private readonly _storagePath: URI | undefined, ) { } private readonly _edits = new Cache('custom documents'); @@ -305,7 +305,11 @@ class CustomDocumentStoreEntry { } getNewBackupUri(): URI { - return joinPath(URI.file(this._storagePath), hashPath(this.document.uri) + (this._backupCounter++)); + if (!this._storagePath) { + throw new Error('Backup requires a valid storage path'); + } + const fileName = hashPath(this.document.uri) + (this._backupCounter++); + return joinPath(this._storagePath, fileName); } updateBackup(backup: vscode.CustomDocumentBackup): void { @@ -334,7 +338,7 @@ class CustomDocumentStore { return this._documents.get(this.key(viewType, resource)); } - public add(viewType: string, document: vscode.CustomDocument, storagePath: string): CustomDocumentStoreEntry { + public add(viewType: string, document: vscode.CustomDocument, storagePath: URI | undefined): CustomDocumentStoreEntry { const key = this.key(viewType, document.uri); if (this._documents.has(key)) { throw new Error(`Document already exists for viewType:${viewType} resource:${document.uri}`); @@ -573,7 +577,7 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { } const { serializer, extension } = entry; - const webview = new ExtHostWebview(webviewHandle, this._proxy, options, this.initData, this.workspace, extension, this._deprecationService); + const webview = new ExtHostWebview(webviewHandle, this._proxy, reviveOptions(options), this.initData, this.workspace, extension, this._deprecationService); const revivedPanel = new ExtHostWebviewEditor(webviewHandle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview); this._webviewPanels.set(webviewHandle, revivedPanel); await serializer.deserializeWebviewPanel(revivedPanel, state); @@ -592,8 +596,11 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { const revivedResource = URI.revive(resource); const document = await entry.provider.openCustomDocument(revivedResource, { backupId }, cancellation); - const storageRoot = this._extensionStoragePaths?.workspaceValue(entry.extension) ?? this._extensionStoragePaths?.globalValue(entry.extension); - this._documents.add(viewType, document, storageRoot!); + let storageRoot: URI | undefined; + if (this.supportEditing(entry.provider) && this._extensionStoragePaths) { + storageRoot = this._extensionStoragePaths.workspaceValue(entry.extension) ?? this._extensionStoragePaths.globalValue(entry.extension); + } + this._documents.add(viewType, document, storageRoot); return { editable: this.supportEditing(entry.provider) }; } @@ -628,7 +635,7 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { throw new Error(`No provider found for '${viewType}'`); } - const webview = new ExtHostWebview(handle, this._proxy, options, this.initData, this.workspace, entry.extension, this._deprecationService); + const webview = new ExtHostWebview(handle, this._proxy, reviveOptions(options), this.initData, this.workspace, entry.extension, this._deprecationService); const revivedPanel = new ExtHostWebviewEditor(handle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview); this._webviewPanels.set(handle, revivedPanel); @@ -761,6 +768,15 @@ function convertWebviewOptions( }; } +function reviveOptions( + options: modes.IWebviewOptions & modes.IWebviewPanelOptions +): vscode.WebviewOptions { + return { + ...options, + localResourceRoots: options.localResourceRoots?.map(components => URI.from(components)), + }; +} + function getDefaultLocalResourceRoots( extension: IExtensionDescription, workspace: IExtHostWorkspace | undefined, diff --git a/src/vs/workbench/api/common/extHostWindow.ts b/src/vs/workbench/api/common/extHostWindow.ts index f8c5d5fa3d7..b9f1cdee4b8 100644 --- a/src/vs/workbench/api/common/extHostWindow.ts +++ b/src/vs/workbench/api/common/extHostWindow.ts @@ -4,13 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; -import { ExtHostWindowShape, MainContext, MainThreadWindowShape, IMainContext, IOpenUriOptions } from './extHost.protocol'; +import { ExtHostWindowShape, MainContext, MainThreadWindowShape, IOpenUriOptions } from './extHost.protocol'; import { WindowState } from 'vscode'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { isFalsyOrWhitespace } from 'vs/base/common/strings'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; -export class ExtHostWindow implements ExtHostWindowShape { +export class ExtHostWindow implements IExtHostWindow { private static InitialState: WindowState = { focused: true @@ -24,8 +26,8 @@ export class ExtHostWindow implements ExtHostWindowShape { private _state = ExtHostWindow.InitialState; get state(): WindowState { return this._state; } - constructor(mainContext: IMainContext) { - this._proxy = mainContext.getProxy(MainContext.MainThreadWindow); + constructor(@IExtHostRpcService extHostRpc: IExtHostRpcService) { + this._proxy = extHostRpc.getProxy(MainContext.MainThreadWindow); this._proxy.$getWindowVisibility().then(isFocused => this.$onDidChangeWindowFocus(isFocused)); } @@ -67,3 +69,6 @@ export class ExtHostWindow implements ExtHostWindowShape { return URI.from(result); } } + +export const IExtHostWindow = createDecorator('IExtHostWindow'); +export interface IExtHostWindow extends ExtHostWindow, ExtHostWindowShape { } diff --git a/src/vs/workbench/api/common/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts index 4342d65c24b..99077b262ed 100644 --- a/src/vs/workbench/api/common/menusExtensionPoint.ts +++ b/src/vs/workbench/api/common/menusExtensionPoint.ts @@ -10,14 +10,166 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { forEach } from 'vs/base/common/collections'; import { IExtensionPointUser, ExtensionMessageCollector, ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { MenuId, MenuRegistry, ILocalizedString, IMenuItem, ICommandAction } from 'vs/platform/actions/common/actions'; +import { MenuId, MenuRegistry, ILocalizedString, IMenuItem, ICommandAction, ISubmenuItem } from 'vs/platform/actions/common/actions'; import { URI } from 'vs/base/common/uri'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { Iterable } from 'vs/base/common/iterator'; +import { index } from 'vs/base/common/arrays'; + +interface IAPIMenu { + readonly key: string; + readonly id: MenuId; + readonly description: string; + readonly proposed?: boolean; // defaults to false + readonly supportsSubmenus?: boolean; // defaults to true +} + +const apiMenus: IAPIMenu[] = [ + { + key: 'commandPalette', + id: MenuId.CommandPalette, + description: localize('menus.commandPalette', "The Command Palette"), + supportsSubmenus: false + }, + { + key: 'touchBar', + id: MenuId.TouchBarContext, + description: localize('menus.touchBar', "The touch bar (macOS only)"), + supportsSubmenus: false + }, + { + key: 'editor/title', + id: MenuId.EditorTitle, + description: localize('menus.editorTitle', "The editor title menu") + }, + { + key: 'editor/context', + id: MenuId.EditorContext, + description: localize('menus.editorContext', "The editor context menu") + }, + { + key: 'explorer/context', + id: MenuId.ExplorerContext, + description: localize('menus.explorerContext', "The file explorer context menu") + }, + { + key: 'editor/title/context', + id: MenuId.EditorTitleContext, + description: localize('menus.editorTabContext', "The editor tabs context menu") + }, + { + key: 'debug/callstack/context', + id: MenuId.DebugCallStackContext, + description: localize('menus.debugCallstackContext', "The debug callstack context menu") + }, + { + key: 'debug/toolBar', + id: MenuId.DebugToolBar, + description: localize('menus.debugToolBar', "The debug toolbar menu") + }, + { + key: 'menuBar/webNavigation', + id: MenuId.MenubarWebNavigationMenu, + description: localize('menus.webNavigation', "The top level navigational menu (web only)"), + proposed: true, + supportsSubmenus: false + }, + { + key: 'scm/title', + id: MenuId.SCMTitle, + description: localize('menus.scmTitle', "The Source Control title menu") + }, + { + key: 'scm/sourceControl', + id: MenuId.SCMSourceControl, + description: localize('menus.scmSourceControl', "The Source Control menu") + }, + { + key: 'scm/resourceState/context', + id: MenuId.SCMResourceContext, + description: localize('menus.resourceGroupContext', "The Source Control resource group context menu") + }, + { + key: 'scm/resourceFolder/context', + id: MenuId.SCMResourceFolderContext, + description: localize('menus.resourceStateContext', "The Source Control resource state context menu") + }, + { + key: 'scm/resourceGroup/context', + id: MenuId.SCMResourceGroupContext, + description: localize('menus.resourceFolderContext', "The Source Control resource folder context menu") + }, + { + key: 'scm/change/title', + id: MenuId.SCMChangeContext, + description: localize('menus.changeTitle', "The Source Control inline change menu") + }, + { + key: 'statusBar/windowIndicator', + id: MenuId.StatusBarWindowIndicatorMenu, + description: localize('menus.statusBarWindowIndicator', "The window indicator menu in the status bar"), + proposed: true, + supportsSubmenus: false + }, + { + key: 'view/title', + id: MenuId.ViewTitle, + description: localize('view.viewTitle', "The contributed view title menu") + }, + { + key: 'view/item/context', + id: MenuId.ViewItemContext, + description: localize('view.itemContext', "The contributed view item context menu") + }, + { + key: 'comments/commentThread/title', + id: MenuId.CommentThreadTitle, + description: localize('commentThread.title', "The contributed comment thread title menu") + }, + { + key: 'comments/commentThread/context', + id: MenuId.CommentThreadActions, + description: localize('commentThread.actions', "The contributed comment thread context menu, rendered as buttons below the comment editor"), + supportsSubmenus: false + }, + { + key: 'comments/comment/title', + id: MenuId.CommentTitle, + description: localize('comment.title', "The contributed comment title menu") + }, + { + key: 'comments/comment/context', + id: MenuId.CommentActions, + description: localize('comment.actions', "The contributed comment context menu, rendered as buttons below the comment editor"), + supportsSubmenus: false + }, + { + key: 'notebook/cell/title', + id: MenuId.NotebookCellTitle, + description: localize('notebook.cell.title', "The contributed notebook cell title menu"), + proposed: true + }, + { + key: 'extension/context', + id: MenuId.ExtensionContext, + description: localize('menus.extensionContext', "The extension context menu") + }, + { + key: 'timeline/title', + id: MenuId.TimelineTitle, + description: localize('view.timelineTitle', "The Timeline view title menu") + }, + { + key: 'timeline/item/context', + id: MenuId.TimelineItemContext, + description: localize('view.timelineContext', "The Timeline view item context menu") + }, +]; namespace schema { - // --- menus contribution point + // --- menus, submenus contribution point export interface IUserFriendlyMenuItem { command: string; @@ -26,79 +178,102 @@ namespace schema { group?: string; } - export function parseMenuId(value: string): MenuId | undefined { - switch (value) { - case 'commandPalette': return MenuId.CommandPalette; - case 'touchBar': return MenuId.TouchBarContext; - case 'editor/title': return MenuId.EditorTitle; - case 'editor/context': return MenuId.EditorContext; - case 'explorer/context': return MenuId.ExplorerContext; - case 'editor/title/context': return MenuId.EditorTitleContext; - case 'debug/callstack/context': return MenuId.DebugCallStackContext; - case 'debug/toolbar': return MenuId.DebugToolBar; - case 'debug/toolBar': return MenuId.DebugToolBar; - case 'menuBar/webNavigation': return MenuId.MenubarWebNavigationMenu; - case 'scm/title': return MenuId.SCMTitle; - case 'scm/sourceControl': return MenuId.SCMSourceControl; - case 'scm/resourceState/context': return MenuId.SCMResourceContext;// - case 'scm/resourceFolder/context': return MenuId.SCMResourceFolderContext; - case 'scm/resourceGroup/context': return MenuId.SCMResourceGroupContext; - case 'scm/change/title': return MenuId.SCMChangeContext;// - case 'statusBar/windowIndicator': return MenuId.StatusBarWindowIndicatorMenu; - case 'view/title': return MenuId.ViewTitle; - case 'view/item/context': return MenuId.ViewItemContext; - case 'comments/commentThread/title': return MenuId.CommentThreadTitle; - case 'comments/commentThread/context': return MenuId.CommentThreadActions; - case 'comments/comment/title': return MenuId.CommentTitle; - case 'comments/comment/context': return MenuId.CommentActions; - case 'notebook/cell/title': return MenuId.NotebookCellTitle; - case 'extension/context': return MenuId.ExtensionContext; - case 'timeline/title': return MenuId.TimelineTitle; - case 'timeline/item/context': return MenuId.TimelineItemContext; - } - - return undefined; + export interface IUserFriendlySubmenuItem { + submenu: string; + when?: string; + group?: string; } - export function isProposedAPI(menuId: MenuId): boolean { - switch (menuId) { - case MenuId.StatusBarWindowIndicatorMenu: - case MenuId.MenubarWebNavigationMenu: - return true; - } - return false; + export interface IUserFriendlySubmenu { + id: string; + label: string; + icon?: IUserFriendlyIcon; } - export function isValidMenuItems(menu: IUserFriendlyMenuItem[], collector: ExtensionMessageCollector): boolean { - if (!Array.isArray(menu)) { - collector.error(localize('requirearray', "menu items must be an array")); + export function isMenuItem(item: IUserFriendlyMenuItem | IUserFriendlySubmenuItem): item is IUserFriendlyMenuItem { + return typeof (item as IUserFriendlyMenuItem).command === 'string'; + } + + export function isValidMenuItem(item: IUserFriendlyMenuItem, collector: ExtensionMessageCollector): boolean { + if (typeof item.command !== 'string') { + collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'command')); + return false; + } + if (item.alt && typeof item.alt !== 'string') { + collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'alt')); + return false; + } + if (item.when && typeof item.when !== 'string') { + collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'when')); + return false; + } + if (item.group && typeof item.group !== 'string') { + collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'group')); return false; } - for (let item of menu) { - if (typeof item.command !== 'string') { - collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'command')); - return false; - } - if (item.alt && typeof item.alt !== 'string') { - collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'alt')); - return false; - } - if (item.when && typeof item.when !== 'string') { - collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'when')); - return false; - } - if (item.group && typeof item.group !== 'string') { - collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'group')); - return false; + return true; + } + + export function isValidSubmenuItem(item: IUserFriendlySubmenuItem, collector: ExtensionMessageCollector): boolean { + if (typeof item.submenu !== 'string') { + collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'submenu')); + return false; + } + if (item.when && typeof item.when !== 'string') { + collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'when')); + return false; + } + if (item.group && typeof item.group !== 'string') { + collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'group')); + return false; + } + + return true; + } + + export function isValidItems(items: (IUserFriendlyMenuItem | IUserFriendlySubmenuItem)[], collector: ExtensionMessageCollector): boolean { + if (!Array.isArray(items)) { + collector.error(localize('requirearray', "submenu items must be an array")); + return false; + } + + for (let item of items) { + if (isMenuItem(item)) { + if (!isValidMenuItem(item, collector)) { + return false; + } + } else { + if (!isValidSubmenuItem(item, collector)) { + return false; + } } } return true; } + export function isValidSubmenu(submenu: IUserFriendlySubmenu, collector: ExtensionMessageCollector): boolean { + if (typeof submenu !== 'object') { + collector.error(localize('require', "submenu items must be an object")); + return false; + } + + if (typeof submenu.id !== 'string') { + collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'id')); + return false; + } + if (typeof submenu.label !== 'string') { + collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'label')); + return false; + } + + return true; + } + const menuItem: IJSONSchema = { type: 'object', + required: ['command'], properties: { command: { description: localize('vscode.extension.contributes.menuItem.command', 'Identifier of the command to execute. The command must be declared in the \'commands\'-section'), @@ -119,138 +294,80 @@ namespace schema { } }; + const submenuItem: IJSONSchema = { + type: 'object', + required: ['submenu'], + properties: { + submenu: { + description: localize('vscode.extension.contributes.menuItem.submenu', 'Identifier of the submenu to display in this item.'), + type: 'string' + }, + when: { + description: localize('vscode.extension.contributes.menuItem.when', 'Condition which must be true to show this item'), + type: 'string' + }, + group: { + description: localize('vscode.extension.contributes.menuItem.group', 'Group into which this command belongs'), + type: 'string' + } + } + }; + + const submenu: IJSONSchema = { + type: 'object', + required: ['id', 'label'], + properties: { + id: { + description: localize('vscode.extension.contributes.submenu.id', 'Identifier of the menu to display as a submenu.'), + type: 'string' + }, + label: { + description: localize('vscode.extension.contributes.submenu.label', 'The label of the menu item which leads to this submenu.'), + type: 'string' + }, + icon: { + description: localize('vscode.extension.contributes.submenu.icon', '(Optional) Icon which is used to represent the submenu in the UI. Either a file path, an object with file paths for dark and light themes, or a theme icon references, like `\\$(zap)`'), + anyOf: [{ + type: 'string' + }, + { + type: 'object', + properties: { + light: { + description: localize('vscode.extension.contributes.submenu.icon.light', 'Icon path when a light theme is used'), + type: 'string' + }, + dark: { + description: localize('vscode.extension.contributes.submenu.icon.dark', 'Icon path when a dark theme is used'), + type: 'string' + } + } + }] + } + } + }; + export const menusContribution: IJSONSchema = { description: localize('vscode.extension.contributes.menus', "Contributes menu items to the editor"), type: 'object', - properties: { - 'commandPalette': { - description: localize('menus.commandPalette', "The Command Palette"), - type: 'array', - items: menuItem - }, - 'touchBar': { - description: localize('menus.touchBar', "The touch bar (macOS only)"), - type: 'array', - items: menuItem - }, - 'editor/title': { - description: localize('menus.editorTitle', "The editor title menu"), - type: 'array', - items: menuItem - }, - 'editor/context': { - description: localize('menus.editorContext', "The editor context menu"), - type: 'array', - items: menuItem - }, - 'explorer/context': { - description: localize('menus.explorerContext', "The file explorer context menu"), - type: 'array', - items: menuItem - }, - 'editor/title/context': { - description: localize('menus.editorTabContext', "The editor tabs context menu"), - type: 'array', - items: menuItem - }, - 'debug/callstack/context': { - description: localize('menus.debugCallstackContext', "The debug callstack context menu"), - type: 'array', - items: menuItem - }, - 'debug/toolBar': { - description: localize('menus.debugToolBar', "The debug toolbar menu"), - type: 'array', - items: menuItem - }, - 'menuBar/webNavigation': { - description: localize('menus.webNavigation', "The top level navigational menu (web only)"), - type: 'array', - items: menuItem - }, - 'scm/title': { - description: localize('menus.scmTitle', "The Source Control title menu"), - type: 'array', - items: menuItem - }, - 'scm/sourceControl': { - description: localize('menus.scmSourceControl', "The Source Control menu"), - type: 'array', - items: menuItem - }, - 'scm/resourceGroup/context': { - description: localize('menus.resourceGroupContext', "The Source Control resource group context menu"), - type: 'array', - items: menuItem - }, - 'scm/resourceState/context': { - description: localize('menus.resourceStateContext', "The Source Control resource state context menu"), - type: 'array', - items: menuItem - }, - 'scm/resourceFolder/context': { - description: localize('menus.resourceFolderContext', "The Source Control resource folder context menu"), - type: 'array', - items: menuItem - }, - 'scm/change/title': { - description: localize('menus.changeTitle', "The Source Control inline change menu"), - type: 'array', - items: menuItem - }, - 'view/title': { - description: localize('view.viewTitle', "The contributed view title menu"), - type: 'array', - items: menuItem - }, - 'view/item/context': { - description: localize('view.itemContext', "The contributed view item context menu"), - type: 'array', - items: menuItem - }, - 'comments/commentThread/title': { - description: localize('commentThread.title', "The contributed comment thread title menu"), - type: 'array', - items: menuItem - }, - 'comments/commentThread/context': { - description: localize('commentThread.actions', "The contributed comment thread context menu, rendered as buttons below the comment editor"), - type: 'array', - items: menuItem - }, - 'comments/comment/title': { - description: localize('comment.title', "The contributed comment title menu"), - type: 'array', - items: menuItem - }, - 'comments/comment/context': { - description: localize('comment.actions', "The contributed comment context menu, rendered as buttons below the comment editor"), - type: 'array', - items: menuItem - }, - 'notebook/cell/title': { - description: localize('notebook.cell.title', "The contributed notebook cell title menu"), - type: 'array', - items: menuItem - }, - 'extension/context': { - description: localize('menus.extensionContext', "The extension context menu"), - type: 'array', - items: menuItem - }, - 'timeline/title': { - description: localize('view.timelineTitle', "The Timeline view title menu"), - type: 'array', - items: menuItem - }, - 'timeline/item/context': { - description: localize('view.timelineContext', "The Timeline view item context menu"), - type: 'array', - items: menuItem - }, + properties: index(apiMenus, menu => menu.key, menu => ({ + description: menu.proposed ? `(${localize('proposed', "Proposed API")}) ${menu.description}` : menu.description, + type: 'array', + items: menu.supportsSubmenus === false ? menuItem : { oneOf: [menuItem, submenuItem] } + })), + additionalProperties: { + description: 'Submenu', + type: 'array', + items: { oneOf: [menuItem, submenuItem] } } }; + export const submenusContribution: IJSONSchema = { + description: localize('vscode.extension.contributes.submenus', "(Proposed API) Contributes submenu items to the editor"), + type: 'array', + items: submenu + }; + // --- commands contribution point export interface IUserFriendlyCommand { @@ -337,7 +454,7 @@ namespace schema { type: 'string' }, icon: { - description: localize('vscode.extension.contributes.commandType.icon', '(Optional) Icon which is used to represent the command in the UI. Either a file path, an object with file paths for dark and light themes, or a theme icon references, like `$(zap)`'), + description: localize('vscode.extension.contributes.commandType.icon', '(Optional) Icon which is used to represent the command in the UI. Either a file path, an object with file paths for dark and light themes, or a theme icon references, like `\\$(zap)`'), anyOf: [{ type: 'string' }, @@ -429,74 +546,175 @@ commandsExtensionPoint.setHandler(extensions => { _commandRegistrations.add(MenuRegistry.addCommands(newCommands)); }); -const _menuRegistrations = new DisposableStore(); +interface IRegisteredSubmenu { + readonly id: MenuId; + readonly label: string; + readonly icon?: { dark: URI; light?: URI; } | ThemeIcon; +} -ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyMenuItem[] }>({ - extensionPoint: 'menus', - jsonSchema: schema.menusContribution -}).setHandler(extensions => { +const _submenus = new Map(); - // remove all previous menu registrations - _menuRegistrations.clear(); +const submenusExtensionPoint = ExtensionsRegistry.registerExtensionPoint({ + extensionPoint: 'submenus', + jsonSchema: schema.submenusContribution +}); - const items: { id: MenuId, item: IMenuItem }[] = []; +submenusExtensionPoint.setHandler(extensions => { + + _submenus.clear(); for (let extension of extensions) { const { value, collector } = extension; forEach(value, entry => { - if (!schema.isValidMenuItems(entry.value, collector)) { + if (!schema.isValidSubmenu(entry.value, collector)) { return; } - const menu = schema.parseMenuId(entry.key); - if (typeof menu === 'undefined') { + if (!entry.value.id) { + collector.warn(localize('submenuId.invalid.id', "`{0}` is not a valid submenu identifier", entry.value.id)); + return; + } + if (!entry.value.label) { + collector.warn(localize('submenuId.invalid.label', "`{0}` is not a valid submenu label", entry.value.label)); + return; + } + + if (!extension.description.enableProposedApi) { + collector.error(localize('submenu.proposedAPI.invalid', "Submenus are proposed API and are only available when running out of dev or with the following command line switch: --enable-proposed-api {0}", extension.description.identifier.value)); + return; + } + + let absoluteIcon: { dark: URI; light?: URI; } | ThemeIcon | undefined; + if (entry.value.icon) { + if (typeof entry.value.icon === 'string') { + absoluteIcon = ThemeIcon.fromString(entry.value.icon) || { dark: resources.joinPath(extension.description.extensionLocation, entry.value.icon) }; + } else { + absoluteIcon = { + dark: resources.joinPath(extension.description.extensionLocation, entry.value.icon.dark), + light: resources.joinPath(extension.description.extensionLocation, entry.value.icon.light) + }; + } + } + + const item: IRegisteredSubmenu = { + id: new MenuId(`api:${entry.value.id}`), + label: entry.value.label, + icon: absoluteIcon + }; + + _submenus.set(entry.value.id, item); + }); + } +}); + +const _apiMenusByKey = new Map(Iterable.map(Iterable.from(apiMenus), menu => ([menu.key, menu]))); +const _menuRegistrations = new DisposableStore(); + +const menusExtensionPoint = ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: (schema.IUserFriendlyMenuItem | schema.IUserFriendlySubmenuItem)[] }>({ + extensionPoint: 'menus', + jsonSchema: schema.menusContribution, + deps: [submenusExtensionPoint] +}); + +menusExtensionPoint.setHandler(extensions => { + + // remove all previous menu registrations + _menuRegistrations.clear(); + + const items: { id: MenuId, item: IMenuItem | ISubmenuItem }[] = []; + + for (let extension of extensions) { + const { value, collector } = extension; + + forEach(value, entry => { + if (!schema.isValidItems(entry.value, collector)) { + return; + } + + let menu = _apiMenusByKey.get(entry.key); + let isSubmenu = false; + + if (!menu) { + const submenu = _submenus.get(entry.key); + + if (submenu) { + menu = { + key: entry.key, + id: submenu.id, + description: '' + }; + isSubmenu = true; + } + } + + if (!menu) { collector.warn(localize('menuId.invalid', "`{0}` is not a valid menu identifier", entry.key)); return; } - if (schema.isProposedAPI(menu) && !extension.description.enableProposedApi) { + if (menu.proposed && !extension.description.enableProposedApi) { collector.error(localize('proposedAPI.invalid', "{0} is a proposed menu identifier and is only available when running out of dev or with the following command line switch: --enable-proposed-api {1}", entry.key, extension.description.identifier.value)); return; } - for (let item of entry.value) { - let command = MenuRegistry.getCommand(item.command); - let alt = item.alt && MenuRegistry.getCommand(item.alt) || undefined; + if (isSubmenu && !extension.description.enableProposedApi) { + collector.error(localize('proposedAPI.invalid.submenu', "{0} is a submenu identifier and is only available when running out of dev or with the following command line switch: --enable-proposed-api {1}", entry.key, extension.description.identifier.value)); + return; + } - if (!command) { - collector.error(localize('missing.command', "Menu item references a command `{0}` which is not defined in the 'commands' section.", item.command)); - continue; - } - if (item.alt && !alt) { - collector.warn(localize('missing.altCommand', "Menu item references an alt-command `{0}` which is not defined in the 'commands' section.", item.alt)); - } - if (item.command === item.alt) { - collector.info(localize('dupe.command', "Menu item references the same command as default and alt-command")); + for (const menuItem of entry.value) { + let item: IMenuItem | ISubmenuItem; + + if (schema.isMenuItem(menuItem)) { + const command = MenuRegistry.getCommand(menuItem.command); + const alt = menuItem.alt && MenuRegistry.getCommand(menuItem.alt) || undefined; + + if (!command) { + collector.error(localize('missing.command', "Menu item references a command `{0}` which is not defined in the 'commands' section.", menuItem.command)); + continue; + } + if (menuItem.alt && !alt) { + collector.warn(localize('missing.altCommand', "Menu item references an alt-command `{0}` which is not defined in the 'commands' section.", menuItem.alt)); + } + if (menuItem.command === menuItem.alt) { + collector.info(localize('dupe.command', "Menu item references the same command as default and alt-command")); + } + + item = { command, alt, group: undefined, order: undefined, when: undefined }; + } else { + if (!extension.description.enableProposedApi) { + collector.error(localize('proposedAPI.invalid.submenureference', "Menu item references a submenu which is only available when running out of dev or with the following command line switch: --enable-proposed-api {0}", extension.description.identifier.value)); + continue; + } + + if (menu.supportsSubmenus === false) { + collector.error(localize('proposedAPI.unsupported.submenureference', "Menu item references a submenu for a menu which doesn't have submenu support.")); + continue; + } + + const submenu = _submenus.get(menuItem.submenu); + + if (!submenu) { + collector.error(localize('missing.submenu', "Menu item references a submenu `{0}` which is not defined in the 'submenus' section.", menuItem.submenu)); + continue; + } + + item = { submenu: submenu.id, icon: submenu.icon, title: submenu.label, group: undefined, order: undefined, when: undefined }; } - let group: string | undefined; - let order: number | undefined; - if (item.group) { - const idx = item.group.lastIndexOf('@'); + if (menuItem.group) { + const idx = menuItem.group.lastIndexOf('@'); if (idx > 0) { - group = item.group.substr(0, idx); - order = Number(item.group.substr(idx + 1)) || undefined; + item.group = menuItem.group.substr(0, idx); + item.order = Number(menuItem.group.substr(idx + 1)) || undefined; } else { - group = item.group; + item.group = menuItem.group; } } - items.push({ - id: menu, - item: { - command, - alt, - group, - order, - when: ContextKeyExpr.deserialize(item.when) - } - }); + item.when = ContextKeyExpr.deserialize(menuItem.when); + items.push({ id: menu.id, item }); } }); } diff --git a/src/vs/workbench/api/common/shared/workspaceContains.ts b/src/vs/workbench/api/common/shared/workspaceContains.ts new file mode 100644 index 00000000000..7f883983ea7 --- /dev/null +++ b/src/vs/workbench/api/common/shared/workspaceContains.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 * as path from 'vs/base/common/path'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; +import * as errors from 'vs/base/common/errors'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder'; +import { ISearchService } from 'vs/workbench/services/search/common/search'; +import { toWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; + +const WORKSPACE_CONTAINS_TIMEOUT = 7000; + +export interface IExtensionActivationHost { + readonly folders: readonly UriComponents[]; + readonly forceUsingSearch: boolean; + + exists(path: string): Promise; + checkExists(folders: readonly UriComponents[], includes: string[], token: CancellationToken): Promise; +} + +export interface IExtensionActivationResult { + activationEvent: string; +} + +export function checkActivateWorkspaceContainsExtension(host: IExtensionActivationHost, desc: IExtensionDescription): Promise { + const activationEvents = desc.activationEvents; + if (!activationEvents) { + return Promise.resolve(undefined); + } + + const fileNames: string[] = []; + const globPatterns: string[] = []; + + for (const activationEvent of activationEvents) { + if (/^workspaceContains:/.test(activationEvent)) { + const fileNameOrGlob = activationEvent.substr('workspaceContains:'.length); + if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0 || host.forceUsingSearch) { + globPatterns.push(fileNameOrGlob); + } else { + fileNames.push(fileNameOrGlob); + } + } + } + + if (fileNames.length === 0 && globPatterns.length === 0) { + return Promise.resolve(undefined); + } + + let resolveResult: (value: IExtensionActivationResult | undefined) => void; + const result = new Promise((resolve, reject) => { resolveResult = resolve; }); + const activate = (activationEvent: string) => resolveResult({ activationEvent }); + + const fileNamePromise = Promise.all(fileNames.map((fileName) => _activateIfFileName(host, fileName, activate))).then(() => { }); + const globPatternPromise = _activateIfGlobPatterns(host, desc.identifier, globPatterns, activate); + + Promise.all([fileNamePromise, globPatternPromise]).then(() => { + // when all are done, resolve with undefined (relevant only if it was not activated so far) + resolveResult(undefined); + }); + + return result; +} + +async function _activateIfFileName(host: IExtensionActivationHost, fileName: string, activate: (activationEvent: string) => void): Promise { + // find exact path + for (const uri of host.folders) { + if (await host.exists(path.join(URI.revive(uri).fsPath, fileName))) { + // the file was found + activate(`workspaceContains:${fileName}`); + return; + } + } +} + +async function _activateIfGlobPatterns(host: IExtensionActivationHost, extensionId: ExtensionIdentifier, globPatterns: string[], activate: (activationEvent: string) => void): Promise { + if (globPatterns.length === 0) { + return Promise.resolve(undefined); + } + + const tokenSource = new CancellationTokenSource(); + const searchP = host.checkExists(host.folders, globPatterns, tokenSource.token); + + const timer = setTimeout(async () => { + tokenSource.cancel(); + activate(`workspaceContainsTimeout:${globPatterns.join(',')}`); + }, WORKSPACE_CONTAINS_TIMEOUT); + + let exists: boolean = false; + try { + exists = await searchP; + } catch (err) { + if (!errors.isPromiseCanceledError(err)) { + errors.onUnexpectedError(err); + } + } + + tokenSource.dispose(); + clearTimeout(timer); + + if (exists) { + // a file was found matching one of the glob patterns + activate(`workspaceContains:${globPatterns.join(',')}`); + } +} + +export function checkGlobFileExists( + accessor: ServicesAccessor, + folders: readonly UriComponents[], + includes: string[], + token: CancellationToken, +): Promise { + const instantiationService = accessor.get(IInstantiationService); + const searchService = accessor.get(ISearchService); + const queryBuilder = instantiationService.createInstance(QueryBuilder); + const query = queryBuilder.file(folders.map(folder => toWorkspaceFolder(URI.revive(folder))), { + _reason: 'checkExists', + includePattern: includes.join(', '), + expandPatterns: true, + exists: true + }); + + return searchService.fileSearch(query, token).then( + result => { + return !!result.limitHit; + }, + err => { + if (!errors.isPromiseCanceledError(err)) { + return Promise.reject(err); + } + + return false; + }); +} diff --git a/src/vs/workbench/api/node/extHost.services.ts b/src/vs/workbench/api/node/extHost.node.services.ts similarity index 60% rename from src/vs/workbench/api/node/extHost.services.ts rename to src/vs/workbench/api/node/extHost.node.services.ts index 72ad75d63ef..b3c89e51cfc 100644 --- a/src/vs/workbench/api/node/extHost.services.ts +++ b/src/vs/workbench/api/node/extHost.node.services.ts @@ -4,46 +4,35 @@ *--------------------------------------------------------------------------------------------*/ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IExtHostOutputService } from 'vs/workbench/api/common/extHostOutput'; import { ExtHostOutputService2 } from 'vs/workbench/api/node/extHostOutputService'; -import { IExtHostWorkspace, ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; -import { IExtHostDecorations, ExtHostDecorations } from 'vs/workbench/api/common/extHostDecorations'; -import { IExtHostConfiguration, ExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; -import { IExtHostCommands, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; -import { IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService'; -import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService'; -import { IExtHostTask } from 'vs/workbench/api/common/extHostTask'; import { ExtHostTask } from 'vs/workbench/api/node/extHostTask'; import { ExtHostDebugService } from 'vs/workbench/api/node/extHostDebugService'; -import { IExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService'; -import { IExtHostSearch } from 'vs/workbench/api/common/extHostSearch'; import { NativeExtHostSearch } from 'vs/workbench/api/node/extHostSearch'; -import { ExtensionStoragePaths } from 'vs/workbench/api/node/extHostStoragePaths'; -import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; -import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; -import { IExtHostStorage, ExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; -import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; -import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; import { ExtHostTunnelService } from 'vs/workbench/api/node/extHostTunnelService'; -import { IExtHostApiDeprecationService, ExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; +import { IExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService'; +import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; +import { IExtHostOutputService } from 'vs/workbench/api/common/extHostOutput'; +import { IExtHostSearch } from 'vs/workbench/api/common/extHostSearch'; +import { IExtHostTask } from 'vs/workbench/api/common/extHostTask'; +import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService'; +import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; +import { ILogService } from 'vs/platform/log/common/log'; + +// ######################################################################### +// ### ### +// ### !!! PLEASE ADD COMMON IMPORTS INTO extHost.common.services.ts !!! ### +// ### ### +// ######################################################################### -// register singleton services -registerSingleton(ILogService, ExtHostLogService); -registerSingleton(IExtHostApiDeprecationService, ExtHostApiDeprecationService); -registerSingleton(IExtHostOutputService, ExtHostOutputService2); -registerSingleton(IExtHostWorkspace, ExtHostWorkspace); -registerSingleton(IExtHostDecorations, ExtHostDecorations); -registerSingleton(IExtHostConfiguration, ExtHostConfiguration); -registerSingleton(IExtHostCommands, ExtHostCommands); -registerSingleton(IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors); -registerSingleton(IExtHostTerminalService, ExtHostTerminalService); -registerSingleton(IExtHostTask, ExtHostTask); -registerSingleton(IExtHostDebugService, ExtHostDebugService); -registerSingleton(IExtHostSearch, NativeExtHostSearch); -registerSingleton(IExtensionStoragePaths, ExtensionStoragePaths); registerSingleton(IExtHostExtensionService, ExtHostExtensionService); -registerSingleton(IExtHostStorage, ExtHostStorage); +registerSingleton(ILogService, ExtHostLogService); + +registerSingleton(IExtHostDebugService, ExtHostDebugService); +registerSingleton(IExtHostOutputService, ExtHostOutputService2); +registerSingleton(IExtHostSearch, NativeExtHostSearch); +registerSingleton(IExtHostTask, ExtHostTask); +registerSingleton(IExtHostTerminalService, ExtHostTerminalService); registerSingleton(IExtHostTunnelService, ExtHostTunnelService); diff --git a/src/vs/workbench/api/node/extHostCLIServer.ts b/src/vs/workbench/api/node/extHostCLIServer.ts index ad28cc495c0..7cae126cc0f 100644 --- a/src/vs/workbench/api/node/extHostCLIServer.ts +++ b/src/vs/workbench/api/node/extHostCLIServer.ts @@ -7,10 +7,9 @@ import { generateRandomPipeName } from 'vs/base/parts/ipc/node/ipc.net'; import * as http from 'http'; import * as fs from 'fs'; import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; -import { IWindowOpenable } from 'vs/platform/windows/common/windows'; +import { IWindowOpenable, IOpenWindowOptions } from 'vs/platform/windows/common/windows'; import { URI } from 'vs/base/common/uri'; import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; -import { INativeOpenWindowOptions } from 'vs/platform/windows/node/window'; import { ILogService } from 'vs/platform/log/common/log'; export interface OpenCommandPipeArgs { @@ -123,7 +122,7 @@ export class CLIServer { if (urisToOpen.length) { const waitMarkerFileURI = waitMarkerFilePath ? URI.file(waitMarkerFilePath) : undefined; const preferNewWindow = !forceReuseWindow && !waitMarkerFileURI && !addMode; - const windowOpenArgs: INativeOpenWindowOptions = { forceNewWindow, diffMode, addMode, gotoLineMode, forceReuseWindow, preferNewWindow, waitMarkerFileURI }; + const windowOpenArgs: IOpenWindowOptions = { forceNewWindow, diffMode, addMode, gotoLineMode, forceReuseWindow, preferNewWindow, waitMarkerFileURI }; this._commands.executeCommand('_files.windowOpen', urisToOpen, windowOpenArgs); } res.writeHead(200); diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 839add08b62..7d3cc3b33c3 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -88,6 +88,7 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { const configProvider = await this._configurationService.getConfigProvider(); const shell = this._terminalService.getDefaultShell(true, configProvider); + let cwdForPrepareCommand: string | undefined; if (needNewTerminal || !this._integratedTerminalInstance) { @@ -97,9 +98,9 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { cwd: args.cwd, name: args.title || nls.localize('debug.terminal.title', "debuggee"), }; - // @ts-ignore - delete args.cwd; this._integratedTerminalInstance = this._terminalService.createTerminalFromOptions(options); + } else { + cwdForPrepareCommand = args.cwd; } const terminal = this._integratedTerminalInstance; @@ -107,7 +108,7 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { terminal.show(); const shellProcessId = await this._integratedTerminalInstance.processId; - const command = prepareCommand(args, shell); + const command = prepareCommand(shell, args.args, cwdForPrepareCommand, args.env); terminal.sendText(command, true); return shellProcessId; @@ -122,5 +123,4 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService { return new ExtHostVariableResolverService(folders, editorService, configurationService, process.env as env.IProcessEnvironment); } - } diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 3a02c5ce0b7..8558835c744 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -13,6 +13,7 @@ import { ExtHostDownloadService } from 'vs/workbench/api/node/extHostDownloadSer import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; class NodeModuleRequireInterceptor extends RequireInterceptor { @@ -76,6 +77,10 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { }; } + protected _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined { + return extensionDescription.main; + } + protected _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { if (module.scheme !== Schemas.file) { throw new Error(`Cannot load URI: '${module}', must be of file-scheme`); diff --git a/src/vs/workbench/api/node/extHostStoragePaths.ts b/src/vs/workbench/api/node/extHostStoragePaths.ts deleted file mode 100644 index afdd6bf3984..00000000000 --- a/src/vs/workbench/api/node/extHostStoragePaths.ts +++ /dev/null @@ -1,80 +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 { URI } from 'vs/base/common/uri'; -import * as pfs from 'vs/base/node/pfs'; -import { IEnvironment, IStaticWorkspaceData } from 'vs/workbench/api/common/extHost.protocol'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; -import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; -import { withNullAsUndefined } from 'vs/base/common/types'; -import { ILogService } from 'vs/platform/log/common/log'; - -export class ExtensionStoragePaths implements IExtensionStoragePaths { - - readonly _serviceBrand: undefined; - - private readonly _workspace?: IStaticWorkspaceData; - private readonly _environment: IEnvironment; - - readonly whenReady: Promise; - private _value?: string; - - constructor( - @IExtHostInitDataService initData: IExtHostInitDataService, - @ILogService private readonly _logService: ILogService, - ) { - this._workspace = withNullAsUndefined(initData.workspace); - this._environment = initData.environment; - this.whenReady = this._getOrCreateWorkspaceStoragePath().then(value => this._value = value); - } - - workspaceValue(extension: IExtensionDescription): string | undefined { - if (this._value) { - return path.join(this._value, extension.identifier.value); - } - return undefined; - } - - globalValue(extension: IExtensionDescription): string { - return path.join(this._environment.globalStorageHome.fsPath, extension.identifier.value.toLowerCase()); - } - - private async _getOrCreateWorkspaceStoragePath(): Promise { - if (!this._workspace) { - return Promise.resolve(undefined); - } - - if (!this._environment.appSettingsHome) { - return undefined; - } - const storageName = this._workspace.id; - const storagePath = path.join(this._environment.appSettingsHome.fsPath, 'workspaceStorage', storageName); - - const exists = await pfs.dirExists(storagePath); - - if (exists) { - return storagePath; - } - - try { - await pfs.mkdirp(storagePath); - await pfs.writeFile( - path.join(storagePath, 'meta.json'), - JSON.stringify({ - id: this._workspace.id, - configuration: this._workspace.configuration && URI.revive(this._workspace.configuration).toString(), - name: this._workspace.name - }, undefined, 2) - ); - return storagePath; - - } catch (e) { - this._logService.error(e); - return undefined; - } - } -} diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index 5ec632cc283..0c59bbd7a6f 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -47,13 +47,22 @@ export class ExtHostTask extends ExtHostTaskBase { platform: process.platform }); } + this._proxy.$registerSupportedExecutions(true, true, true); } public async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise { const tTask = (task as types.Task); // We have a preserved ID. So the task didn't change. if (tTask._id !== undefined) { - return this._proxy.$executeTask(TaskHandleDTO.from(tTask)).then(value => this.getTaskExecution(value, task)); + // Always get the task execution first to prevent timing issues when retrieving it later + const handleDto = TaskHandleDTO.from(tTask); + const executionDTO = await this._proxy.$getTaskExecution(handleDto); + if (executionDTO.task === undefined) { + throw new Error('Task from execution DTO is undefined'); + } + const execution = await this.getTaskExecution(executionDTO, task); + this._proxy.$executeTask(handleDto).catch(() => { /* The error here isn't actionable. */ }); + return execution; } else { const dto = TaskDTO.from(task, extension); if (dto === undefined) { @@ -66,8 +75,10 @@ export class ExtHostTask extends ExtHostTaskBase { if (CustomExecutionDTO.is(dto.execution)) { await this.addCustomExecution(dto, task, false); } - - return this._proxy.$executeTask(dto).then(value => this.getTaskExecution(value, task)); + // Always get the task execution first to prevent timing issues when retrieving it later + const execution = await this.getTaskExecution(await this._proxy.$getTaskExecution(dto), task); + this._proxy.$executeTask(dto).catch(() => { /* The error here isn't actionable. */ }); + return execution; } } @@ -185,4 +196,8 @@ export class ExtHostTask extends ExtHostTaskBase { public async $jsonTasksSupported(): Promise { return true; } + + public async $findExecutable(command: string, cwd?: string, paths?: string[]): Promise { + return win32.findExecutable(command, cwd, paths); + } } diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index ec6a950f155..c777688550a 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -12,7 +12,7 @@ import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/termi import { IShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostConfiguration, ExtHostConfigProvider, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; import { ILogService } from 'vs/platform/log/common/log'; -import { IShellLaunchConfig, ITerminalEnvironment } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalEnvironment, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess'; import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; @@ -20,11 +20,8 @@ import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostD import { ExtHostDocumentsAndEditors, IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { getSystemShell, detectAvailableShells } from 'vs/workbench/contrib/terminal/node/terminal'; import { getMainProcessParentEnv } from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; -import { BaseExtHostTerminalService, ExtHostTerminal, EnvironmentVariableCollection } from 'vs/workbench/api/common/extHostTerminalService'; +import { BaseExtHostTerminalService, ExtHostTerminal } from 'vs/workbench/api/common/extHostTerminalService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared'; -import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { MergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableCollection'; export class ExtHostTerminalService extends BaseExtHostTerminalService { @@ -32,8 +29,6 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { private _variableResolver: ExtHostVariableResolverService | undefined; private _lastActiveWorkspace: IWorkspaceFolder | undefined; - private _environmentVariableCollections: Map = new Map(); - // TODO: Pull this from main side private _isWorkspaceShellAllowed: boolean = false; @@ -129,7 +124,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._extHostDocumentsAndEditors, configProvider, process.env as platform.IProcessEnvironment); } - public async $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise { + public async $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise { const shellLaunchConfig: IShellLaunchConfig = { name: shellLaunchConfigDto.name, executable: shellLaunchConfigDto.executable, @@ -205,11 +200,19 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { this._proxy.$sendResolvedLaunchConfig(id, shellLaunchConfig); // Fork the process and listen for messages - this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, env); + this._logService.debug(`Terminal process launching on ext host`, { shellLaunchConfig, initialCwd, cols, rows, env }); // TODO: Support conpty on remote, it doesn't seem to work for some reason? // TODO: When conpty is enabled, only enable it when accessibilityMode is off const enableConpty = false; //terminalConfig.get('windowsEnableConpty') as boolean; - this._setupExtHostProcessListeners(id, new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, enableConpty, this._logService)); + + const terminalProcess = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, enableConpty, this._logService); + this._setupExtHostProcessListeners(id, terminalProcess); + const error = await terminalProcess.start(); + if (error) { + // TODO: Teardown? + return error; + } + return undefined; } public $getAvailableShells(): Promise { @@ -227,37 +230,4 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { public $acceptWorkspacePermissionsChanged(isAllowed: boolean): void { this._isWorkspaceShellAllowed = isAllowed; } - - public getEnvironmentVariableCollection(extension: IExtensionDescription): vscode.EnvironmentVariableCollection { - let collection = this._environmentVariableCollections.get(extension.identifier.value); - if (!collection) { - collection = new EnvironmentVariableCollection(); - this._setEnvironmentVariableCollection(extension.identifier.value, collection); - } - return collection; - } - - private _syncEnvironmentVariableCollection(extensionIdentifier: string, collection: EnvironmentVariableCollection): void { - const serialized = serializeEnvironmentVariableCollection(collection.map); - this._proxy.$setEnvironmentVariableCollection(extensionIdentifier, collection.persistent, serialized.length === 0 ? undefined : serialized); - } - - public $initEnvironmentVariableCollections(collections: [string, ISerializableEnvironmentVariableCollection][]): void { - collections.forEach(entry => { - const extensionIdentifier = entry[0]; - const collection = new EnvironmentVariableCollection(entry[1]); - this._setEnvironmentVariableCollection(extensionIdentifier, collection); - }); - } - - private _setEnvironmentVariableCollection(extensionIdentifier: string, collection: EnvironmentVariableCollection): void { - this._environmentVariableCollections.set(extensionIdentifier, collection); - collection.onDidChangeCollection(() => { - // When any collection value changes send this immediately, this is done to ensure - // following calls to createTerminal will be created with the new environment. It will - // result in more noise by sending multiple updates when called but collections are - // expected to be small. - this._syncEnvironmentVariableCollection(extensionIdentifier, collection!); - }); - } } diff --git a/src/vs/workbench/api/node/extHostTunnelService.ts b/src/vs/workbench/api/node/extHostTunnelService.ts index eaa5aa0743b..ffd3bd9829c 100644 --- a/src/vs/workbench/api/node/extHostTunnelService.ts +++ b/src/vs/workbench/api/node/extHostTunnelService.ts @@ -181,7 +181,7 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe connections.filter((connection => socketMap[connection.socket])).forEach(({ socket, ip, port }) => { const command = processMap[socketMap[socket].pid].cmd; - if (!command.match('.*\.vscode\-server\-[a-zA-Z]+\/bin.*') && (command.indexOf('out/vs/server/main.js') === -1)) { + if (!command.match(/.*\.vscode-server-[a-zA-Z]+\/bin.*/) && (command.indexOf('out/vs/server/main.js') === -1)) { ports.push({ host: ip, port, detail: processMap[socketMap[socket].pid].cmd }); } }); diff --git a/src/vs/workbench/api/worker/extHost.worker.services.ts b/src/vs/workbench/api/worker/extHost.worker.services.ts new file mode 100644 index 00000000000..3843fdec386 --- /dev/null +++ b/src/vs/workbench/api/worker/extHost.worker.services.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; +import { ExtHostExtensionService } from 'vs/workbench/api/worker/extHostExtensionService'; +import { ExtHostLogService } from 'vs/workbench/api/worker/extHostLogService'; + +// ######################################################################### +// ### ### +// ### !!! PLEASE ADD COMMON IMPORTS INTO extHost.common.services.ts !!! ### +// ### ### +// ######################################################################### + +registerSingleton(IExtHostExtensionService, ExtHostExtensionService); +registerSingleton(ILogService, ExtHostLogService); diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index 10a21537a48..c71ab1c7da4 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -8,6 +8,7 @@ import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHost import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; import { URI } from 'vs/base/common/uri'; import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; class WorkerRequireInterceptor extends RequireInterceptor { @@ -40,6 +41,10 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { await this._fakeModules.install(); } + protected _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined { + return extensionDescription.browser; + } + protected async _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { module = module.with({ path: ensureSuffix(module.path, '.js') }); @@ -50,7 +55,11 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { } // fetch JS sources as text and create a new function around it - const initFn = new Function('module', 'exports', 'require', 'window', await response.text()); + const source = await response.text(); + // Here we append #vscode-extension to serve as a marker, such that source maps + // can be adjusted for the extra wrapping function. + const sourceURL = `${module.toString(true)}#vscode-extension`; + const initFn = new Function('module', 'exports', 'require', `${source}\n//# sourceURL=${sourceURL}`); // define commonjs globals: `module`, `exports`, and `require` const _exports = {}; @@ -65,7 +74,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { try { activationTimesBuilder.codeLoadingStart(); - initFn(_module, _exports, _require, self); + initFn(_module, _exports, _require); return (_module.exports !== _exports ? _module.exports : _exports); } finally { activationTimesBuilder.codeLoadingStop(); diff --git a/src/vs/workbench/api/worker/extHostLogService.ts b/src/vs/workbench/api/worker/extHostLogService.ts index 2c0cb0592a4..51c671d6b88 100644 --- a/src/vs/workbench/api/worker/extHostLogService.ts +++ b/src/vs/workbench/api/worker/extHostLogService.ts @@ -11,7 +11,7 @@ import { UriComponents } from 'vs/base/common/uri'; export class ExtHostLogService extends AbstractLogService implements ILogService, ExtHostLogServiceShape { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _proxy: MainThreadLogShape; private readonly _logFile: UriComponents; diff --git a/src/vs/workbench/browser/actions/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts index 616ebbdcb34..60c53ae74b4 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -5,7 +5,6 @@ import 'vs/css!./media/actions'; -import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { domEvent } from 'vs/base/browser/event'; @@ -19,29 +18,31 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { timeout } from 'vs/base/common/async'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; +import { registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { clamp } from 'vs/base/common/numbers'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { ILogService } from 'vs/platform/log/common/log'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -class InspectContextKeysAction extends Action { +const developerCategory = { value: nls.localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer"), original: 'Developer' }; - static readonly ID = 'workbench.action.inspectContextKeys'; - static readonly LABEL = nls.localize('inspect context keys', "Inspect Context Keys"); +class InspectContextKeysAction extends Action2 { - constructor( - id: string, - label: string, - @IContextKeyService private readonly contextKeyService: IContextKeyService - ) { - super(id, label); + constructor() { + super({ + id: 'workbench.action.inspectContextKeys', + title: { value: nls.localize('inspect context keys', "Inspect Context Keys"), original: 'Inspect Context Keys' }, + category: developerCategory, + f1: true + }); } - async run(): Promise { + run(accessor: ServicesAccessor): void { + const contextKeyService = accessor.get(IContextKeyService); + const disposables = new DisposableStore(); const stylesheet = createStyleSheet(); @@ -80,7 +81,7 @@ class InspectContextKeysAction extends Action { e.preventDefault(); e.stopPropagation(); - const context = this.contextKeyService.getContext(e.target as HTMLElement) as Context; + const context = contextKeyService.getContext(e.target as HTMLElement) as Context; console.log(context.collectAllValues()); dispose(disposables); @@ -88,33 +89,33 @@ class InspectContextKeysAction extends Action { } } -class ToggleScreencastModeAction extends Action { - - static readonly ID = 'workbench.action.toggleScreencastMode'; - static readonly LABEL = nls.localize('toggle screencast mode', "Toggle Screencast Mode"); +class ToggleScreencastModeAction extends Action2 { static disposable: IDisposable | undefined; - constructor( - id: string, - label: string, - @IKeybindingService private readonly keybindingService: IKeybindingService, - @ILayoutService private readonly layoutService: ILayoutService, - @IConfigurationService private readonly configurationService: IConfigurationService - ) { - super(id, label); + constructor() { + super({ + id: 'workbench.action.toggleScreencastMode', + title: { value: nls.localize('toggle screencast mode', "Toggle Screencast Mode"), original: 'Toggle Screencast Mode' }, + category: developerCategory, + f1: true + }); } - async run(): Promise { + run(accessor: ServicesAccessor): void { if (ToggleScreencastModeAction.disposable) { ToggleScreencastModeAction.disposable.dispose(); ToggleScreencastModeAction.disposable = undefined; return; } + const layoutService = accessor.get(ILayoutService); + const configurationService = accessor.get(IConfigurationService); + const keybindingService = accessor.get(IKeybindingService); + const disposables = new DisposableStore(); - const container = this.layoutService.container; + const container = layoutService.container; const mouseMarker = append(container, $('.screencast-mouse')); disposables.add(toDisposable(() => mouseMarker.remove())); @@ -141,15 +142,25 @@ class ToggleScreencastModeAction extends Action { const keyboardMarker = append(container, $('.screencast-keyboard')); disposables.add(toDisposable(() => keyboardMarker.remove())); - const updateKeyboardMarker = () => { - keyboardMarker.style.bottom = `${clamp(this.configurationService.getValue('screencastMode.verticalOffset') || 0, 0, 90)}%`; + const updateKeyboardFontSize = () => { + keyboardMarker.style.fontSize = `${clamp(configurationService.getValue('screencastMode.fontSize') || 56, 20, 100)}px`; }; + const updateKeyboardMarker = () => { + keyboardMarker.style.bottom = `${clamp(configurationService.getValue('screencastMode.verticalOffset') || 0, 0, 90)}%`; + }; + + updateKeyboardFontSize(); updateKeyboardMarker(); - disposables.add(this.configurationService.onDidChangeConfiguration(e => { + + disposables.add(configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('screencastMode.verticalOffset')) { updateKeyboardMarker(); } + + if (e.affectsConfiguration('screencastMode.fontSize')) { + updateKeyboardFontSize(); + } })); const onKeyDown = domEvent(window, 'keydown', true); @@ -160,9 +171,9 @@ class ToggleScreencastModeAction extends Action { keyboardTimeout.dispose(); const event = new StandardKeyboardEvent(e); - const shortcut = this.keybindingService.softDispatch(event, event.target); + const shortcut = keybindingService.softDispatch(event, event.target); - if (shortcut || !this.configurationService.getValue('screencastMode.onlyKeyboardShortcuts')) { + if (shortcut || !configurationService.getValue('screencastMode.onlyKeyboardShortcuts')) { if ( event.ctrlKey || event.altKey || event.metaKey || event.shiftKey || length > 20 @@ -172,7 +183,7 @@ class ToggleScreencastModeAction extends Action { length = 0; } - const keybinding = this.keybindingService.resolveKeyboardEvent(event); + const keybinding = keybindingService.resolveKeyboardEvent(event); const label = keybinding.getLabel(); const key = $('span.key', {}, label || ''); length++; @@ -192,59 +203,54 @@ class ToggleScreencastModeAction extends Action { } } -class LogStorageAction extends Action { +class LogStorageAction extends Action2 { - static readonly ID = 'workbench.action.logStorage'; - static readonly LABEL = nls.localize({ key: 'logStorage', comment: ['A developer only action to log the contents of the storage for the current window.'] }, "Log Storage Database Contents"); - - constructor( - id: string, - label: string, - @IStorageService private readonly storageService: IStorageService - ) { - super(id, label); + constructor() { + super({ + id: 'workbench.action.logStorage', + title: { value: nls.localize({ key: 'logStorage', comment: ['A developer only action to log the contents of the storage for the current window.'] }, "Log Storage Database Contents"), original: 'Log Storage Database Contents' }, + category: developerCategory, + f1: true + }); } - async run(): Promise { - this.storageService.logStorage(); + run(accessor: ServicesAccessor): void { + accessor.get(IStorageService).logStorage(); } } -class LogWorkingCopiesAction extends Action { +class LogWorkingCopiesAction extends Action2 { - static readonly ID = 'workbench.action.logWorkingCopies'; - static readonly LABEL = nls.localize({ key: 'logWorkingCopies', comment: ['A developer only action to log the working copies that exist.'] }, "Log Working Copies"); - - constructor( - id: string, - label: string, - @ILogService private logService: ILogService, - @IWorkingCopyService private workingCopyService: IWorkingCopyService - ) { - super(id, label); + constructor() { + super({ + id: 'workbench.action.logWorkingCopies', + title: { value: nls.localize({ key: 'logWorkingCopies', comment: ['A developer only action to log the working copies that exist.'] }, "Log Working Copies"), original: 'Log Working Copies' }, + category: developerCategory, + f1: true + }); } - async run(): Promise { + run(accessor: ServicesAccessor): void { + const workingCopyService = accessor.get(IWorkingCopyService); + const logService = accessor.get(ILogService); const msg = [ `Dirty Working Copies:`, - ...this.workingCopyService.dirtyWorkingCopies.map(workingCopy => workingCopy.resource.toString(true)), + ...workingCopyService.dirtyWorkingCopies.map(workingCopy => workingCopy.resource.toString(true)), ``, `All Working Copies:`, - ...this.workingCopyService.workingCopies.map(workingCopy => workingCopy.resource.toString(true)), + ...workingCopyService.workingCopies.map(workingCopy => workingCopy.resource.toString(true)), ]; - this.logService.info(msg.join('\n')); + logService.info(msg.join('\n')); } } // --- Actions Registration +registerAction2(InspectContextKeysAction); +registerAction2(ToggleScreencastModeAction); +registerAction2(LogStorageAction); +registerAction2(LogWorkingCopiesAction); -const developerCategory = nls.localize('developer', "Developer"); -const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(SyncActionDescriptor.from(InspectContextKeysAction), 'Developer: Inspect Context Keys', developerCategory); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleScreencastModeAction), 'Developer: Toggle Screencast Mode', developerCategory); -registry.registerWorkbenchAction(SyncActionDescriptor.from(LogStorageAction), 'Developer: Log Storage Database Contents', developerCategory); -registry.registerWorkbenchAction(SyncActionDescriptor.from(LogWorkingCopiesAction), 'Developer: Log Working Copies', developerCategory); // Screencast Mode const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); @@ -261,6 +267,13 @@ configurationRegistry.registerConfiguration({ maximum: 90, description: nls.localize('screencastMode.location.verticalPosition', "Controls the vertical offset of the screencast mode overlay from the bottom as a percentage of the workbench height.") }, + 'screencastMode.fontSize': { + type: 'number', + default: 56, + minimum: 20, + maximum: 100, + description: nls.localize('screencastMode.fontSize', "Controls the font size (in pixels) of the screencast mode keyboard.") + }, 'screencastMode.onlyKeyboardShortcuts': { type: 'boolean', description: nls.localize('screencastMode.onlyKeyboardShortcuts', "Only show keyboard shortcuts in Screencast Mode."), diff --git a/src/vs/workbench/browser/actions/helpActions.ts b/src/vs/workbench/browser/actions/helpActions.ts index 41b041f019a..fe0acdc9c61 100644 --- a/src/vs/workbench/browser/actions/helpActions.ts +++ b/src/vs/workbench/browser/actions/helpActions.ts @@ -3,224 +3,248 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; import product from 'vs/platform/product/common/product'; import { isMacintosh, isLinux, language } from 'vs/base/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { URI } from 'vs/base/common/uri'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { MenuId, Action2, registerAction2, MenuRegistry } from 'vs/platform/actions/common/actions'; import { KeyChord, KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { IProductService } from 'vs/platform/product/common/productService'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -class KeybindingsReferenceAction extends Action { +const helpCategory = { value: nls.localize('help', "Help"), original: 'Help' }; + +class KeybindingsReferenceAction extends Action2 { static readonly ID = 'workbench.action.keybindingsReference'; - static readonly LABEL = nls.localize('keybindingsReference', "Keyboard Shortcuts Reference"); static readonly AVAILABLE = !!(isLinux ? product.keyboardShortcutsUrlLinux : isMacintosh ? product.keyboardShortcutsUrlMac : product.keyboardShortcutsUrlWin); - constructor( - id: string, - label: string, - @IOpenerService private readonly openerService: IOpenerService, - @IProductService private readonly productService: IProductService - ) { - super(id, label); + constructor() { + super({ + id: KeybindingsReferenceAction.ID, + title: { value: nls.localize('keybindingsReference', "Keyboard Shortcuts Reference"), original: 'Keyboard Shortcuts Reference' }, + category: helpCategory, + f1: true, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + when: null, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_R) + } + }); } - async run(): Promise { - const url = isLinux ? this.productService.keyboardShortcutsUrlLinux : isMacintosh ? this.productService.keyboardShortcutsUrlMac : this.productService.keyboardShortcutsUrlWin; + run(accessor: ServicesAccessor): void { + const productService = accessor.get(IProductService); + const openerService = accessor.get(IOpenerService); + + const url = isLinux ? productService.keyboardShortcutsUrlLinux : isMacintosh ? productService.keyboardShortcutsUrlMac : productService.keyboardShortcutsUrlWin; if (url) { - this.openerService.open(URI.parse(url)); + openerService.open(URI.parse(url)); } } } -class OpenDocumentationUrlAction extends Action { +class OpenDocumentationUrlAction extends Action2 { static readonly ID = 'workbench.action.openDocumentationUrl'; - static readonly LABEL = nls.localize('openDocumentationUrl', "Documentation"); static readonly AVAILABLE = !!product.documentationUrl; - constructor( - id: string, - label: string, - @IOpenerService private readonly openerService: IOpenerService, - @IProductService private readonly productService: IProductService - ) { - super(id, label); + constructor() { + super({ + id: OpenDocumentationUrlAction.ID, + title: { value: nls.localize('openDocumentationUrl', "Documentation"), original: 'Documentation' }, + category: helpCategory, + f1: true + }); } - async run(): Promise { - if (this.productService.documentationUrl) { - this.openerService.open(URI.parse(this.productService.documentationUrl)); + run(accessor: ServicesAccessor): void { + const productService = accessor.get(IProductService); + const openerService = accessor.get(IOpenerService); + + if (productService.documentationUrl) { + openerService.open(URI.parse(productService.documentationUrl)); } } } -class OpenIntroductoryVideosUrlAction extends Action { +class OpenIntroductoryVideosUrlAction extends Action2 { static readonly ID = 'workbench.action.openIntroductoryVideosUrl'; - static readonly LABEL = nls.localize('openIntroductoryVideosUrl', "Introductory Videos"); static readonly AVAILABLE = !!product.introductoryVideosUrl; - constructor( - id: string, - label: string, - @IOpenerService private readonly openerService: IOpenerService, - @IProductService private readonly productService: IProductService - ) { - super(id, label); + constructor() { + super({ + id: OpenIntroductoryVideosUrlAction.ID, + title: { value: nls.localize('openIntroductoryVideosUrl', "Introductory Videos"), original: 'Introductory Videos' }, + category: helpCategory, + f1: true + }); } - async run(): Promise { - if (this.productService.introductoryVideosUrl) { - this.openerService.open(URI.parse(this.productService.introductoryVideosUrl)); + run(accessor: ServicesAccessor): void { + const productService = accessor.get(IProductService); + const openerService = accessor.get(IOpenerService); + + if (productService.introductoryVideosUrl) { + openerService.open(URI.parse(productService.introductoryVideosUrl)); } } } -class OpenTipsAndTricksUrlAction extends Action { +class OpenTipsAndTricksUrlAction extends Action2 { static readonly ID = 'workbench.action.openTipsAndTricksUrl'; - static readonly LABEL = nls.localize('openTipsAndTricksUrl', "Tips and Tricks"); static readonly AVAILABLE = !!product.tipsAndTricksUrl; - constructor( - id: string, - label: string, - @IOpenerService private readonly openerService: IOpenerService, - @IProductService private readonly productService: IProductService - ) { - super(id, label); + constructor() { + super({ + id: OpenTipsAndTricksUrlAction.ID, + title: { value: nls.localize('openTipsAndTricksUrl', "Tips and Tricks"), original: 'Tips and Tricks' }, + category: helpCategory, + f1: true + }); } - async run(): Promise { - if (this.productService.tipsAndTricksUrl) { - this.openerService.open(URI.parse(this.productService.tipsAndTricksUrl)); + run(accessor: ServicesAccessor): void { + const productService = accessor.get(IProductService); + const openerService = accessor.get(IOpenerService); + + if (productService.tipsAndTricksUrl) { + openerService.open(URI.parse(productService.tipsAndTricksUrl)); } } } -class OpenNewsletterSignupUrlAction extends Action { +class OpenNewsletterSignupUrlAction extends Action2 { static readonly ID = 'workbench.action.openNewsletterSignupUrl'; - static readonly LABEL = nls.localize('newsletterSignup', "Signup for the VS Code Newsletter"); static readonly AVAILABLE = !!product.newsletterSignupUrl; - constructor( - id: string, - label: string, - @IOpenerService private readonly openerService: IOpenerService, - @ITelemetryService private readonly telemetryService: ITelemetryService, - @IProductService private readonly productService: IProductService - ) { - super(id, label); + constructor() { + super({ + id: OpenNewsletterSignupUrlAction.ID, + title: { value: nls.localize('newsletterSignup', "Signup for the VS Code Newsletter"), original: 'Signup for the VS Code Newsletter' }, + category: helpCategory, + f1: true + }); } - async run(): Promise { - const info = await this.telemetryService.getTelemetryInfo(); + async run(accessor: ServicesAccessor): Promise { + const productService = accessor.get(IProductService); + const openerService = accessor.get(IOpenerService); + const telemetryService = accessor.get(ITelemetryService); - this.openerService.open(URI.parse(`${this.productService.newsletterSignupUrl}?machineId=${encodeURIComponent(info.machineId)}`)); + const info = await telemetryService.getTelemetryInfo(); + + openerService.open(URI.parse(`${productService.newsletterSignupUrl}?machineId=${encodeURIComponent(info.machineId)}`)); } } -class OpenTwitterUrlAction extends Action { +class OpenTwitterUrlAction extends Action2 { static readonly ID = 'workbench.action.openTwitterUrl'; - static readonly LABEL = nls.localize('openTwitterUrl', "Join Us on Twitter"); static readonly AVAILABLE = !!product.twitterUrl; - constructor( - id: string, - label: string, - @IOpenerService private readonly openerService: IOpenerService, - @IProductService private readonly productService: IProductService - ) { - super(id, label); + constructor() { + super({ + id: OpenTwitterUrlAction.ID, + title: { value: nls.localize('openTwitterUrl', "Join Us on Twitter"), original: 'Join Us on Twitter' }, + category: helpCategory, + f1: true + }); } - async run(): Promise { - if (this.productService.twitterUrl) { - this.openerService.open(URI.parse(this.productService.twitterUrl)); + run(accessor: ServicesAccessor): void { + const productService = accessor.get(IProductService); + const openerService = accessor.get(IOpenerService); + + if (productService.twitterUrl) { + openerService.open(URI.parse(productService.twitterUrl)); } } } -class OpenRequestFeatureUrlAction extends Action { +class OpenRequestFeatureUrlAction extends Action2 { static readonly ID = 'workbench.action.openRequestFeatureUrl'; - static readonly LABEL = nls.localize('openUserVoiceUrl', "Search Feature Requests"); static readonly AVAILABLE = !!product.requestFeatureUrl; - constructor( - id: string, - label: string, - @IOpenerService private readonly openerService: IOpenerService, - @IProductService private readonly productService: IProductService - ) { - super(id, label); + constructor() { + super({ + id: OpenRequestFeatureUrlAction.ID, + title: { value: nls.localize('openUserVoiceUrl', "Search Feature Requests"), original: 'Search Feature Requests' }, + category: helpCategory, + f1: true + }); } - async run(): Promise { - if (this.productService.requestFeatureUrl) { - this.openerService.open(URI.parse(this.productService.requestFeatureUrl)); + run(accessor: ServicesAccessor): void { + const productService = accessor.get(IProductService); + const openerService = accessor.get(IOpenerService); + + if (productService.requestFeatureUrl) { + openerService.open(URI.parse(productService.requestFeatureUrl)); } } } -class OpenLicenseUrlAction extends Action { +class OpenLicenseUrlAction extends Action2 { static readonly ID = 'workbench.action.openLicenseUrl'; - static readonly LABEL = nls.localize('openLicenseUrl', "View License"); static readonly AVAILABLE = !!product.licenseUrl; - constructor( - id: string, - label: string, - @IOpenerService private readonly openerService: IOpenerService, - @IProductService private readonly productService: IProductService - ) { - super(id, label); + constructor() { + super({ + id: OpenLicenseUrlAction.ID, + title: { value: nls.localize('openLicenseUrl', "View License"), original: 'View License' }, + category: helpCategory, + f1: true + }); } - async run(): Promise { - if (this.productService.licenseUrl) { + run(accessor: ServicesAccessor): void { + const productService = accessor.get(IProductService); + const openerService = accessor.get(IOpenerService); + + if (productService.licenseUrl) { if (language) { - const queryArgChar = this.productService.licenseUrl.indexOf('?') > 0 ? '&' : '?'; - this.openerService.open(URI.parse(`${this.productService.licenseUrl}${queryArgChar}lang=${language}`)); + const queryArgChar = productService.licenseUrl.indexOf('?') > 0 ? '&' : '?'; + openerService.open(URI.parse(`${productService.licenseUrl}${queryArgChar}lang=${language}`)); } else { - this.openerService.open(URI.parse(this.productService.licenseUrl)); + openerService.open(URI.parse(productService.licenseUrl)); } } } } -class OpenPrivacyStatementUrlAction extends Action { +class OpenPrivacyStatementUrlAction extends Action2 { static readonly ID = 'workbench.action.openPrivacyStatementUrl'; - static readonly LABEL = nls.localize('openPrivacyStatement', "Privacy Statement"); static readonly AVAILABE = !!product.privacyStatementUrl; - constructor( - id: string, - label: string, - @IOpenerService private readonly openerService: IOpenerService, - @IProductService private readonly productService: IProductService - ) { - super(id, label); + constructor() { + super({ + id: OpenPrivacyStatementUrlAction.ID, + title: { value: nls.localize('openPrivacyStatement', "Privacy Statement"), original: 'Privacy Statement' }, + category: helpCategory, + f1: true + }); } - async run(): Promise { - if (this.productService.privacyStatementUrl) { + run(accessor: ServicesAccessor): void { + const productService = accessor.get(IProductService); + const openerService = accessor.get(IOpenerService); + + if (productService.privacyStatementUrl) { if (language) { - const queryArgChar = this.productService.privacyStatementUrl.indexOf('?') > 0 ? '&' : '?'; - this.openerService.open(URI.parse(`${this.productService.privacyStatementUrl}${queryArgChar}lang=${language}`)); + const queryArgChar = productService.privacyStatementUrl.indexOf('?') > 0 ? '&' : '?'; + openerService.open(URI.parse(`${productService.privacyStatementUrl}${queryArgChar}lang=${language}`)); } else { - this.openerService.open(URI.parse(this.productService.privacyStatementUrl)); + openerService.open(URI.parse(productService.privacyStatementUrl)); } } } @@ -228,43 +252,40 @@ class OpenPrivacyStatementUrlAction extends Action { // --- Actions Registration -const registry = Registry.as(Extensions.WorkbenchActions); -const helpCategory = nls.localize('help', "Help"); - if (KeybindingsReferenceAction.AVAILABLE) { - registry.registerWorkbenchAction(SyncActionDescriptor.from(KeybindingsReferenceAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_R) }), 'Help: Keyboard Shortcuts Reference', helpCategory); + registerAction2(KeybindingsReferenceAction); } if (OpenDocumentationUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenDocumentationUrlAction), 'Help: Documentation', helpCategory); + registerAction2(OpenDocumentationUrlAction); } if (OpenIntroductoryVideosUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenIntroductoryVideosUrlAction), 'Help: Introductory Videos', helpCategory); + registerAction2(OpenIntroductoryVideosUrlAction); } if (OpenTipsAndTricksUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenTipsAndTricksUrlAction), 'Help: Tips and Tricks', helpCategory); + registerAction2(OpenTipsAndTricksUrlAction); } if (OpenNewsletterSignupUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenNewsletterSignupUrlAction), 'Help: Tips and Tricks', helpCategory); + registerAction2(OpenNewsletterSignupUrlAction); } if (OpenTwitterUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenTwitterUrlAction), 'Help: Join Us on Twitter', helpCategory); + registerAction2(OpenTwitterUrlAction); } if (OpenRequestFeatureUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenRequestFeatureUrlAction), 'Help: Search Feature Requests', helpCategory); + registerAction2(OpenRequestFeatureUrlAction); } if (OpenLicenseUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenLicenseUrlAction), 'Help: View License', helpCategory); + registerAction2(OpenLicenseUrlAction); } if (OpenPrivacyStatementUrlAction.AVAILABE) { - registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenPrivacyStatementUrlAction), 'Help: Privacy Statement', helpCategory); + registerAction2(OpenPrivacyStatementUrlAction); } // --- Menu Registration diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 45a514e735f..9d5a16a7abe 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -6,12 +6,12 @@ import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuId, MenuRegistry, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/actions'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchLayoutService, Parts, Position } from 'vs/workbench/services/layout/browser/layoutService'; import { IEditorGroupsService, GroupOrientation } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { getMenuBarVisibility } from 'vs/platform/windows/common/windows'; @@ -30,61 +30,58 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { Codicon } from 'vs/base/common/codicons'; const registry = Registry.as(WorkbenchExtensions.WorkbenchActions); -const viewCategory = nls.localize('view', "View"); +const viewCategory = { value: nls.localize('view', "View"), original: 'View' }; // --- Close Side Bar -export class CloseSidebarAction extends Action { +class CloseSidebarAction extends Action2 { - static readonly ID = 'workbench.action.closeSidebar'; - static readonly LABEL = nls.localize('closeSidebar', "Close Side Bar"); - - constructor( - id: string, - label: string, - @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService - ) { - super(id, label); - - this.enabled = !!this.layoutService; + constructor() { + super({ + id: 'workbench.action.closeSidebar', + title: { value: nls.localize('closeSidebar', "Close Side Bar"), original: 'Close Side Bar' }, + category: viewCategory, + f1: true + }); } - async run(): Promise { - this.layoutService.setSideBarHidden(true); + run(accessor: ServicesAccessor): void { + accessor.get(IWorkbenchLayoutService).setSideBarHidden(true); } } -registry.registerWorkbenchAction(SyncActionDescriptor.from(CloseSidebarAction), 'View: Close Side Bar', viewCategory); +registerAction2(CloseSidebarAction); // --- Toggle Activity Bar -export class ToggleActivityBarVisibilityAction extends Action { +export class ToggleActivityBarVisibilityAction extends Action2 { static readonly ID = 'workbench.action.toggleActivityBarVisibility'; static readonly LABEL = nls.localize('toggleActivityBar', "Toggle Activity Bar Visibility"); private static readonly activityBarVisibleKey = 'workbench.activityBar.visible'; - constructor( - id: string, - label: string, - @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, - @IConfigurationService private readonly configurationService: IConfigurationService - ) { - super(id, label); - - this.enabled = !!this.layoutService; + constructor() { + super({ + id: ToggleActivityBarVisibilityAction.ID, + title: { value: ToggleActivityBarVisibilityAction.LABEL, original: 'Toggle Activity Bar Visibility' }, + category: viewCategory, + f1: true + }); } - run(): Promise { - const visibility = this.layoutService.isVisible(Parts.ACTIVITYBAR_PART); + run(accessor: ServicesAccessor): void { + const layoutService = accessor.get(IWorkbenchLayoutService); + const configurationService = accessor.get(IConfigurationService); + + const visibility = layoutService.isVisible(Parts.ACTIVITYBAR_PART); const newVisibilityValue = !visibility; - return this.configurationService.updateValue(ToggleActivityBarVisibilityAction.activityBarVisibleKey, newVisibilityValue, ConfigurationTarget.USER); + configurationService.updateValue(ToggleActivityBarVisibilityAction.activityBarVisibleKey, newVisibilityValue, ConfigurationTarget.USER); } } -registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleActivityBarVisibilityAction), 'View: Toggle Activity Bar Visibility', viewCategory); +registerAction2(ToggleActivityBarVisibilityAction); MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '2_workbench_layout', @@ -98,32 +95,33 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { // --- Toggle Centered Layout -class ToggleCenteredLayout extends Action { +class ToggleCenteredLayout extends Action2 { static readonly ID = 'workbench.action.toggleCenteredLayout'; - static readonly LABEL = nls.localize('toggleCenteredLayout', "Toggle Centered Layout"); - constructor( - id: string, - label: string, - @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService - ) { - super(id, label); - this.enabled = !!this.layoutService; + constructor() { + super({ + id: ToggleCenteredLayout.ID, + title: { value: nls.localize('toggleCenteredLayout', "Toggle Centered Layout"), original: 'Toggle Centered Layout' }, + category: viewCategory, + f1: true + }); } - async run(): Promise { - this.layoutService.centerEditorLayout(!this.layoutService.isEditorLayoutCentered()); + run(accessor: ServicesAccessor): void { + const layoutService = accessor.get(IWorkbenchLayoutService); + + layoutService.centerEditorLayout(!layoutService.isEditorLayoutCentered()); } } -registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleCenteredLayout), 'View: Toggle Centered Layout', viewCategory); +registerAction2(ToggleCenteredLayout); MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '1_toggle_view', command: { id: ToggleCenteredLayout.ID, - title: nls.localize('miToggleCenteredLayout', "Centered Layout"), + title: nls.localize({ key: 'miToggleCenteredLayout', comment: ['&& denotes a mnemonic'] }, "&&Centered Layout"), toggled: IsCenteredLayoutContext }, order: 3 @@ -166,8 +164,7 @@ export class ToggleEditorLayoutAction extends Action { } } -const group = viewCategory; -registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleEditorLayoutAction, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_0, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_0 } }), 'View: Toggle Vertical/Horizontal Editor Layout', group); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleEditorLayoutAction, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_0, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_0 } }), 'View: Toggle Vertical/Horizontal Editor Layout', viewCategory.value); MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { group: 'z_flip', @@ -194,8 +191,6 @@ export class ToggleSidebarPositionAction extends Action { @IConfigurationService private readonly configurationService: IConfigurationService ) { super(id, label); - - this.enabled = !!this.layoutService && !!this.configurationService; } run(): Promise { @@ -210,7 +205,7 @@ export class ToggleSidebarPositionAction extends Action { } } -registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleSidebarPositionAction), 'View: Toggle Side Bar Position', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleSidebarPositionAction), 'View: Toggle Side Bar Position', viewCategory.value); MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '3_workbench_layout_move', @@ -244,8 +239,6 @@ export class ToggleEditorVisibilityAction extends Action { @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService ) { super(id, label); - - this.enabled = !!this.layoutService; } async run(): Promise { @@ -253,7 +246,7 @@ export class ToggleEditorVisibilityAction extends Action { } } -registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleEditorVisibilityAction), 'View: Toggle Editor Area Visibility', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleEditorVisibilityAction), 'View: Toggle Editor Area Visibility', viewCategory.value); MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '2_workbench_layout', @@ -276,8 +269,6 @@ export class ToggleSidebarVisibilityAction extends Action { @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService ) { super(id, label); - - this.enabled = !!this.layoutService; } async run(): Promise { @@ -286,7 +277,7 @@ export class ToggleSidebarVisibilityAction extends Action { } } -registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleSidebarVisibilityAction, { primary: KeyMod.CtrlCmd | KeyCode.KEY_B }), 'View: Toggle Side Bar Visibility', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleSidebarVisibilityAction, { primary: KeyMod.CtrlCmd | KeyCode.KEY_B }), 'View: Toggle Side Bar Visibility', viewCategory.value); MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { group: '2_appearance', @@ -321,8 +312,6 @@ export class ToggleStatusbarVisibilityAction extends Action { @IConfigurationService private readonly configurationService: IConfigurationService ) { super(id, label); - - this.enabled = !!this.layoutService; } run(): Promise { @@ -333,7 +322,7 @@ export class ToggleStatusbarVisibilityAction extends Action { } } -registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleStatusbarVisibilityAction), 'View: Toggle Status Bar Visibility', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleStatusbarVisibilityAction), 'View: Toggle Status Bar Visibility', viewCategory.value); MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '2_workbench_layout', @@ -374,7 +363,7 @@ registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleTabsVisibilityA primary: undefined, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_W, }, linux: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_W, } -}), 'View: Toggle Tab Visibility', viewCategory); +}), 'View: Toggle Tab Visibility', viewCategory.value); // --- Toggle Zen Mode @@ -389,7 +378,6 @@ class ToggleZenMode extends Action { @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService ) { super(id, label); - this.enabled = !!this.layoutService; } async run(): Promise { @@ -397,7 +385,7 @@ class ToggleZenMode extends Action { } } -registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleZenMode, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_Z) }), 'View: Toggle Zen Mode', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleZenMode, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_Z) }), 'View: Toggle Zen Mode', viewCategory.value); MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '1_toggle_view', @@ -458,7 +446,7 @@ export class ToggleMenuBarAction extends Action { } if (isWindows || isLinux || isWeb) { - registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleMenuBarAction), 'View: Toggle Menu Bar', viewCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleMenuBarAction), 'View: Toggle Menu Bar', viewCategory.value); } MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { @@ -508,7 +496,7 @@ export class ResetViewLocationsAction extends Action { } } -registry.registerWorkbenchAction(SyncActionDescriptor.from(ResetViewLocationsAction), 'View: Reset View Locations', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ResetViewLocationsAction), 'View: Reset View Locations', viewCategory.value); // --- Toggle View with Command export abstract class ToggleViewAction extends Action { @@ -542,6 +530,117 @@ export abstract class ToggleViewAction extends Action { } // --- Move View with Command +export class MoveViewAction extends Action { + static readonly ID = 'workbench.action.moveView'; + static readonly LABEL = nls.localize('moveView', "Move View"); + + constructor( + id: string, + label: string, + @IViewDescriptorService private viewDescriptorService: IViewDescriptorService, + @IInstantiationService private instantiationService: IInstantiationService, + @IQuickInputService private quickInputService: IQuickInputService, + @IContextKeyService private contextKeyService: IContextKeyService, + @IActivityBarService private activityBarService: IActivityBarService, + @IPanelService private panelService: IPanelService + ) { + super(id, label); + } + + private getViewItems(): Array { + const results: Array = []; + + const viewlets = this.activityBarService.getVisibleViewContainerIds(); + viewlets.forEach(viewletId => { + const container = this.viewDescriptorService.getViewContainerById(viewletId)!; + const containerModel = this.viewDescriptorService.getViewContainerModel(container); + + let hasAddedView = false; + containerModel.visibleViewDescriptors.forEach(viewDescriptor => { + if (viewDescriptor.canMoveView) { + if (!hasAddedView) { + results.push({ + type: 'separator', + label: nls.localize('sidebarContainer', "Side Bar / {0}", containerModel.title) + }); + hasAddedView = true; + } + + results.push({ + id: viewDescriptor.id, + label: viewDescriptor.name + }); + } + }); + }); + + const panels = this.panelService.getPinnedPanels(); + panels.forEach(panel => { + const container = this.viewDescriptorService.getViewContainerById(panel.id)!; + const containerModel = this.viewDescriptorService.getViewContainerModel(container); + + let hasAddedView = false; + containerModel.visibleViewDescriptors.forEach(viewDescriptor => { + if (viewDescriptor.canMoveView) { + if (!hasAddedView) { + results.push({ + type: 'separator', + label: nls.localize('panelContainer', "Panel / {0}", containerModel.title) + }); + hasAddedView = true; + } + + results.push({ + id: viewDescriptor.id, + label: viewDescriptor.name + }); + } + }); + }); + + return results; + } + + private async getView(viewId?: string): Promise { + const quickPick = this.quickInputService.createQuickPick(); + quickPick.placeholder = nls.localize('moveFocusedView.selectView', "Select a View to Move"); + quickPick.items = this.getViewItems(); + quickPick.selectedItems = quickPick.items.filter(item => (item as IQuickPickItem).id === viewId) as IQuickPickItem[]; + + return new Promise((resolve, reject) => { + quickPick.onDidAccept(() => { + const viewId = quickPick.selectedItems[0]; + resolve(viewId.id); + quickPick.hide(); + }); + + quickPick.onDidHide(() => reject()); + + quickPick.show(); + }); + } + + async run(): Promise { + const focusedViewId = FocusedViewContext.getValue(this.contextKeyService); + let viewId: string; + + if (focusedViewId && this.viewDescriptorService.getViewDescriptorById(focusedViewId)?.canMoveView) { + viewId = focusedViewId; + } + + viewId = await this.getView(viewId!); + + if (!viewId) { + return; + } + + this.instantiationService.createInstance(MoveFocusedViewAction, MoveFocusedViewAction.ID, MoveFocusedViewAction.LABEL).run(viewId); + } +} + +registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveViewAction), 'View: Move View', viewCategory.value); + +// --- Move Focused View with Command export class MoveFocusedViewAction extends Action { static readonly ID = 'workbench.action.moveFocusedView'; static readonly LABEL = nls.localize('moveFocusedView', "Move Focused View"); @@ -560,8 +659,8 @@ export class MoveFocusedViewAction extends Action { super(id, label); } - async run(): Promise { - const focusedViewId = FocusedViewContext.getValue(this.contextKeyService); + async run(viewId: string): Promise { + const focusedViewId = viewId || FocusedViewContext.getValue(this.contextKeyService); if (focusedViewId === undefined || focusedViewId.trim() === '') { this.notificationService.error(nls.localize('moveFocusedView.error.noFocusedView', "There is no view currently focused.")); @@ -576,27 +675,33 @@ export class MoveFocusedViewAction extends Action { const quickPick = this.quickInputService.createQuickPick(); quickPick.placeholder = nls.localize('moveFocusedView.selectDestination', "Select a Destination for the View"); - quickPick.title = nls.localize('moveFocusedView.title', "View: Move {0}", viewDescriptor.name); + quickPick.title = nls.localize({ key: 'moveFocusedView.title', comment: ['{0} indicates the title of the view the user has selected to move.'] }, "View: Move {0}", viewDescriptor.name); const items: Array = []; + const currentContainer = this.viewDescriptorService.getViewContainerByViewId(focusedViewId)!; + const currentLocation = this.viewDescriptorService.getViewLocationById(focusedViewId)!; + const isViewSolo = this.viewDescriptorService.getViewContainerModel(currentContainer).allViewDescriptors.length === 1; + + if (!(isViewSolo && currentLocation === ViewContainerLocation.Panel)) { + items.push({ + id: '_.panel.newcontainer', + label: nls.localize({ key: 'moveFocusedView.newContainerInPanel', comment: ['Creates a new top-level tab in the panel.'] }, "New Panel Entry"), + }); + } + + if (!(isViewSolo && currentLocation === ViewContainerLocation.Sidebar)) { + items.push({ + id: '_.sidebar.newcontainer', + label: nls.localize('moveFocusedView.newContainerInSidebar', "New Side Bar Entry") + }); + } items.push({ type: 'separator', label: nls.localize('sidebar', "Side Bar") }); - const currentContainer = this.viewDescriptorService.getViewContainerByViewId(focusedViewId)!; - const currentLocation = this.viewDescriptorService.getViewLocationById(focusedViewId)!; - const isViewSolo = this.viewDescriptorService.getViewContainerModel(currentContainer).allViewDescriptors.length === 1; - - if (!(isViewSolo && currentLocation === ViewContainerLocation.Sidebar)) { - items.push({ - id: '_.sidebar.newcontainer', - label: nls.localize('moveFocusedView.newContainerInSidebar', "New Container in Side Bar") - }); - } - - const pinnedViewlets = this.activityBarService.getPinnedViewContainerIds(); + const pinnedViewlets = this.activityBarService.getVisibleViewContainerIds(); items.push(...pinnedViewlets .filter(viewletId => { if (viewletId === this.viewDescriptorService.getViewContainerByViewId(focusedViewId)!.id) { @@ -617,13 +722,6 @@ export class MoveFocusedViewAction extends Action { label: nls.localize('panel', "Panel") }); - if (!(isViewSolo && currentLocation === ViewContainerLocation.Panel)) { - items.push({ - id: '_.panel.newcontainer', - label: nls.localize('moveFocusedView.newContainerInPanel', "New Container in Panel"), - }); - } - const pinnedPanels = this.panelService.getPinnedPanels(); items.push(...pinnedPanels .filter(panel => { @@ -663,7 +761,7 @@ export class MoveFocusedViewAction extends Action { } } -registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveFocusedViewAction), 'View: Move Focused View', viewCategory, FocusedViewContext.notEqualsTo('')); +registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveFocusedViewAction), 'View: Move Focused View', viewCategory.value, FocusedViewContext.notEqualsTo('')); // --- Reset View Location with Command @@ -706,7 +804,7 @@ export class ResetFocusedViewLocationAction extends Action { } } -registry.registerWorkbenchAction(SyncActionDescriptor.from(ResetFocusedViewLocationAction), 'View: Reset Focused View Location', viewCategory, FocusedViewContext.notEqualsTo('')); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ResetFocusedViewLocationAction), 'View: Reset Focused View Location', viewCategory.value, FocusedViewContext.notEqualsTo('')); // --- Resize View @@ -780,5 +878,5 @@ export class DecreaseViewSizeAction extends BaseResizeViewAction { } } -registry.registerWorkbenchAction(SyncActionDescriptor.from(IncreaseViewSizeAction, undefined), 'View: Increase Current View Size', viewCategory); -registry.registerWorkbenchAction(SyncActionDescriptor.from(DecreaseViewSizeAction, undefined), 'View: Decrease Current View Size', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.from(IncreaseViewSizeAction, undefined), 'View: Increase Current View Size', viewCategory.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(DecreaseViewSizeAction, undefined), 'View: Decrease Current View Size', viewCategory.value); diff --git a/src/vs/workbench/browser/actions/listCommands.ts b/src/vs/workbench/browser/actions/listCommands.ts index 1726376be57..f0c86427952 100644 --- a/src/vs/workbench/browser/actions/listCommands.ts +++ b/src/vs/workbench/browser/actions/listCommands.ts @@ -31,9 +31,6 @@ function focusDown(accessor: ServicesAccessor, arg2?: number, loop: boolean = fa const focused = accessor.get(IListService).lastFocusedList; const count = typeof arg2 === 'number' ? arg2 : 1; - // Ensure DOM Focus - ensureDOMFocus(focused); - // List if (focused instanceof List || focused instanceof PagedList) { const list = focused; @@ -57,6 +54,9 @@ function focusDown(accessor: ServicesAccessor, arg2?: number, loop: boolean = fa tree.reveal(listFocus[0]); } } + + // Ensure DOM Focus + ensureDOMFocus(focused); } KeybindingsRegistry.registerCommandAndKeybindingRule({ @@ -135,9 +135,6 @@ function focusUp(accessor: ServicesAccessor, arg2?: number, loop: boolean = fals const focused = accessor.get(IListService).lastFocusedList; const count = typeof arg2 === 'number' ? arg2 : 1; - // Ensure DOM Focus - ensureDOMFocus(focused); - // List if (focused instanceof List || focused instanceof PagedList) { const list = focused; @@ -161,6 +158,9 @@ function focusUp(accessor: ServicesAccessor, arg2?: number, loop: boolean = fals tree.reveal(listFocus[0]); } } + + // Ensure DOM Focus + ensureDOMFocus(focused); } KeybindingsRegistry.registerCommandAndKeybindingRule({ @@ -354,9 +354,6 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ handler: (accessor) => { const focused = accessor.get(IListService).lastFocusedList; - // Ensure DOM Focus - ensureDOMFocus(focused); - // List if (focused instanceof List || focused instanceof PagedList) { const list = focused; @@ -373,6 +370,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ list.focusPreviousPage(fakeKeyboardEvent); list.reveal(list.getFocus()[0]); } + + // Ensure DOM Focus + ensureDOMFocus(focused); } }); @@ -384,9 +384,6 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ handler: (accessor) => { const focused = accessor.get(IListService).lastFocusedList; - // Ensure DOM Focus - ensureDOMFocus(focused); - // List if (focused instanceof List || focused instanceof PagedList) { const list = focused; @@ -403,6 +400,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ list.focusNextPage(fakeKeyboardEvent); list.reveal(list.getFocus()[0]); } + + // Ensure DOM Focus + ensureDOMFocus(focused); } }); @@ -425,9 +425,6 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ function listFocusFirst(accessor: ServicesAccessor, options?: { fromFocused: boolean }): void { const focused = accessor.get(IListService).lastFocusedList; - // Ensure DOM Focus - ensureDOMFocus(focused); - // List if (focused instanceof List || focused instanceof PagedList) { const list = focused; @@ -448,6 +445,9 @@ function listFocusFirst(accessor: ServicesAccessor, options?: { fromFocused: boo tree.reveal(focus[0]); } } + + // Ensure DOM Focus + ensureDOMFocus(focused); } KeybindingsRegistry.registerCommandAndKeybindingRule({ @@ -469,9 +469,6 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ function listFocusLast(accessor: ServicesAccessor, options?: { fromFocused: boolean }): void { const focused = accessor.get(IListService).lastFocusedList; - // Ensure DOM Focus - ensureDOMFocus(focused); - // List if (focused instanceof List || focused instanceof PagedList) { const list = focused; @@ -492,6 +489,42 @@ function listFocusLast(accessor: ServicesAccessor, options?: { fromFocused: bool tree.reveal(focus[0]); } } + + // Ensure DOM Focus + ensureDOMFocus(focused); +} + + +function focusElement(accessor: ServicesAccessor, retainCurrentFocus: boolean): void { + const focused = accessor.get(IListService).lastFocusedList; + const fakeKeyboardEvent = getSelectionKeyboardEvent('keydown', retainCurrentFocus); + // List + if (focused instanceof List || focused instanceof PagedList) { + const list = focused; + list.setSelection(list.getFocus(), fakeKeyboardEvent); + } + + // Trees + else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + const tree = focused; + const focus = tree.getFocus(); + + if (focus.length > 0) { + let toggleCollapsed = true; + + if (tree.expandOnlyOnTwistieClick === true) { + toggleCollapsed = false; + } else if (typeof tree.expandOnlyOnTwistieClick !== 'boolean' && tree.expandOnlyOnTwistieClick(focus[0])) { + toggleCollapsed = false; + } + + if (toggleCollapsed) { + tree.toggleCollapsed(focus[0]); + } + } + tree.setSelection(focus, fakeKeyboardEvent); + tree.open(fakeKeyboardEvent); + } } KeybindingsRegistry.registerCommandAndKeybindingRule({ @@ -504,38 +537,16 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ secondary: [KeyMod.CtrlCmd | KeyCode.DownArrow] }, handler: (accessor) => { - const focused = accessor.get(IListService).lastFocusedList; - const fakeKeyboardEvent = getSelectionKeyboardEvent('keydown', false); + focusElement(accessor, false); + } +}); - // List - if (focused instanceof List || focused instanceof PagedList) { - const list = focused; - list.setSelection(list.getFocus(), fakeKeyboardEvent); - list.open(list.getFocus(), fakeKeyboardEvent); - } - - // Tree - else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const list = focused; - const focus = list.getFocus(); - - if (focus.length > 0) { - let toggleCollapsed = true; - - if (list.expandOnlyOnTwistieClick === true) { - toggleCollapsed = false; - } else if (typeof list.expandOnlyOnTwistieClick !== 'boolean' && list.expandOnlyOnTwistieClick(focus[0])) { - toggleCollapsed = false; - } - - if (toggleCollapsed) { - list.toggleCollapsed(focus[0]); - } - } - - list.setSelection(focus, fakeKeyboardEvent); - list.open(focus, fakeKeyboardEvent); - } +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.selectAndPreserveFocus', + weight: KeybindingWeight.WorkbenchContrib, + when: WorkbenchListFocusContextKey, + handler: accessor => { + focusElement(accessor, true); } }); @@ -562,7 +573,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ // Which element should be considered to start selecting all? let start: unknown | undefined = undefined; - if (focus.length > 0 && (selection.length === 0 || selection.indexOf(focus[0]) === -1)) { + if (focus.length > 0 && (selection.length === 0 || !selection.includes(focus[0]))) { start = focus[0]; } @@ -644,18 +655,17 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const focused = accessor.get(IListService).lastFocusedList; // Tree only - if (focused && !(focused instanceof List || focused instanceof PagedList)) { - if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const tree = focused; - const focus = tree.getFocus(); - - if (focus.length === 0) { - return; - } + if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + const tree = focused; + const focus = tree.getFocus(); + if (focus.length > 0 && tree.isCollapsible(focus[0])) { tree.toggleCollapsed(focus[0]); + return; } } + + focusElement(accessor, true); } }); diff --git a/src/vs/workbench/browser/actions/media/actions.css b/src/vs/workbench/browser/actions/media/actions.css index 8bef21b5d17..e87f0156206 100644 --- a/src/vs/workbench/browser/actions/media/actions.css +++ b/src/vs/workbench/browser/actions/media/actions.css @@ -25,15 +25,12 @@ position: absolute; background-color: rgba(0, 0, 0 ,0.5); width: 100%; - height: 100px; - bottom: 20%; left: 0; z-index: 100000; pointer-events: none; color: #eee; - line-height: 100px; + line-height: 1.75em; text-align: center; - font-size: 56px; transition: opacity 0.3s ease-out; white-space: nowrap; overflow: hidden; diff --git a/src/vs/workbench/browser/actions/navigationActions.ts b/src/vs/workbench/browser/actions/navigationActions.ts index 22bae467a85..9ea421e1256 100644 --- a/src/vs/workbench/browser/actions/navigationActions.ts +++ b/src/vs/workbench/browser/actions/navigationActions.ts @@ -12,10 +12,13 @@ import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/bro import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { IPanel } from 'vs/workbench/common/panel'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Action2, MenuId, registerAction2, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { Direction } from 'vs/base/browser/ui/grid/grid'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; abstract class BaseNavigationAction extends Action { @@ -202,9 +205,9 @@ class NavigateDownAction extends BaseNavigationAction { } function findVisibleNeighbour(layoutService: IWorkbenchLayoutService, part: Parts, next: boolean): Parts { - const neighbour = part === Parts.EDITOR_PART ? (next ? Parts.STATUSBAR_PART : Parts.PANEL_PART) : part === Parts.STATUSBAR_PART ? (next ? Parts.ACTIVITYBAR_PART : Parts.EDITOR_PART) : - part === Parts.ACTIVITYBAR_PART ? (next ? Parts.SIDEBAR_PART : Parts.STATUSBAR_PART) : part === Parts.SIDEBAR_PART ? (next ? Parts.PANEL_PART : Parts.ACTIVITYBAR_PART) : - part === Parts.PANEL_PART ? (next ? Parts.EDITOR_PART : Parts.SIDEBAR_PART) : Parts.EDITOR_PART; + const neighbour = part === Parts.EDITOR_PART ? (next ? Parts.PANEL_PART : Parts.SIDEBAR_PART) : part === Parts.PANEL_PART ? (next ? Parts.STATUSBAR_PART : Parts.EDITOR_PART) : + part === Parts.STATUSBAR_PART ? (next ? Parts.ACTIVITYBAR_PART : Parts.PANEL_PART) : part === Parts.ACTIVITYBAR_PART ? (next ? Parts.SIDEBAR_PART : Parts.STATUSBAR_PART) : + part === Parts.SIDEBAR_PART ? (next ? Parts.EDITOR_PART : Parts.ACTIVITYBAR_PART) : Parts.EDITOR_PART; if (layoutService.isVisible(neighbour) || neighbour === Parts.EDITOR_PART) { return neighbour; } @@ -257,12 +260,38 @@ export class FocusPreviousPart extends Action { } } -const registry = Registry.as(Extensions.WorkbenchActions); +class GoHomeContributor implements IWorkbenchContribution { + + constructor( + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService + ) { + const homeIndicator = environmentService.options?.homeIndicator; + if (homeIndicator) { + registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.actions.goHome`, + title: nls.localize('goHome', "Go Home"), + menu: { id: MenuId.MenubarWebNavigationMenu } + }); + } + async run(): Promise { + window.location.href = homeIndicator.href; + } + }); + } + } +} + +const actionsRegistry = Registry.as(Extensions.WorkbenchActions); const viewCategory = nls.localize('view', "View"); -registry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateUpAction, undefined), 'View: Navigate to the View Above', viewCategory); -registry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateDownAction, undefined), 'View: Navigate to the View Below', viewCategory); -registry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateLeftAction, undefined), 'View: Navigate to the View on the Left', viewCategory); -registry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateRightAction, undefined), 'View: Navigate to the View on the Right', viewCategory); -registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusNextPart, { primary: KeyCode.F6 }), 'View: Focus Next Part', viewCategory); -registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusPreviousPart, { primary: KeyMod.Shift | KeyCode.F6 }), 'View: Focus Previous Part', viewCategory); +actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateUpAction, undefined), 'View: Navigate to the View Above', viewCategory); +actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateDownAction, undefined), 'View: Navigate to the View Below', viewCategory); +actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateLeftAction, undefined), 'View: Navigate to the View on the Left', viewCategory); +actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateRightAction, undefined), 'View: Navigate to the View on the Right', viewCategory); +actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(FocusNextPart, { primary: KeyCode.F6 }), 'View: Focus Next Part', viewCategory); +actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(FocusPreviousPart, { primary: KeyMod.Shift | KeyCode.F6 }), 'View: Focus Previous Part', viewCategory); + +const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchRegistry.registerWorkbenchContribution(GoHomeContributor, LifecyclePhase.Ready); diff --git a/src/vs/workbench/browser/actions/quickAccessActions.ts b/src/vs/workbench/browser/actions/quickAccessActions.ts index 2f369a77572..484512fb8bf 100644 --- a/src/vs/workbench/browser/actions/quickAccessActions.ts +++ b/src/vs/workbench/browser/actions/quickAccessActions.ts @@ -4,15 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { MenuRegistry, MenuId, Action2, registerAction2, ILocalizedString } from 'vs/platform/actions/common/actions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; -import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight, IKeybindingRule } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IQuickInputService, ItemActivation } from 'vs/platform/quickinput/common/quickInput'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { Action } from 'vs/base/common/actions'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { inQuickPickContext, defaultQuickAccessContext, getQuickNavigateHandler } from 'vs/workbench/browser/quickaccess'; @@ -138,7 +135,7 @@ CommandsRegistry.registerCommand({ handler: async function (accessor: ServicesAccessor, prefix: unknown) { const quickInputService = accessor.get(IQuickInputService); - quickInputService.quickAccess.show(typeof prefix === 'string' ? prefix : undefined); + quickInputService.quickAccess.show(typeof prefix === 'string' ? prefix : undefined, { preserveValue: typeof prefix === 'string' /* preserve as is if provided */ }); }, description: { description: `Quick access`, @@ -151,7 +148,7 @@ CommandsRegistry.registerCommand({ } }); -CommandsRegistry.registerCommand('workbench.action.quickOpenPreviousEditor', async function (accessor: ServicesAccessor, prefix: string | null = null) { +CommandsRegistry.registerCommand('workbench.action.quickOpenPreviousEditor', async accessor => { const quickInputService = accessor.get(IQuickInputService); quickInputService.quickAccess.show('', { itemActivation: ItemActivation.SECOND }); @@ -161,91 +158,82 @@ CommandsRegistry.registerCommand('workbench.action.quickOpenPreviousEditor', asy //#region Workbench actions -export class BaseQuickAccessNavigateAction extends Action { +class BaseQuickAccessNavigateAction extends Action2 { constructor( - id: string, - label: string, + private id: string, + title: ILocalizedString, private next: boolean, private quickNavigate: boolean, - @IQuickInputService private readonly quickInputService: IQuickInputService, - @IKeybindingService private readonly keybindingService: IKeybindingService + keybinding?: Omit ) { - super(id, label); + super({ id, title, f1: true, keybinding }); } - async run(): Promise { - const keys = this.keybindingService.lookupKeybindings(this.id); + async run(accessor: ServicesAccessor): Promise { + const keybindingService = accessor.get(IKeybindingService); + const quickInputService = accessor.get(IQuickInputService); + + const keys = keybindingService.lookupKeybindings(this.id); const quickNavigate = this.quickNavigate ? { keybindings: keys } : undefined; - this.quickInputService.navigate(this.next, quickNavigate); + quickInputService.navigate(this.next, quickNavigate); } } -export class QuickAccessNavigateNextAction extends BaseQuickAccessNavigateAction { +class QuickAccessNavigateNextAction extends BaseQuickAccessNavigateAction { - static readonly ID = 'workbench.action.quickOpenNavigateNext'; - static readonly LABEL = localize('quickNavigateNext', "Navigate Next in Quick Open"); - - constructor( - id: string, - label: string, - @IQuickInputService quickInputService: IQuickInputService, - @IKeybindingService keybindingService: IKeybindingService - ) { - super(id, label, true, true, quickInputService, keybindingService); + constructor() { + super('workbench.action.quickOpenNavigateNext', { value: localize('quickNavigateNext', "Navigate Next in Quick Open"), original: 'Navigate Next in Quick Open' }, true, true); } } class QuickAccessNavigatePreviousAction extends BaseQuickAccessNavigateAction { - static readonly ID = 'workbench.action.quickOpenNavigatePrevious'; - static readonly LABEL = localize('quickNavigatePrevious', "Navigate Previous in Quick Open"); - - constructor( - id: string, - label: string, - @IQuickInputService quickInputService: IQuickInputService, - @IKeybindingService keybindingService: IKeybindingService - ) { - super(id, label, false, true, quickInputService, keybindingService); + constructor() { + super('workbench.action.quickOpenNavigatePrevious', { value: localize('quickNavigatePrevious', "Navigate Previous in Quick Open"), original: 'Navigate Previous in Quick Open' }, false, true); } } class QuickAccessSelectNextAction extends BaseQuickAccessNavigateAction { - static readonly ID = 'workbench.action.quickOpenSelectNext'; - static readonly LABEL = localize('quickSelectNext', "Select Next in Quick Open"); - - constructor( - id: string, - label: string, - @IQuickInputService quickInputService: IQuickInputService, - @IKeybindingService keybindingService: IKeybindingService - ) { - super(id, label, true, false, quickInputService, keybindingService); + constructor() { + super( + 'workbench.action.quickOpenSelectNext', + { value: localize('quickSelectNext', "Select Next in Quick Open"), original: 'Select Next in Quick Open' }, + true, + false, + { + weight: KeybindingWeight.WorkbenchContrib + 50, + when: inQuickPickContext, + primary: 0, + mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_N } + } + ); } } class QuickAccessSelectPreviousAction extends BaseQuickAccessNavigateAction { - static readonly ID = 'workbench.action.quickOpenSelectPrevious'; - static readonly LABEL = localize('quickSelectPrevious', "Select Previous in Quick Open"); - - constructor( - id: string, - label: string, - @IQuickInputService quickInputService: IQuickInputService, - @IKeybindingService keybindingService: IKeybindingService - ) { - super(id, label, false, false, quickInputService, keybindingService); + constructor() { + super( + 'workbench.action.quickOpenSelectPrevious', + { value: localize('quickSelectPrevious', "Select Previous in Quick Open"), original: 'Select Previous in Quick Open' }, + false, + false, + { + weight: KeybindingWeight.WorkbenchContrib + 50, + when: inQuickPickContext, + primary: 0, + mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_P } + } + ); } } -const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(SyncActionDescriptor.from(QuickAccessSelectNextAction, { primary: 0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_N } }, inQuickPickContext, KeybindingWeight.WorkbenchContrib + 50), 'Select Next in Quick Open'); -registry.registerWorkbenchAction(SyncActionDescriptor.from(QuickAccessSelectPreviousAction, { primary: 0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_P } }, inQuickPickContext, KeybindingWeight.WorkbenchContrib + 50), 'Select Previous in Quick Open'); -registry.registerWorkbenchAction(SyncActionDescriptor.from(QuickAccessNavigateNextAction), 'Navigate Next in Quick Open'); -registry.registerWorkbenchAction(SyncActionDescriptor.from(QuickAccessNavigatePreviousAction), 'Navigate Previous in Quick Open'); +registerAction2(QuickAccessSelectNextAction); +registerAction2(QuickAccessSelectPreviousAction); +registerAction2(QuickAccessNavigateNextAction); +registerAction2(QuickAccessNavigatePreviousAction); //#endregion diff --git a/src/vs/workbench/browser/actions/textInputActions.ts b/src/vs/workbench/browser/actions/textInputActions.ts index b9ae0528184..da382fb4df2 100644 --- a/src/vs/workbench/browser/actions/textInputActions.ts +++ b/src/vs/workbench/browser/actions/textInputActions.ts @@ -3,9 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IAction, Action } from 'vs/base/common/actions'; +import { IAction, Action, Separator } from 'vs/base/common/actions'; import { localize } from 'vs/nls'; -import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { Disposable } from 'vs/base/common/lifecycle'; diff --git a/src/vs/workbench/browser/actions/windowActions.ts b/src/vs/workbench/browser/actions/windowActions.ts index e94ac69f805..2889309b267 100644 --- a/src/vs/workbench/browser/actions/windowActions.ts +++ b/src/vs/workbench/browser/actions/windowActions.ts @@ -344,7 +344,7 @@ registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenRecentAction, { p const viewCategory = nls.localize('view', "View"); registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleFullScreenAction, { primary: KeyCode.F11, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_F } }), 'View: Toggle Full Screen', viewCategory); -const developerCategory = nls.localize('developer', "Developer"); +const developerCategory = nls.localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer"); registry.registerWorkbenchAction(SyncActionDescriptor.from(ReloadWindowAction), 'Developer: Reload Window', developerCategory); const helpCategory = nls.localize('help', "Help"); diff --git a/src/vs/workbench/browser/actions/workspaceCommands.ts b/src/vs/workbench/browser/actions/workspaceCommands.ts index 5a9786f2bfc..24dc2f9d644 100644 --- a/src/vs/workbench/browser/actions/workspaceCommands.ts +++ b/src/vs/workbench/browser/actions/workspaceCommands.ts @@ -18,7 +18,6 @@ import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IViewDescriptorService, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views'; export const ADD_ROOT_FOLDER_COMMAND_ID = 'addRootFolder'; export const ADD_ROOT_FOLDER_LABEL = nls.localize('addFolderToWorkspace', "Add Folder to Workspace..."); @@ -55,8 +54,6 @@ CommandsRegistry.registerCommand({ CommandsRegistry.registerCommand({ id: ADD_ROOT_FOLDER_COMMAND_ID, handler: async (accessor) => { - const viewDescriptorService = accessor.get(IViewDescriptorService); - const viewsService = accessor.get(IViewsService); const workspaceEditingService = accessor.get(IWorkspaceEditingService); const dialogsService = accessor.get(IFileDialogService); const folders = await dialogsService.showOpenDialog({ @@ -72,7 +69,6 @@ CommandsRegistry.registerCommand({ } await workspaceEditingService.addFolders(folders.map(folder => ({ uri: resources.removeTrailingPathSeparator(folder) }))); - await viewsService.openViewContainer(viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.Sidebar)!.id, true); } }); diff --git a/src/vs/workbench/browser/composite.ts b/src/vs/workbench/browser/composite.ts index 0cb69cba7d4..16f7cf4632e 100644 --- a/src/vs/workbench/browser/composite.ts +++ b/src/vs/workbench/browser/composite.ts @@ -3,8 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IAction, IActionRunner, ActionRunner } from 'vs/base/common/actions'; -import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IAction, IActionRunner, ActionRunner, IActionViewItem } from 'vs/base/common/actions'; import { Component } from 'vs/workbench/common/component'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IComposite, ICompositeControl } from 'vs/workbench/common/composite'; @@ -33,9 +32,6 @@ export abstract class Composite extends Component implements IComposite { private readonly _onTitleAreaUpdate = this._register(new Emitter()); readonly onTitleAreaUpdate = this._onTitleAreaUpdate.event; - private readonly _onDidChangeVisibility = this._register(new Emitter()); - readonly onDidChangeVisibility = this._onDidChangeVisibility.event; - private _onDidFocus: Emitter | undefined; get onDidFocus(): Event { if (!this._onDidFocus) { @@ -135,8 +131,6 @@ export abstract class Composite extends Component implements IComposite { setVisible(visible: boolean): void { if (this.visible !== !!visible) { this.visible = visible; - - this._onDidChangeVisibility.fire(visible); } } @@ -239,6 +233,7 @@ export abstract class CompositeDescriptor { readonly name: string, readonly cssClass?: string, readonly order?: number, + readonly requestedIndex?: number, readonly keybindingId?: string, ) { } diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index 544ac2c0492..c691b360893 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -5,7 +5,7 @@ import { hasWorkspaceFileExtension, IWorkspaceFolderCreationData, IRecentFile, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { normalize } from 'vs/base/common/path'; -import { basename } from 'vs/base/common/resources'; +import { basename, extUri } from 'vs/base/common/resources'; import { IFileService } from 'vs/platform/files/common/files'; import { IWindowOpenable } from 'vs/platform/windows/common/windows'; import { URI } from 'vs/base/common/uri'; @@ -36,38 +36,30 @@ export interface IDraggedResource { isExternal: boolean; } +interface ISerializedDraggedResource { + resource: string; +} + export class DraggedEditorIdentifier { - constructor(private _identifier: IEditorIdentifier) { } - - get identifier(): IEditorIdentifier { - return this._identifier; - } + constructor(public readonly identifier: IEditorIdentifier) { } } export class DraggedEditorGroupIdentifier { - constructor(private _identifier: GroupIdentifier) { } - - get identifier(): GroupIdentifier { - return this._identifier; - } + constructor(public readonly identifier: GroupIdentifier) { } } -export interface IDraggedEditor extends IDraggedResource { - content?: string; +interface IDraggedEditorProps { + dirtyContent?: string; encoding?: string; mode?: string; options?: ITextEditorOptions; } -export interface ISerializedDraggedEditor { - resource: string; - content?: string; - encoding?: string; - mode?: string; - options?: ITextEditorOptions; -} +export interface IDraggedEditor extends IDraggedResource, IDraggedEditorProps { } + +export interface ISerializedDraggedEditor extends ISerializedDraggedResource, IDraggedEditorProps { } export const CodeDataTransfers = { EDITORS: 'CodeEditors', @@ -89,7 +81,7 @@ export function extractResources(e: DragEvent, externalOnly?: boolean): Array { resources.push({ resource: URI.parse(draggedEditor.resource), - content: draggedEditor.content, + dirtyContent: draggedEditor.dirtyContent, options: draggedEditor.options, encoding: draggedEditor.encoding, mode: draggedEditor.mode, @@ -186,13 +178,12 @@ export class ResourcesDropHandler { // Check for special things being dropped const isWorkspaceOpening = await this.doHandleDrop(untitledOrFileResources); - if (isWorkspaceOpening) { return; // return early if the drop operation resulted in this window changing to a workspace } // Add external ones to recently open list unless dropped resource is a workspace - const recentFiles: IRecentFile[] = untitledOrFileResources.filter(d => d.isExternal && d.resource.scheme === Schemas.file).map(d => ({ fileUri: d.resource })); + const recentFiles: IRecentFile[] = untitledOrFileResources.filter(untitledOrFileResource => untitledOrFileResource.isExternal && untitledOrFileResource.resource.scheme === Schemas.file).map(d => ({ fileUri: d.resource })); if (recentFiles.length) { this.workspacesService.addRecentlyOpened(recentFiles); } @@ -219,15 +210,15 @@ export class ResourcesDropHandler { private async doHandleDrop(untitledOrFileResources: Array): Promise { // Check for dirty editors being dropped - const resourcesWithContent: IDraggedEditor[] = untitledOrFileResources.filter(resource => !resource.isExternal && !!(resource as IDraggedEditor).content); - if (resourcesWithContent.length > 0) { - await Promise.all(resourcesWithContent.map(resourceWithContent => this.handleDirtyEditorDrop(resourceWithContent))); + const dirtyEditors: IDraggedEditor[] = untitledOrFileResources.filter(untitledOrFileResource => !untitledOrFileResource.isExternal && typeof (untitledOrFileResource as IDraggedEditor).dirtyContent === 'string'); + if (dirtyEditors.length > 0) { + await Promise.all(dirtyEditors.map(dirtyEditor => this.handleDirtyEditorDrop(dirtyEditor))); return false; } // Check for workspace file being dropped if we are allowed to do so if (this.options.allowWorkspaceOpen) { - const externalFileOnDiskResources = untitledOrFileResources.filter(d => d.isExternal && d.resource.scheme === Schemas.file).map(d => d.resource); + const externalFileOnDiskResources = untitledOrFileResources.filter(untitledOrFileResource => untitledOrFileResource.isExternal && untitledOrFileResource.resource.scheme === Schemas.file).map(d => d.resource); if (externalFileOnDiskResources.length > 0) { return this.handleWorkspaceFileDrop(externalFileOnDiskResources); } @@ -253,9 +244,9 @@ export class ResourcesDropHandler { // If the dropped editor is dirty with content we simply take that // content and turn it into a backup so that it loads the contents - if (droppedDirtyEditor.content) { + if (typeof droppedDirtyEditor.dirtyContent === 'string') { try { - await this.backupFileService.backup(droppedDirtyEditor.resource, stringToSnapshot(droppedDirtyEditor.content)); + await this.backupFileService.backup(droppedDirtyEditor.resource, stringToSnapshot(droppedDirtyEditor.dirtyContent)); } catch (e) { // Ignore error } @@ -335,9 +326,9 @@ export function fillResourceDataTransfers(accessor: ServicesAccessor, resources: } // Resource URLs: allows to drop multiple resources to a target in VS Code (not directories) - const files = sources.filter(s => !s.isDirectory); + const files = sources.filter(source => !source.isDirectory); if (files.length) { - event.dataTransfer.setData(DataTransfers.RESOURCES, JSON.stringify(files.map(f => f.resource.toString()))); + event.dataTransfer.setData(DataTransfers.RESOURCES, JSON.stringify(files.map(file => file.resource.toString()))); } // Editors: enables cross window DND of tabs into the editor area @@ -361,7 +352,7 @@ export function fillResourceDataTransfers(accessor: ServicesAccessor, resources: for (const textEditorControl of textEditorControls) { if (isCodeEditor(textEditorControl)) { const model = textEditorControl.getModel(); - if (model?.uri?.toString() === file.resource.toString()) { + if (extUri.isEqual(model?.uri, file.resource)) { return withNullAsUndefined(textEditorControl.saveViewState()); } } @@ -384,13 +375,13 @@ export function fillResourceDataTransfers(accessor: ServicesAccessor, resources: // If the resource is dirty or untitled, send over its content // to restore dirty state. Get that from the text model directly - let content: string | undefined = undefined; + let dirtyContent: string | undefined = undefined; if (model?.isDirty()) { - content = model.textEditorModel.getValue(); + dirtyContent = model.textEditorModel.getValue(); } // Add as dragged editor - draggedEditors.push({ resource: file.resource.toString(), content, options, encoding, mode }); + draggedEditors.push({ resource: file.resource.toString(), dirtyContent, options, encoding, mode }); }); if (draggedEditors.length) { @@ -675,6 +666,11 @@ export class CompositeDragAndDropObserver extends Disposable { disposableStore.add(addDisposableListener(element, EventType.DRAG_START, e => { const { id, type } = draggedItemProvider(); this.writeDragData(id, type); + + if (e.dataTransfer) { + e.dataTransfer.setDragImage(element, 0, 0); + } + this._onDragStart.fire({ eventData: e, dragAndDropData: this.readDragData(type)! }); })); disposableStore.add(new DragAndDropObserver(element, { @@ -748,3 +744,11 @@ export class CompositeDragAndDropObserver extends Disposable { return this._register(disposableStore); } } + +export function toggleDropEffect(dataTransfer: DataTransfer | null, dropEffect: string, shouldHaveIt: boolean) { + if (!dataTransfer) { + return; + } + + dataTransfer.dropEffect = shouldHaveIt ? dropEffect : 'none'; +} diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 6c79377f286..328f6324128 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { dirname, isEqual, basenameOrAuthority } from 'vs/base/common/resources'; +import { dirname, isEqual, basenameOrAuthority, extUri } from 'vs/base/common/resources'; import { IconLabel, IIconLabelValueOptions, IIconLabelCreationOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -24,7 +24,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { withNullAsUndefined } from 'vs/base/common/types'; export interface IResourceLabelProps { - resource?: URI | { master?: URI, detail?: URI }; + resource?: URI | { primary?: URI, secondary?: URI }; name?: string | string[]; description?: string; } @@ -38,7 +38,7 @@ function toResource(props: IResourceLabelProps | undefined): URI | undefined { return props.resource; } - return props.resource.master; + return props.resource.primary; } export interface IResourceLabelOptions extends IIconLabelValueOptions { @@ -307,7 +307,7 @@ class ResourceLabelWidget extends IconLabel { return; // only update if resource exists } - if (model.uri.toString() === resource.toString()) { + if (extUri.isEqual(model.uri, resource)) { if (this.lastKnownDetectedModeId !== model.getModeId()) { this.render(true); // update if the language id of the model has changed from our last known state } @@ -379,9 +379,9 @@ class ResourceLabelWidget extends IconLabel { setResource(label: IResourceLabelProps, options: IResourceLabelOptions = Object.create(null)): void { const resource = toResource(label); - const isMasterDetail = label?.resource && !URI.isUri(label.resource); + const isSideBySideEditor = label?.resource && !URI.isUri(label.resource); - if (!options.forceLabel && !isMasterDetail && resource?.scheme === Schemas.untitled) { + if (!options.forceLabel && !isSideBySideEditor && resource?.scheme === Schemas.untitled) { // Untitled labels are very dynamic because they may change // whenever the content changes (unless a path is associated). // As such we always ask the actual editor for it's name and @@ -390,7 +390,7 @@ class ResourceLabelWidget extends IconLabel { // we assume that the client does not want to display them // and as such do not override. // - // We do not touch the label if it represents a master-detail + // We do not touch the label if it represents a primary-secondary // because in that case we expect it to carry a proper label // and description. const untitledModel = this.textFileService.untitled.get(resource); @@ -505,6 +505,7 @@ class ResourceLabelWidget extends IconLabel { italic: this.options?.italic, strikethrough: this.options?.strikethrough, matches: this.options?.matches, + descriptionMatches: this.options?.descriptionMatches, extraClasses: [], separator: this.options?.separator, domId: this.options?.domId diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index ac32addcfee..2385e99eac7 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -43,8 +43,10 @@ import { WINDOW_ACTIVE_BORDER, WINDOW_INACTIVE_BORDER } from 'vs/workbench/commo import { LineNumbersType } from 'vs/editor/common/config/editorOptions'; import { ActivitybarPart } from 'vs/workbench/browser/parts/activitybar/activitybarPart'; import { URI } from 'vs/base/common/uri'; -import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; +import { IViewDescriptorService, ViewContainerLocation, IViewsService } from 'vs/workbench/common/views'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; +import { mark } from 'vs/base/common/performance'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; export enum Settings { ACTIVITYBAR_VISIBLE = 'workbench.activityBar.visible', @@ -54,7 +56,6 @@ export enum Settings { PANEL_POSITION = 'workbench.panel.defaultLocation', ZEN_MODE_RESTORE = 'zenMode.restore', - WORKSPACE_FIRST_OPEN = 'workbench.workspaceFirstOpen' } enum Storage { @@ -104,7 +105,7 @@ interface SideBarActivityState { export abstract class Layout extends Disposable implements IWorkbenchLayoutService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; //#region Events @@ -131,12 +132,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi //#endregion + readonly container: HTMLElement = document.createElement('div'); + private _dimension!: IDimension; get dimension(): IDimension { return this._dimension; } - private _container: HTMLElement = document.createElement('div'); - get container(): HTMLElement { return this._container; } - get offset() { return { top: (() => { @@ -150,7 +150,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi }; } - private parts: Map = new Map(); + private readonly parts = new Map(); private workbenchGrid!: SerializableGrid; @@ -164,6 +164,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private statusBarPartView!: ISerializableView; private environmentService!: IWorkbenchEnvironmentService; + private extensionService!: IExtensionService; private configurationService!: IConfigurationService; private lifecycleService!: ILifecycleService; private storageService!: IStorageService; @@ -174,6 +175,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private titleService!: ITitleService; private viewletService!: IViewletService; private viewDescriptorService!: IViewDescriptorService; + private viewsService!: IViewsService; private contextService!: IWorkspaceContextService; private backupFileService!: IBackupFileService; private notificationService!: INotificationService; @@ -223,6 +225,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi hidden: false }, + views: { + defaults: undefined as (string[] | undefined) + }, + zenMode: { active: false, restore: false, @@ -233,8 +239,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi transitionDisposables: new DisposableStore(), setNotificationsFilter: false, editorWidgetSet: new Set() - }, - + } }; constructor( @@ -254,6 +259,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.storageService = accessor.get(IStorageService); this.backupFileService = accessor.get(IBackupFileService); this.themeService = accessor.get(IThemeService); + this.extensionService = accessor.get(IExtensionService); // Parts this.editorService = accessor.get(IEditorService); @@ -261,6 +267,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.panelService = accessor.get(IPanelService); this.viewletService = accessor.get(IViewletService); this.viewDescriptorService = accessor.get(IViewDescriptorService); + this.viewsService = accessor.get(IViewsService); this.titleService = accessor.get(ITitleService); this.notificationService = accessor.get(INotificationService); this.activityBarService = accessor.get(IActivityBarService); @@ -476,6 +483,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } private initLayoutState(lifecycleService: ILifecycleService, fileService: IFileService): void { + + // Default Layout this.applyDefaultLayout(this.environmentService, this.storageService); // Fullscreen @@ -551,7 +560,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Window border this.updateWindowBorder(true); - } private applyDefaultLayout(environmentService: IWorkbenchEnvironmentService, storageService: IStorageService) { @@ -560,11 +568,19 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return; } - const firstOpen = storageService.getBoolean(Settings.WORKSPACE_FIRST_OPEN, StorageScope.WORKSPACE); - if (!firstOpen) { + if (!storageService.isNew(StorageScope.WORKSPACE)) { return; } + const { views } = defaultLayout; + if (views?.length) { + this.state.views.defaults = views.map(v => v.id); + + return; + } + + // TODO@eamodio Everything below here is deprecated and will be removed once Codespaces migrates + const { sidebar } = defaultLayout; if (sidebar) { if (sidebar.visible !== undefined) { @@ -773,7 +789,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private getInitialFilesToOpen(): { filesToOpenOrCreate?: IPath[], filesToDiff?: IPath[] } | undefined { const defaultLayout = this.environmentService.options?.defaultLayout; - if (defaultLayout?.editors?.length && this.storageService.getBoolean(Settings.WORKSPACE_FIRST_OPEN, StorageScope.WORKSPACE)) { + if (defaultLayout?.editors?.length && this.storageService.isNew(StorageScope.WORKSPACE)) { this._openedDefaultEditors = true; return { @@ -783,7 +799,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi if ('path' in f && 'scheme' in f) { return { fileUri: URI.file((f as any).path).with({ scheme: (f as any).scheme }) }; } - return { fileUri: URI.revive(f.uri), openOnlyIfExists: f.openOnlyIfExists }; + return { fileUri: URI.revive(f.uri), openOnlyIfExists: f.openOnlyIfExists, overrideId: f.openWith }; }) }; } @@ -799,6 +815,143 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return undefined; } + protected async restoreWorkbenchLayout(): Promise { + const restorePromises: Promise[] = []; + + // Restore editors + restorePromises.push((async () => { + mark('willRestoreEditors'); + + // first ensure the editor part is restored + await this.editorGroupService.whenRestored; + + // then see for editors to open as instructed + let editors: IResourceEditorInputType[]; + if (Array.isArray(this.state.editor.editorsToOpen)) { + editors = this.state.editor.editorsToOpen; + } else { + editors = await this.state.editor.editorsToOpen; + } + + if (editors.length) { + await this.editorService.openEditors(editors); + } + + mark('didRestoreEditors'); + })()); + + // Restore default views + const restoreDefaultViewsPromise = (async () => { + if (this.state.views.defaults?.length) { + mark('willOpenDefaultViews'); + + const defaultViews = [...this.state.views.defaults]; + + let locationsRestored: boolean[] = []; + + const tryOpenView = async (viewId: string, index: number) => { + const location = this.viewDescriptorService.getViewLocationById(viewId); + if (location) { + + // If the view is in the same location that has already been restored, remove it and continue + if (locationsRestored[location]) { + defaultViews.splice(index, 1); + + return; + } + + const view = await this.viewsService.openView(viewId); + if (view) { + locationsRestored[location] = true; + defaultViews.splice(index, 1); + } + } + }; + + let i = -1; + for (const viewId of defaultViews) { + await tryOpenView(viewId, ++i); + } + + // If we still have views left over, wait until all extensions have been registered and try again + if (defaultViews.length) { + await this.extensionService.whenInstalledExtensionsRegistered(); + + let i = -1; + for (const viewId of defaultViews) { + await tryOpenView(viewId, ++i); + } + } + + // If we opened a view in the sidebar, stop any restore there + if (locationsRestored[ViewContainerLocation.Sidebar]) { + this.state.sideBar.viewletToRestore = undefined; + } + + // If we opened a view in the panel, stop any restore there + if (locationsRestored[ViewContainerLocation.Panel]) { + this.state.panel.panelToRestore = undefined; + } + + mark('didOpenDefaultViews'); + } + })(); + restorePromises.push(restoreDefaultViewsPromise); + + // Restore Sidebar + restorePromises.push((async () => { + + // Restoring views could mean that sidebar already + // restored, as such we need to test again + await restoreDefaultViewsPromise; + if (!this.state.sideBar.viewletToRestore) { + return; + } + + mark('willRestoreViewlet'); + + const viewlet = await this.viewletService.openViewlet(this.state.sideBar.viewletToRestore); + if (!viewlet) { + await this.viewletService.openViewlet(this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.Sidebar)?.id); // fallback to default viewlet as needed + } + + mark('didRestoreViewlet'); + })()); + + // Restore Panel + restorePromises.push((async () => { + + // Restoring views could mean that panel already + // restored, as such we need to test again + await restoreDefaultViewsPromise; + if (!this.state.panel.panelToRestore) { + return; + } + + mark('willRestorePanel'); + + const panel = await this.panelService.openPanel(this.state.panel.panelToRestore!); + if (!panel) { + await this.panelService.openPanel(Registry.as(PanelExtensions.Panels).getDefaultPanelId()); // fallback to default panel as needed + } + + mark('didRestorePanel'); + })()); + + // Restore Zen Mode + if (this.state.zenMode.restore) { + this.toggleZenMode(false, true); + } + + // Restore Editor Center Mode + if (this.state.editor.restoreCentered) { + this.centerEditorLayout(true, true); + } + + // Await restore to be done + await Promise.all(restorePromises); + } + private updatePanelPosition() { const defaultPanelPosition = this.configurationService.getValue(Settings.PANEL_POSITION); const panelPosition = this.storageService.get(Storage.PANEL_POSITION, StorageScope.WORKSPACE, defaultPanelPosition); @@ -1394,9 +1547,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } // If panel part becomes hidden, also hide the current active panel if any + let focusEditor = false; if (hidden && this.panelService.getActivePanel()) { this.panelService.hideActivePanel(); - this.editorGroupService.activeGroup.focus(); // Pass focus to editor group if panel part is now hidden + focusEditor = true; } // If panel part becomes visible, show last active panel or default panel @@ -1427,6 +1581,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi if (hidden && this.state.editor.hidden) { this.setEditorHidden(false, true); } + + if (focusEditor) { + this.editorGroupService.activeGroup.focus(); // Pass focus to editor group if panel part is now hidden + } } toggleMaximizedPanel(): void { @@ -1446,7 +1604,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } else { this.setEditorHidden(false); this.workbenchGrid.resizeView(this.panelPartView, { width: this.state.panel.position === Position.BOTTOM ? size.width : this.state.panel.lastNonMaximizedWidth, height: this.state.panel.position === Position.BOTTOM ? this.state.panel.lastNonMaximizedHeight : size.height }); - this.editorGroupService.activeGroup.focus(); } } @@ -1600,11 +1757,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const workbenchDimensions = this.getClientArea(); const width = this.storageService.getNumber(Storage.GRID_WIDTH, StorageScope.GLOBAL, workbenchDimensions.width); const height = this.storageService.getNumber(Storage.GRID_HEIGHT, StorageScope.GLOBAL, workbenchDimensions.height); - // At some point, we will not fall back to old keys from legacy layout, but for now, let's migrate the keys - const sideBarSize = this.storageService.getNumber(Storage.SIDEBAR_SIZE, StorageScope.GLOBAL, this.storageService.getNumber('workbench.sidebar.width', StorageScope.GLOBAL, Math.min(workbenchDimensions.width / 4, 300))); + const sideBarSize = this.storageService.getNumber(Storage.SIDEBAR_SIZE, StorageScope.GLOBAL, Math.min(workbenchDimensions.width / 4, 300)); const panelDimension = positionFromString(this.storageService.get(Storage.PANEL_DIMENSION, StorageScope.GLOBAL, 'bottom')); const fallbackPanelSize = this.state.panel.position === Position.BOTTOM ? workbenchDimensions.height / 3 : workbenchDimensions.width / 4; - const panelSize = panelDimension === this.state.panel.position ? this.storageService.getNumber(Storage.PANEL_SIZE, StorageScope.GLOBAL, this.storageService.getNumber(this.state.panel.position === Position.BOTTOM ? 'workbench.panel.height' : 'workbench.panel.width', StorageScope.GLOBAL, fallbackPanelSize)) : fallbackPanelSize; + const panelSize = panelDimension === this.state.panel.position ? this.storageService.getNumber(Storage.PANEL_SIZE, StorageScope.GLOBAL, fallbackPanelSize) : fallbackPanelSize; const titleBarHeight = this.titleBarPartView.minimumHeight; const statusBarHeight = this.statusBarPartView.minimumHeight; diff --git a/src/vs/workbench/browser/media/style.css b/src/vs/workbench/browser/media/style.css index 51850da3706..9d2fe752921 100644 --- a/src/vs/workbench/browser/media/style.css +++ b/src/vs/workbench/browser/media/style.css @@ -5,18 +5,19 @@ /* Font Families (with CJK support) */ -.mac { font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif; } -.mac:lang(zh-Hans) { font-family: system-ui, -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", sans-serif; } -.mac:lang(zh-Hant) { font-family: system-ui, -apple-system, BlinkMacSystemFont, "PingFang TC", sans-serif; } -.mac:lang(ja) { font-family: system-ui, -apple-system, BlinkMacSystemFont, "Hiragino Kaku Gothic Pro", sans-serif; } -.mac:lang(ko) { font-family: system-ui, -apple-system, BlinkMacSystemFont, "Nanum Gothic", "Apple SD Gothic Neo", "AppleGothic", sans-serif; } +.mac { font-family: -apple-system, BlinkMacSystemFont, sans-serif; } +.mac:lang(zh-Hans) { font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", sans-serif; } +.mac:lang(zh-Hant) { font-family: -apple-system, BlinkMacSystemFont, "PingFang TC", sans-serif; } +.mac:lang(ja) { font-family: -apple-system, BlinkMacSystemFont, "Hiragino Kaku Gothic Pro", sans-serif; } +.mac:lang(ko) { font-family: -apple-system, BlinkMacSystemFont, "Nanum Gothic", "Apple SD Gothic Neo", "AppleGothic", sans-serif; } -.windows { font-family: system-ui, "Segoe WPC", "Segoe UI", sans-serif; } -.windows:lang(zh-Hans) { font-family: system-ui, "Segoe WPC", "Segoe UI", "Microsoft YaHei", sans-serif; } -.windows:lang(zh-Hant) { font-family: system-ui, "Segoe WPC", "Segoe UI", "Microsoft Jhenghei", sans-serif; } -.windows:lang(ja) { font-family: system-ui, "Segoe WPC", "Segoe UI", "Yu Gothic UI", "Meiryo UI", sans-serif; } -.windows:lang(ko) { font-family: system-ui, "Segoe WPC", "Segoe UI", "Malgun Gothic", "Dotom", 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; } @@ -262,3 +263,7 @@ body.web { .monaco-workbench .monaco-list:focus { outline: 0 !important; /* tree indicates focus not via outline but through the focused item */ } + +.monaco-workbench .codicon[class*='codicon-'] { + font-size: 16px; /* sets font-size for codicons in workbench https://github.com/microsoft/vscode/issues/98495 */ +} diff --git a/src/vs/workbench/browser/panecomposite.ts b/src/vs/workbench/browser/panecomposite.ts index 34de2b1a02c..85f520495ce 100644 --- a/src/vs/workbench/browser/panecomposite.ts +++ b/src/vs/workbench/browser/panecomposite.ts @@ -15,30 +15,28 @@ import { Composite } from 'vs/workbench/browser/composite'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { ViewPaneContainer } from './parts/views/viewPaneContainer'; import { IPaneComposite } from 'vs/workbench/common/panecomposite'; -import { IAction, IActionViewItem } from 'vs/base/common/actions'; +import { IAction, IActionViewItem, Separator } from 'vs/base/common/actions'; +import { ViewContainerMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions'; +import { MenuId } from 'vs/platform/actions/common/actions'; export class PaneComposite extends Composite implements IPaneComposite { + private menuActions: ViewContainerMenuActions; + constructor( id: string, protected readonly viewPaneContainer: ViewPaneContainer, - @ITelemetryService - telemetryService: ITelemetryService, - @IStorageService - protected storageService: IStorageService, - @IInstantiationService - protected instantiationService: IInstantiationService, - @IThemeService - themeService: IThemeService, - @IContextMenuService - protected contextMenuService: IContextMenuService, - @IExtensionService - protected extensionService: IExtensionService, - @IWorkspaceContextService - protected contextService: IWorkspaceContextService + @ITelemetryService telemetryService: ITelemetryService, + @IStorageService protected storageService: IStorageService, + @IInstantiationService protected instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IContextMenuService protected contextMenuService: IContextMenuService, + @IExtensionService protected extensionService: IExtensionService, + @IWorkspaceContextService protected contextService: IWorkspaceContextService ) { super(id, telemetryService, themeService, storageService); + this.menuActions = this._register(this.instantiationService.createInstance(ViewContainerMenuActions, this.getId(), MenuId.ViewContainerTitleContext)); this._register(this.viewPaneContainer.onTitleAreaUpdate(() => this.updateTitleArea())); } @@ -68,7 +66,15 @@ export class PaneComposite extends Composite implements IPaneComposite { } getContextMenuActions(): ReadonlyArray { - return this.viewPaneContainer.getContextMenuActions(); + const result = []; + result.push(...this.menuActions.getContextMenuActions()); + + if (result.length) { + result.push(new Separator()); + } + + result.push(...this.viewPaneContainer.getContextMenuActions()); + return result; } getActions(): ReadonlyArray { diff --git a/src/vs/workbench/browser/panel.ts b/src/vs/workbench/browser/panel.ts index bc41a446d73..b2b2a18b46f 100644 --- a/src/vs/workbench/browser/panel.ts +++ b/src/vs/workbench/browser/panel.ts @@ -5,26 +5,24 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IPanel } from 'vs/workbench/common/panel'; -import { Composite, CompositeDescriptor, CompositeRegistry } from 'vs/workbench/browser/composite'; +import { CompositeDescriptor, CompositeRegistry } from 'vs/workbench/browser/composite'; import { IConstructorSignature0, BrandedService } from 'vs/platform/instantiation/common/instantiation'; import { assertIsDefined } from 'vs/base/common/types'; import { PaneComposite } from 'vs/workbench/browser/panecomposite'; -export abstract class Panel extends Composite implements IPanel { } - -export abstract class PaneCompositePanel extends PaneComposite implements IPanel { } +export abstract class Panel extends PaneComposite implements IPanel { } /** * A panel descriptor is a leightweight descriptor of a panel in the workbench. */ export class PanelDescriptor extends CompositeDescriptor { - public static create(ctor: { new(...services: Services): Panel }, id: string, name: string, cssClass?: string, order?: number, _commandId?: string): PanelDescriptor { - return new PanelDescriptor(ctor as IConstructorSignature0, id, name, cssClass, order, _commandId); + static create(ctor: { new(...services: Services): Panel }, id: string, name: string, cssClass?: string, order?: number, requestedIndex?: number, _commandId?: string): PanelDescriptor { + return new PanelDescriptor(ctor as IConstructorSignature0, id, name, cssClass, order, requestedIndex, _commandId); } - private constructor(ctor: IConstructorSignature0, id: string, name: string, cssClass?: string, order?: number, _commandId?: string) { - super(ctor, id, name, cssClass, order, _commandId); + private constructor(ctor: IConstructorSignature0, id: string, name: string, cssClass?: string, order?: number, requestedIndex?: number, _commandId?: string) { + super(ctor, id, name, cssClass, order, requestedIndex, _commandId); } } diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index 4b137bea1af..ff18883e95e 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -8,10 +8,10 @@ import * as nls from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch'; -import { Action, IAction } from 'vs/base/common/actions'; +import { Action, IAction, Separator, SubmenuAction } from 'vs/base/common/actions'; import { KeyCode } from 'vs/base/common/keyCodes'; import { dispose } from 'vs/base/common/lifecycle'; -import { SyncActionDescriptor, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -26,8 +26,14 @@ import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/bro import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { ICommandService } from 'vs/platform/commands/common/commands'; import { Codicon } from 'vs/base/common/codicons'; +import { isMacintosh } from 'vs/base/common/platform'; +import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; +import { AuthenticationSession } from 'vs/editor/common/modes'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class ViewContainerActivityAction extends ActivityAction { @@ -36,6 +42,7 @@ export class ViewContainerActivityAction extends ActivityAction { private readonly viewletService: IViewletService; private readonly layoutService: IWorkbenchLayoutService; private readonly telemetryService: ITelemetryService; + private readonly configurationService: IConfigurationService; private lastRun: number; @@ -43,7 +50,8 @@ export class ViewContainerActivityAction extends ActivityAction { activity: IActivity, @IViewletService viewletService: IViewletService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, - @ITelemetryService telemetryService: ITelemetryService + @ITelemetryService telemetryService: ITelemetryService, + @IConfigurationService configurationService: IConfigurationService ) { super(activity); @@ -51,6 +59,7 @@ export class ViewContainerActivityAction extends ActivityAction { this.viewletService = viewletService; this.layoutService = layoutService; this.telemetryService = telemetryService; + this.configurationService = configurationService; } updateActivity(activity: IActivity): void { @@ -71,11 +80,22 @@ export class ViewContainerActivityAction extends ActivityAction { const sideBarVisible = this.layoutService.isVisible(Parts.SIDEBAR_PART); const activeViewlet = this.viewletService.getActiveViewlet(); + const focusBehavior = this.configurationService.getValue('workbench.activityBar.iconClickBehavior'); - // Hide sidebar if selected viewlet already visible if (sideBarVisible && activeViewlet?.getId() === this.activity.id) { - this.logAction('hide'); - this.layoutService.setSideBarHidden(true); + switch (focusBehavior) { + case 'focus': + this.logAction('refocus'); + this.viewletService.openViewlet(this.activity.id, true); + break; + case 'toggle': + default: + // Hide sidebar if selected viewlet already visible + this.logAction('hide'); + this.layoutService.setSideBarHidden(true); + break; + } + return; } @@ -93,6 +113,8 @@ export class ViewContainerActivityAction extends ActivityAction { } } +export const ACCOUNTS_VISIBILITY_PREFERENCE_KEY = 'workbench.activity.showAccounts'; + export class AccountsActionViewItem extends ActivityActionViewItem { constructor( action: ActivityAction, @@ -101,6 +123,9 @@ export class AccountsActionViewItem extends ActivityActionViewItem { @IContextMenuService protected contextMenuService: IContextMenuService, @IMenuService protected menuService: IMenuService, @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IAuthenticationService private readonly authenticationService: IAuthenticationService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, + @IStorageService private readonly storageService: IStorageService ) { super(action, { draggable: false, colors, icon: true }, themeService); } @@ -130,16 +155,82 @@ export class AccountsActionViewItem extends ActivityActionViewItem { })); } - private showContextMenu(): void { + private async getActions(accountsMenu: IMenu) { + const otherCommands = accountsMenu.getActions(); + const providers = this.authenticationService.getProviderIds(); + const allSessions = providers.map(async id => { + const sessions = await this.authenticationService.getSessions(id); + + const groupedSessions: { [label: string]: AuthenticationSession[] } = {}; + sessions.forEach(session => { + if (groupedSessions[session.account.label]) { + groupedSessions[session.account.label].push(session); + } else { + groupedSessions[session.account.label] = [session]; + } + }); + + return { + providerId: id, + sessions: groupedSessions + }; + }); + + const result = await Promise.all(allSessions); + let menus: IAction[] = []; + result.forEach(sessionInfo => { + const providerDisplayName = this.authenticationService.getLabel(sessionInfo.providerId); + Object.keys(sessionInfo.sessions).forEach(accountName => { + const hasEmbedderAccountSession = sessionInfo.sessions[accountName].some(session => session.id === this.environmentService.options?.authenticationSessionId); + const manageExtensionsAction = new Action(`configureSessions${accountName}`, nls.localize('manageTrustedExtensions', "Manage Trusted Extensions"), '', true, _ => { + return this.authenticationService.manageTrustedExtensionsForAccount(sessionInfo.providerId, accountName); + }); + const signOutAction = new Action('signOut', nls.localize('signOut', "Sign Out"), '', true, _ => { + return this.authenticationService.signOutOfAccount(sessionInfo.providerId, accountName); + }); + + const actions = hasEmbedderAccountSession ? [manageExtensionsAction] : [manageExtensionsAction, signOutAction]; + + const menu = new SubmenuAction('activitybar.submenu', `${accountName} (${providerDisplayName})`, actions); + menus.push(menu); + }); + }); + + if (menus.length && otherCommands.length) { + menus.push(new Separator()); + } + + otherCommands.forEach((group, i) => { + const actions = group[1]; + menus = menus.concat(actions); + if (i !== otherCommands.length - 1) { + menus.push(new Separator()); + } + }); + + if (menus.length) { + menus.push(new Separator()); + } + + menus.push(new Action('hide', nls.localize('hide', "Hide"), undefined, true, _ => { + this.storageService.store(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, false, StorageScope.GLOBAL); + return Promise.resolve(); + })); + + return menus; + } + + private async showContextMenu(): Promise { const accountsActions: IAction[] = []; const accountsMenu = this.menuService.createMenu(MenuId.AccountsContext, this.contextKeyService); const actionsDisposable = createAndFillInActionBarActions(accountsMenu, undefined, { primary: [], secondary: accountsActions }); const containerPosition = DOM.getDomNodePagePosition(this.container); const location = { x: containerPosition.left + containerPosition.width / 2, y: containerPosition.top }; + const actions = await this.getActions(accountsMenu); this.contextMenuService.showContextMenu({ getAnchor: () => location, - getActions: () => accountsActions, + getActions: () => actions, onHide: () => { accountsMenu.dispose(); dispose(actionsDisposable); @@ -288,16 +379,33 @@ export class NextSideBarViewAction extends SwitchSideBarViewAction { export class HomeAction extends Action { constructor( - private readonly command: string, + private readonly href: string, name: string, - icon: Codicon, - @ICommandService private readonly commandService: ICommandService + icon: Codicon ) { super('workbench.action.home', name, icon.classNames); } - async run(): Promise { - this.commandService.executeCommand(this.command); + async run(event: MouseEvent): Promise { + let openInNewWindow = false; + if (isMacintosh) { + openInNewWindow = event.metaKey; + } else { + openInNewWindow = event.ctrlKey; + } + + if (openInNewWindow) { + DOM.windowOpenNoOpener(this.href); + } else { + window.location.href = this.href; + } + } +} + +export class HomeActionViewItem extends ActionViewItem { + + constructor(action: IAction) { + super(undefined, action, { icon: true, label: false, useEventAsContext: true }); } } diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 377942abb07..4149cd623bd 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -6,19 +6,19 @@ import 'vs/css!./media/activitybarpart'; import * as nls from 'vs/nls'; import { ActionsOrientation, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { GLOBAL_ACTIVITY_ID, IActivity } from 'vs/workbench/common/activity'; +import { GLOBAL_ACTIVITY_ID, IActivity, ACCOUNTS_ACTIIVTY_ID } from 'vs/workbench/common/activity'; import { Part } from 'vs/workbench/browser/part'; -import { GlobalActivityActionViewItem, ViewContainerActivityAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewContainerActivityAction, AccountsActionViewItem, HomeAction } from 'vs/workbench/browser/parts/activitybar/activitybarActions'; +import { GlobalActivityActionViewItem, ViewContainerActivityAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewContainerActivityAction, AccountsActionViewItem, HomeAction, HomeActionViewItem, ACCOUNTS_VISIBILITY_PREFERENCE_KEY } from 'vs/workbench/browser/parts/activitybar/activitybarActions'; import { IBadge, NumberBadge } from 'vs/workbench/services/activity/common/activity'; -import { IWorkbenchLayoutService, Parts, Position as SideBarPosition } from 'vs/workbench/services/layout/browser/layoutService'; +import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; import { ToggleActivityBarVisibilityAction, ToggleMenuBarAction } from 'vs/workbench/browser/actions/layoutActions'; import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService'; -import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND, ACTIVITY_BAR_INACTIVE_FOREGROUND, ACTIVITY_BAR_ACTIVE_BACKGROUND } from 'vs/workbench/common/theme'; +import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_INACTIVE_FOREGROUND, ACTIVITY_BAR_ACTIVE_BACKGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BORDER } from 'vs/workbench/common/theme'; import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { CompositeBar, ICompositeBarItem, CompositeDragAndDrop } from 'vs/workbench/browser/parts/compositeBar'; -import { Dimension, addClass, removeNode, createCSSRule, asCSSUrl } from 'vs/base/browser/dom'; +import { Dimension, addClass, removeNode, createCSSRule, asCSSUrl, toggleClass, addDisposableListener, EventType } from 'vs/base/browser/dom'; import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { URI, UriComponents } from 'vs/base/common/uri'; @@ -35,12 +35,12 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { getMenuBarVisibility } from 'vs/platform/windows/common/windows'; import { isWeb } from 'vs/base/common/platform'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; -import { getUserDataSyncStore } from 'vs/platform/userDataSync/common/userDataSync'; -import { IProductService } from 'vs/platform/product/common/productService'; import { Before2D } from 'vs/workbench/browser/dnd'; import { Codicon, iconRegistry } from 'vs/base/common/codicons'; -import { Action } from 'vs/base/common/actions'; +import { Action, Separator } from 'vs/base/common/actions'; import { Event } from 'vs/base/common/event'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode } from 'vs/base/common/keyCodes'; interface IPlaceholderViewContainer { id: string; @@ -69,12 +69,13 @@ interface ICachedViewContainer { export class ActivitybarPart extends Part implements IActivityBarService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private static readonly ACTION_HEIGHT = 48; static readonly PINNED_VIEW_CONTAINERS = 'workbench.activity.pinnedViewlets2'; private static readonly PLACEHOLDER_VIEW_CONTAINERS = 'workbench.activity.placeholderViewlets'; - + private static readonly HOME_BAR_VISIBILITY_PREFERENCE = 'workbench.activity.showHomeIndicator'; + private static readonly ACCOUNTS_ACTION_INDEX = 0; //#region IView readonly minimumWidth: number = 48; @@ -93,15 +94,22 @@ export class ActivitybarPart extends Part implements IActivityBarService { private menuBarContainer: HTMLElement | undefined; private compositeBar: CompositeBar; + private compositeBarContainer: HTMLElement | undefined; private globalActivityAction: ActivityAction | undefined; private globalActivityActionBar: ActionBar | undefined; private readonly globalActivity: ICompositeActivity[] = []; + private globalActivitiesContainer: HTMLElement | undefined; + + private accountsActivityAction: ActivityAction | undefined; + + private accountsActivity: ICompositeActivity[] = []; - private readonly cachedViewContainers: ICachedViewContainer[] = []; private readonly compositeActions = new Map(); private readonly viewContainerDisposables = new Map(); + private readonly keyboardNavigationDisposables = new DisposableStore(); + private readonly location = ViewContainerLocation.Sidebar; constructor( @@ -115,15 +123,14 @@ export class ActivitybarPart extends Part implements IActivityBarService { @IContextKeyService private readonly contextKeyService: IContextKeyService, @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, - @IProductService private readonly productService: IProductService + @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService ) { super(Parts.ACTIVITYBAR_PART, { hasTitle: false }, themeService, storageService, layoutService); storageKeysSyncRegistryService.registerStorageKey({ key: ActivitybarPart.PINNED_VIEW_CONTAINERS, version: 1 }); + storageKeysSyncRegistryService.registerStorageKey({ key: ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE, version: 1 }); this.migrateFromOldCachedViewContainersValue(); - this.cachedViewContainers = this.getCachedViewContainers(); for (const cachedViewContainer of this.cachedViewContainers) { if (environmentService.configuration.remoteAuthority // In remote window, hide activity bar entries until registered. || this.shouldBeHidden(cachedViewContainer.id, cachedViewContainer) @@ -137,6 +144,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { this.compositeBar = this._register(this.instantiationService.createInstance(CompositeBar, cachedItems, { icon: true, orientation: ActionsOrientation.VERTICAL, + preventLoopNavigation: true, openComposite: (compositeId: string) => this.viewsService.openViewContainer(compositeId, true), getActivityAction: (compositeId: string) => this.getCompositeActions(compositeId).activityAction, getCompositePinnedAction: (compositeId: string) => this.getCompositeActions(compositeId).pinnedAction, @@ -144,20 +152,49 @@ export class ActivitybarPart extends Part implements IActivityBarService { getContextMenuActions: () => { const menuBarVisibility = getMenuBarVisibility(this.configurationService, this.environmentService); const actions = []; + if (this.homeBarContainer) { + actions.push(new Action( + 'toggleHomeBarAction', + this.homeBarVisibilityPreference ? nls.localize('hideHomeBar', "Hide Home Button") : nls.localize('showHomeBar', "Show Home Button"), + undefined, + true, + async () => { this.homeBarVisibilityPreference = !this.homeBarVisibilityPreference; } + )); + } if (menuBarVisibility === 'compact' || (menuBarVisibility === 'hidden' && isWeb)) { actions.push(this.instantiationService.createInstance(ToggleMenuBarAction, ToggleMenuBarAction.ID, menuBarVisibility === 'compact' ? nls.localize('hideMenu', "Hide Menu") : nls.localize('showMenu', "Show Menu"))); } - actions.push(this.instantiationService.createInstance(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, nls.localize('hideActivitBar', "Hide Activity Bar"))); + const toggleAccountsVisibilityAction = new Action( + 'toggleAccountsVisibility', + nls.localize('accounts', "Accounts"), + undefined, + true, + async () => { this.accountsVisibilityPreference = !this.accountsVisibilityPreference; } + ); + + toggleAccountsVisibilityAction.checked = !!this.accountsActivityAction; + actions.push(toggleAccountsVisibilityAction); + actions.push(new Separator()); + + actions.push(new Action( + ToggleActivityBarVisibilityAction.ID, + nls.localize('hideActivitBar', "Hide Activity Bar"), + undefined, + true, + async () => { this.instantiationService.invokeFunction(accessor => new ToggleActivityBarVisibilityAction().run(accessor)); } + )); + return actions; }, - getContextMenuActionsForComposite: () => [], + getContextMenuActionsForComposite: compositeId => this.getContextMenuActionsForComposite(compositeId), getDefaultCompositeId: () => this.viewDescriptorService.getDefaultViewContainer(this.location)!.id, hidePart: () => this.layoutService.setSideBarHidden(true), dndHandler: new CompositeDragAndDrop(this.viewDescriptorService, ViewContainerLocation.Sidebar, (id: string, focus?: boolean) => this.viewsService.openViewContainer(id, focus), - (from: string, to: string, before?: Before2D) => this.compositeBar.move(from, to, before?.verticallyBefore) + (from: string, to: string, before?: Before2D) => this.compositeBar.move(from, to, before?.verticallyBefore), + () => this.compositeBar.getCompositeBarItems(), ), compositeSize: 52, colors: (theme: IColorTheme) => this.getActivitybarItemColors(theme), @@ -172,6 +209,31 @@ export class ActivitybarPart extends Part implements IActivityBarService { this.compositeBar.focus(); } + private getContextMenuActionsForComposite(compositeId: string): Action[] { + const viewContainer = this.viewDescriptorService.getViewContainerById(compositeId)!; + + const actions = []; + const defaultLocation = this.viewDescriptorService.getDefaultViewContainerLocation(viewContainer)!; + if (defaultLocation !== this.viewDescriptorService.getViewContainerLocation(viewContainer)) { + actions.push(new Action('resetLocationAction', nls.localize('resetLocation', "Reset Location"), undefined, true, async () => { + this.viewDescriptorService.moveViewContainerToLocation(viewContainer, defaultLocation); + })); + } else { + const viewContainerModel = this.viewDescriptorService.getViewContainerModel(viewContainer); + if (viewContainerModel.allViewDescriptors.length === 1) { + const viewToReset = viewContainerModel.allViewDescriptors[0]; + const defaultContainer = this.viewDescriptorService.getDefaultContainerById(viewToReset.id)!; + if (defaultContainer !== viewContainer) { + actions.push(new Action('resetLocationAction', nls.localize('resetLocation', "Reset Location"), undefined, true, async () => { + this.viewDescriptorService.moveViewsToContainer([viewToReset], defaultContainer); + })); + } + } + } + + return actions; + } + private registerListeners(): void { // View Container Changes @@ -226,6 +288,12 @@ export class ActivitybarPart extends Part implements IActivityBarService { } } + private onDidChangeHomeBarVisibility(): void { + if (this.homeBarContainer) { + this.homeBarContainer.style.display = this.homeBarVisibilityPreference ? '' : 'none'; + } + } + private onDidRegisterExtensions(): void { this.removeNotExistingComposites(); this.saveCachedViewContainers(); @@ -253,45 +321,49 @@ export class ActivitybarPart extends Part implements IActivityBarService { } if (viewContainerOrActionId === GLOBAL_ACTIVITY_ID) { - return this.showGlobalActivity(badge, clazz, priority); + return this.showGlobalActivity(this.globalActivity, this.globalActivityAction, badge, clazz, priority); + } + + if (viewContainerOrActionId === ACCOUNTS_ACTIIVTY_ID) { + return this.showGlobalActivity(this.accountsActivity, this.accountsActivityAction, badge, clazz, priority); } return Disposable.None; } - private showGlobalActivity(badge: IBadge, clazz?: string, priority?: number): IDisposable { + private showGlobalActivity(activityCache: ICompositeActivity[], activityAction: ActivityAction | undefined, badge: IBadge, clazz?: string, priority?: number): IDisposable { if (typeof priority !== 'number') { priority = 0; } const activity: ICompositeActivity = { badge, clazz, priority }; - for (let i = 0; i <= this.globalActivity.length; i++) { - if (i === this.globalActivity.length) { - this.globalActivity.push(activity); + for (let i = 0; i <= activityCache.length; i++) { + if (i === activityCache.length) { + activityCache.push(activity); break; - } else if (this.globalActivity[i].priority <= priority) { - this.globalActivity.splice(i, 0, activity); + } else if (activityCache[i].priority <= priority) { + activityCache.splice(i, 0, activity); break; } } - this.updateGlobalActivity(); + this.updateGlobalActivity(activityCache, activityAction); - return toDisposable(() => this.removeGlobalActivity(activity)); + return toDisposable(() => this.removeGlobalActivity(activityCache, activityAction, activity)); } - private removeGlobalActivity(activity: ICompositeActivity): void { - const index = this.globalActivity.indexOf(activity); + private removeGlobalActivity(activityCache: ICompositeActivity[], activityAction: ActivityAction | undefined, activity: ICompositeActivity): void { + const index = activityCache.indexOf(activity); if (index !== -1) { - this.globalActivity.splice(index, 1); - this.updateGlobalActivity(); + activityCache.splice(index, 1); + this.updateGlobalActivity(activityCache, activityAction); } } - private updateGlobalActivity(): void { - const globalActivityAction = assertIsDefined(this.globalActivityAction); - if (this.globalActivity.length) { - const [{ badge, clazz, priority }] = this.globalActivity; - if (badge instanceof NumberBadge && this.globalActivity.length > 1) { + private updateGlobalActivity(activityCache: ICompositeActivity[], activityAction: ActivityAction | undefined): void { + const globalActivityAction = assertIsDefined(activityAction); + if (activityCache.length) { + const [{ badge, clazz, priority }] = activityCache; + if (badge instanceof NumberBadge && activityCache.length > 1) { const cumulativeNumberBadge = this.getCumulativeNumberBadge(priority); globalActivityAction.setBadge(cumulativeNumberBadge); } else { @@ -320,10 +392,13 @@ export class ActivitybarPart extends Part implements IActivityBarService { private uninstallMenubar() { if (this.menuBar) { this.menuBar.dispose(); + this.menuBar = undefined; } if (this.menuBarContainer) { removeNode(this.menuBarContainer); + this.menuBarContainer = undefined; + this.registerKeyboardNavigationListeners(); } } @@ -337,6 +412,8 @@ export class ActivitybarPart extends Part implements IActivityBarService { // Menubar: install a custom menu bar depending on configuration this.menuBar = this._register(this.instantiationService.createInstance(CustomMenubarControl)); this.menuBar.create(this.menuBarContainer); + + this.registerKeyboardNavigationListeners(); } createContentArea(parent: HTMLElement): HTMLElement { @@ -354,7 +431,9 @@ export class ActivitybarPart extends Part implements IActivityBarService { console.warn(`Unknown home indicator icon ${homeIndicator.icon}`); codicon = Codicon.code; } - this.createHomeBar(homeIndicator.command, homeIndicator.title, codicon); + + this.createHomeBar(homeIndicator.href, homeIndicator.title, codicon); + this.onDidChangeHomeBarVisibility(); } // Install menubar if compact @@ -363,19 +442,88 @@ export class ActivitybarPart extends Part implements IActivityBarService { } // View Containers action bar - this.compositeBar.create(this.content); + this.compositeBarContainer = this.compositeBar.create(this.content); // Global action bar - const globalActivities = document.createElement('div'); - addClass(globalActivities, 'global-activity'); - this.content.appendChild(globalActivities); + this.globalActivitiesContainer = document.createElement('div'); + addClass(this.globalActivitiesContainer, 'global-activity'); + this.content.appendChild(this.globalActivitiesContainer); - this.createGlobalActivityActionBar(globalActivities); + this.createGlobalActivityActionBar(this.globalActivitiesContainer); + + this.registerKeyboardNavigationListeners(); return this.content; } - private createHomeBar(command: string, title: string, icon: Codicon): void { + private registerKeyboardNavigationListeners(): void { + this.keyboardNavigationDisposables.clear(); + + // Down arrow on home indicator + if (this.homeBarContainer) { + this.keyboardNavigationDisposables.add(addDisposableListener(this.homeBarContainer, EventType.KEY_DOWN, e => { + const kbEvent = new StandardKeyboardEvent(e); + if (kbEvent.equals(KeyCode.DownArrow)) { + if (this.menuBar) { + this.menuBar.toggleFocus(); + } else if (this.compositeBar) { + this.compositeBar.focus(); + } + } + })); + } + + // Up/Down arrow on compact menu + if (this.menuBarContainer) { + this.keyboardNavigationDisposables.add(addDisposableListener(this.menuBarContainer, EventType.KEY_DOWN, e => { + const kbEvent = new StandardKeyboardEvent(e); + if (kbEvent.equals(KeyCode.DownArrow)) { + if (this.compositeBar) { + this.compositeBar.focus(); + } + } else if (kbEvent.equals(KeyCode.UpArrow)) { + if (this.homeBar) { + this.homeBar.focus(); + } + } + })); + } + + // Up/Down on Activity Icons + if (this.compositeBarContainer) { + this.keyboardNavigationDisposables.add(addDisposableListener(this.compositeBarContainer, EventType.KEY_DOWN, e => { + const kbEvent = new StandardKeyboardEvent(e); + if (kbEvent.equals(KeyCode.DownArrow)) { + if (this.globalActivityActionBar) { + this.globalActivityActionBar.focus(true); + } + } else if (kbEvent.equals(KeyCode.UpArrow)) { + if (this.menuBar) { + this.menuBar.toggleFocus(); + } else if (this.homeBar) { + this.homeBar.focus(); + } + } + })); + } + + // Up arrow on global icons + if (this.globalActivitiesContainer) { + this.keyboardNavigationDisposables.add(addDisposableListener(this.globalActivitiesContainer, EventType.KEY_DOWN, e => { + const kbEvent = new StandardKeyboardEvent(e); + if (kbEvent.equals(KeyCode.UpArrow)) { + if (this.compositeBar) { + this.compositeBar.focus(this.getVisibleViewContainerIds().length - 1); + } + } + })); + } + + + + } + + private createHomeBar(href: string, title: string, icon: Codicon): void { this.homeBarContainer = document.createElement('div'); this.homeBarContainer.setAttribute('aria-label', nls.localize('homeIndicator', "Home")); this.homeBarContainer.setAttribute('role', 'toolbar'); @@ -383,14 +531,17 @@ export class ActivitybarPart extends Part implements IActivityBarService { this.homeBar = this._register(new ActionBar(this.homeBarContainer, { orientation: ActionsOrientation.VERTICAL, - animated: false + animated: false, + ariaLabel: nls.localize('home', "Home"), + actionViewItemProvider: action => new HomeActionViewItem(action), + allowContextMenu: true, + preventLoopNavigation: true })); const homeBarIconBadge = document.createElement('div'); addClass(homeBarIconBadge, 'home-bar-icon-badge'); this.homeBarContainer.appendChild(homeBarIconBadge); - - this.homeBar.push(this._register(this.instantiationService.createInstance(HomeAction, command, title, icon)), { icon: true, label: false }); + this.homeBar.push(this._register(this.instantiationService.createInstance(HomeAction, href, title, icon))); const content = assertIsDefined(this.content); content.prepend(this.homeBarContainer); @@ -404,14 +555,8 @@ export class ActivitybarPart extends Part implements IActivityBarService { container.style.backgroundColor = background; const borderColor = this.getColor(ACTIVITY_BAR_BORDER) || this.getColor(contrastBorder) || ''; - const isPositionLeft = this.layoutService.getSideBarPosition() === SideBarPosition.LEFT; - container.style.boxSizing = borderColor && isPositionLeft ? 'border-box' : ''; - container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : ''; - container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : ''; - container.style.borderRightColor = isPositionLeft ? borderColor : ''; - container.style.borderLeftWidth = borderColor && !isPositionLeft ? '1px' : ''; - container.style.borderLeftStyle = borderColor && !isPositionLeft ? 'solid' : ''; - container.style.borderLeftColor = !isPositionLeft ? borderColor : ''; + toggleClass(container, 'bordered', !!borderColor); + container.style.borderColor = borderColor ? borderColor : ''; } private getActivitybarItemColors(theme: IColorTheme): ICompositeBarColors { @@ -422,7 +567,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { activeBackground: theme.getColor(ACTIVITY_BAR_ACTIVE_BACKGROUND), badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND), badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND), - dragAndDropBackground: theme.getColor(ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND), + dragAndDropBorder: theme.getColor(ACTIVITY_BAR_DRAG_AND_DROP_BORDER), activeBackgroundColor: undefined, inactiveBackgroundColor: undefined, activeBorderBottomColor: undefined, }; } @@ -442,7 +587,8 @@ export class ActivitybarPart extends Part implements IActivityBarService { }, orientation: ActionsOrientation.VERTICAL, ariaLabel: nls.localize('manage', "Manage"), - animated: false + animated: false, + preventLoopNavigation: true })); this.globalActivityAction = new ActivityAction({ @@ -451,19 +597,37 @@ export class ActivitybarPart extends Part implements IActivityBarService { cssClass: Codicon.settingsGear.classNames }); - if (getUserDataSyncStore(this.productService, this.configurationService)) { - const profileAction = new ActivityAction({ + if (this.accountsVisibilityPreference) { + this.accountsActivityAction = new ActivityAction({ id: 'workbench.actions.accounts', name: nls.localize('accounts', "Accounts"), cssClass: Codicon.account.classNames }); - this.globalActivityActionBar.push(profileAction); + this.globalActivityActionBar.push(this.accountsActivityAction, { index: ActivitybarPart.ACCOUNTS_ACTION_INDEX }); } this.globalActivityActionBar.push(this.globalActivityAction); } + private toggleAccountsActivity() { + if (this.globalActivityActionBar) { + if (this.accountsActivityAction) { + this.globalActivityActionBar.pull(ActivitybarPart.ACCOUNTS_ACTION_INDEX); + this.accountsActivityAction = undefined; + } else { + this.accountsActivityAction = new ActivityAction({ + id: 'workbench.actions.accounts', + name: nls.localize('accounts', "Accounts"), + cssClass: Codicon.account.classNames + }); + this.globalActivityActionBar.push(this.accountsActivityAction, { index: ActivitybarPart.ACCOUNTS_ACTION_INDEX }); + } + } + + this.updateGlobalActivity(this.accountsActivity, this.accountsActivityAction); + } + private getCompositeActions(compositeId: string): { activityAction: ViewContainerActivityAction, pinnedAction: ToggleCompositePinnedAction } { let compositeActions = this.compositeActions.get(compositeId); if (!compositeActions) { @@ -528,7 +692,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { } this.viewContainerDisposables.delete(viewContainer.id); - this.hideComposite(viewContainer.id); + this.removeComposite(viewContainer.id); } private updateActivity(viewContainer: ViewContainer, viewContainerModel: IViewContainerModel): void { @@ -605,6 +769,17 @@ export class ActivitybarPart extends Part implements IActivityBarService { } } + private removeComposite(compositeId: string): void { + this.compositeBar.removeComposite(compositeId); + + const compositeActions = this.compositeActions.get(compositeId); + if (compositeActions) { + compositeActions.activityAction.dispose(); + compositeActions.pinnedAction.dispose(); + this.compositeActions.delete(compositeId); + } + } + getPinnedViewContainerIds(): string[] { const pinnedCompositeIds = this.compositeBar.getPinnedComposites().map(v => v.id); return this.getViewContainers() @@ -654,22 +829,19 @@ export class ActivitybarPart extends Part implements IActivityBarService { if (e.key === ActivitybarPart.PINNED_VIEW_CONTAINERS && e.scope === StorageScope.GLOBAL && this.pinnedViewContainersValue !== this.getStoredPinnedViewContainersValue() /* This checks if current window changed the value or not */) { this._pinnedViewContainersValue = undefined; + this._cachedViewContainers = undefined; + const newCompositeItems: ICompositeBarItem[] = []; const compositeItems = this.compositeBar.getCompositeBarItems(); - const cachedViewContainers = this.getCachedViewContainers(); - for (const cachedViewContainer of cachedViewContainers) { - // Add and update existing items - const existingItem = compositeItems.filter(({ id }) => id === cachedViewContainer.id)[0]; - if (existingItem) { - newCompositeItems.push({ - id: existingItem.id, - name: existingItem.name, - order: existingItem.order, - pinned: cachedViewContainer.pinned, - visible: existingItem.visible - }); - } + for (const cachedViewContainer of this.cachedViewContainers) { + newCompositeItems.push({ + id: cachedViewContainer.id, + name: cachedViewContainer.name, + order: cachedViewContainer.order, + pinned: cachedViewContainer.pinned, + visible: !!compositeItems.find(({ id }) => id === cachedViewContainer.id) + }); } for (let index = 0; index < compositeItems.length; index++) { @@ -681,6 +853,14 @@ export class ActivitybarPart extends Part implements IActivityBarService { this.compositeBar.setCompositeBarItems(newCompositeItems); } + + if (e.key === ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE && e.scope === StorageScope.GLOBAL) { + this.onDidChangeHomeBarVisibility(); + } + + if (e.key === ACCOUNTS_VISIBILITY_PREFERENCE_KEY && e.scope === StorageScope.GLOBAL) { + this.toggleAccountsActivity(); + } } private saveCachedViewContainers(): void { @@ -713,19 +893,21 @@ export class ActivitybarPart extends Part implements IActivityBarService { this.storeCachedViewContainersState(state); } - private getCachedViewContainers(): ICachedViewContainer[] { - const cachedViewContainers: ICachedViewContainer[] = this.getPinnedViewContainers(); - for (const placeholderViewContainer of this.getPlaceholderViewContainers()) { - const cachedViewContainer = cachedViewContainers.filter(cached => cached.id === placeholderViewContainer.id)[0]; - if (cachedViewContainer) { - cachedViewContainer.name = placeholderViewContainer.name; - cachedViewContainer.icon = placeholderViewContainer.iconCSS ? placeholderViewContainer.iconCSS : - placeholderViewContainer.iconUrl ? URI.revive(placeholderViewContainer.iconUrl) : undefined; - cachedViewContainer.views = placeholderViewContainer.views; + private _cachedViewContainers: ICachedViewContainer[] | undefined = undefined; + private get cachedViewContainers(): ICachedViewContainer[] { + if (this._cachedViewContainers === undefined) { + this._cachedViewContainers = this.getPinnedViewContainers(); + for (const placeholderViewContainer of this.getPlaceholderViewContainers()) { + const cachedViewContainer = this._cachedViewContainers.filter(cached => cached.id === placeholderViewContainer.id)[0]; + if (cachedViewContainer) { + cachedViewContainer.name = placeholderViewContainer.name; + cachedViewContainer.icon = placeholderViewContainer.iconCSS ? placeholderViewContainer.iconCSS : + placeholderViewContainer.iconUrl ? URI.revive(placeholderViewContainer.iconUrl) : undefined; + cachedViewContainer.views = placeholderViewContainer.views; + } } } - - return cachedViewContainers; + return this._cachedViewContainers; } private storeCachedViewContainersState(cachedViewContainers: ICachedViewContainer[]): void { @@ -808,6 +990,22 @@ export class ActivitybarPart extends Part implements IActivityBarService { this.storageService.store(ActivitybarPart.PLACEHOLDER_VIEW_CONTAINERS, value, StorageScope.GLOBAL); } + private get homeBarVisibilityPreference(): boolean { + return this.storageService.getBoolean(ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE, StorageScope.GLOBAL, true); + } + + private set homeBarVisibilityPreference(value: boolean) { + this.storageService.store(ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE, value, StorageScope.GLOBAL); + } + + private get accountsVisibilityPreference(): boolean { + return this.storageService.getBoolean(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, StorageScope.GLOBAL, true); + } + + private set accountsVisibilityPreference(value: boolean) { + this.storageService.store(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, value, StorageScope.GLOBAL); + } + private migrateFromOldCachedViewContainersValue(): void { const value = this.storageService.get('workbench.activity.pinnedViewlets', StorageScope.GLOBAL); if (value !== undefined) { diff --git a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css index efb49c8a4ce..39fa9ebde59 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css @@ -20,9 +20,8 @@ width: 48px; height: 2px; display: block; - background-color: var(--insert-border-color); - opacity: 0; - transition-property: opacity; + background-color: transparent; + transition-property: background-color; transition-duration: 0ms; transition-delay: 100ms; } @@ -52,8 +51,9 @@ .monaco-workbench .activitybar > .content > .composite-bar > .monaco-action-bar .action-item.top::before, .monaco-workbench .activitybar > .content > .composite-bar > .monaco-action-bar .action-item.bottom::after, -.monaco-workbench .activitybar > .content.dragged-over > .composite-bar > .monaco-action-bar .action-item:last-of-type::after { - opacity: 1; +.monaco-workbench .activitybar > .content.dragged-over-head > .composite-bar > .monaco-action-bar .action-item:first-of-type::before, +.monaco-workbench .activitybar > .content.dragged-over-tail > .composite-bar > .monaco-action-bar .action-item:last-of-type::after { + background-color: var(--insert-border-color); } .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-label { @@ -101,7 +101,7 @@ } /* Hides active elements in high contrast mode */ -.hc-black .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator { +.monaco-workbench.hc-black .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator { display: none; } @@ -120,8 +120,8 @@ } /* Hides outline on HC as focus is handled by border */ -.hc-black .monaco-workbench .activitybar.left > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before, -.hc-black .monaco-workbench .activitybar.right > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before { +.monaco-workbench.hc-black .activitybar.left > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before, +.monaco-workbench.hc-black .activitybar.right > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before { outline: none; } diff --git a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css index 6b1847fd614..ced2d815834 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css @@ -8,6 +8,28 @@ height: 100%; } +.monaco-workbench .activitybar.bordered::before { + content: ''; + float: left; + position: absolute; + box-sizing: border-box; + height: 100%; + width: 0px; + border-color: inherit; +} + +.monaco-workbench .activitybar.left.bordered::before { + right: 0; + border-right-style: solid; + border-right-width: 1px; +} + +.monaco-workbench .activitybar.right.bordered::before { + left: 0; + border-left-style: solid; + border-left-width: 1px; +} + .monaco-workbench .activitybar > .content { height: 100%; display: flex; diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index 72fa96f1ff5..f029a2c190c 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { Action, IAction } from 'vs/base/common/actions'; +import { Action, IAction, Separator } from 'vs/base/common/actions'; import { illegalArgument } from 'vs/base/common/errors'; import * as arrays from 'vs/base/common/arrays'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IBadge } from 'vs/workbench/services/activity/common/activity'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ActionBar, ActionsOrientation, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { CompositeActionViewItem, CompositeOverflowActivityAction, ICompositeActivity, CompositeOverflowActivityActionViewItem, ActivityAction, ICompositeBar, ICompositeBarColors } from 'vs/workbench/browser/parts/compositeBarActions'; import { Dimension, $, addDisposableListener, EventType, EventHelper, toggleClass, isAncestor } from 'vs/base/browser/dom'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; @@ -22,7 +22,7 @@ import { Emitter } from 'vs/base/common/event'; import { ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views'; import { IPaneComposite } from 'vs/workbench/common/panecomposite'; import { IComposite } from 'vs/workbench/common/composite'; -import { CompositeDragAndDropData, CompositeDragAndDropObserver, IDraggedCompositeData, ICompositeDragAndDrop, Before2D } from 'vs/workbench/browser/dnd'; +import { CompositeDragAndDropData, CompositeDragAndDropObserver, IDraggedCompositeData, ICompositeDragAndDrop, Before2D, toggleDropEffect } from 'vs/workbench/browser/dnd'; export interface ICompositeBarItem { id: string; @@ -39,6 +39,7 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop { private targetContainerLocation: ViewContainerLocation, private openComposite: (id: string, focus?: boolean) => Promise, private moveComposite: (from: string, to: string, before?: Before2D) => void, + private getItems: () => ICompositeBarItem[], ) { } drop(data: CompositeDragAndDropData, targetCompositeId: string | undefined, originalEvent: DragEvent, before?: Before2D): void { @@ -61,11 +62,7 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop { return; } - this.viewDescriptorService.moveViewContainerToLocation(currentContainer, this.targetContainerLocation); - - if (targetCompositeId) { - this.moveComposite(currentContainer.id, targetCompositeId, before); - } + this.viewDescriptorService.moveViewContainerToLocation(currentContainer, this.targetContainerLocation, this.getTargetIndex(targetCompositeId, before)); } } @@ -98,6 +95,16 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop { return this.canDrop(data, targetCompositeId); } + private getTargetIndex(targetId: string | undefined, before2d: Before2D | undefined): number | undefined { + if (!targetId) { + return undefined; + } + + const items = this.getItems(); + const before = this.targetContainerLocation === ViewContainerLocation.Panel ? before2d?.horizontallyBefore : before2d?.verticallyBefore; + return items.filter(o => o.visible).findIndex(o => o.id === targetId) + (before ? 0 : 1); + } + private canDrop(data: CompositeDragAndDropData, targetCompositeId: string | undefined): boolean { const dragData = data.getData(); @@ -139,6 +146,7 @@ export interface ICompositeBarOptions { readonly compositeSize: number; readonly overflowActionSize: number; readonly dndHandler: ICompositeDragAndDrop; + readonly preventLoopNavigation?: boolean; getActivityAction: (compositeId: string) => ActivityAction; getCompositePinnedAction: (compositeId: string) => Action; @@ -218,44 +226,78 @@ export class CompositeBar extends Widget implements ICompositeBar { orientation: this.options.orientation, ariaLabel: nls.localize('activityBarAriaLabel', "Active View Switcher"), animated: false, + preventLoopNavigation: this.options.preventLoopNavigation })); // Contextmenu for composites this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, e => this.showContextMenu(e))); + let insertDropBefore: Before2D | undefined = undefined; // Register a drop target on the whole bar to prevent forbidden feedback this._register(CompositeDragAndDropObserver.INSTANCE.registerTarget(parent, { onDragOver: (e: IDraggedCompositeData) => { - // don't add feedback if this is over the composite bar actions - if (e.eventData.target && isAncestor(e.eventData.target as HTMLElement, actionBarDiv)) { - toggleClass(parent, 'dragged-over', false); + // don't add feedback if this is over the composite bar actions or there are no actions + const visibleItems = this.getVisibleComposites(); + if (!visibleItems.length || (e.eventData.target && isAncestor(e.eventData.target as HTMLElement, actionBarDiv))) { + insertDropBefore = this.updateFromDragging(parent, false, false); return; } - const pinnedItems = this.getPinnedComposites(); - const validDropTarget = this.options.dndHandler.onDragOver(e.dragAndDropData, pinnedItems[pinnedItems.length - 1].id, e.eventData); - toggleClass(parent, 'dragged-over', validDropTarget); + const insertAtFront = this.insertAtFront(actionBarDiv, e.eventData); + const target = insertAtFront ? visibleItems[0] : visibleItems[visibleItems.length - 1]; + const validDropTarget = this.options.dndHandler.onDragOver(e.dragAndDropData, target.id, e.eventData); + toggleDropEffect(e.eventData.dataTransfer, 'move', validDropTarget); + insertDropBefore = this.updateFromDragging(parent, validDropTarget, insertAtFront); }, onDragLeave: (e: IDraggedCompositeData) => { - toggleClass(parent, 'dragged-over', false); + insertDropBefore = this.updateFromDragging(parent, false, false); }, onDragEnd: (e: IDraggedCompositeData) => { - toggleClass(parent, 'dragged-over', false); + insertDropBefore = this.updateFromDragging(parent, false, false); }, onDrop: (e: IDraggedCompositeData) => { - const pinnedItems = this.getPinnedComposites(); - this.options.dndHandler.drop(e.dragAndDropData, pinnedItems[pinnedItems.length - 1].id, e.eventData, { horizontallyBefore: false, verticallyBefore: false }); - toggleClass(parent, 'dragged-over', false); + const visibleItems = this.getVisibleComposites(); + if (visibleItems.length) { + const target = this.insertAtFront(actionBarDiv, e.eventData) ? visibleItems[0] : visibleItems[visibleItems.length - 1]; + this.options.dndHandler.drop(e.dragAndDropData, target.id, e.eventData, insertDropBefore); + } + insertDropBefore = this.updateFromDragging(parent, false, false); } })); return actionBarDiv; } - focus(): void { + private insertAtFront(element: HTMLElement, event: DragEvent): boolean { + const rect = element.getBoundingClientRect(); + const posX = event.clientX; + const posY = event.clientY; + + switch (this.options.orientation) { + case ActionsOrientation.HORIZONTAL: + case ActionsOrientation.HORIZONTAL_REVERSE: + return posX < rect.left; + case ActionsOrientation.VERTICAL: + case ActionsOrientation.VERTICAL_REVERSE: + return posY < rect.top; + } + } + + private updateFromDragging(element: HTMLElement, showFeedback: boolean, front: boolean): Before2D | undefined { + toggleClass(element, 'dragged-over-head', showFeedback && front); + toggleClass(element, 'dragged-over-tail', showFeedback && !front); + + if (!showFeedback) { + return undefined; + } + + return { verticallyBefore: front, horizontallyBefore: front }; + } + + focus(index?: number): void { if (this.compositeSwitcherBar) { - this.compositeSwitcherBar.focus(); + this.compositeSwitcherBar.focus(index); } } @@ -275,9 +317,9 @@ export class CompositeBar extends Widget implements ICompositeBar { this.updateCompositeSwitcher(); } - addComposite({ id, name, order }: { id: string; name: string, order?: number }): void { + addComposite({ id, name, order, requestedIndex }: { id: string; name: string, order?: number, requestedIndex?: number }): void { // Add to the model - if (this.model.add(id, name, order)) { + if (this.model.add(id, name, order, requestedIndex)) { this.computeSizes([this.model.findItem(id)]); this.updateCompositeSwitcher(); } @@ -378,7 +420,7 @@ export class CompositeBar extends Widget implements ICompositeBar { // Case: we closed the last visible composite // Solv: we hide the part - else if (this.visibleComposites.length === 1) { + else if (this.visibleComposites.length === 0) { this.options.hidePart(); } @@ -513,7 +555,7 @@ export class CompositeBar extends Widget implements ICompositeBar { // Pull out composites that overflow or got hidden const compositesToRemove: number[] = []; this.visibleComposites.forEach((compositeId, index) => { - if (compositesToShow.indexOf(compositeId) === -1) { + if (!compositesToShow.includes(compositeId)) { compositesToRemove.push(index); } }); @@ -574,8 +616,8 @@ export class CompositeBar extends Widget implements ICompositeBar { overflowingIds.push(this.model.activeItem.id); } - overflowingIds = overflowingIds.filter(compositeId => this.visibleComposites.indexOf(compositeId) === -1); - return this.model.visibleItems.filter(c => overflowingIds.indexOf(c.id) !== -1).map(item => { return { id: item.id, name: this.getAction(item.id)?.label || item.name }; }); + overflowingIds = overflowingIds.filter(compositeId => !this.visibleComposites.includes(compositeId)); + return this.model.visibleItems.filter(c => overflowingIds.includes(c.id)).map(item => { return { id: item.id, name: this.getAction(item.id)?.label || item.name }; }); } private showContextMenu(e: MouseEvent): void { @@ -587,7 +629,7 @@ export class CompositeBar extends Widget implements ICompositeBar { }); } - private getContextMenuActions(): ReadonlyArray { + private getContextMenuActions(): IAction[] { const actions: IAction[] = this.model.visibleItems .map(({ id, name, activityAction }) => ({ id, @@ -664,6 +706,7 @@ class CompositeBarModel { } this._items = result; } + return hasChanges; } @@ -689,7 +732,7 @@ class CompositeBarModel { }; } - add(id: string, name: string, order: number | undefined): boolean { + add(id: string, name: string, order: number | undefined, requestedIndex: number | undefined): boolean { const item = this.findItem(id); if (item) { let changed = false; @@ -702,10 +745,21 @@ class CompositeBarModel { item.visible = true; changed = true; } + return changed; } else { const item = this.createCompositeBarItem(id, name, order, true, true); - if (isUndefinedOrNull(order)) { + if (!isUndefinedOrNull(requestedIndex)) { + let index = 0; + let rIndex = requestedIndex; + while (rIndex > 0 && index < this.items.length) { + if (this.items[index++].visible) { + rIndex--; + } + } + + this.items.splice(index, 0, item); + } else if (isUndefinedOrNull(order)) { this.items.push(item); } else { let index = 0; @@ -714,6 +768,7 @@ class CompositeBarModel { } this.items.splice(index, 0, item); } + return true; } } diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index 98acbac56f6..9f358b6e25d 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -4,9 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { Action } from 'vs/base/common/actions'; +import { Action, Separator } from 'vs/base/common/actions'; import * as dom from 'vs/base/browser/dom'; -import { BaseActionViewItem, IBaseActionViewItemOptions, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { dispose, toDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -18,9 +17,10 @@ import { DelayedDragHandler } from 'vs/base/browser/dnd'; import { IActivity } from 'vs/workbench/common/activity'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Emitter, Event } from 'vs/base/common/event'; -import { CompositeDragAndDropObserver, ICompositeDragAndDrop, Before2D } from 'vs/workbench/browser/dnd'; +import { CompositeDragAndDropObserver, ICompositeDragAndDrop, Before2D, toggleDropEffect } from 'vs/workbench/browser/dnd'; import { Color } from 'vs/base/common/color'; import { Codicon } from 'vs/base/common/codicons'; +import { IBaseActionViewItemOptions, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; export interface ICompositeActivity { badge: IBadge; @@ -119,7 +119,7 @@ export interface ICompositeBarColors { inactiveForegroundColor?: Color; badgeBackground?: Color; badgeForeground?: Color; - dragAndDropBackground?: Color; + dragAndDropBorder?: Color; } export interface IActivityActionViewItemOptions extends IBaseActionViewItemOptions { @@ -169,16 +169,14 @@ export class ActivityActionViewItem extends BaseActionViewItem { this.label.style.color = foreground ? foreground.toString() : ''; this.label.style.backgroundColor = ''; } - - const dragColor = colors.activeBackgroundColor || colors.activeForegroundColor; - this.container.style.setProperty('--insert-border-color', dragColor ? dragColor.toString() : ''); } else { const foreground = this._action.checked ? colors.activeForegroundColor : colors.inactiveForegroundColor; const borderBottomColor = this._action.checked ? colors.activeBorderBottomColor : null; this.label.style.color = foreground ? foreground.toString() : ''; this.label.style.borderBottomColor = borderBottomColor ? borderBottomColor.toString() : ''; - this.container.style.setProperty('--insert-border-color', colors.activeForegroundColor ? colors.activeForegroundColor.toString() : ''); } + + this.container.style.setProperty('--insert-border-color', colors.dragAndDropBorder ? colors.dragAndDropBorder.toString() : ''); } // Badge @@ -203,7 +201,7 @@ export class ActivityActionViewItem extends BaseActionViewItem { // Make the container tab-able for keyboard navigation this.container.tabIndex = 0; - this.container.setAttribute('role', this.options.icon ? 'button' : 'tab'); + this.container.setAttribute('role', 'tab'); // Try hard to prevent keyboard only focus feedback when using mouse this._register(dom.addDisposableListener(this.container, dom.EventType.MOUSE_DOWN, () => { @@ -524,6 +522,7 @@ export class CompositeActionViewItem extends ActivityActionViewItem { this._register(CompositeDragAndDropObserver.INSTANCE.registerDraggable(this.container, () => { return { type: 'composite', id: this.activity.id }; }, { onDragOver: e => { const isValidMove = e.dragAndDropData.getData().id !== this.activity.id && this.dndHandler.onDragOver(e.dragAndDropData, this.activity.id, e.eventData); + toggleDropEffect(e.eventData.dataTransfer, 'move', isValidMove); insertDropBefore = this.updateFromDragging(container, isValidMove, e.eventData); }, @@ -649,9 +648,11 @@ export class CompositeActionViewItem extends ActivityActionViewItem { if (this.getAction().checked) { dom.addClass(this.container, 'checked'); this.container.setAttribute('aria-label', nls.localize('compositeActive', "{0} active", this.container.title)); + this.container.setAttribute('aria-expanded', 'true'); } else { dom.removeClass(this.container, 'checked'); this.container.setAttribute('aria-label', this.container.title); + this.container.setAttribute('aria-expanded', 'false'); } this.updateStyles(); } diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index 9d647d98524..57adb998ecc 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -11,9 +11,9 @@ import * as strings from 'vs/base/common/strings'; import { Emitter } from 'vs/base/common/event'; import * as errors from 'vs/base/common/errors'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; -import { IActionViewItem, ActionsOrientation, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionsOrientation, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; -import { IAction, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; +import { IAction, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification, IActionViewItem } from 'vs/base/common/actions'; import { Part, IPartOptions } from 'vs/workbench/browser/part'; import { Composite, CompositeRegistry } from 'vs/workbench/browser/composite'; import { IComposite } from 'vs/workbench/common/composite'; @@ -316,11 +316,11 @@ export abstract class CompositePart extends Part { toolBar.setAriaLabel(nls.localize('ariaCompositeToolbarLabel', "{0} actions", compositeTitle)); } - private collectCompositeActions(composite: Composite): () => void { + private collectCompositeActions(composite?: Composite): () => void { // From Composite - const primaryActions: IAction[] = composite.getActions().slice(0); - const secondaryActions: IAction[] = composite.getSecondaryActions().slice(0); + const primaryActions: IAction[] = composite?.getActions().slice(0) || []; + const secondaryActions: IAction[] = composite?.getSecondaryActions().slice(0) || []; // From Part primaryActions.push(...this.getActions()); @@ -331,7 +331,7 @@ export abstract class CompositePart extends Part { toolBar.context = this.actionsContextProvider(); // Return fn to set into toolbar - return toolBar.setActions(prepareActions(primaryActions), prepareActions(secondaryActions)); + return () => toolBar.setActions(prepareActions(primaryActions), prepareActions(secondaryActions)); } protected getActiveComposite(): IComposite | undefined { @@ -368,7 +368,7 @@ export abstract class CompositePart extends Part { // Empty Actions if (this.toolBar) { - this.toolBar.setActions([])(); + this.collectCompositeActions()(); } this.onDidCompositeClose.fire(composite); @@ -392,9 +392,12 @@ export abstract class CompositePart extends Part { actionViewItemProvider: action => this.actionViewItemProvider(action), orientation: ActionsOrientation.HORIZONTAL, getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id), - anchorAlignmentProvider: () => this.getTitleAreaDropDownAnchorAlignment() + anchorAlignmentProvider: () => this.getTitleAreaDropDownAnchorAlignment(), + toggleMenuTitle: nls.localize('viewsAndMoreActions', "Views and More Actions...") })); + this.collectCompositeActions()(); + return titleArea; } diff --git a/src/vs/workbench/browser/parts/editor/baseEditor.ts b/src/vs/workbench/browser/parts/editor/baseEditor.ts index 5c01ee205f7..cf48687419b 100644 --- a/src/vs/workbench/browser/parts/editor/baseEditor.ts +++ b/src/vs/workbench/browser/parts/editor/baseEditor.ts @@ -8,16 +8,15 @@ import { EditorInput, EditorOptions, IEditorPane, GroupIdentifier, IEditorMement import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorGroup, IEditorGroupsService, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { LRUCache, Touch } from 'vs/base/common/map'; import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; -import { isEmptyObject } from 'vs/base/common/types'; +import { isEmptyObject, isUndefinedOrNull } from 'vs/base/common/types'; import { DEFAULT_EDITOR_MIN_DIMENSIONS, DEFAULT_EDITOR_MAX_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor'; import { MementoObject } from 'vs/workbench/common/memento'; -import { isEqualOrParent, joinPath } from 'vs/base/common/resources'; -import { isLinux } from 'vs/base/common/platform'; +import { joinPath, IExtUri } from 'vs/base/common/resources'; import { indexOfPath } from 'vs/base/common/extpath'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -28,9 +27,17 @@ import { IDisposable } from 'vs/base/common/lifecycle'; * information about the state of the editor data. * * The workbench will keep an editor alive after it has been created and show/hide it based on - * user interaction. The lifecycle of a editor goes in the order create(), setVisible(true|false), - * layout(), setInput(), focus(), dispose(). During use of the workbench, a editor will often receive a - * clearInput, setVisible, layout and focus call, but only one create and dispose call. + * user interaction. The lifecycle of a editor goes in the order: + * + * - `createEditor()` + * - `setEditorVisible()` + * - `layout()` + * - `setInput()` + * - `focus()` + * - `dispose()`: when the editor group the editor is in closes + * + * During use of the workbench, a editor will often receive a `clearInput()`, `setEditorVisible()`, `layout()` and + * `focus()` calls, but only one `create()` and `dispose()` call. * * This class is only intended to be subclassed and not instantiated. */ @@ -38,10 +45,10 @@ export abstract class BaseEditor extends Composite implements IEditorPane { private static readonly EDITOR_MEMENTOS = new Map>(); - readonly minimumWidth = DEFAULT_EDITOR_MIN_DIMENSIONS.width; - readonly maximumWidth = DEFAULT_EDITOR_MAX_DIMENSIONS.width; - readonly minimumHeight = DEFAULT_EDITOR_MIN_DIMENSIONS.height; - readonly maximumHeight = DEFAULT_EDITOR_MAX_DIMENSIONS.height; + get minimumWidth() { return DEFAULT_EDITOR_MIN_DIMENSIONS.width; } + get maximumWidth() { return DEFAULT_EDITOR_MAX_DIMENSIONS.width; } + get minimumHeight() { return DEFAULT_EDITOR_MIN_DIMENSIONS.height; } + get maximumHeight() { return DEFAULT_EDITOR_MAX_DIMENSIONS.height; } readonly onDidSizeConstraintsChange = Event.None; @@ -51,7 +58,7 @@ export abstract class BaseEditor extends Composite implements IEditorPane { protected _options: EditorOptions | undefined; get options(): EditorOptions | undefined { return this._options; } - private _group?: IEditorGroup; + private _group: IEditorGroup | undefined; get group(): IEditorGroup | undefined { return this._group; } constructor( @@ -63,12 +70,25 @@ export abstract class BaseEditor extends Composite implements IEditorPane { super(id, telemetryService, themeService, storageService); } + create(parent: HTMLElement): void { + super.create(parent); + + // Create Editor + this.createEditor(parent); + } + + /** + * Called to create the editor in the parent HTMLElement. Subclasses implement + * this method to construct the editor widget. + */ + protected abstract createEditor(parent: HTMLElement): void; + /** * Note: Clients should not call this method, the workbench calls this * method. Calling it otherwise may result in unexpected behavior. * * Sets the given input with the options to the editor. The input is guaranteed - * to be different from the previous input that was set using the input.matches() + * to be different from the previous input that was set using the `input.matches()` * method. * * The provided cancellation token should be used to test if the operation @@ -82,6 +102,12 @@ export abstract class BaseEditor extends Composite implements IEditorPane { /** * Called to indicate to the editor that the input should be cleared and * resources associated with the input should be freed. + * + * This method can be called based on different contexts, e.g. when opening + * a different editor control or when closing all editors in a group. + * + * To monitor the lifecycle of editor inputs, you should not rely on this + * method, rather refer to the listeners on `IEditorGroup` via `IEditorGroupService`. */ clearInput(): void { this._input = undefined; @@ -99,22 +125,6 @@ export abstract class BaseEditor extends Composite implements IEditorPane { this._options = options; } - create(parent: HTMLElement): void { - super.create(parent); - - // Create Editor - this.createEditor(parent); - } - - onHide() { } - - onWillHide() { } - - /** - * Called to create the editor in the parent HTMLElement. - */ - protected abstract createEditor(parent: HTMLElement): void; - setVisible(visible: boolean, group?: IEditorGroup): void { super.setVisible(visible); @@ -217,9 +227,9 @@ export class EditorMemento implements IEditorMemento { } } - loadEditorState(group: IEditorGroup, resource: URI): T | undefined; - loadEditorState(group: IEditorGroup, editor: EditorInput): T | undefined; - loadEditorState(group: IEditorGroup, resourceOrEditor: URI | EditorInput): T | undefined { + loadEditorState(group: IEditorGroup, resource: URI, fallbackToOtherGroupState?: boolean): T | undefined; + loadEditorState(group: IEditorGroup, editor: EditorInput, fallbackToOtherGroupState?: boolean): T | undefined; + loadEditorState(group: IEditorGroup, resourceOrEditor: URI | EditorInput, fallbackToOtherGroupState?: boolean): T | undefined { const resource = this.doGetResource(resourceOrEditor); if (!resource || !group) { return undefined; // we are not in a good state to load any state for a resource @@ -229,7 +239,18 @@ export class EditorMemento implements IEditorMemento { const mementoForResource = cache.get(resource.toString()); if (mementoForResource) { - return mementoForResource[group.id]; + let mementoForResourceAndGroup = mementoForResource[group.id]; + if (!fallbackToOtherGroupState || !isUndefinedOrNull(mementoForResourceAndGroup)) { + return mementoForResourceAndGroup; + } + + // Fallback to retrieve state from the most recently active editor group as instructed + for (const group of this.editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE)) { + mementoForResourceAndGroup = mementoForResource[group.id]; + if (!isUndefinedOrNull(mementoForResourceAndGroup)) { + return mementoForResourceAndGroup; + } + } } return undefined; @@ -246,6 +267,10 @@ export class EditorMemento implements IEditorMemento { const resourceViewState = cache.get(resource.toString()); if (resourceViewState) { delete resourceViewState[group.id]; + + if (isEmptyObject(resourceViewState)) { + cache.delete(resource.toString()); + } } } else { cache.delete(resource.toString()); @@ -253,7 +278,7 @@ export class EditorMemento implements IEditorMemento { } } - moveEditorState(source: URI, target: URI): void { + moveEditorState(source: URI, target: URI, comparer: IExtUri): void { const cache = this.doLoad(); // We need a copy of the keys to not iterate over @@ -262,7 +287,7 @@ export class EditorMemento implements IEditorMemento { for (const cacheKey of cacheKeys) { const resource = URI.parse(cacheKey); - if (!isEqualOrParent(resource, source)) { + if (!comparer.isEqualOrParent(resource, source)) { continue; // not matching our resource } @@ -271,7 +296,7 @@ export class EditorMemento implements IEditorMemento { if (source.toString() === resource.toString()) { targetResource = target; // file got moved } else { - const index = indexOfPath(resource.path, source.path, !isLinux); + const index = indexOfPath(resource.path, source.path); targetResource = joinPath(target, resource.path.substr(index + source.path.length + 1)); // parent folder got moved } @@ -321,18 +346,19 @@ export class EditorMemento implements IEditorMemento { private cleanUp(): void { const cache = this.doLoad(); - // Remove groups from states that no longer exist - cache.forEach((mapGroupToMemento, resource) => { - Object.keys(mapGroupToMemento).forEach(group => { + // Remove groups from states that no longer exist. Since we modify the + // cache and its is a LRU cache make a copy to ensure iteration succeeds + const entries = [...cache.entries()]; + for (const [resource, mapGroupToMemento] of entries) { + for (const group of Object.keys(mapGroupToMemento)) { const groupId: GroupIdentifier = Number(group); if (!this.editorGroupService.getGroup(groupId)) { delete mapGroupToMemento[groupId]; - if (isEmptyObject(mapGroupToMemento)) { cache.delete(resource); } } - }); - }); + } + } } } diff --git a/src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts b/src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts index 964c8852ac5..9d285cea910 100644 --- a/src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts @@ -29,11 +29,11 @@ export class BinaryResourceDiffEditor extends SideBySideEditor { } getMetadata(): string | undefined { - const master = this.masterEditorPane; - const details = this.detailsEditorPane; + const primary = this.primaryEditorPane; + const secondary = this.secondaryEditorPane; - if (master instanceof BaseBinaryResourceEditor && details instanceof BaseBinaryResourceEditor) { - return nls.localize('metadataDiff', "{0} ↔ {1}", details.getMetadata(), master.getMetadata()); + if (primary instanceof BaseBinaryResourceEditor && secondary instanceof BaseBinaryResourceEditor) { + return nls.localize('metadataDiff', "{0} ↔ {1}", secondary.getMetadata(), primary.getMetadata()); } return undefined; diff --git a/src/vs/workbench/browser/parts/editor/binaryEditor.ts b/src/vs/workbench/browser/parts/editor/binaryEditor.ts index 722efdf0105..ca270875dfb 100644 --- a/src/vs/workbench/browser/parts/editor/binaryEditor.ts +++ b/src/vs/workbench/browser/parts/editor/binaryEditor.ts @@ -20,6 +20,7 @@ import { dispose, IDisposable, Disposable, DisposableStore } from 'vs/base/commo import { IStorageService } from 'vs/platform/storage/common/storage'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { assertIsDefined, assertAllDefined } from 'vs/base/common/types'; +import { BinarySize } from 'vs/platform/files/common/files'; export interface IOpenCallbacks { openInternal: (input: EditorInput, options: EditorOptions | undefined) => Promise; @@ -160,6 +161,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor { super.dispose(); } } + export interface IResourceDescriptor { readonly resource: URI; readonly name: string; @@ -168,33 +170,6 @@ export interface IResourceDescriptor { readonly mime: string; } -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 nls.localize('sizeB', "{0}B", size); - } - - if (size < BinarySize.MB) { - return nls.localize('sizeKB', "{0}KB", (size / BinarySize.KB).toFixed(2)); - } - - if (size < BinarySize.GB) { - return nls.localize('sizeMB', "{0}MB", (size / BinarySize.MB).toFixed(2)); - } - - if (size < BinarySize.TB) { - return nls.localize('sizeGB', "{0}GB", (size / BinarySize.GB).toFixed(2)); - } - - return nls.localize('sizeTB', "{0}TB", (size / BinarySize.TB).toFixed(2)); - } -} - interface ResourceViewerContext extends IDisposable { layout?(dimension: Dimension): void; } diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts index dc71187fd56..30e3c09e948 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts @@ -19,7 +19,7 @@ export const IBreadcrumbsService = createDecorator('IEditor export interface IBreadcrumbsService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; register(group: GroupIdentifier, widget: BreadcrumbsWidget): IDisposable; @@ -29,7 +29,7 @@ export interface IBreadcrumbsService { export class BreadcrumbsService implements IBreadcrumbsService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _map = new Map(); diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 368de57eda5..eca511ebe7b 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -11,7 +11,7 @@ import { tail } from 'vs/base/common/arrays'; import { timeout } from 'vs/base/common/async'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { isEqual } from 'vs/base/common/resources'; +import { extUri } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/breadcrumbscontrol'; import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; @@ -38,7 +38,7 @@ import { ResourceLabel } from 'vs/workbench/browser/labels'; import { BreadcrumbsConfig, IBreadcrumbsService } from 'vs/workbench/browser/parts/editor/breadcrumbs'; import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; import { BreadcrumbsPicker, createBreadcrumbsPicker } from 'vs/workbench/browser/parts/editor/breadcrumbsPicker'; -import { SideBySideEditorInput, IEditorPartOptions } from 'vs/workbench/common/editor'; +import { IEditorPartOptions, toResource, SideBySideEditor, SideBySideEditorInput, IEditorInputFactoryRegistry, Extensions } from 'vs/workbench/common/editor'; import { ACTIVE_GROUP, ACTIVE_GROUP_TYPE, IEditorService, SIDE_GROUP, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -49,6 +49,7 @@ import { withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; import { ILabelService } from 'vs/platform/label/common/label'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; +import { Registry } from 'vs/platform/registry/common/platform'; class Item extends BreadcrumbsItem { @@ -71,7 +72,7 @@ class Item extends BreadcrumbsItem { return false; } if (this.element instanceof FileElement && other.element instanceof FileElement) { - return (isEqual(this.element.uri, other.element.uri, false) && + return (extUri.isEqual(this.element.uri, other.element.uri) && this.options.showFileIcons === other.options.showFileIcons && this.options.showSymbolIcons === other.options.showSymbolIcons); } @@ -234,12 +235,9 @@ export class BreadcrumbsControl { this._breadcrumbsDisposables.clear(); // honor diff editors and such - let input = this._editorGroup.activeEditor; - if (input instanceof SideBySideEditorInput) { - input = input.master; - } + const uri = toResource(this._editorGroup.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }); - if (!input || !input.resource || !this._fileService.canHandleResource(input.resource!)) { + if (!uri || !this._fileService.canHandleResource(uri)) { // cleanup and return when there is no input or when // we cannot handle this input this._ckBreadcrumbsPossible.set(false); @@ -251,13 +249,23 @@ export class BreadcrumbsControl { } } + // display uri which can be derived from file input + let fileInfoUri = uri; + let input = this._editorGroup.activeEditor; + if (input instanceof SideBySideEditorInput) { + input = input.primary; + } + if (Registry.as(Extensions.EditorInputFactories).getFileEditorInputFactory().isFileEditorInput(input)) { + fileInfoUri = input.preferredResource; + } + this.domNode.classList.toggle('hidden', false); this._ckBreadcrumbsVisible.set(true); this._ckBreadcrumbsPossible.set(true); - const uri = input.resource; const editor = this._getActiveCodeEditor(); const model = new EditorBreadcrumbsModel( + fileInfoUri, uri, editor, this._configurationService, this._textResourceConfigurationService, diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts index 1d7752320b4..01ff3916e98 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts @@ -52,6 +52,7 @@ export class EditorBreadcrumbsModel { readonly onDidUpdate: Event = this._onDidUpdate.event; constructor( + fileInfoUri: URI, private readonly _uri: URI, private readonly _editor: ICodeEditor | undefined, @IConfigurationService private readonly _configurationService: IConfigurationService, @@ -64,7 +65,7 @@ export class EditorBreadcrumbsModel { this._disposables.add(this._cfgFilePath.onDidChange(_ => this._onDidUpdate.fire(this))); this._disposables.add(this._cfgSymbolPath.onDidChange(_ => this._onDidUpdate.fire(this))); - this._fileInfo = EditorBreadcrumbsModel._initFilePathInfo(this._uri, workspaceService); + this._fileInfo = EditorBreadcrumbsModel._initFilePathInfo(fileInfoUri, workspaceService); this._bindToEditor(); this._onDidUpdate.fire(this); } diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index f25556c08fb..1f168f323af 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -7,7 +7,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import * as nls from 'vs/nls'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; -import { EditorInput, IEditorInputFactory, SideBySideEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, TextCompareEditorActiveContext, EditorPinnedContext, EditorGroupEditorsCountContext, EditorStickyContext } from 'vs/workbench/common/editor'; +import { EditorInput, IEditorInputFactory, SideBySideEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, TextCompareEditorActiveContext, EditorPinnedContext, EditorGroupEditorsCountContext, EditorStickyContext, ActiveEditorAvailableEditorIdsContext } from 'vs/workbench/common/editor'; import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor'; import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; @@ -15,7 +15,6 @@ import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/u import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor'; -import { SUPPORTED_ENCODINGS } from 'vs/workbench/services/textfile/common/textfiles'; import { BinaryResourceDiffEditor } from 'vs/workbench/browser/parts/editor/binaryDiffEditor'; import { ChangeEncodingAction, ChangeEOLAction, ChangeModeAction, EditorStatus } from 'vs/workbench/browser/parts/editor/editorStatus'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; @@ -34,7 +33,7 @@ import { JoinAllGroupsAction, FocusLeftGroup, FocusAboveGroup, FocusRightGroup, FocusBelowGroup, EditorLayoutSingleAction, EditorLayoutTwoColumnsAction, EditorLayoutThreeColumnsAction, EditorLayoutTwoByTwoGridAction, EditorLayoutTwoRowsAction, EditorLayoutThreeRowsAction, EditorLayoutTwoColumnsBottomAction, EditorLayoutTwoRowsRightAction, NewEditorGroupLeftAction, NewEditorGroupRightAction, NewEditorGroupAboveAction, NewEditorGroupBelowAction, SplitEditorOrthogonalAction, CloseEditorInAllGroupsAction, NavigateToLastEditLocationAction, ToggleGroupSizesAction, ShowAllEditorsByMostRecentlyUsedAction, - QuickAccessPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction, QuickAccessLeastRecentlyUsedEditorAction, QuickAccessLeastRecentlyUsedEditorInGroupAction + QuickAccessPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction, QuickAccessLeastRecentlyUsedEditorAction, QuickAccessLeastRecentlyUsedEditorInGroupAction, ReopenResourcesAction, ToggleEditorTypeAction } from 'vs/workbench/browser/parts/editor/editorActions'; import * as editorCommands from 'vs/workbench/browser/parts/editor/editorCommands'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -48,7 +47,6 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { toLocalResource } from 'vs/base/common/resources'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { withNullAsUndefined } from 'vs/base/common/types'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { EditorAutoSave } from 'vs/workbench/browser/parts/editor/editorAutoSave'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; @@ -119,12 +117,12 @@ class UntitledTextEditorInputFactory implements IEditorInputFactory { ) { } canSerialize(editorInput: EditorInput): boolean { - return this.filesConfigurationService.isHotExitEnabled; + return this.filesConfigurationService.isHotExitEnabled && !editorInput.isDisposed(); } serialize(editorInput: EditorInput): string | undefined { - if (!this.filesConfigurationService.isHotExitEnabled) { - return undefined; // never restore untitled unless hot exit is enabled + if (!this.filesConfigurationService.isHotExitEnabled || editorInput.isDisposed()) { + return undefined; } const untitledTextEditorInput = editorInput; @@ -169,55 +167,58 @@ class UntitledTextEditorInputFactory implements IEditorInputFactory { Registry.as(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(UntitledTextEditorInput.ID, UntitledTextEditorInputFactory); +// Register SideBySide/DiffEditor Input Factory interface ISerializedSideBySideEditorInput { name: string; - description: string; + description: string | undefined; - detailsSerialized: string; - masterSerialized: string; + primarySerialized: string; + secondarySerialized: string; - detailsTypeId: string; - masterTypeId: string; + primaryTypeId: string; + secondaryTypeId: string; } -// Register Side by Side Editor Input Factory -class SideBySideEditorInputFactory implements IEditorInputFactory { +export abstract class AbstractSideBySideEditorInputFactory implements IEditorInputFactory { + + private getInputFactories(secondaryId: string, primaryId: string): [IEditorInputFactory | undefined, IEditorInputFactory | undefined] { + const registry = Registry.as(EditorInputExtensions.EditorInputFactories); + + return [registry.getEditorInputFactory(secondaryId), registry.getEditorInputFactory(primaryId)]; + } canSerialize(editorInput: EditorInput): boolean { - const input = editorInput; + const input = editorInput as SideBySideEditorInput | DiffEditorInput; - if (input.details && input.master) { - const registry = Registry.as(EditorInputExtensions.EditorInputFactories); - const detailsInputFactory = registry.getEditorInputFactory(input.details.getTypeId()); - const masterInputFactory = registry.getEditorInputFactory(input.master.getTypeId()); + if (input.primary && input.secondary) { + const [secondaryInputFactory, primaryInputFactory] = this.getInputFactories(input.secondary.getTypeId(), input.primary.getTypeId()); - return !!(detailsInputFactory?.canSerialize(input.details) && masterInputFactory?.canSerialize(input.master)); + return !!(secondaryInputFactory?.canSerialize(input.secondary) && primaryInputFactory?.canSerialize(input.primary)); } return false; } serialize(editorInput: EditorInput): string | undefined { - const input = editorInput; + const input = editorInput as SideBySideEditorInput | DiffEditorInput; - if (input.details && input.master) { - const registry = Registry.as(EditorInputExtensions.EditorInputFactories); - const detailsInputFactory = registry.getEditorInputFactory(input.details.getTypeId()); - const masterInputFactory = registry.getEditorInputFactory(input.master.getTypeId()); + if (input.primary && input.secondary) { + const [secondaryInputFactory, primaryInputFactory] = this.getInputFactories(input.secondary.getTypeId(), input.primary.getTypeId()); + if (primaryInputFactory && secondaryInputFactory) { + const primarySerialized = primaryInputFactory.serialize(input.primary); + const secondarySerialized = secondaryInputFactory.serialize(input.secondary); - if (detailsInputFactory && masterInputFactory) { - const detailsSerialized = detailsInputFactory.serialize(input.details); - const masterSerialized = masterInputFactory.serialize(input.master); - - if (detailsSerialized && masterSerialized) { - return JSON.stringify({ + if (primarySerialized && secondarySerialized) { + const serializedEditorInput: ISerializedSideBySideEditorInput = { name: input.getName(), description: input.getDescription(), - detailsSerialized, - masterSerialized, - detailsTypeId: input.details.getTypeId(), - masterTypeId: input.master.getTypeId() - }); + primarySerialized: primarySerialized, + secondarySerialized: secondarySerialized, + primaryTypeId: input.primary.getTypeId(), + secondaryTypeId: input.secondary.getTypeId() + }; + + return JSON.stringify(serializedEditorInput); } } } @@ -228,24 +229,38 @@ class SideBySideEditorInputFactory implements IEditorInputFactory { deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput | undefined { const deserialized: ISerializedSideBySideEditorInput = JSON.parse(serializedEditorInput); - const registry = Registry.as(EditorInputExtensions.EditorInputFactories); - const detailsInputFactory = registry.getEditorInputFactory(deserialized.detailsTypeId); - const masterInputFactory = registry.getEditorInputFactory(deserialized.masterTypeId); + const [secondaryInputFactory, primaryInputFactory] = this.getInputFactories(deserialized.secondaryTypeId, deserialized.primaryTypeId); + if (primaryInputFactory && secondaryInputFactory) { + const primaryInput = primaryInputFactory.deserialize(instantiationService, deserialized.primarySerialized); + const secondaryInput = secondaryInputFactory.deserialize(instantiationService, deserialized.secondarySerialized); - if (detailsInputFactory && masterInputFactory) { - const detailsInput = detailsInputFactory.deserialize(instantiationService, deserialized.detailsSerialized); - const masterInput = masterInputFactory.deserialize(instantiationService, deserialized.masterSerialized); - - if (detailsInput && masterInput) { - return new SideBySideEditorInput(deserialized.name, withNullAsUndefined(deserialized.description), detailsInput, masterInput); + if (primaryInput && secondaryInput) { + return this.createEditorInput(deserialized.name, deserialized.description, secondaryInput, primaryInput); } } return undefined; } + + protected abstract createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput; +} + +class SideBySideEditorInputFactory extends AbstractSideBySideEditorInputFactory { + + protected createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { + return new SideBySideEditorInput(name, description, secondaryInput, primaryInput); + } +} + +class DiffEditorInputFactory extends AbstractSideBySideEditorInputFactory { + + protected createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { + return new DiffEditorInput(name, description, secondaryInput, primaryInput); + } } Registry.as(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(SideBySideEditorInput.ID, SideBySideEditorInputFactory); +Registry.as(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(DiffEditorInput.ID, DiffEditorInputFactory); // Register Editor Contributions registerEditorContribution(OpenWorkspaceButtonContribution.ID, OpenWorkspaceButtonContribution); @@ -260,10 +275,7 @@ Registry.as(WorkbenchExtensions.Workbench).regi const registry = Registry.as(ActionExtensions.WorkbenchActions); registry.registerWorkbenchAction(SyncActionDescriptor.from(ChangeModeAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_M) }), 'Change Language Mode'); registry.registerWorkbenchAction(SyncActionDescriptor.from(ChangeEOLAction), 'Change End of Line Sequence'); - -if (Object.keys(SUPPORTED_ENCODINGS).length > 1) { - registry.registerWorkbenchAction(SyncActionDescriptor.from(ChangeEncodingAction), 'Change File Encoding'); -} +registry.registerWorkbenchAction(SyncActionDescriptor.from(ChangeEncodingAction), 'Change File Encoding'); // Register Editor Quick Access const quickAccessRegistry = Registry.as(QuickAccessExtensions.Quickaccess); @@ -370,6 +382,8 @@ registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutThreeRows registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutTwoByTwoGridAction), 'View: Grid Editor Layout (2x2)', category); registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutTwoRowsRightAction), 'View: Two Rows Right Editor Layout', category); registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutTwoColumnsBottomAction), 'View: Two Columns Bottom Editor Layout', category); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ReopenResourcesAction), 'View: Reopen Editor With...', category, ActiveEditorAvailableEditorIdsContext); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleEditorTypeAction), 'View: Toggle Editor Type', category, ActiveEditorAvailableEditorIdsContext); // Register Quick Editor Actions including built in quick navigate support for some @@ -432,6 +446,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCo MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: nls.localize('closeRight', "Close to the Right"), precondition: EditorGroupEditorsCountContext.notEqualsTo('1') }, group: '1_close', order: 30, when: ContextKeyExpr.has('config.workbench.editor.showTabs') }); MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '1_close', order: 40 }); MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '1_close', order: 50 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: ReopenResourcesAction.ID, title: ReopenResourcesAction.LABEL }, group: '1_open', order: 10, when: ActiveEditorAvailableEditorIdsContext }); MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.KEEP_EDITOR_COMMAND_ID, title: nls.localize('keepOpen', "Keep Open"), precondition: EditorPinnedContext.toNegated() }, group: '3_preview', order: 10, when: ContextKeyExpr.has('config.workbench.editor.enablePreview') }); MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.PIN_EDITOR_COMMAND_ID, title: nls.localize('pin', "Pin") }, group: '3_preview', order: 20, when: ContextKeyExpr.and(EditorStickyContext.toNegated(), ContextKeyExpr.has('config.workbench.editor.showTabs')) }); MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.UNPIN_EDITOR_COMMAND_ID, title: nls.localize('unpin', "Unpin") }, group: '3_preview', order: 20, when: ContextKeyExpr.and(EditorStickyContext, ContextKeyExpr.has('config.workbench.editor.showTabs')) }); @@ -442,9 +457,9 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCo // Editor Title Menu MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.TOGGLE_DIFF_SIDE_BY_SIDE, title: nls.localize('toggleInlineView', "Toggle Inline View") }, group: '1_diff', order: 10, when: ContextKeyExpr.has('isInDiffEditor') }); -MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.SHOW_EDITORS_IN_GROUP, title: nls.localize('showOpenedEditors', "Show Opened Editors") }, group: '3_open', order: 10, when: ContextKeyExpr.has('config.workbench.editor.showTabs') }); -MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '5_close', order: 10, when: ContextKeyExpr.has('config.workbench.editor.showTabs') }); -MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20, when: ContextKeyExpr.has('config.workbench.editor.showTabs') }); +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.SHOW_EDITORS_IN_GROUP, title: nls.localize('showOpenedEditors', "Show Opened Editors") }, group: '3_open', order: 10 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '5_close', order: 10 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20 }); interface IEditorToolItem { id: string; title: string; icon?: { dark?: URI; light?: URI; } | ThemeIcon; } diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 482abde4189..e562c83198d 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -5,11 +5,12 @@ import { GroupIdentifier, IWorkbenchEditorConfiguration, EditorOptions, TextEditorOptions, IEditorInput, IEditorIdentifier, IEditorCloseEvent, IEditorPane, IEditorPartOptions, IEditorPartOptionsChangeEvent, EditorInput } from 'vs/workbench/common/editor'; import { EditorGroup } from 'vs/workbench/common/editor/editorGroup'; -import { IEditorGroup, GroupDirection, IAddGroupOptions, IMergeGroupOptions, GroupsOrder, GroupsArrangement } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorGroup, GroupDirection, IAddGroupOptions, IMergeGroupOptions, GroupsOrder, GroupsArrangement, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Dimension } from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; -import { IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ISerializableView } from 'vs/base/browser/ui/grid/grid'; import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; @@ -32,12 +33,12 @@ export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = { titleScrollbarSizing: 'default', focusRecentEditorAfterClose: true, showIcons: true, + hasIcons: true, // 'vs-seti' is our default icon theme enablePreview: true, openPositioning: 'right', openSideBySideDirection: 'right', closeEmptyGroups: true, labelFormat: 'default', - iconTheme: 'vs-seti', splitSizing: 'distribute' }; @@ -45,16 +46,14 @@ export function impactsEditorPartOptions(event: IConfigurationChangeEvent): bool return event.affectsConfiguration('workbench.editor') || event.affectsConfiguration('workbench.iconTheme'); } -export function getEditorPartOptions(config: IWorkbenchEditorConfiguration): IEditorPartOptions { - const options = { ...DEFAULT_EDITOR_PART_OPTIONS }; +export function getEditorPartOptions(configurationService: IConfigurationService, themeService: IThemeService): IEditorPartOptions { + const options = { + ...DEFAULT_EDITOR_PART_OPTIONS, + hasIcons: themeService.getFileIconTheme().hasFileIcons + }; - if (!config || !config.workbench) { - return options; - } - - options.iconTheme = config.workbench.iconTheme; - - if (config.workbench.editor) { + const config = configurationService.getValue(); + if (config?.workbench?.editor) { Object.assign(options, config.workbench.editor); } @@ -68,6 +67,11 @@ export interface IEditorOpeningEvent extends IEditorIdentifier { */ options?: IEditorOptions; + /** + * Context indicates how the editor open event is initialized. + */ + context?: OpenEditorContext; + /** * Allows to prevent the opening of an editor by providing a callback * that will be executed instead. By returning another editor promise @@ -75,7 +79,7 @@ export interface IEditorOpeningEvent extends IEditorIdentifier { * to return a promise that resolves to `undefined` to prevent the opening * alltogether. */ - prevent(callback: () => undefined | Promise): void; + prevent(callback: () => Promise | undefined): void; } export interface IEditorGroupsAccessor { @@ -144,11 +148,6 @@ export function getActiveTextEditorOptions(group: IEditorGroup, expectedActiveEd */ export interface EditorServiceImpl extends IEditorService { - /** - * Emitted when an editor is closed. - */ - readonly onDidCloseEditor: Event; - /** * Emitted when an editor failed to open. */ diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index f80f1ff03be..b149f3a788f 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -11,7 +11,7 @@ import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { CLOSE_EDITOR_COMMAND_ID, MOVE_ACTIVE_EDITOR_COMMAND_ID, ActiveEditorMoveArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, mergeAllGroups } from 'vs/workbench/browser/parts/editor/editorCommands'; -import { IEditorGroupsService, IEditorGroup, GroupsArrangement, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorGroupsService, IEditorGroup, GroupsArrangement, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { DisposableStore } from 'vs/base/common/lifecycle'; @@ -22,6 +22,7 @@ import { ItemActivation, IQuickInputService } from 'vs/platform/quickinput/commo import { AllEditorsByMostRecentlyUsedQuickAccess, ActiveGroupEditorsByMostRecentlyUsedQuickAccess, AllEditorsByAppearanceQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess'; import { Codicon } from 'vs/base/common/codicons'; import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { openEditorWith, getAllAvailableEditors } from 'vs/workbench/services/editor/common/editorOpenWith'; export class ExecuteCommandAction extends Action { @@ -491,27 +492,26 @@ export class CloseLeftEditorsInGroupAction extends Action { constructor( id: string, label: string, - @IEditorService private readonly editorService: IEditorService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService ) { super(id, label); } async run(context?: IEditorIdentifier): Promise { - const { group, editor } = getTarget(this.editorService, this.editorGroupService, context); + const { group, editor } = this.getTarget(context); if (group && editor) { return group.closeEditors({ direction: CloseDirection.LEFT, except: editor, excludeSticky: true }); } } -} -function getTarget(editorService: IEditorService, editorGroupService: IEditorGroupsService, context?: IEditorIdentifier): { editor: IEditorInput | null, group: IEditorGroup | undefined } { - if (context) { - return { editor: context.editor, group: editorGroupService.getGroup(context.groupId) }; + private getTarget(context?: IEditorIdentifier): { editor: IEditorInput | null, group: IEditorGroup | undefined } { + if (context) { + return { editor: context.editor, group: this.editorGroupService.getGroup(context.groupId) }; + } + + // Fallback to active group + return { group: this.editorGroupService.activeGroup, editor: this.editorGroupService.activeGroup.activeEditor }; } - - // Fallback to active group - return { group: editorGroupService.activeGroup, editor: editorGroupService.activeGroup.activeEditor }; } abstract class BaseCloseAllAction extends Action { @@ -581,7 +581,7 @@ abstract class BaseCloseAllAction extends Action { else { let name: string; if (editor instanceof SideBySideEditorInput) { - name = editor.master.getName(); // prefer shorter names by using master's name in this case + name = editor.primary.getName(); // prefer shorter names by using primary's name in this case } else { name = editor.getName(); } @@ -1339,7 +1339,8 @@ export class QuickAccessPreviousEditorFromHistoryAction extends Action { id: string, label: string, @IQuickInputService private readonly quickInputService: IQuickInputService, - @IKeybindingService private readonly keybindingService: IKeybindingService + @IKeybindingService private readonly keybindingService: IKeybindingService, + @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService ) { super(id, label); } @@ -1347,7 +1348,14 @@ export class QuickAccessPreviousEditorFromHistoryAction extends Action { async run(): Promise { const keybindings = this.keybindingService.lookupKeybindings(this.id); - this.quickInputService.quickAccess.show('', { quickNavigateConfiguration: { keybindings } }); + // Enforce to activate the first item in quick access if + // the currently active editor group has n editor opened + let itemActivation: ItemActivation | undefined = undefined; + if (this.editorGroupService.activeGroup.count === 0) { + itemActivation = ItemActivation.FIRST; + } + + this.quickInputService.quickAccess.show('', { quickNavigateConfiguration: { keybindings }, itemActivation }); } } @@ -1768,3 +1776,72 @@ export class NewEditorGroupBelowAction extends BaseCreateEditorGroupAction { super(id, label, GroupDirection.DOWN, editorGroupService); } } + +export class ReopenResourcesAction extends Action { + + static readonly ID = 'workbench.action.reopenWithEditor'; + static readonly LABEL = nls.localize('workbench.action.reopenWithEditor', "Reopen Editor With..."); + + constructor( + id: string, + label: string, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @IEditorService private readonly editorService: IEditorService, + @IConfigurationService private readonly configurationService: IConfigurationService + ) { + super(id, label); + } + + async run(): Promise { + const activeInput = this.editorService.activeEditor; + if (!activeInput) { + return; + } + + const activeEditorPane = this.editorService.activeEditorPane; + if (!activeEditorPane) { + return; + } + + const options = activeEditorPane.options; + const group = activeEditorPane.group; + await openEditorWith(activeInput, undefined, options, group, this.editorService, this.configurationService, this.quickInputService); + } +} + +export class ToggleEditorTypeAction extends Action { + + static readonly ID = 'workbench.action.toggleEditorType'; + static readonly LABEL = nls.localize('workbench.action.toggleEditorType', "Toggle Editor Type"); + + constructor( + id: string, + label: string, + @IEditorService private readonly editorService: IEditorService, + ) { + super(id, label); + } + + async run(): Promise { + const activeEditorPane = this.editorService.activeEditorPane; + if (!activeEditorPane) { + return; + } + + const input = activeEditorPane.input; + if (!input.resource) { + return; + } + + const options = activeEditorPane.options; + const group = activeEditorPane.group; + + const overrides = getAllAvailableEditors(input.resource, undefined, options, group, this.editorService); + const firstNonActiveOverride = overrides.find(([_, entry]) => !entry.active); + if (!firstNonActiveOverride) { + return; + } + + await firstNonActiveOverride[0].open(input, { ...options, override: firstNonActiveOverride[1].id }, group, OpenEditorContext.NEW_EDITOR)?.override; + } +} diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index 842c3ea044c..b8ee124d13b 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -600,7 +600,7 @@ function registerCloseEditorCommands() { .filter(context => context.groupId === groupId) .map(context => typeof context.editorIndex === 'number' ? group.getEditorByIndex(context.editorIndex) : group.activeEditor); - const editorsToClose = group.getEditors(EditorsOrder.SEQUENTIAL, { excludeSticky: true }).filter(editor => editors.indexOf(editor) === -1); + const editorsToClose = group.getEditors(EditorsOrder.SEQUENTIAL, { excludeSticky: true }).filter(editor => !editors.includes(editor)); if (group.activeEditor) { group.pinEditor(group.activeEditor); @@ -650,7 +650,7 @@ function registerCloseEditorCommands() { id: PIN_EDITOR_COMMAND_ID, weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(EditorStickyContext.toNegated(), ContextKeyExpr.has('config.workbench.editor.showTabs')), - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.Enter), + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.Shift | KeyCode.Enter), handler: async (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { const editorGroupService = accessor.get(IEditorGroupsService); @@ -665,7 +665,7 @@ function registerCloseEditorCommands() { id: UNPIN_EDITOR_COMMAND_ID, weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(EditorStickyContext, ContextKeyExpr.has('config.workbench.editor.showTabs')), - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.Enter), + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.Shift | KeyCode.Enter), handler: async (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { const editorGroupService = accessor.get(IEditorGroupsService); diff --git a/src/vs/workbench/browser/parts/editor/editorControl.ts b/src/vs/workbench/browser/parts/editor/editorControl.ts index b0e2f9d4a37..b3a49e5d197 100644 --- a/src/vs/workbench/browser/parts/editor/editorControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorControl.ts @@ -35,26 +35,22 @@ export class EditorControl extends Disposable { readonly onDidSizeConstraintsChange = this._onDidSizeConstraintsChange.event; private _activeEditorPane: BaseEditor | null = null; + get activeEditorPane(): IVisibleEditorPane | null { return this._activeEditorPane as IVisibleEditorPane | null; } + private readonly editorPanes: BaseEditor[] = []; private readonly activeEditorPaneDisposables = this._register(new DisposableStore()); private dimension: Dimension | undefined; - private editorOperation: LongRunningOperation; + private readonly editorOperation = this._register(new LongRunningOperation(this.editorProgressService)); constructor( private parent: HTMLElement, private groupView: IEditorGroupView, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IEditorProgressService editorProgressService: IEditorProgressService + @IEditorProgressService private readonly editorProgressService: IEditorProgressService ) { super(); - - this.editorOperation = this._register(new LongRunningOperation(editorProgressService)); - } - - get activeEditorPane(): IVisibleEditorPane | null { - return this._activeEditorPane as IVisibleEditorPane | null; } async openEditor(editor: EditorInput, options?: EditorOptions): Promise { @@ -202,19 +198,19 @@ export class EditorControl extends Disposable { // Stop any running operation this.editorOperation.stop(); - // Remove editor pane from parent and hide - const editorPaneContainer = this._activeEditorPane.getContainer(); - if (editorPaneContainer) { - this._activeEditorPane.onWillHide(); - this.parent.removeChild(editorPaneContainer); - hide(editorPaneContainer); - this._activeEditorPane.onHide(); - } - - // Indicate to editor pane + // Indicate to editor pane before removing the editor from + // the DOM to give a chance to persist certain state that + // might depend on still being the active DOM element. this._activeEditorPane.clearInput(); this._activeEditorPane.setVisible(false, this.groupView); + // Remove editor pane from parent + const editorPaneContainer = this._activeEditorPane.getContainer(); + if (editorPaneContainer) { + this.parent.removeChild(editorPaneContainer); + hide(editorPaneContainer); + } + // Clear active editor pane this.doSetActiveEditorPane(null); } @@ -226,16 +222,12 @@ export class EditorControl extends Disposable { } setVisible(visible: boolean): void { - if (this._activeEditorPane) { - this._activeEditorPane.setVisible(visible, this.groupView); - } + this._activeEditorPane?.setVisible(visible, this.groupView); } layout(dimension: Dimension): void { this.dimension = dimension; - if (this._activeEditorPane && this.dimension) { - this._activeEditorPane.layout(this.dimension); - } + this._activeEditorPane?.layout(dimension); } } diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index 90a75c3c0a0..bc9f5db8216 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -22,6 +22,9 @@ import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { URI } from 'vs/base/common/uri'; import { joinPath } from 'vs/base/common/resources'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { assertIsDefined, assertAllDefined } from 'vs/base/common/types'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { localize } from 'vs/nls'; interface IDropOperation { splitDirection?: GroupDirection; @@ -31,8 +34,10 @@ class DropOverlay extends Themable { private static readonly OVERLAY_ID = 'monaco-workbench-editor-drop-overlay'; - private container!: HTMLElement; - private overlay!: HTMLElement; + private static readonly MAX_FILE_UPLOAD_SIZE = 100 * 1024 * 1024; // 100mb + + private container: HTMLElement | undefined; + private overlay: HTMLElement | undefined; private currentDropOperation: IDropOperation | undefined; private _disposed: boolean | undefined; @@ -48,7 +53,8 @@ class DropOverlay extends Themable { @IThemeService themeService: IThemeService, @IInstantiationService private instantiationService: IInstantiationService, @IFileDialogService private readonly fileDialogService: IFileDialogService, - @IEditorService private readonly editorService: IEditorService + @IEditorService private readonly editorService: IEditorService, + @INotificationService private readonly notificationService: INotificationService ) { super(themeService); @@ -65,45 +71,46 @@ class DropOverlay extends Themable { const overlayOffsetHeight = this.getOverlayOffsetHeight(); // Container - this.container = document.createElement('div'); - this.container.id = DropOverlay.OVERLAY_ID; - this.container.style.top = `${overlayOffsetHeight}px`; + const container = this.container = document.createElement('div'); + container.id = DropOverlay.OVERLAY_ID; + container.style.top = `${overlayOffsetHeight}px`; // Parent - this.groupView.element.appendChild(this.container); + this.groupView.element.appendChild(container); addClass(this.groupView.element, 'dragged-over'); this._register(toDisposable(() => { - this.groupView.element.removeChild(this.container); + this.groupView.element.removeChild(container); removeClass(this.groupView.element, 'dragged-over'); })); // Overlay this.overlay = document.createElement('div'); addClass(this.overlay, 'editor-group-overlay-indicator'); - this.container.appendChild(this.overlay); + container.appendChild(this.overlay); // Overlay Event Handling - this.registerListeners(); + this.registerListeners(container); // Styles this.updateStyles(); } protected updateStyles(): void { + const overlay = assertIsDefined(this.overlay); // Overlay drop background - this.overlay.style.backgroundColor = this.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND) || ''; + overlay.style.backgroundColor = this.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND) || ''; // Overlay contrast border (if any) const activeContrastBorderColor = this.getColor(activeContrastBorder); - this.overlay.style.outlineColor = activeContrastBorderColor || ''; - this.overlay.style.outlineOffset = activeContrastBorderColor ? '-2px' : ''; - this.overlay.style.outlineStyle = activeContrastBorderColor ? 'dashed' : ''; - this.overlay.style.outlineWidth = activeContrastBorderColor ? '2px' : ''; + overlay.style.outlineColor = activeContrastBorderColor || ''; + overlay.style.outlineOffset = activeContrastBorderColor ? '-2px' : ''; + overlay.style.outlineStyle = activeContrastBorderColor ? 'dashed' : ''; + overlay.style.outlineWidth = activeContrastBorderColor ? '2px' : ''; } - private registerListeners(): void { - this._register(new DragAndDropObserver(this.container, { + private registerListeners(container: HTMLElement): void { + this._register(new DragAndDropObserver(container, { onDragEnter: e => undefined, onDragOver: e => { const isDraggingGroup = this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype); @@ -161,7 +168,7 @@ class DropOverlay extends Themable { } })); - this._register(addDisposableListener(this.container, EventType.MOUSE_OVER, () => { + this._register(addDisposableListener(container, EventType.MOUSE_OVER, () => { // Under some circumstances we have seen reports where the drop overlay is not being // cleaned up and as such the editor area remains under the overlay so that you cannot // type into the editor anymore. This seems related to using VMs and DND via host and @@ -295,6 +302,14 @@ class DropOverlay extends Themable { for (let i = 0; i < files.length; i++) { const file = files.item(i); if (file) { + + // Skip for very large files because this operation is unbuffered + if (file.size > DropOverlay.MAX_FILE_UPLOAD_SIZE) { + this.notificationService.warn(localize('fileTooLarge', "File is too large to open as untitled editor. Please upload it first into the file explorer and then try again.")); + continue; + } + + // Read file fully and open as untitled editor const reader = new FileReader(); reader.readAsArrayBuffer(file); reader.onload = async event => { @@ -456,30 +471,32 @@ class DropOverlay extends Themable { } // Make sure the overlay is visible now - this.overlay.style.opacity = '1'; + const overlay = assertIsDefined(this.overlay); + overlay.style.opacity = '1'; // Enable transition after a timeout to prevent initial animation - setTimeout(() => addClass(this.overlay, 'overlay-move-transition'), 0); + setTimeout(() => addClass(overlay, 'overlay-move-transition'), 0); // Remember as current split direction this.currentDropOperation = { splitDirection }; } private doPositionOverlay(options: { top: string, left: string, width: string, height: string }): void { + const [container, overlay] = assertAllDefined(this.container, this.overlay); // Container const offsetHeight = this.getOverlayOffsetHeight(); if (offsetHeight) { - this.container.style.height = `calc(100% - ${offsetHeight}px)`; + container.style.height = `calc(100% - ${offsetHeight}px)`; } else { - this.container.style.height = '100%'; + container.style.height = '100%'; } // Overlay - this.overlay.style.top = options.top; - this.overlay.style.left = options.left; - this.overlay.style.width = options.width; - this.overlay.style.height = options.height; + overlay.style.top = options.top; + overlay.style.left = options.left; + overlay.style.width = options.width; + overlay.style.height = options.height; } private getOverlayOffsetHeight(): number { @@ -491,11 +508,12 @@ class DropOverlay extends Themable { } private hideOverlay(): void { + const overlay = assertIsDefined(this.overlay); // Reset overlay this.doPositionOverlay({ top: '0', left: '0', width: '100%', height: '100%' }); - this.overlay.style.opacity = '0'; - removeClass(this.overlay, 'overlay-move-transition'); + overlay.style.opacity = '0'; + removeClass(overlay, 'overlay-move-transition'); // Reset current operation this.currentDropOperation = undefined; @@ -512,8 +530,12 @@ class DropOverlay extends Themable { } } -export interface EditorDropTargetDelegate { - groupContainsPredicate?(groupView: IEditorGroupView): boolean; +export interface IEditorDropTargetDelegate { + + /** + * A helper to figure out if the drop target contains the provided group. + */ + containsGroup?(groupView: IEditorGroupView): boolean; } export class EditorDropTarget extends Themable { @@ -528,7 +550,7 @@ export class EditorDropTarget extends Themable { constructor( private accessor: IEditorGroupsAccessor, private container: HTMLElement, - private readonly delegate: EditorDropTargetDelegate, + private readonly delegate: IEditorDropTargetDelegate, @IThemeService themeService: IThemeService, @IInstantiationService private readonly instantiationService: IInstantiationService ) { @@ -602,7 +624,8 @@ export class EditorDropTarget extends Themable { private findTargetGroupView(child: HTMLElement): IEditorGroupView | undefined { const groups = this.accessor.groups; - return groups.find(groupView => isAncestor(child, groupView.element) || this.delegate.groupContainsPredicate?.(groupView)); + + return groups.find(groupView => isAncestor(child, groupView.element) || this.delegate.containsGroup?.(groupView)); } private updateContainer(isDraggedOver: boolean): void { diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 3da11230ff9..0bf99cb44ea 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -4,9 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/editorgroupview'; - import { EditorGroup, IEditorOpenOptions, EditorCloseEvent, ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup'; -import { EditorInput, EditorOptions, GroupIdentifier, SideBySideEditorInput, CloseDirection, IEditorCloseEvent, EditorGroupActiveEditorDirtyContext, IEditorPane, EditorGroupEditorsCountContext, SaveReason, IEditorPartOptionsChangeEvent, EditorsOrder, IVisibleEditorPane } from 'vs/workbench/common/editor'; +import { EditorInput, EditorOptions, GroupIdentifier, SideBySideEditorInput, CloseDirection, IEditorCloseEvent, EditorGroupActiveEditorDirtyContext, IEditorPane, EditorGroupEditorsCountContext, SaveReason, IEditorPartOptionsChangeEvent, EditorsOrder, IVisibleEditorPane, EditorStickyContext, EditorPinnedContext } from 'vs/workbench/common/editor'; import { Event, Emitter, Relay } from 'vs/base/common/event'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { addClass, addClasses, Dimension, trackFocus, toggleClass, removeClass, addDisposableListener, EventType, EventHelper, findParentWithClass, clearNode, isAncestor } from 'vs/base/browser/dom'; @@ -17,7 +16,7 @@ import { attachProgressBarStyler } from 'vs/platform/theme/common/styler'; import { IThemeService, registerThemingParticipant, Themable } from 'vs/platform/theme/common/themeService'; import { editorBackground, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { EDITOR_GROUP_HEADER_TABS_BACKGROUND, EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND, EDITOR_GROUP_EMPTY_BACKGROUND, EDITOR_GROUP_FOCUSED_EMPTY_BORDER, EDITOR_GROUP_HEADER_BORDER } from 'vs/workbench/common/theme'; -import { IMoveEditorOptions, ICopyEditorOptions, ICloseEditorsFilter, IGroupChangeEvent, GroupChangeKind, GroupsOrder, ICloseEditorOptions, ICloseAllEditorsOptions } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IMoveEditorOptions, ICopyEditorOptions, ICloseEditorsFilter, IGroupChangeEvent, GroupChangeKind, GroupsOrder, ICloseEditorOptions, ICloseAllEditorsOptions, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService'; import { TabsTitleControl } from 'vs/workbench/browser/parts/editor/tabsTitleControl'; import { EditorControl } from 'vs/workbench/browser/parts/editor/editorControl'; import { IEditorProgressService } from 'vs/platform/progress/common/progress'; @@ -220,8 +219,10 @@ export class EditorGroupView extends Themable implements IEditorGroupView { private handleGroupContextKeys(contextKeyService: IContextKeyService): void { const groupActiveEditorDirtyContextKey = EditorGroupActiveEditorDirtyContext.bindTo(contextKeyService); const groupEditorsCountContext = EditorGroupEditorsCountContext.bindTo(contextKeyService); + const groupActiveEditorPinnedContext = EditorPinnedContext.bindTo(contextKeyService); + const groupActiveEditorStickyContext = EditorStickyContext.bindTo(contextKeyService); - let activeEditorListener = new MutableDisposable(); + const activeEditorListener = new MutableDisposable(); const observeActiveEditor = () => { activeEditorListener.clear(); @@ -237,11 +238,22 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Update group contexts based on group changes this._register(this.onDidGroupChange(e => { - - // Track the active editor and update context key that reflects - // the dirty state of this editor - if (e.kind === GroupChangeKind.EDITOR_ACTIVE) { - observeActiveEditor(); + switch (e.kind) { + case GroupChangeKind.EDITOR_ACTIVE: + // Track the active editor and update context key that reflects + // the dirty state of this editor + observeActiveEditor(); + break; + case GroupChangeKind.EDITOR_PIN: + if (e.editor && e.editor === this._group.activeEditor) { + groupActiveEditorPinnedContext.set(this._group.isPinned(this._group.activeEditor)); + } + break; + case GroupChangeKind.EDITOR_STICKY: + if (e.editor && e.editor === this._group.activeEditor) { + groupActiveEditorStickyContext.set(this._group.isSticky(this._group.activeEditor)); + } + break; } // Group editors count context @@ -263,9 +275,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView { })); // Close empty editor group via middle mouse click - this._register(addDisposableListener(this.element, EventType.MOUSE_UP, e => { + this._register(addDisposableListener(this.element, EventType.AUXCLICK, e => { if (this.isEmpty && e.button === 1 /* Middle Button */) { - EventHelper.stop(e); + EventHelper.stop(e, true); this.accessor.removeGroup(this); } @@ -464,6 +476,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Model Events this._register(this._group.onDidChangeEditorPinned(editor => this.onDidChangeEditorPinned(editor))); + this._register(this._group.onDidChangeEditorSticky(editor => this.onDidChangeEditorSticky(editor))); this._register(this._group.onDidOpenEditor(editor => this.onDidOpenEditor(editor))); this._register(this._group.onDidCloseEditor(editor => this.handleOnDidCloseEditor(editor))); this._register(this._group.onDidDisposeEditor(editor => this.onDidDisposeEditor(editor))); @@ -478,11 +491,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } private onDidChangeEditorPinned(editor: EditorInput): void { - - // Event this._onDidGroupChange.fire({ kind: GroupChangeKind.EDITOR_PIN, editor }); } + private onDidChangeEditorSticky(editor: EditorInput): void { + this._onDidGroupChange.fire({ kind: GroupChangeKind.EDITOR_STICKY, editor }); + } + private onDidOpenEditor(editor: EditorInput): void { /* __GDPR__ @@ -510,17 +525,23 @@ export class EditorGroupView extends Themable implements IEditorGroupView { const editor = event.editor; const editorsToClose = [editor]; - // Include both sides of side by side editors when being closed and not opened multiple times - if (editor instanceof SideBySideEditorInput && !this.accessor.groups.some(groupView => groupView.group.contains(editor))) { - editorsToClose.push(editor.master, editor.details); + // Include both sides of side by side editors when being closed + if (editor instanceof SideBySideEditorInput) { + editorsToClose.push(editor.primary, editor.secondary); } - // Dispose the editor when it is no longer open in any group including diff editors - editorsToClose.forEach(editorToClose => { - if (!this.accessor.groups.some(groupView => groupView.group.contains(editorToClose, true /* include side by side editor master & details */))) { - editorToClose.dispose(); + // For each editor to close, we call dispose() to free up any resources. + // However, certain editors might be shared across multiple editor groups + // (including being visible in side by side / diff editors) and as such we + // only dispose when they are not opened elsewhere. + for (const editor of editorsToClose) { + if (!this.accessor.groups.some(groupView => groupView.group.contains(editor, { + strictEquals: true, // only if this input is not shared across editor groups + supportSideBySide: true // include side by side editor primary & secondary + }))) { + editor.dispose(); } - }); + } /* __GDPR__ "editorClosed" : { @@ -596,11 +617,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Title control Switch between showing tabs <=> not showing tabs if (event.oldPartOptions.showTabs !== event.newPartOptions.showTabs) { - // Recreate and layout control + // Recreate title control this.createTitleAreaControl(); - if (this.dimension) { - this.layoutTitleAreaControl(this.dimension.width); - } + + // Re-layout + this.relayout(); // Ensure to show active editor if any if (this._group.activeEditor) { @@ -848,7 +869,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { //#region openEditor() - async openEditor(editor: EditorInput, options?: EditorOptions): Promise { + async openEditor(editor: EditorInput, options?: EditorOptions, context?: OpenEditorContext): Promise { // Guard against invalid inputs if (!editor) { @@ -856,7 +877,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } // Editor opening event allows for prevention - const event = new EditorOpeningEvent(this._group.id, editor, options); + const event = new EditorOpeningEvent(this._group.id, editor, options, context); this._onWillOpenEditor.fire(event); const prevented = event.isPrevented(); if (prevented) { @@ -1110,7 +1131,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Move across groups else { - this.doMoveOrCopyEditorAcrossGroups(editor, target, options); + this.doMoveOrCopyEditorAcrossGroups(editor, target, options, false); } } @@ -1147,16 +1168,17 @@ export class EditorGroupView extends Themable implements IEditorGroupView { private doMoveOrCopyEditorAcrossGroups(editor: EditorInput, target: IEditorGroupView, moveOptions: IMoveEditorOptions = Object.create(null), keepCopy?: boolean): void { - // When moving an editor, try to preserve as much view state as possible by checking - // for the editor to be a text editor and creating the options accordingly if so + // When moving/copying an editor, try to preserve as much view state as possible + // by checking for the editor to be a text editor and creating the options accordingly + // if so const options = getActiveTextEditorOptions(this, editor, EditorOptions.create({ ...moveOptions, - pinned: true, // always pin moved editor - sticky: this._group.isSticky(editor) // preserve sticky state + pinned: true, // always pin moved editor + sticky: !keepCopy && this._group.isSticky(editor) // preserve sticky state only if editor is moved (https://github.com/microsoft/vscode/issues/99035) })); // A move to another group is an open first... - target.openEditor(editor, options); + target.openEditor(editor, options, keepCopy ? OpenEditorContext.COPY_EDITOR : OpenEditorContext.MOVE_EDITOR); // ...and a close afterwards (unless we copy) if (!keepCopy) { @@ -1337,8 +1359,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView { return false; // editor must be dirty and not saving } - if (editor instanceof SideBySideEditorInput && this._group.contains(editor.master)) { - return false; // master-side of editor is still opened somewhere else + if (editor instanceof SideBySideEditorInput && this._group.contains(editor.primary)) { + return false; // primary-side of editor is still opened somewhere else } // Note: we explicitly decide to ask for confirm if closing a normal editor even @@ -1356,8 +1378,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView { return true; // exact editor still opened } - if (editor instanceof SideBySideEditorInput && otherGroup.contains(editor.master)) { - return true; // master side of side by side editor still opened + if (editor instanceof SideBySideEditorInput && otherGroup.contains(editor.primary)) { + return true; // primary side of side by side editor still opened } return false; @@ -1382,7 +1404,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { let name: string; if (editor instanceof SideBySideEditorInput) { - name = editor.master.getName(); // prefer shorter names by using master's name in this case + name = editor.primary.getName(); // prefer shorter names by using primary's name in this case } else { name = editor.getName(); } @@ -1494,7 +1516,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } // Forward to title control - this.titleAreaControl.closeEditors(editors); + if (editors.length) { + this.titleAreaControl.closeEditors(editors); + } } //#endregion @@ -1542,7 +1566,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } // Forward to title control - this.titleAreaControl.closeEditors(editorsToClose); + if (editorsToClose.length) { + this.titleAreaControl.closeEditors(editorsToClose); + } } //#endregion @@ -1702,7 +1728,8 @@ class EditorOpeningEvent implements IEditorOpeningEvent { constructor( private _group: GroupIdentifier, private _editor: EditorInput, - private _options: EditorOptions | undefined + private _options: EditorOptions | undefined, + private _context: OpenEditorContext | undefined ) { } @@ -1718,6 +1745,10 @@ class EditorOpeningEvent implements IEditorOpeningEvent { return this._options; } + get context(): OpenEditorContext | undefined { + return this._context; + } + prevent(callback: () => Promise): void { this.override = callback; } diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 653d80c73ed..ca5e8da0ea5 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -6,13 +6,13 @@ import 'vs/workbench/browser/parts/editor/editor.contribution'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Part } from 'vs/workbench/browser/part'; -import { Dimension, isAncestor, toggleClass, addClass, $, EventHelper } from 'vs/base/browser/dom'; +import { Dimension, isAncestor, toggleClass, addClass, $, EventHelper, addDisposableGenericMouseDownListner } from 'vs/base/browser/dom'; import { Event, Emitter, Relay } from 'vs/base/common/event'; import { contrastBorder, editorBackground } from 'vs/platform/theme/common/colorRegistry'; -import { GroupDirection, IAddGroupOptions, GroupsArrangement, GroupOrientation, IMergeGroupOptions, MergeGroupMode, ICopyEditorOptions, GroupsOrder, GroupChangeKind, GroupLocation, IFindGroupScope, EditorGroupLayout, GroupLayoutArgument, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { GroupDirection, IAddGroupOptions, GroupsArrangement, GroupOrientation, IMergeGroupOptions, MergeGroupMode, GroupsOrder, GroupChangeKind, GroupLocation, IFindGroupScope, EditorGroupLayout, GroupLayoutArgument, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IView, orthogonal, LayoutPriority, IViewSize, Direction, SerializableGrid, Sizing, ISerializedGrid, Orientation, GridBranchNode, isGridBranchNode, GridNode, createSerializedGrid, Grid } from 'vs/base/browser/ui/grid/grid'; -import { GroupIdentifier, IWorkbenchEditorConfiguration, IEditorPartOptions, IEditorPartOptionsChangeEvent } from 'vs/workbench/common/editor'; +import { GroupIdentifier, IEditorPartOptions, IEditorPartOptionsChangeEvent } from 'vs/workbench/common/editor'; import { EDITOR_GROUP_BORDER, EDITOR_PANE_BACKGROUND } from 'vs/workbench/common/theme'; import { distinct, coalesce } from 'vs/base/common/arrays'; import { IEditorGroupsAccessor, IEditorGroupView, getEditorPartOptions, impactsEditorPartOptions, IEditorPartCreationOptions } from 'vs/workbench/browser/parts/editor/editor'; @@ -21,11 +21,12 @@ import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/co import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup'; -import { EditorDropTarget, EditorDropTargetDelegate } from 'vs/workbench/browser/parts/editor/editorDropTarget'; +import { EditorDropTarget, IEditorDropTargetDelegate } from 'vs/workbench/browser/parts/editor/editorDropTarget'; +import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorDropService'; import { Color } from 'vs/base/common/color'; import { CenteredViewLayout } from 'vs/base/browser/ui/centered/centeredViewLayout'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; +import { Parts, IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/browser/layoutService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { MementoObject } from 'vs/workbench/common/memento'; import { assertIsDefined } from 'vs/base/common/types'; @@ -80,9 +81,9 @@ class GridWidgetView implements IView { } } -export class EditorPart extends Part implements IEditorGroupsService, IEditorGroupsAccessor { +export class EditorPart extends Part implements IEditorGroupsService, IEditorGroupsAccessor, IEditorDropService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private static readonly EDITOR_PART_UI_STATE_STORAGE_KEY = 'editorpart.state'; private static readonly EDITOR_PART_CENTERED_VIEW_STORAGE_KEY = 'editorpart.centeredview'; @@ -123,8 +124,6 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro private readonly workspaceMemento: MementoObject; private readonly globalMemento: MementoObject; - private _partOptions: IEditorPartOptions; - private readonly groupViews = new Map(); private mostRecentActiveGroups: GroupIdentifier[] = []; @@ -149,8 +148,6 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro this.gridWidgetView = new GridWidgetView(); - this._partOptions = getEditorPartOptions(this.configurationService.getValue()); - this.workspaceMemento = this.getMemento(StorageScope.WORKSPACE); this.globalMemento = this.getMemento(StorageScope.GLOBAL); @@ -161,6 +158,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro private registerListeners(): void { this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e))); + this._register(this.themeService.onDidFileIconThemeChange(() => this.handleChangedPartOptions())); } private onConfigurationUpdated(event: IConfigurationChangeEvent): void { @@ -171,7 +169,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro private handleChangedPartOptions(): void { const oldPartOptions = this._partOptions; - const newPartOptions = getEditorPartOptions(this.configurationService.getValue()); + const newPartOptions = getEditorPartOptions(this.configurationService, this.themeService); this.enforcedPartOptions.forEach(enforcedPartOptions => { Object.assign(newPartOptions, enforcedPartOptions); // check for overrides @@ -186,9 +184,8 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro private enforcedPartOptions: IEditorPartOptions[] = []; - get partOptions(): IEditorPartOptions { - return this._partOptions; - } + private _partOptions = getEditorPartOptions(this.configurationService, this.themeService); + get partOptions(): IEditorPartOptions { return this._partOptions; } enforcePartOptions(options: IEditorPartOptions): IDisposable { this.enforcedPartOptions.push(options); @@ -446,7 +443,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro // Events for groups that got added this.getGroups(GroupsOrder.GRID_APPEARANCE).forEach(groupView => { - if (currentGroupViews.indexOf(groupView) === -1) { + if (!currentGroupViews.includes(groupView)) { this._onDidAddGroup.fire(groupView); } }); @@ -747,12 +744,13 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro let index = (options && typeof options.index === 'number') ? options.index : targetView.count; sourceView.editors.forEach(editor => { const inactive = !sourceView.isActive(editor) || this._activeGroup !== sourceView; - const copyOptions: ICopyEditorOptions = { index, inactive, preserveFocus: inactive }; + const sticky = sourceView.isSticky(editor); + const editorOptions = { index: !sticky ? index : undefined /* do not set index to preserve sticky flag */, inactive, preserveFocus: inactive }; if (options?.mode === MergeGroupMode.COPY_EDITORS) { - sourceView.copyEditor(editor, targetView, copyOptions); + sourceView.copyEditor(editor, targetView, editorOptions); } else { - sourceView.moveEditor(editor, targetView, copyOptions); + sourceView.moveEditor(editor, targetView, editorOptions); } index++; @@ -783,6 +781,14 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro //#endregion + //#region IEditorDropService + + createEditorDropTarget(container: HTMLElement, delegate: IEditorDropTargetDelegate): IDisposable { + return this.instantiationService.createInstance(EditorDropTarget, this, container, delegate); + } + + //#endregion + //#region Part // TODO @sbatten @joao find something better to prevent editor taking over #79897 @@ -823,13 +829,18 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro this.centeredLayoutWidget = this._register(new CenteredViewLayout(this.container, this.gridWidgetView, this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY])); // Drop support - this._register(this.createEditorDropTarget(this.container, {})); + this._register(this.createEditorDropTarget(this.container, Object.create(null))); // No drop in the editor const overlay = document.createElement('div'); addClass(overlay, 'drop-block-overlay'); parent.appendChild(overlay); + // Hide the block if a mouse down event occurs #99065 + this._register(addDisposableGenericMouseDownListner(overlay, e => { + toggleClass(overlay, 'visible', false); + })); + this._register(CompositeDragAndDropObserver.INSTANCE.registerTarget(this.element, { onDragStart: e => { toggleClass(overlay, 'visible', true); @@ -839,12 +850,62 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro } })); + let panelOpenerTimeout: any; this._register(CompositeDragAndDropObserver.INSTANCE.registerTarget(overlay, { onDragOver: e => { EventHelper.stop(e.eventData, true); if (e.eventData.dataTransfer) { e.eventData.dataTransfer.dropEffect = 'none'; } + + if (!this.layoutService.isVisible(Parts.PANEL_PART)) { + const boundingRect = overlay.getBoundingClientRect(); + + let openPanel = false; + const proximity = 100; + switch (this.layoutService.getPanelPosition()) { + case Position.BOTTOM: + if (e.eventData.clientY > boundingRect.bottom - proximity) { + openPanel = true; + } + break; + case Position.LEFT: + if (e.eventData.clientX < boundingRect.left + proximity) { + openPanel = true; + } + break; + case Position.RIGHT: + if (e.eventData.clientX > boundingRect.right - proximity) { + openPanel = true; + } + break; + } + + if (!panelOpenerTimeout && openPanel) { + panelOpenerTimeout = setTimeout(() => this.layoutService.setPanelHidden(false), 200); + } else if (panelOpenerTimeout && !openPanel) { + clearTimeout(panelOpenerTimeout); + panelOpenerTimeout = undefined; + } + } + }, + onDragLeave: () => { + if (panelOpenerTimeout) { + clearTimeout(panelOpenerTimeout); + panelOpenerTimeout = undefined; + } + }, + onDragEnd: () => { + if (panelOpenerTimeout) { + clearTimeout(panelOpenerTimeout); + panelOpenerTimeout = undefined; + } + }, + onDrop: () => { + if (panelOpenerTimeout) { + clearTimeout(panelOpenerTimeout); + panelOpenerTimeout = undefined; + } } })); @@ -1045,16 +1106,24 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro } // Persist centered view state - const centeredLayoutState = this.centeredLayoutWidget.state; - if (this.centeredLayoutWidget.isDefault(centeredLayoutState)) { - delete this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY]; - } else { - this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY] = centeredLayoutState; + if (this.centeredLayoutWidget) { + const centeredLayoutState = this.centeredLayoutWidget.state; + if (this.centeredLayoutWidget.isDefault(centeredLayoutState)) { + delete this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY]; + } else { + this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY] = centeredLayoutState; + } } super.saveState(); } + toJSON(): object { + return { + type: Parts.EDITOR_PART + }; + } + dispose(): void { // Forward to all groups @@ -1070,20 +1139,18 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro } //#endregion +} - toJSON(): object { - return { - type: Parts.EDITOR_PART - }; +class EditorDropService implements IEditorDropService { + + declare readonly _serviceBrand: undefined; + + constructor(@IEditorGroupsService private readonly editorPart: EditorPart) { } + + createEditorDropTarget(container: HTMLElement, delegate: IEditorDropTargetDelegate): IDisposable { + return this.editorPart.createEditorDropTarget(container, delegate); } - - //#region TODO@matt this should move into some kind of service - - createEditorDropTarget(container: HTMLElement, delegate: EditorDropTargetDelegate): IDisposable { - return this.instantiationService.createInstance(EditorDropTarget, this, container, delegate); - } - - //#endregion } registerSingleton(IEditorGroupsService, EditorPart); +registerSingleton(IEditorDropService, EditorDropService); diff --git a/src/vs/workbench/browser/parts/editor/editorQuickAccess.ts b/src/vs/workbench/browser/parts/editor/editorQuickAccess.ts index 60e46cc5c02..231efdd147a 100644 --- a/src/vs/workbench/browser/parts/editor/editorQuickAccess.ts +++ b/src/vs/workbench/browser/parts/editor/editorQuickAccess.ts @@ -137,7 +137,7 @@ export abstract class BaseEditorQuickAccessProvider extends PickerQuickAccessPro } return this.doGetEditors().map(({ editor, groupId }): IEditorQuickPickItem => { - const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }); + const resource = toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY }); const isDirty = editor.isDirty() && !editor.isSaving(); const description = editor.getDescription(); const nameAndDescription = description ? `${editor.getName()} ${description}` : editor.getName(); diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index d6f0950099d..14dca63bc68 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -32,7 +32,8 @@ import { Selection } from 'vs/editor/common/core/selection'; import { TabFocus } from 'vs/editor/common/config/commonEditorConfig'; import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { ITextFileService, SUPPORTED_ENCODINGS } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { SUPPORTED_ENCODINGS } from 'vs/workbench/services/textfile/common/encoding'; import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { ConfigurationChangedEvent, IEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; @@ -54,22 +55,22 @@ import { STATUS_BAR_PROMINENT_ITEM_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_FOREGRO import { themeColorFromId } from 'vs/platform/theme/common/themeService'; class SideBySideEditorEncodingSupport implements IEncodingSupport { - constructor(private master: IEncodingSupport, private details: IEncodingSupport) { } + constructor(private primary: IEncodingSupport, private secondary: IEncodingSupport) { } getEncoding(): string | undefined { - return this.master.getEncoding(); // always report from modified (right hand) side + return this.primary.getEncoding(); // always report from modified (right hand) side } setEncoding(encoding: string, mode: EncodingMode): void { - [this.master, this.details].forEach(editor => editor.setEncoding(encoding, mode)); + [this.primary, this.secondary].forEach(editor => editor.setEncoding(encoding, mode)); } } class SideBySideEditorModeSupport implements IModeSupport { - constructor(private master: IModeSupport, private details: IModeSupport) { } + constructor(private primary: IModeSupport, private secondary: IModeSupport) { } setMode(mode: string): void { - [this.master, this.details].forEach(editor => editor.setMode(mode)); + [this.primary, this.secondary].forEach(editor => editor.setMode(mode)); } } @@ -82,14 +83,14 @@ function toEditorWithEncodingSupport(input: IEditorInput): IEncodingSupport | nu // Side by Side (diff) Editor if (input instanceof SideBySideEditorInput) { - const masterEncodingSupport = toEditorWithEncodingSupport(input.master); - const detailsEncodingSupport = toEditorWithEncodingSupport(input.details); + const primaryEncodingSupport = toEditorWithEncodingSupport(input.primary); + const secondaryEncodingSupport = toEditorWithEncodingSupport(input.secondary); - if (masterEncodingSupport && detailsEncodingSupport) { - return new SideBySideEditorEncodingSupport(masterEncodingSupport, detailsEncodingSupport); + if (primaryEncodingSupport && secondaryEncodingSupport) { + return new SideBySideEditorEncodingSupport(primaryEncodingSupport, secondaryEncodingSupport); } - return masterEncodingSupport; + return primaryEncodingSupport; } // File or Resource Editor @@ -111,14 +112,14 @@ function toEditorWithModeSupport(input: IEditorInput): IModeSupport | null { // Side by Side (diff) Editor if (input instanceof SideBySideEditorInput) { - const masterModeSupport = toEditorWithModeSupport(input.master); - const detailsModeSupport = toEditorWithModeSupport(input.details); + const primaryModeSupport = toEditorWithModeSupport(input.primary); + const secondaryModeSupport = toEditorWithModeSupport(input.secondary); - if (masterModeSupport && detailsModeSupport) { - return new SideBySideEditorModeSupport(masterModeSupport, detailsModeSupport); + if (primaryModeSupport && secondaryModeSupport) { + return new SideBySideEditorModeSupport(primaryModeSupport, secondaryModeSupport); } - return masterModeSupport; + return primaryModeSupport; } // File or Resource Editor @@ -296,7 +297,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { private readonly screenRedearModeElement = this._register(new MutableDisposable()); private readonly indentationElement = this._register(new MutableDisposable()); private readonly selectionElement = this._register(new MutableDisposable()); - private readonly encodingElement = Object.keys(SUPPORTED_ENCODINGS).length > 1 ? this._register(new MutableDisposable()) : undefined; + private readonly encodingElement = this._register(new MutableDisposable()); private readonly eolElement = this._register(new MutableDisposable()); private readonly modeElement = this._register(new MutableDisposable()); private readonly metadataElement = this._register(new MutableDisposable()); @@ -483,10 +484,6 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { } private updateEncodingElement(text: string | undefined): void { - if (!this.encodingElement) { - return; // return early if encoding should not show (e.g. in Web we only support utf8) - } - if (!text) { this.encodingElement.clear(); return; @@ -685,14 +682,14 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { else if (activeEditorPane instanceof BaseBinaryResourceEditor || activeEditorPane instanceof BinaryResourceDiffEditor) { const binaryEditors: BaseBinaryResourceEditor[] = []; if (activeEditorPane instanceof BinaryResourceDiffEditor) { - const details = activeEditorPane.getDetailsEditorPane(); - if (details instanceof BaseBinaryResourceEditor) { - binaryEditors.push(details); + const primary = activeEditorPane.getPrimaryEditorPane(); + if (primary instanceof BaseBinaryResourceEditor) { + binaryEditors.push(primary); } - const master = activeEditorPane.getMasterEditorPane(); - if (master instanceof BaseBinaryResourceEditor) { - binaryEditors.push(master); + const secondary = activeEditorPane.getSecondaryEditorPane(); + if (secondary instanceof BaseBinaryResourceEditor) { + binaryEditors.push(secondary); } } else { binaryEditors.push(activeEditorPane); @@ -871,7 +868,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { private onResourceEncodingChange(resource: URI): void { const activeEditorPane = this.editorService.activeEditorPane; if (activeEditorPane) { - const activeResource = toResource(activeEditorPane.input, { supportSideBySide: SideBySideEditor.MASTER }); + const activeResource = toResource(activeEditorPane.input, { supportSideBySide: SideBySideEditor.PRIMARY }); if (activeResource && isEqual(activeResource, resource)) { const activeCodeEditor = withNullAsUndefined(getCodeEditor(activeEditorPane.getControl())); @@ -1064,7 +1061,7 @@ export class ChangeModeAction extends Action { } const textModel = activeTextEditorControl.getModel(); - const resource = this.editorService.activeEditor ? toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }) : null; + const resource = this.editorService.activeEditor ? toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }) : null; let hasLanguageSupport = !!resource; if (resource?.scheme === Schemas.untitled && !this.textFileService.untitled.get(resource)?.hasAssociatedFilePath) { @@ -1161,7 +1158,7 @@ export class ChangeModeAction extends Action { let languageSelection: ILanguageSelection | undefined; if (pick === autoDetectMode) { if (textModel) { - const resource = toResource(activeEditor, { supportSideBySide: SideBySideEditor.MASTER }); + const resource = toResource(activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }); if (resource) { languageSelection = this.modeService.createByFilepathOrFirstLine(resource, textModel.getLineContent(1)); } @@ -1356,7 +1353,7 @@ export class ChangeEncodingAction extends Action { await timeout(50); // quick input is sensitive to being opened so soon after another - const resource = toResource(activeEditorPane.input, { supportSideBySide: SideBySideEditor.MASTER }); + const resource = toResource(activeEditorPane.input, { supportSideBySide: SideBySideEditor.PRIMARY }); if (!resource || (!this.fileService.canHandleResource(resource) && resource.scheme !== Schemas.untitled)) { return; // encoding detection only possible for resources the file service can handle or that are untitled } diff --git a/src/vs/workbench/browser/parts/editor/editorsObserver.ts b/src/vs/workbench/browser/parts/editor/editorsObserver.ts index 4573dbdeb1a..a14a7fdc9c0 100644 --- a/src/vs/workbench/browser/parts/editor/editorsObserver.ts +++ b/src/vs/workbench/browser/parts/editor/editorsObserver.ts @@ -185,7 +185,7 @@ export class EditorsObserver extends Disposable { } private updateEditorResourcesMap(editor: IEditorInput, add: boolean): void { - const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }); + const resource = toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY }); if (!resource) { return; // require a resource } diff --git a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css index 992011d8288..dbf66755d80 100644 --- a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css @@ -62,8 +62,8 @@ padding-left: 10px; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon-theme.close-button-right, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon-theme.close-button-off:not(.sticky) { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon.close-button-right, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon.close-button-off:not(.sticky) { padding-left: 5px; /* reduce padding when we show icons and are in shrinking mode and tab close button is not left (unless sticky) */ } @@ -198,7 +198,7 @@ opacity: 0; /* when tab has the focus this shade breaks the tab border (fixes https://github.com/Microsoft/vscode/issues/57819) */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky:not(.has-icon-theme) .monaco-icon-label { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky:not(.has-icon) .monaco-icon-label { text-align: center; /* ensure that sticky tabs without icon have label centered */ } @@ -211,7 +211,7 @@ text-overflow: clip; } -.hc-black .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-container { +.monaco-workbench.hc-black .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-container { text-overflow: ellipsis; } diff --git a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css index 9d825abeb16..59ab0d4afac 100644 --- a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css @@ -28,8 +28,8 @@ /* Title Actions */ -.monaco-workbench .part.editor > .content .editor-group-container > .title .title-actions .action-label, -.monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions .action-label { +.monaco-workbench .part.editor > .content .editor-group-container > .title .title-actions .action-label:not(span), +.monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions .action-label:not(span) { display: flex; height: 35px; min-width: 28px; @@ -40,8 +40,8 @@ background-repeat: no-repeat; } -.hc-black .monaco-workbench .part.editor > .content .editor-group-container > .title .title-actions .action-label, -.hc-black .monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions .action-label:not(.codicon) { +.monaco-workbench.hc-black .part.editor > .content .editor-group-container > .title .title-actions .action-label, +.monaco-workbench.hc-black .part.editor > .content .editor-group-container > .title .editor-actions .action-label:not(.codicon) { line-height: initial; } diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index f3816e52f9c..f96ff3982d2 100644 --- a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts @@ -9,7 +9,7 @@ import { TitleControl, IToolbarActions } from 'vs/workbench/browser/parts/editor import { ResourceLabel, IResourceLabel } from 'vs/workbench/browser/labels'; import { TAB_ACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme'; import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch'; -import { addDisposableListener, EventType, addClass, EventHelper, removeClass, toggleClass } from 'vs/base/browser/dom'; +import { addDisposableListener, EventType, addClass, EventHelper, removeClass, toggleClass, Dimension } from 'vs/base/browser/dom'; import { EDITOR_TITLE_HEIGHT } from 'vs/workbench/browser/parts/editor/editor'; import { IAction } from 'vs/base/common/actions'; import { CLOSE_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; @@ -67,10 +67,10 @@ export class NoTabsTitleControl extends TitleControl { this._register(addDisposableListener(titleContainer, EventType.DBLCLICK, (e: MouseEvent) => this.onTitleDoubleClick(e))); // Detect mouse click - this._register(addDisposableListener(titleContainer, EventType.MOUSE_UP, (e: MouseEvent) => this.onTitleClick(e))); + this._register(addDisposableListener(titleContainer, EventType.AUXCLICK, (e: MouseEvent) => this.onTitleAuxClick(e))); // Detect touch - this._register(addDisposableListener(titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleClick(e))); + this._register(addDisposableListener(titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleTap(e))); // Context Menu this._register(addDisposableListener(titleContainer, EventType.CONTEXT_MENU, (e: Event) => { @@ -98,25 +98,21 @@ export class NoTabsTitleControl extends TitleControl { this.group.pinEditor(); } - private onTitleClick(e: MouseEvent | GestureEvent): void { - if (e instanceof MouseEvent) { - // Close editor on middle mouse click - if (e.button === 1 /* Middle Button */) { - EventHelper.stop(e, true /* for https://github.com/Microsoft/vscode/issues/56715 */); + private onTitleAuxClick(e: MouseEvent): void { + if (e.button === 1 /* Middle Button */ && this.group.activeEditor) { + EventHelper.stop(e, true /* for https://github.com/Microsoft/vscode/issues/56715 */); - if (this.group.activeEditor) { - this.group.closeEditor(this.group.activeEditor); - } - } - } else { - // TODO@rebornix - // gesture tap should open the quick access - // editorGroupView will focus on the editor again when there are mouse/pointer/touch down events - // we need to wait a bit as `GesureEvent.Tap` is generated from `touchstart` and then `touchend` evnets, which are not an atom event. - setTimeout(() => this.quickInputService.quickAccess.show(), 50); + this.group.closeEditor(this.group.activeEditor); } } + private onTitleTap(e: GestureEvent): void { + // TODO@rebornix gesture tap should open the quick access + // editorGroupView will focus on the editor again when there are mouse/pointer/touch down events + // we need to wait a bit as `GesureEvent.Tap` is generated from `touchstart` and then `touchend` evnets, which are not an atom event. + setTimeout(() => this.quickInputService.quickAccess.show(), 50); + } + getPreferredHeight(): number { return EDITOR_TITLE_HEIGHT; } @@ -232,7 +228,7 @@ export class NoTabsTitleControl extends TitleControl { private redraw(): void { const editor = withNullAsUndefined(this.group.activeEditor); - const isEditorPinned = this.group.activeEditor ? this.group.isPinned(this.group.activeEditor) : false; + const isEditorPinned = editor ? this.group.isPinned(editor) : false; const isGroupActive = this.accessor.activeGroup === this.group; this.activeLabel = { editor, pinned: isEditorPinned }; @@ -320,4 +316,10 @@ export class NoTabsTitleControl extends TitleControl { // Group inactive: only show close action return { primaryEditorActions: editorActions.primary.filter(action => action.id === CLOSE_EDITOR_COMMAND_ID), secondaryEditorActions: [] }; } + + layout(dimension: Dimension): void { + if (this.breadcrumbsControl) { + this.breadcrumbsControl.layout(undefined); + } + } } diff --git a/src/vs/workbench/browser/parts/editor/rangeDecorations.ts b/src/vs/workbench/browser/parts/editor/rangeDecorations.ts index 98304f2366b..5f3a4cf23c5 100644 --- a/src/vs/workbench/browser/parts/editor/rangeDecorations.ts +++ b/src/vs/workbench/browser/parts/editor/rangeDecorations.ts @@ -5,7 +5,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter } from 'vs/base/common/event'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IRange } from 'vs/editor/common/core/range'; import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; @@ -26,9 +26,11 @@ export class RangeHighlightDecorations extends Disposable { private readonly editorDisposables = this._register(new DisposableStore()); private readonly _onHighlightRemoved: Emitter = this._register(new Emitter()); - readonly onHighlightRemoved: Event = this._onHighlightRemoved.event; + readonly onHighlightRemoved = this._onHighlightRemoved.event; - constructor(@IEditorService private readonly editorService: IEditorService) { + constructor( + @IEditorService private readonly editorService: IEditorService + ) { super(); } diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts index fd57b26ca33..aff171a997f 100644 --- a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts @@ -22,17 +22,16 @@ import { assertIsDefined } from 'vs/base/common/types'; export class SideBySideEditor extends BaseEditor { static readonly ID: string = 'workbench.editor.sidebysideEditor'; - static MASTER: SideBySideEditor | undefined; - get minimumMasterWidth() { return this.masterEditorPane ? this.masterEditorPane.minimumWidth : 0; } - get maximumMasterWidth() { return this.masterEditorPane ? this.masterEditorPane.maximumWidth : Number.POSITIVE_INFINITY; } - get minimumMasterHeight() { return this.masterEditorPane ? this.masterEditorPane.minimumHeight : 0; } - get maximumMasterHeight() { return this.masterEditorPane ? this.masterEditorPane.maximumHeight : Number.POSITIVE_INFINITY; } + private get minimumPrimaryWidth() { return this.primaryEditorPane ? this.primaryEditorPane.minimumWidth : 0; } + private get maximumPrimaryWidth() { return this.primaryEditorPane ? this.primaryEditorPane.maximumWidth : Number.POSITIVE_INFINITY; } + private get minimumPrimaryHeight() { return this.primaryEditorPane ? this.primaryEditorPane.minimumHeight : 0; } + private get maximumPrimaryHeight() { return this.primaryEditorPane ? this.primaryEditorPane.maximumHeight : Number.POSITIVE_INFINITY; } - get minimumDetailsWidth() { return this.detailsEditorPane ? this.detailsEditorPane.minimumWidth : 0; } - get maximumDetailsWidth() { return this.detailsEditorPane ? this.detailsEditorPane.maximumWidth : Number.POSITIVE_INFINITY; } - get minimumDetailsHeight() { return this.detailsEditorPane ? this.detailsEditorPane.minimumHeight : 0; } - get maximumDetailsHeight() { return this.detailsEditorPane ? this.detailsEditorPane.maximumHeight : Number.POSITIVE_INFINITY; } + private get minimumSecondaryWidth() { return this.secondaryEditorPane ? this.secondaryEditorPane.minimumWidth : 0; } + private get maximumSecondaryWidth() { return this.secondaryEditorPane ? this.secondaryEditorPane.maximumWidth : Number.POSITIVE_INFINITY; } + private get minimumSecondaryHeight() { return this.secondaryEditorPane ? this.secondaryEditorPane.minimumHeight : 0; } + private get maximumSecondaryHeight() { return this.secondaryEditorPane ? this.secondaryEditorPane.maximumHeight : Number.POSITIVE_INFINITY; } // these setters need to exist because this extends from BaseEditor set minimumWidth(value: number) { /* noop */ } @@ -40,16 +39,16 @@ export class SideBySideEditor extends BaseEditor { set minimumHeight(value: number) { /* noop */ } set maximumHeight(value: number) { /* noop */ } - get minimumWidth() { return this.minimumMasterWidth + this.minimumDetailsWidth; } - get maximumWidth() { return this.maximumMasterWidth + this.maximumDetailsWidth; } - get minimumHeight() { return this.minimumMasterHeight + this.minimumDetailsHeight; } - get maximumHeight() { return this.maximumMasterHeight + this.maximumDetailsHeight; } + get minimumWidth() { return this.minimumPrimaryWidth + this.minimumSecondaryWidth; } + get maximumWidth() { return this.maximumPrimaryWidth + this.maximumSecondaryWidth; } + get minimumHeight() { return this.minimumPrimaryHeight + this.minimumSecondaryHeight; } + get maximumHeight() { return this.maximumPrimaryHeight + this.maximumSecondaryHeight; } - protected masterEditorPane?: BaseEditor; - protected detailsEditorPane?: BaseEditor; + protected primaryEditorPane?: BaseEditor; + protected secondaryEditorPane?: BaseEditor; - private masterEditorContainer: HTMLElement | undefined; - private detailsEditorContainer: HTMLElement | undefined; + private primaryEditorContainer: HTMLElement | undefined; + private secondaryEditorContainer: HTMLElement | undefined; private splitview: SplitView | undefined; private dimension: DOM.Dimension = new DOM.Dimension(0, 0); @@ -74,19 +73,19 @@ export class SideBySideEditor extends BaseEditor { const splitview = this.splitview = this._register(new SplitView(parent, { orientation: Orientation.HORIZONTAL })); this._register(this.splitview.onDidSashReset(() => splitview.distributeViewSizes())); - this.detailsEditorContainer = DOM.$('.details-editor-container'); + this.secondaryEditorContainer = DOM.$('.secondary-editor-container'); this.splitview.addView({ - element: this.detailsEditorContainer, - layout: size => this.detailsEditorPane && this.detailsEditorPane.layout(new DOM.Dimension(size, this.dimension.height)), + element: this.secondaryEditorContainer, + layout: size => this.secondaryEditorPane && this.secondaryEditorPane.layout(new DOM.Dimension(size, this.dimension.height)), minimumSize: 220, maximumSize: Number.POSITIVE_INFINITY, onDidChange: Event.None }, Sizing.Distribute); - this.masterEditorContainer = DOM.$('.master-editor-container'); + this.primaryEditorContainer = DOM.$('.primary-editor-container'); this.splitview.addView({ - element: this.masterEditorContainer, - layout: size => this.masterEditorPane && this.masterEditorPane.layout(new DOM.Dimension(size, this.dimension.height)), + element: this.primaryEditorContainer, + layout: size => this.primaryEditorPane && this.primaryEditorPane.layout(new DOM.Dimension(size, this.dimension.height)), minimumSize: 220, maximumSize: Number.POSITIVE_INFINITY, onDidChange: Event.None @@ -103,30 +102,30 @@ export class SideBySideEditor extends BaseEditor { } setOptions(options: EditorOptions | undefined): void { - if (this.masterEditorPane) { - this.masterEditorPane.setOptions(options); + if (this.primaryEditorPane) { + this.primaryEditorPane.setOptions(options); } } protected setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void { - if (this.masterEditorPane) { - this.masterEditorPane.setVisible(visible, group); + if (this.primaryEditorPane) { + this.primaryEditorPane.setVisible(visible, group); } - if (this.detailsEditorPane) { - this.detailsEditorPane.setVisible(visible, group); + if (this.secondaryEditorPane) { + this.secondaryEditorPane.setVisible(visible, group); } super.setEditorVisible(visible, group); } clearInput(): void { - if (this.masterEditorPane) { - this.masterEditorPane.clearInput(); + if (this.primaryEditorPane) { + this.primaryEditorPane.clearInput(); } - if (this.detailsEditorPane) { - this.detailsEditorPane.clearInput(); + if (this.secondaryEditorPane) { + this.secondaryEditorPane.clearInput(); } this.disposeEditors(); @@ -135,8 +134,8 @@ export class SideBySideEditor extends BaseEditor { } focus(): void { - if (this.masterEditorPane) { - this.masterEditorPane.focus(); + if (this.primaryEditorPane) { + this.primaryEditorPane.focus(); } } @@ -148,19 +147,19 @@ export class SideBySideEditor extends BaseEditor { } getControl(): IEditorControl | undefined { - if (this.masterEditorPane) { - return this.masterEditorPane.getControl(); + if (this.primaryEditorPane) { + return this.primaryEditorPane.getControl(); } return undefined; } - getMasterEditorPane(): IEditorPane | undefined { - return this.masterEditorPane; + getPrimaryEditorPane(): IEditorPane | undefined { + return this.primaryEditorPane; } - getDetailsEditorPane(): IEditorPane | undefined { - return this.detailsEditorPane; + getSecondaryEditorPane(): IEditorPane | undefined { + return this.secondaryEditorPane; } private async updateInput(oldInput: SideBySideEditorInput, newInput: SideBySideEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { @@ -172,21 +171,21 @@ export class SideBySideEditor extends BaseEditor { return this.setNewInput(newInput, options, token); } - if (!this.detailsEditorPane || !this.masterEditorPane) { + if (!this.secondaryEditorPane || !this.primaryEditorPane) { return; } await Promise.all([ - this.detailsEditorPane.setInput(newInput.details, undefined, token), - this.masterEditorPane.setInput(newInput.master, options, token) + this.secondaryEditorPane.setInput(newInput.secondary, undefined, token), + this.primaryEditorPane.setInput(newInput.primary, options, token) ]); } private setNewInput(newInput: SideBySideEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { - const detailsEditor = this.doCreateEditor(newInput.details, assertIsDefined(this.detailsEditorContainer)); - const masterEditor = this.doCreateEditor(newInput.master, assertIsDefined(this.masterEditorContainer)); + const secondaryEditor = this.doCreateEditor(newInput.secondary, assertIsDefined(this.secondaryEditorContainer)); + const primaryEditor = this.doCreateEditor(newInput.primary, assertIsDefined(this.primaryEditorContainer)); - return this.onEditorsCreated(detailsEditor, masterEditor, newInput.details, newInput.master, options, token); + return this.onEditorsCreated(secondaryEditor, primaryEditor, newInput.secondary, newInput.primary, options, token); } private doCreateEditor(editorInput: EditorInput, container: HTMLElement): BaseEditor { @@ -202,48 +201,48 @@ export class SideBySideEditor extends BaseEditor { return editor; } - private async onEditorsCreated(details: BaseEditor, master: BaseEditor, detailsInput: EditorInput, masterInput: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { - this.detailsEditorPane = details; - this.masterEditorPane = master; + private async onEditorsCreated(secondary: BaseEditor, primary: BaseEditor, secondaryInput: EditorInput, primaryInput: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { + this.secondaryEditorPane = secondary; + this.primaryEditorPane = primary; this._onDidSizeConstraintsChange.input = Event.any( - Event.map(details.onDidSizeConstraintsChange, () => undefined), - Event.map(master.onDidSizeConstraintsChange, () => undefined) + Event.map(secondary.onDidSizeConstraintsChange, () => undefined), + Event.map(primary.onDidSizeConstraintsChange, () => undefined) ); this.onDidCreateEditors.fire(undefined); await Promise.all([ - this.detailsEditorPane.setInput(detailsInput, undefined, token), - this.masterEditorPane.setInput(masterInput, options, token)] + this.secondaryEditorPane.setInput(secondaryInput, undefined, token), + this.primaryEditorPane.setInput(primaryInput, options, token)] ); } updateStyles(): void { super.updateStyles(); - if (this.masterEditorContainer) { - this.masterEditorContainer.style.boxShadow = `-6px 0 5px -5px ${this.getColor(scrollbarShadow)}`; + if (this.primaryEditorContainer) { + this.primaryEditorContainer.style.boxShadow = `-6px 0 5px -5px ${this.getColor(scrollbarShadow)}`; } } private disposeEditors(): void { - if (this.detailsEditorPane) { - this.detailsEditorPane.dispose(); - this.detailsEditorPane = undefined; + if (this.secondaryEditorPane) { + this.secondaryEditorPane.dispose(); + this.secondaryEditorPane = undefined; } - if (this.masterEditorPane) { - this.masterEditorPane.dispose(); - this.masterEditorPane = undefined; + if (this.primaryEditorPane) { + this.primaryEditorPane.dispose(); + this.primaryEditorPane = undefined; } - if (this.detailsEditorContainer) { - DOM.clearNode(this.detailsEditorContainer); + if (this.secondaryEditorContainer) { + DOM.clearNode(this.secondaryEditorContainer); } - if (this.masterEditorContainer) { - DOM.clearNode(this.masterEditorContainer); + if (this.primaryEditorContainer) { + DOM.clearNode(this.primaryEditorContainer); } } diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 486383db4c8..f3eb689c73a 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/tabstitlecontrol'; import { isMacintosh, isWindows } from 'vs/base/common/platform'; import { shorten } from 'vs/base/common/labels'; -import { toResource, GroupIdentifier, IEditorInput, Verbosity, EditorCommandsContextActionRunner, IEditorPartOptions, SideBySideEditor } from 'vs/workbench/common/editor'; +import { toResource, GroupIdentifier, IEditorInput, Verbosity, EditorCommandsContextActionRunner, IEditorPartOptions, SideBySideEditor, computeEditorAriaLabel } from 'vs/workbench/common/editor'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -31,10 +31,10 @@ import { ResourcesDropHandler, DraggedEditorIdentifier, DraggedEditorGroupIdenti import { Color } from 'vs/base/common/color'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { MergeGroupMode, IMergeGroupOptions, GroupsArrangement } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { MergeGroupMode, IMergeGroupOptions, GroupsArrangement, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { addClass, addDisposableListener, hasClass, EventType, EventHelper, removeClass, Dimension, scheduleAtNextAnimationFrame, findParentWithClass, clearNode } from 'vs/base/browser/dom'; import { localize } from 'vs/nls'; -import { IEditorGroupsAccessor, IEditorGroupView, EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; +import { IEditorGroupsAccessor, IEditorGroupView, EditorServiceImpl, EDITOR_TITLE_HEIGHT } from 'vs/workbench/browser/parts/editor/editor'; import { CloseOneEditorAction } from 'vs/workbench/browser/parts/editor/editorActions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { BreadcrumbsControl } from 'vs/workbench/browser/parts/editor/breadcrumbsControl'; @@ -102,7 +102,8 @@ export class TabsTitleControl extends TitleControl { @IConfigurationService configurationService: IConfigurationService, @IFileService fileService: IFileService, @IEditorService private readonly editorService: EditorServiceImpl, - @IPathService private readonly pathService: IPathService + @IPathService private readonly pathService: IPathService, + @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService ) { super(parent, accessor, group, contextMenuService, instantiationService, contextKeyService, keybindingService, telemetryService, notificationService, menuService, quickInputService, themeService, extensionService, configurationService, fileService); @@ -194,15 +195,12 @@ export class TabsTitleControl extends TitleControl { private updateBreadcrumbsControl(): void { if (this.breadcrumbsControl && this.breadcrumbsControl.update()) { - // relayout when we have a breadcrumbs and when update changed - // its hidden-status - this.group.relayout(); + this.group.relayout(); // relayout when we have a breadcrumbs and when update changed its hidden-status } } protected handleBreadcrumbsEnablementChange(): void { - // relayout when breadcrumbs are enable/disabled - this.group.relayout(); + this.group.relayout(); // relayout when breadcrumbs are enable/disabled } private registerTabsContainerListeners(tabsContainer: HTMLElement, tabsScrollbar: ScrollableElement): void { @@ -390,6 +388,9 @@ export class TabsTitleControl extends TitleControl { closeEditors(editors: IEditorInput[]): void { this.handleClosedEditors(); + if (this.group.count === 0) { + this.updateBreadcrumbsControl(); + } } private handleClosedEditors(): void { @@ -427,9 +428,6 @@ export class TabsTitleControl extends TitleControl { this.clearEditorActionsToolbar(); } - - // Update Breadcrumbs - this.updateBreadcrumbsControl(); } moveEditor(editor: IEditorInput, fromIndex: number, targetIndex: number): void { @@ -524,7 +522,7 @@ export class TabsTitleControl extends TitleControl { oldOptions.tabCloseButton !== newOptions.tabCloseButton || oldOptions.tabSizing !== newOptions.tabSizing || oldOptions.showIcons !== newOptions.showIcons || - oldOptions.iconTheme !== newOptions.iconTheme || + oldOptions.hasIcons !== newOptions.hasIcons || oldOptions.highlightModifiedTabs !== newOptions.highlightModifiedTabs ) { this.redraw(); @@ -603,7 +601,7 @@ export class TabsTitleControl extends TitleControl { const disposables = new DisposableStore(); const handleClickOrTouch = (e: MouseEvent | GestureEvent): void => { - tab.blur(); + tab.blur(); // prevent flicker of focus outline on tab until editor got focus if (e instanceof MouseEvent && e.button !== 0) { if (e.button === 1) { @@ -644,14 +642,17 @@ export class TabsTitleControl extends TitleControl { tabsScrollbar.setScrollPosition({ scrollLeft: tabsScrollbar.getScrollPosition().scrollLeft - e.translationX }); })); - // Close on mouse middle click + // Prevent flicker of focus outline on tab until editor got focus disposables.add(addDisposableListener(tab, EventType.MOUSE_UP, (e: MouseEvent) => { EventHelper.stop(e); tab.blur(); + })); + // Close on mouse middle click + disposables.add(addDisposableListener(tab, EventType.AUXCLICK, (e: MouseEvent) => { if (e.button === 1 /* Middle Button*/) { - e.stopPropagation(); // for https://github.com/Microsoft/vscode/issues/56715 + EventHelper.stop(e, true /* for https://github.com/Microsoft/vscode/issues/56715 */); this.blockRevealActiveTabOnce(); this.closeOneEditorAction.run({ groupId: this.group.id, editorIndex: index }); @@ -886,12 +887,12 @@ export class TabsTitleControl extends TitleControl { const { verbosity, shortenDuplicates } = this.getLabelConfigFlags(labelFormat); // Build labels and descriptions for each editor - const labels = this.group.editors.map(editor => ({ + const labels = this.group.editors.map((editor, index) => ({ editor, name: editor.getName(), description: editor.getDescription(verbosity), title: withNullAsUndefined(editor.getTitle(Verbosity.LONG)), - ariaLabel: editor.isReadonly() ? localize('readonlyEditor', "{0} readonly", editor.getTitle(Verbosity.SHORT)) : editor.getTitle(Verbosity.SHORT) + ariaLabel: computeEditorAriaLabel(editor, index, this.group, this.editorGroupService.count) })); // Shorten labels as needed @@ -1035,10 +1036,10 @@ export class TabsTitleControl extends TitleControl { domAction(tabContainer, `sizing-${option}`); }); - if (options.showIcons && !!options.iconTheme) { - addClass(tabContainer, 'has-icon-theme'); + if (options.showIcons && options.hasIcons) { + addClass(tabContainer, 'has-icon'); } else { - removeClass(tabContainer, 'has-icon-theme'); + removeClass(tabContainer, 'has-icon'); } // Sticky Tabs need a position to remain at their location @@ -1064,7 +1065,7 @@ export class TabsTitleControl extends TitleControl { let name: string | undefined; let description: string; if (isTabSticky) { - const isShowingIcons = this.accessor.partOptions.showIcons && !!this.accessor.partOptions.iconTheme; + const isShowingIcons = this.accessor.partOptions.showIcons && this.accessor.partOptions.hasIcons; name = isShowingIcons ? '' : tabLabel.name?.charAt(0).toUpperCase(); description = ''; } else { @@ -1089,7 +1090,7 @@ export class TabsTitleControl extends TitleControl { ); // Tests helper - const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }); + const resource = toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY }); if (resource) { tabContainer.setAttribute('data-resource-name', basenameOrAuthority(resource)); } else { @@ -1194,6 +1195,10 @@ export class TabsTitleControl extends TitleControl { return hasModifiedBorderColor; } + getPreferredHeight(): number { + return EDITOR_TITLE_HEIGHT + (this.breadcrumbsControl && !this.breadcrumbsControl.isHidden() ? BreadcrumbsControl.HEIGHT : 0); + } + layout(dimension: Dimension | undefined): void { this.dimension = dimension; diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index c74280811de..e03a4159dc4 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -9,7 +9,7 @@ import { isFunction, isObject, isArray, assertIsDefined } from 'vs/base/common/t import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IDiffEditorOptions, IEditorOptions as ICodeEditorOptions } from 'vs/editor/common/config/editorOptions'; import { BaseTextEditor, IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor'; -import { TextEditorOptions, EditorInput, EditorOptions, TEXT_DIFF_EDITOR_ID, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, ITextDiffEditorPane, IEditorMemento } from 'vs/workbench/common/editor'; +import { TextEditorOptions, EditorInput, EditorOptions, TEXT_DIFF_EDITOR_ID, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, ITextDiffEditorPane, IEditorInput } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { DiffNavigator } from 'vs/editor/browser/widget/diffNavigator'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; @@ -28,7 +28,6 @@ import { Event } from 'vs/base/common/event'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { EditorMemento } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorActivation, IEditorOptions } from 'vs/platform/editor/common/editor'; /** @@ -53,8 +52,12 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService); } - protected getEditorMemento(editorGroupService: IEditorGroupsService, key: string, limit: number = 10): IEditorMemento { - return new EditorMemento(this.getId(), key, Object.create(null), limit, editorGroupService); // do not persist in storage as diff editors are never persisted + protected onWillCloseEditorInGroup(editor: IEditorInput): void { + + // React to editors closing to preserve or clear view state. This needs to happen + // in the onWillCloseEditor because at that time the editor has not yet + // been disposed and we can safely persist the view state still as needed. + this.doSaveOrClearTextDiffEditorViewState(editor); } getTitle(): string { @@ -74,8 +77,8 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan // Dispose previous diff navigator this.diffNavigatorDisposables.clear(); - // Remember view settings if input changes - this.saveTextDiffEditorViewState(this.input); + // Update/clear view settings if input changes + this.doSaveOrClearTextDiffEditorViewState(this.input); // Set input and resolve await super.setInput(input, options, token); @@ -199,7 +202,16 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan // Handle diff editor specially by merging in diffEditor configuration if (isObject(configuration.diffEditor)) { - objects.mixin(editorConfiguration, configuration.diffEditor); + // User settings defines `diffEditor.codeLens`, but there is also `editor.codeLens`. + // Due to the mixin, the two settings cannot be distinguished anymore. + // + // So we map `diffEditor.codeLens` to `diffEditor.originalCodeLens` and `diffEditor.modifiedCodeLens`. + const diffEditorConfiguration = objects.deepClone(configuration.diffEditor); + diffEditorConfiguration.originalCodeLens = diffEditorConfiguration.codeLens; + diffEditorConfiguration.modifiedCodeLens = diffEditorConfiguration.codeLens; + delete diffEditorConfiguration.codeLens; + + objects.mixin(editorConfiguration, diffEditorConfiguration); } return editorConfiguration; @@ -215,19 +227,6 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan return options; } - protected getAriaLabel(): string { - let ariaLabel: string; - - const inputName = this.input?.getName(); - if (this.input?.isReadonly()) { - ariaLabel = inputName ? nls.localize('readonlyEditorWithInputAriaLabel', "{0} readonly compare", inputName) : nls.localize('readonlyEditorAriaLabel', "Readonly compare"); - } else { - ariaLabel = inputName ? nls.localize('editableEditorWithInputAriaLabel', "{0} compare", inputName) : nls.localize('editableEditorAriaLabel', "Compare"); - } - - return ariaLabel; - } - private isFileBinaryError(error: Error[]): boolean; private isFileBinaryError(error: Error): boolean; private isFileBinaryError(error: Error | Error[]): boolean { @@ -245,8 +244,8 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan // Dispose previous diff navigator this.diffNavigatorDisposables.clear(); - // Keep editor view state in settings to restore when coming back - this.saveTextDiffEditorViewState(this.input); + // Update/clear editor view state in settings + this.doSaveOrClearTextDiffEditorViewState(this.input); // Clear Model const diffEditor = this.getControl(); @@ -270,7 +269,15 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan return super.loadTextEditorViewState(resource) as IDiffEditorViewState; // overridden for text diff editor support } - private saveTextDiffEditorViewState(input: EditorInput | undefined): void { + protected saveState(): void { + + // Update/clear editor view State + this.doSaveOrClearTextDiffEditorViewState(this.input); + + super.saveState(); + } + + private doSaveOrClearTextDiffEditorViewState(input: IEditorInput | undefined): void { if (!(input instanceof DiffEditorInput)) { return; // only supported for diff editor inputs } @@ -280,9 +287,9 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan return; // unable to retrieve input resource } - // Clear view state if input is disposed - if (input.isDisposed()) { - super.clearTextEditorViewState([resource]); + // Clear view state if input is disposed or we are configured to not storing any state + if (input.isDisposed() || (!this.shouldRestoreViewState && (!this.group || !this.group.isOpened(input)))) { + super.clearTextEditorViewState([resource], this.group); } // Otherwise save it @@ -291,7 +298,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan // Make sure to clean up when the input gets disposed Event.once(input.onDispose)(() => { - super.clearTextEditorViewState([resource]); + super.clearTextEditorViewState([resource], this.group); }); } } diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index e41206a40d9..d3e594728ed 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -10,7 +10,7 @@ import { Event } from 'vs/base/common/event'; import { isObject, assertIsDefined, withNullAsUndefined, isFunction } from 'vs/base/common/types'; import { Dimension } from 'vs/base/browser/dom'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { EditorInput, EditorOptions, IEditorMemento, ITextEditorPane, TextEditorOptions } from 'vs/workbench/common/editor'; +import { EditorInput, EditorOptions, IEditorMemento, ITextEditorPane, TextEditorOptions, IEditorCloseEvent, IEditorInput, computeEditorAriaLabel } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { IEditorViewState, IEditor, ScrollType } from 'vs/editor/common/editorCommon'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -23,6 +23,8 @@ import { isCodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IExtUri } from 'vs/base/common/resources'; +import { MutableDisposable } from 'vs/base/common/lifecycle'; export interface IEditorConfiguration { editor: object; @@ -43,10 +45,19 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditorPa private lastAppliedEditorOptions?: IEditorOptions; private editorMemento: IEditorMemento; + private readonly groupListener = this._register(new MutableDisposable()); + + private _shouldRestoreViewState: boolean | undefined; + protected get shouldRestoreViewState(): boolean | undefined { return this._shouldRestoreViewState; } + + private _instantiationService: IInstantiationService; + protected get instantiationService(): IInstantiationService { return this._instantiationService; } + protected set instantiationService(value: IInstantiationService) { this._instantiationService = value; } + constructor( id: string, @ITelemetryService telemetryService: ITelemetryService, - @IInstantiationService protected readonly instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IStorageService storageService: IStorageService, @ITextResourceConfigurationService protected readonly textResourceConfigurationService: ITextResourceConfigurationService, @IThemeService protected themeService: IThemeService, @@ -54,6 +65,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditorPa @IEditorGroupsService protected editorGroupService: IEditorGroupsService ) { super(id, telemetryService, themeService, storageService); + this._instantiationService = instantiationService; this.editorMemento = this.getEditorMemento(editorGroupService, BaseTextEditor.TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY, 100); @@ -72,9 +84,13 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditorPa this.editorContainer?.setAttribute('aria-label', ariaLabel); this.editorControl?.updateOptions({ ariaLabel }); })); + + this.updateRestoreViewStateConfiguration(); } protected handleConfigurationChangeEvent(configuration?: IEditorConfiguration): void { + this.updateRestoreViewStateConfiguration(); + if (this.isVisible()) { this.updateEditorConfiguration(configuration); } else { @@ -82,6 +98,10 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditorPa } } + private updateRestoreViewStateConfiguration(): void { + this._shouldRestoreViewState = this.textResourceConfigurationService.getValue(undefined, 'workbench.editor.restoreViewState') ?? true /* default */; + } + private consumePendingConfigurationChangeEvent(): void { if (this.hasPendingConfigurationChange) { this.updateEditorConfiguration(); @@ -102,16 +122,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditorPa } private computeAriaLabel(): string { - let ariaLabel = this.getAriaLabel(); - - // Apply group information to help identify in - // which group we are (only if more than one group - // is actually opened) - if (ariaLabel && this.group && this.editorGroupService.count > 1) { - ariaLabel = localize('editorLabelWithGroup', "{0}, {1}", ariaLabel, this.group.ariaLabel); - } - - return ariaLabel; + return this._input ? computeEditorAriaLabel(this._input, undefined, this.group, this.editorGroupService.count) : localize('editor', "Editor"); } protected getConfigurationOverrides(): IEditorOptions { @@ -183,9 +194,23 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditorPa editorControl.onHide(); } + // Listen to close events to trigger `onWillCloseEditorInGroup` + this.groupListener.value = group?.onWillCloseEditor(e => this.onWillCloseEditor(e)); + super.setEditorVisible(visible, group); } + private onWillCloseEditor(e: IEditorCloseEvent): void { + const editor = e.editor; + if (editor === this.input) { + this.onWillCloseEditorInGroup(editor); + } + } + + protected onWillCloseEditorInGroup(editor: IEditorInput): void { + // Subclasses can override + } + focus(): void { // Pass on to Editor @@ -249,8 +274,8 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditorPa return this.group ? this.editorMemento.loadEditorState(this.group, resource) : undefined; } - protected moveTextEditorViewState(source: URI, target: URI): void { - return this.editorMemento.moveEditorState(source, target); + protected moveTextEditorViewState(source: URI, target: URI, comparer: IExtUri): void { + return this.editorMemento.moveEditorState(source, target, comparer); } protected clearTextEditorViewState(resources: URI[], group?: IEditorGroup): void { @@ -303,8 +328,6 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditorPa return undefined; } - protected abstract getAriaLabel(): string; - dispose(): void { this.lastAppliedEditorOptions = undefined; diff --git a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts index 43320493a52..bf60f8d5e3e 100644 --- a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts @@ -25,7 +25,6 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { EditorOption, IEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { basenameOrAuthority } from 'vs/base/common/resources'; import { ModelConstants } from 'vs/editor/common/model'; /** @@ -108,11 +107,6 @@ export class AbstractTextResourceEditor extends BaseTextEditor { } } - protected getAriaLabel(): string { - const inputName = this.input instanceof UntitledTextEditorInput ? basenameOrAuthority(this.input.resource) : this.input?.getName() || nls.localize('writeableEditorAriaLabel', "Editor"); - return this.input?.isReadonly() ? nls.localize('readonlyEditor', "{0} readonly", inputName) : inputName; - } - /** * Reveals the last line of this editor if it has a model set. */ diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index 3c1561f33b1..c693429a87d 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -7,16 +7,16 @@ import 'vs/css!./media/titlecontrol'; import { applyDragImage, DataTransfers } from 'vs/base/browser/dnd'; import { addDisposableListener, Dimension, EventType } from 'vs/base/browser/dom'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { ActionsOrientation, IActionViewItem, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionsOrientation, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; -import { IAction, IRunEvent, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; +import { IAction, IRunEvent, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification, IActionViewItem } from 'vs/base/common/actions'; import * as arrays from 'vs/base/common/arrays'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { dispose, DisposableStore } from 'vs/base/common/lifecycle'; import { getCodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { localize } from 'vs/nls'; -import { createActionViewItem, createAndFillInActionBarActions, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { ExecuteCommandAction, IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { createAndFillInActionBarActions, createAndFillInContextMenuActions, MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { ExecuteCommandAction, IMenu, IMenuService, MenuId, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -31,7 +31,7 @@ import { DraggedEditorGroupIdentifier, DraggedEditorIdentifier, fillResourceData import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs'; import { BreadcrumbsControl, IBreadcrumbsControlOptions } from 'vs/workbench/browser/parts/editor/breadcrumbsControl'; -import { EDITOR_TITLE_HEIGHT, IEditorGroupsAccessor, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; +import { IEditorGroupsAccessor, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; import { EditorCommandsContextActionRunner, IEditorCommandsContext, IEditorInput, toResource, IEditorPartOptions, SideBySideEditor, EditorPinnedContext, EditorStickyContext } from 'vs/workbench/common/editor'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -51,7 +51,7 @@ export abstract class TitleControl extends Themable { protected readonly groupTransfer = LocalSelectionTransfer.getInstance(); protected readonly editorTransfer = LocalSelectionTransfer.getInstance(); - protected breadcrumbsControl?: BreadcrumbsControl; + protected breadcrumbsControl: BreadcrumbsControl | undefined = undefined; private currentPrimaryEditorActionIds: string[] = []; private currentSecondaryEditorActionIds: string[] = []; @@ -117,6 +117,7 @@ export abstract class TitleControl extends Themable { this.handleBreadcrumbsEnablementChange(); } })); + if (config.getValue()) { this.breadcrumbsControl = this.instantiationService.createInstance(BreadcrumbsControl, container, options, this.group); } @@ -162,17 +163,22 @@ export abstract class TitleControl extends Themable { const activeEditorPane = this.group.activeEditorPane; // Check Active Editor - let actionViewItem: IActionViewItem | undefined = undefined; if (activeEditorPane instanceof BaseEditor) { - actionViewItem = activeEditorPane.getActionViewItem(action); + const result = activeEditorPane.getActionViewItem(action); + + if (result) { + return result; + } } // Check extensions - if (!actionViewItem) { - actionViewItem = createActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService); + if (action instanceof MenuItemAction) { + return this.instantiationService.createInstance(MenuEntryActionViewItem, action); + } else if (action instanceof SubmenuItemAction) { + return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action); } - return actionViewItem; + return undefined; } protected updateEditorActionsToolbar(): void { @@ -190,7 +196,7 @@ export abstract class TitleControl extends Themable { secondaryEditorActions.some(action => action instanceof ExecuteCommandAction) // see also https://github.com/Microsoft/vscode/issues/16298 ) { const editorActionsToolbar = assertIsDefined(this.editorActionsToolbar); - editorActionsToolbar.setActions(primaryEditorActions, secondaryEditorActions)(); + editorActionsToolbar.setActions(primaryEditorActions, secondaryEditorActions); this.currentPrimaryEditorActionIds = primaryEditorActionIds; this.currentSecondaryEditorActionIds = secondaryEditorActionIds; @@ -222,9 +228,11 @@ export abstract class TitleControl extends Themable { this.editorToolBarMenuDisposables.clear(); // Update contexts - this.resourceContext.set(this.group.activeEditor ? withUndefinedAsNull(toResource(this.group.activeEditor, { supportSideBySide: SideBySideEditor.MASTER })) : null); - this.editorPinnedContext.set(this.group.activeEditor ? this.group.isPinned(this.group.activeEditor) : false); - this.editorStickyContext.set(this.group.activeEditor ? this.group.isSticky(this.group.activeEditor) : false); + this.contextKeyService.bufferChangeEvents(() => { + this.resourceContext.set(this.group.activeEditor ? withUndefinedAsNull(toResource(this.group.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY })) : null); + this.editorPinnedContext.set(this.group.activeEditor ? this.group.isPinned(this.group.activeEditor) : false); + this.editorStickyContext.set(this.group.activeEditor ? this.group.isSticky(this.group.activeEditor) : false); + }); // Editor actions require the editor control to be there, so we retrieve it via service const activeEditorPane = this.group.activeEditorPane; @@ -237,7 +245,7 @@ export abstract class TitleControl extends Themable { this.updateEditorActionsToolbar(); // Update editor toolbar whenever contributed actions change })); - this.editorToolBarMenuDisposables.add(createAndFillInActionBarActions(titleBarMenu, { arg: this.resourceContext.get(), shouldForwardArgs: true }, { primary, secondary })); + this.editorToolBarMenuDisposables.add(createAndFillInActionBarActions(titleBarMenu, { arg: this.resourceContext.get(), shouldForwardArgs: true }, { primary, secondary }, (group: string) => group === 'navigation' || group === '1_run')); } return { primary, secondary }; @@ -245,7 +253,7 @@ export abstract class TitleControl extends Themable { protected clearEditorActionsToolbar(): void { if (this.editorActionsToolbar) { - this.editorActionsToolbar.setActions([], [])(); + this.editorActionsToolbar.setActions([], []); } this.currentPrimaryEditorActionIds = []; @@ -297,7 +305,7 @@ export abstract class TitleControl extends Themable { } protected doFillResourceDataTransfers(editor: IEditorInput, e: DragEvent): boolean { - const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }); + const resource = toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY }); if (!resource) { return false; } @@ -325,7 +333,7 @@ export abstract class TitleControl extends Themable { // Update contexts based on editor picked and remember previous to restore const currentResourceContext = this.resourceContext.get(); - this.resourceContext.set(withUndefinedAsNull(toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }))); + this.resourceContext.set(withUndefinedAsNull(toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY }))); const currentPinnedContext = !!this.editorPinnedContext.get(); this.editorPinnedContext.set(this.group.isPinned(editor)); const currentStickyContext = !!this.editorStickyContext.get(); @@ -400,15 +408,9 @@ export abstract class TitleControl extends Themable { abstract updateStyles(): void; - layout(dimension: Dimension): void { - if (this.breadcrumbsControl) { - this.breadcrumbsControl.layout(undefined); - } - } + abstract layout(dimension: Dimension): void; - getPreferredHeight(): number { - return EDITOR_TITLE_HEIGHT + (this.breadcrumbsControl && !this.breadcrumbsControl.isHidden() ? BreadcrumbsControl.HEIGHT : 0); - } + abstract getPreferredHeight(): number; dispose(): void { dispose(this.breadcrumbsControl); diff --git a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css index d5646d18f76..9d01ee4526b 100644 --- a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css +++ b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css @@ -121,3 +121,15 @@ height: 2px; bottom: 0; } + +.monaco-workbench.mac:not(.web) .notifications-list-container .monaco-progress-container.infinite .progress-bit { + /** macOS native: reduce animation steps for reduced CPU load (https://github.com/microsoft/vscode/issues/97900) */ + animation-timing-function: steps(100); +} + +@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /** macOS native: do not change the animation-timing-function on highDPI screens to reduce stutter */ + .monaco-workbench.mac:not(.web) .notifications-list-container .monaco-progress-container.infinite .progress-bit { + animation-timing-function: linear; + } +} diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts index d1dec4f90fd..faff5be9118 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts @@ -305,7 +305,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente this.hide(); // Close all - for (const notification of this.model.notifications) { + for (const notification of [...this.model.notifications] /* copy array since we modify it from closing */) { if (!notification.hasProgress) { notification.close(); } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts b/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts index 3c048a86e08..366e9d64b6b 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts @@ -109,18 +109,18 @@ export class NotificationsStatus extends Disposable { return localize('oneNotification', "1 New Notification"); } - return localize('notifications', "{0} New Notifications", this.newNotificationsCount); + return localize({ key: 'notifications', comment: ['{0} will be replaced by a number'] }, "{0} New Notifications", this.newNotificationsCount); } if (this.newNotificationsCount === 0) { - return localize('noNotificationsWithProgress', "No New Notifications ({0} in progress)", notificationsInProgress); + return localize({ key: 'noNotificationsWithProgress', comment: ['{0} will be replaced by a number'] }, "No New Notifications ({0} in progress)", notificationsInProgress); } if (this.newNotificationsCount === 1) { - return localize('oneNotificationWithProgress', "1 New Notification ({0} in progress)", notificationsInProgress); + return localize({ key: 'oneNotificationWithProgress', comment: ['{0} will be replaced by a number'] }, "1 New Notification ({0} in progress)", notificationsInProgress); } - return localize('notificationsWithProgress', "{0} New Notifications ({0} in progress)", this.newNotificationsCount, notificationsInProgress); + return localize({ key: 'notificationsWithProgress', comment: ['{0} and {1} will be replaced by a number'] }, "{0} New Notifications ({1} in progress)", this.newNotificationsCount, notificationsInProgress); } update(isCenterVisible: boolean, isToastsVisible: boolean): void { diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index f78cee5dd95..567ec2a4a1b 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -16,7 +16,6 @@ import { IAction, IActionRunner } from 'vs/base/common/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { dispose, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdown'; import { INotificationViewItem, NotificationViewItem, NotificationViewItemContentChangeKind, INotificationMessage, ChoiceAction } from 'vs/workbench/common/notifications'; import { ClearNotificationAction, ExpandNotificationAction, CollapseNotificationAction, ConfigureNotificationAction } from 'vs/workbench/browser/parts/notifications/notificationsActions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -24,6 +23,7 @@ import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { Severity } from 'vs/platform/notification/common/notification'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import { Codicon } from 'vs/base/common/codicons'; +import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; export class NotificationsListDelegate implements IListVirtualDelegate { @@ -211,7 +211,7 @@ export class NotificationRenderer implements IListRenderer { if (action && action instanceof ConfigureNotificationAction) { - const item = new DropdownMenuActionViewItem(action, action.configurationActions, this.contextMenuService, undefined, this.actionRunner, undefined, action.class); + const item = new DropdownMenuActionViewItem(action, action.configurationActions, this.contextMenuService, { actionRunner: this.actionRunner, classNames: action.class }); data.toDispose.add(item); return item; @@ -307,9 +307,9 @@ export class NotificationTemplateRenderer extends Disposable { // Container toggleClass(this.template.container, 'expanded', notification.expanded); - this.inputDisposables.add(addDisposableListener(this.template.container, EventType.MOUSE_UP, e => { + this.inputDisposables.add(addDisposableListener(this.template.container, EventType.AUXCLICK, e => { if (!notification.hasProgress && e.button === 1 /* Middle Button */) { - EventHelper.stop(e); + EventHelper.stop(e, true); notification.close(); } diff --git a/src/vs/workbench/browser/parts/panel/media/panelpart.css b/src/vs/workbench/browser/parts/panel/media/panelpart.css index 8d0e2625ee0..96d407d0538 100644 --- a/src/vs/workbench/browser/parts/panel/media/panelpart.css +++ b/src/vs/workbench/browser/parts/panel/media/panelpart.css @@ -50,6 +50,15 @@ outline-offset: -2px; } +.monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item.clicked:not(.checked):focus .action-label { + border-bottom-color: transparent !important; /* hides border on clicked state */ +} + +.monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item.checked.clicked:focus .action-label, +.monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item.checked.active:focus .action-label { + border-bottom-color: currentColor !important; +} + /** Panel Switcher */ .monaco-workbench .part.panel > .composite.title > .panel-switcher-container.composite-bar > .monaco-action-bar .action-label.codicon-more { @@ -63,12 +72,9 @@ } .monaco-workbench .part.panel .empty-panel-message-area { - position: absolute; display: none; - top: 0px; height: 100%; width: 100%; - z-index: 10; } .monaco-workbench .part.panel .empty-panel-message-area.visible { @@ -137,7 +143,8 @@ .monaco-workbench .part.panel > .composite.title> .panel-switcher-container > .monaco-action-bar .action-item.left::before, .monaco-workbench .part.panel > .composite.title> .panel-switcher-container > .monaco-action-bar .action-item.right::after, -.monaco-workbench .part.panel > .composite.title.dragged-over > .panel-switcher-container > .monaco-action-bar .action-item:last-of-type::after { +.monaco-workbench .part.panel > .composite.title.dragged-over-head > .panel-switcher-container > .monaco-action-bar .action-item:first-of-type::before, +.monaco-workbench .part.panel > .composite.title.dragged-over-tail > .panel-switcher-container > .monaco-action-bar .action-item:last-of-type::after { opacity: 1; } @@ -156,6 +163,8 @@ .monaco-workbench .part.panel > .composite.title> .panel-switcher-container > .monaco-action-bar .badge { margin-left: 8px; + display: flex; + align-items: center; } .monaco-workbench .part.panel > .composite.title> .panel-switcher-container > .monaco-action-bar .badge .badge-content { @@ -163,25 +172,13 @@ border-radius: 11px; font-size: 11px; min-width: 18px; - min-height: 18px; + height: 18px; line-height: 11px; font-weight: normal; text-align: center; display: inline-block; box-sizing: border-box; - } - -/** Actions */ - -.monaco-workbench .panel .monaco-action-bar .action-item.select-container { - cursor: default; -} - -.monaco-workbench .panel .monaco-action-bar .action-item .monaco-select-box { - cursor: pointer; - min-width: 110px; - min-height: 18px; - padding: 2px 23px 2px 8px; + position: relative; } /* Rotate icons when panel is on right */ diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index d815edc0f9a..cb0d283b52f 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -282,7 +282,6 @@ actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(TogglePanelActi actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(FocusPanelAction), 'View: Focus into Panel', nls.localize('view', "View")); actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleMaximizedPanelAction), 'View: Toggle Maximized Panel', nls.localize('view', "View")); actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ClosePanelAction), 'View: Close Panel', nls.localize('view', "View")); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleMaximizedPanelAction), 'View: Toggle Panel Position', nls.localize('view', "View")); actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(PreviousPanelViewAction), 'View: Previous Panel View', nls.localize('view', "View")); actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NextPanelViewAction), 'View: Next Panel View', nls.localize('view', "View")); diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 2be7d5f6734..84420014b15 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -20,7 +20,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ClosePanelAction, PanelActivityAction, ToggleMaximizedPanelAction, TogglePanelAction, PlaceHolderPanelActivityAction, PlaceHolderToggleCompositePinnedAction, PositionPanelActionConfigs, SetPanelPositionAction } from 'vs/workbench/browser/parts/panel/panelActions'; import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; -import { PANEL_BACKGROUND, PANEL_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIVE_TITLE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_DRAG_AND_DROP_BACKGROUND, PANEL_INPUT_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme'; +import { PANEL_BACKGROUND, PANEL_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIVE_TITLE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_INPUT_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, PANEL_DRAG_AND_DROP_BORDER } from 'vs/workbench/common/theme'; import { activeContrastBorder, focusBorder, contrastBorder, editorBackground, badgeBackground, badgeForeground } from 'vs/platform/theme/common/colorRegistry'; import { CompositeBar, ICompositeBarItem, CompositeDragAndDrop } from 'vs/workbench/browser/parts/compositeBar'; import { ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositeBarActions'; @@ -35,10 +35,10 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ViewContainer, IViewDescriptorService, IViewContainerModel, ViewContainerLocation } from 'vs/workbench/common/views'; import { MenuId } from 'vs/platform/actions/common/actions'; -import { ViewMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions'; +import { ViewMenuActions, ViewContainerMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions'; import { IPaneComposite } from 'vs/workbench/common/panecomposite'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; -import { Before2D, CompositeDragAndDropObserver, ICompositeDragAndDrop } from 'vs/workbench/browser/dnd'; +import { Before2D, CompositeDragAndDropObserver, ICompositeDragAndDrop, toggleDropEffect } from 'vs/workbench/browser/dnd'; import { IActivity } from 'vs/workbench/common/activity'; interface ICachedPanel { @@ -63,7 +63,7 @@ export class PanelPart extends CompositePart implements IPanelService { static readonly PLACEHOLDER_VIEW_CONTAINERS = 'workbench.panel.placeholderPanels'; private static readonly MIN_COMPOSITE_BAR_WIDTH = 50; - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; //#region IView @@ -144,7 +144,8 @@ export class PanelPart extends CompositePart implements IPanelService { this.dndHandler = new CompositeDragAndDrop(this.viewDescriptorService, ViewContainerLocation.Panel, (id: string, focus?: boolean) => (this.openPanel(id, focus) as Promise).then(panel => panel || null), - (from: string, to: string, before?: Before2D) => this.compositeBar.move(from, to, before?.horizontallyBefore) + (from: string, to: string, before?: Before2D) => this.compositeBar.move(from, to, before?.horizontallyBefore), + () => this.compositeBar.getCompositeBarItems() ); this.compositeBar = this._register(this.instantiationService.createInstance(CompositeBar, this.getCachedPanels(), { @@ -175,7 +176,7 @@ export class PanelPart extends CompositePart implements IPanelService { inactiveForegroundColor: theme.getColor(PANEL_INACTIVE_TITLE_FOREGROUND), badgeBackground: theme.getColor(badgeBackground), badgeForeground: theme.getColor(badgeForeground), - dragAndDropBackground: theme.getColor(PANEL_DRAG_AND_DROP_BACKGROUND) + dragAndDropBorder: theme.getColor(PANEL_DRAG_AND_DROP_BORDER) }) })); @@ -196,6 +197,10 @@ export class PanelPart extends CompositePart implements IPanelService { result.push(...viewMenuActions.getContextMenuActions()); viewMenuActions.dispose(); } + + const viewContainerMenuActions = this.instantiationService.createInstance(ViewContainerMenuActions, container.id, MenuId.ViewContainerTitleContext); + result.push(...viewContainerMenuActions.getContextMenuActions()); + viewContainerMenuActions.dispose(); } return result; } @@ -204,10 +209,22 @@ export class PanelPart extends CompositePart implements IPanelService { for (const panel of panels) { const cachedPanel = this.getCachedPanels().filter(({ id }) => id === panel.id)[0]; const activePanel = this.getActivePanel(); - const isActive = activePanel?.getId() === panel.id || (!activePanel && this.getLastActivePanelId() === panel.id); + const isActive = + activePanel?.getId() === panel.id || + (!activePanel && this.getLastActivePanelId() === panel.id) || + (this.extensionsRegistered && this.compositeBar.getVisibleComposites().length === 0); if (isActive || !this.shouldBeHidden(panel.id, cachedPanel)) { - this.compositeBar.addComposite(panel); + + // Override order + const newPanel = { + id: panel.id, + name: panel.name, + order: panel.order, + requestedIndex: panel.requestedIndex + }; + + this.compositeBar.addComposite(newPanel); // Pin it by default if it is new if (!cachedPanel) { @@ -304,6 +321,7 @@ export class PanelPart extends CompositePart implements IPanelService { } private registerListeners(): void { + // Panel registration this._register(this.registry.onDidRegister(panel => this.onDidRegisterPanels([panel]))); this._register(this.registry.onDidDeregister(panel => this.onDidDeregisterPanel(panel.id))); @@ -402,19 +420,22 @@ export class PanelPart extends CompositePart implements IPanelService { } private createEmptyPanelMessage(): void { + const contentArea = this.getContentArea()!; this.emptyPanelMessageElement = document.createElement('div'); addClass(this.emptyPanelMessageElement, 'empty-panel-message-area'); const messageElement = document.createElement('div'); addClass(messageElement, 'empty-panel-message'); - messageElement.innerText = localize('panel.emptyMessage', "No panels to display. Drag a view into the panel."); + messageElement.innerText = localize('panel.emptyMessage', "Drag a view into the panel to display."); this.emptyPanelMessageElement.appendChild(messageElement); - this.element.appendChild(this.emptyPanelMessageElement); + contentArea.appendChild(this.emptyPanelMessageElement); this._register(CompositeDragAndDropObserver.INSTANCE.registerTarget(this.emptyPanelMessageElement, { onDragOver: (e) => { EventHelper.stop(e.eventData, true); + const validDropTarget = this.dndHandler.onDragEnter(e.dragAndDropData, undefined, e.eventData); + toggleDropEffect(e.eventData.dataTransfer, 'move', validDropTarget); }, onDragEnter: (e) => { EventHelper.stop(e.eventData, true); @@ -469,7 +490,7 @@ export class PanelPart extends CompositePart implements IPanelService { } } - return this.openComposite(id, focus); + return this.openComposite(id, focus) as Panel; } async openPanel(id?: string, focus?: boolean): Promise { @@ -512,7 +533,7 @@ export class PanelPart extends CompositePart implements IPanelService { getPinnedPanels(): readonly PanelDescriptor[] { const pinnedCompositeIds = this.compositeBar.getPinnedComposites().map(c => c.id); return this.getPanels() - .filter(p => pinnedCompositeIds.indexOf(p.id) !== -1) + .filter(p => pinnedCompositeIds.includes(p.id)) .sort((p1, p2) => pinnedCompositeIds.indexOf(p1.id) - pinnedCompositeIds.indexOf(p2.id)); } @@ -658,17 +679,14 @@ export class PanelPart extends CompositePart implements IPanelService { const cachedPanels = this.getCachedPanels(); for (const cachedPanel of cachedPanels) { - // Add and update existing items - const existingItem = compositeItems.filter(({ id }) => id === cachedPanel.id)[0]; - if (existingItem) { - newCompositeItems.push({ - id: existingItem.id, - name: existingItem.name, - order: existingItem.order, - pinned: cachedPanel.pinned, - visible: existingItem.visible - }); - } + // copy behavior from activity bar + newCompositeItems.push({ + id: cachedPanel.id, + name: cachedPanel.name, + order: cachedPanel.order, + pinned: cachedPanel.pinned, + visible: !!compositeItems.find(({ id }) => id === cachedPanel.id) + }); } for (let index = 0; index < compositeItems.length; index++) { diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index 5bac199ce4f..0ab81d493b5 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -9,8 +9,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; import { CompositePart } from 'vs/workbench/browser/parts/compositePart'; import { Viewlet, ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IWorkbenchLayoutService, Parts, Position as SideBarPosition } from 'vs/workbench/services/layout/browser/layoutService'; import { IViewlet, SidebarFocusContext, ActiveViewletContext } from 'vs/workbench/common/viewlet'; @@ -19,7 +18,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { Event, Emitter } from 'vs/base/common/event'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; @@ -35,10 +34,11 @@ import { LayoutPriority } from 'vs/base/browser/ui/grid/grid'; import { assertIsDefined } from 'vs/base/common/types'; import { CompositeDragAndDropObserver } from 'vs/workbench/browser/dnd'; import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; export class SidebarPart extends CompositePart implements IViewletService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; static readonly activeViewletSettingsKey = 'workbench.sidebar.activeviewletid'; @@ -302,39 +302,40 @@ export class SidebarPart extends CompositePart implements IViewletServi } } -class FocusSideBarAction extends Action { +class FocusSideBarAction extends Action2 { - static readonly ID = 'workbench.action.focusSideBar'; - static readonly LABEL = nls.localize('focusSideBar', "Focus into Side Bar"); - - constructor( - id: string, - label: string, - @IViewletService private readonly viewletService: IViewletService, - @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService - ) { - super(id, label); + constructor() { + super({ + id: 'workbench.action.focusSideBar', + title: { value: nls.localize('focusSideBar', "Focus into Side Bar"), original: 'Focus into Side Bar' }, + category: { value: nls.localize('view', "View"), original: 'View' }, + f1: true, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + when: null, + primary: KeyMod.CtrlCmd | KeyCode.KEY_0 + } + }); } - async run(): Promise { + async run(accessor: ServicesAccessor): Promise { + const layoutService = accessor.get(IWorkbenchLayoutService); + const viewletService = accessor.get(IViewletService); // Show side bar - if (!this.layoutService.isVisible(Parts.SIDEBAR_PART)) { - this.layoutService.setSideBarHidden(false); + if (!layoutService.isVisible(Parts.SIDEBAR_PART)) { + layoutService.setSideBarHidden(false); return; } // Focus into active viewlet - const viewlet = this.viewletService.getActiveViewlet(); + const viewlet = viewletService.getActiveViewlet(); if (viewlet) { viewlet.focus(); } } } -const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusSideBarAction, { - primary: KeyMod.CtrlCmd | KeyCode.KEY_0 -}), 'View: Focus into Side Bar', nls.localize('viewCategory', "View")); +registerAction2(FocusSideBarAction); registerSingleton(IViewletService, SidebarPart); diff --git a/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css b/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css index 48e69fc36b1..7817be6c553 100644 --- a/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css +++ b/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css @@ -98,6 +98,7 @@ align-items: center; text-overflow: ellipsis; overflow: hidden; + outline-width: 0px; /* do not render focus outline, we already have background */ } .monaco-workbench .part.statusbar > .items-container > .statusbar-item > a:hover { diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index fa4de06a425..e29be7ab5a0 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -9,17 +9,16 @@ import { toErrorMessage } from 'vs/base/common/errorMessage'; import { dispose, IDisposable, Disposable, toDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { Part } from 'vs/workbench/browser/part'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { StatusbarAlignment, IStatusbarService, IStatusbarEntry, IStatusbarEntryAccessor } from 'vs/workbench/services/statusbar/common/statusbar'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { Action, IAction, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; -import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector, ThemeColor } from 'vs/platform/theme/common/themeService'; +import { Action, IAction, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification, Separator } from 'vs/base/common/actions'; +import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector, ThemeColor, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; import { STATUS_BAR_BACKGROUND, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_ITEM_HOVER_BACKGROUND, STATUS_BAR_ITEM_ACTIVE_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_FOREGROUND, STATUS_BAR_PROMINENT_ITEM_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND, STATUS_BAR_BORDER, STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_NO_FOLDER_BORDER } from 'vs/workbench/common/theme'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; +import { contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { isThemeColor } from 'vs/editor/common/editorCommon'; import { Color } from 'vs/base/common/color'; import { addClass, EventHelper, createStyleSheet, addDisposableListener, addClasses, removeClass, EventType, hide, show, removeClasses, isAncestor } from 'vs/base/browser/dom'; @@ -30,7 +29,6 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { coalesce } from 'vs/base/common/arrays'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { ToggleStatusbarVisibilityAction } from 'vs/workbench/browser/actions/layoutActions'; -import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { assertIsDefined } from 'vs/base/common/types'; import { Emitter } from 'vs/base/common/event'; import { Command } from 'vs/editor/common/modes'; @@ -374,7 +372,7 @@ class HideStatusbarEntryAction extends Action { export class StatusbarPart extends Part implements IStatusbarService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; //#region IView @@ -726,7 +724,6 @@ class StatusbarEntryItem extends Disposable { @ICommandService private readonly commandService: ICommandService, @INotificationService private readonly notificationService: INotificationService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IEditorService private readonly editorService: IEditorService, @IThemeService private readonly themeService: IThemeService ) { super(); @@ -740,6 +737,7 @@ class StatusbarEntryItem extends Disposable { // Label Container this.labelContainer = document.createElement('a'); this.labelContainer.tabIndex = -1; // allows screen readers to read title, but still prevents tab focus. + this.labelContainer.setAttribute('role', 'button'); // Label this.label = new CodiconLabel(this.labelContainer); @@ -766,6 +764,9 @@ class StatusbarEntryItem extends Disposable { this.container.setAttribute('aria-label', entry.ariaLabel); this.labelContainer.setAttribute('aria-label', entry.ariaLabel); } + if (!this.entry || entry.role !== this.entry.role) { + this.labelContainer.setAttribute('role', entry.role || 'button'); + } // Update: Tooltip (on the container, because label can be disabled) if (!this.entry || entry.tooltip !== this.entry.tooltip) { @@ -784,7 +785,7 @@ class StatusbarEntryItem extends Disposable { const command = entry.command; if (command) { this.commandMouseListener.value = addDisposableListener(this.labelContainer, EventType.CLICK, () => this.executeCommand(command)); - this.commandKeyboardListener.value = addDisposableListener(this.labelContainer, EventType.KEY_UP, e => { + this.commandKeyboardListener.value = addDisposableListener(this.labelContainer, EventType.KEY_DOWN, e => { const event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.Space) || event.equals(KeyCode.Enter)) { this.executeCommand(command); @@ -829,12 +830,6 @@ class StatusbarEntryItem extends Disposable { const id = typeof command === 'string' ? command : command.id; const args = typeof command === 'string' ? [] : command.arguments ?? []; - // Maintain old behaviour of always focusing the editor here - const activeTextEditorControl = this.editorService.activeTextEditorControl; - if (activeTextEditorControl) { - activeTextEditorControl.focus(); - } - this.telemetryService.publicLog2('workbenchActionExecuted', { id, from: 'status bar' }); try { await this.commandService.executeCommand(id, ...args); @@ -894,15 +889,34 @@ class StatusbarEntryItem extends Disposable { } registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { - const statusBarItemHoverBackground = theme.getColor(STATUS_BAR_ITEM_HOVER_BACKGROUND); - if (statusBarItemHoverBackground) { - collector.addRule(`.monaco-workbench .part.statusbar > .items-container > .statusbar-item a:hover { background-color: ${statusBarItemHoverBackground}; }`); - collector.addRule(`.monaco-workbench .part.statusbar > .items-container > .statusbar-item a:focus { background-color: ${statusBarItemHoverBackground}; }`); + if (theme.type !== HIGH_CONTRAST) { + const statusBarItemHoverBackground = theme.getColor(STATUS_BAR_ITEM_HOVER_BACKGROUND); + if (statusBarItemHoverBackground) { + collector.addRule(`.monaco-workbench .part.statusbar > .items-container > .statusbar-item a:hover { background-color: ${statusBarItemHoverBackground}; }`); + collector.addRule(`.monaco-workbench .part.statusbar > .items-container > .statusbar-item a:focus { background-color: ${statusBarItemHoverBackground}; }`); + } + + const statusBarItemActiveBackground = theme.getColor(STATUS_BAR_ITEM_ACTIVE_BACKGROUND); + if (statusBarItemActiveBackground) { + collector.addRule(`.monaco-workbench .part.statusbar > .items-container > .statusbar-item a:active { background-color: ${statusBarItemActiveBackground}; }`); + } } - const statusBarItemActiveBackground = theme.getColor(STATUS_BAR_ITEM_ACTIVE_BACKGROUND); - if (statusBarItemActiveBackground) { - collector.addRule(`.monaco-workbench .part.statusbar > .items-container > .statusbar-item a:active { background-color: ${statusBarItemActiveBackground}; }`); + const activeContrastBorderColor = theme.getColor(activeContrastBorder); + if (activeContrastBorderColor) { + collector.addRule(` + .monaco-workbench .part.statusbar > .items-container > .statusbar-item a:focus, + .monaco-workbench .part.statusbar > .items-container > .statusbar-item a:active { + outline: 1px solid ${activeContrastBorderColor} !important; + outline-offset: -1px; + } + `); + collector.addRule(` + .monaco-workbench .part.statusbar > .items-container > .statusbar-item a:hover { + outline: 1px dashed ${activeContrastBorderColor}; + outline-offset: -1px; + } + `); } const statusBarProminentItemForeground = theme.getColor(STATUS_BAR_PROMINENT_ITEM_FOREGROUND); @@ -947,6 +961,30 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'workbench.statusBar.focusFirst', + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyCode.Home, + when: CONTEXT_STATUS_BAR_FOCUSED, + handler: (accessor: ServicesAccessor) => { + const statusBarService = accessor.get(IStatusbarService); + statusBarService.focus(false); + statusBarService.focusNextEntry(); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'workbench.statusBar.focusLast', + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyCode.End, + when: CONTEXT_STATUS_BAR_FOCUSED, + handler: (accessor: ServicesAccessor) => { + const statusBarService = accessor.get(IStatusbarService); + statusBarService.focus(false); + statusBarService.focusPreviousEntry(); + } +}); + KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'workbench.statusBar.clearFocus', weight: KeybindingWeight.WorkbenchContrib, diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index e7fdf3898d5..21f96d64552 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -4,12 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IMenuService, MenuId, IMenu, SubmenuItemAction } from 'vs/platform/actions/common/actions'; +import { IMenuService, MenuId, IMenu, SubmenuItemAction, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { registerThemingParticipant, IColorTheme, ICssStyleCollector, IThemeService } from 'vs/platform/theme/common/themeService'; import { MenuBarVisibility, getTitleBarStyle, IWindowOpenable, getMenuBarVisibility } from 'vs/platform/windows/common/windows'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IAction, Action } from 'vs/base/common/actions'; -import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IAction, Action, SubmenuAction, Separator } from 'vs/base/common/actions'; import * as DOM from 'vs/base/browser/dom'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { isMacintosh, isWeb, isIOS } from 'vs/base/common/platform'; @@ -27,7 +26,7 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/ import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { MenuBar, IMenuBarOptions } from 'vs/base/browser/ui/menu/menubar'; -import { SubmenuAction, Direction } from 'vs/base/browser/ui/menu/menu'; +import { Direction } from 'vs/base/browser/ui/menu/menu'; import { attachMenuStyler } from 'vs/platform/theme/common/styler'; import { mnemonicMenuLabel, unmnemonicLabel } from 'vs/base/common/labels'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; @@ -35,6 +34,9 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la import { isFullscreen } from 'vs/base/browser/browser'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; export abstract class MenubarControl extends Disposable { @@ -312,6 +314,8 @@ export class CustomMenubarControl extends MenubarControl { this.registerListeners(); + this.registerActions(); + registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { const menubarActiveWindowFgColor = theme.getColor(TITLE_BAR_ACTIVE_FOREGROUND); if (menubarActiveWindowFgColor) { @@ -411,6 +415,33 @@ export class CustomMenubarControl extends MenubarControl { this.setupCustomMenubar(firstTime); } + private registerActions(): void { + const that = this; + + if (isWeb) { + this._register(registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.actions.menubar.focus`, + title: { value: nls.localize('focusMenu', "Focus Application Menu"), original: 'Focus Application Menu' }, + keybinding: { + primary: KeyCode.F10, + weight: KeybindingWeight.WorkbenchContrib, + when: IsWebContext + }, + f1: true + }); + } + + async run(): Promise { + if (that.menubar) { + that.menubar.toggleFocus(); + } + } + })); + } + } + private getUpdateAction(): IAction | null { const state = this.updateService.state; @@ -567,7 +598,7 @@ export class CustomMenubarControl extends MenubarControl { const submenuActions: SubmenuAction[] = []; updateActions(submenu, submenuActions, topLevelTitle); - target.push(new SubmenuAction(mnemonicMenuLabel(action.label), submenuActions)); + target.push(new SubmenuAction(action.id, mnemonicMenuLabel(action.label), submenuActions)); } else { action.label = mnemonicMenuLabel(this.calculateActionLabel(action)); target.push(action); @@ -702,4 +733,10 @@ export class CustomMenubarControl extends MenubarControl { this.menubar?.update(this.getMenuBarOptions()); } + + toggleFocus() { + if (this.menubar) { + this.menubar.toggleFocus(); + } + } } diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index bd4cb681639..9c02c267018 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -60,7 +60,7 @@ export class TitlebarPart extends Part implements ITitleService { private _onMenubarVisibilityChange = this._register(new Emitter()); readonly onMenubarVisibilityChange = this._onMenubarVisibilityChange.event; - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; protected title!: HTMLElement; protected customMenubar: CustomMenubarControl | undefined; @@ -271,7 +271,7 @@ export class TitlebarPart extends Part implements ITitleService { if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { folder = workspace.folders[0]; } else { - const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }); + const resource = toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY }); if (resource) { folder = this.contextService.getWorkspaceFolder(resource); } @@ -333,7 +333,6 @@ export class TitlebarPart extends Part implements ITitleService { this.customMenubar = this._register(this.instantiationService.createInstance(CustomMenubarControl)); this.menubar = this.element.insertBefore($('div.menubar'), this.title); - this.menubar.setAttribute('role', 'menubar'); this.customMenubar.create(this.menubar); diff --git a/src/vs/workbench/browser/parts/views/media/paneviewlet.css b/src/vs/workbench/browser/parts/views/media/paneviewlet.css index 318e590df46..3adeb773148 100644 --- a/src/vs/workbench/browser/parts/views/media/paneviewlet.css +++ b/src/vs/workbench/browser/parts/views/media/paneviewlet.css @@ -18,6 +18,16 @@ .monaco-pane-view .pane > .pane-header > .actions.show { display: initial; } +.monaco-pane-view .pane > .pane-header .icon { + display: none; + width: 16px; + height: 16px; +} + +.monaco-pane-view .pane.pane.horizontal:not(.expanded) > .pane-header .icon { + display: inline; + margin-top: 4px; +} .monaco-pane-view .pane > .pane-header h3.title { white-space: nowrap; @@ -28,6 +38,11 @@ -webkit-margin-after: 0; } +.monaco-pane-view .pane.horizontal:not(.expanded) > .pane-header h3.title, +.monaco-pane-view .pane.horizontal:not(.expanded) > .pane-header .description { + display: none; +} + .monaco-pane-view .pane .monaco-progress-container { position: absolute; left: 0; diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 9483f3539e1..6077addea83 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -3,50 +3,24 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/views'; -import { Event, Emitter } from 'vs/base/common/event'; -import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { toDisposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IAction, ActionRunner } from 'vs/base/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IMenuService, MenuId, MenuItemAction, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; -import { ContextAwareMenuEntryActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IContextKeyService, ContextKeyExpr, ContextKeyEqualsExpr, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeViewDescriptor, IViewsRegistry, ITreeItemLabel, Extensions, IViewDescriptorService, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views'; +import { MenuId } from 'vs/platform/actions/common/actions'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ITreeView, ITreeViewDescriptor, IViewsRegistry, Extensions, IViewDescriptorService } from 'vs/workbench/common/views'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IProgressService } from 'vs/platform/progress/common/progress'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import * as DOM from 'vs/base/browser/dom'; -import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; -import { ActionBar, IActionViewItemProvider, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; -import { URI } from 'vs/base/common/uri'; -import { dirname, basename } from 'vs/base/common/resources'; -import { LIGHT, FileThemeIcon, FolderThemeIcon, registerThemingParticipant, ThemeIcon, IThemeService } from 'vs/platform/theme/common/themeService'; -import { FileKind } from 'vs/platform/files/common/files'; -import { WorkbenchAsyncDataTree, TreeResourceNavigator } from 'vs/platform/list/browser/listService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; -import { localize } from 'vs/nls'; -import { timeout } from 'vs/base/common/async'; -import { textLinkForeground, textCodeBlockBackground, focusBorder, listFilterMatchHighlight, listFilterMatchHighlightBorder } from 'vs/platform/theme/common/colorRegistry'; -import { isString } from 'vs/base/common/types'; -import { ILabelService } from 'vs/platform/label/common/label'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { ITreeRenderer, ITreeNode, IAsyncDataSource, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; -import { FuzzyScore, createMatches } from 'vs/base/common/filters'; -import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults'; -import { isFalsyOrWhitespace } from 'vs/base/common/strings'; -import { SIDE_BAR_BACKGROUND, PANEL_BACKGROUND } from 'vs/workbench/common/theme'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; export class TreeViewPane extends ViewPane { - private treeView: ITreeView; + protected readonly treeView: ITreeView; constructor( options: IViewletViewOptions, @@ -68,6 +42,9 @@ export class TreeViewPane extends ViewPane { this._register(toDisposable(() => this.treeView.setVisibility(false))); this._register(this.onDidChangeBodyVisibility(() => this.updateTreeVisibility())); this._register(this.treeView.onDidChangeWelcomeState(() => this._onDidChangeViewWelcomeState.fire())); + if (options.title !== this.treeView.title) { + this.updateTitle(this.treeView.title); + } this.updateTreeVisibility(); } @@ -78,10 +55,7 @@ export class TreeViewPane extends ViewPane { renderBody(container: HTMLElement): void { super.renderBody(container); - - if (this.treeView instanceof TreeView) { - this.treeView.show(container); - } + this.renderTreeView(container); } shouldShowWelcome(): boolean { @@ -90,941 +64,22 @@ export class TreeViewPane extends ViewPane { layoutBody(height: number, width: number): void { super.layoutBody(height, width); - this.treeView.layout(height, width); + this.layoutTreeView(height, width); } getOptimalWidth(): number { return this.treeView.getOptimalWidth(); } + protected renderTreeView(container: HTMLElement): void { + this.treeView.show(container); + } + + protected layoutTreeView(height: number, width: number): void { + this.treeView.layout(height, width); + } + private updateTreeVisibility(): void { this.treeView.setVisibility(this.isBodyVisible()); } } - -class Root implements ITreeItem { - label = { label: 'root' }; - handle = '0'; - parentHandle: string | undefined = undefined; - collapsibleState = TreeItemCollapsibleState.Expanded; - children: ITreeItem[] | undefined = undefined; -} - -const noDataProviderMessage = localize('no-dataprovider', "There is no data provider registered that can provide view data."); - -class Tree extends WorkbenchAsyncDataTree { } - -export class TreeView extends Disposable implements ITreeView { - - private isVisible: boolean = false; - private _hasIconForParentNode = false; - private _hasIconForLeafNode = false; - - private readonly collapseAllContextKey: RawContextKey; - private readonly collapseAllContext: IContextKey; - private readonly refreshContextKey: RawContextKey; - private readonly refreshContext: IContextKey; - - private focused: boolean = false; - private domNode!: HTMLElement; - private treeContainer!: HTMLElement; - private _messageValue: string | undefined; - private _canSelectMany: boolean = false; - private messageElement!: HTMLDivElement; - private tree: Tree | undefined; - private treeLabels: ResourceLabels | undefined; - - private root: ITreeItem; - private elementsToRefresh: ITreeItem[] = []; - - private readonly _onDidExpandItem: Emitter = this._register(new Emitter()); - readonly onDidExpandItem: Event = this._onDidExpandItem.event; - - private readonly _onDidCollapseItem: Emitter = this._register(new Emitter()); - readonly onDidCollapseItem: Event = this._onDidCollapseItem.event; - - private _onDidChangeSelection: Emitter = this._register(new Emitter()); - readonly onDidChangeSelection: Event = this._onDidChangeSelection.event; - - private readonly _onDidChangeVisibility: Emitter = this._register(new Emitter()); - readonly onDidChangeVisibility: Event = this._onDidChangeVisibility.event; - - private readonly _onDidChangeActions: Emitter = this._register(new Emitter()); - readonly onDidChangeActions: Event = this._onDidChangeActions.event; - - private readonly _onDidChangeWelcomeState: Emitter = this._register(new Emitter()); - readonly onDidChangeWelcomeState: Event = this._onDidChangeWelcomeState.event; - - private readonly _onDidChangeTitle: Emitter = this._register(new Emitter()); - readonly onDidChangeTitle: Event = this._onDidChangeTitle.event; - - private readonly _onDidCompleteRefresh: Emitter = this._register(new Emitter()); - - constructor( - protected readonly id: string, - private _title: string, - @IThemeService private readonly themeService: IThemeService, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @ICommandService private readonly commandService: ICommandService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IProgressService protected readonly progressService: IProgressService, - @IContextMenuService private readonly contextMenuService: IContextMenuService, - @IKeybindingService private readonly keybindingService: IKeybindingService, - @INotificationService private readonly notificationService: INotificationService, - @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, - @IContextKeyService contextKeyService: IContextKeyService - ) { - super(); - this.root = new Root(); - this.collapseAllContextKey = new RawContextKey(`treeView.${this.id}.enableCollapseAll`, false); - this.collapseAllContext = this.collapseAllContextKey.bindTo(contextKeyService); - this.refreshContextKey = new RawContextKey(`treeView.${this.id}.enableRefresh`, false); - this.refreshContext = this.refreshContextKey.bindTo(contextKeyService); - - this._register(this.themeService.onDidFileIconThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); - this._register(this.themeService.onDidColorThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); - this._register(this.configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('explorer.decorations')) { - this.doRefresh([this.root]); /** soft refresh **/ - } - })); - this._register(this.viewDescriptorService.onDidChangeLocation(({ views, from, to }) => { - if (views.some(v => v.id === this.id)) { - this.tree?.updateOptions({ overrideStyles: { listBackground: this.viewLocation === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND } }); - } - })); - this.registerActions(); - - this.create(); - } - - get viewContainer(): ViewContainer { - return this.viewDescriptorService.getViewContainerByViewId(this.id)!; - } - - get viewLocation(): ViewContainerLocation { - return this.viewDescriptorService.getViewLocationById(this.id)!; - } - - private _dataProvider: ITreeViewDataProvider | undefined; - get dataProvider(): ITreeViewDataProvider | undefined { - return this._dataProvider; - } - - set dataProvider(dataProvider: ITreeViewDataProvider | undefined) { - if (this.tree === undefined) { - this.createTree(); - } - - if (dataProvider) { - this._dataProvider = new class implements ITreeViewDataProvider { - private _isEmpty: boolean = true; - private _onDidChangeEmpty: Emitter = new Emitter(); - public onDidChangeEmpty: Event = this._onDidChangeEmpty.event; - - get isTreeEmpty(): boolean { - return this._isEmpty; - } - - async getChildren(node: ITreeItem): Promise { - let children: ITreeItem[]; - if (node && node.children) { - children = node.children; - } else { - children = await (node instanceof Root ? dataProvider.getChildren() : dataProvider.getChildren(node)); - node.children = children; - } - if (node instanceof Root) { - const oldEmpty = this._isEmpty; - this._isEmpty = children.length === 0; - if (oldEmpty !== this._isEmpty) { - this._onDidChangeEmpty.fire(); - } - } - return children; - } - }; - if (this._dataProvider.onDidChangeEmpty) { - this._register(this._dataProvider.onDidChangeEmpty(() => this._onDidChangeWelcomeState.fire())); - } - this.updateMessage(); - this.refresh(); - } else { - this._dataProvider = undefined; - this.updateMessage(); - } - - this._onDidChangeWelcomeState.fire(); - } - - private _message: string | undefined; - get message(): string | undefined { - return this._message; - } - - set message(message: string | undefined) { - this._message = message; - this.updateMessage(); - this._onDidChangeWelcomeState.fire(); - } - - get title(): string { - return this._title; - } - - set title(name: string) { - this._title = name; - this._onDidChangeTitle.fire(this._title); - } - - get canSelectMany(): boolean { - return this._canSelectMany; - } - - set canSelectMany(canSelectMany: boolean) { - this._canSelectMany = canSelectMany; - } - - get hasIconForParentNode(): boolean { - return this._hasIconForParentNode; - } - - get hasIconForLeafNode(): boolean { - return this._hasIconForLeafNode; - } - - get visible(): boolean { - return this.isVisible; - } - - get showCollapseAllAction(): boolean { - return !!this.collapseAllContext.get(); - } - - set showCollapseAllAction(showCollapseAllAction: boolean) { - this.collapseAllContext.set(showCollapseAllAction); - } - - get showRefreshAction(): boolean { - return !!this.refreshContext.get(); - } - - set showRefreshAction(showRefreshAction: boolean) { - this.refreshContext.set(showRefreshAction); - } - - private registerActions() { - const that = this; - this._register(registerAction2(class extends Action2 { - constructor() { - super({ - id: `workbench.actions.treeView.${that.id}.refresh`, - title: localize('refresh', "Refresh"), - menu: { - id: MenuId.ViewTitle, - when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', that.id), that.refreshContextKey), - group: 'navigation', - order: Number.MAX_SAFE_INTEGER - 1, - }, - icon: { id: 'codicon/refresh' } - }); - } - async run(): Promise { - return that.refresh(); - } - })); - this._register(registerAction2(class extends Action2 { - constructor() { - super({ - id: `workbench.actions.treeView.${that.id}.collapseAll`, - title: localize('collapseAll', "Collapse All"), - menu: { - id: MenuId.ViewTitle, - when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', that.id), that.collapseAllContextKey), - group: 'navigation', - order: Number.MAX_SAFE_INTEGER, - }, - icon: { id: 'codicon/collapse-all' } - }); - } - async run(): Promise { - if (that.tree) { - return new CollapseAllAction(that.tree, true).run(); - } - } - })); - } - - setVisibility(isVisible: boolean): void { - isVisible = !!isVisible; - if (this.isVisible === isVisible) { - return; - } - - this.isVisible = isVisible; - - if (this.tree) { - if (this.isVisible) { - DOM.show(this.tree.getHTMLElement()); - } else { - DOM.hide(this.tree.getHTMLElement()); // make sure the tree goes out of the tabindex world by hiding it - } - - if (this.isVisible && this.elementsToRefresh.length) { - this.doRefresh(this.elementsToRefresh); - this.elementsToRefresh = []; - } - } - - this._onDidChangeVisibility.fire(this.isVisible); - } - - focus(reveal: boolean = true): void { - if (this.tree && this.root.children && this.root.children.length > 0) { - // Make sure the current selected element is revealed - const selectedElement = this.tree.getSelection()[0]; - if (selectedElement && reveal) { - this.tree.reveal(selectedElement, 0.5); - } - - // Pass Focus to Viewer - this.tree.domFocus(); - } else if (this.tree) { - this.tree.domFocus(); - } else { - this.domNode.focus(); - } - } - - show(container: HTMLElement): void { - DOM.append(container, this.domNode); - } - - private create() { - this.domNode = DOM.$('.tree-explorer-viewlet-tree-view'); - this.messageElement = DOM.append(this.domNode, DOM.$('.message')); - this.treeContainer = DOM.append(this.domNode, DOM.$('.customview-tree')); - DOM.addClass(this.treeContainer, 'file-icon-themable-tree'); - DOM.addClass(this.treeContainer, 'show-file-icons'); - const focusTracker = this._register(DOM.trackFocus(this.domNode)); - this._register(focusTracker.onDidFocus(() => this.focused = true)); - this._register(focusTracker.onDidBlur(() => this.focused = false)); - } - - private createTree() { - const actionViewItemProvider = (action: IAction) => action instanceof MenuItemAction ? this.instantiationService.createInstance(ContextAwareMenuEntryActionViewItem, action) : undefined; - const treeMenus = this._register(this.instantiationService.createInstance(TreeMenus, this.id)); - this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this)); - const dataSource = this.instantiationService.createInstance(TreeDataSource, this, (task: Promise) => this.progressService.withProgress({ location: this.id }, () => task)); - const aligner = new Aligner(this.themeService); - const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, treeMenus, this.treeLabels, actionViewItemProvider, aligner); - const widgetAriaLabel = this._title; - - this.tree = this._register(this.instantiationService.createInstance(Tree, this.id, this.treeContainer, new TreeViewDelegate(), [renderer], - dataSource, { - identityProvider: new TreeViewIdentityProvider(), - accessibilityProvider: { - getAriaLabel(element: ITreeItem): string { - return element.tooltip ? element.tooltip : element.label ? element.label.label : ''; - }, - getWidgetAriaLabel(): string { - return widgetAriaLabel; - } - }, - keyboardNavigationLabelProvider: { - getKeyboardNavigationLabel: (item: ITreeItem) => { - return item.label ? item.label.label : (item.resourceUri ? basename(URI.revive(item.resourceUri)) : undefined); - } - }, - expandOnlyOnTwistieClick: (e: ITreeItem) => !!e.command, - collapseByDefault: (e: ITreeItem): boolean => { - return e.collapsibleState !== TreeItemCollapsibleState.Expanded; - }, - multipleSelectionSupport: this.canSelectMany, - overrideStyles: { - listBackground: this.viewLocation === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND - } - }) as WorkbenchAsyncDataTree); - aligner.tree = this.tree; - const actionRunner = new MultipleSelectionActionRunner(this.notificationService, () => this.tree!.getSelection()); - renderer.actionRunner = actionRunner; - - this.tree.contextKeyService.createKey(this.id, true); - this._register(this.tree.onContextMenu(e => this.onContextMenu(treeMenus, e, actionRunner))); - this._register(this.tree.onDidChangeSelection(e => this._onDidChangeSelection.fire(e.elements))); - this._register(this.tree.onDidChangeCollapseState(e => { - if (!e.node.element) { - return; - } - - const element: ITreeItem = Array.isArray(e.node.element.element) ? e.node.element.element[0] : e.node.element.element; - if (e.node.collapsed) { - this._onDidCollapseItem.fire(element); - } else { - this._onDidExpandItem.fire(element); - } - })); - this.tree.setInput(this.root).then(() => this.updateContentAreas()); - - const treeNavigator = new TreeResourceNavigator(this.tree, { openOnFocus: false, openOnSelection: false }); - this._register(treeNavigator); - this._register(treeNavigator.onDidOpenResource(e => { - if (!e.browserEvent) { - return; - } - const selection = this.tree!.getSelection(); - if ((selection.length === 1) && selection[0].command) { - this.commandService.executeCommand(selection[0].command.id, ...(selection[0].command.arguments || [])); - } - })); - } - - private onContextMenu(treeMenus: TreeMenus, treeEvent: ITreeContextMenuEvent, actionRunner: MultipleSelectionActionRunner): void { - const node: ITreeItem | null = treeEvent.element; - if (node === null) { - return; - } - const event: UIEvent = treeEvent.browserEvent; - - event.preventDefault(); - event.stopPropagation(); - - this.tree!.setFocus([node]); - const actions = treeMenus.getResourceContextActions(node); - if (!actions.length) { - return; - } - this.contextMenuService.showContextMenu({ - getAnchor: () => treeEvent.anchor, - - getActions: () => actions, - - getActionViewItem: (action) => { - const keybinding = this.keybindingService.lookupKeybinding(action.id); - if (keybinding) { - return new ActionViewItem(action, action, { label: true, keybinding: keybinding.getLabel() }); - } - return undefined; - }, - - onHide: (wasCancelled?: boolean) => { - if (wasCancelled) { - this.tree!.domFocus(); - } - }, - - getActionsContext: () => ({ $treeViewId: this.id, $treeItemHandle: node.handle }), - - actionRunner - }); - } - - protected updateMessage(): void { - if (this._message) { - this.showMessage(this._message); - } else if (!this.dataProvider) { - this.showMessage(noDataProviderMessage); - } else { - this.hideMessage(); - } - this.updateContentAreas(); - } - - private showMessage(message: string): void { - DOM.removeClass(this.messageElement, 'hide'); - this.resetMessageElement(); - this._messageValue = message; - if (!isFalsyOrWhitespace(this._message)) { - this.messageElement.textContent = this._messageValue; - } - this.layout(this._height, this._width); - } - - private hideMessage(): void { - this.resetMessageElement(); - DOM.addClass(this.messageElement, 'hide'); - this.layout(this._height, this._width); - } - - private resetMessageElement(): void { - DOM.clearNode(this.messageElement); - } - - private _height: number = 0; - private _width: number = 0; - layout(height: number, width: number) { - if (height && width) { - this._height = height; - this._width = width; - const treeHeight = height - DOM.getTotalHeight(this.messageElement); - this.treeContainer.style.height = treeHeight + 'px'; - if (this.tree) { - this.tree.layout(treeHeight, width); - } - } - } - - getOptimalWidth(): number { - if (this.tree) { - const parentNode = this.tree.getHTMLElement(); - const childNodes = ([] as HTMLElement[]).slice.call(parentNode.querySelectorAll('.outline-item-label > a')); - return DOM.getLargestChildWidth(parentNode, childNodes); - } - return 0; - } - - async refresh(elements?: ITreeItem[]): Promise { - if (this.dataProvider && this.tree) { - if (this.refreshing) { - await Event.toPromise(this._onDidCompleteRefresh.event); - } - if (!elements) { - elements = [this.root]; - // remove all waiting elements to refresh if root is asked to refresh - this.elementsToRefresh = []; - } - for (const element of elements) { - element.children = undefined; // reset children - } - if (this.isVisible) { - return this.doRefresh(elements); - } else { - if (this.elementsToRefresh.length) { - const seen: Set = new Set(); - this.elementsToRefresh.forEach(element => seen.add(element.handle)); - for (const element of elements) { - if (!seen.has(element.handle)) { - this.elementsToRefresh.push(element); - } - } - } else { - this.elementsToRefresh.push(...elements); - } - } - } - return undefined; - } - - async expand(itemOrItems: ITreeItem | ITreeItem[]): Promise { - const tree = this.tree; - if (tree) { - itemOrItems = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems]; - await Promise.all(itemOrItems.map(element => { - return tree.expand(element, false); - })); - } - } - - setSelection(items: ITreeItem[]): void { - if (this.tree) { - this.tree.setSelection(items); - } - } - - setFocus(item: ITreeItem): void { - if (this.tree) { - this.focus(); - this.tree.setFocus([item]); - } - } - - async reveal(item: ITreeItem): Promise { - if (this.tree) { - return this.tree.reveal(item); - } - } - - private refreshing: boolean = false; - private async doRefresh(elements: ITreeItem[]): Promise { - const tree = this.tree; - if (tree && this.visible) { - this.refreshing = true; - await Promise.all(elements.map(element => tree.updateChildren(element, true, true))); - this.refreshing = false; - this._onDidCompleteRefresh.fire(); - this.updateContentAreas(); - if (this.focused) { - this.focus(false); - } - } - } - - private updateContentAreas(): void { - const isTreeEmpty = !this.root.children || this.root.children.length === 0; - // Hide tree container only when there is a message and tree is empty and not refreshing - if (this._messageValue && isTreeEmpty && !this.refreshing) { - DOM.addClass(this.treeContainer, 'hide'); - this.domNode.setAttribute('tabindex', '0'); - } else { - DOM.removeClass(this.treeContainer, 'hide'); - this.domNode.removeAttribute('tabindex'); - } - } -} - -class TreeViewIdentityProvider implements IIdentityProvider { - getId(element: ITreeItem): { toString(): string; } { - return element.handle; - } -} - -class TreeViewDelegate implements IListVirtualDelegate { - - getHeight(element: ITreeItem): number { - return TreeRenderer.ITEM_HEIGHT; - } - - getTemplateId(element: ITreeItem): string { - return TreeRenderer.TREE_TEMPLATE_ID; - } -} - -class TreeDataSource implements IAsyncDataSource { - - constructor( - private treeView: ITreeView, - private withProgress: (task: Promise) => Promise - ) { - } - - hasChildren(element: ITreeItem): boolean { - return !!this.treeView.dataProvider && (element.collapsibleState !== TreeItemCollapsibleState.None); - } - - async getChildren(element: ITreeItem): Promise { - if (this.treeView.dataProvider) { - return this.withProgress(this.treeView.dataProvider.getChildren(element)); - } - return []; - } -} - -// todo@joh,sandy make this proper and contributable from extensions -registerThemingParticipant((theme, collector) => { - - const matchBackgroundColor = theme.getColor(listFilterMatchHighlight); - if (matchBackgroundColor) { - collector.addRule(`.file-icon-themable-tree .monaco-list-row .content .monaco-highlighted-label .highlight { color: unset !important; background-color: ${matchBackgroundColor}; }`); - collector.addRule(`.monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; background-color: ${matchBackgroundColor}; }`); - } - const matchBorderColor = theme.getColor(listFilterMatchHighlightBorder); - if (matchBorderColor) { - collector.addRule(`.file-icon-themable-tree .monaco-list-row .content .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${matchBorderColor}; box-sizing: border-box; }`); - collector.addRule(`.monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${matchBorderColor}; box-sizing: border-box; }`); - } - const link = theme.getColor(textLinkForeground); - if (link) { - collector.addRule(`.tree-explorer-viewlet-tree-view > .message a { color: ${link}; }`); - } - const focusBorderColor = theme.getColor(focusBorder); - if (focusBorderColor) { - collector.addRule(`.tree-explorer-viewlet-tree-view > .message a:focus { outline: 1px solid ${focusBorderColor}; outline-offset: -1px; }`); - } - const codeBackground = theme.getColor(textCodeBlockBackground); - if (codeBackground) { - collector.addRule(`.tree-explorer-viewlet-tree-view > .message code { background-color: ${codeBackground}; }`); - } -}); - -interface ITreeExplorerTemplateData { - elementDisposable: IDisposable; - container: HTMLElement; - resourceLabel: IResourceLabel; - icon: HTMLElement; - actionBar: ActionBar; -} - -class TreeRenderer extends Disposable implements ITreeRenderer { - static readonly ITEM_HEIGHT = 22; - static readonly TREE_TEMPLATE_ID = 'treeExplorer'; - - private _actionRunner: MultipleSelectionActionRunner | undefined; - - constructor( - private treeViewId: string, - private menus: TreeMenus, - private labels: ResourceLabels, - private actionViewItemProvider: IActionViewItemProvider, - private aligner: Aligner, - @IThemeService private readonly themeService: IThemeService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @ILabelService private readonly labelService: ILabelService - ) { - super(); - } - - get templateId(): string { - return TreeRenderer.TREE_TEMPLATE_ID; - } - - set actionRunner(actionRunner: MultipleSelectionActionRunner) { - this._actionRunner = actionRunner; - } - - renderTemplate(container: HTMLElement): ITreeExplorerTemplateData { - DOM.addClass(container, 'custom-view-tree-node-item'); - - const icon = DOM.append(container, DOM.$('.custom-view-tree-node-item-icon')); - - const resourceLabel = this.labels.create(container, { supportHighlights: true }); - const actionsContainer = DOM.append(resourceLabel.element, DOM.$('.actions')); - const actionBar = new ActionBar(actionsContainer, { - actionViewItemProvider: this.actionViewItemProvider - }); - - return { resourceLabel, icon, actionBar, container, elementDisposable: Disposable.None }; - } - - renderElement(element: ITreeNode, index: number, templateData: ITreeExplorerTemplateData): void { - templateData.elementDisposable.dispose(); - const node = element.element; - const resource = node.resourceUri ? URI.revive(node.resourceUri) : null; - const treeItemLabel: ITreeItemLabel | undefined = node.label ? node.label : resource ? { label: basename(resource) } : undefined; - const description = isString(node.description) ? node.description : resource && node.description === true ? this.labelService.getUriLabel(dirname(resource), { relative: true }) : undefined; - const label = treeItemLabel ? treeItemLabel.label : undefined; - const matches = (treeItemLabel && treeItemLabel.highlights && label) ? treeItemLabel.highlights.map(([start, end]) => { - if ((Math.abs(start) > label.length) || (Math.abs(end) >= label.length)) { - return ({ start: 0, end: 0 }); - } - if (start < 0) { - start = label.length + start; - } - if (end < 0) { - end = label.length + end; - } - if (start > end) { - const swap = start; - start = end; - end = swap; - } - return ({ start, end }); - }) : undefined; - const icon = this.themeService.getColorTheme().type === LIGHT ? node.icon : node.iconDark; - const iconUrl = icon ? URI.revive(icon) : null; - const title = node.tooltip ? node.tooltip : resource ? undefined : label; - - // reset - templateData.actionBar.clear(); - - if (resource || this.isFileKindThemeIcon(node.themeIcon)) { - const fileDecorations = this.configurationService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations'); - templateData.resourceLabel.setResource({ name: label, description, resource: resource ? resource : URI.parse('missing:_icon_resource') }, { fileKind: this.getFileKind(node), title, hideIcon: !!iconUrl, fileDecorations, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches: matches ? matches : createMatches(element.filterData) }); - } else { - templateData.resourceLabel.setResource({ name: label, description }, { title, hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches: matches ? matches : createMatches(element.filterData) }); - } - - templateData.icon.title = title ? title : ''; - - if (iconUrl) { - templateData.icon.className = 'custom-view-tree-node-item-icon'; - templateData.icon.style.backgroundImage = DOM.asCSSUrl(iconUrl); - - } else { - let iconClass: string | undefined; - if (node.themeIcon && !this.isFileKindThemeIcon(node.themeIcon)) { - iconClass = ThemeIcon.asClassName(node.themeIcon); - } - templateData.icon.className = iconClass ? `custom-view-tree-node-item-icon ${iconClass}` : ''; - templateData.icon.style.backgroundImage = ''; - } - - templateData.actionBar.context = { $treeViewId: this.treeViewId, $treeItemHandle: node.handle }; - templateData.actionBar.push(this.menus.getResourceActions(node), { icon: true, label: false }); - if (this._actionRunner) { - templateData.actionBar.actionRunner = this._actionRunner; - } - this.setAlignment(templateData.container, node); - templateData.elementDisposable = (this.themeService.onDidFileIconThemeChange(() => this.setAlignment(templateData.container, node))); - } - - private setAlignment(container: HTMLElement, treeItem: ITreeItem) { - DOM.toggleClass(container.parentElement!, 'align-icon-with-twisty', this.aligner.alignIconWithTwisty(treeItem)); - } - - private isFileKindThemeIcon(icon: ThemeIcon | undefined): boolean { - if (icon) { - return icon.id === FileThemeIcon.id || icon.id === FolderThemeIcon.id; - } else { - return false; - } - } - - private getFileKind(node: ITreeItem): FileKind { - if (node.themeIcon) { - switch (node.themeIcon.id) { - case FileThemeIcon.id: - return FileKind.FILE; - case FolderThemeIcon.id: - return FileKind.FOLDER; - } - } - return node.collapsibleState === TreeItemCollapsibleState.Collapsed || node.collapsibleState === TreeItemCollapsibleState.Expanded ? FileKind.FOLDER : FileKind.FILE; - } - - disposeElement(resource: ITreeNode, index: number, templateData: ITreeExplorerTemplateData): void { - templateData.elementDisposable.dispose(); - } - - disposeTemplate(templateData: ITreeExplorerTemplateData): void { - templateData.resourceLabel.dispose(); - templateData.actionBar.dispose(); - templateData.elementDisposable.dispose(); - } -} - -class Aligner extends Disposable { - private _tree: WorkbenchAsyncDataTree | undefined; - - constructor(private themeService: IThemeService) { - super(); - } - - set tree(tree: WorkbenchAsyncDataTree) { - this._tree = tree; - } - - public alignIconWithTwisty(treeItem: ITreeItem): boolean { - if (treeItem.collapsibleState !== TreeItemCollapsibleState.None) { - return false; - } - if (!this.hasIcon(treeItem)) { - return false; - } - - if (this._tree) { - const parent: ITreeItem = this._tree.getParentElement(treeItem) || this._tree.getInput(); - if (this.hasIcon(parent)) { - return false; - } - return !!parent.children && parent.children.every(c => c.collapsibleState === TreeItemCollapsibleState.None || !this.hasIcon(c)); - } else { - return false; - } - } - - private hasIcon(node: ITreeItem): boolean { - const icon = this.themeService.getColorTheme().type === LIGHT ? node.icon : node.iconDark; - if (icon) { - return true; - } - if (node.resourceUri || node.themeIcon) { - const fileIconTheme = this.themeService.getFileIconTheme(); - const isFolder = node.themeIcon ? node.themeIcon.id === FolderThemeIcon.id : node.collapsibleState !== TreeItemCollapsibleState.None; - if (isFolder) { - return fileIconTheme.hasFileIcons && fileIconTheme.hasFolderIcons; - } - return fileIconTheme.hasFileIcons; - } - return false; - } -} - -class MultipleSelectionActionRunner extends ActionRunner { - - constructor(notificationService: INotificationService, private getSelectedResources: (() => ITreeItem[])) { - super(); - this._register(this.onDidRun(e => { - if (e.error) { - notificationService.error(localize('command-error', 'Error running command {1}: {0}. This is likely caused by the extension that contributes {1}.', e.error.message, e.action.id)); - } - })); - } - - runAction(action: IAction, context: TreeViewItemHandleArg): Promise { - const selection = this.getSelectedResources(); - let selectionHandleArgs: TreeViewItemHandleArg[] | undefined = undefined; - let actionInSelected: boolean = false; - if (selection.length > 1) { - selectionHandleArgs = selection.map(selected => { - if (selected.handle === context.$treeItemHandle) { - actionInSelected = true; - } - return { $treeViewId: context.$treeViewId, $treeItemHandle: selected.handle }; - }); - } - - if (!actionInSelected) { - selectionHandleArgs = undefined; - } - - return action.run(...[context, selectionHandleArgs]); - } -} - -class TreeMenus extends Disposable implements IDisposable { - - constructor( - private id: string, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IMenuService private readonly menuService: IMenuService, - @IContextMenuService private readonly contextMenuService: IContextMenuService - ) { - super(); - } - - getResourceActions(element: ITreeItem): IAction[] { - return this.getActions(MenuId.ViewItemContext, { key: 'viewItem', value: element.contextValue }).primary; - } - - getResourceContextActions(element: ITreeItem): IAction[] { - return this.getActions(MenuId.ViewItemContext, { key: 'viewItem', value: element.contextValue }).secondary; - } - - private getActions(menuId: MenuId, context: { key: string, value?: string }): { primary: IAction[]; secondary: IAction[]; } { - const contextKeyService = this.contextKeyService.createScoped(); - contextKeyService.createKey('view', this.id); - contextKeyService.createKey(context.key, context.value); - - const menu = this.menuService.createMenu(menuId, contextKeyService); - const primary: IAction[] = []; - const secondary: IAction[] = []; - const result = { primary, secondary }; - createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, this.contextMenuService, g => /^inline/.test(g)); - - menu.dispose(); - contextKeyService.dispose(); - - return result; - } -} - -export class CustomTreeView extends TreeView { - - private activated: boolean = false; - - constructor( - id: string, - title: string, - @IThemeService themeService: IThemeService, - @IInstantiationService instantiationService: IInstantiationService, - @ICommandService commandService: ICommandService, - @IConfigurationService configurationService: IConfigurationService, - @IProgressService progressService: IProgressService, - @IContextMenuService contextMenuService: IContextMenuService, - @IKeybindingService keybindingService: IKeybindingService, - @INotificationService notificationService: INotificationService, - @IViewDescriptorService viewDescriptorService: IViewDescriptorService, - @IContextKeyService contextKeyService: IContextKeyService, - @IExtensionService private readonly extensionService: IExtensionService, - ) { - super(id, title, themeService, instantiationService, commandService, configurationService, progressService, contextMenuService, keybindingService, notificationService, viewDescriptorService, contextKeyService); - } - - setVisibility(isVisible: boolean): void { - super.setVisibility(isVisible); - if (this.visible) { - this.activate(); - } - } - - private activate() { - if (!this.activated) { - this.progressService.withProgress({ location: this.id }, () => this.extensionService.activateByEvent(`onView:${this.id}`)) - .then(() => timeout(2000)) - .then(() => { - this.updateMessage(); - }); - this.activated = true; - } - } -} diff --git a/src/vs/workbench/browser/parts/views/viewMenuActions.ts b/src/vs/workbench/browser/parts/views/viewMenuActions.ts index 3c0ad25bc70..6d3f84da8d5 100644 --- a/src/vs/workbench/browser/parts/views/viewMenuActions.ts +++ b/src/vs/workbench/browser/parts/views/viewMenuActions.ts @@ -69,3 +69,37 @@ export class ViewMenuActions extends Disposable { return this.contextMenuActions; } } + +export class ViewContainerMenuActions extends Disposable { + + private readonly titleActionsDisposable = this._register(new MutableDisposable()); + private contextMenuActions: IAction[] = []; + + constructor( + containerId: string, + contextMenuId: MenuId, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IMenuService private readonly menuService: IMenuService, + ) { + super(); + + const scopedContextKeyService = this._register(this.contextKeyService.createScoped()); + scopedContextKeyService.createKey('container', containerId); + + const contextMenu = this._register(this.menuService.createMenu(contextMenuId, scopedContextKeyService)); + const updateContextMenuActions = () => { + this.contextMenuActions = []; + this.titleActionsDisposable.value = createAndFillInActionBarActions(contextMenu, { shouldForwardArgs: true }, { primary: [], secondary: this.contextMenuActions }); + }; + this._register(contextMenu.onDidChange(updateContextMenuActions)); + updateContextMenuActions(); + + this._register(toDisposable(() => { + this.contextMenuActions = []; + })); + } + + getContextMenuActions(): IAction[] { + return this.contextMenuActions; + } +} diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 7234e7f5b5e..8c21efbf18b 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -6,41 +6,41 @@ import 'vs/css!./media/paneviewlet'; import * as nls from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; -import { ColorIdentifier, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; +import { ColorIdentifier, activeContrastBorder, foreground } from 'vs/platform/theme/common/colorRegistry'; import { attachStyler, IColorMapping, attachButtonStyler, attachLinkStyler, attachProgressBarStyler } from 'vs/platform/theme/common/styler'; -import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND, SIDE_BAR_SECTION_HEADER_BORDER, PANEL_BACKGROUND, SIDE_BAR_BACKGROUND, EDITOR_DRAG_AND_DROP_BACKGROUND, PANEL_BORDER } from 'vs/workbench/common/theme'; -import { append, $, trackFocus, toggleClass, EventType, isAncestor, Dimension, addDisposableListener, removeClass, addClass } from 'vs/base/browser/dom'; +import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND, SIDE_BAR_SECTION_HEADER_BORDER, PANEL_BACKGROUND, SIDE_BAR_BACKGROUND, PANEL_SECTION_HEADER_FOREGROUND, PANEL_SECTION_HEADER_BACKGROUND, PANEL_SECTION_HEADER_BORDER, PANEL_SECTION_DRAG_AND_DROP_BACKGROUND, PANEL_SECTION_BORDER } from 'vs/workbench/common/theme'; +import { append, $, trackFocus, toggleClass, EventType, isAncestor, Dimension, addDisposableListener, removeClass, addClass, createCSSRule, asCSSUrl, addClasses } from 'vs/base/browser/dom'; import { IDisposable, combinedDisposable, dispose, toDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { firstIndex } from 'vs/base/common/arrays'; -import { IAction } from 'vs/base/common/actions'; -import { IActionViewItem, ActionsOrientation, Separator, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IAction, Separator, IActionViewItem } from 'vs/base/common/actions'; +import { ActionsOrientation, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar'; import { Registry } from 'vs/platform/registry/common/platform'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; -import { PaneView, IPaneViewOptions, IPaneOptions, Pane } from 'vs/base/browser/ui/splitview/paneview'; +import { PaneView, IPaneViewOptions, IPaneOptions, Pane, IPaneStyles } from 'vs/base/browser/ui/splitview/paneview'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/browser/layoutService'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewDescriptor, ViewContainer, IViewDescriptorService, ViewContainerLocation, IViewPaneContainer, IViewsRegistry, IViewContentDescriptor, IAddedViewDescriptorRef, IViewDescriptorRef, IViewContainerModel } from 'vs/workbench/common/views'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { assertIsDefined } from 'vs/base/common/types'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { assertIsDefined, isString } from 'vs/base/common/types'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { Component } from 'vs/workbench/common/component'; -import { MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; -import { ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { MenuId, MenuItemAction, registerAction2, Action2, IAction2Options, SubmenuItemAction } from 'vs/platform/actions/common/actions'; +import { MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { ViewMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions'; import { parseLinkedText } from 'vs/base/common/linkedText'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { Button } from 'vs/base/browser/ui/button/button'; import { Link } from 'vs/platform/opener/browser/link'; -import { CompositeDragAndDropObserver, DragAndDropObserver } from 'vs/workbench/browser/dnd'; +import { CompositeDragAndDropObserver, DragAndDropObserver, toggleDropEffect } from 'vs/workbench/browser/dnd'; import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { CompositeProgressIndicator } from 'vs/workbench/services/progress/browser/progressIndicator'; @@ -48,6 +48,9 @@ import { IProgressIndicator } from 'vs/platform/progress/common/progress'; import { RunOnceScheduler } from 'vs/base/common/async'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { URI } from 'vs/base/common/uri'; +import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; export interface IPaneColors extends IColorMapping { dropBackground?: ColorIdentifier; @@ -175,7 +178,11 @@ export abstract class ViewPane extends Pane implements IView { private _isVisible: boolean = false; readonly id: string; - title: string; + + private _title: string; + public get title(): string { + return this._title; + } private readonly menuActions: ViewMenuActions; private progressBar!: ProgressBar; @@ -185,6 +192,7 @@ export abstract class ViewPane extends Pane implements IView { private readonly showActionsAlways: boolean = false; private headerContainer?: HTMLElement; private titleContainer?: HTMLElement; + private iconContainer?: HTMLElement; protected twistiesContainer?: HTMLElement; private bodyContainer!: HTMLElement; @@ -207,7 +215,7 @@ export abstract class ViewPane extends Pane implements IView { super({ ...options, ...{ orientation: viewDescriptorService.getViewLocationById(options.id) === ViewContainerLocation.Panel ? Orientation.HORIZONTAL : Orientation.VERTICAL } }); this.id = options.id; - this.title = options.title; + this._title = options.title; this.showActionsAlways = !!options.showActionsAlways; this.focusedViewContextKey = FocusedViewContext.bindTo(contextKeyService); @@ -303,25 +311,80 @@ export abstract class ViewPane extends Pane implements IView { this.twistiesContainer = append(container, $('.twisties.codicon.codicon-chevron-right')); } - protected renderHeaderTitle(container: HTMLElement, title: string): void { - this.titleContainer = append(container, $('h3.title', undefined, this.calculateTitle(title))); + style(styles: IPaneStyles): void { + super.style(styles); + + const icon = this.getIcon(); + if (this.iconContainer) { + const fgColor = styles.headerForeground || this.themeService.getColorTheme().getColor(foreground); + if (URI.isUri(icon)) { + // Apply background color to activity bar item provided with iconUrls + this.iconContainer.style.backgroundColor = fgColor ? fgColor.toString() : ''; + this.iconContainer.style.color = ''; + } else { + // Apply foreground color to activity bar items provided with codicons + this.iconContainer.style.color = fgColor ? fgColor.toString() : ''; + this.iconContainer.style.backgroundColor = ''; + } + } } - updateTitle(title: string): void { - if (this.titleContainer) { - this.titleContainer.textContent = this.calculateTitle(title); + private getIcon(): string | URI { + return this.viewDescriptorService.getViewDescriptorById(this.id)?.containerIcon || 'codicon-window'; + } + + protected renderHeaderTitle(container: HTMLElement, title: string): void { + this.iconContainer = append(container, $('.icon', undefined)); + const icon = this.getIcon(); + + let cssClass: string | undefined = undefined; + if (URI.isUri(icon)) { + cssClass = `view-${this.id.replace(/[\.\:]/g, '-')}`; + const iconClass = `.pane-header .icon.${cssClass}`; + + createCSSRule(iconClass, ` + mask: ${asCSSUrl(icon)} no-repeat 50% 50%; + mask-size: 24px; + -webkit-mask: ${asCSSUrl(icon)} no-repeat 50% 50%; + -webkit-mask-size: 16px; + `); + } else if (isString(icon)) { + addClass(this.iconContainer, 'codicon'); + cssClass = icon; } - this.title = title; + + if (cssClass) { + addClasses(this.iconContainer, cssClass); + } + + const calculatedTitle = this.calculateTitle(title); + this.titleContainer = append(container, $('h3.title', undefined, calculatedTitle)); + this.iconContainer.title = calculatedTitle; + this.iconContainer.setAttribute('aria-label', calculatedTitle); + } + + protected updateTitle(title: string): void { + const calculatedTitle = this.calculateTitle(title); + if (this.titleContainer) { + this.titleContainer.textContent = calculatedTitle; + } + + if (this.iconContainer) { + this.iconContainer.title = calculatedTitle; + this.iconContainer.setAttribute('aria-label', calculatedTitle); + } + + this._title = title; this._onDidChangeTitleArea.fire(); } private calculateTitle(title: string): string { const viewContainer = this.viewDescriptorService.getViewContainerByViewId(this.id)!; const model = this.viewDescriptorService.getViewContainerModel(viewContainer); - const viewDescriptor = this.viewDescriptorService.getViewDescriptorById(this.id)!; + const viewDescriptor = this.viewDescriptorService.getViewDescriptorById(this.id); const isDefault = this.viewDescriptorService.getDefaultContainerById(this.id) === viewContainer; - if (!isDefault && viewDescriptor.containerTitle && model.title !== viewDescriptor.containerTitle) { + if (!isDefault && viewDescriptor?.containerTitle && model.title !== viewDescriptor.containerTitle) { return `${viewDescriptor.containerTitle}: ${title}`; } @@ -387,7 +450,7 @@ export abstract class ViewPane extends Pane implements IView { private setActions(): void { if (this.toolbar) { - this.toolbar.setActions(prepareActions(this.getActions()), prepareActions(this.getSecondaryActions()))(); + this.toolbar.setActions(prepareActions(this.getActions()), prepareActions(this.getSecondaryActions())); this.toolbar.context = this.getActionsContext(); } } @@ -419,7 +482,9 @@ export abstract class ViewPane extends Pane implements IView { getActionViewItem(action: IAction): IActionViewItem | undefined { if (action instanceof MenuItemAction) { - return this.instantiationService.createInstance(ContextAwareMenuEntryActionViewItem, action); + return this.instantiationService.createInstance(MenuEntryActionViewItem, action); + } else if (action instanceof SubmenuItemAction) { + return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action); } return undefined; } @@ -562,7 +627,8 @@ class ViewPaneDropOverlay extends Themable { constructor( private paneElement: HTMLElement, private orientation: Orientation | undefined, - protected themeService: IThemeService + protected location: ViewContainerLocation, + protected themeService: IThemeService, ) { super(themeService); this.cleanupOverlayScheduler = this._register(new RunOnceScheduler(() => this.dispose(), 300)); @@ -603,7 +669,7 @@ class ViewPaneDropOverlay extends Themable { protected updateStyles(): void { // Overlay drop background - this.overlay.style.backgroundColor = this.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND) || ''; + this.overlay.style.backgroundColor = this.getColor(this.location === ViewContainerLocation.Panel ? PANEL_SECTION_DRAG_AND_DROP_BACKGROUND : SIDE_BAR_DRAG_AND_DROP_BACKGROUND) || ''; // Overlay contrast border (if any) const activeContrastBorderColor = this.getColor(activeContrastBorder); @@ -846,20 +912,25 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { return; } - overlay = new ViewPaneDropOverlay(parent, undefined, this.themeService); + overlay = new ViewPaneDropOverlay(parent, undefined, this.viewDescriptorService.getViewContainerLocation(this.viewContainer)!, this.themeService); } if (dropData.type === 'composite' && dropData.id !== this.viewContainer.id) { const container = this.viewDescriptorService.getViewContainerById(dropData.id)!; const viewsToMove = this.viewDescriptorService.getViewContainerModel(container).allViewDescriptors; - if (!viewsToMove.some(v => !v.canMoveView)) { - overlay = new ViewPaneDropOverlay(parent, undefined, this.themeService); + if (!viewsToMove.some(v => !v.canMoveView) && viewsToMove.length > 0) { + overlay = new ViewPaneDropOverlay(parent, undefined, this.viewDescriptorService.getViewContainerLocation(this.viewContainer)!, this.themeService); } } } }, + onDragOver: (e) => { + if (this.panes.length === 0) { + toggleDropEffect(e.eventData.dataTransfer, 'move', overlay !== undefined); + } + }, onDragLeave: (e) => { overlay?.dispose(); overlay = undefined; @@ -913,6 +984,8 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { this.updateViewHeaders(); } }); + + this._register(this.viewContainerModel.onDidChangeActiveViewDescriptors(() => this._onTitleAreaUpdate.fire())); } getTitle(): string { @@ -971,14 +1044,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { } } - const viewToggleActions = this.viewContainerModel.activeViewDescriptors.map(viewDescriptor => ({ - id: `${viewDescriptor.id}.toggleVisibility`, - label: viewDescriptor.name, - checked: this.viewContainerModel.isVisible(viewDescriptor.id), - enabled: viewDescriptor.canToggleVisibility && (!this.viewContainerModel.isVisible(viewDescriptor.id) || this.viewContainerModel.visibleViewDescriptors.length > 1), - run: () => this.toggleViewVisibility(viewDescriptor.id) - })); - + const viewToggleActions = this.getViewsVisibilityActions(); if (result.length && viewToggleActions.length) { result.push(new Separator()); } @@ -1004,6 +1070,16 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { return []; } + getViewsVisibilityActions(): IAction[] { + return this.viewContainerModel.activeViewDescriptors.map(viewDescriptor => ({ + id: `${viewDescriptor.id}.toggleVisibility`, + label: viewDescriptor.name, + checked: this.viewContainerModel.isVisible(viewDescriptor.id), + enabled: viewDescriptor.canToggleVisibility && (!this.viewContainerModel.isVisible(viewDescriptor.id) || this.viewContainerModel.visibleViewDescriptors.length > 1), + run: () => this.toggleViewVisibility(viewDescriptor.id) + })); + } + getActionViewItem(action: IAction): IActionViewItem | undefined { if (this.isViewMergedWithContainer()) { return this.paneItems[0].pane.getActionViewItem(action); @@ -1221,6 +1297,10 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { panesToRemove.push(this.panes[index]); } this.removePanes(panesToRemove); + + for (const pane of panesToRemove) { + pane.setVisible(false); + } } protected toggleViewVisibility(viewId: string): void { @@ -1251,13 +1331,13 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { } }); - // TODO@sbatten Styling is viewlet specific, must fix + const isPanel = this.viewDescriptorService.getViewContainerLocation(this.viewContainer) === ViewContainerLocation.Panel; const paneStyler = attachStyler(this.themeService, { - headerForeground: SIDE_BAR_SECTION_HEADER_FOREGROUND, - headerBackground: SIDE_BAR_SECTION_HEADER_BACKGROUND, - headerBorder: SIDE_BAR_SECTION_HEADER_BORDER, - leftBorder: PANEL_BORDER, - dropBackground: SIDE_BAR_DRAG_AND_DROP_BACKGROUND + headerForeground: isPanel ? PANEL_SECTION_HEADER_FOREGROUND : SIDE_BAR_SECTION_HEADER_FOREGROUND, + headerBackground: isPanel ? PANEL_SECTION_HEADER_BACKGROUND : SIDE_BAR_SECTION_HEADER_BACKGROUND, + headerBorder: isPanel ? PANEL_SECTION_HEADER_BORDER : SIDE_BAR_SECTION_HEADER_BORDER, + dropBackground: isPanel ? PANEL_SECTION_DRAG_AND_DROP_BACKGROUND : SIDE_BAR_DRAG_AND_DROP_BACKGROUND, + leftBorder: isPanel ? PANEL_SECTION_BORDER : undefined }, pane); const disposable = combinedDisposable(pane, onDidFocus, onDidChangeTitleArea, paneStyler, onDidChange, onDidChangeVisibility); const paneItem: IViewPaneItem = { pane, disposable }; @@ -1282,19 +1362,22 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { return; } - overlay = new ViewPaneDropOverlay(pane.dropTargetElement, this.orientation ?? Orientation.VERTICAL, this.themeService); + overlay = new ViewPaneDropOverlay(pane.dropTargetElement, this.orientation ?? Orientation.VERTICAL, this.viewDescriptorService.getViewContainerLocation(this.viewContainer)!, this.themeService); } if (dropData.type === 'composite' && dropData.id !== this.viewContainer.id && !this.viewContainer.rejectAddedViews) { const container = this.viewDescriptorService.getViewContainerById(dropData.id)!; const viewsToMove = this.viewDescriptorService.getViewContainerModel(container).allViewDescriptors; - if (!viewsToMove.some(v => !v.canMoveView)) { - overlay = new ViewPaneDropOverlay(pane.dropTargetElement, this.orientation ?? Orientation.VERTICAL, this.themeService); + if (!viewsToMove.some(v => !v.canMoveView) && viewsToMove.length > 0) { + overlay = new ViewPaneDropOverlay(pane.dropTargetElement, this.orientation ?? Orientation.VERTICAL, this.viewDescriptorService.getViewContainerLocation(this.viewContainer)!, this.themeService); } } } }, + onDragOver: (e) => { + toggleDropEffect(e.eventData.dataTransfer, 'move', overlay !== undefined); + }, onDragLeave: (e) => { overlay?.dispose(); overlay = undefined; @@ -1483,3 +1566,96 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { } } } + +class MoveViewPosition extends Action2 { + constructor(desc: Readonly, private readonly offset: number) { + super(desc); + } + + async run(accessor: ServicesAccessor): Promise { + const viewDescriptorService = accessor.get(IViewDescriptorService); + const contextKeyService = accessor.get(IContextKeyService); + + const viewId = FocusedViewContext.getValue(contextKeyService); + if (viewId === undefined) { + return; + } + + const viewContainer = viewDescriptorService.getViewContainerByViewId(viewId)!; + const model = viewDescriptorService.getViewContainerModel(viewContainer); + + const viewDescriptor = model.visibleViewDescriptors.find(vd => vd.id === viewId)!; + const currentIndex = model.visibleViewDescriptors.indexOf(viewDescriptor); + if (currentIndex + this.offset < 0 || currentIndex + this.offset >= model.visibleViewDescriptors.length) { + return; + } + + const newPosition = model.visibleViewDescriptors[currentIndex + this.offset]; + + model.move(viewDescriptor.id, newPosition.id); + } +} + +registerAction2( + class MoveViewUp extends MoveViewPosition { + constructor() { + super({ + id: 'views.moveViewUp', + title: nls.localize('viewMoveUp', "Move View Up"), + keybinding: { + primary: KeyChord(KeyMod.CtrlCmd + KeyCode.KEY_K, KeyCode.UpArrow), + weight: KeybindingWeight.WorkbenchContrib + 1, + when: FocusedViewContext.notEqualsTo('') + } + }, -1); + } + } +); + +registerAction2( + class MoveViewLeft extends MoveViewPosition { + constructor() { + super({ + id: 'views.moveViewLeft', + title: nls.localize('viewMoveLeft', "Move View Left"), + keybinding: { + primary: KeyChord(KeyMod.CtrlCmd + KeyCode.KEY_K, KeyCode.LeftArrow), + weight: KeybindingWeight.WorkbenchContrib + 1, + when: FocusedViewContext.notEqualsTo('') + } + }, -1); + } + } +); + +registerAction2( + class MoveViewDown extends MoveViewPosition { + constructor() { + super({ + id: 'views.moveViewDown', + title: nls.localize('viewMoveDown', "Move View Down"), + keybinding: { + primary: KeyChord(KeyMod.CtrlCmd + KeyCode.KEY_K, KeyCode.DownArrow), + weight: KeybindingWeight.WorkbenchContrib + 1, + when: FocusedViewContext.notEqualsTo('') + } + }, 1); + } + } +); + +registerAction2( + class MoveViewRight extends MoveViewPosition { + constructor() { + super({ + id: 'views.moveViewRight', + title: nls.localize('viewMoveRight', "Move View Right"), + keybinding: { + primary: KeyChord(KeyMod.CtrlCmd + KeyCode.KEY_K, KeyCode.RightArrow), + weight: KeybindingWeight.WorkbenchContrib + 1, + when: FocusedViewContext.notEqualsTo('') + } + }, 1); + } + } +); diff --git a/src/vs/workbench/browser/parts/views/viewsService.ts b/src/vs/workbench/browser/parts/views/viewsService.ts index 5dfbfbbc065..41c37baae29 100644 --- a/src/vs/workbench/browser/parts/views/viewsService.ts +++ b/src/vs/workbench/browser/parts/views/viewsService.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/views'; import { Disposable, IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IViewDescriptorService, ViewContainer, IViewDescriptor, IView, ViewContainerLocation, IViewsService, IViewPaneContainer, getVisbileViewContextKey } from 'vs/workbench/common/views'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -20,7 +19,7 @@ import { IPaneComposite } from 'vs/workbench/common/panecomposite'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; -import { PaneCompositePanel, PanelRegistry, PanelDescriptor, Extensions as PanelExtensions } from 'vs/workbench/browser/panel'; +import { PanelRegistry, PanelDescriptor, Extensions as PanelExtensions, Panel } from 'vs/workbench/browser/panel'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -34,7 +33,7 @@ import { IProgressIndicator } from 'vs/platform/progress/common/progress'; export class ViewsService extends Disposable implements IViewsService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly viewDisposable: Map; private readonly viewPaneContainers: Map; @@ -159,7 +158,7 @@ export class ViewsService extends Disposable implements IViewsService { constructor() { super({ id: viewDescriptor.focusCommand ? viewDescriptor.focusCommand.id : `${viewDescriptor.id}.focus`, - title: { original: `Focus on ${viewDescriptor.name} View`, value: localize('focus view', "Focus on {0} View", viewDescriptor.name) }, + title: { original: `Focus on ${viewDescriptor.name} View`, value: localize({ key: 'focus view', comment: ['{0} indicates the name of the view to be focused.'] }, "Focus on {0} View", viewDescriptor.name) }, category: composite ? composite.name : localize('view category', "View"), menu: [{ id: MenuId.CommandPalette, @@ -186,8 +185,8 @@ export class ViewsService extends Disposable implements IViewsService { super({ id: `${viewDescriptor.id}.resetViewLocation`, title: { - original: 'Reset View Location', - value: localize('resetViewLocation', "Reset View Location") + original: 'Reset Location', + value: localize('resetViewLocation', "Reset Location") }, menu: [{ id: MenuId.ViewTitleContext, @@ -202,6 +201,15 @@ export class ViewsService extends Disposable implements IViewsService { } run(accessor: ServicesAccessor): void { const viewDescriptorService = accessor.get(IViewDescriptorService); + const defaultContainer = viewDescriptorService.getDefaultContainerById(viewDescriptor.id)!; + const containerModel = viewDescriptorService.getViewContainerModel(defaultContainer)!; + + // The default container is hidden so we should try to reset its location first + if (defaultContainer.hideIfEmpty && containerModel.visibleViewDescriptors.length === 0) { + const defaultLocation = viewDescriptorService.getDefaultViewContainerLocation(defaultContainer)!; + viewDescriptorService.moveViewContainerToLocation(defaultContainer, defaultLocation); + } + viewDescriptorService.moveViewsToContainer([viewDescriptor], viewDescriptorService.getDefaultContainerById(viewDescriptor.id)!); accessor.get(IViewsService).openView(viewDescriptor.id, true); } @@ -267,6 +275,11 @@ export class ViewsService extends Disposable implements IViewsService { return viewContainerId ? this.viewDescriptorService.getViewContainerById(viewContainerId) : null; } + getActiveViewPaneContainerWithId(viewContainerId: string): IViewPaneContainer | null { + const viewContainer = this.viewDescriptorService.getViewContainerById(viewContainerId); + return viewContainer ? this.getActiveViewPaneContainer(viewContainer) : null; + } + async openViewContainer(id: string, focus?: boolean): Promise { const viewContainer = this.viewDescriptorService.getViewContainerById(id); if (viewContainer) { @@ -289,9 +302,9 @@ export class ViewsService extends Disposable implements IViewsService { const viewContainerLocation = this.viewDescriptorService.getViewContainerLocation(viewContainer); switch (viewContainerLocation) { case ViewContainerLocation.Panel: - return this.panelService.getActivePanel()?.getId() === id ? this.panelService.hideActivePanel() : undefined; + return this.panelService.getActivePanel()?.getId() === id ? this.layoutService.setPanelHidden(true) : undefined; case ViewContainerLocation.Sidebar: - return this.viewletService.getActiveViewlet()?.getId() === id ? this.viewletService.hideActiveViewlet() : undefined; + return this.viewletService.getActiveViewlet()?.getId() === id ? this.layoutService.setSideBarHidden(true) : undefined; } } } @@ -414,7 +427,7 @@ export class ViewsService extends Disposable implements IViewsService { private registerPanel(viewContainer: ViewContainer): void { const that = this; - class PaneContainerPanel extends PaneCompositePanel { + class PaneContainerPanel extends Panel { constructor( @ITelemetryService telemetryService: ITelemetryService, @IStorageService storageService: IStorageService, @@ -436,6 +449,7 @@ export class ViewsService extends Disposable implements IViewsService { viewContainer.name, undefined, viewContainer.order, + viewContainer.requestedIndex, viewContainer.focusCommand?.id, )); } @@ -471,6 +485,7 @@ export class ViewsService extends Disposable implements IViewsService { viewContainer.name, isString(viewContainer.icon) ? viewContainer.icon : undefined, viewContainer.order, + viewContainer.requestedIndex, viewContainer.icon instanceof URI ? viewContainer.icon : undefined )); } diff --git a/src/vs/workbench/browser/parts/views/viewsViewlet.ts b/src/vs/workbench/browser/parts/views/viewsViewlet.ts index 8335c7352c3..47b8f051583 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -139,4 +139,8 @@ export abstract class FilterViewPaneContainer extends ViewPaneContainer { } abstract getTitle(): string; + + getViewsVisibilityActions(): IAction[] { + return []; + } } diff --git a/src/vs/workbench/browser/style.ts b/src/vs/workbench/browser/style.ts index c226ce43536..d8a43a7ace3 100644 --- a/src/vs/workbench/browser/style.ts +++ b/src/vs/workbench/browser/style.ts @@ -8,7 +8,7 @@ import 'vs/css!./media/style'; import { registerThemingParticipant, IColorTheme, ICssStyleCollector, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; import { iconForeground, foreground, selectionBackground, focusBorder, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, listHighlightForeground, inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry'; import { WORKBENCH_BACKGROUND, TITLE_BAR_ACTIVE_BACKGROUND } from 'vs/workbench/common/theme'; -import { isWeb, isIOS } from 'vs/base/common/platform'; +import { isWeb, isIOS, isMacintosh, isWindows } from 'vs/base/common/platform'; import { createMetaElement } from 'vs/base/browser/dom'; import { isSafari, isStandalone } from 'vs/base/browser/browser'; @@ -183,3 +183,13 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) = collector.addRule(`body { background-color: ${workbenchBackground}; }`); } }); + +/** + * The best font-family to be used in CSS based on the platform: + * - Windows: Segoe preferred, fallback to sans-serif + * - macOS: standard system font, fallback to sans-serif + * - Linux: standard system font preferred, fallback to Ubuntu fonts + * + * Note: this currently does not adjust for different locales. + */ +export const DEFAULT_FONT_FAMILY = isWindows ? '"Segoe WPC", "Segoe UI", sans-serif' : isMacintosh ? '-apple-system, BlinkMacSystemFont, sans-serif' : 'system-ui, "Ubuntu", "Droid Sans", sans-serif'; diff --git a/src/vs/workbench/browser/viewlet.ts b/src/vs/workbench/browser/viewlet.ts index 355c4ada7a3..5cdb3864c88 100644 --- a/src/vs/workbench/browser/viewlet.ts +++ b/src/vs/workbench/browser/viewlet.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; import { Registry } from 'vs/platform/registry/common/platform'; -import { Action, IAction } from 'vs/base/common/actions'; +import { Action, IAction, Separator, SubmenuAction } from 'vs/base/common/actions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { CompositeDescriptor, CompositeRegistry } from 'vs/workbench/browser/composite'; @@ -26,7 +26,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { PaneComposite } from 'vs/workbench/browser/panecomposite'; -import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Event } from 'vs/base/common/event'; export abstract class Viewlet extends PaneComposite implements IViewlet { @@ -43,6 +43,10 @@ export abstract class Viewlet extends PaneComposite implements IViewlet { @IConfigurationService protected configurationService: IConfigurationService ) { super(id, viewPaneContainer, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); + this._register(Event.any(viewPaneContainer.onDidAddViews, viewPaneContainer.onDidRemoveViews, viewPaneContainer.onTitleAreaUpdate)(() => { + // Update title area since there is no better way to update secondary actions + this.updateTitleArea(); + })); } getContextMenuActions(): IAction[] { @@ -60,6 +64,24 @@ export abstract class Viewlet extends PaneComposite implements IViewlet { run: () => this.layoutService.setSideBarHidden(true) }]; } + + getSecondaryActions(): IAction[] { + const viewVisibilityActions = this.viewPaneContainer.getViewsVisibilityActions(); + const secondaryActions = this.viewPaneContainer.getSecondaryActions(); + if (viewVisibilityActions.length <= 1 || viewVisibilityActions.every(({ enabled }) => !enabled)) { + return secondaryActions; + } + + if (secondaryActions.length === 0) { + return viewVisibilityActions; + } + + return [ + new SubmenuAction('workbench.views', nls.localize('views', "Views"), viewVisibilityActions), + new Separator(), + ...secondaryActions + ]; + } } /** @@ -73,10 +95,11 @@ export class ViewletDescriptor extends CompositeDescriptor { name: string, cssClass?: string, order?: number, + requestedIndex?: number, iconUrl?: URI ): ViewletDescriptor { - return new ViewletDescriptor(ctor as IConstructorSignature0, id, name, cssClass, order, iconUrl); + return new ViewletDescriptor(ctor as IConstructorSignature0, id, name, cssClass, order, requestedIndex, iconUrl); } private constructor( @@ -85,9 +108,10 @@ export class ViewletDescriptor extends CompositeDescriptor { name: string, cssClass?: string, order?: number, + requestedIndex?: number, readonly iconUrl?: URI ) { - super(ctor, id, name, cssClass, order, id); + super(ctor, id, name, cssClass, order, requestedIndex, id); } } @@ -143,8 +167,6 @@ export class ShowViewletAction extends Action { @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService ) { super(id, name); - - this.enabled = !!this.viewletService && !!this.editorGroupService; } async run(): Promise { diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 0122e14207a..0462617196b 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { mark } from 'vs/base/common/performance'; -import { domContentLoaded, addDisposableListener, EventType, addClass, EventHelper } from 'vs/base/browser/dom'; +import { domContentLoaded, addDisposableListener, EventType, EventHelper } from 'vs/base/browser/dom'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ILogService, ConsoleLogService, MultiplexLogService } from 'vs/platform/log/common/log'; import { ConsoleLogInAutomationService } from 'vs/platform/log/browser/log'; @@ -19,7 +19,7 @@ import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteA import { RemoteAuthorityResolverService } from 'vs/platform/remote/browser/remoteAuthorityResolverService'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; -import { IFileService } from 'vs/platform/files/common/files'; +import { IFileService, IFileSystemProvider } from 'vs/platform/files/common/files'; import { FileService } from 'vs/platform/files/common/fileService'; import { Schemas } from 'vs/base/common/network'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -38,21 +38,18 @@ import { FileUserDataProvider } from 'vs/workbench/services/userData/common/file import { BACKUPS } from 'vs/platform/environment/common/environment'; import { joinPath } from 'vs/base/common/resources'; import { BrowserStorageService } from 'vs/platform/storage/browser/storageService'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { getThemeTypeSelector, DARK, HIGH_CONTRAST, LIGHT } from 'vs/platform/theme/common/themeService'; +import { IStorageService } from 'vs/platform/storage/common/storage'; import { registerWindowDriver } from 'vs/platform/driver/browser/driver'; import { BufferLogService } from 'vs/platform/log/common/bufferLog'; import { FileLogService } from 'vs/platform/log/common/fileLogService'; import { toLocalISOString } from 'vs/base/common/date'; -import { IndexedDBLogProvider } from 'vs/workbench/services/log/browser/indexedDBLogProvider'; -import { InMemoryLogProvider } from 'vs/workbench/services/log/common/inMemoryLogProvider'; import { isWorkspaceToOpen, isFolderToOpen } from 'vs/platform/windows/common/windows'; import { getWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces'; import { coalesce } from 'vs/base/common/arrays'; import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; import { WebResourceIdentityService, IResourceIdentityService } from 'vs/platform/resource/common/resourceIdentityService'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { Settings } from 'vs/workbench/browser/layout'; +import { IndexedDB, INDEXEDDB_LOGS_OBJECT_STORE, INDEXEDDB_USERDATA_OBJECT_STORE } from 'vs/platform/files/browser/indexedDBFileSystemProvider'; class BrowserMain extends Disposable { @@ -66,17 +63,9 @@ class BrowserMain extends Disposable { async open(): Promise { const services = await this.initServices(); - const firstOpen = services.storageService.getBoolean(Settings.WORKSPACE_FIRST_OPEN, StorageScope.WORKSPACE); - if (firstOpen === undefined || firstOpen) { - services.storageService.store(Settings.WORKSPACE_FIRST_OPEN, !(firstOpen ?? false), StorageScope.WORKSPACE); - } - await domContentLoaded(); mark('willStartWorkbench'); - // Base Theme - this.restoreBaseTheme(); - // Create Workbench const workbench = new Workbench( this.domElement, @@ -131,7 +120,6 @@ class BrowserMain extends Disposable { })); this._register(workbench.onWillShutdown(() => { storageService.close(); - this.saveBaseTheme(); })); this._register(workbench.onShutdown(() => this.dispose())); @@ -147,21 +135,6 @@ class BrowserMain extends Disposable { }); } - private restoreBaseTheme(): void { - addClass(this.domElement, window.localStorage.getItem('vscode.baseTheme') || getThemeTypeSelector(LIGHT) /* Fallback to a light theme by default on web */); - } - - private saveBaseTheme(): void { - const classes = this.domElement.className; - const baseThemes = [DARK, LIGHT, HIGH_CONTRAST].map(baseTheme => getThemeTypeSelector(baseTheme)); - for (const baseTheme of baseThemes) { - if (classes.indexOf(baseTheme) >= 0) { - window.localStorage.setItem('vscode.baseTheme', baseTheme); - break; - } - } - } - private async initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService, storageService: BrowserStorageService }> { const serviceCollection = new ServiceCollection(); @@ -186,10 +159,7 @@ class BrowserMain extends Disposable { serviceCollection.set(IWorkbenchEnvironmentService, environmentService); // Product - const productService = { - _serviceBrand: undefined, - ...product - }; + const productService: IProductService = { _serviceBrand: undefined, ...product, ...this.configuration.productConfiguration }; serviceCollection.set(IProductService, productService); // Remote @@ -207,7 +177,7 @@ class BrowserMain extends Disposable { // Files const fileService = this._register(new FileService(logService)); serviceCollection.set(IFileService, fileService); - this.registerFileSystemProviders(environmentService, fileService, remoteAgentService, logService, logsPath); + await this.registerFileSystemProviders(environmentService, fileService, remoteAgentService, logService, logsPath); // Long running services (workspace, config, storage) const services = await Promise.all([ @@ -234,24 +204,21 @@ class BrowserMain extends Disposable { return { serviceCollection, logService, storageService: services[1] }; } - private registerFileSystemProviders(environmentService: IWorkbenchEnvironmentService, fileService: IFileService, remoteAgentService: IRemoteAgentService, logService: BufferLogService, logsPath: URI): void { + private async registerFileSystemProviders(environmentService: IWorkbenchEnvironmentService, fileService: IFileService, remoteAgentService: IRemoteAgentService, logService: BufferLogService, logsPath: URI): Promise { + const indexedDB = new IndexedDB(); // Logger (async () => { - if (browser.isEdge) { - fileService.registerProvider(logsPath.scheme, new InMemoryLogProvider(logsPath.scheme)); + let indexedDBLogProvider: IFileSystemProvider | null = null; + try { + indexedDBLogProvider = await indexedDB.createFileSystemProvider(logsPath.scheme, INDEXEDDB_LOGS_OBJECT_STORE); + } catch (error) { + console.error(error); + } + if (indexedDBLogProvider) { + fileService.registerProvider(logsPath.scheme, indexedDBLogProvider); } else { - try { - const indexedDBLogProvider = new IndexedDBLogProvider(logsPath.scheme); - await indexedDBLogProvider.database; - - fileService.registerProvider(logsPath.scheme, indexedDBLogProvider); - } catch (error) { - logService.info('Error while creating indexedDB log provider. Falling back to in-memory log provider.'); - logService.error(error); - - fileService.registerProvider(logsPath.scheme, new InMemoryLogProvider(logsPath.scheme)); - } + fileService.registerProvider(logsPath.scheme, new InMemoryFileSystemProvider()); } logService.logger = new MultiplexLogService(coalesce([ @@ -279,8 +246,19 @@ class BrowserMain extends Disposable { // User data if (!this.configuration.userDataProvider) { - this.configuration.userDataProvider = this._register(new InMemoryFileSystemProvider()); + let indexedDBUserDataProvider: IFileSystemProvider | null = null; + try { + indexedDBUserDataProvider = await indexedDB.createFileSystemProvider(Schemas.userData, INDEXEDDB_USERDATA_OBJECT_STORE); + } catch (error) { + console.error(error); + } + if (indexedDBUserDataProvider) { + this.configuration.userDataProvider = indexedDBUserDataProvider; + } else { + this.configuration.userDataProvider = new InMemoryFileSystemProvider(); + } } + fileService.registerProvider(Schemas.userData, this.configuration.userDataProvider); } diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 18196e719a3..c482eae8ef3 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -22,7 +22,7 @@ import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuratio enum: ['default', 'large'], enumDescriptions: [ nls.localize('workbench.editor.titleScrollbarSizing.default', "The default size."), - nls.localize('workbench.editor.titleScrollbarSizing.large', "Increases the size, so it can be grabed more easily with the mouse") + nls.localize('workbench.editor.titleScrollbarSizing.large', "Increases the size, so it can be grabbed more easily with the mouse") ], description: nls.localize('tabScrollbarHeight', "Controls the height of the scrollbars used for tabs and breadcrumbs in the editor title area."), default: 'default', @@ -231,6 +231,16 @@ import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuratio 'default': true, 'description': nls.localize('activityBarVisibility', "Controls the visibility of the activity bar in the workbench.") }, + 'workbench.activityBar.iconClickBehavior': { + 'type': 'string', + 'enum': ['toggle', 'focus'], + 'default': 'toggle', + 'description': nls.localize('activityBarIconClickBehavior', "Controls the behavior of clicking an activity bar icon in the workbench."), + 'enumDescriptions': [ + nls.localize('workbench.activityBar.iconClickBehavior.toggle', "Hide the side bar if the clicked item is already visible."), + nls.localize('workbench.activityBar.iconClickBehavior.focus', "Focus side bar if the clicked item is already visible.") + ] + }, 'workbench.view.alwaysShowHeaderActions': { 'type': 'boolean', 'default': false, diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index 02adff41fcc..b3601db90a7 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -20,9 +20,7 @@ import { getSingletonServiceDescriptors } from 'vs/platform/instantiation/common import { Position, Parts, IWorkbenchLayoutService, positionToString } from 'vs/workbench/services/layout/browser/layoutService'; import { IStorageService, WillSaveStateReason, StorageScope } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { LifecyclePhase, ILifecycleService, WillShutdownEvent, BeforeShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -32,8 +30,6 @@ import { NotificationsAlerts } from 'vs/workbench/browser/parts/notifications/no import { NotificationsStatus } from 'vs/workbench/browser/parts/notifications/notificationsStatus'; import { registerNotificationCommands } from 'vs/workbench/browser/parts/notifications/notificationsCommands'; import { NotificationsToasts } from 'vs/workbench/browser/parts/notifications/notificationsToasts'; -import { IEditorService, IResourceEditorInputType } from 'vs/workbench/services/editor/common/editorService'; -import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { setARIAContainer } from 'vs/base/browser/ui/aria/aria'; import { readFontInfo, restoreFontInfo, serializeFontInfo } from 'vs/editor/browser/config/configuration'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; @@ -44,8 +40,6 @@ import { coalesce } from 'vs/base/common/arrays'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { Layout } from 'vs/workbench/browser/layout'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { Extensions as PanelExtensions, PanelRegistry } from 'vs/workbench/browser/panel'; -import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; export class Workbench extends Layout { @@ -145,7 +139,8 @@ export class Workbench extends Layout { this.initLayout(accessor); // Registries - this.startRegistries(accessor); + Registry.as(WorkbenchExtensions.Workbench).start(accessor); + Registry.as(EditorExtensions.EditorInputFactories).start(accessor); // Context Keys this._register(instantiationService.createInstance(WorkbenchContextKeysHandler)); @@ -164,7 +159,7 @@ export class Workbench extends Layout { // Restore try { - await this.restoreWorkbench(accessor.get(IEditorService), accessor.get(IEditorGroupsService), accessor.get(IViewDescriptorService), accessor.get(IViewletService), accessor.get(IPanelService), accessor.get(ILogService), lifecycleService); + await this.restoreWorkbench(accessor.get(ILogService), lifecycleService); } catch (error) { onUnexpectedError(error); } @@ -215,11 +210,6 @@ export class Workbench extends Layout { return instantiationService; } - private startRegistries(accessor: ServicesAccessor): void { - Registry.as(WorkbenchExtensions.Workbench).start(accessor); - Registry.as(EditorExtensions.EditorInputFactories).start(accessor); - } - private registerListeners( lifecycleService: ILifecycleService, storageService: IStorageService, @@ -365,7 +355,7 @@ export class Workbench extends Layout { } private createPart(id: string, role: string, classes: string[]): HTMLElement { - const part = document.createElement('div'); + const part = document.createElement(role === 'status' ? 'footer' : 'div'); // Use footer element for status bar #98376 addClasses(part, 'part', ...classes); part.id = id; part.setAttribute('role', role); @@ -399,81 +389,15 @@ export class Workbench extends Layout { } private async restoreWorkbench( - editorService: IEditorService, - editorGroupService: IEditorGroupsService, - viewDescriptorService: IViewDescriptorService, - viewletService: IViewletService, - panelService: IPanelService, logService: ILogService, lifecycleService: ILifecycleService ): Promise { - const restorePromises: Promise[] = []; - - // Restore editors - restorePromises.push((async () => { - mark('willRestoreEditors'); - - // first ensure the editor part is restored - await editorGroupService.whenRestored; - - // then see for editors to open as instructed - let editors: IResourceEditorInputType[]; - if (Array.isArray(this.state.editor.editorsToOpen)) { - editors = this.state.editor.editorsToOpen; - } else { - editors = await this.state.editor.editorsToOpen; - } - - if (editors.length) { - await editorService.openEditors(editors); - } - - mark('didRestoreEditors'); - })()); - - // Restore Sidebar - if (this.state.sideBar.viewletToRestore) { - restorePromises.push((async () => { - mark('willRestoreViewlet'); - - const viewlet = await viewletService.openViewlet(this.state.sideBar.viewletToRestore); - if (!viewlet) { - await viewletService.openViewlet(viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.Sidebar)?.id); // fallback to default viewlet as needed - } - - mark('didRestoreViewlet'); - })()); - } - - // Restore Panel - if (this.state.panel.panelToRestore) { - restorePromises.push((async () => { - mark('willRestorePanel'); - - const panel = await panelService.openPanel(this.state.panel.panelToRestore!); - if (!panel) { - await panelService.openPanel(Registry.as(PanelExtensions.Panels).getDefaultPanelId()); // fallback to default panel as needed - } - - mark('didRestorePanel'); - })()); - } - - // Restore Zen Mode - if (this.state.zenMode.restore) { - this.toggleZenMode(false, true); - } - - // Restore Editor Center Mode - if (this.state.editor.restoreCentered) { - this.centerEditorLayout(true, true); - } // Emit a warning after 10s if restore does not complete const restoreTimeoutHandle = setTimeout(() => logService.warn('Workbench did not finish loading in 10 seconds, that might be a problem that should be reported.'), 10000); try { - await Promise.all(restorePromises); + await super.restoreWorkbenchLayout(); clearTimeout(restoreTimeoutHandle); } catch (error) { @@ -490,6 +414,10 @@ export class Workbench extends Layout { // Telemetry: startup metrics mark('didStartWorkbench'); + + // Perf reporting (devtools) + performance.mark('workbench-end'); + performance.measure('perf: workbench create & restore', 'workbench-start', 'workbench-end'); } } } diff --git a/src/vs/workbench/common/activity.ts b/src/vs/workbench/common/activity.ts index 79a7c6586a0..e87a56d8746 100644 --- a/src/vs/workbench/common/activity.ts +++ b/src/vs/workbench/common/activity.ts @@ -14,3 +14,4 @@ export interface IActivity { } export const GLOBAL_ACTIVITY_ID = 'workbench.action.globalActivity'; +export const ACCOUNTS_ACTIIVTY_ID = 'workbench.action.accountsActivity'; diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index ea6fcaa4edf..202f03996a9 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -14,20 +14,15 @@ import { IInstantiationService, IConstructorSignature0, ServicesAccessor, Brande import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITextModel } from 'vs/editor/common/model'; -import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ICompositeControl, IComposite } from 'vs/workbench/common/composite'; import { ActionRunner, IAction } from 'vs/base/common/actions'; -import { IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; +import { IFileService } from 'vs/platform/files/common/files'; import { IPathData } from 'vs/platform/windows/common/windows'; import { coalesce, firstOrDefault } from 'vs/base/common/arrays'; -import { ITextFileSaveOptions, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IEditorService, IResourceEditorInputType } from 'vs/workbench/services/editor/common/editorService'; -import { isEqual, dirname } from 'vs/base/common/resources'; +import { IResourceEditorInputType } from 'vs/workbench/services/editor/common/editorService'; import { IRange } from 'vs/editor/common/core/range'; -import { createMemoizer } from 'vs/base/common/decorators'; -import { ILabelService } from 'vs/platform/label/common/label'; -import { Schemas } from 'vs/base/common/network'; -import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { IExtUri } from 'vs/base/common/resources'; export const DirtyWorkingCopiesContext = new RawContextKey('dirtyWorkingCopies', false); export const ActiveEditorContext = new RawContextKey('activeEditor', null); @@ -169,13 +164,20 @@ export interface IEditorControl extends ICompositeControl { } export interface IFileEditorInputFactory { - createFileEditorInput(resource: URI, encoding: string | undefined, mode: string | undefined, instantiationService: IInstantiationService): IFileEditorInput; + /** + * Creates new new editor input capable of showing files. + */ + createFileEditorInput(resource: URI, preferredResource: URI | undefined, encoding: string | undefined, mode: string | undefined, instantiationService: IInstantiationService): IFileEditorInput; + /** + * Check if the provided object is a file editor input. + */ isFileEditorInput(obj: unknown): obj is IFileEditorInput; } interface ICustomEditorInputFactory { createCustomEditorInput(resource: URI, instantiationService: IInstantiationService): Promise; + canResolveBackup(editorInput: IEditorInput, backupResource: URI): boolean; } export interface IEditorInputFactoryRegistry { @@ -193,12 +195,12 @@ export interface IEditorInputFactoryRegistry { /** * Registers the custom editor input factory to use for custom inputs. */ - registerCustomEditorInputFactory(factory: ICustomEditorInputFactory): void; + registerCustomEditorInputFactory(scheme: string, factory: ICustomEditorInputFactory): void; /** * Returns the custom editor input factory to use for custom inputs. */ - getCustomEditorInputFactory(): ICustomEditorInputFactory; + getCustomEditorInputFactory(scheme: string): ICustomEditorInputFactory | undefined; /** * Registers a editor input factory for the given editor input to the registry. An editor input factory @@ -233,7 +235,7 @@ export interface IEditorInputFactory { * Returns a string representation of the provided editor input that contains enough information * to deserialize back to the original editor input from the deserialize() method. */ - serialize(editorInput: EditorInput): string | undefined; + serialize(editorInput: IEditorInput): string | undefined; /** * Returns an editor input from the provided serialized form of the editor input. This form matches @@ -404,7 +406,14 @@ export interface IEditorInput extends IDisposable { getTitle(verbosity?: Verbosity): string | undefined; /** - * Resolves the input. + * Returns the aria label to be read out by a screen reader. + */ + getAriaLabel(): string; + + /** + * Returns a type of `IEditorModel` that represents the resolved input. + * Subclasses should override to provide a meaningful model or return + * `null` if the editor does not require a model. */ resolve(): Promise; @@ -459,14 +468,19 @@ export interface IEditorInput extends IDisposable { revert(group: GroupIdentifier, options?: IRevertOptions): Promise; /** - * Called to determine how to handle a resource that is moved that matches + * Called to determine how to handle a resource that is renamed that matches * the editors resource (or is a child of). * * Implementors are free to not implement this method to signal no intent * to participate. If an editor is returned though, it will replace the * current one with that editor and optional options. */ - move(group: GroupIdentifier, target: URI): IMoveResult | undefined; + rename(group: GroupIdentifier, target: URI): IMoveResult | undefined; + + /** + * Subclasses can set this to false if it does not make sense to split the editor input. + */ + supportsSplitEditor(): boolean; /** * Returns if the other object matches this input. @@ -512,6 +526,10 @@ export abstract class EditorInput extends Disposable implements IEditorInput { return this.getName(); } + getAriaLabel(): string { + return this.getTitle(Verbosity.SHORT); + } + /** * Returns the preferred editor for this input. A list of candidate editors is passed in that whee registered * for the input. This allows subclasses to decide late which editor to use for the input on a case by case basis. @@ -534,12 +552,6 @@ export abstract class EditorInput extends Disposable implements IEditorInput { return { typeId: this.getTypeId() }; } - /** - * Returns a type of EditorModel that represents the resolved input. Subclasses should - * override to provide a meaningful model. - */ - abstract resolve(): Promise; - isReadonly(): boolean { return true; } @@ -556,6 +568,10 @@ export abstract class EditorInput extends Disposable implements IEditorInput { return false; } + async resolve(): Promise { + return null; + } + async save(group: GroupIdentifier, options?: ISaveOptions): Promise { return this; } @@ -566,13 +582,10 @@ export abstract class EditorInput extends Disposable implements IEditorInput { async revert(group: GroupIdentifier, options?: IRevertOptions): Promise { } - move(group: GroupIdentifier, target: URI): IMoveResult | undefined { + rename(group: GroupIdentifier, target: URI): IMoveResult | undefined { return undefined; } - /** - * Subclasses can set this to false if it does not make sense to split the editor input. - */ supportsSplitEditor(): boolean { return true; } @@ -595,164 +608,6 @@ export abstract class EditorInput extends Disposable implements IEditorInput { } } -export abstract class TextResourceEditorInput extends EditorInput { - - private static readonly MEMOIZER = createMemoizer(); - - constructor( - public readonly resource: URI, - @IEditorService protected readonly editorService: IEditorService, - @IEditorGroupsService protected readonly editorGroupService: IEditorGroupsService, - @ITextFileService protected readonly textFileService: ITextFileService, - @ILabelService protected readonly labelService: ILabelService, - @IFileService protected readonly fileService: IFileService, - @IFilesConfigurationService protected readonly filesConfigurationService: IFilesConfigurationService - ) { - super(); - - this.registerListeners(); - } - - protected registerListeners(): void { - - // Clear label memoizer on certain events that have impact - this._register(this.labelService.onDidChangeFormatters(e => this.onLabelEvent(e.scheme))); - this._register(this.fileService.onDidChangeFileSystemProviderRegistrations(e => this.onLabelEvent(e.scheme))); - this._register(this.fileService.onDidChangeFileSystemProviderCapabilities(e => this.onLabelEvent(e.scheme))); - } - - private onLabelEvent(scheme: string): void { - if (scheme === this.resource.scheme) { - - // Clear any cached labels from before - TextResourceEditorInput.MEMOIZER.clear(); - - // Trigger recompute of label - this._onDidChangeLabel.fire(); - } - } - - getName(): string { - return this.basename; - } - - @TextResourceEditorInput.MEMOIZER - private get basename(): string { - return this.labelService.getUriBasenameLabel(this.resource); - } - - getDescription(verbosity: Verbosity = Verbosity.MEDIUM): string | undefined { - switch (verbosity) { - case Verbosity.SHORT: - return this.shortDescription; - case Verbosity.LONG: - return this.longDescription; - case Verbosity.MEDIUM: - default: - return this.mediumDescription; - } - } - - @TextResourceEditorInput.MEMOIZER - private get shortDescription(): string { - return this.labelService.getUriBasenameLabel(dirname(this.resource)); - } - - @TextResourceEditorInput.MEMOIZER - private get mediumDescription(): string { - return this.labelService.getUriLabel(dirname(this.resource), { relative: true }); - } - - @TextResourceEditorInput.MEMOIZER - private get longDescription(): string { - return this.labelService.getUriLabel(dirname(this.resource)); - } - - @TextResourceEditorInput.MEMOIZER - private get shortTitle(): string { - return this.getName(); - } - - @TextResourceEditorInput.MEMOIZER - private get mediumTitle(): string { - return this.labelService.getUriLabel(this.resource, { relative: true }); - } - - @TextResourceEditorInput.MEMOIZER - private get longTitle(): string { - return this.labelService.getUriLabel(this.resource); - } - - getTitle(verbosity: Verbosity): string { - switch (verbosity) { - case Verbosity.SHORT: - return this.shortTitle; - case Verbosity.LONG: - return this.longTitle; - default: - case Verbosity.MEDIUM: - return this.mediumTitle; - } - } - - isUntitled(): boolean { - return this.resource.scheme === Schemas.untitled; - } - - isReadonly(): boolean { - if (this.isUntitled()) { - return false; // untitled is never readonly - } - - return this.fileService.hasCapability(this.resource, FileSystemProviderCapabilities.Readonly); - } - - isSaving(): boolean { - if (this.isUntitled()) { - return false; // untitled is never saving automatically - } - - if (this.filesConfigurationService.getAutoSaveMode() === AutoSaveMode.AFTER_SHORT_DELAY) { - return true; // a short auto save is configured, treat this as being saved - } - - return false; - } - - async save(group: GroupIdentifier, options?: ITextFileSaveOptions): Promise { - return this.doSave(group, options, false); - } - - saveAs(group: GroupIdentifier, options?: ITextFileSaveOptions): Promise { - return this.doSave(group, options, true); - } - - private async doSave(group: GroupIdentifier, options: ISaveOptions | undefined, saveAs: boolean): Promise { - - // Save / Save As - let target: URI | undefined; - if (saveAs) { - target = await this.textFileService.saveAs(this.resource, undefined, options); - } else { - target = await this.textFileService.save(this.resource, options); - } - - if (!target) { - return undefined; // save cancelled - } - - if (!isEqual(target, this.resource)) { - return this.editorService.createEditorInput({ resource: target }); - } - - return this; - } - - async revert(group: GroupIdentifier, options?: IRevertOptions): Promise { - await this.textFileService.revert(this.resource, options); - } -} - export const enum EncodingMode { /** @@ -794,17 +649,33 @@ export interface IModeSupport { export interface IFileEditorInput extends IEditorInput, IEncodingSupport, IModeSupport { /** - * Gets the resource this editor is about. + * Gets the resource this file input is about. This will always be the + * canonical form of the resource, so it may differ from the original + * resource that was provided to create the input. Use `preferredResource` + * for the form as it was created. */ readonly resource: URI; /** - * Sets the preferred encoding to use for this input. + * Gets the preferred resource of the editor. In most cases this will + * be identical to the resource. But in some cases the preferredResource + * may differ in path casing to the actual resource because we keep + * canonical forms of resources in-memory. + */ + readonly preferredResource: URI; + + /** + * Sets the preferred resource to use for this file input. + */ + setPreferredResource(preferredResource: URI): void; + + /** + * Sets the preferred encoding to use for this file input. */ setPreferredEncoding(encoding: string): void; /** - * Sets the preferred language mode to use for this input. + * Sets the preferred language mode to use for this file input. */ setPreferredMode(mode: string): void; @@ -814,13 +685,13 @@ export interface IFileEditorInput extends IEditorInput, IEncodingSupport, IModeS setForceOpenAsBinary(): void; /** - * Figure out if the input has been resolved or not. + * Figure out if the file input has been resolved or not. */ isResolved(): boolean; } /** - * Side by side editor inputs that have a master and details side. + * Side by side editor inputs that have a primary and secondary side. */ export class SideBySideEditorInput extends EditorInput { @@ -829,24 +700,46 @@ export class SideBySideEditorInput extends EditorInput { constructor( protected readonly name: string | undefined, private readonly description: string | undefined, - private readonly _details: EditorInput, - private readonly _master: EditorInput + private readonly _secondary: EditorInput, + private readonly _primary: EditorInput ) { super(); this.registerListeners(); } + private registerListeners(): void { + + // When the primary or secondary input gets disposed, dispose this diff editor input + const onceSecondaryDisposed = Event.once(this.secondary.onDispose); + this._register(onceSecondaryDisposed(() => { + if (!this.isDisposed()) { + this.dispose(); + } + })); + + const oncePrimaryDisposed = Event.once(this.primary.onDispose); + this._register(oncePrimaryDisposed(() => { + if (!this.isDisposed()) { + this.dispose(); + } + })); + + // Reemit some events from the primary side to the outside + this._register(this.primary.onDidChangeDirty(() => this._onDidChangeDirty.fire())); + this._register(this.primary.onDidChangeLabel(() => this._onDidChangeLabel.fire())); + } + get resource(): URI | undefined { return undefined; } - get master(): EditorInput { - return this._master; + get primary(): EditorInput { + return this._primary; } - get details(): EditorInput { - return this._details; + get secondary(): EditorInput { + return this._secondary; } getTypeId(): string { @@ -855,7 +748,7 @@ export class SideBySideEditorInput extends EditorInput { getName(): string { if (!this.name) { - return localize('sideBySideLabels', "{0} - {1}", this._details.getName(), this._master.getName()); + return localize('sideBySideLabels', "{0} - {1}", this._secondary.getName(), this._primary.getName()); } return this.name; @@ -866,76 +759,46 @@ export class SideBySideEditorInput extends EditorInput { } isReadonly(): boolean { - return this.master.isReadonly(); + return this.primary.isReadonly(); } isUntitled(): boolean { - return this.master.isUntitled(); + return this.primary.isUntitled(); } isDirty(): boolean { - return this.master.isDirty(); + return this.primary.isDirty(); } isSaving(): boolean { - return this.master.isSaving(); + return this.primary.isSaving(); } save(group: GroupIdentifier, options?: ISaveOptions): Promise { - return this.master.save(group, options); + return this.primary.save(group, options); } saveAs(group: GroupIdentifier, options?: ISaveOptions): Promise { - return this.master.saveAs(group, options); + return this.primary.saveAs(group, options); } revert(group: GroupIdentifier, options?: IRevertOptions): Promise { - return this.master.revert(group, options); + return this.primary.revert(group, options); } getTelemetryDescriptor(): { [key: string]: unknown } { - const descriptor = this.master.getTelemetryDescriptor(); + const descriptor = this.primary.getTelemetryDescriptor(); return Object.assign(descriptor, super.getTelemetryDescriptor()); } - private registerListeners(): void { - - // When the details or master input gets disposed, dispose this diff editor input - const onceDetailsDisposed = Event.once(this.details.onDispose); - this._register(onceDetailsDisposed(() => { - if (!this.isDisposed()) { - this.dispose(); - } - })); - - const onceMasterDisposed = Event.once(this.master.onDispose); - this._register(onceMasterDisposed(() => { - if (!this.isDisposed()) { - this.dispose(); - } - })); - - // Reemit some events from the master side to the outside - this._register(this.master.onDidChangeDirty(() => this._onDidChangeDirty.fire())); - this._register(this.master.onDidChangeLabel(() => this._onDidChangeLabel.fire())); - } - - async resolve(): Promise { - return null; - } - matches(otherInput: unknown): boolean { - if (super.matches(otherInput) === true) { + if (otherInput === this) { return true; } - if (otherInput) { - if (!(otherInput instanceof SideBySideEditorInput)) { - return false; - } - - return this.details.matches(otherInput.details) && this.master.matches(otherInput.master); + if (otherInput instanceof SideBySideEditorInput) { + return this.primary.matches(otherInput.primary) && this.secondary.matches(otherInput.secondary); } return false; @@ -1074,9 +937,12 @@ export class EditorOptions implements IEditorOptions { ignoreError: boolean | undefined; /** - * Does not use editor overrides while opening the editor. + * Allows to override the editor that should be used to display the input: + * - `undefined`: let the editor decide for itself + * - `false`: disable overrides + * - `string`: specific override by id */ - ignoreOverrides: boolean | undefined; + override?: false | string; /** * A optional hint to signal in which context the editor opens. @@ -1134,8 +1000,8 @@ export class EditorOptions implements IEditorOptions { this.index = options.index; } - if (typeof options.ignoreOverrides === 'boolean') { - this.ignoreOverrides = options.ignoreOverrides; + if (typeof options.override === 'string' || options.override === false) { + this.override = options.override; } if (typeof options.context === 'number') { @@ -1167,7 +1033,7 @@ export class TextEditorOptions extends EditorOptions implements ITextEditorOptio selectionRevealType: TextEditorSelectionRevealType | undefined; static from(input?: IBaseResourceEditorInput): TextEditorOptions | undefined { - if (!input || !input.options) { + if (!input?.options) { return undefined; } @@ -1342,7 +1208,7 @@ interface IEditorPartConfiguration { } export interface IEditorPartOptions extends IEditorPartConfiguration { - iconTheme?: string; + hasIcons?: boolean; } export interface IEditorPartOptionsChangeEvent { @@ -1351,8 +1217,8 @@ export interface IEditorPartOptionsChangeEvent { } export enum SideBySideEditor { - MASTER = 1, - DETAILS = 2, + PRIMARY = 1, + SECONDARY = 2, BOTH = 3 } @@ -1361,10 +1227,10 @@ export interface IResourceOptions { filterByScheme?: string | string[]; } -export function toResource(editor: IEditorInput | undefined): URI | undefined; -export function toResource(editor: IEditorInput | undefined, options: IResourceOptions & { supportSideBySide?: SideBySideEditor.MASTER | SideBySideEditor.DETAILS }): URI | undefined; -export function toResource(editor: IEditorInput | undefined, options: IResourceOptions & { supportSideBySide: SideBySideEditor.BOTH }): URI | { master?: URI, detail?: URI } | undefined; -export function toResource(editor: IEditorInput | undefined, options?: IResourceOptions): URI | { master?: URI, detail?: URI } | undefined { +export function toResource(editor: IEditorInput | undefined | null): URI | undefined; +export function toResource(editor: IEditorInput | undefined | null, options: IResourceOptions & { supportSideBySide?: SideBySideEditor.PRIMARY | SideBySideEditor.SECONDARY }): URI | undefined; +export function toResource(editor: IEditorInput | undefined | null, options: IResourceOptions & { supportSideBySide: SideBySideEditor.BOTH }): URI | { primary?: URI, secondary?: URI } | undefined; +export function toResource(editor: IEditorInput | undefined | null, options?: IResourceOptions): URI | { primary?: URI, secondary?: URI } | undefined { if (!editor) { return undefined; } @@ -1372,12 +1238,12 @@ export function toResource(editor: IEditorInput | undefined, options?: IResource if (options?.supportSideBySide && editor instanceof SideBySideEditorInput) { if (options?.supportSideBySide === SideBySideEditor.BOTH) { return { - master: toResource(editor.master, { filterByScheme: options.filterByScheme }), - detail: toResource(editor.details, { filterByScheme: options.filterByScheme }) + primary: toResource(editor.primary, { filterByScheme: options.filterByScheme }), + secondary: toResource(editor.secondary, { filterByScheme: options.filterByScheme }) }; } - editor = options.supportSideBySide === SideBySideEditor.MASTER ? editor.master : editor.details; + editor = options.supportSideBySide === SideBySideEditor.PRIMARY ? editor.primary : editor.secondary; } const resource = editor.resource; @@ -1414,13 +1280,13 @@ export interface IEditorMemento { clearEditorState(resource: URI, group?: IEditorGroup): void; clearEditorState(editor: EditorInput, group?: IEditorGroup): void; - moveEditorState(source: URI, target: URI): void; + moveEditorState(source: URI, target: URI, comparer: IExtUri): void; } class EditorInputFactoryRegistry implements IEditorInputFactoryRegistry { private instantiationService: IInstantiationService | undefined; private fileEditorInputFactory: IFileEditorInputFactory | undefined; - private customEditorInputFactory: ICustomEditorInputFactory | undefined; + private customEditorInputFactoryInstances: Map = new Map(); private readonly editorInputFactoryConstructors: Map> = new Map(); private readonly editorInputFactoryInstances: Map = new Map(); @@ -1448,12 +1314,12 @@ class EditorInputFactoryRegistry implements IEditorInputFactoryRegistry { return assertIsDefined(this.fileEditorInputFactory); } - registerCustomEditorInputFactory(factory: ICustomEditorInputFactory): void { - this.customEditorInputFactory = factory; + registerCustomEditorInputFactory(scheme: string, factory: ICustomEditorInputFactory): void { + this.customEditorInputFactoryInstances.set(scheme, factory); } - getCustomEditorInputFactory(): ICustomEditorInputFactory { - return assertIsDefined(this.customEditorInputFactory); + getCustomEditorInputFactory(scheme: string): ICustomEditorInputFactory | undefined { + return this.customEditorInputFactoryInstances.get(scheme); } registerEditorInputFactory(editorInputId: string, ctor: IConstructorSignature0): IDisposable { @@ -1502,8 +1368,12 @@ export async function pathsToEditors(paths: IPathData[] | undefined, fileService startLineNumber: path.lineNumber, startColumn: path.columnNumber || 1 }, - pinned: true - } : { pinned: true }; + pinned: true, + override: path.overrideId + } : { + pinned: true, + override: path.overrideId + }; let input: IResourceEditorInput | IUntitledTextResourceEditorInput; if (!exists) { @@ -1530,3 +1400,23 @@ export const enum EditorsOrder { */ SEQUENTIAL } + +export function computeEditorAriaLabel(input: IEditorInput, index: number | undefined, group: IEditorGroup | undefined, groupCount: number): string { + let ariaLabel = input.getAriaLabel(); + if (group && !group.isPinned(input)) { + ariaLabel = localize('preview', "{0}, preview", ariaLabel); + } + + if (group && group.isSticky(index ?? input)) { + ariaLabel = localize('pinned', "{0}, pinned", ariaLabel); + } + + // Apply group information to help identify in + // which group we are (only if more than one group + // is actually opened) + if (group && groupCount > 1) { + ariaLabel = `${ariaLabel}, ${group.ariaLabel}`; + } + + return ariaLabel; +} diff --git a/src/vs/workbench/common/editor/diffEditorModel.ts b/src/vs/workbench/common/editor/diffEditorModel.ts index 93f9d7e7fe8..dfa51d62d4d 100644 --- a/src/vs/workbench/common/editor/diffEditorModel.ts +++ b/src/vs/workbench/common/editor/diffEditorModel.ts @@ -13,7 +13,10 @@ import { IEditorModel } from 'vs/platform/editor/common/editor'; export class DiffEditorModel extends EditorModel { protected readonly _originalModel: IEditorModel | null; + get originalModel(): IEditorModel | null { return this._originalModel; } + protected readonly _modifiedModel: IEditorModel | null; + get modifiedModel(): IEditorModel | null { return this._modifiedModel; } constructor(originalModel: IEditorModel | null, modifiedModel: IEditorModel | null) { super(); @@ -22,14 +25,6 @@ export class DiffEditorModel extends EditorModel { this._modifiedModel = modifiedModel; } - get originalModel(): IEditorModel | null { - return this._originalModel; - } - - get modifiedModel(): IEditorModel | null { - return this._modifiedModel; - } - async load(): Promise { await Promise.all([ this._originalModel?.load(), diff --git a/src/vs/workbench/common/editor/editorGroup.ts b/src/vs/workbench/common/editor/editorGroup.ts index 537d857d717..99d46391837 100644 --- a/src/vs/workbench/common/editor/editorGroup.ts +++ b/src/vs/workbench/common/editor/editorGroup.ts @@ -83,6 +83,9 @@ export class EditorGroup extends Disposable { private readonly _onDidChangeEditorPinned = this._register(new Emitter()); readonly onDidChangeEditorPinned = this._onDidChangeEditorPinned.event; + private readonly _onDidChangeEditorSticky = this._register(new Emitter()); + readonly onDidChangeEditorSticky = this._onDidChangeEditorSticky.event; + //#endregion private _id: GroupIdentifier; @@ -560,6 +563,9 @@ export class EditorGroup extends Disposable { // Adjust sticky index this.sticky++; + + // Event + this._onDidChangeEditorSticky.fire(editor); } unstick(candidate: EditorInput): EditorInput | undefined { @@ -585,6 +591,9 @@ export class EditorGroup extends Disposable { // Adjust sticky index this.sticky--; + + // Event + this._onDidChangeEditorSticky.fire(editor); } isSticky(candidateOrIndex: EditorInput | number): boolean { @@ -680,14 +689,14 @@ export class EditorGroup extends Disposable { return [this.editors[index], index]; } - contains(candidate: EditorInput, searchInSideBySideEditors?: boolean): boolean { + contains(candidate: EditorInput, options?: { supportSideBySide?: boolean, strictEquals?: boolean }): boolean { for (const editor of this.editors) { - if (this.matches(editor, candidate)) { + if (this.matches(editor, candidate, options?.strictEquals)) { return true; } - if (searchInSideBySideEditors && editor instanceof SideBySideEditorInput) { - if (this.matches(editor.master, candidate) || this.matches(editor.details, candidate)) { + if (options?.supportSideBySide && editor instanceof SideBySideEditorInput) { + if (this.matches(editor.primary, candidate, options?.strictEquals) || this.matches(editor.secondary, candidate, options?.strictEquals)) { return true; } } @@ -696,11 +705,15 @@ export class EditorGroup extends Disposable { return false; } - private matches(editor: IEditorInput | null, candidate: IEditorInput | null): boolean { + private matches(editor: IEditorInput | null, candidate: IEditorInput | null, strictEquals?: boolean): boolean { if (!editor || !candidate) { return false; } + if (strictEquals) { + return editor === candidate; + } + return editor.matches(candidate); } diff --git a/src/vs/workbench/common/editor/resourceEditorInput.ts b/src/vs/workbench/common/editor/resourceEditorInput.ts index 3ea14b511f8..d9006712fcf 100644 --- a/src/vs/workbench/common/editor/resourceEditorInput.ts +++ b/src/vs/workbench/common/editor/resourceEditorInput.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITextEditorModel, IModeSupport, TextResourceEditorInput } from 'vs/workbench/common/editor'; +import { ITextEditorModel, IModeSupport } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; import { IReference } from 'vs/base/common/lifecycle'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; @@ -14,12 +14,14 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { IFileService } from 'vs/platform/files/common/files'; import { ILabelService } from 'vs/platform/label/common/label'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; +import { extUri } from 'vs/base/common/resources'; /** * A read-only text editor input whos contents are made of the provided resource that points to an existing * code editor model. */ -export class ResourceEditorInput extends TextResourceEditorInput implements IModeSupport { +export class ResourceEditorInput extends AbstractTextResourceEditorInput implements IModeSupport { static readonly ID: string = 'workbench.editors.resourceEditorInput'; @@ -27,9 +29,9 @@ export class ResourceEditorInput extends TextResourceEditorInput implements IMod private modelReference: Promise> | undefined = undefined; constructor( + resource: URI, private name: string | undefined, private description: string | undefined, - resource: URI, private preferredMode: string | undefined, @ITextModelService private readonly textModelResolverService: ITextModelService, @ITextFileService textFileService: ITextFileService, @@ -39,7 +41,7 @@ export class ResourceEditorInput extends TextResourceEditorInput implements IMod @ILabelService labelService: ILabelService, @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService ) { - super(resource, editorService, editorGroupService, textFileService, labelService, fileService, filesConfigurationService); + super(resource, undefined, editorService, editorGroupService, textFileService, labelService, fileService, filesConfigurationService); } getTypeId(): string { @@ -64,6 +66,7 @@ export class ResourceEditorInput extends TextResourceEditorInput implements IMod setDescription(description: string): void { if (this.description !== description) { this.description = description; + this._onDidChangeLabel.fire(); } } @@ -87,9 +90,8 @@ export class ResourceEditorInput extends TextResourceEditorInput implements IMod const ref = await this.modelReference; - const model = ref.object; - // Ensure the resolved model is of expected type + const model = ref.object; if (!(model instanceof ResourceEditorModel)) { ref.dispose(); this.modelReference = undefined; @@ -108,13 +110,12 @@ export class ResourceEditorInput extends TextResourceEditorInput implements IMod } matches(otherInput: unknown): boolean { - if (super.matches(otherInput) === true) { + if (otherInput === this) { return true; } - // Compare by properties if (otherInput instanceof ResourceEditorInput) { - return otherInput.resource.toString() === this.resource.toString(); + return extUri.isEqual(otherInput.resource, this.resource); } return false; diff --git a/src/vs/workbench/common/editor/textDiffEditorModel.ts b/src/vs/workbench/common/editor/textDiffEditorModel.ts index 579499c30f6..fcb97b428b2 100644 --- a/src/vs/workbench/common/editor/textDiffEditorModel.ts +++ b/src/vs/workbench/common/editor/textDiffEditorModel.ts @@ -15,9 +15,13 @@ import { DiffEditorModel } from 'vs/workbench/common/editor/diffEditorModel'; export class TextDiffEditorModel extends DiffEditorModel { protected readonly _originalModel: BaseTextEditorModel | null; + get originalModel(): BaseTextEditorModel | null { return this._originalModel; } + protected readonly _modifiedModel: BaseTextEditorModel | null; + get modifiedModel(): BaseTextEditorModel | null { return this._modifiedModel; } private _textDiffEditorModel: IDiffEditorModel | null = null; + get textDiffEditorModel(): IDiffEditorModel | null { return this._textDiffEditorModel; } constructor(originalModel: BaseTextEditorModel, modifiedModel: BaseTextEditorModel) { super(originalModel, modifiedModel); @@ -28,14 +32,6 @@ export class TextDiffEditorModel extends DiffEditorModel { this.updateTextDiffEditorModel(); } - get originalModel(): BaseTextEditorModel | null { - return this._originalModel; - } - - get modifiedModel(): BaseTextEditorModel | null { - return this._modifiedModel; - } - async load(): Promise { await super.load(); @@ -63,10 +59,6 @@ export class TextDiffEditorModel extends DiffEditorModel { } } - get textDiffEditorModel(): IDiffEditorModel | null { - return this._textDiffEditorModel; - } - isResolved(): boolean { return !!this._textDiffEditorModel; } diff --git a/src/vs/workbench/common/editor/textEditorModel.ts b/src/vs/workbench/common/editor/textEditorModel.ts index 2627ffc5890..f730d008e9a 100644 --- a/src/vs/workbench/common/editor/textEditorModel.ts +++ b/src/vs/workbench/common/editor/textEditorModel.ts @@ -41,7 +41,7 @@ export class BaseTextEditorModel extends EditorModel implements ITextEditorModel // We need the resource to point to an existing model const model = this.modelService.getModel(textEditorModelHandle); if (!model) { - throw new Error(`Document with resource ${textEditorModelHandle.toString()} does not exist`); + throw new Error(`Document with resource ${textEditorModelHandle.toString(true)} does not exist`); } this.textEditorModelHandle = textEditorModelHandle; diff --git a/src/vs/workbench/common/editor/textResourceEditorInput.ts b/src/vs/workbench/common/editor/textResourceEditorInput.ts new file mode 100644 index 00000000000..92bb60db4ad --- /dev/null +++ b/src/vs/workbench/common/editor/textResourceEditorInput.ts @@ -0,0 +1,196 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { EditorInput, Verbosity, GroupIdentifier, IEditorInput, ISaveOptions, IRevertOptions } from 'vs/workbench/common/editor'; +import { URI } from 'vs/base/common/uri'; +import { ITextFileService, ITextFileSaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { createMemoizer } from 'vs/base/common/decorators'; +import { Schemas } from 'vs/base/common/network'; +import { dirname, extUri } from 'vs/base/common/resources'; + +/** + * The base class for all editor inputs that open in text editors. + */ +export abstract class AbstractTextResourceEditorInput extends EditorInput { + + private static readonly MEMOIZER = createMemoizer(); + + private _preferredResource: URI; + get preferredResource(): URI { return this._preferredResource; } + + constructor( + public readonly resource: URI, + preferredResource: URI | undefined, + @IEditorService protected readonly editorService: IEditorService, + @IEditorGroupsService protected readonly editorGroupService: IEditorGroupsService, + @ITextFileService protected readonly textFileService: ITextFileService, + @ILabelService protected readonly labelService: ILabelService, + @IFileService protected readonly fileService: IFileService, + @IFilesConfigurationService protected readonly filesConfigurationService: IFilesConfigurationService + ) { + super(); + + this._preferredResource = preferredResource || resource; + + this.registerListeners(); + } + + protected registerListeners(): void { + + // Clear label memoizer on certain events that have impact + this._register(this.labelService.onDidChangeFormatters(e => this.onLabelEvent(e.scheme))); + this._register(this.fileService.onDidChangeFileSystemProviderRegistrations(e => this.onLabelEvent(e.scheme))); + this._register(this.fileService.onDidChangeFileSystemProviderCapabilities(e => this.onLabelEvent(e.scheme))); + } + + private onLabelEvent(scheme: string): void { + if (scheme === this._preferredResource.scheme) { + this.updateLabel(); + } + } + + private updateLabel(): void { + + // Clear any cached labels from before + AbstractTextResourceEditorInput.MEMOIZER.clear(); + + // Trigger recompute of label + this._onDidChangeLabel.fire(); + } + + setPreferredResource(preferredResource: URI): void { + if (!extUri.isEqual(preferredResource, this._preferredResource)) { + this._preferredResource = preferredResource; + + this.updateLabel(); + } + } + + getName(): string { + return this.basename; + } + + @AbstractTextResourceEditorInput.MEMOIZER + private get basename(): string { + return this.labelService.getUriBasenameLabel(this._preferredResource); + } + + getDescription(verbosity: Verbosity = Verbosity.MEDIUM): string | undefined { + switch (verbosity) { + case Verbosity.SHORT: + return this.shortDescription; + case Verbosity.LONG: + return this.longDescription; + case Verbosity.MEDIUM: + default: + return this.mediumDescription; + } + } + + @AbstractTextResourceEditorInput.MEMOIZER + private get shortDescription(): string { + return this.labelService.getUriBasenameLabel(dirname(this._preferredResource)); + } + + @AbstractTextResourceEditorInput.MEMOIZER + private get mediumDescription(): string { + return this.labelService.getUriLabel(dirname(this._preferredResource), { relative: true }); + } + + @AbstractTextResourceEditorInput.MEMOIZER + private get longDescription(): string { + return this.labelService.getUriLabel(dirname(this._preferredResource)); + } + + @AbstractTextResourceEditorInput.MEMOIZER + private get shortTitle(): string { + return this.getName(); + } + + @AbstractTextResourceEditorInput.MEMOIZER + private get mediumTitle(): string { + return this.labelService.getUriLabel(this._preferredResource, { relative: true }); + } + + @AbstractTextResourceEditorInput.MEMOIZER + private get longTitle(): string { + return this.labelService.getUriLabel(this._preferredResource); + } + + getTitle(verbosity: Verbosity): string { + switch (verbosity) { + case Verbosity.SHORT: + return this.shortTitle; + case Verbosity.LONG: + return this.longTitle; + default: + case Verbosity.MEDIUM: + return this.mediumTitle; + } + } + + isUntitled(): boolean { + return this.resource.scheme === Schemas.untitled; + } + + isReadonly(): boolean { + if (this.isUntitled()) { + return false; // untitled is never readonly + } + + return this.fileService.hasCapability(this.resource, FileSystemProviderCapabilities.Readonly); + } + + isSaving(): boolean { + if (this.isUntitled()) { + return false; // untitled is never saving automatically + } + + if (this.filesConfigurationService.getAutoSaveMode() === AutoSaveMode.AFTER_SHORT_DELAY) { + return true; // a short auto save is configured, treat this as being saved + } + + return false; + } + + save(group: GroupIdentifier, options?: ITextFileSaveOptions): Promise { + return this.doSave(group, options, false); + } + + saveAs(group: GroupIdentifier, options?: ITextFileSaveOptions): Promise { + return this.doSave(group, options, true); + } + + private async doSave(group: GroupIdentifier, options: ISaveOptions | undefined, saveAs: boolean): Promise { + + // Save / Save As + let target: URI | undefined; + if (saveAs) { + target = await this.textFileService.saveAs(this.resource, undefined, options); + } else { + target = await this.textFileService.save(this.resource, options); + } + + if (!target) { + return undefined; // save cancelled + } + + // If the target is a different resource, return with a new editor input + if (!extUri.isEqual(target, this.resource)) { + return this.editorService.createEditorInput({ resource: target }); + } + + return this; + } + + async revert(group: GroupIdentifier, options?: IRevertOptions): Promise { + await this.textFileService.revert(this.resource, options); + } +} diff --git a/src/vs/workbench/common/resources.ts b/src/vs/workbench/common/resources.ts index 2a7844da48f..18ea0bfedb4 100644 --- a/src/vs/workbench/common/resources.ts +++ b/src/vs/workbench/common/resources.ts @@ -39,19 +39,19 @@ export class ResourceContextKey extends Disposable implements IContextKey { private readonly _isFileSystemResource: IContextKey; constructor( - @IContextKeyService contextKeyService: IContextKeyService, + @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IFileService private readonly _fileService: IFileService, @IModeService private readonly _modeService: IModeService ) { super(); - this._schemeKey = ResourceContextKey.Scheme.bindTo(contextKeyService); - this._filenameKey = ResourceContextKey.Filename.bindTo(contextKeyService); - this._langIdKey = ResourceContextKey.LangId.bindTo(contextKeyService); - this._resourceKey = ResourceContextKey.Resource.bindTo(contextKeyService); - this._extensionKey = ResourceContextKey.Extension.bindTo(contextKeyService); - this._hasResource = ResourceContextKey.HasResource.bindTo(contextKeyService); - this._isFileSystemResource = ResourceContextKey.IsFileSystemResource.bindTo(contextKeyService); + this._schemeKey = ResourceContextKey.Scheme.bindTo(this._contextKeyService); + this._filenameKey = ResourceContextKey.Filename.bindTo(this._contextKeyService); + this._langIdKey = ResourceContextKey.LangId.bindTo(this._contextKeyService); + this._resourceKey = ResourceContextKey.Resource.bindTo(this._contextKeyService); + this._extensionKey = ResourceContextKey.Extension.bindTo(this._contextKeyService); + this._hasResource = ResourceContextKey.HasResource.bindTo(this._contextKeyService); + this._isFileSystemResource = ResourceContextKey.IsFileSystemResource.bindTo(this._contextKeyService); this._register(_fileService.onDidChangeFileSystemProviderRegistrations(() => { const resource = this._resourceKey.get(); @@ -66,24 +66,28 @@ export class ResourceContextKey extends Disposable implements IContextKey { set(value: URI | null) { if (!ResourceContextKey._uriEquals(this._resourceKey.get(), value)) { - this._resourceKey.set(value); - this._schemeKey.set(value ? value.scheme : null); - this._filenameKey.set(value ? basename(value) : null); - this._langIdKey.set(value ? this._modeService.getModeIdByFilepathOrFirstLine(value) : null); - this._extensionKey.set(value ? extname(value) : null); - this._hasResource.set(!!value); - this._isFileSystemResource.set(value ? this._fileService.canHandleResource(value) : false); + this._contextKeyService.bufferChangeEvents(() => { + this._resourceKey.set(value); + this._schemeKey.set(value ? value.scheme : null); + this._filenameKey.set(value ? basename(value) : null); + this._langIdKey.set(value ? this._modeService.getModeIdByFilepathOrFirstLine(value) : null); + this._extensionKey.set(value ? extname(value) : null); + this._hasResource.set(!!value); + this._isFileSystemResource.set(value ? this._fileService.canHandleResource(value) : false); + }); } } reset(): void { - this._schemeKey.reset(); - this._langIdKey.reset(); - this._resourceKey.reset(); - this._langIdKey.reset(); - this._extensionKey.reset(); - this._hasResource.reset(); - this._isFileSystemResource.reset(); + this._contextKeyService.bufferChangeEvents(() => { + this._schemeKey.reset(); + this._langIdKey.reset(); + this._resourceKey.reset(); + this._langIdKey.reset(); + this._extensionKey.reset(); + this._hasResource.reset(); + this._isFileSystemResource.reset(); + }); } get(): URI | undefined { diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index d5336b2df02..1dac03c1d3b 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -283,18 +283,50 @@ export const PANEL_ACTIVE_TITLE_BORDER = registerColor('panelTitle.activeBorder' hc: contrastBorder }, nls.localize('panelActiveTitleBorder', "Border color for the active panel title. Panels are shown below the editor area and contain views like output and integrated terminal.")); -export const PANEL_DRAG_AND_DROP_BACKGROUND = registerColor('panel.dropBackground', { - dark: Color.white.transparent(0.12), - light: Color.fromHex('#2677CB').transparent(0.18), - hc: Color.white.transparent(0.12) -}, nls.localize('panelDragAndDropBackground', "Drag and drop feedback color for the panel title items. The color should have transparency so that the panel entries can still shine through. Panels are shown below the editor area and contain views like output and integrated terminal.")); - export const PANEL_INPUT_BORDER = registerColor('panelInput.border', { dark: null, light: Color.fromHex('#ddd'), hc: null }, nls.localize('panelInputBorder', "Input box border for inputs in the panel.")); +export const PANEL_DRAG_AND_DROP_BORDER = registerColor('panel.dropBorder', { + dark: PANEL_ACTIVE_TITLE_FOREGROUND, + light: PANEL_ACTIVE_TITLE_FOREGROUND, + hc: PANEL_ACTIVE_TITLE_FOREGROUND, +}, nls.localize('panelDragAndDropBorder', "Drag and drop feedback color for the panel titles. Panels are shown below the editor area and contain views like output and integrated terminal.")); + + +export const PANEL_SECTION_DRAG_AND_DROP_BACKGROUND = registerColor('panelSection.dropBackground', { + dark: EDITOR_DRAG_AND_DROP_BACKGROUND, + light: EDITOR_DRAG_AND_DROP_BACKGROUND, + hc: EDITOR_DRAG_AND_DROP_BACKGROUND, +}, nls.localize('panelSectionDragAndDropBackground', "Drag and drop feedback color for the panel sections. The color should have transparency so that the panel sections can still shine through. Panels are shown below the editor area and contain views like output and integrated terminal.")); + +export const PANEL_SECTION_HEADER_BACKGROUND = registerColor('panelSectionHeader.background', { + dark: Color.fromHex('#808080').transparent(0.2), + light: Color.fromHex('#808080').transparent(0.2), + hc: null +}, nls.localize('panelSectionHeaderBackground', "Panel section header background color. Panels are shown below the editor area and contain views like output and integrated terminal.")); + +export const PANEL_SECTION_HEADER_FOREGROUND = registerColor('panelSectionHeader.foreground', { + dark: null, + light: null, + hc: null +}, nls.localize('panelSectionHeaderForeground', "Panel section header foreground color. Panels are shown below the editor area and contain views like output and integrated terminal.")); + +export const PANEL_SECTION_HEADER_BORDER = registerColor('panelSectionHeader.border', { + dark: contrastBorder, + light: contrastBorder, + hc: contrastBorder +}, nls.localize('panelSectionHeaderBorder', "Panel section header border color used when multiple views are stacked vertically in the panel. Panels are shown below the editor area and contain views like output and integrated terminal.")); + +export const PANEL_SECTION_BORDER = registerColor('panelSection.border', { + dark: PANEL_BORDER, + light: PANEL_BORDER, + hc: PANEL_BORDER +}, nls.localize('panelSectionBorder', "Panel section border color used when multiple views are stacked horizontally in the panel. Panels are shown below the editor area and contain views like output and integrated terminal.")); + + // < --- Status --- > export const STATUS_BAR_FOREGROUND = registerColor('statusBar.foreground', { @@ -407,11 +439,11 @@ export const ACTIVITY_BAR_ACTIVE_BACKGROUND = registerColor('activityBar.activeB hc: null }, nls.localize('activityBarActiveBackground', "Activity bar background color for the active item. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); -export const ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND = registerColor('activityBar.dropBackground', { - dark: Color.white.transparent(0.12), - light: Color.white.transparent(0.12), - hc: Color.white.transparent(0.12), -}, nls.localize('activityBarDragAndDropBackground', "Drag and drop feedback color for the activity bar items. The color should have transparency so that the activity bar entries can still shine through. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); +export const ACTIVITY_BAR_DRAG_AND_DROP_BORDER = registerColor('activityBar.dropBorder', { + dark: ACTIVITY_BAR_FOREGROUND, + light: ACTIVITY_BAR_FOREGROUND, + hc: ACTIVITY_BAR_FOREGROUND, +}, nls.localize('activityBarDragAndDropBorder', "Drag and drop feedback color for the activity bar items. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); export const ACTIVITY_BAR_BADGE_BACKGROUND = registerColor('activityBarBadge.background', { dark: '#007ACC', @@ -480,9 +512,9 @@ export const SIDE_BAR_TITLE_FOREGROUND = registerColor('sideBarTitle.foreground' }, nls.localize('sideBarTitleForeground', "Side bar title foreground color. The side bar is the container for views like explorer and search.")); export const SIDE_BAR_DRAG_AND_DROP_BACKGROUND = registerColor('sideBar.dropBackground', { - dark: Color.white.transparent(0.12), - light: Color.black.transparent(0.1), - hc: Color.white.transparent(0.3), + dark: EDITOR_DRAG_AND_DROP_BACKGROUND, + light: EDITOR_DRAG_AND_DROP_BACKGROUND, + hc: EDITOR_DRAG_AND_DROP_BACKGROUND, }, nls.localize('sideBarDragAndDropBackground', "Drag and drop feedback color for the side bar sections. The color should have transparency so that the side bar sections can still shine through. The side bar is the container for views like explorer and search.")); export const SIDE_BAR_SECTION_HEADER_BACKGROUND = registerColor('sideBarSectionHeader.background', { @@ -510,31 +542,31 @@ export const TITLE_BAR_ACTIVE_FOREGROUND = registerColor('titleBar.activeForegro dark: '#CCCCCC', light: '#333333', hc: '#FFFFFF' -}, nls.localize('titleBarActiveForeground', "Title bar foreground when the window is active. Note that this color is currently only supported on macOS.")); +}, nls.localize('titleBarActiveForeground', "Title bar foreground when the window is active.")); export const TITLE_BAR_INACTIVE_FOREGROUND = registerColor('titleBar.inactiveForeground', { dark: transparent(TITLE_BAR_ACTIVE_FOREGROUND, 0.6), light: transparent(TITLE_BAR_ACTIVE_FOREGROUND, 0.6), hc: null -}, nls.localize('titleBarInactiveForeground', "Title bar foreground when the window is inactive. Note that this color is currently only supported on macOS.")); +}, nls.localize('titleBarInactiveForeground', "Title bar foreground when the window is inactive.")); export const TITLE_BAR_ACTIVE_BACKGROUND = registerColor('titleBar.activeBackground', { dark: '#3C3C3C', light: '#DDDDDD', hc: '#000000' -}, nls.localize('titleBarActiveBackground', "Title bar background when the window is active. Note that this color is currently only supported on macOS.")); +}, nls.localize('titleBarActiveBackground', "Title bar background when the window is active.")); export const TITLE_BAR_INACTIVE_BACKGROUND = registerColor('titleBar.inactiveBackground', { dark: transparent(TITLE_BAR_ACTIVE_BACKGROUND, 0.6), light: transparent(TITLE_BAR_ACTIVE_BACKGROUND, 0.6), hc: null -}, nls.localize('titleBarInactiveBackground', "Title bar background when the window is inactive. Note that this color is currently only supported on macOS.")); +}, nls.localize('titleBarInactiveBackground', "Title bar background when the window is inactive.")); export const TITLE_BAR_BORDER = registerColor('titleBar.border', { dark: null, light: null, hc: contrastBorder -}, nls.localize('titleBarBorder', "Title bar border color. Note that this color is currently only supported on macOS.")); +}, nls.localize('titleBarBorder', "Title bar border color.")); // < --- Menubar --- > diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 6b7a1506c99..8726ead6be5 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -11,7 +11,7 @@ import { localize } from 'vs/nls'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { values, keys, getOrSet } from 'vs/base/common/map'; +import { getOrSet } from 'vs/base/common/map'; import { Registry } from 'vs/platform/registry/common/platform'; import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IAction, IActionViewItem } from 'vs/base/common/actions'; @@ -22,6 +22,9 @@ import { SetMap } from 'vs/base/common/collections'; import { IProgressIndicator } from 'vs/platform/progress/common/progress'; import Severity from 'vs/base/common/severity'; import { IPaneComposite } from 'vs/workbench/common/panecomposite'; +import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { mixin } from 'vs/base/common/objects'; export const TEST_VIEW_CONTAINER_ID = 'workbench.view.extension.test'; @@ -49,8 +52,6 @@ export interface IViewContainerDescriptor { readonly alwaysUseContainerInfo?: boolean; - readonly order?: number; - readonly focusCommand?: { id: string, keybindings?: IKeybindings }; readonly viewOrderDelegate?: ViewOrderDelegate; @@ -60,6 +61,10 @@ export interface IViewContainerDescriptor { readonly extensionId?: ExtensionIdentifier; readonly rejectAddedViews?: boolean; + + readonly order?: number; + + requestedIndex?: number; } export interface IViewContainersRegistry { @@ -136,7 +141,7 @@ class ViewContainersRegistryImpl extends Disposable implements IViewContainersRe private readonly defaultViewContainers: ViewContainer[] = []; get all(): ViewContainer[] { - return flatten(values(this.viewContainers)); + return flatten([...this.viewContainers.values()]); } registerViewContainer(viewContainerDescriptor: IViewContainerDescriptor, viewContainerLocation: ViewContainerLocation, isDefault?: boolean): ViewContainer { @@ -156,7 +161,7 @@ class ViewContainersRegistryImpl extends Disposable implements IViewContainersRe } deregisterViewContainer(viewContainer: ViewContainer): void { - for (const viewContainerLocation of keys(this.viewContainers)) { + for (const viewContainerLocation of this.viewContainers.keys()) { const viewContainers = this.viewContainers.get(viewContainerLocation)!; const index = viewContainers?.indexOf(viewContainer); if (index !== -1) { @@ -179,7 +184,7 @@ class ViewContainersRegistryImpl extends Disposable implements IViewContainersRe } getViewContainerLocation(container: ViewContainer): ViewContainerLocation { - return keys(this.viewContainers).filter(location => this.getViewContainers(location).filter(viewContainer => viewContainer.id === container.id).length > 0)[0]; + return [...this.viewContainers.keys()].filter(location => this.getViewContainers(location).filter(viewContainer => viewContainer.id === container.id).length > 0)[0]; } getDefaultViewContainer(location: ViewContainerLocation): ViewContainer | undefined { @@ -236,6 +241,11 @@ export interface IAddedViewDescriptorRef extends IViewDescriptorRef { size?: number; } +export interface IAddedViewDescriptorState { + viewDescriptor: IViewDescriptor, + collapsed?: boolean; +} + export interface IViewContainerModel { readonly title: string; @@ -356,7 +366,7 @@ class ViewsRegistry extends Disposable implements IViewsRegistry { } moveViews(viewsToMove: IViewDescriptor[], viewContainer: ViewContainer): void { - keys(this._views).forEach(container => { + for (const container of this._views.keys()) { if (container !== viewContainer) { const views = this.removeViews(viewsToMove, container); if (views.length) { @@ -364,7 +374,7 @@ class ViewsRegistry extends Disposable implements IViewsRegistry { this._onDidChangeContainer.fire({ views, from: container, to: viewContainer }); } } - }); + } } getViews(loc: ViewContainer): IViewDescriptor[] { @@ -431,7 +441,7 @@ class ViewsRegistry extends Disposable implements IViewsRegistry { const viewsToDeregister: IViewDescriptor[] = []; const remaningViews: IViewDescriptor[] = []; for (const view of views) { - if (viewDescriptors.indexOf(view) === -1) { + if (!viewDescriptors.includes(view)) { remaningViews.push(view); } else { viewsToDeregister.push(view); @@ -467,7 +477,7 @@ export interface IView { export const IViewsService = createDecorator('viewsService'); export interface IViewsService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; // View Container APIs readonly onDidChangeViewContainerVisibility: Event<{ id: string, visible: boolean, location: ViewContainerLocation }>; @@ -475,6 +485,7 @@ export interface IViewsService { openViewContainer(id: string, focus?: boolean): Promise; closeViewContainer(id: string): void; getVisibleViewContainer(location: ViewContainerLocation): ViewContainer | null; + getActiveViewPaneContainerWithId(viewContainerId: string): IViewPaneContainer | null; // View APIs readonly onDidChangeViewVisibility: Event<{ id: string, visible: boolean }>; @@ -495,7 +506,7 @@ export const IViewDescriptorService = createDecorator('v export interface IViewDescriptorService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; // ViewContainers readonly viewContainers: ReadonlyArray; @@ -509,7 +520,7 @@ export interface IViewDescriptorService { getViewContainerModel(viewContainer: ViewContainer): IViewContainerModel; readonly onDidChangeContainerLocation: Event<{ viewContainer: ViewContainer, from: ViewContainerLocation, to: ViewContainerLocation }>; - moveViewContainerToLocation(viewContainer: ViewContainer, location: ViewContainerLocation): void; + moveViewContainerToLocation(viewContainer: ViewContainer, location: ViewContainerLocation, requestedIndex?: number): void; // Views getViewDescriptorById(id: string): IViewDescriptor | null; @@ -572,6 +583,7 @@ export interface ITreeView extends IDisposable { setFocus(item: ITreeItem): void; + show(container: any): void; } export interface IRevealOptions { @@ -605,6 +617,8 @@ export interface ITreeItemLabel { highlights?: [number, number][]; + strikethrough?: boolean; + } export interface ITreeItem { @@ -627,13 +641,52 @@ export interface ITreeItem { resourceUri?: UriComponents; - tooltip?: string; + tooltip?: string | IMarkdownString; contextValue?: string; command?: Command; children?: ITreeItem[]; + + accessibilityInformation?: IAccessibilityInformation; +} + +export class ResolvableTreeItem implements ITreeItem { + handle!: string; + parentHandle?: string; + collapsibleState!: TreeItemCollapsibleState; + label?: ITreeItemLabel; + description?: string | boolean; + icon?: UriComponents; + iconDark?: UriComponents; + themeIcon?: ThemeIcon; + resourceUri?: UriComponents; + tooltip?: string | IMarkdownString; + contextValue?: string; + command?: Command; + children?: ITreeItem[]; + accessibilityInformation?: IAccessibilityInformation; + resolve: () => Promise; + private resolved: boolean = false; + private _hasResolve: boolean = false; + constructor(treeItem: ITreeItem, resolve?: (() => Promise)) { + mixin(this, treeItem); + this._hasResolve = !!resolve; + this.resolve = async () => { + if (resolve && !this.resolved) { + const resolvedItem = await resolve(); + if (resolvedItem) { + // Resolvable elements. Currently only tooltip. + this.tooltip = resolvedItem.tooltip; + } + } + this.resolved = true; + }; + } + get hasResolve(): boolean { + return this._hasResolve; + } } export interface ITreeViewDataProvider { diff --git a/src/vs/workbench/contrib/backup/common/backupRestorer.ts b/src/vs/workbench/contrib/backup/common/backupRestorer.ts index 483c29c97c0..c126a6b3174 100644 --- a/src/vs/workbench/contrib/backup/common/backupRestorer.ts +++ b/src/vs/workbench/contrib/backup/common/backupRestorer.ts @@ -71,7 +71,10 @@ export class BackupRestorer implements IWorkbenchContribution { private findEditorByResource(resource: URI): IEditorInput | undefined { for (const editor of this.editorService.editors) { - if (isEqual(editor.resource, resource)) { + const customFactory = Registry.as(EditorExtensions.EditorInputFactories).getCustomEditorInputFactory(resource.scheme); + if (customFactory && customFactory.canResolveBackup(editor, resource)) { + return editor; + } else if (isEqual(editor.resource, resource)) { return editor; } } @@ -97,9 +100,12 @@ export class BackupRestorer implements IWorkbenchContribution { return { resource: toLocalResource(resource, this.environmentService.configuration.remoteAuthority), options, forceUntitled: true }; } - if (resource.scheme === Schemas.vscodeCustomEditor) { - const editor = await Registry.as(EditorExtensions.EditorInputFactories).getCustomEditorInputFactory() - .createCustomEditorInput(resource, this.instantiationService); + // handle custom editors by asking the custom editor input factory + // to create the input. + const customFactory = Registry.as(EditorExtensions.EditorInputFactories).getCustomEditorInputFactory(resource.scheme); + + if (customFactory) { + const editor = await customFactory.createCustomEditorInput(resource, this.instantiationService); return { editor, options }; } diff --git a/src/vs/workbench/contrib/backup/electron-browser/backup.contribution.ts b/src/vs/workbench/contrib/backup/electron-sandbox/backup.contribution.ts similarity index 96% rename from src/vs/workbench/contrib/backup/electron-browser/backup.contribution.ts rename to src/vs/workbench/contrib/backup/electron-sandbox/backup.contribution.ts index 9868c6285fe..43a574d8bf4 100644 --- a/src/vs/workbench/contrib/backup/electron-browser/backup.contribution.ts +++ b/src/vs/workbench/contrib/backup/electron-sandbox/backup.contribution.ts @@ -6,7 +6,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { NativeBackupTracker } from 'vs/workbench/contrib/backup/electron-browser/backupTracker'; +import { NativeBackupTracker } from 'vs/workbench/contrib/backup/electron-sandbox/backupTracker'; // Register Backup Tracker Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NativeBackupTracker, LifecyclePhase.Starting); diff --git a/src/vs/workbench/contrib/backup/electron-browser/backupTracker.ts b/src/vs/workbench/contrib/backup/electron-sandbox/backupTracker.ts similarity index 98% rename from src/vs/workbench/contrib/backup/electron-browser/backupTracker.ts rename to src/vs/workbench/contrib/backup/electron-sandbox/backupTracker.ts index 3048c945dc6..e6321a3035c 100644 --- a/src/vs/workbench/contrib/backup/electron-browser/backupTracker.ts +++ b/src/vs/workbench/contrib/backup/electron-sandbox/backupTracker.ts @@ -14,7 +14,7 @@ import Severity from 'vs/base/common/severity'; import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { isMacintosh } from 'vs/base/common/platform'; import { HotExitConfiguration } from 'vs/platform/files/common/files'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { BackupTracker } from 'vs/workbench/contrib/backup/common/backupTracker'; import { ILogService } from 'vs/platform/log/common/log'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -99,7 +99,7 @@ export class NativeBackupTracker extends BackupTracker implements IWorkbenchCont // since a backup did not happen, we have to confirm for // the working copies that did not successfully backup try { - return await this.confirmBeforeShutdown(workingCopies.filter(workingCopy => backups.indexOf(workingCopy) === -1)); + return await this.confirmBeforeShutdown(workingCopies.filter(workingCopy => !backups.includes(workingCopy))); } catch (error) { this.showErrorDialog(localize('backupTrackerConfirmFailed', "One or more dirty editors could not be saved or reverted."), error); diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts index cab5dd56e4b..68c8751e028 100644 --- a/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts @@ -13,7 +13,7 @@ import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { DefaultEndOfLine } from 'vs/editor/common/model'; import { hashPath } from 'vs/workbench/services/backup/node/backupFileService'; -import { NativeBackupTracker } from 'vs/workbench/contrib/backup/electron-browser/backupTracker'; +import { NativeBackupTracker } from 'vs/workbench/contrib/backup/electron-sandbox/backupTracker'; import { workbenchInstantiationService } from 'vs/workbench/test/electron-browser/workbenchTestServices'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts index ea8c85ea656..485390ddab3 100644 --- a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts @@ -11,7 +11,7 @@ import * as pfs from 'vs/base/node/pfs'; import { URI } from 'vs/base/common/uri'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { hashPath } from 'vs/workbench/services/backup/node/backupFileService'; -import { NativeBackupTracker } from 'vs/workbench/contrib/backup/electron-browser/backupTracker'; +import { NativeBackupTracker } from 'vs/workbench/contrib/backup/electron-sandbox/backupTracker'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; @@ -34,7 +34,7 @@ import { HotExitConfiguration } from 'vs/platform/files/common/files'; import { ShutdownReason, ILifecycleService, BeforeShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { IFileDialogService, ConfirmResult, IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IWorkspaceContextService, Workspace } from 'vs/platform/workspace/common/workspace'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { BackupTracker } from 'vs/workbench/contrib/backup/common/backupTracker'; import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/electron-browser/workbenchTestServices'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts index 289ee656910..cfc08528e27 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.contribution.ts @@ -28,6 +28,7 @@ import type { ServicesAccessor } from 'vs/platform/instantiation/common/instanti import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; +import { Codicon } from 'vs/base/common/codicons'; async function getBulkEditPane(viewsService: IViewsService): Promise { const view = await viewsService.openView(BulkEditPane.ID, true); @@ -173,7 +174,7 @@ registerAction2(class ApplyAction extends Action2 { super({ id: 'refactorPreview.apply', title: { value: localize('apply', "Apply Refactoring"), original: 'Apply Refactoring' }, - category: localize('cat', "Refactor Preview"), + category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' }, icon: { id: 'codicon/check' }, precondition: ContextKeyExpr.and(BulkEditPreviewContribution.ctxEnabled, BulkEditPane.ctxHasCheckedChanges), menu: [{ @@ -207,7 +208,7 @@ registerAction2(class DiscardAction extends Action2 { super({ id: 'refactorPreview.discard', title: { value: localize('Discard', "Discard Refactoring"), original: 'Discard Refactoring' }, - category: localize('cat', "Refactor Preview"), + category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' }, icon: { id: 'codicon/clear-all' }, precondition: BulkEditPreviewContribution.ctxEnabled, menu: [{ @@ -237,7 +238,7 @@ registerAction2(class ToggleAction extends Action2 { super({ id: 'refactorPreview.toggleCheckedState', title: { value: localize('toogleSelection', "Toggle Change"), original: 'Toggle Change' }, - category: localize('cat', "Refactor Preview"), + category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' }, precondition: BulkEditPreviewContribution.ctxEnabled, keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -268,7 +269,7 @@ registerAction2(class GroupByFile extends Action2 { super({ id: 'refactorPreview.groupByFile', title: { value: localize('groupByFile', "Group Changes By File"), original: 'Group Changes By File' }, - category: localize('cat', "Refactor Preview"), + category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' }, icon: { id: 'codicon/ungroup-by-ref-type' }, precondition: ContextKeyExpr.and(BulkEditPane.ctxHasCategories, BulkEditPane.ctxGroupByFile.negate(), BulkEditPreviewContribution.ctxEnabled), menu: [{ @@ -295,7 +296,7 @@ registerAction2(class GroupByType extends Action2 { super({ id: 'refactorPreview.groupByType', title: { value: localize('groupByType', "Group Changes By Type"), original: 'Group Changes By Type' }, - category: localize('cat', "Refactor Preview"), + category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' }, icon: { id: 'codicon/group-by-ref-type' }, precondition: ContextKeyExpr.and(BulkEditPane.ctxHasCategories, BulkEditPane.ctxGroupByFile, BulkEditPreviewContribution.ctxEnabled), menu: [{ @@ -322,7 +323,7 @@ registerAction2(class ToggleGrouping extends Action2 { super({ id: 'refactorPreview.toggleGrouping', title: { value: localize('groupByType', "Group Changes By Type"), original: 'Group Changes By Type' }, - category: localize('cat', "Refactor Preview"), + category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' }, icon: { id: 'codicon/list-tree' }, toggled: BulkEditPane.ctxGroupByFile.negate(), precondition: ContextKeyExpr.and(BulkEditPane.ctxHasCategories, BulkEditPreviewContribution.ctxEnabled), @@ -354,6 +355,7 @@ const container = Registry.as(ViewContainerExtensions.V ViewPaneContainer, [BulkEditPane.ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }] ), + icon: Codicon.lightbulb.classNames, storageId: BulkEditPane.ID }, ViewContainerLocation.Panel); @@ -362,5 +364,6 @@ Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews name: localize('panel', "Refactor Preview"), when: BulkEditPreviewContribution.ctxEnabled, ctorDescriptor: new SyncDescriptor(BulkEditPane), + containerIcon: Codicon.lightbulb.classNames, }], container); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css index e1c14d360ec..aa88a067d33 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEdit.css @@ -70,10 +70,10 @@ min-width: 16px; } -.vs-dark .monaco-workbench .bulk-edit-panel .monaco-tl-contents.category .uri-icon, -.hc-black .monaco-workbench .bulk-edit-panel .monaco-tl-contents.category .uri-icon, -.vs-dark .monaco-workbench .bulk-edit-panel .monaco-tl-contents.textedit .uri-icon, -.hc-black .monaco-workbench .bulk-edit-panel .monaco-tl-contents.textedit .uri-icon +.monaco-workbench.vs-dark .bulk-edit-panel .monaco-tl-contents.category .uri-icon, +.monaco-workbench.hc-black .bulk-edit-panel .monaco-tl-contents.category .uri-icon, +.monaco-workbench.vs-dark .bulk-edit-panel .monaco-tl-contents.textedit .uri-icon, +.monaco-workbench.hc-black .bulk-edit-panel .monaco-tl-contents.textedit .uri-icon { background-image: var(--background-dark); } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index 8be28a631d1..b0c6afd8861 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./bulkEdit'; -import { WorkbenchAsyncDataTree, IOpenEvent, TreeResourceNavigator } from 'vs/platform/list/browser/listService'; +import { WorkbenchAsyncDataTree, IOpenEvent } from 'vs/platform/list/browser/listService'; import { WorkspaceEdit } from 'vs/editor/common/modes'; import { BulkEditElement, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement, TextEditElement, BulkEditAccessibilityProvider, CategoryElementRenderer, BulkEditNaviLabelProvider, CategoryElement, BulkEditSorter } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree'; import { FuzzyScore } from 'vs/base/common/filters'; @@ -136,15 +136,13 @@ export class BulkEditPane extends ViewPane { expandOnlyOnTwistieClick: true, multipleSelectionSupport: false, keyboardNavigationLabelProvider: new BulkEditNaviLabelProvider(), - sorter: new BulkEditSorter() + sorter: new BulkEditSorter(), + openOnFocus: true } ); this._disposables.add(this._tree.onContextMenu(this._onContextMenu, this)); - - const navigator = new TreeResourceNavigator(this._tree, { openOnFocus: true }); - this._disposables.add(navigator); - this._disposables.add(navigator.onDidOpenResource(e => this._openElementAsEditor(e))); + this._disposables.add(this._tree.onDidOpen(e => this._openElementAsEditor(e))); // message this._message = document.createElement('span'); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts index 4a9b99b6971..d6d0d1510d3 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts @@ -20,6 +20,7 @@ import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { ConflictDetector } from 'vs/workbench/services/bulkEdit/browser/conflicts'; import { ResourceMap } from 'vs/base/common/map'; import { localize } from 'vs/nls'; +import { extUri } from 'vs/base/common/resources'; export class CheckedStates { @@ -209,13 +210,13 @@ export class BulkFileOperations { } const insert = (uri: URI, map: Map) => { - let key = uri.toString(); + let key = extUri.getComparisonKey(uri, true); let operation = map.get(key); // rename if (!operation && newToOldUri.has(uri)) { uri = newToOldUri.get(uri)!; - key = uri.toString(); + key = extUri.getComparisonKey(uri, true); operation = map.get(key); } @@ -247,15 +248,15 @@ export class BulkFileOperations { for (let file of this.fileOperations) { if (file.type !== BulkFileOperationType.TextEdit) { let checked = true; - file.originalEdits.forEach(edit => { + for (const edit of file.originalEdits.values()) { if (WorkspaceFileEdit.is(edit)) { checked = checked && this.checked.isChecked(edit); } - }); + } if (!checked) { - file.originalEdits.forEach(edit => { + for (const edit of file.originalEdits.values()) { this.checked.updateChecked(edit, checked); - }); + } } } } @@ -304,8 +305,7 @@ export class BulkFileOperations { const result: IIdentifiedSingleEditOperation[] = []; let ignoreAll = false; - file.originalEdits.forEach(edit => { - + for (const edit of file.originalEdits.values()) { if (WorkspaceTextEdit.is(edit)) { if (this.checked.isChecked(edit)) { result.push(EditOperation.replaceMove(Range.lift(edit.edit.range), edit.edit.text)); @@ -315,7 +315,7 @@ export class BulkFileOperations { // UNCHECKED WorkspaceFileEdit disables all text edits ignoreAll = true; } - }); + } if (ignoreAll) { return []; @@ -331,16 +331,11 @@ export class BulkFileOperations { } getUriOfEdit(edit: WorkspaceFileEdit | WorkspaceTextEdit): URI { - for (let file of this.fileOperations) { - let found = false; - file.originalEdits.forEach(value => { - if (!found && value === edit) { - found = true; + for (const value of file.originalEdits.values()) { + if (value === edit) { + return file.uri; } - }); - if (found) { - return file.uri; } } throw new Error('invalid edit'); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index a899a846a6a..52a59197356 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -61,22 +61,22 @@ export class FileElement implements ICheckable { } // multiple file edits -> reflect single state - this.edit.originalEdits.forEach(edit => { + for (let edit of this.edit.originalEdits.values()) { if (WorkspaceFileEdit.is(edit)) { checked = checked && model.checked.isChecked(edit); } - }); + } // multiple categories and text change -> read all elements if (this.parent instanceof CategoryElement && this.edit.type === BulkFileOperationType.TextEdit) { for (let category of model.categories) { for (let file of category.fileOperations) { if (file.uri.toString() === this.edit.uri.toString()) { - file.originalEdits.forEach(edit => { + for (const edit of file.originalEdits.values()) { if (WorkspaceFileEdit.is(edit)) { checked = checked && model.checked.isChecked(edit); } - }); + } } } } @@ -87,18 +87,18 @@ export class FileElement implements ICheckable { setChecked(value: boolean): void { let model = this.parent instanceof CategoryElement ? this.parent.parent : this.parent; - this.edit.originalEdits.forEach(edit => { + for (const edit of this.edit.originalEdits.values()) { model.checked.updateChecked(edit, value); - }); + } // multiple categories and file change -> update all elements if (this.parent instanceof CategoryElement && this.edit.type !== BulkFileOperationType.TextEdit) { for (let category of model.categories) { for (let file of category.fileOperations) { if (file.uri.toString() === this.edit.uri.toString()) { - file.originalEdits.forEach(edit => { + for (const edit of file.originalEdits.values()) { model.checked.updateChecked(edit, value); - }); + } } } } @@ -112,11 +112,11 @@ export class FileElement implements ICheckable { for (let category of model.categories) { for (let file of category.fileOperations) { if (file.uri.toString() === this.edit.uri.toString()) { - file.originalEdits.forEach(edit => { + for (const edit of file.originalEdits.values()) { if (WorkspaceFileEdit.is(edit)) { checked = checked && model.checked.isChecked(edit); } - }); + } } } } @@ -154,11 +154,11 @@ export class TextEditElement implements ICheckable { // make sure parent is checked when this element is checked... if (value) { - this.parent.edit.originalEdits.forEach(edit => { + for (const edit of this.parent.edit.originalEdits.values()) { if (WorkspaceFileEdit.is(edit)) { (model).checked.updateChecked(edit, value); } - }); + } } } diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts index 6ffd11e782c..c32da9ad715 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts @@ -9,7 +9,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { CallHierarchyTreePeekWidget } from 'vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek'; import { Event } from 'vs/base/common/event'; -import { registerEditorContribution, registerEditorAction, EditorAction, registerEditorCommand, EditorCommand } from 'vs/editor/browser/editorExtensions'; +import { registerEditorContribution, EditorAction2 } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IContextKeyService, RawContextKey, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -22,10 +22,18 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { Range } from 'vs/editor/common/core/range'; import { IPosition } from 'vs/editor/common/core/position'; -import { MenuId } from 'vs/platform/actions/common/actions'; +import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { registerIcon, Codicon } from 'vs/base/common/codicons'; const _ctxHasCallHierarchyProvider = new RawContextKey('editorHasCallHierarchyProvider', false); const _ctxCallHierarchyVisible = new RawContextKey('callHierarchyVisible', false); +const _ctxCallHierarchyDirection = new RawContextKey('callHierarchyDirection', undefined); + +function sanitizedDirection(candidate: string): CallHierarchyDirection { + return candidate === CallHierarchyDirection.CallsFrom || candidate === CallHierarchyDirection.CallsTo + ? candidate + : CallHierarchyDirection.CallsTo; +} class CallHierarchyController implements IEditorContribution { @@ -39,6 +47,7 @@ class CallHierarchyController implements IEditorContribution { private readonly _ctxHasProvider: IContextKey; private readonly _ctxIsVisible: IContextKey; + private readonly _ctxDirection: IContextKey; private readonly _dispoables = new DisposableStore(); private readonly _sessionDisposables = new DisposableStore(); @@ -53,6 +62,7 @@ class CallHierarchyController implements IEditorContribution { ) { this._ctxIsVisible = _ctxCallHierarchyVisible.bindTo(this._contextKeyService); this._ctxHasProvider = _ctxHasCallHierarchyProvider.bindTo(this._contextKeyService); + this._ctxDirection = _ctxCallHierarchyDirection.bindTo(this._contextKeyService); this._dispoables.add(Event.any(_editor.onDidChangeModel, _editor.onDidChangeModelLanguage, CallHierarchyProviderRegistry.onDidChange)(() => { this._ctxHasProvider.set(_editor.hasModel() && CallHierarchyProviderRegistry.has(_editor.getModel())); })); @@ -80,7 +90,7 @@ class CallHierarchyController implements IEditorContribution { const cts = new CancellationTokenSource(); const model = CallHierarchyModel.create(document, position, cts.token); - const direction = this._storageService.getNumber(CallHierarchyController._StorageDirection, StorageScope.GLOBAL, CallHierarchyDirection.CallsFrom); + const direction = sanitizedDirection(this._storageService.get(CallHierarchyController._StorageDirection, StorageScope.GLOBAL, CallHierarchyDirection.CallsTo)); this._showCallHierarchyWidget(position, direction, model, cts); } @@ -109,12 +119,13 @@ class CallHierarchyController implements IEditorContribution { ); } - private _showCallHierarchyWidget(position: IPosition, direction: number, model: Promise, cts: CancellationTokenSource) { + private _showCallHierarchyWidget(position: IPosition, direction: CallHierarchyDirection, model: Promise, cts: CancellationTokenSource) { + this._ctxIsVisible.set(true); + this._ctxDirection.set(direction); Event.any(this._editor.onDidChangeModel, this._editor.onDidChangeModelLanguage)(this.endCallHierarchy, this, this._sessionDisposables); this._widget = this._instantiationService.createInstance(CallHierarchyTreePeekWidget, this._editor, position, direction); this._widget.showLoading(); - this._ctxIsVisible.set(true); this._sessionDisposables.add(this._widget.onDidClose(() => { this.endCallHierarchy(); this._storageService.store(CallHierarchyController._StorageDirection, this._widget!.direction, StorageScope.GLOBAL); @@ -139,10 +150,14 @@ class CallHierarchyController implements IEditorContribution { }); } - toggleCallHierarchyDirection(): void { - if (this._widget) { - this._widget.toggleDirection(); - } + showOutgoingCalls(): void { + this._widget?.updateDirection(CallHierarchyDirection.CallsFrom); + this._ctxDirection.set(CallHierarchyDirection.CallsFrom); + } + + showIncomingCalls(): void { + this._widget?.updateDirection(CallHierarchyDirection.CallsTo); + this._ctxDirection.set(CallHierarchyDirection.CallsTo); } endCallHierarchy(): void { @@ -154,20 +169,19 @@ class CallHierarchyController implements IEditorContribution { registerEditorContribution(CallHierarchyController.Id, CallHierarchyController); -registerEditorAction(class extends EditorAction { +registerAction2(class extends EditorAction2 { constructor() { super({ id: 'editor.showCallHierarchy', - label: localize('title', "Peek Call Hierarchy"), - alias: 'Peek Call Hierarchy', - contextMenuOpts: { - menuId: MenuId.EditorContextPeek, + title: { value: localize('title', "Peek Call Hierarchy"), original: 'Peek Call Hierarchy' }, + menu: { + id: MenuId.EditorContextPeek, group: 'navigation', order: 1000 }, - kbOpts: { - kbExpr: EditorContextKeys.editorTextFocus, + keybinding: { + when: EditorContextKeys.editorTextFocus, weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.Shift + KeyMod.Alt + KeyCode.KEY_H }, @@ -178,65 +192,101 @@ registerEditorAction(class extends EditorAction { }); } - async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { + async runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { return CallHierarchyController.get(editor).startCallHierarchyFromEditor(); } }); -registerEditorAction(class extends EditorAction { +registerAction2(class extends EditorAction2 { constructor() { super({ - id: 'editor.toggleCallHierarchy', - label: localize('title.toggle', "Toggle Call Hierarchy"), - alias: 'Toggle Call Hierarchy', - kbOpts: { + id: 'editor.showIncomingCalls', + title: { value: localize('title.incoming', "Show Incoming Calls"), original: 'Show Incoming Calls' }, + icon: registerIcon('callhierarchy-incoming', Codicon.callIncoming), + precondition: ContextKeyExpr.and(_ctxCallHierarchyVisible, _ctxCallHierarchyDirection.isEqualTo(CallHierarchyDirection.CallsFrom)), + keybinding: { weight: KeybindingWeight.WorkbenchContrib, - primary: KeyMod.Shift + KeyMod.Alt + KeyCode.KEY_H + primary: KeyMod.Shift + KeyMod.Alt + KeyCode.KEY_H, }, - precondition: _ctxCallHierarchyVisible + menu: { + id: CallHierarchyTreePeekWidget.TitleMenu, + when: _ctxCallHierarchyDirection.isEqualTo(CallHierarchyDirection.CallsFrom), + order: 1, + } }); } - async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { - return CallHierarchyController.get(editor).toggleCallHierarchyDirection(); + runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor) { + return CallHierarchyController.get(editor).showIncomingCalls(); } }); -registerEditorAction(class extends EditorAction { +registerAction2(class extends EditorAction2 { + + constructor() { + super({ + id: 'editor.showOutgoingCalls', + title: { value: localize('title.outgoing', "Show Outgoing Calls"), original: 'Show Outgoing Calls' }, + icon: registerIcon('callhierarchy-outgoing', Codicon.callOutgoing), + precondition: ContextKeyExpr.and(_ctxCallHierarchyVisible, _ctxCallHierarchyDirection.isEqualTo(CallHierarchyDirection.CallsTo)), + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.Shift + KeyMod.Alt + KeyCode.KEY_H, + }, + menu: { + id: CallHierarchyTreePeekWidget.TitleMenu, + when: _ctxCallHierarchyDirection.isEqualTo(CallHierarchyDirection.CallsTo), + order: 1 + } + }); + } + + runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor) { + return CallHierarchyController.get(editor).showOutgoingCalls(); + } +}); + + +registerAction2(class extends EditorAction2 { constructor() { super({ id: 'editor.refocusCallHierarchy', - label: localize('title.refocus', "Refocus Call Hierarchy"), - alias: 'Refocus Call Hierarchy', - kbOpts: { + title: { value: localize('title.refocus', "Refocus Call Hierarchy"), original: 'Refocus Call Hierarchy' }, + precondition: _ctxCallHierarchyVisible, + keybinding: { weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.Shift + KeyCode.Enter - }, - precondition: _ctxCallHierarchyVisible + } }); } - async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { + async runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { return CallHierarchyController.get(editor).startCallHierarchyFromCallHierarchy(); } }); -registerEditorCommand(new class extends EditorCommand { +registerAction2(class extends EditorAction2 { constructor() { super({ id: 'editor.closeCallHierarchy', - kbOpts: { - weight: KeybindingWeight.WorkbenchContrib + 10, - primary: KeyCode.Escape - }, + title: localize('close', 'Close'), + icon: Codicon.close, precondition: ContextKeyExpr.and( _ctxCallHierarchyVisible, ContextKeyExpr.not('config.editor.stablePeek') - ) + ), + keybinding: { + weight: KeybindingWeight.WorkbenchContrib + 10, + primary: KeyCode.Escape + }, + menu: { + id: CallHierarchyTreePeekWidget.TitleMenu, + order: 1000 + } }); } diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts index e722340f27a..e51a91dc96b 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts @@ -26,12 +26,15 @@ import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions, OverviewRulerLane } from 'vs/editor/common/model'; import { registerThemingParticipant, themeColorFromId, IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService'; import { IPosition } from 'vs/editor/common/core/position'; -import { Action } from 'vs/base/common/actions'; +import { IAction } from 'vs/base/common/actions'; import { IActionBarOptions, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { Color } from 'vs/base/common/color'; import { TreeMouseEventTarget, ITreeNode } from 'vs/base/browser/ui/tree/tree'; import { URI } from 'vs/base/common/uri'; +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'; const enum State { Loading = 'loading', @@ -39,27 +42,6 @@ const enum State { Data = 'data' } -class ChangeHierarchyDirectionAction extends Action { - - constructor(getDirection: () => CallHierarchyDirection, toggleDirection: () => void) { - super('', undefined, '', true, () => { - toggleDirection(); - update(); - return Promise.resolve(); - }); - const update = () => { - if (getDirection() === CallHierarchyDirection.CallsFrom) { - this.label = localize('toggle.from', "Show Incoming Calls"); - this.class = 'codicon codicon-call-incoming'; - } else { - this.label = localize('toggle.to', "Showing Outgoing Calls"); - this.class = 'codicon codicon-call-outgoing'; - } - }; - update(); - } -} - class LayoutInfo { static store(info: LayoutInfo, storageService: IStorageService): void { @@ -86,7 +68,8 @@ class CallHierarchyTree extends WorkbenchAsyncDataTree { + const actions: IAction[] = []; + createAndFillInActionBarActions(menu, undefined, actions); + this._actionbarWidget!.clear(); + this._actionbarWidget!.push(actions, { label: false, icon: true }); + }; + this._disposables.add(menu); + this._disposables.add(menu.onDidChange(updateToolbar)); + updateToolbar(); + } + protected _getActionBarOptions(): IActionBarOptions { return { - orientation: ActionsOrientation.HORIZONTAL_REVERSE + ...super._getActionBarOptions(), + orientation: ActionsOrientation.HORIZONTAL }; } @@ -391,12 +392,6 @@ export class CallHierarchyTreePeekWidget extends peekView.PeekViewWidget { this._tree.domFocus(); this._updatePreview(); } - - if (!this._changeDirectionAction) { - this._changeDirectionAction = new ChangeHierarchyDirectionAction(() => this._direction, () => this.toggleDirection()); - this._disposables.add(this._changeDirectionAction); - this._actionbarWidget!.push(this._changeDirectionAction, { icon: true, label: false }); - } } getModel(): CallHierarchyModel | undefined { @@ -407,10 +402,9 @@ export class CallHierarchyTreePeekWidget extends peekView.PeekViewWidget { return this._tree.getFocus()[0]; } - async toggleDirection(): Promise { + async updateDirection(newDirection: CallHierarchyDirection): Promise { const model = this._tree.getInput(); - if (model) { - const newDirection = this._direction === CallHierarchyDirection.CallsTo ? CallHierarchyDirection.CallsFrom : CallHierarchyDirection.CallsTo; + if (model && newDirection !== this._direction) { this._treeViewStates.set(this._direction, this._tree.getViewState()); this._direction = newDirection; await this.showModel(model); diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts index 2387788c3bf..c1bc2e5377d 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts @@ -9,7 +9,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { FuzzyScore, createMatches } from 'vs/base/common/filters'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; -import { SymbolKinds, Location } from 'vs/editor/common/modes'; +import { SymbolKinds, Location, SymbolTag } from 'vs/editor/common/modes'; import { compare } from 'vs/base/common/strings'; import { Range } from 'vs/editor/common/core/range'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; @@ -117,11 +117,12 @@ export class CallRenderer implements ITreeRenderer, _index: number, template: CallRenderingTemplate): void { const { element, filterData } = node; + const deprecated = element.item.tags?.includes(SymbolTag.Deprecated); template.icon.className = SymbolKinds.toCssClassName(element.item.kind, true); template.label.setLabel( element.item.name, element.item.detail, - { labelEscapeNewLines: true, matches: createMatches(filterData) } + { labelEscapeNewLines: true, matches: createMatches(filterData), strikethrough: deprecated } ); } disposeTemplate(template: CallRenderingTemplate): void { @@ -154,7 +155,7 @@ export class AccessibilityProvider implements IListAccessibilityProvider { if (this.getDirection() === CallHierarchyDirection.CallsFrom) { return localize('from', "calls from {0}", element.item.name); } else { - return localize('to', "callers fo {0}", element.item.name); + return localize('to', "callers of {0}", element.item.name); } } } diff --git a/src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts b/src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts index c82d0a7739d..e895bd00e60 100644 --- a/src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts +++ b/src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IRange } from 'vs/editor/common/core/range'; -import { SymbolKind, ProviderResult } from 'vs/editor/common/modes'; +import { SymbolKind, ProviderResult, SymbolTag } from 'vs/editor/common/modes'; import { ITextModel } from 'vs/editor/common/model'; import { CancellationToken } from 'vs/base/common/cancellation'; import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureRegistry'; @@ -19,8 +19,8 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; export const enum CallHierarchyDirection { - CallsTo = 1, - CallsFrom = 2 + CallsTo = 'incomingCalls', + CallsFrom = 'outgoingCalls' } export interface CallHierarchyItem { @@ -32,6 +32,7 @@ export interface CallHierarchyItem { uri: URI; range: IRange; selectionRange: IRange; + tags?: SymbolTag[] } export interface IncomingCall { diff --git a/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts b/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts index b9992fde979..30a3c94fdf1 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts @@ -12,7 +12,6 @@ import './inspectEditorTokens/inspectEditorTokens'; import './quickaccess/gotoLineQuickAccess'; import './quickaccess/gotoSymbolQuickAccess'; import './saveParticipants'; -import './semanticTokensHelp'; import './toggleColumnSelection'; import './toggleMinimap'; import './toggleMultiCursorModifier'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts index a1ef216b498..088c76bdb4b 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts @@ -15,7 +15,7 @@ import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBo import { SimpleButton, findCloseIcon, findNextMatchIcon, findPreviousMatchIcon, findReplaceIcon, findReplaceAllIcon } from 'vs/editor/contrib/find/findWidget'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { editorWidgetBackground, inputActiveOptionBorder, inputActiveOptionBackground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry'; +import { editorWidgetBackground, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry'; import { IColorTheme, registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService'; import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/browser/contextScopedHistoryWidget'; import { ReplaceInput, IReplaceInputStyles } from 'vs/base/browser/ui/findinput/replaceInput'; @@ -43,7 +43,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { private readonly prevBtn: SimpleButton; private readonly nextBtn: SimpleButton; - private readonly _replaceInput!: ReplaceInput; + protected readonly _replaceInput!: ReplaceInput; private readonly _innerReplaceDomNode!: HTMLElement; private _toggleReplaceBtn!: SimpleButton; private readonly _replaceInputFocusTracker!: dom.IFocusTracker; @@ -62,7 +62,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { @IContextViewService private readonly _contextViewService: IContextViewService, @IContextKeyService contextKeyService: IContextKeyService, @IThemeService private readonly _themeService: IThemeService, - private readonly _state: FindReplaceState = new FindReplaceState(), + protected readonly _state: FindReplaceState = new FindReplaceState(), showOptionButtons?: boolean ) { super(); @@ -267,6 +267,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { public updateTheme(theme: IColorTheme): void { const inputStyles: IFindInputStyles = { inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder), + inputActiveOptionForeground: theme.getColor(inputActiveOptionForeground), inputActiveOptionBackground: theme.getColor(inputActiveOptionBackground), inputBackground: theme.getColor(inputBackground), inputForeground: theme.getColor(inputForeground), @@ -279,11 +280,12 @@ export abstract class SimpleFindReplaceWidget extends Widget { inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder), inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground), inputValidationErrorForeground: theme.getColor(inputValidationErrorForeground), - inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder) + inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder), }; this._findInput.style(inputStyles); const replaceStyles: IReplaceInputStyles = { inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder), + inputActiveOptionForeground: theme.getColor(inputActiveOptionForeground), inputActiveOptionBackground: theme.getColor(inputActiveOptionBackground), inputBackground: theme.getColor(inputBackground), inputForeground: theme.getColor(inputForeground), @@ -296,7 +298,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder), inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground), inputValidationErrorForeground: theme.getColor(inputValidationErrorForeground), - inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder) + inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder), }; this._replaceInput.style(replaceStyles); } @@ -350,6 +352,10 @@ export abstract class SimpleFindReplaceWidget extends Widget { }, 0); } + public focus(): void { + this._findInput.focus(); + } + public show(initialInput?: string): void { if (initialInput && !this._isVisible) { this._findInput.setValue(initialInput); @@ -361,6 +367,36 @@ export abstract class SimpleFindReplaceWidget extends Widget { dom.addClass(this._domNode, 'visible'); dom.addClass(this._domNode, 'visible-transition'); this._domNode.setAttribute('aria-hidden', 'false'); + + this.focus(); + }, 0); + } + + public showWithReplace(initialInput?: string, replaceInput?: string): void { + if (initialInput && !this._isVisible) { + this._findInput.setValue(initialInput); + } + + if (replaceInput && !this._isVisible) { + this._replaceInput.setValue(replaceInput); + } + + this._isVisible = true; + this._isReplaceVisible = true; + this._state.change({ isReplaceRevealed: this._isReplaceVisible }, false); + if (this._isReplaceVisible) { + this._innerReplaceDomNode.style.display = 'flex'; + } else { + this._innerReplaceDomNode.style.display = 'none'; + } + + setTimeout(() => { + dom.addClass(this._domNode, 'visible'); + dom.addClass(this._domNode, 'visible-transition'); + this._domNode.setAttribute('aria-hidden', 'false'); + this._updateButtons(); + + this._replaceInput.focus(); }, 0); } diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts index 4c5dec43974..cb0d5ab2d5f 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts @@ -15,7 +15,7 @@ import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBo import { SimpleButton, findPreviousMatchIcon, findNextMatchIcon, findCloseIcon } from 'vs/editor/contrib/find/findWidget'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { editorWidgetBackground, inputActiveOptionBorder, inputActiveOptionBackground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry'; +import { editorWidgetBackground, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry'; import { IColorTheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ContextScopedFindInput } from 'vs/platform/browser/contextScopedHistoryWidget'; @@ -168,6 +168,7 @@ export abstract class SimpleFindWidget extends Widget { public updateTheme(theme: IColorTheme): void { const inputStyles: IFindInputStyles = { inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder), + inputActiveOptionForeground: theme.getColor(inputActiveOptionForeground), inputActiveOptionBackground: theme.getColor(inputActiveOptionBackground), inputBackground: theme.getColor(inputBackground), inputForeground: theme.getColor(inputForeground), diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts index 4eed83f9e01..069b114a77a 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts @@ -29,10 +29,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { ColorThemeData, TokenStyleDefinitions, TokenStyleDefinition, TextMateThemingRuleDefinitions } from 'vs/workbench/services/themes/common/colorThemeData'; import { SemanticTokenRule, TokenStyleData, TokenStyle } from 'vs/platform/theme/common/tokenClassificationRegistry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; - -export interface IEditorSemanticHighlightingOptions { - enabled?: boolean; -} +import { SEMANTIC_HIGHLIGHTING_SETTING_ID, IEditorSemanticHighlightingOptions } from 'vs/editor/common/services/modelServiceImpl'; class InspectEditorTokensController extends Disposable implements IEditorContribution { @@ -264,11 +261,11 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { } private _isSemanticColoringEnabled() { - if (!this._themeService.getColorTheme().semanticHighlighting) { - return false; + const setting = this._configurationService.getValue(SEMANTIC_HIGHLIGHTING_SETTING_ID, { overrideIdentifier: this._model.getLanguageIdentifier().language, resource: this._model.uri })?.enabled; + if (typeof setting === 'boolean') { + return setting; } - const options = this._configurationService.getValue('editor.semanticHighlighting', { overrideIdentifier: this._model.getLanguageIdentifier().language, resource: this._model.uri }); - return options && options.enabled; + return this._themeService.getColorTheme().semanticHighlighting; } private _compute(grammar: IGrammar | null, semanticTokens: SemanticTokensResult | null, position: Position): string { diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectKeybindings.ts b/src/vs/workbench/contrib/codeEditor/browser/inspectKeybindings.ts index 319869b0a66..e41b57e5af1 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectKeybindings.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectKeybindings.ts @@ -53,4 +53,4 @@ class InspectKeyMapJSON extends Action { } const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(SyncActionDescriptor.from(InspectKeyMapJSON), 'Developer: Inspect Key Mappings (JSON)', nls.localize('developer', "Developer")); +registry.registerWorkbenchAction(SyncActionDescriptor.from(InspectKeyMapJSON), 'Developer: Inspect Key Mappings (JSON)', nls.localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer")); diff --git a/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess.ts b/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess.ts index ae93fa290bc..c05ec1cfa36 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess.ts @@ -13,10 +13,10 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IQuickAccessRegistry, Extensions as QuickaccesExtensions } from 'vs/platform/quickinput/common/quickAccess'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor'; -import { Action } from 'vs/base/common/actions'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; export class GotoLineQuickAccessProvider extends AbstractGotoLineQuickAccessProvider { @@ -66,25 +66,25 @@ Registry.as(QuickaccesExtensions.Quickaccess).registerQuic helpEntries: [{ description: localize('gotoLineQuickAccess', "Go to Line/Column"), needsEditor: true }] }); -export class GotoLineAction extends Action { +class GotoLineAction extends Action2 { - static readonly ID = 'workbench.action.gotoLine'; - static readonly LABEL = localize('gotoLine', "Go to Line/Column..."); - - constructor( - id: string, - label: string, - @IQuickInputService private readonly quickInputService: IQuickInputService - ) { - super(id, label); + constructor() { + super({ + id: 'workbench.action.gotoLine', + title: { value: localize('gotoLine', "Go to Line/Column..."), original: 'Go to Line/Column...' }, + f1: true, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + when: null, + primary: KeyMod.CtrlCmd | KeyCode.KEY_G, + mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_G } + } + }); } - async run(): Promise { - this.quickInputService.quickAccess.show(GotoLineQuickAccessProvider.PREFIX); + async run(accessor: ServicesAccessor): Promise { + accessor.get(IQuickInputService).quickAccess.show(GotoLineQuickAccessProvider.PREFIX); } } -Registry.as(ActionExtensions.WorkbenchActions).registerWorkbenchAction(SyncActionDescriptor.from(GotoLineAction, { - primary: KeyMod.CtrlCmd | KeyCode.KEY_G, - mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_G } -}), 'Go to Line/Column...'); +registerAction2(GotoLineAction); diff --git a/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts b/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts index 43bfc92aa31..5f73a423de1 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts @@ -14,17 +14,18 @@ import { AbstractGotoSymbolQuickAccessProvider, IGotoSymbolQuickPickItem } from import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchEditorConfiguration, IEditorPane } from 'vs/workbench/common/editor'; import { ITextModel } from 'vs/editor/common/model'; -import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable, toDisposable, Disposable } from 'vs/base/common/lifecycle'; import { timeout } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; -import { Action } from 'vs/base/common/actions'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { prepareQuery } from 'vs/base/common/fuzzyScorer'; import { SymbolKind } from 'vs/editor/common/modes'; import { fuzzyScore, createMatches } from 'vs/base/common/filters'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccessProvider { @@ -41,6 +42,13 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess //#region DocumentSymbols (text editor required) + protected provideWithTextEditor(editor: IEditor, picker: IQuickPick, token: CancellationToken): IDisposable { + if (this.canPickFromTableOfContents()) { + return this.doGetTableOfContentsPicks(picker); + } + return super.provideWithTextEditor(editor, picker, token); + } + private get configuration() { const editorConfig = this.configurationService.getValue().workbench.editor; @@ -106,12 +114,21 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess //#endregion protected provideWithoutTextEditor(picker: IQuickPick): IDisposable { - const pane = this.editorService.activeEditorPane; - if (!pane || !TableOfContentsProviderRegistry.has(pane.getId())) { - // - return super.provideWithoutTextEditor(picker); + if (this.canPickFromTableOfContents()) { + return this.doGetTableOfContentsPicks(picker); } + return super.provideWithoutTextEditor(picker); + } + private canPickFromTableOfContents(): boolean { + return this.editorService.activeEditorPane ? TableOfContentsProviderRegistry.has(this.editorService.activeEditorPane.getId()) : false; + } + + private doGetTableOfContentsPicks(picker: IQuickPick): IDisposable { + const pane = this.editorService.activeEditorPane; + if (!pane) { + return Disposable.None; + } const provider = TableOfContentsProviderRegistry.get(pane.getId())!; const cts = new CancellationTokenSource(); @@ -120,7 +137,7 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess picker.busy = true; - provider.provideTableOfContents(pane, cts.token).then(entries => { + provider.provideTableOfContents(pane, { disposables }, cts.token).then(entries => { picker.busy = false; @@ -133,7 +150,8 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess kind: SymbolKind.File, index: idx, score: 0, - label: entry.label, + label: entry.icon ? `$(${entry.icon.id}) ${entry.label}` : entry.label, + ariaLabel: entry.detail ? `${entry.label}, ${entry.detail}` : entry.label, detail: entry.detail, description: entry.description, }; @@ -142,7 +160,7 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess disposables.add(picker.onDidAccept(() => { picker.hide(); const [entry] = picker.selectedItems; - entries[entry.index]?.reveal(); + entries[entry.index]?.pick(); })); const updatePickerItems = () => { @@ -172,17 +190,10 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess updatePickerItems(); disposables.add(picker.onDidChangeValue(updatePickerItems)); - let ignoreFirstActiveEvent = true; disposables.add(picker.onDidChangeActive(() => { const [entry] = picker.activeItems; - - if (entry && entries[entry.index]) { - if (ignoreFirstActiveEvent) { - ignoreFirstActiveEvent = false; - return; - } - - entries[entry.index]?.reveal(); + if (entry) { + entries[entry.index]?.preview(); } })); @@ -206,40 +217,43 @@ Registry.as(QuickaccessExtensions.Quickaccess).registerQui ] }); -export class GotoSymbolAction extends Action { +registerAction2(class GotoSymbolAction extends Action2 { - static readonly ID = 'workbench.action.gotoSymbol'; - static readonly LABEL = localize('gotoSymbol', "Go to Symbol in Editor..."); - - constructor( - id: string, - label: string, - @IQuickInputService private readonly quickInputService: IQuickInputService - ) { - super(id, label); + constructor() { + super({ + id: 'workbench.action.gotoSymbol', + title: { + value: localize('gotoSymbol', "Go to Symbol in Editor..."), + original: 'Go to Symbol in Editor...' + }, + f1: true, + keybinding: { + when: undefined, + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_O + } + }); } - async run(): Promise { - this.quickInputService.quickAccess.show(GotoSymbolQuickAccessProvider.PREFIX); + run(accessor: ServicesAccessor) { + accessor.get(IQuickInputService).quickAccess.show(GotoSymbolQuickAccessProvider.PREFIX); } -} - -Registry.as(ActionExtensions.WorkbenchActions).registerWorkbenchAction(SyncActionDescriptor.from(GotoSymbolAction, { - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_O -}), 'Go to Symbol in Editor...'); - +}); //#region toc definition and logic export interface ITableOfContentsEntry { + icon?: ThemeIcon; label: string; detail?: string; description?: string; - reveal(): any; + pick(): any; + preview(): any; } export interface ITableOfContentsProvider { - provideTableOfContents(editor: T, token: CancellationToken): Promise; + + provideTableOfContents(editor: T, context: { disposables: DisposableStore }, token: CancellationToken): Promise; } class ProviderRegistry { diff --git a/src/vs/workbench/contrib/codeEditor/browser/semanticTokensHelp.ts b/src/vs/workbench/contrib/codeEditor/browser/semanticTokensHelp.ts deleted file mode 100644 index e694687c95b..00000000000 --- a/src/vs/workbench/contrib/codeEditor/browser/semanticTokensHelp.ts +++ /dev/null @@ -1,109 +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 path from 'vs/base/common/path'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; -import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { URI } from 'vs/base/common/uri'; -import { ITextModel } from 'vs/editor/common/model'; -import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; - -/** - * Shows a message when semantic tokens are shown the first time. - */ -export class SemanticTokensHelp extends Disposable implements IEditorContribution { - - public static readonly ID = 'editor.contrib.semanticHighlightHelp'; - - constructor( - _editor: ICodeEditor, - @INotificationService _notificationService: INotificationService, - @IOpenerService _openerService: IOpenerService, - @IWorkbenchThemeService _themeService: IWorkbenchThemeService, - @IEditorService _editorService: IEditorService, - @IStorageService _storageService: IStorageService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService - ) { - super(); - // opt-in to syncing - const neverShowAgainId = 'editor.contrib.semanticTokensHelp'; - - if (_storageService.getBoolean(neverShowAgainId, StorageScope.GLOBAL)) { - return; - } - - storageKeysSyncRegistryService.registerStorageKey({ key: neverShowAgainId, version: 1 }); - - const toDispose = this._register(new DisposableStore()); - const localToDispose = toDispose.add(new DisposableStore()); - const installChangeTokenListener = (model: ITextModel) => { - localToDispose.add(model.onDidChangeTokens((e) => { - if (!e.semanticTokensApplied) { - return; - } - if (_storageService.getBoolean(neverShowAgainId, StorageScope.GLOBAL)) { - toDispose.dispose(); - return; - } - const activeEditorControl = _editorService.activeTextEditorControl; - if (!isCodeEditor(activeEditorControl) || activeEditorControl.getModel() !== model) { - return; // only show if model is in the active code editor - } - - toDispose.dispose(); // uninstall all listeners, make sure the notification is only shown once per window - _storageService.store(neverShowAgainId, true, StorageScope.GLOBAL); // never show again - - const message = nls.localize( - { - key: 'semanticTokensHelp', - comment: [ - 'Variable 0 will be a file name.', - 'Variable 1 will be a theme name.' - ] - }, - "Code coloring of '{0}' has been updated as the theme '{1}' has [semantic highlighting](https://go.microsoft.com/fwlink/?linkid=2122588) enabled.", - path.basename(model.uri.path), _themeService.getColorTheme().label - ); - - _notificationService.prompt(Severity.Info, message, [ - { - label: nls.localize('learnMoreButton', "Learn More"), - run: () => { - const url = 'https://go.microsoft.com/fwlink/?linkid=2122588'; - - _openerService.open(URI.parse(url)); - } - } - ]); - })); - }; - - - const model = _editor.getModel(); - if (model !== null) { - installChangeTokenListener(model); - } - - toDispose.add(_editor.onDidChangeModel((e) => { - localToDispose.clear(); - - const model = _editor.getModel(); - if (!model) { - return; - } - installChangeTokenListener(model); - })); - } -} - -registerEditorContribution(SemanticTokensHelp.ID, SemanticTokensHelp); diff --git a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts index c1201838060..5e809ac197e 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts @@ -34,6 +34,7 @@ import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/ import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; import { IThemable } from 'vs/base/common/styler'; +import { DEFAULT_FONT_FAMILY } from 'vs/workbench/browser/style'; interface SuggestResultsProvider { /** @@ -311,9 +312,8 @@ function getSuggestEnabledInputOptions(ariaLabel?: string): IEditorOptions { roundedSelection: false, renderIndentGuides: false, cursorWidth: 1, - fontFamily: ' system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif', + fontFamily: DEFAULT_FONT_FAMILY, ariaLabel: ariaLabel || '', - snippetSuggestions: 'none', suggest: { filterGraceful: false, showIcons: false }, autoClosingBrackets: 'never' diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts index 5ba70d39d75..ef2018bcd53 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts @@ -47,31 +47,31 @@ export class ToggleColumnSelectionAction extends Action { if (!codeEditor || codeEditor !== this._getCodeEditor() || oldValue === newValue || !codeEditor.hasModel()) { return; } - const cursors = codeEditor._getCursors(); + const viewModel = codeEditor._getViewModel(); if (codeEditor.getOption(EditorOption.columnSelection)) { const selection = codeEditor.getSelection(); const modelSelectionStart = new Position(selection.selectionStartLineNumber, selection.selectionStartColumn); - const viewSelectionStart = cursors.context.convertModelPositionToViewPosition(modelSelectionStart); + const viewSelectionStart = viewModel.coordinatesConverter.convertModelPositionToViewPosition(modelSelectionStart); const modelPosition = new Position(selection.positionLineNumber, selection.positionColumn); - const viewPosition = cursors.context.convertModelPositionToViewPosition(modelPosition); + const viewPosition = viewModel.coordinatesConverter.convertModelPositionToViewPosition(modelPosition); - CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursors, { + CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: modelSelectionStart, viewPosition: viewSelectionStart }); - const visibleColumn = CursorColumns.visibleColumnFromColumn2(cursors.context.config, cursors.context.viewModel, viewPosition); - CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(cursors, { + const visibleColumn = CursorColumns.visibleColumnFromColumn2(viewModel.cursorConfig, viewModel, viewPosition); + CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(viewModel, { position: modelPosition, viewPosition: viewPosition, doColumnSelect: true, mouseColumn: visibleColumn + 1 }); } else { - const columnSelectData = cursors.getColumnSelectData(); - const fromViewColumn = CursorColumns.columnFromVisibleColumn2(cursors.context.config, cursors.context.viewModel, columnSelectData.fromViewLineNumber, columnSelectData.fromViewVisualColumn); - const fromPosition = cursors.context.convertViewPositionToModelPosition(columnSelectData.fromViewLineNumber, fromViewColumn); - const toViewColumn = CursorColumns.columnFromVisibleColumn2(cursors.context.config, cursors.context.viewModel, columnSelectData.toViewLineNumber, columnSelectData.toViewVisualColumn); - const toPosition = cursors.context.convertViewPositionToModelPosition(columnSelectData.toViewLineNumber, toViewColumn); + const columnSelectData = viewModel.getCursorColumnSelectData(); + const fromViewColumn = CursorColumns.columnFromVisibleColumn2(viewModel.cursorConfig, viewModel, columnSelectData.fromViewLineNumber, columnSelectData.fromViewVisualColumn); + const fromPosition = viewModel.coordinatesConverter.convertViewPositionToModelPosition(new Position(columnSelectData.fromViewLineNumber, fromViewColumn)); + const toViewColumn = CursorColumns.columnFromVisibleColumn2(viewModel.cursorConfig, viewModel, columnSelectData.toViewLineNumber, columnSelectData.toViewVisualColumn); + const toPosition = viewModel.coordinatesConverter.convertViewPositionToModelPosition(new Position(columnSelectData.toViewLineNumber, toViewColumn)); codeEditor.setSelection(new Selection(fromPosition.lineNumber, fromPosition.column, toPosition.lineNumber, toPosition.column)); } diff --git a/src/vs/workbench/contrib/codeEditor/electron-browser/codeEditor.contribution.ts b/src/vs/workbench/contrib/codeEditor/electron-browser/codeEditor.contribution.ts index 21de3dba004..22513f6ef6c 100644 --- a/src/vs/workbench/contrib/codeEditor/electron-browser/codeEditor.contribution.ts +++ b/src/vs/workbench/contrib/codeEditor/electron-browser/codeEditor.contribution.ts @@ -3,7 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import './inputClipboardActions'; -import './sleepResumeRepaintMinimap'; -import './selectionClipboard'; import './startDebugTextMate'; diff --git a/src/vs/workbench/contrib/codeEditor/electron-browser/startDebugTextMate.ts b/src/vs/workbench/contrib/codeEditor/electron-browser/startDebugTextMate.ts index b6cc9a28663..b114a94d5f5 100644 --- a/src/vs/workbench/contrib/codeEditor/electron-browser/startDebugTextMate.ts +++ b/src/vs/workbench/contrib/codeEditor/electron-browser/startDebugTextMate.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as os from 'os'; -import * as path from 'path'; import * as nls from 'vs/nls'; import { Range } from 'vs/editor/common/core/range'; @@ -22,6 +21,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { ITextModel } from 'vs/editor/common/model'; import { Constants } from 'vs/base/common/uint'; import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { join } from 'vs/base/common/path'; class StartDebugTextMate extends Action { @@ -59,7 +59,7 @@ class StartDebugTextMate extends Action { } public async run(): Promise { - const pathInTemp = path.join(os.tmpdir(), `vcode-tm-log-${generateUuid()}.txt`); + const pathInTemp = join(os.tmpdir(), `vcode-tm-log-${generateUuid()}.txt`); const logger = createRotatingLogger(`tm-log`, pathInTemp, 1024 * 1024 * 30, 1); const model = this._getOrCreateModel(); const append = (str: string) => { @@ -104,4 +104,4 @@ class StartDebugTextMate extends Action { } const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(SyncActionDescriptor.from(StartDebugTextMate), 'Start Text Mate Syntax Grammar Logging', nls.localize('developer', "Developer")); +registry.registerWorkbenchAction(SyncActionDescriptor.from(StartDebugTextMate), 'Start Text Mate Syntax Grammar Logging', nls.localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer")); diff --git a/src/vs/workbench/contrib/codeEditor/electron-sandbox/codeEditor.contribution.ts b/src/vs/workbench/contrib/codeEditor/electron-sandbox/codeEditor.contribution.ts new file mode 100644 index 00000000000..08763754187 --- /dev/null +++ b/src/vs/workbench/contrib/codeEditor/electron-sandbox/codeEditor.contribution.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 './inputClipboardActions'; +import './selectionClipboard'; +import './sleepResumeRepaintMinimap'; diff --git a/src/vs/workbench/contrib/codeEditor/electron-browser/inputClipboardActions.ts b/src/vs/workbench/contrib/codeEditor/electron-sandbox/inputClipboardActions.ts similarity index 100% rename from src/vs/workbench/contrib/codeEditor/electron-browser/inputClipboardActions.ts rename to src/vs/workbench/contrib/codeEditor/electron-sandbox/inputClipboardActions.ts diff --git a/src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard.ts b/src/vs/workbench/contrib/codeEditor/electron-sandbox/selectionClipboard.ts similarity index 100% rename from src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard.ts rename to src/vs/workbench/contrib/codeEditor/electron-sandbox/selectionClipboard.ts diff --git a/src/vs/workbench/contrib/codeEditor/electron-browser/sleepResumeRepaintMinimap.ts b/src/vs/workbench/contrib/codeEditor/electron-sandbox/sleepResumeRepaintMinimap.ts similarity index 70% rename from src/vs/workbench/contrib/codeEditor/electron-browser/sleepResumeRepaintMinimap.ts rename to src/vs/workbench/contrib/codeEditor/electron-sandbox/sleepResumeRepaintMinimap.ts index 7ade76dcb8d..e5cec611df9 100644 --- a/src/vs/workbench/contrib/codeEditor/electron-browser/sleepResumeRepaintMinimap.ts +++ b/src/vs/workbench/contrib/codeEditor/electron-sandbox/sleepResumeRepaintMinimap.ts @@ -6,17 +6,21 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { ipcRenderer as ipc } from 'electron'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; +import { Disposable } from 'vs/base/common/lifecycle'; -class SleepResumeRepaintMinimap implements IWorkbenchContribution { +class SleepResumeRepaintMinimap extends Disposable implements IWorkbenchContribution { constructor( - @ICodeEditorService codeEditorService: ICodeEditorService + @ICodeEditorService codeEditorService: ICodeEditorService, + @IElectronService electronService: IElectronService ) { - ipc.on('vscode:osResume', () => { + super(); + + this._register(electronService.onOSResume(() => { codeEditorService.listCodeEditors().forEach(editor => editor.render(true)); - }); + })); } } diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index 91cb38fe9fe..ec11e213ac4 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -6,8 +6,8 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import * as modes from 'vs/editor/common/modes'; -import { ActionsOrientation, ActionViewItem, ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { Action, IActionRunner, IAction } from 'vs/base/common/actions'; +import { ActionsOrientation, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Action, IActionRunner, IAction, Separator } from 'vs/base/common/actions'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { ITextModel } from 'vs/editor/common/model'; @@ -23,16 +23,17 @@ import { Emitter, Event } from 'vs/base/common/event'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdown'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { ToggleReactionsAction, ReactionAction, ReactionActionViewItem } from './reactionsAction'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICommentThreadWidget } from 'vs/workbench/contrib/comments/common/commentThreadWidget'; import { MenuItemAction, SubmenuItemAction, IMenu } from 'vs/platform/actions/common/actions'; -import { ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { CommentFormActions } from 'vs/workbench/contrib/comments/browser/commentFormActions'; +import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; export class CommentNode extends Disposable { private _domNode: HTMLElement; @@ -78,7 +79,6 @@ export class CommentNode extends Disposable { @ICommentService private commentService: ICommentService, @IModelService private modelService: IModelService, @IModeService private modeService: IModeService, - @IKeybindingService private keybindingService: IKeybindingService, @INotificationService private notificationService: INotificationService, @IContextMenuService private contextMenuService: IContextMenuService, @IContextKeyService contextKeyService: IContextKeyService @@ -100,7 +100,7 @@ export class CommentNode extends Disposable { this.createHeader(this._commentDetailsContainer); - this._body = dom.append(this._commentDetailsContainer, dom.$('div.comment-body')); + this._body = dom.append(this._commentDetailsContainer, dom.$(`div.comment-body.${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`)); this._md = this.markdownRenderer.render(comment.body).element; this._body.appendChild(this._md); @@ -120,7 +120,7 @@ export class CommentNode extends Disposable { } private createHeader(commentDetailsContainer: HTMLElement): void { - const header = dom.append(commentDetailsContainer, dom.$('div.comment-title')); + const header = dom.append(commentDetailsContainer, dom.$(`div.comment-title.${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`)); const author = dom.append(header, dom.$('strong.author')); author.innerText = this.comment.userName; @@ -153,13 +153,12 @@ export class CommentNode extends Disposable { action, (action).menuActions, this.contextMenuService, - action => { - return this.actionViewItemProvider(action as Action); - }, - this.actionRunner!, - undefined, - 'toolbar-toggle-pickReactions codicon codicon-reactions', - () => { return AnchorAlignment.RIGHT; } + { + actionViewItemProvider: action => this.actionViewItemProvider(action as Action), + actionRunner: this.actionRunner, + classNames: ['toolbar-toggle-pickReactions', 'codicon', 'codicon-reactions'], + anchorAlignmentProvider: () => AnchorAlignment.RIGHT + } ); } return this.actionViewItemProvider(action as Action); @@ -196,7 +195,7 @@ export class CommentNode extends Disposable { this.createToolbar(); } - this.toolbar!.setActions(primary, secondary)(); + this.toolbar!.setActions(primary, secondary); })); const { primary, secondary } = this.getToolbarActions(menu); @@ -204,7 +203,7 @@ export class CommentNode extends Disposable { if (actions.length || secondary.length) { this.createToolbar(); - this.toolbar!.setActions(actions, secondary)(); + this.toolbar!.setActions(actions, secondary); } } @@ -220,8 +219,9 @@ export class CommentNode extends Disposable { let item = new ReactionActionViewItem(action); return item; } else if (action instanceof MenuItemAction) { - let item = new ContextAwareMenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService); - return item; + return this.instantiationService.createInstance(MenuEntryActionViewItem, action); + } else if (action instanceof SubmenuItemAction) { + return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action); } else { let item = new ActionViewItem({}, action, options); return item; @@ -258,16 +258,17 @@ export class CommentNode extends Disposable { toggleReactionAction, (toggleReactionAction).menuActions, this.contextMenuService, - action => { - if (action.id === ToggleReactionsAction.ID) { - return toggleReactionActionViewItem; - } - return this.actionViewItemProvider(action as Action); - }, - this.actionRunner!, - undefined, - 'toolbar-toggle-pickReactions', - () => { return AnchorAlignment.RIGHT; } + { + actionViewItemProvider: action => { + if (action.id === ToggleReactionsAction.ID) { + return toggleReactionActionViewItem; + } + return this.actionViewItemProvider(action as Action); + }, + actionRunner: this.actionRunner, + classNames: 'toolbar-toggle-pickReactions', + anchorAlignmentProvider: () => AnchorAlignment.RIGHT + } ); return toggleReactionAction; @@ -282,13 +283,12 @@ export class CommentNode extends Disposable { action, (action).menuActions, this.contextMenuService, - action => { - return this.actionViewItemProvider(action as Action); - }, - this.actionRunner!, - undefined, - 'toolbar-toggle-pickReactions', - () => { return AnchorAlignment.RIGHT; } + { + actionViewItemProvider: action => this.actionViewItemProvider(action as Action), + actionRunner: this.actionRunner, + classNames: 'toolbar-toggle-pickReactions', + anchorAlignmentProvider: () => AnchorAlignment.RIGHT + } ); } return this.actionViewItemProvider(action as Action); @@ -400,6 +400,10 @@ export class CommentNode extends Disposable { this._commentEditContainer!.remove(); } + layout() { + this._commentEditor?.layout(); + } + public switchToEditMode() { if (this.isEditing) { return; diff --git a/src/vs/workbench/contrib/comments/browser/commentService.ts b/src/vs/workbench/contrib/comments/browser/commentService.ts index 56bae5b2001..0312fdba5df 100644 --- a/src/vs/workbench/contrib/comments/browser/commentService.ts +++ b/src/vs/workbench/contrib/comments/browser/commentService.ts @@ -33,7 +33,7 @@ export interface IWorkspaceCommentThreadsEvent { } export interface ICommentService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly onDidSetResourceCommentInfos: Event; readonly onDidSetAllCommentThreads: Event; readonly onDidUpdateCommentThreads: Event; @@ -60,7 +60,7 @@ export interface ICommentService { } export class CommentService extends Disposable implements ICommentService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _onDidSetDataProvider: Emitter = this._register(new Emitter()); readonly onDidSetDataProvider: Event = this._onDidSetDataProvider.event; diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index b99e99ff0ce..d674f44afb6 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import { ActionBar, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Action, IAction } from 'vs/base/common/actions'; import * as arrays from 'vs/base/common/arrays'; import { Color } from 'vs/base/common/color'; @@ -26,13 +26,10 @@ import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; import { peekViewBorder } from 'vs/editor/contrib/peekView/peekView'; import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget'; import * as nls from 'vs/nls'; -import { ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenu, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextKey, 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 { INotificationService } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { contrastBorder, editorForeground, focusBorder, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, transparent } from 'vs/platform/theme/common/colorRegistry'; import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService'; @@ -48,6 +45,8 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; export const COMMENTEDITOR_DECORATION_KEY = 'commenteditordecoration'; const COLLAPSE_ACTION_CLASS = 'expand-review-action codicon-chevron-up'; @@ -108,15 +107,12 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget private _owner: string, private _commentThread: modes.CommentThread, private _pendingComment: string | null, - @IInstantiationService instantiationService: IInstantiationService, + @IInstantiationService private instantiationService: IInstantiationService, @IModeService private modeService: IModeService, @IModelService private modelService: IModelService, @IThemeService private themeService: IThemeService, @ICommentService private commentService: ICommentService, @IOpenerService private openerService: IOpenerService, - @IKeybindingService private keybindingService: IKeybindingService, - @INotificationService private notificationService: INotificationService, - @IContextMenuService private contextMenuService: IContextMenuService, @IContextKeyService contextKeyService: IContextKeyService ) { super(editor, { keepEditorSelection: true }); @@ -238,11 +234,11 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._actionbarWidget = new ActionBar(actionsContainer, { actionViewItemProvider: (action: IAction) => { if (action instanceof MenuItemAction) { - let item = new ContextAwareMenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService); - return item; + return this.instantiationService.createInstance(MenuEntryActionViewItem, action); + } else if (action instanceof SubmenuItemAction) { + return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action); } else { - let item = new ActionViewItem({}, action, { label: false, icon: true }); - return item; + return new ActionViewItem({}, action, { label: false, icon: true }); } } }); @@ -720,7 +716,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } private createReplyButton() { - this._reviewThreadReplyButton = dom.append(this._commentForm, dom.$('button.review-thread-reply-button')); + this._reviewThreadReplyButton = dom.append(this._commentForm, dom.$(`button.review-thread-reply-button.${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`)); this._reviewThreadReplyButton.title = this._commentOptions?.prompt || nls.localize('reply', "Reply..."); this._reviewThreadReplyButton.textContent = this._commentOptions?.prompt || nls.localize('reply', "Reply..."); @@ -738,6 +734,11 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget _refresh() { if (this._isExpanded && this._bodyElement) { let dimensions = dom.getClientArea(this._bodyElement); + + this._commentElements.forEach(element => { + element.layout(); + }); + const headHeight = Math.ceil(this.editor.getOption(EditorOption.lineHeight) * 1.2); const lineHeight = this.editor.getOption(EditorOption.lineHeight); const arrowHeight = Math.round(lineHeight / 3); diff --git a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts index c6e976913b5..ed8b5735426 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ContextSubMenu } from 'vs/base/browser/contextmenu'; import { $ } from 'vs/base/browser/dom'; import { Action, IAction } from 'vs/base/common/actions'; import { coalesce, findFirstInSorted } from 'vs/base/common/arrays'; @@ -547,8 +546,8 @@ export class CommentController implements IEditorContribution { return picks; } - private getContextMenuActions(commentInfos: { ownerId: string, extensionId: string | undefined, label: string | undefined, commentingRangesInfo: modes.CommentingRanges }[], lineNumber: number): (IAction | ContextSubMenu)[] { - const actions: (IAction | ContextSubMenu)[] = []; + private getContextMenuActions(commentInfos: { ownerId: string, extensionId: string | undefined, label: string | undefined, commentingRangesInfo: modes.CommentingRanges }[], lineNumber: number): IAction[] { + const actions: IAction[] = []; commentInfos.forEach(commentInfo => { const { ownerId, extensionId, label } = commentInfo; diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index 6c2745d2ec8..0abbf8b7626 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -17,11 +17,10 @@ import { IAccessibilityService } from 'vs/platform/accessibility/common/accessib import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { WorkbenchAsyncDataTree, IListService } from 'vs/platform/list/browser/listService'; +import { WorkbenchAsyncDataTree, IListService, IWorkbenchAsyncDataTreeOptions } from 'vs/platform/list/browser/listService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IColorMapping } from 'vs/platform/theme/common/styler'; -import { basename } from 'vs/base/common/resources'; export const COMMENTS_VIEW_ID = 'workbench.panel.comments'; export const COMMENTS_VIEW_TITLE = 'Comments'; @@ -150,7 +149,7 @@ export class CommentNodeRenderer implements IListRenderer } } -export interface ICommentsListOptions { +export interface ICommentsListOptions extends IWorkbenchAsyncDataTreeOptions { overrideStyles?: IColorMapping; } @@ -182,30 +181,7 @@ export class CommentsList extends WorkbenchAsyncDataTree { renderers, dataSource, { - accessibilityProvider: { - getAriaLabel(element: any): string { - if (element instanceof CommentsModel) { - return nls.localize('rootCommentsLabel', "Comments for current workspace"); - } - if (element instanceof ResourceWithCommentThreads) { - return nls.localize('resourceWithCommentThreadsLabel', "Comments in {0}, full path {1}", basename(element.resource), element.resource.fsPath); - } - if (element instanceof CommentNode) { - return nls.localize('resourceWithCommentLabel', - "Comment from ${0} at line {1} column {2} in {3}, source: {4}", - element.comment.userName, - element.range.startLineNumber, - element.range.startColumn, - basename(element.resource), - element.comment.body.value - ); - } - return ''; - }, - getWidgetAriaLabel(): string { - return COMMENTS_VIEW_TITLE; - } - }, + accessibilityProvider: options.accessibilityProvider, keyboardSupport: true, identityProvider: { getId: (element: any) => { diff --git a/src/vs/workbench/contrib/comments/browser/commentsView.ts b/src/vs/workbench/contrib/comments/browser/commentsView.ts index f8c26805cf5..5410708c29f 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsView.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsView.ts @@ -6,6 +6,7 @@ import 'vs/css!./media/panel'; import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; +import { basename } from 'vs/base/common/resources'; import { IAction, Action } from 'vs/base/common/actions'; import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -27,8 +28,6 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { TreeResourceNavigator } from 'vs/platform/list/browser/listService'; - export class CommentsPanel extends ViewPane { private treeLabels!: ResourceLabels; @@ -150,10 +149,36 @@ export class CommentsPanel extends ViewPane { private createTree(): void { this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this)); - this.tree = this._register(this.instantiationService.createInstance(CommentsList, this.treeLabels, this.treeContainer, { overrideStyles: { listBackground: this.getBackgroundColor() } })); + this.tree = this._register(this.instantiationService.createInstance(CommentsList, this.treeLabels, this.treeContainer, { + overrideStyles: { listBackground: this.getBackgroundColor() }, + openOnFocus: true, + accessibilityProvider: { + getAriaLabel(element: any): string { + if (element instanceof CommentsModel) { + return nls.localize('rootCommentsLabel', "Comments for current workspace"); + } + if (element instanceof ResourceWithCommentThreads) { + return nls.localize('resourceWithCommentThreadsLabel', "Comments in {0}, full path {1}", basename(element.resource), element.resource.fsPath); + } + if (element instanceof CommentNode) { + return nls.localize('resourceWithCommentLabel', + "Comment from ${0} at line {1} column {2} in {3}, source: {4}", + element.comment.userName, + element.range.startLineNumber, + element.range.startColumn, + basename(element.resource), + element.comment.body.value + ); + } + return ''; + }, + getWidgetAriaLabel(): string { + return COMMENTS_VIEW_TITLE; + } + } + })); - const commentsNavigator = this._register(new TreeResourceNavigator(this.tree, { openOnFocus: true })); - this._register(commentsNavigator.onDidOpenResource(e => { + this._register(this.tree.onDidOpen(e => { this.openFile(e.element, e.editorOptions.pinned, e.editorOptions.preserveFocus, e.sideBySide); })); } diff --git a/src/vs/workbench/contrib/comments/browser/media/review.css b/src/vs/workbench/contrib/comments/browser/media/review.css index df221e87d14..584c433d6c3 100644 --- a/src/vs/workbench/contrib/comments/browser/media/review.css +++ b/src/vs/workbench/contrib/comments/browser/media/review.css @@ -257,7 +257,6 @@ line-height: 20px; white-space: nowrap; border: 0px; - cursor: text; outline: 1px solid transparent; } diff --git a/src/vs/workbench/contrib/comments/browser/reactionsAction.ts b/src/vs/workbench/contrib/comments/browser/reactionsAction.ts index a5ae07ec17f..ef091f4dd7b 100644 --- a/src/vs/workbench/contrib/comments/browser/reactionsAction.ts +++ b/src/vs/workbench/contrib/comments/browser/reactionsAction.ts @@ -5,9 +5,9 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; -import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { Action, IAction } from 'vs/base/common/actions'; import { URI, UriComponents } from 'vs/base/common/uri'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; export class ToggleReactionsAction extends Action { static readonly ID = 'toolbar.toggle.pickReactions'; diff --git a/src/vs/workbench/contrib/comments/common/commentModel.ts b/src/vs/workbench/contrib/comments/common/commentModel.ts index 256b3dda7e6..19967256c69 100644 --- a/src/vs/workbench/contrib/comments/common/commentModel.ts +++ b/src/vs/workbench/contrib/comments/common/commentModel.ts @@ -8,7 +8,6 @@ import { IRange } from 'vs/editor/common/core/range'; import { Comment, CommentThread, CommentThreadChangedEvent } from 'vs/editor/common/modes'; import { groupBy, firstIndex, flatten } from 'vs/base/common/arrays'; import { localize } from 'vs/nls'; -import { values } from 'vs/base/common/map'; export interface ICommentThreadChangedEvent extends CommentThreadChangedEvent { owner: string; @@ -74,7 +73,7 @@ export class CommentsModel { public setCommentThreads(owner: string, commentThreads: CommentThread[]): void { this.commentThreadsMap.set(owner, this.groupByResource(owner, commentThreads)); - this.resourceCommentThreads = flatten(values(this.commentThreadsMap)); + this.resourceCommentThreads = flatten([...this.commentThreadsMap.values()]); } public updateCommentThreads(event: ICommentThreadChangedEvent): boolean { @@ -124,7 +123,7 @@ export class CommentsModel { }); this.commentThreadsMap.set(owner, threadsForOwner); - this.resourceCommentThreads = flatten(values(this.commentThreadsMap)); + this.resourceCommentThreads = flatten([...this.commentThreadsMap.values()]); return removed.length > 0 || changed.length > 0 || added.length > 0; } @@ -166,4 +165,4 @@ export class CommentsModel { return 0; } } -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/customEditor/browser/commands.ts b/src/vs/workbench/contrib/customEditor/browser/commands.ts deleted file mode 100644 index d96619b257a..00000000000 --- a/src/vs/workbench/contrib/customEditor/browser/commands.ts +++ /dev/null @@ -1,68 +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 { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { Command } from 'vs/editor/browser/editorExtensions'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; -import { CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE } from 'vs/workbench/contrib/customEditor/common/customEditor'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; - - -(new class UndoCustomEditorCommand extends Command { - public static readonly ID = 'editor.action.customEditor.undo'; - - constructor() { - super({ - id: UndoCustomEditorCommand.ID, - precondition: ContextKeyExpr.and( - CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, - ContextKeyExpr.not(InputFocusedContextKey)), - kbOpts: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_Z, - weight: KeybindingWeight.EditorContrib - } - }); - } - - public runCommand(accessor: ServicesAccessor): void { - const editorService = accessor.get(IEditorService); - const activeInput = editorService.activeEditorPane?.input; - if (activeInput instanceof CustomEditorInput) { - activeInput.undo(); - } - } -}).register(); - -(new class RedoWebviewEditorCommand extends Command { - public static readonly ID = 'editor.action.customEditor.redo'; - - constructor() { - super({ - id: RedoWebviewEditorCommand.ID, - precondition: ContextKeyExpr.and( - CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, - ContextKeyExpr.not(InputFocusedContextKey)), - kbOpts: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_Y, - secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z], - mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z }, - weight: KeybindingWeight.EditorContrib - } - }); - } - - public runCommand(accessor: ServicesAccessor): void { - const editorService = accessor.get(IEditorService); - const activeInput = editorService.activeEditorPane?.input; - if (activeInput instanceof CustomEditorInput) { - activeInput.redo(); - } - } -}).register(); - diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditor.contribution.ts b/src/vs/workbench/contrib/customEditor/browser/customEditor.contribution.ts index 2815b286b72..25069373665 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditor.contribution.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditor.contribution.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Schemas } from 'vs/base/common/network'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; @@ -13,7 +14,6 @@ import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from import { CustomEditorInputFactory } from 'vs/workbench/contrib/customEditor/browser/customEditorInputFactory'; import { ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor'; -import './commands'; import { CustomEditorInput } from './customEditorInput'; import { CustomEditorContribution, CustomEditorService } from './customEditors'; @@ -38,4 +38,4 @@ Registry.as(EditorInputExtensions.EditorInputFactor CustomEditorInputFactory); Registry.as(EditorInputExtensions.EditorInputFactories) - .registerCustomEditorInputFactory(CustomEditorInputFactory); + .registerCustomEditorInputFactory(Schemas.vscodeCustomEditor, CustomEditorInputFactory); diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index 96c78b57d37..c75ea750e6f 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts @@ -166,7 +166,7 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput { return undefined; } - return this.move(groupId, target)?.editor; + return this.rename(groupId, target)?.editor; } public async revert(group: GroupIdentifier, options?: IRevertOptions): Promise { @@ -196,7 +196,7 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput { return null; } - move(group: GroupIdentifier, newResource: URI): { editor: IEditorInput } | undefined { + rename(group: GroupIdentifier, newResource: URI): { editor: IEditorInput } | undefined { // See if we can keep using the same custom editor provider const editorInfo = this.customEditorService.getCustomEditor(this.viewType); if (editorInfo?.matches(newResource)) { diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts index 2bfbb0590ef..cf1b74effb8 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts @@ -8,7 +8,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorInput } from 'vs/workbench/common/editor'; import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; -import { IWebviewService, WebviewExtensionDescription } from 'vs/workbench/contrib/webview/browser/webview'; +import { IWebviewService, WebviewExtensionDescription, WebviewContentPurpose } from 'vs/workbench/contrib/webview/browser/webview'; import { reviveWebviewExtensionDescription, SerializedWebview, WebviewEditorInputFactory, DeserializedWebview } from 'vs/workbench/contrib/webview/browser/webviewEditorInputFactory'; import { IWebviewWorkbenchService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; @@ -57,11 +57,12 @@ export class CustomEditorInputFactory extends WebviewEditorInputFactory { } public serialize(input: CustomEditorInput): string | undefined { + const dirty = input.isDirty(); const data: SerializedCustomEditor = { ...this.toJson(input), editorResource: input.resource.toJSON(), - dirty: input.isDirty(), - backupId: input.backupId, + dirty, + backupId: dirty ? input.backupId : undefined, }; try { @@ -95,11 +96,11 @@ export class CustomEditorInputFactory extends WebviewEditorInputFactory { private static reviveWebview(data: { id: string, state: any, options: WebviewInputOptions, extension?: WebviewExtensionDescription, }, webviewService: IWebviewService) { return new Lazy(() => { const webview = webviewService.createWebviewOverlay(data.id, { + purpose: WebviewContentPurpose.CustomEditor, enableFindWidget: data.options.enableFindWidget, retainContextWhenHidden: data.options.retainContextWhenHidden - }, data.options); + }, data.options, data.extension); webview.state = data.state; - webview.extension = data.extension; return webview; }); } @@ -124,4 +125,14 @@ export class CustomEditorInputFactory extends WebviewEditorInputFactory { return editor; }); } + + public static canResolveBackup(editorInput: IEditorInput, backupResource: URI): boolean { + if (editorInput instanceof CustomEditorInput) { + if (editorInput.resource.path === backupResource.path && backupResource.authority === editorInput.viewType) { + return true; + } + } + + return false; + } } diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index 95fecef9b18..76809c233a4 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -10,6 +10,7 @@ import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle' import { basename, extname, isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; +import { RedoCommand, UndoCommand } from 'vs/editor/browser/editorExtensions'; import * as nls from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -17,19 +18,18 @@ import { EditorActivation, IEditorOptions, ITextEditorOptions } from 'vs/platfor import { FileOperation, IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IStorageService } from 'vs/platform/storage/common/storage'; import * as colorRegistry from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { EditorInput, EditorOptions, GroupIdentifier, IEditorInput, IEditorPane } from 'vs/workbench/common/editor'; +import { EditorInput, EditorOptions, Extensions as EditorInputExtensions, GroupIdentifier, IEditorInput, IEditorInputFactoryRegistry, IEditorPane } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { CONTEXT_CUSTOM_EDITORS, CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, CustomEditorCapabilities, CustomEditorInfo, CustomEditorInfoCollection, CustomEditorPriority, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { CustomEditorModelManager } from 'vs/workbench/contrib/customEditor/common/customEditorModelManager'; -import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; -import { defaultEditorOverrideEntry } from 'vs/workbench/contrib/files/common/openWith'; import { IWebviewService, webviewHasOwnEditFunctionsContext } from 'vs/workbench/contrib/webview/browser/webview'; -import { CustomEditorAssociation, CustomEditorsAssociations, customEditorsAssociationsSettingId } from 'vs/workbench/services/editor/common/editorAssociationsSetting'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { CustomEditorAssociation, CustomEditorsAssociations, customEditorsAssociationsSettingId, defaultEditorOverrideEntry } from 'vs/workbench/services/editor/common/editorOpenWith'; import { ICustomEditorInfo, ICustomEditorViewTypesHandler, IEditorService, IOpenEditorOverride, IOpenEditorOverrideEntry } from 'vs/workbench/services/editor/common/editorService'; import { ContributedCustomEditors, defaultCustomEditor } from '../common/contributedCustomEditors'; import { CustomEditorInput } from './customEditorInput'; @@ -37,7 +37,7 @@ import { CustomEditorInput } from './customEditorInput'; export class CustomEditorService extends Disposable implements ICustomEditorService, ICustomEditorViewTypesHandler { _serviceBrand: any; - private readonly _contributedEditors = this._register(new ContributedCustomEditors()); + private readonly _contributedEditors: ContributedCustomEditors; private readonly _editorCapabilities = new Map(); private readonly _models = new CustomEditorModelManager(); @@ -48,9 +48,12 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ private readonly _onDidChangeViewTypes = new Emitter(); onDidChangeViewTypes: Event = this._onDidChangeViewTypes.event; + private readonly _fileEditorInputFactory = Registry.as(EditorInputExtensions.EditorInputFactories).getFileEditorInputFactory(); + constructor( @IContextKeyService contextKeyService: IContextKeyService, @IFileService fileService: IFileService, + @IStorageService storageService: IStorageService, @IConfigurationService private readonly configurationService: IConfigurationService, @IEditorService private readonly editorService: IEditorService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @@ -64,11 +67,12 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ this._focusedCustomEditorIsEditable = CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE.bindTo(contextKeyService); this._webviewHasOwnEditFunctions = webviewHasOwnEditFunctionsContext.bindTo(contextKeyService); - this._register(this.editorService.registerCustomEditorViewTypesHandler('Custom Editor', this)); + this._contributedEditors = this._register(new ContributedCustomEditors(storageService)); this._register(this._contributedEditors.onChange(() => { this.updateContexts(); this._onDidChangeViewTypes.fire(); })); + this._register(this.editorService.registerCustomEditorViewTypesHandler('Custom Editor', this)); this._register(this.editorService.onDidActiveEditorChange(() => this.updateContexts())); this._register(fileService.onDidRunOperation(e => { @@ -77,6 +81,14 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ } })); + const PRIORITY = 105; + this._register(UndoCommand.addImplementation(PRIORITY, () => { + return this.withActiveCustomEditor(editor => editor.undo()); + })); + this._register(RedoCommand.addImplementation(PRIORITY, () => { + return this.withActiveCustomEditor(editor => editor.redo()); + })); + this.updateContexts(); } @@ -84,6 +96,15 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ return [...this._contributedEditors]; } + private withActiveCustomEditor(f: (editor: CustomEditorInput) => void): boolean { + const activeEditor = this.editorService.activeEditor; + if (activeEditor instanceof CustomEditorInput) { + f(activeEditor); + return true; + } + return false; + } + public get models() { return this._models; } public getCustomEditor(viewType: string): CustomEditorInfo | undefined { @@ -200,7 +221,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ ): Promise { if (viewType === defaultCustomEditor.id) { const fileEditorInput = this.editorService.createEditorInput({ resource, forceFile: true }); - return this.openEditorForResource(resource, fileEditorInput, { ...options, ignoreOverrides: true }, group); + return this.openEditorForResource(resource, fileEditorInput, { ...options, override: false }, group); } if (!this._contributedEditors.get(viewType)) { @@ -231,7 +252,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ const id = generateUuid(); const webview = new Lazy(() => { - return this.webviewService.createWebviewOverlay(id, { customClasses: options?.customClasses }, {}); + return this.webviewService.createWebviewOverlay(id, { customClasses: options?.customClasses }, {}, undefined); }); const input = this.instantiationService.createInstance(CustomEditorInput, resource, viewType, id, webview, {}); if (typeof group !== 'undefined') { @@ -319,7 +340,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ const editorsToReplace = new Map(); for (const group of this.editorGroupService.groups) { for (const editor of group.editors) { - if (editor instanceof FileEditorInput + if (this._fileEditorInputFactory.isFileEditorInput(editor) && !(editor instanceof CustomEditorInput) && isEqual(editor.resource, newResource) ) { @@ -388,7 +409,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ } const targetGroup = group || this.editorGroupService.activeGroup; - const newEditor = await this.openEditorForResource(resource, editorToUse.editor, { ...options, ignoreOverrides: true }, targetGroup); + const newEditor = await this.openEditorForResource(resource, editorToUse.editor, { ...options, override: false }, targetGroup); if (targetGroup.id !== editorToUse.group.id) { editorToUse.group.closeEditor(editorToUse.editor); } @@ -417,58 +438,69 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ } export class CustomEditorContribution extends Disposable implements IWorkbenchContribution { + + private readonly _fileEditorInputFactory = Registry.as(EditorInputExtensions.EditorInputFactories).getFileEditorInputFactory(); + constructor( - @IEditorService private readonly editorService: EditorServiceImpl, + @IEditorService private readonly editorService: IEditorService, @ICustomEditorService private readonly customEditorService: ICustomEditorService, ) { super(); this._register(this.editorService.overrideOpenEditor({ - open: (editor, options, group, id) => { - return this.onEditorOpening(editor, options, group, id); + open: (editor, options, group) => { + return this.onEditorOpening(editor, options, group); }, - getEditorOverrides: (resource: URI, _options: IEditorOptions | undefined, group: IEditorGroup | undefined): IOpenEditorOverrideEntry[] => { + getEditorOverrides: (resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined): IOpenEditorOverrideEntry[] => { const currentEditor = group?.editors.find(editor => isEqual(editor.resource, resource)); + const defaultEditorOverride: IOpenEditorOverrideEntry = { + ...defaultEditorOverrideEntry, + active: this._fileEditorInputFactory.isFileEditorInput(currentEditor), + }; + + const toOverride = (entry: CustomEditorInfo): IOpenEditorOverrideEntry => { + return { + id: entry.id, + active: currentEditor instanceof CustomEditorInput && currentEditor.viewType === entry.id, + label: entry.displayName, + detail: entry.providerDisplayName, + }; + }; + + if (typeof options?.override === 'string') { + // A specific override was requested. Only return it. + + if (options.override === defaultEditorOverride.id) { + return [defaultEditorOverride]; + } + + const matchingEditor = this.customEditorService.getCustomEditor(options.override); + return matchingEditor ? [toOverride(matchingEditor)] : []; + } + + // Otherwise, return all potential overrides. const customEditors = this.customEditorService.getAllCustomEditors(resource); if (!customEditors.length) { return []; } return [ - { - ...defaultEditorOverrideEntry, - active: currentEditor instanceof FileEditorInput, - }, - ...customEditors.allEditors.map(entry => { - return { - id: entry.id, - active: currentEditor instanceof CustomEditorInput && currentEditor.viewType === entry.id, - label: entry.displayName, - detail: entry.providerDisplayName, - }; - }) + defaultEditorOverride, + ...customEditors.allEditors + .filter(entry => entry.id !== defaultCustomEditor.id) + .map(toOverride) ]; } })); - - this._register(this.editorService.onDidCloseEditor(({ editor }) => { - if (!(editor instanceof CustomEditorInput)) { - return; - } - - if (!this.editorService.editors.some(other => other === editor)) { - editor.dispose(); - } - })); } private onEditorOpening( editor: IEditorInput, options: ITextEditorOptions | undefined, - group: IEditorGroup, - id?: string, + group: IEditorGroup ): IOpenEditorOverride | undefined { + const id = typeof options?.override === 'string' ? options.override : undefined; if (editor instanceof CustomEditorInput) { if (editor.group === group.id && (editor.viewType === id || typeof id !== 'string')) { // No need to do anything @@ -493,12 +525,8 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo } if (id) { - if (editor instanceof FileEditorInput && id === defaultCustomEditor.id) { - return undefined; - } - return { - override: this.customEditorService.openWith(resource, id, { ...options, ignoreOverrides: true }, group) + override: this.customEditorService.openWith(resource, id, { ...options, override: false }, group) }; } @@ -521,6 +549,11 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo // If there is, we want to open that instead of creating a new editor. // This ensures that we preserve whatever type of editor was previously being used // when the user switches back to it. + const strictMatchEditorInput = group.editors.find(e => e === editor && !this._fileEditorInputFactory.isFileEditorInput(e)); + if (strictMatchEditorInput) { + return; + } + const existingEditorForResource = group.editors.find(editor => isEqual(resource, editor.resource)); if (existingEditorForResource) { if (editor === existingEditorForResource) { @@ -530,7 +563,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo return { override: this.editorService.openEditor(existingEditorForResource, { ...options, - ignoreOverrides: true, + override: false, activation: options?.preserveFocus ? EditorActivation.RESTORE : undefined, }, group) }; @@ -561,7 +594,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo // Open VS Code's standard editor but prompt user to see if they wish to use a custom one instead return { override: (async () => { - const standardEditor = await this.editorService.openEditor(editor, { ...options, ignoreOverrides: true }, group); + const standardEditor = await this.editorService.openEditor(editor, { ...options, override: false }, group); // Give a moment to make sure the editor is showing. // Otherwise the focus shift can cause the prompt to be dismissed right away. await new Promise(resolve => setTimeout(resolve, 20)); @@ -584,7 +617,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo options: ITextEditorOptions | undefined, group: IEditorGroup ): IOpenEditorOverride | undefined { - const getCustomEditorOverrideForSubInput = (subInput: IEditorInput, customClasses: string): EditorInput | undefined => { + const getBestAvailableEditorForSubInput = (subInput: IEditorInput): CustomEditorInfo | undefined => { if (subInput instanceof CustomEditorInput) { return undefined; } @@ -593,33 +626,43 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo return undefined; } - // Prefer default editors in the diff editor case but ultimatly always take the first editor + // Prefer default editors in the diff editor case but ultimately always take the first editor const allEditors = new CustomEditorInfoCollection([ ...this.customEditorService.getUserConfiguredCustomEditors(resource).allEditors, ...this.customEditorService.getContributedCustomEditors(resource).allEditors.filter(x => x.priority !== CustomEditorPriority.option), ]); - - const bestAvailableEditor = allEditors.bestAvailableEditor; - if (!bestAvailableEditor) { - return undefined; - } - - const input = this.customEditorService.createInput(resource, bestAvailableEditor.id, group.id, { customClasses }); - if (input instanceof EditorInput) { - return input; - } - - return undefined; + return allEditors.bestAvailableEditor; }; - const modifiedOverride = getCustomEditorOverrideForSubInput(editor.modifiedInput, 'modified'); - const originalOverride = getCustomEditorOverrideForSubInput(editor.originalInput, 'original'); + const createEditorForSubInput = (subInput: IEditorInput, editor: CustomEditorInfo | undefined, customClasses: string): EditorInput | undefined => { + if (!editor) { + return; + } + if (!subInput.resource) { + return; + } + const input = this.customEditorService.createInput(subInput.resource, editor.id, group.id, { customClasses }); + return input instanceof EditorInput ? input : undefined; + }; + const modifiedEditorInfo = getBestAvailableEditorForSubInput(editor.modifiedInput); + const originalEditorInfo = getBestAvailableEditorForSubInput(editor.originalInput); + + // If we are only using default editors, no need to override anything + if ( + (!modifiedEditorInfo || modifiedEditorInfo.id === defaultCustomEditor.id) && + (!originalEditorInfo || originalEditorInfo.id === defaultCustomEditor.id) + ) { + return undefined; + } + + const modifiedOverride = createEditorForSubInput(editor.modifiedInput, modifiedEditorInfo, 'modified'); + const originalOverride = createEditorForSubInput(editor.originalInput, originalEditorInfo, 'original'); if (modifiedOverride || originalOverride) { return { override: (async () => { const input = new DiffEditorInput(editor.getName(), editor.getDescription(), originalOverride || editor.originalInput, modifiedOverride || editor.modifiedInput, true); - return this.editorService.openEditor(input, { ...options, ignoreOverrides: true }, group); + return this.editorService.openEditor(input, { ...options, override: false }, group); })(), }; } diff --git a/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts b/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts index b00fa6c5f63..d61f9874fcd 100644 --- a/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts +++ b/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts @@ -8,9 +8,12 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { CustomEditorInfo, CustomEditorPriority } from 'vs/workbench/contrib/customEditor/common/customEditor'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { Memento } from 'vs/workbench/common/memento'; +import { CustomEditorDescriptor, CustomEditorInfo, CustomEditorPriority } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { customEditorsExtensionPoint, ICustomEditorsExtensionPoint } from 'vs/workbench/contrib/customEditor/common/extensionPoint'; -import { DEFAULT_EDITOR_ID } from 'vs/workbench/contrib/files/common/files'; +import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { DEFAULT_EDITOR_ID } from 'vs/workbench/services/editor/common/editorOpenWith'; const builtinProviderDisplayName = nls.localize('builtinProviderDisplayName', "Built-in"); @@ -26,32 +29,52 @@ export const defaultCustomEditor = new CustomEditorInfo({ export class ContributedCustomEditors extends Disposable { - private readonly _editors = new Map(); + private static readonly CUSTOM_EDITORS_STORAGE_ID = 'customEditors'; + private static readonly CUSTOM_EDITORS_ENTRY_ID = 'editors'; - constructor() { + private readonly _editors = new Map(); + private readonly _memento: Memento; + + constructor(storageService: IStorageService) { super(); - customEditorsExtensionPoint.setHandler(extensions => { - this._editors.clear(); + this._memento = new Memento(ContributedCustomEditors.CUSTOM_EDITORS_STORAGE_ID, storageService); - for (const extension of extensions) { - for (const webviewEditorContribution of extension.value) { - this.add(new CustomEditorInfo({ - id: webviewEditorContribution.viewType, - displayName: webviewEditorContribution.displayName, - providerDisplayName: extension.description.isBuiltin ? builtinProviderDisplayName : extension.description.displayName || extension.description.identifier.value, - selector: webviewEditorContribution.selector || [], - priority: getPriorityFromContribution(webviewEditorContribution, extension.description), - })); - } - } - this._onChange.fire(); + const mementoObject = this._memento.getMemento(StorageScope.GLOBAL); + for (const info of (mementoObject[ContributedCustomEditors.CUSTOM_EDITORS_ENTRY_ID] || []) as CustomEditorDescriptor[]) { + this.add(new CustomEditorInfo(info)); + } + + customEditorsExtensionPoint.setHandler(extensions => { + this.update(extensions); }); } private readonly _onChange = this._register(new Emitter()); public readonly onChange = this._onChange.event; + private update(extensions: readonly IExtensionPointUser[]) { + this._editors.clear(); + + for (const extension of extensions) { + for (const webviewEditorContribution of extension.value) { + this.add(new CustomEditorInfo({ + id: webviewEditorContribution.viewType, + displayName: webviewEditorContribution.displayName, + providerDisplayName: extension.description.isBuiltin ? builtinProviderDisplayName : extension.description.displayName || extension.description.identifier.value, + selector: webviewEditorContribution.selector || [], + priority: getPriorityFromContribution(webviewEditorContribution, extension.description), + })); + } + } + + const mementoObject = this._memento.getMemento(StorageScope.GLOBAL); + mementoObject[ContributedCustomEditors.CUSTOM_EDITORS_ENTRY_ID] = Array.from(this._editors.values()); + this._memento.saveMemento(); + + this._onChange.fire(); + } + public [Symbol.iterator](): Iterator { return this._editors.values(); } diff --git a/src/vs/workbench/contrib/customEditor/common/customEditor.ts b/src/vs/workbench/contrib/customEditor/common/customEditor.ts index e914afd609f..658ca9c5771 100644 --- a/src/vs/workbench/contrib/customEditor/common/customEditor.ts +++ b/src/vs/workbench/contrib/customEditor/common/customEditor.ts @@ -7,6 +7,7 @@ import { distinct, mergeSort } from 'vs/base/common/arrays'; import { Event } from 'vs/base/common/event'; import * as glob from 'vs/base/common/glob'; import { IDisposable, IReference } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; import { posix } from 'vs/base/common/path'; import { basename } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; @@ -79,7 +80,20 @@ export interface CustomEditorSelector { readonly filenamePattern?: string; } -export class CustomEditorInfo { +export interface CustomEditorDescriptor { + readonly id: string; + readonly displayName: string; + readonly providerDisplayName: string; + readonly priority: CustomEditorPriority; + readonly selector: readonly CustomEditorSelector[]; +} + +export class CustomEditorInfo implements CustomEditorDescriptor { + + private static readonly excludedSchemes = new Set([ + Schemas.extension, + Schemas.webviewPanel, + ]); public readonly id: string; public readonly displayName: string; @@ -87,13 +101,7 @@ export class CustomEditorInfo { public readonly priority: CustomEditorPriority; public readonly selector: readonly CustomEditorSelector[]; - constructor(descriptor: { - readonly id: string; - readonly displayName: string; - readonly providerDisplayName: string; - readonly priority: CustomEditorPriority; - readonly selector: readonly CustomEditorSelector[]; - }) { + constructor(descriptor: CustomEditorDescriptor) { this.id = descriptor.id; this.displayName = descriptor.displayName; this.providerDisplayName = descriptor.providerDisplayName; @@ -106,6 +114,10 @@ export class CustomEditorInfo { } static selectorMatches(selector: CustomEditorSelector, resource: URI): boolean { + if (CustomEditorInfo.excludedSchemes.has(resource.scheme)) { + return false; + } + if (selector.filenamePattern) { const matchOnPath = selector.filenamePattern.indexOf(posix.sep) >= 0; const target = matchOnPath ? resource.path : basename(resource); diff --git a/src/vs/workbench/contrib/customEditor/common/extensionPoint.ts b/src/vs/workbench/contrib/customEditor/common/extensionPoint.ts index 29b23af79c7..279da3d2282 100644 --- a/src/vs/workbench/contrib/customEditor/common/extensionPoint.ts +++ b/src/vs/workbench/contrib/customEditor/common/extensionPoint.ts @@ -45,7 +45,7 @@ const CustomEditorsContribution: IJSONSchema = { properties: { [Fields.viewType]: { type: 'string', - description: nls.localize('contributes.viewType', 'Unique identifier of the custom editor.'), + markdownDescription: nls.localize('contributes.viewType', 'Identifier for the custom editor. This must be unique across all custom editors, so we recommend including your extension id as part of `viewType`. The `viewType` is used when registering custom editors with `vscode.registerCustomEditorProvider` and in the `onCustomEditor:${id}` [activation event](https://code.visualstudio.com/api/references/activation-events).'), }, [Fields.displayName]: { type: 'string', @@ -71,14 +71,14 @@ const CustomEditorsContribution: IJSONSchema = { }, [Fields.priority]: { type: 'string', - description: nls.localize('contributes.priority', 'Controls when the custom editor is used. May be overridden by users.'), + markdownDeprecationMessage: nls.localize('contributes.priority', 'Controls if the custom editor is enabled automatically when the user opens a file. This may be overridden by users using the `workbench.editorAssociations` setting.'), enum: [ CustomEditorPriority.default, CustomEditorPriority.option, ], markdownEnumDescriptions: [ - nls.localize('contributes.priority.default', 'Editor is automatically used for a resource if no other default custom editors are registered for it.'), - nls.localize('contributes.priority.option', 'Editor is not automatically used but can be selected by a user.'), + nls.localize('contributes.priority.default', 'The editor is automatically used when the user opens a resource, provided that no other default custom editors are registered for that resource.'), + nls.localize('contributes.priority.option', 'The editor is not automatically used when the user opens a resource, but a user can switch to the editor using the `Reopen With` command.'), ], default: 'default' } diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index 930bbe1d161..8a98767b7f5 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -8,7 +8,7 @@ import * as env from 'vs/base/common/platform'; import * as dom from 'vs/base/browser/dom'; import { URI } from 'vs/base/common/uri'; import severity from 'vs/base/common/severity'; -import { IAction, Action } from 'vs/base/common/actions'; +import { IAction, Action, SubmenuAction } from 'vs/base/common/actions'; import { Range } from 'vs/editor/common/core/range'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType, IContentWidget, IActiveCodeEditor, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; import { IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness, ITextModel, OverviewRulerLane, IModelDecorationOverviewRulerOptions } from 'vs/editor/common/model'; @@ -18,7 +18,6 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { RemoveBreakpointAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, BreakpointWidgetContext, IBreakpointEditorContribution, IBreakpointUpdateData, IDebugConfiguration, State, IDebugSession } from 'vs/workbench/contrib/debug/common/debug'; import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; -import { ContextSubMenu } from 'vs/base/browser/contextmenu'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { BreakpointWidget } from 'vs/workbench/contrib/debug/browser/breakpointWidget'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -35,6 +34,7 @@ import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { isSafari } from 'vs/base/browser/browser'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { ILabelService } from 'vs/platform/label/common/label'; const $ = dom.$; @@ -72,7 +72,7 @@ export function createBreakpointDecorations(model: ITextModel, breakpoints: Read } function getBreakpointDecorationOptions(model: ITextModel, breakpoint: IBreakpoint, state: State, breakpointsActivated: boolean, showBreakpointsInOverviewRuler: boolean): IModelDecorationOptions { - const { className, message } = getBreakpointMessageAndClassName(state, breakpointsActivated, breakpoint); + const { className, message } = getBreakpointMessageAndClassName(state, breakpointsActivated, breakpoint, undefined); let glyphMarginHoverMessage: MarkdownString | undefined; if (message) { @@ -163,7 +163,8 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi @IInstantiationService private readonly instantiationService: IInstantiationService, @IContextKeyService contextKeyService: IContextKeyService, @IDialogService private readonly dialogService: IDialogService, - @IConfigurationService private readonly configurationService: IConfigurationService + @IConfigurationService private readonly configurationService: IConfigurationService, + @ILabelService private readonly labelService: ILabelService ) { this.breakpointWidgetVisible = CONTEXT_BREAKPOINT_WIDGET_VISIBLE.bindTo(contextKeyService); this.registerListeners(); @@ -286,8 +287,8 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi })); } - private getContextMenuActions(breakpoints: ReadonlyArray, uri: URI, lineNumber: number, column?: number): Array { - const actions: Array = []; + private getContextMenuActions(breakpoints: ReadonlyArray, uri: URI, lineNumber: number, column?: number): IAction[] { + const actions: IAction[] = []; if (breakpoints.length === 1) { const breakpointType = breakpoints[0].logMessage ? nls.localize('logPoint', "Logpoint") : nls.localize('breakpoint', "Breakpoint"); actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, nls.localize('removeBreakpoint', "Remove {0}", breakpointType), this.debugService)); @@ -308,7 +309,7 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi )); } else if (breakpoints.length > 1) { const sorted = breakpoints.slice().sort((first, second) => (first.column && second.column) ? first.column - second.column : 1); - actions.push(new ContextSubMenu(nls.localize('removeBreakpoints', "Remove Breakpoints"), sorted.map(bp => new Action( + actions.push(new SubmenuAction('debug.removeBreakpoints', nls.localize('removeBreakpoints', "Remove Breakpoints"), sorted.map(bp => new Action( 'removeInlineBreakpoint', bp.column ? nls.localize('removeInlineBreakpointOnColumn', "Remove Inline Breakpoint on Column {0}", bp.column) : nls.localize('removeLineBreakpoint', "Remove Line Breakpoint"), undefined, @@ -316,7 +317,7 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi () => this.debugService.removeBreakpoints(bp.getId()) )))); - actions.push(new ContextSubMenu(nls.localize('editBreakpoints', "Edit Breakpoints"), sorted.map(bp => + actions.push(new SubmenuAction('debug.editBReakpoints', nls.localize('editBreakpoints', "Edit Breakpoints"), sorted.map(bp => new Action('editBreakpoint', bp.column ? nls.localize('editInlineBreakpointOnColumn', "Edit Inline Breakpoint on Column {0}", bp.column) : nls.localize('editLineBrekapoint', "Edit Line Breakpoint"), undefined, @@ -325,7 +326,7 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi ) ))); - actions.push(new ContextSubMenu(nls.localize('enableDisableBreakpoints', "Enable/Disable Breakpoints"), sorted.map(bp => new Action( + actions.push(new SubmenuAction('debug.enableDisableBreakpoints', nls.localize('enableDisableBreakpoints', "Enable/Disable Breakpoints"), sorted.map(bp => new Action( bp.enabled ? 'disableColumnBreakpoint' : 'enableColumnBreakpoint', bp.enabled ? (bp.column ? nls.localize('disableInlineColumnBreakpoint', "Disable Inline Breakpoint on Column {0}", bp.column) : nls.localize('disableBreakpointOnLine', "Disable Line Breakpoint")) : (bp.column ? nls.localize('enableBreakpoints', "Enable Inline Breakpoint on Column {0}", bp.column) : nls.localize('enableBreakpointOnLine', "Enable Line Breakpoint")), @@ -443,7 +444,7 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi // Candidate decoration has a breakpoint attached when a breakpoint is already at that location and we did not yet set a decoration there // In practice this happens for the first breakpoint that was set on a line // We could have also rendered this first decoration as part of desiredBreakpointDecorations however at that moment we have no location information - const cssClass = candidate.breakpoint ? getBreakpointMessageAndClassName(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), candidate.breakpoint).className : 'codicon-debug-breakpoint-disabled'; + const cssClass = candidate.breakpoint ? getBreakpointMessageAndClassName(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), candidate.breakpoint, this.labelService).className : 'codicon-debug-breakpoint-disabled'; const contextMenuActions = () => this.getContextMenuActions(candidate.breakpoint ? [candidate.breakpoint] : [], activeCodeEditor.getModel().uri, candidate.range.startLineNumber, candidate.range.startColumn); const inlineWidget = new InlineBreakpointWidget(activeCodeEditor, decorationId, cssClass, candidate.breakpoint, this.debugService, this.contextMenuService, contextMenuActions); @@ -546,7 +547,7 @@ class InlineBreakpointWidget implements IContentWidget, IDisposable { private readonly breakpoint: IBreakpoint | undefined, private readonly debugService: IDebugService, private readonly contextMenuService: IContextMenuService, - private readonly getContextMenuActions: () => ReadonlyArray + private readonly getContextMenuActions: () => IAction[] ) { this.range = this.editor.getModel().getDecorationRange(decorationId); this.toDispose.push(this.editor.onDidChangeModelDecorations(() => { diff --git a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts index 745457c418f..30e9538d233 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts @@ -41,7 +41,7 @@ import { IEditorOptions, EditorOption } from 'vs/editor/common/config/editorOpti const $ = dom.$; const IPrivateBreakpointWidgetService = createDecorator('privateBreakpointWidgetService'); export interface IPrivateBreakpointWidgetService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; close(success: boolean): void; } const DECORATION_KEY = 'breakpointwidgetdecoration'; @@ -75,7 +75,7 @@ function createDecorations(theme: IColorTheme, placeHolder: string): IDecoration } export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWidgetService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private selectContainer!: HTMLElement; private inputContainer!: HTMLElement; @@ -250,7 +250,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi } return { - suggestions: suggestions.map(s => { + suggestions: suggestions.items.map(s => { s.completion.range = Range.fromPositions(position.delta(0, -overwriteBefore), position); return s.completion; }) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 8a691c2ccb1..e2138294a00 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import * as resources from 'vs/base/common/resources'; import * as dom from 'vs/base/browser/dom'; -import { IAction, Action } from 'vs/base/common/actions'; +import { IAction, Action, Separator } from 'vs/base/common/actions'; import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINTS_FOCUSED, State, DEBUG_SCHEME, IFunctionBreakpoint, IExceptionBreakpoint, IEnablement, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, IDebugModel, IDataBreakpoint } from 'vs/workbench/contrib/debug/common/debug'; import { ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; import { AddFunctionBreakpointAction, ToggleBreakpointsActivatedAction, RemoveAllBreakpointsAction, RemoveBreakpointAction, EnableAllBreakpointsAction, DisableAllBreakpointsAction, ReapplyBreakpointsAction } from 'vs/workbench/contrib/debug/browser/debugActions'; @@ -16,13 +16,12 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Constants } from 'vs/base/common/uint'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IListVirtualDelegate, IListContextMenuEvent, IListRenderer } from 'vs/base/browser/ui/list/list'; import { IEditorPane } from 'vs/workbench/common/editor'; import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { WorkbenchList } from 'vs/platform/list/browser/listService'; +import { WorkbenchList, ListResourceNavigator } from 'vs/platform/list/browser/listService'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -77,6 +76,7 @@ export class BreakpointsView extends ViewPane { @IContextKeyService contextKeyService: IContextKeyService, @IOpenerService openerService: IOpenerService, @ITelemetryService telemetryService: ITelemetryService, + @ILabelService private readonly labelService: ILabelService ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); @@ -95,12 +95,12 @@ export class BreakpointsView extends ViewPane { new ExceptionBreakpointsRenderer(this.debugService), this.instantiationService.createInstance(FunctionBreakpointsRenderer), this.instantiationService.createInstance(DataBreakpointsRenderer), - new FunctionBreakpointInputRenderer(this.debugService, this.contextViewService, this.themeService) + new FunctionBreakpointInputRenderer(this.debugService, this.contextViewService, this.themeService, this.labelService) ], { identityProvider: { getId: (element: IEnablement) => element.getId() }, multipleSelectionSupport: false, keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: (e: IEnablement) => e }, - accessibilityProvider: new BreakpointsAccessibilityProvider(this.debugService), + accessibilityProvider: new BreakpointsAccessibilityProvider(this.debugService, this.labelService), overrideStyles: { listBackground: this.getBackgroundColor() } @@ -110,38 +110,33 @@ export class BreakpointsView extends ViewPane { this._register(this.list.onContextMenu(this.onListContextMenu, this)); - this._register(this.list.onDidOpen(async e => { - let isSingleClick = false; - let isDoubleClick = false; - let isMiddleClick = false; - let openToSide = false; - - const browserEvent = e.browserEvent; - if (browserEvent instanceof MouseEvent) { - isSingleClick = browserEvent.detail === 1; - isDoubleClick = browserEvent.detail === 2; - isMiddleClick = browserEvent.button === 1; - openToSide = (browserEvent.ctrlKey || browserEvent.metaKey || browserEvent.altKey); + this.list.onMouseMiddleClick(async ({ element }) => { + if (element instanceof Breakpoint) { + await this.debugService.removeBreakpoints(element.getId()); + } else if (element instanceof FunctionBreakpoint) { + await this.debugService.removeFunctionBreakpoints(element.getId()); + } else if (element instanceof DataBreakpoint) { + await this.debugService.removeDataBreakpoints(element.getId()); } + }); - const focused = this.list.getFocusedElements(); - const element = focused.length ? focused[0] : undefined; - - if (isMiddleClick) { - if (element instanceof Breakpoint) { - await this.debugService.removeBreakpoints(element.getId()); - } else if (element instanceof FunctionBreakpoint) { - await this.debugService.removeFunctionBreakpoints(element.getId()); - } else if (element instanceof DataBreakpoint) { - await this.debugService.removeDataBreakpoints(element.getId()); - } + const resourceNavigator = this._register(new ListResourceNavigator(this.list, { configurationService: this.configurationService })); + this._register(resourceNavigator.onDidOpen(async e => { + if (e.element === null) { return; } - if (element instanceof Breakpoint) { - openBreakpointSource(element, openToSide, isSingleClick, this.debugService, this.editorService); + if (e.browserEvent instanceof MouseEvent && e.browserEvent.button === 1) { // middle click + return; } - if (isDoubleClick && element instanceof FunctionBreakpoint && element !== this.debugService.getViewModel().getSelectedFunctionBreakpoint()) { + + const element = this.list.element(e.element); + + if (element instanceof Breakpoint) { + openBreakpointSource(element, e.sideBySide, e.editorOptions.preserveFocus || false, this.debugService, this.editorService); + } + if (e.browserEvent instanceof MouseEvent && e.browserEvent.detail === 2 && element instanceof FunctionBreakpoint && element !== this.debugService.getViewModel().getSelectedFunctionBreakpoint()) { + // double click this.debugService.getViewModel().setSelectedFunctionBreakpoint(element); this.onBreakpointsChange(); } @@ -154,6 +149,11 @@ export class BreakpointsView extends ViewPane { this.onBreakpointsChange(); } })); + + const containerModel = this.viewDescriptorService.getViewContainerModel(this.viewDescriptorService.getViewContainerByViewId(this.id)!)!; + this._register(containerModel.onDidChangeAllViewDescriptors(() => { + this.updateSize(); + })); } public focus(): void { @@ -237,9 +237,11 @@ export class BreakpointsView extends ViewPane { } private updateSize(): void { + const containerModel = this.viewDescriptorService.getViewContainerModel(this.viewDescriptorService.getViewContainerByViewId(this.id)!)!; + // Adjust expanded body size this.minimumBodySize = this.orientation === Orientation.VERTICAL ? getExpandedBodySize(this.debugService.getModel(), MAX_VISIBLE_BREAKPOINTS) : 170; - this.maximumBodySize = this.orientation === Orientation.VERTICAL ? getExpandedBodySize(this.debugService.getModel(), Number.POSITIVE_INFINITY) : Number.POSITIVE_INFINITY; + this.maximumBodySize = this.orientation === Orientation.VERTICAL && containerModel.visibleViewDescriptors.length > 1 ? getExpandedBodySize(this.debugService.getModel(), Number.POSITIVE_INFINITY) : Number.POSITIVE_INFINITY; } private onBreakpointsChange(): void { @@ -377,7 +379,7 @@ class BreakpointsRenderer implements IListRenderer { constructor( - @IDebugService private readonly debugService: IDebugService + @IDebugService private readonly debugService: IDebugService, + @ILabelService private readonly labelService: ILabelService ) { // noop } @@ -472,7 +475,7 @@ class FunctionBreakpointsRenderer implements IListRenderer { constructor( - @IDebugService private readonly debugService: IDebugService + @IDebugService private readonly debugService: IDebugService, + @ILabelService private readonly labelService: ILabelService ) { // noop } @@ -527,7 +531,7 @@ class DataBreakpointsRenderer implements IListRenderer { - constructor(private readonly debugService: IDebugService) { } + constructor( + private readonly debugService: IDebugService, + private readonly labelService: ILabelService + ) { } getWidgetAriaLabel(): string { return nls.localize('breakpoints', "Breakpoints"); @@ -656,7 +664,7 @@ class BreakpointsAccessibilityProvider implements IListAccessibilityProvider 0 ? '...' : '') + stackFrame.source.uri.path.substr(from); } +async function expandTo(session: IDebugSession, tree: WorkbenchCompressibleAsyncDataTree): Promise { + if (session.parentSession) { + await expandTo(session.parentSession, tree); + } + await tree.expand(session); +} + export class CallStackView extends ViewPane { private pauseMessage!: HTMLSpanElement; private pauseMessageLabel!: HTMLSpanElement; @@ -109,9 +119,9 @@ export class CallStackView extends ViewPane { private ignoreFocusStackFrameEvent = false; private callStackItemType: IContextKey; private dataSource!: CallStackDataSource; - private tree!: WorkbenchAsyncDataTree; + private tree!: WorkbenchCompressibleAsyncDataTree; private menu: IMenu; - private parentSessionToExpand = new Set(); + private autoExpandedSessions = new Set(); private selectionNeedsUpdate = false; constructor( @@ -136,10 +146,14 @@ export class CallStackView extends ViewPane { this._register(this.menu); // Create scheduler to prevent unnecessary flashing of tree when reacting to changes - this.onCallStackChangeScheduler = new RunOnceScheduler(() => { + this.onCallStackChangeScheduler = new RunOnceScheduler(async () => { // Only show the global pause message if we do not display threads. // Otherwise there will be a pause message per thread and there is no need for a global one. const sessions = this.debugService.getModel().getSessions(); + if (sessions.length === 0) { + this.autoExpandedSessions.clear(); + } + const thread = sessions.length === 1 && sessions[0].getAllThreads().length === 1 ? sessions[0].getAllThreads()[0] : undefined; if (thread && thread.stoppedDetails) { this.pauseMessageLabel.textContent = thread.stoppedDetails.description || nls.localize('debugStopped', "Paused on {0}", thread.stoppedDetails.reason || ''); @@ -155,18 +169,26 @@ export class CallStackView extends ViewPane { this.needsRefresh = false; this.dataSource.deemphasizedStackFramesToShow = []; - this.tree.updateChildren().then(() => { - try { - this.parentSessionToExpand.forEach(s => this.tree.expand(s)); - } catch (e) { - // Ignore tree expand errors if element no longer present + await this.tree.updateChildren(); + try { + const toExpand = new Set(); + sessions.forEach(s => { + // Automatically expand sessions that have children, but only do this once. + if (s.parentSession && !this.autoExpandedSessions.has(s.parentSession)) { + toExpand.add(s.parentSession); + } + }); + for (let session of toExpand) { + await expandTo(session, this.tree); + this.autoExpandedSessions.add(session); } - this.parentSessionToExpand.clear(); - if (this.selectionNeedsUpdate) { - this.selectionNeedsUpdate = false; - this.updateTreeSelection(); - } - }); + } catch (e) { + // Ignore tree expand errors if element no longer present + } + if (this.selectionNeedsUpdate) { + this.selectionNeedsUpdate = false; + await this.updateTreeSelection(); + } }, 50); } @@ -195,7 +217,7 @@ export class CallStackView extends ViewPane { this.dataSource = new CallStackDataSource(this.debugService); const sessionsRenderer = this.instantiationService.createInstance(SessionsRenderer, this.menu); - this.tree = >this.instantiationService.createInstance(WorkbenchAsyncDataTree, 'CallStackView', treeContainer, new CallStackDelegate(), [ + this.tree = >this.instantiationService.createInstance(WorkbenchCompressibleAsyncDataTree, 'CallStackView', treeContainer, new CallStackDelegate(), new CallStackCompressionDelegate(this.debugService), [ sessionsRenderer, new ThreadsRenderer(this.instantiationService), this.instantiationService.createInstance(StackFramesRenderer), @@ -204,6 +226,8 @@ export class CallStackView extends ViewPane { new ShowMoreRenderer(this.themeService) ], this.dataSource, { accessibilityProvider: new CallStackAccessibilityProvider(), + compressionEnabled: true, + autoExpandSingleChildren: true, identityProvider: { getId: (element: CallStackItem) => { if (typeof element === 'string') { @@ -232,6 +256,13 @@ export class CallStackView extends ViewPane { } return nls.localize('showMoreStackFrames2', "Show More Stack Frames"); + }, + getCompressedNodeKeyboardNavigationLabel: (e: CallStackItem[]) => { + const firstItem = e[0]; + if (isDebugSession(firstItem)) { + return firstItem.getLabel(); + } + return ''; } }, expandOnlyOnTwistieClick: true, @@ -242,9 +273,7 @@ export class CallStackView extends ViewPane { this.tree.setInput(this.debugService.getModel()); - const callstackNavigator = new TreeResourceNavigator(this.tree); - this._register(callstackNavigator); - this._register(callstackNavigator.onDidOpenResource(e => { + this._register(this.tree.onDidOpen(e => { if (this.ignoreSelectionChangedEvent) { return; } @@ -294,7 +323,7 @@ export class CallStackView extends ViewPane { } })); const onFocusChange = Event.any(this.debugService.getViewModel().onDidFocusStackFrame, this.debugService.getViewModel().onDidFocusSession); - this._register(onFocusChange(() => { + this._register(onFocusChange(async () => { if (this.ignoreFocusStackFrameEvent) { return; } @@ -307,7 +336,7 @@ export class CallStackView extends ViewPane { return; } - this.updateTreeSelection(); + await this.updateTreeSelection(); })); this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); @@ -323,10 +352,12 @@ export class CallStackView extends ViewPane { })); this._register(this.debugService.onDidNewSession(s => { - this._register(s.onDidChangeName(() => this.tree.rerender(s))); + const sessionListeners: IDisposable[] = []; + sessionListeners.push(s.onDidChangeName(() => this.tree.rerender(s))); + sessionListeners.push(s.onDidEndAdapter(() => dispose(sessionListeners))); if (s.parentSession) { - // Auto expand sessions that have sub sessions - this.parentSessionToExpand.add(s.parentSession); + // A session we already expanded has a new child session, allow to expand it again. + this.autoExpandedSessions.delete(s.parentSession); } })); } @@ -340,7 +371,7 @@ export class CallStackView extends ViewPane { this.tree.domFocus(); } - private updateTreeSelection(): void { + private async updateTreeSelection(): Promise { if (!this.tree || !this.tree.getInput()) { // Tree not initialized yet return; @@ -373,20 +404,18 @@ export class CallStackView extends ViewPane { updateSelectionAndReveal(session); } } else { - const expandPromises = [() => ignoreErrors(this.tree.expand(thread))]; - let s: IDebugSession | undefined = thread.session; - while (s) { - const sessionToExpand = s; - expandPromises.push(() => ignoreErrors(this.tree.expand(sessionToExpand))); - s = s.parentSession; - } + // Ignore errors from this expansions because we are not aware if we rendered the threads and sessions or we hide them to declutter the view + try { + await expandTo(thread.session, this.tree); + } catch (e) { } + try { + await this.tree.expand(thread); + } catch (e) { } - sequence(expandPromises.reverse()).then(() => { - const toReveal = stackFrame || session; - if (toReveal) { - updateSelectionAndReveal(toReveal); - } - }); + const toReveal = stackFrame || session; + if (toReveal) { + updateSelectionAndReveal(toReveal); + } } } @@ -420,7 +449,6 @@ export class CallStackView extends ViewPane { interface IThreadTemplateData { thread: HTMLElement; name: HTMLElement; - state: HTMLElement; stateLabel: HTMLSpanElement; label: HighlightedLabel; actionBar: ActionBar; @@ -429,7 +457,6 @@ interface IThreadTemplateData { interface ISessionTemplateData { session: HTMLElement; name: HTMLElement; - state: HTMLElement; stateLabel: HTMLSpanElement; label: HighlightedLabel; actionBar: ActionBar; @@ -454,16 +481,13 @@ interface IStackFrameTemplateData { actionBar: ActionBar; } -class SessionsRenderer implements ITreeRenderer { +class SessionsRenderer implements ICompressibleTreeRenderer { static readonly ID = 'session'; constructor( private menu: IMenu, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IDebugService private readonly debugService: IDebugService, - @IKeybindingService private readonly keybindingService: IKeybindingService, - @INotificationService private readonly notificationService: INotificationService, - @IContextMenuService private readonly contextMenuService: IContextMenuService + @IDebugService private readonly debugService: IDebugService ) { } get templateId(): string { @@ -474,31 +498,40 @@ class SessionsRenderer implements ITreeRenderer { if (action instanceof MenuItemAction) { - // We need the MenuEntryActionViewItem so the icon would get rendered - return new MenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService); + return this.instantiationService.createInstance(MenuEntryActionViewItem, action); + } else if (action instanceof SubmenuItemAction) { + return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action); } return undefined; } }); - return { session, name, state, stateLabel, label, actionBar, elementDisposable: [] }; + return { session, name, stateLabel, label, actionBar, elementDisposable: [] }; } renderElement(element: ITreeNode, _: number, data: ISessionTemplateData): void { - const session = element.element; + this.doRenderElement(element.element, createMatches(element.filterData), data); + } + + renderCompressedElements(node: ITreeNode, FuzzyScore>, index: number, templateData: ISessionTemplateData, height: number | undefined): void { + const lastElement = node.element.elements[node.element.elements.length - 1]; + const matches = createMatches(node.filterData); + this.doRenderElement(lastElement, matches, templateData); + } + + private doRenderElement(session: IDebugSession, matches: IMatch[], data: ISessionTemplateData): void { data.session.title = nls.localize({ key: 'session', comment: ['Session is a noun'] }, "Session"); - data.label.set(session.getLabel(), createMatches(element.filterData)); + data.label.set(session.getLabel(), matches); const thread = session.getAllThreads().find(t => t.stopped); const setActionBar = () => { - const actions = getActions(this.instantiationService, element.element); + const actions = getActions(this.instantiationService, session); const primary: IAction[] = actions; const secondary: IAction[] = []; @@ -533,7 +566,7 @@ class SessionsRenderer implements ITreeRenderer { +class ThreadsRenderer implements ICompressibleTreeRenderer { static readonly ID = 'thread'; constructor(private readonly instantiationService: IInstantiationService) { } @@ -545,12 +578,11 @@ class ThreadsRenderer implements ITreeRenderer, index: number, data: IThreadTemplateData): void { @@ -564,15 +596,22 @@ class ThreadsRenderer implements ITreeRenderer, FuzzyScore>, index: number, templateData: IThreadTemplateData, height: number | undefined): void { + throw new Error('Method not implemented.'); + } + disposeTemplate(templateData: IThreadTemplateData): void { templateData.actionBar.dispose(); } } -class StackFramesRenderer implements ITreeRenderer { +class StackFramesRenderer implements ICompressibleTreeRenderer { static readonly ID = 'stackFrame'; - constructor(@ILabelService private readonly labelService: ILabelService) { } + constructor( + @ILabelService private readonly labelService: ILabelService, + @INotificationService private readonly notificationService: INotificationService + ) { } get templateId(): string { return StackFramesRenderer.ID; @@ -617,19 +656,27 @@ class StackFramesRenderer implements ITreeRenderer { - return stackFrame.restart(); + const action = new Action('debug.callStack.restartFrame', nls.localize('restartFrame', "Restart Frame"), 'codicon-debug-restart-frame', true, async () => { + try { + await stackFrame.restart(); + } catch (e) { + this.notificationService.error(e); + } }); data.actionBar.push(action, { icon: true, label: false }); } } + renderCompressedElements(node: ITreeNode, FuzzyScore>, index: number, templateData: IStackFrameTemplateData, height: number | undefined): void { + throw new Error('Method not implemented.'); + } + disposeTemplate(templateData: IStackFrameTemplateData): void { templateData.actionBar.dispose(); } } -class ErrorsRenderer implements ITreeRenderer { +class ErrorsRenderer implements ICompressibleTreeRenderer { static readonly ID = 'error'; get templateId(): string { @@ -648,12 +695,16 @@ class ErrorsRenderer implements ITreeRenderer, FuzzyScore>, index: number, templateData: IErrorTemplateData, height: number | undefined): void { + throw new Error('Method not implemented.'); + } + disposeTemplate(templateData: IErrorTemplateData): void { // noop } } -class LoadMoreRenderer implements ITreeRenderer { +class LoadMoreRenderer implements ICompressibleTreeRenderer { static readonly ID = 'loadMore'; static readonly LABEL = nls.localize('loadMoreStackFrames', "Load More Stack Frames"); @@ -678,12 +729,16 @@ class LoadMoreRenderer implements ITreeRenderer, FuzzyScore>, index: number, templateData: ILabelTemplateData, height: number | undefined): void { + throw new Error('Method not implemented.'); + } + disposeTemplate(templateData: ILabelTemplateData): void { templateData.toDispose.dispose(); } } -class ShowMoreRenderer implements ITreeRenderer { +class ShowMoreRenderer implements ICompressibleTreeRenderer { static readonly ID = 'showMore'; constructor(private readonly themeService: IThemeService) { } @@ -713,6 +768,10 @@ class ShowMoreRenderer implements ITreeRenderer, FuzzyScore>, index: number, templateData: ILabelTemplateData, height: number | undefined): void { + throw new Error('Method not implemented.'); + } + disposeTemplate(templateData: ILabelTemplateData): void { templateData.toDispose.dispose(); } @@ -1037,3 +1096,24 @@ class ContinueAction extends Action { return this.commandService.executeCommand(CONTINUE_ID, undefined, getContext(this.thread)); } } + +class CallStackCompressionDelegate implements ITreeCompressionDelegate { + + constructor(private readonly debugService: IDebugService) { } + + isIncompressible(stat: CallStackItem): boolean { + if (isDebugSession(stat)) { + if (stat.compact) { + return false; + } + const sessions = this.debugService.getModel().getSessions(); + if (sessions.some(s => s.parentSession === stat && s.compact)) { + return false; + } + + return true; + } + + return true; + } +} diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index f89ac5a0857..abe68683a9c 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -6,6 +6,7 @@ import 'vs/css!./media/debug.contribution'; import 'vs/css!./media/debugHover'; import * as nls from 'vs/nls'; +import { Color } from 'vs/base/common/color'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -37,14 +38,14 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { LoadedScriptsView } from 'vs/workbench/contrib/debug/browser/loadedScriptsView'; -import { TOGGLE_LOG_POINT_ID, TOGGLE_CONDITIONAL_BREAKPOINT_ID, TOGGLE_BREAKPOINT_ID, RunToCursorAction } from 'vs/workbench/contrib/debug/browser/debugEditorActions'; +import { ADD_LOG_POINT_ID, TOGGLE_CONDITIONAL_BREAKPOINT_ID, TOGGLE_BREAKPOINT_ID, RunToCursorAction } from 'vs/workbench/contrib/debug/browser/debugEditorActions'; import { WatchExpressionsView } from 'vs/workbench/contrib/debug/browser/watchExpressionsView'; import { VariablesView } from 'vs/workbench/contrib/debug/browser/variablesView'; import { ClearReplAction, Repl } from 'vs/workbench/contrib/debug/browser/repl'; import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider'; import { WelcomeView } from 'vs/workbench/contrib/debug/browser/welcomeView'; import { ThemeIcon, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { registerColor, foreground, badgeBackground, badgeForeground, listDeemphasizedForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; +import { registerColor, foreground, badgeBackground, badgeForeground, listDeemphasizedForeground, contrastBorder, inputBorder, editorWarningForeground, errorForeground, editorInfoForeground } from 'vs/platform/theme/common/colorRegistry'; import { DebugViewPaneContainer, OpenDebugConsoleAction } from 'vs/workbench/contrib/debug/browser/debugViewlet'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { CallStackEditorContribution } from 'vs/workbench/contrib/debug/browser/callStackEditorContribution'; @@ -55,6 +56,7 @@ import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/pl import { StartDebugQuickAccessProvider } from 'vs/workbench/contrib/debug/browser/debugQuickAccess'; import { DebugProgressContribution } from 'vs/workbench/contrib/debug/browser/debugProgress'; import { DebugTitleContribution } from 'vs/workbench/contrib/debug/browser/debugTitle'; +import { Codicon } from 'vs/base/common/codicons'; class OpenDebugViewletAction extends ShowViewletAction { public static readonly ID = VIEWLET_ID; @@ -75,7 +77,7 @@ const viewContainer = Registry.as(ViewExtensions.ViewCo id: VIEWLET_ID, name: nls.localize('run', "Run"), ctorDescriptor: new SyncDescriptor(DebugViewPaneContainer), - icon: 'codicon-debug-alt-2', + icon: Codicon.debugAlt.classNames, alwaysUseContainerInfo: true, order: 2 }, ViewContainerLocation.Sidebar); @@ -92,21 +94,21 @@ const openPanelKb: IKeybindings = { const VIEW_CONTAINER: ViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ id: DEBUG_PANEL_ID, name: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'), - icon: 'codicon-debug-console', + icon: Codicon.debugConsole.classNames, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [DEBUG_PANEL_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), storageId: DEBUG_PANEL_ID, focusCommand: { id: OpenDebugConsoleAction.ID, keybindings: openPanelKb }, - order: 3, + order: 2, hideIfEmpty: true }, ViewContainerLocation.Panel); Registry.as(ViewExtensions.ViewsRegistry).registerViews([{ id: REPL_VIEW_ID, name: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'), - containerIcon: 'codicon-debug-console', + containerIcon: Codicon.debugConsole.classNames, canToggleVisibility: false, canMoveView: true, ctorDescriptor: new SyncDescriptor(Repl), @@ -114,12 +116,12 @@ Registry.as(ViewExtensions.ViewsRegistry).registerViews([{ // Register default debug views const viewsRegistry = Registry.as(ViewExtensions.ViewsRegistry); -viewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), containerIcon: 'codicon-debug-alt-2', ctorDescriptor: new SyncDescriptor(VariablesView), order: 10, weight: 40, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusVariablesView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); -viewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), containerIcon: 'codicon-debug-alt-2', ctorDescriptor: new SyncDescriptor(WatchExpressionsView), order: 20, weight: 10, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); -viewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), containerIcon: 'codicon-debug-alt-2', ctorDescriptor: new SyncDescriptor(CallStackView), order: 30, weight: 30, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); -viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), containerIcon: 'codicon-debug-alt-2', ctorDescriptor: new SyncDescriptor(BreakpointsView), order: 40, weight: 20, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' }, when: ContextKeyExpr.or(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); -viewsRegistry.registerViews([{ id: WelcomeView.ID, name: WelcomeView.LABEL, containerIcon: 'codicon-debug-alt-2', ctorDescriptor: new SyncDescriptor(WelcomeView), order: 1, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer); -viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), containerIcon: 'codicon-debug-alt-2', ctorDescriptor: new SyncDescriptor(LoadedScriptsView), order: 35, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); +viewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(VariablesView), order: 10, weight: 40, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusVariablesView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); +viewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(WatchExpressionsView), order: 20, weight: 10, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); +viewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(CallStackView), order: 30, weight: 30, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); +viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(BreakpointsView), order: 40, weight: 20, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' }, when: ContextKeyExpr.or(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); +viewsRegistry.registerViews([{ id: WelcomeView.ID, name: WelcomeView.LABEL, containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(WelcomeView), order: 1, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer); +viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(LoadedScriptsView), order: 35, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); registerCommands(); @@ -167,7 +169,8 @@ registerDebugCommandPaletteItem(DISCONNECT_ID, DISCONNECT_LABEL, CONTEXT_IN_DEBU registerDebugCommandPaletteItem(STOP_ID, STOP_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated()); registerDebugCommandPaletteItem(CONTINUE_ID, CONTINUE_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); registerDebugCommandPaletteItem(FOCUS_REPL_ID, nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugFocusConsole' }, 'Focus on Debug Console View')); -registerDebugCommandPaletteItem(JUMP_TO_CURSOR_ID, nls.localize('jumpToCursor', "Jump to Cursor"), ContextKeyExpr.and(CONTEXT_JUMP_TO_CURSOR_SUPPORTED)); +registerDebugCommandPaletteItem(JUMP_TO_CURSOR_ID, nls.localize('jumpToCursor', "Jump to Cursor"), CONTEXT_JUMP_TO_CURSOR_SUPPORTED); +registerDebugCommandPaletteItem(JUMP_TO_CURSOR_ID, nls.localize('SetNextStatement', "Set Next Statement"), CONTEXT_JUMP_TO_CURSOR_SUPPORTED); registerDebugCommandPaletteItem(RunToCursorAction.ID, RunToCursorAction.LABEL, ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped'))); registerDebugCommandPaletteItem(TOGGLE_INLINE_BREAKPOINT_ID, nls.localize('inlineBreakpoint', "Inline Breakpoint")); @@ -182,7 +185,7 @@ Registry.as(QuickAccessExtensions.Quickaccess).registerQui }); // register service -registerSingleton(IDebugService, service.DebugService); +registerSingleton(IDebugService, service.DebugService, true); // Register configuration const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); @@ -226,7 +229,7 @@ configurationRegistry.registerConfiguration({ }, 'debug.openDebug': { enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart', 'openOnDebugBreak'], - default: 'openOnSessionStart', + default: 'openOnFirstSessionStart', description: nls.localize('openDebug', "Controls when the debug view should open.") }, 'debug.showSubSessionsInToolBar': { @@ -512,7 +515,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, { MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, { group: '1_breakpoints', command: { - id: TOGGLE_LOG_POINT_ID, + id: ADD_LOG_POINT_ID, title: nls.localize({ key: 'miLogPoint', comment: ['&& denotes a mnemonic'] }, "&&Logpoint...") }, order: 4 @@ -599,12 +602,18 @@ const debugTokenExpressionBoolean = registerColor('debugTokenExpression.boolean' const debugTokenExpressionNumber = registerColor('debugTokenExpression.number', { dark: '#b5cea8', light: '#098658', hc: '#89d185' }, 'Foreground color for numbers in the debug views (ie. the Variables or Watch view).'); const debugTokenExpressionError = registerColor('debugTokenExpression.error', { dark: '#f48771', light: '#e51400', hc: '#f48771' }, 'Foreground color for expression errors in the debug views (ie. the Variables or Watch view) and for error logs shown in the debug console.'); -const debugViewExceptionLabelForeground = registerColor('debugView.exceptionLabelForeground', { dark: foreground, light: foreground, hc: foreground }, 'Foreground color for a label shown in the CALL STACK view when the debugger breaks on an exception.'); +const debugViewExceptionLabelForeground = registerColor('debugView.exceptionLabelForeground', { dark: foreground, light: '#FFF', hc: foreground }, 'Foreground color for a label shown in the CALL STACK view when the debugger breaks on an exception.'); const debugViewExceptionLabelBackground = registerColor('debugView.exceptionLabelBackground', { dark: '#6C2022', light: '#A31515', hc: '#6C2022' }, 'Background color for a label shown in the CALL STACK view when the debugger breaks on an exception.'); const debugViewStateLabelForeground = registerColor('debugView.stateLabelForeground', { dark: foreground, light: foreground, hc: foreground }, 'Foreground color for a label in the CALL STACK view showing the current session\'s or thread\'s state.'); const debugViewStateLabelBackground = registerColor('debugView.stateLabelBackground', { dark: '#88888844', light: '#88888844', hc: '#88888844' }, 'Background color for a label in the CALL STACK view showing the current session\'s or thread\'s state.'); const debugViewValueChangedHighlight = registerColor('debugView.valueChangedHighlight', { dark: '#569CD6', light: '#569CD6', hc: '#569CD6' }, 'Color used to highlight value changes in the debug views (ie. in the Variables view).'); +const debugConsoleInfoForeground = registerColor('debugConsole.infoForeground', { dark: editorInfoForeground, light: editorInfoForeground, hc: foreground }, 'Foreground color for info messages in debug REPL console.'); +const debugConsoleWarningForeground = registerColor('debugConsole.warningForeground', { dark: editorWarningForeground, light: editorWarningForeground, hc: '#008000' }, 'Foreground color for warning messages in debug REPL console.'); +const debugConsoleErrorForeground = registerColor('debugConsole.errorForeground', { dark: errorForeground, light: errorForeground, hc: errorForeground }, 'Foreground color for error messages in debug REPL console.'); +const debugConsoleSourceForeground = registerColor('debugConsole.sourceForeground', { dark: foreground, light: foreground, hc: foreground }, 'Foreground color for source filenames in debug REPL console.'); +const debugConsoleInputIconForeground = registerColor('debugConsoleInputIcon.foreground', { dark: foreground, light: foreground, hc: foreground }, 'Foreground color for debug console input marker icon.'); + registerThemingParticipant((theme, collector) => { // All these colours provide a default value so they will never be undefined, hence the `!` const badgeBackgroundColor = theme.getColor(badgeBackground)!; @@ -637,10 +646,10 @@ registerThemingParticipant((theme, collector) => { /* State "badge" displaying the active session's current state. * Only visible when there are more active debug sessions/threads running. */ - .debug-pane .debug-call-stack .thread > .state > .label, - .debug-pane .debug-call-stack .session > .state > .label, - .debug-pane .monaco-list-row.selected .thread > .state > .label, - .debug-pane .monaco-list-row.selected .session > .state > .label { + .debug-pane .debug-call-stack .thread > .state.label, + .debug-pane .debug-call-stack .session > .state.label, + .debug-pane .monaco-list-row.selected .thread > .state.label, + .debug-pane .monaco-list-row.selected .session > .state.label { background-color: ${debugViewStateLabelBackgroundColor}; color: ${debugViewStateLabelForegroundColor}; } @@ -714,4 +723,53 @@ registerThemingParticipant((theme, collector) => { color: ${tokenNumberColor}; } `); + + const debugConsoleInputBorderColor = theme.getColor(inputBorder) || Color.fromHex('#80808060'); + const debugConsoleInfoForegroundColor = theme.getColor(debugConsoleInfoForeground)!; + const debugConsoleWarningForegroundColor = theme.getColor(debugConsoleWarningForeground)!; + const debugConsoleErrorForegroundColor = theme.getColor(debugConsoleErrorForeground)!; + const debugConsoleSourceForegroundColor = theme.getColor(debugConsoleSourceForeground)!; + const debugConsoleInputIconForegroundColor = theme.getColor(debugConsoleInputIconForeground)!; + + collector.addRule(` + .repl .repl-input-wrapper { + border-top: 1px solid ${debugConsoleInputBorderColor}; + } + + .monaco-workbench .repl .repl-tree .output .expression .value.info { + color: ${debugConsoleInfoForegroundColor}; + } + + .monaco-workbench .repl .repl-tree .output .expression .value.warn { + color: ${debugConsoleWarningForegroundColor}; + } + + .monaco-workbench .repl .repl-tree .output .expression .value.error { + color: ${debugConsoleErrorForegroundColor}; + } + + .monaco-workbench .repl .repl-tree .output .expression .source { + color: ${debugConsoleSourceForegroundColor}; + } + + .monaco-workbench .repl .repl-tree .monaco-tl-contents .arrow { + color: ${debugConsoleInputIconForegroundColor}; + } + `); + + if (!theme.defines(debugConsoleInputIconForeground)) { + collector.addRule(` + .monaco-workbench.vs .repl .repl-tree .monaco-tl-contents .arrow { + opacity: 0.25; + } + + .monaco-workbench.vs-dark .repl .repl-tree .monaco-tl-contents .arrow { + opacity: 0.4; + } + + .monaco-workbench.hc-black .repl .repl-tree .monaco-tl-contents .arrow { + opacity: 1; + } + `); + } }); diff --git a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts index dc805770ad3..4b3c2a72a1c 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts @@ -4,12 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IAction, IActionRunner } from 'vs/base/common/actions'; +import { IAction, IActionRunner, IActionViewItem } from 'vs/base/common/actions'; import { KeyCode } from 'vs/base/common/keyCodes'; import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { SelectBox, ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; -import { SelectActionViewItem, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IDebugService, IDebugSession, IDebugConfiguration, IConfig, ILaunch } from 'vs/workbench/contrib/debug/common/debug'; @@ -20,7 +19,7 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ADD_CONFIGURATION_ID } from 'vs/workbench/contrib/debug/browser/debugCommands'; -import { StartAction } from 'vs/workbench/contrib/debug/browser/debugActions'; +import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; const $ = dom.$; @@ -32,7 +31,7 @@ export class StartDebugActionViewItem implements IActionViewItem { private container!: HTMLElement; private start!: HTMLElement; private selectBox: SelectBox; - private options: { label: string, handler?: (() => boolean) }[] = []; + private options: { label: string, handler: (() => Promise) }[] = []; private toDispose: IDisposable[]; private selected = 0; private providers: { label: string, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[] = []; @@ -101,9 +100,9 @@ export class StartDebugActionViewItem implements IActionViewItem { event.stopPropagation(); } })); - this.toDispose.push(this.selectBox.onDidSelect(e => { + this.toDispose.push(this.selectBox.onDidSelect(async e => { const target = this.options[e.index]; - const shouldBeSelected = target.handler ? target.handler() : false; + const shouldBeSelected = target.handler ? await target.handler() : false; if (shouldBeSelected) { this.selected = e.index; } else { @@ -126,7 +125,6 @@ export class StartDebugActionViewItem implements IActionViewItem { selectBoxContainer.style.borderLeft = colors.selectBorder ? `1px solid ${colors.selectBorder}` : ''; const selectBackgroundColor = colors.selectBackground ? `${colors.selectBackground}` : ''; this.container.style.backgroundColor = selectBackgroundColor; - this.start.style.backgroundColor = selectBackgroundColor; })); this.debugService.getConfigurationManager().getDynamicProviders().then(providers => { this.providers = providers; @@ -173,7 +171,7 @@ export class StartDebugActionViewItem implements IActionViewItem { if (lastGroup !== presentation?.group) { lastGroup = presentation?.group; if (this.options.length) { - this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: undefined }); + this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) }); disabledIdxs.push(this.options.length - 1); } } @@ -183,8 +181,7 @@ export class StartDebugActionViewItem implements IActionViewItem { const label = inWorkspace ? `${name} (${launch.name})` : name; this.options.push({ - label, handler: () => { - StartAction.GET_CONFIG_AND_LAUNCH = undefined; + label, handler: async () => { manager.selectConfiguration(launch, name); return true; } @@ -192,31 +189,39 @@ export class StartDebugActionViewItem implements IActionViewItem { }); if (this.options.length === 0) { - this.options.push({ label: nls.localize('noConfigurations', "No Configurations"), handler: () => false }); + this.options.push({ label: nls.localize('noConfigurations', "No Configurations"), handler: async () => false }); } else { - this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: undefined }); + this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) }); disabledIdxs.push(this.options.length - 1); } this.providers.forEach(p => { + if (p.label === manager.selectedConfiguration.name) { + this.selected = this.options.length; + } + this.options.push({ - label: `${p.label}...`, handler: () => { - StartAction.GET_CONFIG_AND_LAUNCH = p.pick; - return true; + label: `${p.label}...`, handler: async () => { + const picked = await p.pick(); + if (picked) { + manager.selectConfiguration(picked.launch, p.label, picked.config); + return true; + } + return false; } }); }); if (this.providers.length > 0) { - this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: undefined }); + this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) }); disabledIdxs.push(this.options.length - 1); } manager.getLaunches().filter(l => !l.hidden).forEach(l => { const label = inWorkspace ? nls.localize("addConfigTo", "Add Config ({0})...", l.name) : nls.localize('addConfiguration', "Add Configuration..."); this.options.push({ - label, handler: () => { - this.commandService.executeCommand(ADD_CONFIGURATION_ID, l.uri.toString()); + label, handler: async () => { + await this.commandService.executeCommand(ADD_CONFIGURATION_ID, l.uri.toString()); return false; } }); @@ -247,7 +252,9 @@ export class FocusSessionActionViewItem extends SelectActionViewItem { })); this._register(this.debugService.onDidNewSession(session => { - this._register(session.onDidChangeName(() => this.update())); + const sessionListeners: IDisposable[] = []; + sessionListeners.push(session.onDidChangeName(() => this.update())); + sessionListeners.push(session.onDidEndAdapter(() => dispose(sessionListeners))); this.update(); })); this.getSessions().forEach(session => { diff --git a/src/vs/workbench/contrib/debug/browser/debugActions.ts b/src/vs/workbench/contrib/debug/browser/debugActions.ts index a0ad5e6d2b8..afcae8afe40 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActions.ts @@ -7,12 +7,13 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IDebugService, State, IEnablement, IBreakpoint, IDebugSession, ILaunch, IConfig } from 'vs/workbench/contrib/debug/common/debug'; -import { Variable, Breakpoint, FunctionBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; +import { IDebugService, State, IEnablement, IBreakpoint, IDebugSession, ILaunch } from 'vs/workbench/contrib/debug/common/debug'; +import { Variable, Breakpoint, FunctionBreakpoint, Expression } from 'vs/workbench/contrib/debug/common/debugModel'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { deepClone } from 'vs/base/common/objects'; export abstract class AbstractDebugAction extends Action { @@ -82,7 +83,7 @@ export class ConfigureAction extends AbstractDebugAction { } async run(): Promise { - if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { + if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY || this.contextService.getWorkspace().folders.length === 0) { this.notificationService.info(nls.localize('noFolderDebugConfig', "Please first open a folder in order to do advanced debug configuration.")); return; } @@ -92,12 +93,15 @@ export class ConfigureAction extends AbstractDebugAction { if (configurationManager.selectedConfiguration.name) { launch = configurationManager.selectedConfiguration.launch; } else { - const launches = configurationManager.getLaunches().filter(l => !!l.workspace); + const launches = configurationManager.getLaunches().filter(l => !l.hidden); if (launches.length === 1) { launch = launches[0]; } else { const picks = launches.map(l => ({ label: l.name, launch: l })); - const picked = await this.quickInputService.pick<{ label: string, launch: ILaunch }>(picks, { activeItem: picks[0], placeHolder: nls.localize('selectWorkspaceFolder', "Select a workspace folder to create a launch.json file in") }); + const picked = await this.quickInputService.pick<{ label: string, launch: ILaunch }>(picks, { + activeItem: picks[0], + placeHolder: nls.localize({ key: 'selectWorkspaceFolder', comment: ['User picks a workspace folder or a workspace configuration file here. Workspace configuration files can contain settings and thus a launch.json configuration can be written into one.'] }, "Select a workspace folder to create a launch.json file in or add it to the workspace config file") + }); if (picked) { launch = picked.launch; } @@ -113,7 +117,6 @@ export class ConfigureAction extends AbstractDebugAction { export class StartAction extends AbstractDebugAction { static ID = 'workbench.action.debug.start'; static LABEL = nls.localize('startDebug', "Start Debugging"); - static GET_CONFIG_AND_LAUNCH: (() => Promise<{ config: IConfig, launch: ILaunch } | undefined>) | undefined; constructor(id: string, label: string, @IDebugService debugService: IDebugService, @@ -129,16 +132,9 @@ export class StartAction extends AbstractDebugAction { } async run(): Promise { - if (StartAction.GET_CONFIG_AND_LAUNCH) { - const picked = await StartAction.GET_CONFIG_AND_LAUNCH(); - if (picked) { - return this.debugService.startDebugging(picked.launch, picked.config, { noDebug: this.isNoDebug() }); - } - return Promise.resolve(false); - } else { - let { launch, name } = this.debugService.getConfigurationManager().selectedConfiguration; - return this.debugService.startDebugging(launch, name, { noDebug: this.isNoDebug() }); - } + let { launch, name, config } = this.debugService.getConfigurationManager().selectedConfiguration; + const clonedConfig = deepClone(config); + return this.debugService.startDebugging(launch, clonedConfig || name, { noDebug: this.isNoDebug() }); } protected isNoDebug(): boolean { @@ -151,7 +147,10 @@ export class StartAction extends AbstractDebugAction { if (debugService.state === State.Initializing) { return false; } - if ((sessions.length > 0) && !debugService.getConfigurationManager().selectedConfiguration.name) { + let { name, config } = debugService.getConfigurationManager().selectedConfiguration; + let nameToStart = name || config?.name; + + if (sessions.some(s => s.configuration.name === nameToStart)) { // There is already a debug session running and we do not have any launch configuration selected return false; } @@ -389,12 +388,12 @@ export class CopyValueAction extends Action { static readonly LABEL = nls.localize('copyValue', "Copy Value"); constructor( - id: string, label: string, private value: Variable | string, private context: string, + id: string, label: string, private value: Variable | Expression, private context: string, @IDebugService private readonly debugService: IDebugService, @IClipboardService private readonly clipboardService: IClipboardService ) { - super(id, label, 'debug-action copy-value'); - this._enabled = typeof this.value === 'string' || (this.value instanceof Variable && !!this.value.evaluateName); + super(id, label); + this._enabled = (this.value instanceof Expression) || (this.value instanceof Variable && !!this.value.evaluateName); } async run(): Promise { @@ -405,7 +404,7 @@ export class CopyValueAction extends Action { } const context = session.capabilities.supportsClipboardContext ? 'clipboard' : this.context; - const toEvaluate = typeof this.value === 'string' ? this.value : this.value.evaluateName || this.value.value; + const toEvaluate = this.value instanceof Variable ? (this.value.evaluateName || this.value.value) : this.value.name; try { const evaluation = await session.evaluate(toEvaluate, stackFrame.frameId, context); diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index 14385280e0b..e0ab9d8b528 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -4,10 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { dispose, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import * as strings from 'vs/base/common/strings'; import * as objects from 'vs/base/common/objects'; +import * as json from 'vs/base/common/json'; import { URI as uri } from 'vs/base/common/uri'; import * as resources from 'vs/base/common/resources'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; @@ -15,7 +16,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { IEditorPane } from 'vs/workbench/common/editor'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IFileService } from 'vs/platform/files/common/files'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -36,7 +37,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance import { withUndefinedAsNull } from 'vs/base/common/types'; import { sequence } from 'vs/base/common/async'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; -import { first } from 'vs/base/common/arrays'; +import { first, flatten } from 'vs/base/common/arrays'; import { getVisibleAndSorted } from 'vs/workbench/contrib/debug/common/debugUtils'; import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes'; @@ -46,12 +47,15 @@ jsonRegistry.registerSchema(launchSchemaId, launchSchema); const DEBUG_SELECTED_CONFIG_NAME_KEY = 'debug.selectedconfigname'; const DEBUG_SELECTED_ROOT = 'debug.selectedroot'; +interface IDynamicPickItem { label: string, launch: ILaunch, config: IConfig } + export class ConfigurationManager implements IConfigurationManager { private debuggers: Debugger[]; private breakpointModeIdsSet = new Set(); private launches!: ILaunch[]; private selectedName: string | undefined; private selectedLaunch: ILaunch | undefined; + private selectedConfig: IConfig | undefined; private toDispose: IDisposable[]; private readonly _onDidSelectConfigurationName = new Emitter(); private configProviders: IDebugConfigurationProvider[]; @@ -100,25 +104,25 @@ export class ConfigurationManager implements IConfigurationManager { } createDebugAdapter(session: IDebugSession): IDebugAdapter | undefined { - let dap = this.debugAdapterFactories.get(session.configuration.type); - if (dap) { - return dap.createDebugAdapter(session); + let factory = this.debugAdapterFactories.get(session.configuration.type); + if (factory) { + return factory.createDebugAdapter(session); } return undefined; } substituteVariables(debugType: string, folder: IWorkspaceFolder | undefined, config: IConfig): Promise { - let dap = this.debugAdapterFactories.get(debugType); - if (dap) { - return dap.substituteVariables(folder, config); + let factory = this.debugAdapterFactories.get(debugType); + if (factory) { + return factory.substituteVariables(folder, config); } return Promise.resolve(config); } runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments): Promise { - let tl = this.debugAdapterFactories.get(debugType); - if (tl) { - return tl.runInTerminal(args); + let factory = this.debugAdapterFactories.get(debugType); + if (factory) { + return factory.runInTerminal(args); } return Promise.resolve(void 0); } @@ -249,42 +253,98 @@ export class ConfigurationManager implements IConfigurationManager { async getDynamicProviders(): Promise<{ label: string, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[]> { const extensions = await this.extensionService.getExtensions(); const onDebugDynamicConfigurationsName = 'onDebugDynamicConfigurations'; - const debugDynamicExtensionsTypes = extensions.map(e => { - const activationEvent = e.activationEvents && e.activationEvents.find(e => e.includes(onDebugDynamicConfigurationsName)); - if (activationEvent) { - const type = activationEvent.substr(onDebugDynamicConfigurationsName.length); - return type || (e.contributes && e.contributes.debuggers && e.contributes.debuggers.length ? e.contributes.debuggers[0].type : undefined); + const debugDynamicExtensionsTypes = extensions.reduce((acc, e) => { + if (!e.activationEvents) { + return acc; } - return undefined; - }).filter(e => typeof e === 'string') as string[]; + const explicitTypes: string[] = []; + let hasGenericEvent = false; + for (const event of e.activationEvents) { + if (event === onDebugDynamicConfigurationsName) { + hasGenericEvent = true; + } else if (event.startsWith(`${onDebugDynamicConfigurationsName}:`)) { + explicitTypes.push(event.slice(onDebugDynamicConfigurationsName.length + 1)); + } + } + + if (explicitTypes.length) { + return acc.concat(explicitTypes); + } + + if (hasGenericEvent) { + const debuggerType = e.contributes?.debuggers?.[0].type; + return debuggerType ? acc.concat(debuggerType) : acc; + } + + return acc; + }, [] as string[]); return debugDynamicExtensionsTypes.map(type => { return { label: this.getDebuggerLabel(type)!, pick: async () => { + const disposables = new DisposableStore(); + const input = disposables.add(this.quickInputService.createQuickPick()); + input.busy = true; + input.placeholder = nls.localize('selectConfiguration', "Select Launch Configuration"); + input.show(); + + let chosenDidCancel = false; + const chosenPromise = new Promise(resolve => { + disposables.add(input.onDidAccept(() => resolve(input.activeItems[0]))); + disposables.add(input.onDidTriggerItemButton(async (context) => { + resolve(undefined); + const { launch, config } = context.item; + await launch.openConfigFile(false, config.type); + // Only Launch have a pin trigger button + await (launch as Launch).writeConfiguration(config); + this.selectConfiguration(launch, config.name); + })); + disposables.add(input.onDidHide(() => { chosenDidCancel = true; resolve(); })); + }); + await this.activateDebuggers(onDebugDynamicConfigurationsName, type); const token = new CancellationTokenSource(); - const picks: Promise<{ label: string, launch: ILaunch, config: IConfig }[]>[] = []; + const picks: Promise[] = []; const provider = this.configProviders.filter(p => p.type === type && p.triggerKind === DebugConfigurationProviderTriggerKind.Dynamic && p.provideDebugConfigurations)[0]; this.getLaunches().forEach(launch => { if (launch.workspace && provider) { picks.push(provider.provideDebugConfigurations!(launch.workspace.uri, token.token).then(configurations => configurations.map(config => ({ label: config.name, config, + buttons: [{ + iconClass: 'codicon-gear', + tooltip: nls.localize('editLaunchConfig', "Edit Debug Configuration in launch.json") + }], launch })))); } }); - const promiseOfPicks = Promise.all(picks).then(result => result.reduce((first, second) => first.concat(second), [])); - const result = await this.quickInputService.pick<{ label: string, launch: ILaunch, config: IConfig }>(promiseOfPicks, { placeHolder: nls.localize('selectConfiguration', "Select Debug Configuration") }); - if (!result) { - // User canceled quick input we should notify the provider to cancel computing configurations - token.cancel(); + const nestedPicks = await Promise.all(picks); + const items = flatten(nestedPicks); + + let chosen: IDynamicPickItem | undefined; + + // If there's exactly one item to choose from, pick it automatically + if (items.length === 1 && !chosenDidCancel) { + chosen = items[0]; + } else { + input.items = items; + input.busy = false; + chosen = await chosenPromise; } - return result; + disposables.dispose(); + + if (!chosen) { + // User canceled quick input we should notify the provider to cancel computing configurations + token.cancel(); + return; + } + + return chosen; } }; }); @@ -385,9 +445,9 @@ export class ConfigurationManager implements IConfigurationManager { private initLaunches(): void { this.launches = this.contextService.getWorkspace().folders.map(folder => this.instantiationService.createInstance(Launch, this, folder)); if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { - this.launches.push(this.instantiationService.createInstance(WorkspaceLaunch)); + this.launches.push(this.instantiationService.createInstance(WorkspaceLaunch, this)); } - this.launches.push(this.instantiationService.createInstance(UserLaunch)); + this.launches.push(this.instantiationService.createInstance(UserLaunch, this)); if (this.selectedLaunch && this.launches.indexOf(this.selectedLaunch) === -1) { this.selectConfiguration(undefined); @@ -419,10 +479,11 @@ export class ConfigurationManager implements IConfigurationManager { return this.launches.find(l => l.workspace && l.workspace.uri.toString() === workspaceUri.toString()); } - get selectedConfiguration(): { launch: ILaunch | undefined, name: string | undefined } { + get selectedConfiguration(): { launch: ILaunch | undefined, name: string | undefined, config: IConfig | undefined } { return { launch: this.selectedLaunch, - name: this.selectedName + name: this.selectedName, + config: this.selectedConfig }; } @@ -438,7 +499,7 @@ export class ConfigurationManager implements IConfigurationManager { return undefined; } - selectConfiguration(launch: ILaunch | undefined, name?: string): void { + selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig): void { if (typeof launch === 'undefined') { const rootUri = this.historyService.getLastActiveWorkspaceRoot(); launch = this.getLaunch(rootUri); @@ -457,16 +518,16 @@ export class ConfigurationManager implements IConfigurationManager { this.storageService.remove(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE); } const names = launch ? launch.getConfigurationNames() : []; - if (name && names.indexOf(name) >= 0) { + if ((name && names.indexOf(name) >= 0) || config) { this.setSelectedLaunchName(name); - } - if (!this.selectedName || names.indexOf(this.selectedName) === -1) { + } else if (!this.selectedName || names.indexOf(this.selectedName) === -1) { this.setSelectedLaunchName(names.length ? names[0] : undefined); } - const configuration = this.selectedLaunch && this.selectedName ? this.selectedLaunch.getConfiguration(this.selectedName) : undefined; - if (configuration) { - this.debugConfigurationTypeContext.set(configuration.type); + this.selectedConfig = config; + const configForType = this.selectedConfig || (this.selectedLaunch && this.selectedName ? this.selectedLaunch.getConfiguration(this.selectedName) : undefined); + if (configForType) { + this.debugConfigurationTypeContext.set(configForType.type); } else { this.debugConfigurationTypeContext.reset(); } @@ -565,6 +626,9 @@ export class ConfigurationManager implements IConfigurationManager { abstract class AbstractLaunch { protected abstract getConfig(): IGlobalConfig | undefined; + constructor(protected configurationManager: ConfigurationManager) { + } + getCompound(name: string): ICompound | undefined { const config = this.getConfig(); if (!config || !config.compounds) { @@ -605,6 +669,16 @@ abstract class AbstractLaunch { return config.configurations.find(config => config && config.name === name); } + async getInitialConfigurationContent(folderUri?: uri, type?: string, token?: CancellationToken): Promise { + let content = ''; + const adapter = await this.configurationManager.guessDebugger(type); + if (adapter) { + const initialConfigs = await this.configurationManager.provideDebugConfigurations(folderUri, adapter.type, token || CancellationToken.None); + content = await adapter.getInitialConfigurationContent(initialConfigs); + } + return content; + } + get hidden(): boolean { return false; } @@ -613,14 +687,14 @@ abstract class AbstractLaunch { class Launch extends AbstractLaunch implements ILaunch { constructor( - private configurationManager: ConfigurationManager, + configurationManager: ConfigurationManager, public workspace: IWorkspaceFolder, @IFileService private readonly fileService: IFileService, @ITextFileService private readonly textFileService: ITextFileService, @IEditorService private readonly editorService: IEditorService, @IConfigurationService private readonly configurationService: IConfigurationService ) { - super(); + super(configurationManager); } get uri(): uri { @@ -644,12 +718,7 @@ class Launch extends AbstractLaunch implements ILaunch { content = fileContent.value.toString(); } catch { // launch.json not found: create one by collecting launch configs from debugConfigProviders - const adapter = await this.configurationManager.guessDebugger(type); - - if (adapter) { - const initialConfigs = await this.configurationManager.provideDebugConfigurations(this.workspace.uri, adapter.type, token || CancellationToken.None); - content = await adapter.getInitialConfigurationContent(initialConfigs); - } + content = await this.getInitialConfigurationContent(this.workspace.uri, type, token); if (content) { created = true; // pin only if config file is created #8727 try { @@ -688,15 +757,25 @@ class Launch extends AbstractLaunch implements ILaunch { created }); } + + async writeConfiguration(configuration: IConfig): Promise { + const fullConfig = objects.deepClone(this.getConfig()!); + if (!fullConfig.configurations) { + fullConfig.configurations = []; + } + fullConfig.configurations.push(configuration); + await this.configurationService.updateValue('launch', fullConfig, { resource: this.workspace.uri }, ConfigurationTarget.WORKSPACE_FOLDER); + } } class WorkspaceLaunch extends AbstractLaunch implements ILaunch { constructor( + configurationManager: ConfigurationManager, @IEditorService private readonly editorService: IEditorService, @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService ) { - super(); + super(configurationManager); } get workspace(): undefined { @@ -715,7 +794,17 @@ class WorkspaceLaunch extends AbstractLaunch implements ILaunch { return this.configurationService.inspect('launch').workspaceValue; } - async openConfigFile(preserveFocus: boolean): Promise<{ editor: IEditorPane | null, created: boolean }> { + async openConfigFile(preserveFocus: boolean, type?: string, token?: CancellationToken): Promise<{ editor: IEditorPane | null, created: boolean }> { + let launchExistInFile = !!this.getConfig(); + if (!launchExistInFile) { + // Launch property in workspace config not found: create one by collecting launch configs from debugConfigProviders + let content = await this.getInitialConfigurationContent(undefined, type, token); + if (content) { + await this.configurationService.updateValue('launch', json.parse(content), ConfigurationTarget.WORKSPACE); + } else { + return { editor: null, created: false }; + } + } const editor = await this.editorService.openEditor({ resource: this.contextService.getWorkspace().configuration!, @@ -732,10 +821,11 @@ class WorkspaceLaunch extends AbstractLaunch implements ILaunch { class UserLaunch extends AbstractLaunch implements ILaunch { constructor( + configurationManager: ConfigurationManager, @IConfigurationService private readonly configurationService: IConfigurationService, @IPreferencesService private readonly preferencesService: IPreferencesService ) { - super(); + super(configurationManager); } get workspace(): undefined { diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index 2ca496d131c..c7249d02612 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -9,7 +9,7 @@ import { Range } from 'vs/editor/common/core/range'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ServicesAccessor, registerEditorAction, EditorAction, IActionOptions } from 'vs/editor/browser/editorExtensions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, VIEWLET_ID, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, REPL_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, VIEWLET_ID, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, REPL_VIEW_ID, CONTEXT_STEP_INTO_TARGETS_SUPPORTED } from 'vs/workbench/contrib/debug/common/debug'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -17,6 +17,9 @@ import { openBreakpointSource } from 'vs/workbench/contrib/debug/browser/breakpo import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { PanelFocusContext } from 'vs/workbench/common/panel'; import { IViewsService } from 'vs/workbench/common/views'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { Action } from 'vs/base/common/actions'; +import { getDomNodePagePosition } from 'vs/base/browser/dom'; export const TOGGLE_BREAKPOINT_ID = 'editor.debug.action.toggleBreakpoint'; class ToggleBreakpointAction extends EditorAction { @@ -78,12 +81,12 @@ class ConditionalBreakpointAction extends EditorAction { } } -export const TOGGLE_LOG_POINT_ID = 'editor.debug.action.toggleLogPoint'; +export const ADD_LOG_POINT_ID = 'editor.debug.action.addLogPoint'; class LogPointAction extends EditorAction { constructor() { super({ - id: TOGGLE_LOG_POINT_ID, + id: ADD_LOG_POINT_ID, label: nls.localize('logPointEditorAction', "Debug: Add Logpoint..."), alias: 'Debug: Add Logpoint...', precondition: undefined @@ -178,7 +181,7 @@ class SelectionToReplAction extends EditorAction { const text = editor.getModel().getValueInRange(editor.getSelection()); await session.addReplExpression(viewModel.focusedStackFrame!, text); - await viewsService.openView(REPL_VIEW_ID, true); + await viewsService.openView(REPL_VIEW_ID, false); } } @@ -241,6 +244,48 @@ class ShowDebugHoverAction extends EditorAction { } } +class StepIntoTargetsAction extends EditorAction { + + public static readonly ID = 'editor.debug.action.stepIntoTargets'; + public static readonly LABEL = nls.localize({ key: 'stepIntoTargets', comment: ['Step Into Targets lets the user step into an exact function he or she is interested in.'] }, "Step Into Targets..."); + + constructor() { + super({ + id: StepIntoTargetsAction.ID, + label: StepIntoTargetsAction.LABEL, + alias: 'Debug: Step Into Targets...', + precondition: ContextKeyExpr.and(CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), EditorContextKeys.editorTextFocus), + contextMenuOpts: { + group: 'debug', + order: 1.5 + } + }); + } + + async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { + const debugService = accessor.get(IDebugService); + const contextMenuService = accessor.get(IContextMenuService); + const session = debugService.getViewModel().focusedSession; + const frame = debugService.getViewModel().focusedStackFrame; + + if (session && frame && editor.hasModel() && editor.getModel().uri.toString() === frame.source.uri.toString()) { + const targets = await session.stepInTargets(frame.frameId); + editor.revealLineInCenterIfOutsideViewport(frame.range.startLineNumber); + const cursorCoords = editor.getScrolledVisiblePosition({ lineNumber: frame.range.startLineNumber, column: frame.range.startColumn }); + const editorCoords = getDomNodePagePosition(editor.getDomNode()); + const x = editorCoords.left + cursorCoords.left; + const y = editorCoords.top + cursorCoords.top + cursorCoords.height; + + contextMenuService.showContextMenu({ + getAnchor: () => ({ x, y }), + getActions: () => { + return targets.map(t => new Action(`stepIntoTarget:${t.id}`, t.label, undefined, true, () => session.stepIn(frame.thread.threadId, t.id))); + } + }); + } + } +} + class GoToBreakpointAction extends EditorAction { constructor(private isNext: boolean, opts: IActionOptions) { super(opts); @@ -307,6 +352,7 @@ registerEditorAction(ToggleBreakpointAction); registerEditorAction(ConditionalBreakpointAction); registerEditorAction(LogPointAction); registerEditorAction(RunToCursorAction); +registerEditorAction(StepIntoTargetsAction); registerEditorAction(SelectionToReplAction); registerEditorAction(SelectionToWatchExpressionsAction); registerEditorAction(ShowDebugHoverAction); diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index 754fc963e34..72a879b493e 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -341,7 +341,7 @@ class DebugHoverAccessibilityProvider implements IListAccessibilityProvider { @@ -34,9 +36,11 @@ export class DebugProgressContribution implements IWorkbenchContribution { }); }); - this.progressService.withProgress({ location: VIEWLET_ID }, () => promise); - const source = this.debugService.getConfigurationManager().getDebuggerLabel(session.configuration.type); - this.progressService.withProgress({ + if (viewsService.isViewContainerVisible(VIEWLET_ID)) { + progressService.withProgress({ location: VIEWLET_ID }, () => promise); + } + const source = debugService.getConfigurationManager().getDebuggerLabel(session.configuration.type); + progressService.withProgress({ location: ProgressLocation.Notification, title: progressStartEvent.body.title, cancellable: progressStartEvent.body.cancellable, @@ -72,9 +76,9 @@ export class DebugProgressContribution implements IWorkbenchContribution { }); } }; - this.toDispose.push(this.debugService.getViewModel().onDidFocusSession(listenOnProgress)); - listenOnProgress(this.debugService.getViewModel().focusedSession); - this.toDispose.push(this.debugService.onWillNewSession(session => { + this.toDispose.push(debugService.getViewModel().onDidFocusSession(listenOnProgress)); + listenOnProgress(debugService.getViewModel().focusedSession); + this.toDispose.push(debugService.onWillNewSession(session => { if (!progressListener) { listenOnProgress(session); } diff --git a/src/vs/workbench/contrib/debug/browser/debugQuickAccess.ts b/src/vs/workbench/contrib/debug/browser/debugQuickAccess.ts index e4c1c9427a2..cec5357ecc9 100644 --- a/src/vs/workbench/contrib/debug/browser/debugQuickAccess.ts +++ b/src/vs/workbench/contrib/debug/browser/debugQuickAccess.ts @@ -11,7 +11,6 @@ import { IDebugService } from 'vs/workbench/contrib/debug/common/debug'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { matchesFuzzy } from 'vs/base/common/filters'; -import { StartAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { withNullAsUndefined } from 'vs/base/common/types'; import { ADD_CONFIGURATION_ID } from 'vs/workbench/contrib/debug/browser/debugCommands'; @@ -65,13 +64,11 @@ export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider { - if (StartAction.isEnabled(this.debugService)) { - this.debugService.getConfigurationManager().selectConfiguration(config.launch, config.name); - try { - await this.debugService.startDebugging(config.launch); - } catch (error) { - this.notificationService.error(error); - } + this.debugService.getConfigurationManager().selectConfiguration(config.launch, config.name); + try { + await this.debugService.startDebugging(config.launch); + } catch (error) { + this.notificationService.error(error); } } }); @@ -81,13 +78,18 @@ export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider 0) { - picks.push({ type: 'separator', label: localize('contributed', "contributed") }); + picks.push({ + type: 'separator', label: localize({ + key: 'contributed', + comment: ['contributed is lower case because it looks better like that in UI. Nothing preceeds it. It is a name of the grouping of debug configurations.'] + }, "contributed") + }); } dynamicProviders.forEach(provider => { picks.push({ label: `$(folder) ${provider.label}...`, - ariaLabel: localize('providerAriaLabel', "{0} contributed configurations", provider.label), + ariaLabel: localize({ key: 'providerAriaLabel', comment: ['Placeholder stands for the provider label. For example "NodeJS".'] }, "{0} contributed configurations", provider.label), accept: async () => { const pick = await provider.pick(); if (pick) { diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 203c7f85a30..7bba378be0c 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -21,7 +21,6 @@ import * as debugactions from 'vs/workbench/contrib/debug/browser/debugActions'; import { ConfigurationManager } from 'vs/workbench/contrib/debug/browser/debugConfigurationManager'; import { VIEWLET_ID as EXPLORER_VIEWLET_ID } from 'vs/workbench/contrib/files/common/files'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; @@ -33,7 +32,7 @@ import { IAction } from 'vs/base/common/actions'; import { deepClone, equals } from 'vs/base/common/objects'; import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, DEBUG_PANEL_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IStackFrame, getStateLabel, IDebugSessionOptions, CONTEXT_DEBUG_UX, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, IGlobalConfig } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IStackFrame, getStateLabel, IDebugSessionOptions, CONTEXT_DEBUG_UX, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, IGlobalConfig } from 'vs/workbench/contrib/debug/common/debug'; import { getExtensionHostDebugSession } from 'vs/workbench/contrib/debug/common/debugUtils'; import { isErrorWithActions } from 'vs/base/common/errorsWithActions'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -46,9 +45,10 @@ import { IViewsService } from 'vs/workbench/common/views'; import { generateUuid } from 'vs/base/common/uuid'; import { DebugStorage } from 'vs/workbench/contrib/debug/common/debugStorage'; import { DebugTelemetry } from 'vs/workbench/contrib/debug/common/debugTelemetry'; +import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompoundRoot'; export class DebugService implements IDebugService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _onDidChangeState: Emitter; private readonly _onDidNewSession: Emitter; @@ -61,11 +61,11 @@ export class DebugService implements IDebugService { private taskRunner: DebugTaskRunner; private configurationManager: ConfigurationManager; private toDispose: IDisposable[]; - private debugType: IContextKey; - private debugState: IContextKey; - private inDebugMode: IContextKey; - private debugUx: IContextKey; - private breakpointsExist: IContextKey; + private debugType!: IContextKey; + private debugState!: IContextKey; + private inDebugMode!: IContextKey; + private debugUx!: IContextKey; + private breakpointsExist!: IContextKey; private breakpointsToSendOnResourceSaved: Set; private initializing = false; private previousState: State | undefined; @@ -75,13 +75,12 @@ export class DebugService implements IDebugService { constructor( @IEditorService private readonly editorService: IEditorService, @IViewletService private readonly viewletService: IViewletService, - @IPanelService private readonly panelService: IPanelService, @IViewsService private readonly viewsService: IViewsService, @INotificationService private readonly notificationService: INotificationService, @IDialogService private readonly dialogService: IDialogService, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IContextKeyService contextKeyService: IContextKeyService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, @ILifecycleService private readonly lifecycleService: ILifecycleService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IExtensionService private readonly extensionService: IExtensionService, @@ -102,17 +101,19 @@ export class DebugService implements IDebugService { this.configurationManager = this.instantiationService.createInstance(ConfigurationManager); this.toDispose.push(this.configurationManager); - this.debugType = CONTEXT_DEBUG_TYPE.bindTo(contextKeyService); - this.debugState = CONTEXT_DEBUG_STATE.bindTo(contextKeyService); - this.inDebugMode = CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService); - this.debugUx = CONTEXT_DEBUG_UX.bindTo(contextKeyService); - this.debugUx.set(!!this.configurationManager.selectedConfiguration.name ? 'default' : 'simple'); + contextKeyService.bufferChangeEvents(() => { + this.debugType = CONTEXT_DEBUG_TYPE.bindTo(contextKeyService); + this.debugState = CONTEXT_DEBUG_STATE.bindTo(contextKeyService); + this.inDebugMode = CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService); + this.debugUx = CONTEXT_DEBUG_UX.bindTo(contextKeyService); + this.debugUx.set(!!this.configurationManager.selectedConfiguration.name ? 'default' : 'simple'); + this.breakpointsExist = CONTEXT_BREAKPOINTS_EXIST.bindTo(contextKeyService); + }); this.debugStorage = this.instantiationService.createInstance(DebugStorage); this.model = this.instantiationService.createInstance(DebugModel, this.debugStorage); this.telemetry = this.instantiationService.createInstance(DebugTelemetry, this.model); const setBreakpointsExistContext = () => this.breakpointsExist.set(!!(this.model.getBreakpoints().length || this.model.getDataBreakpoints().length || this.model.getFunctionBreakpoints().length)); - this.breakpointsExist = CONTEXT_BREAKPOINTS_EXIST.bindTo(contextKeyService); setBreakpointsExistContext(); this.viewModel = new ViewModel(contextKeyService); @@ -230,10 +231,12 @@ export class DebugService implements IDebugService { private onStateChange(): void { const state = this.state; if (this.previousState !== state) { - this.debugState.set(getStateLabel(state)); - this.inDebugMode.set(state !== State.Inactive); - // Only show the simple ux if debug is not yet started and if no launch.json exists - this.debugUx.set(((state !== State.Inactive && state !== State.Initializing) || this.configurationManager.selectedConfiguration.name) ? 'default' : 'simple'); + this.contextKeyService.bufferChangeEvents(() => { + this.debugState.set(getStateLabel(state)); + this.inDebugMode.set(state !== State.Inactive); + // Only show the simple ux if debug is not yet started and if no launch.json exists + this.debugUx.set(((state !== State.Inactive && state !== State.Initializing) || this.configurationManager.selectedConfiguration.name) ? 'default' : 'simple'); + }); this.previousState = state; this._onDidChangeState.fire(state); } @@ -267,7 +270,9 @@ export class DebugService implements IDebugService { try { // make sure to save all files and that the configuration is up to date await this.extensionService.activateByEvent('onDebug'); - await this.editorService.saveAll(); + if (!options?.parentSession) { + await this.editorService.saveAll(); + } await this.configurationService.reloadConfiguration(launch ? launch.workspace : undefined); await this.extensionService.whenInstalledExtensionsRegistered(); @@ -305,6 +310,9 @@ export class DebugService implements IDebugService { return false; } } + if (compound.stopAll) { + options = { ...options, compoundRoot: new DebugCompoundRoot() }; + } const values = await Promise.all(compound.configurations.map(configData => { const name = typeof configData === 'string' ? configData : configData.name; @@ -342,8 +350,8 @@ export class DebugService implements IDebugService { } if (configOrName && !config) { - const message = !!launch ? nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", typeof configOrName === 'string' ? configOrName : JSON.stringify(configOrName)) : - nls.localize('launchJsonDoesNotExist', "'launch.json' does not exist."); + const message = !!launch ? nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", typeof configOrName === 'string' ? configOrName : configOrName.name) : + nls.localize('launchJsonDoesNotExist', "'launch.json' does not exist for passed workspace folder."); throw new Error(message); } @@ -352,6 +360,7 @@ export class DebugService implements IDebugService { return result; } catch (err) { // make sure to get out of initializing state, and propagate the result + this.notificationService.error(err); this.endInitializingState(); return Promise.reject(err); } @@ -370,11 +379,12 @@ export class DebugService implements IDebugService { // a no-folder workspace has no launch.config config = Object.create(null); } - const unresolvedConfig = deepClone(config); - if (options && options.noDebug) { config!.noDebug = true; + } else if (options && typeof options.noDebug === 'undefined' && options.parentSession && options.parentSession.configuration.noDebug) { + config!.noDebug = true; } + const unresolvedConfig = deepClone(config); if (!type) { const guess = await this.configurationManager.guessDebugger(); @@ -553,8 +563,11 @@ export class DebugService implements IDebugService { this.toDispose.push(session.onDidEndAdapter(async adapterExitEvent => { - if (adapterExitEvent.error) { - this.notificationService.error(nls.localize('debugAdapterCrash', "Debug adapter process has terminated unexpectedly ({0})", adapterExitEvent.error.message || adapterExitEvent.error.toString())); + if (adapterExitEvent) { + if (adapterExitEvent.error) { + this.notificationService.error(nls.localize('debugAdapterCrash', "Debug adapter process has terminated unexpectedly ({0})", adapterExitEvent.error.message || adapterExitEvent.error.toString())); + } + this.telemetry.logDebugSessionStop(session, adapterExitEvent); } // 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905 @@ -563,8 +576,6 @@ export class DebugService implements IDebugService { this.extensionHostDebugService.close(extensionDebugSession.getId()); } - this.telemetry.logDebugSessionStop(session, adapterExitEvent); - if (session.configuration.postDebugTask) { try { await this.taskRunner.runTask(session.root, session.configuration.postDebugTask); @@ -593,8 +604,8 @@ export class DebugService implements IDebugService { const dataBreakpoints = this.model.getDataBreakpoints().filter(dbp => !dbp.canPersist); dataBreakpoints.forEach(dbp => this.model.removeDataBreakpoints(dbp.getId())); - if (this.panelService.getLastActivePanelId() === DEBUG_PANEL_ID && this.configurationService.getValue('debug').console.closeOnEnd) { - this.panelService.hideActivePanel(); + if (this.viewsService.isViewVisible(REPL_VIEW_ID) && this.configurationService.getValue('debug').console.closeOnEnd) { + this.viewsService.closeView(REPL_VIEW_ID); } } })); @@ -611,8 +622,15 @@ export class DebugService implements IDebugService { } const root = session.root || this.contextService.getWorkspace(); + await this.taskRunner.runTask(root, session.configuration.preRestartTask); await this.taskRunner.runTask(root, session.configuration.postDebugTask); - return this.taskRunner.runTaskAndCheckErrors(root, session.configuration.preLaunchTask, (msg, actions) => this.showError(msg, actions)); + + const taskResult1 = await this.taskRunner.runTaskAndCheckErrors(root, session.configuration.preLaunchTask, (msg, actions) => this.showError(msg, actions)); + if (taskResult1 !== TaskRunResult.Success) { + return taskResult1; + } + + return this.taskRunner.runTaskAndCheckErrors(root, session.configuration.postRestartTask, (msg, actions) => this.showError(msg, actions)); }; const extensionDebugSession = getExtensionHostDebugSession(session); @@ -696,7 +714,7 @@ export class DebugService implements IDebugService { }); } - stopSession(session: IDebugSession): Promise { + stopSession(session: IDebugSession | undefined): Promise { if (session) { return session.terminate(); } @@ -802,6 +820,7 @@ export class DebugService implements IDebugService { async enableOrDisableBreakpoints(enable: boolean, breakpoint?: IEnablement): Promise { if (breakpoint) { this.model.setEnablement(breakpoint, enable); + this.debugStorage.storeBreakpoints(this.model); if (breakpoint instanceof Breakpoint) { await this.sendBreakpoints(breakpoint.uri); } else if (breakpoint instanceof FunctionBreakpoint) { @@ -813,6 +832,7 @@ export class DebugService implements IDebugService { } } else { this.model.enableOrDisableAllBreakpoints(enable); + this.debugStorage.storeBreakpoints(this.model); await this.sendAllBreakpoints(); } this.debugStorage.storeBreakpoints(this.model); @@ -823,6 +843,9 @@ export class DebugService implements IDebugService { breakpoints.forEach(bp => aria.status(nls.localize('breakpointAdded', "Added breakpoint, line {0}, file {1}", bp.lineNumber, uri.fsPath))); breakpoints.forEach(bp => this.telemetry.logDebugAddBreakpoint(bp, context)); + // In some cases we need to store breakpoints before we send them because sending them can take a long time + // And after sending them because the debug adapter can attach adapter data to a breakpoint + this.debugStorage.storeBreakpoints(this.model); await this.sendBreakpoints(uri); this.debugStorage.storeBreakpoints(this.model); return breakpoints; @@ -830,12 +853,13 @@ export class DebugService implements IDebugService { async updateBreakpoints(uri: uri, data: Map, sendOnResourceSaved: boolean): Promise { this.model.updateBreakpoints(data); + this.debugStorage.storeBreakpoints(this.model); if (sendOnResourceSaved) { this.breakpointsToSendOnResourceSaved.add(uri.toString()); } else { await this.sendBreakpoints(uri); + this.debugStorage.storeBreakpoints(this.model); } - this.debugStorage.storeBreakpoints(this.model); } async removeBreakpoints(id?: string): Promise { @@ -845,8 +869,8 @@ export class DebugService implements IDebugService { this.model.removeBreakpoints(toRemove); - await Promise.all(urisToClear.map(uri => this.sendBreakpoints(uri))); this.debugStorage.storeBreakpoints(this.model); + await Promise.all(urisToClear.map(uri => this.sendBreakpoints(uri))); } setBreakpointsActivated(activated: boolean): Promise { @@ -861,27 +885,27 @@ export class DebugService implements IDebugService { async renameFunctionBreakpoint(id: string, newFunctionName: string): Promise { this.model.renameFunctionBreakpoint(id, newFunctionName); - await this.sendFunctionBreakpoints(); this.debugStorage.storeBreakpoints(this.model); + await this.sendFunctionBreakpoints(); } async removeFunctionBreakpoints(id?: string): Promise { this.model.removeFunctionBreakpoints(id); - await this.sendFunctionBreakpoints(); this.debugStorage.storeBreakpoints(this.model); + await this.sendFunctionBreakpoints(); } async addDataBreakpoint(label: string, dataId: string, canPersist: boolean, accessTypes: DebugProtocol.DataBreakpointAccessType[] | undefined): Promise { this.model.addDataBreakpoint(label, dataId, canPersist, accessTypes); + this.debugStorage.storeBreakpoints(this.model); await this.sendDataBreakpoints(); - this.debugStorage.storeBreakpoints(this.model); } async removeDataBreakpoints(id?: string): Promise { this.model.removeDataBreakpoints(id); - await this.sendDataBreakpoints(); this.debugStorage.storeBreakpoints(this.model); + await this.sendDataBreakpoints(); } async sendAllBreakpoints(session?: IDebugSession): Promise { @@ -894,7 +918,6 @@ export class DebugService implements IDebugService { private async sendBreakpoints(modelUri: uri, sourceModified = false, session?: IDebugSession): Promise { const breakpointsToSend = this.model.getBreakpoints({ uri: modelUri, enabledOnly: true }); - await sendToOneOrAllSessions(this.model, session, s => s.sendBreakpoints(modelUri, breakpointsToSend, sourceModified)); } @@ -908,10 +931,10 @@ export class DebugService implements IDebugService { }); } - private sendDataBreakpoints(session?: IDebugSession): Promise { + private async sendDataBreakpoints(session?: IDebugSession): Promise { const breakpointsToSend = this.model.getDataBreakpoints().filter(fbp => fbp.enabled && this.model.areBreakpointsActivated()); - return sendToOneOrAllSessions(this.model, session, async s => { + await sendToOneOrAllSessions(this.model, session, async s => { if (s.capabilities.supportsDataBreakpoints) { await s.sendDataBreakpoints(breakpointsToSend); } diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 325f1e7f4f7..cd67a54853f 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -35,6 +35,8 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { localize } from 'vs/nls'; import { canceled } from 'vs/base/common/errors'; +import { filterExceptionsFromTelemetry } from 'vs/workbench/contrib/debug/common/debugUtils'; +import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompoundRoot'; export class DebugSession implements IDebugSession { @@ -51,7 +53,7 @@ export class DebugSession implements IDebugSession { private repl: ReplModel; private readonly _onDidChangeState = new Emitter(); - private readonly _onDidEndAdapter = new Emitter(); + private readonly _onDidEndAdapter = new Emitter(); private readonly _onDidLoadedSource = new Emitter(); private readonly _onDidCustomEvent = new Emitter(); @@ -97,6 +99,11 @@ export class DebugSession implements IDebugSession { dispose(toDispose); })); } + + const compoundRoot = this._options.compoundRoot; + if (compoundRoot) { + toDispose.push(compoundRoot.onDidSessionStop(() => this.terminate())); + } } getId(): string { @@ -123,6 +130,14 @@ export class DebugSession implements IDebugSession { return this._options.parentSession; } + get compact(): boolean { + return !!this._options.compact; + } + + get compoundRoot(): DebugCompoundRoot | undefined { + return this._options.compoundRoot; + } + setConfiguration(configuration: { resolved: IConfig, unresolved: IConfig | undefined }) { this._configuration = configuration; } @@ -166,7 +181,7 @@ export class DebugSession implements IDebugSession { return this._onDidChangeState.event; } - get onDidEndAdapter(): Event { + get onDidEndAdapter(): Event { return this._onDidEndAdapter.event; } @@ -270,14 +285,21 @@ export class DebugSession implements IDebugSession { */ async terminate(restart = false): Promise { if (!this.raw) { - throw new Error(localize('noDebugAdapter', "No debug adapter, can not send '{0}'", 'terminate')); + // Adapter went down but it did not send a 'terminated' event, simulate like the event has been sent + this.onDidExitAdapter(); } this.cancelAllRequests(); - if (this.raw.capabilities.supportsTerminateRequest && this._configuration.resolved.request === 'launch') { - await this.raw.terminate(restart); - } else { - await this.raw.disconnect(restart); + if (this.raw) { + if (this.raw.capabilities.supportsTerminateRequest && this._configuration.resolved.request === 'launch') { + await this.raw.terminate(restart); + } else { + await this.raw.disconnect(restart); + } + } + + if (!restart) { + this._options.compoundRoot?.sessionStopped(); } } @@ -286,11 +308,18 @@ export class DebugSession implements IDebugSession { */ async disconnect(restart = false): Promise { if (!this.raw) { - throw new Error(localize('noDebugAdapter', "No debug adapter, can not send '{0}'", 'disconnect')); + // Adapter went down but it did not send a 'terminated' event, simulate like the event has been sent + this.onDidExitAdapter(); } this.cancelAllRequests(); - await this.raw.disconnect(restart); + if (this.raw) { + await this.raw.disconnect(restart); + } + + if (!restart) { + this._options.compoundRoot?.sessionStopped(); + } } /** @@ -366,7 +395,7 @@ export class DebugSession implements IDebugSession { } } - async dataBreakpointInfo(name: string, variablesReference?: number): Promise<{ dataId: string | null, description: string, canPersist?: boolean }> { + async dataBreakpointInfo(name: string, variablesReference?: number): Promise<{ dataId: string | null, description: string, canPersist?: boolean } | undefined> { if (!this.raw) { throw new Error(localize('noDebugAdapter', "No debug adapter, can not send '{0}'", 'data breakpoints info')); } @@ -488,12 +517,12 @@ export class DebugSession implements IDebugSession { await this.raw.next({ threadId }); } - async stepIn(threadId: number): Promise { + async stepIn(threadId: number, targetId?: number): Promise { if (!this.raw) { throw new Error(localize('noDebugAdapter', "No debug adapter, can not send '{0}'", 'stepIn')); } - await this.raw.stepIn({ threadId }); + await this.raw.stepIn({ threadId, targetId }); } async stepOut(threadId: number): Promise { @@ -592,7 +621,7 @@ export class DebugSession implements IDebugSession { } const response = await this.raw.loadedSources({}); - if (response.body && response.body.sources) { + if (response && response.body && response.body.sources) { return response.body.sources.map(src => this.getSource(src)); } else { return []; @@ -612,6 +641,15 @@ export class DebugSession implements IDebugSession { }, token); } + async stepInTargets(frameId: number): Promise<{ id: number, label: string }[]> { + if (!this.raw) { + return Promise.reject(new Error(localize('noDebugAdapter', "No debug adapter, can not send '{0}'", 'stepInTargets'))); + } + + const response = await this.raw.stepInTargets({ frameId }); + return response.body.targets; + } + async cancel(progressId: string): Promise { if (!this.raw) { return Promise.reject(new Error(localize('noDebugAdapter', "No debug adapter, can not send '{0}'", 'cancel'))); @@ -848,7 +886,12 @@ export class DebugSession implements IDebugSession { // and the user opted in telemetry if (this.raw.customTelemetryService && this.telemetryService.isOptedIn) { // __GDPR__TODO__ We're sending events in the name of the debug extension and we can not ensure that those are declared correctly. - this.raw.customTelemetryService.publicLog(event.body.output, event.body.data); + let data = event.body.data; + if (!this.raw.customTelemetryService.sendErrorTelemetry && event.body.data) { + data = filterExceptionsFromTelemetry(event.body.data); + } + + this.raw.customTelemetryService.publicLog(event.body.output, data); } return; @@ -952,12 +995,14 @@ export class DebugSession implements IDebugSession { this._onDidProgressEnd.fire(event); })); - this.rawListeners.push(this.raw.onDidExitAdapter(event => { - this.initialized = true; - this.model.setBreakpointSessionData(this.getId(), this.capabilities, undefined); - this.shutdown(); - this._onDidEndAdapter.fire(event); - })); + this.rawListeners.push(this.raw.onDidExitAdapter(event => this.onDidExitAdapter(event))); + } + + private onDidExitAdapter(event?: AdapterEndEvent): void { + this.initialized = true; + this.model.setBreakpointSessionData(this.getId(), this.capabilities, undefined); + this.shutdown(); + this._onDidEndAdapter.fire(event); } // Disconnects and clears state. Session can be initialized again for a new connection. diff --git a/src/vs/workbench/contrib/debug/browser/debugStatus.ts b/src/vs/workbench/contrib/debug/browser/debugStatus.ts index e1736d44af7..a83612abdca 100644 --- a/src/vs/workbench/contrib/debug/browser/debugStatus.ts +++ b/src/vs/workbench/contrib/debug/browser/debugStatus.ts @@ -65,7 +65,7 @@ export class DebugStatusContribution implements IWorkbenchContribution { } return { - text: '$(play) ' + text, + text: '$(debug-alt-small) ' + text, ariaLabel: nls.localize('debugTarget', "Debug: {0}", text), tooltip: nls.localize('selectAndStartDebug', "Select and start debug configuration"), command: 'workbench.action.debug.selectandstart' diff --git a/src/vs/workbench/contrib/debug/browser/debugTaskRunner.ts b/src/vs/workbench/contrib/debug/browser/debugTaskRunner.ts index 7ea7a2c36fc..689328eb3f1 100644 --- a/src/vs/workbench/contrib/debug/browser/debugTaskRunner.ts +++ b/src/vs/workbench/contrib/debug/browser/debugTaskRunner.ts @@ -69,7 +69,7 @@ export class DebugTaskRunner { return TaskRunResult.Success; } if (onTaskErrors === 'showErrors') { - await this.viewsService.openView(Constants.MARKERS_VIEW_ID); + await this.viewsService.openView(Constants.MARKERS_VIEW_ID, true); return Promise.resolve(TaskRunResult.Failure); } if (onTaskErrors === 'abort') { @@ -106,7 +106,7 @@ export class DebugTaskRunner { return TaskRunResult.Success; } - await this.viewsService.openView(Constants.MARKERS_VIEW_ID); + await this.viewsService.openView(Constants.MARKERS_VIEW_ID, true); return Promise.resolve(TaskRunResult.Failure); } catch (err) { await onError(err.message, [this.taskService.configureAction()]); diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index f3577d18bd0..54c7593b579 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -9,8 +9,8 @@ import * as browser from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; import * as arrays from 'vs/base/common/arrays'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { IAction, IRunEvent, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; -import { ActionBar, ActionsOrientation, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IAction, IRunEvent, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification, Separator } from 'vs/base/common/actions'; +import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IDebugConfiguration, IDebugService, State } from 'vs/workbench/contrib/debug/common/debug'; @@ -21,13 +21,12 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { registerThemingParticipant, IThemeService, Themable } from 'vs/platform/theme/common/themeService'; import { registerColor, contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; import { localize } from 'vs/nls'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { createAndFillInActionBarActions, MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IMenu, IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { createAndFillInActionBarActions, MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenu, IMenuService, MenuId, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { FocusSessionAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -57,7 +56,6 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { @IStorageService private readonly storageService: IStorageService, @IConfigurationService private readonly configurationService: IConfigurationService, @IThemeService themeService: IThemeService, - @IKeybindingService private readonly keybindingService: IKeybindingService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IMenuService menuService: IMenuService, @IContextMenuService contextMenuService: IContextMenuService, @@ -80,9 +78,10 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { actionViewItemProvider: (action: IAction) => { if (action.id === FocusSessionAction.ID) { return this.instantiationService.createInstance(FocusSessionActionViewItem, action); - } - if (action instanceof MenuItemAction) { - return new MenuEntryActionViewItem(action, this.keybindingService, this.notificationService, contextMenuService); + } else if (action instanceof MenuItemAction) { + return this.instantiationService.createInstance(MenuEntryActionViewItem, action); + } else if (action instanceof SubmenuItemAction) { + return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action); } return undefined; @@ -364,7 +363,7 @@ registerThemingParticipant((theme, collector) => { const debugIconStopColor = theme.getColor(debugIconStopForeground); if (debugIconStopColor) { - collector.addRule(`.monaco-workbench .codicon-debug-stop { color: ${debugIconStopColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-debug-stop, .monaco-workbench .debug-view-content .codicon-record { color: ${debugIconStopColor} !important; }`); } const debugIconDisconnectColor = theme.getColor(debugIconDisconnectForeground); diff --git a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts index ca458d7b809..bbe7ac03099 100644 --- a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts @@ -5,9 +5,8 @@ import 'vs/css!./media/debugViewlet'; import * as nls from 'vs/nls'; -import { IAction } from 'vs/base/common/actions'; +import { IAction, IActionViewItem } from 'vs/base/common/actions'; import * as DOM from 'vs/base/browser/dom'; -import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IDebugService, VIEWLET_ID, State, BREAKPOINTS_VIEW_ID, IDebugConfiguration, CONTEXT_DEBUG_UX, CONTEXT_DEBUG_UX_KEY, REPL_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; import { StartAction, ConfigureAction, SelectAndStartAction, FocusSessionAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { StartDebugActionViewItem, FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems'; @@ -24,15 +23,14 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la import { memoize } from 'vs/base/common/decorators'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { DebugToolBar } from 'vs/workbench/contrib/debug/browser/debugToolBar'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ViewPane, ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; -import { IMenu, MenuId, IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { IMenu, MenuId, IMenuService, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { INotificationService } from 'vs/platform/notification/common/notification'; +import { MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IViewDescriptorService, IViewsService } from 'vs/workbench/common/views'; import { WelcomeView } from 'vs/workbench/contrib/debug/browser/welcomeView'; import { ToggleViewAction } from 'vs/workbench/browser/actions/layoutActions'; +import { RunOnceScheduler } from 'vs/base/common/async'; export class DebugViewPaneContainer extends ViewPaneContainer { @@ -42,6 +40,7 @@ export class DebugViewPaneContainer extends ViewPaneContainer { private paneListeners = new Map(); private debugToolBarMenu: IMenu | undefined; private disposeOnTitleUpdate: IDisposable | undefined; + private updateToolBarScheduler: RunOnceScheduler; constructor( @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, @@ -55,17 +54,24 @@ export class DebugViewPaneContainer extends ViewPaneContainer { @IContextMenuService contextMenuService: IContextMenuService, @IExtensionService extensionService: IExtensionService, @IConfigurationService configurationService: IConfigurationService, - @IKeybindingService private readonly keybindingService: IKeybindingService, @IContextViewService private readonly contextViewService: IContextViewService, @IMenuService private readonly menuService: IMenuService, @IContextKeyService private readonly contextKeyService: IContextKeyService, - @INotificationService private readonly notificationService: INotificationService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService ) { super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService); + this.updateToolBarScheduler = this._register(new RunOnceScheduler(() => { + if (this.configurationService.getValue('debug').toolBarLocation === 'docked') { + this.updateTitleArea(); + } + }, 20)); + + // When there are potential updates to the docked debug toolbar we need to update it this._register(this.debugService.onDidChangeState(state => this.onDebugServiceStateChange(state))); - this._register(this.debugService.onDidNewSession(() => this.updateToolBar())); + this._register(this.debugService.onDidNewSession(() => this.updateToolBarScheduler.schedule())); + this._register(this.debugService.getViewModel().onDidFocusSession(() => this.updateToolBarScheduler.schedule())); + this._register(this.contextKeyService.onDidChangeContext(e => { if (e.affectsSome(new Set([CONTEXT_DEBUG_UX_KEY]))) { this.updateTitleArea(); @@ -125,6 +131,7 @@ export class DebugViewPaneContainer extends ViewPaneContainer { if (!this.debugToolBarMenu) { this.debugToolBarMenu = this.menuService.createMenu(MenuId.DebugToolBar, this.contextKeyService); this._register(this.debugToolBarMenu); + this._register(this.debugToolBarMenu.onDidChange(() => this.updateToolBarScheduler.schedule())); } const { actions, disposable } = DebugToolBar.getActions(this.debugToolBarMenu, this.debugService, this.instantiationService); @@ -165,7 +172,9 @@ export class DebugViewPaneContainer extends ViewPaneContainer { return new FocusSessionActionViewItem(action, this.debugService, this.themeService, this.contextViewService, this.configurationService); } if (action instanceof MenuItemAction) { - return new MenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService); + return this.instantiationService.createInstance(MenuEntryActionViewItem, action); + } else if (action instanceof SubmenuItemAction) { + return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action); } return undefined; @@ -190,13 +199,7 @@ export class DebugViewPaneContainer extends ViewPaneContainer { }); } - this.updateToolBar(); - } - - private updateToolBar(): void { - if (this.configurationService.getValue('debug').toolBarLocation === 'docked') { - this.updateTitleArea(); - } + this.updateToolBarScheduler.schedule(); } addPanes(panes: { pane: ViewPane, size: number, index?: number }[]): void { diff --git a/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts b/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts index 5b0ab1a43f3..ee999b258b5 100644 --- a/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts +++ b/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts @@ -6,7 +6,7 @@ import { ExtensionHostDebugChannelClient, ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; +import { IExtensionHostDebugService, IOpenExtensionWindowResult } from 'vs/platform/debug/common/extensionHostDebug'; import { IDebugHelperService } from 'vs/workbench/contrib/debug/common/debug'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; @@ -62,7 +62,7 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient i })); } - openExtensionDevelopmentHostWindow(args: string[], env: IProcessEnvironment): Promise { + async openExtensionDevelopmentHostWindow(args: string[], env: IProcessEnvironment): Promise { // Find out which workspace to open debug window on let debugWorkspace: IWorkspace = undefined; @@ -104,11 +104,18 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient i environment.set('inspect-brk-extensions', inspectBrkExtensions); } + const inspectExtensions = this.findArgument('inspect-extensions', args); + if (inspectExtensions) { + environment.set('inspect-extensions', inspectExtensions); + } + // Open debug window as new window. Pass ParsedArgs over. - return this.workspaceProvider.open(debugWorkspace, { + await this.workspaceProvider.open(debugWorkspace, { reuse: false, // debugging always requires a new window payload: Array.from(environment.entries()) // mandatory properties to enable debugging }); + + return {}; } private findArgument(key: string, args: string[]): string | undefined { @@ -123,15 +130,15 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient i } } -registerSingleton(IExtensionHostDebugService, BrowserExtensionHostDebugService); +registerSingleton(IExtensionHostDebugService, BrowserExtensionHostDebugService, true); class BrowserDebugHelperService implements IDebugHelperService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; createTelemetryService(configurationService: IConfigurationService, args: string[]): TelemetryService | undefined { return undefined; } } -registerSingleton(IDebugHelperService, BrowserDebugHelperService); +registerSingleton(IDebugHelperService, BrowserDebugHelperService, true); diff --git a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts index 3a160385f6d..ef585e084aa 100644 --- a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts @@ -28,7 +28,7 @@ import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, ITreeElement } from 'vs/base/browser/ui/tree/tree'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { TreeResourceNavigator, WorkbenchCompressibleObjectTree } from 'vs/platform/list/browser/listService'; +import { WorkbenchCompressibleObjectTree } from 'vs/platform/list/browser/listService'; import { dispose } from 'vs/base/common/lifecycle'; import { createMatches, FuzzyScore } from 'vs/base/common/filters'; import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider'; @@ -58,6 +58,10 @@ class BaseTreeItem { this._showedMoreThanOne = false; } + updateLabel(label: string) { + this._label = label; + } + isLeaf(): boolean { return this._children.size === 0; } @@ -310,7 +314,7 @@ class SessionTreeItem extends BaseTreeItem { return 999; } - addPath(source: Source): void { + async addPath(source: Source): Promise { let folder: IWorkspaceFolder | null; let url: string; @@ -347,9 +351,8 @@ class SessionTreeItem extends BaseTreeItem { } else { // on unix try to tildify absolute paths path = normalize(path); - const userHome = this._pathService.resolvedUserHome; - if (userHome && !isWindows) { - path = tildify(path, userHome.fsPath); + if (!isWindows) { + path = tildify(path, (await this._pathService.userHome()).fsPath); } } } @@ -491,9 +494,7 @@ export class LoadedScriptsView extends ViewPane { }, 300); this._register(this.changeScheduler); - const loadedScriptsNavigator = new TreeResourceNavigator(this.tree); - this._register(loadedScriptsNavigator); - this._register(loadedScriptsNavigator.onDidOpenResource(e => { + this._register(this.tree.onDidOpen(e => { if (e.element instanceof BaseTreeItem) { const source = e.element.getSource(); if (source && source.available) { @@ -520,27 +521,30 @@ export class LoadedScriptsView extends ViewPane { } }; - const addSourcePathsToSession = (session: IDebugSession) => { + const addSourcePathsToSession = async (session: IDebugSession) => { const sessionNode = root.add(session); - return session.getLoadedSources().then(paths => { - paths.forEach(path => sessionNode.addPath(path)); - scheduleRefreshOnVisible(); - }); + const paths = await session.getLoadedSources(); + for (const path of paths) { + await sessionNode.addPath(path); + } + scheduleRefreshOnVisible(); }; const registerSessionListeners = (session: IDebugSession) => { - this._register(session.onDidChangeName(() => { - // Re-add session, this will trigger proper sorting and id recalculation. - root.remove(session.getId()); - addSourcePathsToSession(session); + this._register(session.onDidChangeName(async () => { + const sessionRoot = root.find(session); + if (sessionRoot) { + sessionRoot.updateLabel(session.getLabel()); + scheduleRefreshOnVisible(); + } })); - this._register(session.onDidLoadedSource(event => { + this._register(session.onDidLoadedSource(async event => { let sessionRoot: SessionTreeItem; switch (event.reason) { case 'new': case 'changed': sessionRoot = root.add(session); - sessionRoot.addPath(event.source); + await sessionRoot.addPath(event.source); scheduleRefreshOnVisible(); if (event.reason === 'changed') { DebugContentProvider.refreshDebugContent(event.source.uri); diff --git a/src/vs/workbench/contrib/debug/browser/media/debugHover.css b/src/vs/workbench/contrib/debug/browser/media/debugHover.css index d9a5672b41f..0ec21e70d7e 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugHover.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugHover.css @@ -12,6 +12,7 @@ user-select: text; -webkit-user-select: text; word-break: break-all; + white-space: pre; } .monaco-editor .debug-hover-widget .complex-value { diff --git a/src/vs/workbench/contrib/debug/browser/media/debugToolBar.css b/src/vs/workbench/contrib/debug/browser/media/debugToolBar.css index 70847c6647a..1889028b46b 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugToolBar.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugToolBar.css @@ -38,7 +38,7 @@ cursor: grabbing; } -.monaco-workbench .debug-toolbar .monaco-action-bar .action-item > .action-label { +.monaco-workbench .debug-toolbar .monaco-action-bar .action-item .action-label { width: 32px; height: 32px; margin-right: 0; diff --git a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css index 43dfc59aa30..b7e09375af9 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css @@ -51,6 +51,8 @@ margin-top: 0px; cursor: pointer; line-height: inherit; + padding-top: 0; + padding-bottom: 0; } .monaco-workbench .monaco-action-bar .start-debug-action-item .configuration.disabled .monaco-select-box { @@ -75,14 +77,18 @@ cursor: initial; } -/* Make icons the same color as the list foreground on focus selection */ +/* Make icons and text the same color as the list foreground on focus selection */ +.debug-pane .monaco-list:focus .monaco-list-row.selected .state.label, +.debug-pane .monaco-list:focus .monaco-list-row.selected.focused .state.label, +.debug-pane .monaco-list:focus .monaco-list-row.selected .codicon, .debug-pane .monaco-list:focus .monaco-list-row.selected.focused .codicon { color: inherit !important; } /* Call stack */ -.debug-pane .debug-call-stack-title { +.debug-pane.expanded .debug-call-stack-title, +.debug-pane.vertical .debug-call-stack-title { display: flex; width: 100%; } @@ -114,18 +120,17 @@ text-overflow: ellipsis; } -.debug-pane .debug-call-stack .thread > .state, -.debug-pane .debug-call-stack .session > .state { - display: flex; - align-items: center; - text-align: right; +.debug-pane .debug-call-stack .thread > .state.label, +.debug-pane .debug-call-stack .session > .state.label { overflow: hidden; text-overflow: ellipsis; - padding: 0 10px; + margin: 0 10px; text-transform: uppercase; + align-self: center; + font-size: 0.8em; } -.debug-pane .debug-call-stack .monaco-list-row:hover .state { +.debug-pane .debug-call-stack .monaco-list-row:hover .state.label { display: none; } @@ -136,6 +141,7 @@ .debug-pane .debug-call-stack .monaco-list-row .monaco-action-bar { display: none; flex-shrink: 0; + margin-right: 1px; } .debug-pane .debug-call-stack .monaco-list-row:hover .monaco-action-bar { @@ -157,12 +163,6 @@ background-repeat: no-repeat; } -.debug-pane .debug-call-stack .thread > .state > .label, -.debug-pane .debug-call-stack .session > .state > .label { - font-size: 0.8em; - min-height: auto; -} - .debug-pane .debug-call-stack .stack-frame { overflow: hidden; text-overflow: ellipsis; diff --git a/src/vs/workbench/contrib/debug/browser/media/repl.css b/src/vs/workbench/contrib/debug/browser/media/repl.css index f4ece53d9f0..1970134e9c3 100644 --- a/src/vs/workbench/contrib/debug/browser/media/repl.css +++ b/src/vs/workbench/contrib/debug/browser/media/repl.css @@ -5,19 +5,19 @@ /* Debug repl */ -.repl { +.monaco-workbench .repl { height: 100%; box-sizing: border-box; overflow: hidden; } -.repl .repl-tree .monaco-tl-contents { +.monaco-workbench .repl .repl-tree .monaco-tl-contents { user-select: text; -webkit-user-select: text; white-space: pre; } -.repl .repl-tree.word-wrap .monaco-tl-contents { +.monaco-workbench .repl .repl-tree.word-wrap .monaco-tl-contents { /* Wrap words but also do not trim whitespace #6275 */ word-wrap: break-word; white-space: pre-wrap; @@ -30,25 +30,20 @@ cursor: pointer; } -.repl .repl-tree .output.expression.value-and-source { +.monaco-workbench .repl .repl-tree .output.expression.value-and-source { display: flex; } -.repl .repl-tree .output.expression.value-and-source .value { +.monaco-workbench .repl .repl-tree .output.expression.value-and-source .value { flex: 1; } -.repl .repl-tree .monaco-tl-contents .arrow { +.monaco-workbench .repl .repl-tree .monaco-tl-contents .arrow { position:absolute; left: 2px; - opacity: 0.25; } -.vs-dark .repl .repl-tree .monaco-tl-contents .arrow { - opacity: 0.4; -} - -.repl .repl-tree .output.expression.value-and-source .source { +.monaco-workbench .repl .repl-tree .output.expression.value-and-source .source { margin-left: 4px; margin-right: 8px; cursor: pointer; @@ -59,36 +54,21 @@ max-width: 150px; } -.repl .repl-tree .monaco-list-row { - cursor: text; -} - -.repl .repl-tree .output.expression > .value, -.repl .repl-tree .evaluation-result.expression > .value { +.monaco-workbench .repl .repl-tree .output.expression > .value, +.monaco-workbench .repl .repl-tree .evaluation-result.expression > .value { margin-left: 0px; } -.repl .repl-tree .output.expression > .annotation, -.repl .repl-tree .evaluation-result.expression > .annotation { - font-size: inherit; - padding-left: 6px; -} - -.repl .repl-tree .output.expression .name:not(:empty) { +.monaco-workbench .repl .repl-tree .output.expression .name:not(:empty) { margin-right: 6px; } -.repl .repl-input-wrapper { +.monaco-workbench .repl .repl-input-wrapper { display: flex; align-items: center; } -/* Only show 'stale expansion' info when the element gets expanded. */ -.repl .repl-tree .evaluation-result > .annotation::before { - content: ''; -} - -.repl .repl-input-wrapper .repl-input-chevron { +.monaco-workbench .repl .repl-input-wrapper .repl-input-chevron { padding: 0 6px 0 8px; width: 16px; height: 100%; @@ -99,34 +79,10 @@ } /* Output coloring and styling */ -.repl .repl-tree .output.expression > .ignore { +.monaco-workbench .repl .repl-tree .output.expression > .ignore { font-style: italic; } -.vs .repl .repl-tree .output.expression > .annotation { - color: #007ACC; -} - -.vs-dark .repl .repl-tree .output.expression > .annotation { - color: #1B80B2; -} - -.hc-black .repl .repl-tree .output.expression > .annotation { - color: #0000FF; -} - -.vs .repl .repl-tree .output.expression > .warn { - color: #cd9731; -} - -.vs-dark .repl .repl-tree .output.expression > .warn { - color: #cd9731; -} - -.hc-black .repl .repl-tree .output.expression > .warn { - color: #008000; -} - /* ANSI Codes */ .monaco-workbench .repl .repl-tree .output.expression .code-bold { font-weight: bold; } .monaco-workbench .repl .repl-tree .output.expression .code-italic { font-style: italic; } diff --git a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts index 152e48fd1ff..a400dde3bd5 100644 --- a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts @@ -12,7 +12,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { formatPII, isUri } from 'vs/workbench/contrib/debug/common/debugUtils'; import { IDebugAdapter, IConfig, AdapterEndEvent, IDebugger } from 'vs/workbench/contrib/debug/common/debug'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; -import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; +import { IExtensionHostDebugService, IOpenExtensionWindowResult } from 'vs/platform/debug/common/extensionHostDebug'; import { URI } from 'vs/base/common/uri'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { env as processEnv } from 'vs/base/common/process'; @@ -33,6 +33,7 @@ interface ILaunchVSCodeArgument { interface ILaunchVSCodeArguments { args: ILaunchVSCodeArgument[]; + debugRenderer?: boolean; env?: { [key: string]: string | null; }; } @@ -349,6 +350,13 @@ export class RawDebugSession implements IDisposable { return Promise.reject(new Error('restartFrame not supported')); } + stepInTargets(args: DebugProtocol.StepInTargetsArguments): Promise { + if (this.capabilities.supportsStepInTargetsRequest) { + return this.send('stepInTargets', args); + } + return Promise.reject(new Error('stepInTargets not supported')); + } + completions(args: DebugProtocol.CompletionsArguments, token: CancellationToken): Promise { if (this.capabilities.supportsCompletionsRequest) { return this.send('completions', args, token); @@ -543,8 +551,9 @@ export class RawDebugSession implements IDisposable { switch (request.command) { case 'launchVSCode': - this.launchVsCode(request.arguments).then(_ => { + this.launchVsCode(request.arguments).then(result => { response.body = { + rendererDebugPort: result.rendererDebugPort, //processId: pid }; safeSendResponse(response); @@ -577,7 +586,7 @@ export class RawDebugSession implements IDisposable { } } - private launchVsCode(vscodeArgs: ILaunchVSCodeArguments): Promise { + private launchVsCode(vscodeArgs: ILaunchVSCodeArguments): Promise { const args: string[] = []; @@ -605,7 +614,7 @@ export class RawDebugSession implements IDisposable { Object.keys(env).filter(k => env[k] === null).forEach(key => delete env[key]); } - return this.extensionHostDebugService.openExtensionDevelopmentHostWindow(args, env); + return this.extensionHostDebugService.openExtensionDevelopmentHostWindow(args, env, !!vscodeArgs.debugRenderer); } private send(command: string, args: any, token?: CancellationToken, timeout?: number): Promise { diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 16bdbd92f9c..6afa8ef0e4f 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -5,8 +5,7 @@ import 'vs/css!./media/repl'; import { URI as uri } from 'vs/base/common/uri'; -import { Color } from 'vs/base/common/color'; -import { IAction, IActionViewItem, Action } from 'vs/base/common/actions'; +import { IAction, IActionViewItem, Action, Separator } from 'vs/base/common/actions'; import * as dom from 'vs/base/browser/dom'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -21,7 +20,7 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { memoize } from 'vs/base/common/decorators'; import { dispose, IDisposable, Disposable } from 'vs/base/common/lifecycle'; @@ -34,7 +33,7 @@ import { createAndBindHistoryNavigationWidgetScopedContextKeyService } from 'vs/ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { getSimpleEditorOptions, getSimpleCodeEditorWidgetOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; import { IDecorationOptions } from 'vs/editor/common/editorCommon'; -import { transparent, editorForeground, inputBorder } from 'vs/platform/theme/common/colorRegistry'; +import { transparent, editorForeground } from 'vs/platform/theme/common/colorRegistry'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems'; import { CompletionContext, CompletionList, CompletionProviderRegistry, CompletionItem, completionKindFromString, CompletionItemKind, CompletionItemInsertTextRule } from 'vs/editor/common/modes'; @@ -42,7 +41,6 @@ import { first } from 'vs/base/common/arrays'; import { ITreeNode, ITreeContextMenuEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; -import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { removeAnsiEscapeCodes } from 'vs/base/common/strings'; import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; @@ -60,6 +58,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ReplGroup } from 'vs/workbench/contrib/debug/common/replModel'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { EDITOR_FONT_DEFAULTS, EditorOption } from 'vs/editor/common/config/editorOptions'; +import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; const $ = dom.$; @@ -74,7 +73,7 @@ function revealLastElement(tree: WorkbenchAsyncDataTree) { const sessionsToIgnore = new Set(); export class Repl extends ViewPane implements IHistoryNavigationWidget { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private static readonly REFRESH_DELAY = 100; // delay in ms to refresh the repl for new elements to show private static readonly URI = uri.parse(`${DEBUG_SCHEME}:replinput`); @@ -87,7 +86,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { private replInputContainer!: HTMLElement; private dimension!: dom.Dimension; private replInputLineCount = 1; - private model!: ITextModel; + private model: ITextModel | undefined; private historyNavigationEnablement!: IContextKey; private scopedInstantiationService!: IInstantiationService; private replElementsChangeListener: IDisposable | undefined; @@ -271,7 +270,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { if (isCodeEditor(activeEditorControl)) { this.modelChangeListener.dispose(); this.modelChangeListener = activeEditorControl.onDidChangeModelLanguage(() => this.setMode()); - if (activeEditorControl.hasModel()) { + if (this.model && activeEditorControl.hasModel()) { this.model.setMode(activeEditorControl.getModel().getLanguageIdentifier()); } } @@ -397,16 +396,18 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { getVisibleContent(): string { let text = ''; - const lineDelimiter = this.textResourcePropertiesService.getEOL(this.model.uri); - const traverseAndAppend = (node: ITreeNode) => { - node.children.forEach(child => { - text += child.element.toString().trimRight() + lineDelimiter; - if (!child.collapsed && child.children.length) { - traverseAndAppend(child); - } - }); - }; - traverseAndAppend(this.tree.getNode()); + if (this.model) { + const lineDelimiter = this.textResourcePropertiesService.getEOL(this.model.uri); + const traverseAndAppend = (node: ITreeNode) => { + node.children.forEach(child => { + text += child.element.toString().trimRight() + lineDelimiter; + if (!child.collapsed && child.children.length) { + traverseAndAppend(child); + } + }); + }; + traverseAndAppend(this.tree.getNode()); + } return removeAnsiEscapeCodes(text); } @@ -438,7 +439,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { return this.instantiationService.createInstance(SelectReplActionViewItem, this.selectReplAction); } - return undefined; + return super.getActionViewItem(action); } getActions(): IAction[] { @@ -508,7 +509,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { super.renderBody(parent); this.container = dom.append(parent, $('.repl')); - const treeContainer = dom.append(this.container, $('.repl-tree')); + const treeContainer = dom.append(this.container, $(`.repl-tree.${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`)); this.createReplInput(this.container); this.replDelegate = new ReplDelegate(this.configurationService); @@ -604,6 +605,20 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { await this.clipboardService.writeText(this.getVisibleContent()); return Promise.resolve(); })); + actions.push(new Action('debug.replPaste', localize('paste', "Paste"), undefined, this.debugService.state !== State.Inactive, async () => { + const clipboardText = await this.clipboardService.readText(); + if (clipboardText) { + this.replInput.setValue(this.replInput.getValue().concat(clipboardText)); + this.replInput.focus(); + const model = this.replInput.getModel(); + const lineNumber = model ? model.getLineCount() : 0; + const column = model?.getLineMaxColumn(lineNumber); + if (typeof lineNumber === 'number' && typeof column === 'number') { + this.replInput.setPosition({ lineNumber, column }); + } + } + })); + actions.push(new Separator()); actions.push(new Action('debug.collapseRepl', localize('collapse', "Collapse All"), undefined, true, () => { this.tree.collapseAll(); this.replInput.focus(); @@ -812,13 +827,3 @@ export class ClearReplAction extends Action { function getReplView(viewsService: IViewsService): Repl | undefined { return viewsService.getActiveViewWithId(REPL_VIEW_ID) as Repl ?? undefined; } - -registerThemingParticipant((theme, collector) => { - const inputBorderColor = theme.getColor(inputBorder) || Color.fromHex('#80808060'); - - collector.addRule(` - .repl .repl-input-wrapper { - border-top: 1px solid ${inputBorderColor}; - } - `); -}); diff --git a/src/vs/workbench/contrib/debug/browser/replViewer.ts b/src/vs/workbench/contrib/debug/browser/replViewer.ts index 25337d5249a..ea99871de6d 100644 --- a/src/vs/workbench/contrib/debug/browser/replViewer.ts +++ b/src/vs/workbench/contrib/debug/browser/replViewer.ts @@ -23,7 +23,6 @@ import { IReplElementSource, IDebugService, IExpression, IReplElement, IDebugCon import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { localize } from 'vs/nls'; -import { Codicon } from 'vs/base/common/codicons'; const $ = dom.$; @@ -37,7 +36,6 @@ interface IReplGroupTemplateData { interface IReplEvaluationResultTemplateData { value: HTMLElement; - annotation: HTMLElement; } interface ISimpleReplElementTemplateData { @@ -53,7 +51,6 @@ interface IRawObjectReplTemplateData { expression: HTMLElement; name: HTMLElement; value: HTMLElement; - annotation: HTMLElement; label: HighlightedLabel; } @@ -116,9 +113,8 @@ export class ReplEvaluationResultsRenderer implements ITreeRenderer, index: number, templateData: IReplEvaluationResultTemplateData): void { @@ -128,10 +124,6 @@ export class ReplEvaluationResultsRenderer implements ITreeRenderer, index: number, templateData: IRawObjectReplTemplateData): void { @@ -260,15 +251,6 @@ export class ReplRawObjectsRenderer implements ITreeRenderer; - private savedViewState: IAsyncDataTreeViewState | undefined; + private tree!: WorkbenchAsyncDataTree; + private savedViewState = new Map(); + private autoExpandedScopes = new Set(); constructor( options: IViewletViewOptions, @@ -68,26 +69,24 @@ export class VariablesView extends ViewPane { const stackFrame = this.debugService.getViewModel().focusedStackFrame; this.needsRefresh = false; - if (stackFrame && this.savedViewState) { - await this.tree.setInput(this.debugService.getViewModel(), this.savedViewState); - this.savedViewState = undefined; - } else { - if (!stackFrame) { - // We have no stackFrame, save tree state before it is cleared - this.savedViewState = this.tree.getViewState(); - } - await this.tree.updateChildren(); - if (stackFrame) { - const scopes = await stackFrame.getScopes(); - // Expand the first scope if it is not expensive and if there is no expansion state (all are collapsed) - if (scopes.every(s => this.tree.getNode(s).collapsed) && scopes.length > 0) { - const toExpand = scopes.find(s => !s.expensive); - if (toExpand) { - this.tree.expand(toExpand); - } - } - } + const input = this.tree.getInput(); + if (input) { + this.savedViewState.set(input.getId(), this.tree.getViewState()); + } + if (!stackFrame) { + await this.tree.setInput(null); + return; + } + const viewState = this.savedViewState.get(stackFrame.getId()); + await this.tree.setInput(stackFrame, viewState); + + // Automatically expand the first scope if it is not expensive and if all scopes are collapsed + const scopes = await stackFrame.getScopes(); + const toExpand = scopes.find(s => !s.expensive); + if (toExpand && (scopes.every(s => this.tree.isCollapsed(s)) || !this.autoExpandedScopes.has(toExpand.getId()))) { + this.autoExpandedScopes.add(toExpand.getId()); + await this.tree.expand(toExpand); } }, 400); } @@ -99,7 +98,7 @@ export class VariablesView extends ViewPane { dom.addClass(container, 'debug-variables'); const treeContainer = renderViewTree(container); - this.tree = >this.instantiationService.createInstance(WorkbenchAsyncDataTree, 'VariablesView', treeContainer, new VariablesDelegate(), + this.tree = >this.instantiationService.createInstance(WorkbenchAsyncDataTree, 'VariablesView', treeContainer, new VariablesDelegate(), [this.instantiationService.createInstance(VariablesRenderer), new ScopesRenderer(), new ScopeErrorRenderer()], new VariablesDataSource(), { accessibilityProvider: new VariablesAccessibilityProvider(), @@ -110,12 +109,10 @@ export class VariablesView extends ViewPane { } }); - this.tree.setInput(this.debugService.getViewModel()); + this.tree.setInput(withUndefinedAsNull(this.debugService.getViewModel().focusedStackFrame)); CONTEXT_VARIABLES_FOCUSED.bindTo(this.tree.contextKeyService); - this.tree.updateChildren(); - this._register(this.debugService.getViewModel().onDidFocusStackFrame(sf => { if (!this.isBodyVisible()) { this.needsRefresh = true; @@ -143,11 +140,24 @@ export class VariablesView extends ViewPane { this.onFocusStackFrameScheduler.schedule(); } })); + let horizontalScrolling: boolean | undefined; this._register(this.debugService.getViewModel().onDidSelectExpression(e => { if (e instanceof Variable) { + horizontalScrolling = this.tree.options.horizontalScrolling; + if (horizontalScrolling) { + this.tree.updateOptions({ horizontalScrolling: false }); + } + this.tree.rerender(e); + } else if (!e && horizontalScrolling !== undefined) { + this.tree.updateOptions({ horizontalScrolling: horizontalScrolling }); + horizontalScrolling = undefined; } })); + this._register(this.debugService.onDidEndSession(() => { + this.savedViewState.clear(); + this.autoExpandedScopes.clear(); + })); } getActions(): IAction[] { @@ -194,8 +204,8 @@ export class VariablesView extends ViewPane { } if (session && session.capabilities.supportsDataBreakpoints) { const response = await session.dataBreakpointInfo(variable.name, variable.parent.reference); - const dataid = response.dataId; - if (dataid) { + const dataid = response?.dataId; + if (response && dataid) { actions.push(new Separator()); actions.push(new Action('debug.breakWhenValueChanges', nls.localize('breakWhenValueChanges', "Break When Value Changes"), undefined, true, () => { return this.debugService.addDataBreakpoint(response.description, dataid, !!response.canPersist, response.accessTypes); @@ -213,24 +223,26 @@ export class VariablesView extends ViewPane { } } -function isViewModel(obj: any): obj is IViewModel { - return typeof obj.getSelectedExpression === 'function'; +function isStackFrame(obj: any): obj is IStackFrame { + return obj instanceof StackFrame; } -export class VariablesDataSource implements IAsyncDataSource { +export class VariablesDataSource implements IAsyncDataSource { - hasChildren(element: IViewModel | IExpression | IScope): boolean { - if (isViewModel(element)) { + hasChildren(element: IStackFrame | null | IExpression | IScope): boolean { + if (!element) { + return false; + } + if (isStackFrame(element)) { return true; } return element.hasChildren; } - getChildren(element: IViewModel | IExpression | IScope): Promise<(IExpression | IScope)[]> { - if (isViewModel(element)) { - const stackFrame = element.focusedStackFrame; - return stackFrame ? stackFrame.getScopes() : Promise.resolve([]); + getChildren(element: IStackFrame | IExpression | IScope): Promise<(IExpression | IScope)[]> { + if (isStackFrame(element)) { + return element.getScopes(); } return element.getChildren(); @@ -359,7 +371,7 @@ class VariablesAccessibilityProvider implements IListAccessibilityProvider { if (e instanceof Expression && e.name) { + horizontalScrolling = this.tree.options.horizontalScrolling; + if (horizontalScrolling) { + this.tree.updateOptions({ horizontalScrolling: false }); + } + this.tree.rerender(e); + } else if (!e && horizontalScrolling !== undefined) { + this.tree.updateOptions({ horizontalScrolling: horizontalScrolling }); + horizontalScrolling = undefined; } })); } @@ -189,7 +197,7 @@ export class WatchExpressionsView extends ViewPane { this.debugService.getViewModel().setSelectedExpression(expression); return Promise.resolve(); })); - actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, expression.value, 'watch')); + actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, expression, 'watch')); actions.push(new Separator()); actions.push(new Action('debug.removeWatchExpression', nls.localize('removeWatchExpression', "Remove Expression"), undefined, true, () => { diff --git a/src/vs/workbench/contrib/debug/browser/welcomeView.ts b/src/vs/workbench/contrib/debug/browser/welcomeView.ts index 04260343a37..d9238fcd9e7 100644 --- a/src/vs/workbench/contrib/debug/browser/welcomeView.ts +++ b/src/vs/workbench/contrib/debug/browser/welcomeView.ts @@ -10,12 +10,12 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { localize } from 'vs/nls'; -import { StartAction, ConfigureAction } from 'vs/workbench/contrib/debug/browser/debugActions'; +import { StartAction, ConfigureAction, SelectAndStartAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { IDebugService } from 'vs/workbench/contrib/debug/common/debug'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IViewDescriptorService, IViewsRegistry, Extensions } from 'vs/workbench/common/views'; +import { IViewDescriptorService, IViewsRegistry, Extensions, ViewContentPriority } from 'vs/workbench/common/views'; import { Registry } from 'vs/platform/registry/common/platform'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys'; @@ -119,6 +119,12 @@ viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, { preconditions: [CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR] }); +viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, { + content: localize({ key: 'detectThenRunAndDebug', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] }, + "[Show](command:{0}) all automatic debug configurations.", SelectAndStartAction.ID), + priority: ViewContentPriority.Lowest +}); + viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, { content: localize({ key: 'customizeRunAndDebug', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] }, "To customize Run and Debug [create a launch.json file](command:{0}).", ConfigureAction.ID), diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 51113e0b135..d74d8cce52f 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -24,6 +24,7 @@ import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService' import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CancellationToken } from 'vs/base/common/cancellation'; import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes'; +import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompoundRoot'; export const VIEWLET_ID = 'workbench.view.debug'; @@ -56,6 +57,7 @@ export const CONTEXT_FOCUSED_SESSION_IS_ATTACH = new RawContextKey('foc export const CONTEXT_STEP_BACK_SUPPORTED = new RawContextKey('stepBackSupported', false); export const CONTEXT_RESTART_FRAME_SUPPORTED = new RawContextKey('restartFrameSupported', false); export const CONTEXT_JUMP_TO_CURSOR_SUPPORTED = new RawContextKey('jumpToCursorSupported', false); +export const CONTEXT_STEP_INTO_TARGETS_SUPPORTED = new RawContextKey('stepIntoTargetsSupported', false); export const CONTEXT_BREAKPOINTS_EXIST = new RawContextKey('breakpointsExist', false); export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug'; @@ -154,6 +156,8 @@ export interface IDebugSessionOptions { noDebug?: boolean; parentSession?: IDebugSession; repl?: IDebugSessionReplMode; + compoundRoot?: DebugCompoundRoot; + compact?: boolean; } export interface IDebugSession extends ITreeElement { @@ -164,6 +168,8 @@ export interface IDebugSession extends ITreeElement { readonly root: IWorkspaceFolder | undefined; readonly parentSession: IDebugSession | undefined; readonly subId: string | undefined; + readonly compact: boolean; + readonly compoundRoot: DebugCompoundRoot | undefined; setSubId(subId: string | undefined): void; @@ -189,7 +195,7 @@ export interface IDebugSession extends ITreeElement { logToRepl(sev: severity, args: any[], frame?: { uri: uri, line: number, column: number }): void; // session events - readonly onDidEndAdapter: Event; + readonly onDidEndAdapter: Event; readonly onDidChangeState: Event; readonly onDidChangeReplElements: Event; @@ -214,7 +220,7 @@ export interface IDebugSession extends ITreeElement { sendBreakpoints(modelUri: uri, bpts: IBreakpoint[], sourceModified: boolean): Promise; sendFunctionBreakpoints(fbps: IFunctionBreakpoint[]): Promise; - dataBreakpointInfo(name: string, variablesReference?: number): Promise<{ dataId: string | null, description: string, canPersist?: boolean, accessTypes?: DebugProtocol.DataBreakpointAccessType[] }>; + dataBreakpointInfo(name: string, variablesReference?: number): Promise<{ dataId: string | null, description: string, canPersist?: boolean, accessTypes?: DebugProtocol.DataBreakpointAccessType[] } | undefined>; sendDataBreakpoints(dbps: IDataBreakpoint[]): Promise; sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise; breakpointsLocations(uri: uri, lineNumber: number): Promise; @@ -229,7 +235,7 @@ export interface IDebugSession extends ITreeElement { restartFrame(frameId: number, threadId: number): Promise; next(threadId: number): Promise; - stepIn(threadId: number): Promise; + stepIn(threadId: number, targetId?: number): Promise; stepOut(threadId: number): Promise; stepBack(threadId: number): Promise; continue(threadId: number): Promise; @@ -237,6 +243,7 @@ export interface IDebugSession extends ITreeElement { pause(threadId: number): Promise; terminateThreads(threadIds: number[]): Promise; + stepInTargets(frameId: number): Promise<{ id: number, label: string }[]>; completions(frameId: number | undefined, text: string, position: Position, overwriteBefore: number, token: CancellationToken): Promise; setVariable(variablesReference: number | undefined, name: string, value: string): Promise; loadSource(resource: uri): Promise; @@ -483,6 +490,8 @@ export interface IGlobalConfig { export interface IEnvConfig { internalConsoleOptions?: 'neverOpen' | 'openOnSessionStart' | 'openOnFirstSessionStart'; + preRestartTask?: string | TaskIdentifier; + postRestartTask?: string | TaskIdentifier; preLaunchTask?: string | TaskIdentifier; postDebugTask?: string | TaskIdentifier; debugServer?: number; @@ -516,6 +525,7 @@ export interface IConfig extends IEnvConfig { export interface ICompound { name: string; + stopAll?: boolean; preLaunchTask?: string | TaskIdentifier; configurations: (string | { name: string, folder: string })[]; presentation?: IConfigPresentation; @@ -633,10 +643,11 @@ export interface IConfigurationManager { */ readonly selectedConfiguration: { launch: ILaunch | undefined; + config: IConfig | undefined; name: string | undefined; }; - selectConfiguration(launch: ILaunch | undefined, name?: string, debugStarted?: boolean): void; + selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig): void; getLaunches(): ReadonlyArray; @@ -725,7 +736,7 @@ export interface ILaunch { export const IDebugService = createDecorator(DEBUG_SERVICE_ID); export interface IDebugService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Gets the current debug state. @@ -860,7 +871,7 @@ export interface IDebugService { restartSession(session: IDebugSession, restartData?: any): Promise; /** - * Stops the session. If the session does not exist then stops all sessions. + * Stops the session. If no session is specified then all sessions are stopped. */ stopSession(session: IDebugSession | undefined): Promise; @@ -903,7 +914,7 @@ export const DEBUG_HELPER_SERVICE_ID = 'debugHelperService'; export const IDebugHelperService = createDecorator(DEBUG_HELPER_SERVICE_ID); export interface IDebugHelperService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; createTelemetryService(configurationService: IConfigurationService, args: string[]): TelemetryService | undefined; } diff --git a/src/vs/workbench/contrib/debug/common/debugCompoundRoot.ts b/src/vs/workbench/contrib/debug/common/debugCompoundRoot.ts new file mode 100644 index 00000000000..adbf292db5c --- /dev/null +++ b/src/vs/workbench/contrib/debug/common/debugCompoundRoot.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter } from 'vs/base/common/event'; + +export class DebugCompoundRoot { + private stopped = false; + private stopEmitter = new Emitter(); + + onDidSessionStop = this.stopEmitter.event; + + sessionStopped(): void { + if (!this.stopped) { // avoid sending extranous terminate events + this.stopped = true; + this.stopEmitter.fire(); + } + } +} diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index efc329254a4..1a8a5879ca6 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -298,7 +298,7 @@ export class StackFrame implements IStackFrame { ) { } getId(): string { - return `stackframe:${this.thread.getId()}:${this.frameId}:${this.index}`; + return `stackframe:${this.thread.getId()}:${this.index}:${this.source.name}`; } getScopes(): Promise { @@ -689,7 +689,7 @@ export class Breakpoint extends BaseBreakpoint implements IBreakpoint { toJSON(): any { const result = super.toJSON(); - result.uri = this.uri; + result.uri = this._uri; result.lineNumber = this._lineNumber; result.column = this._column; result.adapterData = this.adapterData; diff --git a/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts b/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts index 4307295e632..7d1d51444c3 100644 --- a/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts +++ b/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts @@ -120,14 +120,14 @@ declare module DebugProtocol { /** Event message for 'stopped' event type. The event indicates that the execution of the debuggee has stopped due to some condition. - This can be caused by a break point previously set, a stepping action has completed, by executing a debugger statement etc. + This can be caused by a break point previously set, a stepping request has completed, by executing a debugger statement etc. */ export interface StoppedEvent extends Event { // event: 'stopped'; body: { /** The reason for the event. For backward compatibility this string is shown in the UI if the 'description' attribute is missing (but it must not be translated). - Values: 'step', 'breakpoint', 'exception', 'pause', 'entry', 'goto', 'function breakpoint', 'data breakpoint', etc. + Values: 'step', 'breakpoint', 'exception', 'pause', 'entry', 'goto', 'function breakpoint', 'data breakpoint', 'instruction breakpoint', etc. */ reason: string; /** The full reason for the event, e.g. 'Paused on exception'. This string is shown in the UI as is and must be translated. */ @@ -765,6 +765,31 @@ declare module DebugProtocol { }; } + /** SetInstructionBreakpoints request; value of command field is 'setInstructionBreakpoints'. + Replaces all existing instruction breakpoints. Typically, instruction breakpoints would be set from a diassembly window. + To clear all instruction breakpoints, specify an empty array. + When an instruction breakpoint is hit, a 'stopped' event (with reason 'instruction breakpoint') is generated. + Clients should only call this request if the capability 'supportsInstructionBreakpoints' is true. + */ + export interface SetInstructionBreakpointsRequest extends Request { + // command: 'setInstructionBreakpoints'; + arguments: SetInstructionBreakpointsArguments; + } + + /** Arguments for 'setInstructionBreakpoints' request */ + export interface SetInstructionBreakpointsArguments { + /** The instruction references of the breakpoints */ + breakpoints: InstructionBreakpoint[]; + } + + /** Response to 'setInstructionBreakpoints' request */ + export interface SetInstructionBreakpointsResponse extends Response { + body: { + /** Information about the breakpoints. The array elements correspond to the elements of the 'breakpoints' array. */ + breakpoints: Breakpoint[]; + }; + } + /** Continue request; value of command field is 'continue'. The request starts the debuggee to run again. */ @@ -804,6 +829,8 @@ declare module DebugProtocol { export interface NextArguments { /** Execute 'next' for this thread. */ threadId: number; + /** Optional granularity to step. If no granularity is specified, a granularity of 'statement' is assumed. */ + granularity?: SteppingGranularity; } /** Response to 'next' request. This is just an acknowledgement, so no body field is required. */ @@ -829,6 +856,8 @@ declare module DebugProtocol { threadId: number; /** Optional id of the target to step into. */ targetId?: number; + /** Optional granularity to step. If no granularity is specified, a granularity of 'statement' is assumed. */ + granularity?: SteppingGranularity; } /** Response to 'stepIn' request. This is just an acknowledgement, so no body field is required. */ @@ -848,6 +877,8 @@ declare module DebugProtocol { export interface StepOutArguments { /** Execute 'stepOut' for this thread. */ threadId: number; + /** Optional granularity to step. If no granularity is specified, a granularity of 'statement' is assumed. */ + granularity?: SteppingGranularity; } /** Response to 'stepOut' request. This is just an acknowledgement, so no body field is required. */ @@ -868,6 +899,8 @@ declare module DebugProtocol { export interface StepBackArguments { /** Execute 'stepBack' for this thread. */ threadId: number; + /** Optional granularity to step. If no granularity is specified, a granularity of 'statement' is assumed. */ + granularity?: SteppingGranularity; } /** Response to 'stepBack' request. This is just an acknowledgement, so no body field is required. */ @@ -1560,6 +1593,10 @@ declare module DebugProtocol { supportsBreakpointLocationsRequest?: boolean; /** The debug adapter supports the 'clipboard' context value in the 'evaluate' request. */ supportsClipboardContext?: boolean; + /** The debug adapter supports stepping granularities (argument 'granularity') for the stepping requests. */ + supportsSteppingGranularity?: boolean; + /** The debug adapter supports adding breakpoints based on instruction references. */ + supportsInstructionBreakpoints?: boolean; } /** An ExceptionBreakpointsFilter is shown in the UI as an option for configuring how exceptions are dealt with. */ @@ -1896,7 +1933,28 @@ declare module DebugProtocol { hitCondition?: string; } - /** Information about a Breakpoint created in setBreakpoints or setFunctionBreakpoints. */ + /** Properties of a breakpoint passed to the setInstructionBreakpoints request */ + export interface InstructionBreakpoint { + /** The instruction reference of the breakpoint. + This should be a memory or instruction pointer reference from an EvaluateResponse, Variable, StackFrame, GotoTarget, or Breakpoint. + */ + instructionReference: string; + /** An optional offset from the instruction reference. + This can be negative. + */ + offset?: number; + /** An optional expression for conditional breakpoints. + It is only honored by a debug adapter if the capability 'supportsConditionalBreakpoints' is true. + */ + condition?: string; + /** An optional expression that controls how many hits of the breakpoint are ignored. + The backend is expected to interpret the expression as needed. + The attribute is only honored by a debug adapter if the capability 'supportsHitConditionalBreakpoints' is true. + */ + hitCondition?: string; + } + + /** Information about a Breakpoint created in setBreakpoints, setFunctionBreakpoints, setInstructionBreakpoints, or setDataBreakpoints. */ export interface Breakpoint { /** An optional identifier for the breakpoint. It is needed if breakpoint events are used to update or remove breakpoints. */ id?: number; @@ -1918,8 +1976,23 @@ declare module DebugProtocol { If no end line is given, then the end column is assumed to be in the start line. */ endColumn?: number; + /** An optional memory reference to where the breakpoint is set. */ + instructionReference?: string; + /** An optional offset from the instruction reference. + This can be negative. + */ + offset?: number; } + /** The granularity of one 'step' in the stepping requests 'next', 'stepIn', 'stepOut', and 'stepBack'. + 'statement': The step should allow the program to run until the current statement has finished executing. + The meaning of a statement is determined by the adapter and it may be considered equivalent to a line. + For example 'for(int i = 0; i < 10; i++) could be considered to have 3 statements 'int i = 0', 'i < 10', and 'i++'. + 'line': The step should allow the program to run until the current source line has executed. + 'instruction': The step should allow one instruction to execute (e.g. one x86 instruction). + */ + export type SteppingGranularity = 'statement' | 'line' | 'instruction'; + /** A StepInTarget can be used in the 'stepIn' request and determines into which single target the stepIn request should step. */ export interface StepInTarget { /** Unique identifier for a stepIn target. */ diff --git a/src/vs/workbench/contrib/debug/common/debugSchemas.ts b/src/vs/workbench/contrib/debug/common/debugSchemas.ts index 77f2180954a..d2e735701be 100644 --- a/src/vs/workbench/contrib/debug/common/debugSchemas.ts +++ b/src/vs/workbench/contrib/debug/common/debugSchemas.ts @@ -215,6 +215,11 @@ export const launchSchema: IJSONSchema = { }, description: nls.localize('app.launch.json.compounds.configurations', "Names of configurations that will be started as part of this compound.") }, + stopAll: { + type: 'boolean', + default: false, + description: nls.localize('app.launch.json.compound.stopAll', "Controls whether manually terminating one session will stop all of the compound sessions.") + }, preLaunchTask: { type: 'string', default: '', diff --git a/src/vs/workbench/contrib/debug/common/debugUtils.ts b/src/vs/workbench/contrib/debug/common/debugUtils.ts index 6acddd72ff0..7e7e887a60e 100644 --- a/src/vs/workbench/contrib/debug/common/debugUtils.ts +++ b/src/vs/workbench/contrib/debug/common/debugUtils.ts @@ -23,6 +23,22 @@ export function formatPII(value: string, excludePII: boolean, args: { [key: stri }); } +/** + * Filters exceptions (keys marked with "!") from the given object. Used to + * ensure exception data is not sent on web remotes, see #97628. + */ +export function filterExceptionsFromTelemetry(data: T): Partial { + const output: Partial = {}; + for (const key of Object.keys(data) as (keyof T & string)[]) { + if (!key.startsWith('!')) { + output[key] = data[key]; + } + } + + return output; +} + + export function isSessionAttach(session: IDebugSession): boolean { return session.configuration.request === 'attach' && !getExtensionHostDebugSession(session); } diff --git a/src/vs/workbench/contrib/debug/common/debugViewModel.ts b/src/vs/workbench/contrib/debug/common/debugViewModel.ts index 7d0889b5c26..f1b3ed17894 100644 --- a/src/vs/workbench/contrib/debug/common/debugViewModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugViewModel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; -import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, IDebugSession, IThread, IExpression, IFunctionBreakpoint, CONTEXT_BREAKPOINT_SELECTED, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED } from 'vs/workbench/contrib/debug/common/debug'; +import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, IDebugSession, IThread, IExpression, IFunctionBreakpoint, CONTEXT_BREAKPOINT_SELECTED, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_STEP_INTO_TARGETS_SUPPORTED } from 'vs/workbench/contrib/debug/common/debug'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { isSessionAttach } from 'vs/workbench/contrib/debug/common/debugUtils'; @@ -21,23 +21,27 @@ export class ViewModel implements IViewModel { private readonly _onDidFocusStackFrame = new Emitter<{ stackFrame: IStackFrame | undefined, explicit: boolean }>(); private readonly _onDidSelectExpression = new Emitter(); private multiSessionView: boolean; - private expressionSelectedContextKey: IContextKey; - private breakpointSelectedContextKey: IContextKey; - private loadedScriptsSupportedContextKey: IContextKey; - private stepBackSupportedContextKey: IContextKey; - private focusedSessionIsAttach: IContextKey; - private restartFrameSupportedContextKey: IContextKey; - private jumpToCursorSupported: IContextKey; + private expressionSelectedContextKey!: IContextKey; + private breakpointSelectedContextKey!: IContextKey; + private loadedScriptsSupportedContextKey!: IContextKey; + private stepBackSupportedContextKey!: IContextKey; + private focusedSessionIsAttach!: IContextKey; + private restartFrameSupportedContextKey!: IContextKey; + private stepIntoTargetsSupported!: IContextKey; + private jumpToCursorSupported!: IContextKey; - constructor(contextKeyService: IContextKeyService) { + constructor(private contextKeyService: IContextKeyService) { this.multiSessionView = false; - this.expressionSelectedContextKey = CONTEXT_EXPRESSION_SELECTED.bindTo(contextKeyService); - this.breakpointSelectedContextKey = CONTEXT_BREAKPOINT_SELECTED.bindTo(contextKeyService); - this.loadedScriptsSupportedContextKey = CONTEXT_LOADED_SCRIPTS_SUPPORTED.bindTo(contextKeyService); - this.stepBackSupportedContextKey = CONTEXT_STEP_BACK_SUPPORTED.bindTo(contextKeyService); - this.focusedSessionIsAttach = CONTEXT_FOCUSED_SESSION_IS_ATTACH.bindTo(contextKeyService); - this.restartFrameSupportedContextKey = CONTEXT_RESTART_FRAME_SUPPORTED.bindTo(contextKeyService); - this.jumpToCursorSupported = CONTEXT_JUMP_TO_CURSOR_SUPPORTED.bindTo(contextKeyService); + contextKeyService.bufferChangeEvents(() => { + this.expressionSelectedContextKey = CONTEXT_EXPRESSION_SELECTED.bindTo(contextKeyService); + this.breakpointSelectedContextKey = CONTEXT_BREAKPOINT_SELECTED.bindTo(contextKeyService); + this.loadedScriptsSupportedContextKey = CONTEXT_LOADED_SCRIPTS_SUPPORTED.bindTo(contextKeyService); + this.stepBackSupportedContextKey = CONTEXT_STEP_BACK_SUPPORTED.bindTo(contextKeyService); + this.focusedSessionIsAttach = CONTEXT_FOCUSED_SESSION_IS_ATTACH.bindTo(contextKeyService); + this.restartFrameSupportedContextKey = CONTEXT_RESTART_FRAME_SUPPORTED.bindTo(contextKeyService); + this.stepIntoTargetsSupported = CONTEXT_STEP_INTO_TARGETS_SUPPORTED.bindTo(contextKeyService); + this.jumpToCursorSupported = CONTEXT_JUMP_TO_CURSOR_SUPPORTED.bindTo(contextKeyService); + }); } getId(): string { @@ -64,12 +68,15 @@ export class ViewModel implements IViewModel { this._focusedThread = thread; this._focusedSession = session; - this.loadedScriptsSupportedContextKey.set(session ? !!session.capabilities.supportsLoadedSourcesRequest : false); - this.stepBackSupportedContextKey.set(session ? !!session.capabilities.supportsStepBack : false); - this.restartFrameSupportedContextKey.set(session ? !!session.capabilities.supportsRestartFrame : false); - this.jumpToCursorSupported.set(session ? !!session.capabilities.supportsGotoTargetsRequest : false); - const attach = !!session && isSessionAttach(session); - this.focusedSessionIsAttach.set(attach); + this.contextKeyService.bufferChangeEvents(() => { + this.loadedScriptsSupportedContextKey.set(session ? !!session.capabilities.supportsLoadedSourcesRequest : false); + this.stepBackSupportedContextKey.set(session ? !!session.capabilities.supportsStepBack : false); + this.restartFrameSupportedContextKey.set(session ? !!session.capabilities.supportsRestartFrame : false); + this.stepIntoTargetsSupported.set(session ? !!session.capabilities.supportsStepInTargetsRequest : false); + this.jumpToCursorSupported.set(session ? !!session.capabilities.supportsGotoTargetsRequest : false); + const attach = !!session && isSessionAttach(session); + this.focusedSessionIsAttach.set(attach); + }); if (shouldEmitForSession) { this._onDidFocusSession.fire(session); diff --git a/src/vs/workbench/contrib/debug/common/debugger.ts b/src/vs/workbench/contrib/debug/common/debugger.ts index ca1ee7ebfc3..f467b6e4cbd 100644 --- a/src/vs/workbench/contrib/debug/common/debugger.ts +++ b/src/vs/workbench/contrib/debug/common/debugger.ts @@ -58,19 +58,21 @@ export class Debugger implements IDebugger { if (isObject(source)) { Object.keys(source).forEach(key => { - if (isObject(destination[key]) && isObject(source[key])) { - mixin(destination[key], source[key], overwrite, level + 1); - } else { - if (key in destination) { - if (overwrite) { - if (level === 0 && key === 'type') { - // don't merge the 'type' property - } else { - destination[key] = source[key]; - } - } + if (key !== '__proto__') { + if (isObject(destination[key]) && isObject(source[key])) { + mixin(destination[key], source[key], overwrite, level + 1); } else { - destination[key] = source[key]; + if (key in destination) { + if (overwrite) { + if (level === 0 && key === 'type') { + // don't merge the 'type' property + } else { + destination[key] = source[key]; + } + } + } else { + destination[key] = source[key]; + } } } }); diff --git a/src/vs/workbench/contrib/debug/electron-browser/extensionHostDebugService.ts b/src/vs/workbench/contrib/debug/electron-sandbox/extensionHostDebugService.ts similarity index 91% rename from src/vs/workbench/contrib/debug/electron-browser/extensionHostDebugService.ts rename to src/vs/workbench/contrib/debug/electron-sandbox/extensionHostDebugService.ts index ce00abedcab..5b504f89cbf 100644 --- a/src/vs/workbench/contrib/debug/electron-browser/extensionHostDebugService.ts +++ b/src/vs/workbench/contrib/debug/electron-sandbox/extensionHostDebugService.ts @@ -5,7 +5,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; -import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; import { ExtensionHostDebugChannelClient, ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc'; export class ExtensionHostDebugService extends ExtensionHostDebugChannelClient { diff --git a/src/vs/workbench/contrib/debug/node/debugHelperService.ts b/src/vs/workbench/contrib/debug/node/debugHelperService.ts index d9d76f35ca0..4089f00771a 100644 --- a/src/vs/workbench/contrib/debug/node/debugHelperService.ts +++ b/src/vs/workbench/contrib/debug/node/debugHelperService.ts @@ -10,9 +10,16 @@ import { getPathFromAmdModule } from 'vs/base/common/amd'; import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { cleanRemoteAuthority } from 'vs/platform/telemetry/common/telemetryUtils'; export class NodeDebugHelperService implements IDebugHelperService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; + + constructor( + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, + ) { } + createTelemetryService(configurationService: IConfigurationService, args: string[]): TelemetryService | undefined { @@ -33,8 +40,11 @@ export class NodeDebugHelperService implements IDebugHelperService { const channel = client.getChannel('telemetryAppender'); const appender = new TelemetryAppenderClient(channel); - return new TelemetryService({ appender, sendErrorTelemetry: true }, configurationService); + return new TelemetryService({ + appender, + sendErrorTelemetry: cleanRemoteAuthority(this.environmentService.configuration.remoteAuthority) !== 'other' + }, configurationService); } } -registerSingleton(IDebugHelperService, NodeDebugHelperService); +registerSingleton(IDebugHelperService, NodeDebugHelperService, true); diff --git a/src/vs/workbench/contrib/debug/node/terminals.ts b/src/vs/workbench/contrib/debug/node/terminals.ts index 9abe1a5cb22..3c5fcd77c9a 100644 --- a/src/vs/workbench/contrib/debug/node/terminals.ts +++ b/src/vs/workbench/contrib/debug/node/terminals.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as cp from 'child_process'; -import * as env from 'vs/base/common/platform'; +import * as platform from 'vs/base/common/platform'; import { WindowsExternalTerminalService, MacExternalTerminalService, LinuxExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/node/externalTerminalService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/common/externalTerminal'; @@ -14,11 +14,11 @@ let externalTerminalService: IExternalTerminalService | undefined = undefined; export function runInExternalTerminal(args: DebugProtocol.RunInTerminalRequestArguments, configProvider: ExtHostConfigProvider): Promise { if (!externalTerminalService) { - if (env.isWindows) { + if (platform.isWindows) { externalTerminalService = new WindowsExternalTerminalService(undefined); - } else if (env.isMacintosh) { + } else if (platform.isMacintosh) { externalTerminalService = new MacExternalTerminalService(undefined); - } else if (env.isLinux) { + } else if (platform.isLinux) { externalTerminalService = new LinuxExternalTerminalService(undefined); } else { throw new Error('external terminals not supported on this platform'); @@ -49,7 +49,7 @@ function spawnAsPromised(command: string, args: string[]): Promise { export function hasChildProcesses(processId: number | undefined): Promise { if (processId) { // if shell has at least one child process, assume that shell is busy - if (env.isWindows) { + if (platform.isWindows) { return spawnAsPromised('wmic', ['process', 'get', 'ParentProcessId']).then(stdout => { const pids = stdout.split('\r\n'); return pids.some(p => parseInt(p) === processId); @@ -75,7 +75,8 @@ export function hasChildProcesses(processId: number | undefined): Promise= 0) { shellType = ShellType.bash; - } else if (env.isWindows) { + } else if (platform.isWindows) { shellType = ShellType.cmd; // pick a good default for Windows } else { shellType = ShellType.bash; // pick a good default for anything else @@ -109,12 +110,12 @@ export function prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments return `'${s}'`; }; - if (args.cwd) { - command += `cd '${args.cwd}'; `; + if (cwd) { + command += `cd '${cwd}'; `; } - if (args.env) { - for (let key in args.env) { - const value = args.env[key]; + if (env) { + for (let key in env) { + const value = env[key]; if (value === null) { command += `Remove-Item env:${key}; `; } else { @@ -122,10 +123,10 @@ export function prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments } } } - if (args.args && args.args.length > 0) { - const cmd = quote(args.args.shift()!); + if (args.length > 0) { + const cmd = quote(args.shift()!); command += (cmd[0] === '\'') ? `& ${cmd} ` : `${cmd} `; - for (let a of args.args) { + for (let a of args) { command += `${quote(a)} `; } } @@ -138,13 +139,13 @@ export function prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments return (s.indexOf(' ') >= 0 || s.indexOf('"') >= 0 || s.length === 0) ? `"${s}"` : s; }; - if (args.cwd) { - command += `cd ${quote(args.cwd)} && `; + if (cwd) { + command += `cd ${quote(cwd)} && `; } - if (args.env) { + if (env) { command += 'cmd /C "'; - for (let key in args.env) { - let value = args.env[key]; + for (let key in env) { + let value = env[key]; if (value === null) { command += `set "${key}=" && `; } else { @@ -153,10 +154,10 @@ export function prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments } } } - for (let a of args.args) { + for (let a of args) { command += `${quote(a)} `; } - if (args.env) { + if (env) { command += '"'; } break; @@ -164,7 +165,7 @@ export function prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments case ShellType.bash: quote = (s: string) => { - s = s.replace(/(["'\\])/g, '\\$1'); + s = s.replace(/(["'\\\$])/g, '\\$1'); return (s.indexOf(' ') >= 0 || s.indexOf(';') >= 0 || s.length === 0) ? `"${s}"` : s; }; @@ -172,13 +173,13 @@ export function prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments return /[^\w@%\/+=,.:^-]/.test(s) ? `'${s.replace(/'/g, '\'\\\'\'')}'` : s; }; - if (args.cwd) { - command += `cd ${quote(args.cwd)} ; `; + if (cwd) { + command += `cd ${quote(cwd)} ; `; } - if (args.env) { + if (env) { command += 'env'; - for (let key in args.env) { - const value = args.env[key]; + for (let key in env) { + const value = env[key]; if (value === null) { command += ` -u ${hardQuote(key)}`; } else { @@ -187,7 +188,7 @@ export function prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments } command += ' '; } - for (let a of args.args) { + for (let a of args) { command += `${quote(a)} `; } break; diff --git a/src/vs/workbench/contrib/debug/test/browser/telemetry.test.ts b/src/vs/workbench/contrib/debug/test/browser/telemetry.test.ts new file mode 100644 index 00000000000..276463f95b8 --- /dev/null +++ b/src/vs/workbench/contrib/debug/test/browser/telemetry.test.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { MockDebugAdapter, createMockDebugModel } from 'vs/workbench/contrib/debug/test/common/mockDebug'; +import { DebugModel } from 'vs/workbench/contrib/debug/common/debugModel'; +import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession'; +import { generateUuid } from 'vs/base/common/uuid'; +import { NullOpenerService } from 'vs/platform/opener/common/opener'; +import { RawDebugSession } from 'vs/workbench/contrib/debug/browser/rawDebugSession'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { stub, SinonStub } from 'sinon'; +import { timeout } from 'vs/base/common/async'; + +suite('Debug - DebugSession telemetry', () => { + let model: DebugModel; + let session: DebugSession; + let adapter: MockDebugAdapter; + let telemetry: { isOptedIn: boolean; sendErrorTelemetry: boolean; publicLog: SinonStub }; + + setup(() => { + telemetry = { isOptedIn: true, sendErrorTelemetry: true, publicLog: stub() }; + adapter = new MockDebugAdapter(); + model = createMockDebugModel(); + + const telemetryService = telemetry as Partial as ITelemetryService; + session = new DebugSession(generateUuid(), undefined!, undefined!, model, undefined, undefined!, telemetryService, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService, undefined!, undefined!); + session.initializeForTest(new RawDebugSession(adapter, undefined!, undefined!, telemetryService, undefined!, undefined!, undefined!)); + }); + + test('does not send telemetry when opted out', async () => { + telemetry.isOptedIn = false; + adapter.sendEventBody('output', { + category: 'telemetry', + output: 'someEvent', + data: { foo: 'bar', '!err': 'oh no!' } + }); + + await timeout(0); + assert.strictEqual(telemetry.publicLog.callCount, 0); + }); + + test('logs telemetry and exceptions when enabled', async () => { + adapter.sendEventBody('output', { + category: 'telemetry', + output: 'someEvent', + data: { foo: 'bar', '!err': 'oh no!' } + }); + + await timeout(0); + assert.deepStrictEqual(telemetry.publicLog.args[0], [ + 'someEvent', + { foo: 'bar', '!err': 'oh no!' } + ]); + }); + + test('filters exceptions when error reporting disabled', async () => { + telemetry.sendErrorTelemetry = false; + + adapter.sendEventBody('output', { + category: 'telemetry', + output: 'someEvent', + data: { foo: 'bar', '!err': 'oh no!' } + }); + + await timeout(0); + assert.deepStrictEqual(telemetry.publicLog.args[0], [ + 'someEvent', + { foo: 'bar' } + ]); + }); +}); diff --git a/src/vs/workbench/contrib/debug/test/common/debugViewModel.test.ts b/src/vs/workbench/contrib/debug/test/common/debugViewModel.test.ts index ed656d4eb91..576e7bfc44c 100644 --- a/src/vs/workbench/contrib/debug/test/common/debugViewModel.test.ts +++ b/src/vs/workbench/contrib/debug/test/common/debugViewModel.test.ts @@ -8,6 +8,7 @@ import { ViewModel } from 'vs/workbench/contrib/debug/common/debugViewModel'; import { StackFrame, Expression, Thread } from 'vs/workbench/contrib/debug/common/debugModel'; import { MockSession } from 'vs/workbench/contrib/debug/test/common/mockDebug'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; +import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; suite('Debug - View Model', () => { let model: ViewModel; @@ -21,7 +22,12 @@ suite('Debug - View Model', () => { assert.equal(model.focusedThread, null); const session = new MockSession(); const thread = new Thread(session, 'myThread', 1); - const frame = new StackFrame(thread, 1, undefined!, 'app.js', 'normal', { startColumn: 1, startLineNumber: 1, endColumn: 1, endLineNumber: 1 }, 0); + const source = new Source({ + name: 'internalModule.js', + sourceReference: 11, + presentationHint: 'deemphasize' + }, 'aDebugSessionId'); + const frame = new StackFrame(thread, 1, source, 'app.js', 'normal', { startColumn: 1, startLineNumber: 1, endColumn: 1, endLineNumber: 1 }, 0); model.setFocus(frame, thread, session, false); assert.equal(model.focusedStackFrame!.getId(), frame.getId()); diff --git a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts index 5ce7663389a..7e9b4377b82 100644 --- a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts @@ -13,6 +13,7 @@ import Severity from 'vs/base/common/severity'; import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter'; import { DebugStorage } from 'vs/workbench/contrib/debug/common/debugStorage'; import { ExceptionBreakpoint, Expression, DataBreakpoint, FunctionBreakpoint, Breakpoint, DebugModel } from 'vs/workbench/contrib/debug/common/debugModel'; +import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompoundRoot'; export class MockDebugService implements IDebugService { @@ -135,6 +136,13 @@ export class MockDebugService implements IDebugService { } export class MockSession implements IDebugSession { + get compoundRoot(): DebugCompoundRoot | undefined { + return undefined; + } + + stepInTargets(frameId: number): Promise<{ id: number; label: string; }[]> { + throw new Error('Method not implemented.'); + } cancel(_progressId: string): Promise { throw new Error('Method not implemented.'); @@ -144,7 +152,7 @@ export class MockSession implements IDebugSession { throw new Error('Method not implemented.'); } - dataBreakpointInfo(name: string, variablesReference?: number | undefined): Promise<{ dataId: string | null; description: string; canPersist?: boolean | undefined; }> { + dataBreakpointInfo(name: string, variablesReference?: number | undefined): Promise<{ dataId: string | null; description: string; canPersist?: boolean | undefined; } | undefined> { throw new Error('Method not implemented.'); } @@ -154,6 +162,10 @@ export class MockSession implements IDebugSession { subId: string | undefined; + get compact(): boolean { + return false; + } + setSubId(subId: string | undefined): void { throw new Error('Method not implemented.'); } @@ -220,7 +232,7 @@ export class MockSession implements IDebugSession { throw new Error('not implemented'); } - get onDidEndAdapter(): Event { + get onDidEndAdapter(): Event { throw new Error('not implemented'); } @@ -304,7 +316,7 @@ export class MockSession implements IDebugSession { next(threadId: number): Promise { throw new Error('Method not implemented.'); } - stepIn(threadId: number): Promise { + stepIn(threadId: number, targetId?: number): Promise { throw new Error('Method not implemented.'); } stepOut(threadId: number): Promise { diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index ece41041668..fa09f280e86 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -70,7 +70,7 @@ export interface IExperiment { } export interface IExperimentService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; getExperimentById(id: string): Promise; getExperimentsByType(type: ExperimentActionType): Promise; getCuratedExtensionsList(curatedExtensionsKey: string): Promise; @@ -163,7 +163,7 @@ export const getCurrentActivationRecord = (previous?: IActivationEventRecord, da }; export class ExperimentService extends Disposable implements IExperimentService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _experiments: IExperiment[] = []; private _loadExperimentsPromise: Promise; private _curatedMapping = Object.create(null); diff --git a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts index 54d67cfe69c..a69f5495dc4 100644 --- a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts +++ b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts @@ -15,7 +15,7 @@ import { IWorkbenchExtensionEnablementService } from 'vs/workbench/services/exte import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { Emitter } from 'vs/base/common/event'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test'; -import { URLService } from 'vs/platform/url/node/urlService'; +import { NativeURLService } from 'vs/platform/url/common/urlService'; import { IURLService } from 'vs/platform/url/common/url'; import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; @@ -89,7 +89,7 @@ suite('Experiment Service', () => { instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event); instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); instantiationService.stub(ITelemetryService, NullTelemetryService); - instantiationService.stub(IURLService, URLService); + instantiationService.stub(IURLService, NativeURLService); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); testConfigurationService = new TestConfigurationService(); instantiationService.stub(IConfigurationService, testConfigurationService); diff --git a/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts index c092c7c81ea..b9cc0f9941e 100644 --- a/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts @@ -7,7 +7,6 @@ import { IExtensionTipsService, IExtensionManagementService, ILocalExtension, IC import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { localize } from 'vs/nls'; -import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; @@ -16,7 +15,6 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IWorkspaceContextService, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; import { distinct } from 'vs/base/common/arrays'; -import { values } from 'vs/base/common/map'; export class ConfigBasedRecommendations extends ExtensionRecommendations { @@ -61,8 +59,8 @@ export class ConfigBasedRecommendations extends ExtensionRecommendations { } } } - this.importantTips = values(importantTips); - this.otherTips = values(otherTips).filter(tip => !importantTips.has(tip.extensionId)); + this.importantTips = [...importantTips.values()]; + this.otherTips = [...otherTips.values()].filter(tip => !importantTips.has(tip.extensionId)); this._recommendations = [...this.importantTips, ...this.otherTips].map(tip => this.toExtensionRecommendation(tip)); } @@ -75,7 +73,7 @@ export class ConfigBasedRecommendations extends ExtensionRecommendations { return; } - const local = await this.extensionManagementService.getInstalled(ExtensionType.User); + const local = await this.extensionManagementService.getInstalled(); const { uninstalled } = this.groupByInstalled(distinct(this.importantTips.map(({ extensionId }) => extensionId)), local); if (uninstalled.length === 0) { return; diff --git a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts index 92d7e07c4fe..10602205369 100644 --- a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts @@ -9,14 +9,14 @@ import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/ import { timeout } from 'vs/base/common/async'; import { localize } from 'vs/nls'; import { IStringDictionary } from 'vs/base/common/collections'; -import { ExtensionType } from 'vs/platform/extensions/common/extensions'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { basename } from 'vs/base/common/path'; import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService'; type ExeExtensionRecommendationsClassification = { extensionId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; @@ -28,10 +28,13 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { readonly _recommendations: ExtensionRecommendation[] = []; get recommendations(): ReadonlyArray { return this._recommendations; } + private readonly tasExperimentService: ITASExperimentService | undefined; + constructor( isExtensionAllowedToBeRecommended: (extensionId: string) => boolean, @IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, + @optional(ITASExperimentService) tasExperimentService: ITASExperimentService, @IInstantiationService instantiationService: IInstantiationService, @IConfigurationService configurationService: IConfigurationService, @INotificationService notificationService: INotificationService, @@ -40,6 +43,7 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, ) { super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, storageKeysSyncRegistryService); + this.tasExperimentService = tasExperimentService; /* 3s has come out to be the good number to fetch and prompt important exe based recommendations @@ -61,7 +65,7 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { importantExeBasedRecommendations[tip.extensionId.toLowerCase()] = tip; }); - const local = await this.extensionManagementService.getInstalled(ExtensionType.User); + const local = await this.extensionManagementService.getInstalled(); const { installed, uninstalled } = this.groupByInstalled(Object.keys(importantExeBasedRecommendations), local); /* Log installed and uninstalled exe based recommendations */ @@ -77,7 +81,7 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { this.promptImportantExeBasedRecommendations(uninstalled, importantExeBasedRecommendations); } - private promptImportantExeBasedRecommendations(recommendations: string[], importantExeBasedRecommendations: IStringDictionary): void { + private async promptImportantExeBasedRecommendations(recommendations: string[], importantExeBasedRecommendations: IStringDictionary): Promise { if (this.hasToIgnoreRecommendationNotifications()) { return; } @@ -86,10 +90,16 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { return; } - const extensionId = recommendations[0]; - const tip = importantExeBasedRecommendations[extensionId]; - const message = localize('exeRecommended', "The '{0}' extension is recommended as you have {1} installed on your system.", tip.friendlyName!, tip.exeFriendlyName || basename(tip.windowsPath!)); - this.promptImportantExtensionInstallNotification(extensionId, message); + for (const extensionId of recommendations) { + if (this.tasExperimentService && extensionId === 'ms-vscode-remote.remote-wsl') { + await this.tasExperimentService.getTreatment('wslpopupaa'); + } + + const tip = importantExeBasedRecommendations[extensionId]; + const message = tip.isExtensionPack ? localize('extensionPackRecommended', "The '{0}' extension pack is recommended as you have {1} installed on your system.", tip.extensionName!, tip.exeFriendlyName || basename(tip.windowsPath!)) + : localize('exeRecommended', "The '{0}' extension is recommended as you have {1} installed on your system.", tip.extensionName!, tip.exeFriendlyName || basename(tip.windowsPath!)); + this.promptImportantExtensionInstallNotification(extensionId, message); + } } private groupByInstalled(recommendationsToSuggest: string[], local: ILocalExtension[]): { installed: string[], uninstalled: string[] } { @@ -111,7 +121,7 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { source: 'executable', reason: { reasonId: ExtensionRecommendationReason.Executable, - reasonText: localize('exeBasedRecommendation', "This extension is recommended because you have {0} installed.", tip.friendlyName) + reasonText: localize('exeBasedRecommendation', "This extension is recommended because you have {0} installed.", tip.extensionName) } }; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 5ad28727bc8..51ca34594bc 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -224,23 +224,23 @@ export class ExtensionEditor extends BaseEditor { builtin.textContent = localize('builtin', "Built-in"); const subtitle = append(details, $('.subtitle')); - const publisher = append(subtitle, $('span.publisher.clickable', { title: localize('publisher', "Publisher name"), tabIndex: 0 })); + const publisher = append(append(subtitle, $('.subtitle-entry')), $('span.publisher.clickable', { title: localize('publisher', "Publisher name"), tabIndex: 0 })); - const installCount = append(subtitle, $('span.install', { title: localize('install count', "Install count"), tabIndex: 0 })); + const installCount = append(append(subtitle, $('.subtitle-entry')), $('span.install', { title: localize('install count', "Install count"), tabIndex: 0 })); - const rating = append(subtitle, $('span.rating.clickable', { title: localize('rating', "Rating"), tabIndex: 0 })); + const rating = append(append(subtitle, $('.subtitle-entry')), $('span.rating.clickable', { title: localize('rating', "Rating"), tabIndex: 0 })); - const repository = append(subtitle, $('span.repository.clickable')); + const repository = append(append(subtitle, $('.subtitle-entry')), $('span.repository.clickable')); repository.textContent = localize('repository', 'Repository'); repository.style.display = 'none'; repository.tabIndex = 0; - const license = append(subtitle, $('span.license.clickable')); + const license = append(append(subtitle, $('.subtitle-entry')), $('span.license.clickable')); license.textContent = localize('license', 'License'); license.style.display = 'none'; license.tabIndex = 0; - const version = append(subtitle, $('span.version')); + const version = append(append(subtitle, $('.subtitle-entry')), $('span.version')); version.textContent = localize('version', 'Version'); const description = append(details, $('.description')); @@ -528,21 +528,22 @@ export class ExtensionEditor extends BaseEditor { } focus(): void { - if (this.activeElement) { - this.activeElement.focus(); - } + this.activeElement?.focus(); } showFind(): void { - if (this.activeElement && (this.activeElement).showFind) { - (this.activeElement).showFind(); - } + this.activeWebview?.showFind(); } runFindAction(previous: boolean): void { - if (this.activeElement && (this.activeElement).runFindAction) { - (this.activeElement).runFindAction(previous); + this.activeWebview?.runFindAction(previous); + } + + public get activeWebview(): Webview | undefined { + if (!this.activeElement || !(this.activeElement as Webview).runFindAction) { + return undefined; } + return this.activeElement as Webview; } private onNavbarChange(extension: IExtension, { id, focus }: { id: string | null, focus: boolean }, template: IExtensionEditorTemplate): void { @@ -588,7 +589,7 @@ export class ExtensionEditor extends BaseEditor { const webview = this.contentDisposables.add(this.webviewService.createWebviewOverlay('extensionEditor', { enableFindWidget: true, - }, {})); + }, {}, undefined)); webview.claim(this); webview.layoutWebviewOverElement(template.content); @@ -617,7 +618,7 @@ export class ExtensionEditor extends BaseEditor { if (!link) { return; } - // Whitelist supported schemes for links + // Only allow links with specific schemes if (matchesScheme(link, Schemas.http) || matchesScheme(link, Schemas.https) || matchesScheme(link, Schemas.mailto) || (matchesScheme(link, Schemas.command) && URI.parse(link).path === ShowCurrentReleaseNotesActionId) ) { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts index eba729c554b..1001b15bfd4 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts @@ -8,12 +8,13 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { localize } from 'vs/nls'; -import { InstallRecommendedExtensionAction, ShowRecommendedExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { InstallRecommendedExtensionAction, ShowRecommendedExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { ExtensionRecommendationSource, IExtensionRecommendationReson } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IExtensionsConfiguration, ConfigurationKey } from 'vs/workbench/contrib/extensions/common/extensions'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { IAction } from 'vs/base/common/actions'; type ExtensionRecommendationsNotificationClassification = { userReaction: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; @@ -56,22 +57,27 @@ export abstract class ExtensionRecommendations extends Disposable { return this._activationPromise; } + private runAction(action: IAction) { + try { + action.run(); + } finally { + action.dispose(); + } + } + protected promptImportantExtensionInstallNotification(extensionId: string, message: string): void { this.notificationService.prompt(Severity.Info, message, [{ label: localize('install', 'Install'), run: () => { this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'install', extensionId }); - this.instantiationService.createInstance(InstallRecommendedExtensionAction, extensionId).run(); + this.runAction(this.instantiationService.createInstance(InstallRecommendedExtensionAction, extensionId)); } }, { - label: localize('showRecommendations', "Show Recommendations"), + label: localize('moreInformation', "More Information"), run: () => { this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'show', extensionId }); - - const recommendationsAction = this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('showRecommendations', "Show Recommendations")); - recommendationsAction.run(); - recommendationsAction.dispose(); + this.runAction(this.instantiationService.createInstance(ShowRecommendedExtensionAction, extensionId)); } }, { label: choiceNever, diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index 3c244101c51..20116eb72c7 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -35,7 +35,7 @@ const ignoredRecommendationsStorageKey = 'extensionsAssistant/ignored_recommenda export class ExtensionRecommendationsService extends Disposable implements IExtensionRecommendationsService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; // Recommendations private readonly fileBasedRecommendations: FileBasedRecommendations; diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index dfbef720e2a..b6a43859392 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -19,7 +19,7 @@ import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/brow import { OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction, ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, ShowBuiltInExtensionsAction, UpdateAllAction, - EnableAllAction, EnableAllWorkspaceAction, DisableAllAction, DisableAllWorkspaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, ShowAzureExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ConfigureRecommendedExtensionsCommandsContributor, InstallVSIXAction, ReinstallAction, InstallSpecificVersionOfExtensionAction + EnableAllAction, EnableAllWorkspaceAction, DisableAllAction, DisableAllWorkspaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, ShowAzureExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ConfigureRecommendedExtensionsCommandsContributor, InstallVSIXAction, ReinstallAction, InstallSpecificVersionOfExtensionAction, ClearExtensionsSearchResultsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; import { ExtensionEditor } from 'vs/workbench/contrib/extensions/browser/extensionEditor'; @@ -45,11 +45,14 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { CONTEXT_SYNC_ENABLEMENT } from 'vs/platform/userDataSync/common/userDataSync'; import { IQuickAccessRegistry, Extensions } from 'vs/platform/quickinput/common/quickAccess'; import { InstallExtensionQuickAccessProvider, ManageExtensionsQuickAccessProvider } from 'vs/workbench/contrib/extensions/browser/extensionsQuickAccess'; import { ExtensionRecommendationsService } from 'vs/workbench/contrib/extensions/browser/extensionRecommendationsService'; +import { CONTEXT_SYNC_ENABLEMENT } from 'vs/workbench/services/userDataSync/common/userDataSync'; +import { CopyAction, CutAction, PasteAction } from 'vs/editor/contrib/clipboard/clipboard'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { MultiCommand } from 'vs/editor/browser/editorExtensions'; +import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; // Singletons registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); @@ -149,10 +152,11 @@ actionRegistry.registerWorkbenchAction(enableAllWorkspaceAction, 'Extensions: En const checkForUpdatesAction = SyncActionDescriptor.from(CheckForUpdatesAction); actionRegistry.registerWorkbenchAction(checkForUpdatesAction, `Extensions: Check for Extension Updates`, ExtensionsLabel); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ClearExtensionsSearchResultsAction), 'Extensions: Clear Extensions Search Results', ExtensionsLabel); actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(EnableAutoUpdateAction), `Extensions: Enable Auto Updating Extensions`, ExtensionsLabel); actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(DisableAutoUpdateAction), `Extensions: Disable Auto Updating Extensions`, ExtensionsLabel); actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(InstallSpecificVersionOfExtensionAction), 'Install Specific Version of Extension...', ExtensionsLabel); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ReinstallAction), 'Reinstall Extension...', localize('developer', "Developer")); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ReinstallAction), 'Reinstall Extension...', localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer")); Registry.as(ConfigurationExtensions.Configuration) .registerConfiguration({ @@ -195,6 +199,11 @@ Registry.as(ConfigurationExtensions.Configuration) type: 'array', description: localize('handleUriConfirmedExtensions', "When an extension is listed here, a confirmation prompt will not be shown when that extension handles a URI."), default: [] + }, + 'extensions.webWorker': { + type: 'boolean', + description: localize('extensionsWebWorker', "Enable web worker extension host."), + default: false } } }); @@ -450,18 +459,33 @@ registerAction2(class extends Action2 { } async run(accessor: ServicesAccessor, id: string) { - const configurationService = accessor.get(IConfigurationService); - const ignoredExtensions = [...configurationService.getValue('sync.ignoredExtensions')]; - const index = ignoredExtensions.findIndex(ignoredExtension => areSameExtensions({ id: ignoredExtension }, { id })); - if (index !== -1) { - ignoredExtensions.splice(index, 1); - } else { - ignoredExtensions.push(id); + const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService); + const extension = extensionsWorkbenchService.local.find(e => areSameExtensions({ id }, e.identifier)); + if (extension) { + return extensionsWorkbenchService.toggleExtensionIgnoredToSync(extension); } - return configurationService.updateValue('sync.ignoredExtensions', ignoredExtensions.length ? ignoredExtensions : undefined, ConfigurationTarget.USER); } }); +function overrideActionForActiveExtensionEditorWebview(command: MultiCommand | undefined, f: (webview: Webview) => void) { + command?.addImplementation(105, (accessor) => { + const editorService = accessor.get(IEditorService); + const editor = editorService.activeEditorPane; + if (editor instanceof ExtensionEditor) { + if (editor.activeWebview) { + f(editor.activeWebview); + return true; + } + } + return false; + }); +} + +overrideActionForActiveExtensionEditorWebview(CopyAction, webview => webview.copy()); +overrideActionForActiveExtensionEditorWebview(CutAction, webview => webview.cut()); +overrideActionForActiveExtensionEditorWebview(PasteAction, webview => webview.paste()); + + const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); class ExtensionsContributions implements IWorkbenchContribution { @@ -470,9 +494,10 @@ class ExtensionsContributions implements IWorkbenchContribution { @IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService ) { - const canManageExtensions = extensionManagementServerService.localExtensionManagementServer || extensionManagementServerService.remoteExtensionManagementServer; - - if (canManageExtensions) { + if (extensionManagementServerService.localExtensionManagementServer + || extensionManagementServerService.remoteExtensionManagementServer + || extensionManagementServerService.webExtensionManagementServer + ) { Registry.as(Extensions.Quickaccess).registerQuickAccessProvider({ ctor: InstallExtensionQuickAccessProvider, prefix: InstallExtensionQuickAccessProvider.PREFIX, diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index a08e08da5cd..889bfcebc0d 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -5,12 +5,11 @@ import 'vs/css!./media/extensionActions'; import { localize } from 'vs/nls'; -import { IAction, Action } from 'vs/base/common/actions'; +import { IAction, Action, Separator, SubmenuAction } from 'vs/base/common/actions'; import { Delayer } from 'vs/base/common/async'; import * as DOM from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; import * as json from 'vs/base/common/json'; -import { ActionViewItem, Separator, IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionbar'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { dispose, Disposable } from 'vs/base/common/lifecycle'; import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, AutoUpdateConfigurationKey, IExtensionContainer, EXTENSIONS_CONFIG, TOGGLE_IGNORE_EXTENSION_ACTION_ID } from 'vs/workbench/contrib/extensions/common/extensions'; @@ -26,7 +25,7 @@ import { Query } from 'vs/workbench/contrib/extensions/common/extensionQuery'; import { IFileService, IFileContent } from 'vs/platform/files/common/files'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -60,6 +59,8 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { Codicon } from 'vs/base/common/codicons'; +import { IViewsService } from 'vs/workbench/common/views'; +import { IActionViewItemOptions, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; export function toExtensionDescription(local: ILocalExtension): IExtensionDescription { return { @@ -709,7 +710,7 @@ export class DropDownMenuActionViewItem extends ExtensionActionViewItem { } } -export function getContextMenuActions(menuService: IMenuService, contextKeyService: IContextKeyService, instantiationService: IInstantiationService, extension: IExtension | undefined | null): ExtensionAction[][] { +export function getContextMenuActions(menuService: IMenuService, contextKeyService: IContextKeyService, instantiationService: IInstantiationService, extension: IExtension | undefined | null): IAction[][] { const scopedContextKeyService = contextKeyService.createScoped(); if (extension) { scopedContextKeyService.createKey('extension', extension.identifier.id); @@ -720,9 +721,14 @@ export function getContextMenuActions(menuService: IMenuService, contextKeyServi } } - const groups: ExtensionAction[][] = []; + const groups: IAction[][] = []; const menu = menuService.createMenu(MenuId.ExtensionContext, scopedContextKeyService); - menu.getActions({ shouldForwardArgs: true }).forEach(([, actions]) => groups.push(actions.map(action => instantiationService.createInstance(MenuItemExtensionAction, action)))); + menu.getActions({ shouldForwardArgs: true }).forEach(([, actions]) => groups.push(actions.map(action => { + if (action instanceof SubmenuAction) { + return action; + } + return instantiationService.createInstance(MenuItemExtensionAction, action); + }))); menu.dispose(); return groups; @@ -751,7 +757,7 @@ export class ManageExtensionAction extends ExtensionDropDownAction { } async getActionGroups(runningExtensions: IExtensionDescription[]): Promise { - const groups: ExtensionAction[][] = []; + const groups: IAction[][] = []; if (this.extension) { const actions = await Promise.all([ SetColorThemeAction.create(this.workbenchThemeService, this.instantiationService, this.extension), @@ -782,7 +788,11 @@ export class ManageExtensionAction extends ExtensionDropDownAction { getContextMenuActions(this.menuService, this.contextKeyService, this.instantiationService, this.extension).forEach(actions => groups.push(actions)); - groups.forEach(group => group.forEach(extensionAction => extensionAction.extension = this.extension)); + groups.forEach(group => group.forEach(extensionAction => { + if (extensionAction instanceof ExtensionAction) { + extensionAction.extension = this.extension; + } + })); return groups; } @@ -808,7 +818,7 @@ export class MenuItemExtensionAction extends ExtensionAction { constructor( private readonly action: IAction, - @IConfigurationService private readonly configurationService: IConfigurationService + @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, ) { super(action.id, action.label); } @@ -818,7 +828,7 @@ export class MenuItemExtensionAction extends ExtensionAction { return; } if (this.action.id === TOGGLE_IGNORE_EXTENSION_ACTION_ID) { - this.checked = !this.configurationService.getValue('sync.ignoredExtensions').some(id => areSameExtensions({ id }, this.extension!.identifier)); + this.checked = !this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension); } } @@ -928,7 +938,7 @@ export class EnableGloballyAction extends ExtensionAction { this.enabled = false; if (this.extension && this.extension.local) { this.enabled = this.extension.state === ExtensionState.Installed - && this.extension.enablementState === EnablementState.DisabledGlobally + && this.extensionEnablementService.isDisabledGlobally(this.extension.local) && this.extensionEnablementService.canChangeEnablement(this.extension.local); } } @@ -1082,7 +1092,6 @@ export class CheckForUpdatesAction extends Action { @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, @IViewletService private readonly viewletService: IViewletService, - @IDialogService private readonly dialogService: IDialogService, @INotificationService private readonly notificationService: INotificationService ) { super(id, label, '', true); @@ -1091,7 +1100,7 @@ export class CheckForUpdatesAction extends Action { private checkUpdatesAndNotify(): void { const outdated = this.extensionsWorkbenchService.outdated; if (!outdated.length) { - this.dialogService.show(Severity.Info, localize('noUpdatesAvailable', "All extensions are up to date."), [localize('ok', "OK")]); + this.notificationService.info(localize('noUpdatesAvailable', "All extensions are up to date.")); return; } @@ -1261,7 +1270,7 @@ export class ReloadAction extends ExtensionAction { const isUninstalled = this.extension.state === ExtensionState.Uninstalled; const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))[0]; - const isSameExtensionRunning = runningExtension && this.extension.server === this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation); + const isSameExtensionRunning = runningExtension && this.extension.server === this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension)); if (isUninstalled) { if (isSameExtensionRunning && !this.extensionService.canRemoveExtension(runningExtension)) { @@ -1282,7 +1291,7 @@ export class ReloadAction extends ExtensionAction { if (this.extensionService.canAddExtension(toExtensionDescription(this.extension.local))) { return; } - const runningExtensionServer = this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation); + const runningExtensionServer = this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension)); if (isSameExtensionRunning) { // Different version of same extension is running. Requires reload to run the current version @@ -1641,19 +1650,39 @@ export class ShowDisabledExtensionsAction extends Action { } } -export class ClearExtensionsInputAction extends Action { +export class ClearExtensionsSearchResultsAction extends Action { - static readonly ID = 'workbench.extensions.action.clearExtensionsInput'; - static readonly LABEL = localize('clearExtensionsInput', "Clear Extensions Input"); + static readonly ID = 'workbench.extensions.action.clearExtensionsSearchResults'; + static readonly LABEL = localize('clearExtensionsSearchResults', "Clear Extensions Search Results"); + + constructor( + id: string, + label: string, + @IViewsService private readonly viewsService: IViewsService + ) { + super(id, label, 'codicon-clear-all', true); + } + + async run(): Promise { + const viewPaneContainer = this.viewsService.getActiveViewPaneContainerWithId(VIEWLET_ID); + if (viewPaneContainer) { + const extensionsViewPaneContainer = viewPaneContainer as IExtensionsViewPaneContainer; + extensionsViewPaneContainer.search(''); + extensionsViewPaneContainer.focus(); + } + } +} + +export class ClearExtensionsInputAction extends ClearExtensionsSearchResultsAction { constructor( id: string, label: string, onSearchChange: Event, value: string, - @IViewletService private readonly viewletService: IViewletService + @IViewsService viewsService: IViewsService ) { - super(id, label, 'codicon-clear-all', true); + super(id, label, viewsService); this.onSearchChange(value); this._register(onSearchChange(this.onSearchChange, this)); } @@ -1662,14 +1691,6 @@ export class ClearExtensionsInputAction extends Action { this.enabled = !!value; } - run(): Promise { - return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) - .then(viewlet => { - viewlet.search(''); - viewlet.focus(); - }); - } } export class ShowBuiltInExtensionsAction extends Action { @@ -1735,7 +1756,51 @@ export class ShowPopularExtensionsAction extends Action { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { - viewlet.search('@sort:installs '); + viewlet.search('@popular '); + viewlet.focus(); + }); + } +} + +export class PredefinedExtensionFilterAction extends Action { + + constructor( + id: string, + label: string, + private readonly filter: string, + @IViewletService private readonly viewletService: IViewletService + ) { + super(id, label, undefined, true); + } + + run(): Promise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) + .then(viewlet => { + viewlet.search(`${this.filter} `); + viewlet.focus(); + }); + } +} + +export class RecentlyPublishedExtensionsAction extends Action { + + static readonly ID = 'workbench.extensions.action.recentlyPublishedExtensions'; + static readonly LABEL = localize('recentlyPublishedExtensions', "Recently Published Extensions"); + + constructor( + id: string, + label: string, + @IViewletService private readonly viewletService: IViewletService + ) { + super(id, label, undefined, true); + } + + run(): Promise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) + .then(viewlet => { + viewlet.search('@sort:publishedDate '); viewlet.focus(); }); } @@ -1827,6 +1892,40 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action { } } +export class ShowRecommendedExtensionAction extends Action { + + static readonly ID = 'workbench.extensions.action.showRecommendedExtension'; + static readonly LABEL = localize('showRecommendedExtension', "Show Recommended Extension"); + + private extensionId: string; + + constructor( + extensionId: string, + @IViewletService private readonly viewletService: IViewletService, + @IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService, + ) { + super(InstallRecommendedExtensionAction.ID, InstallRecommendedExtensionAction.LABEL, undefined, false); + this.extensionId = extensionId; + } + + run(): Promise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) + .then(viewlet => { + viewlet.search(`@id:${this.extensionId}`); + viewlet.focus(); + return this.extensionWorkbenchService.queryGallery({ names: [this.extensionId], source: 'install-recommendation', pageSize: 1 }, CancellationToken.None) + .then(pager => { + if (pager && pager.firstPage && pager.firstPage.length) { + const extension = pager.firstPage[0]; + return this.extensionWorkbenchService.open(extension); + } + return null; + }); + }); + } +} + export class InstallRecommendedExtensionAction extends Action { static readonly ID = 'workbench.extensions.action.installRecommendedExtension'; @@ -1855,6 +1954,7 @@ export class InstallRecommendedExtensionAction extends Action { if (pager && pager.firstPage && pager.firstPage.length) { const extension = pager.firstPage[0]; return this.extensionWorkbenchService.install(extension) + .then(() => this.extensionWorkbenchService.open(extension)) .then(() => null, err => { console.error(err); return promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.identifier.id), err, this.instantiationService); @@ -1981,6 +2081,27 @@ export class ShowAzureExtensionsAction extends Action { } } +export class SearchCategoryAction extends Action { + + constructor( + id: string, + label: string, + private readonly category: string, + @IViewletService private readonly viewletService: IViewletService + ) { + super(id, label, undefined, true); + } + + run(): Promise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) + .then(viewlet => { + viewlet.search(`@category:"${this.category.toLowerCase()}"`); + viewlet.focus(); + }); + } +} + export class ChangeSortAction extends Action { private query: Query; @@ -2195,7 +2316,7 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio return this.jsonEditingService.write(workspaceConfigurationFile, [{ - key: 'extensions', + path: ['extensions'], value: { recommendations: shouldRecommend ? insertInto : removeFrom, unwantedRecommendations: shouldRecommend ? removeFrom : insertInto @@ -2224,7 +2345,7 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio removeFrom = removeFrom.filter(x => x.toLowerCase() !== extensionIdLowerCase); removeFromPromise = this.jsonEditingService.write(extensionsFileResource, [{ - key: shouldRecommend ? 'unwantedRecommendations' : 'recommendations', + path: shouldRecommend ? ['unwantedRecommendations'] : ['recommendations'], value: removeFrom }], true); @@ -2233,7 +2354,7 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio return removeFromPromise.then(() => this.jsonEditingService.write(extensionsFileResource, [{ - key: shouldRecommend ? 'recommendations' : 'unwantedRecommendations', + path: shouldRecommend ? ['recommendations'] : ['unwantedRecommendations'], value: insertInto }], true) @@ -2260,7 +2381,7 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio .then(content => { const workspaceRecommendations = json.parse(content.value.toString())['extensions']; if (!workspaceRecommendations || !workspaceRecommendations.recommendations) { - return this.jsonEditingService.write(workspaceConfigurationFile, [{ key: 'extensions', value: { recommendations: [] } }], true) + return this.jsonEditingService.write(workspaceConfigurationFile, [{ path: ['extensions'], value: { recommendations: [] } }], true) .then(() => this.fileService.readFile(workspaceConfigurationFile)); } return content; @@ -2591,7 +2712,7 @@ export class StatusLabelAction extends Action implements IExtensionContainer { }; const canRemoveExtension = () => { if (this.extension!.local) { - if (runningExtensions.every(e => !(areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier) && this.extension!.server === this.extensionManagementServerService.getExtensionManagementServer(e.extensionLocation)))) { + if (runningExtensions.every(e => !(areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier) && this.extension!.server === this.extensionManagementServerService.getExtensionManagementServer(toExtension(e))))) { return true; } return this.extensionService.canRemoveExtension(toExtensionDescription(this.extension!.local)); @@ -2636,7 +2757,7 @@ export class MaliciousStatusLabelAction extends ExtensionAction { constructor(long: boolean) { const tooltip = localize('malicious tooltip', "This extension was reported to be problematic."); - const label = long ? tooltip : localize('malicious', "Malicious"); + const label = long ? tooltip : localize({ key: 'malicious', comment: ['Refers to a malicious extension'] }, "Malicious"); super('extensions.install', label, '', false); this.tooltip = localize('malicious tooltip', "This extension was reported to be problematic."); } @@ -2660,7 +2781,8 @@ export class SyncIgnoredIconAction extends ExtensionAction { private static readonly DISABLE_CLASS = `${SyncIgnoredIconAction.ENABLE_CLASS} hide`; constructor( - @IConfigurationService private readonly configurationService: IConfigurationService + @IConfigurationService private readonly configurationService: IConfigurationService, + @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, ) { super('extensions.syncignore', '', SyncIgnoredIconAction.DISABLE_CLASS, false); this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectedKeys.includes('sync.ignoredExtensions'))(() => this.update())); @@ -2670,11 +2792,8 @@ export class SyncIgnoredIconAction extends ExtensionAction { update(): void { this.class = SyncIgnoredIconAction.DISABLE_CLASS; - if (this.extension) { - const ignoredExtensions = this.configurationService.getValue('sync.ignoredExtensions') || []; - if (ignoredExtensions.some(id => areSameExtensions({ id }, this.extension!.identifier))) { - this.class = SyncIgnoredIconAction.ENABLE_CLASS; - } + if (this.extension && this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension)) { + this.class = SyncIgnoredIconAction.ENABLE_CLASS; } } @@ -2825,7 +2944,7 @@ export class SystemDisabledWarningAction extends ExtensionAction { } if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) { const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))[0]; - const runningExtensionServer = runningExtension ? this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation) : null; + const runningExtensionServer = runningExtension ? this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension)) : null; if (this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && runningExtensionServer === this.extensionManagementServerService.remoteExtensionManagementServer) { if (prefersExecuteOnWorkspace(this.extension.local!.manifest, this.productService, this.configurationService)) { this.class = `${SystemDisabledWarningAction.INFO_CLASS}`; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsDependencyChecker.ts b/src/vs/workbench/contrib/extensions/browser/extensionsDependencyChecker.ts index 206a53472e1..382344da69b 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsDependencyChecker.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsDependencyChecker.ts @@ -9,7 +9,6 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { localize } from 'vs/nls'; -import { values } from 'vs/base/common/map'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { Action } from 'vs/base/common/actions'; @@ -55,7 +54,7 @@ export class ExtensionDependencyChecker extends Disposable implements IWorkbench }); } } - return values(missingDependencies); + return [...missingDependencies.values()]; } private async installMissingDependencies(): Promise { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index 5eb1fff35fd..4c8e6373219 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -17,10 +17,13 @@ import { IExtension, ExtensionContainers, ExtensionState, IExtensionsWorkbenchSe import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, MaliciousStatusLabelAction, ExtensionActionViewItem, StatusLabelAction, RemoteInstallAction, SystemDisabledWarningAction, ExtensionToolTipAction, LocalInstallAction, SyncIgnoredIconAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { Label, RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, TooltipWidget, ExtensionPackCountWidget as ExtensionPackBadgeWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; +import { registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { foreground, listActiveSelectionForeground, listActiveSelectionBackground, listInactiveSelectionForeground, listInactiveSelectionBackground, listFocusForeground, listFocusBackground, listHoverForeground, listHoverBackground } from 'vs/platform/theme/common/colorRegistry'; +import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme'; export interface IExtensionsViewState { onFocus: Event; @@ -120,10 +123,10 @@ export class Renderer implements IPagedRenderer { const extensionContainers: ExtensionContainers = this.instantiationService.createInstance(ExtensionContainers, [...actions, ...widgets, extensionTooltipAction]); actionbar.push(actions, actionOptions); - const disposables = combinedDisposable(...actions, ...widgets, actionbar, extensionContainers, extensionTooltipAction); + const disposable = combinedDisposable(...actions, ...widgets, actionbar, extensionContainers, extensionTooltipAction); return { - root, element, icon, name, installCount, ratings, author, description, disposables: [disposables], actionbar, + root, element, icon, name, installCount, ratings, author, description, disposables: [disposable], actionbar, extensionDisposables: [], set extension(extension: IExtension) { extensionContainers.extension = extension; @@ -155,15 +158,15 @@ export class Renderer implements IPagedRenderer { data.extensionDisposables = dispose(data.extensionDisposables); + let isDisabled: boolean = false; const updateEnablement = async () => { const runningExtensions = await this.extensionService.getExtensions(); + isDisabled = false; if (extension.local && !isLanguagePackExtension(extension.local.manifest)) { const runningExtension = runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, extension.identifier))[0]; - const isSameExtensionRunning = runningExtension && extension.server === this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation); - toggleClass(data.root, 'disabled', !isSameExtensionRunning); - } else { - removeClass(data.root, 'disabled'); + isDisabled = !(runningExtension && extension.server === this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension))); } + toggleClass(data.root, 'disabled', isDisabled); }; updateEnablement(); this.extensionService.onDidChangeExtensions(() => updateEnablement(), this, data.extensionDisposables); @@ -208,3 +211,50 @@ export class Renderer implements IPagedRenderer { data.disposables = dispose(data.disposables); } } + +registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { + const foregroundColor = theme.getColor(foreground); + if (foregroundColor) { + const authorForeground = foregroundColor.transparent(.9).makeOpaque(WORKBENCH_BACKGROUND(theme)); + collector.addRule(`.extensions-list .monaco-list .monaco-list-row:not(.disabled) .author { color: ${authorForeground}; }`); + const disabledExtensionForeground = foregroundColor.transparent(.5).makeOpaque(WORKBENCH_BACKGROUND(theme)); + collector.addRule(`.extensions-list .monaco-list .monaco-list-row.disabled { color: ${disabledExtensionForeground}; }`); + } + + const listActiveSelectionForegroundColor = theme.getColor(listActiveSelectionForeground); + const listActiveSelectionBackgroundColor = theme.getColor(listActiveSelectionBackground); + if (listActiveSelectionForegroundColor && listActiveSelectionBackgroundColor) { + const authorForeground = listActiveSelectionForegroundColor.transparent(.9).makeOpaque(listActiveSelectionBackgroundColor); + collector.addRule(`.extensions-list .monaco-list:focus .monaco-list-row:not(.disabled).selected .author { color: ${authorForeground}; }`); + const disabledExtensionForeground = listActiveSelectionForegroundColor.transparent(.5).makeOpaque(listActiveSelectionBackgroundColor); + collector.addRule(`.extensions-list .monaco-list:focus .monaco-list-row.disabled.selected { color: ${disabledExtensionForeground}; }`); + } + + const listInactiveSelectionForegroundColor = theme.getColor(listInactiveSelectionForeground); + const listInactiveSelectionBackgroundColor = theme.getColor(listInactiveSelectionBackground); + if (listInactiveSelectionForegroundColor && listInactiveSelectionBackgroundColor) { + const authorForeground = listInactiveSelectionForegroundColor.transparent(.9).makeOpaque(listInactiveSelectionBackgroundColor); + collector.addRule(`.extensions-list .monaco-list .monaco-list-row:not(.disabled).selected .author { color: ${authorForeground}; }`); + const disabledExtensionForeground = listInactiveSelectionForegroundColor.transparent(.5).makeOpaque(listInactiveSelectionBackgroundColor); + collector.addRule(`.extensions-list .monaco-list .monaco-list-row.disabled.selected { color: ${disabledExtensionForeground}; }`); + } + + const listFocusForegroundColor = theme.getColor(listFocusForeground); + const listFocusBackgroundColor = theme.getColor(listFocusBackground); + if (listFocusForegroundColor && listFocusBackgroundColor) { + const authorForeground = listFocusForegroundColor.transparent(.9).makeOpaque(listFocusBackgroundColor); + collector.addRule(`.extensions-list .monaco-list:focus .monaco-list-row:not(.disabled).focused .author { color: ${authorForeground}; }`); + const disabledExtensionForeground = listFocusForegroundColor.transparent(.5).makeOpaque(listFocusBackgroundColor); + collector.addRule(`.extensions-list .monaco-list:focus .monaco-list-row.disabled.focused { color: ${disabledExtensionForeground}; }`); + } + + const listHoverForegroundColor = theme.getColor(listHoverForeground); + const listHoverBackgroundColor = theme.getColor(listHoverBackground); + if (listHoverForegroundColor && listHoverBackgroundColor) { + const authorForeground = listHoverForegroundColor.transparent(.9).makeOpaque(listHoverBackgroundColor); + collector.addRule(`.extensions-list .monaco-list .monaco-list-row:hover:not(.disabled):not(.selected):.not(.focused) .author { color: ${authorForeground}; }`); + const disabledExtensionForeground = listHoverForegroundColor.transparent(.5).makeOpaque(listHoverBackgroundColor); + collector.addRule(`.extensions-list .monaco-list .monaco-list-row.disabled:hover:not(.selected):.not(.focused) { color: ${disabledExtensionForeground}; }`); + } +}); + diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts index 89ac951350f..5ee60dcc409 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts @@ -271,7 +271,8 @@ export class ExtensionsTree extends WorkbenchAsyncDataTree>{ getAriaLabel(extensionData: IExtensionData): string { - return localize('extension-arialabel', "{0}. Press enter for extension details.", extensionData.extension.displayName); + const extension = extensionData.extension; + return localize('extension-arialabel', "{0}, {1}, {2}, press enter for extension details.", extension.displayName, extension.version, extension.publisherDisplayName); }, getWidgetAriaLabel(): string { return localize('extensions', "Extensions"); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index ff63e6e1c02..a154ca87f76 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -10,24 +10,24 @@ import { isPromiseCanceledError } from 'vs/base/common/errors'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { Event as EventOf, Emitter } from 'vs/base/common/event'; -import { IAction, Action } from 'vs/base/common/actions'; -import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IAction, Action, Separator, SubmenuAction } from 'vs/base/common/actions'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { append, $, addClass, toggleClass, Dimension, hide, show } from 'vs/base/browser/dom'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, AutoUpdateConfigurationKey, ShowRecommendationsOnlyOnDemandKey, CloseExtensionDetailsOnViewChangeKey } from '../common/extensions'; +import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, AutoUpdateConfigurationKey, CloseExtensionDetailsOnViewChangeKey } from '../common/extensions'; import { - ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowDisabledExtensionsAction, - ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction, - EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction + ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction, + EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction, SearchCategoryAction, + RecentlyPublishedExtensionsAction, ShowInstalledExtensionsAction, ShowOutdatedExtensionsAction, ShowDisabledExtensionsAction, + ShowEnabledExtensionsAction, PredefinedExtensionFilterAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; -import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; -import { ExtensionsListView, EnabledExtensionsView, DisabledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, BuiltInExtensionsView, BuiltInThemesExtensionsView, BuiltInBasicsExtensionsView, ServerExtensionsView, DefaultRecommendedExtensionsView } from 'vs/workbench/contrib/extensions/browser/extensionsViews'; +import { ExtensionsListView, EnabledExtensionsView, DisabledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, BuiltInFeatureExtensionsView, BuiltInThemesExtensionsView, BuiltInProgrammingLanguageExtensionsView, ServerExtensionsView, DefaultRecommendedExtensionsView, OutdatedExtensionsView, InstalledExtensionsView, SearchBuiltInExtensionsView } from 'vs/workbench/contrib/extensions/browser/extensionsViews'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import Severity from 'vs/base/common/severity'; @@ -50,9 +50,8 @@ import { SuggestEnabledInput, attachSuggestEnabledInputBoxStyler } from 'vs/work import { alert } from 'vs/base/browser/ui/aria/aria'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { ExtensionType, EXTENSION_CATEGORIES } from 'vs/platform/extensions/common/extensions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { RemoteNameContext } from 'vs/workbench/browser/contextkeys'; import { ILabelService } from 'vs/platform/label/common/label'; import { MementoObject } from 'vs/workbench/common/memento'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; @@ -69,24 +68,9 @@ const SearchOutdatedExtensionsContext = new RawContextKey('searchOutdat const SearchEnabledExtensionsContext = new RawContextKey('searchEnabledExtensions', false); const SearchDisabledExtensionsContext = new RawContextKey('searchDisabledExtensions', false); const HasInstalledExtensionsContext = new RawContextKey('hasInstalledExtensions', true); +const BuiltInExtensionsContext = new RawContextKey('builtInExtensions', false); const SearchBuiltInExtensionsContext = new RawContextKey('searchBuiltInExtensions', false); const RecommendedExtensionsContext = new RawContextKey('recommendedExtensions', false); -const DefaultRecommendedExtensionsContext = new RawContextKey('defaultRecommendedExtensions', false); -const viewIdNameMappings: { [id: string]: string } = { - 'extensions.listView': localize('marketPlace', "Marketplace"), - 'extensions.enabledExtensionList': localize('enabledExtensions', "Enabled"), - 'extensions.enabledExtensionList2': localize('enabledExtensions', "Enabled"), - 'extensions.disabledExtensionList': localize('disabledExtensions', "Disabled"), - 'extensions.disabledExtensionList2': localize('disabledExtensions', "Disabled"), - 'extensions.popularExtensionsList': localize('popularExtensions', "Popular"), - 'extensions.recommendedList': localize('recommendedExtensions', "Recommended"), - 'extensions.otherrecommendedList': localize('otherRecommendedExtensions', "Other Recommendations"), - 'extensions.workspaceRecommendedList': localize('workspaceRecommendedExtensions', "Workspace Recommendations"), - 'extensions.builtInExtensionsList': localize('builtInExtensions', "Features"), - 'extensions.builtInThemesExtensionsList': localize('builtInThemesExtensions', "Themes"), - 'extensions.builtInBasicsExtensionsList': localize('builtInBasicsExtensions', "Programming Languages"), - 'extensions.syncedExtensionsList': localize('syncedExtensions', "My Account"), -}; export class ExtensionsViewletViewsContribution implements IWorkbenchContribution { @@ -102,220 +86,236 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio } private registerViews(): void { - let viewDescriptors: IViewDescriptor[] = []; - viewDescriptors.push(this.createMarketPlaceExtensionsListViewDescriptor()); - viewDescriptors.push(this.createDefaultEnabledExtensionsListViewDescriptor()); - viewDescriptors.push(this.createDefaultDisabledExtensionsListViewDescriptor()); - viewDescriptors.push(this.createDefaultPopularExtensionsListViewDescriptor()); - viewDescriptors.push(this.createEnabledExtensionsListViewDescriptor()); - viewDescriptors.push(this.createDisabledExtensionsListViewDescriptor()); - viewDescriptors.push(this.createBuiltInExtensionsListViewDescriptor()); - viewDescriptors.push(this.createBuiltInBasicsExtensionsListViewDescriptor()); - viewDescriptors.push(this.createBuiltInThemesExtensionsListViewDescriptor()); - viewDescriptors.push(this.createDefaultRecommendedExtensionsListViewDescriptor()); - viewDescriptors.push(this.createOtherRecommendedExtensionsListViewDescriptor()); - viewDescriptors.push(this.createWorkspaceRecommendedExtensionsListViewDescriptor()); + const viewDescriptors: IViewDescriptor[] = []; - if (this.extensionManagementServerService.localExtensionManagementServer) { - viewDescriptors.push(...this.createExtensionsViewDescriptorsForServer(this.extensionManagementServerService.localExtensionManagementServer)); - } - if (this.extensionManagementServerService.remoteExtensionManagementServer) { - viewDescriptors.push(...this.createExtensionsViewDescriptorsForServer(this.extensionManagementServerService.remoteExtensionManagementServer)); - } + /* Default views */ + viewDescriptors.push(...this.createDefaultExtensionsViewDescriptors()); + + /* Search views */ + viewDescriptors.push(...this.createSearchExtensionsViewDescriptors()); + + /* Recommendations views */ + viewDescriptors.push(...this.createRecommendedExtensionsViewDescriptors()); + + /* Built-in extensions views */ + viewDescriptors.push(...this.createBuiltinExtensionsViewDescriptors()); Registry.as(Extensions.ViewsRegistry).registerViews(viewDescriptors, this.container); } - // View used for any kind of searching - private createMarketPlaceExtensionsListViewDescriptor(): IViewDescriptor { - const id = 'extensions.listView'; - return { - id, - name: viewIdNameMappings[id], - ctorDescriptor: new SyncDescriptor(ExtensionsListView), - when: ContextKeyExpr.and(ContextKeyExpr.has('searchMarketplaceExtensions')), - weight: 100 - }; - } + private createDefaultExtensionsViewDescriptors(): IViewDescriptor[] { + const viewDescriptors: IViewDescriptor[] = []; - // Separate view for enabled extensions required as we need to show enabled, disabled and recommended sections - // in the default view when there is no search text, but user has installed extensions. - private createDefaultEnabledExtensionsListViewDescriptor(): IViewDescriptor { - const id = 'extensions.enabledExtensionList'; - return { - id, - name: viewIdNameMappings[id], - ctorDescriptor: new SyncDescriptor(EnabledExtensionsView), - when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteNameContext.isEqualTo('')), - weight: 40, - canToggleVisibility: true, - order: 1 - }; - } - - // Separate view for disabled extensions required as we need to show enabled, disabled and recommended sections - // in the default view when there is no search text, but user has installed extensions. - private createDefaultDisabledExtensionsListViewDescriptor(): IViewDescriptor { - const id = 'extensions.disabledExtensionList'; - return { - id, - name: viewIdNameMappings[id], - ctorDescriptor: new SyncDescriptor(DisabledExtensionsView), - when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteNameContext.isEqualTo('')), - weight: 10, - canToggleVisibility: true, - order: 3, - collapsed: true - }; - } - - // Separate view for popular extensions required as we need to show popular and recommended sections - // in the default view when there is no search text, and user has no installed extensions. - private createDefaultPopularExtensionsListViewDescriptor(): IViewDescriptor { - const id = 'extensions.popularExtensionsList'; - return { - id, - name: viewIdNameMappings[id], + /* + * Default popular extensions view + * Separate view for popular extensions required as we need to show popular and recommended sections + * in the default view when there is no search text, and user has no installed extensions. + */ + viewDescriptors.push({ + id: 'workbench.views.extensions.popular', + name: localize('popularExtensions', "Popular"), ctorDescriptor: new SyncDescriptor(ExtensionsListView), when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.not('hasInstalledExtensions')), weight: 60, - order: 1 - }; - } + order: 1, + }); - private createExtensionsViewDescriptorsForServer(server: IExtensionManagementServer): IViewDescriptor[] { + /* + * Default installed extensions views - Shows all user installed extensions. + */ + const servers: IExtensionManagementServer[] = []; + if (this.extensionManagementServerService.localExtensionManagementServer) { + servers.push(this.extensionManagementServerService.localExtensionManagementServer); + } + if (this.extensionManagementServerService.remoteExtensionManagementServer) { + servers.push(this.extensionManagementServerService.remoteExtensionManagementServer); + } + if (servers.length === 0 && this.extensionManagementServerService.webExtensionManagementServer) { + servers.push(this.extensionManagementServerService.webExtensionManagementServer); + } const getViewName = (viewTitle: string, server: IExtensionManagementServer): string => { - const serverLabel = server.label; - if (viewTitle && this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) { - return `${serverLabel} - ${viewTitle}`; - } - return viewTitle ? viewTitle : serverLabel; + return servers.length > 1 ? `${server.label} - ${viewTitle}` : viewTitle; }; - const getInstalledViewName = (): string => getViewName(localize('installed', "Installed"), server); - const getOutdatedViewName = (): string => getViewName(localize('outdated', "Outdated"), server); - const onDidChangeServerLabel: EventOf = EventOf.map(this.labelService.onDidChangeFormatters, () => undefined); - return [{ - id: `extensions.${server.authority}.installed`, - get name() { return getInstalledViewName(); }, - ctorDescriptor: new SyncDescriptor(ServerExtensionsView, [server, EventOf.map(onDidChangeServerLabel, () => getInstalledViewName())]), - when: ContextKeyExpr.and(ContextKeyExpr.has('searchInstalledExtensions')), - weight: 100 - }, { - id: `extensions.${server.authority}.outdated`, - get name() { return getOutdatedViewName(); }, - ctorDescriptor: new SyncDescriptor(ServerExtensionsView, [server, EventOf.map(onDidChangeServerLabel, () => getOutdatedViewName())]), - when: ContextKeyExpr.and(ContextKeyExpr.has('searchOutdatedExtensions')), - weight: 100 - }, { - id: `extensions.${server.authority}.default`, - get name() { return getInstalledViewName(); }, - ctorDescriptor: new SyncDescriptor(ServerExtensionsView, [server, EventOf.map(onDidChangeServerLabel, () => getInstalledViewName())]), - when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteNameContext.notEqualsTo('')), - weight: 40, - order: 1 - }]; - } + for (const server of servers) { + const getInstalledViewName = (): string => getViewName(localize('installed', "Installed"), server); + const onDidChangeServerLabel: EventOf = EventOf.map(this.labelService.onDidChangeFormatters, () => undefined); + viewDescriptors.push({ + id: servers.length > 1 ? `workbench.views.extensions.${server.id}.installed` : `workbench.views.extensions.installed`, + get name() { return getInstalledViewName(); }, + ctorDescriptor: new SyncDescriptor(ServerExtensionsView, [server, EventOf.map(onDidChangeServerLabel, () => getInstalledViewName())]), + when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions')), + weight: 100, + order: 2, + /* Installed extensions views shall not be hidden when there are more than one server */ + canToggleVisibility: servers.length === 1 + }); + } - // Separate view for recommended extensions required as we need to show it along with other views when there is no search text. - // When user has installed extensions, this is shown along with the views for enabled & disabled extensions - // When user has no installed extensions, this is shown along with the view for popular extensions - private createDefaultRecommendedExtensionsListViewDescriptor(): IViewDescriptor { - const id = 'extensions.recommendedList'; - return { - id, - name: viewIdNameMappings[id], + /* + * Default recommended extensions view + * When user has installed extensions, this is shown along with the views for enabled & disabled extensions + * When user has no installed extensions, this is shown along with the view for popular extensions + */ + viewDescriptors.push({ + id: 'extensions.recommendedList', + name: localize('recommendedExtensions', "Recommended"), ctorDescriptor: new SyncDescriptor(DefaultRecommendedExtensionsView), - when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('defaultRecommendedExtensions')), + when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.not('config.extensions.showRecommendationsOnlyOnDemand')), weight: 40, - order: 2, + order: 3, canToggleVisibility: true - }; + }); + + /* Installed views shall be default in multi server window */ + if (servers.length === 1) { + /* + * Default enabled extensions view - Shows all user installed enabled extensions. + * Hidden by default + */ + viewDescriptors.push({ + id: 'workbench.views.extensions.enabled', + name: localize('enabledExtensions', "Enabled"), + ctorDescriptor: new SyncDescriptor(EnabledExtensionsView), + when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions')), + hideByDefault: true, + weight: 40, + order: 4, + canToggleVisibility: true + }); + + /* + * Default disabled extensions view - Shows all disabled extensions. + * Hidden by default + */ + viewDescriptors.push({ + id: 'workbench.views.extensions.disabled', + name: localize('disabledExtensions', "Disabled"), + ctorDescriptor: new SyncDescriptor(DisabledExtensionsView), + when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions')), + hideByDefault: true, + weight: 10, + order: 5, + canToggleVisibility: true + }); + + } + + return viewDescriptors; } - // Separate view for recommedations that are not workspace recommendations. - // Shown along with view for workspace recommendations, when using the command that shows recommendations - private createOtherRecommendedExtensionsListViewDescriptor(): IViewDescriptor { - const id = 'extensions.otherrecommendedList'; - return { - id, - name: viewIdNameMappings[id], - ctorDescriptor: new SyncDescriptor(RecommendedExtensionsView), - when: ContextKeyExpr.has('recommendedExtensions'), - weight: 50, - order: 2 - }; - } + private createSearchExtensionsViewDescriptors(): IViewDescriptor[] { + const viewDescriptors: IViewDescriptor[] = []; - // Separate view for workspace recommendations. - // Shown along with view for other recommendations, when using the command that shows recommendations - private createWorkspaceRecommendedExtensionsListViewDescriptor(): IViewDescriptor { - const id = 'extensions.workspaceRecommendedList'; - return { - id, - name: viewIdNameMappings[id], - ctorDescriptor: new SyncDescriptor(WorkspaceRecommendedExtensionsView), - when: ContextKeyExpr.and(ContextKeyExpr.has('recommendedExtensions'), ContextKeyExpr.has('nonEmptyWorkspace')), - weight: 50, - order: 1 - }; - } + /* + * View used for searching Marketplace + */ + viewDescriptors.push({ + id: 'workbench.views.extensions.marketplace', + name: localize('marketPlace', "Marketplace"), + ctorDescriptor: new SyncDescriptor(ExtensionsListView), + when: ContextKeyExpr.and(ContextKeyExpr.has('searchMarketplaceExtensions')), + }); - private createEnabledExtensionsListViewDescriptor(): IViewDescriptor { - const id = 'extensions.enabledExtensionList2'; - return { - id, - name: viewIdNameMappings[id], + /* + * View used for searching all installed extensions + */ + viewDescriptors.push({ + id: 'workbench.views.extensions.searchInstalled', + name: localize('installed', "Installed"), + ctorDescriptor: new SyncDescriptor(InstalledExtensionsView), + when: ContextKeyExpr.and(ContextKeyExpr.has('searchInstalledExtensions')), + }); + + /* + * View used for searching enabled extensions + */ + viewDescriptors.push({ + id: 'workbench.views.extensions.searchEnabled', + name: localize('enabled', "Enabled"), ctorDescriptor: new SyncDescriptor(EnabledExtensionsView), when: ContextKeyExpr.and(ContextKeyExpr.has('searchEnabledExtensions')), - weight: 40, - order: 1 - }; - } + }); - private createDisabledExtensionsListViewDescriptor(): IViewDescriptor { - const id = 'extensions.disabledExtensionList2'; - return { - id, - name: viewIdNameMappings[id], + /* + * View used for searching disabled extensions + */ + viewDescriptors.push({ + id: 'workbench.views.extensions.searchDisabled', + name: localize('disabled', "Disabled"), ctorDescriptor: new SyncDescriptor(DisabledExtensionsView), when: ContextKeyExpr.and(ContextKeyExpr.has('searchDisabledExtensions')), - weight: 10, - order: 3, - collapsed: true - }; + }); + + /* + * View used for searching outdated extensions + */ + viewDescriptors.push({ + id: 'workbench.views.extensions.searchOutdated', + name: localize('outdated', "Outdated"), + ctorDescriptor: new SyncDescriptor(OutdatedExtensionsView), + when: ContextKeyExpr.and(ContextKeyExpr.has('searchOutdatedExtensions')), + }); + + /* + * View used for searching builtin extensions + */ + viewDescriptors.push({ + id: 'workbench.views.extensions.searchBuiltin', + name: localize('builtin', "Builtin"), + ctorDescriptor: new SyncDescriptor(SearchBuiltInExtensionsView), + when: ContextKeyExpr.and(ContextKeyExpr.has('searchBuiltInExtensions')), + }); + + return viewDescriptors; } - private createBuiltInExtensionsListViewDescriptor(): IViewDescriptor { - const id = 'extensions.builtInExtensionsList'; - return { - id, - name: viewIdNameMappings[id], - ctorDescriptor: new SyncDescriptor(BuiltInExtensionsView), - when: ContextKeyExpr.has('searchBuiltInExtensions'), - weight: 100 - }; + private createRecommendedExtensionsViewDescriptors(): IViewDescriptor[] { + const viewDescriptors: IViewDescriptor[] = []; + + viewDescriptors.push({ + id: 'workbench.views.extensions.workspaceRecommendations', + name: localize('workspaceRecommendedExtensions', "Workspace Recommendations"), + ctorDescriptor: new SyncDescriptor(WorkspaceRecommendedExtensionsView), + when: ContextKeyExpr.and(ContextKeyExpr.has('recommendedExtensions'), ContextKeyExpr.has('nonEmptyWorkspace')), + order: 1 + }); + + viewDescriptors.push({ + id: 'workbench.views.extensions.otherRecommendations', + name: localize('otherRecommendedExtensions', "Other Recommendations"), + ctorDescriptor: new SyncDescriptor(RecommendedExtensionsView), + when: ContextKeyExpr.has('recommendedExtensions'), + order: 2 + }); + + return viewDescriptors; } - private createBuiltInThemesExtensionsListViewDescriptor(): IViewDescriptor { - const id = 'extensions.builtInThemesExtensionsList'; - return { - id, - name: viewIdNameMappings[id], + private createBuiltinExtensionsViewDescriptors(): IViewDescriptor[] { + const viewDescriptors: IViewDescriptor[] = []; + + viewDescriptors.push({ + id: 'workbench.views.extensions.builtinFeatureExtensions', + name: localize('builtinFeatureExtensions', "Features"), + ctorDescriptor: new SyncDescriptor(BuiltInFeatureExtensionsView), + when: ContextKeyExpr.has('builtInExtensions'), + }); + + viewDescriptors.push({ + id: 'workbench.views.extensions.builtinThemeExtensions', + name: localize('builtInThemesExtensions', "Themes"), ctorDescriptor: new SyncDescriptor(BuiltInThemesExtensionsView), - when: ContextKeyExpr.has('searchBuiltInExtensions'), - weight: 100 - }; - } + when: ContextKeyExpr.has('builtInExtensions'), + }); - private createBuiltInBasicsExtensionsListViewDescriptor(): IViewDescriptor { - const id = 'extensions.builtInBasicsExtensionsList'; - return { - id, - name: viewIdNameMappings[id], - ctorDescriptor: new SyncDescriptor(BuiltInBasicsExtensionsView), - when: ContextKeyExpr.has('searchBuiltInExtensions'), - weight: 100 - }; + viewDescriptors.push({ + id: 'workbench.views.extensions.builtinProgrammingLanguageExtensions', + name: localize('builtinProgrammingLanguageExtensions', "Programming Languages"), + ctorDescriptor: new SyncDescriptor(BuiltInProgrammingLanguageExtensionsView), + when: ContextKeyExpr.has('builtInExtensions'), + }); + + return viewDescriptors; } } @@ -332,16 +332,15 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE private searchEnabledExtensionsContextKey: IContextKey; private searchDisabledExtensionsContextKey: IContextKey; private hasInstalledExtensionsContextKey: IContextKey; + private builtInExtensionsContextKey: IContextKey; private searchBuiltInExtensionsContextKey: IContextKey; private recommendedExtensionsContextKey: IContextKey; - private defaultRecommendedExtensionsContextKey: IContextKey; private searchDelayer: Delayer; private root: HTMLElement | undefined; private searchBox: SuggestEnabledInput | undefined; - private primaryActions: IAction[] | undefined; - private secondaryActions: IAction[] | null = null; private readonly searchViewletState: MementoObject; + private readonly sortActions: ChangeSortAction[]; constructor( @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, @@ -350,6 +349,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE @IInstantiationService instantiationService: IInstantiationService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, + @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, @INotificationService private readonly notificationService: INotificationService, @IViewletService private readonly viewletService: IViewletService, @IThemeService themeService: IThemeService, @@ -360,7 +360,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE @IContextMenuService contextMenuService: IContextMenuService, @IExtensionService extensionService: IExtensionService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, - @IPreferencesService private readonly preferencesService: IPreferencesService + @IPreferencesService private readonly preferencesService: IPreferencesService, ) { super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService); @@ -373,10 +373,9 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE this.searchEnabledExtensionsContextKey = SearchEnabledExtensionsContext.bindTo(contextKeyService); this.searchDisabledExtensionsContextKey = SearchDisabledExtensionsContext.bindTo(contextKeyService); this.hasInstalledExtensionsContextKey = HasInstalledExtensionsContext.bindTo(contextKeyService); + this.builtInExtensionsContextKey = BuiltInExtensionsContext.bindTo(contextKeyService); this.searchBuiltInExtensionsContextKey = SearchBuiltInExtensionsContext.bindTo(contextKeyService); this.recommendedExtensionsContextKey = RecommendedExtensionsContext.bindTo(contextKeyService); - this.defaultRecommendedExtensionsContextKey = DefaultRecommendedExtensionsContext.bindTo(contextKeyService); - this.defaultRecommendedExtensionsContextKey.set(!this.configurationService.getValue(ShowRecommendationsOnlyOnDemandKey)); this._register(this.viewletService.onDidViewletOpen(this.onViewletOpen, this)); this.searchViewletState = this.getMemento(StorageScope.WORKSPACE); @@ -386,13 +385,16 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE this._register(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(AutoUpdateConfigurationKey)) { - this.secondaryActions = null; this.updateTitleArea(); } - if (e.affectedKeys.indexOf(ShowRecommendationsOnlyOnDemandKey) > -1) { - this.defaultRecommendedExtensionsContextKey.set(!this.configurationService.getValue(ShowRecommendationsOnlyOnDemandKey)); - } }, this)); + + this.sortActions = [ + this._register(this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.install', localize('sort by installs', "Install Count"), this.onSearchChange, 'installs')), + this._register(this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.rating', localize('sort by rating', "Rating"), this.onSearchChange, 'rating')), + this._register(this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.name', localize('sort by name', "Name"), this.onSearchChange, 'name')), + this._register(this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.publishedDate', localize('sort by date', "Published Date"), this.onSearchChange, 'publishedDate')), + ]; } create(parent: HTMLElement): void { @@ -452,8 +454,8 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE } }, onDragOver: (e: DragEvent) => { - if (e.dataTransfer) { - e.dataTransfer.dropEffect = this.isSupportedDragElement(e) ? 'copy' : 'none'; + if (this.isSupportedDragElement(e)) { + e.dataTransfer!.dropEffect = 'copy'; } }, onDrop: async (e: DragEvent) => { @@ -465,7 +467,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE for (let index = 0; index < e.dataTransfer.files.length; index++) { const path = e.dataTransfer.files.item(index)!.path; if (path.indexOf('.vsix') !== -1) { - vsixPaths.push(URI.parse(path)); + vsixPaths.push(URI.file(path)); } } @@ -478,7 +480,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE } } } - }, + } })); super.create(append(this.root, $('.extensions'))); @@ -505,39 +507,56 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE } getActions(): IAction[] { - if (!this.primaryActions) { - this.primaryActions = [ - this.instantiationService.createInstance(ClearExtensionsInputAction, ClearExtensionsInputAction.ID, ClearExtensionsInputAction.LABEL, this.onSearchChange, this.searchBox ? this.searchBox.getValue() : '') - ]; + const filterActions: IAction[] = []; + + // Local extensions filters + filterActions.push(...[ + this.instantiationService.createInstance(ShowBuiltInExtensionsAction, ShowBuiltInExtensionsAction.ID, localize('builtin filter', "Built-in")), + this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, localize('installed filter', "Installed")), + this.instantiationService.createInstance(ShowEnabledExtensionsAction, ShowEnabledExtensionsAction.ID, localize('enabled filter', "Enabled")), + this.instantiationService.createInstance(ShowDisabledExtensionsAction, ShowDisabledExtensionsAction.ID, localize('disabled filter', "Disabled")), + this.instantiationService.createInstance(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, localize('outdated filter', "Outdated")), + ]); + + if (this.extensionGalleryService.isEnabled()) { + filterActions.splice(0, 0, ...[ + this.instantiationService.createInstance(PredefinedExtensionFilterAction, 'extensions.filter.featured', localize('featured filter', "Featured"), '@featured'), + this.instantiationService.createInstance(PredefinedExtensionFilterAction, 'extensions.filter.popular', localize('most popular filter', "Most Popular"), '@popular'), + this.instantiationService.createInstance(PredefinedExtensionFilterAction, 'extensions.filter.recommended', localize('most popular recommended', "Recommended"), '@recommended'), + this.instantiationService.createInstance(RecentlyPublishedExtensionsAction, RecentlyPublishedExtensionsAction.ID, localize('recently published filter', "Recently Published")), + new SubmenuAction('workbench.extensions.action.filterExtensionsByCategory', localize('filter by category', "Category"), EXTENSION_CATEGORIES.map(category => this.instantiationService.createInstance(SearchCategoryAction, `extensions.actions.searchByCategory.${category}`, category, category))), + new Separator(), + ]); + filterActions.push(...[ + new Separator(), + new SubmenuAction('workbench.extensions.action.sortBy', localize('sorty by', "Sort By"), this.sortActions), + ]); } - return this.primaryActions; + + return [ + new SubmenuAction('workbench.extensions.action.filterExtensions', localize('filterExtensions', "Filter Extensions..."), filterActions, 'codicon-filter'), + this.instantiationService.createInstance(ClearExtensionsInputAction, ClearExtensionsInputAction.ID, ClearExtensionsInputAction.LABEL, this.onSearchChange, this.searchBox ? this.searchBox.getValue() : ''), + ]; } getSecondaryActions(): IAction[] { - if (!this.secondaryActions) { - this.secondaryActions = [ - this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL), - this.instantiationService.createInstance(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, ShowOutdatedExtensionsAction.LABEL), - this.instantiationService.createInstance(ShowEnabledExtensionsAction, ShowEnabledExtensionsAction.ID, ShowEnabledExtensionsAction.LABEL), - this.instantiationService.createInstance(ShowDisabledExtensionsAction, ShowDisabledExtensionsAction.ID, ShowDisabledExtensionsAction.LABEL), - this.instantiationService.createInstance(ShowBuiltInExtensionsAction, ShowBuiltInExtensionsAction.ID, ShowBuiltInExtensionsAction.LABEL), - this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, ShowRecommendedExtensionsAction.LABEL), - this.instantiationService.createInstance(ShowPopularExtensionsAction, ShowPopularExtensionsAction.ID, ShowPopularExtensionsAction.LABEL), - new Separator(), - this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.install', localize('sort by installs', "Sort By: Install Count"), this.onSearchChange, 'installs'), - this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.rating', localize('sort by rating', "Sort By: Rating"), this.onSearchChange, 'rating'), - this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.name', localize('sort by name', "Sort By: Name"), this.onSearchChange, 'name'), - new Separator(), - this.instantiationService.createInstance(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL), - ...(this.configurationService.getValue(AutoUpdateConfigurationKey) ? [this.instantiationService.createInstance(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL)] : [this.instantiationService.createInstance(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL), this.instantiationService.createInstance(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL)]), - this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL), - new Separator(), - this.instantiationService.createInstance(DisableAllAction, DisableAllAction.ID, DisableAllAction.LABEL), - this.instantiationService.createInstance(EnableAllAction, EnableAllAction.ID, EnableAllAction.LABEL) - ]; + const actions: IAction[] = []; + + actions.push(this.instantiationService.createInstance(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL)); + if (this.configurationService.getValue(AutoUpdateConfigurationKey)) { + actions.push(this.instantiationService.createInstance(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL)); + } else { + actions.push(this.instantiationService.createInstance(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL), this.instantiationService.createInstance(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL)); } - return this.secondaryActions; + actions.push(new Separator()); + actions.push(this.instantiationService.createInstance(EnableAllAction, EnableAllAction.ID, EnableAllAction.LABEL)); + actions.push(this.instantiationService.createInstance(DisableAllAction, DisableAllAction.ID, DisableAllAction.LABEL)); + + actions.push(new Separator()); + actions.push(this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL)); + + return actions; } search(value: string, refresh: boolean = false): void { @@ -555,7 +574,14 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE } private normalizedQuery(): string { - return this.searchBox ? this.searchBox.getValue().replace(/@category/g, 'category').replace(/@tag:/g, 'tag:').replace(/@ext:/g, 'ext:') : ''; + return this.searchBox + ? this.searchBox.getValue() + .replace(/@category/g, 'category') + .replace(/@tag:/g, 'tag:') + .replace(/@ext:/g, 'ext:') + .replace(/@featured/g, 'featured') + .replace(/@popular/g, '@sort:installs') + : ''; } saveState(): void { @@ -575,7 +601,8 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE this.searchOutdatedExtensionsContextKey.set(ExtensionsListView.isOutdatedExtensionsQuery(value)); this.searchEnabledExtensionsContextKey.set(ExtensionsListView.isEnabledExtensionsQuery(value)); this.searchDisabledExtensionsContextKey.set(ExtensionsListView.isDisabledExtensionsQuery(value)); - this.searchBuiltInExtensionsContextKey.set(ExtensionsListView.isBuiltInExtensionsQuery(value)); + this.searchBuiltInExtensionsContextKey.set(ExtensionsListView.isSearchBuiltInExtensionsQuery(value)); + this.builtInExtensionsContextKey.set(ExtensionsListView.isBuiltInExtensionsQuery(value)); this.recommendedExtensionsContextKey.set(isRecommendedExtensionsQuery); this.searchMarketplaceExtensionsContextKey.set(!!value && !ExtensionsListView.isLocalExtensionsQuery(value) && !isRecommendedExtensionsQuery); this.nonEmptyWorkspaceContextKey.set(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY); @@ -597,19 +624,20 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE } private alertSearchResult(count: number, viewId: string): void { + const view = this.viewContainerModel.visibleViewDescriptors.find(view => view.id === viewId); switch (count) { case 0: break; case 1: - if (viewIdNameMappings[viewId]) { - alert(localize('extensionFoundInSection', "1 extension found in the {0} section.", viewIdNameMappings[viewId])); + if (view) { + alert(localize('extensionFoundInSection', "1 extension found in the {0} section.", view.name)); } else { alert(localize('extensionFound', "1 extension found.")); } break; default: - if (viewIdNameMappings[viewId]) { - alert(localize('extensionsFoundInSection', "{0} extensions found in the {1} section.", count, viewIdNameMappings[viewId])); + if (view) { + alert(localize('extensionsFoundInSection', "{0} extensions found in the {1} section.", count, view.name)); } else { alert(localize('extensionsFound', "{0} extensions found.", count)); } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 0cb220e341c..2fdba645427 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -19,15 +19,13 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { Delegate, Renderer, IExtensionsViewState } from 'vs/workbench/contrib/extensions/browser/extensionsList'; import { IExtension, IExtensionsWorkbenchService, ExtensionState } from 'vs/workbench/contrib/extensions/common/extensions'; import { Query } from 'vs/workbench/contrib/extensions/common/extensionQuery'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; -import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { InstallWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, ManageExtensionAction, InstallLocalExtensionsInRemoteAction, getContextMenuActions } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { InstallWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, ManageExtensionAction, InstallLocalExtensionsInRemoteAction, getContextMenuActions, ExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { WorkbenchPagedList, ListResourceNavigator } from 'vs/platform/list/browser/listService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; @@ -39,7 +37,7 @@ import { alert } from 'vs/base/browser/ui/aria/aria'; import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IAction, Action } from 'vs/base/common/actions'; +import { IAction, Action, Separator } from 'vs/base/common/actions'; import { ExtensionType, ExtensionIdentifier, IExtensionDescription, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -100,7 +98,6 @@ export class ExtensionsListView extends ViewPane { @IThemeService themeService: IThemeService, @IExtensionService private readonly extensionService: IExtensionService, @IExtensionsWorkbenchService protected extensionsWorkbenchService: IExtensionsWorkbenchService, - @IEditorService private readonly editorService: IEditorService, @IExtensionRecommendationsService protected tipsService: IExtensionRecommendationsService, @ITelemetryService telemetryService: ITelemetryService, @IConfigurationService configurationService: IConfigurationService, @@ -142,7 +139,7 @@ export class ExtensionsListView extends ViewPane { horizontalScrolling: false, accessibilityProvider: >{ getAriaLabel(extension: IExtension | null): string { - return extension ? localize('extension-arialabel', "{0}. Press enter for extension details.", extension.displayName) : ''; + return extension ? localize('extension-arialabel', "{0}, {1}, {2}, press enter for extension details.", extension.displayName, extension.version, extension.publisherDisplayName) : ''; }, getWidgetAriaLabel(): string { return localize('extensions', "Extensions"); @@ -157,16 +154,11 @@ export class ExtensionsListView extends ViewPane { this._register(this.list); this._register(extensionsViewState); - const resourceNavigator = this._register(new ListResourceNavigator(this.list, { openOnFocus: false, openOnSelection: true, openOnSingleClick: true })); - this._register(Event.debounce(Event.filter(resourceNavigator.onDidOpenResource, e => e.element !== null), (last, event) => event, 75, true)(options => { + const resourceNavigator = this._register(new ListResourceNavigator(this.list, { openOnSingleClick: true })); + this._register(Event.debounce(Event.filter(resourceNavigator.onDidOpen, e => e.element !== null), (_, event) => event, 75, true)(options => { this.openExtension(this.list!.model.get(options.element!), { sideByside: options.sideBySide, ...options.editorOptions }); })); - this._register(Event.chain(this.list.onPin) - .map(e => e.elements[0]) - .filter(e => !!e) - .on(this.pin, this)); - this.bodyTemplate = { extensionsList, messageBox, @@ -204,6 +196,7 @@ export class ExtensionsListView extends ViewPane { case 'installs': options = assign(options, { sortBy: SortBy.InstallCount }); break; case 'rating': options = assign(options, { sortBy: SortBy.WeightedRating }); break; case 'name': options = assign(options, { sortBy: SortBy.Title }); break; + case 'publishedDate': options = assign(options, { sortBy: SortBy.PublishedDate }); break; } const successCallback = (model: IPagedModel) => { @@ -254,7 +247,11 @@ export class ExtensionsListView extends ViewPane { }); } else if (e.element) { const groups = getContextMenuActions(this.menuService, this.contextKeyService.createScoped(), this.instantiationService, e.element); - groups.forEach(group => group.forEach(extensionAction => extensionAction.extension = e.element!)); + groups.forEach(group => group.forEach(extensionAction => { + if (extensionAction instanceof ExtensionAction) { + extensionAction.extension = e.element!; + } + })); let actions: IAction[] = []; for (const menuActions of groups) { actions = [...actions, ...menuActions, new Separator()]; @@ -323,33 +320,29 @@ export class ExtensionsListView extends ViewPane { result = result .filter(e => e.type === ExtensionType.System && (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1)); + const isThemeExtension = (e: IExtension): boolean => { + return (Array.isArray(e.local?.manifest?.contributes?.themes) && e.local!.manifest!.contributes!.themes.length > 0) + || (Array.isArray(e.local?.manifest?.contributes?.iconThemes) && e.local!.manifest!.contributes!.iconThemes.length > 0); + }; if (showThemesOnly) { - const themesExtensions = result.filter(e => { - return e.local - && e.local.manifest - && e.local.manifest.contributes - && Array.isArray(e.local.manifest.contributes.themes) - && e.local.manifest.contributes.themes.length; - }); + const themesExtensions = result.filter(isThemeExtension); return this.getPagedModel(this.sortExtensions(themesExtensions, options)); } + + const isLangaugeBasicExtension = (e: IExtension): boolean => { + return FORCE_FEATURE_EXTENSIONS.indexOf(e.identifier.id) === -1 + && (Array.isArray(e.local?.manifest?.contributes?.grammars) && e.local!.manifest!.contributes!.grammars.length > 0); + }; if (showBasicsOnly) { - const basics = result.filter(e => { - return e.local && e.local.manifest - && e.local.manifest.contributes - && Array.isArray(e.local.manifest.contributes.grammars) - && e.local.manifest.contributes.grammars.length - && FORCE_FEATURE_EXTENSIONS.indexOf(e.local.identifier.id) === -1; - }); + const basics = result.filter(isLangaugeBasicExtension); return this.getPagedModel(this.sortExtensions(basics, options)); } if (showFeaturesOnly) { const others = result.filter(e => { return e.local && e.local.manifest - && e.local.manifest.contributes - && (!Array.isArray(e.local.manifest.contributes.grammars) || FORCE_FEATURE_EXTENSIONS.indexOf(e.local.identifier.id) !== -1) - && !Array.isArray(e.local.manifest.contributes.themes); + && !isThemeExtension(e) + && !isLangaugeBasicExtension(e); }); return this.getPagedModel(this.sortExtensions(others, options)); } @@ -384,9 +377,9 @@ export class ExtensionsListView extends ViewPane { const runningExtensionsById = runningExtensions.reduce((result, e) => { result.set(ExtensionIdentifier.toKey(e.identifier.value), e); return result; }, new Map()); result = result.sort((e1, e2) => { const running1 = runningExtensionsById.get(ExtensionIdentifier.toKey(e1.identifier.id)); - const isE1Running = running1 && this.extensionManagementServerService.getExtensionManagementServer(running1.extensionLocation) === e1.server; + const isE1Running = running1 && this.extensionManagementServerService.getExtensionManagementServer(toExtension(running1)) === e1.server; const running2 = runningExtensionsById.get(ExtensionIdentifier.toKey(e2.identifier.id)); - const isE2Running = running2 && this.extensionManagementServerService.getExtensionManagementServer(running2.extensionLocation) === e2.server; + const isE2Running = running2 && this.extensionManagementServerService.getExtensionManagementServer(toExtension(running2)) === e2.server; if ((isE1Running && isE2Running)) { return e1.displayName.localeCompare(e2.displayName); } @@ -772,14 +765,6 @@ export class ExtensionsListView extends ViewPane { this.extensionsWorkbenchService.open(extension, options).then(undefined, err => this.onError(err)); } - private pin(): void { - const activeEditorPane = this.editorService.activeEditorPane; - if (activeEditorPane) { - activeEditorPane.group.pinEditor(activeEditorPane.input); - activeEditorPane.focus(); - } - } - private onError(err: any): void { if (isPromiseCanceledError(err)) { return; @@ -823,16 +808,21 @@ export class ExtensionsListView extends ViewPane { this.list = null; } - static isBuiltInExtensionsQuery(query: string): boolean { - return /^\s*@builtin\s*$/i.test(query); - } - static isLocalExtensionsQuery(query: string): boolean { return this.isInstalledExtensionsQuery(query) || this.isOutdatedExtensionsQuery(query) || this.isEnabledExtensionsQuery(query) || this.isDisabledExtensionsQuery(query) - || this.isBuiltInExtensionsQuery(query); + || this.isBuiltInExtensionsQuery(query) + || this.isSearchBuiltInExtensionsQuery(query); + } + + static isSearchBuiltInExtensionsQuery(query: string): boolean { + return /@builtin\s.+/i.test(query); + } + + static isBuiltInExtensionsQuery(query: string): boolean { + return /@builtin$/i.test(query.trim()); } static isInstalledExtensionsQuery(query: string): boolean { @@ -892,7 +882,6 @@ export class ServerExtensionsView extends ExtensionsListView { @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IInstantiationService instantiationService: IInstantiationService, @IExtensionService extensionService: IExtensionService, - @IEditorService editorService: IEditorService, @IExtensionRecommendationsService tipsService: IExtensionRecommendationsService, @ITelemetryService telemetryService: ITelemetryService, @IConfigurationService configurationService: IConfigurationService, @@ -908,13 +897,13 @@ export class ServerExtensionsView extends ExtensionsListView { @IPreferencesService preferencesService: IPreferencesService, ) { options.server = server; - super(options, notificationService, keybindingService, contextMenuService, instantiationService, themeService, extensionService, extensionsWorkbenchService, editorService, tipsService, telemetryService, configurationService, contextService, experimentService, extensionManagementServerService, productService, contextKeyService, viewDescriptorService, menuService, openerService, preferencesService); + super(options, notificationService, keybindingService, contextMenuService, instantiationService, themeService, extensionService, extensionsWorkbenchService, tipsService, telemetryService, configurationService, contextService, experimentService, extensionManagementServerService, productService, contextKeyService, viewDescriptorService, menuService, openerService, preferencesService); this._register(onDidChangeTitle(title => this.updateTitle(title))); } async show(query: string): Promise> { query = query ? query : '@installed'; - if (!ExtensionsListView.isLocalExtensionsQuery(query) && !ExtensionsListView.isBuiltInExtensionsQuery(query)) { + if (!ExtensionsListView.isLocalExtensionsQuery(query)) { query = query += ' @installed'; } return super.show(query.trim()); @@ -946,7 +935,29 @@ export class DisabledExtensionsView extends ExtensionsListView { } } -export class BuiltInExtensionsView extends ExtensionsListView { +export class OutdatedExtensionsView extends ExtensionsListView { + + async show(query: string): Promise> { + query = query || '@outdated'; + return ExtensionsListView.isOutdatedExtensionsQuery(query) ? super.show(query) : this.showEmptyModel(); + } +} + +export class InstalledExtensionsView extends ExtensionsListView { + + async show(query: string): Promise> { + query = query || '@installed'; + return ExtensionsListView.isInstalledExtensionsQuery(query) ? super.show(query) : this.showEmptyModel(); + } +} + +export class SearchBuiltInExtensionsView extends ExtensionsListView { + async show(query: string): Promise> { + return ExtensionsListView.isSearchBuiltInExtensionsQuery(query) ? super.show(query) : this.showEmptyModel(); + } +} + +export class BuiltInFeatureExtensionsView extends ExtensionsListView { async show(query: string): Promise> { return (query && query.trim() !== '@builtin') ? this.showEmptyModel() : super.show('@builtin:features'); } @@ -958,7 +969,7 @@ export class BuiltInThemesExtensionsView extends ExtensionsListView { } } -export class BuiltInBasicsExtensionsView extends ExtensionsListView { +export class BuiltInProgrammingLanguageExtensionsView extends ExtensionsListView { async show(query: string): Promise> { return (query && query.trim() !== '@builtin') ? this.showEmptyModel() : super.show('@builtin:basics'); } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index ea288746ac5..fbe745b3696 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -17,9 +17,9 @@ import { InstallExtensionEvent, DidInstallExtensionEvent, DidUninstallExtensionEvent, IExtensionIdentifier, InstallOperation, DefaultIconPath } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, getMaliciousExtensionsSet, groupByExtension, ExtensionIdentifierWithVersion } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, getMaliciousExtensionsSet, groupByExtension, ExtensionIdentifierWithVersion, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { URI } from 'vs/base/common/uri'; import { IExtension, ExtensionState, IExtensionsWorkbenchService, AutoUpdateConfigurationKey, AutoCheckUpdatesConfigurationKey } from 'vs/workbench/contrib/extensions/common/extensions'; @@ -37,6 +37,9 @@ import { IExtensionManifest, ExtensionType, IExtension as IPlatformExtension, is import { IModeService } from 'vs/editor/common/services/modeService'; import { IProductService } from 'vs/platform/product/common/productService'; import { asDomUri } from 'vs/base/browser/dom'; +import { getIgnoredExtensions } from 'vs/platform/userDataSync/common/extensionsMerge'; +import { isWeb } from 'vs/base/common/platform'; +import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil'; interface IExtensionStateProvider { (extension: Extension): T; @@ -94,8 +97,8 @@ class Extension implements IExtension { return this.gallery.publisherDisplayName || this.gallery.publisher; } - if (this.local!.metadata && this.local!.metadata.publisherDisplayName) { - return this.local!.metadata.publisherDisplayName; + if (this.local?.publisherDisplayName) { + return this.local.publisherDisplayName; } return this.local!.manifest.publisher; @@ -339,7 +342,16 @@ class Extensions extends Disposable { } async queryInstalled(): Promise { - const installed = await this.server.extensionManagementService.getInstalled(); + const all = await this.server.extensionManagementService.getInstalled(); + + // dedup user and system extensions by giving priority to user extensions. + const installed = groupByExtension(all, r => r.identifier).reduce((result, extensions) => { + const extension = extensions.length === 1 ? extensions[0] + : extensions.find(e => e.type === ExtensionType.User) || extensions.find(e => e.type === ExtensionType.System); + result.push(extension!); + return result; + }, []); + const byId = index(this.installed, e => e.local ? e.local.identifier.id : e.identifier.id); this.installed = installed.map(local => { const extension = byId[local.identifier.id] || this.instantiationService.createInstance(Extension, this.stateProvider, this.server, local, undefined); @@ -367,7 +379,7 @@ class Extensions extends Disposable { } // Sync the local extension with gallery extension if local extension doesnot has metadata if (extension.local) { - const local = extension.local.metadata ? extension.local : await this.server.extensionManagementService.updateMetadata(extension.local, { id: compatible.identifier.uuid, publisherDisplayName: compatible.publisherDisplayName, publisherId: compatible.publisherId }); + const local = extension.local.identifier.uuid ? extension.local : await this.server.extensionManagementService.updateMetadata(extension.local, { id: compatible.identifier.uuid, publisherDisplayName: compatible.publisherDisplayName, publisherId: compatible.publisherId }); extension.local = local; extension.gallery = compatible; this._onChange.fire({ extension }); @@ -475,10 +487,11 @@ class Extensions extends Disposable { export class ExtensionsWorkbenchService extends Disposable implements IExtensionsWorkbenchService, IURLHandler { private static readonly SyncPeriod = 1000 * 60 * 60 * 12; // 12 hours - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly localExtensions: Extensions | null = null; private readonly remoteExtensions: Extensions | null = null; + private readonly webExtensions: Extensions | null = null; private syncDelayer: ThrottledDelayer; private autoUpdateDelayer: ThrottledDelayer; @@ -513,6 +526,10 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension this.remoteExtensions = this._register(instantiationService.createInstance(Extensions, extensionManagementServerService.remoteExtensionManagementServer, ext => this.getExtensionState(ext))); this._register(this.remoteExtensions.onChange(e => this._onChange.fire(e ? e.extension : undefined))); } + if (extensionManagementServerService.webExtensionManagementServer) { + this.webExtensions = this._register(instantiationService.createInstance(Extensions, extensionManagementServerService.webExtensionManagementServer, ext => this.getExtensionState(ext))); + this._register(this.webExtensions.onChange(e => this._onChange.fire(e ? e.extension : undefined))); + } this.syncDelayer = new ThrottledDelayer(ExtensionsWorkbenchService.SyncPeriod); this.autoUpdateDelayer = new ThrottledDelayer(1000); @@ -541,8 +558,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension } get local(): IExtension[] { - const result = [...this.installed]; - const byId = groupByExtension(result, r => r.identifier); + const byId = groupByExtension(this.installed, r => r.identifier); return byId.reduce((result, extensions) => { result.push(this.getPrimaryExtension(extensions)); return result; }, []); } @@ -554,6 +570,9 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension if (this.remoteExtensions) { result.push(...this.remoteExtensions.local); } + if (this.webExtensions) { + result.push(...this.webExtensions.local); + } return result; } @@ -565,6 +584,9 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension if (this.remoteExtensions) { allLocal.push(...this.remoteExtensions.local); } + if (this.webExtensions) { + allLocal.push(...this.webExtensions.local); + } return allLocal.filter(e => e.outdated && e.local && e.state === ExtensionState.Installed); } @@ -576,6 +598,9 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension if (this.remoteExtensions && this.extensionManagementServerService.remoteExtensionManagementServer === server) { return this.remoteExtensions.queryInstalled(); } + if (this.webExtensions && this.extensionManagementServerService.webExtensionManagementServer === server) { + return this.webExtensions.queryInstalled(); + } } if (this.localExtensions) { @@ -584,6 +609,9 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension if (this.remoteExtensions) { await this.remoteExtensions.queryInstalled(); } + if (this.webExtensions) { + await this.webExtensions.queryInstalled(); + } return this.local; } @@ -638,18 +666,86 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension if (extensions.length === 1) { return extensions[0]; } - const pickRemoteOrFirstExtension = (from: IExtension[]): IExtension => { - const remoteExtension = from.filter(e => e.server === this.extensionManagementServerService.remoteExtensionManagementServer)[0]; - return remoteExtension ? remoteExtension : from[0]; - }; + const enabledExtensions = extensions.filter(e => e.local && this.extensionEnablementService.isEnabled(e.local)); - return enabledExtensions.length === 1 ? enabledExtensions[0] : pickRemoteOrFirstExtension(extensions); + if (enabledExtensions.length === 1) { + return enabledExtensions[0]; + } + + const extensionsToChoose = enabledExtensions.length ? enabledExtensions : extensions; + + let extension = extensionsToChoose.find(extension => { + for (const extensionKind of getExtensionKind(extension.local!.manifest, this.productService, this.configurationService)) { + switch (extensionKind) { + case 'ui': + /* UI extension is chosen only if it is installed locally */ + if (extension.server === this.extensionManagementServerService.localExtensionManagementServer) { + return true; + } + return false; + case 'workspace': + /* Choose remote workspace extension if exists */ + if (extension.server === this.extensionManagementServerService.remoteExtensionManagementServer) { + return true; + } + return false; + case 'web': + /* Choose web extension if exists */ + if (extension.server === this.extensionManagementServerService.webExtensionManagementServer) { + return true; + } + return false; + } + } + return false; + }); + + if (!extension && this.extensionManagementServerService.localExtensionManagementServer) { + extension = extensionsToChoose.find(extension => { + for (const extensionKind of getExtensionKind(extension.local!.manifest, this.productService, this.configurationService)) { + switch (extensionKind) { + case 'workspace': + /* Choose local workspace extension if exists */ + if (extension.server === this.extensionManagementServerService.localExtensionManagementServer) { + return true; + } + return false; + case 'web': + /* Choose local web extension if exists */ + if (extension.server === this.extensionManagementServerService.localExtensionManagementServer) { + return true; + } + return false; + } + } + return false; + }); + } + + if (!extension && this.extensionManagementServerService.remoteExtensionManagementServer) { + extension = extensionsToChoose.find(extension => { + for (const extensionKind of getExtensionKind(extension.local!.manifest, this.productService, this.configurationService)) { + switch (extensionKind) { + case 'web': + /* Choose remote web extension if exists */ + if (extension.server === this.extensionManagementServerService.remoteExtensionManagementServer) { + return true; + } + return false; + } + } + return false; + }); + } + + return extension || extensions[0]; } private fromGallery(gallery: IGalleryExtension, maliciousExtensionSet: Set): IExtension { Promise.all([ this.localExtensions ? this.localExtensions.syncLocalWithGalleryExtension(gallery, maliciousExtensionSet) : Promise.resolve(false), - this.remoteExtensions ? this.remoteExtensions.syncLocalWithGalleryExtension(gallery, maliciousExtensionSet) : Promise.resolve(false) + this.remoteExtensions ? this.remoteExtensions.syncLocalWithGalleryExtension(gallery, maliciousExtensionSet) : Promise.resolve(false), + this.webExtensions ? this.webExtensions.syncLocalWithGalleryExtension(gallery, maliciousExtensionSet) : Promise.resolve(false) ]) .then(result => { if (result[0] || result[1]) { @@ -686,7 +782,8 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension private getExtensionState(extension: Extension): ExtensionState { const isInstalling = this.installing.some(i => areSameExtensions(i.identifier, extension.identifier)); if (extension.server) { - const state = (extension.server === this.extensionManagementServerService.localExtensionManagementServer ? this.localExtensions! : this.remoteExtensions!).getExtensionState(extension); + const state = (extension.server === this.extensionManagementServerService.localExtensionManagementServer + ? this.localExtensions! : extension.server === this.extensionManagementServerService.remoteExtensionManagementServer ? this.remoteExtensions! : this.webExtensions!).getExtensionState(extension); return state === ExtensionState.Uninstalled && isInstalling ? ExtensionState.Installing : state; } else if (isInstalling) { return ExtensionState.Installing; @@ -697,6 +794,12 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension return state; } } + if (this.webExtensions) { + const state = this.webExtensions.getExtensionState(extension); + if (state !== ExtensionState.Uninstalled) { + return state; + } + } if (this.localExtensions) { return this.localExtensions.getExtensionState(extension); } @@ -774,7 +877,9 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension return false; } - if (this.extensionManagementServerService.localExtensionManagementServer || this.extensionManagementServerService.remoteExtensionManagementServer) { + if (this.extensionManagementServerService.localExtensionManagementServer + || this.extensionManagementServerService.remoteExtensionManagementServer + || this.extensionManagementServerService.webExtensionManagementServer) { return true; } @@ -783,10 +888,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension install(extension: URI | IExtension): Promise { if (extension instanceof URI) { - return this.installWithProgress(async () => { - const { identifier } = await this.extensionService.install(extension); - return this.local.filter(local => areSameExtensions(local.identifier, identifier))[0]; - }); + return this.installWithProgress(() => this.installFromVSIX(extension)); } if (extension.isMalicious) { @@ -799,10 +901,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension return Promise.reject(new Error('Missing gallery')); } - return this.installWithProgress(async () => { - await this.installFromGallery(extension, gallery); - return this.local.filter(local => areSameExtensions(local.identifier, gallery.identifier))[0]; - }, gallery.displayName); + return this.installWithProgress(() => this.installFromGallery(extension, gallery), gallery.displayName); } setEnablement(extensions: IExtension | IExtension[], enablementState: EnablementState): Promise { @@ -839,11 +938,11 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension return Promise.reject(new Error(nls.localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", extension.gallery!.identifier.id, version))); } return this.installWithProgress(async () => { - await this.installFromGallery(extension, gallery); + const installed = await this.installFromGallery(extension, gallery); if (extension.latestVersion !== version) { this.ignoreAutoUpdate(new ExtensionIdentifierWithVersion(gallery.identifier, version)); } - return this.local.filter(local => areSameExtensions(local.identifier, gallery.identifier))[0]; + return installed; } , gallery.displayName); }); @@ -863,6 +962,37 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension }, () => this.extensionService.reinstallFromGallery(toReinstall).then(() => this.local.filter(local => areSameExtensions(local.identifier, extension.identifier))[0])); } + isExtensionIgnoredToSync(extension: IExtension): boolean { + const localExtensions = (!isWeb && this.localExtensions ? this.localExtensions.local : this.local) + .filter(l => !!l.local) + .map(l => l.local!); + + const ignoredExtensions = getIgnoredExtensions(localExtensions, this.configurationService); + return ignoredExtensions.includes(extension.identifier.id.toLowerCase()); + } + + toggleExtensionIgnoredToSync(extension: IExtension): Promise { + const isIgnored = this.isExtensionIgnoredToSync(extension); + const isDefaultIgnored = extension.local?.isMachineScoped; + const id = extension.identifier.id.toLowerCase(); + + // first remove the extension completely from ignored extensions + let currentValue = [...this.configurationService.getValue('sync.ignoredExtensions')].map(id => id.toLowerCase()); + currentValue = currentValue.filter(v => v !== id && v !== `-${id}`); + + // If ignored, then add only if it is ignored by default + if (isIgnored && isDefaultIgnored) { + currentValue.push(`-${id}`); + } + + // If asked not to sync, then add only if it is not ignored by default + if (!isIgnored && !isDefaultIgnored) { + currentValue.push(id); + } + + return this.configurationService.updateValue('sync.ignoredExtensions', currentValue.length ? currentValue : undefined, ConfigurationTarget.USER); + } + private installWithProgress(installTask: () => Promise, extensionName?: string): Promise { const title = extensionName ? nls.localize('installing named extension', "Installing '{0}' extension....", extensionName) : nls.localize('installing extension', 'Installing extension....'); return this.progressService.withProgress({ @@ -871,7 +1001,19 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension }, () => installTask()); } - private async installFromGallery(extension: IExtension, gallery: IGalleryExtension): Promise { + private async installFromVSIX(vsix: URI): Promise { + const manifest = await this.extensionService.getManifest(vsix); + const existingExtension = this.local.find(local => areSameExtensions(local.identifier, { id: getGalleryExtensionId(manifest.publisher, manifest.name) })); + const { identifier } = await this.extensionService.install(vsix); + + if (existingExtension && existingExtension.latestVersion !== manifest.version) { + this.ignoreAutoUpdate(new ExtensionIdentifierWithVersion(identifier, manifest.version)); + } + + return this.local.filter(local => areSameExtensions(local.identifier, identifier))[0]; + } + + private async installFromGallery(extension: IExtension, gallery: IGalleryExtension): Promise { this.installing.push(extension); this._onChange.fire(extension); try { @@ -880,6 +1022,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension const ids: string[] | undefined = extension.identifier.uuid ? [extension.identifier.uuid] : undefined; const names: string[] | undefined = extension.identifier.uuid ? undefined : [extension.identifier.id]; this.queryGallery({ names, ids, pageSize: 1 }, CancellationToken.None); + return this.local.filter(local => areSameExtensions(local.identifier, gallery.identifier))[0]; } finally { this.installing = this.installing.filter(e => e !== extension); this._onChange.fire(this.local.filter(e => areSameExtensions(e.identifier, extension.identifier))[0]); @@ -1010,7 +1153,8 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension private _activityCallBack: (() => void) | null = null; private updateActivity(): void { if ((this.localExtensions && this.localExtensions.local.some(e => e.state === ExtensionState.Installing || e.state === ExtensionState.Uninstalling)) - || (this.remoteExtensions && this.remoteExtensions.local.some(e => e.state === ExtensionState.Installing || e.state === ExtensionState.Uninstalling))) { + || (this.remoteExtensions && this.remoteExtensions.local.some(e => e.state === ExtensionState.Installing || e.state === ExtensionState.Uninstalling)) + || (this.webExtensions && this.webExtensions.local.some(e => e.state === ExtensionState.Installing || e.state === ExtensionState.Uninstalling))) { if (!this._activityCallBack) { this.progressService.withProgress({ location: ProgressLocation.Extensions }, () => new Promise(c => this._activityCallBack = c)); } diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index af198f44531..b3f4de96701 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -12,7 +12,6 @@ import { ExtensionRecommendationSource, ExtensionRecommendationReason } from 'vs import { IExtensionsViewPaneContainer, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; import { CancellationToken } from 'vs/base/common/cancellation'; import { localize } from 'vs/nls'; -import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -161,7 +160,9 @@ export class FileBasedRecommendations extends ExtensionRecommendations { if (match(pattern, uri.toString())) { for (const extensionId of extensionIds) { // Add to recommendation to prompt if it is an important tip - if (this.importantExtensionTips[extensionId]) { + // Only prompt if the pattern matches the extensionImportantTips pattern + // Otherwise, assume pattern is from extensionTips, which means it should be a file based "passive" recommendation + if (this.importantExtensionTips[extensionId]?.pattern === pattern) { recommendationsToPrompt.push(extensionId); } // Update file based recommendations @@ -181,7 +182,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { return; } - const installed = await this.extensionManagementService.getInstalled(ExtensionType.User); + const installed = await this.extensionManagementService.getInstalled(); if (await this.promptRecommendedExtensionForFileType(recommendationsToPrompt, installed)) { return; } diff --git a/src/vs/workbench/contrib/extensions/browser/media/extension.css b/src/vs/workbench/contrib/extensions/browser/media/extension.css index 7e293b7776e..95db4c08066 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extension.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extension.css @@ -154,7 +154,6 @@ .extension-list-item > .details > .footer > .author { flex: 1; font-size: 90%; - opacity: 0.9; font-weight: 600; } diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css b/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css index b1ce2490802..7f98e83ebe1 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css @@ -59,7 +59,6 @@ .monaco-action-bar .action-item.disabled .action-label.extension-action { opacity: 1; - pointer-events: none; } .monaco-action-bar .action-item.disabled .action-label.extension-action.text { diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css index 36d89e741bc..3270a34f18e 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css @@ -91,7 +91,7 @@ margin-left: 10px; } -.vs .extension-editor > .header > .details > .title > .preview { +.monaco-workbench.vs .extension-editor > .header > .details > .title > .preview { color: white; } @@ -113,7 +113,7 @@ line-height: 20px; } -.extension-editor > .header > .details > .subtitle > .publisher { +.extension-editor > .header > .details > .subtitle .publisher { font-size: 18px; } @@ -124,12 +124,11 @@ align-items: center; } -.extension-editor > .header > .details > .subtitle > .install > .count { +.extension-editor > .header > .details > .subtitle .install > .count { margin-left: 6px; } -.extension-editor > .header > .details > .subtitle > span:not(:first-child):not(:empty), -.extension-editor > .header > .details > .subtitle > a:not(:first-child):not(:empty) { +.extension-editor > .header > .details > .subtitle > div:not(:first-child):not(:empty) { border-left: 1px solid rgba(128, 128, 128, 0.7); margin-left: 14px; padding-left: 14px; @@ -408,8 +407,8 @@ vertical-align: middle; } -.vs-dark .extension-editor > .body > .content table .colorBox, -.hc-black .extension-editor > .body > .content table .colorBox { +.monaco-workbench.vs-dark .extension-editor > .body > .content table .colorBox, +.monaco-workbench.hc-black .extension-editor > .body > .content table .colorBox { border-color: rgb(238, 238, 238); } diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css b/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css index 35a9606cadd..5110de46a71 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css @@ -103,25 +103,14 @@ display: none; } -.extensions-viewlet > .extensions .selected .extension-list-item > .details > .footer > .author, -.extensions-viewlet > .extensions .selected.focused .extension-list-item > .details > .footer > .author { - opacity: 1; -} - .extensions-viewlet.narrow > .extensions .extension-list-item > .details > .footer > .monaco-action-bar > .actions-container .extension-action { max-width: 100px; } -.vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .bookmark, -.vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .bookmark, -.vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension-list-item > .icon-container > .icon, -.vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension-list-item > .icon-container > .icon, -.vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension-list-item > .details > .header-container, -.vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension-list-item > .details > .header-container, -.vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension-list-item > .details > .description, -.vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension-list-item > .details > .description, -.vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension-list-item > .details > .footer > .author, -.vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension-list-item > .details > .footer > .author { +.monaco-workbench.vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension-list-item > .icon-container > .icon, +.monaco-workbench.vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension-list-item > .icon-container > .icon, +.monaco-workbench.vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension-list-item > .details > .header-container .codicon, +.monaco-workbench.vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension-list-item > .details > .header-container .codicon { opacity: 0.5; } diff --git a/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts b/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts index 84c47be3064..3ce9d79e586 100644 --- a/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts +++ b/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts @@ -30,7 +30,7 @@ export class RemoteExtensionsInstaller extends Disposable implements IWorkbenchC disposable = MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: 'workbench.extensions.installLocalExtensions', - category: localize('remote', "Remote"), + category: localize({ key: 'remote', comment: ['Remote as in remote machine'] }, "Remote"), title: installLocalExtensionsInRemoteAction.label } }); diff --git a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts index 4d05b1d420e..b17fd1dec71 100644 --- a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts @@ -17,7 +17,6 @@ import { EXTENSIONS_CONFIG } from 'vs/workbench/contrib/extensions/common/extens import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; import { localize } from 'vs/nls'; -import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { InstallWorkspaceRecommendedExtensionsAction, ShowRecommendedExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage'; @@ -105,7 +104,7 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { return; } - let installed = await this.extensionManagementService.getInstalled(ExtensionType.User); + let installed = await this.extensionManagementService.getInstalled(); installed = installed.filter(l => this.extensionEnablementService.getEnablementState(l) !== EnablementState.DisabledByExtensionKind); // Filter extensions disabled by kind const recommendations = allowedRecommendations.filter(({ extensionId }) => installed.every(local => !areSameExtensions({ id: extensionId }, local.identifier))); diff --git a/src/vs/workbench/contrib/extensions/common/extensionQuery.ts b/src/vs/workbench/contrib/extensions/common/extensionQuery.ts index e46b0ee8260..e28e8348283 100644 --- a/src/vs/workbench/contrib/extensions/common/extensionQuery.ts +++ b/src/vs/workbench/contrib/extensions/common/extensionQuery.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { flatten } from 'vs/base/common/arrays'; +import { EXTENSION_CATEGORIES } from 'vs/platform/extensions/common/extensions'; export class Query { @@ -15,7 +16,7 @@ export class Query { const commands = ['installed', 'outdated', 'enabled', 'disabled', 'builtin', 'recommended', 'sort', 'category', 'tag', 'ext', 'id'] as const; const subcommands = { 'sort': ['installs', 'rating', 'name'], - 'category': ['"programming languages"', 'snippets', 'linters', 'themes', 'debuggers', 'formatters', 'keymaps', '"scm providers"', 'other', '"extension packs"', '"language packs"'], + 'category': EXTENSION_CATEGORIES.map(c => `"${c.toLowerCase()}"`), 'tag': [''], 'ext': [''], 'id': [''] diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts index fae5943aaf6..43b3cb97ead 100644 --- a/src/vs/workbench/contrib/extensions/common/extensions.ts +++ b/src/vs/workbench/contrib/extensions/common/extensions.ts @@ -71,7 +71,7 @@ export const SERVICE_ID = 'extensionsWorkbenchService'; export const IExtensionsWorkbenchService = createDecorator(SERVICE_ID); export interface IExtensionsWorkbenchService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; onChange: Event; local: IExtension[]; installed: IExtension[]; @@ -88,6 +88,10 @@ export interface IExtensionsWorkbenchService { setEnablement(extensions: IExtension | IExtension[], enablementState: EnablementState): Promise; open(extension: IExtension, options?: { sideByside?: boolean, preserveFocus?: boolean, pinned?: boolean }): Promise; checkForUpdates(): Promise; + + // Sync APIs + isExtensionIgnoredToSync(extension: IExtension): boolean; + toggleExtensionIgnoredToSync(extension: IExtension): Promise; } export const ConfigurationKey = 'extensions'; diff --git a/src/vs/workbench/contrib/extensions/common/extensionsInput.ts b/src/vs/workbench/contrib/extensions/common/extensionsInput.ts index 1fd19bffb54..b1ba1de3af6 100644 --- a/src/vs/workbench/contrib/extensions/common/extensionsInput.ts +++ b/src/vs/workbench/contrib/extensions/common/extensionsInput.ts @@ -3,25 +3,26 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { EditorInput } from 'vs/workbench/common/editor'; import { IExtension } from 'vs/workbench/contrib/extensions/common/extensions'; -import { URI } from 'vs/base/common/uri'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; export class ExtensionsInput extends EditorInput { static readonly ID = 'workbench.extensions.input2'; - get extension(): IExtension { return this._extension; } get resource() { return URI.from({ - scheme: 'extension', + scheme: Schemas.extension, path: this.extension.identifier.id }); } constructor( - private readonly _extension: IExtension + public readonly extension: IExtension ) { super(); } @@ -34,26 +35,15 @@ export class ExtensionsInput extends EditorInput { return localize('extensionsInputName', "Extension: {0}", this.extension.displayName); } - matches(other: unknown): boolean { - if (super.matches(other) === true) { - return true; - } - - if (!(other instanceof ExtensionsInput)) { - return false; - } - - const otherExtensionInput = other as ExtensionsInput; - - // TODO@joao is this correct? - return this.extension === otherExtensionInput.extension; - } - - resolve(): Promise { - return Promise.resolve(null); - } - supportsSplitEditor(): boolean { return false; } + + matches(other: unknown): boolean { + if (super.matches(other)) { + return true; + } + + return other instanceof ExtensionsInput && areSameExtensions(this.extension.identifier, other.extension.identifier); + } } diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts index 7f988b5c93c..7e4d16d09d8 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts @@ -12,10 +12,10 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor, IStatusbarEntry } from 'vs/workbench/services/statusbar/common/statusbar'; import { IExtensionHostProfileService, ProfileSessionState } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { randomPort } from 'vs/base/node/ports'; -import product from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler'; @@ -23,7 +23,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; export class ExtensionHostProfileService extends Disposable implements IExtensionHostProfileService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _onDidChangeState: Emitter = this._register(new Emitter()); public readonly onDidChangeState: Event = this._onDidChangeState.event; @@ -49,6 +49,7 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio @IElectronService private readonly _electronService: IElectronService, @IDialogService private readonly _dialogService: IDialogService, @IStatusbarService private readonly _statusbarService: IStatusbarService, + @IProductService private readonly _productService: IProductService ) { super(); this._profile = null; @@ -57,7 +58,7 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio CommandsRegistry.registerCommand('workbench.action.extensionHostProfilder.stop', () => { this.stopProfiling(); - this._editorService.openEditor(this._instantiationService.createInstance(RuntimeExtensionsInput), { revealIfOpened: true }); + this._editorService.openEditor(RuntimeExtensionsInput.instance, { revealIfOpened: true }); }); } @@ -118,7 +119,7 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio return this._dialogService.confirm({ type: 'info', message: nls.localize('restart1', "Profile Extensions"), - detail: nls.localize('restart2', "In order to profile extensions a restart is required. Do you want to restart '{0}' now?", product.nameLong), + detail: nls.localize('restart2', "In order to profile extensions a restart is required. Do you want to restart '{0}' now?", this._productService.nameLong), primaryButton: nls.localize('restart3', "Restart"), secondaryButton: nls.localize('cancel', "Cancel") }).then(res => { diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts index 559178e4bf9..6d0222c798e 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts @@ -50,7 +50,7 @@ class RuntimeExtensionsInputFactory implements IEditorInputFactory { return ''; } deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput { - return new RuntimeExtensionsInput(); + return RuntimeExtensionsInput.instance; } } @@ -60,7 +60,7 @@ Registry.as(EditorInputExtensions.EditorInputFactor // Global actions const actionRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ShowRuntimeExtensionsAction), 'Show Running Extensions', localize('developer', "Developer")); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ShowRuntimeExtensionsAction), 'Show Running Extensions', localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer")); class ExtensionsContributions implements IWorkbenchContribution { diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts index 94a29c5e229..4289877aade 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts @@ -9,7 +9,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { Schemas } from 'vs/base/common/network'; export class OpenExtensionsFolderAction extends Action { diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts index c5b6f8b87a1..47a104f77a2 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts @@ -186,7 +186,7 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont ), [{ label: localize('show', 'Show Extensions'), - run: () => this._editorService.openEditor(new RuntimeExtensionsInput()) + run: () => this._editorService.openEditor(RuntimeExtensionsInput.instance) }, action ], diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts index fd2aded4b93..3fd95995fea 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as os from 'os'; -import product from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { Action } from 'vs/base/common/actions'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; @@ -118,7 +118,8 @@ class ReportExtensionSlowAction extends Action { readonly repoInfo: RepoInfo, readonly profile: IExtensionHostProfile, @IDialogService private readonly _dialogService: IDialogService, - @IOpenerService private readonly _openerService: IOpenerService + @IOpenerService private readonly _openerService: IOpenerService, + @IProductService private readonly _productService: IProductService ) { super('report.slow', localize('cmd.report', "Report Issue")); } @@ -139,7 +140,7 @@ class ReportExtensionSlowAction extends Action { - Extension Name: \`${this.extension.name}\` - Extension Version: \`${this.extension.version}\` - OS Version: \`${osVersion}\` -- VSCode version: \`${product.version}\`\n\n${message}`); +- VSCode version: \`${this._productService.version}\`\n\n${message}`); const url = `${this.repoInfo.base}/${this.repoInfo.owner}/${this.repoInfo.repo}/issues/new/?body=${body}&title=${title}`; this._openerService.open(URI.parse(url)); diff --git a/src/vs/workbench/contrib/extensions/electron-browser/media/runtimeExtensionsEditor.css b/src/vs/workbench/contrib/extensions/electron-browser/media/runtimeExtensionsEditor.css index 12d9ceac77a..8403039d724 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/media/runtimeExtensionsEditor.css +++ b/src/vs/workbench/contrib/extensions/electron-browser/media/runtimeExtensionsEditor.css @@ -57,8 +57,8 @@ color: currentColor; } -.vs .runtime-extensions-editor .extension > .icon-container > .icon, -.vs-dark .runtime-extensions-editor .extension > .icon-container > .icon { +.monaco-workbench.vs .runtime-extensions-editor .extension > .icon-container > .icon, +.monaco-workbench.vs-dark .runtime-extensions-editor .extension > .icon-container > .icon { opacity: 0.5; } diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts index cc851c4bc4d..1c780f7fb4f 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -6,8 +6,8 @@ import 'vs/css!./media/runtimeExtensionsEditor'; import * as nls from 'vs/nls'; import * as os from 'os'; -import product from 'vs/platform/product/common/product'; -import { Action, IAction } from 'vs/base/common/actions'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { Action, IAction, Separator } from 'vs/base/common/actions'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -18,13 +18,13 @@ import { IExtensionService, IExtensionsStatus, IExtensionHostProfile } from 'vs/ import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { append, $, addClass, toggleClass, Dimension, clearNode } from 'vs/base/browser/dom'; -import { ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { writeFile } from 'vs/base/node/pfs'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { memoize } from 'vs/base/common/decorators'; @@ -62,7 +62,7 @@ export enum ProfileSessionState { } export interface IExtensionHostProfileService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly onDidChangeState: Event; readonly onDidChangeLastProfile: Event; @@ -127,7 +127,8 @@ export class RuntimeExtensionsEditor extends BaseEditor { @ILabelService private readonly _labelService: ILabelService, @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, @IOpenerService private readonly _openerService: IOpenerService, - @IClipboardService private readonly _clipboardService: IClipboardService + @IClipboardService private readonly _clipboardService: IClipboardService, + @IProductService private readonly _productService: IProductService ) { super(RuntimeExtensionsEditor.ID, telemetryService, themeService, storageService); @@ -156,23 +157,24 @@ export class RuntimeExtensionsEditor extends BaseEditor { this._extensionService.getExtensions().then((extensions) => { // We only deal with extensions with source code! this._extensionsDescriptions = extensions.filter((extension) => { - return !!extension.main; + return Boolean(extension.main) || Boolean(extension.browser); }); this._updateExtensions(); }); this._register(this._extensionService.onDidChangeExtensionsStatus(() => this._updateSoon.schedule())); } - private _updateExtensions(): void { - this._elements = this._resolveExtensions(); + private async _updateExtensions(): Promise { + this._elements = await this._resolveExtensions(); if (this._list) { this._list.splice(0, this._list.length, this._elements); } } - private _resolveExtensions(): IRuntimeExtension[] { + private async _resolveExtensions(): Promise { let marketplaceMap: { [id: string]: IExtension; } = Object.create(null); - for (let extension of this._extensionsWorkbenchService.local) { + const marketPlaceExtensions = await this._extensionsWorkbenchService.queryLocal(); + for (let extension of marketPlaceExtensions) { marketplaceMap[ExtensionIdentifier.toKey(extension.identifier.id)] = extension; } @@ -327,7 +329,7 @@ export class RuntimeExtensionsEditor extends BaseEditor { } else { data.icon.style.visibility = 'inherit'; } - data.name.textContent = element.marketplaceInfo ? element.marketplaceInfo.displayName : element.description.displayName || ''; + data.name.textContent = element.marketplaceInfo.displayName; data.version.textContent = element.description.version; const activationTimes = element.status.activationTimes!; @@ -339,7 +341,7 @@ export class RuntimeExtensionsEditor extends BaseEditor { data.actionbar.push(this._instantiationService.createInstance(SlowExtensionAction, element.description, element.unresponsiveProfile), { icon: true, label: true }); } if (isNonEmptyArray(element.status.runtimeErrors)) { - data.actionbar.push(new ReportExtensionIssueAction(element, this._openerService, this._clipboardService), { icon: true, label: true }); + data.actionbar.push(new ReportExtensionIssueAction(element, this._openerService, this._clipboardService, this._productService), { icon: true, label: true }); } let title: string; @@ -372,6 +374,13 @@ export class RuntimeExtensionsEditor extends BaseEditor { '{0} will be a glob pattern' ] }, "Activated by {1} because searching for {0} took too long", glob, activationId); + } else if (activationEvent === 'onStartupFinished') { + title = nls.localize({ + key: 'startupFinishedActivation', + comment: [ + 'This refers to an extension. {0} will be an activation event.' + ] + }, "Activated by {0} after start-up finished", activationId); } else if (/^onLanguage:/.test(activationEvent)) { let language = activationEvent.substr('onLanguage:'.length); title = nls.localize('languageActivation', "Activated by {1} because you opened a {0} file", language, activationId); @@ -451,14 +460,13 @@ export class RuntimeExtensionsEditor extends BaseEditor { const actions: IAction[] = []; - actions.push(new ReportExtensionIssueAction(e.element, this._openerService, this._clipboardService)); + actions.push(new ReportExtensionIssueAction(e.element, this._openerService, this._clipboardService, this._productService)); + actions.push(new Separator()); + + actions.push(new Action('runtimeExtensionsEditor.action.disableWorkspace', nls.localize('disable workspace', "Disable (Workspace)"), undefined, true, () => this._extensionsWorkbenchService.setEnablement(e.element!.marketplaceInfo, EnablementState.DisabledWorkspace))); + actions.push(new Action('runtimeExtensionsEditor.action.disable', nls.localize('disable', "Disable"), undefined, true, () => this._extensionsWorkbenchService.setEnablement(e.element!.marketplaceInfo, EnablementState.DisabledGlobally))); actions.push(new Separator()); - if (e.element.marketplaceInfo) { - actions.push(new Action('runtimeExtensionsEditor.action.disableWorkspace', nls.localize('disable workspace', "Disable (Workspace)"), undefined, true, () => this._extensionsWorkbenchService.setEnablement(e.element!.marketplaceInfo, EnablementState.DisabledWorkspace))); - actions.push(new Action('runtimeExtensionsEditor.action.disable', nls.localize('disable', "Disable"), undefined, true, () => this._extensionsWorkbenchService.setEnablement(e.element!.marketplaceInfo, EnablementState.DisabledGlobally))); - actions.push(new Separator()); - } const state = this._extensionHostProfileService.state; if (state === ProfileSessionState.Running) { actions.push(this._instantiationService.createInstance(StopExtensionHostProfileAction, StopExtensionHostProfileAction.ID, StopExtensionHostProfileAction.LABEL)); @@ -492,14 +500,13 @@ export class ShowRuntimeExtensionsAction extends Action { constructor( id: string, label: string, - @IEditorService private readonly _editorService: IEditorService, - @IInstantiationService private readonly _instantiationService: IInstantiationService + @IEditorService private readonly _editorService: IEditorService ) { super(id, label); } public async run(e?: any): Promise { - await this._editorService.openEditor(this._instantiationService.createInstance(RuntimeExtensionsInput), { revealIfOpened: true }); + await this._editorService.openEditor(RuntimeExtensionsInput.instance, { revealIfOpened: true }); } } @@ -518,7 +525,8 @@ export class ReportExtensionIssueAction extends Action { unresponsiveProfile?: IExtensionHostProfile }, @IOpenerService private readonly openerService: IOpenerService, - @IClipboardService private readonly clipboardService: IClipboardService + @IClipboardService private readonly clipboardService: IClipboardService, + @IProductService private readonly productService: IProductService ) { super(ReportExtensionIssueAction._id, ReportExtensionIssueAction._label, 'extension-action report-issue'); this.enabled = extension.marketplaceInfo @@ -542,7 +550,7 @@ export class ReportExtensionIssueAction extends Action { if (!!baseUrl) { baseUrl = `${baseUrl.indexOf('.git') !== -1 ? baseUrl.substr(0, baseUrl.length - 4) : baseUrl}/issues/new/`; } else { - baseUrl = product.reportIssueUrl!; + baseUrl = this.productService.reportIssueUrl!; } let reason = 'Bug'; @@ -557,7 +565,7 @@ export class ReportExtensionIssueAction extends Action { - Extension Name: \`${extension.description.name}\` - Extension Version: \`${extension.description.version}\` - OS Version: \`${osVersion}\` -- VSCode version: \`${product.version}\`\n\n${message}` +- VSCode version: \`${this.productService.version}\`\n\n${message}` ); return `${baseUrl}${queryStringPrefix}body=${body}&title=${encodeURIComponent(title)}`; @@ -574,6 +582,7 @@ export class DebugExtensionHostAction extends Action { @IElectronService private readonly _electronService: IElectronService, @IDialogService private readonly _dialogService: IDialogService, @IExtensionService private readonly _extensionService: IExtensionService, + @IProductService private readonly productService: IProductService ) { super(DebugExtensionHostAction.ID, DebugExtensionHostAction.LABEL, DebugExtensionHostAction.CSS_CLASS); } @@ -585,7 +594,7 @@ export class DebugExtensionHostAction extends Action { const res = await this._dialogService.confirm({ type: 'info', message: nls.localize('restart1', "Profile Extensions"), - detail: nls.localize('restart2', "In order to profile extensions a restart is required. Do you want to restart '{0}' now?", product.nameLong), + detail: nls.localize('restart2', "In order to profile extensions a restart is required. Do you want to restart '{0}' now?", this.productService.nameLong), primaryButton: nls.localize('restart3', "Restart"), secondaryButton: nls.localize('cancel', "Cancel") }); diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput.ts b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput.ts index c4c1d43fe72..8fa384f074a 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput.ts @@ -11,6 +11,15 @@ export class RuntimeExtensionsInput extends EditorInput { static readonly ID = 'workbench.runtimeExtensions.input'; + static _instance: RuntimeExtensionsInput; + static get instance() { + if (!RuntimeExtensionsInput._instance || RuntimeExtensionsInput._instance.isDisposed()) { + RuntimeExtensionsInput._instance = new RuntimeExtensionsInput(); + } + + return RuntimeExtensionsInput._instance; + } + readonly resource = URI.from({ scheme: 'runtime-extensions', path: 'default' @@ -24,18 +33,11 @@ export class RuntimeExtensionsInput extends EditorInput { return nls.localize('extensionsInputName', "Running Extensions"); } - matches(other: unknown): boolean { - if (!(other instanceof RuntimeExtensionsInput)) { - return false; - } - return true; - } - - resolve(): Promise { - return Promise.resolve(null); - } - supportsSplitEditor(): boolean { return false; } + + matches(other: unknown): boolean { + return other instanceof RuntimeExtensionsInput; + } } diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts index ef9b59e46c6..b20db7d9a94 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts @@ -41,7 +41,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { INotificationService, Severity, IPromptChoice, IPromptOptions } from 'vs/platform/notification/common/notification'; -import { URLService } from 'vs/platform/url/node/urlService'; +import { NativeURLService } from 'vs/platform/url/common/urlService'; import { IExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { TestExperimentService } from 'vs/workbench/contrib/experiments/test/electron-browser/experimentService.test'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -206,7 +206,7 @@ suite('ExtensionRecommendationsService Test', () => { instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event); instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); instantiationService.stub(ITelemetryService, NullTelemetryService); - instantiationService.stub(IURLService, URLService); + instantiationService.stub(IURLService, NativeURLService); instantiationService.stub(IWorkspaceTagsService, new NoOpWorkspaceTagsService()); instantiationService.stub(IStorageService, new TestStorageService()); instantiationService.stub(ILogService, new NullLogService()); diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts index dfe557cec9d..7c83b8ea7b1 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts @@ -11,11 +11,10 @@ import * as ExtensionsActions from 'vs/workbench/contrib/extensions/browser/exte import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, - DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, InstallOperation, IExtensionTipsService + DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, InstallOperation, IExtensionTipsService, IGalleryMetadata } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IExtensionRecommendationsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IURLService } from 'vs/platform/url/common/url'; @@ -30,12 +29,12 @@ import { TestContextService } from 'vs/workbench/test/common/workbenchTestServic import { TestSharedProcessService } from 'vs/workbench/test/electron-browser/workbenchTestServices'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; -import { URLService } from 'vs/platform/url/node/urlService'; +import { NativeURLService } from 'vs/platform/url/common/urlService'; import { URI } from 'vs/base/common/uri'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; -import { ExtensionIdentifier, IExtensionContributions, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionContributions, ExtensionType, IExtensionDescription, IExtension } from 'vs/platform/extensions/common/extensions'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ILabelService, IFormatterChangeEvent } from 'vs/platform/label/common/label'; @@ -82,19 +81,29 @@ async function setupTest() { instantiationService.stub(IExtensionGalleryService, ExtensionGalleryService); instantiationService.stub(ISharedProcessService, TestSharedProcessService); - instantiationService.stub(IExtensionManagementService, ExtensionManagementService); - instantiationService.stub(IExtensionManagementService, 'onInstallExtension', installEvent.event); - instantiationService.stub(IExtensionManagementService, 'onDidInstallExtension', didInstallEvent.event); - instantiationService.stub(IExtensionManagementService, 'onUninstallExtension', uninstallEvent.event); - instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event); + instantiationService.stub(IExtensionManagementService, >{ + onInstallExtension: installEvent.event, + onDidInstallExtension: didInstallEvent.event, + onUninstallExtension: uninstallEvent.event, + onDidUninstallExtension: didUninstallEvent.event, + async getInstalled() { return []; }, + async getExtensionsReport() { return []; }, + async updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata) { + local.identifier.uuid = metadata.id; + local.publisherDisplayName = metadata.publisherDisplayName; + local.publisherId = metadata.publisherId; + return local; + } + }); + instantiationService.stub(IRemoteAgentService, RemoteAgentService); instantiationService.stub(IExtensionManagementServerService, new class extends ExtensionManagementServerService { - private _localExtensionManagementServer: IExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', authority: 'vscode-local' }; + #localExtensionManagementServer: IExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local' }; constructor() { super(instantiationService.get(ISharedProcessService), instantiationService.get(IRemoteAgentService), instantiationService.get(IExtensionGalleryService), instantiationService.get(IConfigurationService), instantiationService.get(IProductService), instantiationService.get(ILogService), instantiationService.get(ILabelService)); } - get localExtensionManagementServer(): IExtensionManagementServer { return this._localExtensionManagementServer; } + get localExtensionManagementServer(): IExtensionManagementServer { return this.#localExtensionManagementServer; } set localExtensionManagementServer(server: IExtensionManagementServer) { } }()); @@ -105,10 +114,8 @@ async function setupTest() { instantiationService.stub(IExperimentService, instantiationService.createInstance(TestExperimentService)); instantiationService.stub(IExtensionTipsService, instantiationService.createInstance(ExtensionTipsService)); instantiationService.stub(IExtensionRecommendationsService, {}); - instantiationService.stub(IURLService, URLService); + instantiationService.stub(IURLService, NativeURLService); - instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', []); - instantiationService.stubPromise(IExtensionManagementService, 'getExtensionsReport', []); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage()); instantiationService.stub(IExtensionService, >{ getExtensions: () => Promise.resolve([]), onDidChangeExtensions: new Emitter().event, canAddExtension: (extension: IExtensionDescription) => false, canRemoveExtension: (extension: IExtensionDescription) => false }); (instantiationService.get(IWorkbenchExtensionEnablementService)).reset(); @@ -117,7 +124,7 @@ async function setupTest() { } -suite('ExtensionsActions Test', () => { +suite('ExtensionsActions', () => { setup(setupTest); teardown(() => disposables.dispose()); @@ -720,7 +727,7 @@ suite('ExtensionsActions Test', () => { .then(extensions => { const testObject: ExtensionsActions.EnableGloballyAction = instantiationService.createInstance(ExtensionsActions.EnableGloballyAction); testObject.extension = extensions[0]; - assert.ok(!testObject.enabled); + assert.ok(testObject.enabled); }); }); }); @@ -1115,7 +1122,7 @@ suite('ReloadAction', () => { test('Test ReloadAction when extension is newly installed', async () => { const onDidChangeExtensionsEmitter: Emitter = new Emitter(); - const runningExtensions = [{ identifier: new ExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]; + const runningExtensions = [ExtensionsActions.toExtensionDescription(aLocalExtension('b'))]; instantiationService.stub(IExtensionService, >{ getExtensions: () => Promise.resolve(runningExtensions), onDidChangeExtensions: onDidChangeExtensionsEmitter.event, @@ -1138,7 +1145,7 @@ suite('ReloadAction', () => { test('Test ReloadAction when extension is newly installed and reload is not required', async () => { const onDidChangeExtensionsEmitter: Emitter = new Emitter(); - const runningExtensions = [{ identifier: new ExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]; + const runningExtensions = [ExtensionsActions.toExtensionDescription(aLocalExtension('b'))]; instantiationService.stub(IExtensionService, >{ getExtensions: () => Promise.resolve(runningExtensions), onDidChangeExtensions: onDidChangeExtensionsEmitter.event, @@ -1159,7 +1166,7 @@ suite('ReloadAction', () => { }); test('Test ReloadAction when extension is installed and uninstalled', async () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [ExtensionsActions.toExtensionDescription(aLocalExtension('b'))]); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); const gallery = aGalleryExtension('a'); @@ -1177,7 +1184,7 @@ suite('ReloadAction', () => { }); test('Test ReloadAction when extension is uninstalled', async () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a'), version: '1.0.0' }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [ExtensionsActions.toExtensionDescription(aLocalExtension('a', { version: '1.0.0' }))]); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); const local = aLocalExtension('a'); @@ -1211,7 +1218,7 @@ suite('ReloadAction', () => { }); test('Test ReloadAction when extension is uninstalled and installed', async () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.a'), version: '1.0.0', extensionLocation: URI.file('pub.a') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [ExtensionsActions.toExtensionDescription(aLocalExtension('a', { version: '1.0.0' }))]); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); const local = aLocalExtension('a'); @@ -1231,7 +1238,7 @@ suite('ReloadAction', () => { }); test('Test ReloadAction when extension is updated while running', async () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.a'), version: '1.0.1', extensionLocation: URI.file('pub.a') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [ExtensionsActions.toExtensionDescription(aLocalExtension('a', { version: '1.0.1' }))]); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); const local = aLocalExtension('a', { version: '1.0.1' }); @@ -1253,7 +1260,7 @@ suite('ReloadAction', () => { }); test('Test ReloadAction when extension is updated when not running', async () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [ExtensionsActions.toExtensionDescription(aLocalExtension('b'))]); const local = aLocalExtension('a', { version: '1.0.1' }); await instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([local], EnablementState.DisabledGlobally); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); @@ -1271,7 +1278,7 @@ suite('ReloadAction', () => { }); test('Test ReloadAction when extension is disabled when running', async () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [ExtensionsActions.toExtensionDescription(aLocalExtension('a'))]); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); const local = aLocalExtension('a'); @@ -1287,7 +1294,7 @@ suite('ReloadAction', () => { }); test('Test ReloadAction when extension enablement is toggled when running', async () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.a'), version: '1.0.0', extensionLocation: URI.file('pub.a') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [ExtensionsActions.toExtensionDescription(aLocalExtension('a', { version: '1.0.0' }))]); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); const local = aLocalExtension('a'); @@ -1301,7 +1308,7 @@ suite('ReloadAction', () => { }); test('Test ReloadAction when extension is enabled when not running', async () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [ExtensionsActions.toExtensionDescription(aLocalExtension('b'))]); const local = aLocalExtension('a'); await instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([local], EnablementState.DisabledGlobally); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); @@ -1317,7 +1324,7 @@ suite('ReloadAction', () => { }); test('Test ReloadAction when extension enablement is toggled when not running', async () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [ExtensionsActions.toExtensionDescription(aLocalExtension('b'))]); const local = aLocalExtension('a'); await instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([local], EnablementState.DisabledGlobally); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); @@ -1332,7 +1339,7 @@ suite('ReloadAction', () => { }); test('Test ReloadAction when extension is updated when not running and enabled', async () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [ExtensionsActions.toExtensionDescription(aLocalExtension('b'))]); const local = aLocalExtension('a', { version: '1.0.1' }); await instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([local], EnablementState.DisabledGlobally); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); @@ -1352,7 +1359,7 @@ suite('ReloadAction', () => { }); test('Test ReloadAction when a localization extension is newly installed', async () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [ExtensionsActions.toExtensionDescription(aLocalExtension('b'))]); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); const gallery = aGalleryExtension('a'); @@ -1368,7 +1375,7 @@ suite('ReloadAction', () => { }); test('Test ReloadAction when a localization extension is updated while running', async () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.a'), version: '1.0.1', extensionLocation: URI.file('pub.a') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [ExtensionsActions.toExtensionDescription(aLocalExtension('a', { version: '1.0.1' }))]); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.createInstance(ExtensionContainers, [testObject]); const local = aLocalExtension('a', { version: '1.0.1', contributes: { localizations: [{ languageId: 'de', translations: [] }] } }); @@ -2491,8 +2498,7 @@ function aLocalExtension(name: string = 'someext', manifest: any = {}, propertie properties = assign({ type: ExtensionType.User, location: URI.file(`pub.${name}`), - identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: undefined }, - metadata: { id: getGalleryExtensionId(manifest.publisher, manifest.name), publisherId: manifest.publisher, publisherDisplayName: 'somename' } + identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name) } }, properties); return Object.create({ manifest, ...properties }); } @@ -2512,7 +2518,7 @@ function aPage(...objects: T[]): IPager { function aSingleRemoteExtensionManagementServerService(instantiationService: TestInstantiationService, remoteExtensionManagementService?: IExtensionManagementService): IExtensionManagementServerService { const remoteExtensionManagementServer: IExtensionManagementServer = { - authority: 'vscode-remote', + id: 'vscode-remote', label: 'remote', extensionManagementService: remoteExtensionManagementService || createExtensionManagementService() }; @@ -2520,8 +2526,9 @@ function aSingleRemoteExtensionManagementServerService(instantiationService: Tes _serviceBrand: undefined, localExtensionManagementServer: null, remoteExtensionManagementServer, - getExtensionManagementServer: (location: URI) => { - if (location.scheme === REMOTE_HOST_SCHEME) { + webExtensionManagementServer: null, + getExtensionManagementServer: (extension: IExtension) => { + if (extension.location.scheme === REMOTE_HOST_SCHEME) { return remoteExtensionManagementServer; } return null; @@ -2531,12 +2538,12 @@ function aSingleRemoteExtensionManagementServerService(instantiationService: Tes function aMultiExtensionManagementServerService(instantiationService: TestInstantiationService, localExtensionManagementService?: IExtensionManagementService, remoteExtensionManagementService?: IExtensionManagementService): IExtensionManagementServerService { const localExtensionManagementServer: IExtensionManagementServer = { - authority: 'vscode-local', + id: 'vscode-local', label: 'local', extensionManagementService: localExtensionManagementService || createExtensionManagementService() }; const remoteExtensionManagementServer: IExtensionManagementServer = { - authority: 'vscode-remote', + id: 'vscode-remote', label: 'remote', extensionManagementService: remoteExtensionManagementService || createExtensionManagementService() }; @@ -2544,14 +2551,15 @@ function aMultiExtensionManagementServerService(instantiationService: TestInstan _serviceBrand: undefined, localExtensionManagementServer, remoteExtensionManagementServer, - getExtensionManagementServer: (location: URI) => { - if (location.scheme === Schemas.file) { + webExtensionManagementServer: null, + getExtensionManagementServer: (extension: IExtension) => { + if (extension.location.scheme === Schemas.file) { return localExtensionManagementServer; } - if (location.scheme === REMOTE_HOST_SCHEME) { + if (extension.location.scheme === REMOTE_HOST_SCHEME) { return remoteExtensionManagementServer; } - return null; + throw new Error(''); } }; } @@ -2563,7 +2571,13 @@ function createExtensionManagementService(installed: ILocalExtension[] = []): IE onUninstallExtension: Event.None, onDidUninstallExtension: Event.None, getInstalled: () => Promise.resolve(installed), - installFromGallery: (extension: IGalleryExtension) => Promise.reject(new Error('not supported')) + installFromGallery: (extension: IGalleryExtension) => Promise.reject(new Error('not supported')), + updateMetadata: async (local: ILocalExtension, metadata: IGalleryMetadata) => { + local.identifier.uuid = metadata.id; + local.publisherDisplayName = metadata.publisherDisplayName; + local.publisherId = metadata.publisherId; + return local; + } }; } diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index a9d3ab44f81..58c893753df 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -30,7 +30,7 @@ import { TestMenuService } from 'vs/workbench/test/browser/workbenchTestServices import { TestSharedProcessService } from 'vs/workbench/test/electron-browser/workbenchTestServices'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; -import { URLService } from 'vs/platform/url/node/urlService'; +import { NativeURLService } from 'vs/platform/url/common/urlService'; import { URI } from 'vs/base/common/uri'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { SinonStub } from 'sinon'; @@ -99,11 +99,11 @@ suite('ExtensionsListView Tests', () => { instantiationService.stub(IMenuService, new TestMenuService()); instantiationService.stub(IExtensionManagementServerService, new class extends ExtensionManagementServerService { - private _localExtensionManagementServer: IExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', authority: 'vscode-local' }; + #localExtensionManagementServer: IExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local' }; constructor() { super(instantiationService.get(ISharedProcessService), instantiationService.get(IRemoteAgentService), instantiationService.get(IExtensionGalleryService), instantiationService.get(IConfigurationService), instantiationService.get(IProductService), instantiationService.get(ILogService), instantiationService.get(ILabelService)); } - get localExtensionManagementServer(): IExtensionManagementServer { return this._localExtensionManagementServer; } + get localExtensionManagementServer(): IExtensionManagementServer { return this.#localExtensionManagementServer; } set localExtensionManagementServer(server: IExtensionManagementServer) { } }()); @@ -142,7 +142,7 @@ suite('ExtensionsListView Tests', () => { return reasons; } }); - instantiationService.stub(IURLService, URLService); + instantiationService.stub(IURLService, NativeURLService); }); setup(async () => { diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index 48af67b6e03..b7f98de6930 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -12,11 +12,10 @@ import { IExtensionsWorkbenchService, ExtensionState, AutoCheckUpdatesConfigurat import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, - DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets, IExtensionIdentifier, InstallOperation, IExtensionTipsService + DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets, IExtensionIdentifier, InstallOperation, IExtensionTipsService, IGalleryMetadata } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionRecommendationsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionRecommendationsService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IURLService } from 'vs/platform/url/common/url'; @@ -32,10 +31,10 @@ import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { ProgressService } from 'vs/workbench/services/progress/browser/progressService'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { URLService } from 'vs/platform/url/node/urlService'; +import { NativeURLService } from 'vs/platform/url/common/urlService'; import { URI } from 'vs/base/common/uri'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { ExtensionType, IExtension, ExtensionKind } from 'vs/platform/extensions/common/extensions'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; @@ -47,6 +46,8 @@ import { TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestSer import { IExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { TestExperimentService } from 'vs/workbench/contrib/experiments/test/electron-browser/experimentService.test'; import { ExtensionTipsService } from 'vs/platform/extensionManagement/node/extensionTipsService'; +import { Schemas } from 'vs/base/common/network'; +import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; suite('ExtensionsWorkbenchServiceTest', () => { @@ -72,7 +73,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { instantiationService.stub(IProductService, {}); instantiationService.stub(IExtensionGalleryService, ExtensionGalleryService); - instantiationService.stub(IURLService, URLService); + instantiationService.stub(IURLService, NativeURLService); instantiationService.stub(ISharedProcessService, TestSharedProcessService); instantiationService.stub(IWorkspaceContextService, new TestContextService()); @@ -85,11 +86,20 @@ suite('ExtensionsWorkbenchServiceTest', () => { instantiationService.stub(IRemoteAgentService, RemoteAgentService); - instantiationService.stub(IExtensionManagementService, ExtensionManagementService); - instantiationService.stub(IExtensionManagementService, 'onInstallExtension', installEvent.event); - instantiationService.stub(IExtensionManagementService, 'onDidInstallExtension', didInstallEvent.event); - instantiationService.stub(IExtensionManagementService, 'onUninstallExtension', uninstallEvent.event); - instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event); + instantiationService.stub(IExtensionManagementService, >{ + onInstallExtension: installEvent.event, + onDidInstallExtension: didInstallEvent.event, + onUninstallExtension: uninstallEvent.event, + onDidUninstallExtension: didUninstallEvent.event, + async getInstalled() { return []; }, + async getExtensionsReport() { return []; }, + async updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata) { + local.identifier.uuid = metadata.id; + local.publisherDisplayName = metadata.publisherDisplayName; + local.publisherId = metadata.publisherId; + return local; + } + }); instantiationService.stub(IExtensionManagementServerService, { localExtensionManagementServer: { @@ -109,7 +119,6 @@ suite('ExtensionsWorkbenchServiceTest', () => { setup(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', []); - instantiationService.stubPromise(IExtensionManagementService, 'getExtensionsReport', []); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage()); instantiationService.stubPromise(INotificationService, 'prompt', 0); await (instantiationService.get(IWorkbenchExtensionEnablementService)).reset(); @@ -974,6 +983,384 @@ suite('ExtensionsWorkbenchServiceTest', () => { assert.equal(actual[0].enablementState, EnablementState.DisabledWorkspace); }); + test('test user extension is preferred when the same extension exists as system and user extension', async () => { + testObject = await aWorkbenchService(); + const userExtension = aLocalExtension('pub.a'); + const systemExtension = aLocalExtension('pub.a', {}, { type: ExtensionType.System }); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [systemExtension, userExtension]); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, userExtension); + }); + + test('test user extension is disabled when the same extension exists as system and user extension and system extension is disabled', async () => { + testObject = await aWorkbenchService(); + const systemExtension = aLocalExtension('pub.a', {}, { type: ExtensionType.System }); + await instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([systemExtension], EnablementState.DisabledGlobally); + const userExtension = aLocalExtension('pub.a'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [systemExtension, userExtension]); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, userExtension); + assert.equal(actual[0].enablementState, EnablementState.DisabledGlobally); + }); + + test('Test local ui extension is chosen if it exists only in local server', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['ui']; + const localExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localExtension]), createExtensionManagementService([])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, localExtension); + }); + + test('Test local workspace extension is chosen if it exists only in local server', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['workspace']; + const localExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localExtension]), createExtensionManagementService([])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, localExtension); + }); + + test('Test local web extension is chosen if it exists only in local server', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['web']; + const localExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localExtension]), createExtensionManagementService([])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, localExtension); + }); + + test('Test local ui,workspace extension is chosen if it exists only in local server', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['ui', 'workspace']; + const localExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localExtension]), createExtensionManagementService([])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, localExtension); + }); + + test('Test local workspace,ui extension is chosen if it exists only in local server', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['workspace', 'ui']; + const localExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localExtension]), createExtensionManagementService([])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, localExtension); + }); + + test('Test local ui,workspace,web extension is chosen if it exists only in local server', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['ui', 'workspace', 'web']; + const localExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localExtension]), createExtensionManagementService([])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, localExtension); + }); + + test('Test local ui,web,workspace extension is chosen if it exists only in local server', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['ui', 'web', 'workspace']; + const localExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localExtension]), createExtensionManagementService([])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, localExtension); + }); + + test('Test local web,ui,workspace extension is chosen if it exists only in local server', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['web', 'ui', 'workspace']; + const localExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localExtension]), createExtensionManagementService([])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, localExtension); + }); + + test('Test local web,workspace,ui extension is chosen if it exists only in local server', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['web', 'workspace', 'ui']; + const localExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localExtension]), createExtensionManagementService([])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, localExtension); + }); + + test('Test local workspace,web,ui extension is chosen if it exists only in local server', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['workspace', 'web', 'ui']; + const localExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localExtension]), createExtensionManagementService([])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, localExtension); + }); + + test('Test local workspace,ui,web extension is chosen if it exists only in local server', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['workspace', 'ui', 'web']; + const localExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localExtension]), createExtensionManagementService([])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, localExtension); + }); + + test('Test local UI extension is chosen if it exists in both servers', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['ui']; + const localExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`) }); + const remoteExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localExtension]), createExtensionManagementService([remoteExtension])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, localExtension); + }); + + test('Test local ui,workspace extension is chosen if it exists in both servers', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['ui', 'workspace']; + const localExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`) }); + const remoteExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localExtension]), createExtensionManagementService([remoteExtension])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, localExtension); + }); + + test('Test remote workspace extension is chosen if it exists in remote server', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['workspace']; + const remoteExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), createExtensionManagementService([remoteExtension])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, remoteExtension); + }); + + test('Test remote workspace extension is chosen if it exists in both servers', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['workspace']; + const localExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`) }); + const remoteExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localExtension]), createExtensionManagementService([remoteExtension])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, remoteExtension); + }); + + test('Test remote workspace extension is chosen if it exists in both servers and local is disabled', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['workspace']; + const localExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`) }); + const remoteExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localExtension]), createExtensionManagementService([remoteExtension])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + await instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([localExtension], EnablementState.DisabledGlobally); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, remoteExtension); + assert.equal(actual[0].enablementState, EnablementState.DisabledGlobally); + }); + + test('Test remote workspace extension is chosen if it exists in both servers and remote is disabled in workspace', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['workspace']; + const localExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`) }); + const remoteExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localExtension]), createExtensionManagementService([remoteExtension])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + await instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([remoteExtension], EnablementState.DisabledWorkspace); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, remoteExtension); + assert.equal(actual[0].enablementState, EnablementState.DisabledWorkspace); + }); + + test('Test local ui, workspace extension is chosen if it exists in both servers and local is disabled', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['ui', 'workspace']; + const localExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`) }); + const remoteExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localExtension]), createExtensionManagementService([remoteExtension])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + await instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([localExtension], EnablementState.DisabledGlobally); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, localExtension); + assert.equal(actual[0].enablementState, EnablementState.DisabledGlobally); + }); + + test('Test local ui, workspace extension is chosen if it exists in both servers and local is disabled in workspace', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['ui', 'workspace']; + const localExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`) }); + const remoteExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localExtension]), createExtensionManagementService([remoteExtension])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + await instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([localExtension], EnablementState.DisabledWorkspace); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, localExtension); + assert.equal(actual[0].enablementState, EnablementState.DisabledWorkspace); + }); + + test('Test local web extension is chosen if it exists in both servers', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['web']; + const localExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`) }); + const remoteExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localExtension]), createExtensionManagementService([remoteExtension])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, localExtension); + }); + + test('Test remote web extension is chosen if it exists only in remote', async () => { + // multi server setup + const extensionKind: ExtensionKind[] = ['web']; + const remoteExtension = aLocalExtension('a', { extensionKind }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); + + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([]), createExtensionManagementService([remoteExtension])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + testObject = await aWorkbenchService(); + + const actual = await testObject.queryLocal(); + + assert.equal(actual.length, 1); + assert.equal(actual[0].local, remoteExtension); + }); + async function aWorkbenchService(): Promise { const workbenchService: ExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService); await workbenchService.queryLocal(); @@ -985,8 +1372,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { properties = assign({ type: ExtensionType.User, location: URI.file(`pub.${name}`), - identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: undefined }, - metadata: { id: getGalleryExtensionId(manifest.publisher, manifest.name), publisherId: manifest.publisher, publisherDisplayName: 'somename' } + identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name) } }, properties); return Object.create({ manifest, ...properties }); } @@ -1025,4 +1411,49 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); }); } + + function aMultiExtensionManagementServerService(instantiationService: TestInstantiationService, localExtensionManagementService?: IExtensionManagementService, remoteExtensionManagementService?: IExtensionManagementService): IExtensionManagementServerService { + const localExtensionManagementServer: IExtensionManagementServer = { + id: 'vscode-local', + label: 'local', + extensionManagementService: localExtensionManagementService || createExtensionManagementService() + }; + const remoteExtensionManagementServer: IExtensionManagementServer = { + id: 'vscode-remote', + label: 'remote', + extensionManagementService: remoteExtensionManagementService || createExtensionManagementService() + }; + return { + _serviceBrand: undefined, + localExtensionManagementServer, + remoteExtensionManagementServer, + webExtensionManagementServer: null, + getExtensionManagementServer: (extension: IExtension) => { + if (extension.location.scheme === Schemas.file) { + return localExtensionManagementServer; + } + if (extension.location.scheme === REMOTE_HOST_SCHEME) { + return remoteExtensionManagementServer; + } + throw new Error(''); + } + }; + } + + function createExtensionManagementService(installed: ILocalExtension[] = []): IExtensionManagementService { + return { + onInstallExtension: Event.None, + onDidInstallExtension: Event.None, + onUninstallExtension: Event.None, + onDidUninstallExtension: Event.None, + getInstalled: () => Promise.resolve(installed), + installFromGallery: (extension: IGalleryExtension) => Promise.reject(new Error('not supported')), + updateMetadata: async (local: ILocalExtension, metadata: IGalleryMetadata) => { + local.identifier.uuid = metadata.id; + local.publisherDisplayName = metadata.publisherDisplayName; + local.publisherId = metadata.publisherId; + return local; + } + }; + } }); diff --git a/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts b/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts index 5cdfe7aba3d..3fd4c0fe013 100644 --- a/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts +++ b/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts @@ -5,16 +5,11 @@ import * as nls from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import * as paths from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; import { IExternalTerminalConfiguration, IExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/common/externalTerminal'; -import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; -import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; -import { KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED } from 'vs/workbench/contrib/terminal/common/terminal'; +import { MenuId, MenuRegistry, IMenuItem } from 'vs/platform/actions/common/actions'; import { ITerminalService as IIntegratedTerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { ResourceContextKey } from 'vs/workbench/common/resources'; -import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IFileService } from 'vs/platform/files/common/files'; import { IListService } from 'vs/platform/list/browser/listService'; import { getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/files'; @@ -25,9 +20,13 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { optional } from 'vs/platform/instantiation/common/instantiation'; import { IExplorerService } from 'vs/workbench/contrib/files/common/files'; -import { isWeb } from 'vs/base/common/platform'; -import { IPathService } from 'vs/workbench/services/path/common/pathService'; - +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { isWeb, isWindows } from 'vs/base/common/platform'; +import { dirname, basename } from 'vs/base/common/path'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { Registry } from 'vs/platform/registry/common/platform'; const OPEN_IN_TERMINAL_COMMAND_ID = 'openInTerminal'; CommandsRegistry.registerCommand({ @@ -62,7 +61,7 @@ CommandsRegistry.registerCommand({ authority: resource.authority, fragment: resource.fragment, query: resource.query, - path: paths.dirname(resource.path) + path: dirname(resource.path) }); }).forEach(cwd => { if (opened[cwd.path]) { @@ -70,13 +69,13 @@ CommandsRegistry.registerCommand({ } opened[cwd.path] = true; const instance = integratedTerminalService.createTerminal({ cwd }); - if (instance && (resources.length === 1 || !resource || cwd.path === resource.path || cwd.path === paths.dirname(resource.path))) { + if (instance && (resources.length === 1 || !resource || cwd.path === resource.path || cwd.path === dirname(resource.path))) { integratedTerminalService.setActiveInstance(instance); integratedTerminalService.showPanel(true); } }); } else { - distinct(targets.map(({ stat }) => stat!.isDirectory ? stat!.resource.fsPath : paths.dirname(stat!.resource.fsPath))).forEach(cwd => { + distinct(targets.map(({ stat }) => stat!.isDirectory ? stat!.resource.fsPath : dirname(stat!.resource.fsPath))).forEach(cwd => { terminalService!.openTerminal(cwd); }); } @@ -84,67 +83,56 @@ CommandsRegistry.registerCommand({ } }); -if (!isWeb) { - const OPEN_NATIVE_CONSOLE_COMMAND_ID = 'workbench.action.terminal.openNativeConsole'; - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: OPEN_NATIVE_CONSOLE_COMMAND_ID, - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C, - when: KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED, - weight: KeybindingWeight.WorkbenchContrib, - handler: async (accessor) => { - const historyService = accessor.get(IHistoryService); - // Open external terminal in local workspaces - const terminalService = accessor.get(IExternalTerminalService); - const root = historyService.getLastActiveWorkspaceRoot(Schemas.file); - if (root) { - terminalService.openTerminal(root.fsPath); - } else { - // Opens current file's folder, if no folder is open in editor - const activeFile = historyService.getLastActiveFile(Schemas.file); - if (activeFile) { - terminalService.openTerminal(paths.dirname(activeFile.fsPath)); - } else { - const pathService = accessor.get(IPathService); - const userHome = await pathService.userHome; - terminalService.openTerminal(userHome.fsPath); - } +export class ExternalTerminalContribution extends Disposable implements IWorkbenchContribution { + private _openInTerminalMenuItem: IMenuItem; + + constructor( + @IConfigurationService private readonly _configurationService: IConfigurationService + ) { + super(); + + this._openInTerminalMenuItem = { + group: 'navigation', + order: 30, + command: { + id: OPEN_IN_TERMINAL_COMMAND_ID, + title: nls.localize('scopedConsoleAction', "Open in Terminal") + }, + when: ContextKeyExpr.or(ResourceContextKey.Scheme.isEqualTo(Schemas.file), ResourceContextKey.Scheme.isEqualTo(Schemas.vscodeRemote)) + }; + MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, this._openInTerminalMenuItem); + MenuRegistry.appendMenuItem(MenuId.ExplorerContext, this._openInTerminalMenuItem); + + this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('terminal.explorerKind') || e.affectsConfiguration('terminal.external')) { + this._refreshOpenInTerminalMenuItemTitle(); + } + }); + this._refreshOpenInTerminalMenuItemTitle(); + } + + private _refreshOpenInTerminalMenuItemTitle(): void { + if (isWeb) { + this._openInTerminalMenuItem.command.title = nls.localize('scopedConsoleAction.integrated', "Open in Integrated Terminal"); + return; + } + + const config = this._configurationService.getValue().terminal; + if (config.explorerKind === 'integrated') { + this._openInTerminalMenuItem.command.title = nls.localize('scopedConsoleAction.integrated', "Open in Integrated Terminal"); + return; + } + + if (isWindows && config.external.windowsExec) { + const file = basename(config.external.windowsExec); + if (file === 'wt' || file === 'wt.exe') { + this._openInTerminalMenuItem.command.title = nls.localize('scopedConsoleAction.wt', "Open in Windows Terminal"); + return; } } - }); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: OPEN_NATIVE_CONSOLE_COMMAND_ID, - title: { value: nls.localize('globalConsoleAction', "Open New External Terminal"), original: 'Open New External Terminal' } - } - }); + this._openInTerminalMenuItem.command.title = nls.localize('scopedConsoleAction.external', "Open in External Terminal"); + } } -const openConsoleCommand = { - id: OPEN_IN_TERMINAL_COMMAND_ID, - title: nls.localize('scopedConsoleAction', "Open in Terminal") -}; -MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { - group: 'navigation', - order: 30, - command: openConsoleCommand, - when: ResourceContextKey.Scheme.isEqualTo(Schemas.file) -}); -MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { - group: 'navigation', - order: 30, - command: openConsoleCommand, - when: ResourceContextKey.Scheme.isEqualTo(Schemas.vscodeRemote) -}); -MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { - group: 'navigation', - order: 30, - command: openConsoleCommand, - when: ResourceContextKey.Scheme.isEqualTo(Schemas.file) -}); -MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { - group: 'navigation', - order: 30, - command: openConsoleCommand, - when: ResourceContextKey.Scheme.isEqualTo(Schemas.vscodeRemote) -}); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExternalTerminalContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/externalTerminal/common/externalTerminal.ts b/src/vs/workbench/contrib/externalTerminal/common/externalTerminal.ts index 6bbcd810c1e..4e46df2b22b 100644 --- a/src/vs/workbench/contrib/externalTerminal/common/externalTerminal.ts +++ b/src/vs/workbench/contrib/externalTerminal/common/externalTerminal.ts @@ -14,7 +14,7 @@ export interface IExternalTerminalSettings { } export interface IExternalTerminalService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; openTerminal(path: string): void; runInTerminal(title: string, cwd: string, args: string[], env: { [key: string]: string | null; }, settings: IExternalTerminalSettings): Promise; } @@ -24,4 +24,4 @@ export interface IExternalTerminalConfiguration { explorerKind: 'integrated' | 'external', external: IExternalTerminalSettings; }; -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/externalTerminal/node/externalTerminal.contribution.ts b/src/vs/workbench/contrib/externalTerminal/node/externalTerminal.contribution.ts new file mode 100644 index 00000000000..84e406323be --- /dev/null +++ b/src/vs/workbench/contrib/externalTerminal/node/externalTerminal.contribution.ts @@ -0,0 +1,106 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as paths from 'vs/base/common/path'; +import { IExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/common/externalTerminal'; +import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IHistoryService } from 'vs/workbench/services/history/common/history'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { Schemas } from 'vs/base/common/network'; +import { IPathService } from 'vs/workbench/services/path/common/pathService'; +import { WindowsExternalTerminalService, MacExternalTerminalService, LinuxExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/node/externalTerminalService'; +import { IConfigurationRegistry, Extensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { isWindows, isMacintosh, isLinux } from 'vs/base/common/platform'; +import { DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/externalTerminal/node/externalTerminal'; + +const OPEN_NATIVE_CONSOLE_COMMAND_ID = 'workbench.action.terminal.openNativeConsole'; +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: OPEN_NATIVE_CONSOLE_COMMAND_ID, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C, + when: KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED, + weight: KeybindingWeight.WorkbenchContrib, + handler: async (accessor) => { + const historyService = accessor.get(IHistoryService); + // Open external terminal in local workspaces + const terminalService = accessor.get(IExternalTerminalService); + const root = historyService.getLastActiveWorkspaceRoot(Schemas.file); + if (root) { + terminalService.openTerminal(root.fsPath); + } else { + // Opens current file's folder, if no folder is open in editor + const activeFile = historyService.getLastActiveFile(Schemas.file); + if (activeFile) { + terminalService.openTerminal(paths.dirname(activeFile.fsPath)); + } else { + const pathService = accessor.get(IPathService); + const userHome = await pathService.userHome(); + terminalService.openTerminal(userHome.fsPath); + } + } + } +}); + +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: OPEN_NATIVE_CONSOLE_COMMAND_ID, + title: { value: nls.localize('globalConsoleAction', "Open New External Terminal"), original: 'Open New External Terminal' } + } +}); + +if (isWindows) { + registerSingleton(IExternalTerminalService, WindowsExternalTerminalService, true); +} else if (isMacintosh) { + registerSingleton(IExternalTerminalService, MacExternalTerminalService, true); +} else if (isLinux) { + registerSingleton(IExternalTerminalService, LinuxExternalTerminalService, true); +} + +LinuxExternalTerminalService.getDefaultTerminalLinuxReady().then(defaultTerminalLinux => { + let configurationRegistry = Registry.as(Extensions.Configuration); + configurationRegistry.registerConfiguration({ + id: 'externalTerminal', + order: 100, + title: nls.localize('terminalConfigurationTitle', "External Terminal"), + type: 'object', + properties: { + 'terminal.explorerKind': { + type: 'string', + enum: [ + 'integrated', + 'external' + ], + enumDescriptions: [ + nls.localize('terminal.explorerKind.integrated', "Use VS Code's integrated terminal."), + nls.localize('terminal.explorerKind.external', "Use the configured external terminal.") + ], + description: nls.localize('explorer.openInTerminalKind', "Customizes what kind of terminal to launch."), + default: 'integrated' + }, + 'terminal.external.windowsExec': { + type: 'string', + description: nls.localize('terminal.external.windowsExec', "Customizes which terminal to run on Windows."), + default: WindowsExternalTerminalService.getDefaultTerminalWindows(), + scope: ConfigurationScope.APPLICATION + }, + 'terminal.external.osxExec': { + type: 'string', + description: nls.localize('terminal.external.osxExec', "Customizes which terminal application to run on macOS."), + default: DEFAULT_TERMINAL_OSX, + scope: ConfigurationScope.APPLICATION + }, + 'terminal.external.linuxExec': { + type: 'string', + description: nls.localize('terminal.external.linuxExec', "Customizes which terminal to run on Linux."), + default: defaultTerminalLinux, + scope: ConfigurationScope.APPLICATION + } + } + }); +}); diff --git a/src/vs/workbench/contrib/externalTerminal/node/externalTerminal.ts b/src/vs/workbench/contrib/externalTerminal/node/externalTerminal.ts new file mode 100644 index 00000000000..ff0fc24b7cf --- /dev/null +++ b/src/vs/workbench/contrib/externalTerminal/node/externalTerminal.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const DEFAULT_TERMINAL_OSX = 'Terminal.app'; diff --git a/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.test.ts b/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.test.ts index 8217f144e92..264e8a93275 100644 --- a/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.test.ts +++ b/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.test.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { deepEqual, equal } from 'assert'; -import { WindowsExternalTerminalService, LinuxExternalTerminalService, MacExternalTerminalService, DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/externalTerminal/node/externalTerminalService'; +import { WindowsExternalTerminalService, LinuxExternalTerminalService, MacExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/node/externalTerminalService'; +import { DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/externalTerminal/node/externalTerminal'; suite('ExternalTerminalService', () => { let mockOnExit: Function; diff --git a/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts b/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts index d8ba1abc301..067cc377d6c 100644 --- a/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts +++ b/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts @@ -13,14 +13,10 @@ import { assign } from 'vs/base/common/objects'; import { IExternalTerminalService, IExternalTerminalConfiguration, IExternalTerminalSettings } from 'vs/workbench/contrib/externalTerminal/common/externalTerminal'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { getPathFromAmdModule } from 'vs/base/common/amd'; -import { IConfigurationRegistry, Extensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { Registry } from 'vs/platform/registry/common/platform'; import { optional } from 'vs/platform/instantiation/common/instantiation'; - +import { DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/externalTerminal/node/externalTerminal'; const TERMINAL_TITLE = nls.localize('console.title', "VS Code Console"); -export const DEFAULT_TERMINAL_OSX = 'Terminal.app'; export class WindowsExternalTerminalService implements IExternalTerminalService { public _serviceBrand: undefined; @@ -358,54 +354,3 @@ function quote(args: string[]): string { } return r; } - -if (env.isWindows) { - registerSingleton(IExternalTerminalService, WindowsExternalTerminalService, true); -} else if (env.isMacintosh) { - registerSingleton(IExternalTerminalService, MacExternalTerminalService, true); -} else if (env.isLinux) { - registerSingleton(IExternalTerminalService, LinuxExternalTerminalService, true); -} - -LinuxExternalTerminalService.getDefaultTerminalLinuxReady().then(defaultTerminalLinux => { - let configurationRegistry = Registry.as(Extensions.Configuration); - configurationRegistry.registerConfiguration({ - id: 'externalTerminal', - order: 100, - title: nls.localize('terminalConfigurationTitle', "External Terminal"), - type: 'object', - properties: { - 'terminal.explorerKind': { - type: 'string', - enum: [ - 'integrated', - 'external' - ], - enumDescriptions: [ - nls.localize('terminal.explorerKind.integrated', "Use VS Code's integrated terminal."), - nls.localize('terminal.explorerKind.external', "Use the configured external terminal.") - ], - description: nls.localize('explorer.openInTerminalKind', "Customizes what kind of terminal to launch."), - default: 'integrated' - }, - 'terminal.external.windowsExec': { - type: 'string', - description: nls.localize('terminal.external.windowsExec', "Customizes which terminal to run on Windows."), - default: WindowsExternalTerminalService.getDefaultTerminalWindows(), - scope: ConfigurationScope.APPLICATION - }, - 'terminal.external.osxExec': { - type: 'string', - description: nls.localize('terminal.external.osxExec', "Customizes which terminal application to run on macOS."), - default: DEFAULT_TERMINAL_OSX, - scope: ConfigurationScope.APPLICATION - }, - 'terminal.external.linuxExec': { - type: 'string', - description: nls.localize('terminal.external.linuxExec', "Customizes which terminal to run on Linux."), - default: defaultTerminalLinux, - scope: ConfigurationScope.APPLICATION - } - } - }); -}); diff --git a/src/vs/workbench/contrib/feedback/browser/feedback.ts b/src/vs/workbench/contrib/feedback/browser/feedback.ts index 766154643a4..c251c876ccb 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedback.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedback.ts @@ -385,6 +385,8 @@ export class FeedbackDropdown extends Dropdown { if (this.options.onFeedbackVisibilityChange) { this.options.onFeedbackVisibilityChange(true); } + + this.updateCharCountText(); } protected onHide(): void { diff --git a/src/vs/workbench/contrib/feedback/browser/media/feedback.css b/src/vs/workbench/contrib/feedback/browser/media/feedback.css index c7620661268..409ebc60eb2 100644 --- a/src/vs/workbench/contrib/feedback/browser/media/feedback.css +++ b/src/vs/workbench/contrib/feedback/browser/media/feedback.css @@ -115,7 +115,8 @@ } /* Theming */ -.vs .monaco-workbench .feedback-form .feedback-alias, .vs .monaco-workbench .feedback-form .feedback-description { +.monaco-workbench.vs .feedback-form .feedback-alias, +.monaco-workbench.vs .feedback-form .feedback-description { font-family: inherit; border: 1px solid transparent; } @@ -167,16 +168,17 @@ background-color: #E51400; } -.vs-dark .monaco-workbench .feedback-form h3 { +.monaco-workbench.vs-dark .feedback-form h3 { font-weight: normal; font-size: 1.2em; } -.vs-dark .monaco-workbench .feedback-form .sentiment:hover { +.monaco-workbench.vs-dark .feedback-form .sentiment:hover { background-color: rgba(30,30,30,0.8); } -.vs-dark .monaco-workbench .feedback-form .feedback-alias, .vs-dark .monaco-workbench .feedback-form .feedback-description { +.monaco-workbench.vs-dark .feedback-form .feedback-alias, +.monaco-workbench.vs-dark .feedback-form .feedback-description { font-family: inherit; } @@ -193,28 +195,29 @@ } /* High Contrast Theming */ -.hc-black .monaco-workbench .feedback-form { +.monaco-workbench.hc-black .feedback-form { outline: 2px solid #6fc3df; outline-offset: -2px; } -.hc-black .monaco-workbench .feedback-form .feedback-alias, .hc-black .monaco-workbench .feedback-form .feedback-description { +.monaco-workbench.hc-black .feedback-form .feedback-alias, +.monaco-workbench.hc-black .feedback-form .feedback-description { font-family: inherit; } -.hc-black .monaco-workbench .feedback-form .content .contactus { +.monaco-workbench.hc-black .feedback-form .content .contactus { padding: 10px; float: right; } -.hc-black .monaco-workbench .feedback-form .form-buttons .send, -.hc-black .monaco-workbench .feedback-form .form-buttons .send.in-progress, -.hc-black .monaco-workbench .feedback-form .form-buttons .send.success { +.monaco-workbench.hc-black .feedback-form .form-buttons .send, +.monaco-workbench.hc-black .feedback-form .form-buttons .send.in-progress, +.monaco-workbench.hc-black .feedback-form .form-buttons .send.success { background-color: #0C141F; color: #D4D4D4; border: 1px solid #6FC3DF; } -.hc-black .monaco-workbench .feedback-form .form-buttons .send:hover { +.monaco-workbench.hc-black .feedback-form .form-buttons .send:hover { background-color: #0C141F; } diff --git a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts index 389bcae9802..1daf1517449 100644 --- a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts @@ -14,6 +14,9 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IStorageService } from 'vs/platform/storage/common/storage'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith'; /** * An implementation of editor for binary files that cannot be displayed. @@ -27,6 +30,8 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor { @IThemeService themeService: IThemeService, @IOpenerService private readonly openerService: IOpenerService, @IEditorService private readonly editorService: IEditorService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IQuickInputService private readonly quickInputService: IQuickInputService, @IStorageService storageService: IStorageService, @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, ) { @@ -46,8 +51,11 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor { private async openInternal(input: EditorInput, options: EditorOptions | undefined): Promise { if (input instanceof FileEditorInput) { input.setForceOpenAsText(); - - await this.editorService.openEditor(input, options, this.group); + if (this.group !== undefined) { + await openEditorWith(input, undefined, options, this.group, this.editorService, this.configurationService, this.quickInputService); + } else { + await this.editorService.openEditor(input, options, this.group); + } } } diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts index 7924a4dde95..4beec737735 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts @@ -11,8 +11,8 @@ import { basename } from 'vs/base/common/resources'; import { Action } from 'vs/base/common/actions'; import { VIEWLET_ID, TEXT_FILE_EDITOR_ID, IExplorerService } from 'vs/workbench/contrib/files/common/files'; import { ITextFileService, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; -import { BaseTextEditor, IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor'; -import { EditorOptions, TextEditorOptions, IEditorCloseEvent, Verbosity } from 'vs/workbench/common/editor'; +import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; +import { EditorOptions, TextEditorOptions, IEditorInput } from 'vs/workbench/common/editor'; import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -25,13 +25,11 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; -import { MutableDisposable } from 'vs/base/common/lifecycle'; import { EditorActivation, IEditorOptions } from 'vs/platform/editor/common/editor'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; /** * An implementation of editor for file system resources. @@ -40,9 +38,6 @@ export class TextFileEditor extends BaseTextEditor { static readonly ID = TEXT_FILE_EDITOR_ID; - private restoreViewState: boolean | undefined; - private readonly groupListener = this._register(new MutableDisposable()); - constructor( @ITelemetryService telemetryService: ITelemetryService, @IFileService private readonly fileService: IFileService, @@ -56,12 +51,10 @@ export class TextFileEditor extends BaseTextEditor { @IEditorGroupsService editorGroupService: IEditorGroupsService, @ITextFileService private readonly textFileService: ITextFileService, @IExplorerService private readonly explorerService: IExplorerService, - @IConfigurationService private readonly configurationService: IConfigurationService + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService ) { super(TextFileEditor.ID, telemetryService, instantiationService, storageService, textResourceConfigurationService, themeService, editorService, editorGroupService); - this.updateRestoreViewStateConfiguration(); - // Clear view state for deleted files this._register(this.fileService.onDidFilesChange(e => this.onDidFilesChange(e))); @@ -78,18 +71,16 @@ export class TextFileEditor extends BaseTextEditor { private onDidRunOperation(e: FileOperationEvent): void { if (e.operation === FileOperation.MOVE && e.target) { - this.moveTextEditorViewState(e.resource, e.target.resource); + this.moveTextEditorViewState(e.resource, e.target.resource, this.uriIdentityService.extUri); } } - protected handleConfigurationChangeEvent(configuration?: IEditorConfiguration): void { - super.handleConfigurationChangeEvent(configuration); + protected onWillCloseEditorInGroup(editor: IEditorInput): void { - this.updateRestoreViewStateConfiguration(); - } - - private updateRestoreViewStateConfiguration(): void { - this.restoreViewState = this.configurationService.getValue('workbench.editor.restoreViewState') ?? true /* default */; + // React to editors closing to preserve or clear view state. This needs to happen + // in the onWillCloseEditor because at that time the editor has not yet + // been disposed and we can safely persist the view state still as needed. + this.doSaveOrClearTextEditorViewState(editor); } getTitle(): string { @@ -100,30 +91,6 @@ export class TextFileEditor extends BaseTextEditor { return this._input as FileEditorInput; } - setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void { - super.setEditorVisible(visible, group); - - // React to editors closing to preserve or clear view state. This needs to happen - // in the onWillCloseEditor because at that time the editor has not yet - // been disposed and we can safely persist the view state still as needed. - this.groupListener.value = ((group as IEditorGroupView).onWillCloseEditor(e => this.onWillCloseEditorInGroup(e))); - } - - private onWillCloseEditorInGroup(e: IEditorCloseEvent): void { - const editor = e.editor; - if (!(editor instanceof FileEditorInput)) { - return; // only handle files - } - - // If the editor is currently active we can always save or clear the view state. - // If the editor is not active, we can only clear the view state because it needs - // an active editor with the file opened, so we check for the restoreViewState flag - // being set. - if (editor === this.input || !this.restoreViewState) { - this.doSaveOrClearTextEditorViewState(editor); - } - } - async setInput(input: FileEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { // Update/clear view settings if input changes @@ -245,11 +212,6 @@ export class TextFileEditor extends BaseTextEditor { } } - protected getAriaLabel(): string { - const title = this.input?.getTitle(Verbosity.SHORT) || nls.localize('fileEditorAriaLabel', "editor"); - return this.input?.isReadonly() ? nls.localize('readonlyEditor', "{0} readonly", title) : title; - } - clearInput(): void { // Update/clear editor view state in settings @@ -273,14 +235,14 @@ export class TextFileEditor extends BaseTextEditor { super.saveState(); } - private doSaveOrClearTextEditorViewState(input: FileEditorInput | undefined): void { - if (!input) { + private doSaveOrClearTextEditorViewState(input: IEditorInput | undefined): void { + if (!(input instanceof FileEditorInput)) { return; // ensure we have an input to handle view state for } // If the user configured to not restore view state, we clear the view // state unless the editor is still opened in the group. - if (!this.restoreViewState && (!this.group || !this.group.isOpened(input))) { + if (!this.shouldRestoreViewState && (!this.group || !this.group.isOpened(input))) { this.clearTextEditorViewState([input.resource], this.group); } diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts index 6a2baff6544..ab7a78c8b07 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import { toErrorMessage } from 'vs/base/common/errorMessage'; -import { basename } from 'vs/base/common/resources'; +import { basename, extUri } from 'vs/base/common/resources'; import { Action, IAction } from 'vs/base/common/actions'; import { URI } from 'vs/base/common/uri'; import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; @@ -17,7 +17,7 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ResourceMap } from 'vs/base/common/map'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; -import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { TextFileContentProvider } from 'vs/workbench/contrib/files/common/files'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { SAVE_FILE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL } from 'vs/workbench/contrib/files/browser/fileCommands'; @@ -43,14 +43,15 @@ const conflictEditorHelp = nls.localize('userGuide', "Use the actions in the edi // A handler for text file save error happening with conflict resolution actions export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHandler, IWorkbenchContribution { - private messages: ResourceMap; - private conflictResolutionContext: IContextKey; - private activeConflictResolutionResource?: URI; + + private readonly messages = new ResourceMap(); + private readonly conflictResolutionContext = new RawContextKey(CONFLICT_RESOLUTION_CONTEXT, false).bindTo(this.contextKeyService); + private activeConflictResolutionResource: URI | undefined = undefined; constructor( @INotificationService private readonly notificationService: INotificationService, @ITextFileService private readonly textFileService: ITextFileService, - @IContextKeyService contextKeyService: IContextKeyService, + @IContextKeyService private contextKeyService: IContextKeyService, @IEditorService private readonly editorService: IEditorService, @ITextModelService textModelService: ITextModelService, @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -62,9 +63,6 @@ export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHa // opt-in to syncing storageKeysSyncRegistryService.registerStorageKey({ key: LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, version: 1 }); - this.messages = new ResourceMap(); - this.conflictResolutionContext = new RawContextKey(CONFLICT_RESOLUTION_CONTEXT, false).bindTo(contextKeyService); - const provider = this._register(instantiationService.createInstance(TextFileContentProvider)); this._register(textModelService.registerTextModelContentProvider(CONFLICT_RESOLUTION_SCHEME, provider)); @@ -117,7 +115,7 @@ export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHa if (fileOperationError.fileOperationResult === FileOperationResult.FILE_MODIFIED_SINCE) { // If the user tried to save from the opened conflict editor, show its message again - if (this.activeConflictResolutionResource && this.activeConflictResolutionResource.toString() === model.resource.toString()) { + if (this.activeConflictResolutionResource && extUri.isEqual(this.activeConflictResolutionResource, model.resource)) { if (this.storageService.getBoolean(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, StorageScope.GLOBAL)) { return; // return if this message is ignored } @@ -177,7 +175,7 @@ export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHa } else if (canHandlePermissionOrReadonlyErrors && isPermissionDenied) { message = isWindows ? nls.localize('permissionDeniedSaveError', "Failed to save '{0}': Insufficient permissions. Select 'Retry as Admin' to retry as administrator.", basename(resource)) : nls.localize('permissionDeniedSaveErrorSudo', "Failed to save '{0}': Insufficient permissions. Select 'Retry as Sudo' to retry as superuser.", basename(resource)); } else { - message = nls.localize('genericSaveError', "Failed to save '{0}': {1}", basename(resource), toErrorMessage(error, false)); + message = nls.localize({ key: 'genericSaveError', comment: ['{0} is the resource that failed to save and {1} the error message'] }, "Failed to save '{0}': {1}", basename(resource), toErrorMessage(error, false)); } } @@ -350,18 +348,23 @@ export const acceptLocalChangesCommand = async (accessor: ServicesAccessor, reso const reference = await resolverService.createModelReference(resource); const model = reference.object as IResolvedTextFileEditorModel; - clearPendingResolveSaveConflictMessages(); // hide any previously shown message about how to use these actions + try { - // Trigger save - await model.save({ ignoreModifiedSince: true, reason: SaveReason.EXPLICIT }); + // hide any previously shown message about how to use these actions + clearPendingResolveSaveConflictMessages(); - // Reopen file input - await editorService.openEditor({ resource: model.resource }, group); + // Trigger save + await model.save({ ignoreModifiedSince: true, reason: SaveReason.EXPLICIT }); - // Clean up - group.closeEditor(editor); - editor.dispose(); - reference.dispose(); + // Reopen file input + await editorService.openEditor({ resource: model.resource }, group); + + // Clean up + group.closeEditor(editor); + editor.dispose(); + } finally { + reference.dispose(); + } }; export const revertLocalChangesCommand = async (accessor: ServicesAccessor, resource: URI) => { @@ -379,16 +382,21 @@ export const revertLocalChangesCommand = async (accessor: ServicesAccessor, reso const reference = await resolverService.createModelReference(resource); const model = reference.object as ITextFileEditorModel; - clearPendingResolveSaveConflictMessages(); // hide any previously shown message about how to use these actions + try { - // Revert on model - await model.revert(); + // hide any previously shown message about how to use these actions + clearPendingResolveSaveConflictMessages(); - // Reopen file input - await editorService.openEditor({ resource: model.resource }, group); + // Revert on model + await model.revert(); - // Clean up - group.closeEditor(editor); - editor.dispose(); - reference.dispose(); + // Reopen file input + await editorService.openEditor({ resource: model.resource }, group); + + // Clean up + group.closeEditor(editor); + editor.dispose(); + } finally { + reference.dispose(); + } }; diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index 3cd3acbcbbc..560a311196e 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -109,10 +109,12 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor id: OpenEditorsView.ID, name: OpenEditorsView.NAME, ctorDescriptor: new SyncDescriptor(OpenEditorsView), + containerIcon: 'codicon-files', order: 0, when: OpenEditorsVisibleContext, canToggleVisibility: true, canMoveView: true, + collapsed: true, focusCommand: { id: 'workbench.files.action.focusOpenEditorsView', keybindings: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_E) } @@ -218,7 +220,6 @@ export class ExplorerViewPaneContainer extends ViewPaneContainer { } catch (error) { return null; // ignore } finally { - const openEditorsView = this.getOpenEditorsView(); if (openEditorsView) { openEditorsView.setStructuralRefreshDelay(0); } diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index 4ef2a0dd098..89b4e9ee7bd 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -5,12 +5,12 @@ import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; -import { ToggleAutoSaveAction, GlobalNewUntitledFileAction, FocusFilesExplorer, GlobalCompareResourcesAction, SaveAllAction, ShowActiveFileInExplorer, CollapseExplorerView, RefreshExplorerView, CompareWithClipboardAction, NEW_FILE_COMMAND_ID, NEW_FILE_LABEL, NEW_FOLDER_COMMAND_ID, NEW_FOLDER_LABEL, TRIGGER_RENAME_LABEL, MOVE_FILE_TO_TRASH_LABEL, COPY_FILE_LABEL, PASTE_FILE_LABEL, FileCopiedContext, renameHandler, moveFileToTrashHandler, copyFileHandler, pasteFileHandler, deleteFileHandler, cutFileHandler, DOWNLOAD_COMMAND_ID, openFilePreserveFocusHandler, DOWNLOAD_LABEL, ShowOpenedFileInNewWindow, ReopenResourcesAction, ToggleEditorTypeCommand as ToggleEditorTypeAction } from 'vs/workbench/contrib/files/browser/fileActions'; +import { ToggleAutoSaveAction, FocusFilesExplorer, GlobalCompareResourcesAction, SaveAllAction, ShowActiveFileInExplorer, CollapseExplorerView, RefreshExplorerView, CompareWithClipboardAction, NEW_FILE_COMMAND_ID, NEW_FILE_LABEL, NEW_FOLDER_COMMAND_ID, NEW_FOLDER_LABEL, TRIGGER_RENAME_LABEL, MOVE_FILE_TO_TRASH_LABEL, COPY_FILE_LABEL, PASTE_FILE_LABEL, FileCopiedContext, renameHandler, moveFileToTrashHandler, copyFileHandler, pasteFileHandler, deleteFileHandler, cutFileHandler, DOWNLOAD_COMMAND_ID, openFilePreserveFocusHandler, DOWNLOAD_LABEL, ShowOpenedFileInNewWindow } from 'vs/workbench/contrib/files/browser/fileActions'; import { revertLocalChangesCommand, acceptLocalChangesCommand, CONFLICT_RESOLUTION_CONTEXT } from 'vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler'; import { SyncActionDescriptor, MenuId, MenuRegistry, ILocalizedString } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; -import { openWindowCommand, COPY_PATH_COMMAND_ID, REVEAL_IN_EXPLORER_COMMAND_ID, OPEN_TO_SIDE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_COMMAND_ID, SAVE_FILE_LABEL, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID, OpenEditorsGroupContext, COMPARE_WITH_SAVED_COMMAND_ID, COMPARE_RESOURCE_COMMAND_ID, SELECT_FOR_COMPARE_COMMAND_ID, ResourceSelectedForCompareContext, DirtyEditorContext, COMPARE_SELECTED_COMMAND_ID, REMOVE_ROOT_FOLDER_COMMAND_ID, REMOVE_ROOT_FOLDER_LABEL, SAVE_FILES_COMMAND_ID, COPY_RELATIVE_PATH_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_LABEL, newWindowCommand, ReadonlyEditorContext, OPEN_WITH_EXPLORER_COMMAND_ID } from 'vs/workbench/contrib/files/browser/fileCommands'; +import { openWindowCommand, COPY_PATH_COMMAND_ID, REVEAL_IN_EXPLORER_COMMAND_ID, OPEN_TO_SIDE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_COMMAND_ID, SAVE_FILE_LABEL, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID, OpenEditorsGroupContext, COMPARE_WITH_SAVED_COMMAND_ID, COMPARE_RESOURCE_COMMAND_ID, SELECT_FOR_COMPARE_COMMAND_ID, ResourceSelectedForCompareContext, DirtyEditorContext, COMPARE_SELECTED_COMMAND_ID, REMOVE_ROOT_FOLDER_COMMAND_ID, REMOVE_ROOT_FOLDER_LABEL, SAVE_FILES_COMMAND_ID, COPY_RELATIVE_PATH_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_LABEL, newWindowCommand, ReadonlyEditorContext, OPEN_WITH_EXPLORER_COMMAND_ID, NEW_UNTITLED_FILE_COMMAND_ID, NEW_UNTITLED_FILE_LABEL } from 'vs/workbench/contrib/files/browser/fileCommands'; import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -26,7 +26,7 @@ import { WorkspaceFolderCountContext } from 'vs/workbench/browser/contextkeys'; import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { OpenFileFolderAction, OpenFileAction, OpenFolderAction, OpenWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; -import { ActiveEditorIsReadonlyContext, DirtyWorkingCopiesContext, ActiveEditorContext, ActiveEditorAvailableEditorIdsContext } from 'vs/workbench/common/editor'; +import { ActiveEditorIsReadonlyContext, DirtyWorkingCopiesContext, ActiveEditorContext } from 'vs/workbench/common/editor'; import { SidebarFocusContext } from 'vs/workbench/common/viewlet'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; @@ -36,13 +36,10 @@ const category = { value: nls.localize('filesCategory', "File"), original: 'File const registry = Registry.as(ActionExtensions.WorkbenchActions); registry.registerWorkbenchAction(SyncActionDescriptor.from(SaveAllAction, { primary: undefined, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_S }, win: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_S) } }), 'File: Save All', category.value); registry.registerWorkbenchAction(SyncActionDescriptor.from(GlobalCompareResourcesAction), 'File: Compare Active File With...', category.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ReopenResourcesAction), 'File: Reopen With...', category.value, ActiveEditorAvailableEditorIdsContext); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleEditorTypeAction), 'File: Toggle Editor Type', category.value, ActiveEditorAvailableEditorIdsContext); registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusFilesExplorer), 'File: Focus on Files Explorer', category.value); registry.registerWorkbenchAction(SyncActionDescriptor.from(ShowActiveFileInExplorer), 'File: Reveal Active File in Side Bar', category.value); registry.registerWorkbenchAction(SyncActionDescriptor.from(CollapseExplorerView), 'File: Collapse Folders in Explorer', category.value); registry.registerWorkbenchAction(SyncActionDescriptor.from(RefreshExplorerView), 'File: Refresh Explorer', category.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(GlobalNewUntitledFileAction, { primary: KeyMod.CtrlCmd | KeyCode.KEY_N }), 'File: New Untitled File', category.value); registry.registerWorkbenchAction(SyncActionDescriptor.from(CompareWithClipboardAction, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_C) }), 'File: Compare Active File with Clipboard', category.value); registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleAutoSaveAction), 'File: Toggle Auto Save', category.value); registry.registerWorkbenchAction(SyncActionDescriptor.from(ShowOpenedFileInNewWindow, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_O) }), 'File: Open Active File in New Window', category.value); @@ -144,9 +141,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ weight: KeybindingWeight.WorkbenchContrib + explorerCommandsWeightBonus, when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerResourceCut), primary: KeyCode.Escape, - handler: (accessor: ServicesAccessor) => { + handler: async (accessor: ServicesAccessor) => { const explorerService = accessor.get(IExplorerService); - explorerService.setToCopy([], true); + await explorerService.setToCopy([], true); } }); @@ -183,19 +180,6 @@ export function appendEditorTitleContextMenuItem(id: string, title: string, when }); } -// Reopen with editor title - -MenuRegistry.appendMenuItem(MenuId.EditorTitle, { - command: { - id: ReopenResourcesAction.ID, - title: ReopenResourcesAction.LABEL, - category: fileCategory, - }, - group: '6_reopen', - order: 20, - when: ActiveEditorAvailableEditorIdsContext, -}); - // Editor Title Menu for Conflict Resolution appendSaveConflictEditorTitleAction('workbench.files.action.acceptLocalChanges', nls.localize('acceptLocalChanges', "Use your changes and overwrite file contents"), { id: 'codicon/check' }, -10, acceptLocalChangesCommand); appendSaveConflictEditorTitleAction('workbench.files.action.revertLocalChanges', nls.localize('revertLocalChanges', "Discard your changes and revert to file contents"), { id: 'codicon/discard' }, -9, revertLocalChangesCommand); @@ -240,6 +224,7 @@ appendToCommandPalette(CLOSE_EDITOR_COMMAND_ID, { value: nls.localize('closeEdit appendToCommandPalette(NEW_FILE_COMMAND_ID, { value: NEW_FILE_LABEL, original: 'New File' }, category, WorkspaceFolderCountContext.notEqualsTo('0')); appendToCommandPalette(NEW_FOLDER_COMMAND_ID, { value: NEW_FOLDER_LABEL, original: 'New Folder' }, category, WorkspaceFolderCountContext.notEqualsTo('0')); appendToCommandPalette(DOWNLOAD_COMMAND_ID, { value: DOWNLOAD_LABEL, original: 'Download' }, category, ContextKeyExpr.and(ResourceContextKey.Scheme.notEqualsTo(Schemas.file))); +appendToCommandPalette(NEW_UNTITLED_FILE_COMMAND_ID, { value: NEW_UNTITLED_FILE_LABEL, original: 'New Untitled File' }, category); // Menu registration - open editors @@ -581,7 +566,7 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { }); // Empty Editor Group Context Menu -MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: GlobalNewUntitledFileAction.ID, title: nls.localize('newFile', "New File") }, group: '1_file', order: 10 }); +MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: NEW_UNTITLED_FILE_COMMAND_ID, title: nls.localize('newFile', "New File") }, group: '1_file', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: 'workbench.action.quickOpen', title: nls.localize('openFile', "Open File...") }, group: '1_file', order: 20 }); // File menu @@ -589,7 +574,7 @@ MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: 'wo MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '1_new', command: { - id: GlobalNewUntitledFileAction.ID, + id: NEW_UNTITLED_FILE_COMMAND_ID, title: nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File") }, order: 1 diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 2b4911f6fde..56f08c0e5e4 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -15,7 +15,7 @@ import { Action } from 'vs/base/common/actions'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { VIEWLET_ID, IExplorerService, IFilesConfiguration, VIEW_ID } from 'vs/workbench/contrib/files/common/files'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IFileService } from 'vs/platform/files/common/files'; +import { IFileService, IFileStatWithMetadata } from 'vs/platform/files/common/files'; import { toResource, SideBySideEditor } from 'vs/workbench/common/editor'; import { ExplorerViewPaneContainer } from 'vs/workbench/contrib/files/browser/explorerViewlet'; import { IQuickInputService, ItemActivation } from 'vs/platform/quickinput/common/quickInput'; @@ -47,11 +47,8 @@ import { IWorkingCopyService, IWorkingCopy } from 'vs/workbench/services/working import { sequence, timeout } from 'vs/base/common/async'; import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { once } from 'vs/base/common/functional'; -import { IEditorOptions } from 'vs/platform/editor/common/editor'; -import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { Codicon } from 'vs/base/common/codicons'; import { IViewsService } from 'vs/workbench/common/views'; -import { openEditorWith, getAllAvailableEditors } from 'vs/workbench/contrib/files/common/openWith'; export const NEW_FILE_COMMAND_ID = 'explorer.newFile'; export const NEW_FILE_LABEL = nls.localize('newFile', "New File"); @@ -122,24 +119,6 @@ export class NewFolderAction extends Action { } } -/* Create new file from anywhere: Open untitled */ -export class GlobalNewUntitledFileAction extends Action { - static readonly ID = 'workbench.action.files.newUntitledFile'; - static readonly LABEL = nls.localize('newUntitledFile', "New Untitled File"); - - constructor( - id: string, - label: string, - @IEditorService private readonly editorService: IEditorService - ) { - super(id, label); - } - - async run(): Promise { - await this.editorService.openEditor({ options: { pinned: true } }); // untitled are always pinned - } -} - async function deleteFiles(workingCopyFileService: IWorkingCopyFileService, dialogService: IDialogService, configurationService: IConfigurationService, elements: ExplorerItem[], useTrash: boolean, skipConfirm = false): Promise { let primaryButton: string; if (useTrash) { @@ -243,7 +222,7 @@ async function deleteFiles(workingCopyFileService: IWorkingCopyFileService, dial // Call function try { - await Promise.all(distinctElements.map(e => workingCopyFileService.delete(e.resource, { useTrash: useTrash, recursive: true }))); + await workingCopyFileService.delete(distinctElements.map(e => e.resource), { useTrash, recursive: true }); } catch (error) { // Handle error to delete file(s) from a modal confirmation dialog @@ -465,6 +444,7 @@ export class GlobalCompareResourcesAction extends Action { @IQuickInputService private readonly quickInputService: IQuickInputService, @IEditorService private readonly editorService: IEditorService, @INotificationService private readonly notificationService: INotificationService, + @ITextModelService private readonly textModelService: ITextModelService ) { super(id, label); } @@ -472,29 +452,35 @@ export class GlobalCompareResourcesAction extends Action { async run(): Promise { const activeInput = this.editorService.activeEditor; const activeResource = activeInput ? activeInput.resource : undefined; - if (activeResource) { + if (activeResource && this.textModelService.canHandleResource(activeResource)) { // Compare with next editor that opens const toDispose = this.editorService.overrideOpenEditor({ - getEditorOverrides: (resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined) => { - return []; - }, open: editor => { + // Only once! toDispose.dispose(); // Open editor as diff const resource = editor.resource; - if (resource) { + if (resource && this.textModelService.canHandleResource(resource)) { return { override: this.editorService.openEditor({ leftResource: activeResource, - rightResource: resource + rightResource: resource, + options: { override: false } }) }; } - return undefined; + // Otherwise stay on current resource + this.notificationService.info(nls.localize('fileToCompareNoFile', "Please select a file to compare with.")); + return { + override: this.editorService.openEditor({ + resource: activeResource, + options: { override: false } + }) + }; } }); @@ -511,75 +497,6 @@ export class GlobalCompareResourcesAction extends Action { } } -export class ReopenResourcesAction extends Action { - - static readonly ID = 'workbench.files.action.reopenWithEditor'; - static readonly LABEL = nls.localize('workbench.files.action.reopenWithEditor', "Reopen With..."); - - constructor( - id: string, - label: string, - @IQuickInputService private readonly quickInputService: IQuickInputService, - @IEditorService private readonly editorService: IEditorService, - @IConfigurationService private readonly configurationService: IConfigurationService - ) { - super(id, label); - } - - async run(): Promise { - const activeInput = this.editorService.activeEditor; - if (!activeInput) { - return; - } - - const activeEditorPane = this.editorService.activeEditorPane; - if (!activeEditorPane) { - return; - } - - const options = activeEditorPane.options; - const group = activeEditorPane.group; - await openEditorWith(activeInput, undefined, options, group, this.editorService, this.configurationService, this.quickInputService); - } -} - -export class ToggleEditorTypeCommand extends Action { - - static readonly ID = 'workbench.files.action.toggleEditorType'; - static readonly LABEL = nls.localize('workbench.files.action.toggleEditorType', "Toggle Editor Type"); - - constructor( - id: string, - label: string, - @IEditorService private readonly editorService: IEditorService, - ) { - super(id, label); - } - - async run(): Promise { - const activeEditorPane = this.editorService.activeEditorPane; - if (!activeEditorPane) { - return; - } - - const input = activeEditorPane.input; - if (!input.resource) { - return; - } - - const options = activeEditorPane.options; - const group = activeEditorPane.group; - - const overrides = getAllAvailableEditors(input.resource, options, group, this.editorService); - const firstNonActiveOverride = overrides.find(([_, entry]) => !entry.active); - if (!firstNonActiveOverride) { - return; - } - - await firstNonActiveOverride[0].open(input, options, group, firstNonActiveOverride[1].id)?.override; - } -} - export class ToggleAutoSaveAction extends Action { static readonly ID = 'workbench.action.toggleAutoSave'; static readonly LABEL = nls.localize('toggleAutoSave', "Toggle Auto Save"); @@ -716,7 +633,7 @@ export class ShowActiveFileInExplorer extends Action { } async run(): Promise { - const resource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }); + const resource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }); if (resource) { this.commandService.executeCommand(REVEAL_IN_EXPLORER_COMMAND_ID, resource); } else { @@ -784,7 +701,7 @@ export class ShowOpenedFileInNewWindow extends Action { } async run(): Promise { - const fileResource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }); + const fileResource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }); if (fileResource) { if (this.fileService.canHandleResource(fileResource)) { this.hostService.openWindow([{ fileUri: fileResource }], { forceNewWindow: true }); @@ -896,7 +813,7 @@ export class CompareWithClipboardAction extends Action { } async run(): Promise { - const resource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }); + const resource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }); const scheme = `clipboardCompare${CompareWithClipboardAction.SCHEME_COUNTER++}`; if (resource && (this.fileService.canHandleResource(resource) || resource.scheme === Schemas.untitled)) { if (!this.registrationDisposal) { @@ -1030,7 +947,7 @@ export const renameHandler = async (accessor: ServicesAccessor) => { const targetResource = resources.joinPath(parentResource, value); if (stat.resource.toString() !== targetResource.toString()) { try { - await workingCopyFileService.move(stat.resource, targetResource); + await workingCopyFileService.move([{ source: stat.resource, target: targetResource }]); await refreshIfSeparator(value, explorerService); } catch (e) { notificationService.error(e); @@ -1060,20 +977,20 @@ export const deleteFileHandler = async (accessor: ServicesAccessor) => { }; let pasteShouldMove = false; -export const copyFileHandler = (accessor: ServicesAccessor) => { +export const copyFileHandler = async (accessor: ServicesAccessor) => { const explorerService = accessor.get(IExplorerService); const stats = explorerService.getContext(true); if (stats.length > 0) { - explorerService.setToCopy(stats, false); + await explorerService.setToCopy(stats, false); pasteShouldMove = false; } }; -export const cutFileHandler = (accessor: ServicesAccessor) => { +export const cutFileHandler = async (accessor: ServicesAccessor) => { const explorerService = accessor.get(IExplorerService); const stats = explorerService.getContext(true); if (stats.length > 0) { - explorerService.setToCopy(stats, true); + await explorerService.setToCopy(stats, true); pasteShouldMove = true; } }; @@ -1116,7 +1033,7 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { defaultUri }); if (destination) { - await workingCopyFileService.copy(s.resource, destination, true); + await workingCopyFileService.copy([{ source: s.resource, target: destination }], { overwrite: true }); } else { // User canceled a download. In case there were multiple files selected we should cancel the remainder of the prompts #86100 canceled = true; @@ -1140,17 +1057,16 @@ export const pasteFileHandler = async (accessor: ServicesAccessor) => { const configurationService = accessor.get(IConfigurationService); const context = explorerService.getContext(true); - const toPaste = resources.distinctParents(clipboardService.readResources(), r => r); + const toPaste = resources.distinctParents(await clipboardService.readResources(), r => r); const element = context.length ? context[0] : explorerService.roots[0]; - // Check if target is ancestor of pasted folder - const stats = await Promise.all(toPaste.map(async fileToPaste => { + try { + // Check if target is ancestor of pasted folder + const sourceTargetPairs = await Promise.all(toPaste.map(async fileToPaste => { - if (element.resource.toString() !== fileToPaste.toString() && resources.isEqualOrParent(element.resource, fileToPaste)) { - throw new Error(nls.localize('fileIsAncestor', "File to paste is an ancestor of the destination folder")); - } - - try { + if (element.resource.toString() !== fileToPaste.toString() && resources.isEqualOrParent(element.resource, fileToPaste)) { + throw new Error(nls.localize('fileIsAncestor', "File to paste is an ancestor of the destination folder")); + } const fileToPasteStat = await fileService.resolve(fileToPaste); // Find target @@ -1164,30 +1080,33 @@ export const pasteFileHandler = async (accessor: ServicesAccessor) => { const incrementalNaming = configurationService.getValue().explorer.incrementalNaming; const targetFile = findValidPasteFileTarget(explorerService, target, { resource: fileToPaste, isDirectory: fileToPasteStat.isDirectory, allowOverwrite: pasteShouldMove }, incrementalNaming); - // Move/Copy File - if (pasteShouldMove) { - return await workingCopyFileService.move(fileToPaste, targetFile); - } else { - return await workingCopyFileService.copy(fileToPaste, targetFile); - } - } catch (e) { - onError(notificationService, new Error(nls.localize('fileDeleted', "The file to paste has been deleted or moved since you copied it. {0}", getErrorMessage(e)))); - return undefined; - } - })); + return { source: fileToPaste, target: targetFile }; + })); - if (pasteShouldMove) { - // Cut is done. Make sure to clear cut state. - explorerService.setToCopy([], false); - pasteShouldMove = false; - } - if (stats.length >= 1) { - const stat = stats[0]; - if (stat && !stat.isDirectory && stats.length === 1) { - await editorService.openEditor({ resource: stat.resource, options: { pinned: true, preserveFocus: true } }); + // Move/Copy File + let stats: IFileStatWithMetadata[] = []; + if (pasteShouldMove) { + stats = await workingCopyFileService.move(sourceTargetPairs); + } else { + stats = await workingCopyFileService.copy(sourceTargetPairs); } - if (stat) { - await explorerService.select(stat.resource); + + if (stats.length >= 1) { + const stat = stats[0]; + if (stat && !stat.isDirectory && stats.length === 1) { + await editorService.openEditor({ resource: stat.resource, options: { pinned: true, preserveFocus: true } }); + } + if (stat) { + await explorerService.select(stat.resource); + } + } + } catch (e) { + onError(notificationService, new Error(nls.localize('fileDeleted', "The file(s) to paste have been deleted or moved since you copied them. {0}", getErrorMessage(e)))); + } finally { + if (pasteShouldMove) { + // Cut is done. Make sure to clear cut state. + await explorerService.setToCopy([], false); + pasteShouldMove = false; } } }; diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index fbca13d1641..441537060af 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -40,9 +40,10 @@ import { coalesce } from 'vs/base/common/arrays'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { openEditorWith } from 'vs/workbench/contrib/files/common/openWith'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith'; // Commands @@ -84,6 +85,8 @@ export const PREVIOUS_COMPRESSED_FOLDER = 'previousCompressedFolder'; export const NEXT_COMPRESSED_FOLDER = 'nextCompressedFolder'; export const FIRST_COMPRESSED_FOLDER = 'firstCompressedFolder'; export const LAST_COMPRESSED_FOLDER = 'lastCompressedFolder'; +export const NEW_UNTITLED_FILE_COMMAND_ID = 'workbench.action.files.newUntitledFile'; +export const NEW_UNTITLED_FILE_LABEL = nls.localize('newUntitledFile', "New Untitled File"); export const openWindowCommand = (accessor: ServicesAccessor, toOpen: IWindowOpenable[], options?: IOpenWindowOptions) => { if (Array.isArray(toOpen)) { @@ -175,7 +178,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ // Dispose once no more diff editor is opened with the scheme if (registerEditorListener) { providerDisposables.push(editorService.onDidVisibleEditorsChange(() => { - if (!editorService.editors.some(editor => !!toResource(editor, { supportSideBySide: SideBySideEditor.DETAILS, filterByScheme: COMPARE_WITH_SAVED_SCHEMA }))) { + if (!editorService.editors.some(editor => !!toResource(editor, { supportSideBySide: SideBySideEditor.SECONDARY, filterByScheme: COMPARE_WITH_SAVED_SCHEMA }))) { providerDisposables = dispose(providerDisposables); } })); @@ -353,8 +356,8 @@ async function saveSelectedEditors(accessor: ServicesAccessor, options?: ISaveEd // We only allow this when saving, not for "Save As". // See also https://github.com/microsoft/vscode/issues/4180 if (activeGroup.activeEditor instanceof SideBySideEditorInput && !options?.saveAs) { - editors.push({ groupId: activeGroup.id, editor: activeGroup.activeEditor.master }); - editors.push({ groupId: activeGroup.id, editor: activeGroup.activeEditor.details }); + editors.push({ groupId: activeGroup.id, editor: activeGroup.activeEditor.primary }); + editors.push({ groupId: activeGroup.id, editor: activeGroup.activeEditor.secondary }); } else { editors.push({ groupId: activeGroup.id, editor: activeGroup.activeEditor }); } @@ -377,7 +380,7 @@ async function saveSelectedEditors(accessor: ServicesAccessor, options?: ISaveEd const resource = focusedCodeEditor.getModel()?.uri; // Check that the resource of the model was not saved already - if (resource && !editors.some(({ editor }) => isEqual(toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }), resource))) { + if (resource && !editors.some(({ editor }) => isEqual(toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY }), resource))) { const model = textFileService.files.get(resource); if (!model?.isReadonly()) { await textFileService.save(resource, options); @@ -406,7 +409,7 @@ async function doSaveEditors(accessor: ServicesAccessor, editors: IEditorIdentif try { await editorService.save(editors, options); } catch (error) { - notificationService.error(nls.localize('genericSaveError', "Failed to save '{0}': {1}", editors.map(({ editor }) => editor.getName()).join(', '), toErrorMessage(error, false))); + notificationService.error(nls.localize({ key: 'genericSaveError', comment: ['{0} is the resource that failed to save and {1} the error message'] }, "Failed to save '{0}': {1}", editors.map(({ editor }) => editor.getName()).join(', '), toErrorMessage(error, false))); } } @@ -509,10 +512,10 @@ CommandsRegistry.registerCommand({ handler: (accessor, resource: URI | object) => { const workspaceEditingService = accessor.get(IWorkspaceEditingService); const contextService = accessor.get(IWorkspaceContextService); + const uriIdentityService = accessor.get(IUriIdentityService); const workspace = contextService.getWorkspace(); - const resources = getMultiSelectedResources(resource, accessor.get(IListService), accessor.get(IEditorService), accessor.get(IExplorerService)).filter(r => - // Need to verify resources are workspaces since multi selection can trigger this command on some non workspace resources - workspace.folders.some(f => isEqual(f.uri, r)) + const resources = getMultiSelectedResources(resource, accessor.get(IListService), accessor.get(IEditorService), accessor.get(IExplorerService)).filter(resource => + workspace.folders.some(folder => uriIdentityService.extUri.isEqual(folder.uri, resource)) // Need to verify resources are workspaces since multi selection can trigger this command on some non workspace resources ); return workspaceEditingService.removeFolders(resources); @@ -596,3 +599,41 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ view.lastCompressedStat(); } }); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + weight: KeybindingWeight.WorkbenchContrib, + when: null, + primary: KeyMod.CtrlCmd | KeyCode.KEY_N, + id: NEW_UNTITLED_FILE_COMMAND_ID, + description: { + description: NEW_UNTITLED_FILE_LABEL, + args: [ + { + name: 'viewType', description: 'The editor view type', schema: { + 'type': 'object', + 'required': ['viewType'], + 'properties': { + 'viewType': { + 'type': 'string' + } + } + } + } + ] + }, + handler: async (accessor, args?: { viewType: string }) => { + const editorService = accessor.get(IEditorService); + + if (args) { + const editorGroupsService = accessor.get(IEditorGroupsService); + const configurationService = accessor.get(IConfigurationService); + const quickInputService = accessor.get(IQuickInputService); + + const textInput = editorService.createEditorInput({ options: { pinned: true } }); + const group = editorGroupsService.activeGroup; + await openEditorWith(textInput, args.viewType, { pinned: true }, group, editorService, configurationService, quickInputService); + } else { + await editorService.openEditor({ options: { pinned: true } }); // untitled are always pinned + } + } +}); diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index c03187a2388..71f86a19285 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -34,11 +34,12 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ExplorerService } from 'vs/workbench/contrib/files/common/explorerService'; -import { SUPPORTED_ENCODINGS } from 'vs/workbench/services/textfile/common/textfiles'; +import { SUPPORTED_ENCODINGS } from 'vs/workbench/services/textfile/common/encoding'; import { Schemas } from 'vs/base/common/network'; import { WorkspaceWatcher } from 'vs/workbench/contrib/files/common/workspaceWatcher'; import { editorConfigurationBaseNode } from 'vs/editor/common/config/commonEditorConfig'; import { DirtyFilesIndicator } from 'vs/workbench/contrib/files/common/dirtyFilesIndicator'; +import { extUri } from 'vs/base/common/resources'; // Viewlet Action export class OpenExplorerViewletAction extends ShowViewletAction { @@ -101,8 +102,8 @@ Registry.as(EditorExtensions.Editors).registerEditor( // Register default file input factory Registry.as(EditorInputExtensions.EditorInputFactories).registerFileEditorInputFactory({ - createFileEditorInput: (resource, encoding, mode, instantiationService): IFileEditorInput => { - return instantiationService.createInstance(FileEditorInput, resource, encoding, mode); + createFileEditorInput: (resource, preferredResource, encoding, mode, instantiationService): IFileEditorInput => { + return instantiationService.createInstance(FileEditorInput, resource, preferredResource, encoding, mode); }, isFileEditorInput: (obj): obj is IFileEditorInput => { @@ -112,6 +113,7 @@ Registry.as(EditorInputExtensions.EditorInputFactor interface ISerializedFileEditorInput { resourceJSON: UriComponents; + preferredResourceJSON?: UriComponents; encoding?: string; modeId?: string; } @@ -126,8 +128,10 @@ class FileEditorInputFactory implements IEditorInputFactory { serialize(editorInput: EditorInput): string { const fileEditorInput = editorInput; const resource = fileEditorInput.resource; + const preferredResource = fileEditorInput.preferredResource; const serializedFileEditorInput: ISerializedFileEditorInput = { resourceJSON: resource.toJSON(), + preferredResourceJSON: extUri.isEqual(resource, preferredResource) ? undefined : preferredResource, // only storing preferredResource if it differs from the resource encoding: fileEditorInput.getEncoding(), modeId: fileEditorInput.getPreferredMode() // only using the preferred user associated mode here if available to not store redundant data }; @@ -139,10 +143,16 @@ class FileEditorInputFactory implements IEditorInputFactory { return instantiationService.invokeFunction(accessor => { const serializedFileEditorInput: ISerializedFileEditorInput = JSON.parse(serializedEditorInput); const resource = URI.revive(serializedFileEditorInput.resourceJSON); + const preferredResource = URI.revive(serializedFileEditorInput.preferredResourceJSON); const encoding = serializedFileEditorInput.encoding; const mode = serializedFileEditorInput.modeId; - return accessor.get(IEditorService).createEditorInput({ resource, encoding, mode, forceFile: true }) as FileEditorInput; + const fileEditorInput = accessor.get(IEditorService).createEditorInput({ resource, encoding, mode, forceFile: true }) as FileEditorInput; + if (preferredResource) { + fileEditorInput.setPreferredResource(preferredResource); + } + + return fileEditorInput; }); } } @@ -228,6 +238,9 @@ configurationRegistry.registerConfiguration({ [FILES_ASSOCIATIONS_CONFIG]: { 'type': 'object', 'markdownDescription': nls.localize('associations', "Configure file associations to languages (e.g. `\"*.extension\": \"html\"`). These have precedence over the default associations of the languages installed."), + 'additionalProperties': { + 'type': 'string' + } }, 'files.encoding': { 'type': 'string', @@ -235,15 +248,13 @@ configurationRegistry.registerConfiguration({ 'default': 'utf8', 'description': nls.localize('encoding', "The default character set encoding to use when reading and writing files. This setting can also be configured per language."), 'scope': ConfigurationScope.LANGUAGE_OVERRIDABLE, - 'enumDescriptions': Object.keys(SUPPORTED_ENCODINGS).map(key => SUPPORTED_ENCODINGS[key].labelLong), - 'included': Object.keys(SUPPORTED_ENCODINGS).length > 1 + 'enumDescriptions': Object.keys(SUPPORTED_ENCODINGS).map(key => SUPPORTED_ENCODINGS[key].labelLong) }, 'files.autoGuessEncoding': { 'type': 'boolean', 'default': false, 'description': nls.localize('autoGuessEncoding', "When enabled, the editor will attempt to guess the character set encoding when opening files. This setting can also be configured per language."), - 'scope': ConfigurationScope.LANGUAGE_OVERRIDABLE, - 'included': Object.keys(SUPPORTED_ENCODINGS).length > 1 + 'scope': ConfigurationScope.LANGUAGE_OVERRIDABLE }, 'files.eol': { 'type': 'string', @@ -369,9 +380,15 @@ configurationRegistry.registerConfiguration({ 'default': 9 }, 'explorer.autoReveal': { - 'type': 'boolean', - 'description': nls.localize('autoReveal', "Controls whether the explorer should automatically reveal and select files when opening them."), - 'default': true + 'type': ['boolean', 'string'], + 'enum': [true, false, 'focusNoScroll'], + 'default': true, + 'enumDescriptions': [ + nls.localize('autoReveal.on', 'Files will be revealed and selected.'), + nls.localize('autoReveal.off', 'Files will not be revealed and selected.'), + nls.localize('autoReveal.focusNoScroll', 'Files will not be scrolled into view, but will still be focused.'), + ], + 'description': nls.localize('autoReveal', "Controls whether the explorer should automatically reveal and select files when opening them.") }, 'explorer.enableDragAndDrop': { 'type': 'boolean', diff --git a/src/vs/workbench/contrib/files/browser/files.ts b/src/vs/workbench/contrib/files/browser/files.ts index 1274f2b97d5..ac012e62fc4 100644 --- a/src/vs/workbench/contrib/files/browser/files.ts +++ b/src/vs/workbench/contrib/files/browser/files.ts @@ -50,7 +50,7 @@ export function getResourceForCommand(resource: URI | object | undefined, listSe return focus.getResource(); } - return editorService.activeEditor ? toResource(editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }) : undefined; + return editorService.activeEditor ? toResource(editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }) : undefined; } export function getMultiSelectedResources(resource: URI | object | undefined, listService: IListService, editorService: IEditorService, explorerService: IExplorerService): Array { diff --git a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css index b77e6fb7065..7257c3e62c6 100644 --- a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css +++ b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css @@ -55,6 +55,7 @@ align-items: center; } +.pane.horizontal:not(.expanded) .pane-header .dirty-count.monaco-count-badge, .pane-header .dirty-count.monaco-count-badge.hidden { display: none; } @@ -95,6 +96,6 @@ } /* High Contrast Theming */ -.hc-black .monaco-workbench .explorer-viewlet .explorer-item { +.monaco-workbench.hc-black .explorer-viewlet .explorer-item { line-height: 20px; } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 0b77b620e36..66d94162794 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -24,7 +24,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { IDecorationsService } from 'vs/workbench/services/decorations/browser/decorations'; -import { TreeResourceNavigator, WorkbenchCompressibleAsyncDataTree } from 'vs/platform/list/browser/listService'; +import { WorkbenchCompressibleAsyncDataTree } from 'vs/platform/list/browser/listService'; import { DelayedDragHandler } from 'vs/base/browser/dnd'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; @@ -37,15 +37,11 @@ import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions' import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExplorerItem, NewExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; -import { onUnexpectedError } from 'vs/base/common/errors'; import { ResourceLabels } from 'vs/workbench/browser/labels'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataTree'; import { FuzzyScore } from 'vs/base/common/filters'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { isEqualOrParent } from 'vs/base/common/resources'; -import { values } from 'vs/base/common/map'; -import { first } from 'vs/base/common/arrays'; import { withNullAsUndefined } from 'vs/base/common/types'; import { IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; @@ -56,6 +52,7 @@ import { Color } from 'vs/base/common/color'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; interface IExplorerViewColors extends IColorMapping { listDropBackground?: ColorValue | undefined; @@ -143,10 +140,12 @@ export class ExplorerView extends ViewPane { private compressedFocusFirstContext: IContextKey; private compressedFocusLastContext: IContextKey; + private horizontalScrolling: boolean | undefined; + // Refresh is needed on the initial explorer open private shouldRefresh = true; private dragHandler!: DelayedDragHandler; - private autoReveal = false; + private autoReveal: boolean | 'focusNoScroll' = false; private actions: IAction[] | undefined; private decorationsProvider: ExplorerDecorationsProvider | undefined; @@ -171,6 +170,7 @@ export class ExplorerView extends ViewPane { @IStorageService private readonly storageService: IStorageService, @IClipboardService private clipboardService: IClipboardService, @IFileService private readonly fileService: IFileService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @IOpenerService openerService: IOpenerService, ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); @@ -311,8 +311,19 @@ export class ExplorerView extends ViewPane { async setEditable(stat: ExplorerItem, isEditing: boolean): Promise { if (isEditing) { + this.horizontalScrolling = this.tree.options.horizontalScrolling; + + if (this.horizontalScrolling) { + this.tree.updateOptions({ horizontalScrolling: false }); + } + await this.tree.expand(stat.parent!); } else { + if (this.horizontalScrolling !== undefined) { + this.tree.updateOptions({ horizontalScrolling: this.horizontalScrolling }); + } + + this.horizontalScrolling = undefined; DOM.removeClass(this.treeContainer, 'highlight'); } @@ -410,22 +421,23 @@ export class ExplorerView extends ViewPane { // Update resource context based on focused element this._register(this.tree.onDidChangeFocus(e => this.onFocusChanged(e.elements))); this.onFocusChanged([]); - const explorerNavigator = new TreeResourceNavigator(this.tree); - this._register(explorerNavigator); // Open when selecting via keyboard - this._register(explorerNavigator.onDidOpenResource(async e => { - const selection = this.tree.getSelection(); + this._register(this.tree.onDidOpen(async e => { + const element = e.element; + if (!element) { + return; + } // Do not react if the user is expanding selection via keyboard. // Check if the item was previously also selected, if yes the user is simply expanding / collapsing current selection #66589. const shiftDown = e.browserEvent instanceof KeyboardEvent && e.browserEvent.shiftKey; - if (selection.length === 1 && !shiftDown) { - if (selection[0].isDirectory || this.explorerService.isEditable(undefined)) { + if (!shiftDown) { + if (element.isDirectory || this.explorerService.isEditable(undefined)) { // Do not react if user is clicking on explorer items while some are being edited #70276 // Do not react if clicking on directories return; } this.telemetryService.publicLog2('workbenchActionExecuted', { id: 'workbench.files.openFile', from: 'explorer' }); - await this.editorService.openEditor({ resource: selection[0].resource, options: { preserveFocus: e.editorOptions.preserveFocus, pinned: e.editorOptions.pinned } }, e.sideBySide ? SIDE_GROUP : ACTIVE_GROUP); + await this.editorService.openEditor({ resource: element.resource, options: { preserveFocus: e.editorOptions.preserveFocus, pinned: e.editorOptions.pinned } }, e.sideBySide ? SIDE_GROUP : ACTIVE_GROUP); } })); @@ -438,6 +450,16 @@ export class ExplorerView extends ViewPane { } })); + this._register(this.tree.onDidChangeCollapseState(e => { + const element = e.node.element?.element; + if (element) { + const navigationController = this.renderer.getCompressedNavigationController(element instanceof Array ? element[0] : element); + if (navigationController) { + navigationController.updateCollapsed(e.node.collapsed); + } + } + })); + // save view state this._register(this.storageService.onWillSaveState(() => { this.storageService.store(ExplorerView.TREE_VIEW_STATE_STORAGE_KEY, JSON.stringify(this.tree.getViewState()), StorageScope.WORKSPACE); @@ -471,7 +493,7 @@ export class ExplorerView extends ViewPane { } } - private onContextMenu(e: ITreeContextMenuEvent): void { + private async onContextMenu(e: ITreeContextMenuEvent): Promise { const disposables = new DisposableStore(); let stat = e.element; let anchor = e.anchor; @@ -490,7 +512,7 @@ export class ExplorerView extends ViewPane { } // update dynamic contexts - this.fileCopiedContextKey.set(this.clipboardService.hasResources()); + this.fileCopiedContextKey.set(await this.clipboardService.hasResources()); this.setContextKeys(stat); const selection = this.tree.getSelection(); @@ -605,11 +627,19 @@ export class ExplorerView extends ViewPane { if (Array.isArray(input)) { if (!viewState || previousInput instanceof ExplorerItem) { // There is no view state for this workspace, expand all roots. Or we transitioned from a folder workspace. - input.forEach(item => this.tree.expand(item).then(undefined, onUnexpectedError)); + input.forEach(async item => { + try { + await this.tree.expand(item); + } catch (e) { } + }); } if (Array.isArray(previousInput) && previousInput.length < input.length) { // Roots added to the explorer -> expand them. - input.slice(previousInput.length).forEach(item => this.tree.expand(item).then(undefined, onUnexpectedError)); + input.slice(previousInput.length).forEach(async item => { + try { + await this.tree.expand(item); + } catch (e) { } + }); } } if (initialInputSetup) { @@ -638,7 +668,7 @@ export class ExplorerView extends ViewPane { } // check for files - return withNullAsUndefined(toResource(input, { supportSideBySide: SideBySideEditor.MASTER })); + return withNullAsUndefined(toResource(input, { supportSideBySide: SideBySideEditor.PRIMARY })); } public async selectResource(resource: URI | undefined, reveal = this.autoReveal, retry = 0): Promise { @@ -652,8 +682,7 @@ export class ExplorerView extends ViewPane { } // Expand all stats in the parent chain. - const ignoreCase = !this.fileService.hasCapability(resource, FileSystemProviderCapabilities.PathCaseSensitive); - let item: ExplorerItem | undefined = this.explorerService.roots.filter(i => isEqualOrParent(resource, i.resource, ignoreCase)) + let item: ExplorerItem | undefined = this.explorerService.roots.filter(i => this.uriIdentityService.extUri.isEqualOrParent(resource, i.resource)) // Take the root that is the closest to the stat #72299 .sort((first, second) => second.resource.path.length - first.resource.path.length)[0]; @@ -663,7 +692,14 @@ export class ExplorerView extends ViewPane { } catch (e) { return this.selectResource(resource, reveal, retry + 1); } - item = first(values(item.children), i => isEqualOrParent(resource, i.resource, ignoreCase)); + + for (let child of item.children.values()) { + if (this.uriIdentityService.extUri.isEqualOrParent(resource, child.resource)) { + item = child; + break; + } + item = undefined; + } } if (item) { @@ -674,11 +710,9 @@ export class ExplorerView extends ViewPane { } try { - if (reveal) { - // Don't scroll to the item if it's already visible - if (this.tree.getRelativeTop(item) === null) { - this.tree.reveal(item, 0.5); - } + if (reveal === true && this.tree.getRelativeTop(item) === null) { + // Don't scroll to the item if it's already visible, or if set not to. + this.tree.reveal(item, 0.5); } this.tree.setFocus([item]); diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 7458682b6c6..08a100254f8 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -7,9 +7,9 @@ import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import * as DOM from 'vs/base/browser/dom'; import * as glob from 'vs/base/common/glob'; import { IListVirtualDelegate, ListDragOverEffect } from 'vs/base/browser/ui/list/list'; -import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; +import { IProgressService, ProgressLocation, IProgressStep, IProgress } from 'vs/platform/progress/common/progress'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { IFileService, FileKind, FileOperationError, FileOperationResult, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; +import { IFileService, FileKind, FileOperationError, FileOperationResult, FileSystemProviderCapabilities, BinarySize } from 'vs/platform/files/common/files'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IDisposable, Disposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; @@ -19,7 +19,7 @@ import { ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IAsyncDataSou import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { IFilesConfiguration, IExplorerService } from 'vs/workbench/contrib/files/common/files'; +import { IFilesConfiguration, IExplorerService, VIEW_ID } from 'vs/workbench/contrib/files/common/files'; import { dirname, joinPath, isEqualOrParent, basename, distinctParents } from 'vs/base/common/resources'; import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; import { localize } from 'vs/nls'; @@ -50,12 +50,13 @@ import { Emitter, Event, EventMultiplexer } from 'vs/base/common/event'; import { ITreeCompressionDelegate } from 'vs/base/browser/ui/tree/asyncDataTree'; import { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; import { ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; -import { VSBuffer } from 'vs/base/common/buffer'; +import { VSBuffer, newWriteableBufferStream } from 'vs/base/common/buffer'; import { ILabelService } from 'vs/platform/label/common/label'; import { isNumber } from 'vs/base/common/types'; import { domEvent } from 'vs/base/browser/event'; import { IEditableData } from 'vs/workbench/common/views'; import { IEditorInput } from 'vs/workbench/common/editor'; +import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; export class ExplorerDelegate implements IListVirtualDelegate { @@ -133,6 +134,7 @@ export interface ICompressedNavigationController { first(): void; last(): void; setIndex(index: number): void; + updateCollapsed(collapsed: boolean): void; } export class CompressedNavigationController implements ICompressedNavigationController, IDisposable { @@ -152,7 +154,7 @@ export class CompressedNavigationController implements ICompressedNavigationCont private _onDidChange = new Emitter(); readonly onDidChange = this._onDidChange.event; - constructor(private id: string, readonly items: ExplorerItem[], templateData: IFileTemplateData) { + constructor(private id: string, readonly items: ExplorerItem[], templateData: IFileTemplateData, private depth: number, private collapsed: boolean) { this._index = items.length - 1; this.updateLabels(templateData); @@ -164,7 +166,9 @@ export class CompressedNavigationController implements ICompressedNavigationCont for (let i = 0; i < this.labels.length; i++) { this.labels[i].setAttribute('aria-label', this.items[i].name); + this.labels[i].setAttribute('aria-level', `${this.depth + i}`); } + this.updateCollapsed(this.collapsed); if (this._index < this.labels.length) { DOM.addClass(this.labels[this._index], 'active'); @@ -215,6 +219,13 @@ export class CompressedNavigationController implements ICompressedNavigationCont this._onDidChange.fire(); } + updateCollapsed(collapsed: boolean): void { + this.collapsed = collapsed; + for (let i = 0; i < this.labels.length; i++) { + this.labels[i].setAttribute('aria-expanded', collapsed ? 'false' : 'true'); + } + } + dispose(): void { this._onDidChange.dispose(); this._updateLabelDisposable.dispose(); @@ -307,7 +318,7 @@ export class FilesRenderer implements ICompressibleTreeRenderer e.name); disposables.add(this.renderStat(stat, label, id, node.filterData, templateData)); - const compressedNavigationController = new CompressedNavigationController(id, node.element.elements, templateData); + const compressedNavigationController = new CompressedNavigationController(id, node.element.elements, templateData, node.depth, node.collapsed); disposables.add(compressedNavigationController); this.compressedNavigationControllers.set(stat, compressedNavigationController); @@ -532,7 +543,7 @@ export class FilesFilter implements ITreeFilter { }); this.editorsAffectingFilter.forEach(e => { - if (editors.indexOf(e) === -1) { + if (!editors.includes(e)) { // Editor that was affecting filtering is no longer visible shouldFire = true; } @@ -616,7 +627,7 @@ export class FilesFilter implements ITreeFilter { } } -// // Explorer Sorter +// Explorer Sorter export class FileSorter implements ITreeSorter { constructor( @@ -624,7 +635,7 @@ export class FileSorter implements ITreeSorter { @IWorkspaceContextService private readonly contextService: IWorkspaceContextService ) { } - public compare(statA: ExplorerItem, statB: ExplorerItem): number { + compare(statA: ExplorerItem, statB: ExplorerItem): number { // Do not sort roots if (statA.isRoot) { if (statB.isRoot) { @@ -703,14 +714,56 @@ export class FileSorter implements ITreeSorter { } } -const getFileOverwriteConfirm = (name: string) => { - return { +function getFileOverwriteConfirm(name: string): IConfirmation { + return { message: localize('confirmOverwrite', "A file or folder with the name '{0}' already exists in the destination folder. Do you want to replace it?", name), detail: localize('irreversible', "This action is irreversible!"), primaryButton: localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"), type: 'warning' }; -}; +} + +function getMultipleFilesOverwriteConfirm(files: URI[]): IConfirmation { + if (files.length > 1) { + return { + message: localize('confirmManyOverwrites', "The following {0} files and/or folders already exist in the destination folder. Do you want to replace them?", files.length), + detail: getFileNamesMessage(files) + '\n' + localize('irreversible', "This action is irreversible!"), + primaryButton: localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"), + type: 'warning' + }; + } + + return getFileOverwriteConfirm(basename(files[0])); +} + +interface IWebkitDataTransfer { + items: IWebkitDataTransferItem[]; +} + +interface IWebkitDataTransferItem { + webkitGetAsEntry(): IWebkitDataTransferItemEntry; +} + +interface IWebkitDataTransferItemEntry { + name: string | undefined; + isFile: boolean; + isDirectory: boolean; + + file(resolve: (file: File) => void, reject: () => void): void; + createReader(): IWebkitDataTransferItemEntryReader; +} + +interface IWebkitDataTransferItemEntryReader { + readEntries(resolve: (file: IWebkitDataTransferItemEntry[]) => void, reject: () => void): void +} + +interface IUploadOperation { + filesTotal: number; + filesUploaded: number; + + startTime: number; + bytesUploaded: number; +} export class FileDragAndDrop implements ITreeDragAndDrop { private static readonly CONFIRM_DND_SETTING_KEY = 'explorer.confirmDragAndDrop'; @@ -732,7 +785,8 @@ export class FileDragAndDrop implements ITreeDragAndDrop { @IInstantiationService private instantiationService: IInstantiationService, @IWorkingCopyFileService private workingCopyFileService: IWorkingCopyFileService, @IHostService private hostService: IHostService, - @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService + @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService, + @IProgressService private readonly progressService: IProgressService ) { this.toDispose = []; @@ -756,7 +810,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { const iconLabelName = getIconLabelNameFromHTMLElement(originalEvent.target); if (iconLabelName && iconLabelName.index < iconLabelName.count - 1) { - const result = this._onDragOver(data, compressedTarget, targetIndex, originalEvent); + const result = this.handleDragOver(data, compressedTarget, targetIndex, originalEvent); if (result) { if (iconLabelName.element !== this.compressedDragOverElement) { @@ -780,10 +834,10 @@ export class FileDragAndDrop implements ITreeDragAndDrop { } this.compressedDropTargetDisposable.dispose(); - return this._onDragOver(data, target, targetIndex, originalEvent); + return this.handleDragOver(data, target, targetIndex, originalEvent); } - private _onDragOver(data: IDragAndDropData, target: ExplorerItem | undefined, targetIndex: number | undefined, originalEvent: DragEvent): boolean | ITreeDragOverReaction { + private handleDragOver(data: IDragAndDropData, target: ExplorerItem | undefined, targetIndex: number | undefined, originalEvent: DragEvent): boolean | ITreeDragOverReaction { const isCopy = originalEvent && ((originalEvent.ctrlKey && !isMacintosh) || (originalEvent.altKey && isMacintosh)); const fromDesktop = data instanceof DesktopDragAndDropData; const effect = (fromDesktop || isCopy) ? ListDragOverEffect.Copy : ListDragOverEffect.Move; @@ -939,32 +993,222 @@ export class FileDragAndDrop implements ITreeDragAndDrop { } private async handleWebExternalDrop(data: DesktopDragAndDropData, target: ExplorerItem, originalEvent: DragEvent): Promise { - data.files.forEach(file => { - const reader = new FileReader(); - reader.readAsArrayBuffer(file); - reader.onload = async (event) => { - const name = file.name; - if (typeof name === 'string' && event.target?.result instanceof ArrayBuffer) { - if (target.getChild(name)) { - const { confirmed } = await this.dialogService.confirm(getFileOverwriteConfirm(name)); - if (!confirmed) { - return; - } + const items = (originalEvent.dataTransfer as unknown as IWebkitDataTransfer).items; + + // Somehow the items thing is being modified at random, maybe as a security + // measure since this is a DND operation. As such, we copy the items into + // an array we own as early as possible before using it. + const entries: IWebkitDataTransferItemEntry[] = []; + for (const item of items) { + entries.push(item.webkitGetAsEntry()); + } + + const results: { isFile: boolean, resource: URI }[] = []; + const cts = new CancellationTokenSource(); + const operation: IUploadOperation = { filesTotal: entries.length, filesUploaded: 0, startTime: Date.now(), bytesUploaded: 0 }; + + // Start upload and report progress globally + const uploadPromise = this.progressService.withProgress({ + location: ProgressLocation.Window, + delay: 800, + cancellable: true, + title: localize('uploadingFiles', "Uploading") + }, async progress => { + for (let entry of entries) { + + // Confirm overwrite as needed + if (target && entry.name && target.getChild(entry.name)) { + const { confirmed } = await this.dialogService.confirm(getFileOverwriteConfirm(entry.name)); + if (!confirmed) { + continue; } - const resource = joinPath(target.resource, name); - await this.fileService.writeFile(resource, VSBuffer.wrap(new Uint8Array(event.target.result))); - if (data.files.length === 1) { - await this.editorService.openEditor({ resource, options: { pinned: true } }); + await this.workingCopyFileService.delete([joinPath(target.resource, entry.name)], { recursive: true }); + } + + // Upload entry + const result = await this.doUploadWebFileEntry(entry, target.resource, target, progress, operation, cts.token); + if (result) { + results.push(result); + } + } + }, () => cts.dispose(true)); + + // Also indicate progress in the files view + this.progressService.withProgress({ location: VIEW_ID, delay: 800 }, () => uploadPromise); + + // Wait until upload is done + await uploadPromise; + + // Open uploaded file in editor only if we upload just one + if (!cts.token.isCancellationRequested && results.length === 1 && results[0].isFile) { + await this.editorService.openEditor({ resource: results[0].resource, options: { pinned: true } }); + } + } + + private async doUploadWebFileEntry(entry: IWebkitDataTransferItemEntry, parentResource: URI, target: ExplorerItem | undefined, progress: IProgress, operation: IUploadOperation, token: CancellationToken): Promise<{ isFile: boolean, resource: URI } | undefined> { + if (token.isCancellationRequested || !entry.name || (!entry.isFile && !entry.isDirectory)) { + return undefined; + } + + // Report progress + let fileBytesUploaded = 0; + const reportProgress = (fileSize: number, bytesUploaded: number): void => { + fileBytesUploaded += bytesUploaded; + operation.bytesUploaded += bytesUploaded; + + const bytesUploadedPerSecond = operation.bytesUploaded / ((Date.now() - operation.startTime) / 1000); + + let message: string; + if (operation.filesTotal === 1 && entry.name) { + message = entry.name; + } else { + message = localize('uploadProgress', "{0} of {1} files ({2}/s)", operation.filesUploaded, operation.filesTotal, BinarySize.formatSize(bytesUploadedPerSecond)); + } + + if (fileSize > BinarySize.MB) { + message = localize('uploadProgressDetail', "{0} ({1} of {2}, {3}/s)", message, BinarySize.formatSize(fileBytesUploaded), BinarySize.formatSize(fileSize), BinarySize.formatSize(bytesUploadedPerSecond)); + } + + progress.report({ message }); + }; + operation.filesUploaded++; + reportProgress(0, 0); + + // Handle file upload + const resource = joinPath(parentResource, entry.name); + if (entry.isFile) { + const file = await new Promise((resolve, reject) => entry.file(resolve, reject)); + + if (token.isCancellationRequested) { + return undefined; + } + + // Chrome/Edge/Firefox support stream method + if (typeof file.stream === 'function') { + await this.doUploadWebFileEntryBuffered(resource, file, reportProgress, token); + } + + // Fallback to unbuffered upload for other browsers + else { + await this.doUploadWebFileEntryUnbuffered(resource, file, reportProgress); + } + + return { isFile: true, resource }; + } + + // Handle folder upload + else { + + // Create target folder + await this.fileService.createFolder(resource); + + if (token.isCancellationRequested) { + return undefined; + } + + // Recursive upload files in this directory + const dirReader = entry.createReader(); + const childEntries: IWebkitDataTransferItemEntry[] = []; + let done = false; + do { + const childEntriesChunk = await new Promise((resolve, reject) => dirReader.readEntries(resolve, reject)); + if (childEntriesChunk.length > 0) { + childEntries.push(...childEntriesChunk); + } else { + done = true; // an empty array is a signal that all entries have been read + } + } while (!done); + + // Update operation total based on new counts + operation.filesTotal += childEntries.length; + + // Upload all entries as files to target + const folderTarget = target && target.getChild(entry.name) || undefined; + for (let childEntry of childEntries) { + await this.doUploadWebFileEntry(childEntry, resource, folderTarget, progress, operation, token); + } + + return { isFile: false, resource }; + } + } + + private async doUploadWebFileEntryBuffered(resource: URI, file: File, progressReporter: (fileSize: number, bytesUploaded: number) => void, token: CancellationToken): Promise { + const writeableStream = newWriteableBufferStream({ + // Set a highWaterMark to prevent the stream + // for file upload to produce large buffers + // in-memory + highWaterMark: 10 + }); + const writeFilePromise = this.fileService.writeFile(resource, writeableStream); + + // Read the file in chunks using File.stream() web APIs + try { + const reader: ReadableStreamDefaultReader = file.stream().getReader(); + + let res = await reader.read(); + while (!res.done) { + if (token.isCancellationRequested) { + return undefined; + } + + // Write buffer into stream but make sure to wait + // in case the highWaterMark is reached + const buffer = VSBuffer.wrap(res.value); + await writeableStream.write(buffer); + + if (token.isCancellationRequested) { + return undefined; + } + + // Report progress + progressReporter(file.size, buffer.byteLength); + + res = await reader.read(); + } + writeableStream.end(res.value instanceof Uint8Array ? VSBuffer.wrap(res.value) : undefined); + } catch (error) { + writeableStream.end(error); + } + + if (token.isCancellationRequested) { + return undefined; + } + + // Wait for file being written to target + await writeFilePromise; + } + + private doUploadWebFileEntryUnbuffered(resource: URI, file: File, progressReporter: (fileSize: number, bytesUploaded: number) => void): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = async event => { + try { + if (event.target?.result instanceof ArrayBuffer) { + const buffer = VSBuffer.wrap(new Uint8Array(event.target.result)); + await this.fileService.writeFile(resource, buffer); + + // Report progress + progressReporter(file.size, buffer.byteLength); + } else { + throw new Error('Could not read from dropped file.'); } + + resolve(); + } catch (error) { + reject(error); } }; + + // Start reading the file to trigger `onload` + reader.readAsArrayBuffer(file); }); } private async handleExternalDrop(data: DesktopDragAndDropData, target: ExplorerItem, originalEvent: DragEvent): Promise { - const droppedResources = extractResources(originalEvent, true); + // Check for dropped external files to be folders + const droppedResources = extractResources(originalEvent, true); const result = await this.fileService.resolveAll(droppedResources.map(droppedResource => ({ resource: droppedResource.resource }))); // Pass focus to window @@ -973,7 +1217,6 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // Handle folders by adding to workspace if we are in workspace context const folders = result.filter(r => r.success && r.stat && r.stat.isDirectory).map(result => ({ uri: result.stat!.resource })); if (folders.length > 0) { - const buttons = [ folders.length > 1 ? localize('copyFolders', "&&Copy Folders") : localize('copyFolder', "&&Copy Folder"), localize('cancel', "Cancel") @@ -1033,7 +1276,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { const sourceFile = resource; const targetFile = joinPath(target.resource, basename(sourceFile)); - const stat = await this.workingCopyFileService.copy(sourceFile, targetFile, true); + const stat = (await this.workingCopyFileService.copy([{ source: sourceFile, target: targetFile }], { overwrite: true }))[0]; // if we only add one file, just open it directly if (resources.length === 1 && !stat.isDirectory) { this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); @@ -1079,8 +1322,14 @@ export class FileDragAndDrop implements ITreeDragAndDrop { } } - const rootDropPromise = this.doHandleRootDrop(items.filter(s => s.isRoot), target); - await Promise.all(items.filter(s => !s.isRoot).map(source => this.doHandleExplorerDrop(source, target, isCopy)).concat(rootDropPromise)); + await this.doHandleRootDrop(items.filter(s => s.isRoot), target); + + const sources = items.filter(s => !s.isRoot); + if (isCopy) { + await this.doHandleExplorerDropOnCopy(sources, target); + } else { + return this.doHandleExplorerDropOnMove(sources, target); + } } private doHandleRootDrop(roots: ExplorerItem[], target: ExplorerItem): Promise { @@ -1116,36 +1365,40 @@ export class FileDragAndDrop implements ITreeDragAndDrop { return this.workspaceEditingService.updateFolders(0, workspaceCreationData.length, workspaceCreationData); } - private async doHandleExplorerDrop(source: ExplorerItem, target: ExplorerItem, isCopy: boolean): Promise { - // Reuse duplicate action if user copies - if (isCopy) { - const incrementalNaming = this.configurationService.getValue().explorer.incrementalNaming; - const stat = await this.workingCopyFileService.copy(source.resource, findValidPasteFileTarget(this.explorerService, target, { resource: source.resource, isDirectory: source.isDirectory, allowOverwrite: false }, incrementalNaming)); - if (!stat.isDirectory) { - await this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); - } + private async doHandleExplorerDropOnCopy(sources: ExplorerItem[], target: ExplorerItem): Promise { + // Reuse duplicate action when user copies + const incrementalNaming = this.configurationService.getValue().explorer.incrementalNaming; + const sourceTargetPairs = sources.map(({ resource, isDirectory }) => ({ source: resource, target: findValidPasteFileTarget(this.explorerService, target, { resource, isDirectory, allowOverwrite: false }, incrementalNaming) })); + const stats = await this.workingCopyFileService.copy(sourceTargetPairs); + const editors = stats.filter(stat => !stat.isDirectory).map(({ resource }) => ({ resource, options: { pinned: true } })); - return; - } + await this.editorService.openEditors(editors); + } - // Otherwise move - const targetResource = joinPath(target.resource, source.name); - if (source.isReadonly) { - // Do not allow moving readonly items - return Promise.resolve(); - } + private async doHandleExplorerDropOnMove(sources: ExplorerItem[], target: ExplorerItem): Promise { + + // Do not allow moving readonly items + const sourceTargetPairs = sources.filter(source => !source.isReadonly).map(source => ({ source: source.resource, target: joinPath(target.resource, source.name) })); try { - await this.workingCopyFileService.move(source.resource, targetResource); + await this.workingCopyFileService.move(sourceTargetPairs); } catch (error) { // Conflict if ((error).fileOperationResult === FileOperationResult.FILE_MOVE_CONFLICT) { - const confirm = getFileOverwriteConfirm(source.name); + + const overwrites: URI[] = []; + for (const { target } of sourceTargetPairs) { + if (await this.fileService.exists(target)) { + overwrites.push(target); + } + } + + const confirm = getMultipleFilesOverwriteConfirm(overwrites); // Move with overwrite if the user confirms const { confirmed } = await this.dialogService.confirm(confirm); if (confirmed) { try { - await this.workingCopyFileService.move(source.resource, targetResource, true /* overwrite */); + await this.workingCopyFileService.move(sourceTargetPairs, { overwrite: true }); } catch (error) { this.notificationService.error(error); } diff --git a/src/vs/workbench/contrib/files/browser/views/media/openeditors.css b/src/vs/workbench/contrib/files/browser/views/media/openeditors.css index 1f5fd5b019e..ee672533ae4 100644 --- a/src/vs/workbench/contrib/files/browser/views/media/openeditors.css +++ b/src/vs/workbench/contrib/files/browser/views/media/openeditors.css @@ -81,7 +81,7 @@ overflow: hidden; } -.hc-black .monaco-workbench .open-editors .open-editor, -.hc-black .monaco-workbench .open-editors .editor-group { +.monaco-workbench.hc-black .open-editors .open-editor, +.monaco-workbench.hc-black .open-editors .editor-group { line-height: 20px; } diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index dfdbc4bac2c..843ea8cd16f 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -22,7 +22,7 @@ import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/c import { attachStylerCallback } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { badgeBackground, badgeForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; -import { WorkbenchList } from 'vs/platform/list/browser/listService'; +import { WorkbenchList, ListResourceNavigator } from 'vs/platform/list/browser/listService'; import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction } from 'vs/base/browser/ui/list/list'; import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -269,29 +269,21 @@ export class OpenEditorsView extends ViewPane { e.element.group.closeEditor(e.element.editor, { preserveFocus: true }); } })); - this._register(this.list.onDidOpen(e => { - const browserEvent = e.browserEvent; - - let openToSide = false; - let isSingleClick = false; - let isDoubleClick = false; - let isMiddleClick = false; - if (browserEvent instanceof MouseEvent) { - isSingleClick = browserEvent.detail === 1; - isDoubleClick = browserEvent.detail === 2; - isMiddleClick = browserEvent.button === 1; - openToSide = this.list.useAltAsMultipleSelectionModifier ? (browserEvent.ctrlKey || browserEvent.metaKey) : browserEvent.altKey; + const resourceNavigator = this._register(new ListResourceNavigator(this.list, { configurationService: this.configurationService })); + this._register(resourceNavigator.onDidOpen(e => { + if (typeof e.element !== 'number') { + return; } - const focused = this.list.getFocusedElements(); - const element = focused.length ? focused[0] : undefined; + const element = this.list.element(e.element); + if (element instanceof OpenEditor) { - if (isMiddleClick) { - return; // already handled above: closes the editor + if (e.browserEvent instanceof MouseEvent && e.browserEvent.button === 1) { + return; // middle click already handled above: closes the editor } - this.openEditor(element, { preserveFocus: isSingleClick, pinned: isDoubleClick, sideBySide: openToSide }); - } else if (element) { + this.openEditor(element, { preserveFocus: e.editorOptions.preserveFocus, pinned: e.editorOptions.pinned, sideBySide: e.sideBySide }); + } else { this.editorGroupService.activateGroup(element); } })); @@ -303,6 +295,11 @@ export class OpenEditorsView extends ViewPane { this.listRefreshScheduler.schedule(0); } })); + + const containerModel = this.viewDescriptorService.getViewContainerModel(this.viewDescriptorService.getViewContainerByViewId(this.id)!)!; + this._register(containerModel.onDidChangeAllViewDescriptors(() => { + this.updateSize(); + })); } getActions(): IAction[] { @@ -362,7 +359,7 @@ export class OpenEditorsView extends ViewPane { return -1; } - private openEditor(element: OpenEditor, options: { preserveFocus: boolean; pinned: boolean; sideBySide: boolean; }): void { + private openEditor(element: OpenEditor, options: { preserveFocus?: boolean; pinned?: boolean; sideBySide?: boolean; }): void { if (element) { this.telemetryService.publicLog2('workbenchActionExecuted', { id: 'workbench.files.openFile', from: 'openEditors' }); @@ -446,6 +443,11 @@ export class OpenEditorsView extends ViewPane { } private getMaxExpandedBodySize(): number { + const containerModel = this.viewDescriptorService.getViewContainerModel(this.viewDescriptorService.getViewContainerByViewId(this.id)!)!; + if (containerModel.visibleViewDescriptors.length <= 1) { + return Number.POSITIVE_INFINITY; + } + return this.elementCount * OpenEditorsDelegate.ITEM_HEIGHT; } @@ -676,13 +678,13 @@ class OpenEditorsDragAndDrop implements IListDragAndDrop { + elementsData.forEach((oe: OpenEditor, offset) => { oe.group.moveEditor(oe.editor, group, { index: index + offset, preserveFocus: true }); }); this.editorGroupService.activateGroup(group); diff --git a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts index e4ae372e9d9..6d104cc918a 100644 --- a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts @@ -5,7 +5,8 @@ import { localize } from 'vs/nls'; import { URI } from 'vs/base/common/uri'; -import { EncodingMode, IFileEditorInput, Verbosity, TextResourceEditorInput, GroupIdentifier, IMoveResult, isTextEditorPane } from 'vs/workbench/common/editor'; +import { EncodingMode, IFileEditorInput, Verbosity, GroupIdentifier, IMoveResult, isTextEditorPane } from 'vs/workbench/common/editor'; +import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; import { ITextFileService, TextFileEditorModelState, TextFileLoadReason, TextFileOperationError, TextFileOperationResult, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; @@ -17,7 +18,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { isEqual } from 'vs/base/common/resources'; +import { extUri } from 'vs/base/common/resources'; import { Event } from 'vs/base/common/event'; import { IEditorViewState } from 'vs/editor/common/editorCommon'; @@ -30,7 +31,7 @@ const enum ForceOpenAs { /** * A file editor input is the input type for the file editor of file system resources. */ -export class FileEditorInput extends TextResourceEditorInput implements IFileEditorInput { +export class FileEditorInput extends AbstractTextResourceEditorInput implements IFileEditorInput { private preferredEncoding: string | undefined; private preferredMode: string | undefined; @@ -40,10 +41,11 @@ export class FileEditorInput extends TextResourceEditorInput implements IFileEdi private model: ITextFileEditorModel | undefined = undefined; private cachedTextFileModelReference: IReference | undefined = undefined; - private modelListeners: DisposableStore = this._register(new DisposableStore()); + private readonly modelListeners: DisposableStore = this._register(new DisposableStore()); constructor( resource: URI, + preferredResource: URI | undefined, preferredEncoding: string | undefined, preferredMode: string | undefined, @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -55,7 +57,7 @@ export class FileEditorInput extends TextResourceEditorInput implements IFileEdi @IEditorService editorService: IEditorService, @IEditorGroupsService editorGroupService: IEditorGroupsService ) { - super(resource, editorService, editorGroupService, textFileService, labelService, fileService, filesConfigurationService); + super(resource, preferredResource, editorService, editorGroupService, textFileService, labelService, fileService, filesConfigurationService); this.model = this.textFileService.files.get(resource); @@ -84,7 +86,7 @@ export class FileEditorInput extends TextResourceEditorInput implements IFileEdi // Once the text file model is created, we keep it inside // the input to be able to implement some methods properly - if (isEqual(model.resource, this.resource)) { + if (extUri.isEqual(model.resource, this.resource)) { this.model = model; this.registerModelListeners(model); @@ -112,6 +114,37 @@ export class FileEditorInput extends TextResourceEditorInput implements IFileEdi })); } + getTypeId(): string { + return FILE_EDITOR_INPUT_ID; + } + + getName(): string { + return this.decorateLabel(super.getName()); + } + + getTitle(verbosity: Verbosity): string { + return this.decorateLabel(super.getTitle(verbosity)); + } + + private decorateLabel(label: string): string { + const orphaned = this.model?.hasState(TextFileEditorModelState.ORPHAN); + const readonly = this.isReadonly(); + + if (orphaned && readonly) { + return localize('orphanedReadonlyFile', "{0} (deleted, read-only)", label); + } + + if (orphaned) { + return localize('orphanedFile', "{0} (deleted)", label); + } + + if (readonly) { + return localize('readonlyFile', "{0} (read-only)", label); + } + + return label; + } + getEncoding(): string | undefined { if (this.model) { return this.model.getEncoding(); @@ -162,37 +195,6 @@ export class FileEditorInput extends TextResourceEditorInput implements IFileEdi this.forceOpenAs = ForceOpenAs.Binary; } - getTypeId(): string { - return FILE_EDITOR_INPUT_ID; - } - - getName(): string { - return this.decorateLabel(super.getName()); - } - - getTitle(verbosity: Verbosity): string { - return this.decorateLabel(super.getTitle(verbosity)); - } - - private decorateLabel(label: string): string { - const orphaned = this.model?.hasState(TextFileEditorModelState.ORPHAN); - const readonly = this.isReadonly(); - - if (orphaned && readonly) { - return localize('orphanedReadonlyFile', "{0} (deleted, read-only)", label); - } - - if (orphaned) { - return localize('orphanedFile', "{0} (deleted)", label); - } - - if (readonly) { - return localize('readonlyFile', "{0} (read-only)", label); - } - - return label; - } - isDirty(): boolean { return !!(this.model?.isDirty()); } @@ -234,9 +236,10 @@ export class FileEditorInput extends TextResourceEditorInput implements IFileEdi } private async doResolveAsText(): Promise { - - // Resolve as text try { + + // Resolve resource via text file service and only allow + // to open binary files if we are instructed so await this.textFileService.files.resolve(this.resource, { mode: this.preferredMode, encoding: this.preferredEncoding, @@ -253,7 +256,16 @@ export class FileEditorInput extends TextResourceEditorInput implements IFileEdi this.cachedTextFileModelReference = await this.textModelResolverService.createModelReference(this.resource) as IReference; } - return this.cachedTextFileModelReference.object; + const model = this.cachedTextFileModelReference.object; + + // It is possible that this input was disposed before the model + // finished resolving. As such, we need to make sure to dispose + // the model reference to not leak it. + if (this.isDisposed()) { + this.disposeModelReference(); + } + + return model; } catch (error) { // In case of an error that indicates that the file is binary or too large, just return with the binary editor model @@ -277,7 +289,7 @@ export class FileEditorInput extends TextResourceEditorInput implements IFileEdi return !!this.model; } - move(group: GroupIdentifier, target: URI): IMoveResult { + rename(group: GroupIdentifier, target: URI): IMoveResult { return { editor: { resource: target, @@ -291,7 +303,7 @@ export class FileEditorInput extends TextResourceEditorInput implements IFileEdi private getViewStateFor(group: GroupIdentifier): IEditorViewState | undefined { for (const editorPane of this.editorService.visibleEditorPanes) { - if (editorPane.group.id === group && isEqual(editorPane.input.resource, this.resource)) { + if (editorPane.group.id === group && extUri.isEqual(editorPane.input.resource, this.resource)) { if (isTextEditorPane(editorPane)) { return editorPane.getViewState(); } @@ -302,12 +314,12 @@ export class FileEditorInput extends TextResourceEditorInput implements IFileEdi } matches(otherInput: unknown): boolean { - if (super.matches(otherInput) === true) { + if (otherInput === this) { return true; } - if (otherInput) { - return otherInput instanceof FileEditorInput && otherInput.resource.toString() === this.resource.toString(); + if (otherInput instanceof FileEditorInput) { + return extUri.isEqual(otherInput.resource, this.resource); } return false; @@ -319,9 +331,13 @@ export class FileEditorInput extends TextResourceEditorInput implements IFileEdi this.model = undefined; // Model reference - dispose(this.cachedTextFileModelReference); - this.cachedTextFileModelReference = undefined; + this.disposeModelReference(); super.dispose(); } + + private disposeModelReference(): void { + dispose(this.cachedTextFileModelReference); + this.cachedTextFileModelReference = undefined; + } } diff --git a/src/vs/workbench/contrib/files/common/explorerService.ts b/src/vs/workbench/contrib/files/common/explorerService.ts index 4552b78f62e..4ad1fc336d7 100644 --- a/src/vs/workbench/contrib/files/common/explorerService.ts +++ b/src/vs/workbench/contrib/files/common/explorerService.ts @@ -28,7 +28,7 @@ function getFileEventsExcludes(configurationService: IConfigurationService, root } export class ExplorerService implements IExplorerService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private static readonly EXPLORER_FILE_CHANGES_REACT_DELAY = 500; // delay in ms to react to file changes to give our internal events a chance to react first @@ -126,10 +126,10 @@ export class ExplorerService implements IExplorerService { await this.view.setEditable(stat, isEditing); } - setToCopy(items: ExplorerItem[], cut: boolean): void { + async setToCopy(items: ExplorerItem[], cut: boolean): Promise { const previouslyCutItems = this.cutItems; this.cutItems = cut ? items : undefined; - this.clipboardService.writeResources(items.map(s => s.resource)); + await this.clipboardService.writeResources(items.map(s => s.resource)); this.view?.itemsCopied(items, cut, previouslyCutItems); } @@ -150,10 +150,11 @@ export class ExplorerService implements IExplorerService { return !!this.editable && (this.editable.stat === stat || !stat); } - async select(resource: URI, reveal?: boolean): Promise { + async select(resource: URI, reveal?: boolean | string): Promise { if (!this.view) { return; } + const fileStat = this.findClosest(resource); if (fileStat) { await this.view.selectResource(fileStat.resource, reveal); @@ -197,7 +198,7 @@ export class ExplorerService implements IExplorerService { if (reveal && resource && autoReveal) { // We did a top level refresh, reveal the active file #67118 - this.select(resource, true); + this.select(resource, autoReveal); } } } @@ -361,7 +362,7 @@ export class ExplorerService implements IExplorerService { } private filterToViewRelevantEvents(e: FileChangesEvent): FileChangesEvent { - return new FileChangesEvent(e.changes.filter(change => { + return e.filter(change => { if (change.type === FileChangeType.UPDATED && this._sortOrder !== SortOrder.Modified) { return false; // we only are about updated if we sort by modified time } @@ -375,7 +376,7 @@ export class ExplorerService implements IExplorerService { } return true; - })); + }); } private async onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): Promise { diff --git a/src/vs/workbench/contrib/files/common/files.ts b/src/vs/workbench/contrib/files/common/files.ts index e16e50d265b..3fe4953a5c6 100644 --- a/src/vs/workbench/contrib/files/common/files.ts +++ b/src/vs/workbench/contrib/files/common/files.ts @@ -33,13 +33,8 @@ export const VIEWLET_ID = 'workbench.view.explorer'; */ export const VIEW_ID = 'workbench.explorer.fileView'; -/** - * Id of the default editor for open with. - */ -export const DEFAULT_EDITOR_ID = 'default'; - export interface IExplorerService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly roots: ExplorerItem[]; readonly sortOrder: SortOrder; @@ -51,14 +46,14 @@ export interface IExplorerService { isEditable(stat: ExplorerItem | undefined): boolean; findClosest(resource: URI): ExplorerItem | null; refresh(): Promise; - setToCopy(stats: ExplorerItem[], cut: boolean): void; + setToCopy(stats: ExplorerItem[], cut: boolean): Promise; isCut(stat: ExplorerItem): boolean; /** * Selects and reveal the file element provided by the given resource if its found in the explorer. * Will try to resolve the path in case the explorer is not yet expanded to the file yet. */ - select(resource: URI, reveal?: boolean): Promise; + select(resource: URI, reveal?: boolean | string): Promise; registerView(contextAndRefreshProvider: IExplorerView): void; } @@ -66,7 +61,7 @@ export interface IExplorerService { export interface IExplorerView { getContext(respectMultiSelection: boolean): ExplorerItem[]; refresh(recursive: boolean, item?: ExplorerItem): Promise; - selectResource(resource: URI | undefined, reveal?: boolean): Promise; + selectResource(resource: URI | undefined, reveal?: boolean | string): Promise; setTreeInput(): Promise; itemsCopied(tats: ExplorerItem[], cut: boolean, previousCut: ExplorerItem[] | undefined): void; setEditable(stat: ExplorerItem, isEditing: boolean): Promise; @@ -121,7 +116,7 @@ export interface IFilesConfiguration extends PlatformIFilesConfiguration, IWorkb openEditors: { visible: number; }; - autoReveal: boolean; + autoReveal: boolean | 'focusNoScroll'; enableDragAndDrop: boolean; confirmDelete: boolean; sortOrder: SortOrder; @@ -256,6 +251,6 @@ export class OpenEditor implements IEditorIdentifier { } getResource(): URI | undefined { - return toResource(this.editor, { supportSideBySide: SideBySideEditor.MASTER }); + return toResource(this.editor, { supportSideBySide: SideBySideEditor.PRIMARY }); } } diff --git a/src/vs/workbench/contrib/files/electron-browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/electron-sandbox/fileActions.contribution.ts similarity index 97% rename from src/vs/workbench/contrib/files/electron-browser/fileActions.contribution.ts rename to src/vs/workbench/contrib/files/electron-sandbox/fileActions.contribution.ts index 52b1cf0897f..a335946a0d5 100644 --- a/src/vs/workbench/contrib/files/electron-browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/electron-sandbox/fileActions.contribution.ts @@ -9,7 +9,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { isWindows, isMacintosh } from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; @@ -17,7 +17,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/files'; import { IListService } from 'vs/platform/list/browser/listService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { revealResourcesInOS } from 'vs/workbench/contrib/files/electron-browser/fileCommands'; +import { revealResourcesInOS } from 'vs/workbench/contrib/files/electron-sandbox/fileCommands'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { appendToCommandPalette, appendEditorTitleContextMenuItem } from 'vs/workbench/contrib/files/browser/fileActions.contribution'; diff --git a/src/vs/workbench/contrib/files/electron-browser/fileCommands.ts b/src/vs/workbench/contrib/files/electron-sandbox/fileCommands.ts similarity index 94% rename from src/vs/workbench/contrib/files/electron-browser/fileCommands.ts rename to src/vs/workbench/contrib/files/electron-sandbox/fileCommands.ts index 274ee632713..e941d1ece27 100644 --- a/src/vs/workbench/contrib/files/electron-browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/electron-sandbox/fileCommands.ts @@ -9,7 +9,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { sequence } from 'vs/base/common/async'; import { Schemas } from 'vs/base/common/network'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; // Commands diff --git a/src/vs/workbench/contrib/files/electron-browser/files.contribution.ts b/src/vs/workbench/contrib/files/electron-sandbox/files.contribution.ts similarity index 76% rename from src/vs/workbench/contrib/files/electron-browser/files.contribution.ts rename to src/vs/workbench/contrib/files/electron-sandbox/files.contribution.ts index 737eeea2d5c..3f6524d0f64 100644 --- a/src/vs/workbench/contrib/files/electron-browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/electron-sandbox/files.contribution.ts @@ -3,17 +3,13 @@ * 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 nls from 'vs/nls'; -import { join } from 'vs/base/common/path'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorInput } from 'vs/workbench/common/editor'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; -import { NativeTextFileEditor } from 'vs/workbench/contrib/files/electron-browser/textFileEditor'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { NativeTextFileEditor } from 'vs/workbench/contrib/files/electron-sandbox/textFileEditor'; // Register file editor Registry.as(EditorExtensions.Editors).registerEditor( @@ -26,8 +22,3 @@ Registry.as(EditorExtensions.Editors).registerEditor( new SyncDescriptor(FileEditorInput) ] ); - -// Register mkdtemp command -CommandsRegistry.registerCommand('mkdtemp', function () { - return fs.promises.mkdtemp(join(os.tmpdir(), 'vscodetmp-')); -}); diff --git a/src/vs/workbench/contrib/files/electron-browser/textFileEditor.ts b/src/vs/workbench/contrib/files/electron-sandbox/textFileEditor.ts similarity index 89% rename from src/vs/workbench/contrib/files/electron-browser/textFileEditor.ts rename to src/vs/workbench/contrib/files/electron-sandbox/textFileEditor.ts index 4ea06a47056..258de1884e8 100644 --- a/src/vs/workbench/contrib/files/electron-browser/textFileEditor.ts +++ b/src/vs/workbench/contrib/files/electron-sandbox/textFileEditor.ts @@ -7,8 +7,7 @@ import * as nls from 'vs/nls'; import { TextFileEditor } from 'vs/workbench/contrib/files/browser/editors/textFileEditor'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { EditorOptions } from 'vs/workbench/common/editor'; -import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; -import { MIN_MAX_MEMORY_SIZE_MB, FALLBACK_MAX_MEMORY_SIZE_MB } from 'vs/platform/files/node/files'; +import { FileOperationError, FileOperationResult, IFileService, MIN_MAX_MEMORY_SIZE_MB, FALLBACK_MAX_MEMORY_SIZE_MB } from 'vs/platform/files/common/files'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; import { Action } from 'vs/base/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -23,8 +22,8 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { IExplorerService } from 'vs/workbench/contrib/files/common/files'; -import { IElectronService } from 'vs/platform/electron/node/electron'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; /** * An implementation of editor for file system resources. @@ -46,9 +45,9 @@ export class NativeTextFileEditor extends TextFileEditor { @IElectronService private readonly electronService: IElectronService, @IPreferencesService private readonly preferencesService: IPreferencesService, @IExplorerService explorerService: IExplorerService, - @IConfigurationService configurationService: IConfigurationService + @IUriIdentityService uriIdentityService: IUriIdentityService ) { - super(telemetryService, fileService, viewletService, instantiationService, contextService, storageService, textResourceConfigurationService, editorService, themeService, editorGroupService, textFileService, explorerService, configurationService); + super(telemetryService, fileService, viewletService, instantiationService, contextService, storageService, textResourceConfigurationService, editorService, themeService, editorGroupService, textFileService, explorerService, uriIdentityService); } protected handleSetInputError(error: Error, input: FileEditorInput, options: EditorOptions | undefined): void { diff --git a/src/vs/workbench/contrib/files/test/browser/explorerView.test.ts b/src/vs/workbench/contrib/files/test/browser/explorerView.test.ts index e8904d74e27..ae9dc54c9dc 100644 --- a/src/vs/workbench/contrib/files/test/browser/explorerView.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/explorerView.test.ts @@ -87,7 +87,7 @@ suite('Files - ExplorerView', () => { container: label, onDidRender: emitter.event } - }); + }, 1, false); assert.equal(navigationController.count, 3); assert.equal(navigationController.index, 2); diff --git a/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts b/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts index 5b78f85f045..8e0e190bf45 100644 --- a/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts @@ -7,9 +7,9 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { toResource } from 'vs/base/test/common/utils'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; -import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestServiceAccessor, TestEditorService } from 'vs/workbench/test/browser/workbenchTestServices'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { EncodingMode, Verbosity } from 'vs/workbench/common/editor'; +import { EncodingMode, IEditorInputFactoryRegistry, Verbosity, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; import { TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; import { FileOperationResult, FileOperationError } from 'vs/platform/files/common/files'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; @@ -17,20 +17,31 @@ import { timeout } from 'vs/base/common/async'; import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; +import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; +import { Registry } from 'vs/platform/registry/common/platform'; suite('Files - FileEditorInput', () => { let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; setup(() => { - instantiationService = workbenchInstantiationService(); + instantiationService = workbenchInstantiationService({ + editorService: () => { + return new class extends TestEditorService { + createEditorInput(input: IResourceEditorInput) { + return instantiationService.createInstance(FileEditorInput, input.resource, undefined, undefined, undefined); + } + }; + } + }); + accessor = instantiationService.createInstance(TestServiceAccessor); }); test('Basics', async function () { - let input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined); - const otherInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, 'foo/bar/otherfile.js'), undefined, undefined); - const otherInputSame = instantiationService.createInstance(FileEditorInput, toResource.call(this, 'foo/bar/file.js'), undefined, undefined); + let input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined, undefined); + const otherInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, 'foo/bar/otherfile.js'), undefined, undefined, undefined); + const otherInputSame = instantiationService.createInstance(FileEditorInput, toResource.call(this, 'foo/bar/file.js'), undefined, undefined, undefined); assert(input.matches(input)); assert(input.matches(otherInputSame)); @@ -45,10 +56,10 @@ suite('Files - FileEditorInput', () => { assert.strictEqual(toResource.call(this, '/foo/bar/file.js').fsPath, input.resource.fsPath); assert(input.resource instanceof URI); - input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar.html'), undefined, undefined); + input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar.html'), undefined, undefined, undefined); - const inputToResolve: FileEditorInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined); - const sameOtherInput: FileEditorInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined); + const inputToResolve: FileEditorInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined, undefined); + const sameOtherInput: FileEditorInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined, undefined); let resolved = await inputToResolve.resolve(); assert.ok(inputToResolve.isResolved()); @@ -82,13 +93,44 @@ suite('Files - FileEditorInput', () => { } }); + test('preferred resource', function () { + const resource = toResource.call(this, '/foo/bar/updatefile.js'); + const preferredResource = toResource.call(this, '/foo/bar/UPDATEFILE.js'); + + const inputWithoutPreferredResource = instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined); + assert.equal(inputWithoutPreferredResource.resource.toString(), resource.toString()); + assert.equal(inputWithoutPreferredResource.preferredResource.toString(), resource.toString()); + + const inputWithPreferredResource = instantiationService.createInstance(FileEditorInput, resource, preferredResource, undefined, undefined); + + assert.equal(inputWithPreferredResource.resource.toString(), resource.toString()); + assert.equal(inputWithPreferredResource.preferredResource.toString(), preferredResource.toString()); + + let didChangeLabel = false; + const listener = inputWithPreferredResource.onDidChangeLabel(e => { + didChangeLabel = true; + }); + + assert.equal(inputWithPreferredResource.getName(), 'UPDATEFILE.js'); + + const otherPreferredResource = toResource.call(this, '/FOO/BAR/updateFILE.js'); + inputWithPreferredResource.setPreferredResource(otherPreferredResource); + + assert.equal(inputWithPreferredResource.resource.toString(), resource.toString()); + assert.equal(inputWithPreferredResource.preferredResource.toString(), otherPreferredResource.toString()); + assert.equal(inputWithPreferredResource.getName(), 'updateFILE.js'); + assert.equal(didChangeLabel, true); + + listener.dispose(); + }); + test('preferred mode', async function () { const mode = 'file-input-test'; ModesRegistry.registerLanguage({ id: mode, }); - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, mode); + const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined, mode); assert.equal(input.getPreferredMode(), mode); const model = await input.resolve() as TextFileEditorModel; @@ -98,7 +140,7 @@ suite('Files - FileEditorInput', () => { assert.equal(input.getPreferredMode(), 'text'); assert.equal(model.textEditorModel!.getModeId(), PLAINTEXT_MODE_ID); - const input2 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined); + const input2 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined, undefined); input2.setPreferredMode(mode); const model2 = await input2.resolve() as TextFileEditorModel; @@ -106,10 +148,10 @@ suite('Files - FileEditorInput', () => { }); test('matches', function () { - const input1 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined); - const input2 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined); - const input3 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/other.js'), undefined, undefined); - const input2Upper = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/UPDATEFILE.js'), undefined, undefined); + const input1 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); + const input2 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); + const input3 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/other.js'), undefined, undefined, undefined); + const input2Upper = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/UPDATEFILE.js'), undefined, undefined, undefined); assert.strictEqual(input1.matches(null), false); assert.strictEqual(input1.matches(input1), true); @@ -120,7 +162,7 @@ suite('Files - FileEditorInput', () => { }); test('getEncoding/setEncoding', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined); + const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); input.setEncoding('utf16', EncodingMode.Encode); assert.equal(input.getEncoding(), 'utf16'); @@ -131,7 +173,7 @@ suite('Files - FileEditorInput', () => { }); test('save', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined); + const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); const resolved = await input.resolve() as TextFileEditorModel; resolved.textEditorModel!.setValue('changed'); @@ -143,7 +185,7 @@ suite('Files - FileEditorInput', () => { }); test('revert', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined); + const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); const resolved = await input.resolve() as TextFileEditorModel; resolved.textEditorModel!.setValue('changed'); @@ -159,7 +201,7 @@ suite('Files - FileEditorInput', () => { }); test('resolve handles binary files', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined); + const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); accessor.textFileService.setResolveTextContentErrorOnce(new TextFileOperationError('error', TextFileOperationResult.FILE_IS_BINARY)); @@ -169,7 +211,7 @@ suite('Files - FileEditorInput', () => { }); test('resolve handles too large files', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined); + const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); accessor.textFileService.setResolveTextContentErrorOnce(new FileOperationError('error', FileOperationResult.FILE_TOO_LARGE)); @@ -179,7 +221,7 @@ suite('Files - FileEditorInput', () => { }); test('attaches to model when created and reports dirty', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined); + const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); let listenerCount = 0; const listener = input.onDidChangeDirty(() => { @@ -199,7 +241,7 @@ suite('Files - FileEditorInput', () => { }); test('force open text/binary', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined); + const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); input.setForceOpenAsBinary(); let resolved = await input.resolve(); @@ -212,4 +254,37 @@ suite('Files - FileEditorInput', () => { resolved.dispose(); }); + + test('file editor input factory', async function () { + instantiationService.invokeFunction(accessor => Registry.as(EditorExtensions.EditorInputFactories).start(accessor)); + + const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); + + const factory = Registry.as(EditorExtensions.EditorInputFactories).getEditorInputFactory(input.getTypeId()); + if (!factory) { + assert.fail('File Editor Input Factory missing'); + } + + assert.equal(factory.canSerialize(input), true); + + const inputSerialized = factory.serialize(input); + if (!inputSerialized) { + assert.fail('Unexpected serialized file input'); + } + + const inputDeserialized = factory.deserialize(instantiationService, inputSerialized); + assert.equal(input.matches(inputDeserialized), true); + + const preferredResource = toResource.call(this, '/foo/bar/UPDATEfile.js'); + const inputWithPreferredResource = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), preferredResource, undefined, undefined); + + const inputWithPreferredResourceSerialized = factory.serialize(inputWithPreferredResource); + if (!inputWithPreferredResourceSerialized) { + assert.fail('Unexpected serialized file input'); + } + + const inputWithPreferredResourceDeserialized = factory.deserialize(instantiationService, inputWithPreferredResourceSerialized) as FileEditorInput; + assert.equal(inputWithPreferredResource.resource.toString(), inputWithPreferredResourceDeserialized.resource.toString()); + assert.equal(inputWithPreferredResource.preferredResource.toString(), inputWithPreferredResourceDeserialized.preferredResource.toString()); + }); }); diff --git a/src/vs/workbench/contrib/files/test/browser/textFileEditor.test.ts b/src/vs/workbench/contrib/files/test/browser/textFileEditor.test.ts index 9325e95d69b..9b346315b6c 100644 --- a/src/vs/workbench/contrib/files/test/browser/textFileEditor.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/textFileEditor.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { toResource } from 'vs/base/test/common/utils'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { workbenchInstantiationService, TestServiceAccessor, TestFilesConfigurationService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestServiceAccessor, TestFilesConfigurationService, TestTextResourceConfigurationService } from 'vs/workbench/test/browser/workbenchTestServices'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; @@ -26,6 +26,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; +import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; suite('Files - TextFileEditor', () => { @@ -54,6 +55,8 @@ suite('Files - TextFileEditor', () => { configurationService.setUserConfiguration('workbench', { editor: { restoreViewState } }); instantiationService.stub(IConfigurationService, configurationService); + instantiationService.stub(ITextResourceConfigurationService, new TestTextResourceConfigurationService(configurationService)); + instantiationService.stub(IFilesConfigurationService, new TestFilesConfigurationService( instantiationService.createInstance(MockContextKeyService), configurationService diff --git a/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts b/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts index 7f8da4afd56..d97c0447e45 100644 --- a/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts @@ -25,7 +25,7 @@ import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; import { EditorService } from 'vs/workbench/services/editor/browser/editorService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; -import { isEqual } from 'vs/base/common/resources'; +import { isEqual, extUri } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -99,7 +99,7 @@ suite('Files - TextFileEditorTracker', () => { await model.save(); // change event (watcher) - accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource, type: FileChangeType.UPDATED }])); + accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource, type: FileChangeType.UPDATED }], extUri)); await timeout(0); // due to event updating model async diff --git a/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts b/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts index f856f4e05aa..26599aef2be 100644 --- a/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts +++ b/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts @@ -64,7 +64,7 @@ class DefaultFormatter extends Disposable implements IWorkbenchContribution { DefaultFormatter.extensionDescriptions.push(nls.localize('nullFormatterDescription', "None")); for (const extension of extensions) { - if (extension.main) { + if (extension.main || extension.browser) { DefaultFormatter.extensionIds.push(extension.identifier.value); DefaultFormatter.extensionDescriptions.push(extension.description || ''); } diff --git a/src/vs/workbench/contrib/issue/browser/issue.contribution.ts b/src/vs/workbench/contrib/issue/browser/issue.web.contribution.ts similarity index 93% rename from src/vs/workbench/contrib/issue/browser/issue.contribution.ts rename to src/vs/workbench/contrib/issue/browser/issue.web.contribution.ts index 65f8ffb8245..9285c7067a4 100644 --- a/src/vs/workbench/contrib/issue/browser/issue.contribution.ts +++ b/src/vs/workbench/contrib/issue/browser/issue.web.contribution.ts @@ -47,4 +47,8 @@ class RegisterIssueContribution implements IWorkbenchContribution { Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(RegisterIssueContribution, LifecyclePhase.Starting); +CommandsRegistry.registerCommand('_issues.getSystemStatus', (accessor) => { + return nls.localize('statusUnsupported', "The --status argument is not yet supported in browsers."); +}); + registerSingleton(IWebIssueService, WebIssueService, true); diff --git a/src/vs/workbench/contrib/issue/browser/issueService.ts b/src/vs/workbench/contrib/issue/browser/issueService.ts index 5fcf51485aa..fea2b9e7ce8 100644 --- a/src/vs/workbench/contrib/issue/browser/issueService.ts +++ b/src/vs/workbench/contrib/issue/browser/issueService.ts @@ -18,12 +18,12 @@ export interface IIssueReporterOptions { } export interface IWebIssueService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; openReporter(options?: IIssueReporterOptions): Promise; } export class WebIssueService implements IWebIssueService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, diff --git a/src/vs/workbench/contrib/issue/electron-browser/issue.contribution.ts b/src/vs/workbench/contrib/issue/electron-browser/issue.contribution.ts index 542cfb5d5dc..8799eee4c2d 100644 --- a/src/vs/workbench/contrib/issue/electron-browser/issue.contribution.ts +++ b/src/vs/workbench/contrib/issue/electron-browser/issue.contribution.ts @@ -13,7 +13,8 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-browser/issue'; import { WorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-browser/issueService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { IIssueService, IssueReporterData } from 'vs/platform/issue/node/issue'; +import { IssueReporterData } from 'vs/platform/issue/common/issue'; +import { IIssueService } from 'vs/platform/issue/electron-sandbox/issue'; import { OpenIssueReporterArgs, OpenIssueReporterActionId } from 'vs/workbench/contrib/issue/common/commands'; const helpCategory = { value: nls.localize('help', "Help"), original: 'Help' }; @@ -22,7 +23,7 @@ const workbenchActionsRegistry = Registry.as(Extension if (!!product.reportIssueUrl) { workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ReportPerformanceIssueUsingReporterAction), 'Help: Report Performance Issue', helpCategory.value); - const OpenIssueReporterActionLabel = nls.localize({ key: 'reportIssueInEnglish', comment: ['Translate this to "Report Issue in English" in all languages please!'] }, "Report Issue"); + const OpenIssueReporterActionLabel = nls.localize({ key: 'reportIssueInEnglish', comment: ['Translate this to "Report Issue in English" in all languages please!'] }, "Report Issue..."); CommandsRegistry.registerCommand(OpenIssueReporterActionId, function (accessor, args?: [string] | OpenIssueReporterArgs) { const data: Partial = Array.isArray(args) @@ -41,7 +42,7 @@ if (!!product.reportIssueUrl) { MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command }); } -const developerCategory = nls.localize('developer', "Developer"); +const developerCategory = nls.localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer"); workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(OpenProcessExplorer), 'Developer: Open Process Explorer', developerCategory); registerSingleton(IWorkbenchIssueService, WorkbenchIssueService, true); diff --git a/src/vs/workbench/contrib/issue/electron-browser/issue.ts b/src/vs/workbench/contrib/issue/electron-browser/issue.ts index da0a15a4e64..f5cb676c561 100644 --- a/src/vs/workbench/contrib/issue/electron-browser/issue.ts +++ b/src/vs/workbench/contrib/issue/electron-browser/issue.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IssueReporterData } from 'vs/platform/issue/node/issue'; +import { IssueReporterData } from 'vs/platform/issue/common/issue'; export const IWorkbenchIssueService = createDecorator('workbenchIssueService'); export interface IWorkbenchIssueService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; openReporter(dataOverrides?: Partial): Promise; openProcessExplorer(): Promise; } diff --git a/src/vs/workbench/contrib/issue/electron-browser/issueActions.ts b/src/vs/workbench/contrib/issue/electron-browser/issueActions.ts index 4b6b25c31c0..4eaaf7c3029 100644 --- a/src/vs/workbench/contrib/issue/electron-browser/issueActions.ts +++ b/src/vs/workbench/contrib/issue/electron-browser/issueActions.ts @@ -5,7 +5,7 @@ import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; -import { IssueType } from 'vs/platform/issue/node/issue'; +import { IssueType } from 'vs/platform/issue/common/issue'; import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-browser/issue'; export class OpenProcessExplorer extends Action { diff --git a/src/vs/workbench/contrib/issue/electron-browser/issueService.ts b/src/vs/workbench/contrib/issue/electron-browser/issueService.ts index 3efe946f1ee..92dd4581ccc 100644 --- a/src/vs/workbench/contrib/issue/electron-browser/issueService.ts +++ b/src/vs/workbench/contrib/issue/electron-browser/issueService.ts @@ -3,28 +3,31 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IssueReporterStyles, IIssueService, IssueReporterData, ProcessExplorerData, IssueReporterExtensionData } from 'vs/platform/issue/node/issue'; +import { IssueReporterStyles, IssueReporterData, ProcessExplorerData, IssueReporterExtensionData } from 'vs/platform/issue/common/issue'; +import { IIssueService } from 'vs/platform/issue/electron-sandbox/issue'; import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService'; import { textLinkForeground, inputBackground, inputBorder, inputForeground, buttonBackground, buttonHoverBackground, buttonForeground, inputValidationErrorBorder, foreground, inputActiveOptionBorder, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, editorBackground, editorForeground, listHoverBackground, listHoverForeground, listHighlightForeground, textLinkActiveForeground, inputValidationErrorBackground, inputValidationErrorForeground } from 'vs/platform/theme/common/colorRegistry'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { webFrame } from 'electron'; -import { assign } from 'vs/base/common/objects'; +import { getZoomLevel } from 'vs/base/browser/browser'; import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-browser/issue'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { platform } from 'process'; +import { IProductService } from 'vs/platform/product/common/productService'; export class WorkbenchIssueService implements IWorkbenchIssueService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @IIssueService private readonly issueService: IIssueService, @IThemeService private readonly themeService: IThemeService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, - @IWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService + @IWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService, + @IProductService private readonly productService: IProductService ) { } async openReporter(dataOverrides: Partial = {}): Promise { @@ -48,9 +51,9 @@ export class WorkbenchIssueService implements IWorkbenchIssueService { }; }); const theme = this.themeService.getColorTheme(); - const issueReporterData: IssueReporterData = assign({ + const issueReporterData: IssueReporterData = Object.assign({ styles: getIssueReporterStyles(theme), - zoomLevel: webFrame.getZoomLevel(), + zoomLevel: getZoomLevel(), enabledExtensions: extensionData, }, dataOverrides); return this.issueService.openReporter(issueReporterData); @@ -60,14 +63,16 @@ export class WorkbenchIssueService implements IWorkbenchIssueService { const theme = this.themeService.getColorTheme(); const data: ProcessExplorerData = { pid: this.environmentService.configuration.mainPid, - zoomLevel: webFrame.getZoomLevel(), + zoomLevel: getZoomLevel(), styles: { backgroundColor: getColor(theme, editorBackground), color: getColor(theme, editorForeground), hoverBackground: getColor(theme, listHoverBackground), hoverForeground: getColor(theme, listHoverForeground), highlightForeground: getColor(theme, listHighlightForeground), - } + }, + platform, + applicationName: this.productService.applicationName }; return this.issueService.openProcessExplorer(data); } diff --git a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts index 1c1ea040c05..ca889b6ed58 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts +++ b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts @@ -67,7 +67,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo [{ label: updateAndRestart ? localize('yes', "Yes") : localize('restart now', "Restart Now"), run: () => { - const updatePromise = updateAndRestart ? this.jsonEditingService.write(this.environmentService.argvResource, [{ key: 'locale', value: locale }], true) : Promise.resolve(undefined); + const updatePromise = updateAndRestart ? this.jsonEditingService.write(this.environmentService.argvResource, [{ path: ['locale'], value: locale }], true) : Promise.resolve(undefined); updatePromise.then(() => this.hostService.restart(), e => this.notificationService.error(e)); } }], diff --git a/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts b/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts index ff17e5b4347..a935f264672 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts +++ b/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts @@ -6,7 +6,7 @@ import { localize } from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ILocalizationsService, LanguageType } from 'vs/platform/localizations/common/localizations'; +import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { IHostService } from 'vs/workbench/services/host/browser/host'; @@ -37,8 +37,7 @@ export class ConfigureLocaleAction extends Action { } private async getLanguageOptions(): Promise { - // Contributed languages are those installed via extension packs, so does not include English - const availableLanguages = ['en', ...await this.localizationService.getLanguageIds(LanguageType.Contributed)]; + const availableLanguages = await this.localizationService.getLanguageIds(); availableLanguages.sort(); return availableLanguages @@ -69,7 +68,7 @@ export class ConfigureLocaleAction extends Action { } if (selectedLanguage) { - await this.jsonEditingService.write(this.environmentService.argvResource, [{ key: 'locale', value: selectedLanguage.label }], true); + await this.jsonEditingService.write(this.environmentService.argvResource, [{ path: ['locale'], value: selectedLanguage.label }], true); const restart = await this.dialogService.confirm({ type: 'info', message: localize('relaunchDisplayLanguageMessage', "A restart is required for the change in display language to take effect."), diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts index 0075652ceaa..e737ad31f7f 100644 --- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts @@ -24,7 +24,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { LogsDataCleaner } from 'vs/workbench/contrib/logs/common/logsDataCleaner'; const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); -const devCategory = nls.localize('developer', "Developer"); +const devCategory = nls.localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer"); workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(SetLogLevelAction), 'Developer: Set Log Level...', devCategory); class LogOutputChannels extends Disposable implements IWorkbenchContribution { @@ -45,7 +45,7 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { } private registerCommonContributions(): void { - this.registerLogChannel(Constants.userDataSyncLogChannelId, nls.localize('userDataSyncLog', "Preferences Sync"), this.environmentService.userDataSyncLogResource); + this.registerLogChannel(Constants.userDataSyncLogChannelId, nls.localize('userDataSyncLog', "Settings Sync"), this.environmentService.userDataSyncLogResource); this.registerLogChannel(Constants.rendererLogChannelId, nls.localize('rendererLog', "Window"), this.environmentService.logFile); } @@ -53,7 +53,7 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { this.instantiationService.createInstance(LogsDataCleaner); const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); - const devCategory = nls.localize('developer', "Developer"); + const devCategory = nls.localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer"); workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(OpenWindowSessionLogFileAction), 'Developer: Open Window Log File (Session)...', devCategory); } @@ -73,13 +73,16 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { private async registerLogChannel(id: string, label: string, file: URI): Promise { await whenProviderRegistered(file, this.fileService); const outputChannelRegistry = Registry.as(OutputExt.OutputChannels); + + /* watch first and then check if file exists so that to avoid missing file creation event after watching #102117 */ + const watcher = this.fileService.watch(dirname(file)); const exists = await this.fileService.exists(file); if (exists) { + watcher.dispose(); outputChannelRegistry.registerChannel({ id, label, file, log: true }); return; } - const watcher = this.fileService.watch(dirname(file)); const disposable = this.fileService.onDidFilesChange(e => { if (e.contains(file, FileChangeType.ADDED) || e.contains(file, FileChangeType.UPDATED)) { watcher.dispose(); diff --git a/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts b/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts index 56e7710a32e..8196c1feabb 100644 --- a/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts @@ -10,6 +10,6 @@ import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { OpenLogsFolderAction, OpenExtensionLogsFolderAction } from 'vs/workbench/contrib/logs/electron-browser/logsActions'; const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); -const devCategory = nls.localize('developer', "Developer"); +const devCategory = nls.localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer"); workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(OpenLogsFolderAction), 'Developer: Open Logs Folder', devCategory); workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(OpenExtensionLogsFolderAction), 'Developer: Open Extension Logs Folder', devCategory); diff --git a/src/vs/workbench/contrib/logs/electron-browser/logsActions.ts b/src/vs/workbench/contrib/logs/electron-browser/logsActions.ts index 97c16d4d4af..3109475f950 100644 --- a/src/vs/workbench/contrib/logs/electron-browser/logsActions.ts +++ b/src/vs/workbench/contrib/logs/electron-browser/logsActions.ts @@ -7,7 +7,7 @@ import { Action } from 'vs/base/common/actions'; import { join } from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IFileService } from 'vs/platform/files/common/files'; diff --git a/src/vs/workbench/contrib/markers/browser/markers.ts b/src/vs/workbench/contrib/markers/browser/markers.ts index a6e8ee54060..383d52fcacf 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.ts @@ -5,9 +5,9 @@ import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { MarkersModel, compareMarkersByUri } from './markersModel'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, MutableDisposable, IDisposable } from 'vs/base/common/lifecycle'; import { IMarkerService, MarkerSeverity, IMarker } from 'vs/platform/markers/common/markers'; -import { NumberBadge, ViewContaierActivityByView } from 'vs/workbench/services/activity/common/activity'; +import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; import { localize } from 'vs/nls'; import Constants from './constants'; import { URI } from 'vs/base/common/uri'; @@ -19,12 +19,12 @@ import { ResourceMap } from 'vs/base/common/map'; export const IMarkersWorkbenchService = createDecorator('markersWorkbenchService'); export interface IMarkersWorkbenchService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly markersModel: MarkersModel; } export class MarkersWorkbenchService extends Disposable implements IMarkersWorkbenchService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; readonly markersModel: MarkersModel; @@ -55,22 +55,21 @@ export class MarkersWorkbenchService extends Disposable implements IMarkersWorkb export class ActivityUpdater extends Disposable implements IWorkbenchContribution { - private readonly activity: ViewContaierActivityByView; + private readonly activity = this._register(new MutableDisposable()); constructor( - @IInstantiationService instantiationService: IInstantiationService, + @IActivityService private readonly activityService: IActivityService, @IMarkerService private readonly markerService: IMarkerService ) { super(); - this.activity = this._register(instantiationService.createInstance(ViewContaierActivityByView, Constants.MARKERS_VIEW_ID)); - this._register(this.markerService.onMarkerChanged(() => this.updateActivity())); - this.updateActivity(); + this._register(this.markerService.onMarkerChanged(() => this.updateBadge())); + this.updateBadge(); } - private updateActivity(): void { + private updateBadge(): void { const { errors, warnings, infos } = this.markerService.getStatistics(); const total = errors + warnings + infos; const message = localize('totalProblems', 'Total {0} Problems', total); - this.activity.setActivity({ badge: new NumberBadge(total, () => message) }); + this.activity.value = this.activityService.showViewActivity(Constants.MARKERS_VIEW_ID, { badge: new NumberBadge(total, () => message) }); } } diff --git a/src/vs/workbench/contrib/markers/browser/markersFilterOptions.ts b/src/vs/workbench/contrib/markers/browser/markersFilterOptions.ts index 5d928a6cc1f..393094a99f6 100644 --- a/src/vs/workbench/contrib/markers/browser/markersFilterOptions.ts +++ b/src/vs/workbench/contrib/markers/browser/markersFilterOptions.ts @@ -34,7 +34,7 @@ export class FilterOptions { if (filter) { const filters = splitGlobAware(filter, ',').map(s => s.trim()).filter(s => !!s.length); for (const f of filters) { - if (strings.startsWith(f, '!')) { + if (f.startsWith('!')) { this.setPattern(excludesExpression, strings.ltrim(f, '!')); } else { this.setPattern(includeExpression, f); diff --git a/src/vs/workbench/contrib/markers/browser/markersModel.ts b/src/vs/workbench/contrib/markers/browser/markersModel.ts index 773a02c319a..f8016d3d663 100644 --- a/src/vs/workbench/contrib/markers/browser/markersModel.ts +++ b/src/vs/workbench/contrib/markers/browser/markersModel.ts @@ -3,25 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { basename } from 'vs/base/common/resources'; +import { basename, extUri } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { Range, IRange } from 'vs/editor/common/core/range'; import { IMarker, MarkerSeverity, IRelatedInformation, IMarkerData } from 'vs/platform/markers/common/markers'; -import { isFalsyOrEmpty, mergeSort } from 'vs/base/common/arrays'; -import { values } from 'vs/base/common/map'; -import { memoize } from 'vs/base/common/decorators'; +import { mergeSort, isNonEmptyArray, flatten } from 'vs/base/common/arrays'; +import { ResourceMap } from 'vs/base/common/map'; import { Emitter, Event } from 'vs/base/common/event'; import { Hasher } from 'vs/base/common/hash'; import { withUndefinedAsNull } from 'vs/base/common/types'; -function compareUris(a: URI, b: URI) { - const astr = a.toString(); - const bstr = b.toString(); - return astr === bstr ? 0 : (astr < bstr ? -1 : 1); -} export function compareMarkersByUri(a: IMarker, b: IMarker) { - return compareUris(a.resource, b.resource); + return extUri.compare(a.resource, b.resource); } function compareResourceMarkers(a: ResourceMarkers, b: ResourceMarkers): number { @@ -37,20 +31,60 @@ function compareResourceMarkers(a: ResourceMarkers, b: ResourceMarkers): number return res; } -function compareMarkers(a: Marker, b: Marker): number { - return MarkerSeverity.compare(a.marker.severity, b.marker.severity) - || Range.compareRangesUsingStarts(a.marker, b.marker); -} export class ResourceMarkers { - @memoize - get path(): string { return this.resource.fsPath; } + readonly path: string; - @memoize - get name(): string { return basename(this.resource); } + readonly name: string; - constructor(readonly id: string, readonly resource: URI, public markers: Marker[]) { } + private _markersMap = new ResourceMap(); + private _cachedMarkers: Marker[] | undefined; + private _total: number = 0; + + constructor(readonly id: string, readonly resource: URI) { + this.path = this.resource.fsPath; + this.name = basename(this.resource); + } + + get markers(): readonly Marker[] { + if (!this._cachedMarkers) { + this._cachedMarkers = mergeSort(flatten([...this._markersMap.values()]), ResourceMarkers._compareMarkers); + } + return this._cachedMarkers; + } + + has(uri: URI) { + return this._markersMap.has(uri); + } + + set(uri: URI, marker: Marker[]) { + this.delete(uri); + if (isNonEmptyArray(marker)) { + this._markersMap.set(uri, marker); + this._total += marker.length; + this._cachedMarkers = undefined; + } + } + + delete(uri: URI) { + let array = this._markersMap.get(uri); + if (array) { + this._total -= array.length; + this._cachedMarkers = undefined; + this._markersMap.delete(uri); + } + } + + get total() { + return this._total; + } + + private static _compareMarkers(a: Marker, b: Marker): number { + return MarkerSeverity.compare(a.marker.severity, b.marker.severity) + || extUri.compare(a.resource, b.resource) + || Range.compareRangesUsingStarts(a.marker, b.marker); + } } export class Marker { @@ -91,9 +125,9 @@ export class RelatedInformation { } export interface MarkerChangesEvent { - readonly added: ResourceMarkers[]; - readonly removed: ResourceMarkers[]; - readonly updated: ResourceMarkers[]; + readonly added: Set; + readonly removed: Set; + readonly updated: Set; } export class MarkersModel { @@ -105,9 +139,8 @@ export class MarkersModel { get resourceMarkers(): ResourceMarkers[] { if (!this.cachedSortedResources) { - this.cachedSortedResources = values(this.resourcesByUri).sort(compareResourceMarkers); + this.cachedSortedResources = [...this.resourcesByUri.values()].sort(compareResourceMarkers); } - return this.cachedSortedResources; } @@ -123,28 +156,33 @@ export class MarkersModel { } getResourceMarkers(resource: URI): ResourceMarkers | null { - return withUndefinedAsNull(this.resourcesByUri.get(resource.toString())); + return withUndefinedAsNull(this.resourcesByUri.get(extUri.getComparisonKey(resource, true))); } setResourceMarkers(resourcesMarkers: [URI, IMarker[]][]): void { - const change: MarkerChangesEvent = { added: [], removed: [], updated: [] }; + const change: MarkerChangesEvent = { added: new Set(), removed: new Set(), updated: new Set() }; for (const [resource, rawMarkers] of resourcesMarkers) { - let resourceMarkers = this.resourcesByUri.get(resource.toString()); - if (isFalsyOrEmpty(rawMarkers)) { - if (resourceMarkers) { - this.resourcesByUri.delete(resource.toString()); - change.removed.push(resourceMarkers); - this._total -= resourceMarkers.markers.length; + + const key = extUri.getComparisonKey(resource, true); + let resourceMarkers = this.resourcesByUri.get(key); + + if (isNonEmptyArray(rawMarkers)) { + // update, add + if (!resourceMarkers) { + const resourceMarkersId = this.id(resource.toString()); + resourceMarkers = new ResourceMarkers(resourceMarkersId, resource.with({ fragment: null })); + this.resourcesByUri.set(key, resourceMarkers); + change.added.add(resourceMarkers); + } else { + change.updated.add(resourceMarkers); } - } else { - const resourceMarkersId = this.id(resource.toString()); const markersCountByKey = new Map(); - const markers = mergeSort(rawMarkers.map((rawMarker) => { + const markers = rawMarkers.map((rawMarker) => { const key = IMarkerData.makeKey(rawMarker); const index = markersCountByKey.get(key) || 0; markersCountByKey.set(key, index + 1); - const markerId = this.id(resourceMarkersId, key, index); + const markerId = this.id(resourceMarkers!.id, key, index, rawMarker.resource.toString()); let relatedInformation: RelatedInformation[] | undefined = undefined; if (rawMarker.relatedInformation) { @@ -152,23 +190,28 @@ export class MarkersModel { } return new Marker(markerId, rawMarker, relatedInformation); - }), compareMarkers); + }); - if (resourceMarkers) { - this._total -= resourceMarkers.markers.length; - resourceMarkers.markers = markers; - change.updated.push(resourceMarkers); + this._total -= resourceMarkers.total; + resourceMarkers.set(resource, markers); + this._total += resourceMarkers.total; + + } else if (resourceMarkers) { + // clear + this._total -= resourceMarkers.total; + resourceMarkers.delete(resource); + this._total += resourceMarkers.total; + if (resourceMarkers.total === 0) { + this.resourcesByUri.delete(key); + change.removed.add(resourceMarkers); } else { - resourceMarkers = new ResourceMarkers(resourceMarkersId, resource, markers); - change.added.push(resourceMarkers); + change.updated.add(resourceMarkers); } - this._total += resourceMarkers.markers.length; - this.resourcesByUri.set(resource.toString(), resourceMarkers); } } this.cachedSortedResources = undefined; - if (change.added.length || change.removed.length || change.updated.length) { + if (change.added.size || change.removed.size || change.updated.size) { this._onDidChange.fire(change); } } diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 44ec95995b1..45c6e3f2145 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -52,6 +52,7 @@ import { domEvent } from 'vs/base/browser/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Progress } from 'vs/platform/progress/common/progress'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; export type TreeElement = ResourceMarkers | Marker | RelatedInformation; @@ -254,6 +255,30 @@ export class MarkerRenderer implements ITreeRenderer action.id === QuickFixAction.ID ? _instantiationService.createInstance(QuickFixActionViewItem, action) : undefined })); this.icon = dom.append(parent, dom.$('')); - this.multilineActionbar = this._register(new ActionBar(dom.append(parent, dom.$('.multiline-actions')))); + this.multilineActionbar = this._register(new ActionBar(dom.append(parent, dom.$('.multiline-actions')), { + actionViewItemProvider: (action) => { + if (action.id === toggleMultilineAction) { + return new ToggleMultilineActionViewItem(undefined, action, { icon: true }); + } + return undefined; + } + })); this.messageAndDetailsContainer = dom.append(parent, dom.$('.marker-message-details-container')); this._clickModifierKey = this._getClickModifierKey(); @@ -332,10 +364,10 @@ class MarkerWidget extends Disposable { private renderMultilineActionbar(marker: Marker): void { const viewModel = this.markersViewModel.getViewModel(marker); const multiline = viewModel && viewModel.multiline; - const action = new Action('problems.action.toggleMultiline'); + const action = new Action(toggleMultilineAction); action.enabled = !!viewModel && marker.lines.length > 1; action.tooltip = multiline ? localize('single line', "Show message in single line") : localize('multi line', "Show message in multiple lines"); - action.class = multiline ? 'codicon codicon-chevron-up' : 'codicon codicon-chevron-down'; + action.class = multiline ? expandedClass : collapsedClass; action.run = () => { if (viewModel) { viewModel.multiline = !viewModel.multiline; } return Promise.resolve(); }; this.multilineActionbar.push([action], { icon: true, label: false }); } diff --git a/src/vs/workbench/contrib/markers/browser/markersView.ts b/src/vs/workbench/contrib/markers/browser/markersView.ts index 397cd7f274d..c7cff71ecf5 100644 --- a/src/vs/workbench/contrib/markers/browser/markersView.ts +++ b/src/vs/workbench/contrib/markers/browser/markersView.ts @@ -7,7 +7,7 @@ import 'vs/css!./media/markers'; import { URI } from 'vs/base/common/uri'; import * as dom from 'vs/base/browser/dom'; -import { IAction, IActionViewItem, Action } from 'vs/base/common/actions'; +import { IAction, IActionViewItem, Action, Separator } from 'vs/base/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import Constants from 'vs/workbench/contrib/markers/browser/constants'; @@ -26,14 +26,14 @@ import { IContextKey, IContextKeyService, ContextKeyEqualsExpr, ContextKeyExpr } import { Iterable } from 'vs/base/common/iterator'; import { ITreeElement, ITreeNode, ITreeContextMenuEvent, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; import { Relay, Event, Emitter } from 'vs/base/common/event'; -import { WorkbenchObjectTree, TreeResourceNavigator, IListService, IWorkbenchObjectTreeOptions } from 'vs/platform/list/browser/listService'; +import { WorkbenchObjectTree, IListService, IWorkbenchObjectTreeOptions } from 'vs/platform/list/browser/listService'; import { FilterOptions } from 'vs/workbench/contrib/markers/browser/markersFilterOptions'; import { IExpression } from 'vs/base/common/glob'; import { deepClone } from 'vs/base/common/objects'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { FilterData, Filter, VirtualDelegate, ResourceMarkersRenderer, MarkerRenderer, RelatedInformationRenderer, TreeElement, MarkersTreeAccessibilityProvider, MarkersViewModel, ResourceDragAndDrop } from 'vs/workbench/contrib/markers/browser/markersTreeViewer'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { Separator, ActionViewItem, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { IMenuService, MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -50,6 +50,7 @@ import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/vie import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { Codicon } from 'vs/base/common/codicons'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; function createResourceMarkersIterator(resourceMarkers: ResourceMarkers): Iterable> { return Iterable.map(resourceMarkers.markers, m => { @@ -198,7 +199,8 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { if (this.hasNoProblems() && this.messageBoxContainer) { this.messageBoxContainer.focus(); } else if (this.tree) { - this.tree.getHTMLElement().focus(); + this.tree.domFocus(); + this.setTreeSelection(); } } @@ -295,13 +297,14 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { private refreshPanel(markerOrChange?: Marker | MarkerChangesEvent): void { if (this.isVisible() && this.tree) { + const hasSelection = this.tree.getSelection().length > 0; this.cachedFilterStats = undefined; if (markerOrChange) { if (markerOrChange instanceof Marker) { this.tree.rerender(markerOrChange); } else { - if (markerOrChange.added.length || markerOrChange.removed.length) { + if (markerOrChange.added.size || markerOrChange.removed.size) { // Reset complete tree this.resetTree(); } else { @@ -321,6 +324,20 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { this.tree.toggleVisibility(total === 0 || filtered === 0); this.renderMessage(); this._onDidChangeFilterStats.fire(this.getFilterStats()); + + if (hasSelection) { + this.setTreeSelection(); + } + } + } + + private setTreeSelection(): void { + if (this.tree && this.tree.getSelection().length === 0) { + const firstMarker = this.markersWorkbenchService.markersModel.resourceMarkers[0].markers[0]; + if (firstMarker) { + this.tree.setFocus([firstMarker]); + this.tree.setSelection([firstMarker]); + } } } @@ -424,7 +441,8 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { expandOnlyOnTwistieClick: (e: TreeElement) => e instanceof Marker && e.relatedInformation.length > 0, overrideStyles: { listBackground: this.getBackgroundColor() - } + }, + openOnFocus: true }, )); @@ -437,8 +455,7 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { relatedInformationFocusContextKey.set(focus.elements.some(e => e instanceof RelatedInformation)); })); - const markersNavigator = this._register(new TreeResourceNavigator(this.tree, { openOnFocus: true })); - this._register(Event.debounce(markersNavigator.onDidOpenResource, (last, event) => event, 75, true)(options => { + this._register(Event.debounce(this.tree.onDidOpen, (last, event) => event, 75, true)(options => { this.openFileAtElement(options.element, !!options.editorOptions.preserveFocus, options.sideBySide, !!options.editorOptions.pinned); })); this._register(this.tree.onDidChangeCollapseState(({ node }) => { @@ -675,7 +692,7 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { if (typeof autoReveal === 'boolean' && autoReveal) { let currentActiveResource = this.getResourceForCurrentActiveResource(); if (currentActiveResource) { - if (!this.tree.isCollapsed(currentActiveResource) && this.hasSelectedMarkerFor(currentActiveResource)) { + if (this.tree.hasElement(currentActiveResource) && !this.tree.isCollapsed(currentActiveResource) && this.hasSelectedMarkerFor(currentActiveResource)) { this.tree.reveal(this.tree.getSelection()[0], this.lastSelectedRelativeTop); if (focus) { this.tree.setFocus(this.tree.getSelection()); @@ -705,7 +722,7 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { let selectedElement = this.tree.getSelection(); if (selectedElement && selectedElement.length > 0) { if (selectedElement[0] instanceof Marker) { - if (resource.resource.toString() === (selectedElement[0]).marker.resource.toString()) { + if (resource.has((selectedElement[0]).marker.resource)) { return true; } } diff --git a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts index 430cecfcc62..48d031cea16 100644 --- a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts +++ b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts @@ -5,7 +5,7 @@ import { Delayer } from 'vs/base/common/async'; import * as DOM from 'vs/base/browser/dom'; -import { Action, IAction, IActionRunner } from 'vs/base/common/actions'; +import { Action, IAction, IActionRunner, Separator } from 'vs/base/common/actions'; import { HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -15,18 +15,19 @@ import Constants from 'vs/workbench/contrib/markers/browser/constants'; import { IThemeService, registerThemingParticipant, ICssStyleCollector, IColorTheme } from 'vs/platform/theme/common/themeService'; import { attachInputBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { toDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { BaseActionViewItem, ActionViewItem, ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { badgeBackground, badgeForeground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground } from 'vs/platform/theme/common/colorRegistry'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { badgeBackground, badgeForeground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground } from 'vs/platform/theme/common/colorRegistry'; import { localize } from 'vs/nls'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ContextScopedHistoryInputBox } from 'vs/platform/browser/contextScopedHistoryWidget'; import { Marker } from 'vs/workbench/contrib/markers/browser/markersModel'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { Event, Emitter } from 'vs/base/common/event'; -import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdown'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { IViewsService } from 'vs/workbench/common/views'; import { Codicon } from 'vs/base/common/codicons'; +import { BaseActionViewItem, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; export class ShowProblemsPanelAction extends Action { @@ -179,11 +180,12 @@ class FiltersDropdownMenuActionViewItem extends DropdownMenuActionViewItem { super(action, { getActions: () => this.getActions() }, contextMenuService, - action => undefined, - actionRunner!, - undefined, - action.class, - () => { return AnchorAlignment.RIGHT; }); + { + actionRunner, + classNames: action.class, + anchorAlignmentProvider: () => AnchorAlignment.RIGHT + } + ); } render(container: HTMLElement): void { @@ -323,7 +325,6 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem { ariaLabel: Messages.MARKERS_PANEL_FILTER_ARIA_LABEL, history: this.filterController.filters.filterHistory })); - this.filterInputBox.inputElement.setAttribute('aria-labelledby', 'markers-panel-arialabel'); this._register(attachInputBoxStyler(this.filterInputBox, this.themeService)); this.filterInputBox.value = this.filterController.filters.filterText; this._register(this.filterInputBox.onDidChange(filter => this.delayedFilterUpdate.trigger(() => this.onDidInputChange(this.filterInputBox!)))); @@ -515,6 +516,10 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) = if (inputActiveOptionBorderColor) { collector.addRule(`.markers-panel-action-filter > .markers-panel-filter-controls > .monaco-action-bar .action-label.markers-filters.checked { border-color: ${inputActiveOptionBorderColor}; }`); } + const inputActiveOptionForegroundColor = theme.getColor(inputActiveOptionForeground); + if (inputActiveOptionForegroundColor) { + collector.addRule(`.markers-panel-action-filter > .markers-panel-filter-controls > .monaco-action-bar .action-label.markers-filters.checked { color: ${inputActiveOptionForegroundColor}; }`); + } const inputActiveOptionBackgroundColor = theme.getColor(inputActiveOptionBackground); if (inputActiveOptionBackgroundColor) { collector.addRule(`.markers-panel-action-filter > .markers-panel-filter-controls > .monaco-action-bar .action-label.markers-filters.checked { background-color: ${inputActiveOptionBackgroundColor}; }`); diff --git a/src/vs/workbench/contrib/markers/browser/media/markers.css b/src/vs/workbench/contrib/markers/browser/media/markers.css index f68d0539c83..b898358d421 100644 --- a/src/vs/workbench/contrib/markers/browser/media/markers.css +++ b/src/vs/workbench/contrib/markers/browser/media/markers.css @@ -20,9 +20,13 @@ flex: 1; } -.vs .monaco-action-bar .markers-panel-action-filter .monaco-inputbox { +.pane-header .monaco-action-bar .markers-panel-action-filter .monaco-inputbox { + height: 20px; + line-height: 18px; +} + +.monaco-workbench.vs .monaco-action-bar .markers-panel-action-filter .monaco-inputbox { height: 25px; - border: 1px solid transparent; } .markers-panel-action-filter > .markers-panel-filter-controls { @@ -45,13 +49,21 @@ display: none; } -.markers-panel-action-filter > .markers-panel-filter-controls > .monaco-action-bar .action-label.markers-filters { +.markers-panel-action-filter > .markers-panel-filter-controls > .monaco-action-bar .action-item .action-label.codicon.markers-filters { line-height: 20px; height: 20px; min-width: 22px; margin-left: 4px; } +.pane-header .markers-panel-action-filter > .markers-panel-filter-controls > .monaco-action-bar .action-item .action-label.codicon.markers-filters { + line-height: 18px; + height: 18px; + width: 28px; + margin-right: 0px; + justify-content: center; +} + .panel > .title .monaco-action-bar .action-item.markers-panel-action-filter-container { max-width: 600px; min-width: 300px; @@ -101,7 +113,7 @@ padding-right: 10px; } -.hc-black .markers-panel .markers-panel-container .tree-container .monaco-tl-contents { +.monaco-workbench.hc-black .markers-panel .markers-panel-container .tree-container .monaco-tl-contents { line-height: 20px; } diff --git a/src/vs/workbench/contrib/markers/test/browser/markersModel.test.ts b/src/vs/workbench/contrib/markers/test/browser/markersModel.test.ts index 1cff079b0ae..dbc57637709 100644 --- a/src/vs/workbench/contrib/markers/test/browser/markersModel.test.ts +++ b/src/vs/workbench/contrib/markers/test/browser/markersModel.test.ts @@ -141,6 +141,51 @@ suite('MarkersModel Test', () => { assert.equal(JSON.stringify({ ...marker, resource: marker.resource.path, relatedInformation: marker.relatedInformation!.map(r => ({ ...r, resource: r.resource.path })) }, null, '\t'), testObject.toString()); }); + test('Markers for same-document but different fragment', function () { + const model = new TestMarkersModel([anErrorWithRange(1)]); + + assert.equal(model.total, 1); + + const document = URI.parse('foo://test/path/file'); + const frag1 = URI.parse('foo://test/path/file#1'); + const frag2 = URI.parse('foo://test/path/file#two'); + + model.setResourceMarkers([[document, [{ ...aMarker(), resource: frag1 }, { ...aMarker(), resource: frag2 }]]]); + + assert.equal(model.total, 3); + let a = model.getResourceMarkers(document); + let b = model.getResourceMarkers(frag1); + let c = model.getResourceMarkers(frag2); + assert.ok(a === b); + assert.ok(a === c); + + model.setResourceMarkers([[document, [{ ...aMarker(), resource: frag2 }]]]); + assert.equal(model.total, 2); + }); + + test('Problems are no sorted correctly #99135', function () { + const model = new TestMarkersModel([]); + assert.equal(model.total, 0); + + const document = URI.parse('foo://test/path/file'); + const frag1 = URI.parse('foo://test/path/file#1'); + const frag2 = URI.parse('foo://test/path/file#2'); + + model.setResourceMarkers([[frag1, [ + { ...aMarker(), resource: frag1 }, + { ...aMarker(undefined, MarkerSeverity.Warning), resource: frag1 }, + ]]]); + + model.setResourceMarkers([[frag2, [ + { ...aMarker(), resource: frag2 } + ]]]); + + assert.equal(model.total, 3); + const markers = model.getResourceMarkers(document)?.markers; + assert.deepEqual(markers?.map(m => m.marker.severity), [MarkerSeverity.Error, MarkerSeverity.Error, MarkerSeverity.Warning]); + assert.deepEqual(markers?.map(m => m.marker.resource.toString()), [frag1.toString(), frag2.toString(), frag1.toString()]); + }); + function compareResource(a: ResourceMarkers, b: string): boolean { return a.resource.toString() === URI.file(b).toString(); } diff --git a/src/vs/workbench/contrib/notebook/browser/constants.ts b/src/vs/workbench/contrib/notebook/browser/constants.ts index e65cd871273..b6b383674be 100644 --- a/src/vs/workbench/contrib/notebook/browser/constants.ts +++ b/src/vs/workbench/contrib/notebook/browser/constants.ts @@ -5,20 +5,26 @@ // Scrollable Element -export const SCROLLABLE_ELEMENT_PADDING_TOP = 16; +export const SCROLLABLE_ELEMENT_PADDING_TOP = 20; // Cell sizing related -export const CELL_MARGIN = 20; -export const CELL_RUN_GUTTER = 32; +export const CELL_MARGIN = 8; +export const CELL_RUN_GUTTER = 28; +export const CODE_CELL_LEFT_MARGIN = 32; export const EDITOR_TOOLBAR_HEIGHT = 0; -export const BOTTOM_CELL_TOOLBAR_HEIGHT = 36; +export const BOTTOM_CELL_TOOLBAR_HEIGHT = 18; +export const BOTTOM_CELL_TOOLBAR_OFFSET = 12; export const CELL_STATUSBAR_HEIGHT = 22; -// Top margin of editor -export const EDITOR_TOP_MARGIN = 0; +// Margin above editor +export const CELL_TOP_MARGIN = 6; +export const CELL_BOTTOM_MARGIN = 6; // Top and bottom padding inside the monaco editor in a cell, which are included in `cell.editorHeight` export const EDITOR_TOP_PADDING = 12; -export const EDITOR_BOTTOM_PADDING = 12; +export const EDITOR_BOTTOM_PADDING = 4; +export const CELL_OUTPUT_PADDING = 14; + +export const COLLAPSED_INDICATOR_HEIGHT = 24; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 391e17f1827..49b2ac84c5b 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { URI } from 'vs/base/common/uri'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; @@ -18,9 +18,11 @@ import { InputFocusedContext, InputFocusedContextKey } from 'vs/platform/context import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; -import { BaseCellRenderTemplate, CellEditState, CellRunState, ICellViewModel, INotebookEditor, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_RUNNABLE, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { CellKind, NOTEBOOK_EDITOR_CURSOR_BOUNDARY } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { BaseCellRenderTemplate, CellEditState, CellFocusMode, ICellViewModel, INotebookEditor, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; +import { CellKind, CellUri, NotebookCellRunState, NOTEBOOK_EDITOR_CURSOR_BOUNDARY } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; // Notebook Commands @@ -28,11 +30,10 @@ const EXECUTE_NOTEBOOK_COMMAND_ID = 'notebook.execute'; const CANCEL_NOTEBOOK_COMMAND_ID = 'notebook.cancelExecution'; const NOTEBOOK_FOCUS_TOP = 'notebook.focusTop'; const NOTEBOOK_FOCUS_BOTTOM = 'notebook.focusBottom'; -const NOTEBOOK_REDO = 'notebook.redo'; -const NOTEBOOK_UNDO = 'notebook.undo'; -const NOTEBOOK_CURSOR_UP = 'notebook.cursorUp'; -const NOTEBOOK_CURSOR_DOWN = 'notebook.cursorDown'; +const NOTEBOOK_FOCUS_PREVIOUS_EDITOR = 'notebook.focusPreviousEditor'; +const NOTEBOOK_FOCUS_NEXT_EDITOR = 'notebook.focusNextEditor'; const CLEAR_ALL_CELLS_OUTPUTS_COMMAND_ID = 'notebook.clearAllCellsOutputs'; +const RENDER_ALL_MARKDOWN_CELLS = 'notebook.renderAllMarkdownCells'; // Cell Commands const INSERT_CODE_CELL_ABOVE_COMMAND_ID = 'notebook.cell.insertCodeCellAbove'; @@ -44,7 +45,6 @@ const CHANGE_CELL_TO_MARKDOWN_COMMAND_ID = 'notebook.cell.changeToMarkdown'; const EDIT_CELL_COMMAND_ID = 'notebook.cell.edit'; const QUIT_EDIT_CELL_COMMAND_ID = 'notebook.cell.quitEdit'; -const SAVE_CELL_COMMAND_ID = 'notebook.cell.save'; const DELETE_CELL_COMMAND_ID = 'notebook.cell.delete'; const MOVE_CELL_UP_COMMAND_ID = 'notebook.cell.moveUp'; @@ -65,33 +65,129 @@ const EXECUTE_CELL_SELECT_BELOW = 'notebook.cell.executeAndSelectBelow'; const EXECUTE_CELL_INSERT_BELOW = 'notebook.cell.executeAndInsertBelow'; const CLEAR_CELL_OUTPUTS_COMMAND_ID = 'notebook.cell.clearOutputs'; const CHANGE_CELL_LANGUAGE = 'notebook.cell.changeLanguage'; +const CENTER_ACTIVE_CELL = 'notebook.centerActiveCell'; const FOCUS_IN_OUTPUT_COMMAND_ID = 'notebook.cell.focusInOutput'; const FOCUS_OUT_OUTPUT_COMMAND_ID = 'notebook.cell.focusOutOutput'; -export const NOTEBOOK_ACTIONS_CATEGORY = localize('notebookActions.category', "Notebook"); +const COLLAPSE_CELL_INPUT_COMMAND_ID = 'notebook.cell.collapseCellContent'; +const COLLAPSE_CELL_OUTPUT_COMMAND_ID = 'notebook.cell.collapseCellOutput'; +const EXPAND_CELL_CONTENT_COMMAND_ID = 'notebook.cell.expandCellContent'; +const EXPAND_CELL_OUTPUT_COMMAND_ID = 'notebook.cell.expandCellOutput'; + +export const NOTEBOOK_ACTIONS_CATEGORY = { value: localize('notebookActions.category', "Notebook"), original: 'Notebook' }; + +export const CELL_TITLE_CELL_GROUP_ID = 'inline/cell'; +export const CELL_TITLE_OUTPUT_GROUP_ID = 'inline/output'; const EDITOR_WIDGET_ACTION_WEIGHT = KeybindingWeight.EditorContrib; // smaller than Suggest Widget, etc const enum CellToolbarOrder { - MoveCellUp, - MoveCellDown, EditCell, SplitCell, SaveCell, ClearCellOutput, - InsertCell, DeleteCell } -registerAction2(class extends Action2 { +export interface INotebookActionContext { + readonly cellTemplate?: BaseCellRenderTemplate; + readonly cell?: ICellViewModel; + readonly notebookEditor: INotebookEditor; + readonly ui?: boolean; +} + +export interface INotebookCellActionContext extends INotebookActionContext { + cell: ICellViewModel; +} + +abstract class NotebookAction extends Action2 { + constructor(desc: IAction2Options) { + if (desc.f1 !== false) { + desc.f1 = false; + const f1Menu = { + id: MenuId.CommandPalette, + when: NOTEBOOK_IS_ACTIVE_EDITOR + }; + + if (!desc.menu) { + desc.menu = []; + } else if (!Array.isArray(desc.menu)) { + desc.menu = [desc.menu]; + } + + desc.menu = [ + ...desc.menu, + f1Menu + ]; + } + + desc.category = NOTEBOOK_ACTIONS_CATEGORY; + + super(desc); + } + + async run(accessor: ServicesAccessor, context?: INotebookActionContext): Promise { + if (!this.isNotebookActionContext(context)) { + context = this.getActiveEditorContext(accessor); + if (!context) { + return; + } + } + + this.runWithContext(accessor, context); + } + + abstract async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise; + + private isNotebookActionContext(context?: unknown): context is INotebookActionContext { + return !!context && !!(context as INotebookActionContext).notebookEditor; + } + + protected getActiveEditorContext(accessor: ServicesAccessor): INotebookActionContext | undefined { + const editorService = accessor.get(IEditorService); + + const editor = getActiveNotebookEditor(editorService); + if (!editor) { + return; + } + + const activeCell = editor.getActiveCell(); + return { + cell: activeCell, + notebookEditor: editor + }; + } +} + +abstract class NotebookCellAction extends NotebookAction { + protected isCellActionContext(context?: unknown): context is INotebookCellActionContext { + return !!context && !!(context as INotebookCellActionContext).notebookEditor && !!(context as INotebookCellActionContext).cell; + } + + async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise { + if (!this.isCellActionContext(context)) { + const activeEditorContext = this.getActiveEditorContext(accessor); + if (this.isCellActionContext(activeEditorContext)) { + context = activeEditorContext; + } else { + return; + } + } + + this.runWithContext(accessor, context); + } + + abstract async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise; +} + +registerAction2(class extends NotebookCellAction { constructor() { super({ id: EXECUTE_CELL_COMMAND_ID, - category: NOTEBOOK_ACTIONS_CATEGORY, title: localize('notebookActions.execute', "Execute Cell"), keybinding: { - when: NOTEBOOK_EDITOR_FOCUSED, + when: NOTEBOOK_CELL_LIST_FOCUSED, primary: KeyMod.WinCtrl | KeyCode.Enter, win: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Enter @@ -99,41 +195,24 @@ registerAction2(class extends Action2 { weight: EDITOR_WIDGET_ACTION_WEIGHT }, icon: { id: 'codicon/play' }, - f1: true }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - - return runCell(context); + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { + return runCell(accessor, context); } }); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super({ id: CANCEL_CELL_COMMAND_ID, title: localize('notebookActions.cancel', "Stop Cell Execution"), - category: NOTEBOOK_ACTIONS_CATEGORY, icon: { id: 'codicon/primitive-square' }, - f1: true }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { return context.notebookEditor.cancelNotebookCellExecution(context.cell); } }); @@ -175,155 +254,123 @@ export class CancelCellAction extends MenuItemAction { } -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super({ id: EXECUTE_CELL_SELECT_BELOW, title: localize('notebookActions.executeAndSelectBelow', "Execute Notebook Cell and Select Below"), - category: NOTEBOOK_ACTIONS_CATEGORY, keybinding: { - when: NOTEBOOK_EDITOR_FOCUSED, + when: NOTEBOOK_CELL_LIST_FOCUSED, primary: KeyMod.Shift | KeyCode.Enter, weight: EDITOR_WIDGET_ACTION_WEIGHT - } + }, }); } - async run(accessor: ServicesAccessor): Promise { - const editorService = accessor.get(IEditorService); - const activeCell = await runActiveCell(accessor); - if (!activeCell) { - return; - } - - const editor = getActiveNotebookEditor(editorService); - if (!editor) { - return; - } - - const idx = editor.viewModel?.getCellIndex(activeCell); + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { + const idx = context.notebookEditor.viewModel?.getCellIndex(context.cell); if (typeof idx !== 'number') { return; } + const executionP = runCell(accessor, context); + // Try to select below, fall back on inserting - const nextCell = editor.viewModel?.viewCells[idx + 1]; + const nextCell = context.notebookEditor.viewModel?.viewCells[idx + 1]; if (nextCell) { - editor.focusNotebookCell(nextCell, activeCell.editState === CellEditState.Editing ? 'editor' : 'container'); + context.notebookEditor.focusNotebookCell(nextCell, 'container'); } else { - const newCell = editor.insertNotebookCell(activeCell, CellKind.Code, 'below'); + const newCell = context.notebookEditor.insertNotebookCell(context.cell, CellKind.Code, 'below'); if (newCell) { - editor.focusNotebookCell(newCell, 'editor'); + context.notebookEditor.focusNotebookCell(newCell, 'editor'); } } + + return executionP; } }); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super({ id: EXECUTE_CELL_INSERT_BELOW, title: localize('notebookActions.executeAndInsertBelow', "Execute Notebook Cell and Insert Below"), - category: NOTEBOOK_ACTIONS_CATEGORY, keybinding: { - when: NOTEBOOK_EDITOR_FOCUSED, + when: NOTEBOOK_CELL_LIST_FOCUSED, primary: KeyMod.Alt | KeyCode.Enter, weight: EDITOR_WIDGET_ACTION_WEIGHT - } + }, }); } - async run(accessor: ServicesAccessor): Promise { - const editorService = accessor.get(IEditorService); - const activeCell = await runActiveCell(accessor); - if (!activeCell) { - return; - } + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { + const newFocusMode = context.cell.focusMode === CellFocusMode.Editor ? 'editor' : 'container'; - const editor = getActiveNotebookEditor(editorService); - if (!editor) { - return; - } - - const newCell = editor.insertNotebookCell(activeCell, CellKind.Code, 'below'); + const executionP = runCell(accessor, context); + const newCell = context.notebookEditor.insertNotebookCell(context.cell, CellKind.Code, 'below'); if (newCell) { - editor.focusNotebookCell(newCell, 'editor'); + context.notebookEditor.focusNotebookCell(newCell, newFocusMode); } + + return executionP; } }); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookAction { + constructor() { + super({ + id: RENDER_ALL_MARKDOWN_CELLS, + title: localize('notebookActions.renderMarkdown', "Render All Markdown Cells"), + }); + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise { + renderAllMarkdownCells(context); + } +}); + +registerAction2(class extends NotebookAction { constructor() { super({ id: EXECUTE_NOTEBOOK_COMMAND_ID, title: localize('notebookActions.executeNotebook', "Execute Notebook"), - category: NOTEBOOK_ACTIONS_CATEGORY, - f1: true }); } - async run(accessor: ServicesAccessor): Promise { - const editorService = accessor.get(IEditorService); - const editor = getActiveNotebookEditor(editorService); - if (!editor) { - return; + async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise { + renderAllMarkdownCells(context); + + const editorGroupService = accessor.get(IEditorGroupsService); + const group = editorGroupService.activeGroup; + + if (group) { + if (group.activeEditor) { + group.pinEditor(group.activeEditor); + } } - return editor.executeNotebook(); + return context.notebookEditor.executeNotebook(); } }); -registerAction2(class extends Action2 { +function renderAllMarkdownCells(context: INotebookActionContext): void { + context.notebookEditor.viewModel!.viewCells.forEach(cell => { + if (cell.cellKind === CellKind.Markdown) { + cell.editState = CellEditState.Preview; + } + }); +} + +registerAction2(class extends NotebookAction { constructor() { super({ id: CANCEL_NOTEBOOK_COMMAND_ID, title: localize('notebookActions.cancelNotebook', "Cancel Notebook Execution"), - category: NOTEBOOK_ACTIONS_CATEGORY, - f1: true }); } - async run(accessor: ServicesAccessor): Promise { - const editorService = accessor.get(IEditorService); - const editor = getActiveNotebookEditor(editorService); - if (!editor) { - return; - } - - return editor.cancelNotebookExecution(); - } -}); - -registerAction2(class extends Action2 { - constructor() { - super({ - id: QUIT_EDIT_CELL_COMMAND_ID, - title: localize('notebookActions.quitEditing', "Quit Notebook Cell Editing"), - category: NOTEBOOK_ACTIONS_CATEGORY, - keybinding: { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext), - primary: KeyCode.Escape, - weight: EDITOR_WIDGET_ACTION_WEIGHT - 5 - } - }); - } - - async run(accessor: ServicesAccessor): Promise { - let editorService = accessor.get(IEditorService); - let editor = getActiveNotebookEditor(editorService); - - if (!editor) { - return; - } - - let activeCell = editor.getActiveCell(); - if (activeCell) { - if (activeCell.cellKind === CellKind.Markdown) { - activeCell.editState = CellEditState.Preview; - } - - editor.focusNotebookCell(activeCell, 'container'); - } + async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise { + return context.notebookEditor.cancelNotebookExecution(); } }); @@ -331,40 +378,25 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: EXECUTE_NOTEBOOK_COMMAND_ID, title: localize('notebookActions.menu.executeNotebook', "Execute Notebook (Run all cells)"), - category: NOTEBOOK_ACTIONS_CATEGORY, icon: { id: 'codicon/run-all' } }, order: -1, group: 'navigation', - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK.toNegated(), NOTEBOOK_EDITOR_RUNNABLE) + when: ContextKeyExpr.and(NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK.toNegated(), NOTEBOOK_EDITOR_RUNNABLE) }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CANCEL_NOTEBOOK_COMMAND_ID, title: localize('notebookActions.menu.cancelNotebook', "Stop Notebook Execution"), - category: NOTEBOOK_ACTIONS_CATEGORY, icon: { id: 'codicon/primitive-square' } }, order: -1, group: 'navigation', - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK) + when: ContextKeyExpr.and(NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK) }); - -MenuRegistry.appendMenuItem(MenuId.EditorTitle, { - command: { - id: EXECUTE_CELL_COMMAND_ID, - title: localize('notebookActions.menu.execute', "Execute Notebook Cell"), - category: NOTEBOOK_ACTIONS_CATEGORY, - icon: { id: 'codicon/run' } - }, - order: 0, - group: 'navigation', - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_CELL_RUNNABLE) -}); - -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super({ id: CHANGE_CELL_TO_CODE_COMMAND_ID, @@ -374,17 +406,21 @@ registerAction2(class extends Action2 { primary: KeyCode.KEY_Y, weight: KeybindingWeight.WorkbenchContrib }, - category: NOTEBOOK_ACTIONS_CATEGORY, - f1: true + precondition: ContextKeyExpr.and(NOTEBOOK_IS_ACTIVE_EDITOR), + menu: { + id: MenuId.NotebookCellTitle, + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE), + group: '2_edit', + } }); } - async run(accessor: ServicesAccessor): Promise { - return changeActiveCellToKind(CellKind.Code, accessor); + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { + await changeCellToKind(CellKind.Code, context); } }); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super({ id: CHANGE_CELL_TO_MARKDOWN_COMMAND_ID, @@ -394,61 +430,42 @@ registerAction2(class extends Action2 { primary: KeyCode.KEY_M, weight: KeybindingWeight.WorkbenchContrib }, - category: NOTEBOOK_ACTIONS_CATEGORY, - f1: true + menu: { + id: MenuId.NotebookCellTitle, + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE), + group: '2_edit', + } }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise { - return changeActiveCellToKind(CellKind.Markdown, accessor); + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { + await changeCellToKind(CellKind.Markdown, context); } }); export function getActiveNotebookEditor(editorService: IEditorService): INotebookEditor | undefined { // TODO can `isNotebookEditor` be on INotebookEditor to avoid a circular dependency? - const activeEditorPane = editorService.activeEditorPane as any | undefined; - return activeEditorPane?.isNotebookEditor ? activeEditorPane.getControl() : undefined; + const activeEditorPane = editorService.activeEditorPane as unknown as { isNotebookEditor?: boolean } | undefined; + return activeEditorPane?.isNotebookEditor ? (editorService.activeEditorPane?.getControl() as INotebookEditor) : undefined; } -async function runActiveCell(accessor: ServicesAccessor): Promise { - const editorService = accessor.get(IEditorService); - const editor = getActiveNotebookEditor(editorService); - if (!editor) { +async function runCell(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { + if (context.cell.metadata?.runState === NotebookCellRunState.Running) { return; } - const activeCell = editor.getActiveCell(); - if (!activeCell) { - return; - } + const editorGroupService = accessor.get(IEditorGroupsService); + const group = editorGroupService.activeGroup; - editor.executeNotebookCell(activeCell); - return activeCell; -} - -async function runCell(context: INotebookCellActionContext): Promise { - if (context.cell.runState === CellRunState.Running) { - return; + if (group) { + if (group.activeEditor) { + group.pinEditor(group.activeEditor); + } } return context.notebookEditor.executeNotebookCell(context.cell); } -async function changeActiveCellToKind(kind: CellKind, accessor: ServicesAccessor): Promise { - const editorService = accessor.get(IEditorService); - const editor = getActiveNotebookEditor(editorService); - if (!editor) { - return; - } - - const activeCell = editor.getActiveCell(); - if (!activeCell) { - return; - } - - changeCellToKind(kind, { cell: activeCell, notebookEditor: editor }); -} - export async function changeCellToKind(kind: CellKind, context: INotebookCellActionContext, language?: string): Promise { const { cell, notebookEditor } = context; @@ -481,37 +498,7 @@ export async function changeCellToKind(kind: CellKind, context: INotebookCellAct return newCell; } -export interface INotebookCellActionContext { - cellTemplate?: BaseCellRenderTemplate; - cell: ICellViewModel; - notebookEditor: INotebookEditor; - ui?: boolean; -} - -function isCellActionContext(context: any): context is INotebookCellActionContext { - return context && !!context.cell && !!context.notebookEditor; -} - -function getActiveCellContext(accessor: ServicesAccessor): INotebookCellActionContext | undefined { - const editorService = accessor.get(IEditorService); - - const editor = getActiveNotebookEditor(editorService); - if (!editor) { - return; - } - - const activeCell = editor.getActiveCell(); - if (!activeCell) { - return; - } - - return { - cell: activeCell, - notebookEditor: editor - }; -} - -abstract class InsertCellCommand extends Action2 { +abstract class InsertCellCommand extends NotebookAction { constructor( desc: Readonly, private kind: CellKind, @@ -520,15 +507,8 @@ abstract class InsertCellCommand extends Action2 { super(desc); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - - const newCell = context.notebookEditor.insertNotebookCell(context.cell, this.kind, this.direction, undefined, context.ui); + async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise { + const newCell = context.notebookEditor.insertNotebookCell(context.cell, this.kind, this.direction, undefined, true); if (newCell) { context.notebookEditor.focusNotebookCell(newCell, 'editor'); } @@ -541,109 +521,86 @@ registerAction2(class extends InsertCellCommand { { id: INSERT_CODE_CELL_ABOVE_COMMAND_ID, title: localize('notebookActions.insertCodeCellAbove', "Insert Code Cell Above"), - category: NOTEBOOK_ACTIONS_CATEGORY, - f1: true, keybinding: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter, - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext.toNegated()), + when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, InputFocusedContext.toNegated()), weight: KeybindingWeight.WorkbenchContrib - } + }, }, CellKind.Code, 'above'); } }); -export class InsertCodeCellAction extends MenuItemAction { - constructor( - @IContextKeyService contextKeyService: IContextKeyService, - @ICommandService commandService: ICommandService - ) { - super( - { - id: INSERT_CODE_CELL_BELOW_COMMAND_ID, - title: localize('notebookActions.insertCodeCellBelow', "Insert Code Cell Below") - }, - undefined, - { shouldForwardArgs: true }, - contextKeyService, - commandService); - } -} - registerAction2(class extends InsertCellCommand { constructor() { super( { id: INSERT_CODE_CELL_BELOW_COMMAND_ID, title: localize('notebookActions.insertCodeCellBelow', "Insert Code Cell Below"), - category: NOTEBOOK_ACTIONS_CATEGORY, - icon: { id: 'codicon/add' }, - f1: true, keybinding: { primary: KeyMod.CtrlCmd | KeyCode.Enter, - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext.toNegated()), + when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, InputFocusedContext.toNegated()), weight: KeybindingWeight.WorkbenchContrib - } + }, }, CellKind.Code, 'below'); } }); +MenuRegistry.appendMenuItem(MenuId.NotebookCellBetween, { + command: { + id: INSERT_CODE_CELL_BELOW_COMMAND_ID, + title: localize('notebookActions.menu.insertCode', "$(add) Code"), + tooltip: localize('notebookActions.menu.insertCode.tooltip', "Add Code Cell") + }, + order: 0, + group: 'inline' +}); + registerAction2(class extends InsertCellCommand { constructor() { super( { id: INSERT_MARKDOWN_CELL_ABOVE_COMMAND_ID, title: localize('notebookActions.insertMarkdownCellAbove', "Insert Markdown Cell Above"), - category: NOTEBOOK_ACTIONS_CATEGORY, - f1: true }, CellKind.Markdown, 'above'); } }); -export class InsertMarkdownCellAction extends MenuItemAction { - constructor( - @IContextKeyService contextKeyService: IContextKeyService, - @ICommandService commandService: ICommandService - ) { - super( - { - id: INSERT_MARKDOWN_CELL_BELOW_COMMAND_ID, - title: localize('notebookActions.insertMarkdownCellBelow', "Insert Markdown Cell Below") - }, - undefined, - { shouldForwardArgs: true }, - contextKeyService, - commandService); - } -} - registerAction2(class extends InsertCellCommand { constructor() { super( { id: INSERT_MARKDOWN_CELL_BELOW_COMMAND_ID, title: localize('notebookActions.insertMarkdownCellBelow', "Insert Markdown Cell Below"), - category: NOTEBOOK_ACTIONS_CATEGORY, - f1: true }, CellKind.Markdown, 'below'); } }); -registerAction2(class extends Action2 { +MenuRegistry.appendMenuItem(MenuId.NotebookCellBetween, { + command: { + id: INSERT_MARKDOWN_CELL_BELOW_COMMAND_ID, + title: localize('notebookActions.menu.insertMarkdown', "$(add) Markdown"), + tooltip: localize('notebookActions.menu.insertMarkdown.tooltip', "Add Markdown Cell") + }, + order: 1, + group: 'inline' +}); + +registerAction2(class extends NotebookCellAction { constructor() { super( { id: EDIT_CELL_COMMAND_ID, title: localize('notebookActions.editCell', "Edit Cell"), keybinding: { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), + when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), primary: KeyCode.Enter, weight: KeybindingWeight.WorkbenchContrib }, @@ -653,65 +610,66 @@ registerAction2(class extends Action2 { NOTEBOOK_CELL_TYPE.isEqualTo('markdown'), NOTEBOOK_CELL_MARKDOWN_EDIT_MODE.toNegated(), NOTEBOOK_CELL_EDITABLE), - order: CellToolbarOrder.EditCell + order: CellToolbarOrder.EditCell, + group: CELL_TITLE_CELL_GROUP_ID }, icon: { id: 'codicon/pencil' } }); } - run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - - return context.notebookEditor.editNotebookCell(context.cell); + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { + context.notebookEditor.focusNotebookCell(context.cell, 'editor'); } }); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super( { - id: SAVE_CELL_COMMAND_ID, - title: localize('notebookActions.saveCell', "Save Cell"), + id: QUIT_EDIT_CELL_COMMAND_ID, + title: localize('notebookActions.quitEdit', "Stop Editing Cell"), menu: { id: MenuId.NotebookCellTitle, when: ContextKeyExpr.and( NOTEBOOK_CELL_TYPE.isEqualTo('markdown'), NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_EDITABLE), - order: CellToolbarOrder.SaveCell + order: CellToolbarOrder.SaveCell, + group: CELL_TITLE_CELL_GROUP_ID + }, + icon: { id: 'codicon/check' }, + keybinding: { + when: ContextKeyExpr.and( + NOTEBOOK_EDITOR_FOCUSED, + InputFocusedContext, + EditorContextKeys.hoverVisible.toNegated(), + EditorContextKeys.hasNonEmptySelection.toNegated()), + primary: KeyCode.Escape, + weight: EDITOR_WIDGET_ACTION_WEIGHT - 5 }, - icon: { id: 'codicon/check' } }); } - run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { + if (context.cell.cellKind === CellKind.Markdown) { + context.cell.editState = CellEditState.Preview; } - return context.notebookEditor.saveNotebookCell(context.cell); + return context.notebookEditor.focusNotebookCell(context.cell, 'container'); } }); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super( { id: DELETE_CELL_COMMAND_ID, title: localize('notebookActions.deleteCell', "Delete Cell"), - category: NOTEBOOK_ACTIONS_CATEGORY, menu: { id: MenuId.NotebookCellTitle, order: CellToolbarOrder.DeleteCell, - when: NOTEBOOK_EDITOR_EDITABLE + when: NOTEBOOK_EDITOR_EDITABLE, + group: CELL_TITLE_CELL_GROUP_ID }, keybinding: { primary: KeyCode.Delete, @@ -722,18 +680,10 @@ registerAction2(class extends Action2 { weight: KeybindingWeight.WorkbenchContrib }, icon: { id: 'codicon/trash' }, - f1: true }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { const index = context.notebookEditor.viewModel!.getCellIndex(context.cell); const result = await context.notebookEditor.deleteNotebookCell(context.cell); @@ -742,12 +692,6 @@ registerAction2(class extends Action2 { const nextCellIdx = index < context.notebookEditor.viewModel!.length ? index : context.notebookEditor.viewModel!.length - 1; if (nextCellIdx >= 0) { context.notebookEditor.focusNotebookCell(context.notebookEditor.viewModel!.viewCells[nextCellIdx], 'container'); - } else { - // No cells left, insert a new empty one - const newCell = context.notebookEditor.insertNotebookCell(undefined, context.cell.cellKind); - if (newCell) { - context.notebookEditor.focusNotebookCell(newCell, 'editor'); - } } } } @@ -760,7 +704,7 @@ async function moveCell(context: INotebookCellActionContext, direction: 'up' | ' if (result) { // move cell command only works when the cell container has focus - context.notebookEditor.focusNotebookCell(context.cell, 'container'); + context.notebookEditor.focusNotebookCell(result, 'container'); } } @@ -773,15 +717,13 @@ async function copyCell(context: INotebookCellActionContext, direction: 'up' | ' } } -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super( { id: MOVE_CELL_UP_COMMAND_ID, title: localize('notebookActions.moveCellUp', "Move Cell Up"), - category: NOTEBOOK_ACTIONS_CATEGORY, icon: { id: 'codicon/arrow-up' }, - f1: true, keybinding: { primary: KeyMod.Alt | KeyCode.UpArrow, when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext.toNegated()), @@ -790,27 +732,18 @@ registerAction2(class extends Action2 { }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { return moveCell(context, 'up'); } }); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super( { id: MOVE_CELL_DOWN_COMMAND_ID, title: localize('notebookActions.moveCellDown', "Move Cell Down"), - category: NOTEBOOK_ACTIONS_CATEGORY, icon: { id: 'codicon/arrow-down' }, - f1: true, keybinding: { primary: KeyMod.Alt | KeyCode.DownArrow, when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext.toNegated()), @@ -819,73 +752,48 @@ registerAction2(class extends Action2 { }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { return moveCell(context, 'down'); } }); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super( { id: COPY_CELL_COMMAND_ID, title: localize('notebookActions.copy', "Copy Cell"), - category: NOTEBOOK_ACTIONS_CATEGORY, - f1: true, - keybinding: { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), - primary: KeyMod.CtrlCmd | KeyCode.KEY_C, - weight: EDITOR_WIDGET_ACTION_WEIGHT - }, + menu: { + id: MenuId.NotebookCellTitle, + when: NOTEBOOK_EDITOR_FOCUSED, + group: '1_copy', + } }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { const clipboardService = accessor.get(IClipboardService); const notebookService = accessor.get(INotebookService); clipboardService.writeText(context.cell.getText()); - notebookService.setToCopy([context.cell.model]); + notebookService.setToCopy([context.cell.model], true); } }); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super( { id: CUT_CELL_COMMAND_ID, title: localize('notebookActions.cut', "Cut Cell"), - category: NOTEBOOK_ACTIONS_CATEGORY, - f1: true, - keybinding: { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), - primary: KeyMod.CtrlCmd | KeyCode.KEY_X, - weight: EDITOR_WIDGET_ACTION_WEIGHT - }, + menu: { + id: MenuId.NotebookCellTitle, + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE), + group: '1_copy', + } }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { const clipboardService = accessor.get(IClipboardService); const notebookService = accessor.get(INotebookService); clipboardService.writeText(context.cell.getText()); @@ -896,18 +804,72 @@ registerAction2(class extends Action2 { } viewModel.deleteCell(viewModel.getCellIndex(context.cell), true); - notebookService.setToCopy([context.cell.model]); + notebookService.setToCopy([context.cell.model], false); } }); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookAction { + constructor() { + super( + { + id: PASTE_CELL_COMMAND_ID, + title: localize('notebookActions.paste', "Paste Cell"), + menu: { + id: MenuId.NotebookCellTitle, + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE), + group: '1_copy', + } + }); + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext) { + const notebookService = accessor.get(INotebookService); + const pasteCells = notebookService.getToCopy(); + + const viewModel = context.notebookEditor.viewModel; + + if (!viewModel) { + return; + } + + if (!pasteCells) { + return; + } + + const currCellIndex = context.cell && viewModel.getCellIndex(context.cell); + + let topPastedCell: CellViewModel | undefined = undefined; + pasteCells.items.reverse().map(cell => { + const data = CellUri.parse(cell.uri); + + if (pasteCells.isCopy || data?.notebook.toString() !== viewModel.uri.toString()) { + return viewModel.notebookDocument.createCellTextModel( + cell.getValue(), + cell.language, + cell.cellKind, + [], + cell.metadata + ); + } else { + return cell; + } + }).forEach(pasteCell => { + const newIdx = typeof currCellIndex === 'number' ? currCellIndex + 1 : 0; + topPastedCell = viewModel.insertCell(newIdx, pasteCell, true); + }); + + if (topPastedCell) { + context.notebookEditor.focusNotebookCell(topPastedCell, 'container'); + } + } +}); + +registerAction2(class extends NotebookCellAction { constructor() { super( { id: PASTE_CELL_ABOVE_COMMAND_ID, title: localize('notebookActions.pasteAbove', "Paste Cell Above"), - category: NOTEBOOK_ACTIONS_CATEGORY, - f1: true, keybinding: { when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V, @@ -916,16 +878,9 @@ registerAction2(class extends Action2 { }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { const notebookService = accessor.get(INotebookService); - const pasteCells = notebookService.getToCopy() || []; + const pasteCells = notebookService.getToCopy(); const viewModel = context.notebookEditor.viewModel; @@ -933,64 +888,44 @@ registerAction2(class extends Action2 { return; } + if (!pasteCells) { + return; + } + const currCellIndex = viewModel.getCellIndex(context!.cell); - pasteCells.reverse().forEach(pasteCell => { - viewModel.insertCell(currCellIndex, pasteCell, true); - return; - }); - } -}); -registerAction2(class extends Action2 { - constructor() { - super( - { - id: PASTE_CELL_COMMAND_ID, - title: localize('notebookActions.paste', "Paste Cell"), - category: NOTEBOOK_ACTIONS_CATEGORY, - f1: true, - keybinding: { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), - primary: KeyMod.CtrlCmd | KeyCode.KEY_V, - weight: EDITOR_WIDGET_ACTION_WEIGHT - }, - }); - } + let topPastedCell: CellViewModel | undefined = undefined; + pasteCells.items.reverse().map(cell => { + const data = CellUri.parse(cell.uri); - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; + if (pasteCells.isCopy || data?.notebook.toString() !== viewModel.uri.toString()) { + return viewModel.notebookDocument.createCellTextModel( + cell.getValue(), + cell.language, + cell.cellKind, + [], + cell.metadata + ); + } else { + return cell; } - } - - const notebookService = accessor.get(INotebookService); - const pasteCells = notebookService.getToCopy() || []; - - const viewModel = context.notebookEditor.viewModel; - - if (!viewModel) { - return; - } - - const currCellIndex = viewModel.getCellIndex(context!.cell); - - pasteCells.reverse().forEach(pasteCell => { - viewModel.insertCell(currCellIndex + 1, pasteCell, true); + }).forEach(pasteCell => { + topPastedCell = viewModel.insertCell(currCellIndex, pasteCell, true); return; }); + + if (topPastedCell) { + context.notebookEditor.focusNotebookCell(topPastedCell, 'container'); + } } }); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super( { id: COPY_CELL_UP_COMMAND_ID, title: localize('notebookActions.copyCellUp', "Copy Cell Up"), - category: NOTEBOOK_ACTIONS_CATEGORY, - f1: true, keybinding: { primary: KeyMod.Alt | KeyMod.Shift | KeyCode.UpArrow, when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext.toNegated()), @@ -999,26 +934,17 @@ registerAction2(class extends Action2 { }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { return copyCell(context, 'up'); } }); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super( { id: COPY_CELL_DOWN_COMMAND_ID, title: localize('notebookActions.copyCellDown', "Copy Cell Down"), - category: NOTEBOOK_ACTIONS_CATEGORY, - f1: true, keybinding: { primary: KeyMod.Alt | KeyMod.Shift | KeyCode.DownArrow, when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext.toNegated()), @@ -1027,40 +953,38 @@ registerAction2(class extends Action2 { }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { return copyCell(context, 'down'); } }); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super({ - id: NOTEBOOK_CURSOR_DOWN, - title: localize('cursorMoveDown', 'Cursor Move Down'), - category: NOTEBOOK_ACTIONS_CATEGORY, - keybinding: { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.has(InputFocusedContextKey), EditorContextKeys.editorTextFocus, NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('top'), NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('none')), - primary: KeyCode.DownArrow, - weight: EDITOR_WIDGET_ACTION_WEIGHT - } + id: NOTEBOOK_FOCUS_NEXT_EDITOR, + title: localize('cursorMoveDown', 'Focus Next Cell Editor'), + keybinding: [ + { + when: ContextKeyExpr.and( + NOTEBOOK_EDITOR_FOCUSED, + ContextKeyExpr.has(InputFocusedContextKey), + EditorContextKeys.editorTextFocus, + NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('top'), + NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('none')), + primary: KeyCode.DownArrow, + weight: EDITOR_WIDGET_ACTION_WEIGHT + }, + { + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_OUTPUT_FOCUSED), + primary: KeyMod.CtrlCmd | KeyCode.DownArrow, + mac: { primary: KeyMod.WinCtrl | KeyMod.CtrlCmd | KeyCode.DownArrow, }, + weight: KeybindingWeight.WorkbenchContrib + } + ] }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { const editor = context.notebookEditor; const activeCell = context.cell; @@ -1075,32 +999,31 @@ registerAction2(class extends Action2 { return; } - editor.focusNotebookCell(newCell, 'editor'); + const newFocusMode = newCell.cellKind === CellKind.Markdown && newCell.editState === CellEditState.Preview ? 'container' : 'editor'; + editor.focusNotebookCell(newCell, newFocusMode); + editor.cursorNavigationMode = true; } }); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super({ - id: NOTEBOOK_CURSOR_UP, - title: localize('cursorMoveUp', 'Cursor Move Up'), - category: NOTEBOOK_ACTIONS_CATEGORY, + id: NOTEBOOK_FOCUS_PREVIOUS_EDITOR, + title: localize('cursorMoveUp', 'Focus Previous Cell Editor'), keybinding: { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.has(InputFocusedContextKey), EditorContextKeys.editorTextFocus, NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('bottom'), NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('none')), + when: ContextKeyExpr.and( + NOTEBOOK_EDITOR_FOCUSED, + ContextKeyExpr.has(InputFocusedContextKey), + EditorContextKeys.editorTextFocus, + NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('bottom'), + NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('none')), primary: KeyCode.UpArrow, weight: EDITOR_WIDGET_ACTION_WEIGHT }, }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { const editor = context.notebookEditor; const activeCell = context.cell; @@ -1120,64 +1043,48 @@ registerAction2(class extends Action2 { return; } - editor.focusNotebookCell(newCell, 'editor'); + const newFocusMode = newCell.cellKind === CellKind.Markdown && newCell.editState === CellEditState.Preview ? 'container' : 'editor'; + editor.focusNotebookCell(newCell, newFocusMode); + editor.cursorNavigationMode = true; } }); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super({ id: FOCUS_IN_OUTPUT_COMMAND_ID, title: localize('focusOutput', 'Focus In Active Cell Output'), - category: NOTEBOOK_ACTIONS_CATEGORY, keybinding: { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED), + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_CELL_HAS_OUTPUTS), primary: KeyMod.CtrlCmd | KeyCode.DownArrow, - mac: { primary: KeyMod.WinCtrl | KeyCode.DownArrow, }, + mac: { primary: KeyMod.WinCtrl | KeyMod.CtrlCmd | KeyCode.DownArrow, }, weight: KeybindingWeight.WorkbenchContrib }, - f1: true }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { const editor = context.notebookEditor; const activeCell = context.cell; editor.focusNotebookCell(activeCell, 'output'); } }); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super({ id: FOCUS_OUT_OUTPUT_COMMAND_ID, title: localize('focusOutputOut', 'Focus Out Active Cell Output'), - category: NOTEBOOK_ACTIONS_CATEGORY, keybinding: { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED), + when: NOTEBOOK_EDITOR_FOCUSED, primary: KeyMod.CtrlCmd | KeyCode.UpArrow, - mac: { primary: KeyMod.WinCtrl | KeyCode.UpArrow, }, + mac: { primary: KeyMod.WinCtrl | KeyMod.CtrlCmd | KeyCode.UpArrow, }, weight: KeybindingWeight.WorkbenchContrib }, - f1: true }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { const editor = context.notebookEditor; const activeCell = context.cell; editor.focusNotebookCell(activeCell, 'editor'); @@ -1185,94 +1092,21 @@ registerAction2(class extends Action2 { }); -registerAction2(class extends Action2 { - constructor() { - super({ - id: NOTEBOOK_UNDO, - title: localize('undo', 'Undo'), - category: NOTEBOOK_ACTIONS_CATEGORY, - keybinding: { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), - primary: KeyMod.CtrlCmd | KeyCode.KEY_Z, - weight: KeybindingWeight.WorkbenchContrib - } - }); - } - - async run(accessor: ServicesAccessor): Promise { - const editorService = accessor.get(IEditorService); - - const editor = getActiveNotebookEditor(editorService); - if (!editor) { - return; - } - - const viewModel = editor.viewModel; - - if (!viewModel) { - return; - } - - viewModel.undo(); - } -}); - -registerAction2(class extends Action2 { - constructor() { - super({ - id: NOTEBOOK_REDO, - title: localize('redo', 'Redo'), - category: NOTEBOOK_ACTIONS_CATEGORY, - keybinding: { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z, - weight: KeybindingWeight.WorkbenchContrib - } - }); - } - - async run(accessor: ServicesAccessor): Promise { - const editorService = accessor.get(IEditorService); - - const editor = getActiveNotebookEditor(editorService); - if (!editor) { - return; - } - - const viewModel = editor.viewModel; - - if (!viewModel) { - return; - } - - viewModel.redo(); - } -}); - -registerAction2(class extends Action2 { +registerAction2(class extends NotebookAction { constructor() { super({ id: NOTEBOOK_FOCUS_TOP, title: localize('focusFirstCell', 'Focus First Cell'), - category: NOTEBOOK_ACTIONS_CATEGORY, keybinding: { when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), primary: KeyMod.CtrlCmd | KeyCode.Home, mac: { primary: KeyMod.CtrlCmd | KeyCode.UpArrow }, weight: KeybindingWeight.WorkbenchContrib }, - f1: true }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise { const editor = context.notebookEditor; if (!editor.viewModel || !editor.viewModel.length) { return; @@ -1283,30 +1117,21 @@ registerAction2(class extends Action2 { } }); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookAction { constructor() { super({ id: NOTEBOOK_FOCUS_BOTTOM, title: localize('focusLastCell', 'Focus Last Cell'), - category: NOTEBOOK_ACTIONS_CATEGORY, keybinding: { when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), primary: KeyMod.CtrlCmd | KeyCode.End, mac: { primary: KeyMod.CtrlCmd | KeyCode.DownArrow }, weight: KeybindingWeight.WorkbenchContrib }, - f1: true }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise { const editor = context.notebookEditor; if (!editor.viewModel || !editor.viewModel.length) { return; @@ -1317,30 +1142,27 @@ registerAction2(class extends Action2 { } }); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super({ id: CLEAR_CELL_OUTPUTS_COMMAND_ID, title: localize('clearActiveCellOutputs', 'Clear Active Cell Outputs'), - category: NOTEBOOK_ACTIONS_CATEGORY, menu: { id: MenuId.NotebookCellTitle, when: ContextKeyExpr.and(NOTEBOOK_CELL_TYPE.isEqualTo('code'), NOTEBOOK_EDITOR_RUNNABLE), - order: CellToolbarOrder.ClearCellOutput + order: CellToolbarOrder.ClearCellOutput, + group: CELL_TITLE_OUTPUT_GROUP_ID + }, + keybinding: { + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey), NOTEBOOK_CELL_HAS_OUTPUTS), + primary: KeyMod.Alt | KeyCode.Delete, + weight: KeybindingWeight.WorkbenchContrib }, icon: { id: 'codicon/clear-all' }, - f1: true }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { const editor = context.notebookEditor; if (!editor.viewModel || !editor.viewModel.length) { return; @@ -1355,24 +1177,15 @@ interface ILanguagePickInput extends IQuickPickItem { description: string; } -export class ChangeCellLanguageAction extends Action2 { +export class ChangeCellLanguageAction extends NotebookCellAction { constructor() { super({ id: CHANGE_CELL_LANGUAGE, title: localize('changeLanguage', 'Change Cell Language'), - category: NOTEBOOK_ACTIONS_CATEGORY, - f1: true }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { this.showLanguagePicker(accessor, context); } @@ -1459,31 +1272,22 @@ export class ChangeCellLanguageAction extends Action2 { } registerAction2(ChangeCellLanguageAction); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookAction { constructor() { super({ id: CLEAR_ALL_CELLS_OUTPUTS_COMMAND_ID, title: localize('clearAllCellsOutputs', 'Clear All Cells Outputs'), - category: NOTEBOOK_ACTIONS_CATEGORY, menu: { id: MenuId.EditorTitle, - when: NOTEBOOK_EDITOR_FOCUSED, + when: NOTEBOOK_IS_ACTIVE_EDITOR, group: 'navigation', order: 0 }, icon: { id: 'codicon/clear-all' }, - f1: true }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise { const editor = context.notebookEditor; if (!editor.viewModel || !editor.viewModel.length) { return; @@ -1494,94 +1298,202 @@ registerAction2(class extends Action2 { }); async function splitCell(context: INotebookCellActionContext): Promise { - if (context.cell.cellKind === CellKind.Code) { - const newCells = await context.notebookEditor.splitNotebookCell(context.cell); - if (newCells) { - context.notebookEditor.focusNotebookCell(newCells[newCells.length - 1], 'editor'); - } + const newCells = await context.notebookEditor.splitNotebookCell(context.cell); + if (newCells) { + context.notebookEditor.focusNotebookCell(newCells[newCells.length - 1], 'editor'); } } -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super( { id: SPLIT_CELL_COMMAND_ID, title: localize('notebookActions.splitCell', "Split Cell"), - category: NOTEBOOK_ACTIONS_CATEGORY, menu: { id: MenuId.NotebookCellTitle, - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_CELL_TYPE.isEqualTo('code'), NOTEBOOK_EDITOR_EDITABLE, InputFocusedContext), - order: CellToolbarOrder.SplitCell + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, InputFocusedContext), + order: CellToolbarOrder.SplitCell, + group: CELL_TITLE_CELL_GROUP_ID, + // alt: { + // id: JOIN_CELL_BELOW_COMMAND_ID, + // title: localize('notebookActions.joinCellBelow', "Join with Next Cell") + // } }, icon: { id: 'codicon/split-vertical' }, - f1: true + keybinding: { + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, InputFocusedContext), + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKSLASH), + weight: KeybindingWeight.WorkbenchContrib + }, }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { return splitCell(context); } }); async function joinCells(context: INotebookCellActionContext, direction: 'above' | 'below'): Promise { - const cell = await context.notebookEditor.joinNotebookCells(context.cell, direction, CellKind.Code); + const cell = await context.notebookEditor.joinNotebookCells(context.cell, direction); if (cell) { context.notebookEditor.focusNotebookCell(cell, 'editor'); } } -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super( { id: JOIN_CELL_ABOVE_COMMAND_ID, - title: localize('notebookActions.joinCellAbove', "Join with Previous Cell"), - category: NOTEBOOK_ACTIONS_CATEGORY, - f1: true + title: localize('notebookActions.joinCellAbove', "Join With Previous Cell"), + keybinding: { + when: NOTEBOOK_EDITOR_FOCUSED, + primary: KeyMod.WinCtrl | KeyMod.Alt | KeyMod.Shift | KeyCode.KEY_J, + weight: KeybindingWeight.WorkbenchContrib + } }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { return joinCells(context, 'above'); } }); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookCellAction { constructor() { super( { id: JOIN_CELL_BELOW_COMMAND_ID, - title: localize('notebookActions.joinCellBelow', "Join with Next Cell"), - category: NOTEBOOK_ACTIONS_CATEGORY, - f1: true + title: localize('notebookActions.joinCellBelow', "Join With Next Cell"), + keybinding: { + when: NOTEBOOK_EDITOR_FOCUSED, + primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.KEY_J, + weight: KeybindingWeight.WorkbenchContrib + }, + menu: { + id: MenuId.NotebookCellTitle, + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE), + group: '2_edit', + } }); } - async run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!isCellActionContext(context)) { - context = getActiveCellContext(accessor); - if (!context) { - return; - } - } - + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { return joinCells(context, 'below'); } }); +registerAction2(class extends NotebookCellAction { + constructor() { + super({ + id: CENTER_ACTIVE_CELL, + title: localize('notebookActions.centerActiveCell', "Center Active Cell"), + keybinding: { + when: NOTEBOOK_EDITOR_FOCUSED, + primary: KeyMod.CtrlCmd | KeyCode.KEY_L, + mac: { + primary: KeyMod.WinCtrl | KeyCode.KEY_L, + }, + weight: KeybindingWeight.WorkbenchContrib + }, + }); + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { + return context.notebookEditor.revealInCenter(context.cell); + } +}); + +registerAction2(class extends NotebookCellAction { + constructor() { + super({ + id: COLLAPSE_CELL_INPUT_COMMAND_ID, + title: localize('notebookActions.collapseCellInput', "Collapse Cell Input"), + keybinding: { + when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_INPUT_COLLAPSED.toNegated(), InputFocusedContext.toNegated()), + primary: KeyChord(KeyCode.KEY_C, KeyCode.KEY_C), + weight: KeybindingWeight.WorkbenchContrib + }, + menu: { + id: MenuId.NotebookCellTitle, + when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_INPUT_COLLAPSED.toNegated()), + group: '3_collapse', + } + }); + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { + context.notebookEditor.viewModel!.notebookDocument.changeCellMetadata(context.cell.handle, { inputCollapsed: true }); + } +}); + +registerAction2(class extends NotebookCellAction { + constructor() { + super({ + id: EXPAND_CELL_CONTENT_COMMAND_ID, + title: localize('notebookActions.expandCellContent', "Expand Cell Content"), + keybinding: { + when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_INPUT_COLLAPSED), + primary: KeyChord(KeyCode.KEY_C, KeyCode.KEY_C), + weight: KeybindingWeight.WorkbenchContrib + }, + menu: { + id: MenuId.NotebookCellTitle, + when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_INPUT_COLLAPSED), + group: '3_collapse', + } + }); + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { + context.notebookEditor.viewModel!.notebookDocument.changeCellMetadata(context.cell.handle, { inputCollapsed: false }); + } +}); + +registerAction2(class extends NotebookCellAction { + constructor() { + super({ + id: COLLAPSE_CELL_OUTPUT_COMMAND_ID, + title: localize('notebookActions.collapseCellOutput', "Collapse Cell Output"), + keybinding: { + when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_OUTPUT_COLLAPSED.toNegated(), InputFocusedContext.toNegated(), NOTEBOOK_CELL_HAS_OUTPUTS), + primary: KeyChord(KeyCode.KEY_C, KeyCode.KEY_O), + weight: KeybindingWeight.WorkbenchContrib + }, + menu: { + id: MenuId.NotebookCellTitle, + when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_OUTPUT_COLLAPSED.toNegated(), NOTEBOOK_CELL_HAS_OUTPUTS), + group: '3_collapse', + } + }); + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { + context.notebookEditor.viewModel!.notebookDocument.changeCellMetadata(context.cell.handle, { outputCollapsed: true }); + } +}); + +registerAction2(class extends NotebookCellAction { + constructor() { + super({ + id: EXPAND_CELL_OUTPUT_COMMAND_ID, + title: localize('notebookActions.expandCellOutput', "Expand Cell Output"), + keybinding: { + when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_OUTPUT_COLLAPSED), + primary: KeyChord(KeyCode.KEY_C, KeyCode.KEY_O), + weight: KeybindingWeight.WorkbenchContrib + }, + menu: { + id: MenuId.NotebookCellTitle, + when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_OUTPUT_COLLAPSED), + group: '3_collapse', + } + }); + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { + context.notebookEditor.viewModel!.notebookDocument.changeCellMetadata(context.cell.handle, { inputCollapsed: false }); + } +}); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/findController.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/findController.ts index 7bf08ff33d1..71f74948655 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/findController.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/findController.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/css!./media/notebookFind'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED, INotebookEditor, CellFindMatch, CellEditState, INotebookEditorContribution, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; @@ -23,6 +24,14 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { getActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; +import { FindReplaceState } from 'vs/editor/contrib/find/findState'; +import { INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { EditorStartFindAction, EditorStartFindReplaceAction } from 'vs/editor/contrib/find/findController'; + +const FIND_HIDE_TRANSITION = 'find-hide-transition'; +const FIND_SHOW_TRANSITION = 'find-show-transition'; + export class NotebookFindWidget extends SimpleFindReplaceWidget implements INotebookEditorContribution { static id: string = 'workbench.notebook.find'; @@ -32,20 +41,30 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote private _currentMatch: number = -1; private _allMatchesDecorations: ICellModelDecorations[] = []; private _currentMatchDecorations: ICellModelDecorations[] = []; + private _showTimeout: number | null = null; + private _hideTimeout: number | null = null; constructor( private readonly _notebookEditor: INotebookEditor, @IContextViewService contextViewService: IContextViewService, @IContextKeyService contextKeyService: IContextKeyService, @IThemeService themeService: IThemeService, + @IConfigurationService private readonly _configurationService: IConfigurationService ) { - super(contextViewService, contextKeyService, themeService); + super(contextViewService, contextKeyService, themeService, new FindReplaceState(), true); DOM.append(this._notebookEditor.getDomNode(), this.getDomNode()); this._findWidgetFocused = KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED.bindTo(contextKeyService); this._register(this._findInput.onKeyDown((e) => this._onFindInputKeyDown(e))); this.updateTheme(themeService.getColorTheme()); + this._register(themeService.onDidColorThemeChange(() => { + this.updateTheme(themeService.getColorTheme()); + })); + + this._register(this._state.onFindReplaceStateChange(() => { + this.onInputChanged(); + })); } private _onFindInputKeyDown(e: IKeyboardEvent): void { @@ -53,7 +72,7 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote if (this._findMatches.length) { this.find(false); } else { - this.set(null); + this.set(null, true); } e.preventDefault(); return; @@ -61,7 +80,7 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote if (this._findMatches.length) { this.find(true); } else { - this.set(null); + this.set(null, true); } e.preventDefault(); return; @@ -70,13 +89,18 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote protected onInputChanged(): boolean { const val = this.inputValue; + const wordSeparators = this._configurationService.inspect('editor.wordSeparators').value; + const options: INotebookSearchOptions = { regex: this._getRegexValue(), wholeWord: this._getWholeWordValue(), caseSensitive: this._getCaseSensitiveValue(), wordSeparators: wordSeparators }; if (val) { - this._findMatches = this._notebookEditor.viewModel!.find(val).filter(match => match.matches.length > 0); + this._findMatches = this._notebookEditor.viewModel!.find(val, options).filter(match => match.matches.length > 0); + this.set(this._findMatches, false); if (this._findMatches.length) { return true; } else { return false; } + } else { + this.set([], false); } return false; @@ -88,7 +112,7 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote } if (!this._findMatchesStarts) { - this.set(this._findMatches); + this.set(this._findMatches, true); } else { const totalVal = this._findMatchesStarts!.getTotalValue(); const nextVal = (this._currentMatch + (previous ? -1 : 1) + totalVal) % totalVal; @@ -107,7 +131,7 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote } if (!this._findMatchesStarts) { - this.set(this._findMatches); + this.set(this._findMatches, true); } const nextIndex = this._findMatchesStarts!.getIndexOf(this._currentMatch); @@ -133,12 +157,7 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote this._findMatches[cellIndex].cell.editState = CellEditState.Editing; this._notebookEditor.selectElement(this._findMatches[cellIndex].cell); this._notebookEditor.setCellSelection(this._findMatches[cellIndex].cell, this._findMatches[cellIndex].matches[matchIndex].range); - this._notebookEditor.revealRangeInCenterIfOutsideViewport(this._findMatches[cellIndex].cell, this._findMatches[cellIndex].matches[matchIndex].range); - } - - hide() { - super.hide(); - this.set([]); + this._notebookEditor.revealRangeInCenterIfOutsideViewportAsync(this._findMatches[cellIndex].cell, this._findMatches[cellIndex].matches[matchIndex].range); } protected findFirst(): void { } @@ -174,7 +193,7 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote } } - private set(cellFindMatches: CellFindMatch[] | null): void { + private set(cellFindMatches: CellFindMatch[] | null, autoStart: boolean): void { if (!cellFindMatches || !cellFindMatches.length) { this._findMatches = []; this.setAllFindMatchesDecorations([]); @@ -191,12 +210,15 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote // current match this.constructFindMatchesStarts(); - this._currentMatch = 0; - this.setCurrentFindMatchDecoration(0, 0); + + if (autoStart) { + this._currentMatch = 0; + this.setCurrentFindMatchDecoration(0, 0); + } } private setCurrentFindMatchDecoration(cellIndex: number, matchIndex: number) { - this._notebookEditor.changeDecorations(accessor => { + this._notebookEditor.changeModelDecorations(accessor => { const findMatchesOptions: ModelDecorationOptions = FindDecorations._CURRENT_FIND_MATCH_DECORATION; const cell = this._findMatches[cellIndex].cell; @@ -214,21 +236,21 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote } private clearCurrentFindMatchDecoration() { - this._notebookEditor.changeDecorations(accessor => { + this._notebookEditor.changeModelDecorations(accessor => { this._currentMatchDecorations = accessor.deltaDecorations(this._currentMatchDecorations, []); }); } private setAllFindMatchesDecorations(cellFindMatches: CellFindMatch[]) { - this._notebookEditor.changeDecorations((accessor) => { + this._notebookEditor.changeModelDecorations((accessor) => { - let findMatchesOptions: ModelDecorationOptions = FindDecorations._FIND_MATCH_DECORATION; + const findMatchesOptions: ModelDecorationOptions = FindDecorations._FIND_MATCH_DECORATION; - let deltaDecorations: ICellModelDeltaDecorations[] = cellFindMatches.map(cellFindMatch => { + const deltaDecorations: ICellModelDeltaDecorations[] = cellFindMatches.map(cellFindMatch => { const findMatches = cellFindMatch.matches; // Find matches - let newFindMatchesDecorations: IModelDeltaDecoration[] = new Array(findMatches.length); + const newFindMatchesDecorations: IModelDeltaDecoration[] = new Array(findMatches.length); for (let i = 0, len = findMatches.length; i < len; i++) { newFindMatchesDecorations[i] = { range: findMatches[i].range, @@ -243,10 +265,78 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote }); } + show(initialInput?: string): void { + super.show(initialInput); + this._findInput.select(); + + if (this._showTimeout === null) { + if (this._hideTimeout !== null) { + window.clearTimeout(this._hideTimeout); + this._hideTimeout = null; + this._notebookEditor.removeClassName(FIND_HIDE_TRANSITION); + } + + this._notebookEditor.addClassName(FIND_SHOW_TRANSITION); + this._showTimeout = window.setTimeout(() => { + this._notebookEditor.removeClassName(FIND_SHOW_TRANSITION); + this._showTimeout = null; + }, 200); + } else { + // no op + } + } + + replace(initialFindInput?: string, initialReplaceInput?: string) { + super.showWithReplace(initialFindInput, initialReplaceInput); + this._replaceInput.select(); + + if (this._showTimeout === null) { + if (this._hideTimeout !== null) { + window.clearTimeout(this._hideTimeout); + this._hideTimeout = null; + this._notebookEditor.removeClassName(FIND_HIDE_TRANSITION); + } + + this._notebookEditor.addClassName(FIND_SHOW_TRANSITION); + this._showTimeout = window.setTimeout(() => { + this._notebookEditor.removeClassName(FIND_SHOW_TRANSITION); + this._showTimeout = null; + }, 200); + } else { + // no op + } + } + + hide() { + super.hide(); + this.set([], false); + + if (this._hideTimeout === null) { + if (this._showTimeout !== null) { + window.clearTimeout(this._showTimeout); + this._showTimeout = null; + this._notebookEditor.removeClassName(FIND_SHOW_TRANSITION); + } + this._notebookEditor.addClassName(FIND_HIDE_TRANSITION); + this._hideTimeout = window.setTimeout(() => { + this._notebookEditor.removeClassName(FIND_HIDE_TRANSITION); + }, 200); + } else { + // no op + } + } + clear() { this._currentMatch = -1; this._findMatches = []; } + + dispose() { + this._notebookEditor?.removeClassName(FIND_SHOW_TRANSITION); + this._notebookEditor?.removeClassName(FIND_HIDE_TRANSITION); + super.dispose(); + } + } registerNotebookContribution(NotebookFindWidget.id, NotebookFindWidget); @@ -255,7 +345,7 @@ registerAction2(class extends Action2 { constructor() { super({ id: 'notebook.hideFind', - title: localize('notebookActions.hideFind', "Hide Find in Notebook"), + title: { value: localize('notebookActions.hideFind', "Hide Find in Notebook"), original: 'Hide Find in Notebook' }, keybinding: { when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED), primary: KeyCode.Escape, @@ -265,8 +355,8 @@ registerAction2(class extends Action2 { } async run(accessor: ServicesAccessor): Promise { - let editorService = accessor.get(IEditorService); - let editor = getActiveNotebookEditor(editorService); + const editorService = accessor.get(IEditorService); + const editor = getActiveNotebookEditor(editorService); if (!editor) { return; @@ -282,7 +372,7 @@ registerAction2(class extends Action2 { constructor() { super({ id: 'notebook.find', - title: localize('notebookActions.findInNotebook', "Find in Notebook"), + title: { value: localize('notebookActions.findInNotebook', "Find in Notebook"), original: 'Find in Notebook' }, keybinding: { when: NOTEBOOK_EDITOR_FOCUSED, primary: KeyCode.KEY_F | KeyMod.CtrlCmd, @@ -292,8 +382,8 @@ registerAction2(class extends Action2 { } async run(accessor: ServicesAccessor): Promise { - let editorService = accessor.get(IEditorService); - let editor = getActiveNotebookEditor(editorService); + const editorService = accessor.get(IEditorService); + const editor = getActiveNotebookEditor(editorService); if (!editor) { return; @@ -303,3 +393,29 @@ registerAction2(class extends Action2 { controller.show(); } }); + +EditorStartFindAction.addImplementation(100, (accessor: ServicesAccessor, args: any) => { + const editorService = accessor.get(IEditorService); + const editor = getActiveNotebookEditor(editorService); + + if (!editor) { + return false; + } + + const controller = editor.getContribution(NotebookFindWidget.id); + controller.show(); + return true; +}); + +EditorStartFindReplaceAction.addImplementation(100, (accessor: ServicesAccessor, args: any) => { + const editorService = accessor.get(IEditorService); + const editor = getActiveNotebookEditor(editorService); + + if (!editor) { + return false; + } + + const controller = editor.getContribution(NotebookFindWidget.id); + controller.replace(); + return true; +}); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/media/notebookFind.css b/src/vs/workbench/contrib/notebook/browser/contrib/find/media/notebookFind.css new file mode 100644 index 00000000000..98fa75e3930 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/media/notebookFind.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-workbench .notebookOverlay.notebook-editor.find-hide-transition { + overflow-y: hidden; +} + +.monaco-workbench .notebookOverlay.notebook-editor.find-show-transition { + overflow-y: hidden; +} diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts b/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts index 5f0605296dc..814dc3a2527 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { INotebookEditor, INotebookEditorMouseEvent, ICellRange, INotebookEditorContribution, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { INotebookEditor, INotebookEditorMouseEvent, ICellRange, INotebookEditorContribution, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import * as DOM from 'vs/base/browser/dom'; import { CellFoldingState, FoldingModel } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel'; import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -12,7 +12,7 @@ import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/brow import { registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys'; -import { KeyCode } from 'vs/base/common/keyCodes'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -56,7 +56,7 @@ export class FoldingController extends Disposable implements INotebookEditorCont })); } - saveViewState(): any { + saveViewState(): ICellRange[] { return this._foldingModel?.getMemento() || []; } @@ -134,13 +134,19 @@ registerAction2(class extends Action2 { constructor() { super({ id: 'notebook.fold', - title: localize('fold.cell', 'Fold Cell'), + title: { value: localize('fold.cell', "Fold Cell"), original: 'Fold Cell' }, category: NOTEBOOK_ACTIONS_CATEGORY, keybinding: { when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), - primary: KeyCode.LeftArrow, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET, + mac: { + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_OPEN_SQUARE_BRACKET, + secondary: [KeyCode.LeftArrow], + }, + secondary: [KeyCode.LeftArrow], weight: KeybindingWeight.WorkbenchContrib }, + precondition: NOTEBOOK_IS_ACTIVE_EDITOR, f1: true }); } @@ -172,13 +178,19 @@ registerAction2(class extends Action2 { constructor() { super({ id: 'notebook.unfold', - title: localize('unfold.cell', 'Unfold Cell'), + title: { value: localize('unfold.cell', "Unfold Cell"), original: 'Unfold Cell' }, category: NOTEBOOK_ACTIONS_CATEGORY, keybinding: { when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), - primary: KeyCode.RightArrow, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_CLOSE_SQUARE_BRACKET, + mac: { + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_CLOSE_SQUARE_BRACKET, + secondary: [KeyCode.RightArrow], + }, + secondary: [KeyCode.RightArrow], weight: KeybindingWeight.WorkbenchContrib }, + precondition: NOTEBOOK_IS_ACTIVE_EDITOR, f1: true }); } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts index d612cf6e87a..18f853b05e5 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts @@ -79,7 +79,7 @@ export class FoldingModel extends Disposable { recompute() { const cells = this._viewModel!.viewCells; - let stack: { index: number, level: number, endIndex: number }[] = []; + const stack: { index: number, level: number, endIndex: number }[] = []; for (let i = 0; i < cells.length; i++) { const cell = cells[i]; @@ -129,9 +129,9 @@ export class FoldingModel extends Disposable { // restore collased state let i = 0; - let nextCollapsed = () => { + const nextCollapsed = () => { while (i < this._regions.length) { - let isCollapsed = this._regions.isCollapsed(i); + const isCollapsed = this._regions.isCollapsed(i); i++; if (isCollapsed) { return i - 1; @@ -145,12 +145,12 @@ export class FoldingModel extends Disposable { while (collapsedIndex !== -1 && k < newRegions.length) { // get the latest range - let decRange = this._viewModel!.getTrackedRange(this._foldingRangeDecorationIds[collapsedIndex]); + const decRange = this._viewModel!.getTrackedRange(this._foldingRangeDecorationIds[collapsedIndex]); if (decRange) { - let collasedStartIndex = decRange.start; + const collasedStartIndex = decRange.start; while (k < newRegions.length) { - let startIndex = newRegions.getStartLineNumber(k) - 1; + const startIndex = newRegions.getStartLineNumber(k) - 1; if (collasedStartIndex >= startIndex) { newRegions.setCollapsed(k, collasedStartIndex === startIndex); k++; @@ -186,7 +186,7 @@ export class FoldingModel extends Disposable { const collapsedRanges: ICellRange[] = []; let i = 0; while (i < this._regions.length) { - let isCollapsed = this._regions.isCollapsed(i); + const isCollapsed = this._regions.isCollapsed(i); if (isCollapsed) { const region = this._regions.toRegion(i); @@ -205,12 +205,12 @@ export class FoldingModel extends Disposable { while (k < state.length && i < this._regions.length) { // get the latest range - let decRange = this._viewModel!.getTrackedRange(this._foldingRangeDecorationIds[i]); + const decRange = this._viewModel!.getTrackedRange(this._foldingRangeDecorationIds[i]); if (decRange) { - let collasedStartIndex = state[k].start; + const collasedStartIndex = state[k].start; while (i < this._regions.length) { - let startIndex = this._regions.getStartLineNumber(i) - 1; + const startIndex = this._regions.getStartLineNumber(i) - 1; if (collasedStartIndex >= startIndex) { this._regions.setCollapsed(i, collasedStartIndex === startIndex); i++; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/format/formatting.ts b/src/vs/workbench/contrib/notebook/browser/contrib/format/formatting.ts index 417ed162bd1..6cb8b4a2bb0 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/format/formatting.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/format/formatting.ts @@ -29,7 +29,7 @@ registerAction2(class extends Action2 { constructor() { super({ id: 'notebook.format', - title: localize('format.title', 'Format Notebook'), + title: { value: localize('format.title', "Format Notebook"), original: 'Format Notebook' }, category: NOTEBOOK_ACTIONS_CATEGORY, precondition: ContextKeyExpr.and(NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_EDITOR_EDITABLE), keybinding: { @@ -65,7 +65,7 @@ registerAction2(class extends Action2 { const edits: WorkspaceTextEdit[] = []; - for (let cell of notebook.cells) { + for (const cell of notebook.cells) { const ref = await textModelService.createModelReference(cell.uri); dispoables.add(ref); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts new file mode 100644 index 00000000000..99fc865265a --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts @@ -0,0 +1,205 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { Registry } from 'vs/platform/registry/common/platform'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; +import { INotebookActionContext, NOTEBOOK_ACTIONS_CATEGORY, getActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; +import { INotebookEditor, NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; + +import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { INotebookKernelInfo2, INotebookKernelInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; +import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/common/statusbar'; +import { NotebookKernelProviderAssociation, NotebookKernelProviderAssociations, notebookKernelProviderAssociationsSettingId } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + + +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'notebook.selectKernel', + category: NOTEBOOK_ACTIONS_CATEGORY, + title: { value: nls.localize('notebookActions.selectKernel', "Select Notebook Kernel"), original: 'Select Notebook Kernel' }, + precondition: NOTEBOOK_IS_ACTIVE_EDITOR, + icon: { id: 'codicon/server-environment' }, + f1: true + }); + } + + async run(accessor: ServicesAccessor, context?: INotebookActionContext): Promise { + const editorService = accessor.get(IEditorService); + const notebookService = accessor.get(INotebookService); + const quickInputService = accessor.get(IQuickInputService); + const configurationService = accessor.get(IConfigurationService); + + const activeEditorPane = editorService.activeEditorPane as unknown as { isNotebookEditor?: boolean } | undefined; + if (!activeEditorPane?.isNotebookEditor) { + return; + } + const editor = editorService.activeEditorPane?.getControl() as INotebookEditor; + const activeKernel = editor.activeKernel; + + const tokenSource = new CancellationTokenSource(); + const availableKernels2 = await notebookService.getContributedNotebookKernels2(editor.viewModel!.viewType, editor.viewModel!.uri, tokenSource.token); + const availableKernels = notebookService.getContributedNotebookKernels(editor.viewModel!.viewType, editor.viewModel!.uri); + const picks: QuickPickInput[] = [...availableKernels2, ...availableKernels].map((a) => { + return { + id: a.id, + label: a.label, + picked: a.id === activeKernel?.id, + description: + (a as INotebookKernelInfo2).description + ? (a as INotebookKernelInfo2).description + : a.extension.value + (a.id === activeKernel?.id + ? nls.localize('currentActiveKernel', " (Currently Active)") + : ''), + kernelProviderId: a.extension.value, + run: async () => { + editor.activeKernel = a; + if ((a as any).resolve) { + (a as INotebookKernelInfo2).resolve(editor.uri!, editor.getId(), tokenSource.token); + } + }, + buttons: [{ + iconClass: 'codicon-settings-gear', + tooltip: nls.localize('notebook.promptKernel.setDefaultTooltip', "Set as default kernel provider for '{0}'", editor.viewModel!.viewType) + }] + }; + }); + + const provider = notebookService.getContributedNotebookProviders(editor.viewModel!.uri)[0]; + + if (provider.kernel) { + picks.unshift({ + id: provider.id, + label: provider.displayName, + picked: !activeKernel, // no active kernel, the builtin kernel of the provider is used + description: activeKernel === undefined + ? nls.localize('currentActiveBuiltinKernel', " (Currently Active)") + : '', + kernelProviderId: provider.providerExtensionId, + run: () => { + editor.activeKernel = undefined; + }, + buttons: [{ + iconClass: 'codicon-settings-gear', + tooltip: nls.localize('notebook.promptKernel.setDefaultTooltip', "Set as default kernel provider for '{0}'", editor.viewModel!.viewType) + }] + }); + } + + const picker = quickInputService.createQuickPick<(IQuickPickItem & { run(): void; kernelProviderId?: string })>(); + picker.items = picks; + picker.placeholder = nls.localize('pickAction', "Select Action"); + picker.matchOnDetail = true; + + const pickedItem = await new Promise<(IQuickPickItem & { run(): void; kernelProviderId?: string; }) | undefined>(resolve => { + picker.onDidAccept(() => { + resolve(picker.selectedItems.length === 1 ? picker.selectedItems[0] : undefined); + picker.dispose(); + }); + + picker.onDidTriggerItemButton(e => { + const pick = e.item; + const id = pick.id; + resolve(pick); // open the view + picker.dispose(); + + // And persist the setting + if (pick && id && pick.kernelProviderId) { + const newAssociation: NotebookKernelProviderAssociation = { viewType: editor.viewModel!.viewType, kernelProvider: pick.kernelProviderId }; + const currentAssociations = [...configurationService.getValue(notebookKernelProviderAssociationsSettingId)]; + + // First try updating existing association + for (let i = 0; i < currentAssociations.length; ++i) { + const existing = currentAssociations[i]; + if (existing.viewType === newAssociation.viewType) { + currentAssociations.splice(i, 1, newAssociation); + configurationService.updateValue(notebookKernelProviderAssociationsSettingId, currentAssociations); + return; + } + } + + // Otherwise, create a new one + currentAssociations.unshift(newAssociation); + configurationService.updateValue(notebookKernelProviderAssociationsSettingId, currentAssociations); + } + }); + + picker.show(); + }); + + tokenSource.dispose(); + return pickedItem?.run(); + } +}); + +export class KernelStatus extends Disposable implements IWorkbenchContribution { + private _editorDisposable = new DisposableStore(); + private readonly kernelInfoElement = this._register(new MutableDisposable()); + constructor( + @IEditorService private readonly _editorService: IEditorService, + @INotebookService private readonly _notebookService: INotebookService, + @IStatusbarService private readonly _statusbarService: IStatusbarService, + ) { + super(); + this.registerListeners(); + } + + registerListeners() { + this._register(this._editorService.onDidActiveEditorChange(() => this.updateStatusbar())); + this._register(this._notebookService.onDidChangeActiveEditor(() => this.updateStatusbar())); + this._register(this._notebookService.onDidChangeKernels(() => this.updateStatusbar())); + } + + updateStatusbar() { + this._editorDisposable.clear(); + + const activeEditor = getActiveNotebookEditor(this._editorService); + + if (activeEditor) { + this._editorDisposable.add(activeEditor.onDidChangeKernel(() => { + if (activeEditor.multipleKernelsAvailable) { + this.showKernelStatus(activeEditor.activeKernel); + } else { + this.kernelInfoElement.clear(); + } + })); + + this._editorDisposable.add(activeEditor.onDidChangeAvailableKernels(() => { + if (activeEditor.multipleKernelsAvailable) { + this.showKernelStatus(activeEditor.activeKernel); + } else { + this.kernelInfoElement.clear(); + } + })); + } + + if (activeEditor && activeEditor.multipleKernelsAvailable) { + this.showKernelStatus(activeEditor.activeKernel); + } else { + this.kernelInfoElement.clear(); + } + } + + showKernelStatus(kernel: INotebookKernelInfo | INotebookKernelInfo2 | undefined) { + this.kernelInfoElement.value = this._statusbarService.addEntry({ + text: kernel ? kernel.label : 'Choose Kernel', + ariaLabel: kernel ? kernel.label : 'Choose Kernel', + tooltip: nls.localize('chooseActiveKernel', "Choose kernel for current notebook"), + command: 'notebook.selectKernel', + }, 'notebook.selectKernel', nls.localize('notebook.selectKernel', "Choose kernel for current notebook"), StatusbarAlignment.RIGHT, 100); + } +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(KernelStatus, LifecyclePhase.Ready); + diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/toc/tocProvider.ts b/src/vs/workbench/contrib/notebook/browser/contrib/toc/tocProvider.ts index d7305d9e53f..48599557f3a 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/toc/tocProvider.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/toc/tocProvider.ts @@ -6,17 +6,32 @@ import { TableOfContentsProviderRegistry, ITableOfContentsProvider, ITableOfContentsEntry } from 'vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess'; import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEditor'; import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; - +import { Codicon } from 'vs/base/common/codicons'; +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; TableOfContentsProviderRegistry.register(NotebookEditor.ID, new class implements ITableOfContentsProvider { - async provideTableOfContents(editor: NotebookEditor) { + async provideTableOfContents(editor: NotebookEditor, context: { disposables: DisposableStore }) { if (!editor.viewModel) { return undefined; } // return an entry per markdown header - const editorWidget = editor.getControl(); + const notebookWidget = editor.getControl(); + if (!notebookWidget) { + return undefined; + } + + // restore initial view state when no item was picked + let didPickOne = false; + const viewState = notebookWidget.getEditorViewState(); + context.disposables.add(toDisposable(() => { + if (!didPickOne) { + notebookWidget.restoreListViewState(viewState); + } + })); + + let lastDecorationId: string[] = []; const result: ITableOfContentsEntry[] = []; - for (let cell of editor.viewModel.viewCells) { + for (const cell of editor.viewModel.viewCells) { const content = cell.getText(); const regexp = cell.cellKind === CellKind.Markdown ? /^[ \t]*(\#+)(.+)$/gm // md: header @@ -26,16 +41,32 @@ TableOfContentsProviderRegistry.register(NotebookEditor.ID, new class implements if (matches && matches.length) { for (let j = 0; j < matches.length; j++) { result.push({ + icon: cell.cellKind === CellKind.Markdown ? Codicon.markdown : Codicon.code, label: matches[j].replace(/^[ \t]*(\#+)/, ''), - reveal: () => { - editorWidget.revealInCenterIfOutsideViewport(cell); - editorWidget.selectElement(cell); - // editor.focusNotebookCell(cell, 'container'); + pick() { + didPickOne = true; + notebookWidget.revealInCenterIfOutsideViewport(cell); + notebookWidget.selectElement(cell); + notebookWidget.focusNotebookCell(cell, cell.cellKind === CellKind.Markdown ? 'container' : 'editor'); + lastDecorationId = notebookWidget.deltaCellDecorations(lastDecorationId, []); + }, + preview() { + notebookWidget.revealInCenterIfOutsideViewport(cell); + notebookWidget.selectElement(cell); + lastDecorationId = notebookWidget.deltaCellDecorations(lastDecorationId, [{ + handle: cell.handle, + options: { className: 'nb-symbolHighlight', outputClassName: 'nb-symbolHighlight' } + }]); } }); } } } + + context.disposables.add(toDisposable(() => { + notebookWidget.deltaCellDecorations(lastDecorationId, []); + })); + return result; } }); diff --git a/src/vs/workbench/contrib/notebook/browser/extensionPoint.ts b/src/vs/workbench/contrib/notebook/browser/extensionPoint.ts index 7dbcd235058..c7b5c94f98d 100644 --- a/src/vs/workbench/contrib/notebook/browser/extensionPoint.ts +++ b/src/vs/workbench/contrib/notebook/browser/extensionPoint.ts @@ -7,33 +7,36 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; import * as nls from 'vs/nls'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { NotebookSelector } from 'vs/workbench/contrib/notebook/common/notebookProvider'; +import { NotebookEditorPriority } from 'vs/workbench/contrib/notebook/common/notebookCommon'; namespace NotebookEditorContribution { export const viewType = 'viewType'; export const displayName = 'displayName'; export const selector = 'selector'; + export const priority = 'priority'; } -interface INotebookEditorContribution { +export interface INotebookEditorContribution { readonly [NotebookEditorContribution.viewType]: string; readonly [NotebookEditorContribution.displayName]: string; readonly [NotebookEditorContribution.selector]?: readonly NotebookSelector[]; + readonly [NotebookEditorContribution.priority]?: string; } namespace NotebookRendererContribution { export const viewType = 'viewType'; export const displayName = 'displayName'; export const mimeTypes = 'mimeTypes'; + export const entrypoint = 'entrypoint'; } -interface INotebookRendererContribution { +export interface INotebookRendererContribution { readonly [NotebookRendererContribution.viewType]: string; readonly [NotebookRendererContribution.displayName]: string; readonly [NotebookRendererContribution.mimeTypes]?: readonly string[]; + readonly [NotebookRendererContribution.entrypoint]?: string; } - - const notebookProviderContribution: IJSONSchema = { description: nls.localize('contributes.notebook.provider', 'Contributes notebook document provider.'), type: 'array', @@ -70,6 +73,19 @@ const notebookProviderContribution: IJSONSchema = { } } } + }, + [NotebookEditorContribution.priority]: { + type: 'string', + markdownDeprecationMessage: nls.localize('contributes.priority', 'Controls if the custom editor is enabled automatically when the user opens a file. This may be overridden by users using the `workbench.editorAssociations` setting.'), + enum: [ + NotebookEditorPriority.default, + NotebookEditorPriority.option, + ], + markdownEnumDescriptions: [ + nls.localize('contributes.priority.default', 'The editor is automatically used when the user opens a resource, provided that no other default custom editors are registered for that resource.'), + nls.localize('contributes.priority.option', 'The editor is not automatically used when the user opens a resource, but a user can switch to the editor using the `Reopen With` command.'), + ], + default: 'default' } } } @@ -101,7 +117,11 @@ const notebookRendererContribution: IJSONSchema = { items: { type: 'string' } - } + }, + [NotebookRendererContribution.entrypoint]: { + type: 'string', + description: nls.localize('contributes.notebook.renderer.entrypoint', 'File to load in the webview to render the extension.'), + }, } } }; diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index a9584f7cdf1..21682bece5b 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -11,46 +11,51 @@ position: relative; } -.cell.markdown { +.monaco-workbench .cell.markdown { user-select: text; -webkit-user-select: text; white-space: initial; } -.notebookOverlay .simple-fr-find-part-wrapper.visible { +.monaco-workbench .cell.markdown p.emptyMarkdownPlaceholder { + font-style: italic; + opacity: 0.6; +} + +.monaco-workbench .notebookOverlay .simple-fr-find-part-wrapper.visible { z-index: 100; } -.notebookOverlay .cell-list-container .overflowingContentWidgets > div { +.monaco-workbench .notebookOverlay .cell-list-container .overflowingContentWidgets > div { z-index: 600 !important; /* @rebornix: larger than the editor title bar */ } -.notebookOverlay .cell-list-container .monaco-list-rows { +.monaco-workbench .notebookOverlay .cell-list-container .monaco-list-rows { min-height: 100%; overflow: visible !important; } -.notebookOverlay .cell-list-container { +.monaco-workbench .notebookOverlay .cell-list-container { position: relative; } -.notebookOverlay.global-drag-active .webview { +.monaco-workbench .notebookOverlay.global-drag-active .webview { pointer-events: none; } -.notebookOverlay .cell-list-container .webview-cover { +.monaco-workbench .notebookOverlay .cell-list-container .webview-cover { position: absolute; top: 0; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row { cursor: default; overflow: visible !important; width: 100%; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image { position: absolute; top: -500px; z-index: 1000; @@ -58,108 +63,128 @@ padding-top: 8px; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image .notebook-cell-focus-indicator { - top: 8px !important; +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image .cell-focus-indicator { + display: none; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image.markdown-cell-row .notebook-cell-focus-indicator { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image.markdown-cell-row .cell-focus-indicator { bottom: 8px; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image .output { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image .output { display: none !important; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image > .monaco-toolbar { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image > .monaco-toolbar { display: none; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image .cell-statusbar-container { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image .cell-statusbar-container { display: none; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image .cell-editor-part { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image .cell-editor-part { width: calc(100% - 32px); /* minus left gutter */ } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image .cell-editor-container > div > div { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image .cell-editor-container > div > div { /* Rendered code content - show a single unwrapped line */ height: 20px; overflow: hidden; white-space: pre-wrap; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image.markdown-cell-row .cell.markdown { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image.markdown-cell-row .cell.markdown { white-space: nowrap; overflow: hidden; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell { display: flex; } -.notebookOverlay .notebook-content-widgets { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:not(.selected) .monaco-editor .lines-content .selected-text, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:not(.selected) .monaco-editor .lines-content .selectionHighlight { + opacity: 0.33; +} + +.monaco-workbench .notebookOverlay .notebook-content-widgets { position: absolute; top: 0; left: 0; width: 100%; } -.notebookOverlay .output { - padding-left: 8px; - padding-right: 8px; +.monaco-workbench .notebookOverlay .output { + position: absolute; + height: 0px; user-select: text; transform: translate3d(0px, 0px, 0px); cursor: auto; box-sizing: border-box; } -.notebookOverlay .output p { +.monaco-workbench .notebookOverlay .output p { white-space: initial; overflow-x: auto; margin: 0px; } -.notebookOverlay .output > div.foreground { +.monaco-workbench .notebookOverlay .output > div.foreground { + width: 100%; + min-height: 24px; + box-sizing: border-box; +} + +.monaco-workbench .notebookOverlay .output > div.foreground > .output-inner-container { + width: 100%; padding: 8px; box-sizing: border-box; } -.notebookOverlay .cell-drag-image .output .multi-mimetype-output { +.monaco-workbench .notebookOverlay .output > div.foreground .output-stream, +.monaco-workbench .notebookOverlay .output > div.foreground .output-plaintext { + font-family: var(--monaco-monospace-font); + white-space: pre-wrap; + word-wrap: break-word; +} + +.monaco-workbench .notebookOverlay .cell-drag-image .output .multi-mimetype-output { display: none; } -.notebookOverlay .output .multi-mimetype-output { +.monaco-workbench .notebookOverlay .output .multi-mimetype-output { position: absolute; top: 4px; - left: -32px; + left: -26px; width: 16px; height: 16px; cursor: pointer; + padding: 4px; } -.notebookOverlay .output .error_message { +.monaco-workbench .notebookOverlay .output .error_message { color: red; } -.notebookOverlay .output .error > div { +.monaco-workbench .notebookOverlay .output .error > div { white-space: normal; } -.notebookOverlay .output .error pre.traceback { +.monaco-workbench .notebookOverlay .output .error pre.traceback { margin: 8px 0; } -.notebookOverlay .output .error .traceback > span { +.monaco-workbench .notebookOverlay .output .error .traceback > span { display: block; } -.notebookOverlay .output .display img { +.monaco-workbench .notebookOverlay .output .display img { max-width: 100%; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu { position: absolute; left: 0; top: 28px; @@ -170,40 +195,97 @@ } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu.mouseover, -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .menu, -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-output-hover .menu { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu.mouseover, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .menu, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-output-hover .menu { visibility: visible; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row, -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover, -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-output-hover { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-output-hover { outline: none !important; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected, -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused { outline: none !important; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu.mouseover, -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu:hover { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-collapsed-part { + box-sizing: border-box; +} + +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-collapsed-part .codicon { + margin-top: 4px; + position: relative; + left: -23px; cursor: pointer; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .monaco-toolbar { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.collapsed .notebook-folding-indicator, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.collapsed > .monaco-toolbar { + display: none; +} + +/* top and bottom borders on cells */ +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-top:before, +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before, +.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:before, +.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:after { + content: ""; + position: absolute; + width: 100%; + height: 1px; +} + +/* top border */ +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-top:before, +.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:before { + border-top: 1px solid transparent; +} + +.monaco-workbench.hc-black .notebookOverlay .monaco-list .monaco-list-row.focused.cell-editor-focus .cell-focus-indicator-top:before, +.monaco-workbench.hc-black .notebookOverlay .monaco-list .markdown-cell-row.focused.cell-editor-focus:before { + border-top-style: dashed; +} + +/* bottom border */ +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before, +.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:after { + border-bottom: 1px solid transparent; +} + +.monaco-workbench.hc-black .notebookOverlay .monaco-list .monaco-list-row.focused.cell-editor-focus .cell-focus-indicator-bottom:before, +.monaco-workbench.hc-black .notebookOverlay .monaco-list .markdown-cell-row.focused.cell-editor-focus:after { + border-bottom-style: dashed; +} + +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-top:before, +.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:before { + top: 0; +} +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before, +.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:after { + bottom: 0px; +} + +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu.mouseover, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu:hover { + cursor: pointer; +} + +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .monaco-toolbar { visibility: hidden; display: inline-block; position: absolute; height: 26px; - right: 36px; - top: -14px; + right: 44px; + top: -12px; /* this lines up the bottom toolbar border with the current line when on line 01 */ z-index: 30; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .monaco-toolbar .action-item { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .monaco-toolbar .action-item { width: 24px; height: 24px; display: flex; @@ -211,55 +293,52 @@ margin: 1px 2px; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .monaco-toolbar .action-item .action-label { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .monaco-toolbar .action-item .action-label { display: flex; align-items: center; margin: auto; } -.notebookOverlay .cell-statusbar-container { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .monaco-toolbar .action-item .monaco-dropdown { + width: 100%; +} + +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .monaco-toolbar .action-item .monaco-dropdown .dropdown-label { + display: flex; +} + +.monaco-workbench .notebookOverlay .cell-statusbar-container { height: 21px; font-size: 12px; display: flex; position: relative; } -.notebookOverlay .cell-statusbar-container .cell-status-left { +.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-status-left { display: flex; flex-grow: 1; } -.notebookOverlay .cell-statusbar-container .cell-status-right { +.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-status-right { padding-right: 12px; } -.notebookOverlay .cell-statusbar-container .cell-language-picker { +.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-language-picker { padding: 0px 6px; cursor: pointer; } -.notebookOverlay .cell-statusbar-container .cell-language-picker:hover { - background-color: rgba(255, 255, 255, 0.6); +.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-duration { + margin-right: 8px; } -.notebookOverlay .cell-statusbar-container .cell-language-picker:hover { - background-color: rgba(255, 255, 255, 0.9); -} - -.vs-dark .notebookOverlay .cell-statusbar-container .cell-language-picker:hover { - background-color: rgba(255, 255, 255, 0.15); -} - -.vs-dark .notebookOverlay .cell-statusbar-container .cell-language-picker:active { - background-color: rgba(255, 255, 255, 0.2); -} - -.notebookOverlay .cell-statusbar-container .cell-status-message { +.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-duration, +.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-status-message { display: flex; align-items: center; } -.notebookOverlay .cell-statusbar-container .cell-run-status { +.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-status { height: 100%; display: flex; align-items: center; @@ -268,114 +347,124 @@ margin-right: 2px; } -.notebookOverlay .cell-statusbar-container .codicon { +.monaco-workbench .notebookOverlay .cell-statusbar-container .codicon { font-size: 14px; } -.notebookOverlay .cell-statusbar-container .cell-run-status .codicon-check { - color: #89D185; -} - -.vs .notebookOverlay .cell-statusbar-container .cell-run-status .codicon-check { - color: #388A34; -} - -.notebookOverlay .cell-status-placeholder { +.monaco-workbench .notebookOverlay .cell-status-placeholder { position: absolute; left: 18px; - color: #ccc9; display: flex; align-items: center; bottom: 0px; top: 0px; } - -.vs .notebookOverlay .cell-status-placeholder { - color: #616161e6; -} - -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container { position: relative; height: 22px; flex-shrink: 0; + top: 9px; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .monaco-toolbar { - margin-top: 8px; +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .monaco-toolbar { visibility: hidden; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .monaco-toolbar .codicon { - margin-right: 8px; +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .monaco-toolbar .codicon { + margin: 0; + padding-right: 4px; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .cell.runnable .run-button-container .monaco-toolbar, -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused .cell.runnable .run-button-container .monaco-toolbar, -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-output-hover .cell.runnable .run-button-container .monaco-toolbar { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .monaco-toolbar .actions-container { + justify-content: center; +} + +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .cell.runnable .run-button-container .monaco-toolbar, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused .cell.runnable .run-button-container .monaco-toolbar, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-output-hover .cell.runnable .run-button-container .monaco-toolbar { visibility: visible; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .execution-count-label { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell .run-button-container .execution-count-label { position: absolute; - top: 2px; + top: -2px; font-size: 10px; - font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; + font-family: var(--monaco-monospace-font); visibility: visible; white-space: pre; width: 100%; text-align: center; - padding-right: 2px; + padding-right: 8px; box-sizing: border-box; opacity: .6; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .cell .run-button-container .execution-count-label, -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-output-hover .cell .run-button-container .execution-count-label, -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused .cell .run-button-container .execution-count-label { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .cell .run-button-container .execution-count-label, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-output-hover .cell .run-button-container .execution-count-label, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused .cell .run-button-container .execution-count-label { visibility: hidden; } -.notebookOverlay .cell .cell-editor-part { +.monaco-workbench .notebookOverlay .cell .cell-editor-part { position: relative; } -.notebookOverlay .cell .monaco-progress-container { +.monaco-workbench .notebookOverlay .cell .monaco-progress-container { top: -5px; + + position: absolute; + left: 0; + z-index: 5; + height: 2px; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-has-toolbar-actions.focused > .monaco-toolbar, -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-has-toolbar-actions.cell-output-hover > .monaco-toolbar, -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-has-toolbar-actions:hover > .monaco-toolbar { +.monaco-workbench .notebookOverlay .cell .monaco-progress-container .progress-bit { + height: 2px; +} + +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-has-toolbar-actions.focused > .monaco-toolbar, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-has-toolbar-actions.cell-output-hover > .monaco-toolbar, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-has-toolbar-actions:hover > .monaco-toolbar { visibility: visible; } -.notebookOverlay > .cell-list-container > .monaco-list:not(.element-focused):focus:before { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list:not(.element-focused):focus:before { outline: none !important; } - -.notebookOverlay .monaco-list .monaco-list-row .notebook-cell-focus-indicator { +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator { display: block; content: ' '; position: absolute; - width: 32px; box-sizing: border-box; - border-left-width: 2px; - border-left-style: solid; - top: 22px; - bottom: 36px; - visibility: hidden; + top: 0px; +} + +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-side { + /** Overidden for code cells */ + top: 0px; + bottom: 0px; +} + +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-top, +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-bottom { + width: 100%; +} + +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-right { + right: 0px; +} + +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator:hover { + cursor: grab; +} + +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator .codicon:hover { cursor: pointer; } -.notebookOverlay .monaco-list .monaco-list-row:hover .notebook-cell-focus-indicator, -.notebookOverlay .monaco-list .monaco-list-row.cell-output-hover .notebook-cell-focus-indicator, -.notebookOverlay .monaco-list .monaco-list-row.focused .notebook-cell-focus-indicator { - visibility: visible; -} - -.notebookOverlay .monaco-list-row.cell-editor-focus .cell-editor-part:before { +.monaco-workbench .notebookOverlay .monaco-list-row .cell-editor-part:before { z-index: 20; content: ""; right: 0px; @@ -388,72 +477,77 @@ pointer-events: none; } -.notebookOverlay .monaco-list .monaco-list-row .cell-insertion-indicator-top { +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-insertion-indicator-top { top: -15px; } -.notebookOverlay .monaco-list .monaco-list-row .cell-insertion-indicator-bottom { - bottom: 13px; -} - -.notebookOverlay .monaco-list .monaco-list-row.cell-dragover-top .cell-insertion-indicator-top, -.notebookOverlay .monaco-list .monaco-list-row.cell-dragover-bottom .cell-insertion-indicator-bottom { - opacity: 1; -} - -.notebookOverlay .monaco-list .monaco-list-row .cell-insertion-indicator { - opacity: 0; - transition: opacity 0.2s ease-in-out; +.monaco-workbench .notebookOverlay > .cell-list-container > .cell-list-insertion-indicator { position: absolute; height: 2px; + left: 0px; + right: 0px; + opacity: 0; + z-index: 10; } -.notebookOverlay .monaco-list .monaco-list-row.cell-dragging { - opacity: 0.5; +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list .monaco-list-row.cell-dragging { + opacity: 0.5 !important; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container { position: absolute; display: flex; + justify-content: center; + z-index: 30; /* over the focus outline on the editor */ + width: 100%; opacity: 0; transition: opacity 0.2s ease-in-out; cursor: auto; padding: 0; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image .cell-bottom-toolbar-container { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image .cell-bottom-toolbar-container { display: none; } -.notebookOverlay.notebook-editor-editable > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container:focus-within, -.notebookOverlay.notebook-editor-editable > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container:hover { +.monaco-workbench .notebookOverlay.notebook-editor-editable > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container:focus-within, +.monaco-workbench .notebookOverlay.notebook-editor-editable > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container:hover { opacity: 1; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .seperator { - height: 1px; - flex-grow: 1; - align-self: center; +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .monaco-toolbar { + margin: 0px 8px; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .seperator-short { - height: 1px; - width: 16px; - align-self: center; -} - -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .button { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .monaco-toolbar .action-item { display: flex; - margin: 0 8px; - padding: 0 8px; - align-self: center; - align-items: center; - white-space: pre; - cursor: pointer; - font-size: 12px; } -.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container span.codicon { +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .monaco-toolbar .action-item.active { + transform: none; +} + +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .monaco-toolbar .action-label { + font-size: 12px; + margin: 0px; + display: inline-flex; + padding: 0px 4px; +} + +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .monaco-toolbar .action-label .codicon { + margin-right: 3px; +} + +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .monaco-action-bar { + display: flex; + align-items: center; +} + +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .action-item:first-child { + margin-right: 16px; +} + +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container span.codicon { text-align: center; font-size: 14px; color: inherit; @@ -462,34 +556,34 @@ /* markdown */ -.notebookOverlay .cell.markdown img { +.monaco-workbench .notebookOverlay .cell.markdown img { max-width: 100%; max-height: 100%; } -.notebookOverlay .cell.markdown a { +.monaco-workbench .notebookOverlay .cell.markdown a { text-decoration: none; } -.notebookOverlay .cell.markdown a:hover { +.monaco-workbench .notebookOverlay .cell.markdown a:hover { text-decoration: underline; } -.notebookOverlay .cell.markdown a:focus, -.notebookOverlay .cell.markdown input:focus, -.notebookOverlay .cell.markdown select:focus, -.notebookOverlay .cell.markdown textarea:focus { +.monaco-workbench .notebookOverlay .cell.markdown a:focus, +.monaco-workbench .notebookOverlay .cell.markdown input:focus, +.monaco-workbench .notebookOverlay .cell.markdown select:focus, +.monaco-workbench .notebookOverlay .cell.markdown textarea:focus { outline: 1px solid -webkit-focus-ring-color; outline-offset: -1px; } -.notebookOverlay .cell.markdown hr { +.monaco-workbench .notebookOverlay .cell.markdown hr { border: 0; height: 2px; border-bottom: 2px solid; } -.notebookOverlay .cell.markdown h1 { +.monaco-workbench .notebookOverlay .cell.markdown h1 { padding-bottom: 0.3em; line-height: 1.2; border-bottom-width: 1px; @@ -497,156 +591,159 @@ border-color: rgba(255, 255, 255, 0.18); } -.vs .notebookOverlay .cell.markdown h1 { +.monaco-workbench.vs .monaco-workbench .notebookOverlay .cell.markdown h1 { border-color: rgba(0, 0, 0, 0.18); } -.notebookOverlay .cell.markdown h1, -.notebookOverlay .cell.markdown h2, -.notebookOverlay .cell.markdown h3 { +.monaco-workbench .notebookOverlay .cell.markdown h1, +.monaco-workbench .notebookOverlay .cell.markdown h2, +.monaco-workbench .notebookOverlay .cell.markdown h3 { font-weight: normal; } -.notebookOverlay .cell.markdown div { +.monaco-workbench .notebookOverlay .cell.markdown div { width: 100%; } /* Adjust margin of first item in markdown cell */ -.notebookOverlay .cell.markdown div *:first-child { - margin-top: 4px; +.monaco-workbench .notebookOverlay .cell.markdown div *:first-child { + margin-top: 0px; } /* h1 tags don't need top margin */ -.notebookOverlay .cell.markdown div h1:first-child { +.monaco-workbench .notebookOverlay .cell.markdown div h1:first-child { margin-top: 0; } /* Removes bottom margin when only one item exists in markdown cell */ -.notebookOverlay .cell.markdown div *:only-child, -.notebookOverlay .cell.markdown div *:last-child { +.monaco-workbench .notebookOverlay .cell.markdown div *:only-child, +.monaco-workbench .notebookOverlay .cell.markdown div *:last-child { margin-bottom: 0; + padding-bottom: 0; } /* makes all markdown cells consistent */ -.notebookOverlay .cell.markdown div { - min-height: 32px; +.monaco-workbench .notebookOverlay .cell.markdown div { + min-height: 24px; } -.notebookOverlay .cell.markdown table { +.monaco-workbench .notebookOverlay .cell.markdown table { border-collapse: collapse; border-spacing: 0; } -.notebookOverlay .cell.markdown table th, -.notebookOverlay .cell.markdown table td { +.monaco-workbench .notebookOverlay .cell.markdown table th, +.monaco-workbench .notebookOverlay .cell.markdown table td { border: 1px solid; } -.notebookOverlay .cell.markdown table > thead > tr > th { +.monaco-workbench .notebookOverlay .cell.markdown table > thead > tr > th { text-align: left; border-bottom: 1px solid; } -.notebookOverlay .cell.markdown table > thead > tr > th, -.notebookOverlay .cell.markdown table > thead > tr > td, -.notebookOverlay .cell.markdown table > tbody > tr > th, -.notebookOverlay .cell.markdown table > tbody > tr > td { +.monaco-workbench .notebookOverlay .cell.markdown table > thead > tr > th, +.monaco-workbench .notebookOverlay .cell.markdown table > thead > tr > td, +.monaco-workbench .notebookOverlay .cell.markdown table > tbody > tr > th, +.monaco-workbench .notebookOverlay .cell.markdown table > tbody > tr > td { padding: 5px 10px; } -.notebookOverlay .cell.markdown table > tbody > tr + tr > td { +.monaco-workbench .notebookOverlay .cell.markdown table > tbody > tr + tr > td { border-top: 1px solid; } -.notebookOverlay .cell.markdown blockquote { +.monaco-workbench .notebookOverlay .cell.markdown blockquote { margin: 0 7px 0 5px; padding: 0 16px 0 10px; border-left-width: 5px; border-left-style: solid; } -.notebookOverlay .cell.markdown code { - font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; +.monaco-workbench .notebookOverlay .cell.markdown code, +.monaco-workbench .notebookOverlay .cell.markdown .code { + font-family: var(--monaco-monospace-font); font-size: 1em; line-height: 1.357em; } -.notebookOverlay .cell.markdown body.wordWrap pre { +.monaco-workbench .notebookOverlay .cell.markdown .code { white-space: pre-wrap; } -.notebookOverlay .cell.markdown pre:not(.hljs), -.notebookOverlay .cell.markdown pre.hljs code > div { - padding: 16px; - border-radius: 3px; - overflow: auto; -} - -.notebookOverlay .cell.markdown pre code { - color: var(--vscode-editor-foreground); - tab-size: 4; -} - -.notebookOverlay .cell.markdown .latex-block { +.monaco-workbench .notebookOverlay .cell.markdown .latex-block { display: block; } -.notebookOverlay .cell.markdown .latex { +.monaco-workbench .notebookOverlay .cell.markdown .latex { vertical-align: middle; display: inline-block; } -.notebookOverlay .cell.markdown .latex img, -.notebookOverlay .cell.markdown .latex-block img { +.monaco-workbench .notebookOverlay .cell.markdown .latex img, +.monaco-workbench .notebookOverlay .cell.markdown .latex-block img { filter: brightness(0) invert(0) } -.vs-dark .notebookOverlay .cell.markdown .latex img, -.vs-dark .notebookOverlay .cell.markdown .latex-block img { +.monaco-workbench.vs-dark .notebookOverlay .cell.markdown .latex img, +.monaco-workbench.vs-dark .notebookOverlay .cell.markdown .latex-block img { filter: brightness(0) invert(1) } -.notebookOverlay > .cell-list-container .notebook-folding-indicator { +.monaco-workbench .notebookOverlay > .cell-list-container .notebook-folding-indicator { position: absolute; - top: 8px; - left: 6px; - cursor: pointer; + top: 0; + left: 0; + right: 0; + height: 100%; +} + +.monaco-workbench .notebookOverlay > .cell-list-container .notebook-folding-indicator .codicon { + visibility: visible; + padding: 8px 0 0 10px; } /** Theming */ -/* .notebookOverlay .cell.markdown pre { +/* .monaco-workbench .notebookOverlay .cell.markdown pre { background-color: rgba(220, 220, 220, 0.4); } -.vs-dark .notebookOverlay .cell.markdown pre { +.monaco-workbench.vs-dark .monaco-workbench .notebookOverlay .cell.markdown pre { background-color: rgba(10, 10, 10, 0.4); } -.hc-black .notebookOverlay .cell.markdown pre { +.monaco-workbench.hc-black .monaco-workbench .notebookOverlay .cell.markdown pre { background-color: rgb(0, 0, 0); } -.hc-black .notebookOverlay .cell.markdown h1 { +.monaco-workbench.hc-black .monaco-workbench .notebookOverlay .cell.markdown h1 { border-color: rgb(0, 0, 0); } -.notebookOverlay .cell.markdown table > thead > tr > th { +.monaco-workbench .notebookOverlay .cell.markdown table > thead > tr > th { border-color: rgba(0, 0, 0, 0.18); } -.vs-dark .notebookOverlay .cell.markdown table > thead > tr > th { +.monaco-workbench.vs-dark .monaco-workbench .notebookOverlay .cell.markdown table > thead > tr > th { border-color: rgba(255, 255, 255, 0.18); } -.notebookOverlay .cell.markdown h1, -.notebookOverlay .cell.markdown hr, -.notebookOverlay .cell.markdown table > tbody > tr > td { +.monaco-workbench .notebookOverlay .cell.markdown h1, +.monaco-workbench .notebookOverlay .cell.markdown hr, +.monaco-workbench .notebookOverlay .cell.markdown table > tbody > tr > td { border-color: rgba(0, 0, 0, 0.18); } -.vs-dark .notebookOverlay .cell.markdown h1, -.vs-dark .notebookOverlay .cell.markdown hr, -.vs-dark .notebookOverlay .cell.markdown table > tbody > tr > td { +.monaco-workbench.vs-dark .monaco-workbench .notebookOverlay .cell.markdown h1, +.monaco-workbench.vs-dark .monaco-workbench .notebookOverlay .cell.markdown hr, +.monaco-workbench.vs-dark .monaco-workbench .notebookOverlay .cell.markdown table > tbody > tr > td { border-color: rgba(255, 255, 255, 0.18); } */ + +.monaco-action-bar .action-item.verticalSeparator { + width: 1px !important; + height: 16px !important; + margin: 5px 4px !important; + cursor: none; +} diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 26916bceee7..136cfd3b622 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -3,10 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable } from 'vs/base/common/lifecycle'; -import { ResourceMap } from 'vs/base/common/map'; +import { coalesce, distinct } from 'vs/base/common/arrays'; +import { Schemas } from 'vs/base/common/network'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { parse } from 'vs/base/common/marshalling'; -import { basename, isEqual } from 'vs/base/common/resources'; +import { isEqual } from 'vs/base/common/resources'; import { assertType } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { ITextModel, ITextBufferFactory, DefaultEndOfLine, ITextBuffer } from 'vs/editor/common/model'; @@ -15,7 +16,7 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService'; import * as nls from 'vs/nls'; import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; -import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { IEditorOptions, ITextEditorOptions, IResourceEditorInput } from 'vs/platform/editor/common/editor'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -24,18 +25,22 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { EditorInput, Extensions as EditorInputExtensions, IEditorInput, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEditor'; import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { NotebookService } from 'vs/workbench/contrib/notebook/browser/notebookServiceImpl'; -import { CellKind, CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellUri, NotebookDocumentBackupData, NotebookEditorPriority } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService, IOpenEditorOverride } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { CustomEditorsAssociations, customEditorsAssociationsSettingId } from 'vs/workbench/services/editor/common/editorAssociationsSetting'; -import { coalesce, distinct } from 'vs/base/common/arrays'; +import { CustomEditorsAssociations, customEditorsAssociationsSettingId } from 'vs/workbench/services/editor/common/editorOpenWith'; import { CustomEditorInfo } from 'vs/workbench/contrib/customEditor/common/customEditor'; +import { NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; +import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; +import { INotebookEditorModelResolverService, NotebookModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; // Editor Contribution @@ -45,13 +50,14 @@ import 'vs/workbench/contrib/notebook/browser/contrib/fold/folding'; import 'vs/workbench/contrib/notebook/browser/contrib/format/formatting'; import 'vs/workbench/contrib/notebook/browser/contrib/toc/tocProvider'; import 'vs/workbench/contrib/notebook/browser/contrib/marker/markerProvider'; +import 'vs/workbench/contrib/notebook/browser/contrib/status/editorStatus'; // Output renderers registration import 'vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform'; import 'vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform'; import 'vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform'; -import { NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; +import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; /*--------------------------------------------------------------------------------------------- */ @@ -66,51 +72,97 @@ Registry.as(EditorExtensions.Editors).registerEditor( ] ); +class NotebookEditorFactory implements IEditorInputFactory { + canSerialize(): boolean { + return true; + } + serialize(input: EditorInput): string { + assertType(input instanceof NotebookEditorInput); + return JSON.stringify({ + resource: input.resource, + name: input.name, + viewType: input.viewType, + }); + } + deserialize(instantiationService: IInstantiationService, raw: string) { + type Data = { resource: URI, name: string, viewType: string, group: number }; + const data = parse(raw); + if (!data) { + return undefined; + } + const { resource, name, viewType } = data; + if (!data || !URI.isUri(resource) || typeof name !== 'string' || typeof viewType !== 'string') { + return undefined; + } + + const input = NotebookEditorInput.create(instantiationService, resource, name, viewType); + return input; + } + + static async createCustomEditorInput(resource: URI, instantiationService: IInstantiationService): Promise { + return instantiationService.invokeFunction(async accessor => { + const backupFileService = accessor.get(IBackupFileService); + + const backup = await backupFileService.resolve(resource); + if (!backup?.meta) { + throw new Error(`No backup found for Notebook editor: ${resource}`); + } + + const input = NotebookEditorInput.create(instantiationService, resource, backup.meta.name, backup.meta.viewType, { startDirty: true }); + return input; + }); + } + + static canResolveBackup(editorInput: IEditorInput, backupResource: URI): boolean { + if (editorInput instanceof NotebookEditorInput) { + if (isEqual(editorInput.resource.with({ scheme: Schemas.vscodeNotebook }), backupResource)) { + return true; + } + } + + return false; + } +} + Registry.as(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory( NotebookEditorInput.ID, - class implements IEditorInputFactory { - canSerialize(): boolean { - return true; - } - serialize(input: EditorInput): string { - assertType(input instanceof NotebookEditorInput); - return JSON.stringify({ - resource: input.resource, - name: input.name, - viewType: input.viewType, - }); - } - deserialize(instantiationService: IInstantiationService, raw: string) { - type Data = { resource: URI, name: string, viewType: string }; - const data = parse(raw); - if (!data) { - return undefined; - } - const { resource, name, viewType } = data; - if (!data || !URI.isUri(resource) || typeof name !== 'string' || typeof viewType !== 'string') { - return undefined; - } - return NotebookEditorInput.getOrCreate(instantiationService, resource, name, viewType); - } - } + NotebookEditorFactory +); + +Registry.as(EditorInputExtensions.EditorInputFactories).registerCustomEditorInputFactory( + Schemas.vscodeNotebook, + NotebookEditorFactory ); function getFirstNotebookInfo(notebookService: INotebookService, uri: URI): NotebookProviderInfo | undefined { return notebookService.getContributedNotebookProviders(uri)[0]; } -export class NotebookContribution implements IWorkbenchContribution { - private _resourceMapping = new ResourceMap(); +export class NotebookContribution extends Disposable implements IWorkbenchContribution { constructor( @IEditorService private readonly editorService: IEditorService, @INotebookService private readonly notebookService: INotebookService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IConfigurationService private readonly configurationService: IConfigurationService - + @IConfigurationService private readonly configurationService: IConfigurationService, + @IUndoRedoService undoRedoService: IUndoRedoService, ) { - this.editorService.overrideOpenEditor({ + super(); + + this._register(undoRedoService.registerUriComparisonKeyComputer(CellUri.scheme, { + getComparisonKey: (uri: URI): string => { + const data = CellUri.parse(uri); + if (!data) { + return uri.toString(); + } + + return data.notebook.toString(); + } + })); + + this._register(this.editorService.overrideOpenEditor({ getEditorOverrides: (resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined) => { + const currentEditorForResource = group?.editors.find(editor => isEqual(editor.resource, resource)); const associatedEditors = distinct([ @@ -127,15 +179,30 @@ export class NotebookContribution implements IWorkbenchContribution { }; }); }, - open: (editor, options, group, id) => this.onEditorOpening(editor, options, group, id) - }); - - this.editorService.onDidActiveEditorChange(() => { - if (this.editorService.activeEditor && this.editorService.activeEditor! instanceof NotebookEditorInput) { - let editorInput = this.editorService.activeEditor! as NotebookEditorInput; - this.notebookService.updateActiveNotebookDocument(editorInput.viewType!, editorInput.resource!); + open: (editor, options, group) => { + return this.onEditorOpening2(editor, options, group); } - }); + })); + + this._register(this.editorService.onDidVisibleEditorsChange(() => { + const visibleNotebookEditors = editorService.visibleEditorPanes + .filter(pane => (pane as unknown as { isNotebookEditor?: boolean }).isNotebookEditor) + .map(pane => pane.getControl() as INotebookEditor) + .filter(control => !!control) + .map(editor => editor.getId()); + + this.notebookService.updateVisibleNotebookEditor(visibleNotebookEditors); + })); + + this._register(this.editorService.onDidActiveEditorChange(() => { + const activeEditorPane = editorService.activeEditorPane as { isNotebookEditor?: boolean } | undefined; + const notebookEditor = activeEditorPane?.isNotebookEditor ? (editorService.activeEditorPane?.getControl() as INotebookEditor) : undefined; + if (notebookEditor) { + this.notebookService.updateActiveNotebookEditor(notebookEditor); + } else { + this.notebookService.updateActiveNotebookEditor(null); + } + })); } getUserAssociatedEditors(resource: URI) { @@ -157,65 +224,87 @@ export class NotebookContribution implements IWorkbenchContribution { return this.notebookService.getContributedNotebookProviders(resource); } - private onEditorOpening(originalInput: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup, id: string | undefined): IOpenEditorOverride | undefined { - let resource = originalInput.resource; - if (!resource) { + private onEditorOpening2(originalInput: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup): IOpenEditorOverride | undefined { + + let id = typeof options?.override === 'string' ? options.override : undefined; + if (id === undefined && originalInput.isUntitled()) { return undefined; } + if (!originalInput.resource) { + return undefined; + } + + if (originalInput instanceof NotebookEditorInput) { + return undefined; + } + + let notebookUri: URI = originalInput.resource; + let cellOptions: IResourceEditorInput | undefined; + + const data = CellUri.parse(originalInput.resource); + if (data) { + notebookUri = data.notebook; + cellOptions = { resource: originalInput.resource, options }; + } + + if (id === undefined && originalInput instanceof ResourceEditorInput) { + const exitingNotebookEditor = group.editors.find(editor => editor instanceof NotebookEditorInput && isEqual(editor.resource, notebookUri)); + id = exitingNotebookEditor?.viewType; + } + if (id === undefined) { - const existingEditors = group.editors.filter(editor => editor.resource && isEqual(editor.resource, resource) && !(editor instanceof NotebookEditorInput)); + const existingEditors = group.editors.filter(editor => editor.resource && isEqual(editor.resource, notebookUri) && !(editor instanceof NotebookEditorInput)); if (existingEditors.length) { return undefined; } - const userAssociatedEditors = this.getUserAssociatedEditors(resource); + const userAssociatedEditors = this.getUserAssociatedEditors(notebookUri); const notebookEditor = userAssociatedEditors.filter(association => this.notebookService.getContributedNotebookProvider(association.viewType)); if (userAssociatedEditors.length && !notebookEditor.length) { // user pick a non-notebook editor for this resource return undefined; } - } - if (this._resourceMapping.has(resource)) { - const input = this._resourceMapping.get(resource); + // user might pick a notebook editor - if (!input!.isDisposed()) { - return { override: this.editorService.openEditor(input!, new NotebookEditorOptions(options || {}).with({ ignoreOverrides: true }), group) }; + const associatedEditors = distinct([ + ...this.getUserAssociatedNotebookEditors(notebookUri), + ...(this.getContributedEditors(notebookUri).filter(editor => editor.priority === NotebookEditorPriority.default)) + ], editor => editor.id); + + if (!associatedEditors.length) { + // there is no notebook editor contribution which is enabled by default + return undefined; } } - let info: NotebookProviderInfo | undefined; - const data = CellUri.parse(resource); - if (data) { - const infos = this.getContributedEditors(data.notebook); + const infos = this.notebookService.getContributedNotebookProviders(notebookUri); + let info = infos.find(info => !id || info.id === id); - if (infos.length) { - const info = id === undefined ? infos[0] : (infos.find(info => info.id === id) || infos[0]); - // cell-uri -> open (container) notebook - const name = basename(data.notebook); - let input = this._resourceMapping.get(data.notebook); - if (!input || input.isDisposed()) { - input = NotebookEditorInput.getOrCreate(this.instantiationService, data.notebook, name, info.id); - this._resourceMapping.set(data.notebook, input); - } - return { override: this.editorService.openEditor(input, new NotebookEditorOptions({ ...options, forceReload: true, cellOptions: { resource, options } }), group) }; - } + if (!info && id !== undefined) { + info = this.notebookService.getContributedNotebookProvider(id); } - const infos = this.notebookService.getContributedNotebookProviders(resource); - info = id === undefined ? infos[0] : infos.find(info => info.id === id); - if (!info) { return undefined; } - const input = NotebookEditorInput.getOrCreate(this.instantiationService, resource, originalInput.getName(), info.id); - this._resourceMapping.set(resource, input); - return { override: this.editorService.openEditor(input, options, group) }; + /** + * Scenario: we are reopening a file editor input which is pinned, we should open in a new editor tab. + */ + let index = undefined; + if (group.activeEditor === originalInput && isEqual(originalInput.resource, notebookUri)) { + const originalEditorIndex = group.getIndexOfEditor(originalInput); + index = group.isPinned(originalInput) ? originalEditorIndex + 1 : originalEditorIndex; + } + + const notebookInput = NotebookEditorInput.create(this.instantiationService, notebookUri, originalInput.getName(), info.id); + const notebookOptions = new NotebookEditorOptions({ ...options, cellOptions, override: false, index }); + return { override: this.editorService.openEditor(notebookInput, notebookOptions, group) }; } } @@ -228,8 +317,9 @@ class CellContentProvider implements ITextModelContentProvider { @IModelService private readonly _modelService: IModelService, @IModeService private readonly _modeService: IModeService, @INotebookService private readonly _notebookService: INotebookService, + @INotebookEditorModelResolverService private readonly _notebookModelResolverService: INotebookEditorModelResolverService, ) { - this._registration = textModelService.registerTextModelContentProvider('vscode-notebook', this); + this._registration = textModelService.registerTextModelContentProvider(CellUri.scheme, this); } dispose(): void { @@ -251,12 +341,10 @@ class CellContentProvider implements ITextModelContentProvider { return null; } - const editorModel = await this._notebookService.modelManager.get(data.notebook); - if (!editorModel) { - return null; - } + const ref = await this._notebookModelResolverService.resolve(data.notebook, info.id); + let result: ITextModel | null = null; - for (let cell of editorModel.notebook.cells) { + for (const cell of ref.object.notebook.cells) { if (cell.uri.toString() === resource.toString()) { const bufferFactory: ITextBufferFactory = { create: (defaultEOL) => { @@ -269,15 +357,23 @@ class CellContentProvider implements ITextModelContentProvider { } }; const language = cell.cellKind === CellKind.Markdown ? this._modeService.create('markdown') : (cell.language ? this._modeService.create(cell.language) : this._modeService.createByFilepathOrFirstLine(resource, cell.textBuffer.getLineContent(1))); - return this._modelService.createModel( + result = this._modelService.createModel( bufferFactory, language, resource ); + break; } } - return null; + if (result) { + const once = result.onWillDispose(() => { + once.dispose(); + ref.dispose(); + }); + } + + return result; } } @@ -286,6 +382,7 @@ workbenchContributionsRegistry.registerWorkbenchContribution(NotebookContributio workbenchContributionsRegistry.registerWorkbenchContribution(CellContentProvider, LifecyclePhase.Starting); registerSingleton(INotebookService, NotebookService); +registerSingleton(INotebookEditorModelResolverService, NotebookModelResolverService, true); const configurationRegistry = Registry.as(Extensions.Configuration); configurationRegistry.registerConfiguration({ diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 23d7b2e5c81..26cbf361055 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -8,7 +8,6 @@ import { IListEvent, IListMouseEvent } from 'vs/base/browser/ui/list/list'; import { IListOptions, IListStyles } from 'vs/base/browser/ui/list/listWidget'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { ScrollEvent } from 'vs/base/common/scrollable'; @@ -18,14 +17,16 @@ import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { IPosition } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { FindMatch, IReadonlyTextBuffer, ITextModel } from 'vs/editor/common/model'; -import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr, RawContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; -import { CellLanguageStatusBarItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; +import { CellLanguageStatusBarItem, RunStateRenderer, TimerRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { CellKind, IOutput, IRenderOutput, NotebookCellMetadata, NotebookDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, IProcessedOutput, IRenderOutput, NotebookCellMetadata, NotebookDocumentMetadata, INotebookKernelInfo, IEditor, INotebookKernelInfo2 } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; -import { ICompositeCodeEditor } from 'vs/editor/common/editorCommon'; +import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; +import { IMenu } from 'vs/platform/actions/common/actions'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; export const KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED = new RawContextKey('notebookFindWidgetFocused', false); @@ -34,6 +35,8 @@ export const NOTEBOOK_IS_ACTIVE_EDITOR = ContextKeyExpr.equals('activeEditor', ' // Editor keys export const NOTEBOOK_EDITOR_FOCUSED = new RawContextKey('notebookEditorFocused', false); +export const NOTEBOOK_CELL_LIST_FOCUSED = new RawContextKey('notebookCellListFocused', false); +export const NOTEBOOK_OUTPUT_FOCUSED = new RawContextKey('notebookOutputFocused', false); export const NOTEBOOK_EDITOR_EDITABLE = new RawContextKey('notebookEditable', true); export const NOTEBOOK_EDITOR_RUNNABLE = new RawContextKey('notebookRunnable', true); export const NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK = new RawContextKey('notebookExecuting', false); @@ -46,6 +49,12 @@ export const NOTEBOOK_CELL_RUNNABLE = new RawContextKey('notebookCellRu export const NOTEBOOK_CELL_MARKDOWN_EDIT_MODE = new RawContextKey('notebookCellMarkdownEditMode', false); // bool export const NOTEBOOK_CELL_RUN_STATE = new RawContextKey('notebookCellRunState', undefined); // idle, running export const NOTEBOOK_CELL_HAS_OUTPUTS = new RawContextKey('notebookCellHasOutputs', false); // bool +export const NOTEBOOK_CELL_INPUT_COLLAPSED = new RawContextKey('notebookCellInputIsCollapsed', false); // bool +export const NOTEBOOK_CELL_OUTPUT_COLLAPSED = new RawContextKey('notebookCellOutputIsCollapsed', false); // bool + +// Kernels + +export const NOTEBOOK_HAS_MULTIPLE_KERNELS = new RawContextKey('notebookHasMultipleKernels', false); export interface NotebookLayoutInfo { width: number; @@ -59,6 +68,13 @@ export interface NotebookLayoutChangeEvent { fontInfo?: boolean; } +export enum CodeCellLayoutState { + Uninitialized, + Estimated, + FromCache, + Measured +} + export interface CodeCellLayoutInfo { readonly fontInfo: BareFontInfo | null; readonly editorHeight: number; @@ -68,6 +84,7 @@ export interface CodeCellLayoutInfo { readonly outputTotalHeight: number; readonly indicatorHeight: number; readonly bottomToolbarOffset: number; + readonly layoutState: CodeCellLayoutState; } export interface CodeCellLayoutChangeEvent { @@ -81,6 +98,7 @@ export interface CodeCellLayoutChangeEvent { export interface MarkdownCellLayoutInfo { readonly fontInfo: BareFontInfo | null; readonly editorWidth: number; + readonly editorHeight: number; readonly bottomToolbarOffset: number; readonly totalHeight: number; } @@ -101,16 +119,16 @@ export interface ICellViewModel { language: string; cellKind: CellKind; editState: CellEditState; - readonly runState: CellRunState; - currentTokenSource: CancellationTokenSource | undefined; focusMode: CellFocusMode; getText(): string; + getTextLength(): number; metadata: NotebookCellMetadata | undefined; textModel: ITextModel | undefined; hasModel(): this is IEditableCellViewModel; resolveTextModel(): Promise; getEvaluatedMetadata(documentMetadata: NotebookDocumentMetadata | undefined): NotebookCellMetadata; getSelectionsStartPosition(): IPosition[] | undefined; + getCellDecorations(): INotebookCellDecorationOptions[]; } export interface IEditableCellViewModel extends ICellViewModel { @@ -130,14 +148,26 @@ export interface INotebookEditorContribution { /** * Store view state. */ - saveViewState?(): any; + saveViewState?(): unknown; /** * Restore view state. */ - restoreViewState?(state: any): void; + restoreViewState?(state: unknown): void; } -export interface INotebookEditor extends ICompositeCodeEditor { +export interface INotebookCellDecorationOptions { + className?: string; + outputClassName?: string; +} + +export interface INotebookDeltaDecoration { + handle: number; + options: INotebookCellDecorationOptions; +} + +export interface INotebookEditor extends IEditor { + + cursorNavigationMode: boolean; /** * Notebook view model attached to the current editor @@ -148,9 +178,17 @@ export interface INotebookEditor extends ICompositeCodeEditor { * An event emitted when the model of this editor has changed. * @event */ - readonly onDidChangeModel: Event; + readonly onDidChangeModel: Event; + readonly onDidFocusEditorWidget: Event; isNotebookEditor: boolean; + activeKernel: INotebookKernelInfo | INotebookKernelInfo2 | undefined; + multipleKernelsAvailable: boolean; + readonly onDidChangeAvailableKernels: Event; + readonly onDidChangeKernel: Event; + isDisposed: boolean; + + getId(): string; getDomNode(): HTMLElement; getInnerWebview(): Webview | undefined; @@ -159,6 +197,10 @@ export interface INotebookEditor extends ICompositeCodeEditor { */ focus(): void; + hasFocus(): boolean; + + hasOutputTextSelection(): boolean; + /** * Select & focus cell */ @@ -196,30 +238,23 @@ export interface INotebookEditor extends ICompositeCodeEditor { /** * Move a cell up one spot */ - moveCellUp(cell: ICellViewModel): Promise; + moveCellUp(cell: ICellViewModel): Promise; /** * Move a cell down one spot */ - moveCellDown(cell: ICellViewModel): Promise; + moveCellDown(cell: ICellViewModel): Promise; /** + * @deprecated Note that this method doesn't support batch operations, use #moveCellToIdx instead. * Move a cell above or below another cell */ - moveCell(cell: ICellViewModel, relativeToCell: ICellViewModel, direction: 'above' | 'below'): Promise; + moveCell(cell: ICellViewModel, relativeToCell: ICellViewModel, direction: 'above' | 'below'): Promise; /** - * Switch the cell into editing mode. - * - * For code cell, the monaco editor will be focused. - * For markdown cell, it will switch from preview mode to editing mode, which focuses the monaco editor. + * Move a cell to a specific position */ - editNotebookCell(cell: ICellViewModel): void; - - /** - * Quit cell editing mode. - */ - saveNotebookCell(cell: ICellViewModel): void; + moveCellToIdx(cell: ICellViewModel, index: number): Promise; /** * Focus the container of a cell (the monaco editor inside is not focused). @@ -259,17 +294,39 @@ export interface INotebookEditor extends ICompositeCodeEditor { /** * Render the output in webview layer */ - createInset(cell: ICellViewModel, output: IOutput, shadowContent: string, offset: number): void; + createInset(cell: ICellViewModel, output: IProcessedOutput, shadowContent: string, offset: number): Promise; /** * Remove the output from the webview layer */ - removeInset(output: IOutput): void; + removeInset(output: IProcessedOutput): void; + + /** + * Hide the inset in the webview layer without removing it + */ + hideInset(output: IProcessedOutput): void; /** * Send message to the webview for outputs. */ - postMessage(message: any): void; + postMessage(forRendererId: string | undefined, message: any): void; + + /** + * Toggle class name on the notebook editor root DOM node. + */ + toggleClassName(className: string): void; + + /** + * Remove class name on the notebook editor root DOM node. + */ + addClassName(className: string): void; + + /** + * Remove class name on the notebook editor root DOM node. + */ + removeClassName(className: string): void; + + deltaCellOutputContainerClassNames(cellId: string, added: string[], removed: string[]): void; /** * Trigger the editor to scroll from scroll event programmatically @@ -294,32 +351,32 @@ export interface INotebookEditor extends ICompositeCodeEditor { /** * Reveal a line in notebook cell into viewport with minimal scrolling. */ - revealLineInView(cell: ICellViewModel, line: number): void; + revealLineInViewAsync(cell: ICellViewModel, line: number): Promise; /** * Reveal a line in notebook cell into viewport center. */ - revealLineInCenter(cell: ICellViewModel, line: number): void; + revealLineInCenterAsync(cell: ICellViewModel, line: number): Promise; /** * Reveal a line in notebook cell into viewport center. */ - revealLineInCenterIfOutsideViewport(cell: ICellViewModel, line: number): void; + revealLineInCenterIfOutsideViewportAsync(cell: ICellViewModel, line: number): Promise; /** * Reveal a range in notebook cell into viewport with minimal scrolling. */ - revealRangeInView(cell: ICellViewModel, range: Range): void; + revealRangeInViewAsync(cell: ICellViewModel, range: Range): Promise; /** * Reveal a range in notebook cell into viewport center. */ - revealRangeInCenter(cell: ICellViewModel, range: Range): void; + revealRangeInCenterAsync(cell: ICellViewModel, range: Range): Promise; /** * Reveal a range in notebook cell into viewport center. */ - revealRangeInCenterIfOutsideViewport(cell: ICellViewModel, range: Range): void; + revealRangeInCenterIfOutsideViewportAsync(cell: ICellViewModel, range: Range): Promise; /** * Set hidden areas on cell text models. @@ -332,7 +389,7 @@ export interface INotebookEditor extends ICompositeCodeEditor { * Change the decorations on cells. * The notebook is virtualized and this method should be called to create/delete editor decorations safely. */ - changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => any): any; + changeModelDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): T | null; /** * An event emitted on a "mouseup". @@ -355,6 +412,10 @@ export interface INotebookEditor extends ICompositeCodeEditor { } export interface INotebookCellList { + isDisposed: boolean + readonly contextKeyService: IContextKeyService; + elementAt(position: number): ICellViewModel | undefined; + elementHeight(element: ICellViewModel): number; onWillScroll: Event; onDidChangeFocus: Event>; onDidChangeContentHeight: Event; @@ -363,8 +424,8 @@ export interface INotebookCellList { scrollLeft: number; length: number; rowsContainer: HTMLElement; - readonly onDidRemoveOutput: Event; - readonly onDidHideOutput: Event; + readonly onDidRemoveOutput: Event; + readonly onDidHideOutput: Event; readonly onMouseUp: Event>; readonly onMouseDown: Event>; detachViewModel(): void; @@ -377,12 +438,12 @@ export interface INotebookCellList { revealElementInView(element: ICellViewModel): void; revealElementInCenterIfOutsideViewport(element: ICellViewModel): void; revealElementInCenter(element: ICellViewModel): void; - revealElementLineInView(element: ICellViewModel, line: number): void; - revealElementLineInCenter(element: ICellViewModel, line: number): void; - revealElementLineInCenterIfOutsideViewport(element: ICellViewModel, line: number): void; - revealElementRangeInView(element: ICellViewModel, range: Range): void; - revealElementRangeInCenter(element: ICellViewModel, range: Range): void; - revealElementRangeInCenterIfOutsideViewport(element: ICellViewModel, range: Range): void; + revealElementLineInViewAsync(element: ICellViewModel, line: number): Promise; + revealElementLineInCenterAsync(element: ICellViewModel, line: number): Promise; + revealElementLineInCenterIfOutsideViewportAsync(element: ICellViewModel, line: number): Promise; + revealElementRangeInViewAsync(element: ICellViewModel, range: Range): Promise; + revealElementRangeInCenterAsync(element: ICellViewModel, range: Range): Promise; + revealElementRangeInCenterIfOutsideViewportAsync(element: ICellViewModel, range: Range): Promise; setHiddenAreas(_ranges: ICellRange[], triggerViewUpdate: boolean): boolean; domElementOfElement(element: ICellViewModel): HTMLElement | null; focusView(): void; @@ -403,35 +464,48 @@ export interface INotebookCellList { } export interface BaseCellRenderTemplate { + editorPart: HTMLElement; + collapsedPart: HTMLElement; + expandButton: HTMLElement; + contextKeyService: IContextKeyService; container: HTMLElement; cellContainer: HTMLElement; toolbar: ToolBar; - focusIndicator: HTMLElement; + betweenCellToolbar: ToolBar; + focusIndicatorLeft: HTMLElement; disposables: DisposableStore; elementDisposables: DisposableStore; bottomCellContainer: HTMLElement; currentRenderedCell?: ICellViewModel; statusBarContainer: HTMLElement; languageStatusBarItem: CellLanguageStatusBarItem; - toJSON: () => any; + titleMenu: IMenu; + toJSON: () => object; } export interface MarkdownCellRenderTemplate extends BaseCellRenderTemplate { - editorPart: HTMLElement; editorContainer: HTMLElement; foldingIndicator: HTMLElement; currentEditor?: ICodeEditor; } export interface CodeCellRenderTemplate extends BaseCellRenderTemplate { - cellRunStatusContainer: HTMLElement; + cellRunState: RunStateRenderer; cellStatusMessageContainer: HTMLElement; runToolbar: ToolBar; runButtonContainer: HTMLElement; executionOrderLabel: HTMLElement; outputContainer: HTMLElement; + focusSinkElement: HTMLElement; editor: ICodeEditor; progressBar: ProgressBar; + timer: TimerRenderer; + focusIndicatorRight: HTMLElement; + focusIndicatorBottom: HTMLElement; +} + +export function isCodeCellRenderTemplate(templateData: BaseCellRenderTemplate): templateData is CodeCellRenderTemplate { + return !!(templateData as CodeCellRenderTemplate).runToolbar; } export interface IOutputTransformContribution { @@ -440,7 +514,7 @@ export interface IOutputTransformContribution { */ dispose(): void; - render(output: IOutput, container: HTMLElement, preferredMimeType: string | undefined): IRenderOutput; + render(output: IProcessedOutput, container: HTMLElement, preferredMimeType: string | undefined): IRenderOutput; } export interface CellFindMatch { @@ -458,11 +532,6 @@ export enum CellRevealPosition { Center } -export enum CellRunState { - Idle, - Running -} - export enum CellEditState { /** * Default state. @@ -494,7 +563,6 @@ export interface CellViewModelStateChangeEvent { metadataChanged?: boolean; selectionChanged?: boolean; focusModeChanged?: boolean; - runStateChanged?: boolean; editStateChanged?: boolean; languageChanged?: boolean; foldingStateChanged?: boolean; @@ -526,13 +594,13 @@ export function reduceCellRanges(_ranges: ICellRange[]): ICellRange[] { return []; } - let ranges = _ranges.sort((a, b) => a.start - b.start); - let result: ICellRange[] = []; + const ranges = _ranges.sort((a, b) => a.start - b.start); + const result: ICellRange[] = []; let currentRangeStart = ranges[0].start; let currentRangeEnd = ranges[0].end + 1; for (let i = 0, len = ranges.length; i < len; i++) { - let range = ranges[i]; + const range = ranges[i]; if (range.start > currentRangeEnd) { result.push({ start: currentRangeStart, end: currentRangeEnd - 1 }); @@ -554,7 +622,7 @@ export function getVisibleCells(cells: CellViewModel[], hiddenRanges: ICellRange let start = 0; let hiddenRangeIndex = 0; - let result: any[] = []; + const result: CellViewModel[] = []; while (start < cells.length && hiddenRangeIndex < hiddenRanges.length) { if (start < hiddenRanges[hiddenRangeIndex].start) { @@ -571,3 +639,9 @@ export function getVisibleCells(cells: CellViewModel[], hiddenRanges: ICellRange return result; } + +export function getActiveNotebookEditor(editorService: IEditorService): INotebookEditor | undefined { + // TODO can `isNotebookEditor` be on INotebookEditor to avoid a circular dependency? + const activeEditorPane = editorService.activeEditorPane as unknown as { isNotebookEditor?: boolean } | undefined; + return activeEditorPane?.isNotebookEditor ? (editorService.activeEditorPane?.getControl() as INotebookEditor) : undefined; +} diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index 815c2e919fc..a954e866d9a 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -6,50 +6,68 @@ import * as DOM from 'vs/base/browser/dom'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; -import { MutableDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import 'vs/css!./media/notebook'; +import { localize } from 'vs/nls'; +import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; -import { EditorOptions, IEditorCloseEvent, IEditorMemento } from 'vs/workbench/common/editor'; +import { EditorOptions, IEditorInput, IEditorMemento } from 'vs/workbench/common/editor'; import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput'; +import { NotebookEditorOptions, NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; +import { IBorrowValue, INotebookEditorWidgetService } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidgetService'; import { INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; +import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorDropService'; +import { IEditorGroup, IEditorGroupsService, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; const NOTEBOOK_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'NotebookEditorViewState'; export class NotebookEditor extends BaseEditor { static readonly ID: string = 'workbench.editor.notebook'; - private editorMemento: IEditorMemento; - private readonly groupListener = this._register(new MutableDisposable()); - private _widget: NotebookEditorWidget; + + private readonly _editorMemento: IEditorMemento; + private readonly _groupListener = this._register(new DisposableStore()); + private readonly _widgetDisposableStore: DisposableStore = new DisposableStore(); + private _widget: IBorrowValue = { value: undefined }; + private _rootElement!: HTMLElement; + private _dimension?: DOM.Dimension; + + // todo@rebornix is there a reason that `super.fireOnDidFocus` isn't used? + private readonly _onDidFocusWidget = this._register(new Emitter()); + get onDidFocus(): Event { return this._onDidFocusWidget.event; } + + private readonly _onDidChangeModel = this._register(new Emitter()); + readonly onDidChangeModel: Event = this._onDidChangeModel.event; constructor( @ITelemetryService telemetryService: ITelemetryService, @IThemeService themeService: IThemeService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IStorageService storageService: IStorageService, - @IEditorGroupsService editorGroupService: IEditorGroupsService) { + @IEditorService private readonly _editorService: IEditorService, + @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, + @IEditorDropService private readonly _editorDropService: IEditorDropService, + @INotificationService private readonly _notificationService: INotificationService, + @INotebookEditorWidgetService private readonly _notebookWidgetService: INotebookEditorWidgetService, + ) { super(NotebookEditor.ID, telemetryService, themeService, storageService); - - this._widget = this.instantiationService.createInstance(NotebookEditorWidget); - this.editorMemento = this.getEditorMemento(editorGroupService, NOTEBOOK_EDITOR_VIEW_STATE_PREFERENCE_KEY); + this._editorMemento = this.getEditorMemento(_editorGroupService, NOTEBOOK_EDITOR_VIEW_STATE_PREFERENCE_KEY); } - private readonly _onDidChangeModel = new Emitter(); - readonly onDidChangeModel: Event = this._onDidChangeModel.event; - - set viewModel(newModel: NotebookViewModel | undefined) { - this._widget.viewModel = newModel; - this._onDidChangeModel.fire(); + if (this._widget.value) { + this._widget.value.viewModel = newModel; + this._onDidChangeModel.fire(); + } } get viewModel() { - return this._widget.viewModel; + return this._widget.value?.viewModel; } get minimumWidth(): number { return 375; } @@ -59,99 +77,182 @@ export class NotebookEditor extends BaseEditor { set minimumWidth(value: number) { /*noop*/ } set maximumWidth(value: number) { /*noop*/ } - //#region Editor Core - - public get isNotebookEditor() { + get isNotebookEditor() { return true; } protected createEditor(parent: HTMLElement): void { - this._widget.createEditor(parent); - this._register(this.onDidFocus(() => this._widget.updateEditorFocus())); - this._register(this.onDidBlur(() => this._widget.updateEditorFocus())); + this._rootElement = DOM.append(parent, DOM.$('.notebook-editor')); + + // this._widget.createEditor(); + this._register(this.onDidFocus(() => this._widget.value?.updateEditorFocus())); + this._register(this.onDidBlur(() => this._widget.value?.updateEditorFocus())); } getDomNode() { - return this._widget.getShadowDomNode(); + return this._rootElement; } - getControl() { - return this._widget; - } - - onWillHide() { - if (this.input && this.input instanceof NotebookEditorInput && !this.input.isDisposed()) { - this.saveEditorViewState(this.input); - } - - this._widget.onWillHide(); - super.onHide(); + getControl(): NotebookEditorWidget | undefined { + return this._widget.value; } setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void { super.setEditorVisible(visible, group); - this.groupListener.value = ((group as IEditorGroupView).onWillCloseEditor(e => this.onWillCloseEditorInGroup(e))); - } - - private onWillCloseEditorInGroup(e: IEditorCloseEvent): void { - const editor = e.editor; - if (!(editor instanceof NotebookEditorInput)) { - return; // only handle files + if (group) { + this._groupListener.clear(); + this._groupListener.add(group.onWillCloseEditor(e => this._saveEditorViewState(e.editor))); + this._groupListener.add(group.onDidGroupChange(() => { + if (this._editorGroupService.activeGroup !== group) { + this._widget?.value?.updateEditorFocus(); + } + })); } - if (editor === this.input) { - this.saveEditorViewState(editor); + if (!visible) { + this._saveEditorViewState(this.input); + if (this.input && this._widget.value) { + // the widget is not transfered to other editor inputs + this._widget.value.onWillHide(); + } } } focus() { super.focus(); - this._widget.focus(); + this._widget.value?.focus(); } async setInput(input: NotebookEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { - if (this.input instanceof NotebookEditorInput) { - this.saveEditorViewState(this.input); + + const group = this.group!; + + this._saveEditorViewState(this.input); + await super.setInput(input, options, token); + + // Check for cancellation + if (token.isCancellationRequested) { + return undefined; } - await super.setInput(input, options, token); - const model = await input.resolve(); + this._widgetDisposableStore.clear(); - const viewState = this.loadTextEditorViewState(input); - this._widget.setModel(model, viewState, options); + // there currently is a widget which we still own so + // we need to hide it before getting a new widget + if (this._widget.value) { + this._widget.value.onWillHide(); + } + + this._widget = this.instantiationService.invokeFunction(this._notebookWidgetService.retrieveWidget, group, input); + + if (this._dimension) { + this._widget.value!.layout(this._dimension, this._rootElement); + } + + const model = await input.resolve(this._widget.value!.getId()); + // Check for cancellation + if (token.isCancellationRequested) { + return undefined; + } + + if (model === null) { + this._notificationService.prompt( + Severity.Error, + localize('fail.noEditor', "Cannot open resource with notebook editor type '{0}', please check if you have the right extension installed or enabled.", input.viewType), + [{ + label: localize('fail.reOpen', "Reopen file with VS Code standard text editor"), + run: async () => { + const fileEditorInput = this._editorService.createEditorInput({ resource: input.resource, forceFile: true }); + const textOptions: IEditorOptions | ITextEditorOptions = options ? { ...options, override: false } : { override: false }; + await this._editorService.openEditor(fileEditorInput, textOptions); + } + }] + ); + return; + } + + const viewState = this._loadNotebookEditorViewState(input); + + await this._widget.value!.setModel(model.notebook, viewState); + await this._widget.value!.setOptions(options instanceof NotebookEditorOptions ? options : undefined); + this._widgetDisposableStore.add(this._widget.value!.onDidFocus(() => this._onDidFocusWidget.fire())); + + this._widgetDisposableStore.add(this._editorDropService.createEditorDropTarget(this._widget.value!.getDomNode(), { + containsGroup: (group) => this.group?.id === group.group.id + })); } clearInput(): void { + if (this._widget.value) { + this._saveEditorViewState(this.input); + this._widget.value.onWillHide(); + } super.clearInput(); } - private saveEditorViewState(input: NotebookEditorInput): void { - if (this.group) { - const state = this._widget.getEditorViewState(); - this.editorMemento.saveEditorState(this.group, input.resource, state); + setOptions(options: EditorOptions | undefined): void { + if (options instanceof NotebookEditorOptions) { + this._widget.value?.setOptions(options); + } + super.setOptions(options); + } + + protected saveState(): void { + this._saveEditorViewState(this.input); + super.saveState(); + } + + private _saveEditorViewState(input: IEditorInput | undefined): void { + if (this.group && this._widget.value && input instanceof NotebookEditorInput) { + if (this._widget.value.isDisposed) { + return; + } + + const state = this._widget.value.getEditorViewState(); + this._editorMemento.saveEditorState(this.group, input.resource, state); } } - private loadTextEditorViewState(input: NotebookEditorInput): INotebookEditorViewState | undefined { + private _loadNotebookEditorViewState(input: NotebookEditorInput): INotebookEditorViewState | undefined { + let result: INotebookEditorViewState | undefined; if (this.group) { - return this.editorMemento.loadEditorState(this.group, input.resource); + result = this._editorMemento.loadEditorState(this.group, input.resource); + } + if (result) { + return result; + } + // when we don't have a view state for the group/input-tuple then we try to use an existing + // editor for the same resource. + for (const group of this._editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE)) { + if (group.activeEditorPane !== this && group.activeEditorPane instanceof NotebookEditor && group.activeEditor?.matches(input)) { + return group.activeEditorPane._widget.value?.getEditorViewState(); + } } - return; } layout(dimension: DOM.Dimension): void { - this._widget.layout(dimension); - } + this._rootElement.classList.toggle('mid-width', dimension.width < 1000 && dimension.width >= 600); + this._rootElement.classList.toggle('narrow-width', dimension.width < 600); + this._dimension = dimension; - protected saveState(): void { - if (this.input instanceof NotebookEditorInput) { - this.saveEditorViewState(this.input); + if (!this._widget.value || !(this._input instanceof NotebookEditorInput)) { + return; } - super.saveState(); + if (this._input.resource.toString() !== this._widget.value.viewModel?.uri.toString() && this._widget.value?.viewModel) { + // input and widget mismatch + // this happens when + // 1. open document A, pin the document + // 2. open document B + // 3. close document B + // 4. a layout is triggered + return; + } + + this._widget.value.layout(this._dimension, this._rootElement); } //#endregion @@ -161,14 +262,12 @@ export class NotebookEditor extends BaseEditor { //#endregion dispose() { - this._widget.dispose(); super.dispose(); } - toJSON(): any { + toJSON(): object { return { notebookHandle: this.viewModel?.handle }; } } - diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts index fd7dff392af..1df2b996c03 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts @@ -6,46 +6,43 @@ import { EditorInput, IEditorInput, GroupIdentifier, ISaveOptions, IMoveResult, IRevertOptions } from 'vs/workbench/common/editor'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { URI } from 'vs/base/common/uri'; -import { isEqual, basename } from 'vs/base/common/resources'; +import { isEqual, basename, joinPath } from 'vs/base/common/resources'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; -import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; +import { IReference } from 'vs/base/common/lifecycle'; +import { INotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { IPathService } from 'vs/workbench/services/path/common/pathService'; + +interface NotebookEditorInputOptions { + startDirty?: boolean; +} export class NotebookEditorInput extends EditorInput { - - private static readonly _instances = new Map(); - - static getOrCreate(instantiationService: IInstantiationService, resource: URI, name: string, viewType: string | undefined) { - const key = resource.toString() + viewType; - let input = NotebookEditorInput._instances.get(key); - if (!input) { - input = instantiationService.createInstance(class extends NotebookEditorInput { - dispose() { - NotebookEditorInput._instances.delete(key); - super.dispose(); - } - }, resource, name, viewType); - - NotebookEditorInput._instances.set(key, input); - } - return input; + static create(instantiationService: IInstantiationService, resource: URI, name: string, viewType: string | undefined, options: NotebookEditorInputOptions = {}) { + return instantiationService.createInstance(NotebookEditorInput, resource, name, viewType, options); } static readonly ID: string = 'workbench.input.notebook'; - private textModel: NotebookEditorModel | null = null; + + private _textModel: IReference | null = null; + private _defaultDirtyState: boolean = false; constructor( - public resource: URI, - public name: string, + public readonly resource: URI, + public readonly name: string, public readonly viewType: string | undefined, - @INotebookService private readonly notebookService: INotebookService, - @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService, - @IFileDialogService private readonly fileDialogService: IFileDialogService, - // @IEditorService private readonly editorService: IEditorService, - @IInstantiationService private readonly instantiationService: IInstantiationService + public readonly options: NotebookEditorInputOptions, + @INotebookService private readonly _notebookService: INotebookService, + @INotebookEditorModelResolverService private readonly _notebookModelResolverService: INotebookEditorModelResolverService, + @IFilesConfigurationService private readonly _filesConfigurationService: IFilesConfigurationService, + @IFileDialogService private readonly _fileDialogService: IFileDialogService, + @IPathService private readonly _pathService: IPathService, + @IInstantiationService private readonly _instantiationService: IInstantiationService ) { super(); + this._defaultDirtyState = !!options.startDirty; } getTypeId(): string { @@ -57,14 +54,21 @@ export class NotebookEditorInput extends EditorInput { } isDirty() { - return this.textModel?.isDirty() || false; + if (!this._textModel) { + return !!this._defaultDirtyState; + } + return this._textModel.object.isDirty(); + } + + isUntitled(): boolean { + return this._textModel?.object.isUntitled() || false; } isReadonly() { return false; } - public isSaving(): boolean { + isSaving(): boolean { if (this.isUntitled()) { return false; // untitled is never saving automatically } @@ -73,7 +77,7 @@ export class NotebookEditorInput extends EditorInput { return false; // the editor needs to be dirty for being saved } - if (this.filesConfigurationService.getAutoSaveMode() === AutoSaveMode.AFTER_SHORT_DELAY) { + if (this._filesConfigurationService.getAutoSaveMode() === AutoSaveMode.AFTER_SHORT_DELAY) { return true; // a short auto save is configured, treat this as being saved } @@ -81,8 +85,14 @@ export class NotebookEditorInput extends EditorInput { } async save(group: GroupIdentifier, options?: ISaveOptions): Promise { - if (this.textModel) { - await this.textModel.save(); + if (this._textModel) { + + if (this.isUntitled()) { + return this.saveAs(group, options); + } else { + await this._textModel.object.save(); + } + return this; } @@ -90,63 +100,92 @@ export class NotebookEditorInput extends EditorInput { } async saveAs(group: GroupIdentifier, options?: ISaveOptions): Promise { - if (!this.textModel) { + if (!this._textModel || !this.viewType) { return undefined; } - const dialogPath = this.textModel.resource; - const target = await this.fileDialogService.pickFileToSave(dialogPath, options?.availableFileSystems); + const provider = this._notebookService.getContributedNotebookProvider(this.viewType!); + + if (!provider) { + return undefined; + } + + const dialogPath = this.isUntitled() ? await this.suggestName(this.name) : this._textModel.object.resource; + + const target = await this._fileDialogService.pickFileToSave(dialogPath, options?.availableFileSystems); if (!target) { return undefined; // save cancelled } - if (!await this.textModel.saveAs(target)) { + if (!provider.matches(target)) { + const patterns = provider.selector.map(pattern => { + if (pattern.excludeFileNamePattern) { + return `${pattern.filenamePattern} (exclude: ${pattern.excludeFileNamePattern})`; + } + + return pattern.filenamePattern; + }).join(', '); + throw new Error(`File name ${target} is not supported by ${provider.providerDisplayName}. + +Please make sure the file name matches following patterns: +${patterns} +`); + } + + if (!await this._textModel.object.saveAs(target)) { return undefined; } return this._move(group, target)?.editor; } - move(group: GroupIdentifier, target: URI): IMoveResult | undefined { - if (this.textModel) { - const contributedNotebookProviders = this.notebookService.getContributedNotebookProviders(target); + async suggestName(suggestedFilename: string) { + return joinPath(this._fileDialogService.defaultFilePath() || (await this._pathService.userHome()), suggestedFilename); + } - if (contributedNotebookProviders.find(provider => provider.id === this.textModel!.viewType)) { + // called when users rename a notebook document + rename(group: GroupIdentifier, target: URI): IMoveResult | undefined { + if (this._textModel) { + const contributedNotebookProviders = this._notebookService.getContributedNotebookProviders(target); + + if (contributedNotebookProviders.find(provider => provider.id === this._textModel!.object.viewType)) { return this._move(group, target); } } return undefined; } - _move(group: GroupIdentifier, newResource: URI): { editor: IEditorInput } | undefined { - const editorInput = NotebookEditorInput.getOrCreate(this.instantiationService, newResource, basename(newResource), this.viewType); + private _move(group: GroupIdentifier, newResource: URI): { editor: IEditorInput } | undefined { + const editorInput = NotebookEditorInput.create(this._instantiationService, newResource, basename(newResource), this.viewType); return { editor: editorInput }; } async revert(group: GroupIdentifier, options?: IRevertOptions): Promise { - if (this.textModel) { - await this.textModel.revert(options); + if (this._textModel && this._textModel.object.isDirty()) { + await this._textModel.object.revert(options); } return; } - async resolve(): Promise { - if (!await this.notebookService.canResolve(this.viewType!)) { - throw new Error(`Cannot open notebook of type '${this.viewType}'`); + async resolve(editorId?: string): Promise { + if (!await this._notebookService.canResolve(this.viewType!)) { + return null; } - this.textModel = await this.notebookService.modelManager.resolve(this.resource, this.viewType!); + if (!this._textModel) { + this._textModel = await this._notebookModelResolverService.resolve(this.resource, this.viewType!, editorId); - this._register(this.textModel.onDidChangeDirty(() => { - this._onDidChangeDirty.fire(); - })); + this._register(this._textModel.object.onDidChangeDirty(() => { + this._onDidChangeDirty.fire(); + })); - if (this.textModel.isDirty()) { - this._onDidChangeDirty.fire(); + if (this._textModel.object.isDirty()) { + this._onDidChangeDirty.fire(); + } } - return this.textModel; + return this._textModel.object; } matches(otherInput: unknown): boolean { @@ -161,11 +200,10 @@ export class NotebookEditorInput extends EditorInput { } dispose() { - if (this.textModel) { - this.notebookService.destoryNotebookDocument(this.textModel!.notebook.viewType, this.textModel!.notebook); - this.textModel.dispose(); + if (this._textModel) { + this._textModel.dispose(); + this._textModel = null; } - super.dispose(); } } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 80e94d806b2..387b8ce47ea 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -10,27 +10,25 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Color, RGBA } from 'vs/base/common/color'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { combinedDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; +import { combinedDisposable, DisposableStore, Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import 'vs/css!./media/notebook'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; -import { IPosition, Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { IEditor } from 'vs/editor/common/editorCommon'; -import { IReadonlyTextBuffer } from 'vs/editor/common/model'; import * as nls from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { contrastBorder, editorBackground, focusBorder, foreground, registerColor, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, textPreformatForeground } from 'vs/platform/theme/common/colorRegistry'; +import { contrastBorder, editorBackground, focusBorder, foreground, registerColor, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, textPreformatForeground, errorForeground, transparent, listFocusBackground, listInactiveSelectionBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, scrollbarSliderActiveBackground } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { EditorMemento } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorOptions, IEditorMemento } from 'vs/workbench/common/editor'; -import { CELL_MARGIN, CELL_RUN_GUTTER, EDITOR_BOTTOM_PADDING, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING, SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFocusMode, ICellRange, ICellViewModel, IEditableCellViewModel, INotebookCellList, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CELL_MARGIN, CELL_RUN_GUTTER, EDITOR_BOTTOM_PADDING, CELL_TOP_MARGIN, EDITOR_TOP_PADDING, SCROLLABLE_ELEMENT_PADDING_TOP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; +import { CellEditState, CellFocusMode, ICellRange, ICellViewModel, INotebookCellList, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED, INotebookDeltaDecoration } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; @@ -39,15 +37,20 @@ import { CellDragAndDropController, CodeCellRenderer, MarkdownCellRenderer, Note import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { CellViewModel, IModelDecorationsChangeAccessor, INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { CellKind, CellUri, IOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel'; +import { CellKind, IProcessedOutput, INotebookKernelInfo, INotebookKernelInfoDto, INotebookKernelInfo2, NotebookRunState, NotebookCellRunState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; -import { getExtraColor } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughUtils'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { generateUuid } from 'vs/base/common/uuid'; import { Memento, MementoObject } from 'vs/workbench/common/memento'; +import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; +import { URI } from 'vs/base/common/uri'; +import { PANEL_BORDER } from 'vs/workbench/common/theme'; +import { debugIconStartForeground } from 'vs/workbench/contrib/debug/browser/debugToolBar'; +import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; +import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; +import { notebookKernelProviderAssociationsSettingId, NotebookKernelProviderAssociations } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation'; const $ = DOM.$; @@ -66,104 +69,194 @@ export class NotebookEditorOptions extends EditorOptions { } } - +const NotebookEditorActiveKernelCache = 'workbench.editor.notebook.activeKernel'; export class NotebookEditorWidget extends Disposable implements INotebookEditor { static readonly ID: string = 'workbench.editor.notebook'; - private static readonly EDITOR_MEMENTOS = new Map>(); - private _rootElement!: HTMLElement; - private overlayContainer!: HTMLElement; - private body!: HTMLElement; - private webview: BackLayerWebView | null = null; - private webviewTransparentCover: HTMLElement | null = null; - private list: INotebookCellList | undefined; - private renderedEditors: Map = new Map(); - private eventDispatcher: NotebookEventDispatcher | undefined; - private notebookViewModel: NotebookViewModel | undefined; - private localStore: DisposableStore = this._register(new DisposableStore()); - // private readonly groupListener = this._register(new MutableDisposable()); - private fontInfo: BareFontInfo | undefined; - private dimension: DOM.Dimension | null = null; - private editorFocus: IContextKey | null = null; - private editorEditable: IContextKey | null = null; - private editorRunnable: IContextKey | null = null; - private editorExecutingNotebook: IContextKey | null = null; - private outputRenderer: OutputRenderer; + private static readonly EDITOR_MEMENTOS = new Map>(); + private _overlayContainer!: HTMLElement; + private _body!: HTMLElement; + private _webview: BackLayerWebView | null = null; + private _webviewResolved: boolean = false; + private _webviewResolvePromise: Promise | null = null; + private _webviewTransparentCover: HTMLElement | null = null; + private _list: INotebookCellList | undefined; + private _dndController: CellDragAndDropController | null = null; + private _renderedEditors: Map = new Map(); + private _eventDispatcher: NotebookEventDispatcher | undefined; + private _notebookViewModel: NotebookViewModel | undefined; + private _localStore: DisposableStore = this._register(new DisposableStore()); + private _fontInfo: BareFontInfo | undefined; + private _dimension: DOM.Dimension | null = null; + private _shadowElementViewInfo: { height: number, width: number, top: number; left: number; } | null = null; + + private _editorFocus: IContextKey | null = null; + private _outputFocus: IContextKey | null = null; + private _editorEditable: IContextKey | null = null; + private _editorRunnable: IContextKey | null = null; + private _notebookExecuting: IContextKey | null = null; + private _notebookHasMultipleKernels: IContextKey | null = null; + private _outputRenderer: OutputRenderer; protected readonly _contributions: { [key: string]: INotebookEditorContribution; }; - private scrollBeyondLastLine: boolean; - private readonly memento: Memento; + private _scrollBeyondLastLine: boolean; + private readonly _memento: Memento; + private readonly _activeKernelMemento: Memento; + private readonly _onDidFocusEmitter = this._register(new Emitter()); + public readonly onDidFocus = this._onDidFocusEmitter.event; + private _cellContextKeyManager: CellContextKeyManager | null = null; + private _isVisible = false; + private readonly _uuid = generateUuid(); + private _webiewFocused: boolean = false; + private _isDisposed: boolean = false; - constructor( - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IStorageService storageService: IStorageService, - @INotebookService private notebookService: INotebookService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @ILayoutService private readonly _layoutService: ILayoutService - ) { - super(); - this.memento = new Memento(NotebookEditorWidget.ID, storageService); - - this.outputRenderer = new OutputRenderer(this, this.instantiationService); - this._contributions = {}; - this.scrollBeyondLastLine = this.configurationService.getValue('editor.scrollBeyondLastLine'); - - this.configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('editor.scrollBeyondLastLine')) { - this.scrollBeyondLastLine = this.configurationService.getValue('editor.scrollBeyondLastLine'); - if (this.dimension) { - this.layout(this.dimension); - } - } - }); + get isDisposed() { + return this._isDisposed; } - private readonly _onDidChangeModel = new Emitter(); - readonly onDidChangeModel: Event = this._onDidChangeModel.event; + private readonly _onDidChangeModel = this._register(new Emitter()); + readonly onDidChangeModel: Event = this._onDidChangeModel.event; + private readonly _onDidFocusEditorWidget = this._register(new Emitter()); + readonly onDidFocusEditorWidget = this._onDidFocusEditorWidget.event; set viewModel(newModel: NotebookViewModel | undefined) { - this.notebookViewModel = newModel; - this._onDidChangeModel.fire(); + this._notebookViewModel = newModel; + this._onDidChangeModel.fire(newModel?.notebookDocument); } get viewModel() { - return this.notebookViewModel; + return this._notebookViewModel; + } + + get uri() { + return this._notebookViewModel?.uri; + } + + get textModel() { + return this._notebookViewModel?.notebookDocument; + } + + private _activeKernel: INotebookKernelInfo | INotebookKernelInfo2 | undefined = undefined; + private readonly _onDidChangeKernel = this._register(new Emitter()); + readonly onDidChangeKernel: Event = this._onDidChangeKernel.event; + private readonly _onDidChangeAvailableKernels = this._register(new Emitter()); + readonly onDidChangeAvailableKernels: Event = this._onDidChangeAvailableKernels.event; + + get activeKernel() { + return this._activeKernel; + } + + set activeKernel(kernel: INotebookKernelInfo | INotebookKernelInfo2 | undefined) { + if (this._isDisposed) { + return; + } + + if (this._activeKernel === kernel) { + return; + } + + this._activeKernel = kernel; + this._activeKernelResolvePromise = undefined; + + const memento = this._activeKernelMemento.getMemento(StorageScope.GLOBAL); + memento[this.viewModel!.viewType] = this._activeKernel?.id; + this._activeKernelMemento.saveMemento(); + this._onDidChangeKernel.fire(); + } + + private _activeKernelResolvePromise: Promise | undefined = undefined; + + private _currentKernelTokenSource: CancellationTokenSource | undefined = undefined; + private _multipleKernelsAvailable: boolean = false; + + get multipleKernelsAvailable() { + return this._multipleKernelsAvailable; + } + + set multipleKernelsAvailable(state: boolean) { + this._multipleKernelsAvailable = state; + this._onDidChangeAvailableKernels.fire(); } private readonly _onDidChangeActiveEditor = this._register(new Emitter()); readonly onDidChangeActiveEditor: Event = this._onDidChangeActiveEditor.event; get activeCodeEditor(): IEditor | undefined { - const [focused] = this.list!.getFocusedElements(); - return this.renderedEditors.get(focused); + if (this._isDisposed) { + return; + } + + const [focused] = this._list!.getFocusedElements(); + return this._renderedEditors.get(focused); + } + + private _cursorNavigationMode: boolean = false; + get cursorNavigationMode(): boolean { + return this._cursorNavigationMode; + } + + set cursorNavigationMode(v: boolean) { + this._cursorNavigationMode = v; + } + + constructor( + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IStorageService storageService: IStorageService, + @INotebookService private notebookService: INotebookService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IContextKeyService readonly contextKeyService: IContextKeyService, + @ILayoutService private readonly layoutService: ILayoutService + ) { + super(); + this._memento = new Memento(NotebookEditorWidget.ID, storageService); + this._activeKernelMemento = new Memento(NotebookEditorActiveKernelCache, storageService); + + this._outputRenderer = new OutputRenderer(this, this.instantiationService); + this._contributions = {}; + this._scrollBeyondLastLine = this.configurationService.getValue('editor.scrollBeyondLastLine'); + + this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('editor.scrollBeyondLastLine')) { + this._scrollBeyondLastLine = this.configurationService.getValue('editor.scrollBeyondLastLine'); + if (this._dimension && this._isVisible) { + this.layout(this._dimension); + } + } + }); + + this.notebookService.addNotebookEditor(this); + } + + /** + * EditorId + */ + public getId(): string { + return this._uuid; + } + + hasModel() { + return !!this._notebookViewModel; } //#region Editor Core protected getEditorMemento(editorGroupService: IEditorGroupsService, key: string, limit: number = 10): IEditorMemento { - const mementoKey = `${this.getId()}${key}`; + const mementoKey = `${NotebookEditorWidget.ID}${key}`; let editorMemento = NotebookEditorWidget.EDITOR_MEMENTOS.get(mementoKey); if (!editorMemento) { - editorMemento = new EditorMemento(this.getId(), key, this.getMemento(StorageScope.WORKSPACE), limit, editorGroupService); + editorMemento = new EditorMemento(NotebookEditorWidget.ID, key, this.getMemento(StorageScope.WORKSPACE), limit, editorGroupService); NotebookEditorWidget.EDITOR_MEMENTOS.set(mementoKey, editorMemento); } - return editorMemento; + return editorMemento as IEditorMemento; } protected getMemento(scope: StorageScope): MementoObject { - return this.memento.getMemento(scope); + return this._memento.getMemento(scope); } - - getId(): string { - return NotebookEditorWidget.ID; - } - - public get isNotebookEditor() { return true; } @@ -171,32 +264,71 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor updateEditorFocus() { // Note - focus going to the webview will fire 'blur', but the webview element will be // a descendent of the notebook editor root. - this.editorFocus?.set(DOM.isAncestor(document.activeElement, this.overlayContainer)); + const focused = DOM.isAncestor(document.activeElement, this._overlayContainer); + this._editorFocus?.set(focused); + this._notebookViewModel?.setFocus(focused); } - createEditor(parent: HTMLElement): void { - this._rootElement = DOM.append(parent, $('.notebook-editor')); + hasFocus() { + return this._editorFocus?.get() || false; + } - this.overlayContainer = document.createElement('div'); + hasOutputTextSelection() { + if (!this.hasFocus()) { + return false; + } + + const windowSelection = window.getSelection(); + if (windowSelection?.rangeCount !== 1) { + return false; + } + + const activeSelection = windowSelection.getRangeAt(0); + if (activeSelection.endOffset - activeSelection.startOffset === 0) { + return false; + } + + let container: any = activeSelection.commonAncestorContainer; + + if (!this._body.contains(container)) { + return false; + } + + while (container + && + container !== this._body) { + + if (DOM.hasClass(container as HTMLElement, 'output')) { + return true; + } + + container = container.parentNode; + } + + return false; + } + + createEditor(): void { + this._overlayContainer = document.createElement('div'); const id = generateUuid(); - this.overlayContainer.id = `notebook-${id}`; - this.overlayContainer.className = 'notebookOverlay'; - DOM.addClass(this.overlayContainer, 'notebook-editor'); - this.overlayContainer.style.visibility = 'hidden'; + this._overlayContainer.id = `notebook-${id}`; + this._overlayContainer.className = 'notebookOverlay'; + DOM.addClass(this._overlayContainer, 'notebook-editor'); + this._overlayContainer.style.visibility = 'hidden'; - this._layoutService.container.appendChild(this.overlayContainer); - this.createBody(this.overlayContainer); - this.generateFontInfo(); - this.editorFocus = NOTEBOOK_EDITOR_FOCUSED.bindTo(this.contextKeyService); - this.editorFocus.set(true); - // this._register(this.onDidFocus(() => this.updateEditorFocus())); - // this._register(this.onDidBlur(() => this.updateEditorFocus())); - - this.editorEditable = NOTEBOOK_EDITOR_EDITABLE.bindTo(this.contextKeyService); - this.editorEditable.set(true); - this.editorRunnable = NOTEBOOK_EDITOR_RUNNABLE.bindTo(this.contextKeyService); - this.editorRunnable.set(true); - this.editorExecutingNotebook = NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK.bindTo(this.contextKeyService); + this.layoutService.container.appendChild(this._overlayContainer); + this._createBody(this._overlayContainer); + this._generateFontInfo(); + this._editorFocus = NOTEBOOK_EDITOR_FOCUSED.bindTo(this.contextKeyService); + this._isVisible = true; + this._outputFocus = NOTEBOOK_OUTPUT_FOCUSED.bindTo(this.contextKeyService); + this._editorEditable = NOTEBOOK_EDITOR_EDITABLE.bindTo(this.contextKeyService); + this._editorEditable.set(true); + this._editorRunnable = NOTEBOOK_EDITOR_RUNNABLE.bindTo(this.contextKeyService); + this._editorRunnable.set(true); + this._notebookExecuting = NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK.bindTo(this.contextKeyService); + this._notebookHasMultipleKernels = NOTEBOOK_HAS_MULTIPLE_KERNELS.bindTo(this.contextKeyService); + this._notebookHasMultipleKernels.set(false); const contributions = NotebookEditorExtensionsRegistry.getEditorContributions(); @@ -210,33 +342,34 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } } - private generateFontInfo(): void { + private _generateFontInfo(): void { const editorOptions = this.configurationService.getValue('editor'); - this.fontInfo = BareFontInfo.createFromRawSettings(editorOptions, getZoomLevel()); + this._fontInfo = BareFontInfo.createFromRawSettings(editorOptions, getZoomLevel()); } - private createBody(parent: HTMLElement): void { - this.body = document.createElement('div'); - DOM.addClass(this.body, 'cell-list-container'); - this.createCellList(); - DOM.append(parent, this.body); + private _createBody(parent: HTMLElement): void { + this._body = document.createElement('div'); + DOM.addClass(this._body, 'cell-list-container'); + this._createCellList(); + DOM.append(parent, this._body); } - private createCellList(): void { - DOM.addClass(this.body, 'cell-list-container'); + private _createCellList(): void { + DOM.addClass(this._body, 'cell-list-container'); - const dndController = this._register(new CellDragAndDropController(this)); - const renders = [ - this.instantiationService.createInstance(CodeCellRenderer, this, this.renderedEditors, dndController), - this.instantiationService.createInstance(MarkdownCellRenderer, this.contextKeyService, this, dndController, this.renderedEditors), + this._dndController = this._register(new CellDragAndDropController(this, this._body)); + const getScopedContextKeyService = (container?: HTMLElement) => this._list!.contextKeyService.createScoped(container); + const renderers = [ + this.instantiationService.createInstance(CodeCellRenderer, this, this._renderedEditors, this._dndController, getScopedContextKeyService), + this.instantiationService.createInstance(MarkdownCellRenderer, this, this._dndController, this._renderedEditors, getScopedContextKeyService), ]; - this.list = this.instantiationService.createInstance( + this._list = this.instantiationService.createInstance( NotebookCellList, 'NotebookCellList', - this.body, + this._body, this.instantiationService.createInstance(NotebookCellListDelegate), - renders, + renderers, this.contextKeyService, { setRowLineHeight: false, @@ -249,7 +382,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor enableKeyboardNavigation: true, additionalScrollHeight: 0, transformOptimization: false, - styleController: (_suffix: string) => { return this.list!; }, + styleController: (_suffix: string) => { return this._list!; }, overrideStyles: { listBackground: editorBackground, listActiveSelectionBackground: editorBackground, @@ -272,93 +405,160 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor getWidgetAriaLabel() { return nls.localize('notebookTreeAriaLabel', "Notebook"); } + }, + focusNextPreviousDelegate: { + onFocusNext: (applyFocusNext: () => void) => this._updateForCursorNavigationMode(applyFocusNext), + onFocusPrevious: (applyFocusPrevious: () => void) => this._updateForCursorNavigationMode(applyFocusPrevious), } }, ); + this._dndController.setList(this._list); - this.webview = this.instantiationService.createInstance(BackLayerWebView, this); - this.webview.webview.onDidBlur(() => this.updateEditorFocus()); - this.webview.webview.onDidFocus(() => this.updateEditorFocus()); - this._register(this.webview.onMessage(message => { - if (this.viewModel) { - this.notebookService.onDidReceiveMessage(this.viewModel.viewType, this.viewModel.uri, message); - } - })); - this.list.rowsContainer.appendChild(this.webview.element); + // create Webview - this._register(this.list); - this._register(combinedDisposable(...renders)); + this._register(this._list); + this._register(combinedDisposable(...renderers)); // transparent cover - this.webviewTransparentCover = DOM.append(this.list.rowsContainer, $('.webview-cover')); - this.webviewTransparentCover.style.display = 'none'; + this._webviewTransparentCover = DOM.append(this._list.rowsContainer, $('.webview-cover')); + this._webviewTransparentCover.style.display = 'none'; - this._register(DOM.addStandardDisposableGenericMouseDownListner(this.overlayContainer, (e: StandardMouseEvent) => { - if (DOM.hasClass(e.target, 'slider') && this.webviewTransparentCover) { - this.webviewTransparentCover.style.display = 'block'; + this._register(DOM.addStandardDisposableGenericMouseDownListner(this._overlayContainer, (e: StandardMouseEvent) => { + if (DOM.hasClass(e.target, 'slider') && this._webviewTransparentCover) { + this._webviewTransparentCover.style.display = 'block'; } })); - this._register(DOM.addStandardDisposableGenericMouseUpListner(this.overlayContainer, () => { - if (this.webviewTransparentCover) { + this._register(DOM.addStandardDisposableGenericMouseUpListner(this._overlayContainer, () => { + if (this._webviewTransparentCover) { // no matter when - this.webviewTransparentCover.style.display = 'none'; + this._webviewTransparentCover.style.display = 'none'; } })); - this._register(this.list.onMouseDown(e => { + this._register(this._list.onMouseDown(e => { if (e.element) { this._onMouseDown.fire({ event: e.browserEvent, target: e.element }); } })); - this._register(this.list.onMouseUp(e => { + this._register(this._list.onMouseUp(e => { if (e.element) { this._onMouseUp.fire({ event: e.browserEvent, target: e.element }); } })); - this._register(this.list.onDidChangeFocus(_e => this._onDidChangeActiveEditor.fire(this))); + this._register(this._list.onDidChangeFocus(_e => { + this._onDidChangeActiveEditor.fire(this); + this._cursorNavigationMode = false; + })); + + const widgetFocusTracker = DOM.trackFocus(this.getDomNode()); + this._register(widgetFocusTracker); + this._register(widgetFocusTracker.onDidFocus(() => this._onDidFocusEmitter.fire())); + } - getShadowDomNode() { - return this._rootElement; + private _updateForCursorNavigationMode(applyFocusChange: () => void): void { + if (this._cursorNavigationMode) { + // Will fire onDidChangeFocus, resetting the state to Container + applyFocusChange(); + + const newFocusedCell = this._list!.getFocusedElements()[0]; + if (newFocusedCell.cellKind === CellKind.Code || newFocusedCell.editState === CellEditState.Editing) { + this.focusNotebookCell(newFocusedCell, 'editor'); + } else { + // Reset to "Editor", the state has not been consumed + this._cursorNavigationMode = true; + } + } else { + applyFocusChange(); + } } getDomNode() { - return this.overlayContainer; + return this._overlayContainer; } onWillHide() { - this.editorFocus?.set(false); - this.overlayContainer.style.visibility = 'hidden'; - this.overlayContainer.style.display = 'none'; + this._isVisible = false; + this._editorFocus?.set(false); + this._overlayContainer.style.visibility = 'hidden'; + this._overlayContainer.style.left = '-50000px'; } getInnerWebview(): Webview | undefined { - return this.webview?.webview; + return this._webview?.webview; } - focus() { - this.editorFocus?.set(true); - this.list?.domFocus(); - } + this._isVisible = true; + this._editorFocus?.set(true); - async setModel(model: NotebookEditorModel, viewState: INotebookEditorViewState | undefined, options: EditorOptions | undefined): Promise { - if (this.notebookViewModel === undefined || !this.notebookViewModel.equal(model.notebook) || this.webview === null) { - this.detachModel(); - await this.attachModel(model, viewState); + if (this._webiewFocused) { + this._webview?.focusWebview(); + } else { + const focus = this._list?.getFocus()[0]; + if (typeof focus === 'number') { + const element = this._notebookViewModel!.viewCells[focus]; + + if (element.focusMode === CellFocusMode.Editor) { + element.editState = CellEditState.Editing; + element.focusMode = CellFocusMode.Editor; + this._onDidFocusEditorWidget.fire(); + return; + } + + } + this._list?.domFocus(); } + this._onDidFocusEditorWidget.fire(); + } + + async setModel(textModel: NotebookTextModel, viewState: INotebookEditorViewState | undefined): Promise { + if (this._notebookViewModel === undefined || !this._notebookViewModel.equal(textModel)) { + this._detachModel(); + await this._attachModel(textModel, viewState); + } else { + this.restoreListViewState(viewState); + } + + // clear state + this._dndController?.clearGlobalDragState(); + + this._currentKernelTokenSource = new CancellationTokenSource(); + this._localStore.add(this._currentKernelTokenSource); + // we don't await for it, otherwise it will slow down the file opening + this._setKernels(textModel, this._currentKernelTokenSource); + + this._localStore.add(this.notebookService.onDidChangeKernels(async () => { + this._currentKernelTokenSource?.cancel(); + this._currentKernelTokenSource = new CancellationTokenSource(); + await this._setKernels(textModel, this._currentKernelTokenSource); + })); + + this._localStore.add(this._list!.onDidChangeFocus(() => { + const focused = this._list!.getFocusedElements()[0]; + if (focused) { + if (!this._cellContextKeyManager) { + this._cellContextKeyManager = this._localStore.add(new CellContextKeyManager(this.contextKeyService, textModel, focused as CellViewModel)); + } + + this._cellContextKeyManager.updateForElement(focused as CellViewModel); + } + })); + } + + async setOptions(options: NotebookEditorOptions | undefined) { // reveal cell if editor options tell to do so - if (options instanceof NotebookEditorOptions && options.cellOptions) { + if (options?.cellOptions) { const cellOptions = options.cellOptions; - const cell = this.notebookViewModel!.viewCells.find(cell => cell.uri.toString() === cellOptions.resource.toString()); + const cell = this._notebookViewModel!.viewCells.find(cell => cell.uri.toString() === cellOptions.resource.toString()); if (cell) { this.selectElement(cell); this.revealInCenterIfOutsideViewport(cell); - const editor = this.renderedEditors.get(cell)!; + const editor = this._renderedEditors.get(cell)!; if (editor) { if (cellOptions.options?.selection) { const { selection } = cellOptions.options; @@ -367,48 +567,268 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor endLineNumber: selection.endLineNumber || selection.startLineNumber, endColumn: selection.endColumn || selection.startColumn }); + editor.revealPositionInCenterIfOutsideViewport({ + lineNumber: selection.startLineNumber, + column: selection.startColumn + }); } if (!cellOptions.options?.preserveFocus) { editor.focus(); } } } + } else if (this._notebookViewModel && this._notebookViewModel.viewCells.length === 1 && this._notebookViewModel.viewCells[0].cellKind === CellKind.Code) { + // there is only one code cell in the document + const cell = this._notebookViewModel!.viewCells[0]; + if (cell.getTextLength() === 0) { + // the cell is empty, very likely a template cell, focus it + this.selectElement(cell); + await this.revealLineInCenterAsync(cell, 1); + const editor = this._renderedEditors.get(cell)!; + if (editor) { + editor.focus(); + } + } } } - private detachModel() { - this.localStore.clear(); - this.list?.detachViewModel(); + private _detachModel() { + this._localStore.clear(); + this._list?.detachViewModel(); this.viewModel?.dispose(); // avoid event - this.notebookViewModel = undefined; - this.webview?.clearInsets(); - this.webview?.clearPreloadsCache(); - this.list?.clear(); + this._notebookViewModel = undefined; + // this.webview?.clearInsets(); + // this.webview?.clearPreloadsCache(); + this._webview?.dispose(); + this._webview?.element.remove(); + this._webview = null; + this._list?.clear(); } - private updateForMetadata(): void { - this.editorEditable?.set(!!this.viewModel!.metadata?.editable); - this.editorRunnable?.set(!!this.viewModel!.metadata?.runnable); - DOM.toggleClass(this.overlayContainer, 'notebook-editor-editable', !!this.viewModel!.metadata?.editable); - DOM.toggleClass(this.getDomNode(), 'notebook-editor-editable', !!this.viewModel!.metadata?.editable); - } + private async _setKernels(textModel: NotebookTextModel, tokenSource: CancellationTokenSource) { + const provider = this.notebookService.getContributedNotebookProviders(this.viewModel!.uri)[0]; + const availableKernels2 = await this.notebookService.getContributedNotebookKernels2(textModel.viewType, textModel.uri, tokenSource.token); - private async attachModel(model: NotebookEditorModel, viewState: INotebookEditorViewState | undefined) { - if (!this.webview) { - this.webview = this.instantiationService.createInstance(BackLayerWebView, this); - this.list?.rowsContainer.insertAdjacentElement('afterbegin', this.webview!.element); + if (tokenSource.token.isCancellationRequested) { + return; } - await this.webview.waitForInitialization(); + const availableKernels = this.notebookService.getContributedNotebookKernels(textModel.viewType, textModel.uri); - this.eventDispatcher = new NotebookEventDispatcher(); - this.viewModel = this.instantiationService.createInstance(NotebookViewModel, model.viewType, model.notebook, this.eventDispatcher, this.getLayoutInfo()); - this.eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]); + if (tokenSource.token.isCancellationRequested) { + return; + } - this.updateForMetadata(); - this.localStore.add(this.eventDispatcher.onDidChangeMetadata(() => { - this.updateForMetadata(); + if (provider.kernel && (availableKernels.length + availableKernels2.length) > 0) { + this._notebookHasMultipleKernels!.set(true); + this.multipleKernelsAvailable = true; + } else if ((availableKernels.length + availableKernels2.length) > 1) { + this._notebookHasMultipleKernels!.set(true); + this.multipleKernelsAvailable = true; + } else { + this._notebookHasMultipleKernels!.set(false); + this.multipleKernelsAvailable = false; + } + + // @deprecated + if (provider && provider.kernel) { + // it has a builtin kernel, don't automatically choose a kernel + await this._loadKernelPreloads(provider.providerExtensionLocation, provider.kernel); + tokenSource.dispose(); + return; + } + + const activeKernelStillExist = [...availableKernels2, ...availableKernels].find(kernel => kernel.id === this.activeKernel?.id && this.activeKernel?.id !== undefined); + + if (activeKernelStillExist) { + // the kernel still exist, we don't want to modify the selection otherwise user's temporary preference is lost + return; + } + + if (availableKernels2.length) { + return this._setKernelsFromProviders(provider, availableKernels2, tokenSource); + } + + // the provider doesn't have a builtin kernel, choose a kernel + this.activeKernel = availableKernels[0]; + if (this.activeKernel) { + await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel); + } + + tokenSource.dispose(); + } + + private async _setKernelsFromProviders(provider: NotebookProviderInfo, kernels: INotebookKernelInfo2[], tokenSource: CancellationTokenSource) { + const rawAssociations = this.configurationService.getValue(notebookKernelProviderAssociationsSettingId) || []; + const userSetKernelProvider = rawAssociations.filter(e => e.viewType === this.viewModel?.viewType)[0]?.kernelProvider; + const memento = this._activeKernelMemento.getMemento(StorageScope.GLOBAL); + + if (userSetKernelProvider) { + const filteredKernels = kernels.filter(kernel => kernel.extension.value === userSetKernelProvider); + + if (filteredKernels.length) { + const cachedKernelId = memento[provider.id]; + this.activeKernel = + filteredKernels.find(kernel => kernel.isPreferred) + || filteredKernels.find(kernel => kernel.id === cachedKernelId) + || filteredKernels[0]; + } else { + this.activeKernel = undefined; + } + + if (this.activeKernel) { + await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel); + + if (tokenSource.token.isCancellationRequested) { + return; + } + + this._activeKernelResolvePromise = this.activeKernel.resolve(this.viewModel!.uri, this.getId(), tokenSource.token); + await this._activeKernelResolvePromise; + + if (tokenSource.token.isCancellationRequested) { + return; + } + } + + memento[provider.id] = this._activeKernel?.id; + this._activeKernelMemento.saveMemento(); + + tokenSource.dispose(); + return; + } + + // choose a preferred kernel + const kernelsFromSameExtension = kernels.filter(kernel => kernel.extension.value === provider.providerExtensionId); + if (kernelsFromSameExtension.length) { + const cachedKernelId = memento[provider.id]; + + const preferedKernel = kernelsFromSameExtension.find(kernel => kernel.isPreferred) + || kernelsFromSameExtension.find(kernel => kernel.id === cachedKernelId) + || kernelsFromSameExtension[0]; + this.activeKernel = preferedKernel; + await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel); + + if (tokenSource.token.isCancellationRequested) { + return; + } + + await preferedKernel.resolve(this.viewModel!.uri, this.getId(), tokenSource.token); + + if (tokenSource.token.isCancellationRequested) { + return; + } + + memento[provider.id] = this._activeKernel?.id; + this._activeKernelMemento.saveMemento(); + tokenSource.dispose(); + return; + } + + // the provider doesn't have a builtin kernel, choose a kernel + this.activeKernel = kernels[0]; + if (this.activeKernel) { + await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel); + if (tokenSource.token.isCancellationRequested) { + return; + } + + await this.activeKernel.resolve(this.viewModel!.uri, this.getId(), tokenSource.token); + if (tokenSource.token.isCancellationRequested) { + return; + } + } + + tokenSource.dispose(); + } + + private async _loadKernelPreloads(extensionLocation: URI, kernel: INotebookKernelInfoDto) { + if (kernel.preloads && kernel.preloads.length) { + await this._resolveWebview(); + this._webview?.updateKernelPreloads([extensionLocation], kernel.preloads.map(preload => URI.revive(preload))); + } + } + + private _updateForMetadata(): void { + const notebookMetadata = this.viewModel!.metadata; + this._editorEditable?.set(!!notebookMetadata?.editable); + this._editorRunnable?.set(!!notebookMetadata?.runnable); + DOM.toggleClass(this._overlayContainer, 'notebook-editor-editable', !!notebookMetadata?.editable); + DOM.toggleClass(this.getDomNode(), 'notebook-editor-editable', !!notebookMetadata?.editable); + + this._notebookExecuting?.set(notebookMetadata.runState === NotebookRunState.Running); + } + + private async _resolveWebview(): Promise { + if (!this.textModel) { + return null; + } + + if (this._webviewResolvePromise) { + return this._webviewResolvePromise; + } + + if (!this._webview) { + this._webview = this.instantiationService.createInstance(BackLayerWebView, this, this.getId(), this.textModel!.uri); + // attach the webview container to the DOM tree first + this._list?.rowsContainer.insertAdjacentElement('afterbegin', this._webview.element); + } + + this._webviewResolvePromise = new Promise(async resolve => { + await this._webview!.createWebview(); + this._webview!.webview!.onDidBlur(() => { + this._outputFocus?.set(false); + this.updateEditorFocus(); + + if (this._overlayContainer.contains(document.activeElement)) { + this._webiewFocused = false; + } + }); + this._webview!.webview!.onDidFocus(() => { + this._outputFocus?.set(true); + this.updateEditorFocus(); + this._onDidFocusEmitter.fire(); + + if (this._overlayContainer.contains(document.activeElement)) { + this._webiewFocused = true; + } + }); + + this._localStore.add(this._webview!.onMessage(({ message, forRenderer }) => { + if (this.viewModel) { + this.notebookService.onDidReceiveMessage(this.viewModel.viewType, this.getId(), forRenderer, message); + } + })); + + if (this.viewModel && this.viewModel!.renderers.size) { + this._webview?.updateRendererPreloads(this.viewModel!.renderers); + } + + this._webviewResolved = true; + + resolve(this._webview!); + }); + + return this._webviewResolvePromise; + } + + private async _createWebview(id: string, resource: URI): Promise { + this._webview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource); + // attach the webview container to the DOM tree first + this._list?.rowsContainer.insertAdjacentElement('afterbegin', this._webview.element); + } + + private async _attachModel(textModel: NotebookTextModel, viewState: INotebookEditorViewState | undefined) { + await this._createWebview(this.getId(), textModel.uri); + + this._eventDispatcher = new NotebookEventDispatcher(); + this.viewModel = this.instantiationService.createInstance(NotebookViewModel, textModel.viewType, textModel, this._eventDispatcher, this.getLayoutInfo()); + this._eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]); + + this._updateForMetadata(); + this._localStore.add(this._eventDispatcher.onDidChangeMetadata(() => { + this._updateForMetadata(); })); // restore view states, including contributions @@ -430,25 +850,41 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } } - this.webview?.updateRendererPreloads(this.viewModel.renderers); + if (this.viewModel.renderers.size) { + await this._resolveWebview(); + this._webview?.updateRendererPreloads(this.viewModel.renderers); + } - this.localStore.add(this.list!.onWillScroll(e => { - this.webview!.updateViewScrollTop(-e.scrollTop, []); - this.webviewTransparentCover!.style.top = `${e.scrollTop}px`; + this._localStore.add(this._list!.onWillScroll(e => { + if (!this._webviewResolved) { + return; + } + + this._webview?.updateViewScrollTop(-e.scrollTop, []); + this._webviewTransparentCover!.style.top = `${e.scrollTop}px`; })); - this.localStore.add(this.list!.onDidChangeContentHeight(() => { + this._localStore.add(this._list!.onDidChangeContentHeight(() => { DOM.scheduleAtNextAnimationFrame(() => { - const scrollTop = this.list?.scrollTop || 0; - const scrollHeight = this.list?.scrollHeight || 0; - this.webview!.element.style.height = `${scrollHeight}px`; + if (this._isDisposed) { + return; + } - if (this.webview?.insetMapping) { - let updateItems: { cell: CodeCellViewModel, output: IOutput, cellTop: number }[] = []; - let removedItems: IOutput[] = []; - this.webview?.insetMapping.forEach((value, key) => { + const scrollTop = this._list?.scrollTop || 0; + const scrollHeight = this._list?.scrollHeight || 0; + + if (!this._webviewResolved) { + return; + } + + this._webview!.element.style.height = `${scrollHeight}px`; + + if (this._webview?.insetMapping) { + const updateItems: { cell: CodeCellViewModel, output: IProcessedOutput, cellTop: number }[] = []; + const removedItems: IProcessedOutput[] = []; + this._webview?.insetMapping.forEach((value, key) => { const cell = value.cell; - const viewIndex = this.list?.getViewIndex(cell); + const viewIndex = this._list?.getViewIndex(cell); if (viewIndex === undefined) { return; @@ -459,8 +895,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor removedItems.push(key); } - const cellTop = this.list?.getAbsoluteTopOfElement(cell) || 0; - if (this.webview!.shouldUpdateInset(cell, key, cellTop)) { + const cellTop = this._list?.getAbsoluteTopOfElement(cell) || 0; + if (this._webview!.shouldUpdateInset(cell, key, cellTop)) { updateItems.push({ cell: cell, output: key, @@ -469,87 +905,93 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } }); - removedItems.forEach(output => this.webview?.removeInset(output)); + removedItems.forEach(output => this._webview?.removeInset(output)); if (updateItems.length) { - this.webview?.updateViewScrollTop(-scrollTop, updateItems); + this._webview?.updateViewScrollTop(-scrollTop, updateItems); } } }); })); - this.list!.attachViewModel(this.viewModel); - this.localStore.add(this.list!.onDidRemoveOutput(output => { + this._list!.attachViewModel(this.viewModel); + this._localStore.add(this._list!.onDidRemoveOutput(output => { this.removeInset(output); })); - this.localStore.add(this.list!.onDidHideOutput(output => { + this._localStore.add(this._list!.onDidHideOutput(output => { this.hideInset(output); })); - this.list!.layout(); + this._list!.layout(); + this._dndController?.clearGlobalDragState(); // restore list state at last, it must be after list layout this.restoreListViewState(viewState); } - private restoreListViewState(viewState: INotebookEditorViewState | undefined): void { + restoreListViewState(viewState: INotebookEditorViewState | undefined): void { if (viewState?.scrollPosition !== undefined) { - this.list!.scrollTop = viewState!.scrollPosition.top; - this.list!.scrollLeft = viewState!.scrollPosition.left; + this._list!.scrollTop = viewState!.scrollPosition.top; + this._list!.scrollLeft = viewState!.scrollPosition.left; } else { - this.list!.scrollTop = 0; - this.list!.scrollLeft = 0; + this._list!.scrollTop = 0; + this._list!.scrollLeft = 0; } const focusIdx = typeof viewState?.focus === 'number' ? viewState.focus : 0; - if (focusIdx < this.list!.length) { - this.list!.setFocus([focusIdx]); - this.list!.setSelection([focusIdx]); - } else if (this.list!.length > 0) { - this.list!.setFocus([0]); + if (focusIdx < this._list!.length) { + this._list!.setFocus([focusIdx]); + this._list!.setSelection([focusIdx]); + } else if (this._list!.length > 0) { + this._list!.setFocus([0]); } if (viewState?.editorFocused) { - this.list?.focusView(); - const cell = this.notebookViewModel?.viewCells[focusIdx]; + const cell = this._notebookViewModel?.viewCells[focusIdx]; if (cell) { cell.focusMode = CellFocusMode.Editor; } } } - getEditorViewState() { - const state = this.notebookViewModel!.geteEditorViewState(); - if (this.list) { - state.scrollPosition = { left: this.list.scrollLeft, top: this.list.scrollTop }; - let cellHeights: { [key: number]: number } = {}; + getEditorViewState(): INotebookEditorViewState { + const state = this._notebookViewModel?.getEditorViewState(); + if (!state) { + return { + editingCells: {}, + editorViewStates: {} + }; + } + + if (this._list) { + state.scrollPosition = { left: this._list.scrollLeft, top: this._list.scrollTop }; + const cellHeights: { [key: number]: number } = {}; for (let i = 0; i < this.viewModel!.length; i++) { const elm = this.viewModel!.viewCells[i] as CellViewModel; if (elm.cellKind === CellKind.Code) { cellHeights[i] = elm.layoutInfo.totalHeight; } else { - cellHeights[i] = 0; + cellHeights[i] = elm.layoutInfo.totalHeight; } } state.cellTotalHeights = cellHeights; - const focus = this.list.getFocus()[0]; + const focus = this._list.getFocus()[0]; if (typeof focus === 'number') { - const element = this.notebookViewModel!.viewCells[focus]; - const itemDOM = this.list?.domElementOfElement(element!); - let editorFocused = false; - if (document.activeElement && itemDOM && itemDOM.contains(document.activeElement)) { - editorFocused = true; - } + const element = this._notebookViewModel!.viewCells[focus]; + if (element) { + const itemDOM = this._list?.domElementOfElement(element); + const editorFocused = !!(document.activeElement && itemDOM && itemDOM.contains(document.activeElement)); - state.editorFocused = editorFocused; - state.focus = focus; + state.editorFocused = editorFocused; + state.focus = focus; + } } } // Save contribution view states - const contributionsState: { [key: string]: any } = {}; + const contributionsState: { [key: string]: unknown } = {}; const keys = Object.keys(this._contributions); for (const id of keys) { @@ -572,29 +1014,42 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor // return this.editorMemento.loadEditorState(this.group, input.resource); // } - layout(dimension: DOM.Dimension): void { - this.dimension = new DOM.Dimension(dimension.width, dimension.height); - DOM.toggleClass(this._rootElement, 'mid-width', dimension.width < 1000 && dimension.width >= 600); - DOM.toggleClass(this._rootElement, 'narrow-width', dimension.width < 600); - DOM.size(this.body, dimension.width, dimension.height); - this.list?.updateOptions({ additionalScrollHeight: this.scrollBeyondLastLine ? dimension.height - SCROLLABLE_ELEMENT_PADDING_TOP : 0 }); - this.list?.layout(dimension.height - SCROLLABLE_ELEMENT_PADDING_TOP, dimension.width); - - this.overlayContainer.style.visibility = 'visible'; - this.overlayContainer.style.display = 'block'; - const containerRect = this._rootElement.getBoundingClientRect(); - this.overlayContainer.style.position = 'absolute'; - this.overlayContainer.style.top = `${containerRect.top}px`; - this.overlayContainer.style.left = `${containerRect.left}px`; - this.overlayContainer.style.width = `${dimension ? dimension.width : containerRect.width}px`; - this.overlayContainer.style.height = `${dimension ? dimension.height : containerRect.height}px`; - - if (this.webviewTransparentCover) { - this.webviewTransparentCover.style.height = `${dimension.height}px`; - this.webviewTransparentCover.style.width = `${dimension.width}px`; + layout(dimension: DOM.Dimension, shadowElement?: HTMLElement): void { + if (!shadowElement && this._shadowElementViewInfo === null) { + this._dimension = dimension; + return; } - this.eventDispatcher?.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]); + if (shadowElement) { + const containerRect = shadowElement.getBoundingClientRect(); + + this._shadowElementViewInfo = { + height: containerRect.height, + width: containerRect.width, + top: containerRect.top, + left: containerRect.left + }; + } + + this._dimension = new DOM.Dimension(dimension.width, dimension.height); + DOM.size(this._body, dimension.width, dimension.height); + this._list?.updateOptions({ additionalScrollHeight: this._scrollBeyondLastLine ? dimension.height - SCROLLABLE_ELEMENT_PADDING_TOP : 0 }); + this._list?.layout(dimension.height - SCROLLABLE_ELEMENT_PADDING_TOP, dimension.width); + + this._overlayContainer.style.visibility = 'visible'; + this._overlayContainer.style.display = 'block'; + this._overlayContainer.style.position = 'absolute'; + this._overlayContainer.style.top = `${this._shadowElementViewInfo!.top}px`; + this._overlayContainer.style.left = `${this._shadowElementViewInfo!.left}px`; + this._overlayContainer.style.width = `${dimension ? dimension.width : this._shadowElementViewInfo!.width}px`; + this._overlayContainer.style.height = `${dimension ? dimension.height : this._shadowElementViewInfo!.height}px`; + + if (this._webviewTransparentCover) { + this._webviewTransparentCover.style.height = `${dimension.height}px`; + this._webviewTransparentCover.style.width = `${dimension.width}px`; + } + + this._eventDispatcher?.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]); } // protected saveState(): void { @@ -610,56 +1065,56 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor //#region Editor Features selectElement(cell: ICellViewModel) { - this.list?.selectElement(cell); + this._list?.selectElement(cell); // this.viewModel!.selectionHandles = [cell.handle]; } revealInView(cell: ICellViewModel) { - this.list?.revealElementInView(cell); + this._list?.revealElementInView(cell); } revealInCenterIfOutsideViewport(cell: ICellViewModel) { - this.list?.revealElementInCenterIfOutsideViewport(cell); + this._list?.revealElementInCenterIfOutsideViewport(cell); } revealInCenter(cell: ICellViewModel) { - this.list?.revealElementInCenter(cell); + this._list?.revealElementInCenter(cell); } - revealLineInView(cell: ICellViewModel, line: number): void { - this.list?.revealElementLineInView(cell, line); + async revealLineInViewAsync(cell: ICellViewModel, line: number): Promise { + return this._list?.revealElementLineInViewAsync(cell, line); } - revealLineInCenter(cell: ICellViewModel, line: number) { - this.list?.revealElementLineInCenter(cell, line); + async revealLineInCenterAsync(cell: ICellViewModel, line: number): Promise { + return this._list?.revealElementLineInCenterAsync(cell, line); } - revealLineInCenterIfOutsideViewport(cell: ICellViewModel, line: number) { - this.list?.revealElementLineInCenterIfOutsideViewport(cell, line); + async revealLineInCenterIfOutsideViewportAsync(cell: ICellViewModel, line: number): Promise { + return this._list?.revealElementLineInCenterIfOutsideViewportAsync(cell, line); } - revealRangeInView(cell: ICellViewModel, range: Range): void { - this.list?.revealElementRangeInView(cell, range); + async revealRangeInViewAsync(cell: ICellViewModel, range: Range): Promise { + return this._list?.revealElementRangeInViewAsync(cell, range); } - revealRangeInCenter(cell: ICellViewModel, range: Range): void { - this.list?.revealElementRangeInCenter(cell, range); + async revealRangeInCenterAsync(cell: ICellViewModel, range: Range): Promise { + return this._list?.revealElementRangeInCenterAsync(cell, range); } - revealRangeInCenterIfOutsideViewport(cell: ICellViewModel, range: Range): void { - this.list?.revealElementRangeInCenterIfOutsideViewport(cell, range); + async revealRangeInCenterIfOutsideViewportAsync(cell: ICellViewModel, range: Range): Promise { + return this._list?.revealElementRangeInCenterIfOutsideViewportAsync(cell, range); } setCellSelection(cell: ICellViewModel, range: Range): void { - this.list?.setCellSelection(cell, range); + this._list?.setCellSelection(cell, range); } - changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => any): any { - return this.notebookViewModel?.changeDecorations(callback); + changeModelDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): T | null { + return this._notebookViewModel?.changeModelDecorations(callback) || null; } setHiddenAreas(_ranges: ICellRange[]): boolean { - return this.list!.setHiddenAreas(_ranges, true); + return this._list!.setHiddenAreas(_ranges, true); } //#endregion @@ -671,294 +1126,203 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private readonly _onMouseDown: Emitter = this._register(new Emitter()); public readonly onMouseDown: Event = this._onMouseDown.event; + private pendingLayouts = new WeakMap(); + //#endregion //#region Cell operations async layoutNotebookCell(cell: ICellViewModel, height: number): Promise { - const viewIndex = this.list!.getViewIndex(cell); + const viewIndex = this._list!.getViewIndex(cell); if (viewIndex === undefined) { // the cell is hidden return; } - let relayout = (cell: ICellViewModel, height: number) => { - this.list?.updateElementHeight2(cell, height); + const relayout = (cell: ICellViewModel, height: number) => { + if (this._isDisposed) { + return; + } + + this._list?.updateElementHeight2(cell, height); }; + if (this.pendingLayouts.has(cell)) { + this.pendingLayouts.get(cell)!.dispose(); + } + let r: () => void; - DOM.scheduleAtNextAnimationFrame(() => { + const layoutDisposable = DOM.scheduleAtNextAnimationFrame(() => { + if (this._isDisposed) { + return; + } + + this.pendingLayouts.delete(cell); + relayout(cell, height); r(); }); + this.pendingLayouts.set(cell, toDisposable(() => { + layoutDisposable.dispose(); + r(); + })); + return new Promise(resolve => { r = resolve; }); } insertNotebookCell(cell: ICellViewModel | undefined, type: CellKind, direction: 'above' | 'below' = 'above', initialText: string = '', ui: boolean = false): CellViewModel | null { - if (!this.notebookViewModel!.metadata.editable) { + if (!this._notebookViewModel!.metadata.editable) { return null; } - const newLanguages = this.notebookViewModel!.languages; - const language = (type === CellKind.Code && newLanguages && newLanguages.length) ? newLanguages[0] : 'markdown'; - const index = cell ? this.notebookViewModel!.getCellIndex(cell) : 0; - const nextIndex = ui ? this.notebookViewModel!.getNextVisibleCellIndex(index) : index + 1; + const index = cell ? this._notebookViewModel!.getCellIndex(cell) : 0; + const nextIndex = ui ? this._notebookViewModel!.getNextVisibleCellIndex(index) : index + 1; + const newLanguages = this._notebookViewModel!.languages; + const language = (cell?.cellKind === CellKind.Code && type === CellKind.Code) + ? cell.language + : ((type === CellKind.Code && newLanguages && newLanguages.length) ? newLanguages[0] : 'markdown'); const insertIndex = cell ? (direction === 'above' ? index : nextIndex) : index; - const newCell = this.notebookViewModel!.createCell(insertIndex, initialText.split(/\r?\n/g), language, type, true); - return newCell; - } - - private pushIfAbsent(positions: IPosition[], p: IPosition) { - const last = positions.length > 0 ? positions[positions.length - 1] : undefined; - if (!last || last.lineNumber !== p.lineNumber || last.column !== p.column) { - positions.push(p); - } - } - - /** - * Add split point at the beginning and the end; - * Move end of line split points to the beginning of the next line; - * Avoid duplicate split points - */ - private splitPointsToBoundaries(splitPoints: IPosition[], textBuffer: IReadonlyTextBuffer): IPosition[] | null { - const boundaries: IPosition[] = []; - const lineCnt = textBuffer.getLineCount(); - const getLineLen = (lineNumber: number) => { - return textBuffer.getLineLength(lineNumber); - }; - - // split points need to be sorted - splitPoints = splitPoints.sort((l, r) => { - const lineDiff = l.lineNumber - r.lineNumber; - const columnDiff = l.column - r.column; - return lineDiff !== 0 ? lineDiff : columnDiff; - }); - - // eat-up any split point at the beginning, i.e. we ignore the split point at the very beginning - this.pushIfAbsent(boundaries, new Position(1, 1)); - - for (let sp of splitPoints) { - if (getLineLen(sp.lineNumber) + 1 === sp.column && sp.lineNumber < lineCnt) { - sp = new Position(sp.lineNumber + 1, 1); - } - this.pushIfAbsent(boundaries, sp); - } - - // eat-up any split point at the beginning, i.e. we ignore the split point at the very end - this.pushIfAbsent(boundaries, new Position(lineCnt, getLineLen(lineCnt) + 1)); - - // if we only have two then they describe the whole range and nothing needs to be split - return boundaries.length > 2 ? boundaries : null; - } - - private computeCellLinesContents(cell: IEditableCellViewModel, splitPoints: IPosition[]): string[] | null { - const rangeBoundaries = this.splitPointsToBoundaries(splitPoints, cell.textBuffer); - if (!rangeBoundaries) { - return null; - } - const newLineModels: string[] = []; - for (let i = 1; i < rangeBoundaries.length; i++) { - const start = rangeBoundaries[i - 1]; - const end = rangeBoundaries[i]; - - newLineModels.push(cell.textModel.getValueInRange(new Range(start.lineNumber, start.column, end.lineNumber, end.column))); - } - - return newLineModels; + const newCell = this._notebookViewModel!.createCell(insertIndex, initialText.split(/\r?\n/g), language, type, undefined, true); + return newCell as CellViewModel; } async splitNotebookCell(cell: ICellViewModel): Promise { - if (!this.notebookViewModel!.metadata.editable) { - return null; - } + const index = this._notebookViewModel!.getCellIndex(cell); - let splitPoints = cell.getSelectionsStartPosition(); - if (splitPoints && splitPoints.length > 0) { - await cell.resolveTextModel(); - - if (!cell.hasModel()) { - return null; - } - - let newLinesContents = this.computeCellLinesContents(cell, splitPoints); - if (newLinesContents) { - - // update the contents of the first cell - cell.textModel.applyEdits([ - { range: cell.textModel.getFullModelRange(), text: newLinesContents[0] } - ], true); - - // create new cells based on the new text models - const language = cell.model.language; - const kind = cell.cellKind; - let insertIndex = this.notebookViewModel!.getCellIndex(cell) + 1; - const newCells = []; - for (let j = 1; j < newLinesContents.length; j++, insertIndex++) { - newCells.push(this.notebookViewModel!.createCell(insertIndex, newLinesContents[j], language, kind, true)); - } - return newCells; - } - } - - return null; + return this._notebookViewModel!.splitNotebookCell(index); } async joinNotebookCells(cell: ICellViewModel, direction: 'above' | 'below', constraint?: CellKind): Promise { - if (!this.notebookViewModel!.metadata.editable) { - return null; - } + const index = this._notebookViewModel!.getCellIndex(cell); + const ret = await this._notebookViewModel!.joinNotebookCells(index, direction, constraint); - if (constraint && cell.cellKind !== constraint) { - return null; - } + if (ret) { + ret.deletedCells.forEach(cell => { + if (this.pendingLayouts.has(cell)) { + this.pendingLayouts.get(cell)!.dispose(); + } + }); - const index = this.notebookViewModel!.getCellIndex(cell); - if (index === 0 && direction === 'above') { - return null; - } - - if (index === this.notebookViewModel!.length - 1 && direction === 'below') { - return null; - } - - if (direction === 'above') { - const above = this.notebookViewModel!.viewCells[index - 1]; - if (constraint && above.cellKind !== constraint) { - return null; - } - - await above.resolveTextModel(); - if (!above.hasModel()) { - return null; - } - - const insertContent = cell.getText(); - const aboveCellLineCount = above.textModel.getLineCount(); - const aboveCellLastLineEndColumn = above.textModel.getLineLength(aboveCellLineCount); - above.textModel.applyEdits([ - { range: new Range(aboveCellLineCount, aboveCellLastLineEndColumn + 1, aboveCellLineCount, aboveCellLastLineEndColumn + 1), text: insertContent } - ]); - - await this.deleteNotebookCell(cell); - return above; + return ret.cell; } else { - const below = this.notebookViewModel!.viewCells[index + 1]; - if (constraint && below.cellKind !== constraint) { - return null; - } - - await cell.resolveTextModel(); - if (!cell.hasModel()) { - return null; - } - - const insertContent = below.getText(); - - const cellLineCount = cell.textModel.getLineCount(); - const cellLastLineEndColumn = cell.textModel.getLineLength(cellLineCount); - cell.textModel.applyEdits([ - { range: new Range(cellLineCount, cellLastLineEndColumn + 1, cellLineCount, cellLastLineEndColumn + 1), text: insertContent } - ]); - - await this.deleteNotebookCell(below); - return cell; + return null; } } async deleteNotebookCell(cell: ICellViewModel): Promise { - if (!this.notebookViewModel!.metadata.editable) { + if (!this._notebookViewModel!.metadata.editable) { return false; } - const index = this.notebookViewModel!.getCellIndex(cell); - this.notebookViewModel!.deleteCell(index, true); + if (this.pendingLayouts.has(cell)) { + this.pendingLayouts.get(cell)!.dispose(); + } + + const index = this._notebookViewModel!.getCellIndex(cell); + this._notebookViewModel!.deleteCell(index, true); return true; } - async moveCellDown(cell: ICellViewModel): Promise { - if (!this.notebookViewModel!.metadata.editable) { - return false; + async moveCellDown(cell: ICellViewModel): Promise { + if (!this._notebookViewModel!.metadata.editable) { + return null; } - const index = this.notebookViewModel!.getCellIndex(cell); - if (index === this.notebookViewModel!.length - 1) { - return false; + const index = this._notebookViewModel!.getCellIndex(cell); + if (index === this._notebookViewModel!.length - 1) { + return null; } - const newIdx = index + 1; - return this.moveCellToIndex(index, newIdx); + const newIdx = index + 2; // This is the adjustment for the index before the cell has been "removed" from its original index + return this._moveCellToIndex(index, newIdx); } - async moveCellUp(cell: ICellViewModel): Promise { - if (!this.notebookViewModel!.metadata.editable) { - return false; + async moveCellUp(cell: ICellViewModel): Promise { + if (!this._notebookViewModel!.metadata.editable) { + return null; } - const index = this.notebookViewModel!.getCellIndex(cell); + const index = this._notebookViewModel!.getCellIndex(cell); if (index === 0) { - return false; + return null; } const newIdx = index - 1; - return this.moveCellToIndex(index, newIdx); + return this._moveCellToIndex(index, newIdx); } - async moveCell(cell: ICellViewModel, relativeToCell: ICellViewModel, direction: 'above' | 'below'): Promise { - if (!this.notebookViewModel!.metadata.editable) { - return false; + async moveCell(cell: ICellViewModel, relativeToCell: ICellViewModel, direction: 'above' | 'below'): Promise { + if (!this._notebookViewModel!.metadata.editable) { + return null; } if (cell === relativeToCell) { - return false; + return null; } - const originalIdx = this.notebookViewModel!.getCellIndex(cell); - const relativeToIndex = this.notebookViewModel!.getCellIndex(relativeToCell); + const originalIdx = this._notebookViewModel!.getCellIndex(cell); + const relativeToIndex = this._notebookViewModel!.getCellIndex(relativeToCell); - let newIdx = direction === 'above' ? relativeToIndex : relativeToIndex + 1; - if (originalIdx < newIdx) { + const newIdx = direction === 'above' ? relativeToIndex : relativeToIndex + 1; + return this._moveCellToIndex(originalIdx, newIdx); + } + + async moveCellToIdx(cell: ICellViewModel, index: number): Promise { + if (!this._notebookViewModel!.metadata.editable) { + return null; + } + + const originalIdx = this._notebookViewModel!.getCellIndex(cell); + return this._moveCellToIndex(originalIdx, index); + } + + /** + * @param index The current index of the cell + * @param newIdx The desired index, in an index scheme for the state of the tree before the current cell has been "removed". + * @example to move the cell from index 0 down one spot, call with (0, 2) + */ + private async _moveCellToIndex(index: number, newIdx: number): Promise { + if (index < newIdx) { + // The cell is moving "down", it will free up one index spot and consume a new one newIdx--; } - return this.moveCellToIndex(originalIdx, newIdx); - } - - private async moveCellToIndex(index: number, newIdx: number): Promise { if (index === newIdx) { - return false; + return null; } - if (!this.notebookViewModel!.moveCellToIdx(index, newIdx, true)) { + if (!this._notebookViewModel!.moveCellToIdx(index, newIdx, true)) { throw new Error('Notebook Editor move cell, index out of range'); } - let r: (val: boolean) => void; + let r: (val: ICellViewModel | null) => void; DOM.scheduleAtNextAnimationFrame(() => { - this.list?.revealElementInView(this.notebookViewModel!.viewCells[newIdx]); - r(true); + if (this._isDisposed) { + r(null); + } + + const viewCell = this._notebookViewModel!.viewCells[newIdx]; + this._list?.revealElementInView(viewCell); + r(viewCell); }); return new Promise(resolve => { r = resolve; }); } editNotebookCell(cell: CellViewModel): void { - if (!cell.getEvaluatedMetadata(this.notebookViewModel!.metadata).editable) { + if (!cell.getEvaluatedMetadata(this._notebookViewModel!.metadata).editable) { return; } cell.editState = CellEditState.Editing; - this.renderedEditors.get(cell)?.focus(); - } - - saveNotebookCell(cell: ICellViewModel): void { - cell.editState = CellEditState.Preview; + this._renderedEditors.get(cell)?.focus(); } getActiveCell() { - let elements = this.list?.getFocusedElements(); + const elements = this._list?.getFocusedElements(); if (elements && elements.length) { return elements[0]; @@ -967,107 +1331,149 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return undefined; } - cancelNotebookExecution(): void { - if (!this.notebookViewModel!.currentTokenSource) { - throw new Error('Notebook is not executing'); + async cancelNotebookExecution(): Promise { + if (this._notebookViewModel?.metadata.runState !== NotebookRunState.Running) { + return; } + return this._cancelNotebookExecution(); + } - this.notebookViewModel!.currentTokenSource.cancel(); - this.notebookViewModel!.currentTokenSource = undefined; + private async _cancelNotebookExecution(): Promise { + const provider = this.notebookService.getContributedNotebookProviders(this.viewModel!.uri)[0]; + if (provider) { + const viewType = provider.id; + const notebookUri = this._notebookViewModel!.uri; + + if (this._activeKernel) { + await (this._activeKernel as INotebookKernelInfo2).cancelNotebookCell!(this._notebookViewModel!.uri, undefined); + } else if (provider.kernel) { + return await this.notebookService.cancelNotebook(viewType, notebookUri); + } + } } async executeNotebook(): Promise { - if (!this.notebookViewModel!.metadata.runnable) { + if (!this._notebookViewModel!.metadata.runnable) { return; } - // return this.progressService.showWhile(this._executeNotebook()); return this._executeNotebook(); } - async _executeNotebook(): Promise { - if (this.notebookViewModel!.currentTokenSource) { - return; - } + private async _executeNotebook(): Promise { + const provider = this.notebookService.getContributedNotebookProviders(this.viewModel!.uri)[0]; + if (provider) { + const viewType = provider.id; + const notebookUri = this._notebookViewModel!.uri; - const tokenSource = new CancellationTokenSource(); - try { - this.editorExecutingNotebook!.set(true); - this.notebookViewModel!.currentTokenSource = tokenSource; + if (this._activeKernel) { + // TODO@rebornix temp any cast, should be removed once we remove legacy kernel support + if ((this._activeKernel as INotebookKernelInfo2).executeNotebookCell) { + if (this._activeKernelResolvePromise) { + await this._activeKernelResolvePromise; + } - for (let cell of this.notebookViewModel!.viewCells) { - if (cell.cellKind === CellKind.Code) { - await this._executeNotebookCell(cell, tokenSource); + await (this._activeKernel as INotebookKernelInfo2).executeNotebookCell!(this._notebookViewModel!.uri, undefined); + } else { + await this.notebookService.executeNotebook2(this._notebookViewModel!.viewType, this._notebookViewModel!.uri, this._activeKernel.id); } + } else if (provider.kernel) { + return await this.notebookService.executeNotebook(viewType, notebookUri); } - } finally { - this.editorExecutingNotebook!.set(false); - this.notebookViewModel!.currentTokenSource = undefined; - tokenSource.dispose(); } } - cancelNotebookCellExecution(cell: ICellViewModel): void { - if (!cell.currentTokenSource) { - throw new Error('Cell is not executing'); + async cancelNotebookCellExecution(cell: ICellViewModel): Promise { + if (cell.cellKind !== CellKind.Code) { + return; } - cell.currentTokenSource.cancel(); - cell.currentTokenSource = undefined; + const metadata = cell.getEvaluatedMetadata(this._notebookViewModel!.metadata); + if (!metadata.runnable) { + return; + } + + if (metadata.runState !== NotebookCellRunState.Running) { + return; + } + + await this._cancelNotebookCell(cell); + } + + private async _cancelNotebookCell(cell: ICellViewModel): Promise { + const provider = this.notebookService.getContributedNotebookProviders(this.viewModel!.uri)[0]; + if (provider) { + const viewType = provider.id; + const notebookUri = this._notebookViewModel!.uri; + + if (this._activeKernel) { + return await (this._activeKernel as INotebookKernelInfo2).cancelNotebookCell!(this._notebookViewModel!.uri, cell.handle); + } else if (provider.kernel) { + return await this.notebookService.cancelNotebookCell(viewType, notebookUri, cell.handle); + } + } } async executeNotebookCell(cell: ICellViewModel): Promise { - if (!cell.getEvaluatedMetadata(this.notebookViewModel!.metadata).runnable) { + if (cell.cellKind === CellKind.Markdown) { + this.focusNotebookCell(cell, 'container'); return; } - const tokenSource = new CancellationTokenSource(); - try { - this._executeNotebookCell(cell, tokenSource); - } finally { - tokenSource.dispose(); + if (!cell.getEvaluatedMetadata(this._notebookViewModel!.metadata).runnable) { + return; } + + await this._executeNotebookCell(cell); } - private async _executeNotebookCell(cell: ICellViewModel, tokenSource: CancellationTokenSource): Promise { - try { - cell.currentTokenSource = tokenSource; - const provider = this.notebookService.getContributedNotebookProviders(this.viewModel!.uri)[0]; - if (provider) { - const viewType = provider.id; - const notebookUri = CellUri.parse(cell.uri)?.notebook; - if (notebookUri) { - return await this.notebookService.executeNotebookCell(viewType, notebookUri, cell.handle, tokenSource.token); + private async _executeNotebookCell(cell: ICellViewModel): Promise { + const provider = this.notebookService.getContributedNotebookProviders(this.viewModel!.uri)[0]; + if (provider) { + const viewType = provider.id; + const notebookUri = this._notebookViewModel!.uri; + + if (this._activeKernel) { + // TODO@rebornix temp any cast, should be removed once we remove legacy kernel support + if ((this._activeKernel as INotebookKernelInfo2).executeNotebookCell) { + await (this._activeKernel as INotebookKernelInfo2).executeNotebookCell!(this._notebookViewModel!.uri, cell.handle); + } else { + + return await this.notebookService.executeNotebookCell2(viewType, notebookUri, cell.handle, this._activeKernel.id); } + } else if (provider.kernel) { + return await this.notebookService.executeNotebookCell(viewType, notebookUri, cell.handle); } - } finally { - cell.currentTokenSource = undefined; } } focusNotebookCell(cell: ICellViewModel, focusItem: 'editor' | 'container' | 'output') { + if (this._isDisposed) { + return; + } + if (focusItem === 'editor') { this.selectElement(cell); - this.list?.focusView(); + this._list?.focusView(); cell.editState = CellEditState.Editing; cell.focusMode = CellFocusMode.Editor; this.revealInCenterIfOutsideViewport(cell); } else if (focusItem === 'output') { this.selectElement(cell); - this.list?.focusView(); + this._list?.focusView(); - if (!this.webview) { + if (!this._webview) { return; } - this.webview.focusOutput(cell.id); + this._webview.focusOutput(cell.id); cell.editState = CellEditState.Preview; cell.focusMode = CellFocusMode.Container; this.revealInCenterIfOutsideViewport(cell); } else { - let itemDOM = this.list?.domElementOfElement(cell); + const itemDOM = this._list?.domElementOfElement(cell); if (document.activeElement && itemDOM && itemDOM.contains(document.activeElement)) { (document.activeElement as HTMLElement).blur(); } @@ -1077,7 +1483,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this.selectElement(cell); this.revealInCenterIfOutsideViewport(cell); - this.list?.focusView(); + this._list?.focusView(); } } @@ -1085,64 +1491,95 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor //#region MISC + deltaCellDecorations(oldDecorations: string[], newDecorations: INotebookDeltaDecoration[]): string[] { + return this._notebookViewModel?.deltaCellDecorations(oldDecorations, newDecorations) || []; + } + + deltaCellOutputContainerClassNames(cellId: string, added: string[], removed: string[]) { + this._webview?.deltaCellOutputContainerClassNames(cellId, added, removed); + } + getLayoutInfo(): NotebookLayoutInfo { - if (!this.list) { + if (!this._list) { throw new Error('Editor is not initalized successfully'); } return { - width: this.dimension!.width, - height: this.dimension!.height, - fontInfo: this.fontInfo! + width: this._dimension!.width, + height: this._dimension!.height, + fontInfo: this._fontInfo! }; } triggerScroll(event: IMouseWheelEvent) { - this.list?.triggerScrollFromMouseWheelEvent(event); + this._list?.triggerScrollFromMouseWheelEvent(event); } - createInset(cell: CodeCellViewModel, output: IOutput, shadowContent: string, offset: number) { - if (!this.webview) { + async createInset(cell: CodeCellViewModel, output: IProcessedOutput, shadowContent: string, offset: number) { + if (!this._webview) { return; } - let preloads = this.notebookViewModel!.renderers; + await this._resolveWebview(); - if (!this.webview!.insetMapping.has(output)) { - let cellTop = this.list?.getAbsoluteTopOfElement(cell) || 0; - this.webview!.createInset(cell, output, cellTop, offset, shadowContent, preloads); + const preloads = this._notebookViewModel!.renderers; + + if (!this._webview!.insetMapping.has(output)) { + const cellTop = this._list?.getAbsoluteTopOfElement(cell) || 0; + await this._webview!.createInset(cell, output, cellTop, offset, shadowContent, preloads); } else { - let cellTop = this.list?.getAbsoluteTopOfElement(cell) || 0; - let scrollTop = this.list?.scrollTop || 0; + const cellTop = this._list?.getAbsoluteTopOfElement(cell) || 0; + const scrollTop = this._list?.scrollTop || 0; - this.webview!.updateViewScrollTop(-scrollTop, [{ cell: cell, output: output, cellTop: cellTop }]); + this._webview!.updateViewScrollTop(-scrollTop, [{ cell: cell, output: output, cellTop: cellTop }]); } } - removeInset(output: IOutput) { - if (!this.webview) { + removeInset(output: IProcessedOutput) { + if (!this._webview || !this._webviewResolved) { return; } - this.webview!.removeInset(output); + this._webview!.removeInset(output); } - hideInset(output: IOutput) { - if (!this.webview) { + hideInset(output: IProcessedOutput) { + if (!this._webview || !this._webviewResolved) { return; } - this.webview!.hideInset(output); + this._webview!.hideInset(output); } getOutputRenderer(): OutputRenderer { - return this.outputRenderer; + return this._outputRenderer; } - postMessage(message: any) { - this.webview?.webview.sendMessage(message); + postMessage(forRendererId: string | undefined, message: any) { + if (!this._webview || !this._webviewResolved) { + return; + } + + if (forRendererId === undefined) { + this._webview.webview?.postMessage(message); + } else { + this._webview.postRendererMessage(forRendererId, message); + } } + toggleClassName(className: string) { + DOM.toggleClass(this._overlayContainer, className); + } + + addClassName(className: string) { + DOM.addClass(this._overlayContainer, className); + } + + removeClassName(className: string) { + DOM.removeClass(this._overlayContainer, className); + } + + //#endregion //#region Editor Contributions @@ -1153,46 +1590,132 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor //#endregion dispose() { + this._isDisposed = true; + // dispose webview first + this._webview?.dispose(); + + this.notebookService.removeNotebookEditor(this); const keys = Object.keys(this._contributions); for (let i = 0, len = keys.length; i < len; i++) { const contributionId = keys[i]; this._contributions[contributionId].dispose(); } - this._layoutService.container.removeChild(this.overlayContainer); + this._localStore.clear(); + this._list?.dispose(); + + this._overlayContainer.remove(); + this.viewModel?.dispose(); + + // this._layoutService.container.removeChild(this.overlayContainer); super.dispose(); } - toJSON(): any { + toJSON(): object { return { notebookHandle: this.viewModel?.handle }; } } -const embeddedEditorBackground = 'walkThrough.embeddedEditorBackground'; +export const notebookCellBorder = registerColor('notebook.cellBorderColor', { + dark: transparent(PANEL_BORDER, .4), + light: transparent(listInactiveSelectionBackground, 1), + hc: PANEL_BORDER +}, nls.localize('notebook.cellBorderColor', "The border color for notebook cells.")); -export const focusedCellIndicator = registerColor('notebook.focusedCellIndicator', { - light: new Color(new RGBA(102, 175, 224)), - dark: new Color(new RGBA(12, 125, 157)), - hc: new Color(new RGBA(0, 73, 122)) -}, nls.localize('notebook.focusedCellIndicator', "The color of the focused notebook cell indicator.")); +export const focusedEditorBorderColor = registerColor('notebook.focusedEditorBorder', { + light: focusBorder, + dark: focusBorder, + hc: focusBorder +}, nls.localize('notebook.focusedEditorBorder', "The color of the notebook cell editor border.")); + +export const cellStatusIconSuccess = registerColor('notebookStatusSuccessIcon.foreground', { + light: debugIconStartForeground, + dark: debugIconStartForeground, + hc: debugIconStartForeground +}, nls.localize('notebookStatusSuccessIcon.foreground', "The error icon color of notebook cells in the cell status bar.")); + +export const cellStatusIconError = registerColor('notebookStatusErrorIcon.foreground', { + light: errorForeground, + dark: errorForeground, + hc: errorForeground +}, nls.localize('notebookStatusErrorIcon.foreground', "The error icon color of notebook cells in the cell status bar.")); + +export const cellStatusIconRunning = registerColor('notebookStatusRunningIcon.foreground', { + light: foreground, + dark: foreground, + hc: foreground +}, nls.localize('notebookStatusRunningIcon.foreground', "The running icon color of notebook cells in the cell status bar.")); export const notebookOutputContainerColor = registerColor('notebook.outputContainerBackgroundColor', { - dark: new Color(new RGBA(255, 255, 255, 0.06)), - light: new Color(new RGBA(237, 239, 249)), + dark: notebookCellBorder, + light: transparent(listFocusBackground, .4), hc: null -} - , nls.localize('notebook.outputContainerBackgroundColor', "The Color of the notebook output container background.")); +}, nls.localize('notebook.outputContainerBackgroundColor', "The Color of the notebook output container background.")); // TODO currently also used for toolbar border, if we keep all of this, pick a generic name -export const CELL_TOOLBAR_SEPERATOR = registerColor('notebook.cellToolbarSeperator', { +export const CELL_TOOLBAR_SEPERATOR = registerColor('notebook.cellToolbarSeparator', { dark: Color.fromHex('#808080').transparent(0.35), light: Color.fromHex('#808080').transparent(0.35), hc: contrastBorder -}, nls.localize('cellToolbarSeperator', "The color of seperator in Cell bottom toolbar")); +}, nls.localize('notebook.cellToolbarSeparator', "The color of the seperator in the cell bottom toolbar")); +export const focusedCellBackground = registerColor('notebook.focusedCellBackground', { + dark: transparent(PANEL_BORDER, .4), + light: transparent(listFocusBackground, .4), + hc: null +}, nls.localize('focusedCellBackground', "The background color of a cell when the cell is focused.")); + +export const cellHoverBackground = registerColor('notebook.cellHoverBackground', { + dark: transparent(focusedCellBackground, .5), + light: transparent(focusedCellBackground, .7), + hc: null +}, nls.localize('notebook.cellHoverBackground', "The background color of a cell when the cell is hovered.")); + +export const focusedCellBorder = registerColor('notebook.focusedCellBorder', { + dark: Color.white.transparent(0.12), + light: Color.black.transparent(0.12), + hc: focusBorder +}, nls.localize('notebook.focusedCellBorder', "The color of the cell's top and bottom border when the cell is focused.")); + +export const cellStatusBarItemHover = registerColor('notebook.cellStatusBarItemHoverBackground', { + light: new Color(new RGBA(0, 0, 0, 0.08)), + dark: new Color(new RGBA(255, 255, 255, 0.15)), + hc: new Color(new RGBA(255, 255, 255, 0.15)), +}, nls.localize('notebook.cellStatusBarItemHoverBackground', "The background color of notebook cell status bar items.")); + +export const cellInsertionIndicator = registerColor('notebook.cellInsertionIndicator', { + light: focusBorder, + dark: focusBorder, + hc: focusBorder +}, nls.localize('notebook.cellInsertionIndicator', "The color of the notebook cell insertion indicator.")); + + +export const listScrollbarSliderBackground = registerColor('notebookScrollbarSlider.background', { + dark: scrollbarSliderBackground, + light: scrollbarSliderBackground, + hc: scrollbarSliderBackground +}, nls.localize('notebookScrollbarSliderBackground', "Notebook scrollbar slider background color.")); + +export const listScrollbarSliderHoverBackground = registerColor('notebookScrollbarSlider.hoverBackground', { + dark: scrollbarSliderHoverBackground, + light: scrollbarSliderHoverBackground, + hc: scrollbarSliderHoverBackground +}, nls.localize('notebookScrollbarSliderHoverBackground', "Notebook scrollbar slider background color when hovering.")); + +export const listScrollbarSliderActiveBackground = registerColor('notebookScrollbarSlider.activeBackground', { + dark: scrollbarSliderActiveBackground, + light: scrollbarSliderActiveBackground, + hc: scrollbarSliderActiveBackground +}, nls.localize('notebookScrollbarSliderActiveBackground', "Notebook scrollbar slider background color when clicked on.")); + +export const cellSymbolHighlight = registerColor('notebook.symbolHighlightBackground', { + dark: Color.fromHex('#ffffff0b'), + light: Color.fromHex('#fdff0033'), + hc: null +}, nls.localize('notebook.symbolHighlightBackground', "Background color of highlighted cell")); registerThemingParticipant((theme, collector) => { collector.addRule(`.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element { @@ -1200,7 +1723,8 @@ registerThemingParticipant((theme, collector) => { box-sizing: border-box; }`); - const color = getExtraColor(theme, embeddedEditorBackground, { dark: 'rgba(0, 0, 0, .4)', extra_dark: 'rgba(200, 235, 255, .064)', light: '#f4f4f4', hc: null }); + // const color = getExtraColor(theme, embeddedEditorBackground, { dark: 'rgba(0, 0, 0, .4)', extra_dark: 'rgba(200, 235, 255, .064)', light: '#f4f4f4', hc: null }); + const color = theme.getColor(editorBackground); if (color) { collector.addRule(`.notebookOverlay .cell .monaco-editor-background, .notebookOverlay .cell .margin-view-overlays, @@ -1238,6 +1762,7 @@ registerThemingParticipant((theme, collector) => { const containerBackground = theme.getColor(notebookOutputContainerColor); if (containerBackground) { collector.addRule(`.notebookOverlay .output { background-color: ${containerBackground}; }`); + collector.addRule(`.notebookOverlay .output-element { background-color: ${containerBackground}; }`); } const editorBackgroundColor = theme.getColor(editorBackground); @@ -1245,42 +1770,122 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.notebookOverlay .cell-statusbar-container { border-top: solid 1px ${editorBackgroundColor}; }`); collector.addRule(`.notebookOverlay .monaco-list-row > .monaco-toolbar { background-color: ${editorBackgroundColor}; }`); collector.addRule(`.notebookOverlay .monaco-list-row.cell-drag-image { background-color: ${editorBackgroundColor}; }`); + collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container .action-item { background-color: ${editorBackgroundColor} }`); } const cellToolbarSeperator = theme.getColor(CELL_TOOLBAR_SEPERATOR); if (cellToolbarSeperator) { - collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container .seperator { background-color: ${cellToolbarSeperator} }`); - collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container .seperator-short { background-color: ${cellToolbarSeperator} }`); collector.addRule(`.notebookOverlay .monaco-list-row > .monaco-toolbar { border: solid 1px ${cellToolbarSeperator}; }`); - collector.addRule(`.notebookOverlay .monaco-list-row:hover .notebook-cell-focus-indicator, - .notebookOverlay .monaco-list-row.cell-output-hover .notebook-cell-focus-indicator { border-color: ${cellToolbarSeperator}; }`); + collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container .action-item { border: solid 1px ${cellToolbarSeperator} }`); + collector.addRule(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-collapsed-part { border-bottom: solid 1px ${cellToolbarSeperator} }`); + collector.addRule(`.notebookOverlay .monaco-action-bar .action-item.verticalSeparator { background-color: ${cellToolbarSeperator} }`); } - const focusedCellIndicatorColor = theme.getColor(focusedCellIndicator); - if (focusedCellIndicatorColor) { - collector.addRule(`.notebookOverlay .monaco-list-row.focused .notebook-cell-focus-indicator { border-color: ${focusedCellIndicatorColor}; }`); - collector.addRule(`.notebookOverlay .monaco-list-row .notebook-cell-focus-indicator { border-color: ${focusedCellIndicatorColor}; }`); - collector.addRule(`.notebookOverlay .monaco-list-row .cell-insertion-indicator { background-color: ${focusedCellIndicatorColor}; }`); - collector.addRule(`.notebookOverlay .monaco-list-row.cell-editor-focus .cell-editor-part:before { outline: solid 1px ${focusedCellIndicatorColor}; }`); + const focusedCellBackgroundColor = theme.getColor(focusedCellBackground); + if (focusedCellBackgroundColor) { + collector.addRule(`.notebookOverlay .code-cell-row.focused .cell-focus-indicator, + .notebookOverlay .markdown-cell-row.focused { background-color: ${focusedCellBackgroundColor} !important; }`); + collector.addRule(`.notebookOverlay .code-cell-row.focused .cell-collapsed-part { background-color: ${focusedCellBackgroundColor} !important; }`); } - // const widgetShadowColor = theme.getColor(widgetShadow); - // if (widgetShadowColor) { - // collector.addRule(`.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .monaco-toolbar { - // box-shadow: 0 0 8px 4px ${widgetShadowColor} - // }`) - // } + const cellHoverBackgroundColor = theme.getColor(cellHoverBackground); + if (cellHoverBackgroundColor) { + collector.addRule(`.notebookOverlay .code-cell-row:not(.focused):hover .cell-focus-indicator, + .notebookOverlay .code-cell-row:not(.focused).cell-output-hover .cell-focus-indicator, + .notebookOverlay .markdown-cell-row:not(.focused):hover { background-color: ${cellHoverBackgroundColor} !important; }`); + collector.addRule(`.notebookOverlay .code-cell-row:not(.focused):hover .cell-collapsed-part, + .notebookOverlay .code-cell-row:not(.focused).cell-output-hover .cell-collapsed-part { background-color: ${cellHoverBackgroundColor}; }`); + } + + const focusedCellBorderColor = theme.getColor(focusedCellBorder); + collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-top:before, + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before, + .monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:before, + .monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:after { + border-color: ${focusedCellBorderColor} !important; + }`); + + const cellSymbolHighlightColor = theme.getColor(cellSymbolHighlight); + if (cellSymbolHighlightColor) { + collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.nb-symbolHighlight .cell-focus-indicator, + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.nb-symbolHighlight.markdown-cell-row { + background-color: ${cellSymbolHighlightColor} !important; + }`); + } + + const focusedEditorBorderColorColor = theme.getColor(focusedEditorBorderColor); + if (focusedEditorBorderColorColor) { + collector.addRule(`.notebookOverlay .monaco-list-row.cell-editor-focus .cell-editor-part:before { outline: solid 1px ${focusedEditorBorderColorColor}; }`); + } + + const cellBorderColor = theme.getColor(notebookCellBorder); + if (cellBorderColor) { + collector.addRule(`.notebookOverlay .cell.markdown h1 { border-color: ${cellBorderColor}; }`); + collector.addRule(`.notebookOverlay .monaco-list-row .cell-editor-part:before { outline: solid 1px ${cellBorderColor}; }`); + } + + const cellStatusSuccessIcon = theme.getColor(cellStatusIconSuccess); + if (cellStatusSuccessIcon) { + collector.addRule(`.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-status .codicon-check { color: ${cellStatusSuccessIcon} }`); + } + + const cellStatusErrorIcon = theme.getColor(cellStatusIconError); + if (cellStatusErrorIcon) { + collector.addRule(`.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-status .codicon-error { color: ${cellStatusErrorIcon} }`); + } + + const cellStatusRunningIcon = theme.getColor(cellStatusIconRunning); + if (cellStatusRunningIcon) { + collector.addRule(`.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-status .codicon-sync { color: ${cellStatusRunningIcon} }`); + } + + const cellStatusBarHoverBg = theme.getColor(cellStatusBarItemHover); + if (cellStatusBarHoverBg) { + collector.addRule(`.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-language-picker:hover { background-color: ${cellStatusBarHoverBg}; }`); + } + + const cellInsertionIndicatorColor = theme.getColor(cellInsertionIndicator); + if (cellInsertionIndicatorColor) { + collector.addRule(`.notebookOverlay > .cell-list-container > .cell-list-insertion-indicator { background-color: ${cellInsertionIndicatorColor}; }`); + } + + const scrollbarSliderBackgroundColor = theme.getColor(listScrollbarSliderBackground); + if (scrollbarSliderBackgroundColor) { + collector.addRule(` .notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .scrollbar > .slider { background: ${editorBackgroundColor}; } `); + collector.addRule(` .notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .scrollbar > .slider:before { content: ""; width: 100%; height: 100%; position: absolute; background: ${scrollbarSliderBackgroundColor}; } `); /* hack to not have cells see through scroller */ + } + + const scrollbarSliderHoverBackgroundColor = theme.getColor(listScrollbarSliderHoverBackground); + if (scrollbarSliderHoverBackgroundColor) { + collector.addRule(` .notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .scrollbar > .slider:hover { background: ${editorBackgroundColor}; } `); + collector.addRule(` .notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .scrollbar > .slider:hover:before { content: ""; width: 100%; height: 100%; position: absolute; background: ${scrollbarSliderHoverBackgroundColor}; } `); /* hack to not have cells see through scroller */ + } + + const scrollbarSliderActiveBackgroundColor = theme.getColor(listScrollbarSliderActiveBackground); + if (scrollbarSliderActiveBackgroundColor) { + collector.addRule(` .notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .scrollbar > .slider.active { background: ${editorBackgroundColor}; } `); + collector.addRule(` .notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .scrollbar > .slider.active:before { content: ""; width: 100%; height: 100%; position: absolute; background: ${scrollbarSliderActiveBackgroundColor}; } `); /* hack to not have cells see through scroller */ + } // Cell Margin - collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > div.cell { margin: 0px ${CELL_MARGIN}px 0px ${CELL_MARGIN}px; }`); - collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row { padding-top: ${EDITOR_TOP_MARGIN}px; }`); - collector.addRule(`.notebookOverlay .output { margin: 0px ${CELL_MARGIN}px 0px ${CELL_MARGIN + CELL_RUN_GUTTER}px }`); - collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container { width: calc(100% - ${CELL_MARGIN * 2 + CELL_RUN_GUTTER}px); margin: 0px ${CELL_MARGIN}px 0px ${CELL_MARGIN + CELL_RUN_GUTTER}px }`); + collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > div.cell { margin: 0px ${CELL_MARGIN * 2}px 0px ${CELL_MARGIN}px; }`); + collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > div.cell.code { margin-left: ${CODE_CELL_LEFT_MARGIN}px; }`); + collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row { padding-top: ${CELL_TOP_MARGIN}px; }`); + collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row { padding-bottom: ${CELL_BOTTOM_MARGIN}px; }`); + collector.addRule(`.notebookOverlay .output { margin: 0px ${CELL_MARGIN}px 0px ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`); + collector.addRule(`.notebookOverlay .output { width: calc(100% - ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER + (CELL_MARGIN * 2)}px); }`); - collector.addRule(`.notebookOverlay .markdown-cell-row .cell .cell-editor-part { margin-left: ${CELL_RUN_GUTTER}px; }`); collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > div.cell.markdown { padding-left: ${CELL_RUN_GUTTER}px; }`); collector.addRule(`.notebookOverlay .cell .run-button-container { width: ${CELL_RUN_GUTTER}px; }`); - collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-insertion-indicator { left: ${CELL_MARGIN + CELL_RUN_GUTTER}px; right: ${CELL_MARGIN}px; }`); collector.addRule(`.notebookOverlay .cell-drag-image .cell-editor-container > div { padding: ${EDITOR_TOP_PADDING}px 16px ${EDITOR_BOTTOM_PADDING}px 16px; }`); - collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .notebook-cell-focus-indicator { left: ${CELL_MARGIN}px; }`); + collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-top { height: ${CELL_TOP_MARGIN}px; }`); + collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-side { bottom: ${BOTTOM_CELL_TOOLBAR_HEIGHT}px; }`); + collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row.code-cell-row .cell-focus-indicator-left { width: ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`); + collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row .cell-focus-indicator-left { width: ${CODE_CELL_LEFT_MARGIN}px; }`); + collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator.cell-focus-indicator-right { width: ${CELL_MARGIN * 2}px; }`); + // collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row.collapsed .cell-focus-indicator.cell-focus-indicator-right { width: initial; left: ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px }`); + collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-bottom { height: ${CELL_BOTTOM_MARGIN}px; }`); + collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-shadow-container-bottom { top: ${CELL_BOTTOM_MARGIN}px; }`); + + collector.addRule(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-collapsed-part { margin-left: ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; height: ${COLLAPSED_INDICATOR_HEIGHT}px; }`); }); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidgetService.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidgetService.ts new file mode 100644 index 00000000000..def8c4e957a --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidgetService.ts @@ -0,0 +1,159 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ResourceMap } from 'vs/base/common/map'; +import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { IEditorGroupsService, IEditorGroup, GroupChangeKind, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IInstantiationService, createDecorator, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; + +export const INotebookEditorWidgetService = createDecorator('INotebookEditorWidgetService'); + +export interface IBorrowValue { + readonly value: T | undefined; +} + +export interface INotebookEditorWidgetService { + _serviceBrand: undefined; + retrieveWidget(accessor: ServicesAccessor, group: IEditorGroup, input: NotebookEditorInput): IBorrowValue; +} + +class NotebookEditorWidgetService implements INotebookEditorWidgetService { + + readonly _serviceBrand: undefined; + + private _tokenPool = 1; + + private readonly _notebookWidgets = new Map>(); + private readonly _disposables = new DisposableStore(); + + constructor( + @IEditorGroupsService editorGroupService: IEditorGroupsService, + @IEditorService editorService: IEditorService, + ) { + + const groupListener = new Map(); + const onNewGroup = (group: IEditorGroup) => { + const { id } = group; + const listener = group.onDidGroupChange(e => { + const widgets = this._notebookWidgets.get(group.id); + if (!widgets || e.kind !== GroupChangeKind.EDITOR_CLOSE || !(e.editor instanceof NotebookEditorInput)) { + return; + } + const value = widgets.get(e.editor.resource); + if (!value) { + return; + } + value.token = undefined; + this._disposeWidget(value.widget); + widgets.delete(e.editor.resource); + }); + groupListener.set(id, listener); + }; + this._disposables.add(editorGroupService.onDidAddGroup(onNewGroup)); + editorGroupService.groups.forEach(onNewGroup); + + // group removed -> clean up listeners, clean up widgets + this._disposables.add(editorGroupService.onDidRemoveGroup(group => { + const listener = groupListener.get(group.id); + if (listener) { + listener.dispose(); + groupListener.delete(group.id); + } + const widgets = this._notebookWidgets.get(group.id); + this._notebookWidgets.delete(group.id); + if (widgets) { + for (const value of widgets.values()) { + value.token = undefined; + this._disposeWidget(value.widget); + } + } + })); + + // HACK + // we use the open override to spy on tab movements because that's the only + // way to do that... + this._disposables.add(editorService.overrideOpenEditor({ + open: (input, _options, group, context) => { + if (input instanceof NotebookEditorInput && context === OpenEditorContext.MOVE_EDITOR) { + // when moving a notebook editor we release it from its current tab and we + // "place" it into its future slot so that the editor can pick it up from there + this._freeWidget(input, editorGroupService.activeGroup, group); + } + return undefined; + } + })); + } + + private _disposeWidget(widget: NotebookEditorWidget): void { + widget.onWillHide(); + const domNode = widget.getDomNode(); + widget.dispose(); + domNode.remove(); + } + + private _freeWidget(input: NotebookEditorInput, source: IEditorGroup, target: IEditorGroup): void { + const targetWidget = this._notebookWidgets.get(target.id)?.get(input.resource); + if (targetWidget) { + // not needed + return; + } + + const widget = this._notebookWidgets.get(source.id)?.get(input.resource); + if (!widget) { + throw new Error('no widget at source group'); + } + this._notebookWidgets.get(source.id)?.delete(input.resource); + widget.token = undefined; + + let targetMap = this._notebookWidgets.get(target.id); + if (!targetMap) { + targetMap = new ResourceMap(); + this._notebookWidgets.set(target.id, targetMap); + } + targetMap.set(input.resource, widget); + } + + retrieveWidget(accessor: ServicesAccessor, group: IEditorGroup, input: NotebookEditorInput): IBorrowValue { + + let value = this._notebookWidgets.get(group.id)?.get(input.resource); + + if (!value) { + // NEW widget + const instantiationService = accessor.get(IInstantiationService); + const widget = instantiationService.createInstance(NotebookEditorWidget); + widget.createEditor(); + const token = this._tokenPool++; + value = { widget, token }; + + let map = this._notebookWidgets.get(group.id); + if (!map) { + map = new ResourceMap(); + this._notebookWidgets.set(group.id, map); + } + map.set(input.resource, value); + + } else { + // reuse a widget which was either free'ed before or which + // is simply being reused... + value.token = this._tokenPool++; + } + + return this._createBorrowValue(value.token!, value); + } + + private _createBorrowValue(myToken: number, widget: { widget: NotebookEditorWidget, token: number | undefined }): IBorrowValue { + return { + get value() { + return widget.token === myToken ? widget.widget : undefined; + } + }; + } +} + +registerSingleton(INotebookEditorWidgetService, NotebookEditorWidgetService, true); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookKernelAssociation.ts b/src/vs/workbench/contrib/notebook/browser/notebookKernelAssociation.ts new file mode 100644 index 00000000000..c98c49112af --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/notebookKernelAssociation.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 * as nls from 'vs/nls'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { IConfigurationNode, IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; +import { Registry } from 'vs/platform/registry/common/platform'; + +export class NotebookKernelProviderAssociationRegistry { + static extensionIds: (string | null)[] = []; + static extensionDescriptions: string[] = []; +} + +export class NotebookViewTypesExtensionRegistry { + static viewTypes: string[] = []; + static viewTypeDescriptions: string[] = []; +} + +export type NotebookKernelProviderAssociation = { + readonly viewType: string; + readonly kernelProvider?: string; +}; + +export type NotebookKernelProviderAssociations = readonly NotebookKernelProviderAssociation[]; + + +export const notebookKernelProviderAssociationsSettingId = 'notebook.kernelProviderAssociations'; + +export const viewTypeSchamaAddition: IJSONSchema = { + type: 'string', + enum: [] +}; + +export const notebookKernelProviderAssociationsConfigurationNode: IConfigurationNode = { + ...workbenchConfigurationNodeBase, + properties: { + [notebookKernelProviderAssociationsSettingId]: { + type: 'array', + markdownDescription: nls.localize('notebook.kernelProviderAssociations', "Defines a default kernel provider which takes precedence over all other kernel providers settings. Must be the identifier of an extension contributing a kernel provider."), + items: { + type: 'object', + defaultSnippets: [{ + body: { + 'viewType': '$1', + 'kernelProvider': '$2' + } + }], + properties: { + 'viewType': { + type: ['string', 'null'], + default: null, + enum: NotebookViewTypesExtensionRegistry.viewTypes, + markdownEnumDescriptions: NotebookViewTypesExtensionRegistry.viewTypeDescriptions + }, + 'kernelProvider': { + type: ['string', 'null'], + default: null, + enum: NotebookKernelProviderAssociationRegistry.extensionIds, + markdownEnumDescriptions: NotebookKernelProviderAssociationRegistry.extensionDescriptions + } + } + } + } + } +}; + +export function updateNotebookKernelProvideAssociationSchema(): void { + Registry.as(Extensions.Configuration) + .notifyConfigurationSchemaUpdated(notebookKernelProviderAssociationsConfigurationNode); +} + +Registry.as(Extensions.Configuration) + .registerConfiguration(notebookKernelProviderAssociationsConfigurationNode); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookPureOutputRenderer.ts b/src/vs/workbench/contrib/notebook/browser/notebookPureOutputRenderer.ts new file mode 100644 index 00000000000..c1f42f14d6c --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/notebookPureOutputRenderer.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 { URI, UriComponents } from 'vs/base/common/uri'; +import { INotebookRendererInfo, IOutputRenderResponse, IOutputRenderRequest } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { joinPath } from 'vs/base/common/resources'; + +/** + * A 'stub' output renderer used when the contribution has an `entrypoint` + * property. Include the entrypoint as its reload and renders an empty string. + */ +export class PureNotebookOutputRenderer implements INotebookRendererInfo { + + public readonly extensionId: ExtensionIdentifier; + public readonly extensionLocation: URI; + public readonly preloads: URI[]; + + + constructor(public readonly id: string, extension: IExtensionDescription, entrypoint: string) { + this.extensionId = extension.identifier; + this.extensionLocation = extension.extensionLocation; + this.preloads = [joinPath(extension.extensionLocation, entrypoint)]; + } + + public render(uri: URI, request: IOutputRenderRequest): Promise | undefined> { + return this.render2(uri, request); + } + + public render2(_uri: URI, request: IOutputRenderRequest): Promise | undefined> { + return Promise.resolve({ + items: request.items.map(cellInfo => ({ + key: cellInfo.key, + outputs: cellInfo.outputs.map(output => ({ + index: output.index, + outputId: output.outputId, + mimeType: output.mimeType, + handlerId: this.id, + // todo@connor4312: temp approach exploring this API: + transformedOutput: `` + })) + })) + }); + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/notebookRegistry.ts b/src/vs/workbench/contrib/notebook/browser/notebookRegistry.ts index f6ddc3cb0de..abf2220cc6c 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookRegistry.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookRegistry.ts @@ -15,31 +15,16 @@ export interface IOutputTransformDescription { ctor: IOutputTransformCtor; } -export namespace NotebookRegistry { - export function getOutputTransformContributions(): IOutputTransformDescription[] { - return NotebookRegistryImpl.INSTANCE.getNotebookOutputTransform(); - } -} -export function registerOutputTransform(id: string, kind: CellOutputKind, ctor: { new(editor: INotebookEditor, ...services: Services): IOutputTransformContribution }): void { - NotebookRegistryImpl.INSTANCE.registerOutputTransform(id, kind, ctor); -} +export const NotebookRegistry = new class NotebookRegistryImpl { -class NotebookRegistryImpl { - - static readonly INSTANCE = new NotebookRegistryImpl(); - - private readonly outputTransforms: IOutputTransformDescription[]; - - constructor() { - this.outputTransforms = []; - } + readonly outputTransforms: IOutputTransformDescription[] = []; registerOutputTransform(id: string, kind: CellOutputKind, ctor: { new(editor: INotebookEditor, ...services: Services): IOutputTransformContribution }): void { this.outputTransforms.push({ id: id, kind: kind, ctor: ctor as IOutputTransformCtor }); } - getNotebookOutputTransform(): IOutputTransformDescription[] { + getOutputTransformContributions(): IOutputTransformDescription[] { return this.outputTransforms.slice(0); } -} +}; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index ca530378bbc..40331f7bb70 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -4,53 +4,191 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { Disposable, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { URI } from 'vs/base/common/uri'; -import { notebookProviderExtensionPoint, notebookRendererExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint'; -import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; +import { Disposable, IDisposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { notebookProviderExtensionPoint, notebookRendererExtensionPoint, INotebookEditorContribution } from 'vs/workbench/contrib/notebook/browser/extensionPoint'; +import { NotebookProviderInfo, NotebookEditorDescriptor } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol'; import { Emitter, Event } from 'vs/base/common/event'; -import { INotebookTextModel, INotebookMimeTypeSelector, INotebookRendererInfo, NotebookDocumentMetadata, CellEditType, ICellDto2 } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookTextModel, INotebookRendererInfo, INotebookKernelInfo, CellOutputKind, ITransformedDisplayOutputDto, IDisplayOutput, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, NOTEBOOK_DISPLAY_ORDER, sortMimeTypes, IOrderedMimeType, mimeTypeSupportedByCore, IOutputRenderRequestOutputInfo, IOutputRenderRequestCellInfo, NotebookCellOutputsSplice, ICellEditOperation, CellEditType, ICellInsertEdit, IOutputRenderResponse, IProcessedOutput, BUILTIN_RENDERER_ID, NotebookEditorPriority, INotebookKernelProvider, notebookDocumentFilterMatch, INotebookKernelInfo2, CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer'; import { Iterable } from 'vs/base/common/iterator'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { IEditorService, ICustomEditorViewTypesHandler, ICustomEditorInfo } from 'vs/workbench/services/editor/common/editorService'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { NotebookEditorModelManager } from 'vs/workbench/contrib/notebook/common/notebookEditorModel'; import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/notebook/common/notebookService'; +import * as glob from 'vs/base/common/glob'; +import { basename } from 'vs/base/common/path'; +import { getActiveNotebookEditor, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { Memento } from 'vs/workbench/common/memento'; +import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage'; +import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { generateUuid } from 'vs/base/common/uuid'; +import { flatten } from 'vs/base/common/arrays'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { NotebookKernelProviderAssociationRegistry, updateNotebookKernelProvideAssociationSchema, NotebookViewTypesExtensionRegistry } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation'; +import { PureNotebookOutputRenderer } from 'vs/workbench/contrib/notebook/browser/notebookPureOutputRenderer'; +import { RedoCommand, UndoCommand } from 'vs/editor/browser/editorExtensions'; +import { CopyAction, CutAction, PasteAction } from 'vs/editor/contrib/clipboard/clipboard'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; function MODEL_ID(resource: URI): string { return resource.toString(); } -export class NotebookProviderInfoStore { - private readonly contributedEditors = new Map(); +export class NotebookKernelProviderInfoStore extends Disposable { + private readonly _notebookKernelProviders: INotebookKernelProvider[] = []; + + constructor() { + super(); + } + + add(provider: INotebookKernelProvider) { + this._notebookKernelProviders.push(provider); + this._updateProviderExtensionsInfo(); + + return toDisposable(() => { + const idx = this._notebookKernelProviders.indexOf(provider); + if (idx >= 0) { + this._notebookKernelProviders.splice(idx, 1); + } + + this._updateProviderExtensionsInfo(); + }); + } + + get(viewType: string, resource: URI) { + return this._notebookKernelProviders.filter(provider => notebookDocumentFilterMatch(provider.selector, viewType, resource)); + } + + private _updateProviderExtensionsInfo() { + NotebookKernelProviderAssociationRegistry.extensionIds.length = 0; + NotebookKernelProviderAssociationRegistry.extensionDescriptions.length = 0; + + this._notebookKernelProviders.forEach(provider => { + NotebookKernelProviderAssociationRegistry.extensionIds.push(provider.providerExtensionId); + NotebookKernelProviderAssociationRegistry.extensionDescriptions.push(provider.providerDescription || ''); + }); + + updateNotebookKernelProvideAssociationSchema(); + } +} + +export class NotebookProviderInfoStore extends Disposable { + private static readonly CUSTOM_EDITORS_STORAGE_ID = 'notebookEditors'; + private static readonly CUSTOM_EDITORS_ENTRY_ID = 'editors'; + + private readonly _memento: Memento; + private _handled: boolean = false; + constructor( + storageService: IStorageService, + extensionService: IExtensionService + + ) { + super(); + this._memento = new Memento(NotebookProviderInfoStore.CUSTOM_EDITORS_STORAGE_ID, storageService); + + const mementoObject = this._memento.getMemento(StorageScope.GLOBAL); + for (const info of (mementoObject[NotebookProviderInfoStore.CUSTOM_EDITORS_ENTRY_ID] || []) as NotebookEditorDescriptor[]) { + this.add(new NotebookProviderInfo(info)); + } + + this._updateProviderExtensionsInfo(); + + this._register(extensionService.onDidRegisterExtensions(() => { + if (!this._handled) { + // there is no extension point registered for notebook content provider + // clear the memento and cache + this.clear(); + mementoObject[NotebookProviderInfoStore.CUSTOM_EDITORS_ENTRY_ID] = []; + this._memento.saveMemento(); + + this._updateProviderExtensionsInfo(); + } + })); + } + + setupHandler(extensions: readonly IExtensionPointUser[]) { + this._handled = true; + this.clear(); + + for (const extension of extensions) { + for (const notebookContribution of extension.value) { + this.add(new NotebookProviderInfo({ + id: notebookContribution.viewType, + displayName: notebookContribution.displayName, + selector: notebookContribution.selector || [], + priority: this._convertPriority(notebookContribution.priority), + providerExtensionId: extension.description.identifier.value, + providerDescription: extension.description.description, + providerDisplayName: extension.description.isBuiltin ? nls.localize('builtinProviderDisplayName', "Built-in") : extension.description.displayName || extension.description.identifier.value, + providerExtensionLocation: extension.description.extensionLocation + })); + } + } + + const mementoObject = this._memento.getMemento(StorageScope.GLOBAL); + mementoObject[NotebookProviderInfoStore.CUSTOM_EDITORS_ENTRY_ID] = Array.from(this._contributedEditors.values()); + this._memento.saveMemento(); + + this._updateProviderExtensionsInfo(); + } + + private _updateProviderExtensionsInfo() { + NotebookViewTypesExtensionRegistry.viewTypes.length = 0; + NotebookViewTypesExtensionRegistry.viewTypeDescriptions.length = 0; + + for (const contribute of this._contributedEditors) { + if (contribute[1].providerExtensionId) { + NotebookViewTypesExtensionRegistry.viewTypes.push(contribute[1].id); + NotebookViewTypesExtensionRegistry.viewTypeDescriptions.push(`${contribute[1].displayName}`); + } + } + + updateNotebookKernelProvideAssociationSchema(); + } + + private _convertPriority(priority?: string) { + if (!priority) { + return NotebookEditorPriority.default; + } + + if (priority === NotebookEditorPriority.default) { + return NotebookEditorPriority.default; + } + + return NotebookEditorPriority.option; + + } + + private readonly _contributedEditors = new Map(); clear() { - this.contributedEditors.clear(); + this._contributedEditors.clear(); } get(viewType: string): NotebookProviderInfo | undefined { - return this.contributedEditors.get(viewType); + return this._contributedEditors.get(viewType); } add(info: NotebookProviderInfo): void { - if (this.contributedEditors.has(info.id)) { - console.log(`Custom editor with id '${info.id}' already registered`); + if (this._contributedEditors.has(info.id)) { return; } - this.contributedEditors.set(info.id, info); + this._contributedEditors.set(info.id, info); } getContributedNotebook(resource: URI): readonly NotebookProviderInfo[] { - return [...Iterable.filter(this.contributedEditors.values(), customEditor => customEditor.matches(resource))]; + return [...Iterable.filter(this._contributedEditors.values(), customEditor => resource.scheme === 'untitled' || customEditor.matches(resource))]; } public [Symbol.iterator](): Iterator { - return this.contributedEditors.values(); + return this._contributedEditors.values(); } } @@ -67,7 +205,6 @@ export class NotebookOutputRendererInfoStore { add(info: NotebookOutputRendererInfo): void { if (this.contributedRenderers.has(info.id)) { - console.log(`Custom notebook output renderer with id '${info.id}' already registered`); return; } this.contributedRenderers.set(info.id, info); @@ -94,45 +231,59 @@ class ModelData implements IDisposable { } } export class NotebookService extends Disposable implements INotebookService, ICustomEditorViewTypesHandler { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; + static mainthreadNotebookDocumentHandle: number = 0; private readonly _notebookProviders = new Map(); - private readonly _notebookRenderers = new Map(); - notebookProviderInfoStore: NotebookProviderInfoStore = new NotebookProviderInfoStore(); + private readonly _notebookRenderers = new Map(); + private readonly _notebookKernels = new Map(); + notebookProviderInfoStore: NotebookProviderInfoStore; notebookRenderersInfoStore: NotebookOutputRendererInfoStore = new NotebookOutputRendererInfoStore(); - private readonly _models: { [modelId: string]: ModelData; }; - private _onDidChangeActiveEditor = new Emitter<{ viewType: string, uri: URI }>(); - onDidChangeActiveEditor: Event<{ viewType: string, uri: URI }> = this._onDidChangeActiveEditor.event; + notebookKernelProviderInfoStore: NotebookKernelProviderInfoStore = new NotebookKernelProviderInfoStore(); + private readonly _models = new Map(); + private _onDidChangeActiveEditor = new Emitter(); + onDidChangeActiveEditor: Event = this._onDidChangeActiveEditor.event; + private _activeEditorDisposables = new DisposableStore(); + private _onDidChangeVisibleEditors = new Emitter(); + onDidChangeVisibleEditors: Event = this._onDidChangeVisibleEditors.event; + private readonly _onNotebookEditorAdd: Emitter = this._register(new Emitter()); + public readonly onNotebookEditorAdd: Event = this._onNotebookEditorAdd.event; + private readonly _onNotebookEditorsRemove: Emitter = this._register(new Emitter()); + public readonly onNotebookEditorsRemove: Event = this._onNotebookEditorsRemove.event; + private readonly _onNotebookDocumentAdd: Emitter = this._register(new Emitter()); + public readonly onNotebookDocumentAdd: Event = this._onNotebookDocumentAdd.event; + private readonly _onNotebookDocumentRemove: Emitter = this._register(new Emitter()); + public readonly onNotebookDocumentRemove: Event = this._onNotebookDocumentRemove.event; + private readonly _onNotebookDocumentSaved: Emitter = this._register(new Emitter()); + public readonly onNotebookDocumentSaved: Event = this._onNotebookDocumentSaved.event; + private readonly _notebookEditors = new Map(); private readonly _onDidChangeViewTypes = new Emitter(); onDidChangeViewTypes: Event = this._onDidChangeViewTypes.event; - private cutItems: NotebookCellTextModel[] | undefined; - modelManager: NotebookEditorModelManager; + private readonly _onDidChangeKernels = new Emitter(); + onDidChangeKernels: Event = this._onDidChangeKernels.event; + private readonly _onDidChangeNotebookActiveKernel = new Emitter<{ uri: URI, providerHandle: number | undefined, kernelId: string | undefined }>(); + onDidChangeNotebookActiveKernel: Event<{ uri: URI, providerHandle: number | undefined, kernelId: string | undefined }> = this._onDidChangeNotebookActiveKernel.event; + private cutItems: NotebookCellTextModel[] | undefined; + private _lastClipboardIsCopy: boolean = true; + + private _displayOrder: { userOrder: string[], defaultOrder: string[] } = Object.create(null); constructor( - @IExtensionService private readonly extensionService: IExtensionService, - @IEditorService private readonly editorService: IEditorService, - @IInstantiationService private readonly instantiationService: IInstantiationService + @IExtensionService private readonly _extensionService: IExtensionService, + @IEditorService private readonly _editorService: IEditorService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, + @IStorageService private readonly _storageService: IStorageService, + @IInstantiationService private readonly _instantiationService: IInstantiationService ) { super(); - this._models = {}; - this.modelManager = this.instantiationService.createInstance(NotebookEditorModelManager); + this.notebookProviderInfoStore = new NotebookProviderInfoStore(this._storageService, this._extensionService); + this._register(this.notebookProviderInfoStore); notebookProviderExtensionPoint.setHandler((extensions) => { - this.notebookProviderInfoStore.clear(); - - for (const extension of extensions) { - for (const notebookContribution of extension.value) { - this.notebookProviderInfoStore.add(new NotebookProviderInfo({ - id: notebookContribution.viewType, - displayName: notebookContribution.displayName, - selector: notebookContribution.selector || [], - providerDisplayName: extension.description.isBuiltin ? nls.localize('builtinProviderDisplayName', "Built-in") : extension.description.displayName || extension.description.identifier.value, - })); - } - } - // console.log(this._notebookProviderInfoStore); + this.notebookProviderInfoStore.setupHandler(extensions); }); notebookRendererExtensionPoint.setHandler((renderers) => { @@ -143,15 +294,164 @@ export class NotebookService extends Disposable implements INotebookService, ICu this.notebookRenderersInfoStore.add(new NotebookOutputRendererInfo({ id: notebookContribution.viewType, displayName: notebookContribution.displayName, - mimeTypes: notebookContribution.mimeTypes || [] + mimeTypes: notebookContribution.mimeTypes || [], })); + + if (notebookContribution.entrypoint) { + this._notebookRenderers.set(notebookContribution.viewType, new PureNotebookOutputRenderer(notebookContribution.viewType, extension.description, notebookContribution.entrypoint)); + } } } // console.log(this.notebookRenderersInfoStore); }); - this.editorService.registerCustomEditorViewTypesHandler('Notebook', this); + this._editorService.registerCustomEditorViewTypesHandler('Notebook', this); + + const updateOrder = () => { + const userOrder = this._configurationService.getValue('notebook.displayOrder'); + this._displayOrder = { + defaultOrder: this._accessibilityService.isScreenReaderOptimized() ? ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER : NOTEBOOK_DISPLAY_ORDER, + userOrder: userOrder + }; + }; + + updateOrder(); + + this._register(this._configurationService.onDidChangeConfiguration(e => { + if (e.affectedKeys.indexOf('notebook.displayOrder') >= 0) { + updateOrder(); + } + })); + + this._register(this._accessibilityService.onDidChangeScreenReaderOptimized(() => { + updateOrder(); + })); + + const getContext = () => { + const editor = getActiveNotebookEditor(this._editorService); + const activeCell = editor?.getActiveCell(); + + return { + editor, + activeCell + }; + }; + + const PRIORITY = 50; + this._register(UndoCommand.addImplementation(PRIORITY, () => { + const { editor } = getContext(); + if (editor?.viewModel) { + editor?.viewModel.undo(); + return true; + } + + return false; + })); + + this._register(RedoCommand.addImplementation(PRIORITY, () => { + const { editor } = getContext(); + if (editor?.viewModel) { + editor?.viewModel.redo(); + return true; + } + + return false; + })); + + if (CopyAction) { + this._register(CopyAction.addImplementation(PRIORITY, accessor => { + const { editor, activeCell } = getContext(); + if (!editor || !activeCell) { + return false; + } + + if (editor.hasOutputTextSelection()) { + document.execCommand('copy'); + return true; + } + + const clipboardService = accessor.get(IClipboardService); + const notebookService = accessor.get(INotebookService); + clipboardService.writeText(activeCell.getText()); + notebookService.setToCopy([activeCell.model], true); + + return true; + })); + } + + if (PasteAction) { + PasteAction.addImplementation(PRIORITY, () => { + const pasteCells = this.getToCopy(); + + if (!pasteCells) { + return false; + } + + const { editor, activeCell } = getContext(); + if (!editor || !activeCell) { + return false; + } + + const viewModel = editor.viewModel; + + if (!viewModel) { + return false; + } + + const currCellIndex = viewModel.getCellIndex(activeCell); + + let topPastedCell: CellViewModel | undefined = undefined; + pasteCells.items.reverse().map(cell => { + const data = CellUri.parse(cell.uri); + + if (pasteCells.isCopy || data?.notebook.toString() !== viewModel.uri.toString()) { + return viewModel.notebookDocument.createCellTextModel( + cell.getValue(), + cell.language, + cell.cellKind, + [], + cell.metadata + ); + } else { + return cell; + } + }).forEach(pasteCell => { + const newIdx = typeof currCellIndex === 'number' ? currCellIndex + 1 : 0; + topPastedCell = viewModel.insertCell(newIdx, pasteCell, true); + }); + + if (topPastedCell) { + editor.focusNotebookCell(topPastedCell, 'container'); + } + + return true; + }); + } + + if (CutAction) { + CutAction.addImplementation(PRIORITY, accessor => { + const { editor, activeCell } = getContext(); + if (!editor || !activeCell) { + return false; + } + + const viewModel = editor.viewModel; + + if (!viewModel) { + return false; + } + + const clipboardService = accessor.get(IClipboardService); + const notebookService = accessor.get(INotebookService); + clipboardService.writeText(activeCell.getText()); + viewModel.deleteCell(viewModel.getCellIndex(activeCell), true); + notebookService.setToCopy([activeCell.model], false); + + return true; + }); + } + } getViewTypes(): ICustomEditorInfo[] { @@ -164,14 +464,18 @@ export class NotebookService extends Disposable implements INotebookService, ICu async canResolve(viewType: string): Promise { if (!this._notebookProviders.has(viewType)) { + await this._extensionService.whenInstalledExtensionsRegistered(); + // notebook providers/kernels/renderers might use `*` as activation event. + await this._extensionService.activateByEvent(`*`); // this awaits full activation of all matching extensions - await this.extensionService.activateByEvent(`onNotebookEditor:${viewType}`); + await this._extensionService.activateByEvent(`onNotebookEditor:${viewType}`); } return this._notebookProviders.has(viewType); } registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController) { this._notebookProviders.set(viewType, { extensionData, controller }); + this.notebookProviderInfoStore.get(viewType)!.kernel = controller.kernel; this._onDidChangeViewTypes.fire(); } @@ -180,96 +484,445 @@ export class NotebookService extends Disposable implements INotebookService, ICu this._onDidChangeViewTypes.fire(); } - registerNotebookRenderer(handle: number, extensionData: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, preloads: URI[]) { - this._notebookRenderers.set(handle, { extensionData, type, selectors, preloads }); + registerNotebookRenderer(id: string, renderer: INotebookRendererInfo) { + this._notebookRenderers.set(id, renderer); } - unregisterNotebookRenderer(handle: number) { - this._notebookRenderers.delete(handle); + unregisterNotebookRenderer(id: string) { + this._notebookRenderers.delete(id); } - getRendererInfo(handle: number): INotebookRendererInfo | undefined { - const renderer = this._notebookRenderers.get(handle); + registerNotebookKernel(notebook: INotebookKernelInfo): void { + this._notebookKernels.set(notebook.id, notebook); + this._onDidChangeKernels.fire(); + } + + unregisterNotebookKernel(id: string): void { + this._notebookKernels.delete(id); + this._onDidChangeKernels.fire(); + } + + registerNotebookKernelProvider(provider: INotebookKernelProvider): IDisposable { + const d = this.notebookKernelProviderInfoStore.add(provider); + const kernelChangeEventListener = provider.onDidChangeKernels(() => { + this._onDidChangeKernels.fire(); + }); + + this._onDidChangeKernels.fire(); + return toDisposable(() => { + kernelChangeEventListener.dispose(); + d.dispose(); + }); + } + + async getContributedNotebookKernels2(viewType: string, resource: URI, token: CancellationToken): Promise { + const filteredProvider = this.notebookKernelProviderInfoStore.get(viewType, resource); + const result = new Array(filteredProvider.length); + + const promises = filteredProvider.map(async (provider, index) => { + const data = await provider.provideKernels(resource, token); + result[index] = data.map(dto => { + return { + extension: dto.extension, + extensionLocation: URI.revive(dto.extensionLocation), + id: dto.id, + label: dto.label, + description: dto.description, + isPreferred: dto.isPreferred, + preloads: dto.preloads, + providerHandle: dto.providerHandle, + resolve: async (uri: URI, editorId: string, token: CancellationToken) => { + return provider.resolveKernel(editorId, uri, dto.id, token); + }, + executeNotebookCell: async (uri: URI, handle: number | undefined) => { + return provider.executeNotebook(uri, dto.id, handle); + }, + cancelNotebookCell: (uri: URI, handle: number | undefined): Promise => { + return provider.cancelNotebook(uri, dto.id, handle); + } + }; + }); + }); + + await Promise.all(promises); + + return flatten(result); + } + + getContributedNotebookKernels(viewType: string, resource: URI): INotebookKernelInfo[] { + let kernelInfos: INotebookKernelInfo[] = []; + this._notebookKernels.forEach(kernel => { + if (this._notebookKernelMatch(resource, kernel!.selectors)) { + kernelInfos.push(kernel!); + } + }); + + // sort by extensions + + const notebookContentProvider = this._notebookProviders.get(viewType); + + if (!notebookContentProvider) { + return kernelInfos; + } + + kernelInfos = kernelInfos.sort((a, b) => { + if (a.extension.value === notebookContentProvider!.extensionData.id.value) { + return -1; + } else if (b.extension.value === notebookContentProvider!.extensionData.id.value) { + return 1; + } else { + return 0; + } + }); + + return kernelInfos; + } + + private _notebookKernelMatch(resource: URI, selectors: (string | glob.IRelativePattern)[]): boolean { + for (let i = 0; i < selectors.length; i++) { + const pattern = typeof selectors[i] !== 'string' ? selectors[i] : selectors[i].toString(); + if (glob.match(pattern, basename(resource.fsPath).toLowerCase())) { + return true; + } + } + + return false; + } + + getRendererInfo(id: string): INotebookRendererInfo | undefined { + const renderer = this._notebookRenderers.get(id); + + return renderer; + } + + async resolveNotebook(viewType: string, uri: URI, forceReload: boolean, editorId?: string, backupId?: string): Promise { + const provider = this._notebookProviders.get(viewType); + if (!provider) { + return undefined; + } + + const modelId = MODEL_ID(uri); + + let notebookModel: NotebookTextModel | undefined = undefined; + if (this._models.has(modelId)) { + // the model already exists + notebookModel = this._models.get(modelId)!.model; + if (forceReload) { + await provider.controller.reloadNotebook(notebookModel); + } + + return notebookModel; + } else { + notebookModel = this._instantiationService.createInstance(NotebookTextModel, NotebookService.mainthreadNotebookDocumentHandle++, viewType, provider.controller.supportBackup, uri); + await provider.controller.createNotebook(notebookModel, backupId); + + if (!notebookModel) { + return undefined; + } + } + + // new notebook model created + const modelData = new ModelData( + notebookModel!, + (model) => this._onWillDisposeDocument(model), + ); + + this._models.set(modelId, modelData); + this._onNotebookDocumentAdd.fire([notebookModel!.uri]); + // after the document is added to the store and sent to ext host, we transform the ouputs + await this.transformTextModelOutputs(notebookModel!); + + if (editorId) { + await provider.controller.resolveNotebookEditor(viewType, uri, editorId); + } + + return modelData.model; + } + + getNotebookTextModel(uri: URI): NotebookTextModel | undefined { + const modelId = MODEL_ID(uri); + + return this._models.get(modelId)?.model; + } + + private async _fillInTransformedOutputs( + renderers: Set, + requestItems: IOutputRenderRequestCellInfo[], + renderFunc: (rendererId: string, items: IOutputRenderRequestCellInfo[]) => Promise | undefined>, + lookUp: (key: T) => { outputs: IProcessedOutput[] } + ) { + for (const id of renderers) { + const requestsPerRenderer: IOutputRenderRequestCellInfo[] = requestItems.map(req => { + return { + key: req.key, + outputs: req.outputs.filter(output => output.handlerId === id) + }; + }); + + const response = await renderFunc(id, requestsPerRenderer); + + // mix the response with existing outputs, which will replace the picked transformed mimetype with resolved result + if (response) { + response.items.forEach(cellInfo => { + const cell = lookUp(cellInfo.key)!; + cellInfo.outputs.forEach(outputInfo => { + const output = cell.outputs[outputInfo.index]; + if (output.outputKind === CellOutputKind.Rich && output.orderedMimeTypes && output.orderedMimeTypes.length) { + output.orderedMimeTypes[0] = { + mimeType: outputInfo.mimeType, + isResolved: true, + rendererId: outputInfo.handlerId, + output: outputInfo.transformedOutput + }; + } + }); + }); + } + } + } + + async transformTextModelOutputs(textModel: NotebookTextModel) { + const renderers = new Set(); + + const cellMapping: Map = new Map(); + + const requestItems: IOutputRenderRequestCellInfo[] = []; + for (let i = 0; i < textModel.cells.length; i++) { + const cell = textModel.cells[i]; + cellMapping.set(cell.uri.fragment, cell); + const outputs = cell.outputs; + const outputRequest: IOutputRenderRequestOutputInfo[] = []; + + outputs.forEach((output, index) => { + if (output.outputKind === CellOutputKind.Rich) { + // TODO no string[] casting + const ret = this._transformMimeTypes(output, output.outputId, textModel.metadata.displayOrder as string[] || []); + const orderedMimeTypes = ret.orderedMimeTypes!; + const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!; + output.pickedMimeTypeIndex = pickedMimeTypeIndex; + output.orderedMimeTypes = orderedMimeTypes; + + if (orderedMimeTypes[pickedMimeTypeIndex!].rendererId && orderedMimeTypes[pickedMimeTypeIndex].rendererId !== BUILTIN_RENDERER_ID) { + outputRequest.push({ index, handlerId: orderedMimeTypes[pickedMimeTypeIndex].rendererId!, mimeType: orderedMimeTypes[pickedMimeTypeIndex].mimeType, outputId: output.outputId }); + renderers.add(orderedMimeTypes[pickedMimeTypeIndex].rendererId!); + } + } + }); + + requestItems.push({ key: cell.uri, outputs: outputRequest }); + } + + await this._fillInTransformedOutputs(renderers, requestItems, async (rendererId, items) => { + return await this._notebookRenderers.get(rendererId)?.render(textModel.uri, { items: items }); + }, (key: UriComponents) => { return cellMapping.get(URI.revive(key).fragment)!; }); + + textModel.updateRenderers([...renderers]); + } + + async transformEditsOutputs(textModel: NotebookTextModel, edits: ICellEditOperation[]) { + const renderers = new Set(); + const requestItems: IOutputRenderRequestCellInfo<[number, number]>[] = []; + + edits.forEach((edit, editIndex) => { + if (edit.editType === CellEditType.Insert) { + edit.cells.forEach((cell, cellIndex) => { + const outputs = cell.outputs; + const outputRequest: IOutputRenderRequestOutputInfo[] = []; + outputs.map((output, index) => { + if (output.outputKind === CellOutputKind.Rich) { + const ret = this._transformMimeTypes(output, output.outputId, textModel.metadata.displayOrder as string[] || []); + const orderedMimeTypes = ret.orderedMimeTypes!; + const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!; + output.pickedMimeTypeIndex = pickedMimeTypeIndex; + output.orderedMimeTypes = orderedMimeTypes; + + if (orderedMimeTypes[pickedMimeTypeIndex!].rendererId && orderedMimeTypes[pickedMimeTypeIndex].rendererId !== BUILTIN_RENDERER_ID) { + outputRequest.push({ index, handlerId: orderedMimeTypes[pickedMimeTypeIndex].rendererId!, mimeType: orderedMimeTypes[pickedMimeTypeIndex].mimeType, output: output, outputId: output.outputId }); + renderers.add(orderedMimeTypes[pickedMimeTypeIndex].rendererId!); + } + } + }); + + requestItems.push({ key: [editIndex, cellIndex], outputs: outputRequest }); + }); + } + }); + + await this._fillInTransformedOutputs<[number, number]>(renderers, requestItems, async (rendererId, items) => { + return await this._notebookRenderers.get(rendererId)?.render2<[number, number]>(textModel.uri, { items: items }); + }, (key: [number, number]) => { + return (edits[key[0]] as ICellInsertEdit).cells[key[1]]; + }); + + textModel.updateRenderers([...renderers]); + } + + async transformSpliceOutputs(textModel: NotebookTextModel, splices: NotebookCellOutputsSplice[]) { + const renderers = new Set(); + const requestItems: IOutputRenderRequestCellInfo[] = []; + + splices.forEach((splice, spliceIndex) => { + const outputs = splice[2]; + const outputRequest: IOutputRenderRequestOutputInfo[] = []; + outputs.map((output, index) => { + if (output.outputKind === CellOutputKind.Rich) { + const ret = this._transformMimeTypes(output, output.outputId, textModel.metadata.displayOrder as string[] || []); + const orderedMimeTypes = ret.orderedMimeTypes!; + const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!; + output.pickedMimeTypeIndex = pickedMimeTypeIndex; + output.orderedMimeTypes = orderedMimeTypes; + + if (orderedMimeTypes[pickedMimeTypeIndex!].rendererId && orderedMimeTypes[pickedMimeTypeIndex].rendererId !== BUILTIN_RENDERER_ID) { + outputRequest.push({ index, handlerId: orderedMimeTypes[pickedMimeTypeIndex].rendererId!, mimeType: orderedMimeTypes[pickedMimeTypeIndex].mimeType, output: output, outputId: output.outputId }); + renderers.add(orderedMimeTypes[pickedMimeTypeIndex].rendererId!); + } + } + }); + requestItems.push({ key: spliceIndex, outputs: outputRequest }); + }); + + await this._fillInTransformedOutputs(renderers, requestItems, async (rendererId, items) => { + return await this._notebookRenderers.get(rendererId)?.render2(textModel.uri, { items: items }); + }, (key: number) => { + return { outputs: splices[key][2] }; + }); + + textModel.updateRenderers([...renderers]); + } + + async transformSingleOutput(textModel: NotebookTextModel, output: IProcessedOutput, rendererId: string, mimeType: string): Promise { + const items = [ + { + key: 0, + outputs: [ + { + index: 0, + outputId: generateUuid(), + handlerId: rendererId, + mimeType: mimeType, + output: output + } + ] + } + ]; + const response = await this._notebookRenderers.get(rendererId)?.render2(textModel.uri, { items: items }); + + if (response) { + textModel.updateRenderers([rendererId]); + const outputInfo = response.items[0].outputs[0]; - if (renderer) { return { - id: renderer.extensionData.id, - extensionLocation: URI.revive(renderer.extensionData.location), - preloads: renderer.preloads + mimeType: outputInfo.mimeType, + isResolved: true, + rendererId: outputInfo.handlerId, + output: outputInfo.transformedOutput }; } return; } - async createNotebookFromBackup(viewType: string, uri: URI, metadata: NotebookDocumentMetadata, languages: string[], cells: ICellDto2[]): Promise { - const provider = this._notebookProviders.get(viewType); - if (!provider) { - return undefined; - } + private _transformMimeTypes(output: IDisplayOutput, outputId: string, documentDisplayOrder: string[]): ITransformedDisplayOutputDto { + const mimeTypes = Object.keys(output.data); + const coreDisplayOrder = this._displayOrder; + const sorted = sortMimeTypes(mimeTypes, coreDisplayOrder?.userOrder || [], documentDisplayOrder, coreDisplayOrder?.defaultOrder || []); - const notebookModel = await provider.controller.createNotebook(viewType, uri, true, false); - if (!notebookModel) { - return undefined; - } + const orderMimeTypes: IOrderedMimeType[] = []; - // new notebook model created - const modelId = MODEL_ID(uri); - const modelData = new ModelData( - notebookModel, - (model) => this._onWillDispose(model), - ); - this._models[modelId] = modelData; + sorted.forEach(mimeType => { + const handlers = this._findBestMatchedRenderer(mimeType); - notebookModel.metadata = metadata; - notebookModel.languages = languages; + if (handlers.length) { + const handler = handlers[0]; - notebookModel.applyEdit(notebookModel.versionId, [ - { - editType: CellEditType.Insert, - index: 0, - cells: cells + orderMimeTypes.push({ + mimeType: mimeType, + isResolved: false, + rendererId: handler.id, + }); + + for (let i = 1; i < handlers.length; i++) { + orderMimeTypes.push({ + mimeType: mimeType, + isResolved: false, + rendererId: handlers[i].id + }); + } + + if (mimeTypeSupportedByCore(mimeType)) { + orderMimeTypes.push({ + mimeType: mimeType, + isResolved: false, + rendererId: BUILTIN_RENDERER_ID + }); + } + } else { + orderMimeTypes.push({ + mimeType: mimeType, + isResolved: false, + rendererId: BUILTIN_RENDERER_ID + }); } - ]); + }); - return modelData.model; + return { + outputKind: output.outputKind, + outputId, + data: output.data, + orderedMimeTypes: orderMimeTypes, + pickedMimeTypeIndex: 0 + }; } - async resolveNotebook(viewType: string, uri: URI, forceReload: boolean): Promise { - const provider = this._notebookProviders.get(viewType); - if (!provider) { - return undefined; - } - - let notebookModel: NotebookTextModel | undefined; - - notebookModel = await provider.controller.createNotebook(viewType, uri, false, forceReload); - - // new notebook model created - const modelId = MODEL_ID(uri); - const modelData = new ModelData( - notebookModel!, - (model) => this._onWillDispose(model), - ); - - this._models[modelId] = modelData; - return modelData.model; + private _findBestMatchedRenderer(mimeType: string): readonly NotebookOutputRendererInfo[] { + return this.notebookRenderersInfoStore.getContributedRenderer(mimeType); } async executeNotebook(viewType: string, uri: URI): Promise { - let provider = this._notebookProviders.get(viewType); + const provider = this._notebookProviders.get(viewType); if (provider) { - return provider.controller.executeNotebook(viewType, uri, new CancellationTokenSource().token); // Cancellation for notebooks - TODO + return provider.controller.executeNotebookByAttachedKernel(viewType, uri); } return; } - async executeNotebookCell(viewType: string, uri: URI, handle: number, token: CancellationToken): Promise { + async executeNotebookCell(viewType: string, uri: URI, handle: number): Promise { const provider = this._notebookProviders.get(viewType); if (provider) { - await provider.controller.executeNotebookCell(uri, handle, token); + await provider.controller.executeNotebookCell(uri, handle); + } + } + + async cancelNotebook(viewType: string, uri: URI): Promise { + const provider = this._notebookProviders.get(viewType); + + if (provider) { + return provider.controller.cancelNotebookByAttachedKernel(viewType, uri); + } + + return; + } + + async cancelNotebookCell(viewType: string, uri: URI, handle: number): Promise { + const provider = this._notebookProviders.get(viewType); + if (provider) { + await provider.controller.cancelNotebookCell(uri, handle); + } + } + + async executeNotebook2(viewType: string, uri: URI, kernelId: string): Promise { + const kernel = this._notebookKernels.get(kernelId); + if (kernel) { + await kernel.executeNotebook(viewType, uri, undefined); + } + } + + async executeNotebookCell2(viewType: string, uri: URI, handle: number, kernelId: string): Promise { + const kernel = this._notebookKernels.get(kernelId); + if (kernel) { + await kernel.executeNotebook(viewType, uri, handle); } } @@ -286,7 +939,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu } getNotebookProviderResourceRoots(): URI[] { - let ret: URI[] = []; + const ret: URI[] = []; this._notebookProviders.forEach(val => { ret.push(URI.revive(val.extensionData.location)); }); @@ -294,61 +947,153 @@ export class NotebookService extends Disposable implements INotebookService, ICu return ret; } - destoryNotebookDocument(viewType: string, notebook: INotebookTextModel): void { - let provider = this._notebookProviders.get(viewType); + removeNotebookEditor(editor: INotebookEditor) { + const editorCache = this._notebookEditors.get(editor.getId()); - if (provider) { - provider.controller.removeNotebookDocument(notebook); + if (editorCache) { + this._notebookEditors.delete(editor.getId()); + this._onNotebookEditorsRemove.fire([editor]); } } - updateActiveNotebookDocument(viewType: string, resource: URI): void { - this._onDidChangeActiveEditor.fire({ viewType, uri: resource }); + addNotebookEditor(editor: INotebookEditor) { + this._notebookEditors.set(editor.getId(), editor); + this._onNotebookEditorAdd.fire(editor); } - setToCopy(items: NotebookCellTextModel[]) { + getNotebookEditor(editorId: string) { + return this._notebookEditors.get(editorId); + } + + listNotebookEditors(): INotebookEditor[] { + return [...this._notebookEditors].map(e => e[1]); + } + + listVisibleNotebookEditors(): INotebookEditor[] { + return this._editorService.visibleEditorPanes + .filter(pane => (pane as unknown as { isNotebookEditor?: boolean }).isNotebookEditor) + .map(pane => pane.getControl() as INotebookEditor) + .filter(editor => !!editor) + .filter(editor => this._notebookEditors.has(editor.getId())); + } + + listNotebookDocuments(): NotebookTextModel[] { + return [...this._models].map(e => e[1].model); + } + + destoryNotebookDocument(viewType: string, notebook: INotebookTextModel): void { + this._onWillDisposeDocument(notebook); + } + + updateActiveNotebookEditor(editor: INotebookEditor | null) { + this._activeEditorDisposables.clear(); + + if (editor) { + this._activeEditorDisposables.add(editor.onDidChangeKernel(() => { + this._onDidChangeNotebookActiveKernel.fire({ + uri: editor.uri!, + providerHandle: editor.activeKernel?.providerHandle, + kernelId: editor.activeKernel?.id + }); + })); + } + this._onDidChangeActiveEditor.fire(editor ? editor.getId() : null); + } + + updateVisibleNotebookEditor(editors: string[]) { + const alreadyCreated = editors.filter(editorId => this._notebookEditors.has(editorId)); + this._onDidChangeVisibleEditors.fire(alreadyCreated); + } + + setToCopy(items: NotebookCellTextModel[], isCopy: boolean) { this.cutItems = items; + this._lastClipboardIsCopy = isCopy; } - getToCopy(): NotebookCellTextModel[] | undefined { - return this.cutItems; + getToCopy(): { items: NotebookCellTextModel[], isCopy: boolean; } | undefined { + if (this.cutItems) { + return { items: this.cutItems, isCopy: this._lastClipboardIsCopy }; + } + + return undefined; } async save(viewType: string, resource: URI, token: CancellationToken): Promise { - let provider = this._notebookProviders.get(viewType); + const provider = this._notebookProviders.get(viewType); if (provider) { - return provider.controller.save(resource, token); + const ret = await provider.controller.save(resource, token); + if (ret) { + this._onNotebookDocumentSaved.fire(resource); + } + + return ret; } return false; } async saveAs(viewType: string, resource: URI, target: URI, token: CancellationToken): Promise { - let provider = this._notebookProviders.get(viewType); + const provider = this._notebookProviders.get(viewType); if (provider) { - return provider.controller.saveAs(resource, target, token); + const ret = await provider.controller.saveAs(resource, target, token); + if (ret) { + this._onNotebookDocumentSaved.fire(resource); + } + + return ret; } return false; } - onDidReceiveMessage(viewType: string, uri: URI, message: any): void { - let provider = this._notebookProviders.get(viewType); + async backup(viewType: string, uri: URI, token: CancellationToken): Promise { + const provider = this._notebookProviders.get(viewType); if (provider) { - return provider.controller.onDidReceiveMessage(uri, message); + return provider.controller.backup(uri, token); + } + + return; + } + + onDidReceiveMessage(viewType: string, editorId: string, rendererType: string | undefined, message: any): void { + const provider = this._notebookProviders.get(viewType); + + if (provider) { + return provider.controller.onDidReceiveMessage(editorId, rendererType, message); } } - private _onWillDispose(model: INotebookTextModel): void { - let modelId = MODEL_ID(model.uri); - let modelData = this._models[modelId]; + private _onWillDisposeDocument(model: INotebookTextModel): void { + const modelId = MODEL_ID(model.uri); - delete this._models[modelId]; - modelData?.dispose(); + const modelData = this._models.get(modelId); + this._models.delete(modelId); - // this._onModelRemoved.fire(model); + if (modelData) { + // delete editors and documents + const willRemovedEditors: INotebookEditor[] = []; + this._notebookEditors.forEach(editor => { + if (editor.textModel === modelData!.model) { + willRemovedEditors.push(editor); + } + }); + + willRemovedEditors.forEach(e => this._notebookEditors.delete(e.getId())); + + const provider = this._notebookProviders.get(modelData!.model.viewType); + + if (provider) { + provider.controller.removeNotebookDocument(modelData!.model.uri); + modelData!.model.dispose(); + } + + + this._onNotebookEditorsRemove.fire(willRemovedEditors.map(e => e)); + this._onNotebookDocumentRemove.fire([modelData.model.uri]); + modelData?.dispose(); + } } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index f82bf348e93..d324fb3a8bc 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -6,7 +6,7 @@ import * as DOM from 'vs/base/browser/dom'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { IListRenderer, IListVirtualDelegate, ListError } from 'vs/base/browser/ui/list/list'; -import { IListStyles, IStyleController, MouseController, IListOptions } from 'vs/base/browser/ui/list/listWidget'; +import { IListStyles, IStyleController } from 'vs/base/browser/ui/list/listWidget'; import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { isMacintosh } from 'vs/base/common/platform'; @@ -19,9 +19,20 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IListService, IWorkbenchListOptions, WorkbenchList } from 'vs/platform/list/browser/listService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { CellRevealPosition, CellRevealType, CursorAtBoundary, getVisibleCells, ICellRange, ICellViewModel, INotebookCellList, reduceCellRanges, CellEditState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellRevealPosition, CellRevealType, CursorAtBoundary, getVisibleCells, ICellRange, ICellViewModel, INotebookCellList, reduceCellRanges, CellEditState, CellFocusMode, BaseCellRenderTemplate, NOTEBOOK_CELL_LIST_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { diff, IOutput, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { diff, IProcessedOutput, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { clamp } from 'vs/base/common/numbers'; +import { SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/browser/constants'; + +export interface IFocusNextPreviousDelegate { + onFocusNext(applyFocusNext: () => void): void; + onFocusPrevious(applyFocusPrevious: () => void): void; +} + +export interface INotebookCellListOptions extends IWorkbenchListOptions { + focusNextPreviousDelegate: IFocusNextPreviousDelegate; +} export class NotebookCellList extends WorkbenchList implements IDisposable, IStyleController, INotebookCellList { get onWillScroll(): Event { return this.view.onWillScroll; } @@ -34,30 +45,38 @@ export class NotebookCellList extends WorkbenchList implements ID private _viewModelStore = new DisposableStore(); private styleElement?: HTMLStyleElement; - private readonly _onDidRemoveOutput = new Emitter(); - readonly onDidRemoveOutput: Event = this._onDidRemoveOutput.event; - private readonly _onDidHideOutput = new Emitter(); - readonly onDidHideOutput: Event = this._onDidHideOutput.event; + private readonly _onDidRemoveOutput = new Emitter(); + readonly onDidRemoveOutput: Event = this._onDidRemoveOutput.event; + private readonly _onDidHideOutput = new Emitter(); + readonly onDidHideOutput: Event = this._onDidHideOutput.event; private _viewModel: NotebookViewModel | null = null; private _hiddenRangeIds: string[] = []; private hiddenRangesPrefixSum: PrefixSumComputer | null = null; + private _isDisposed = false; + + get isDisposed() { + return this._isDisposed; + } + + private readonly _focusNextPreviousDelegate: IFocusNextPreviousDelegate; + constructor( private listUser: string, container: HTMLElement, delegate: IListVirtualDelegate, - renderers: IListRenderer[], + renderers: IListRenderer[], contextKeyService: IContextKeyService, - options: IWorkbenchListOptions, + options: INotebookCellListOptions, @IListService listService: IListService, @IThemeService themeService: IThemeService, @IConfigurationService configurationService: IConfigurationService, @IKeybindingService keybindingService: IKeybindingService - ) { super(listUser, container, delegate, renderers, options, contextKeyService, listService, themeService, configurationService, keybindingService); - + NOTEBOOK_CELL_LIST_FOCUSED.bindTo(this.contextKeyService).set(true); + this._focusNextPreviousDelegate = options.focusNextPreviousDelegate; this._previousFocusedElements = this.getFocusedElements(); this._localDisposableStore.add(this.onDidChangeFocus((e) => { this._previousFocusedElements.forEach(element => { @@ -66,6 +85,10 @@ export class NotebookCellList extends WorkbenchList implements ID } }); this._previousFocusedElements = e.elements; + + if (document.activeElement && document.activeElement.classList.contains('webview')) { + super.domFocus(); + } })); const notebookEditorCursorAtBoundaryContext = NOTEBOOK_EDITOR_CURSOR_BOUNDARY.bindTo(contextKeyService); @@ -120,10 +143,33 @@ export class NotebookCellList extends WorkbenchList implements ID notebookEditorCursorAtBoundaryContext.set('none'); })); + this._localDisposableStore.add(this.view.onMouseDblClick(() => { + const focus = this.getFocusedElements()[0]; + if (focus && focus.cellKind === CellKind.Markdown) { + focus.editState = CellEditState.Editing; + focus.focusMode = CellFocusMode.Editor; + } + })); } - protected createMouseController(_options: IListOptions): MouseController { - return new NotebookMouseController(this); + elementAt(position: number): ICellViewModel | undefined { + if (!this.view.length) { + return undefined; + } + + const idx = this.view.indexAt(position); + const clamped = clamp(idx, 0, this.view.length - 1); + return this.element(clamped); + } + + elementHeight(element: ICellViewModel): number { + const index = this._getViewIndexUpperBound(element); + if (index === undefined || index < 0 || index >= this.length) { + this._getViewIndexUpperBound(element); + throw new ListError(this.listUser, `Invalid index ${index}`); + } + + return this.view.elementHeight(index); } detachViewModel() { @@ -135,6 +181,10 @@ export class NotebookCellList extends WorkbenchList implements ID attachViewModel(model: NotebookViewModel) { this._viewModel = model; this._viewModelStore.add(model.onDidChangeViewCells((e) => { + if (this._isDisposed) { + return; + } + const currentRanges = this._hiddenRangeIds.map(id => this._viewModel!.getTrackedRange(id)).filter(range => range !== null) as ICellRange[]; const newVisibleViewCells: CellViewModel[] = getVisibleCells(this._viewModel!.viewCells as CellViewModel[], currentRanges); @@ -152,8 +202,8 @@ export class NotebookCellList extends WorkbenchList implements ID if (e.synchronous) { viewDiffs.reverse().forEach((diff) => { // remove output in the webview - const hideOutputs: IOutput[] = []; - const deletedOutputs: IOutput[] = []; + const hideOutputs: IProcessedOutput[] = []; + const deletedOutputs: IProcessedOutput[] = []; for (let i = diff.start; i < diff.start + diff.deleteCount; i++) { const cell = this.element(i); @@ -170,10 +220,14 @@ export class NotebookCellList extends WorkbenchList implements ID deletedOutputs.forEach(output => this._onDidRemoveOutput.fire(output)); }); } else { - DOM.scheduleAtNextAnimationFrame(() => { + this._viewModelStore.add(DOM.scheduleAtNextAnimationFrame(() => { + if (this._isDisposed) { + return; + } + viewDiffs.reverse().forEach((diff) => { - const hideOutputs: IOutput[] = []; - const deletedOutputs: IOutput[] = []; + const hideOutputs: IProcessedOutput[] = []; + const deletedOutputs: IProcessedOutput[] = []; for (let i = diff.start; i < diff.start + diff.deleteCount; i++) { const cell = this.element(i); @@ -189,7 +243,7 @@ export class NotebookCellList extends WorkbenchList implements ID hideOutputs.forEach(output => this._onDidHideOutput.fire(output)); deletedOutputs.forEach(output => this._onDidRemoveOutput.fire(output)); }); - }); + })); } })); @@ -198,7 +252,7 @@ export class NotebookCellList extends WorkbenchList implements ID const viewSelections = model.selectionHandles.map(handle => { return model.getCellByHandle(handle); }).filter(cell => !!cell).map(cell => this._getViewIndexUpperBound(cell!)); - this.setFocus(viewSelections); + this.setFocus(viewSelections, undefined, true); })); const hiddenRanges = model.getHiddenRanges(); @@ -246,7 +300,7 @@ export class NotebookCellList extends WorkbenchList implements ID // set hidden ranges prefix sum let start = 0; let index = 0; - let ret: number[] = []; + const ret: number[] = []; while (index < newRanges.length) { for (let j = start; j < newRanges[index].start - 1; j++) { @@ -294,8 +348,8 @@ export class NotebookCellList extends WorkbenchList implements ID viewDiffs.reverse().forEach((diff) => { // remove output in the webview - const hideOutputs: IOutput[] = []; - const deletedOutputs: IOutput[] = []; + const hideOutputs: IProcessedOutput[] = []; + const deletedOutputs: IProcessedOutput[] = []; for (let i = diff.start; i < diff.start + diff.deleteCount; i++) { const cell = this.element(i); @@ -315,6 +369,10 @@ export class NotebookCellList extends WorkbenchList implements ID splice2(start: number, deleteCount: number, elements: CellViewModel[] = []): void { // we need to convert start and delete count based on hidden ranges + if (start < 0 || start > this.view.length) { + return; + } + super.splice(start, deleteCount, elements); const selectionsLeft = []; @@ -324,7 +382,7 @@ export class NotebookCellList extends WorkbenchList implements ID } }); - if (!selectionsLeft.length && this._viewModel!.viewCells) { + if (!selectionsLeft.length && this._viewModel!.viewCells.length) { // after splice, the selected cells are deleted this._viewModel!.selectionHandles = [this._viewModel!.viewCells[0].handle]; } @@ -346,7 +404,7 @@ export class NotebookCellList extends WorkbenchList implements ID } - private _getViewIndexUpperBound(cell: ICellViewModel) { + private _getViewIndexUpperBound(cell: ICellViewModel): number { const modelIndex = this._viewModel!.getCellIndex(cell); if (!this.hiddenRangesPrefixSum) { return modelIndex; @@ -360,7 +418,7 @@ export class NotebookCellList extends WorkbenchList implements ID focusElement(cell: ICellViewModel) { const index = this._getViewIndexUpperBound(cell); - if (index !== undefined) { + if (index >= 0) { this.setFocus([index]); } } @@ -371,14 +429,26 @@ export class NotebookCellList extends WorkbenchList implements ID } const index = this._getViewIndexUpperBound(cell); - if (index !== undefined) { + if (index >= 0) { this.setSelection([index]); this.setFocus([index]); } } - setFocus(indexes: number[], browserEvent?: UIEvent): void { - if (this._viewModel) { + focusNext(n: number | undefined, loop: boolean | undefined, browserEvent?: UIEvent, filter?: (element: CellViewModel) => boolean): void { + this._focusNextPreviousDelegate.onFocusNext(() => super.focusNext(n, loop, browserEvent, filter)); + } + + focusPrevious(n: number | undefined, loop: boolean | undefined, browserEvent?: UIEvent, filter?: (element: CellViewModel) => boolean): void { + this._focusNextPreviousDelegate.onFocusPrevious(() => super.focusPrevious(n, loop, browserEvent, filter)); + } + + setFocus(indexes: number[], browserEvent?: UIEvent, ignoreTextModelUpdate?: boolean): void { + if (!indexes.length) { + return; + } + + if (this._viewModel && !ignoreTextModelUpdate) { this._viewModel.selectionHandles = indexes.map(index => this.element(index)).map(cell => cell.handle); } @@ -388,7 +458,7 @@ export class NotebookCellList extends WorkbenchList implements ID revealElementInView(cell: ICellViewModel) { const index = this._getViewIndexUpperBound(cell); - if (index !== undefined) { + if (index >= 0) { this._revealInView(index); } } @@ -396,7 +466,7 @@ export class NotebookCellList extends WorkbenchList implements ID revealElementInCenterIfOutsideViewport(cell: ICellViewModel) { const index = this._getViewIndexUpperBound(cell); - if (index !== undefined) { + if (index >= 0) { this._revealInCenterIfOutsideViewport(index); } } @@ -404,62 +474,62 @@ export class NotebookCellList extends WorkbenchList implements ID revealElementInCenter(cell: ICellViewModel) { const index = this._getViewIndexUpperBound(cell); - if (index !== undefined) { + if (index >= 0) { this._revealInCenter(index); } } - revealElementLineInView(cell: ICellViewModel, line: number): void { + async revealElementLineInViewAsync(cell: ICellViewModel, line: number): Promise { const index = this._getViewIndexUpperBound(cell); - if (index !== undefined) { - this._revealLineInView(index, line); + if (index >= 0) { + return this._revealLineInViewAsync(index, line); } } - revealElementLineInCenter(cell: ICellViewModel, line: number) { + async revealElementLineInCenterAsync(cell: ICellViewModel, line: number): Promise { const index = this._getViewIndexUpperBound(cell); - if (index !== undefined) { - this._revealLineInCenter(index, line); + if (index >= 0) { + return this._revealLineInCenterAsync(index, line); } } - revealElementLineInCenterIfOutsideViewport(cell: ICellViewModel, line: number) { + async revealElementLineInCenterIfOutsideViewportAsync(cell: ICellViewModel, line: number): Promise { const index = this._getViewIndexUpperBound(cell); - if (index !== undefined) { - this._revealLineInCenterIfOutsideViewport(index, line); + if (index >= 0) { + return this._revealLineInCenterIfOutsideViewportAsync(index, line); } } - revealElementRangeInView(cell: ICellViewModel, range: Range): void { + async revealElementRangeInViewAsync(cell: ICellViewModel, range: Range): Promise { const index = this._getViewIndexUpperBound(cell); - if (index !== undefined) { - this._revealRangeInView(index, range); + if (index >= 0) { + return this._revealRangeInView(index, range); } } - revealElementRangeInCenter(cell: ICellViewModel, range: Range): void { + async revealElementRangeInCenterAsync(cell: ICellViewModel, range: Range): Promise { const index = this._getViewIndexUpperBound(cell); - if (index !== undefined) { - this._revealRangeInCenter(index, range); + if (index >= 0) { + return this._revealRangeInCenterAsync(index, range); } } - revealElementRangeInCenterIfOutsideViewport(cell: ICellViewModel, range: Range): void { + async revealElementRangeInCenterIfOutsideViewportAsync(cell: ICellViewModel, range: Range): Promise { const index = this._getViewIndexUpperBound(cell); - if (index !== undefined) { - this._revealRangeInCenterIfOutsideViewport(index, range); + if (index >= 0) { + return this._revealRangeInCenterIfOutsideViewportAsync(index, range); } } domElementOfElement(element: ICellViewModel): HTMLElement | null { const index = this._getViewIndexUpperBound(element); - if (index !== undefined) { + if (index >= 0) { return this.view.domElement(index); } @@ -471,7 +541,7 @@ export class NotebookCellList extends WorkbenchList implements ID } getAbsoluteTopOfElement(element: ICellViewModel): number { - let index = this._getViewIndexUpperBound(element); + const index = this._getViewIndexUpperBound(element); if (index === undefined || index < 0 || index >= this.length) { this._getViewIndexUpperBound(element); throw new ListError(this.listUser, `Invalid index ${index}`); @@ -487,7 +557,7 @@ export class NotebookCellList extends WorkbenchList implements ID updateElementHeight2(element: ICellViewModel, size: number): void { const index = this._getViewIndexUpperBound(element); - if (index === undefined) { + if (index === undefined || index < 0 || index >= this.length) { return; } @@ -497,7 +567,10 @@ export class NotebookCellList extends WorkbenchList implements ID // override domFocus() { - if (document.activeElement && this.view.domNode.contains(document.activeElement)) { + const focused = this.getFocusedElements()[0]; + const focusedDomElement = focused && this.domElementOfElement(focused); + + if (document.activeElement && focusedDomElement && focusedDomElement.contains(document.activeElement)) { // for example, when focus goes into monaco editor, if we refocus the list view, the editor will lose focus. return; } @@ -509,28 +582,35 @@ export class NotebookCellList extends WorkbenchList implements ID super.domFocus(); } + getViewScrollTop() { + return this.view.getScrollTop(); + } + + getViewScrollBottom() { + return this.getViewScrollTop() + this.view.renderHeight - SCROLLABLE_ELEMENT_PADDING_TOP; + } + private _revealRange(viewIndex: number, range: Range, revealType: CellRevealType, newlyCreated: boolean, alignToBottom: boolean) { const element = this.view.element(viewIndex); - const scrollTop = this.view.getScrollTop(); - const wrapperBottom = scrollTop + this.view.renderHeight; - const startLineNumber = range.startLineNumber; - const lineOffset = element.getLineScrollTopOffset(startLineNumber); + const scrollTop = this.getViewScrollTop(); + const wrapperBottom = this.getViewScrollBottom(); + const positionOffset = element.getPositionScrollTopOffset(range.startLineNumber, range.startColumn); const elementTop = this.view.elementTop(viewIndex); - const lineTop = elementTop + lineOffset; + const positionTop = elementTop + positionOffset; // TODO@rebornix 30 ---> line height * 1.5 - if (lineTop < scrollTop) { - this.view.setScrollTop(lineTop - 30); - } else if (lineTop > wrapperBottom) { - this.view.setScrollTop(scrollTop + lineTop - wrapperBottom + 30); + if (positionTop < scrollTop) { + this.view.setScrollTop(positionTop - 30); + } else if (positionTop > wrapperBottom) { + this.view.setScrollTop(scrollTop + positionTop - wrapperBottom + 30); } else if (newlyCreated) { // newly scrolled into view if (alignToBottom) { // align to the bottom - this.view.setScrollTop(scrollTop + lineTop - wrapperBottom + 30); + this.view.setScrollTop(scrollTop + positionTop - wrapperBottom + 30); } else { // align to to top - this.view.setScrollTop(lineTop - 30); + this.view.setScrollTop(positionTop - 30); } } @@ -542,9 +622,9 @@ export class NotebookCellList extends WorkbenchList implements ID // List items have real dynamic heights, which means after we set `scrollTop` based on the `elementTop(index)`, the element at `index` might still be removed from the view once all relayouting tasks are done. // For example, we scroll item 10 into the view upwards, in the first round, items 7, 8, 9, 10 are all in the viewport. Then item 7 and 8 resize themselves to be larger and finally item 10 is removed from the view. // To ensure that item 10 is always there, we need to scroll item 10 to the top edge of the viewport. - private _revealRangeInternal(viewIndex: number, range: Range, revealType: CellRevealType) { - const scrollTop = this.view.getScrollTop(); - const wrapperBottom = scrollTop + this.view.renderHeight; + private async _revealRangeInternalAsync(viewIndex: number, range: Range, revealType: CellRevealType): Promise { + const scrollTop = this.getViewScrollTop(); + const wrapperBottom = this.getViewScrollBottom(); const elementTop = this.view.elementTop(viewIndex); const element = this.view.element(viewIndex); @@ -570,26 +650,26 @@ export class NotebookCellList extends WorkbenchList implements ID }); }); - editorAttachedPromise.then(() => { + return editorAttachedPromise.then(() => { this._revealRange(viewIndex, range, revealType, true, upwards); }); } } - private _revealLineInView(viewIndex: number, line: number) { - this._revealRangeInternal(viewIndex, new Range(line, 1, line, 1), CellRevealType.Line); + private async _revealLineInViewAsync(viewIndex: number, line: number): Promise { + return this._revealRangeInternalAsync(viewIndex, new Range(line, 1, line, 1), CellRevealType.Line); } - private _revealRangeInView(viewIndex: number, range: Range): void { - this._revealRangeInternal(viewIndex, range, CellRevealType.Range); + private async _revealRangeInView(viewIndex: number, range: Range): Promise { + return this._revealRangeInternalAsync(viewIndex, range, CellRevealType.Range); } - private _revealRangeInCenterInternal(viewIndex: number, range: Range, revealType: CellRevealType) { + private async _revealRangeInCenterInternalAsync(viewIndex: number, range: Range, revealType: CellRevealType): Promise { const reveal = (viewIndex: number, range: Range, revealType: CellRevealType) => { const element = this.view.element(viewIndex); - let lineOffset = element.getLineScrollTopOffset(range.startLineNumber); - let lineOffsetInView = this.view.elementTop(viewIndex) + lineOffset; - this.view.setScrollTop(lineOffsetInView - this.view.renderHeight / 2); + const positionOffset = element.getPositionScrollTopOffset(range.startLineNumber, range.startColumn); + const positionOffsetInView = this.view.elementTop(viewIndex) + positionOffset; + this.view.setScrollTop(positionOffsetInView - this.view.renderHeight / 2); if (revealType === CellRevealType.Range) { element.revealRangeInCenter(range); @@ -602,41 +682,42 @@ export class NotebookCellList extends WorkbenchList implements ID const element = this.view.element(viewIndex); if (!element.editorAttached) { - getEditorAttachedPromise(element).then(() => reveal(viewIndex, range, revealType)); + return getEditorAttachedPromise(element).then(() => reveal(viewIndex, range, revealType)); } else { reveal(viewIndex, range, revealType); } } - private _revealLineInCenter(viewIndex: number, line: number) { - this._revealRangeInCenterInternal(viewIndex, new Range(line, 1, line, 1), CellRevealType.Line); + private async _revealLineInCenterAsync(viewIndex: number, line: number): Promise { + return this._revealRangeInCenterInternalAsync(viewIndex, new Range(line, 1, line, 1), CellRevealType.Line); } - private _revealRangeInCenter(viewIndex: number, range: Range): void { - this._revealRangeInCenterInternal(viewIndex, range, CellRevealType.Range); + private _revealRangeInCenterAsync(viewIndex: number, range: Range): Promise { + return this._revealRangeInCenterInternalAsync(viewIndex, range, CellRevealType.Range); } - private _revealRangeInCenterIfOutsideViewportInternal(viewIndex: number, range: Range, revealType: CellRevealType) { + private async _revealRangeInCenterIfOutsideViewportInternalAsync(viewIndex: number, range: Range, revealType: CellRevealType): Promise { const reveal = (viewIndex: number, range: Range, revealType: CellRevealType) => { const element = this.view.element(viewIndex); - let lineOffset = element.getLineScrollTopOffset(range.startLineNumber); - let lineOffsetInView = this.view.elementTop(viewIndex) + lineOffset; - this.view.setScrollTop(lineOffsetInView - this.view.renderHeight / 2); + const positionOffset = element.getPositionScrollTopOffset(range.startLineNumber, range.startColumn); + const positionOffsetInView = this.view.elementTop(viewIndex) + positionOffset; + this.view.setScrollTop(positionOffsetInView - this.view.renderHeight / 2); if (revealType === CellRevealType.Range) { element.revealRangeInCenter(range); } }; - const scrollTop = this.view.getScrollTop(); - const wrapperBottom = scrollTop + this.view.renderHeight; + const scrollTop = this.getViewScrollTop(); + const wrapperBottom = this.getViewScrollBottom(); const elementTop = this.view.elementTop(viewIndex); const viewItemOffset = elementTop; const element = this.view.element(viewIndex); + const positionOffset = viewItemOffset + element.getPositionScrollTopOffset(range.startLineNumber, range.startColumn); - if (viewItemOffset < scrollTop || viewItemOffset > wrapperBottom) { + if (positionOffset < scrollTop || positionOffset > wrapperBottom) { // let it render - this.view.setScrollTop(viewItemOffset - this.view.renderHeight / 2); + this.view.setScrollTop(positionOffset - this.view.renderHeight / 2); // after rendering, it might be pushed down due to markdown cell dynamic height const elementTop = this.view.elementTop(viewIndex); @@ -644,7 +725,7 @@ export class NotebookCellList extends WorkbenchList implements ID // reveal editor if (!element.editorAttached) { - getEditorAttachedPromise(element).then(() => reveal(viewIndex, range, revealType)); + return getEditorAttachedPromise(element).then(() => reveal(viewIndex, range, revealType)); } else { // for example markdown } @@ -653,17 +734,17 @@ export class NotebookCellList extends WorkbenchList implements ID element.revealRangeInCenter(range); } else { // for example, markdown cell in preview mode - getEditorAttachedPromise(element).then(() => reveal(viewIndex, range, revealType)); + return getEditorAttachedPromise(element).then(() => reveal(viewIndex, range, revealType)); } } } - private _revealLineInCenterIfOutsideViewport(viewIndex: number, line: number) { - this._revealRangeInCenterIfOutsideViewportInternal(viewIndex, new Range(line, 1, line, 1), CellRevealType.Line); + private async _revealLineInCenterIfOutsideViewportAsync(viewIndex: number, line: number): Promise { + return this._revealRangeInCenterIfOutsideViewportInternalAsync(viewIndex, new Range(line, 1, line, 1), CellRevealType.Line); } - private _revealRangeInCenterIfOutsideViewport(viewIndex: number, range: Range): void { - this._revealRangeInCenterIfOutsideViewportInternal(viewIndex, range, CellRevealType.Range); + private async _revealRangeInCenterIfOutsideViewportAsync(viewIndex: number, range: Range): Promise { + return this._revealRangeInCenterIfOutsideViewportInternalAsync(viewIndex, range, CellRevealType.Range); } private _revealInternal(viewIndex: number, ignoreIfInsideViewport: boolean, revealPosition: CellRevealPosition) { @@ -671,13 +752,22 @@ export class NotebookCellList extends WorkbenchList implements ID return; } - const scrollTop = this.view.getScrollTop(); - const wrapperBottom = scrollTop + this.view.renderHeight; + const scrollTop = this.getViewScrollTop(); + const wrapperBottom = this.getViewScrollBottom(); const elementTop = this.view.elementTop(viewIndex); + const elementBottom = this.view.elementHeight(viewIndex) + elementTop; - if (ignoreIfInsideViewport && elementTop >= scrollTop && elementTop < wrapperBottom) { - // inside the viewport - return; + if (ignoreIfInsideViewport + && elementTop >= scrollTop + && elementTop < wrapperBottom) { + + if (revealPosition === CellRevealPosition.Center + && elementBottom > wrapperBottom + && elementTop > (scrollTop + wrapperBottom) / 2) { + // the element is partially visible and it's below the center of the viewport + } else { + return; + } } // first render @@ -832,6 +922,8 @@ export class NotebookCellList extends WorkbenchList implements ID } dispose() { + this._isDisposed = true; + this._viewModelStore.dispose(); this._localDisposableStore.dispose(); super.dispose(); } @@ -846,13 +938,3 @@ function getEditorAttachedPromise(element: CellViewModel) { function isContextMenuFocused() { return !!DOM.findParentWithClass(document.activeElement, 'context-view'); } - - -class NotebookMouseController extends MouseController { - protected onDoubleClick(): void { - const focus = this.list.getFocusedElements()[0]; - if (focus && focus.cellKind === CellKind.Markdown) { - focus.editState = CellEditState.Editing; - } - } -} diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts index b6d6134632f..7e988c86142 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IOutput, IRenderOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { IProcessedOutput, IRenderOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookRegistry } from 'vs/workbench/contrib/notebook/browser/notebookRegistry'; import { onUnexpectedError } from 'vs/base/common/errors'; import { INotebookEditor, IOutputTransformContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; @@ -20,7 +20,7 @@ export class OutputRenderer { this._contributions = {}; this._mimeTypeMapping = {}; - let contributions = NotebookRegistry.getOutputTransformContributions(); + const contributions = NotebookRegistry.getOutputTransformContributions(); for (const desc of contributions) { try { @@ -33,7 +33,7 @@ export class OutputRenderer { } } - renderNoop(output: IOutput, container: HTMLElement): IRenderOutput { + renderNoop(output: IProcessedOutput, container: HTMLElement): IRenderOutput { const contentNode = document.createElement('p'); contentNode.innerText = `No renderer could be found for output. It has the following output type: ${output.outputKind}`; @@ -43,8 +43,8 @@ export class OutputRenderer { }; } - render(output: IOutput, container: HTMLElement, preferredMimeType: string | undefined): IRenderOutput { - let transform = this._mimeTypeMapping[output.outputKind]; + render(output: IProcessedOutput, container: HTMLElement, preferredMimeType: string | undefined): IRenderOutput { + const transform = this._mimeTypeMapping[output.outputKind]; if (transform) { return transform.render(output, container, preferredMimeType); diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform.ts index cb02e31a669..7998129daa4 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IRenderOutput, CellOutputKind, IErrorOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { registerOutputTransform } from 'vs/workbench/contrib/notebook/browser/notebookRegistry'; +import { NotebookRegistry } from 'vs/workbench/contrib/notebook/browser/notebookRegistry'; import * as DOM from 'vs/base/browser/dom'; import { RGBA, Color } from 'vs/base/common/color'; import { ansiColorIdentifiers } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; @@ -45,7 +45,7 @@ class ErrorTransform implements IOutputTransformContribution { } } -registerOutputTransform('notebook.output.error', CellOutputKind.Error, ErrorTransform); +NotebookRegistry.registerOutputTransform('notebook.output.error', CellOutputKind.Error, ErrorTransform); /** * @param text The content to stylize. @@ -174,7 +174,7 @@ export function handleANSIOutput(text: string, themeService: IThemeService): HTM * @see {@link https://en.wikipedia.org/wiki/ANSI_escape_code } */ function setBasicFormatters(styleCodes: number[]): void { - for (let code of styleCodes) { + for (const code of styleCodes) { switch (code) { case 0: { styleNames = []; diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts index 49227bc1356..73a1fd4af7e 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IRenderOutput, CellOutputKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { registerOutputTransform } from 'vs/workbench/contrib/notebook/browser/notebookRegistry'; +import { IRenderOutput, CellOutputKind, ITransformedDisplayOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookRegistry } from 'vs/workbench/contrib/notebook/browser/notebookRegistry'; import * as DOM from 'vs/base/browser/dom'; import { INotebookEditor, IOutputTransformContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { isArray } from 'vs/base/common/types'; @@ -15,30 +15,33 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { URI } from 'vs/base/common/uri'; import { MarkdownRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { handleANSIOutput } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform'; class RichRenderer implements IOutputTransformContribution { private _mdRenderer: MarkdownRenderer; - private _richMimeTypeRenderers = new Map IRenderOutput>(); + private _richMimeTypeRenderers = new Map IRenderOutput>(); constructor( public notebookEditor: INotebookEditor, @IInstantiationService private readonly instantiationService: IInstantiationService, @IModelService private readonly modelService: IModelService, - @IModeService private readonly modeService: IModeService + @IModeService private readonly modeService: IModeService, + @IThemeService private readonly themeService: IThemeService ) { - this._mdRenderer = instantiationService.createInstance(MarkdownRenderer); + this._mdRenderer = instantiationService.createInstance(MarkdownRenderer, undefined); this._richMimeTypeRenderers.set('application/json', this.renderJSON.bind(this)); this._richMimeTypeRenderers.set('application/javascript', this.renderJavaScript.bind(this)); this._richMimeTypeRenderers.set('text/html', this.renderHTML.bind(this)); this._richMimeTypeRenderers.set('image/svg+xml', this.renderSVG.bind(this)); this._richMimeTypeRenderers.set('text/markdown', this.renderMarkdown.bind(this)); this._richMimeTypeRenderers.set('image/png', this.renderPNG.bind(this)); - this._richMimeTypeRenderers.set('image/jpeg', this.renderJavaScript.bind(this)); + this._richMimeTypeRenderers.set('image/jpeg', this.renderJPEG.bind(this)); this._richMimeTypeRenderers.set('text/plain', this.renderPlainText.bind(this)); this._richMimeTypeRenderers.set('text/x-javascript', this.renderCode.bind(this)); } - render(output: any, container: HTMLElement, preferredMimeType: string | undefined): IRenderOutput { + render(output: ITransformedDisplayOutputDto, container: HTMLElement, preferredMimeType: string | undefined): IRenderOutput { if (!output.data) { const contentNode = document.createElement('p'); contentNode.innerText = `No data could be found for output.`; @@ -51,14 +54,19 @@ class RichRenderer implements IOutputTransformContribution { if (!preferredMimeType || !this._richMimeTypeRenderers.has(preferredMimeType)) { const contentNode = document.createElement('p'); - let mimeTypes = []; + const mimeTypes = []; for (const property in output.data) { mimeTypes.push(property); } - let mimeTypesMessage = mimeTypes.join(', '); + const mimeTypesMessage = mimeTypes.join(', '); + + if (preferredMimeType) { + contentNode.innerText = `No renderer could be found for MIME type: ${preferredMimeType}`; + } else { + contentNode.innerText = `No renderer could be found for output. It has the following MIME types: ${mimeTypesMessage}`; + } - contentNode.innerText = `No renderer could be found for output. It has the following MIME types: ${mimeTypesMessage}`; container.appendChild(contentNode); return { @@ -66,13 +74,13 @@ class RichRenderer implements IOutputTransformContribution { }; } - let renderer = this._richMimeTypeRenderers.get(preferredMimeType); + const renderer = this._richMimeTypeRenderers.get(preferredMimeType); return renderer!(output, container); } - renderJSON(output: any, container: HTMLElement) { - let data = output.data['application/json']; - let str = JSON.stringify(data, null, '\t'); + renderJSON(output: ITransformedDisplayOutputDto, container: HTMLElement) { + const data = output.data['application/json']; + const str = JSON.stringify(data, null, '\t'); const editor = this.instantiationService.createInstance(CodeEditorWidget, container, { ...getOutputSimpleEditorOptions(), @@ -84,14 +92,14 @@ class RichRenderer implements IOutputTransformContribution { isSimpleWidget: true }); - let mode = this.modeService.create('json'); - let resource = URI.parse(`notebook-output-${Date.now()}.json`); + const mode = this.modeService.create('json'); + const resource = URI.parse(`notebook-output-${Date.now()}.json`); const textModel = this.modelService.createModel(str, mode, resource, false); editor.setModel(textModel); - let width = this.notebookEditor.getLayoutInfo().width; - let fontInfo = this.notebookEditor.getLayoutInfo().fontInfo; - let height = Math.min(textModel.getLineCount(), 16) * (fontInfo.lineHeight || 18); + const width = this.notebookEditor.getLayoutInfo().width; + const fontInfo = this.notebookEditor.getLayoutInfo().fontInfo; + const height = Math.min(textModel.getLineCount(), 16) * (fontInfo.lineHeight || 18); editor.layout({ height, @@ -105,9 +113,9 @@ class RichRenderer implements IOutputTransformContribution { }; } - renderCode(output: any, container: HTMLElement) { - let data = output.data['text/x-javascript']; - let str = isArray(data) ? data.join('') : data; + renderCode(output: ITransformedDisplayOutputDto, container: HTMLElement) { + const data = output.data['text/x-javascript']; + const str = (isArray(data) ? data.join('') : data) as string; const editor = this.instantiationService.createInstance(CodeEditorWidget, container, { ...getOutputSimpleEditorOptions(), @@ -119,14 +127,14 @@ class RichRenderer implements IOutputTransformContribution { isSimpleWidget: true }); - let mode = this.modeService.create('javascript'); - let resource = URI.parse(`notebook-output-${Date.now()}.js`); + const mode = this.modeService.create('javascript'); + const resource = URI.parse(`notebook-output-${Date.now()}.js`); const textModel = this.modelService.createModel(str, mode, resource, false); editor.setModel(textModel); - let width = this.notebookEditor.getLayoutInfo().width; - let fontInfo = this.notebookEditor.getLayoutInfo().fontInfo; - let height = Math.min(textModel.getLineCount(), 16) * (fontInfo.lineHeight || 18); + const width = this.notebookEditor.getLayoutInfo().width; + const fontInfo = this.notebookEditor.getLayoutInfo().fontInfo; + const height = Math.min(textModel.getLineCount(), 16) * (fontInfo.lineHeight || 18); editor.layout({ height, @@ -140,19 +148,19 @@ class RichRenderer implements IOutputTransformContribution { }; } - renderJavaScript(output: any, container: HTMLElement) { - let data = output.data['application/javascript']; - let str = isArray(data) ? data.join('') : data; - let scriptVal = ``; + renderJavaScript(output: ITransformedDisplayOutputDto, container: HTMLElement) { + const data = output.data['application/javascript']; + const str = isArray(data) ? data.join('') : data; + const scriptVal = ``; return { shadowContent: scriptVal, hasDynamicHeight: false }; } - renderHTML(output: any, container: HTMLElement) { - let data = output.data['text/html']; - let str = isArray(data) ? data.join('') : data; + renderHTML(output: ITransformedDisplayOutputDto, container: HTMLElement) { + const data = output.data['text/html']; + const str = (isArray(data) ? data.join('') : data) as string; return { shadowContent: str, hasDynamicHeight: false @@ -160,20 +168,20 @@ class RichRenderer implements IOutputTransformContribution { } - renderSVG(output: any, container: HTMLElement) { - let data = output.data['image/svg+xml']; - let str = isArray(data) ? data.join('') : data; + renderSVG(output: ITransformedDisplayOutputDto, container: HTMLElement) { + const data = output.data['image/svg+xml']; + const str = (isArray(data) ? data.join('') : data) as string; return { shadowContent: str, hasDynamicHeight: false }; } - renderMarkdown(output: any, container: HTMLElement) { - let data = output.data['text/markdown']; - const str = isArray(data) ? data.join('') : data; + renderMarkdown(output: ITransformedDisplayOutputDto, container: HTMLElement) { + const data = output.data['text/markdown']; + const str = (isArray(data) ? data.join('') : data) as string; const mdOutput = document.createElement('div'); - mdOutput.appendChild(this._mdRenderer.render({ value: str, isTrusted: false, supportThemeIcons: true }).element); + mdOutput.appendChild(this._mdRenderer.render({ value: str, isTrusted: true, supportThemeIcons: true }).element); container.appendChild(mdOutput); return { @@ -181,7 +189,7 @@ class RichRenderer implements IOutputTransformContribution { }; } - renderPNG(output: any, container: HTMLElement) { + renderPNG(output: ITransformedDisplayOutputDto, container: HTMLElement) { const image = document.createElement('img'); image.src = `data:image/png;base64,${output.data['image/png']}`; const display = document.createElement('div'); @@ -194,7 +202,7 @@ class RichRenderer implements IOutputTransformContribution { } - renderJPEG(output: any, container: HTMLElement) { + renderJPEG(output: ITransformedDisplayOutputDto, container: HTMLElement) { const image = document.createElement('img'); image.src = `data:image/jpeg;base64,${output.data['image/jpeg']}`; const display = document.createElement('div'); @@ -206,11 +214,11 @@ class RichRenderer implements IOutputTransformContribution { }; } - renderPlainText(output: any, container: HTMLElement) { - let data = output.data['text/plain']; - let str = isArray(data) ? data.join('') : data; - const contentNode = document.createElement('p'); - contentNode.innerText = str; + renderPlainText(output: ITransformedDisplayOutputDto, container: HTMLElement) { + const data = output.data['text/plain']; + const str = (isArray(data) ? data.join('') : data) as string; + const contentNode = DOM.$('.output-plaintext'); + contentNode.appendChild(handleANSIOutput(str, this.themeService)); container.appendChild(contentNode); return { @@ -222,7 +230,7 @@ class RichRenderer implements IOutputTransformContribution { } } -registerOutputTransform('notebook.output.rich', CellOutputKind.Rich, RichRenderer); +NotebookRegistry.registerOutputTransform('notebook.output.rich', CellOutputKind.Rich, RichRenderer); export function getOutputSimpleEditorOptions(): IEditorOptions { diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform.ts index 32ed484dd50..b3d7698c233 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform.ts @@ -3,8 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IRenderOutput, CellOutputKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { registerOutputTransform } from 'vs/workbench/contrib/notebook/browser/notebookRegistry'; +import * as DOM from 'vs/base/browser/dom'; +import { IRenderOutput, CellOutputKind, IStreamOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookRegistry } from 'vs/workbench/contrib/notebook/browser/notebookRegistry'; import { INotebookEditor, IOutputTransformContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; class StreamRenderer implements IOutputTransformContribution { @@ -13,8 +14,8 @@ class StreamRenderer implements IOutputTransformContribution { ) { } - render(output: any, container: HTMLElement): IRenderOutput { - const contentNode = document.createElement('p'); + render(output: IStreamOutput, container: HTMLElement): IRenderOutput { + const contentNode = DOM.$('.output-stream'); contentNode.innerText = output.text; container.appendChild(contentNode); return { @@ -27,4 +28,4 @@ class StreamRenderer implements IOutputTransformContribution { } } -registerOutputTransform('notebook.output.stream', CellOutputKind.Text, StreamRenderer); +NotebookRegistry.registerOutputTransform('notebook.output.stream', CellOutputKind.Text, StreamRenderer); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 52d93010242..a02b42db806 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -12,14 +12,28 @@ import { isWeb } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import * as UUID from 'vs/base/common/uuid'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { CELL_MARGIN, CELL_RUN_GUTTER } from 'vs/workbench/contrib/notebook/browser/constants'; +import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener'; +import { CELL_MARGIN, CELL_RUN_GUTTER, CODE_CELL_LEFT_MARGIN, CELL_OUTPUT_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; -import { IOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellOutputKind, IProcessedOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; -import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview'; -import { WebviewResourceScheme } from 'vs/workbench/contrib/webview/common/resourceLoader'; +import { IWebviewService, WebviewElement, WebviewContentPurpose } from 'vs/workbench/contrib/webview/browser/webview'; +import { asWebviewUri } from 'vs/workbench/contrib/webview/common/webviewUri'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { dirname, joinPath } from 'vs/base/common/resources'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { preloadsScriptStr } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads'; +import { Schemas } from 'vs/base/common/network'; +import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IFileService } from 'vs/platform/files/common/files'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { getExtensionForMimeType } from 'vs/base/common/mime'; + +export interface WebviewIntialized { + __vscode_notebook_message: boolean; + type: 'initialized' +} export interface IDimensionMessage { __vscode_notebook_message: boolean; @@ -61,23 +75,27 @@ export interface IBlurOutputMessage { focusNext?: boolean; } -export interface IClearMessage { - type: 'clear'; +export interface IClickedDataUrlMessage { + __vscode_notebook_message: boolean; + type: 'clicked-data-url'; + data: string; + downloadName?: string; } -export interface IFocusOutputMessage { - type: 'focus-output'; - id: string; +export interface IClearMessage { + type: 'clear'; } export interface ICreationRequestMessage { type: 'html'; content: string; - id: string; + cellId: string; outputId: string; top: number; left: number; + requiredPreloads: IPreloadResource[]; initiallyHidden?: boolean; + apiNamespace?: string | undefined; } export interface IContentWidgetTopRequest { @@ -101,15 +119,87 @@ export interface IScrollRequestMessage { version: number; } +export interface IClearOutputRequestMessage { + type: 'clearOutput'; + cellId: string; + outputId: string; + cellUri: string; + apiNamespace: string | undefined; +} + +export interface IHideOutputMessage { + type: 'hideOutput'; + outputId: string; + cellId: string; +} + +export interface IShowOutputMessage { + type: 'showOutput'; + cellId: string; + outputId: string; + top: number; +} + +export interface IFocusOutputMessage { + type: 'focus-output'; + cellId: string; +} + +export interface IPreloadResource { + uri: string +} + export interface IUpdatePreloadResourceMessage { type: 'preload'; - resources: string[]; + resources: IPreloadResource[]; + source: 'renderer' | 'kernel'; } +export interface IUpdateDecorationsMessage { + type: 'decorations'; + cellId: string; + addedClassNames: string[]; + removedClassNames: string[]; +} + +export interface ICustomRendererMessage { + __vscode_notebook_message: boolean; + type: 'customRendererMessage'; + rendererId: string; + message: unknown; +} + +export type FromWebviewMessage = + | WebviewIntialized + | IDimensionMessage + | IMouseEnterMessage + | IMouseLeaveMessage + | IWheelMessage + | IScrollAckMessage + | IBlurOutputMessage + | ICustomRendererMessage + | IClickedDataUrlMessage; + +export type ToWebviewMessage = + | IClearMessage + | IFocusOutputMessage + | ICreationRequestMessage + | IViewScrollTopRequestMessage + | IScrollRequestMessage + | IClearOutputRequestMessage + | IHideOutputMessage + | IShowOutputMessage + | IUpdatePreloadResourceMessage + | IFocusOutputMessage + | IUpdateDecorationsMessage + | ICustomRendererMessage; + +export type AnyMessage = FromWebviewMessage | ToWebviewMessage; + interface ICachedInset { outputId: string; cell: CodeCellViewModel; - preloads: ReadonlySet; + preloads: ReadonlySet; cachedCreation: ICreationRequestMessage; } @@ -121,39 +211,141 @@ function html(strings: TemplateStringsArray, ...values: any[]): string { return str; } -type IMessage = IDimensionMessage | IScrollAckMessage | IWheelMessage | IMouseEnterMessage | IMouseLeaveMessage | IBlurOutputMessage; +export interface INotebookWebviewMessage { + message: unknown; + forRenderer?: string; +} let version = 0; export class BackLayerWebView extends Disposable { element: HTMLElement; - webview!: WebviewElement; - insetMapping: Map = new Map(); - hiddenInsetMapping: Set = new Set(); - reversedInsetMapping: Map = new Map(); + webview: WebviewElement | undefined = undefined; + insetMapping: Map = new Map(); + hiddenInsetMapping: Set = new Set(); + reversedInsetMapping: Map = new Map(); preloadsCache: Map = new Map(); localResourceRootsCache: URI[] | undefined = undefined; rendererRootsCache: URI[] = []; - private readonly _onMessage = this._register(new Emitter()); - public readonly onMessage: Event = this._onMessage.event; - private _initalized: Promise; + kernelRootsCache: URI[] = []; + private readonly _onMessage = this._register(new Emitter()); + public readonly onMessage: Event = this._onMessage.event; + private _loaded!: Promise; + private _initalized?: Promise; + private _disposed = false; constructor( public notebookEditor: INotebookEditor, + public id: string, + public documentUri: URI, @IWebviewService readonly webviewService: IWebviewService, @IOpenerService readonly openerService: IOpenerService, @INotebookService private readonly notebookService: INotebookService, - @IEnvironmentService private readonly environmentService: IEnvironmentService + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService, + @IFileDialogService private readonly fileDialogService: IFileDialogService, + @IFileService private readonly fileService: IFileService, ) { super(); + this.element = document.createElement('div'); - this.element.style.width = `calc(100% - ${CELL_MARGIN * 2 + CELL_RUN_GUTTER}px)`; + this.element.style.width = `calc(100% - ${CODE_CELL_LEFT_MARGIN + (CELL_MARGIN * 2) + CELL_RUN_GUTTER}px)`; this.element.style.height = '1400px'; this.element.style.position = 'absolute'; - this.element.style.margin = `0px 0 0px ${CELL_MARGIN + CELL_RUN_GUTTER}px`; + this.element.style.margin = `0px 0 0px ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px`; + } + generateContent(outputNodePadding: number, coreDependencies: string, baseUrl: string) { + return html` + + + + + + + + + ${coreDependencies} +
+
+ + + `; + } + + postRendererMessage(rendererId: string, message: any) { + this._sendMessageToWebview({ + __vscode_notebook_message: true, + type: 'customRendererMessage', + message, + rendererId + }); + } + + private resolveOutputId(id: string): { cell: CodeCellViewModel, output: IProcessedOutput } | undefined { + const output = this.reversedInsetMapping.get(id); + if (!output) { + return; + } + + return { cell: this.insetMapping.get(output)!.cell, output }; + } + + async createWebview(): Promise { const pathsPath = getPathFromAmdModule(require, 'vs/loader.js'); - const loader = URI.file(pathsPath).with({ scheme: WebviewResourceScheme }); + const loader = asWebviewUri(this.workbenchEnvironmentService, this.id, URI.file(pathsPath)); let coreDependencies = ''; let resolveFunc: () => void; @@ -162,9 +354,11 @@ export class BackLayerWebView extends Disposable { resolveFunc = resolve; }); + const baseUrl = asWebviewUri(this.workbenchEnvironmentService, this.id, dirname(this.documentUri)); + if (!isWeb) { coreDependencies = ``; - const htmlContent = this.generateContent(8, coreDependencies); + const htmlContent = this.generateContent(CELL_OUTPUT_PADDING, coreDependencies, baseUrl.toString()); this.initialize(htmlContent); resolveFunc!(); } else { @@ -180,316 +374,66 @@ export class BackLayerWebView extends Disposable { ${loaderJs} `; - const htmlContent = this.generateContent(8, coreDependencies); + + const htmlContent = this.generateContent(CELL_OUTPUT_PADDING, coreDependencies, baseUrl.toString()); this.initialize(htmlContent); resolveFunc!(); }); } + + await this._initalized; } - generateContent(outputNodePadding: number, coreDependencies: string) { - return html` - - - - - - - - ${coreDependencies} -
-
- - -`; - } - - private resolveOutputId(id: string): { cell: CodeCellViewModel, output: IOutput } | undefined { - const output = this.reversedInsetMapping.get(id); - if (!output) { - return; - } - - return { cell: this.insetMapping.get(output)!.cell, output }; - } - - initialize(content: string) { this.webview = this._createInset(this.webviewService, content); this.webview.mountTo(this.element); + this._register(this.webview); this._register(this.webview.onDidClickLink(link => { - this.openerService.open(link, { fromUserGesture: true }); + if (this._disposed) { + return; + } + + if (!link) { + return; + } + + if (matchesScheme(link, Schemas.http) || matchesScheme(link, Schemas.https) || matchesScheme(link, Schemas.mailto) + || matchesScheme(link, Schemas.command)) { + this.openerService.open(link, { fromUserGesture: true }); + } })); this._register(this.webview.onDidReload(() => { + if (this._disposed) { + return; + } + this.preloadsCache.clear(); for (const [output, inset] of this.insetMapping.entries()) { this.updateRendererPreloads(inset.preloads); - this.webview.sendMessage({ ...inset.cachedCreation, initiallyHidden: this.hiddenInsetMapping.has(output) }); + this._sendMessageToWebview({ ...inset.cachedCreation, initiallyHidden: this.hiddenInsetMapping.has(output) }); } })); - this._register(this.webview.onMessage((data: IMessage) => { + this._register(this.webview.onMessage((data: FromWebviewMessage) => { + if (this._disposed) { + return; + } + if (data.__vscode_notebook_message) { if (data.type === 'dimension') { - let height = data.data.height; - let outputHeight = height; + const height = data.data.height; + const outputHeight = height; const info = this.resolveOutputId(data.id); if (info) { const { cell, output } = info; - let outputIndex = cell.outputs.indexOf(output); + const outputIndex = cell.outputs.indexOf(output); cell.updateOutputHeight(outputIndex, outputHeight); this.notebookEditor.layoutNotebookCell(cell, cell.layoutInfo.totalHeight); } @@ -534,36 +478,96 @@ ${loaderJs} this.notebookEditor.focusNotebookCell(info.cell, 'editor'); } } + } else if (data.type === 'clicked-data-url') { + this._onDidClickDataLink(data); + } else if (data.type === 'customRendererMessage') { + this._onMessage.fire({ message: data.message, forRenderer: data.rendererId }); } return; } - this._onMessage.fire(data); + this._onMessage.fire({ message: data }); })); } - async waitForInitialization() { - await this._initalized; + private async _onDidClickDataLink(event: IClickedDataUrlMessage): Promise { + const [splitStart, splitData] = event.data.split(';base64,'); + if (!splitData || !splitStart) { + return; + } + + const defaultDir = dirname(this.documentUri); + let defaultName: string; + if (event.downloadName) { + defaultName = event.downloadName; + } else { + const mimeType = splitStart.replace(/^data:/, ''); + const candidateExtension = mimeType && getExtensionForMimeType(mimeType); + defaultName = candidateExtension ? `download${candidateExtension}` : 'download'; + } + + const defaultUri = joinPath(defaultDir, defaultName); + const newFileUri = await this.fileDialogService.showSaveDialog({ + defaultUri + }); + if (!newFileUri) { + return; + } + + const decoded = atob(splitData); + const typedArray = new Uint8Array(decoded.length); + for (let i = 0; i < decoded.length; i++) { + typedArray[i] = decoded.charCodeAt(i); + } + + const buff = VSBuffer.wrap(typedArray); + await this.fileService.writeFile(newFileUri, buff); + await this.openerService.open(newFileUri); } private _createInset(webviewService: IWebviewService, content: string) { const rootPath = URI.file(path.dirname(getPathFromAmdModule(require, ''))); - this.localResourceRootsCache = [...this.notebookService.getNotebookProviderResourceRoots(), rootPath]; - const webview = webviewService.createWebviewElement('' + UUID.generateUuid(), { + const workspaceFolders = this.contextService.getWorkspace().folders.map(x => x.uri); + + this.localResourceRootsCache = [...this.notebookService.getNotebookProviderResourceRoots(), ...workspaceFolders, rootPath]; + + const webview = webviewService.createWebviewElement(this.id, { + purpose: WebviewContentPurpose.NotebookRenderer, enableFindWidget: false, }, { allowMultipleAPIAcquire: true, allowScripts: true, localResourceRoots: this.localResourceRootsCache + }, undefined); + + let resolveFunc: () => void; + this._loaded = new Promise((resolve, reject) => { + resolveFunc = resolve; }); + + const dispose = webview.onMessage((data: FromWebviewMessage) => { + if (data.__vscode_notebook_message && data.type === 'initialized') { + resolveFunc(); + dispose.dispose(); + } + }); + webview.html = content; return webview; } - shouldUpdateInset(cell: CodeCellViewModel, output: IOutput, cellTop: number) { - let outputCache = this.insetMapping.get(output)!; - let outputIndex = cell.outputs.indexOf(output); - let outputOffset = cellTop + cell.getOutputOffset(outputIndex); + shouldUpdateInset(cell: CodeCellViewModel, output: IProcessedOutput, cellTop: number) { + if (this._disposed) { + return; + } + + if (cell.metadata?.outputCollapsed) { + return false; + } + + const outputCache = this.insetMapping.get(output)!; + const outputIndex = cell.outputs.indexOf(output); + const outputOffset = cellTop + cell.getOutputOffset(outputIndex); if (this.hiddenInsetMapping.has(output)) { return true; @@ -576,13 +580,17 @@ ${loaderJs} return true; } - updateViewScrollTop(top: number, items: { cell: CodeCellViewModel, output: IOutput, cellTop: number }[]) { - let widgets: IContentWidgetTopRequest[] = items.map(item => { - let outputCache = this.insetMapping.get(item.output)!; - let id = outputCache.outputId; - let outputIndex = item.cell.outputs.indexOf(item.output); + updateViewScrollTop(top: number, items: { cell: CodeCellViewModel, output: IProcessedOutput, cellTop: number }[]) { + if (this._disposed) { + return; + } - let outputOffset = item.cellTop + item.cell.getOutputOffset(outputIndex); + const widgets: IContentWidgetTopRequest[] = items.map(item => { + const outputCache = this.insetMapping.get(item.output)!; + const id = outputCache.outputId; + const outputIndex = item.cell.outputs.indexOf(item.output); + + const outputOffset = item.cellTop + item.cell.getOutputOffset(outputIndex); outputCache.cachedCreation.top = outputOffset; this.hiddenInsetMapping.delete(item.output); @@ -593,84 +601,111 @@ ${loaderJs} }; }); - let message: IViewScrollTopRequestMessage = { + this._sendMessageToWebview({ top, type: 'view-scroll', version: version++, widgets: widgets - }; - - this.webview.sendMessage(message); + }); } - createInset(cell: CodeCellViewModel, output: IOutput, cellTop: number, offset: number, shadowContent: string, preloads: Set) { - this.updateRendererPreloads(preloads); - let initialTop = cellTop + offset; + async createInset(cell: CodeCellViewModel, output: IProcessedOutput, cellTop: number, offset: number, shadowContent: string, preloads: Set) { + if (this._disposed) { + return; + } + + const requiredPreloads = await this.updateRendererPreloads(preloads); + const initialTop = cellTop + offset; if (this.insetMapping.has(output)) { - let outputCache = this.insetMapping.get(output); + const outputCache = this.insetMapping.get(output); if (outputCache) { this.hiddenInsetMapping.delete(output); - this.webview.sendMessage({ + this._sendMessageToWebview({ type: 'showOutput', - id: outputCache.outputId, + cellId: outputCache.cell.id, + outputId: outputCache.outputId, top: initialTop }); return; } } - let outputId = UUID.generateUuid(); + const outputId = output.outputKind === CellOutputKind.Rich ? output.outputId : UUID.generateUuid(); + let apiNamespace: string | undefined; + if (output.outputKind === CellOutputKind.Rich && output.pickedMimeTypeIndex !== undefined) { + const pickedMimeTypeRenderer = output.orderedMimeTypes?.[output.pickedMimeTypeIndex]; + if (pickedMimeTypeRenderer?.rendererId) { + apiNamespace = this.notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId)?.id; + } + } - let message: ICreationRequestMessage = { + const message: ICreationRequestMessage = { type: 'html', content: shadowContent, - id: cell.id, + cellId: cell.id, + apiNamespace, outputId: outputId, top: initialTop, + requiredPreloads, left: 0 }; - this.webview.sendMessage(message); + this._sendMessageToWebview(message); this.insetMapping.set(output, { outputId: outputId, cell: cell, preloads, cachedCreation: message }); this.hiddenInsetMapping.delete(output); this.reversedInsetMapping.set(outputId, output); } - removeInset(output: IOutput) { - let outputCache = this.insetMapping.get(output); + removeInset(output: IProcessedOutput) { + if (this._disposed) { + return; + } + + const outputCache = this.insetMapping.get(output); if (!outputCache) { return; } - let id = outputCache.outputId; + const id = outputCache.outputId; - this.webview.sendMessage({ + this._sendMessageToWebview({ type: 'clearOutput', - id: id + apiNamespace: outputCache.cachedCreation.apiNamespace, + cellUri: outputCache.cell.uri.toString(), + outputId: id, + cellId: outputCache.cell.id }); this.insetMapping.delete(output); this.reversedInsetMapping.delete(id); } - hideInset(output: IOutput) { - let outputCache = this.insetMapping.get(output); + hideInset(output: IProcessedOutput) { + if (this._disposed) { + return; + } + + const outputCache = this.insetMapping.get(output); if (!outputCache) { return; } - let id = outputCache.outputId; this.hiddenInsetMapping.add(output); - this.webview.sendMessage({ + this._sendMessageToWebview({ type: 'hideOutput', - id: id + outputId: outputCache.outputId, + cellId: outputCache.cell.id, }); } clearInsets() { - this.webview.sendMessage({ + if (this._disposed) { + return; + } + + this._sendMessageToWebview({ type: 'clear' }); @@ -678,58 +713,141 @@ ${loaderJs} this.reversedInsetMapping = new Map(); } + focusWebview() { + if (this._disposed) { + return; + } + + this.webview?.focus(); + } + focusOutput(cellId: string) { - this.webview.focus(); + if (this._disposed) { + return; + } + + this.webview?.focus(); setTimeout(() => { // Need this, or focus decoration is not shown. No clue. - this.webview.sendMessage({ + this._sendMessageToWebview({ type: 'focus-output', - id: cellId + cellId, }); }, 50); } - updateRendererPreloads(preloads: ReadonlySet) { - let resources: string[] = []; - let extensionLocations: URI[] = []; + deltaCellOutputContainerClassNames(cellId: string, added: string[], removed: string[]) { + this._sendMessageToWebview({ + type: 'decorations', + cellId, + addedClassNames: added, + removedClassNames: removed + }); + + } + + async updateKernelPreloads(extensionLocations: URI[], preloads: URI[]) { + if (this._disposed) { + return; + } + + await this._loaded; + + const resources: IPreloadResource[] = []; + preloads = preloads.map(preload => { + if (this.environmentService.isExtensionDevelopment && (preload.scheme === 'http' || preload.scheme === 'https')) { + return preload; + } + return asWebviewUri(this.workbenchEnvironmentService, this.id, preload); + }); + + preloads.forEach(e => { + if (!this.preloadsCache.has(e.toString())) { + resources.push({ uri: e.toString() }); + this.preloadsCache.set(e.toString(), true); + } + }); + + if (!resources.length) { + return; + } + + this.kernelRootsCache = [...extensionLocations, ...this.kernelRootsCache]; + this._updatePreloads(resources, 'kernel'); + } + + async updateRendererPreloads(preloads: ReadonlySet) { + if (this._disposed) { + return []; + } + + await this._loaded; + + const requiredPreloads: IPreloadResource[] = []; + const resources: IPreloadResource[] = []; + const extensionLocations: URI[] = []; preloads.forEach(preload => { - let rendererInfo = this.notebookService.getRendererInfo(preload); + const rendererInfo = this.notebookService.getRendererInfo(preload); if (rendererInfo) { - let preloadResources = rendererInfo.preloads.map(preloadResource => { + const preloadResources = rendererInfo.preloads.map(preloadResource => { if (this.environmentService.isExtensionDevelopment && (preloadResource.scheme === 'http' || preloadResource.scheme === 'https')) { return preloadResource; } - return preloadResource.with({ scheme: WebviewResourceScheme }); + return asWebviewUri(this.workbenchEnvironmentService, this.id, preloadResource); }); extensionLocations.push(rendererInfo.extensionLocation); preloadResources.forEach(e => { + const resource: IPreloadResource = { uri: e.toString() }; + requiredPreloads.push(resource); + if (!this.preloadsCache.has(e.toString())) { - resources.push(e.toString()); + resources.push(resource); this.preloadsCache.set(e.toString(), true); } }); } }); + if (!resources.length) { + return requiredPreloads; + } + this.rendererRootsCache = extensionLocations; - const mixedResourceRoots = [...(this.localResourceRootsCache || []), ...this.rendererRootsCache]; + this._updatePreloads(resources, 'renderer'); + return requiredPreloads; + } - this.webview.contentOptions = { - allowMultipleAPIAcquire: true, - allowScripts: true, - enableCommandUris: true, - localResourceRoots: mixedResourceRoots - }; + private _updatePreloads(resources: IPreloadResource[], source: 'renderer' | 'kernel') { + if (!this.webview) { + return; + } - let message: IUpdatePreloadResourceMessage = { + const mixedResourceRoots = [...(this.localResourceRootsCache || []), ...this.rendererRootsCache, ...this.kernelRootsCache]; + + this.webview.localResourcesRoot = mixedResourceRoots; + + this._sendMessageToWebview({ type: 'preload', - resources: resources - }; + resources: resources, + source: source + }); + } - this.webview.sendMessage(message); + private _sendMessageToWebview(message: ToWebviewMessage) { + if (this._disposed) { + return; + } + + this.webview?.postMessage(message); } clearPreloadsCache() { this.preloadsCache.clear(); } + + dispose() { + this._disposed = true; + this.webview?.dispose(); + super.dispose(); + } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts new file mode 100644 index 00000000000..c5ccf32bc91 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +import * as DOM from 'vs/base/browser/dom'; +import { Action, IAction, Separator } from 'vs/base/common/actions'; +import { IMenu, IMenuActionOptions, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; + +export class VerticalSeparator extends Action { + static readonly ID = 'vs.actions.verticalSeparator'; + + constructor( + label?: string + ) { + super(VerticalSeparator.ID, label, label ? 'verticalSeparator text' : 'verticalSeparator'); + this.checked = false; + this.enabled = false; + } +} + +export class VerticalSeparatorViewItem extends BaseActionViewItem { + render(container: HTMLElement) { + DOM.addClass(container, 'verticalSeparator'); + // const iconContainer = DOM.append(container, $('.verticalSeparator')); + // DOM.addClasses(iconContainer, 'codicon', 'codicon-chrome-minimize'); + } +} + +export function createAndFillInActionBarActionsWithVerticalSeparators(menu: IMenu, options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, isPrimaryGroup?: (group: string) => boolean): IDisposable { + const groups = menu.getActions(options); + // Action bars handle alternative actions on their own so the alternative actions should be ignored + fillInActions(groups, target, false, isPrimaryGroup); + return asDisposable(groups); +} + +function fillInActions(groups: ReadonlyArray<[string, ReadonlyArray]>, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, useAlternativeActions: boolean, isPrimaryGroup: (group: string) => boolean = group => group === 'navigation'): void { + for (const tuple of groups) { + let [group, actions] = tuple; + if (useAlternativeActions) { + actions = actions.map(a => (a instanceof MenuItemAction) && !!a.alt ? a.alt : a); + } + + if (isPrimaryGroup(group)) { + const to = Array.isArray(target) ? target : target.primary; + + if (to.length > 0) { + to.push(new VerticalSeparator()); + } + + to.push(...actions); + } else { + const to = Array.isArray(target) ? target : target.secondary; + + if (to.length > 0) { + to.push(new Separator()); + } + + to.push(...actions); + } + } +} + +function asDisposable(groups: ReadonlyArray<[string, ReadonlyArray]>): IDisposable { + const disposables = new DisposableStore(); + for (const [, actions] of groups) { + for (const action of actions) { + disposables.add(action); + } + } + return disposables; +} diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts new file mode 100644 index 00000000000..3c3b9eac7f4 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts @@ -0,0 +1,123 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { INotebookTextModel, NotebookCellRunState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel'; +import { NOTEBOOK_CELL_TYPE, NOTEBOOK_VIEW_TYPE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_RUNNABLE, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_RUN_STATE, NOTEBOOK_CELL_HAS_OUTPUTS, CellViewModelStateChangeEvent, CellEditState, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_OUTPUT_COLLAPSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; +import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; + +export class CellContextKeyManager extends Disposable { + + private cellType!: IContextKey; + private viewType!: IContextKey; + private cellEditable!: IContextKey; + private cellRunnable!: IContextKey; + private cellRunState!: IContextKey; + private cellHasOutputs!: IContextKey; + private cellContentCollapsed!: IContextKey; + private cellOutputCollapsed!: IContextKey; + + private markdownEditMode!: IContextKey; + + private elementDisposables = new DisposableStore(); + + constructor( + private readonly contextKeyService: IContextKeyService, + private readonly notebookTextModel: INotebookTextModel, + private element: BaseCellViewModel + ) { + super(); + + this.contextKeyService.bufferChangeEvents(() => { + this.cellType = NOTEBOOK_CELL_TYPE.bindTo(this.contextKeyService); + this.viewType = NOTEBOOK_VIEW_TYPE.bindTo(this.contextKeyService); + this.cellEditable = NOTEBOOK_CELL_EDITABLE.bindTo(this.contextKeyService); + this.cellRunnable = NOTEBOOK_CELL_RUNNABLE.bindTo(this.contextKeyService); + this.markdownEditMode = NOTEBOOK_CELL_MARKDOWN_EDIT_MODE.bindTo(this.contextKeyService); + this.cellRunState = NOTEBOOK_CELL_RUN_STATE.bindTo(this.contextKeyService); + this.cellHasOutputs = NOTEBOOK_CELL_HAS_OUTPUTS.bindTo(this.contextKeyService); + this.cellContentCollapsed = NOTEBOOK_CELL_INPUT_COLLAPSED.bindTo(this.contextKeyService); + this.cellOutputCollapsed = NOTEBOOK_CELL_OUTPUT_COLLAPSED.bindTo(this.contextKeyService); + + this.updateForElement(element); + }); + } + + public updateForElement(element: BaseCellViewModel) { + this.elementDisposables.clear(); + this.elementDisposables.add(element.onDidChangeState(e => this.onDidChangeState(e))); + + if (element instanceof CodeCellViewModel) { + this.elementDisposables.add(element.onDidChangeOutputs(() => this.updateForOutputs())); + } + + this.elementDisposables.add(element.model.onDidChangeMetadata(() => this.updateForCollapseState())); + + this.element = element; + if (this.element instanceof MarkdownCellViewModel) { + this.cellType.set('markdown'); + } else if (this.element instanceof CodeCellViewModel) { + this.cellType.set('code'); + } + + this.contextKeyService.bufferChangeEvents(() => { + this.updateForMetadata(); + this.updateForEditState(); + this.updateForCollapseState(); + this.updateForOutputs(); + + this.viewType.set(this.element.viewType); + }); + } + + private onDidChangeState(e: CellViewModelStateChangeEvent) { + this.contextKeyService.bufferChangeEvents(() => { + if (e.metadataChanged) { + this.updateForMetadata(); + } + + if (e.editStateChanged) { + this.updateForEditState(); + } + + // if (e.collapseStateChanged) { + // this.updateForCollapseState(); + // } + }); + } + + private updateForMetadata() { + const metadata = this.element.getEvaluatedMetadata(this.notebookTextModel.metadata); + this.cellEditable.set(!!metadata.editable); + this.cellRunnable.set(!!metadata.runnable); + + const runState = metadata.runState ?? NotebookCellRunState.Idle; + this.cellRunState.set(NotebookCellRunState[runState]); + } + + private updateForEditState() { + if (this.element instanceof MarkdownCellViewModel) { + this.markdownEditMode.set(this.element.editState === CellEditState.Editing); + } else { + this.markdownEditMode.set(false); + } + } + + private updateForCollapseState() { + this.cellContentCollapsed.set(!!this.element.metadata?.inputCollapsed); + this.cellOutputCollapsed.set(!!this.element.metadata?.outputCollapsed); + } + + private updateForOutputs() { + if (this.element instanceof CodeCellViewModel) { + this.cellHasOutputs.set(this.element.outputs.length > 0); + } else { + this.cellHasOutputs.set(false); + } + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellMenus.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellMenus.ts index 324d509dafa..8c3f6e513b5 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellMenus.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellMenus.ts @@ -3,30 +3,25 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IAction } from 'vs/base/common/actions'; -import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; export class CellMenus { constructor( @IMenuService private readonly menuService: IMenuService, - @IContextMenuService private readonly contextMenuService: IContextMenuService ) { } getCellTitleMenu(contextKeyService: IContextKeyService): IMenu { return this.getMenu(MenuId.NotebookCellTitle, contextKeyService); } + getCellInsertionMenu(contextKeyService: IContextKeyService): IMenu { + return this.getMenu(MenuId.NotebookCellBetween, contextKeyService); + } + private getMenu(menuId: MenuId, contextKeyService: IContextKeyService): IMenu { const menu = this.menuService.createMenu(menuId, contextKeyService); - const primary: IAction[] = []; - const secondary: IAction[] = []; - const result = { primary, secondary }; - - createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, this.contextMenuService, g => /^inline/.test(g)); return menu; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index a3c99526d5f..bd860ff204f 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -6,21 +6,17 @@ import { getZoomLevel } from 'vs/base/browser/browser'; import * as DOM from 'vs/base/browser/dom'; import { domEvent } from 'vs/base/browser/event'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; -import { ActionRunner, IAction } from 'vs/base/common/actions'; +import { IAction } from 'vs/base/common/actions'; +import { Delayer } from 'vs/base/common/async'; import { renderCodicons } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; -import { KeyCode } from 'vs/base/common/keyCodes'; -import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { deepClone } from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; -import { escape } from 'vs/base/common/strings'; -// eslint-disable-next-line code-import-patterns -import 'vs/css!vs/workbench/contrib/notebook/browser/media/notebook'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { EditorOption, EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions'; @@ -31,42 +27,42 @@ import { ITextModel } from 'vs/editor/common/model'; import * as modes from 'vs/editor/common/modes'; import { tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer'; import { IModeService } from 'vs/editor/common/services/modeService'; -import * as nls from 'vs/nls'; -import { ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IMenu, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenu, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { BOTTOM_CELL_TOOLBAR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CancelCellAction, ChangeCellLanguageAction, ExecuteCellAction, INotebookCellActionContext, InsertCodeCellAction, InsertMarkdownCellAction } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; -import { BaseCellRenderTemplate, CellEditState, CellRunState, CodeCellRenderTemplate, ICellViewModel, INotebookEditor, MarkdownCellRenderTemplate, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_RUNNABLE, NOTEBOOK_CELL_RUN_STATE, NOTEBOOK_CELL_TYPE, NOTEBOOK_VIEW_TYPE } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_TOP_MARGIN, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; +import { CancelCellAction, ChangeCellLanguageAction, ExecuteCellAction, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; +import { BaseCellRenderTemplate, CellEditState, CodeCellRenderTemplate, ICellViewModel, INotebookCellList, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; import { CellMenus } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellMenus'; import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/codeCell'; -import { StatefullMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/markdownCell'; -import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel'; +import { StatefulMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/markdownCell'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { CellKind, NotebookCellRunState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, NotebookCellMetadata, NotebookCellRunState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { createAndFillInActionBarActionsWithVerticalSeparators, VerticalSeparator, VerticalSeparatorViewItem } from './cellActionView'; const $ = DOM.$; export class NotebookCellListDelegate implements IListVirtualDelegate { - private _lineHeight: number; + private readonly lineHeight: number; constructor( @IConfigurationService private readonly configurationService: IConfigurationService ) { const editorOptions = this.configurationService.getValue('editor'); - this._lineHeight = BareFontInfo.createFromRawSettings(editorOptions, getZoomLevel()).lineHeight; + this.lineHeight = BareFontInfo.createFromRawSettings(editorOptions, getZoomLevel()).lineHeight; } getHeight(element: CellViewModel): number { - return element.getHeight(this._lineHeight); + return element.getHeight(this.lineHeight); } hasDynamicHeight(element: CellViewModel): boolean { @@ -82,14 +78,14 @@ export class NotebookCellListDelegate implements IListVirtualDelegate(); readonly onDidChange: Event = this._onDidChange.event; constructor(configurationService: IConfigurationService, language: string) { - this._disposable = configurationService.onDidChangeConfiguration(e => { + this.disposable = configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('editor')) { this._value = computeEditorOptions(); this._onDidChange.fire(this.value); @@ -142,10 +138,16 @@ export class CellEditorOptions { const computeEditorOptions = () => { const editorOptions = deepClone(configurationService.getValue('editor', { overrideIdentifier: language })); - return { + const computed = { ...editorOptions, ...CellEditorOptions.fixedEditorOptions }; + + if (!computed.folding) { + computed.lineDecorationsWidth = 16; + } + + return computed; }; this._value = computeEditorOptions(); @@ -153,17 +155,24 @@ export class CellEditorOptions { dispose(): void { this._onDidChange.dispose(); - this._disposable.dispose(); + this.disposable.dispose(); } get value(): IEditorOptions { return this._value; } + + setGlyphMargin(gm: boolean): void { + if (gm !== this._value.glyphMargin) { + this._value.glyphMargin = gm; + this._onDidChange.fire(this.value); + } + } } abstract class AbstractCellRenderer { - protected editorOptions: CellEditorOptions; - private actionRunner = new ActionRunner(); + protected readonly editorOptions: CellEditorOptions; + protected readonly cellMenus: CellMenus; constructor( protected readonly instantiationService: IInstantiationService, @@ -172,18 +181,19 @@ abstract class AbstractCellRenderer { configurationService: IConfigurationService, private readonly keybindingService: IKeybindingService, private readonly notificationService: INotificationService, - protected readonly contextKeyService: IContextKeyService, + protected readonly contextKeyServiceProvider: (container?: HTMLElement) => IContextKeyService, language: string, protected readonly dndController: CellDragAndDropController ) { this.editorOptions = new CellEditorOptions(configurationService, language); + this.cellMenus = this.instantiationService.createInstance(CellMenus); } dispose() { this.editorOptions.dispose(); } - protected createBottomCellToolbar(container: HTMLElement): ToolBar { + protected createBetweenCellToolbar(container: HTMLElement, disposables: DisposableStore, contextKeyService: IContextKeyService): ToolBar { const toolbar = new ToolBar(container, this.contextMenuService, { actionViewItemProvider: action => { if (action instanceof MenuItemAction) { @@ -195,81 +205,40 @@ abstract class AbstractCellRenderer { } }); - toolbar.getContainer().style.height = `${BOTTOM_CELL_TOOLBAR_HEIGHT}px`; + const cellMenu = this.instantiationService.createInstance(CellMenus); + const menu = disposables.add(cellMenu.getCellInsertionMenu(contextKeyService)); + + const actions = this.getCellToolbarActions(menu); + toolbar.setActions(actions.primary, actions.secondary); + return toolbar; } - protected setupBetweenCellToolbarActions(element: CodeCellViewModel | MarkdownCellViewModel, templateData: BaseCellRenderTemplate, disposables: DisposableStore, context: INotebookCellActionContext): void { + protected setBetweenCellToolbarContext(templateData: BaseCellRenderTemplate, element: CodeCellViewModel | MarkdownCellViewModel, context: INotebookCellActionContext): void { + templateData.betweenCellToolbar.context = context; + const container = templateData.bottomCellContainer; - container.innerHTML = ''; - container.style.height = `${BOTTOM_CELL_TOOLBAR_HEIGHT}px`; + const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset; + container.style.top = `${bottomToolbarOffset}px`; - DOM.append(container, $('.seperator')); - const addCodeCell = DOM.append(container, $('span.button')); - addCodeCell.innerHTML = renderCodicons(escape(`$(add) Code `)); - addCodeCell.tabIndex = 0; - const insertCellBelow = this.instantiationService.createInstance(InsertCodeCellAction); - - const toolbarContext = { - ...context, - ui: true - }; - - disposables.add(DOM.addDisposableListener(addCodeCell, DOM.EventType.CLICK, e => { - this.actionRunner.run(insertCellBelow, toolbarContext); - e.stopPropagation(); - })); - - disposables.add((DOM.addDisposableListener(addCodeCell, DOM.EventType.KEY_DOWN, async e => { - const event = new StandardKeyboardEvent(e); - if ((event.equals(KeyCode.Enter) || event.equals(KeyCode.Space))) { - e.preventDefault(); - e.stopPropagation(); - this.actionRunner.run(insertCellBelow, toolbarContext); - } - }))); - - DOM.append(container, $('.seperator-short')); - const addMarkdownCell = DOM.append(container, $('span.button')); - addMarkdownCell.innerHTML = renderCodicons(escape('$(add) Markdown ')); - addMarkdownCell.tabIndex = 0; - const insertMarkdownBelow = this.instantiationService.createInstance(InsertMarkdownCellAction); - disposables.add(DOM.addDisposableListener(addMarkdownCell, DOM.EventType.CLICK, e => { - this.actionRunner.run(insertMarkdownBelow, toolbarContext); - e.stopPropagation(); - })); - - disposables.add((DOM.addDisposableListener(addMarkdownCell, DOM.EventType.KEY_DOWN, async e => { - const event = new StandardKeyboardEvent(e); - if ((event.equals(KeyCode.Enter) || event.equals(KeyCode.Space))) { - e.preventDefault(); - e.stopPropagation(); - this.actionRunner.run(insertMarkdownBelow, toolbarContext); - } - }))); - - DOM.append(container, $('.seperator')); - - if (element instanceof CodeCellViewModel) { + templateData.elementDisposables.add(element.onDidChangeLayout(() => { const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset; container.style.top = `${bottomToolbarOffset}px`; - - disposables.add(element.onDidChangeLayout(() => { - const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset; - container.style.top = `${bottomToolbarOffset}px`; - })); - } else { - container.style.position = 'static'; - container.style.height = `${BOTTOM_CELL_TOOLBAR_HEIGHT}`; - } + })); } protected createToolbar(container: HTMLElement): ToolBar { const toolbar = new ToolBar(container, this.contextMenuService, { + getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id), actionViewItemProvider: action => { if (action instanceof MenuItemAction) { - const item = new ContextAwareMenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService); - return item; + return this.instantiationService.createInstance(MenuEntryActionViewItem, action); + } else if (action instanceof SubmenuItemAction) { + return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action); + } + + if (action.id === VerticalSeparator.ID) { + return new VerticalSeparatorViewItem(undefined, action); } return undefined; @@ -279,65 +248,108 @@ abstract class AbstractCellRenderer { return toolbar; } - private getCellToolbarActions(menu: IMenu): IAction[] { - const actions: IAction[] = []; - for (let [, menuActions] of menu.getActions({ shouldForwardArgs: true })) { - actions.push(...menuActions); - } + private getCellToolbarActions(menu: IMenu): { primary: IAction[], secondary: IAction[] } { + const primary: IAction[] = []; + const secondary: IAction[] = []; + const result = { primary, secondary }; - return actions; + createAndFillInActionBarActionsWithVerticalSeparators(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g)); + + return result; } - protected setupCellToolbarActions(scopedContextKeyService: IContextKeyService, templateData: BaseCellRenderTemplate, disposables: DisposableStore): void { - const cellMenu = this.instantiationService.createInstance(CellMenus); - const menu = disposables.add(cellMenu.getCellTitleMenu(scopedContextKeyService)); - + protected setupCellToolbarActions(templateData: BaseCellRenderTemplate, disposables: DisposableStore): void { const updateActions = () => { - const actions = this.getCellToolbarActions(menu); + const actions = this.getCellToolbarActions(templateData.titleMenu); - templateData.toolbar.setActions(actions)(); + const hadFocus = DOM.isAncestor(document.activeElement, templateData.toolbar.getContainer()); + templateData.toolbar.setActions(actions.primary, actions.secondary); + if (hadFocus) { + this.notebookEditor.focus(); + } - if (templateData.focusIndicator) { - if (actions.length) { - templateData.container.classList.add('cell-has-toolbar-actions'); - templateData.focusIndicator.style.top = `${EDITOR_TOOLBAR_HEIGHT + EDITOR_TOP_MARGIN}px`; - } else { - templateData.container.classList.remove('cell-has-toolbar-actions'); - templateData.focusIndicator.style.top = `${EDITOR_TOP_MARGIN}px`; + if (actions.primary.length || actions.secondary.length) { + templateData.container.classList.add('cell-has-toolbar-actions'); + if (isCodeCellRenderTemplate(templateData)) { + templateData.focusIndicatorLeft.style.top = `${EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN}px`; + templateData.focusIndicatorRight.style.top = `${EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN}px`; + } + } else { + templateData.container.classList.remove('cell-has-toolbar-actions'); + if (isCodeCellRenderTemplate(templateData)) { + templateData.focusIndicatorLeft.style.top = `${CELL_TOP_MARGIN}px`; + templateData.focusIndicatorRight.style.top = `${CELL_TOP_MARGIN}px`; } } }; updateActions(); - disposables.add(menu.onDidChange(() => { + disposables.add(templateData.titleMenu.onDidChange(() => { + if (this.notebookEditor.isDisposed) { + return; + } + updateActions(); })); } - protected commonRenderElement(element: BaseCellViewModel, index: number, templateData: BaseCellRenderTemplate): void { + protected commonRenderTemplate(templateData: BaseCellRenderTemplate): void { + templateData.disposables.add(DOM.addDisposableListener(templateData.container, DOM.EventType.FOCUS, () => { + if (templateData.currentRenderedCell) { + this.notebookEditor.selectElement(templateData.currentRenderedCell); + } + }, true)); + + this.addExpandListener(templateData); + } + + protected commonRenderElement(element: ICellViewModel, index: number, templateData: BaseCellRenderTemplate): void { if (element.dragging) { templateData.container.classList.add(DRAGGING_CLASS); } else { templateData.container.classList.remove(DRAGGING_CLASS); } } + + protected addExpandListener(templateData: BaseCellRenderTemplate): void { + templateData.disposables.add(domEvent(templateData.expandButton, DOM.EventType.CLICK)(() => { + if (!templateData.currentRenderedCell) { + return; + } + + if (templateData.currentRenderedCell.metadata?.inputCollapsed) { + this.notebookEditor.viewModel!.notebookDocument.changeCellMetadata(templateData.currentRenderedCell.handle, { inputCollapsed: false }); + } else if (templateData.currentRenderedCell.metadata?.outputCollapsed) { + this.notebookEditor.viewModel!.notebookDocument.changeCellMetadata(templateData.currentRenderedCell.handle, { outputCollapsed: false }); + } + })); + } + + protected setupCollapsedPart(container: HTMLElement): { collapsedPart: HTMLElement, expandButton: HTMLElement } { + const collapsedPart = DOM.append(container, $('.cell.cell-collapsed-part')); + collapsedPart.innerHTML = renderCodicons('$(unfold)'); + const expandButton = collapsedPart.querySelector('.codicon') as HTMLElement; + DOM.hide(collapsedPart); + + return { collapsedPart, expandButton }; + } } export class MarkdownCellRenderer extends AbstractCellRenderer implements IListRenderer { static readonly TEMPLATE_ID = 'markdown_cell'; constructor( - contextKeyService: IContextKeyService, - notehookEditor: INotebookEditor, + notebookEditor: INotebookEditor, dndController: CellDragAndDropController, private renderedEditors: Map, + contextKeyServiceProvider: (container?: HTMLElement) => IContextKeyService, @IInstantiationService instantiationService: IInstantiationService, @IConfigurationService configurationService: IConfigurationService, @IContextMenuService contextMenuService: IContextMenuService, @IKeybindingService keybindingService: IKeybindingService, @INotificationService notificationService: INotificationService, ) { - super(instantiationService, notehookEditor, contextMenuService, configurationService, keybindingService, notificationService, contextKeyService, 'markdown', dndController); + super(instantiationService, notebookEditor, contextMenuService, configurationService, keybindingService, notificationService, contextKeyServiceProvider, 'markdown', dndController); } get templateId() { @@ -347,8 +359,9 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR renderTemplate(container: HTMLElement): MarkdownCellRenderTemplate { container.classList.add('markdown-cell-row'); const disposables = new DisposableStore(); + const contextKeyService = disposables.add(this.contextKeyServiceProvider(container)); const toolbar = disposables.add(this.createToolbar(container)); - const focusIndicator = DOM.append(container, DOM.$('.notebook-cell-focus-indicator')); + const focusIndicator = DOM.append(container, DOM.$('.cell-focus-indicator.cell-focus-indicator-side.cell-focus-indicator-left')); focusIndicator.setAttribute('draggable', 'true'); const codeInnerContent = DOM.append(container, $('.cell.code')); @@ -357,42 +370,51 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR editorPart.style.display = 'none'; const innerContent = DOM.append(container, $('.cell.markdown')); - DOM.append(container, DOM.$('.cell-insertion-indicator.cell-insertion-indicator-top')); - DOM.append(container, DOM.$('.cell-insertion-indicator.cell-insertion-indicator-bottom')); const foldingIndicator = DOM.append(focusIndicator, DOM.$('.notebook-folding-indicator')); + const { collapsedPart, expandButton } = this.setupCollapsedPart(container); + const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container')); + const betweenCellToolbar = disposables.add(this.createBetweenCellToolbar(bottomCellContainer, disposables, contextKeyService)); const statusBar = this.instantiationService.createInstance(CellEditorStatusBar, editorPart); + const titleMenu = disposables.add(this.cellMenus.getCellTitleMenu(contextKeyService)); const templateData: MarkdownCellRenderTemplate = { + collapsedPart, + expandButton, + contextKeyService, container, cellContainer: innerContent, editorPart, editorContainer, - focusIndicator, + focusIndicatorLeft: focusIndicator, foldingIndicator, disposables, elementDisposables: new DisposableStore(), toolbar, + betweenCellToolbar, bottomCellContainer, statusBarContainer: statusBar.statusBarContainer, languageStatusBarItem: statusBar.languageStatusBarItem, + titleMenu, toJSON: () => { return {}; } }; - this.dndController.addListeners(templateData, () => this.getDragImage(templateData)); + this.dndController.registerDragHandle(templateData, () => this.getDragImage(templateData)); + this.commonRenderTemplate(templateData); + return templateData; } private getDragImage(templateData: MarkdownCellRenderTemplate): HTMLElement { if (templateData.currentRenderedCell!.editState === CellEditState.Editing) { - return this._getEditDragImage(templateData); + return this.getEditDragImage(templateData); } else { - return this._getMarkdownDragImage(templateData); + return this.getMarkdownDragImage(templateData); } } - private _getMarkdownDragImage(templateData: MarkdownCellRenderTemplate): HTMLElement { + private getMarkdownDragImage(templateData: MarkdownCellRenderTemplate): HTMLElement { const dragImageContainer = DOM.$('.cell-drag-image.monaco-list-row.focused.markdown-cell-row'); dragImageContainer.innerHTML = templateData.container.innerHTML; @@ -406,7 +428,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR return dragImageContainer; } - private _getEditDragImage(templateData: MarkdownCellRenderTemplate): HTMLElement { + private getEditDragImage(templateData: MarkdownCellRenderTemplate): HTMLElement { return new CodeCellDragImageRenderer().getDragImage(templateData, templateData.currentEditor!, 'markdown'); } @@ -417,93 +439,162 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR templateData.currentEditor = undefined; templateData.editorPart!.style.display = 'none'; templateData.cellContainer.innerHTML = ''; - let renderedHTML = element.getHTML(); + const renderedHTML = element.getHTML(); if (renderedHTML) { templateData.cellContainer.appendChild(renderedHTML); } - if (height) { - const elementDisposables = templateData.elementDisposables; - - // render toolbar first - const contextKeyService = this.contextKeyService.createScoped(templateData.container); - this.setupCellToolbarActions(contextKeyService, templateData, elementDisposables); - - const toolbarContext = { - cell: element, - notebookEditor: this.notebookEditor, - $mid: 12 - }; - templateData.toolbar.context = toolbarContext; - - this.setupBetweenCellToolbarActions(element, templateData, elementDisposables, toolbarContext); - - const markdownCell = this.instantiationService.createInstance(StatefullMarkdownCell, this.notebookEditor, element, templateData, this.editorOptions.value, this.renderedEditors); - elementDisposables.add(this.editorOptions.onDidChange(newValue => markdownCell.updateEditorOptions(newValue))); - elementDisposables.add(markdownCell); - - NOTEBOOK_CELL_TYPE.bindTo(contextKeyService).set('markdown'); - NOTEBOOK_VIEW_TYPE.bindTo(contextKeyService).set(element.viewType); - const metadata = element.getEvaluatedMetadata(this.notebookEditor.viewModel!.notebookDocument.metadata); - const cellEditableKey = NOTEBOOK_CELL_EDITABLE.bindTo(contextKeyService); - cellEditableKey.set(!!metadata.editable); - const updateForMetadata = () => { - const metadata = element.getEvaluatedMetadata(this.notebookEditor.viewModel!.notebookDocument.metadata); - cellEditableKey.set(!!metadata.editable); - }; - - updateForMetadata(); - elementDisposables.add(element.onDidChangeState((e) => { - if (e.metadataChanged) { - updateForMetadata(); - } - })); - - const editModeKey = NOTEBOOK_CELL_MARKDOWN_EDIT_MODE.bindTo(contextKeyService); - editModeKey.set(element.editState === CellEditState.Editing); - elementDisposables.add(element.onDidChangeState((e) => { - if (e.editStateChanged) { - editModeKey.set(element.editState === CellEditState.Editing); - } - })); - - element.totalHeight = height; - - templateData.languageStatusBarItem.update(element, this.notebookEditor); + if (height === undefined) { + return; } + + const elementDisposables = templateData.elementDisposables; + + elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor.viewModel?.notebookDocument!, element)); + + // render toolbar first + this.setupCellToolbarActions(templateData, elementDisposables); + + const toolbarContext = { + cell: element, + notebookEditor: this.notebookEditor, + $mid: 12 + }; + templateData.toolbar.context = toolbarContext; + + this.setBetweenCellToolbarContext(templateData, element, toolbarContext); + + const scopedInstaService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, templateData.contextKeyService])); + const markdownCell = scopedInstaService.createInstance(StatefulMarkdownCell, this.notebookEditor, element, templateData, this.editorOptions.value, this.renderedEditors); + elementDisposables.add(this.editorOptions.onDidChange(newValue => markdownCell.updateEditorOptions(newValue))); + elementDisposables.add(markdownCell); + + templateData.languageStatusBarItem.update(element, this.notebookEditor); } disposeTemplate(templateData: MarkdownCellRenderTemplate): void { templateData.disposables.clear(); } - disposeElement(element: ICellViewModel, index: number, templateData: MarkdownCellRenderTemplate, height: number | undefined): void { - if (height) { - templateData.elementDisposables.clear(); - } + disposeElement(element: ICellViewModel, _index: number, templateData: MarkdownCellRenderTemplate): void { + templateData.elementDisposables.clear(); + element.getCellDecorations().forEach(e => { + if (e.className) { + templateData.container.classList.remove(e.className); + } + }); } } const DRAGGING_CLASS = 'cell-dragging'; -const DRAGOVER_TOP_CLASS = 'cell-dragover-top'; -const DRAGOVER_BOTTOM_CLASS = 'cell-dragover-bottom'; - const GLOBAL_DRAG_CLASS = 'global-drag-active'; type DragImageProvider = () => HTMLElement; +interface CellDragEvent { + browserEvent: DragEvent; + draggedOverCell: ICellViewModel; + cellTop: number; + cellHeight: number; + dragPosRatio: number; +} + export class CellDragAndDropController extends Disposable { // TODO@roblourens - should probably use dataTransfer here, but any dataTransfer set makes the editor think I am dropping a file, need // to figure out how to prevent that private currentDraggedCell: ICellViewModel | undefined; + private listInsertionIndicator: HTMLElement; + + private list!: INotebookCellList; + + private isScrolling = false; + private scrollingDelayer: Delayer; + constructor( - private readonly notebookEditor: INotebookEditor + private readonly notebookEditor: INotebookEditor, + insertionIndicatorContainer: HTMLElement ) { super(); + this.listInsertionIndicator = DOM.append(insertionIndicatorContainer, $('.cell-list-insertion-indicator')); + this._register(domEvent(document.body, DOM.EventType.DRAG_START, true)(this.onGlobalDragStart.bind(this))); this._register(domEvent(document.body, DOM.EventType.DRAG_END, true)(this.onGlobalDragEnd.bind(this))); + + const addCellDragListener = (eventType: string, handler: (e: CellDragEvent) => void) => { + this._register(DOM.addDisposableListener( + notebookEditor.getDomNode(), + eventType, + e => { + const cellDragEvent = this.toCellDragEvent(e); + if (cellDragEvent) { + handler(cellDragEvent); + } + })); + }; + + addCellDragListener(DOM.EventType.DRAG_OVER, event => { + event.browserEvent.preventDefault(); + this.onCellDragover(event); + }); + addCellDragListener(DOM.EventType.DROP, event => { + event.browserEvent.preventDefault(); + this.onCellDrop(event); + }); + addCellDragListener(DOM.EventType.DRAG_LEAVE, event => { + event.browserEvent.preventDefault(); + this.onCellDragLeave(event); + }); + + this.scrollingDelayer = new Delayer(200); + } + + setList(value: INotebookCellList) { + this.list = value; + + this.list.onWillScroll(e => { + if (!e.scrollTopChanged) { + return; + } + + this.setInsertIndicatorVisibility(false); + this.isScrolling = true; + this.scrollingDelayer.trigger(() => { + this.isScrolling = false; + }); + }); + } + + private setInsertIndicatorVisibility(visible: boolean) { + this.listInsertionIndicator.style.opacity = visible ? '1' : '0'; + } + + private toCellDragEvent(event: DragEvent): CellDragEvent | undefined { + const targetTop = this.notebookEditor.getDomNode().getBoundingClientRect().top; + const dragOffset = this.list.scrollTop + event.clientY - targetTop; + const draggedOverCell = this.list.elementAt(dragOffset); + if (!draggedOverCell) { + return undefined; + } + + const cellTop = this.list.getAbsoluteTopOfElement(draggedOverCell); + const cellHeight = this.list.elementHeight(draggedOverCell); + + const dragPosInElement = dragOffset - cellTop; + const dragPosRatio = dragPosInElement / cellHeight; + + return { + browserEvent: event, + draggedOverCell, + cellTop, + cellHeight, + dragPosRatio + }; + } + + clearGlobalDragState() { + this.notebookEditor.getDomNode().classList.remove(GLOBAL_DRAG_CLASS); } private onGlobalDragStart() { @@ -514,21 +605,98 @@ export class CellDragAndDropController extends Disposable { this.notebookEditor.getDomNode().classList.remove(GLOBAL_DRAG_CLASS); } - addListeners(templateData: BaseCellRenderTemplate, dragImageProvider: DragImageProvider): void { - const container = templateData.container; - const dragHandle = templateData.focusIndicator; + private onCellDragover(event: CellDragEvent): void { + if (!event.browserEvent.dataTransfer) { + return; + } - const dragCleanup = () => { - if (this.currentDraggedCell) { - this.currentDraggedCell.dragging = false; - this.currentDraggedCell = undefined; + if (!this.currentDraggedCell) { + event.browserEvent.dataTransfer.dropEffect = 'none'; + return; + } + + if (this.isScrolling || this.currentDraggedCell === event.draggedOverCell) { + this.setInsertIndicatorVisibility(false); + return; + } + + const dropDirection = this.getDropInsertDirection(event); + const insertionIndicatorAbsolutePos = dropDirection === 'above' ? event.cellTop : event.cellTop + event.cellHeight; + const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + BOTTOM_CELL_TOOLBAR_HEIGHT / 2; + if (insertionIndicatorTop >= 0) { + this.listInsertionIndicator.style.top = `${insertionIndicatorTop}px`; + this.setInsertIndicatorVisibility(true); + } else { + this.setInsertIndicatorVisibility(false); + } + } + + private getDropInsertDirection(event: CellDragEvent): 'above' | 'below' { + return event.dragPosRatio < 0.5 ? 'above' : 'below'; + } + + private onCellDrop(event: CellDragEvent): void { + const draggedCell = this.currentDraggedCell!; + + if (this.isScrolling || this.currentDraggedCell === event.draggedOverCell) { + return; + } + + let draggedCells: ICellViewModel[] = [draggedCell]; + + if (draggedCell.cellKind === CellKind.Markdown) { + const currCellIndex = this.notebookEditor.viewModel!.getCellIndex(draggedCell); + const nextVisibleCellIndex = this.notebookEditor.viewModel!.getNextVisibleCellIndex(currCellIndex); + + if (nextVisibleCellIndex > currCellIndex + 1) { + // folding ;) + draggedCells = this.notebookEditor.viewModel!.viewCells.slice(currCellIndex, nextVisibleCellIndex); } - }; + } + + this.dragCleanup(); + + const isCopy = (event.browserEvent.ctrlKey && !platform.isMacintosh) || (event.browserEvent.altKey && platform.isMacintosh); + + const dropDirection = this.getDropInsertDirection(event); + const insertionIndicatorAbsolutePos = dropDirection === 'above' ? event.cellTop : event.cellTop + event.cellHeight; + const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + BOTTOM_CELL_TOOLBAR_HEIGHT / 2; + const editorHeight = this.notebookEditor.getDomNode().getBoundingClientRect().height; + if (insertionIndicatorTop < 0 || insertionIndicatorTop > editorHeight) { + // Ignore drop, insertion point is off-screen + return; + } + + if (isCopy) { + this.copyCells(draggedCells, event.draggedOverCell, dropDirection); + } else { + this.moveCells(draggedCells, event.draggedOverCell, dropDirection); + } + } + + private onCellDragLeave(event: CellDragEvent): void { + if (!event.browserEvent.relatedTarget || !DOM.isAncestor(event.browserEvent.relatedTarget as HTMLElement, this.notebookEditor.getDomNode())) { + this.setInsertIndicatorVisibility(false); + } + } + + private dragCleanup(): void { + if (this.currentDraggedCell) { + this.currentDraggedCell.dragging = false; + this.currentDraggedCell = undefined; + } + + this.setInsertIndicatorVisibility(false); + } + + registerDragHandle(templateData: BaseCellRenderTemplate, dragImageProvider: DragImageProvider): void { + const container = templateData.container; + const dragHandle = templateData.focusIndicatorLeft; templateData.disposables.add(domEvent(dragHandle, DOM.EventType.DRAG_END)(() => { // Note, templateData may have a different element rendered into it by now container.classList.remove(DRAGGING_CLASS); - dragCleanup(); + this.dragCleanup(); })); templateData.disposables.add(domEvent(dragHandle, DOM.EventType.DRAG_START)(event => { @@ -546,84 +714,47 @@ export class CellDragAndDropController extends Disposable { container.classList.add(DRAGGING_CLASS); })); + } - templateData.disposables.add(domEvent(container, DOM.EventType.DRAG_OVER)(event => { - event.preventDefault(); + private async moveCells(draggedCells: ICellViewModel[], ontoCell: ICellViewModel, direction: 'above' | 'below') { + const relativeToIndex = this.notebookEditor!.viewModel!.getCellIndex(ontoCell); + const newIdx = direction === 'above' ? relativeToIndex : relativeToIndex + 1; - const location = this.getDropInsertDirection(templateData, event); - DOM.toggleClass(container, DRAGOVER_TOP_CLASS, location === 'above'); - DOM.toggleClass(container, DRAGOVER_BOTTOM_CLASS, location === 'below'); - })); + this.notebookEditor.textModel!.pushStackElement('Move Cells'); + for (let i = draggedCells.length - 1; i >= 0; i--) { + await this.notebookEditor.moveCellToIdx(draggedCells[i], newIdx); + } - templateData.disposables.add(domEvent(container, DOM.EventType.DROP)(event => { - event.preventDefault(); + this.notebookEditor.textModel!.pushStackElement('Move Cells'); + } - const draggedCell = this.currentDraggedCell!; - dragCleanup(); + private copyCells(draggedCells: ICellViewModel[], ontoCell: ICellViewModel, direction: 'above' | 'below') { + this.notebookEditor.textModel!.pushStackElement('Copy Cells'); + let firstNewCell: ICellViewModel | undefined = undefined; + let firstNewCellState: CellEditState = CellEditState.Preview; + for (let i = 0; i < draggedCells.length; i++) { + const draggedCell = draggedCells[i]; + const newCell = this.notebookEditor.insertNotebookCell(ontoCell, draggedCell.cellKind, direction, draggedCell.getText()); - const isCopy = (event.ctrlKey && !platform.isMacintosh) || (event.altKey && platform.isMacintosh); - - const direction = this.getDropInsertDirection(templateData, event); - if (direction) { - const dropTarget = templateData.currentRenderedCell!; - if (isCopy) { - this.copyCell(draggedCell, dropTarget, direction); - } else { - this.moveCell(draggedCell, dropTarget, direction); - } + if (newCell && !firstNewCell) { + firstNewCell = newCell; + firstNewCellState = draggedCell.editState; } - - container.classList.remove(DRAGOVER_TOP_CLASS, DRAGOVER_BOTTOM_CLASS); - })); - - templateData.disposables.add(domEvent(container, DOM.EventType.DRAG_LEAVE)(event => { - if (!event.relatedTarget || !DOM.isAncestor(event.relatedTarget as HTMLElement, container)) { - container.classList.remove(DRAGOVER_TOP_CLASS, DRAGOVER_BOTTOM_CLASS); - } - })); - } - - private moveCell(draggedCell: ICellViewModel, ontoCell: ICellViewModel, direction: 'above' | 'below') { - const editState = draggedCell.editState; - this.notebookEditor.moveCell(draggedCell, ontoCell, direction); - this.notebookEditor.focusNotebookCell(draggedCell, editState === CellEditState.Editing ? 'editor' : 'container'); - } - - private copyCell(draggedCell: ICellViewModel, ontoCell: ICellViewModel, direction: 'above' | 'below') { - const editState = draggedCell.editState; - const newCell = this.notebookEditor.insertNotebookCell(ontoCell, draggedCell.cellKind, direction, draggedCell.getText()); - if (newCell) { - this.notebookEditor.focusNotebookCell(newCell, editState === CellEditState.Editing ? 'editor' : 'container'); - } - } - - private getDropInsertDirection(templateData: BaseCellRenderTemplate, event: DragEvent): 'above' | 'below' | undefined { - if (templateData.currentRenderedCell === this.currentDraggedCell) { - return; } - const dragOffset = this.getDragOffset(templateData.container, event); - if (dragOffset < 0.3) { - return 'above'; - } else if (dragOffset >= 0.5) { - return 'below'; - } else { - return; + if (firstNewCell) { + this.notebookEditor.focusNotebookCell(firstNewCell, firstNewCellState === CellEditState.Editing ? 'editor' : 'container'); } - } - private getDragOffset(container: HTMLElement, event: DragEvent): number { - const containerRect = container.getBoundingClientRect(); - const dragoverContainerY = event.clientY - containerRect.top; - return dragoverContainerY / containerRect.height; + this.notebookEditor.textModel!.pushStackElement('Copy Cells'); } } export class CellLanguageStatusBarItem extends Disposable { - private labelElement: HTMLElement; + private readonly labelElement: HTMLElement; - private _cell: ICellViewModel | undefined; - private _editor: INotebookEditor | undefined; + private cell: ICellViewModel | undefined; + private editor: INotebookEditor | undefined; private cellDisposables: DisposableStore; @@ -634,11 +765,11 @@ export class CellLanguageStatusBarItem extends Disposable { ) { super(); this.labelElement = DOM.append(container, $('.cell-language-picker')); - this.labelElement.tabIndex = -1; // allows screen readers to read title, but still prevents tab focus. + this.labelElement.tabIndex = 0; this._register(DOM.addDisposableListener(this.labelElement, DOM.EventType.CLICK, () => { this.instantiationService.invokeFunction(accessor => { - new ChangeCellLanguageAction().run(accessor, { notebookEditor: this._editor!, cell: this._cell! }); + new ChangeCellLanguageAction().run(accessor, { notebookEditor: this.editor!, cell: this.cell! }); }); })); this._register(this.cellDisposables = new DisposableStore()); @@ -646,15 +777,16 @@ export class CellLanguageStatusBarItem extends Disposable { update(cell: ICellViewModel, editor: INotebookEditor): void { this.cellDisposables.clear(); - this._cell = cell; - this._editor = editor; + this.cell = cell; + this.editor = editor; this.render(); - this.cellDisposables.add(this._cell.model.onDidChangeLanguage(() => this.render())); + this.cellDisposables.add(this.cell.model.onDidChangeLanguage(() => this.render())); } private render(): void { - this.labelElement.textContent = this.modeService.getLanguageName(this._cell!.language!); + const modeId = this.modeService.getModeIdForLanguageName(this.cell!.language) || this.cell!.language; + this.labelElement.textContent = this.modeService.getLanguageName(modeId) || this.modeService.getLanguageName('plaintext'); } } @@ -666,7 +798,7 @@ class EditorTextRenderer { return null; } - const colorMap = this._getDefaultColorMap(); + const colorMap = this.getDefaultColorMap(); const fontInfo = editor.getOptions().get(EditorOption.fontInfo); const fontFamily = fontInfo.fontFamily === EDITOR_FONT_DEFAULTS.fontFamily ? fontInfo.fontFamily : `'${fontInfo.fontFamily}', ${EDITOR_FONT_DEFAULTS.fontFamily}`; @@ -679,11 +811,11 @@ class EditorTextRenderer { + `line-height: ${fontInfo.lineHeight}px;` + `white-space: pre;` + `">` - + this._getRichTextLines(model, modelRange, colorMap) + + this.getRichTextLines(model, modelRange, colorMap) + '
'; } - private _getRichTextLines(model: ITextModel, modelRange: Range, colorMap: string[]): string { + private getRichTextLines(model: ITextModel, modelRange: Range, colorMap: string[]): string { const startLineNumber = modelRange.startLineNumber; const startColumn = modelRange.startColumn; const endLineNumber = modelRange.endLineNumber; @@ -709,9 +841,9 @@ class EditorTextRenderer { return result; } - private _getDefaultColorMap(): string[] { - let colorMap = modes.TokenizationRegistry.getColorMap(); - let result: string[] = ['#000000']; + private getDefaultColorMap(): string[] { + const colorMap = modes.TokenizationRegistry.getColorMap(); + const result: string[] = ['#000000']; if (colorMap) { for (let i = 1, len = colorMap.length; i < len; i++) { result[i] = Color.Format.CSS.formatHex(colorMap[i]); @@ -723,7 +855,7 @@ class EditorTextRenderer { class CodeCellDragImageRenderer { getDragImage(templateData: BaseCellRenderTemplate, editor: ICodeEditor, type: 'code' | 'markdown'): HTMLElement { - let dragImage = this._getDragImage(templateData, editor, type); + let dragImage = this.getDragImageImpl(templateData, editor, type); if (!dragImage) { // TODO@roblourens I don't think this can happen dragImage = document.createElement('div'); @@ -733,7 +865,7 @@ class CodeCellDragImageRenderer { return dragImage; } - private _getDragImage(templateData: BaseCellRenderTemplate, editor: ICodeEditor, type: 'code' | 'markdown'): HTMLElement | null { + private getDragImageImpl(templateData: BaseCellRenderTemplate, editor: ICodeEditor, type: 'code' | 'markdown'): HTMLElement | null { const dragImageContainer = DOM.$(`.cell-drag-image.monaco-list-row.focused.${type}-cell-row`); dragImageContainer.innerHTML = templateData.container.innerHTML; @@ -742,11 +874,6 @@ class CodeCellDragImageRenderer { return null; } - const focusIndicator = dragImageContainer.querySelector('.notebook-cell-focus-indicator') as HTMLElement; - if (focusIndicator) { - focusIndicator.style.height = '40px'; - } - const richEditorText = new EditorTextRenderer().getRichText(editor, new Range(1, 1, 1, 1000)); if (!richEditorText) { return null; @@ -763,6 +890,7 @@ class CellEditorStatusBar { readonly cellRunStatusContainer: HTMLElement; readonly statusBarContainer: HTMLElement; readonly languageStatusBarItem: CellLanguageStatusBarItem; + readonly durationContainer: HTMLElement; constructor( container: HTMLElement, @@ -772,6 +900,7 @@ class CellEditorStatusBar { const leftStatusBarItems = DOM.append(this.statusBarContainer, $('.cell-status-left')); const rightStatusBarItems = DOM.append(this.statusBarContainer, $('.cell-status-right')); this.cellRunStatusContainer = DOM.append(leftStatusBarItems, $('.cell-run-status')); + this.durationContainer = DOM.append(leftStatusBarItems, $('.cell-run-duration')); this.cellStatusMessageContainer = DOM.append(leftStatusBarItems, $('.cell-status-message')); this.languageStatusBarItem = instantiationService.createInstance(CellLanguageStatusBarItem, rightStatusBarItems); } @@ -784,14 +913,14 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende protected notebookEditor: INotebookEditor, private renderedEditors: Map, dndController: CellDragAndDropController, - @IContextKeyService protected contextKeyService: IContextKeyService, + contextKeyServiceProvider: (container?: HTMLElement) => IContextKeyService, @IContextMenuService contextMenuService: IContextMenuService, @IConfigurationService configurationService: IConfigurationService, @IInstantiationService instantiationService: IInstantiationService, @IKeybindingService keybindingService: IKeybindingService, @INotificationService notificationService: INotificationService, ) { - super(instantiationService, notebookEditor, contextMenuService, configurationService, keybindingService, notificationService, contextKeyService, 'python', dndController); + super(instantiationService, notebookEditor, contextMenuService, configurationService, keybindingService, notificationService, contextKeyServiceProvider, 'python', dndController); } get templateId() { @@ -800,10 +929,12 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende renderTemplate(container: HTMLElement): CodeCellRenderTemplate { container.classList.add('code-cell-row'); - container.tabIndex = 0; const disposables = new DisposableStore(); + const contextKeyService = disposables.add(this.contextKeyServiceProvider(container)); + + DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-top')); const toolbar = disposables.add(this.createToolbar(container)); - const focusIndicator = DOM.append(container, DOM.$('.notebook-cell-focus-indicator')); + const focusIndicator = DOM.append(container, DOM.$('.cell-focus-indicator.cell-focus-indicator-side.cell-focus-indicator-left')); focusIndicator.setAttribute('draggable', 'true'); const cellContainer = DOM.append(container, $('.cell.code')); @@ -814,7 +945,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const executionOrderLabel = DOM.append(runButtonContainer, $('div.execution-count-label')); // create a special context key service that set the inCompositeEditor-contextkey - const editorContextKeyService = this.contextKeyService.createScoped(); + const editorContextKeyService = disposables.add(this.contextKeyServiceProvider(container)); const editorInstaService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, editorContextKeyService])); EditorContextKeys.inCompositeEditor.bindTo(editorContextKeyService).set(true); @@ -830,26 +961,47 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende disposables.add(this.editorOptions.onDidChange(newValue => editor.updateOptions(newValue))); + const { collapsedPart, expandButton } = this.setupCollapsedPart(container); + const progressBar = new ProgressBar(editorPart); progressBar.hide(); disposables.add(progressBar); const statusBar = this.instantiationService.createInstance(CellEditorStatusBar, editorPart); + const timer = new TimerRenderer(statusBar.durationContainer); + const cellRunState = new RunStateRenderer(statusBar.cellRunStatusContainer, runToolbar, this.instantiationService); - DOM.append(container, DOM.$('.cell-insertion-indicator.cell-insertion-indicator-top')); const outputContainer = DOM.append(container, $('.output')); + + const focusIndicatorRight = DOM.append(container, DOM.$('.cell-focus-indicator.cell-focus-indicator-side.cell-focus-indicator-right')); + focusIndicatorRight.setAttribute('draggable', 'true'); + + const focusSinkElement = DOM.append(container, $('.cell-editor-focus-sink')); + focusSinkElement.setAttribute('tabindex', '0'); const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container')); + const focusIndicatorBottom = DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-bottom')); + const betweenCellToolbar = this.createBetweenCellToolbar(bottomCellContainer, disposables, contextKeyService); + + const titleMenu = disposables.add(this.cellMenus.getCellTitleMenu(contextKeyService)); const templateData: CodeCellRenderTemplate = { + editorPart, + collapsedPart, + expandButton, + contextKeyService, container, cellContainer, statusBarContainer: statusBar.statusBarContainer, - cellRunStatusContainer: statusBar.cellRunStatusContainer, + cellRunState, cellStatusMessageContainer: statusBar.cellStatusMessageContainer, languageStatusBarItem: statusBar.languageStatusBarItem, progressBar, - focusIndicator, + focusIndicatorLeft: focusIndicator, + focusIndicatorRight, + focusIndicatorBottom, toolbar, + betweenCellToolbar, + focusSinkElement, runToolbar, runButtonContainer, executionOrderLabel, @@ -858,57 +1010,69 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende disposables, elementDisposables: new DisposableStore(), bottomCellContainer, + timer, + titleMenu, toJSON: () => { return {}; } }; - this.dndController.addListeners(templateData, () => new CodeCellDragImageRenderer().getDragImage(templateData, templateData.editor, 'code')); + this.dndController.registerDragHandle(templateData, () => new CodeCellDragImageRenderer().getDragImage(templateData, templateData.editor, 'code')); + + disposables.add(DOM.addDisposableListener(focusSinkElement, DOM.EventType.FOCUS, () => { + if (templateData.currentRenderedCell && (templateData.currentRenderedCell as CodeCellViewModel).outputs.length) { + this.notebookEditor.focusNotebookCell(templateData.currentRenderedCell, 'output'); + } + })); + + this.commonRenderTemplate(templateData); + return templateData; } - private updateForRunState(element: CodeCellViewModel, templateData: CodeCellRenderTemplate, runStateKey: IContextKey): void { - runStateKey.set(CellRunState[element.runState]); - if (element.runState === CellRunState.Running) { - templateData.progressBar.infinite().show(500); - - templateData.runToolbar.setActions([ - this.instantiationService.createInstance(CancelCellAction) - ])(); + private updateForOutputs(element: CodeCellViewModel, templateData: CodeCellRenderTemplate): void { + if (element.outputs.length) { + DOM.show(templateData.focusSinkElement); } else { - templateData.progressBar.hide(); - - templateData.runToolbar.setActions([ - this.instantiationService.createInstance(ExecuteCellAction) - ])(); + DOM.hide(templateData.focusSinkElement); } } - private updateForMetadata(element: CodeCellViewModel, templateData: CodeCellRenderTemplate, cellEditableKey: IContextKey, cellRunnableKey: IContextKey): void { + private updateForMetadata(element: CodeCellViewModel, templateData: CodeCellRenderTemplate): void { const metadata = element.getEvaluatedMetadata(this.notebookEditor.viewModel!.notebookDocument.metadata); DOM.toggleClass(templateData.cellContainer, 'runnable', !!metadata.runnable); - this.renderExecutionOrder(element, templateData); - cellEditableKey.set(!!metadata.editable); - cellRunnableKey.set(!!metadata.runnable); + this.updateExecutionOrder(metadata, templateData); templateData.cellStatusMessageContainer.textContent = metadata?.statusMessage || ''; - if (metadata.runState === NotebookCellRunState.Success) { - templateData.cellRunStatusContainer.innerHTML = renderCodicons('$(check)'); - } else if (metadata.runState === NotebookCellRunState.Error) { - templateData.cellRunStatusContainer.innerHTML = renderCodicons('$(error)'); - } else if (metadata.runState === NotebookCellRunState.Running) { - // TODO@roblourens should extensions be able to customize the status message while running to show progress? - templateData.cellStatusMessageContainer.textContent = nls.localize('cellRunningStatus', "Running"); - templateData.cellRunStatusContainer.innerHTML = renderCodicons('$(sync~spin)'); + templateData.cellRunState.renderState(element.metadata?.runState); + + if (metadata.runState === NotebookCellRunState.Running) { + if (metadata.runStartTime) { + templateData.elementDisposables.add(templateData.timer.start(metadata.runStartTime)); + } else { + templateData.timer.clear(); + } + } else if (typeof metadata.lastRunDuration === 'number') { + templateData.timer.show(metadata.lastRunDuration); } else { - templateData.cellRunStatusContainer.innerHTML = ''; + templateData.timer.clear(); + } + + if (typeof metadata.breakpointMargin === 'boolean') { + this.editorOptions.setGlyphMargin(metadata.breakpointMargin); + } + + if (metadata.runState === NotebookCellRunState.Running) { + templateData.progressBar.infinite().show(500); + } else { + templateData.progressBar.hide(); } } - private renderExecutionOrder(element: CodeCellViewModel, templateData: CodeCellRenderTemplate): void { - const hasExecutionOrder = this.notebookEditor.viewModel!.notebookDocument.metadata?.hasExecutionOrder; - if (hasExecutionOrder) { - const executionOrdeerLabel = typeof element.metadata?.executionOrder === 'number' ? `[${element.metadata.executionOrder}]` : + private updateExecutionOrder(metadata: NotebookCellMetadata, templateData: CodeCellRenderTemplate): void { + if (metadata.hasExecutionOrder) { + const executionOrderLabel = typeof metadata.executionOrder === 'number' ? + `[${metadata.executionOrder}]` : '[ ]'; - templateData.executionOrderLabel.innerText = executionOrdeerLabel; + templateData.executionOrderLabel.innerText = executionOrderLabel; } else { templateData.executionOrderLabel.innerText = ''; } @@ -918,7 +1082,25 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende DOM.toggleClass(templateData.container, 'cell-output-hover', element.outputIsHovered); } + private updateForLayout(element: CodeCellViewModel, templateData: CodeCellRenderTemplate): void { + templateData.focusIndicatorLeft.style.height = `${element.layoutInfo.indicatorHeight}px`; + templateData.focusIndicatorRight.style.height = `${element.layoutInfo.indicatorHeight}px`; + templateData.focusIndicatorBottom.style.top = `${element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT - CELL_BOTTOM_MARGIN}px`; + templateData.outputContainer.style.top = `${element.layoutInfo.outputContainerOffset}px`; + } + renderElement(element: CodeCellViewModel, index: number, templateData: CodeCellRenderTemplate, height: number | undefined): void { + const removedClassNames: string[] = []; + templateData.container.classList.forEach(className => { + if (/^nb\-.*$/.test(className)) { + removedClassNames.push(className); + } + }); + + removedClassNames.forEach(className => { + templateData.container.classList.remove(className); + }); + this.commonRenderElement(element, index, templateData); templateData.currentRenderedCell = element; @@ -934,39 +1116,19 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende elementDisposables.add(this.instantiationService.createInstance(CodeCell, this.notebookEditor, element, templateData)); this.renderedEditors.set(element, templateData.editor); - templateData.focusIndicator.style.height = `${element.layoutInfo.indicatorHeight}px`; + elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor.viewModel?.notebookDocument!, element)); + + this.updateForLayout(element, templateData); elementDisposables.add(element.onDidChangeLayout(() => { - templateData.focusIndicator.style.height = `${element.layoutInfo.indicatorHeight}px`; + this.updateForLayout(element, templateData); })); - const contextKeyService = this.contextKeyService.createScoped(templateData.container); - - const runStateKey = NOTEBOOK_CELL_RUN_STATE.bindTo(contextKeyService); - runStateKey.set(CellRunState[element.runState]); - this.updateForRunState(element, templateData, runStateKey); - elementDisposables.add(element.onDidChangeState((e) => { - if (e.runStateChanged) { - this.updateForRunState(element, templateData, runStateKey); - } - })); - - const cellHasOutputsContext = NOTEBOOK_CELL_HAS_OUTPUTS.bindTo(contextKeyService); - cellHasOutputsContext.set(element.outputs.length > 0); - elementDisposables.add(element.onDidChangeOutputs(() => { - cellHasOutputsContext.set(element.outputs.length > 0); - })); - - NOTEBOOK_CELL_TYPE.bindTo(contextKeyService).set('code'); - NOTEBOOK_VIEW_TYPE.bindTo(contextKeyService).set(element.viewType); - const metadata = element.getEvaluatedMetadata(this.notebookEditor.viewModel!.notebookDocument.metadata); - const cellEditableKey = NOTEBOOK_CELL_EDITABLE.bindTo(contextKeyService); - cellEditableKey.set(!!metadata.editable); - const cellRunnableKey = NOTEBOOK_CELL_RUNNABLE.bindTo(contextKeyService); - cellRunnableKey.set(!!metadata.runnable); - this.updateForMetadata(element, templateData, cellEditableKey, cellRunnableKey); + templateData.cellRunState.clear(); + this.updateForMetadata(element, templateData); + this.updateForHover(element, templateData); elementDisposables.add(element.onDidChangeState((e) => { if (e.metadataChanged) { - this.updateForMetadata(element, templateData, cellEditableKey, cellRunnableKey); + this.updateForMetadata(element, templateData); } if (e.outputIsHoveredChanged) { @@ -974,7 +1136,10 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende } })); - this.setupCellToolbarActions(contextKeyService, templateData, elementDisposables); + this.updateForOutputs(element, templateData); + elementDisposables.add(element.onDidChangeOutputs(_e => this.updateForOutputs(element, templateData))); + + this.setupCellToolbarActions(templateData, elementDisposables); const toolbarContext = { cell: element, @@ -985,7 +1150,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende templateData.toolbar.context = toolbarContext; templateData.runToolbar.context = toolbarContext; - this.setupBetweenCellToolbarActions(element, templateData, elementDisposables, toolbarContext); + this.setBetweenCellToolbarContext(templateData, element, toolbarContext); templateData.languageStatusBarItem.update(element, this.notebookEditor); } @@ -997,6 +1162,101 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende disposeElement(element: ICellViewModel, index: number, templateData: CodeCellRenderTemplate, height: number | undefined): void { templateData.elementDisposables.clear(); this.renderedEditors.delete(element); - templateData.focusIndicator.style.height = 'initial'; + } +} + +export class TimerRenderer { + constructor(private readonly container: HTMLElement) { + DOM.hide(container); + } + + private intervalTimer: number | undefined; + + start(startTime: number): IDisposable { + this.stop(); + + DOM.show(this.container); + const intervalTimer = setInterval(() => { + const duration = Date.now() - startTime; + this.container.textContent = this.formatDuration(duration); + }, 100); + this.intervalTimer = intervalTimer as unknown as number | undefined; + + return toDisposable(() => { + clearInterval(intervalTimer); + }); + } + + stop() { + if (this.intervalTimer) { + clearInterval(this.intervalTimer); + } + } + + show(duration: number) { + this.stop(); + + DOM.show(this.container); + this.container.textContent = this.formatDuration(duration); + } + + clear() { + DOM.hide(this.container); + this.stop(); + this.container.textContent = ''; + } + + private formatDuration(duration: number) { + const seconds = Math.floor(duration / 1000); + const tenths = String(duration - seconds * 1000).charAt(0); + + return `${seconds}.${tenths}s`; + } +} + +export class RunStateRenderer { + private static readonly MIN_SPINNER_TIME = 200; + + private spinnerTimer: any | undefined; + private pendingNewState: NotebookCellRunState | undefined; + + constructor(private readonly element: HTMLElement, private readonly runToolbar: ToolBar, private readonly instantiationService: IInstantiationService) { + } + + clear() { + if (this.spinnerTimer) { + clearTimeout(this.spinnerTimer); + } + } + + renderState(runState: NotebookCellRunState = NotebookCellRunState.Idle) { + if (this.spinnerTimer) { + this.pendingNewState = runState; + return; + } + + if (runState === NotebookCellRunState.Running) { + this.runToolbar.setActions([this.instantiationService.createInstance(CancelCellAction)]); + } else { + this.runToolbar.setActions([this.instantiationService.createInstance(ExecuteCellAction)]); + } + + if (runState === NotebookCellRunState.Success) { + this.element.innerHTML = renderCodicons('$(check)'); + } else if (runState === NotebookCellRunState.Error) { + this.element.innerHTML = renderCodicons('$(error)'); + } else if (runState === NotebookCellRunState.Running) { + this.element.innerHTML = renderCodicons('$(sync~spin)'); + + this.spinnerTimer = setTimeout(() => { + this.spinnerTimer = undefined; + if (this.pendingNewState) { + this.renderState(this.pendingNewState); + this.pendingNewState = undefined; + } + }, RunStateRenderer.MIN_SPINNER_TIME); + } else { + this.element.innerHTML = ''; + } } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index 339a1717ea2..c541cb9b136 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -15,7 +15,7 @@ import { CellFocusMode, CodeCellRenderTemplate, INotebookEditor } from 'vs/workb import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; -import { CellOutputKind, IOutput, IRenderOutput, ITransformedDisplayOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellOutputKind, IProcessedOutput, IRenderOutput, ITransformedDisplayOutputDto, BUILTIN_RENDERER_ID } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IDimension } from 'vs/editor/common/editorCommon'; @@ -24,9 +24,14 @@ interface IMimeTypeRenderer extends IQuickPickItem { index: number; } +interface IRenderedOutput { + element: HTMLElement; + renderResult: IRenderOutput; +} + export class CodeCell extends Disposable { - private outputResizeListeners = new Map(); - private outputElements = new Map(); + private outputResizeListeners = new Map(); + private outputElements = new Map(); constructor( private notebookEditor: INotebookEditor, private viewCell: CodeCellViewModel, @@ -40,14 +45,14 @@ export class CodeCell extends Disposable { const width = this.viewCell.layoutInfo.editorWidth; const lineNum = this.viewCell.lineCount; const lineHeight = this.viewCell.layoutInfo.fontInfo?.lineHeight || 17; - const totalHeight = this.viewCell.layoutInfo.editorHeight === 0 + const editorHeight = this.viewCell.layoutInfo.editorHeight === 0 ? lineNum * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING : this.viewCell.layoutInfo.editorHeight; this.layoutEditor( { width: width, - height: totalHeight + height: editorHeight } ); @@ -57,16 +62,16 @@ export class CodeCell extends Disposable { if (model && templateData.editor) { templateData.editor.setModel(model); viewCell.attachTextEditor(templateData.editor); - if (notebookEditor.getActiveCell() === viewCell && viewCell.focusMode === CellFocusMode.Editor) { + if (notebookEditor.getActiveCell() === viewCell && viewCell.focusMode === CellFocusMode.Editor && this.notebookEditor.hasFocus()) { templateData.editor?.focus(); } const realContentHeight = templateData.editor?.getContentHeight(); - if (realContentHeight !== undefined && realContentHeight !== totalHeight) { + if (realContentHeight !== undefined && realContentHeight !== editorHeight) { this.onCellHeightChange(realContentHeight); } - if (this.notebookEditor.getActiveCell() === this.viewCell && viewCell.focusMode === CellFocusMode.Editor) { + if (this.notebookEditor.getActiveCell() === this.viewCell && viewCell.focusMode === CellFocusMode.Editor && this.notebookEditor.hasFocus()) { templateData.editor?.focus(); } } @@ -79,39 +84,41 @@ export class CodeCell extends Disposable { DOM.toggleClass(templateData.container, 'cell-editor-focus', viewCell.focusMode === CellFocusMode.Editor); }; + const updateForCollapseState = () => { + this.viewUpdate(); + }; this._register(viewCell.onDidChangeState((e) => { - if (!e.focusModeChanged) { - return; + if (e.focusModeChanged) { + updateForFocusMode(); } - - updateForFocusMode(); })); updateForFocusMode(); - templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel?.metadata).editable) }); + templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel!.metadata).editable) }); this._register(viewCell.onDidChangeState((e) => { if (e.metadataChanged) { - templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel?.metadata).editable) }); + templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel!.metadata).editable) }); + + // TODO@rob this isn't nice + this.viewCell.layoutChange({}); + updateForCollapseState(); + this.relayoutCell(); } })); this._register(viewCell.onDidChangeState((e) => { - if (!e.languageChanged) { - return; + if (e.languageChanged) { + const mode = this._modeService.create(viewCell.language); + templateData.editor?.getModel()?.setMode(mode.languageIdentifier); } - - const mode = this._modeService.create(viewCell.language); - templateData.editor?.getModel()?.setMode(mode.languageIdentifier); })); this._register(viewCell.onDidChangeLayout((e) => { - if (e.outerWidth === undefined) { - return; - } - - const layoutInfo = templateData.editor!.getLayoutInfo(); - if (layoutInfo.width !== viewCell.layoutInfo.editorWidth) { - this.onCellWidthChange(); + if (e.outerWidth !== undefined) { + const layoutInfo = templateData.editor!.getLayoutInfo(); + if (layoutInfo.width !== viewCell.layoutInfo.editorWidth) { + this.onCellWidthChange(); + } } })); @@ -132,7 +139,7 @@ export class CodeCell extends Disposable { const primarySelection = templateData.editor!.getSelection(); if (primarySelection) { - this.notebookEditor.revealLineInView(viewCell, primarySelection!.positionLineNumber); + this.notebookEditor.revealLineInViewAsync(viewCell, primarySelection!.positionLineNumber); } })); @@ -149,20 +156,20 @@ export class CodeCell extends Disposable { this.templateData.outputContainer!.style.display = 'none'; } - let reversedSplices = splices.reverse(); + const reversedSplices = splices.reverse(); reversedSplices.forEach(splice => { viewCell.spliceOutputHeights(splice[0], splice[1], splice[2].map(_ => 0)); }); - let removedKeys: IOutput[] = []; + const removedKeys: IProcessedOutput[] = []; this.outputElements.forEach((value, key) => { if (viewCell.outputs.indexOf(key) < 0) { // already removed removedKeys.push(key); // remove element from DOM - this.templateData?.outputContainer?.removeChild(value); + this.templateData?.outputContainer?.removeChild(value.element); this.notebookEditor.removeInset(key); } }); @@ -176,20 +183,20 @@ export class CodeCell extends Disposable { let prevElement: HTMLElement | undefined = undefined; - this.viewCell.outputs.reverse().forEach(output => { + [...this.viewCell.outputs].reverse().forEach(output => { if (this.outputElements.has(output)) { // already exist - prevElement = this.outputElements.get(output); + prevElement = this.outputElements.get(output)!.element; return; } // newly added element - let currIndex = this.viewCell.outputs.indexOf(output); + const currIndex = this.viewCell.outputs.indexOf(output); this.renderOutput(output, currIndex, prevElement); - prevElement = this.outputElements.get(output); + prevElement = this.outputElements.get(output)!.element; }); - let editorHeight = templateData.editor!.getContentHeight(); + const editorHeight = templateData.editor!.getContentHeight(); viewCell.editorHeight = editorHeight; if (previousOutputHeight === 0 || this.viewCell.outputs.length === 0) { @@ -200,6 +207,58 @@ export class CodeCell extends Disposable { } })); + this._register(viewCell.onDidChangeLayout(() => { + this.outputElements.forEach((value, key) => { + const index = viewCell.outputs.indexOf(key); + if (index >= 0) { + const top = this.viewCell.getOutputOffsetInContainer(index); + value.element.style.top = `${top}px`; + } + }); + + })); + + this._register(viewCell.onCellDecorationsChanged((e) => { + e.added.forEach(options => { + if (options.className) { + DOM.addClass(templateData.container, options.className); + } + + if (options.outputClassName) { + this.notebookEditor.deltaCellOutputContainerClassNames(this.viewCell.id, [options.outputClassName], []); + } + }); + + e.removed.forEach(options => { + if (options.className) { + DOM.removeClass(templateData.container, options.className); + } + + if (options.outputClassName) { + this.notebookEditor.deltaCellOutputContainerClassNames(this.viewCell.id, [], [options.outputClassName]); + } + }); + })); + // apply decorations + + viewCell.getCellDecorations().forEach(options => { + if (options.className) { + DOM.addClass(templateData.container, options.className); + } + + if (options.outputClassName) { + this.notebookEditor.deltaCellOutputContainerClassNames(this.viewCell.id, [options.outputClassName], []); + } + }); + + this._register(templateData.editor!.onMouseDown(e => { + // prevent default on right mouse click, otherwise it will trigger unexpected focus changes + // the catch is, it means we don't allow customization of right button mouse down handlers other than the built in ones. + if (e.event.rightButton) { + e.event.preventDefault(); + } + })); + const updateFocusMode = () => viewCell.focusMode = templateData.editor!.hasWidgetFocus() ? CellFocusMode.Editor : CellFocusMode.Container; this._register(templateData.editor!.onDidFocusEditorWidget(() => { updateFocusMode(); @@ -213,7 +272,7 @@ export class CodeCell extends Disposable { if (viewCell.outputs.length > 0) { let layoutCache = false; - if (this.viewCell.layoutInfo.totalHeight !== 0 && this.viewCell.layoutInfo.totalHeight > totalHeight) { + if (this.viewCell.layoutInfo.totalHeight !== 0 && this.viewCell.layoutInfo.editorHeight > editorHeight) { layoutCache = true; this.relayoutCell(); } @@ -228,7 +287,7 @@ export class CodeCell extends Disposable { this.renderOutput(currOutput, index, undefined); } - viewCell.editorHeight = totalHeight; + viewCell.editorHeight = editorHeight; if (layoutCache) { this.relayoutCellDebounced(); } else { @@ -236,10 +295,100 @@ export class CodeCell extends Disposable { } } else { // noop - viewCell.editorHeight = totalHeight; + viewCell.editorHeight = editorHeight; this.relayoutCell(); this.templateData.outputContainer!.style.display = 'none'; } + + // Need to do this after the intial renderOutput + updateForCollapseState(); + } + + private viewUpdate(): void { + if (this.viewCell.metadata?.inputCollapsed && this.viewCell.metadata.outputCollapsed) { + this.viewUpdateAllCollapsed(); + } else if (this.viewCell.metadata?.inputCollapsed) { + this.viewUpdateInputCollapsed(); + } else if (this.viewCell.metadata?.outputCollapsed && this.viewCell.outputs.length) { + this.viewUpdateOutputCollapsed(); + } else { + this.viewUpdateExpanded(); + } + } + + private viewUpdateShowOutputs(): void { + for (let index = 0; index < this.viewCell.outputs.length; index++) { + const currOutput = this.viewCell.outputs[index]; + + const renderedOutput = this.outputElements.get(currOutput); + if (renderedOutput) { + if (renderedOutput.renderResult.shadowContent) { + // Show inset in webview, or render output that isn't rendered + this.renderOutput(currOutput, index, undefined); + } else { + // Anything else, just update the height + this.viewCell.updateOutputHeight(index, renderedOutput.element.clientHeight); + } + } + } + + this.relayoutCell(); + } + + private viewUpdateInputCollapsed(): void { + DOM.hide(this.templateData.cellContainer); + DOM.show(this.templateData.collapsedPart); + DOM.show(this.templateData.outputContainer); + this.templateData.container.classList.toggle('collapsed', true); + + this.viewUpdateShowOutputs(); + + this.relayoutCell(); + } + + private viewUpdateHideOuputs(): void { + for (const e of this.outputElements.keys()) { + this.notebookEditor.hideInset(e); + } + } + + private viewUpdateOutputCollapsed(): void { + DOM.show(this.templateData.cellContainer); + DOM.show(this.templateData.collapsedPart); + DOM.hide(this.templateData.outputContainer); + + this.viewUpdateHideOuputs(); + + this.templateData.container.classList.toggle('collapsed', false); + this.templateData.container.classList.toggle('output-collapsed', true); + + this.relayoutCell(); + } + + private viewUpdateAllCollapsed(): void { + DOM.hide(this.templateData.cellContainer); + DOM.show(this.templateData.collapsedPart); + DOM.hide(this.templateData.outputContainer); + this.templateData.container.classList.toggle('collapsed', true); + this.templateData.container.classList.toggle('output-collapsed', true); + + for (const e of this.outputElements.keys()) { + this.notebookEditor.hideInset(e); + } + + this.relayoutCell(); + } + + private viewUpdateExpanded(): void { + DOM.show(this.templateData.cellContainer); + DOM.hide(this.templateData.collapsedPart); + DOM.show(this.templateData.outputContainer); + this.templateData.container.classList.toggle('collapsed', false); + this.templateData.container.classList.toggle('output-collapsed', false); + + this.viewUpdateShowOutputs(); + + this.relayoutCell(); } private layoutEditor(dimension: IDimension): void { @@ -249,6 +398,9 @@ export class CodeCell extends Disposable { private onCellWidthChange(): void { const realContentHeight = this.templateData.editor!.getContentHeight(); + this.viewCell.editorHeight = realContentHeight; + this.relayoutCell(); + this.layoutEditor( { width: this.viewCell.layoutInfo.editorWidth, @@ -256,40 +408,43 @@ export class CodeCell extends Disposable { } ); - this.viewCell.editorHeight = realContentHeight; - this.relayoutCell(); + this.viewCell.outputs.forEach((o, i) => { + const renderedOutput = this.outputElements.get(o); + if (renderedOutput && !renderedOutput.renderResult.hasDynamicHeight && !renderedOutput.renderResult.shadowContent) { + this.viewCell.updateOutputHeight(i, renderedOutput.element.clientHeight); + } + }); } private onCellHeightChange(newHeight: number): void { const viewLayout = this.templateData.editor!.getLayoutInfo(); + this.viewCell.editorHeight = newHeight; + this.relayoutCell(); this.layoutEditor( { width: viewLayout.width, height: newHeight } ); - - this.viewCell.editorHeight = newHeight; - this.relayoutCell(); } - renderOutput(currOutput: IOutput, index: number, beforeElement?: HTMLElement) { + private renderOutput(currOutput: IProcessedOutput, index: number, beforeElement?: HTMLElement) { if (!this.outputResizeListeners.has(currOutput)) { this.outputResizeListeners.set(currOutput, new DisposableStore()); } - let outputItemDiv = document.createElement('div'); + const outputItemDiv = document.createElement('div'); let result: IRenderOutput | undefined = undefined; if (currOutput.outputKind === CellOutputKind.Rich) { - let transformedDisplayOutput = currOutput as ITransformedDisplayOutputDto; + const transformedDisplayOutput = currOutput as ITransformedDisplayOutputDto; - if (transformedDisplayOutput.orderedMimeTypes.length > 1) { + if (transformedDisplayOutput.orderedMimeTypes!.length > 1) { outputItemDiv.style.position = 'relative'; const mimeTypePicker = DOM.$('.multi-mimetype-output'); DOM.addClasses(mimeTypePicker, 'codicon', 'codicon-code'); mimeTypePicker.tabIndex = 0; - mimeTypePicker.title = nls.localize('mimeTypePicker', "Choose a different output mimetype, available mimetypes: {0}", transformedDisplayOutput.orderedMimeTypes.map(mimeType => mimeType.mimeType).join(', ')); + mimeTypePicker.title = nls.localize('mimeTypePicker', "Choose a different output mimetype, available mimetypes: {0}", transformedDisplayOutput.orderedMimeTypes!.map(mimeType => mimeType.mimeType).join(', ')); outputItemDiv.appendChild(mimeTypePicker); this.outputResizeListeners.get(currOutput)!.add(DOM.addStandardDisposableListener(mimeTypePicker, 'mousedown', async e => { if (e.leftButton) { @@ -309,17 +464,23 @@ export class CodeCell extends Disposable { }))); } - let pickedMimeTypeRenderer = currOutput.orderedMimeTypes[currOutput.pickedMimeTypeIndex]; + const pickedMimeTypeRenderer = currOutput.orderedMimeTypes![currOutput.pickedMimeTypeIndex!]; + + const innerContainer = DOM.$('.output-inner-container'); + DOM.append(outputItemDiv, innerContainer); if (pickedMimeTypeRenderer.isResolved) { // html - result = this.notebookEditor.getOutputRenderer().render({ outputKind: CellOutputKind.Rich, data: { 'text/html': pickedMimeTypeRenderer.output! } } as any, outputItemDiv, 'text/html'); + result = this.notebookEditor.getOutputRenderer().render({ outputId: currOutput.outputId, outputKind: CellOutputKind.Rich, data: { 'text/html': pickedMimeTypeRenderer.output! } }, innerContainer, 'text/html'); } else { - result = this.notebookEditor.getOutputRenderer().render(currOutput, outputItemDiv, pickedMimeTypeRenderer.mimeType); + result = this.notebookEditor.getOutputRenderer().render(currOutput, innerContainer, pickedMimeTypeRenderer.mimeType); } } else { // for text and error, there is no mimetype - result = this.notebookEditor.getOutputRenderer().render(currOutput, outputItemDiv, undefined); + const innerContainer = DOM.$('.output-inner-container'); + DOM.append(outputItemDiv, innerContainer); + + result = this.notebookEditor.getOutputRenderer().render(currOutput, innerContainer, undefined); } if (!result) { @@ -327,7 +488,7 @@ export class CodeCell extends Disposable { return; } - this.outputElements.set(currOutput, outputItemDiv); + this.outputElements.set(currOutput, { element: outputItemDiv, renderResult: result }); if (beforeElement) { this.templateData.outputContainer?.insertBefore(outputItemDiv, beforeElement); @@ -340,22 +501,25 @@ export class CodeCell extends Disposable { this.notebookEditor.createInset(this.viewCell, currOutput, result.shadowContent, this.viewCell.getOutputOffset(index)); } else { DOM.addClass(outputItemDiv, 'foreground'); + DOM.addClass(outputItemDiv, 'output-element'); + outputItemDiv.style.position = 'absolute'; } - let hasDynamicHeight = result.hasDynamicHeight; + const hasDynamicHeight = result.hasDynamicHeight; if (hasDynamicHeight) { - let clientHeight = outputItemDiv.clientHeight; - let dimension = { + this.viewCell.selfSizeMonitoring = true; + + const clientHeight = outputItemDiv.clientHeight; + const dimension = { width: this.viewCell.layoutInfo.editorWidth, height: clientHeight }; const elementSizeObserver = getResizesObserver(outputItemDiv, dimension, () => { if (this.templateData.outputContainer && document.body.contains(this.templateData.outputContainer!)) { - let height = elementSizeObserver.getHeight() + 8 * 2; // include padding + const height = Math.ceil(elementSizeObserver.getHeight()); if (clientHeight === height) { - // console.log(this.viewCell.outputs); return; } @@ -375,41 +539,40 @@ export class CodeCell extends Disposable { if (result.shadowContent) { // webview // noop - // let cachedHeight = this.viewCell.getOutputHeight(currOutput); } else { // static output - - // @TODO@rebornix, if we stop checking output height, we need to evaluate it later when checking the height of output container - let clientHeight = outputItemDiv.clientHeight; + const clientHeight = Math.ceil(outputItemDiv.clientHeight); this.viewCell.updateOutputHeight(index, clientHeight); + + const top = this.viewCell.getOutputOffsetInContainer(index); + outputItemDiv.style.top = `${top}px`; } } } - generateRendererInfo(renderId: number | undefined): string { - if (renderId === undefined || renderId === -1) { + generateRendererInfo(renderId: string | undefined): string { + if (renderId === undefined || renderId === BUILTIN_RENDERER_ID) { return nls.localize('builtinRenderInfo', "built-in"); } - let renderInfo = this.notebookService.getRendererInfo(renderId); + const renderInfo = this.notebookService.getRendererInfo(renderId); if (renderInfo) { - return renderInfo.id.value; + return `${renderId} (${renderInfo.extensionId.value})`; } return nls.localize('builtinRenderInfo', "built-in"); } async pickActiveMimeTypeRenderer(output: ITransformedDisplayOutputDto) { - let currIndex = output.pickedMimeTypeIndex; - const items = output.orderedMimeTypes.map((mimeType, index): IMimeTypeRenderer => ({ + const currIndex = output.pickedMimeTypeIndex; + const items = output.orderedMimeTypes!.map((mimeType, index): IMimeTypeRenderer => ({ label: mimeType.mimeType, id: mimeType.mimeType, index: index, picked: index === currIndex, - description: this.generateRendererInfo(mimeType.rendererId) + (index === currIndex - ? nls.localize('curruentActiveMimeType', " (Currently Active)") - : ''), + detail: this.generateRendererInfo(mimeType.rendererId), + description: index === currIndex ? nls.localize('curruentActiveMimeType', "Currently Active") : undefined })); const picker = this.quickInputService.createQuickPick(); @@ -431,10 +594,10 @@ export class CodeCell extends Disposable { if (pick !== currIndex) { // user chooses another mimetype - let index = this.viewCell.outputs.indexOf(output); - let nextElement = index + 1 < this.viewCell.outputs.length ? this.outputElements.get(this.viewCell.outputs[index + 1]) : undefined; + const index = this.viewCell.outputs.indexOf(output); + const nextElement = index + 1 < this.viewCell.outputs.length ? this.outputElements.get(this.viewCell.outputs[index + 1])?.element : undefined; this.outputResizeListeners.get(output)?.clear(); - let element = this.outputElements.get(output); + const element = this.outputElements.get(output)?.element; if (element) { this.templateData?.outputContainer?.removeChild(element); this.notebookEditor.removeInset(output); @@ -442,6 +605,16 @@ export class CodeCell extends Disposable { output.pickedMimeTypeIndex = pick; + if (!output.orderedMimeTypes![pick].isResolved && output.orderedMimeTypes![pick].rendererId !== BUILTIN_RENDERER_ID) { + // since it's not build in renderer and not resolved yet + // let's see if we can activate the extension and then render + // await this.notebookService.transformSpliceOutputs(this.notebookEditor.textModel!, [[0, 0, output]]) + const outputRet = await this.notebookService.transformSingleOutput(this.notebookEditor.textModel!, output, output.orderedMimeTypes![pick].rendererId!, output.orderedMimeTypes![pick].mimeType); + if (outputRet) { + output.orderedMimeTypes![pick] = outputRet; + } + } + this.renderOutput(output, index, nextElement); this.relayoutCell(); } @@ -455,14 +628,17 @@ export class CodeCell extends Disposable { this.notebookEditor.layoutNotebookCell(this.viewCell, this.viewCell.layoutInfo.totalHeight); } - private _timer: any = null; + private _timer: number | null = null; relayoutCellDebounced() { - clearTimeout(this._timer); + if (this._timer !== null) { + clearTimeout(this._timer); + } + this._timer = setTimeout(() => { this.notebookEditor.layoutNotebookCell(this.viewCell, this.viewCell.layoutInfo.totalHeight); this._timer = null; - }, 200); + }, 200) as unknown as number | null; } dispose() { @@ -471,7 +647,7 @@ export class CodeCell extends Disposable { value.dispose(); }); - this.templateData.focusIndicator!.style.height = 'initial'; + this.templateData.focusIndicatorLeft!.style.height = 'initial'; super.dispose(); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts index 8d276c0a999..c3aed546e16 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts @@ -3,197 +3,74 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { hide, IDimension, show, toggleClass } from 'vs/base/browser/dom'; +import * as DOM from 'vs/base/browser/dom'; import { raceCancellation } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { renderCodicons } from 'vs/base/common/codicons'; import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { ITextModel } from 'vs/editor/common/model'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING, CELL_STATUSBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; +import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; import { CellEditState, CellFocusMode, INotebookEditor, MarkdownCellRenderTemplate, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver'; import { CellFoldingState } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver'; -export class StatefullMarkdownCell extends Disposable { +export class StatefulMarkdownCell extends Disposable { private editor: CodeEditorWidget | null = null; - private editorOptions: IEditorOptions; private markdownContainer: HTMLElement; private editorPart: HTMLElement; - private localDisposables: DisposableStore; + private localDisposables = new DisposableStore(); private foldingState: CellFoldingState; constructor( - private notebookEditor: INotebookEditor, - private viewCell: MarkdownCellViewModel, - private templateData: MarkdownCellRenderTemplate, - editorOptions: IEditorOptions, - renderedEditors: Map, - @IContextKeyService contextKeyService: IContextKeyService, - @IInstantiationService instantiationService: IInstantiationService, + private readonly notebookEditor: INotebookEditor, + private readonly viewCell: MarkdownCellViewModel, + private readonly templateData: MarkdownCellRenderTemplate, + private editorOptions: IEditorOptions, + private readonly renderedEditors: Map, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IInstantiationService private readonly instantiationService: IInstantiationService, ) { super(); this.markdownContainer = templateData.cellContainer; this.editorPart = templateData.editorPart; - this.editorOptions = editorOptions; - this.localDisposables = new DisposableStore(); this._register(this.localDisposables); this._register(toDisposable(() => renderedEditors.delete(this.viewCell))); - const viewUpdate = () => { - if (viewCell.editState === CellEditState.Editing) { - // switch to editing mode - let editorHeight: number; - - show(this.editorPart); - hide(this.markdownContainer); - if (this.editor) { - editorHeight = this.editor!.getContentHeight(); - - // not first time, we don't need to create editor or bind listeners - viewCell.attachTextEditor(this.editor); - if (notebookEditor.getActiveCell() === viewCell) { - this.editor.focus(); - } - - this.bindEditorListeners(this.editor.getModel()!); - } else { - const width = viewCell.layoutInfo.editorWidth; - const lineNum = viewCell.lineCount; - const lineHeight = viewCell.layoutInfo.fontInfo?.lineHeight || 17; - editorHeight = Math.max(lineNum, 1) * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; - - this.templateData.editorContainer.innerHTML = ''; - - // create a special context key service that set the inCompositeEditor-contextkey - const editorContextKeyService = contextKeyService.createScoped(); - const editorInstaService = instantiationService.createChild(new ServiceCollection([IContextKeyService, editorContextKeyService])); - EditorContextKeys.inCompositeEditor.bindTo(editorContextKeyService).set(true); - - this.editor = editorInstaService.createInstance(CodeEditorWidget, this.templateData.editorContainer, { - ...this.editorOptions, - dimension: { - width: width, - height: editorHeight - } - }, {}); - templateData.currentEditor = this.editor; - - const cts = new CancellationTokenSource(); - this._register({ dispose() { cts.dispose(true); } }); - raceCancellation(viewCell.resolveTextModel(), cts.token).then(model => { - if (!model) { - return; - } - - this.editor!.setModel(model); - if (notebookEditor.getActiveCell() === viewCell) { - this.editor!.focus(); - } - - const realContentHeight = this.editor!.getContentHeight(); - if (realContentHeight !== editorHeight) { - this.editor!.layout( - { - width: width, - height: realContentHeight - } - ); - editorHeight = realContentHeight; - } - - viewCell.attachTextEditor(this.editor!); - - if (viewCell.editState === CellEditState.Editing) { - this.editor!.focus(); - } - - this.bindEditorListeners(model, { - width: width, - height: editorHeight - }); - - - const clientHeight = this.markdownContainer.clientHeight; - const totalHeight = editorHeight + 32 + clientHeight + CELL_STATUSBAR_HEIGHT; - this.viewCell.totalHeight = totalHeight; - notebookEditor.layoutNotebookCell(viewCell, totalHeight); - }); - } - - const clientHeight = this.markdownContainer.clientHeight; - const totalHeight = editorHeight + 32 + clientHeight + CELL_STATUSBAR_HEIGHT; - this.viewCell.totalHeight = totalHeight; - notebookEditor.layoutNotebookCell(viewCell, totalHeight); - this.editor.focus(); - renderedEditors.set(this.viewCell, this.editor!); - } else { - this.viewCell.detachTextEditor(); - hide(this.editorPart); - show(this.markdownContainer); - renderedEditors.delete(this.viewCell); - if (this.editor) { - // switch from editing mode - const clientHeight = templateData.container.clientHeight; - this.viewCell.totalHeight = clientHeight; - notebookEditor.layoutNotebookCell(viewCell, clientHeight); - } else { - // first time, readonly mode - this.markdownContainer.innerHTML = ''; - let markdownRenderer = viewCell.getMarkdownRenderer(); - let renderedHTML = viewCell.getHTML(); - if (renderedHTML) { - this.markdownContainer.appendChild(renderedHTML); - } - - this.localDisposables.add(markdownRenderer.onDidUpdateRender(() => { - const clientHeight = templateData.container.clientHeight; - this.viewCell.totalHeight = clientHeight; - notebookEditor.layoutNotebookCell(viewCell, clientHeight); - })); - - this.localDisposables.add(viewCell.onDidChangeState((e) => { - if (!e.contentChanged) { - return; - } - - this.markdownContainer.innerHTML = ''; - let renderedHTML = viewCell.getHTML(); - if (renderedHTML) { - this.markdownContainer.appendChild(renderedHTML); - } - })); - - const clientHeight = templateData.container.clientHeight; - this.viewCell.totalHeight = clientHeight; - notebookEditor.layoutNotebookCell(viewCell, clientHeight); - } - } - }; - this._register(viewCell.onDidChangeState((e) => { if (e.editStateChanged) { this.localDisposables.clear(); - viewUpdate(); + this.viewUpdate(); + } else if (e.contentChanged) { + this.viewUpdate(); } })); + this._register(viewCell.model.onDidChangeMetadata(() => { + this.viewUpdate(); + })); + + this._register(getResizesObserver(this.markdownContainer, undefined, () => { + if (viewCell.editState === CellEditState.Preview) { + this.viewCell.renderedMarkdownHeight = templateData.container.clientHeight; + } + })).startObserving(); + const updateForFocusMode = () => { if (viewCell.focusMode === CellFocusMode.Editor) { - this.editor?.focus(); + this.focusEditorIfNeeded(); } - toggleClass(templateData.container, 'cell-editor-focus', viewCell.focusMode === CellFocusMode.Editor); + templateData.container.classList.toggle('cell-editor-focus', viewCell.focusMode === CellFocusMode.Editor); }; this._register(viewCell.onDidChangeState((e) => { if (!e.focusModeChanged) { @@ -220,10 +97,210 @@ export class StatefullMarkdownCell extends Disposable { } })); - viewUpdate(); + this._register(viewCell.onDidChangeLayout((e) => { + const layoutInfo = this.editor?.getLayoutInfo(); + if (e.outerWidth && this.viewCell.editState === CellEditState.Editing && layoutInfo && layoutInfo.width !== viewCell.layoutInfo.editorWidth) { + this.onCellEditorWidthChange(); + } else if (e.totalHeight || e.outerWidth) { + this.relayoutCell(); + } + })); + + this._register(viewCell.onCellDecorationsChanged((e) => { + e.added.forEach(options => { + if (options.className) { + DOM.addClass(templateData.container, options.className); + } + }); + + e.removed.forEach(options => { + if (options.className) { + DOM.removeClass(templateData.container, options.className); + } + }); + })); + + // apply decorations + + viewCell.getCellDecorations().forEach(options => { + if (options.className) { + DOM.addClass(templateData.container, options.className); + } + }); + + this.viewUpdate(); } - updateEditorOptions(newValue: IEditorOptions): any { + private viewUpdate(): void { + if (this.viewCell.metadata?.inputCollapsed) { + this.viewUpdateCollapsed(); + } else if (this.viewCell.editState === CellEditState.Editing) { + this.viewUpdateEditing(); + } else { + this.viewUpdatePreview(); + } + } + + private viewUpdateCollapsed(): void { + DOM.show(this.templateData.collapsedPart); + DOM.hide(this.editorPart); + DOM.hide(this.markdownContainer); + this.templateData.container.classList.toggle('collapsed', true); + } + + private viewUpdateEditing(): void { + // switch to editing mode + let editorHeight: number; + + DOM.show(this.editorPart); + DOM.hide(this.markdownContainer); + DOM.hide(this.templateData.collapsedPart); + this.templateData.container.classList.toggle('collapsed', false); + + if (this.editor) { + editorHeight = this.editor!.getContentHeight(); + + // not first time, we don't need to create editor or bind listeners + this.viewCell.attachTextEditor(this.editor); + this.focusEditorIfNeeded(); + + this.bindEditorListeners(); + + this.editor.layout({ + width: this.viewCell.layoutInfo.editorWidth, + height: editorHeight + }); + } else { + const width = this.viewCell.layoutInfo.editorWidth; + const lineNum = this.viewCell.lineCount; + const lineHeight = this.viewCell.layoutInfo.fontInfo?.lineHeight || 17; + editorHeight = Math.max(lineNum, 1) * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + + this.templateData.editorContainer.innerHTML = ''; + + // create a special context key service that set the inCompositeEditor-contextkey + const editorContextKeyService = this.contextKeyService.createScoped(); + const editorInstaService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, editorContextKeyService])); + EditorContextKeys.inCompositeEditor.bindTo(editorContextKeyService).set(true); + + this.editor = editorInstaService.createInstance(CodeEditorWidget, this.templateData.editorContainer, { + ...this.editorOptions, + dimension: { + width: width, + height: editorHeight + } + }, {}); + this.templateData.currentEditor = this.editor; + + const cts = new CancellationTokenSource(); + this._register({ dispose() { cts.dispose(true); } }); + raceCancellation(this.viewCell.resolveTextModel(), cts.token).then(model => { + if (!model) { + return; + } + + this.editor!.setModel(model); + this.focusEditorIfNeeded(); + + const realContentHeight = this.editor!.getContentHeight(); + if (realContentHeight !== editorHeight) { + this.editor!.layout( + { + width: width, + height: realContentHeight + } + ); + editorHeight = realContentHeight; + } + + this.viewCell.attachTextEditor(this.editor!); + + if (this.viewCell.editState === CellEditState.Editing) { + this.focusEditorIfNeeded(); + } + + this.bindEditorListeners(); + + this.viewCell.editorHeight = editorHeight; + }); + } + + this.viewCell.editorHeight = editorHeight; + this.focusEditorIfNeeded(); + this.renderedEditors.set(this.viewCell, this.editor!); + } + + private viewUpdatePreview(): void { + this.viewCell.detachTextEditor(); + DOM.hide(this.editorPart); + DOM.hide(this.templateData.collapsedPart); + DOM.show(this.markdownContainer); + this.templateData.container.classList.toggle('collapsed', false); + + this.renderedEditors.delete(this.viewCell); + + this.markdownContainer.innerHTML = ''; + this.viewCell.clearHTML(); + const markdownRenderer = this.viewCell.getMarkdownRenderer(); + const renderedHTML = this.viewCell.getHTML(); + if (renderedHTML) { + this.markdownContainer.appendChild(renderedHTML); + } + + if (this.editor) { + // switch from editing mode + this.viewCell.renderedMarkdownHeight = this.templateData.container.clientHeight; + this.relayoutCell(); + } else { + // first time, readonly mode + this.localDisposables.add(markdownRenderer.onDidUpdateRender(() => { + this.viewCell.renderedMarkdownHeight = this.templateData.container.clientHeight; + this.relayoutCell(); + })); + + this.localDisposables.add(this.viewCell.textBuffer.onDidChangeContent(() => { + this.markdownContainer.innerHTML = ''; + this.viewCell.clearHTML(); + const renderedHTML = this.viewCell.getHTML(); + if (renderedHTML) { + this.markdownContainer.appendChild(renderedHTML); + } + })); + + this.viewCell.renderedMarkdownHeight = this.templateData.container.clientHeight; + this.relayoutCell(); + } + } + + private focusEditorIfNeeded() { + if (this.viewCell.focusMode === CellFocusMode.Editor && this.notebookEditor.hasFocus()) { + this.editor?.focus(); + } + } + + private layoutEditor(dimension: DOM.IDimension): void { + this.editor?.layout(dimension); + this.templateData.statusBarContainer.style.width = `${dimension.width}px`; + } + + private onCellEditorWidthChange(): void { + const realContentHeight = this.editor!.getContentHeight(); + this.layoutEditor( + { + width: this.viewCell.layoutInfo.editorWidth, + height: realContentHeight + } + ); + + this.viewCell.editorHeight = realContentHeight; + this.relayoutCell(); + } + + private relayoutCell(): void { + this.notebookEditor.layoutNotebookCell(this.viewCell, this.viewCell.layoutInfo.totalHeight); + } + + updateEditorOptions(newValue: IEditorOptions): void { this.editorOptions = newValue; if (this.editor) { this.editor.updateOptions(this.editorOptions); @@ -247,35 +324,18 @@ export class StatefullMarkdownCell extends Disposable { } } - bindEditorListeners(model: ITextModel, dimension?: IDimension) { - this.localDisposables.add(model.onDidChangeContent(() => { - // we don't need to update view cell text anymore as the textbuffer is shared - this.viewCell.clearHTML(); - let clientHeight = this.markdownContainer.clientHeight; - this.markdownContainer.innerHTML = ''; - let renderedHTML = this.viewCell.getHTML(); - if (renderedHTML) { - this.markdownContainer.appendChild(renderedHTML); - clientHeight = this.markdownContainer.clientHeight; - } - - this.viewCell.totalHeight = this.editor!.getContentHeight() + 32 + clientHeight + CELL_STATUSBAR_HEIGHT; - this.notebookEditor.layoutNotebookCell(this.viewCell, this.viewCell.layoutInfo.totalHeight); - })); - + private bindEditorListeners() { this.localDisposables.add(this.editor!.onDidContentSizeChange(e => { - let viewLayout = this.editor!.getLayoutInfo(); + const viewLayout = this.editor!.getLayoutInfo(); if (e.contentHeightChanged) { + this.viewCell.editorHeight = e.contentHeight; this.editor!.layout( { width: viewLayout.width, height: e.contentHeight } ); - const clientHeight = this.markdownContainer.clientHeight; - this.viewCell.totalHeight = e.contentHeight + 32 + clientHeight; - this.notebookEditor.layoutNotebookCell(this.viewCell, this.viewCell.layoutInfo.totalHeight); } })); @@ -288,41 +348,10 @@ export class StatefullMarkdownCell extends Disposable { const primarySelection = this.editor!.getSelection(); if (primarySelection) { - this.notebookEditor.revealLineInView(this.viewCell, primarySelection!.positionLineNumber); + this.notebookEditor.revealLineInViewAsync(this.viewCell, primarySelection!.positionLineNumber); } })); - let cellWidthResizeObserver = getResizesObserver(this.templateData.editorContainer, dimension, () => { - let newWidth = cellWidthResizeObserver.getWidth(); - let realContentHeight = this.editor!.getContentHeight(); - let layoutInfo = this.editor!.getLayoutInfo(); - - // the dimension generated by the resize observer are float numbers, let's round it a bit to avoid relayout. - if (newWidth < layoutInfo.width - 0.3 || layoutInfo.width + 0.3 < newWidth) { - this.editor!.layout( - { - width: newWidth, - height: realContentHeight - } - ); - } - }); - - cellWidthResizeObserver.startObserving(); - this.localDisposables.add(cellWidthResizeObserver); - - let markdownRenderer = this.viewCell.getMarkdownRenderer(); - this.markdownContainer.innerHTML = ''; - let renderedHTML = this.viewCell.getHTML(); - if (renderedHTML) { - this.markdownContainer.appendChild(renderedHTML); - this.localDisposables.add(markdownRenderer.onDidUpdateRender(() => { - const clientHeight = this.markdownContainer.clientHeight; - this.viewCell.totalHeight = clientHeight; - this.notebookEditor.layoutNotebookCell(this.viewCell, this.viewCell.layoutInfo.totalHeight); - })); - } - const updateFocusMode = () => this.viewCell.focusMode = this.editor!.hasWidgetFocus() ? CellFocusMode.Editor : CellFocusMode.Container; this.localDisposables.add(this.editor!.onDidFocusEditorWidget(() => { updateFocusMode(); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer.ts index 4806000fadf..b2ccbc43f50 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer.ts @@ -12,6 +12,7 @@ import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer'; import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; import { TokenizationRegistry } from 'vs/editor/common/modes'; +import { URI } from 'vs/base/common/uri'; export interface IMarkdownRenderResult extends IDisposable { element: HTMLElement; @@ -23,6 +24,7 @@ export class MarkdownRenderer extends Disposable { readonly onDidUpdateRender: Event = this._onDidUpdateRender.event; constructor( + private readonly _baseUrl: URI | undefined, @IModeService private readonly _modeService: IModeService, @IOpenerService private readonly _openerService: IOpenerService ) { @@ -31,6 +33,7 @@ export class MarkdownRenderer extends Disposable { private getOptions(disposeables: DisposableStore): MarkdownRenderOptions { return { + baseUrl: this._baseUrl, codeBlockRenderer: (languageAlias, value) => { // In markdown, // it is possible that we stumble upon language aliases (e.g.js instead of javascript) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver.ts index 6a5b6ba6041..d9be462c054 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver.ts @@ -33,7 +33,7 @@ export class BrowserResizeObserver extends Disposable implements IResizeObserver this.height = -1; this.observer = new ResizeObserver((entries: any) => { - for (let entry of entries) { + for (const entry of entries) { if (entry.target === referenceDomElement && entry.contentRect) { if (this.width !== entry.contentRect.width || this.height !== entry.contentRect.height) { this.width = entry.contentRect.width; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts new file mode 100644 index 00000000000..f2f4a217b4f --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.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 type { Event } from 'vs/base/common/event'; +import type { IDisposable } from 'vs/base/common/lifecycle'; +import { ToWebviewMessage } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView'; + +// !! IMPORTANT !! everything must be in-line within the webviewPreloads +// function. Imports are not allowed. This is stringifies and injected into +// the webview. + +declare const acquireVsCodeApi: () => ({ getState(): { [key: string]: unknown; }, setState(data: { [key: string]: unknown; }): void, postMessage: (msg: unknown) => void; }); + +declare class ResizeObserver { + constructor(onChange: (entries: { target: HTMLElement, contentRect?: ClientRect; }[]) => void); + observe(element: Element): void; + disconnect(): void; +} + +declare const __outputNodePadding__: number; + +type Listener = { fn: (evt: T) => void; thisArg: unknown; }; + +interface EmitterLike { + fire(data: T): void; + event: Event; +} + +function webviewPreloads() { + const vscode = acquireVsCodeApi(); + + const handleInnerClick = (event: MouseEvent) => { + if (!event || !event.view || !event.view.document) { + return; + } + + for (let node = event.target as HTMLElement | null; node; node = node.parentNode as HTMLElement) { + if (node instanceof HTMLAnchorElement && node.href) { + if (node.href.startsWith('blob:')) { + handleBlobUrlClick(node.href, node.download); + } + event.preventDefault(); + break; + } + } + }; + + const handleBlobUrlClick = async (url: string, downloadName: string) => { + try { + const response = await fetch(url); + const blob = await response.blob(); + const reader = new FileReader(); + reader.addEventListener('load', () => { + const data = reader.result; + vscode.postMessage({ + __vscode_notebook_message: true, + type: 'clicked-data-url', + data, + downloadName + }); + }); + reader.readAsDataURL(blob); + } catch (e) { + console.error(e.message); + } + }; + + document.body.addEventListener('click', handleInnerClick); + + const preservedScriptAttributes: (keyof HTMLScriptElement)[] = [ + 'type', 'src', 'nonce', 'noModule', 'async', + ]; + + // derived from https://github.com/jquery/jquery/blob/d0ce00cdfa680f1f0c38460bc51ea14079ae8b07/src/core/DOMEval.js + const domEval = (container: Element) => { + const arr = Array.from(container.getElementsByTagName('script')); + for (let n = 0; n < arr.length; n++) { + const node = arr[n]; + const scriptTag = document.createElement('script'); + scriptTag.text = node.innerText; + for (const key of preservedScriptAttributes) { + const val = node[key] || node.getAttribute && node.getAttribute(key); + if (val) { + scriptTag.setAttribute(key, val as any); + } + } + + // TODO: should script with src not be removed? + container.appendChild(scriptTag).parentNode!.removeChild(scriptTag); + } + }; + + const outputObservers = new Map(); + + const resizeObserve = (container: Element, id: string) => { + const resizeObserver = new ResizeObserver(entries => { + for (const entry of entries) { + if (!document.body.contains(entry.target)) { + return; + } + + if (entry.target.id === id && entry.contentRect) { + vscode.postMessage({ + __vscode_notebook_message: true, + type: 'dimension', + id: id, + data: { + height: entry.contentRect.height + __outputNodePadding__ * 2 + } + }); + } + } + }); + + resizeObserver.observe(container); + if (outputObservers.has(id)) { + outputObservers.get(id)?.disconnect(); + } + + outputObservers.set(id, resizeObserver); + }; + + function scrollWillGoToParent(event: WheelEvent) { + for (let node = event.target as Node | null; node; node = node.parentNode) { + if (!(node instanceof Element) || node.id === 'container') { + return false; + } + + if (event.deltaY < 0 && node.scrollTop > 0) { + return true; + } + + if (event.deltaY > 0 && node.scrollTop + node.clientHeight < node.scrollHeight) { + return true; + } + } + + return false; + } + + const handleWheel = (event: WheelEvent) => { + if (event.defaultPrevented || scrollWillGoToParent(event)) { + return; + } + + vscode.postMessage({ + __vscode_notebook_message: true, + type: 'did-scroll-wheel', + payload: { + deltaMode: event.deltaMode, + deltaX: event.deltaX, + deltaY: event.deltaY, + deltaZ: event.deltaZ, + detail: event.detail, + type: event.type + } + }); + }; + + function focusFirstFocusableInCell(cellId: string) { + const cellOutputContainer = document.getElementById(cellId); + if (cellOutputContainer) { + const focusableElement = cellOutputContainer.querySelector('[tabindex="0"], [href], button, input, option, select, textarea') as HTMLElement | null; + focusableElement?.focus(); + } + } + + function createFocusSink(cellId: string, outputId: string, focusNext?: boolean) { + const element = document.createElement('div'); + element.tabIndex = 0; + element.addEventListener('focus', () => { + vscode.postMessage({ + __vscode_notebook_message: true, + type: 'focus-editor', + id: outputId, + focusNext + }); + + setTimeout(() => { // Wait a tick to prevent the focus indicator blinking before webview blurs + // Move focus off the focus sink - single use + focusFirstFocusableInCell(cellId); + }, 50); + }); + + return element; + } + + function addMouseoverListeners(element: HTMLElement, outputId: string): void { + element.addEventListener('mouseenter', () => { + vscode.postMessage({ + __vscode_notebook_message: true, + type: 'mouseenter', + id: outputId, + data: {} + }); + }); + element.addEventListener('mouseleave', () => { + vscode.postMessage({ + __vscode_notebook_message: true, + type: 'mouseleave', + id: outputId, + data: {} + }); + }); + } + + const dontEmit = Symbol('dontEmit'); + + function createEmitter(listenerChange: (listeners: Set>) => void = () => undefined): EmitterLike { + const listeners = new Set>(); + return { + fire(data) { + for (const listener of [...listeners]) { + listener.fn.call(listener.thisArg, data); + } + }, + event(fn, thisArg, disposables) { + const listenerObj = { fn, thisArg }; + const disposable: IDisposable = { + dispose: () => { + listeners.delete(listenerObj); + listenerChange(listeners); + }, + }; + + listeners.add(listenerObj); + listenerChange(listeners); + + if (disposables instanceof Array) { + disposables.push(disposable); + } else if (disposables) { + disposables.add(disposable); + } + + return disposable; + }, + }; + } + + // Maps the events in the given emitter, invoking mapFn on each one. mapFn can return + // the dontEmit symbol to skip emission. + function mapEmitter(emitter: EmitterLike, mapFn: (data: T) => R | typeof dontEmit) { + let listener: IDisposable; + const mapped = createEmitter(listeners => { + if (listeners.size && !listener) { + listener = emitter.event(data => { + const v = mapFn(data); + if (v !== dontEmit) { + mapped.fire(v); + } + }); + } else if (listener && !listeners.size) { + listener.dispose(); + } + }); + + return mapped.event; + } + + interface ICreateCellInfo { + outputId: string; + output?: unknown; + mimeType?: string; + element: HTMLElement; + } + + interface IDestroyCellInfo { + outputId: string; + } + + const onWillDestroyOutput = createEmitter<[string | undefined /* namespace */, IDestroyCellInfo | undefined /* cell uri */]>(); + const onDidCreateOutput = createEmitter<[string | undefined /* namespace */, ICreateCellInfo]>(); + const onDidReceiveMessage = createEmitter<[string, unknown]>(); + + const matchesNs = (namespace: string, query: string | undefined) => namespace === '*' || query === namespace || query === 'undefined'; + + (window as any).acquireNotebookRendererApi = (namespace: string) => { + if (!namespace || typeof namespace !== 'string') { + throw new Error(`acquireNotebookRendererApi should be called your renderer type as a string, got: ${namespace}.`); + } + + return { + postMessage(message: unknown) { + vscode.postMessage({ + __vscode_notebook_message: true, + type: 'customRendererMessage', + rendererId: namespace, + message, + }); + }, + setState(newState: T) { + vscode.setState({ ...vscode.getState(), [namespace]: newState }); + }, + getState(): T | undefined { + const state = vscode.getState(); + return typeof state === 'object' && state ? state[namespace] as T : undefined; + }, + onDidReceiveMessage: mapEmitter(onDidReceiveMessage, ([ns, data]) => ns === namespace ? data : dontEmit), + onWillDestroyOutput: mapEmitter(onWillDestroyOutput, ([ns, data]) => matchesNs(namespace, ns) ? data : dontEmit), + onDidCreateOutput: mapEmitter(onDidCreateOutput, ([ns, data]) => matchesNs(namespace, ns) ? data : dontEmit), + }; + }; + + /** + * Map of preload resource URIs to promises that resolve one the resource + * loads or errors. + */ + const preloadPromises = new Map>(); + const queuedOuputActions = new Map>(); + + /** + * Enqueues an action that affects a output. This blocks behind renderer load + * requests that affect the same output. This should be called whenever you + * do something that affects output to ensure it runs in + * the correct order. + */ + const enqueueOutputAction = (event: T, fn: (event: T) => Promise | void) => { + const queued = queuedOuputActions.get(event.outputId); + const maybePromise = queued ? queued.then(() => fn(event)) : fn(event); + if (typeof maybePromise === 'undefined') { + return; // a synchonrously-called function, we're done + } + + const promise = maybePromise.then(() => { + if (queuedOuputActions.get(event.outputId) === promise) { + queuedOuputActions.delete(event.outputId); + } + }); + + queuedOuputActions.set(event.outputId, promise); + }; + + window.addEventListener('wheel', handleWheel); + + window.addEventListener('message', rawEvent => { + const event = rawEvent as ({ data: ToWebviewMessage; }); + + switch (event.data.type) { + case 'html': + enqueueOutputAction(event.data, async data => { + await Promise.all(data.requiredPreloads.map(p => preloadPromises.get(p.uri))); + if (!queuedOuputActions.has(data.outputId)) { // output was cleared while loading + return; + } + + let cellOutputContainer = document.getElementById(data.cellId); + const outputId = data.outputId; + if (!cellOutputContainer) { + const container = document.getElementById('container')!; + + const upperWrapperElement = createFocusSink(data.cellId, outputId); + container.appendChild(upperWrapperElement); + + const newElement = document.createElement('div'); + + newElement.id = data.cellId; + container.appendChild(newElement); + cellOutputContainer = newElement; + + const lowerWrapperElement = createFocusSink(data.cellId, outputId, true); + container.appendChild(lowerWrapperElement); + } + + const outputNode = document.createElement('div'); + outputNode.style.position = 'absolute'; + outputNode.style.top = data.top + 'px'; + outputNode.style.left = data.left + 'px'; + outputNode.style.width = 'calc(100% - ' + data.left + 'px)'; + outputNode.style.minHeight = '32px'; + outputNode.id = outputId; + + addMouseoverListeners(outputNode, outputId); + const content = data.content; + outputNode.innerHTML = content; + cellOutputContainer.appendChild(outputNode); + + let pureData: { mimeType: string, output: unknown } | undefined; + const outputScript = cellOutputContainer.querySelector('script.vscode-pure-data'); + if (outputScript) { + try { pureData = JSON.parse(outputScript.innerHTML); } catch { } + } + + // eval + domEval(outputNode); + resizeObserve(outputNode, outputId); + onDidCreateOutput.fire([data.apiNamespace, { + element: outputNode, + output: pureData?.output, + mimeType: pureData?.mimeType, + outputId + }]); + + vscode.postMessage({ + __vscode_notebook_message: true, + type: 'dimension', + id: outputId, + data: { + height: outputNode.clientHeight + } + }); + + // don't hide until after this step so that the height is right + cellOutputContainer.style.display = data.initiallyHidden ? 'none' : 'block'; + }); + break; + case 'view-scroll': + { + // const date = new Date(); + // console.log('----- will scroll ---- ', date.getMinutes() + ':' + date.getSeconds() + ':' + date.getMilliseconds()); + + for (let i = 0; i < event.data.widgets.length; i++) { + const widget = document.getElementById(event.data.widgets[i].id)!; + widget.style.top = event.data.widgets[i].top + 'px'; + widget.parentElement!.style.display = 'block'; + } + break; + } + case 'clear': + queuedOuputActions.clear(); // stop all loading outputs + onWillDestroyOutput.fire([undefined, undefined]); + document.getElementById('container')!.innerHTML = ''; + + outputObservers.forEach(ob => { + ob.disconnect(); + }); + outputObservers.clear(); + break; + case 'clearOutput': + const output = document.getElementById(event.data.outputId); + queuedOuputActions.delete(event.data.outputId); // stop any in-progress rendering + if (output && output.parentNode) { + onWillDestroyOutput.fire([event.data.apiNamespace, { outputId: event.data.outputId }]); + output.parentNode.removeChild(output); + } + break; + case 'hideOutput': + enqueueOutputAction(event.data, ({ outputId }) => { + const container = document.getElementById(outputId)?.parentElement; + if (container) { + container.style.display = 'none'; + } + }); + break; + case 'showOutput': + enqueueOutputAction(event.data, ({ outputId, top }) => { + const output = document.getElementById(outputId); + if (output) { + output.parentElement!.style.display = 'block'; + output.style.top = top + 'px'; + } + }); + break; + case 'preload': + const resources = event.data.resources; + const preloadsContainer = document.getElementById('__vscode_preloads')!; + for (let i = 0; i < resources.length; i++) { + const { uri } = resources[i]; + const scriptTag = document.createElement('script'); + scriptTag.setAttribute('src', uri); + preloadsContainer.appendChild(scriptTag); + preloadPromises.set(uri, new Promise(resolve => { + scriptTag.addEventListener('load', () => resolve()); + scriptTag.addEventListener('error', () => resolve()); + })); + } + break; + case 'focus-output': + focusFirstFocusableInCell(event.data.cellId); + break; + case 'decorations': + { + const outputContainer = document.getElementById(event.data.cellId); + event.data.addedClassNames.forEach(n => { + outputContainer?.classList.add(n); + }); + + event.data.removedClassNames.forEach(n => { + outputContainer?.classList.remove(n); + }); + } + + break; + case 'customRendererMessage': + onDidReceiveMessage.fire([event.data.rendererId, event.data.message]); + break; + } + }); + + vscode.postMessage({ + __vscode_notebook_message: true, + type: 'initialized' + }); +} + +export const preloadsScriptStr = (outputNodePadding: number) => `(${webviewPreloads})()`.replace(/__outputNodePadding__/g, `${outputNodePadding}`); diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts index e17d301efb3..d70f6e4c842 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts @@ -3,25 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; import { IPosition } from 'vs/editor/common/core/position'; import * as editorCommon from 'vs/editor/common/editorCommon'; import * as model from 'vs/editor/common/model'; import { SearchParams } from 'vs/editor/common/model/textModelSearch'; -import { EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_MARGIN } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFocusMode, CellRunState, CursorAtBoundary, CellViewModelStateChangeEvent, IEditableCellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { CellKind, NotebookCellMetadata, NotebookDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; +import { CellEditState, CellFocusMode, CursorAtBoundary, CellViewModelStateChangeEvent, IEditableCellViewModel, INotebookCellDecorationOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellKind, NotebookCellMetadata, NotebookDocumentMetadata, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -export const NotebookCellMetadataDefaults = { - editable: true, - runnable: true -}; - export abstract class BaseCellViewModel extends Disposable { protected readonly _onDidChangeEditorAttachState = new Emitter(); // Do not merge this event with `onDidChangeState` as we are using `Event.once(onDidChangeEditorAttachState)` elsewhere. @@ -65,32 +60,13 @@ export abstract class BaseCellViewModel extends Disposable { } } - // TODO@roblourens - move any "run"/"status" concept to Code-specific places - private _currentTokenSource: CancellationTokenSource | undefined; - public set currentTokenSource(v: CancellationTokenSource | undefined) { - this._currentTokenSource = v; - this._onDidChangeState.fire({ runStateChanged: true }); - } - - public get currentTokenSource(): CancellationTokenSource | undefined { - return this._currentTokenSource; - } - - get runState(): CellRunState { - return this._currentTokenSource ? CellRunState.Running : CellRunState.Idle; - } - private _focusMode: CellFocusMode = CellFocusMode.Container; get focusMode() { return this._focusMode; } set focusMode(newMode: CellFocusMode) { - const changed = this._focusMode !== newMode; this._focusMode = newMode; - - if (changed) { - this._onDidChangeState.fire({ focusModeChanged: true }); - } + this._onDidChangeState.fire({ focusModeChanged: true }); } protected _textEditor?: ICodeEditor; @@ -99,17 +75,24 @@ export abstract class BaseCellViewModel extends Disposable { } private _cursorChangeListener: IDisposable | null = null; private _editorViewStates: editorCommon.ICodeEditorViewState | null = null; + private _resolvedCellDecorations = new Map(); + private _cellDecorationsChanged = new Emitter<{ added: INotebookCellDecorationOptions[], removed: INotebookCellDecorationOptions[] }>(); + onCellDecorationsChanged: Event<{ added: INotebookCellDecorationOptions[], removed: INotebookCellDecorationOptions[] }> = this._cellDecorationsChanged.event; private _resolvedDecorations = new Map(); private _lastDecorationId: number = 0; - protected _textModel?: model.ITextModel; + private _textModel: model.ITextModel | undefined = undefined; get textModel(): model.ITextModel | undefined { return this._textModel; } + set textModel(m: model.ITextModel | undefined) { + this._textModel = m; + } + hasModel(): this is IEditableCellViewModel { return !!this._textModel; } @@ -123,7 +106,7 @@ export abstract class BaseCellViewModel extends Disposable { this._dragging = v; } - constructor(readonly viewType: string, readonly notebookHandle: number, readonly model: NotebookCellTextModel, public id: string) { + constructor(readonly viewType: string, readonly model: NotebookCellTextModel, public id: string) { super(); this._register(model.onDidChangeLanguage(() => { @@ -141,7 +124,7 @@ export abstract class BaseCellViewModel extends Disposable { abstract onDeselect(): void; assertTextModelAttached(): boolean { - if (this._textModel && this._textEditor && this._textEditor.getModel() === this._textModel) { + if (this.textModel && this._textEditor && this._textEditor.getModel() === this.textModel) { return true; } @@ -162,9 +145,10 @@ export abstract class BaseCellViewModel extends Disposable { } this._textEditor = editor; + this.textModel = this._textEditor.getModel() || undefined; if (this._editorViewStates) { - this.restoreViewState(this._editorViewStates); + this._restoreViewState(this._editorViewStates); } this._resolvedDecorations.forEach((value, key) => { @@ -188,7 +172,7 @@ export abstract class BaseCellViewModel extends Disposable { this.saveViewState(); // decorations need to be cleared first as editors can be resued. this._resolvedDecorations.forEach(value => { - let resolvedid = value.id; + const resolvedid = value.id; if (resolvedid) { this._textEditor?.deltaDecorations([resolvedid], []); @@ -196,6 +180,7 @@ export abstract class BaseCellViewModel extends Disposable { }); this._textEditor = undefined; + this.textModel = undefined; this._cursorChangeListener?.dispose(); this._cursorChangeListener = null; this._onDidChangeEditorAttachState.fire(); @@ -205,6 +190,10 @@ export abstract class BaseCellViewModel extends Disposable { return this.model.getValue(); } + getTextLength(): number { + return this.model.getTextLength(); + } + private saveViewState(): void { if (!this._textEditor) { return; @@ -225,13 +214,13 @@ export abstract class BaseCellViewModel extends Disposable { this._editorViewStates = editorViewStates; } - private restoreViewState(state: editorCommon.ICodeEditorViewState | null): void { + private _restoreViewState(state: editorCommon.ICodeEditorViewState | null): void { if (state) { this._textEditor?.restoreViewState(state); } } - addDecoration(decoration: model.IModelDeltaDecoration): string { + addModelDecoration(decoration: model.IModelDeltaDecoration): string { if (!this._textEditor) { const id = ++this._lastDecorationId; const decorationId = `_lazy_${this.id};${id}`; @@ -244,7 +233,7 @@ export abstract class BaseCellViewModel extends Disposable { return result[0]; } - removeDecoration(decorationId: string) { + removeModelDecoration(decorationId: string) { const realDecorationId = this._resolvedDecorations.get(decorationId); if (this._textEditor && realDecorationId && realDecorationId.id !== undefined) { @@ -255,13 +244,46 @@ export abstract class BaseCellViewModel extends Disposable { this._resolvedDecorations.delete(decorationId); } - deltaDecorations(oldDecorations: string[], newDecorations: model.IModelDeltaDecoration[]): string[] { + deltaModelDecorations(oldDecorations: string[], newDecorations: model.IModelDeltaDecoration[]): string[] { oldDecorations.forEach(id => { - this.removeDecoration(id); + this.removeModelDecoration(id); }); const ret = newDecorations.map(option => { - return this.addDecoration(option); + return this.addModelDecoration(option); + }); + + return ret; + } + + private _removeCellDecoration(decorationId: string) { + const options = this._resolvedCellDecorations.get(decorationId); + + if (options) { + this._cellDecorationsChanged.fire({ added: [], removed: [options] }); + this._resolvedCellDecorations.delete(decorationId); + } + } + + private _addCellDecoration(options: INotebookCellDecorationOptions): string { + const id = ++this._lastDecorationId; + const decorationId = `_cell_${this.id};${id}`; + this._resolvedCellDecorations.set(decorationId, options); + this._cellDecorationsChanged.fire({ added: [options], removed: [] }); + return decorationId; + } + + getCellDecorations() { + return [...this._resolvedCellDecorations.values()]; + } + + deltaCellDecorations(oldDecorations: string[], newDecorations: INotebookCellDecorationOptions[]): string[] { + oldDecorations.forEach(id => { + this._removeCellDecoration(id); + }); + + const ret = newDecorations.map(option => { + return this._addCellDecoration(option); }); return ret; @@ -275,6 +297,16 @@ export abstract class BaseCellViewModel extends Disposable { this._textEditor?.setSelection(range); } + setSelections(selections: Selection[]) { + if (selections.length) { + this._textEditor?.setSelections(selections); + } + } + + getSelections() { + return this._textEditor?.getSelections() || []; + } + getSelectionsStartPosition(): IPosition[] | undefined { if (this._textEditor) { const selections = this._textEditor.getSelections(); @@ -290,7 +322,15 @@ export abstract class BaseCellViewModel extends Disposable { return 0; } - return this._textEditor.getTopForLineNumber(line) + EDITOR_TOP_MARGIN + EDITOR_TOOLBAR_HEIGHT; + return this._textEditor.getTopForLineNumber(line) + EDITOR_TOP_PADDING; + } + + getPositionScrollTopOffset(line: number, column: number): number { + if (!this._textEditor) { + return 0; + } + + return this._textEditor.getTopForPosition(line, column) + EDITOR_TOP_PADDING; } cursorAtBoundary(): CursorAtBoundary { @@ -298,6 +338,10 @@ export abstract class BaseCellViewModel extends Disposable { return CursorAtBoundary.None; } + if (!this.textModel) { + return CursorAtBoundary.None; + } + // only validate primary cursor const selection = this._textEditor.getSelection(); @@ -307,7 +351,7 @@ export abstract class BaseCellViewModel extends Disposable { } const firstViewLineTop = this._textEditor.getTopForPosition(1, 1); - const lastViewLineTop = this._textEditor.getTopForPosition(this._textModel!.getLineCount(), this._textModel!.getLineLength(this._textModel!.getLineCount())); + const lastViewLineTop = this._textEditor.getTopForPosition(this.textModel!.getLineCount(), this.textModel!.getLineLength(this.textModel!.getLineCount())); const selectionTop = this._textEditor.getTopForPosition(selection.startLineNumber, selection.startColumn); if (selectionTop === lastViewLineTop) { @@ -329,15 +373,23 @@ export abstract class BaseCellViewModel extends Disposable { return this.model.textBuffer; } - protected cellStartFind(value: string): model.FindMatch[] | null { + abstract resolveTextModel(): Promise; + + protected cellStartFind(value: string, options: INotebookSearchOptions): model.FindMatch[] | null { let cellMatches: model.FindMatch[] = []; if (this.assertTextModelAttached()) { - cellMatches = this._textModel!.findMatches(value, false, false, false, null, false); + cellMatches = this.textModel!.findMatches( + value, + false, + options.regex || false, + options.caseSensitive || false, + options.wholeWord ? options.wordSeparators || null : null, + false); } else { const lineCount = this.textBuffer.getLineCount(); const fullRange = new Range(1, 1, lineCount, this.textBuffer.getLineLength(lineCount) + 1); - const searchParams = new SearchParams(value, false, false, null); + const searchParams = new SearchParams(value, options.regex || false, options.caseSensitive || false, options.wholeWord ? options.wordSeparators || null : null,); const searchData = searchParams.parseSearchRequest(); if (!searchData) { @@ -350,25 +402,31 @@ export abstract class BaseCellViewModel extends Disposable { return cellMatches; } - getEvaluatedMetadata(documentMetadata: NotebookDocumentMetadata | undefined): NotebookCellMetadata { - const editable: boolean = this.metadata?.editable === undefined - ? (documentMetadata?.cellEditable === undefined ? NotebookCellMetadataDefaults.editable : documentMetadata?.cellEditable) - : this.metadata?.editable; + getEvaluatedMetadata(documentMetadata: NotebookDocumentMetadata): NotebookCellMetadata { + const editable = this.metadata?.editable ?? + documentMetadata.cellEditable; - const runnable: boolean = this.metadata?.runnable === undefined - ? (documentMetadata?.cellRunnable === undefined ? NotebookCellMetadataDefaults.runnable : documentMetadata?.cellRunnable) - : this.metadata?.runnable; + const runnable = this.metadata?.runnable ?? + documentMetadata.cellRunnable; + + const hasExecutionOrder = this.metadata?.hasExecutionOrder ?? + documentMetadata.cellHasExecutionOrder; return { - editable, - runnable, - executionOrder: this.metadata?.executionOrder, - runState: this.metadata?.runState, - statusMessage: this.metadata?.statusMessage + ...(this.metadata || {}), + ...{ + editable, + runnable, + hasExecutionOrder + } }; } - toJSON(): any { + dispose() { + super.dispose(); + } + + toJSON(): object { return { handle: this.handle }; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/cellEdit.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/cellEdit.ts index 0803c645e71..709c41859b2 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/cellEdit.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/cellEdit.ts @@ -3,171 +3,139 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ICell } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IResourceUndoRedoElement, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo'; import { URI } from 'vs/base/common/uri'; import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel'; -import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; +import { CellFocusMode } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; +import { ITextCellEditingDelegate } from 'vs/workbench/contrib/notebook/common/model/cellEdit'; -/** - * It should not modify Undo/Redo stack - */ -export interface ICellEditingDelegate { - insertCell?(index: number, viewCell: BaseCellViewModel): void; - deleteCell?(index: number): void; - moveCell?(fromIndex: number, toIndex: number): void; - createCellViewModel?(cell: ICell): BaseCellViewModel; - setSelections(selections: number[]): void; + +export interface IViewCellEditingDelegate extends ITextCellEditingDelegate { + createCellViewModel?(cell: NotebookCellTextModel): BaseCellViewModel; + createCell?(index: number, source: string | string[], language: string, type: CellKind): BaseCellViewModel; } -export class InsertCellEdit implements IResourceUndoRedoElement { +export class JoinCellEdit implements IResourceUndoRedoElement { type: UndoRedoElementType.Resource = UndoRedoElementType.Resource; - label: string = 'Insert Cell'; + label: string = 'Join Cell'; + private _deletedRawCell: NotebookCellTextModel; constructor( public resource: URI, - private insertIndex: number, + private index: number, + private direction: 'above' | 'below', private cell: BaseCellViewModel, - private editingDelegate: ICellEditingDelegate, - private beforedSelections: number[], - private endSelections: number[] + private selections: Selection[], + private inverseRange: Range, + private insertContent: string, + private removedCell: BaseCellViewModel, + private editingDelegate: IViewCellEditingDelegate, ) { + this._deletedRawCell = this.removedCell.model; } - undo(): void | Promise { - if (!this.editingDelegate.deleteCell) { - throw new Error('Notebook Delete Cell not implemented for Undo/Redo'); - } - - this.editingDelegate.deleteCell(this.insertIndex); - this.editingDelegate.setSelections(this.beforedSelections); - } - redo(): void | Promise { - if (!this.editingDelegate.insertCell) { - throw new Error('Notebook Insert Cell not implemented for Undo/Redo'); - } - - this.editingDelegate.insertCell(this.insertIndex, this.cell); - this.editingDelegate.setSelections(this.endSelections); - } -} - -export class DeleteCellEdit implements IResourceUndoRedoElement { - type: UndoRedoElementType.Resource = UndoRedoElementType.Resource; - label: string = 'Delete Cell'; - - private _rawCell: ICell; - constructor( - public resource: URI, - private insertIndex: number, - cell: BaseCellViewModel, - private editingDelegate: ICellEditingDelegate, - private beforedSelections: number[], - private endSelections: number[] - ) { - this._rawCell = cell.model; - - // save inmem text to `ICell` - // no needed any more as the text buffer is transfered to `raw_cell` - // this._rawCell.source = [cell.getText()]; - } - - undo(): void | Promise { + async undo(): Promise { if (!this.editingDelegate.insertCell || !this.editingDelegate.createCellViewModel) { throw new Error('Notebook Insert Cell not implemented for Undo/Redo'); } - const cell = this.editingDelegate.createCellViewModel(this._rawCell); - this.editingDelegate.insertCell(this.insertIndex, cell); - this.editingDelegate.setSelections(this.beforedSelections); + await this.cell.resolveTextModel(); + + this.cell.textModel?.applyEdits([ + { range: this.inverseRange, text: '' } + ]); + + this.cell.setSelections(this.selections); + + const cell = this.editingDelegate.createCellViewModel(this._deletedRawCell); + if (this.direction === 'above') { + this.editingDelegate.insertCell(this.index, this._deletedRawCell); + this.editingDelegate.emitSelections([cell.handle]); + cell.focusMode = CellFocusMode.Editor; + } else { + this.editingDelegate.insertCell(this.index, cell.model); + this.editingDelegate.emitSelections([this.cell.handle]); + this.cell.focusMode = CellFocusMode.Editor; + } } - redo(): void | Promise { + async redo(): Promise { if (!this.editingDelegate.deleteCell) { throw new Error('Notebook Delete Cell not implemented for Undo/Redo'); } - this.editingDelegate.deleteCell(this.insertIndex); - this.editingDelegate.setSelections(this.endSelections); + await this.cell.resolveTextModel(); + this.cell.textModel?.applyEdits([ + { range: this.inverseRange, text: this.insertContent } + ]); + + this.editingDelegate.deleteCell(this.index); + this.editingDelegate.emitSelections([this.cell.handle]); + this.cell.focusMode = CellFocusMode.Editor; } } -export class MoveCellEdit implements IResourceUndoRedoElement { - type: UndoRedoElementType.Resource = UndoRedoElementType.Resource; - label: string = 'Delete Cell'; +export class SplitCellEdit implements IResourceUndoRedoElement { + type: UndoRedoElementType.Resource = UndoRedoElementType.Resource; + label: string = 'Join Cell'; constructor( public resource: URI, - private fromIndex: number, - private toIndex: number, - private editingDelegate: ICellEditingDelegate, - private beforedSelections: number[], - private endSelections: number[] + private index: number, + private cell: BaseCellViewModel, + private selections: Selection[], + private cellContents: string[], + private language: string, + private cellKind: CellKind, + private editingDelegate: IViewCellEditingDelegate ) { + } - undo(): void | Promise { - if (!this.editingDelegate.moveCell) { - throw new Error('Notebook Move Cell not implemented for Undo/Redo'); + async undo(): Promise { + if (!this.editingDelegate.deleteCell) { + throw new Error('Notebook Delete Cell not implemented for Undo/Redo'); } - this.editingDelegate.moveCell(this.toIndex, this.fromIndex); - this.editingDelegate.setSelections(this.beforedSelections); + await this.cell.resolveTextModel(); + this.cell.textModel!.applyEdits([ + { + range: this.cell.textModel!.getFullModelRange(), + text: this.cellContents.join('') + } + ]); + this.cell.setSelections(this.selections); + + for (let j = 1; j < this.cellContents.length; j++) { + this.editingDelegate.deleteCell(this.index + 1); + } + + this.editingDelegate.emitSelections([this.cell.handle]); + this.cell.focusMode = CellFocusMode.Editor; } - redo(): void | Promise { - if (!this.editingDelegate.moveCell) { - throw new Error('Notebook Move Cell not implemented for Undo/Redo'); + async redo(): Promise { + if (!this.editingDelegate.createCell) { + throw new Error('Notebook Insert Cell not implemented for Undo/Redo'); } - this.editingDelegate.moveCell(this.fromIndex, this.toIndex); - this.editingDelegate.setSelections(this.endSelections); - } -} - -export class SpliceCellsEdit implements IResourceUndoRedoElement { - type: UndoRedoElementType.Resource = UndoRedoElementType.Resource; - label: string = 'Insert Cell'; - constructor( - public resource: URI, - private diffs: [number, CellViewModel[], CellViewModel[]][], - private editingDelegate: ICellEditingDelegate, - private beforeHandles: number[], - private endHandles: number[] - ) { - } - - undo(): void | Promise { - if (!this.editingDelegate.deleteCell || !this.editingDelegate.insertCell) { - throw new Error('Notebook Insert/Delete Cell not implemented for Undo/Redo'); - } - - this.diffs.forEach(diff => { - for (let i = 0; i < diff[2].length; i++) { - this.editingDelegate.deleteCell!(diff[0]); - } - - diff[1].reverse().forEach(cell => { - this.editingDelegate.insertCell!(diff[0], cell); - }); - }); - this.editingDelegate.setSelections(this.beforeHandles); - } - - redo(): void | Promise { - if (!this.editingDelegate.deleteCell || !this.editingDelegate.insertCell) { - throw new Error('Notebook Insert/Delete Cell not implemented for Undo/Redo'); - } - - this.diffs.reverse().forEach(diff => { - for (let i = 0; i < diff[1].length; i++) { - this.editingDelegate.deleteCell!(diff[0]); - } - - diff[2].reverse().forEach(cell => { - this.editingDelegate.insertCell!(diff[0], cell); - }); - }); - - this.editingDelegate.setSelections(this.endHandles); + await this.cell.resolveTextModel(); + this.cell.textModel!.applyEdits([ + { range: this.cell.textModel!.getFullModelRange(), text: this.cellContents[0] } + ], false); + + let insertIndex = this.index + 1; + let lastCell; + for (let j = 1; j < this.cellContents.length; j++, insertIndex++) { + lastCell = this.editingDelegate.createCell(insertIndex, this.cellContents[j], this.language, this.cellKind); + } + + if (lastCell) { + this.editingDelegate.emitSelections([lastCell.handle]); + lastCell.focusMode = CellFocusMode.Editor; + } } } diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index cbcab113254..306c9722c8d 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -7,17 +7,16 @@ import { Emitter, Event } from 'vs/base/common/event'; import * as UUID from 'vs/base/common/uuid'; import * as editorCommon from 'vs/editor/common/editorCommon'; import * as model from 'vs/editor/common/model'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; -import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CELL_RUN_GUTTER, CELL_STATUSBAR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, ICellViewModel, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CELL_RUN_GUTTER, CELL_STATUSBAR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, CELL_TOP_MARGIN, EDITOR_TOP_PADDING, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, BOTTOM_CELL_TOOLBAR_OFFSET, COLLAPSED_INDICATOR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; +import { CellEditState, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, ICellViewModel, NotebookLayoutInfo, CodeCellLayoutState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { CellKind, NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, NotebookCellOutputsSplice, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { BaseCellViewModel } from './baseCellViewModel'; import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; export class CodeCellViewModel extends BaseCellViewModel implements ICellViewModel { - cellKind: CellKind.Code = CellKind.Code; + readonly cellKind = CellKind.Code; protected readonly _onDidChangeOutputs = new Emitter(); readonly onDidChangeOutputs = this._onDidChangeOutputs.event; private _outputCollection: number[] = []; @@ -46,7 +45,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod } get editorHeight() { - return this._editorHeight; + throw new Error('editorHeight is write-only'); } private _hoveringOutput: boolean = false; @@ -67,13 +66,11 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod constructor( readonly viewType: string, - readonly notebookHandle: number, readonly model: NotebookCellTextModel, initialNotebookLayoutInfo: NotebookLayoutInfo | null, - readonly eventDispatcher: NotebookEventDispatcher, - @ITextModelService private readonly _modelService: ITextModelService, + readonly eventDispatcher: NotebookEventDispatcher ) { - super(viewType, notebookHandle, model, UUID.generateUuid()); + super(viewType, model, UUID.generateUuid()); this._register(this.model.onDidChangeOutputs((splices) => { this._outputCollection = new Array(this.model.outputs.length); this._outputsTop = null; @@ -90,33 +87,76 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod outputTotalHeight: 0, totalHeight: 0, indicatorHeight: 0, - bottomToolbarOffset: 0 + bottomToolbarOffset: 0, + layoutState: CodeCellLayoutState.Uninitialized }; } private computeEditorWidth(outerWidth: number): number { - return outerWidth - (CELL_MARGIN * 2 + CELL_RUN_GUTTER); + return outerWidth - (CODE_CELL_LEFT_MARGIN + (CELL_MARGIN * 2) + CELL_RUN_GUTTER); } layoutChange(state: CodeCellLayoutChangeEvent) { // recompute this._ensureOutputsTop(); - const outputTotalHeight = this._outputsTop!.getTotalValue(); - const totalHeight = EDITOR_TOOLBAR_HEIGHT + this.editorHeight + EDITOR_TOP_MARGIN + outputTotalHeight + BOTTOM_CELL_TOOLBAR_HEIGHT + CELL_STATUSBAR_HEIGHT; - const indicatorHeight = this.editorHeight + CELL_STATUSBAR_HEIGHT + outputTotalHeight; - const outputContainerOffset = EDITOR_TOOLBAR_HEIGHT + EDITOR_TOP_MARGIN + this.editorHeight + CELL_STATUSBAR_HEIGHT; - const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT; - const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth; - this._layoutInfo = { - fontInfo: state.font || null, - editorHeight: this._editorHeight, - editorWidth, - outputContainerOffset, - outputTotalHeight, - totalHeight, - indicatorHeight, - bottomToolbarOffset: bottomToolbarOffset - }; + let outputTotalHeight = this.metadata?.outputCollapsed ? COLLAPSED_INDICATOR_HEIGHT : this._outputsTop!.getTotalValue(); + + if (!this.metadata?.inputCollapsed) { + let newState: CodeCellLayoutState; + let editorHeight: number; + let totalHeight: number; + if (!state.editorHeight && this._layoutInfo.layoutState === CodeCellLayoutState.FromCache) { + // No new editorHeight info - keep cached totalHeight and estimate editorHeight + editorHeight = this.estimateEditorHeight(state.font?.lineHeight); + totalHeight = this._layoutInfo.totalHeight; + newState = CodeCellLayoutState.FromCache; + } else if (state.editorHeight || this._layoutInfo.layoutState === CodeCellLayoutState.Measured) { + // Editor has been measured + editorHeight = this._editorHeight; + totalHeight = this.computeTotalHeight(this._editorHeight, outputTotalHeight); + newState = CodeCellLayoutState.Measured; + } else { + editorHeight = this.estimateEditorHeight(state.font?.lineHeight); + totalHeight = this.computeTotalHeight(editorHeight, outputTotalHeight); + newState = CodeCellLayoutState.Estimated; + } + + const indicatorHeight = editorHeight + CELL_STATUSBAR_HEIGHT + outputTotalHeight; + const outputContainerOffset = EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + CELL_STATUSBAR_HEIGHT; + const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT - BOTTOM_CELL_TOOLBAR_OFFSET; + const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth; + + this._layoutInfo = { + fontInfo: state.font || null, + editorHeight, + editorWidth, + outputContainerOffset, + outputTotalHeight, + totalHeight, + indicatorHeight, + bottomToolbarOffset, + layoutState: newState + }; + } else { + outputTotalHeight = this.metadata?.inputCollapsed && this.metadata.outputCollapsed ? 0 : outputTotalHeight; + const indicatorHeight = COLLAPSED_INDICATOR_HEIGHT + outputTotalHeight; + const outputContainerOffset = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT; + const totalHeight = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT + CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_HEIGHT + outputTotalHeight; + const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT - BOTTOM_CELL_TOOLBAR_OFFSET; + const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth; + + this._layoutInfo = { + fontInfo: state.font || null, + editorHeight: this._layoutInfo.editorHeight, + editorWidth, + outputContainerOffset, + outputTotalHeight, + totalHeight, + indicatorHeight, + bottomToolbarOffset, + layoutState: this._layoutInfo.layoutState + }; + } if (state.editorHeight || state.outputHeight) { state.totalHeight = true; @@ -131,7 +171,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod restoreEditorViewState(editorViewStates: editorCommon.ICodeEditorViewState | null, totalHeight?: number) { super.restoreEditorViewState(editorViewStates); - if (totalHeight !== undefined) { + if (totalHeight !== undefined && this._layoutInfo.layoutState !== CodeCellLayoutState.Measured) { this._layoutInfo = { fontInfo: this._layoutInfo.fontInfo, editorHeight: this._layoutInfo.editorHeight, @@ -140,51 +180,53 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod outputTotalHeight: this._layoutInfo.outputTotalHeight, totalHeight: totalHeight, indicatorHeight: this._layoutInfo.indicatorHeight, - bottomToolbarOffset: this._layoutInfo.bottomToolbarOffset + bottomToolbarOffset: this._layoutInfo.bottomToolbarOffset, + layoutState: CodeCellLayoutState.FromCache }; } } hasDynamicHeight() { - if (this.selfSizeMonitoring) { - // if there is an output rendered in the webview, it should always be false - return false; - } + // CodeCellVM always measures itself and controls its cell's height + return false; + } - if (this.outputs && this.outputs.length > 0) { - // if it contains output, it will be marked as dynamic height - // thus when it's being rendered, the list view will `probeHeight` - // inside which, we will check domNode's height directly instead of doing another `renderElement` with height undefined. - return true; - } - else { - return false; - } + firstLine(): string { + return this.getText().split('\n')[0]; } getHeight(lineHeight: number) { - if (this._layoutInfo.totalHeight === 0) { - return EDITOR_TOOLBAR_HEIGHT + EDITOR_TOP_MARGIN + this.lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING + BOTTOM_CELL_TOOLBAR_HEIGHT; + if (this._layoutInfo.layoutState === CodeCellLayoutState.Uninitialized) { + const editorHeight = this.estimateEditorHeight(lineHeight); + return this.computeTotalHeight(editorHeight, 0); } else { return this._layoutInfo.totalHeight; } } + private estimateEditorHeight(lineHeight: number | undefined = 20): number { + return this.lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + } + + private computeTotalHeight(editorHeight: number, outputsTotalHeight: number): number { + return EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + CELL_STATUSBAR_HEIGHT + outputsTotalHeight + BOTTOM_CELL_TOOLBAR_HEIGHT + CELL_BOTTOM_MARGIN; + } + /** * Text model is used for editing. */ async resolveTextModel(): Promise { - if (!this._textModel) { - const ref = await this._modelService.createModelReference(this.model.uri); - this._textModel = ref.object.textEditorModel; + if (!this.textModel) { + const ref = await this.model.resolveTextModelRef(); + this.textModel = ref.object.textEditorModel; this._register(ref); - this._register(this._textModel.onDidChangeContent(() => { + this._register(this.textModel.onDidChangeContent(() => { this.editState = CellEditState.Editing; this._onDidChangeState.fire({ contentChanged: true }); })); } - return this._textModel; + return this.textModel; } onDeselect() { @@ -198,19 +240,23 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod this._outputCollection[index] = height; this._ensureOutputsTop(); - this._outputsTop!.changeValue(index, height); - this.layoutChange({ outputHeight: true }); + if (this._outputsTop!.changeValue(index, height)) { + this.layoutChange({ outputHeight: true }); + } } - getOutputOffset(index: number): number { + getOutputOffsetInContainer(index: number) { this._ensureOutputsTop(); if (index >= this._outputCollection.length) { throw new Error('Output index out of range!'); } - const offset = this._outputsTop!.getAccumulatedValue(index - 1); - return this.layoutInfo.outputContainerOffset + offset; + return this._outputsTop!.getAccumulatedValue(index - 1); + } + + getOutputOffset(index: number): number { + return this.layoutInfo.outputContainerOffset + this.getOutputOffsetInContainer(index); } spliceOutputHeights(start: number, deleteCnt: number, heights: number[]) { @@ -243,8 +289,8 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod private readonly _hasFindResult = this._register(new Emitter()); public readonly hasFindResult: Event = this._hasFindResult.event; - startFind(value: string): CellFindMatch | null { - const matches = super.cellStartFind(value); + startFind(value: string, options: INotebookSearchOptions): CellFindMatch | null { + const matches = super.cellStartFind(value, options); if (matches === null) { return null; @@ -255,4 +301,8 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod matches }; } + + dispose() { + super.dispose(); + } } diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher.ts index 6b45a999f89..8700ba68862 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher.ts @@ -54,7 +54,7 @@ export class NotebookEventDispatcher { emit(events: NotebookViewEvent[]) { for (let i = 0, len = events.length; i < len; i++) { - let e = events[i]; + const e = events[i]; switch (e.type) { case NotebookViewEventType.LayoutChanged: diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts index 05772e1ec8b..9c1dc125163 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts @@ -3,24 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import { Emitter, Event } from 'vs/base/common/event'; import * as UUID from 'vs/base/common/uuid'; import * as editorCommon from 'vs/editor/common/editorCommon'; import * as model from 'vs/editor/common/model'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CELL_RUN_GUTTER } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFindMatch, ICellViewModel, MarkdownCellLayoutChangeEvent, MarkdownCellLayoutInfo, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CELL_STATUSBAR_HEIGHT, CELL_TOP_MARGIN, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, BOTTOM_CELL_TOOLBAR_OFFSET, COLLAPSED_INDICATOR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; +import { CellFindMatch, ICellViewModel, MarkdownCellLayoutChangeEvent, MarkdownCellLayoutInfo, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { MarkdownRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer'; import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel'; import { EditorFoldingStateDelegate } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookEventDispatcher, NotebookCellStateChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; export class MarkdownCellViewModel extends BaseCellViewModel implements ICellViewModel { - cellKind: CellKind.Markdown = CellKind.Markdown; - private _mdRenderer: MarkdownRenderer | null = null; + readonly cellKind = CellKind.Markdown; private _html: HTMLElement | null = null; private _layoutInfo: MarkdownCellLayoutInfo; @@ -28,14 +26,32 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie return this._layoutInfo; } - set totalHeight(newHeight: number) { - this.layoutChange({ totalHeight: newHeight }); + set renderedMarkdownHeight(newHeight: number) { + const newTotalHeight = newHeight + BOTTOM_CELL_TOOLBAR_HEIGHT; + this.totalHeight = newTotalHeight; } - get totalHeight() { + private set totalHeight(newHeight: number) { + if (newHeight !== this.layoutInfo.totalHeight) { + this.layoutChange({ totalHeight: newHeight }); + } + } + + private get totalHeight() { throw new Error('MarkdownCellViewModel.totalHeight is write only'); } + private _editorHeight = 0; + set editorHeight(newHeight: number) { + this._editorHeight = newHeight; + + this.totalHeight = this._editorHeight + CELL_TOP_MARGIN + CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_HEIGHT + CELL_STATUSBAR_HEIGHT; + } + + get editorHeight() { + throw new Error('MarkdownCellViewModel.editorHeight is write only'); + } + protected readonly _onDidChangeLayout = new Emitter(); readonly onDidChangeLayout = this._onDidChangeLayout.event; @@ -45,16 +61,16 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie constructor( readonly viewType: string, - readonly notebookHandle: number, readonly model: NotebookCellTextModel, initialNotebookLayoutInfo: NotebookLayoutInfo | null, readonly foldingDelegate: EditorFoldingStateDelegate, readonly eventDispatcher: NotebookEventDispatcher, - @IInstantiationService private readonly _instaService: IInstantiationService, - @ITextModelService private readonly _modelService: ITextModelService) { - super(viewType, notebookHandle, model, UUID.generateUuid()); + private readonly _mdRenderer: MarkdownRenderer + ) { + super(viewType, model, UUID.generateUuid()); this._layoutInfo = { + editorHeight: 0, fontInfo: initialNotebookLayoutInfo?.fontInfo || null, editorWidth: initialNotebookLayoutInfo?.width ? this.computeEditorWidth(initialNotebookLayoutInfo.width) : 0, bottomToolbarOffset: BOTTOM_CELL_TOOLBAR_HEIGHT, @@ -71,19 +87,36 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie } private computeEditorWidth(outerWidth: number) { - return outerWidth - (CELL_MARGIN * 2) - CELL_RUN_GUTTER; + return outerWidth - (CELL_MARGIN * 2) - CODE_CELL_LEFT_MARGIN; } layoutChange(state: MarkdownCellLayoutChangeEvent) { // recompute - const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo.editorWidth; - this._layoutInfo = { - fontInfo: state.font || this._layoutInfo.fontInfo, - editorWidth, - bottomToolbarOffset: BOTTOM_CELL_TOOLBAR_HEIGHT, - totalHeight: state.totalHeight === undefined ? this._layoutInfo.totalHeight : state.totalHeight - }; + if (!this.metadata?.inputCollapsed) { + const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo.editorWidth; + const totalHeight = state.totalHeight === undefined ? this._layoutInfo.totalHeight : state.totalHeight; + + this._layoutInfo = { + fontInfo: state.font || this._layoutInfo.fontInfo, + editorWidth, + editorHeight: this._editorHeight, + bottomToolbarOffset: totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT - BOTTOM_CELL_TOOLBAR_OFFSET, + totalHeight + }; + } else { + const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo.editorWidth; + const totalHeight = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT + BOTTOM_CELL_TOOLBAR_HEIGHT + CELL_BOTTOM_MARGIN; + state.totalHeight = totalHeight; + + this._layoutInfo = { + fontInfo: state.font || this._layoutInfo.fontInfo, + editorWidth, + editorHeight: this._editorHeight, + bottomToolbarOffset: totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT - BOTTOM_CELL_TOOLBAR_OFFSET, + totalHeight + }; + } this._onDidChangeLayout.fire(state); } @@ -95,13 +128,15 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie fontInfo: this._layoutInfo.fontInfo, editorWidth: this._layoutInfo.editorWidth, bottomToolbarOffset: this._layoutInfo.bottomToolbarOffset, - totalHeight: totalHeight + totalHeight: totalHeight, + editorHeight: this._editorHeight }; + this.layoutChange({}); } } hasDynamicHeight() { - return true; + return false; } getHeight(lineHeight: number) { @@ -121,42 +156,48 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie if (this._html) { return this._html; } - let renderer = this.getMarkdownRenderer(); - this._html = renderer.render({ value: this.getText(), isTrusted: true }).element; + const renderer = this.getMarkdownRenderer(); + const text = this.getText(); + + if (text.length === 0) { + const el = document.createElement('p'); + el.className = 'emptyMarkdownPlaceholder'; + el.innerText = nls.localize('notebook.emptyMarkdownPlaceholder', "Empty markdown cell, double click or press enter to edit."); + this._html = el; + } else { + this._html = renderer.render({ value: this.getText(), isTrusted: true }).element; + } + return this._html; } return null; } async resolveTextModel(): Promise { - if (!this._textModel) { - const ref = await this._modelService.createModelReference(this.model.uri); - this._textModel = ref.object.textEditorModel; + if (!this.textModel) { + const ref = await this.model.resolveTextModelRef(); + this.textModel = ref.object.textEditorModel; this._register(ref); - this._register(this._textModel.onDidChangeContent(() => { + this._register(this.textModel.onDidChangeContent(() => { this._html = null; this._onDidChangeState.fire({ contentChanged: true }); })); } - return this._textModel; + return this.textModel; } onDeselect() { - this.editState = CellEditState.Preview; } getMarkdownRenderer() { - if (!this._mdRenderer) { - this._mdRenderer = this._instaService.createInstance(MarkdownRenderer); - } return this._mdRenderer; } private readonly _hasFindResult = this._register(new Emitter()); public readonly hasFindResult: Event = this._hasFindResult.event; - startFind(value: string): CellFindMatch | null { - const matches = super.cellStartFind(value); + startFind(value: string, options: INotebookSearchOptions): CellFindMatch | null { + const matches = super.cellStartFind(value, options); if (matches === null) { return null; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts index 5b56bce0939..057122d125b 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; @@ -12,22 +11,27 @@ import { URI } from 'vs/base/common/uri'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { Range } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness, IReadonlyTextBuffer } from 'vs/editor/common/model'; import { IntervalNode, IntervalTree } from 'vs/editor/common/model/intervalTree'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { WorkspaceTextEdit } from 'vs/editor/common/modes'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; -import { CellEditState, CellFindMatch, ICellRange, ICellViewModel, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { DeleteCellEdit, InsertCellEdit, MoveCellEdit, SpliceCellsEdit } from 'vs/workbench/contrib/notebook/browser/viewModel/cellEdit'; +import { CellEditState, CellFindMatch, ICellRange, ICellViewModel, NotebookLayoutInfo, IEditableCellViewModel, INotebookDeltaDecoration } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookEventDispatcher, NotebookMetadataChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { CellFoldingState, EditorFoldingStateDelegate } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, NotebookCellMetadata, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { FoldingRegions } from 'vs/editor/contrib/folding/foldingRanges'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; +import { MarkdownRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer'; +import { dirname } from 'vs/base/common/resources'; +import { IPosition, Position } from 'vs/editor/common/core/position'; +import { SplitCellEdit, JoinCellEdit } from 'vs/workbench/contrib/notebook/browser/viewModel/cellEdit'; +import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel'; +import { PieceTreeTextBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer'; export interface INotebookEditorViewState { editingCells: { [key: number]: boolean }; @@ -37,7 +41,7 @@ export interface INotebookEditorViewState { scrollPosition?: { left: number; top: number; }; focus?: number; editorFocused?: boolean; - contributionsState?: { [id: string]: any }; + contributionsState?: { [id: string]: unknown }; } export interface ICellModelDecorations { @@ -149,16 +153,6 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD private _viewCells: CellViewModel[] = []; private _handleToViewCellMapping = new Map(); - private _currentTokenSource: CancellationTokenSource | undefined; - - get currentTokenSource(): CancellationTokenSource | undefined { - return this._currentTokenSource; - } - - set currentTokenSource(v: CancellationTokenSource | undefined) { - this._currentTokenSource = v; - } - get viewCells(): ICellViewModel[] { return this._viewCells; } @@ -195,7 +189,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD return this._notebook.metadata; } - private readonly _onDidChangeViewCells = new Emitter(); + private readonly _onDidChangeViewCells = this._register(new Emitter()); get onDidChangeViewCells(): Event { return this._onDidChangeViewCells.event; } private _lastNotebookEditResource: URI[] = []; @@ -211,7 +205,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD return this._layoutInfo; } - private readonly _onDidChangeSelection = new Emitter(); + private readonly _onDidChangeSelection = this._register(new Emitter()); get onDidChangeSelection(): Event { return this._onDidChangeSelection.event; } private _selections: number[] = []; @@ -238,15 +232,22 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD public readonly id: string; private _foldingRanges: FoldingRegions | null = null; private _hiddenRanges: ICellRange[] = []; + private _focused: boolean = true; + + get focused() { + return this._focused; + } + + private _decorationIdToCellMap = new Map(); constructor( public viewType: string, private _notebook: NotebookTextModel, readonly eventDispatcher: NotebookEventDispatcher, private _layoutInfo: NotebookLayoutInfo | null, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IBulkEditService private readonly bulkEditService: IBulkEditService, - @IUndoRedoService private readonly undoService: IUndoRedoService + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IBulkEditService private readonly _bulkEditService: IBulkEditService, + @IUndoRedoService private readonly _undoService: IUndoRedoService ) { super(); @@ -255,23 +256,20 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD this._instanceId = strings.singleLetterHash(MODEL_ID); this._register(this._notebook.onDidChangeCells(e => { - const diffs = e.map(splice => { + const diffs = e.splices.map(splice => { return [splice[0], splice[1], splice[2].map(cell => { - return createCellViewModel(this.instantiationService, this, cell as NotebookCellTextModel); + return createCellViewModel(this._instantiationService, this, cell as NotebookCellTextModel); })] as [number, number, CellViewModel[]]; }); - const undoDiff = diffs.map(diff => { - const deletedCells = this.viewCells.slice(diff[0], diff[0] + diff[1]); - - return [diff[0], deletedCells, diff[2]] as [number, CellViewModel[], CellViewModel[]]; - }); - diffs.reverse().forEach(diff => { const deletedCells = this._viewCells.splice(diff[0], diff[1], ...diff[2]); + this._decorationsTree.acceptReplace(diff[0], diff[1], diff[2].length, true); deletedCells.forEach(cell => { this._handleToViewCellMapping.delete(cell.handle); + // dispsoe the cell to release ref to the cell text document + cell.dispose(); }); diff[2].forEach(cell => { @@ -281,7 +279,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD }); this._onDidChangeViewCells.fire({ - synchronous: true, + synchronous: e.synchronous, splices: diffs }); @@ -311,12 +309,6 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD } } - this.undoService.pushElement(new SpliceCellsEdit(this.uri, undoDiff, { - insertCell: this._insertCellDelegate.bind(this), - deleteCell: this._deleteCellDelegate.bind(this), - setSelections: this._setSelectionsDelegate.bind(this) - }, this.selectionHandles, endSelectionHandles)); - this.selectionHandles = endSelectionHandles; })); @@ -324,6 +316,13 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD this.eventDispatcher.emit([new NotebookMetadataChangedEvent(e)]); })); + this._register(this._notebook.emitSelections(selections => { + // text model emit selection change (for example, undo/redo) + // we should update the selection handle wisely + // TODO, if the editor is note selected, undo/redo should not change the focused element selection + this.updateSelectionsFromEdits(selections); + })); + this._register(this.eventDispatcher.onDidChangeLayout((e) => { this._layoutInfo = e.value; @@ -341,7 +340,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD })); this._viewCells = this._notebook!.cells.map(cell => { - return createCellViewModel(this.instantiationService, this, cell); + return createCellViewModel(this._instantiationService, this, cell); }); this._viewCells.forEach(cell => { @@ -349,6 +348,16 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD }); } + setFocus(focused: boolean) { + this._focused = focused; + } + + updateSelectionsFromEdits(selections: number[]) { + if (this._focused) { + this.selectionHandles = selections; + } + } + getFoldingStartIndex(index: number): number { if (!this._foldingRanges) { return -1; @@ -377,7 +386,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD updateFoldingRanges(ranges: FoldingRegions) { this._foldingRanges = ranges; let updateHiddenAreas = false; - let newHiddenAreas: ICellRange[] = []; + const newHiddenAreas: ICellRange[] = []; let i = 0; // index into hidden let k = 0; @@ -390,8 +399,8 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD continue; } - let startLineNumber = ranges.getStartLineNumber(i) + 1; // the first line is not hidden - let endLineNumber = ranges.getEndLineNumber(i); + const startLineNumber = ranges.getStartLineNumber(i) + 1; // the first line is not hidden + const endLineNumber = ranges.getEndLineNumber(i); if (lastCollapsedStart <= startLineNumber && endLineNumber <= lastCollapsedEnd) { // ignore ranges contained in collapsed regions continue; @@ -523,7 +532,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD const newDecorationsLen = newDecorations.length; let newDecorationIndex = 0; - let result = new Array(newDecorationsLen); + const result = new Array(newDecorationsLen); while (oldDecorationIndex < oldDecorationsLen || newDecorationIndex < newDecorationsLen) { let node: IntervalNode | null = null; @@ -578,73 +587,45 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD return result; } - private _insertCellDelegate(insertIndex: number, insertCell: CellViewModel) { - this._viewCells!.splice(insertIndex, 0, insertCell); - this._handleToViewCellMapping.set(insertCell.handle, insertCell); - this._notebook.insertNewCell(insertIndex, [insertCell.model as NotebookCellTextModel]); - this._localStore.add(insertCell); - this._onDidChangeViewCells.fire({ synchronous: true, splices: [[insertIndex, 0, [insertCell]]] }); + deltaCellDecorations(oldDecorations: string[], newDecorations: INotebookDeltaDecoration[]): string[] { + oldDecorations.forEach(id => { + const handle = this._decorationIdToCellMap.get(id); + + if (handle !== undefined) { + const cell = this.getCellByHandle(handle); + cell?.deltaCellDecorations([id], []); + } + }); + + const result: string[] = []; + + newDecorations.forEach(decoration => { + const cell = this.getCellByHandle(decoration.handle); + const ret = cell?.deltaCellDecorations([], [decoration.options]) || []; + ret.forEach(id => { + this._decorationIdToCellMap.set(id, decoration.handle); + }); + + result.push(...ret); + }); + + return result; } - private _deleteCellDelegate(deleteIndex: number) { - const deleteCell = this._viewCells[deleteIndex]; - this._viewCells.splice(deleteIndex, 1); - this._handleToViewCellMapping.delete(deleteCell.handle); - - this._notebook.removeCell(deleteIndex, 1); - this._onDidChangeViewCells.fire({ synchronous: true, splices: [[deleteIndex, 1, []]] }); + createCell(index: number, source: string | string[], language: string, type: CellKind, metadata: NotebookCellMetadata | undefined, synchronous: boolean, pushUndoStop: boolean = true) { + this._notebook.createCell2(index, source, language, type, metadata, synchronous, pushUndoStop, undefined, undefined); + // TODO, rely on createCell to be sync + return this.viewCells[index]; } - private _setSelectionsDelegate(selections: number[]) { - this.selectionHandles = selections; + insertCell(index: number, cell: NotebookCellTextModel, synchronous: boolean, pushUndoStop: boolean = true): CellViewModel { + this._notebook.insertCell2(index, cell, synchronous, pushUndoStop); + // TODO, rely on createCell to be sync // this will trigger it to synchronous update + return this._viewCells[index]; } - createCell(index: number, source: string | string[], language: string, type: CellKind, synchronous: boolean) { - const cell = this._notebook.createCellTextModel(source, language, type, [], undefined); - let newCell: CellViewModel = createCellViewModel(this.instantiationService, this, cell); - this._viewCells!.splice(index, 0, newCell); - this._handleToViewCellMapping.set(newCell.handle, newCell); - this._notebook.insertNewCell(index, [cell]); - this._localStore.add(newCell); - - this.undoService.pushElement(new InsertCellEdit(this.uri, index, newCell, { - insertCell: this._insertCellDelegate.bind(this), - deleteCell: this._deleteCellDelegate.bind(this), - setSelections: this._setSelectionsDelegate.bind(this) - }, this.selectionHandles, this.selectionHandles)); - - this._decorationsTree.acceptReplace(index, 0, 1, true); - this._onDidChangeViewCells.fire({ synchronous: synchronous, splices: [[index, 0, [newCell]]] }); - return newCell; - } - - insertCell(index: number, cell: NotebookCellTextModel, synchronous: boolean): CellViewModel { - let newCell: CellViewModel = createCellViewModel(this.instantiationService, this, cell); - this._viewCells!.splice(index, 0, newCell); - this._handleToViewCellMapping.set(newCell.handle, newCell); - - this._notebook.insertNewCell(index, [newCell.model]); - this._localStore.add(newCell); - this.undoService.pushElement(new InsertCellEdit(this.uri, index, newCell, { - insertCell: this._insertCellDelegate.bind(this), - deleteCell: this._deleteCellDelegate.bind(this), - setSelections: this._setSelectionsDelegate.bind(this) - }, this.selectionHandles, this.selectionHandles)); - - this._decorationsTree.acceptReplace(index, 0, 1, true); - this._onDidChangeViewCells.fire({ synchronous: synchronous, splices: [[index, 0, [newCell]]] }); - return newCell; - } - - deleteCell(index: number, synchronous: boolean) { + deleteCell(index: number, synchronous: boolean, pushUndoStop: boolean = true) { const primarySelectionIndex = this.selectionHandles.length ? this._viewCells.indexOf(this.getCellByHandle(this.selectionHandles[0])!) : null; - - let viewCell = this._viewCells[index]; - this._viewCells.splice(index, 1); - this._handleToViewCellMapping.delete(viewCell.handle); - - this._notebook.removeCell(index, 1); - let endSelections: number[] = []; if (this.selectionHandles.length) { const primarySelectionHandle = this.selectionHandles[0]; @@ -662,21 +643,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD } } - this.undoService.pushElement(new DeleteCellEdit(this.uri, index, viewCell, { - insertCell: this._insertCellDelegate.bind(this), - deleteCell: this._deleteCellDelegate.bind(this), - createCellViewModel: (cell: NotebookCellTextModel) => { - return createCellViewModel(this.instantiationService, this, cell); - }, - setSelections: this._setSelectionsDelegate.bind(this) - }, this.selectionHandles, endSelections)); - - this.selectionHandles = endSelections; - - this._decorationsTree.acceptReplace(index, 1, 0, true); - - this._onDidChangeViewCells.fire({ synchronous: synchronous, splices: [[index, 1, []]] }); - viewCell.dispose(); + this._notebook.deleteCell2(index, synchronous, pushUndoStop, this.selectionHandles, endSelections); } moveCellToIdx(index: number, newIdx: number, synchronous: boolean, pushedToUndoStack: boolean = true): boolean { @@ -685,28 +652,254 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD return false; } - this.viewCells.splice(index, 1); - this.viewCells!.splice(newIdx, 0, viewCell); - this._notebook.moveCellToIdx(index, newIdx); - - if (pushedToUndoStack) { - this.undoService.pushElement(new MoveCellEdit(this.uri, index, newIdx, { - moveCell: (fromIndex: number, toIndex: number) => { - this.moveCellToIdx(fromIndex, toIndex, true, false); - }, - setSelections: this._setSelectionsDelegate.bind(this) - }, this.selectionHandles, this.selectionHandles)); - } - - this.selectionHandles = this.selectionHandles; - - this._onDidChangeViewCells.fire({ synchronous: synchronous, splices: [[index, 1, []]] }); - this._onDidChangeViewCells.fire({ synchronous: synchronous, splices: [[newIdx, 0, [viewCell]]] }); - + this._notebook.moveCellToIdx2(index, newIdx, synchronous, pushedToUndoStack, undefined, [viewCell.handle]); return true; } - geteEditorViewState(): INotebookEditorViewState { + private _pushIfAbsent(positions: IPosition[], p: IPosition) { + const last = positions.length > 0 ? positions[positions.length - 1] : undefined; + if (!last || last.lineNumber !== p.lineNumber || last.column !== p.column) { + positions.push(p); + } + } + + /** + * Add split point at the beginning and the end; + * Move end of line split points to the beginning of the next line; + * Avoid duplicate split points + */ + private _splitPointsToBoundaries(splitPoints: IPosition[], textBuffer: IReadonlyTextBuffer): IPosition[] | null { + const boundaries: IPosition[] = []; + const lineCnt = textBuffer.getLineCount(); + const getLineLen = (lineNumber: number) => { + return textBuffer.getLineLength(lineNumber); + }; + + // split points need to be sorted + splitPoints = splitPoints.sort((l, r) => { + const lineDiff = l.lineNumber - r.lineNumber; + const columnDiff = l.column - r.column; + return lineDiff !== 0 ? lineDiff : columnDiff; + }); + + // eat-up any split point at the beginning, i.e. we ignore the split point at the very beginning + this._pushIfAbsent(boundaries, new Position(1, 1)); + + for (let sp of splitPoints) { + if (getLineLen(sp.lineNumber) + 1 === sp.column && sp.lineNumber < lineCnt) { + sp = new Position(sp.lineNumber + 1, 1); + } + this._pushIfAbsent(boundaries, sp); + } + + // eat-up any split point at the beginning, i.e. we ignore the split point at the very end + this._pushIfAbsent(boundaries, new Position(lineCnt, getLineLen(lineCnt) + 1)); + + // if we only have two then they describe the whole range and nothing needs to be split + return boundaries.length > 2 ? boundaries : null; + } + + private _computeCellLinesContents(cell: IEditableCellViewModel, splitPoints: IPosition[]): string[] | null { + const rangeBoundaries = this._splitPointsToBoundaries(splitPoints, cell.textBuffer); + if (!rangeBoundaries) { + return null; + } + const newLineModels: string[] = []; + for (let i = 1; i < rangeBoundaries.length; i++) { + const start = rangeBoundaries[i - 1]; + const end = rangeBoundaries[i]; + + newLineModels.push(cell.textModel.getValueInRange(new Range(start.lineNumber, start.column, end.lineNumber, end.column))); + } + + return newLineModels; + } + + async splitNotebookCell(index: number): Promise { + const cell = this.viewCells[index] as CellViewModel; + + if (!this.metadata.editable) { + return null; + } + + if (!cell.getEvaluatedMetadata(this.notebookDocument.metadata).editable) { + return null; + } + + const splitPoints = cell.getSelectionsStartPosition(); + if (splitPoints && splitPoints.length > 0) { + await cell.resolveTextModel(); + + if (!cell.hasModel()) { + return null; + } + + const newLinesContents = this._computeCellLinesContents(cell, splitPoints); + if (newLinesContents) { + const editorSelections = cell.getSelections(); + this._notebook.splitNotebookCell(index, newLinesContents, this.selectionHandles); + const language = cell.language; + const kind = cell.cellKind; + + this._undoService.pushElement(new SplitCellEdit( + this.uri, + index, + cell, + editorSelections, + newLinesContents, + language, + kind, + { + createCell: (index: number, source: string | string[], language: string, type: CellKind) => { + return this.createCell(index, source, language, type, undefined, true, false) as BaseCellViewModel; + }, + deleteCell: (index: number) => { + this.deleteCell(index, true, false); + }, + emitSelections: (selections: number[]) => { + this.updateSelectionsFromEdits(selections); + } + } + )); + } + } + + return null; + } + + async joinNotebookCells(index: number, direction: 'above' | 'below', constraint?: CellKind): Promise<{ cell: ICellViewModel, deletedCells: ICellViewModel[] } | null> { + const cell = this.viewCells[index] as CellViewModel; + + if (!this.metadata.editable) { + return null; + } + + if (!cell.getEvaluatedMetadata(this.notebookDocument.metadata).editable) { + return null; + } + + if (constraint && cell.cellKind !== constraint) { + return null; + } + + if (index === 0 && direction === 'above') { + return null; + } + + if (index === this.length - 1 && direction === 'below') { + return null; + } + + if (direction === 'above') { + const above = this.viewCells[index - 1] as CellViewModel; + if (constraint && above.cellKind !== constraint) { + return null; + } + + if (!above.getEvaluatedMetadata(this.notebookDocument.metadata).editable) { + return null; + } + + await above.resolveTextModel(); + if (!above.hasModel()) { + return null; + } + + const endSelections = [cell.handle]; + const insertContent = (cell.textModel?.getEOL() ?? '') + cell.getText(); + const aboveCellLineCount = above.textModel.getLineCount(); + const aboveCellLastLineEndColumn = above.textModel.getLineLength(aboveCellLineCount); + const editOperation = { range: new Range(aboveCellLineCount, aboveCellLastLineEndColumn + 1, aboveCellLineCount, aboveCellLastLineEndColumn + 1), text: insertContent }; + const editorSelections = above.getSelections(); + above.textModel.applyEdits([editOperation]); + const inverseRange = PieceTreeTextBuffer._getInverseEditRange(editOperation.range, editOperation.text); + + await this.deleteCell(index, true, false); + + this._undoService.pushElement(new JoinCellEdit( + this.uri, + index, + direction, + above, + editorSelections, + inverseRange, + insertContent, + cell, + { + insertCell: (index: number, cell: NotebookCellTextModel) => { + this.insertCell(index, cell, true, false); + }, + deleteCell: (index: number) => { + this.deleteCell(index, true, false); + }, + createCellViewModel: (cell: NotebookCellTextModel) => { + return createCellViewModel(this._instantiationService, this, cell); + }, + emitSelections: (selections: number[]) => { + this.updateSelectionsFromEdits(selections); + } + }) + ); + + this.selectionHandles = endSelections; + + return { cell: above, deletedCells: [cell] }; + } else { + const below = this.viewCells[index + 1] as CellViewModel; + if (constraint && below.cellKind !== constraint) { + return null; + } + + if (!below.getEvaluatedMetadata(this.notebookDocument.metadata).editable) { + return null; + } + + await cell.resolveTextModel(); + if (!cell.hasModel()) { + return null; + } + + const insertContent = (cell.textModel?.getEOL() ?? '') + below.getText(); + + const cellLineCount = cell.textModel.getLineCount(); + const cellLastLineEndColumn = cell.textModel.getLineLength(cellLineCount); + const editOperation = { range: new Range(cellLineCount, cellLastLineEndColumn + 1, cellLineCount, cellLastLineEndColumn + 1), text: insertContent }; + const editorSelections = cell.getSelections(); + cell.textModel.applyEdits([editOperation]); + const inverseRange = PieceTreeTextBuffer._getInverseEditRange(editOperation.range, editOperation.text); + + await this.deleteCell(index + 1, true, false); + + this._undoService.pushElement(new JoinCellEdit( + this.uri, + index + 1, + direction, + cell, + editorSelections, + inverseRange, + insertContent, + below, + { + insertCell: (index: number, cell: NotebookCellTextModel) => { + this.insertCell(index, cell, true, false); + }, + deleteCell: (index: number) => { + this.deleteCell(index, true, false); + }, + createCellViewModel: (cell: NotebookCellTextModel) => { + return createCellViewModel(this._instantiationService, this, cell); + }, + emitSelections: (selections: number[]) => { + this.updateSelectionsFromEdits(selections); + } + }) + ); + + return { cell, deletedCells: [below] }; + } + } + + getEditorViewState(): INotebookEditorViewState { const editingCells: { [key: number]: boolean } = {}; this._viewCells.filter(cell => cell.editState === CellEditState.Editing).forEach(cell => editingCells[cell.model.handle] = true); const editorViewStates: { [key: number]: editorCommon.ICodeEditorViewState } = {}; @@ -741,10 +934,10 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD * Editor decorations across cells. For example, find decorations for multiple code cells * The reason that we can't completely delegate this to CodeEditorWidget is most of the time, the editors for cells are not created yet but we already have decorations for them. */ - changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): T | null { + changeModelDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): T | null { const changeAccessor: IModelDecorationsChangeAccessor = { deltaDecorations: (oldDecorations: ICellModelDecorations[], newDecorations: ICellModelDeltaDecorations[]): ICellModelDecorations[] => { - return this.deltaDecorationsImpl(oldDecorations, newDecorations); + return this._deltaModelDecorationsImpl(oldDecorations, newDecorations); } }; @@ -760,7 +953,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD return result; } - deltaDecorationsImpl(oldDecorations: ICellModelDecorations[], newDecorations: ICellModelDeltaDecorations[]): ICellModelDecorations[] { + private _deltaModelDecorationsImpl(oldDecorations: ICellModelDecorations[], newDecorations: ICellModelDeltaDecorations[]): ICellModelDecorations[] { const mapping = new Map(); oldDecorations.forEach(oldDecoration => { @@ -798,7 +991,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD const ret: ICellModelDecorations[] = []; mapping.forEach((value, ownerId) => { - const cellRet = value.cell.deltaDecorations(value.oldDecorations, value.newDecorations); + const cellRet = value.cell.deltaModelDecorations(value.oldDecorations, value.newDecorations); ret.push({ ownerId: ownerId, decorations: cellRet @@ -813,10 +1006,10 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD * Search in notebook text model * @param value */ - find(value: string): CellFindMatch[] { + find(value: string, options: INotebookSearchOptions): CellFindMatch[] { const matches: CellFindMatch[] = []; this._viewCells.forEach(cell => { - const cellMatches = cell.startFind(value); + const cellMatches = cell.startFind(value, options); if (cellMatches) { matches.push(cellMatches); } @@ -829,7 +1022,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD const viewCell = cell as CellViewModel; this._lastNotebookEditResource.push(viewCell.uri); return viewCell.resolveTextModel().then(() => { - this.bulkEditService.apply({ edits: [{ edit: { range: range, text: text }, resource: cell.uri }] }, { quotableLabel: 'Notebook Replace' }); + this._bulkEditService.apply({ edits: [{ edit: { range: range, text: text }, resource: cell.uri }] }, { quotableLabel: 'Notebook Replace' }); }); } @@ -838,7 +1031,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD return; } - let textEdits: WorkspaceTextEdit[] = []; + const textEdits: WorkspaceTextEdit[] = []; this._lastNotebookEditResource.push(matches[0].cell.uri); matches.forEach(match => { @@ -853,21 +1046,17 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD return Promise.all(matches.map(match => { return match.cell.resolveTextModel(); })).then(async () => { - this.bulkEditService.apply({ edits: textEdits }, { quotableLabel: 'Notebook Replace All' }); + this._bulkEditService.apply({ edits: textEdits }, { quotableLabel: 'Notebook Replace All' }); return; }); } - canUndo(): boolean { - return this.undoService.canUndo(this.uri); + async undo() { + await this._undoService.undo(this.uri); } - undo() { - this.undoService.undo(this.uri); - } - - redo() { - this.undoService.redo(this.uri); + async redo() { + await this._undoService.redo(this.uri); } equal(notebook: NotebookTextModel) { @@ -888,8 +1077,9 @@ export type CellViewModel = CodeCellViewModel | MarkdownCellViewModel; export function createCellViewModel(instantiationService: IInstantiationService, notebookViewModel: NotebookViewModel, cell: NotebookCellTextModel) { if (cell.cellKind === CellKind.Code) { - return instantiationService.createInstance(CodeCellViewModel, notebookViewModel.viewType, notebookViewModel.handle, cell, notebookViewModel.layoutInfo, notebookViewModel.eventDispatcher); + return instantiationService.createInstance(CodeCellViewModel, notebookViewModel.viewType, cell, notebookViewModel.layoutInfo, notebookViewModel.eventDispatcher); } else { - return instantiationService.createInstance(MarkdownCellViewModel, notebookViewModel.viewType, notebookViewModel.handle, cell, notebookViewModel.layoutInfo, notebookViewModel, notebookViewModel.eventDispatcher); + const mdRenderer = instantiationService.createInstance(MarkdownRenderer, dirname(notebookViewModel.uri)); + return instantiationService.createInstance(MarkdownCellViewModel, notebookViewModel.viewType, cell, notebookViewModel.layoutInfo, notebookViewModel, notebookViewModel.eventDispatcher, mdRenderer); } } diff --git a/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts b/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts new file mode 100644 index 00000000000..0d609102629 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts @@ -0,0 +1,184 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IResourceUndoRedoElement, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo'; +import { URI } from 'vs/base/common/uri'; +import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; + +/** + * It should not modify Undo/Redo stack + */ +export interface ITextCellEditingDelegate { + insertCell?(index: number, cell: NotebookCellTextModel): void; + deleteCell?(index: number): void; + moveCell?(fromIndex: number, toIndex: number, beforeSelections: number[] | undefined, endSelections: number[] | undefined): void; + emitSelections(selections: number[]): void; +} + + +export class InsertCellEdit implements IResourceUndoRedoElement { + type: UndoRedoElementType.Resource = UndoRedoElementType.Resource; + label: string = 'Insert Cell'; + constructor( + public resource: URI, + private insertIndex: number, + private cell: NotebookCellTextModel, + private editingDelegate: ITextCellEditingDelegate, + private beforedSelections: number[] | undefined, + private endSelections: number[] | undefined + ) { + } + + undo(): void { + if (!this.editingDelegate.deleteCell) { + throw new Error('Notebook Delete Cell not implemented for Undo/Redo'); + } + + this.editingDelegate.deleteCell(this.insertIndex); + if (this.beforedSelections) { + this.editingDelegate.emitSelections(this.beforedSelections); + } + } + redo(): void { + if (!this.editingDelegate.insertCell) { + throw new Error('Notebook Insert Cell not implemented for Undo/Redo'); + } + + this.editingDelegate.insertCell(this.insertIndex, this.cell); + if (this.endSelections) { + this.editingDelegate.emitSelections(this.endSelections); + } + } +} + +export class DeleteCellEdit implements IResourceUndoRedoElement { + type: UndoRedoElementType.Resource = UndoRedoElementType.Resource; + label: string = 'Delete Cell'; + constructor( + public resource: URI, + private insertIndex: number, + private _cell: NotebookCellTextModel, + private editingDelegate: ITextCellEditingDelegate, + private beforedSelections: number[] | undefined, + private endSelections: number[] | undefined + ) { + + // save inmem text to `ICell` + // no needed any more as the text buffer is transfered to `raw_cell` + // this._rawCell.source = [cell.getText()]; + } + + undo(): void { + if (!this.editingDelegate.insertCell) { + throw new Error('Notebook Insert Cell not implemented for Undo/Redo'); + } + + this.editingDelegate.insertCell(this.insertIndex, this._cell); + if (this.beforedSelections) { + this.editingDelegate.emitSelections(this.beforedSelections); + } + } + + redo(): void { + if (!this.editingDelegate.deleteCell) { + throw new Error('Notebook Delete Cell not implemented for Undo/Redo'); + } + + this.editingDelegate.deleteCell(this.insertIndex); + if (this.endSelections) { + this.editingDelegate.emitSelections(this.endSelections); + } + } +} + +export class MoveCellEdit implements IResourceUndoRedoElement { + type: UndoRedoElementType.Resource = UndoRedoElementType.Resource; + label: string = 'Move Cell'; + + constructor( + public resource: URI, + private fromIndex: number, + private toIndex: number, + private editingDelegate: ITextCellEditingDelegate, + private beforedSelections: number[] | undefined, + private endSelections: number[] | undefined + ) { + } + + undo(): void { + if (!this.editingDelegate.moveCell) { + throw new Error('Notebook Move Cell not implemented for Undo/Redo'); + } + + this.editingDelegate.moveCell(this.toIndex, this.fromIndex, this.endSelections, this.beforedSelections); + if (this.beforedSelections) { + this.editingDelegate.emitSelections(this.beforedSelections); + } + } + + redo(): void { + if (!this.editingDelegate.moveCell) { + throw new Error('Notebook Move Cell not implemented for Undo/Redo'); + } + + this.editingDelegate.moveCell(this.fromIndex, this.toIndex, this.beforedSelections, this.endSelections); + if (this.endSelections) { + this.editingDelegate.emitSelections(this.endSelections); + } + } +} + +export class SpliceCellsEdit implements IResourceUndoRedoElement { + type: UndoRedoElementType.Resource = UndoRedoElementType.Resource; + label: string = 'Insert Cell'; + constructor( + public resource: URI, + private diffs: [number, NotebookCellTextModel[], NotebookCellTextModel[]][], + private editingDelegate: ITextCellEditingDelegate, + private beforeHandles: number[] | undefined, + private endHandles: number[] | undefined + ) { + } + + undo(): void { + if (!this.editingDelegate.deleteCell || !this.editingDelegate.insertCell) { + throw new Error('Notebook Insert/Delete Cell not implemented for Undo/Redo'); + } + + this.diffs.forEach(diff => { + for (let i = 0; i < diff[2].length; i++) { + this.editingDelegate.deleteCell!(diff[0]); + } + + diff[1].reverse().forEach(cell => { + this.editingDelegate.insertCell!(diff[0], cell); + }); + }); + + if (this.beforeHandles) { + this.editingDelegate.emitSelections(this.beforeHandles); + } + } + + redo(): void { + if (!this.editingDelegate.deleteCell || !this.editingDelegate.insertCell) { + throw new Error('Notebook Insert/Delete Cell not implemented for Undo/Redo'); + } + + this.diffs.reverse().forEach(diff => { + for (let i = 0; i < diff[1].length; i++) { + this.editingDelegate.deleteCell!(diff[0]); + } + + diff[2].reverse().forEach(cell => { + this.editingDelegate.insertCell!(diff[0], cell); + }); + }); + + if (this.endHandles) { + this.editingDelegate.emitSelections(this.endHandles); + } + } +} diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index 4ce48fcb70a..015f62a96e3 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -4,12 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from 'vs/base/common/event'; -import { ICell, IOutput, NotebookCellOutputsSplice, CellKind, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICell, IProcessedOutput, NotebookCellOutputsSplice, CellKind, NotebookCellMetadata, NotebookDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; import { URI } from 'vs/base/common/uri'; import * as model from 'vs/editor/common/model'; import { Range } from 'vs/editor/common/core/range'; import { Disposable } from 'vs/base/common/lifecycle'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; export class NotebookCellTextModel extends Disposable implements ICell { private _onDidChangeOutputs = new Emitter(); @@ -24,9 +25,9 @@ export class NotebookCellTextModel extends Disposable implements ICell { private _onDidChangeLanguage = new Emitter(); onDidChangeLanguage: Event = this._onDidChangeLanguage.event; - private _outputs: IOutput[]; + private _outputs: IProcessedOutput[]; - get outputs(): IOutput[] { + get outputs(): IProcessedOutput[] { return this._outputs; } @@ -57,7 +58,7 @@ export class NotebookCellTextModel extends Disposable implements ICell { return this._textBuffer; } - let builder = new PieceTreeTextBufferBuilder(); + const builder = new PieceTreeTextBufferBuilder(); builder.acceptChunk(Array.isArray(this._source) ? this._source.join('\n') : this._source); const bufferFactory = builder.finish(true); this._textBuffer = bufferFactory.create(model.DefaultEndOfLine.LF); @@ -69,14 +70,16 @@ export class NotebookCellTextModel extends Disposable implements ICell { return this._textBuffer; } + constructor( readonly uri: URI, public handle: number, private _source: string | string[], private _language: string, public cellKind: CellKind, - outputs: IOutput[], - metadata: NotebookCellMetadata | undefined + outputs: IProcessedOutput[], + metadata: NotebookCellMetadata | undefined, + private readonly _modelService: ITextModelService ) { super(); this._outputs = outputs; @@ -93,6 +96,10 @@ export class NotebookCellTextModel extends Disposable implements ICell { } } + getTextLength(): number { + return this.textBuffer.getLength(); + } + getFullModelRange() { const lineCount = this.textBuffer.getLineCount(); return new Range(1, 1, lineCount, this.textBuffer.getLineLength(lineCount) + 1); @@ -105,4 +112,33 @@ export class NotebookCellTextModel extends Disposable implements ICell { this._onDidChangeOutputs.fire(splices); } + + getEvaluatedMetadata(documentMetadata: NotebookDocumentMetadata): NotebookCellMetadata { + const editable = this.metadata?.editable ?? + documentMetadata.cellEditable; + + const runnable = this.metadata?.runnable ?? + documentMetadata.cellRunnable; + + const hasExecutionOrder = this.metadata?.hasExecutionOrder ?? + documentMetadata.cellHasExecutionOrder; + + return { + ...(this.metadata || {}), + ...{ + editable, + runnable, + hasExecutionOrder + } + }; + } + + async resolveTextModelRef() { + const ref = await this._modelService.createModelReference(this.uri); + return ref; + } + + dispose() { + super.dispose(); + } } diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index f768bc5d1ce..5aaf438642f 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -3,12 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { INotebookTextModel, NotebookCellOutputsSplice, NotebookCellTextModelSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, ICellInsertEdit, NotebookCellsChangedEvent, CellKind, IOutput, notebookDocumentMetadataDefaults, diff, ICellDeleteEdit, NotebookCellsChangeType, ICellDto2 } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookTextModel, NotebookCellOutputsSplice, NotebookCellTextModelSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, ICellInsertEdit, NotebookCellsChangedEvent, CellKind, IProcessedOutput, notebookDocumentMetadataDefaults, diff, ICellDeleteEdit, NotebookCellsChangeType, ICellDto2, IMainCellDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ITextSnapshot } from 'vs/editor/common/model'; +import { IUndoRedoService, UndoRedoElementType, IUndoRedoElement, IResourceUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo'; +import { InsertCellEdit, DeleteCellEdit, MoveCellEdit, SpliceCellsEdit } from 'vs/workbench/contrib/notebook/common/model/cellEdit'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; function compareRangesUsingEnds(a: [number, number], b: [number, number]): number { if (a[1] === b[1]) { @@ -43,7 +47,8 @@ export class NotebookTextModelSnapshot implements ITextSnapshot { source: cell.getValue(), metadata: cell.metadata, cellKind: cell.cellKind, - language: cell.language + language: cell.language, + outputs: cell.outputs }; const rawStr = JSON.stringify(data); @@ -61,27 +66,77 @@ export class NotebookTextModelSnapshot implements ITextSnapshot { } +class StackOperation implements IResourceUndoRedoElement { + type: UndoRedoElementType.Resource; + + private _operations: IUndoRedoElement[] = []; + + constructor(readonly resource: URI, readonly label: string) { + this.type = UndoRedoElementType.Resource; + } + + pushEditOperation(element: IUndoRedoElement) { + this._operations.push(element); + } + + undo(): void { + this._operations.reverse().forEach(o => o.undo()); + } + redo(): void | Promise { + this._operations.forEach(o => o.redo()); + } +} + +export class NotebookOperationManager { + private _pendingStackOperation: StackOperation | null = null; + constructor(private _undoService: IUndoRedoService, private _resource: URI) { + + } + + pushStackElement(label: string) { + if (this._pendingStackOperation) { + this._undoService.pushElement(this._pendingStackOperation); + this._pendingStackOperation = null; + return; + } + + this._pendingStackOperation = new StackOperation(this._resource, label); + } + + pushEditOperation(element: IUndoRedoElement) { + if (this._pendingStackOperation) { + this._pendingStackOperation.pushEditOperation(element); + return; + } + + this._undoService.pushElement(element); + } +} + export class NotebookTextModel extends Disposable implements INotebookTextModel { - private static _cellhandlePool: number = 0; + + private _cellhandlePool: number = 0; private readonly _onWillDispose: Emitter = this._register(new Emitter()); readonly onWillDispose: Event = this._onWillDispose.event; - private readonly _onDidChangeCells = new Emitter(); - get onDidChangeCells(): Event { return this._onDidChangeCells.event; } - private _onDidModelChangeProxy = new Emitter(); - get onDidModelChange(): Event { return this._onDidModelChangeProxy.event; } - private _onDidSelectionChangeProxy = new Emitter(); + private readonly _onDidChangeCells = this._register(new Emitter<{ synchronous: boolean, splices: NotebookCellTextModelSplice[] }>()); + get onDidChangeCells() { return this._onDidChangeCells.event; } + private readonly _emitSelections = this._register(new Emitter()); + get emitSelections() { return this._emitSelections.event; } + private _onDidModelChangeProxy = this._register(new Emitter()); + get onDidModelChangeProxy(): Event { return this._onDidModelChangeProxy.event; } + private _onDidSelectionChangeProxy = this._register(new Emitter()); get onDidSelectionChange(): Event { return this._onDidSelectionChangeProxy.event; } - private _onDidChangeContent = new Emitter(); + private _onDidChangeContent = this._register(new Emitter()); onDidChangeContent: Event = this._onDidChangeContent.event; - private _onDidChangeMetadata = new Emitter(); + private _onDidChangeMetadata = this._register(new Emitter()); onDidChangeMetadata: Event = this._onDidChangeMetadata.event; private _mapping: Map = new Map(); private _cellListeners: Map = new Map(); cells: NotebookCellTextModel[]; languages: string[] = []; metadata: NotebookDocumentMetadata = notebookDocumentMetadataDefaults; - renderers = new Set(); + renderers = new Set(); private _isUntitled: boolean | undefined = undefined; private _versionId = 0; @@ -100,25 +155,47 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._onDidSelectionChangeProxy.fire(this._selections); } + private _dirty = false; + protected readonly _onDidChangeDirty = this._register(new Emitter()); + readonly onDidChangeDirty = this._onDidChangeDirty.event; + + private _operationManager: NotebookOperationManager; + constructor( public handle: number, public viewType: string, - public uri: URI + public supportBackup: boolean, + public uri: URI, + @IUndoRedoService private _undoService: IUndoRedoService, + @ITextModelService private _modelService: ITextModelService ) { super(); this.cells = []; + + this._operationManager = new NotebookOperationManager(this._undoService, uri); + } + + get isDirty() { + return this._dirty; + } + + setDirty(newState: boolean) { + if (this._dirty !== newState) { + this._dirty = newState; + this._onDidChangeDirty.fire(); + } } createCellTextModel( source: string | string[], language: string, cellKind: CellKind, - outputs: IOutput[], + outputs: IProcessedOutput[], metadata: NotebookCellMetadata | undefined ) { - const cellHandle = NotebookTextModel._cellhandlePool++; + const cellHandle = this._cellhandlePool++; const cellUri = CellUri.generate(this.uri, cellHandle); - return new NotebookCellTextModel(cellUri, cellHandle, source, language, cellKind, outputs || [], metadata); + return new NotebookCellTextModel(cellUri, cellHandle, source, language, cellKind, outputs || [], metadata, this._modelService); } initialize(cells: ICellDto2[]) { @@ -126,14 +203,32 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._versionId = 0; const mainCells = cells.map(cell => { - const cellHandle = NotebookTextModel._cellhandlePool++; + const cellHandle = this._cellhandlePool++; const cellUri = CellUri.generate(this.uri, cellHandle); - return new NotebookCellTextModel(cellUri, cellHandle, cell.source, cell.language, cell.cellKind, cell.outputs || [], cell.metadata); + return new NotebookCellTextModel(cellUri, cellHandle, cell.source, cell.language, cell.cellKind, cell.outputs || [], cell.metadata, this._modelService); }); - this.insertNewCell(0, mainCells); + + this._isUntitled = false; + + for (let i = 0; i < mainCells.length; i++) { + this._mapping.set(mainCells[i].handle, mainCells[i]); + const dirtyStateListener = mainCells[i].onDidChangeContent(() => { + this.setDirty(true); + this._onDidChangeContent.fire(); + }); + + this._cellListeners.set(mainCells[i].handle, dirtyStateListener); + } + + this.cells.splice(0, 0, ...mainCells); + this._increaseVersionId(); } - applyEdit(modelVersionId: number, rawEdits: ICellEditOperation[]): boolean { + pushStackElement(label: string) { + this._operationManager.pushStackElement(label); + } + + $applyEdit(modelVersionId: number, rawEdits: ICellEditOperation[], synchronous: boolean): boolean { if (modelVersionId !== this._versionId) { return false; } @@ -164,7 +259,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel // const edits operations = operations.sort((a, b) => { - let r = compareRangesUsingEnds([a.start, a.end], [b.start, b.end]); + const r = compareRangesUsingEnds([a.start, a.end], [b.start, b.end]); if (r === 0) { return b.sortIndex - a.sortIndex; } @@ -176,14 +271,14 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel case CellEditType.Insert: const insertEdit = operations[i] as ICellInsertEdit; const mainCells = insertEdit.cells.map(cell => { - const cellHandle = NotebookTextModel._cellhandlePool++; + const cellHandle = this._cellhandlePool++; const cellUri = CellUri.generate(this.uri, cellHandle); - return new NotebookCellTextModel(cellUri, cellHandle, cell.source, cell.language, cell.cellKind, cell.outputs || [], cell.metadata); + return new NotebookCellTextModel(cellUri, cellHandle, cell.source, cell.language, cell.cellKind, cell.outputs || [], cell.metadata, this._modelService); }); - this.insertNewCell(insertEdit.index, mainCells); + this.insertNewCell(insertEdit.index, mainCells, false); break; case CellEditType.Delete: - this.removeCell(operations[i].index, operations[i].end - operations[i].start); + this.removeCell(operations[i].index, operations[i].end - operations[i].start, false); break; } } @@ -194,10 +289,52 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel return [diff.start, diff.deleteCount, diff.toInsert] as [number, number, NotebookCellTextModel[]]; }); - this._onDidChangeCells.fire(diffs); + this._onDidModelChangeProxy.fire({ + kind: NotebookCellsChangeType.ModelChange, + versionId: this._versionId, + changes: diffs.map(diff => [diff[0], diff[1], diff[2].map(cell => ({ + handle: cell.handle, + uri: cell.uri, + source: cell.textBuffer.getLinesContent(), + eol: cell.textBuffer.getEOL(), + language: cell.language, + cellKind: cell.cellKind, + outputs: cell.outputs, + metadata: cell.metadata + }))] as [number, number, IMainCellDto[]]) + }); + + const undoDiff = diffs.map(diff => { + const deletedCells = this.cells.slice(diff[0], diff[0] + diff[1]); + + return [diff[0], deletedCells, diff[2]] as [number, NotebookCellTextModel[], NotebookCellTextModel[]]; + }); + + this._operationManager.pushEditOperation(new SpliceCellsEdit(this.uri, undoDiff, { + insertCell: this._insertCellDelegate.bind(this), + deleteCell: this._deleteCellDelegate.bind(this), + emitSelections: this._emitSelectionsDelegate.bind(this) + }, undefined, undefined)); + + this._onDidChangeCells.fire({ synchronous: synchronous, splices: diffs }); return true; } + $handleEdit(label: string | undefined, undo: () => void, redo: () => void): void { + this._operationManager.pushEditOperation({ + type: UndoRedoElementType.Resource, + resource: this.uri, + label: label ?? nls.localize('defaultEditLabel', "Edit"), + undo: async () => { + undo(); + }, + redo: async () => { + redo(); + }, + }); + this.setDirty(true); + } + createSnapshot(preserveBOM?: boolean): ITextSnapshot { return new NotebookTextModelSnapshot(this); } @@ -206,6 +343,10 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._versionId = this._versionId + 1; } + handleUnknownChange() { + this.setDirty(true); + } + updateLanguages(languages: string[]) { this.languages = languages; @@ -228,7 +369,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } } - updateRenderers(renderers: number[]) { + updateRenderers(renderers: string[]) { renderers.forEach(render => { this.renderers.add(render); }); @@ -243,42 +384,45 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this.cells = [cell]; this._mapping.set(cell.handle, cell); - let dirtyStateListener = Event.any(cell.onDidChangeContent, cell.onDidChangeOutputs)(() => { + const dirtyStateListener = cell.onDidChangeContent(() => { this._isUntitled = false; + this.setDirty(true); this._onDidChangeContent.fire(); }); this._cellListeners.set(cell.handle, dirtyStateListener); + this.setDirty(false); this._onDidChangeContent.fire(); this._onDidModelChangeProxy.fire({ kind: NotebookCellsChangeType.ModelChange, - versionId: this._versionId, changes: [ - [ + versionId: this._versionId, changes: + [[ 0, 0, [{ handle: cell.handle, uri: cell.uri, source: cell.textBuffer.getLinesContent(), + eol: cell.textBuffer.getEOL(), language: cell.language, cellKind: cell.cellKind, outputs: cell.outputs, metadata: cell.metadata }] - ] - ] + ]] }); return; } - insertNewCell(index: number, cells: NotebookCellTextModel[]): void { + insertNewCell(index: number, cells: NotebookCellTextModel[], emitToExtHost: boolean = true): void { this._isUntitled = false; for (let i = 0; i < cells.length; i++) { this._mapping.set(cells[i].handle, cells[i]); - let dirtyStateListener = Event.any(cells[i].onDidChangeContent, cells[i].onDidChangeOutputs)(() => { + const dirtyStateListener = cells[i].onDidChangeContent(() => { + this.setDirty(true); this._onDidChangeContent.fire(); }); @@ -286,54 +430,67 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } this.cells.splice(index, 0, ...cells); + this.setDirty(true); this._onDidChangeContent.fire(); + this._increaseVersionId(); - this._onDidModelChangeProxy.fire({ - kind: NotebookCellsChangeType.ModelChange, - versionId: this._versionId, changes: [ - [ - index, - 0, - cells.map(cell => ({ - handle: cell.handle, - uri: cell.uri, - source: cell.textBuffer.getLinesContent(), - language: cell.language, - cellKind: cell.cellKind, - outputs: cell.outputs, - metadata: cell.metadata - })) - ] - ] - }); + + if (emitToExtHost) { + this._onDidModelChangeProxy.fire({ + kind: NotebookCellsChangeType.ModelChange, + versionId: this._versionId, changes: + [[ + index, + 0, + cells.map(cell => ({ + handle: cell.handle, + uri: cell.uri, + source: cell.textBuffer.getLinesContent(), + eol: cell.textBuffer.getEOL(), + language: cell.language, + cellKind: cell.cellKind, + outputs: cell.outputs, + metadata: cell.metadata + })) + ]] + }); + } return; } - removeCell(index: number, count: number) { + removeCell(index: number, count: number, emitToExtHost: boolean = true) { this._isUntitled = false; for (let i = index; i < index + count; i++) { - let cell = this.cells[i]; + const cell = this.cells[i]; this._cellListeners.get(cell.handle)?.dispose(); this._cellListeners.delete(cell.handle); } this.cells.splice(index, count); + this.setDirty(true); this._onDidChangeContent.fire(); this._increaseVersionId(); - this._onDidModelChangeProxy.fire({ kind: NotebookCellsChangeType.ModelChange, versionId: this._versionId, changes: [[index, count, []]] }); + if (emitToExtHost) { + this._onDidModelChangeProxy.fire({ kind: NotebookCellsChangeType.ModelChange, versionId: this._versionId, changes: [[index, count, []]] }); + } } - moveCellToIdx(index: number, newIdx: number) { + moveCellToIdx(index: number, newIdx: number, emitToExtHost: boolean = true) { this.assertIndex(index); this.assertIndex(newIdx); const cells = this.cells.splice(index, 1); this.cells.splice(newIdx, 0, ...cells); + this.setDirty(true); + this._onDidChangeContent.fire(); this._increaseVersionId(); - this._onDidModelChangeProxy.fire({ kind: NotebookCellsChangeType.Move, versionId: this._versionId, index, newIdx }); + + if (emitToExtHost) { + this._onDidModelChangeProxy.fire({ kind: NotebookCellsChangeType.Move, versionId: this._versionId, index, newIdx }); + } } assertIndex(index: number) { @@ -344,12 +501,12 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel // TODO@rebornix should this trigger content change event? $spliceNotebookCellOutputs(cellHandle: number, splices: NotebookCellOutputsSplice[]): void { - let cell = this._mapping.get(cellHandle); + const cell = this._mapping.get(cellHandle); cell?.spliceNotebookCellOutputs(splices); } clearCellOutput(handle: number) { - let cell = this._mapping.get(handle); + const cell = this._mapping.get(handle); if (cell) { cell.spliceNotebookCellOutputs([ [0, cell.outputs.length, []] @@ -361,8 +518,8 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } changeCellLanguage(handle: number, languageId: string) { - let cell = this._mapping.get(handle); - if (cell) { + const cell = this._mapping.get(handle); + if (cell && cell.language !== languageId) { cell.language = languageId; this._increaseVersionId(); @@ -370,6 +527,19 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } } + changeCellMetadata(handle: number, newMetadata: NotebookCellMetadata) { + const cell = this._mapping.get(handle); + if (cell) { + cell.metadata = { + ...cell.metadata, + ...newMetadata + }; + + this._increaseVersionId(); + this._onDidModelChangeProxy.fire({ kind: NotebookCellsChangeType.ChangeMetadata, versionId: this._versionId, index: this.cells.indexOf(cell), metadata: newMetadata }); + } + } + clearAllCellOutputs() { this.cells.forEach(cell => { cell.spliceNotebookCellOutputs([ @@ -380,9 +550,128 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._onDidModelChangeProxy.fire({ kind: NotebookCellsChangeType.CellsClearOutput, versionId: this._versionId }); } + //#region Notebook Text Model Edit API + + private _insertCellDelegate(insertIndex: number, insertCell: NotebookCellTextModel) { + this.insertNewCell(insertIndex, [insertCell]); + this._onDidChangeCells.fire({ synchronous: true, splices: [[insertIndex, 0, [insertCell]]] }); + } + + private _deleteCellDelegate(deleteIndex: number) { + this.removeCell(deleteIndex, 1); + this._onDidChangeCells.fire({ synchronous: true, splices: [[deleteIndex, 1, []]] }); + } + + private _emitSelectionsDelegate(selections: number[]) { + this._emitSelections.fire(selections); + } + + createCell2(index: number, source: string | string[], language: string, type: CellKind, metadata: NotebookCellMetadata | undefined, synchronous: boolean, pushUndoStop: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined) { + const cell = this.createCellTextModel(source, language, type, [], metadata); + + if (pushUndoStop) { + this._operationManager.pushEditOperation(new InsertCellEdit(this.uri, index, cell, { + insertCell: this._insertCellDelegate.bind(this), + deleteCell: this._deleteCellDelegate.bind(this), + emitSelections: this._emitSelectionsDelegate.bind(this) + }, beforeSelections, endSelections)); + } + + + this.insertNewCell(index, [cell]); + + this._onDidChangeCells.fire({ synchronous, splices: [[index, 0, [cell]]] }); + + if (endSelections) { + this._emitSelections.fire(endSelections); + } + return cell; + } + + insertCell2(index: number, cell: NotebookCellTextModel, synchronous: boolean, pushUndoStop: boolean): void { + if (pushUndoStop) { + this._operationManager.pushEditOperation(new InsertCellEdit(this.uri, index, cell, { + insertCell: this._insertCellDelegate.bind(this), + deleteCell: this._deleteCellDelegate.bind(this), + emitSelections: this._emitSelectionsDelegate.bind(this) + }, undefined, undefined)); + } + + this.insertNewCell(index, [cell]); + this._onDidChangeCells.fire({ synchronous: synchronous, splices: [[index, 0, [cell]]] }); + } + + deleteCell2(index: number, synchronous: boolean, pushUndoStop: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined) { + const cell = this.cells[index]; + if (pushUndoStop) { + this._operationManager.pushEditOperation(new DeleteCellEdit(this.uri, index, cell, { + insertCell: this._insertCellDelegate.bind(this), + deleteCell: this._deleteCellDelegate.bind(this), + emitSelections: this._emitSelectionsDelegate.bind(this) + }, beforeSelections, endSelections)); + } + + this.removeCell(index, 1); + this._onDidChangeCells.fire({ synchronous: synchronous, splices: [[index, 1, []]] }); + if (endSelections) { + this._emitSelections.fire(endSelections); + } + } + + moveCellToIdx2(index: number, newIdx: number, synchronous: boolean, pushedToUndoStack: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined): boolean { + const cell = this.cells[index]; + if (pushedToUndoStack) { + this._operationManager.pushEditOperation(new MoveCellEdit(this.uri, index, newIdx, { + moveCell: (fromIndex: number, toIndex: number, beforeSelections: number[] | undefined, endSelections: number[] | undefined) => { + this.moveCellToIdx2(fromIndex, toIndex, true, false, beforeSelections, endSelections); + }, + emitSelections: this._emitSelectionsDelegate.bind(this) + }, beforeSelections, endSelections)); + } + + this.moveCellToIdx(index, newIdx); + // todo, we can't emit this change as it will create a new view model and that will hold + // a new reference to the document, thus + this._onDidChangeCells.fire({ synchronous: synchronous, splices: [[index, 1, []]] }); + this._onDidChangeCells.fire({ synchronous: synchronous, splices: [[newIdx, 0, [cell]]] }); + if (endSelections) { + this._emitSelections.fire(endSelections); + } + + return true; + } + + async splitNotebookCell(index: number, newLinesContents: string[], endSelections: number[]) { + const cell = this.cells[index]; + + const ref = await cell.resolveTextModelRef(); + const textModel = ref.object.textEditorModel; + + textModel.applyEdits([ + { range: textModel.getFullModelRange(), text: newLinesContents[0] } + ], false); + + ref.dispose(); + + // create new cells based on the new text models + const language = cell.language; + const kind = cell.cellKind; + let insertIndex = index + 1; + const newCells = []; + for (let j = 1; j < newLinesContents.length; j++, insertIndex++) { + newCells.push(this.createCell2(insertIndex, newLinesContents[j], language, kind, undefined, true, false, undefined, undefined)); + } + + if (endSelections) { + this._emitSelections.fire(endSelections); + } + } + //#endregion + dispose() { this._onWillDispose.fire(); this._cellListeners.forEach(val => val.dispose()); + this.cells.forEach(cell => cell.dispose()); super.dispose(); } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 90006f52f2a..99d94fc20f4 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -14,7 +14,10 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IEditorModel } from 'vs/platform/editor/common/editor'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { GlobPattern } from 'vs/workbench/api/common/extHost.protocol'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Schemas } from 'vs/base/common/network'; +import { IRevertOptions } from 'vs/workbench/common/editor'; +import { basename } from 'vs/base/common/path'; export enum CellKind { Markdown = 1, @@ -48,13 +51,22 @@ export const ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER = [ 'image/jpeg', ]; +export const BUILTIN_RENDERER_ID = '_builtin'; + +export enum NotebookRunState { + Running = 1, + Idle = 2 +} + export const notebookDocumentMetadataDefaults: Required = { editable: true, runnable: true, cellEditable: true, cellRunnable: true, - hasExecutionOrder: true, - displayOrder: NOTEBOOK_DISPLAY_ORDER + cellHasExecutionOrder: true, + displayOrder: NOTEBOOK_DISPLAY_ORDER, + custom: {}, + runState: NotebookRunState.Idle }; export interface NotebookDocumentMetadata { @@ -62,8 +74,10 @@ export interface NotebookDocumentMetadata { runnable: boolean; cellEditable: boolean; cellRunnable: boolean; - hasExecutionOrder: boolean; - displayOrder?: GlobPattern[]; + cellHasExecutionOrder: boolean; + displayOrder?: (string | glob.IRelativePattern)[]; + custom?: { [key: string]: unknown }; + runState?: NotebookRunState; } export enum NotebookCellRunState { @@ -76,9 +90,16 @@ export enum NotebookCellRunState { export interface NotebookCellMetadata { editable?: boolean; runnable?: boolean; + breakpointMargin?: boolean; + hasExecutionOrder?: boolean; executionOrder?: number; statusMessage?: string; runState?: NotebookCellRunState; + runStartTime?: number; + lastRunDuration?: number; + inputCollapsed?: boolean; + outputCollapsed?: boolean; + custom?: { [key: string]: unknown }; } export interface INotebookDisplayOrder { @@ -87,14 +108,35 @@ export interface INotebookDisplayOrder { } export interface INotebookMimeTypeSelector { - type: string; - subTypes?: string[]; + mimeTypes?: string[]; } export interface INotebookRendererInfo { - id: ExtensionIdentifier; + id: string; + extensionId: ExtensionIdentifier; extensionLocation: URI, - preloads: URI[] + preloads: URI[], + render(uri: URI, request: IOutputRenderRequest): Promise | undefined>; + render2(uri: URI, request: IOutputRenderRequest): Promise | undefined>; +} + +export interface INotebookKernelInfo { + id: string; + label: string, + selectors: (string | glob.IRelativePattern)[], + extension: ExtensionIdentifier; + extensionLocation: URI, + preloads: URI[]; + providerHandle?: number; + executeNotebook(viewType: string, uri: URI, handle: number | undefined): Promise; + +} + +export interface INotebookKernelInfoDto { + id: string; + label: string, + extensionLocation: URI; + preloads?: UriComponents[]; } export interface INotebookSelectors { @@ -122,12 +164,21 @@ export interface IErrorOutput { traceback?: string[]; } +export interface NotebookCellOutputMetadata { + /** + * Additional attributes of a cell metadata. + */ + custom?: { [key: string]: unknown }; +} + export interface IDisplayOutput { outputKind: CellOutputKind.Rich; /** * { mime_type: value } */ - data: { [key: string]: any; } + data: { [key: string]: unknown; } + + metadata?: NotebookCellOutputMetadata; } export enum MimeTypeRendererResolver { @@ -139,16 +190,18 @@ export enum MimeTypeRendererResolver { export interface IOrderedMimeType { mimeType: string; isResolved: boolean; - rendererId?: number; + rendererId?: string; output?: string; } export interface ITransformedDisplayOutputDto { outputKind: CellOutputKind.Rich; - data: { [key: string]: any; } + outputId: string; + data: { [key: string]: unknown; } + metadata?: NotebookCellOutputMetadata; - orderedMimeTypes: IOrderedMimeType[]; - pickedMimeTypeIndex: number; + orderedMimeTypes?: IOrderedMimeType[]; + pickedMimeTypeIndex?: number; } export interface IGenericOutput { @@ -158,14 +211,52 @@ export interface IGenericOutput { transformedOutput?: { [key: string]: IDisplayOutput }; } -export type IOutput = ITransformedDisplayOutputDto | IStreamOutput | IErrorOutput; +export type IProcessedOutput = ITransformedDisplayOutputDto | IStreamOutput | IErrorOutput; + +export type IRawOutput = IDisplayOutput | IStreamOutput | IErrorOutput; + +export interface IOutputRenderRequestOutputInfo { + index: number; + outputId: string; + handlerId: string; + mimeType: string; + output?: IRawOutput; +} + +export interface IOutputRenderRequestCellInfo { + key: T; + outputs: IOutputRenderRequestOutputInfo[]; +} + +export interface IOutputRenderRequest { + items: IOutputRenderRequestCellInfo[]; +} + +export interface IOutputRenderResponseOutputInfo { + index: number; + outputId: string; + mimeType: string; + handlerId: string; + transformedOutput: string; +} + +export interface IOutputRenderResponseCellInfo { + key: T; + outputs: IOutputRenderResponseOutputInfo[]; +} + + +export interface IOutputRenderResponse { + items: IOutputRenderResponseCellInfo[]; +} + export interface ICell { readonly uri: URI; handle: number; language: string; cellKind: CellKind; - outputs: IOutput[]; + outputs: IProcessedOutput[]; metadata?: NotebookCellMetadata; onDidChangeOutputs?: Event; onDidChangeLanguage: Event; @@ -183,13 +274,13 @@ export interface IMetadata { export interface INotebookTextModel { handle: number; viewType: string; - // metadata: IMetadata; + metadata: NotebookDocumentMetadata readonly uri: URI; readonly versionId: number; languages: string[]; cells: ICell[]; - renderers: Set; - onDidChangeCells?: Event; + renderers: Set; + onDidChangeCells?: Event<{ synchronous: boolean, splices: NotebookCellTextModelSplice[] }>; onDidChangeContent: Event; onWillDispose(listener: () => void): IDisposable; } @@ -208,16 +299,17 @@ export type NotebookCellTextModelSplice = [ export type NotebookCellOutputsSplice = [ number /* start */, number /* delete count */, - IOutput[] + IProcessedOutput[] ]; export interface IMainCellDto { handle: number; uri: UriComponents, source: string[]; + eol: string; language: string; cellKind: CellKind; - outputs: IOutput[]; + outputs: IProcessedOutput[]; metadata?: NotebookCellMetadata; } @@ -232,7 +324,15 @@ export enum NotebookCellsChangeType { Move = 2, CellClearOutput = 3, CellsClearOutput = 4, - ChangeLanguage = 5 + ChangeLanguage = 5, + Initialize = 6, + ChangeMetadata = 7 +} + +export interface NotebookCellsInitializeEvent { + readonly kind: NotebookCellsChangeType.Initialize; + readonly changes: NotebookCellsSplice2[]; + readonly versionId: number; } export interface NotebookCellsModelChangedEvent { @@ -266,7 +366,14 @@ export interface NotebookCellsChangeLanguageEvent { readonly language: string; } -export type NotebookCellsChangedEvent = NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookCellClearOutputEvent | NotebookCellsClearOutputEvent | NotebookCellsChangeLanguageEvent; +export interface NotebookCellsChangeMetadataEvent { + readonly kind: NotebookCellsChangeType.ChangeMetadata; + readonly versionId: number; + readonly index: number; + readonly metadata: NotebookCellMetadata; +} + +export type NotebookCellsChangedEvent = NotebookCellsInitializeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookCellClearOutputEvent | NotebookCellsClearOutputEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent; export enum CellEditType { Insert = 1, Delete = 2 @@ -276,7 +383,7 @@ export interface ICellDto2 { source: string | string[]; language: string; cellKind: CellKind; - outputs: IOutput[]; + outputs: IProcessedOutput[]; metadata?: NotebookCellMetadata; } @@ -309,13 +416,13 @@ export interface NotebookDataDto { export namespace CellUri { - export const scheme = 'vscode-notebook'; + export const scheme = 'vscode-notebook-cell'; + const _regex = /^\d{7,}/; export function generate(notebook: URI, handle: number): URI { return notebook.with({ - path: `${notebook.path}, cell ${handle + 1}`, - query: JSON.stringify({ cell: handle, notebook: notebook.toString() }), scheme, + fragment: `${handle.toString().padStart(7, '0')}${notebook.scheme !== Schemas.file ? notebook.scheme : ''}` }); } @@ -323,19 +430,18 @@ export namespace CellUri { if (cell.scheme !== scheme) { return undefined; } - try { - const data = <{ cell: number, notebook: string }>JSON.parse(cell.query); - return { - handle: data.cell, - notebook: URI.parse(data.notebook) - }; - } catch { + const match = _regex.exec(cell.fragment); + if (!match) { return undefined; } - } - - export function equal(a: URI, b: URI): boolean { - return a.path === b.path && a.query === b.query && a.scheme === b.scheme; + const handle = Number(match[0]); + return { + handle, + notebook: cell.with({ + scheme: cell.fragment.substr(match[0].length) || Schemas.file, + fragment: null + }) + }; } } @@ -475,7 +581,104 @@ export const NOTEBOOK_EDITOR_CURSOR_BOUNDARY = new RawContextKey<'none' | 'top' export interface INotebookEditorModel extends IEditorModel { - notebook: NotebookTextModel; + readonly onDidChangeDirty: Event; + readonly resource: URI; + readonly viewType: string; + readonly notebook: NotebookTextModel; isDirty(): boolean; + isUntitled(): boolean; save(): Promise; + saveAs(target: URI): Promise; + revert(options?: IRevertOptions | undefined): Promise; +} + +export interface INotebookTextModelBackup { + metadata: NotebookDocumentMetadata; + languages: string[]; + cells: ICellDto2[] +} + +export interface NotebookDocumentBackupData { + readonly viewType: string; + readonly name: string; + readonly backupId?: string; + readonly mtime?: number; +} + +export interface IEditor extends editorCommon.ICompositeCodeEditor { + readonly onDidChangeModel: Event; + readonly onDidFocusEditorWidget: Event; + isNotebookEditor: boolean; + uri?: URI; + textModel?: NotebookTextModel; + getId(): string; + hasFocus(): boolean; + hasModel(): boolean; +} + +export enum NotebookEditorPriority { + default = 'default', + option = 'option', +} + +export interface INotebookSearchOptions { + regex?: boolean; + wholeWord?: boolean; + caseSensitive?: boolean + wordSeparators?: string; +} + +export interface INotebookDocumentFilter { + viewType?: string; + filenamePattern?: string | glob.IRelativePattern; + excludeFileNamePattern?: string | glob.IRelativePattern; +} + +//TODO@rebornix test +export function notebookDocumentFilterMatch(filter: INotebookDocumentFilter, viewType: string, resource: URI): boolean { + if (filter.viewType === viewType) { + return true; + } + + if (filter.filenamePattern) { + if (glob.match(filter.filenamePattern, basename(resource.fsPath).toLowerCase())) { + if (filter.excludeFileNamePattern) { + if (glob.match(filter.excludeFileNamePattern, basename(resource.fsPath).toLowerCase())) { + // should exclude + + return false; + } + } + return true; + } + } + return false; +} + +export interface INotebookKernelInfoDto2 { + id: string; + label: string; + extension: ExtensionIdentifier; + extensionLocation: URI; + providerHandle?: number; + description?: string; + isPreferred?: boolean; + preloads?: UriComponents[]; +} + +export interface INotebookKernelInfo2 extends INotebookKernelInfoDto2 { + resolve(uri: URI, editorId: string, token: CancellationToken): Promise; + executeNotebookCell?(uri: URI, handle: number | undefined): Promise; + cancelNotebookCell?(uri: URI, handle: number | undefined): Promise; +} + +export interface INotebookKernelProvider { + providerExtensionId: string; + providerDescription?: string; + selector: INotebookDocumentFilter; + onDidChangeKernels: Event; + provideKernels(uri: URI, token: CancellationToken): Promise; + resolveKernel(editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise; + executeNotebook(uri: URI, kernelId: string, handle: number | undefined): Promise; + cancelNotebook(uri: URI, kernelId: string, handle: number | undefined): Promise; } diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index 823c790efbb..a0e064a2aec 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -3,44 +3,46 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import { EditorModel, IRevertOptions } from 'vs/workbench/common/editor'; import { Emitter, Event } from 'vs/base/common/event'; -import { INotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookEditorModel, NotebookDocumentBackupData } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { ResourceMap } from 'vs/base/common/map'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { URI } from 'vs/base/common/uri'; -import { IWorkingCopyService, IWorkingCopy, IWorkingCopyBackup } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { IWorkingCopyService, IWorkingCopy, IWorkingCopyBackup, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { basename } from 'vs/base/common/resources'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; -import { DefaultEndOfLine, ITextBuffer, EndOfLinePreference } from 'vs/editor/common/model'; +import { Schemas } from 'vs/base/common/network'; +import { IFileStatWithMetadata, IFileService } from 'vs/platform/files/common/files'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; export interface INotebookEditorModelManager { models: NotebookEditorModel[]; - resolve(resource: URI, viewType: string): Promise; + resolve(resource: URI, viewType: string, editorId?: string): Promise; get(resource: URI): NotebookEditorModel | undefined; } -export interface INotebookRevertOptions { +export interface INotebookLoadOptions { /** * Go to disk bypassing any cache of the model if any. */ forceReadFromDisk?: boolean; + + editorId?: string; } export class NotebookEditorModel extends EditorModel implements IWorkingCopy, INotebookEditorModel { - private _dirty = false; protected readonly _onDidChangeDirty = this._register(new Emitter()); readonly onDidChangeDirty = this._onDidChangeDirty.event; private readonly _onDidChangeContent = this._register(new Emitter()); readonly onDidChangeContent: Event = this._onDidChangeContent.event; private _notebook!: NotebookTextModel; + private _lastResolvedFileStat: IFileStatWithMetadata | undefined; get notebook() { return this._notebook; @@ -52,91 +54,118 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN return this._name; } + private _workingCopyResource: URI; + constructor( public readonly resource: URI, public readonly viewType: string, - @INotebookService private readonly notebookService: INotebookService, - @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService, - @IBackupFileService private readonly backupFileService: IBackupFileService + @INotebookService private readonly _notebookService: INotebookService, + @IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService, + @IBackupFileService private readonly _backupFileService: IBackupFileService, + @IFileService private readonly _fileService: IFileService, + @INotificationService private readonly _notificationService: INotificationService ) { super(); - this._register(this.workingCopyService.registerWorkingCopy(this)); + + const input = this; + this._workingCopyResource = resource.with({ scheme: Schemas.vscodeNotebook }); + const workingCopyAdapter = new class implements IWorkingCopy { + readonly resource = input._workingCopyResource; + get name() { return input.name; } + readonly capabilities = input.isUntitled() ? WorkingCopyCapabilities.Untitled : input.capabilities; + readonly onDidChangeDirty = input.onDidChangeDirty; + readonly onDidChangeContent = input.onDidChangeContent; + isDirty(): boolean { return input.isDirty(); } + backup(): Promise { return input.backup(); } + save(): Promise { return input.save(); } + revert(options?: IRevertOptions): Promise { return input.revert(options); } + }; + + this._register(this._workingCopyService.registerWorkingCopy(workingCopyAdapter)); } capabilities = 0; - async backup(): Promise { - return { content: this._notebook.createSnapshot(true) }; + async backup(): Promise> { + if (this._notebook.supportBackup) { + const tokenSource = new CancellationTokenSource(); + const backupId = await this._notebookService.backup(this.viewType, this.resource, tokenSource.token); + const stats = await this._resolveStats(this.resource); + + return { + meta: { + mtime: stats?.mtime || new Date().getTime(), + name: this._name, + viewType: this._notebook.viewType, + backupId: backupId + } + }; + } else { + return { + meta: { + mtime: new Date().getTime(), + name: this._name, + viewType: this._notebook.viewType + }, + content: this._notebook.createSnapshot(true) + }; + } } async revert(options?: IRevertOptions | undefined): Promise { if (options?.soft) { - await this.backupFileService.discardBackup(this.resource); + await this._backupFileService.discardBackup(this.resource); return; } await this.load({ forceReadFromDisk: true }); - this._dirty = false; + const newStats = await this._resolveStats(this.resource); + this._lastResolvedFileStat = newStats; + + this._notebook.setDirty(false); this._onDidChangeDirty.fire(); - return; } - async load(options?: INotebookRevertOptions): Promise { + async load(options?: INotebookLoadOptions): Promise { if (options?.forceReadFromDisk) { - return this.loadFromProvider(true); + return this.loadFromProvider(true, undefined, undefined); } + if (this.isResolved()) { return this; } - const backup = await this.backupFileService.resolve(this.resource); + const backup = await this._backupFileService.resolve(this._workingCopyResource); if (this.isResolved()) { return this; // Make sure meanwhile someone else did not succeed in loading } - if (backup) { - try { - return await this.loadFromBackup(backup.value.create(DefaultEndOfLine.LF)); - } catch (error) { - // this.logService.error('[text file model] load() from backup', error); // ignore error and continue to load as file below - } + return this.loadFromProvider(false, options?.editorId, backup?.meta?.backupId); + } + + private async loadFromProvider(forceReloadFromDisk: boolean, editorId: string | undefined, backupId: string | undefined) { + const notebook = await this._notebookService.resolveNotebook(this.viewType!, this.resource, forceReloadFromDisk, editorId, backupId); + this._notebook = notebook!; + const newStats = await this._resolveStats(this.resource); + this._lastResolvedFileStat = newStats; + + this._register(this._notebook); + + this._name = basename(this._notebook!.uri); + + this._register(this._notebook.onDidChangeContent(() => { + this._onDidChangeContent.fire(); + })); + this._register(this._notebook.onDidChangeDirty(() => { + this._onDidChangeDirty.fire(); + })); + + if (backupId) { + await this._backupFileService.discardBackup(this._workingCopyResource); + this._notebook.setDirty(true); } - return this.loadFromProvider(false); - } - - private async loadFromBackup(content: ITextBuffer): Promise { - const fullRange = content.getRangeAt(0, content.getLength()); - const data = JSON.parse(content.getValueInRange(fullRange, EndOfLinePreference.LF)); - - const notebook = await this.notebookService.createNotebookFromBackup(this.viewType!, this.resource, data.metadata, data.languages, data.cells); - this._notebook = notebook!; - - this._name = basename(this._notebook!.uri); - - this._register(this._notebook.onDidChangeContent(() => { - this.setDirty(true); - this._onDidChangeContent.fire(); - })); - - await this.backupFileService.discardBackup(this.resource); - this.setDirty(true); - - return this; - } - - private async loadFromProvider(forceReloadFromDisk: boolean) { - const notebook = await this.notebookService.resolveNotebook(this.viewType!, this.resource, forceReloadFromDisk); - this._notebook = notebook!; - - this._name = basename(this._notebook!.uri); - - this._register(this._notebook.onDidChangeContent(() => { - this.setDirty(true); - this._onDidChangeContent.fire(); - })); - return this; } @@ -144,146 +173,98 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN return !!this._notebook; } - setDirty(newState: boolean) { - if (this._dirty !== newState) { - this._dirty = newState; - this._onDidChangeDirty.fire(); - } + isDirty() { + return this._notebook?.isDirty; } - isDirty() { - return this._dirty; + isUntitled() { + return this.resource.scheme === Schemas.untitled; + } + + private async _assertStat() { + const stats = await this._resolveStats(this.resource); + if (this._lastResolvedFileStat && stats && stats.mtime > this._lastResolvedFileStat.mtime) { + return new Promise<'overwrite' | 'revert' | 'none'>(resolve => { + const handle = this._notificationService.prompt( + Severity.Info, + nls.localize('notebook.staleSaveError', "The content of the file is newer. Please revert your version with the file contents or overwrite the content of the file with your changes"), + [{ + label: nls.localize('notebook.staleSaveError.revert', "Revert"), + run: () => { + resolve('revert'); + } + }, { + label: nls.localize('notebook.staleSaveError.overwrite.', "Overwrite"), + run: () => { + resolve('overwrite'); + } + }], + { sticky: true } + ); + + Event.once(handle.onDidClose)(() => { + resolve('none'); + }); + }); + } + + return 'overwrite'; } async save(): Promise { + const result = await this._assertStat(); + if (result === 'none') { + return false; + } + + if (result === 'revert') { + await this.revert(); + return true; + } + const tokenSource = new CancellationTokenSource(); - await this.notebookService.save(this.notebook.viewType, this.notebook.uri, tokenSource.token); - this._dirty = false; - this._onDidChangeDirty.fire(); + await this._notebookService.save(this.notebook.viewType, this.notebook.uri, tokenSource.token); + const newStats = await this._resolveStats(this.resource); + this._lastResolvedFileStat = newStats; + this._notebook.setDirty(false); return true; } async saveAs(targetResource: URI): Promise { + const result = await this._assertStat(); + + if (result === 'none') { + return false; + } + + if (result === 'revert') { + await this.revert(); + return true; + } + const tokenSource = new CancellationTokenSource(); - await this.notebookService.saveAs(this.notebook.viewType, this.notebook.uri, targetResource, tokenSource.token); - this._dirty = false; - this._onDidChangeDirty.fire(); + await this._notebookService.saveAs(this.notebook.viewType, this.notebook.uri, targetResource, tokenSource.token); + const newStats = await this._resolveStats(this.resource); + this._lastResolvedFileStat = newStats; + this._notebook.setDirty(false); return true; } -} -export class NotebookEditorModelManager extends Disposable implements INotebookEditorModelManager { - - private readonly mapResourceToModel = new ResourceMap(); - private readonly mapResourceToModelListeners = new ResourceMap(); - private readonly mapResourceToDisposeListener = new ResourceMap(); - private readonly mapResourceToPendingModelLoaders = new ResourceMap>(); - - // private readonly modelLoadQueue = this._register(new ResourceQueue()); - - get models(): NotebookEditorModel[] { - return [...this.mapResourceToModel.values()]; - } - constructor( - @IInstantiationService readonly instantiationService: IInstantiationService - ) { - super(); - } - - async resolve(resource: URI, viewType: string): Promise { - // Return early if model is currently being loaded - const pendingLoad = this.mapResourceToPendingModelLoaders.get(resource); - if (pendingLoad) { - return pendingLoad; + private async _resolveStats(resource: URI) { + if (resource.scheme === Schemas.untitled) { + return undefined; } - let modelPromise: Promise; - let model = this.get(resource); - // let didCreateModel = false; - - // Model exists - if (model) { - // if (options?.reload) { - // } else { - modelPromise = Promise.resolve(model); - // } - } - - // Model does not exist - else { - // didCreateModel = true; - const newModel = model = this.instantiationService.createInstance(NotebookEditorModel, resource, viewType); - modelPromise = model.load(); - - this.registerModel(newModel); - } - - // Store pending loads to avoid race conditions - this.mapResourceToPendingModelLoaders.set(resource, modelPromise); - - // Make known to manager (if not already known) - this.add(resource, model); - - // dispose and bind new listeners - try { - const resolvedModel = await modelPromise; - - // Remove from pending loads - this.mapResourceToPendingModelLoaders.delete(resource); - return resolvedModel; - } catch (error) { - // Free resources of this invalid model - if (model) { - model.dispose(); - } - - // Remove from pending loads - this.mapResourceToPendingModelLoaders.delete(resource); - - throw error; + const newStats = await this._fileService.resolve(this.resource, { resolveMetadata: true }); + return newStats; + } catch (e) { + return undefined; } - } - - add(resource: URI, model: NotebookEditorModel): void { - const knownModel = this.mapResourceToModel.get(resource); - if (knownModel === model) { - return; // already cached - } - - // dispose any previously stored dispose listener for this resource - const disposeListener = this.mapResourceToDisposeListener.get(resource); - if (disposeListener) { - disposeListener.dispose(); - } - - // store in cache but remove when model gets disposed - this.mapResourceToModel.set(resource, model); - this.mapResourceToDisposeListener.set(resource, model.onDispose(() => this.remove(resource))); - } - - remove(resource: URI): void { - this.mapResourceToModel.delete(resource); - - const disposeListener = this.mapResourceToDisposeListener.get(resource); - if (disposeListener) { - dispose(disposeListener); - this.mapResourceToDisposeListener.delete(resource); - } - - const modelListener = this.mapResourceToModelListeners.get(resource); - if (modelListener) { - dispose(modelListener); - this.mapResourceToModelListeners.delete(resource); - } - } - - - private registerModel(model: NotebookEditorModel): void { } - get(resource: URI): NotebookEditorModel | undefined { - return this.mapResourceToModel.get(resource); + dispose() { + super.dispose(); } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts new file mode 100644 index 00000000000..cedfaeef088 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { URI } from 'vs/base/common/uri'; +import { INotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel'; +import { IReference, ReferenceCollection } from 'vs/base/common/lifecycle'; +import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; +import { ILogService } from 'vs/platform/log/common/log'; + +export const INotebookEditorModelResolverService = createDecorator('INotebookModelResolverService'); + +export interface INotebookEditorModelResolverService { + readonly _serviceBrand: undefined; + resolve(resource: URI, viewType: string, editorId?: string): Promise>; +} + + +export class NotebookModelReferenceCollection extends ReferenceCollection> { + + constructor( + @IInstantiationService readonly _instantiationService: IInstantiationService, + @INotebookService private readonly _notebookService: INotebookService, + @ILogService private readonly _logService: ILogService, + ) { + super(); + } + + protected createReferencedObject(key: string, ...args: any[]): Promise { + const [viewType, editorId] = args as [string, string | undefined]; + + const resource = URI.parse(key); + const model = this._instantiationService.createInstance(NotebookEditorModel, resource, viewType); + const promise = model.load({ editorId }); + return promise; + } + + protected destroyReferencedObject(_key: string, object: Promise): void { + object.then(model => { + this._notebookService.destoryNotebookDocument(model.viewType, model.notebook); + model.dispose(); + }).catch(err => { + this._logService.critical('FAILED to destory notebook', err); + }); + } +} + +export class NotebookModelResolverService implements INotebookEditorModelResolverService { + + readonly _serviceBrand: undefined; + + private readonly _data: NotebookModelReferenceCollection; + + constructor( + @IInstantiationService instantiationService: IInstantiationService + ) { + this._data = instantiationService.createInstance(NotebookModelReferenceCollection); + } + + async resolve(resource: URI, viewType: string, editorId?: string | undefined): Promise> { + const reference = this._data.acquire(resource.toString(), viewType, editorId); + const model = await reference.object; + return { + object: model, + dispose() { reference.dispose(); } + }; + } +} diff --git a/src/vs/workbench/contrib/notebook/common/notebookOutputRenderer.ts b/src/vs/workbench/contrib/notebook/common/notebookOutputRenderer.ts index 796de2f51da..b22c64145eb 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookOutputRenderer.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookOutputRenderer.ts @@ -24,7 +24,11 @@ export class NotebookOutputRendererInfo { } matches(mimeType: string) { - let matched = this.mimeTypeGlobs.find(pattern => pattern(mimeType)); - return matched; + const matched = this.mimeTypeGlobs.find(pattern => pattern(mimeType)); + if (matched) { + return true; + } + + return this.mimeTypes.find(pattern => pattern === mimeType); } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookProvider.ts b/src/vs/workbench/contrib/notebook/common/notebookProvider.ts index a1f4ca196a4..2ffcad9a909 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookProvider.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookProvider.ts @@ -5,30 +5,48 @@ import * as glob from 'vs/base/common/glob'; import { URI } from 'vs/base/common/uri'; -import { basename } from 'vs/base/common/resources'; +import { basename } from 'vs/base/common/path'; +import { INotebookKernelInfoDto, NotebookEditorPriority } from 'vs/workbench/contrib/notebook/common/notebookCommon'; export interface NotebookSelector { readonly filenamePattern?: string; readonly excludeFileNamePattern?: string; } -export class NotebookProviderInfo { +export interface NotebookEditorDescriptor { + readonly id: string; + readonly displayName: string; + readonly selector: readonly NotebookSelector[]; + readonly priority: NotebookEditorPriority; + readonly providerExtensionId?: string; + readonly providerDescription?: string; + readonly providerDisplayName: string; + readonly providerExtensionLocation: URI; + kernel?: INotebookKernelInfoDto; +} + +export class NotebookProviderInfo implements NotebookEditorDescriptor { readonly id: string; readonly displayName: string; readonly selector: readonly NotebookSelector[]; + readonly priority: NotebookEditorPriority; + // it's optional as the memento might not have it + readonly providerExtensionId?: string; + readonly providerDescription?: string; readonly providerDisplayName: string; + readonly providerExtensionLocation: URI; + kernel?: INotebookKernelInfoDto; - constructor(descriptor: { - readonly id: string; - readonly displayName: string; - readonly selector: readonly NotebookSelector[]; - readonly providerDisplayName: string; - }) { + constructor(descriptor: NotebookEditorDescriptor) { this.id = descriptor.id; this.displayName = descriptor.displayName; this.selector = descriptor.selector; + this.priority = descriptor.priority; + this.providerExtensionId = descriptor.providerExtensionId; + this.providerDescription = descriptor.providerDescription; this.providerDisplayName = descriptor.providerDisplayName; + this.providerExtensionLocation = descriptor.providerExtensionLocation; } matches(resource: URI): boolean { @@ -37,9 +55,9 @@ export class NotebookProviderInfo { static selectorMatches(selector: NotebookSelector, resource: URI): boolean { if (selector.filenamePattern) { - if (glob.match(selector.filenamePattern.toLowerCase(), basename(resource).toLowerCase())) { + if (glob.match(selector.filenamePattern.toLowerCase(), basename(resource.fsPath).toLowerCase())) { if (selector.excludeFileNamePattern) { - if (glob.match(selector.excludeFileNamePattern.toLowerCase(), basename(resource).toLowerCase())) { + if (glob.match(selector.excludeFileNamePattern.toLowerCase(), basename(resource.fsPath).toLowerCase())) { // should exclude return false; diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index 6a808bafa99..9c6d199d7ed 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -8,47 +8,87 @@ import { URI } from 'vs/base/common/uri'; import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol'; import { Event } from 'vs/base/common/event'; -import { INotebookTextModel, INotebookMimeTypeSelector, INotebookRendererInfo, NotebookDocumentMetadata, ICellDto2 } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { + INotebookTextModel, INotebookRendererInfo, INotebookKernelInfo, INotebookKernelInfoDto, + IEditor, ICellEditOperation, NotebookCellOutputsSplice, IOrderedMimeType, IProcessedOutput, INotebookKernelProvider, INotebookKernelInfo2 +} from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { CancellationToken } from 'vs/base/common/cancellation'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { INotebookEditorModelManager } from 'vs/workbench/contrib/notebook/common/notebookEditorModel'; +import { IDisposable } from 'vs/base/common/lifecycle'; + export const INotebookService = createDecorator('notebookService'); export interface IMainNotebookController { - createNotebook(viewType: string, uri: URI, forBackup: boolean, forceReload: boolean): Promise; - executeNotebook(viewType: string, uri: URI, token: CancellationToken): Promise; - onDidReceiveMessage(uri: URI, message: any): void; - executeNotebookCell(uri: URI, handle: number, token: CancellationToken): Promise; - removeNotebookDocument(notebook: INotebookTextModel): Promise; + kernel: INotebookKernelInfoDto | undefined; + supportBackup: boolean; + createNotebook(textModel: NotebookTextModel, editorId?: string, backupId?: string): Promise; + reloadNotebook(mainthreadTextModel: NotebookTextModel): Promise; + resolveNotebookEditor(viewType: string, uri: URI, editorId: string): Promise; + executeNotebookByAttachedKernel(viewType: string, uri: URI): Promise; + cancelNotebookByAttachedKernel(viewType: string, uri: URI): Promise; + onDidReceiveMessage(editorId: string, rendererType: string | undefined, message: any): void; + executeNotebookCell(uri: URI, handle: number): Promise; + cancelNotebookCell(uri: URI, handle: number): Promise; + removeNotebookDocument(uri: URI): Promise; save(uri: URI, token: CancellationToken): Promise; saveAs(uri: URI, target: URI, token: CancellationToken): Promise; + backup(uri: URI, token: CancellationToken): Promise; } export interface INotebookService { - _serviceBrand: undefined; - modelManager: INotebookEditorModelManager; + readonly _serviceBrand: undefined; canResolve(viewType: string): Promise; - onDidChangeActiveEditor: Event<{ viewType: string, uri: URI }>; + onDidChangeActiveEditor: Event; + onDidChangeVisibleEditors: Event; + onNotebookEditorAdd: Event; + onNotebookEditorsRemove: Event; + onNotebookDocumentRemove: Event; + onNotebookDocumentAdd: Event; + onNotebookDocumentSaved: Event; + onDidChangeKernels: Event; + onDidChangeNotebookActiveKernel: Event<{ uri: URI, providerHandle: number | undefined, kernelId: string | undefined }>; registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): void; unregisterNotebookProvider(viewType: string): void; - registerNotebookRenderer(handle: number, extensionData: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, preloads: URI[]): void; - unregisterNotebookRenderer(handle: number): void; - getRendererInfo(handle: number): INotebookRendererInfo | undefined; - resolveNotebook(viewType: string, uri: URI, forceReload: boolean): Promise; - createNotebookFromBackup(viewType: string, uri: URI, metadata: NotebookDocumentMetadata, languages: string[], cells: ICellDto2[]): Promise; + registerNotebookRenderer(id: string, renderer: INotebookRendererInfo): void; + unregisterNotebookRenderer(id: string): void; + transformEditsOutputs(textModel: NotebookTextModel, edits: ICellEditOperation[]): Promise; + transformSpliceOutputs(textModel: NotebookTextModel, splices: NotebookCellOutputsSplice[]): Promise; + transformSingleOutput(textModel: NotebookTextModel, output: IProcessedOutput, rendererId: string, mimeType: string): Promise; + registerNotebookKernel(kernel: INotebookKernelInfo): void; + unregisterNotebookKernel(id: string): void; + registerNotebookKernelProvider(provider: INotebookKernelProvider): IDisposable; + getContributedNotebookKernels(viewType: string, resource: URI): readonly INotebookKernelInfo[]; + getContributedNotebookKernels2(viewType: string, resource: URI, token: CancellationToken): Promise; + getRendererInfo(id: string): INotebookRendererInfo | undefined; + resolveNotebook(viewType: string, uri: URI, forceReload: boolean, editorId?: string, backupId?: string): Promise; + getNotebookTextModel(uri: URI): NotebookTextModel | undefined; executeNotebook(viewType: string, uri: URI): Promise; - executeNotebookCell(viewType: string, uri: URI, handle: number, token: CancellationToken): Promise; - + cancelNotebook(viewType: string, uri: URI): Promise; + executeNotebookCell(viewType: string, uri: URI, handle: number): Promise; + cancelNotebookCell(viewType: string, uri: URI, handle: number): Promise; + executeNotebook2(viewType: string, uri: URI, kernelId: string): Promise; + executeNotebookCell2(viewType: string, uri: URI, handle: number, kernelId: string): Promise; getContributedNotebookProviders(resource: URI): readonly NotebookProviderInfo[]; getContributedNotebookProvider(viewType: string): NotebookProviderInfo | undefined; getNotebookProviderResourceRoots(): URI[]; destoryNotebookDocument(viewType: string, notebook: INotebookTextModel): void; - updateActiveNotebookDocument(viewType: string, resource: URI): void; + updateActiveNotebookEditor(editor: IEditor | null): void; + updateVisibleNotebookEditor(editors: string[]): void; save(viewType: string, resource: URI, token: CancellationToken): Promise; saveAs(viewType: string, resource: URI, target: URI, token: CancellationToken): Promise; - onDidReceiveMessage(viewType: string, uri: URI, message: any): void; - setToCopy(items: NotebookCellTextModel[]): void; - getToCopy(): NotebookCellTextModel[] | undefined; + backup(viewType: string, uri: URI, token: CancellationToken): Promise; + onDidReceiveMessage(viewType: string, editorId: string, rendererType: string | undefined, message: unknown): void; + setToCopy(items: NotebookCellTextModel[], isCopy: boolean): void; + getToCopy(): { items: NotebookCellTextModel[], isCopy: boolean; } | undefined; + + // editor events + addNotebookEditor(editor: IEditor): void; + removeNotebookEditor(editor: IEditor): void; + getNotebookEditor(editorId: string): IEditor | undefined; + listNotebookEditors(): readonly IEditor[]; + listVisibleNotebookEditors(): readonly IEditor[]; + listNotebookDocuments(): readonly NotebookTextModel[]; + } diff --git a/src/vs/workbench/contrib/notebook/electron-browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/electron-browser/notebook.contribution.ts index 9c6734c9afe..d0e8f6b32f5 100644 --- a/src/vs/workbench/contrib/notebook/electron-browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/electron-browser/notebook.contribution.ts @@ -4,40 +4,56 @@ *--------------------------------------------------------------------------------------------*/ import { isMacintosh } from 'vs/base/common/platform'; -import { registerAction2 } from 'vs/platform/actions/common/actions'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import * as webviewCommands from 'vs/workbench/contrib/webview/electron-browser/webviewCommands'; -import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEditor'; +import { CopyAction, CutAction, PasteAction } from 'vs/editor/contrib/clipboard/clipboard'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { getActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; import { ElectronWebviewBasedWebview } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { getActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; - -function getActiveElectronBasedWebviewDelegate(accessor: ServicesAccessor): ElectronWebviewBasedWebview | undefined { +import { UndoCommand, RedoCommand } from 'vs/editor/browser/editorExtensions'; +function getFocusedElectronBasedWebviewDelegate(accessor: ServicesAccessor): ElectronWebviewBasedWebview | undefined { const editorService = accessor.get(IEditorService); const editor = getActiveNotebookEditor(editorService); + if (!editor?.hasFocus()) { + return; + } const webview = editor?.getInnerWebview(); - if (webview && webview instanceof ElectronWebviewBasedWebview) { return webview; } - return; } -function registerNotebookCommands(editorId: string): void { - const contextKeyExpr = ContextKeyExpr.and(ContextKeyExpr.equals('activeEditor', editorId), ContextKeyExpr.not('editorFocus') /* https://github.com/Microsoft/vscode/issues/58668 */)!; - - // These commands are only needed on MacOS where we have to disable the menu bar commands - if (isMacintosh) { - registerAction2(class extends webviewCommands.CopyWebviewEditorCommand { constructor() { super(contextKeyExpr, getActiveElectronBasedWebviewDelegate); } }); - registerAction2(class extends webviewCommands.PasteWebviewEditorCommand { constructor() { super(contextKeyExpr, getActiveElectronBasedWebviewDelegate); } }); - registerAction2(class extends webviewCommands.CutWebviewEditorCommand { constructor() { super(contextKeyExpr, getActiveElectronBasedWebviewDelegate); } }); - registerAction2(class extends webviewCommands.UndoWebviewEditorCommand { constructor() { super(contextKeyExpr, getActiveElectronBasedWebviewDelegate); } }); - registerAction2(class extends webviewCommands.RedoWebviewEditorCommand { constructor() { super(contextKeyExpr, getActiveElectronBasedWebviewDelegate); } }); +if (isMacintosh) { + function withWebview(accessor: ServicesAccessor, f: (webviewe: ElectronWebviewBasedWebview) => void) { + const webview = getFocusedElectronBasedWebviewDelegate(accessor); + if (webview) { + f(webview); + return true; + } + return false; } -} -registerNotebookCommands(NotebookEditor.ID); + const PRIORITY = 100; + + UndoCommand.addImplementation(PRIORITY, accessor => { + return withWebview(accessor, webview => webview.undo()); + }); + + RedoCommand.addImplementation(PRIORITY, accessor => { + return withWebview(accessor, webview => webview.redo()); + }); + + CopyAction?.addImplementation(PRIORITY, accessor => { + return withWebview(accessor, webview => webview.copy()); + }); + + PasteAction?.addImplementation(PRIORITY, accessor => { + return withWebview(accessor, webview => webview.paste()); + }); + + CutAction?.addImplementation(PRIORITY, accessor => { + return withWebview(accessor, webview => webview.cut()); + }); +} diff --git a/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts b/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts index 11f71061e37..d092389a66e 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts @@ -5,10 +5,14 @@ import * as assert from 'assert'; import { NOTEBOOK_DISPLAY_ORDER, sortMimeTypes, CellKind, diff, CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { TestCell } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; +import { TestCell, setupInstantiationService } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; import { URI } from 'vs/base/common/uri'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; suite('NotebookCommon', () => { + const instantiationService = setupInstantiationService(); + const textModelService = instantiationService.get(ITextModelService); + test('sortMimeTypes default orders', function () { const defaultDisplayOrder = NOTEBOOK_DISPLAY_ORDER; @@ -265,7 +269,7 @@ suite('NotebookCommon', () => { for (let i = 0; i < 5; i++) { cells.push( - new TestCell('notebook', i, [`var a = ${i};`], 'javascript', CellKind.Code, []) + new TestCell('notebook', i, [`var a = ${i};`], 'javascript', CellKind.Code, [], textModelService) ); } @@ -291,8 +295,8 @@ suite('NotebookCommon', () => { ] ); - const cellA = new TestCell('notebook', 6, ['var a = 6;'], 'javascript', CellKind.Code, []); - const cellB = new TestCell('notebook', 7, ['var a = 7;'], 'javascript', CellKind.Code, []); + const cellA = new TestCell('notebook', 6, ['var a = 6;'], 'javascript', CellKind.Code, [], textModelService); + const cellB = new TestCell('notebook', 7, ['var a = 7;'], 'javascript', CellKind.Code, [], textModelService); const modifiedCells = [ cells[0], @@ -327,7 +331,19 @@ suite('NotebookCommon', () => { suite('CellUri', function () { - test('parse, generate', function () { + test('parse, generate (file-scheme)', function () { + + const nb = URI.parse('foo:///bar/følder/file.nb'); + const id = 17; + + const data = CellUri.generate(nb, id); + const actual = CellUri.parse(data); + assert.ok(Boolean(actual)); + assert.equal(actual?.handle, id); + assert.equal(actual?.notebook.toString(), nb.toString()); + }); + + test('parse, generate (foo-scheme)', function () { const nb = URI.parse('foo:///bar/følder/file.nb'); const id = 17; diff --git a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts index 06df06f42de..23ffd1fb439 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts @@ -4,14 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { CellKind, CellEditType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { withTestNotebook, TestCell } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; +import { withTestNotebook, TestCell, setupInstantiationService } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; suite('NotebookTextModel', () => { - const instantiationService = new TestInstantiationService(); + const instantiationService = setupInstantiationService(); + const textModelService = instantiationService.get(ITextModelService); const blukEditService = instantiationService.get(IBulkEditService); const undoRedoService = instantiationService.stub(IUndoRedoService, () => { }); instantiationService.spy(IUndoRedoService, 'pushElement'); @@ -28,10 +29,10 @@ suite('NotebookTextModel', () => { [['var d = 4;'], 'javascript', CellKind.Code, [], { editable: false }] ], (editor, viewModel, textModel) => { - textModel.applyEdit(textModel.versionId, [ - { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [])] }, - { editType: CellEditType.Insert, index: 3, cells: [new TestCell(viewModel.viewType, 6, ['var f = 6;'], 'javascript', CellKind.Code, [])] }, - ]); + textModel.$applyEdit(textModel.versionId, [ + { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [], textModelService)] }, + { editType: CellEditType.Insert, index: 3, cells: [new TestCell(viewModel.viewType, 6, ['var f = 6;'], 'javascript', CellKind.Code, [], textModelService)] }, + ], true); assert.equal(textModel.cells.length, 6); @@ -53,10 +54,10 @@ suite('NotebookTextModel', () => { [['var d = 4;'], 'javascript', CellKind.Code, [], { editable: false }] ], (editor, viewModel, textModel) => { - textModel.applyEdit(textModel.versionId, [ - { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [])] }, - { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 6, ['var f = 6;'], 'javascript', CellKind.Code, [])] }, - ]); + textModel.$applyEdit(textModel.versionId, [ + { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [], textModelService)] }, + { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 6, ['var f = 6;'], 'javascript', CellKind.Code, [], textModelService)] }, + ], true); assert.equal(textModel.cells.length, 6); @@ -78,10 +79,10 @@ suite('NotebookTextModel', () => { [['var d = 4;'], 'javascript', CellKind.Code, [], { editable: false }] ], (editor, viewModel, textModel) => { - textModel.applyEdit(textModel.versionId, [ + textModel.$applyEdit(textModel.versionId, [ { editType: CellEditType.Delete, index: 1, count: 1 }, { editType: CellEditType.Delete, index: 3, count: 1 }, - ]); + ], true); assert.equal(textModel.cells[0].getValue(), 'var a = 1;'); assert.equal(textModel.cells[1].getValue(), 'var c = 3;'); @@ -101,10 +102,10 @@ suite('NotebookTextModel', () => { [['var d = 4;'], 'javascript', CellKind.Code, [], { editable: false }] ], (editor, viewModel, textModel) => { - textModel.applyEdit(textModel.versionId, [ + textModel.$applyEdit(textModel.versionId, [ { editType: CellEditType.Delete, index: 1, count: 1 }, - { editType: CellEditType.Insert, index: 3, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [])] }, - ]); + { editType: CellEditType.Insert, index: 3, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [], textModelService)] }, + ], true); assert.equal(textModel.cells.length, 4); @@ -126,10 +127,10 @@ suite('NotebookTextModel', () => { [['var d = 4;'], 'javascript', CellKind.Code, [], { editable: false }] ], (editor, viewModel, textModel) => { - textModel.applyEdit(textModel.versionId, [ + textModel.$applyEdit(textModel.versionId, [ { editType: CellEditType.Delete, index: 1, count: 1 }, - { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [])] }, - ]); + { editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [], textModelService)] }, + ], true); assert.equal(textModel.cells.length, 4); assert.equal(textModel.cells[0].getValue(), 'var a = 1;'); diff --git a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts index 4acc1060be1..60d7d3cfdf7 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts @@ -5,25 +5,25 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; -import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { CellKind, NotebookCellMetadata, diff } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { withTestNotebook, TestCell, NotebookEditorTestModel } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; +import { withTestNotebook, TestCell, NotebookEditorTestModel, setupInstantiationService } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { TrackedRangeStickiness } from 'vs/editor/common/model'; import { reduceCellRanges, ICellRange } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; suite('NotebookViewModel', () => { - const instantiationService = new TestInstantiationService(); + const instantiationService = setupInstantiationService(); + const textModelService = instantiationService.get(ITextModelService); const blukEditService = instantiationService.get(IBulkEditService); - const undoRedoService = instantiationService.stub(IUndoRedoService, () => { }); - instantiationService.spy(IUndoRedoService, 'pushElement'); + const undoRedoService = instantiationService.get(IUndoRedoService); test('ctor', function () { - const notebook = new NotebookTextModel(0, 'notebook', URI.parse('test')); + const notebook = new NotebookTextModel(0, 'notebook', false, URI.parse('test'), undoRedoService, textModelService); const model = new NotebookEditorTestModel(notebook); const eventDispatcher = new NotebookEventDispatcher(); const viewModel = new NotebookViewModel('notebook', model.notebook, eventDispatcher, null, instantiationService, blukEditService, undoRedoService); @@ -43,7 +43,7 @@ suite('NotebookViewModel', () => { assert.equal(viewModel.viewCells[0].metadata?.editable, true); assert.equal(viewModel.viewCells[1].metadata?.editable, false); - const cell = viewModel.insertCell(1, new TestCell(viewModel.viewType, 0, ['var c = 3;'], 'javascript', CellKind.Code, []), true); + const cell = viewModel.insertCell(1, new TestCell(viewModel.viewType, 0, ['var c = 3;'], 'javascript', CellKind.Code, [], textModelService), true); assert.equal(viewModel.viewCells.length, 3); assert.equal(viewModel.notebookDocument.cells.length, 3); assert.equal(viewModel.getCellIndex(cell), 1); @@ -126,13 +126,13 @@ suite('NotebookViewModel', () => { const lastViewCell = viewModel.viewCells[viewModel.viewCells.length - 1]; const insertIndex = viewModel.getCellIndex(firstViewCell) + 1; - const cell = viewModel.insertCell(insertIndex, new TestCell(viewModel.viewType, 3, ['var c = 3;'], 'javascript', CellKind.Code, []), true); + const cell = viewModel.insertCell(insertIndex, new TestCell(viewModel.viewType, 3, ['var c = 3;'], 'javascript', CellKind.Code, [], textModelService), true); const addedCellIndex = viewModel.getCellIndex(cell); viewModel.deleteCell(addedCellIndex, true); const secondInsertIndex = viewModel.getCellIndex(lastViewCell) + 1; - const cell2 = viewModel.insertCell(secondInsertIndex, new TestCell(viewModel.viewType, 4, ['var d = 4;'], 'javascript', CellKind.Code, []), true); + const cell2 = viewModel.insertCell(secondInsertIndex, new TestCell(viewModel.viewType, 4, ['var d = 4;'], 'javascript', CellKind.Code, [], textModelService), true); assert.equal(viewModel.viewCells.length, 3); assert.equal(viewModel.notebookDocument.cells.length, 3); @@ -154,13 +154,9 @@ suite('NotebookViewModel', () => { [['var e = 5;'], 'javascript', CellKind.Code, [], { editable: false, runnable: false }], ], (editor, viewModel) => { - viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: true, cellEditable: true, hasExecutionOrder: true }; + viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: true, cellEditable: true, cellHasExecutionOrder: true }; - const defaults = { - runState: undefined, - statusMessage: undefined, - executionOrder: undefined - }; + const defaults = { hasExecutionOrder: true }; assert.deepEqual(viewModel.viewCells[0].getEvaluatedMetadata(viewModel.metadata), { editable: true, @@ -192,7 +188,7 @@ suite('NotebookViewModel', () => { ...defaults }); - viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: false, cellEditable: true, hasExecutionOrder: true }; + viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: false, cellEditable: true, cellHasExecutionOrder: true }; assert.deepEqual(viewModel.viewCells[0].getEvaluatedMetadata(viewModel.metadata), { editable: true, @@ -224,7 +220,7 @@ suite('NotebookViewModel', () => { ...defaults }); - viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: false, cellEditable: false, hasExecutionOrder: true }; + viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: false, cellEditable: false, cellHasExecutionOrder: true }; assert.deepEqual(viewModel.viewCells[0].getEvaluatedMetadata(viewModel.metadata), { editable: false, @@ -236,14 +232,14 @@ suite('NotebookViewModel', () => { }); }); -function getVisibleCells(cells: any[], hiddenRanges: ICellRange[]) { +function getVisibleCells(cells: T[], hiddenRanges: ICellRange[]) { if (!hiddenRanges.length) { return cells; } let start = 0; let hiddenRangeIndex = 0; - let result: any[] = []; + const result: T[] = []; while (start < cells.length && hiddenRangeIndex < hiddenRanges.length) { if (start < hiddenRanges[hiddenRangeIndex].start) { @@ -262,10 +258,10 @@ function getVisibleCells(cells: any[], hiddenRanges: ICellRange[]) { } suite('NotebookViewModel Decorations', () => { - const instantiationService = new TestInstantiationService(); + const instantiationService = setupInstantiationService(); + const textModelService = instantiationService.get(ITextModelService); const blukEditService = instantiationService.get(IBulkEditService); - const undoRedoService = instantiationService.stub(IUndoRedoService, () => { }); - instantiationService.spy(IUndoRedoService, 'pushElement'); + const undoRedoService = instantiationService.get(IUndoRedoService); test('tracking range', function () { withTestNotebook( @@ -287,7 +283,7 @@ suite('NotebookViewModel Decorations', () => { end: 2, }); - viewModel.insertCell(0, new TestCell(viewModel.viewType, 5, ['var d = 6;'], 'javascript', CellKind.Code, []), true); + viewModel.insertCell(0, new TestCell(viewModel.viewType, 5, ['var d = 6;'], 'javascript', CellKind.Code, [], textModelService), true); assert.deepEqual(viewModel.getTrackedRange(trackedId!), { start: 2, @@ -301,7 +297,7 @@ suite('NotebookViewModel Decorations', () => { end: 2 }); - viewModel.insertCell(3, new TestCell(viewModel.viewType, 6, ['var d = 7;'], 'javascript', CellKind.Code, []), true); + viewModel.insertCell(3, new TestCell(viewModel.viewType, 6, ['var d = 7;'], 'javascript', CellKind.Code, [], textModelService), true); assert.deepEqual(viewModel.getTrackedRange(trackedId!), { start: 1, @@ -347,14 +343,14 @@ suite('NotebookViewModel Decorations', () => { end: 3 }); - viewModel.insertCell(5, new TestCell(viewModel.viewType, 8, ['var d = 9;'], 'javascript', CellKind.Code, []), true); + viewModel.insertCell(5, new TestCell(viewModel.viewType, 8, ['var d = 9;'], 'javascript', CellKind.Code, [], textModelService), true); assert.deepEqual(viewModel.getTrackedRange(trackedId!), { start: 1, end: 3 }); - viewModel.insertCell(4, new TestCell(viewModel.viewType, 9, ['var d = 10;'], 'javascript', CellKind.Code, []), true); + viewModel.insertCell(4, new TestCell(viewModel.viewType, 9, ['var d = 10;'], 'javascript', CellKind.Code, [], textModelService), true); assert.deepEqual(viewModel.getTrackedRange(trackedId!), { start: 1, @@ -384,10 +380,10 @@ suite('NotebookViewModel Decorations', () => { }); test('diff hidden ranges', function () { - assert.deepEqual(getVisibleCells([1, 2, 3, 4, 5], []), [1, 2, 3, 4, 5]); + assert.deepEqual(getVisibleCells([1, 2, 3, 4, 5], []), [1, 2, 3, 4, 5]); assert.deepEqual( - getVisibleCells( + getVisibleCells( [1, 2, 3, 4, 5], [{ start: 1, end: 2 }] ), @@ -395,7 +391,7 @@ suite('NotebookViewModel Decorations', () => { ); assert.deepEqual( - getVisibleCells( + getVisibleCells( [1, 2, 3, 4, 5, 6, 7, 8, 9], [ { start: 1, end: 2 }, @@ -405,7 +401,7 @@ suite('NotebookViewModel Decorations', () => { [1, 4, 7, 8, 9] ); - const original = getVisibleCells( + const original = getVisibleCells( [1, 2, 3, 4, 5, 6, 7, 8, 9], [ { start: 1, end: 2 }, @@ -413,7 +409,7 @@ suite('NotebookViewModel Decorations', () => { ] ); - const modified = getVisibleCells( + const modified = getVisibleCells( [1, 2, 3, 4, 5, 6, 7, 8, 9], [ { start: 2, end: 4 } diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index 1271f64e9ee..98503fbd0e2 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -10,7 +10,6 @@ import { URI } from 'vs/base/common/uri'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { Range } from 'vs/editor/common/core/range'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { EditorModel } from 'vs/workbench/common/editor'; import { ICellRange, ICellViewModel, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; @@ -19,9 +18,22 @@ import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/v import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, CellUri, INotebookEditorModel, IOutput, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellUri, INotebookEditorModel, IProcessedOutput, NotebookCellMetadata, INotebookKernelInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { ICompositeCodeEditor, IEditor } from 'vs/editor/common/editorCommon'; +import { NotImplementedError } from 'vs/base/common/errors'; +import { Schemas } from 'vs/base/common/network'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; +import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; + export class TestCell extends NotebookCellTextModel { constructor( public viewType: string, @@ -29,13 +41,19 @@ export class TestCell extends NotebookCellTextModel { public source: string[], language: string, cellKind: CellKind, - outputs: IOutput[] + outputs: IProcessedOutput[], + modelService: ITextModelService ) { - super(CellUri.generate(URI.parse('test:///fake/notebook'), handle), handle, source, language, cellKind, outputs, undefined); + super(CellUri.generate(URI.parse('test:///fake/notebook'), handle), handle, source, language, cellKind, outputs, undefined, modelService); } } export class TestNotebookEditor implements INotebookEditor { + private _isDisposed = false; + + get isDisposed() { + return this._isDisposed; + } get viewModel() { return undefined; @@ -43,14 +61,46 @@ export class TestNotebookEditor implements INotebookEditor { constructor( ) { } + + hideInset(output: IProcessedOutput): void { + throw new Error('Method not implemented.'); + } + + multipleKernelsAvailable: boolean = false; + onDidChangeAvailableKernels: Event = new Emitter().event; + + + uri?: URI | undefined; + textModel?: NotebookTextModel | undefined; + + hasModel(): boolean { + return true; + } + + onDidFocusEditorWidget: Event = new Emitter().event; + hasFocus(): boolean { + return true; + } + + hasOutputTextSelection() { + return false; + } + + getId(): string { + return 'notebook.testEditor'; + } + + cursorNavigationMode = false; + activeKernel: INotebookKernelInfo | undefined; + onDidChangeKernel: Event = new Emitter().event; onDidChangeActiveEditor: Event = new Emitter().event; activeCodeEditor: IEditor | undefined; getDomNode(): HTMLElement { throw new Error('Method not implemented.'); } - private _onDidChangeModel = new Emitter(); - onDidChangeModel: Event = this._onDidChangeModel.event; + private _onDidChangeModel = new Emitter(); + onDidChangeModel: Event = this._onDidChangeModel.event; getContribution(id: string): T { throw new Error('Method not implemented.'); } @@ -87,7 +137,19 @@ export class TestNotebookEditor implements INotebookEditor { isNotebookEditor = true; - postMessage(message: any): void { + postMessage(): void { + throw new Error('Method not implemented.'); + } + + toggleClassName(className: string): void { + throw new Error('Method not implemented.'); + } + + addClassName(className: string): void { + throw new Error('Method not implemented.'); + } + + removeClassName(className: string): void { throw new Error('Method not implemented.'); } @@ -99,15 +161,19 @@ export class TestNotebookEditor implements INotebookEditor { throw new Error('Method not implemented.'); } - moveCellDown(cell: CellViewModel): Promise { + moveCellDown(cell: CellViewModel): Promise { throw new Error('Method not implemented.'); } - moveCellUp(cell: CellViewModel): Promise { + moveCellUp(cell: CellViewModel): Promise { throw new Error('Method not implemented.'); } - moveCell(cell: ICellViewModel, relativeToCell: ICellViewModel, direction: 'above' | 'below'): Promise { + moveCellToIdx(cell: ICellViewModel, index: number): Promise { + throw new Error('Method not implemented.'); + } + + moveCell(cell: ICellViewModel, relativeToCell: ICellViewModel, direction: 'above' | 'below'): Promise { throw new Error('Method not implemented.'); } @@ -122,26 +188,26 @@ export class TestNotebookEditor implements INotebookEditor { setSelection(cell: CellViewModel, selection: Range): void { throw new Error('Method not implemented.'); } - revealRangeInView(cell: CellViewModel, range: Range): void { + revealRangeInViewAsync(cell: CellViewModel, range: Range): Promise { throw new Error('Method not implemented.'); } - revealRangeInCenter(cell: CellViewModel, range: Range): void { + revealRangeInCenterAsync(cell: CellViewModel, range: Range): Promise { throw new Error('Method not implemented.'); } - revealRangeInCenterIfOutsideViewport(cell: CellViewModel, range: Range): void { + revealRangeInCenterIfOutsideViewportAsync(cell: CellViewModel, range: Range): Promise { throw new Error('Method not implemented.'); } - revealLineInView(cell: CellViewModel, line: number): void { + revealLineInViewAsync(cell: CellViewModel, line: number): Promise { throw new Error('Method not implemented.'); } getLayoutInfo(): NotebookLayoutInfo { throw new Error('Method not implemented.'); } - revealLineInCenterIfOutsideViewport(cell: CellViewModel, line: number): void { + revealLineInCenterIfOutsideViewportAsync(cell: CellViewModel, line: number): Promise { throw new Error('Method not implemented.'); } - revealLineInCenter(cell: CellViewModel, line: number): void { + revealLineInCenterAsync(cell: CellViewModel, line: number): Promise { throw new Error('Method not implemented.'); } focus(): void { @@ -168,12 +234,6 @@ export class TestNotebookEditor implements INotebookEditor { deleteNotebookCell(cell: CellViewModel): Promise { throw new Error('Method not implemented.'); } - editNotebookCell(cell: CellViewModel): void { - // throw new Error('Method not implemented.'); - } - saveNotebookCell(cell: CellViewModel): void { - // throw new Error('Method not implemented.'); - } focusNotebookCell(cell: CellViewModel, focusItem: 'editor' | 'container' | 'output'): void { // throw new Error('Method not implemented.'); } @@ -185,10 +245,10 @@ export class TestNotebookEditor implements INotebookEditor { // throw new Error('Method not implemented.'); return; } - createInset(cell: CellViewModel, output: IOutput, shadowContent: string, offset: number): void { - // throw new Error('Method not implemented.'); + createInset(cell: CellViewModel, output: IProcessedOutput, shadowContent: string, offset: number): Promise { + return Promise.resolve(); } - removeInset(output: IOutput): void { + removeInset(output: IProcessedOutput): void { // throw new Error('Method not implemented.'); } triggerScroll(event: IMouseWheelEvent): void { @@ -203,9 +263,17 @@ export class TestNotebookEditor implements INotebookEditor { throw new Error('Method not implemented.'); } - changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => any): any { + changeModelDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): T | null { throw new Error('Method not implemented.'); } + + deltaCellOutputContainerClassNames(cellId: string, added: string[], removed: string[]): void { + throw new Error('Method not implemented.'); + } + + dispose() { + this._isDisposed = true; + } } // export function createTestCellViewModel(instantiationService: IInstantiationService, viewType: string, notebookHandle: number, cellhandle: number, source: string[], language: string, cellKind: CellKind, outputs: IOutput[]) { @@ -223,6 +291,14 @@ export class NotebookEditorTestModel extends EditorModel implements INotebookEdi readonly onDidChangeContent: Event = this._onDidChangeContent.event; + get viewType() { + return this._notebook.viewType; + } + + get resource() { + return this._notebook.uri; + } + get notebook() { return this._notebook; } @@ -245,6 +321,10 @@ export class NotebookEditorTestModel extends EditorModel implements INotebookEdi return this._dirty; } + isUntitled() { + return this._notebook.uri.scheme === Schemas.untitled; + } + getNotebook(): NotebookTextModel { return this._notebook; } @@ -259,14 +339,36 @@ export class NotebookEditorTestModel extends EditorModel implements INotebookEdi return false; } + + saveAs(): Promise { + throw new NotImplementedError(); + } + + revert(): Promise { + throw new NotImplementedError(); + } } -export function withTestNotebook(instantiationService: IInstantiationService, blukEditService: IBulkEditService, undoRedoService: IUndoRedoService, cells: [string[], string, CellKind, IOutput[], NotebookCellMetadata][], callback: (editor: TestNotebookEditor, viewModel: NotebookViewModel, textModel: NotebookTextModel) => void) { +export function setupInstantiationService() { + const instantiationService = new TestInstantiationService(); + + instantiationService.stub(IUndoRedoService, instantiationService.createInstance(UndoRedoService)); + instantiationService.stub(IConfigurationService, new TestConfigurationService()); + instantiationService.stub(IThemeService, new TestThemeService()); + instantiationService.stub(IModelService, instantiationService.createInstance(ModelServiceImpl)); + instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); + + return instantiationService; +} + +export function withTestNotebook(instantiationService: TestInstantiationService, blukEditService: IBulkEditService, undoRedoService: IUndoRedoService, cells: [string[], string, CellKind, IProcessedOutput[], NotebookCellMetadata][], callback: (editor: TestNotebookEditor, viewModel: NotebookViewModel, textModel: NotebookTextModel) => void) { + const textModelService = instantiationService.get(ITextModelService); + const viewType = 'notebook'; const editor = new TestNotebookEditor(); - const notebook = new NotebookTextModel(0, viewType, URI.parse('test')); + const notebook = new NotebookTextModel(0, viewType, false, URI.parse('test'), undoRedoService, textModelService); notebook.cells = cells.map((cell, index) => { - return new NotebookCellTextModel(notebook.uri, index, cell[0], cell[1], cell[2], cell[3], cell[4]); + return new NotebookCellTextModel(notebook.uri, index, cell[0], cell[1], cell[2], cell[3], cell[4], textModelService); }); const model = new NotebookEditorTestModel(notebook); const eventDispatcher = new NotebookEventDispatcher(); diff --git a/src/vs/workbench/contrib/outline/browser/outlinePane.ts b/src/vs/workbench/contrib/outline/browser/outlinePane.ts index c09de9ec1a9..a277a22ed17 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePane.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.ts @@ -4,9 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; -import { Action, IAction, RadioGroup } from 'vs/base/common/actions'; +import { Action, IAction, RadioGroup, Separator } from 'vs/base/common/actions'; import { createCancelablePromise, TimeoutTimer } from 'vs/base/common/async'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; @@ -42,7 +41,6 @@ import { OutlineConfigKeys, OutlineViewFocused, OutlineViewFiltered } from 'vs/e import { FuzzyScore } from 'vs/base/common/filters'; import { OutlineDataSource, OutlineItemComparator, OutlineSortOrder, OutlineVirtualDelegate, OutlineGroupRenderer, OutlineElementRenderer, OutlineItem, OutlineIdentityProvider, OutlineNavigationLabelProvider, OutlineFilter, OutlineAccessibilityProvider } from 'vs/editor/contrib/documentSymbols/outlineTree'; import { IDataTreeViewState } from 'vs/base/browser/ui/tree/dataTree'; -import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { basename } from 'vs/base/common/resources'; import { IDataSource } from 'vs/base/browser/ui/tree/tree'; import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService'; @@ -344,7 +342,8 @@ export class OutlinePane extends ViewPane { hideTwistiesOfChildlessElements: true, overrideStyles: { listBackground: this.getBackgroundColor() - } + }, + openOnSingleClick: true } ); @@ -410,9 +409,7 @@ export class OutlinePane extends ViewPane { getActions(): IAction[] { return [ - new Action('collapse', localize('collapse', "Collapse All"), 'explorer-action codicon-collapse-all', true, () => { - return new CollapseAction(() => this._tree, true, undefined).run(); - }) + new CollapseAction(() => this._tree, true, 'explorer-action codicon-collapse-all') ]; } @@ -560,26 +557,11 @@ export class OutlinePane extends ViewPane { // feature: reveal outline selection in editor // on change -> reveal/select defining range this._editorDisposables.add(this._tree.onDidOpen(e => { - - let [first] = e.elements; - if (!(first instanceof OutlineElement)) { + if (!(e.element instanceof OutlineElement)) { return; } - let focus = false; - let aside = false; - // todo@Joh - if (e.browserEvent) { - if (e.browserEvent.type === 'keydown') { - focus = true; - } else if (e.browserEvent.type === 'click') { - const event = new StandardMouseEvent(e.browserEvent as MouseEvent); - focus = e.browserEvent.detail === 2; - aside = (!this._tree.useAltAsMultipleSelectionModifier && event.altKey) - || (this._tree.useAltAsMultipleSelectionModifier && (event.ctrlKey || event.metaKey)); - } - } - this._revealTreeSelection(newModel, first, focus, aside); + this._revealTreeSelection(newModel, e.element, !!e.editorOptions.preserveFocus || !e.editorOptions.pinned, e.sideBySide); })); // feature: reveal editor selection in outline @@ -633,12 +615,12 @@ export class OutlinePane extends ViewPane { })); } - private async _revealTreeSelection(model: OutlineModel, element: OutlineElement, focus: boolean, aside: boolean): Promise { + private async _revealTreeSelection(model: OutlineModel, element: OutlineElement, preserveFocus: boolean, aside: boolean): Promise { await this._editorService.openCodeEditor( { resource: model.uri, options: { - preserveFocus: !focus, + preserveFocus, selection: Range.collapseToStart(element.symbol.selectionRange), selectionRevealType: TextEditorSelectionRevealType.NearTopIfOutsideViewport, } diff --git a/src/vs/workbench/contrib/output/browser/logViewer.ts b/src/vs/workbench/contrib/output/browser/logViewer.ts index 560b003c2d2..a8fb736ea74 100644 --- a/src/vs/workbench/contrib/output/browser/logViewer.ts +++ b/src/vs/workbench/contrib/output/browser/logViewer.ts @@ -39,9 +39,9 @@ export class LogViewerInput extends ResourceEditorInput { @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService ) { super( + URI.from({ scheme: LOG_SCHEME, path: outputChannelDescriptor.id }), basename(outputChannelDescriptor.file.path), dirname(outputChannelDescriptor.file.path), - URI.from({ scheme: LOG_SCHEME, path: outputChannelDescriptor.id }), undefined, textModelResolverService, textFileService, diff --git a/src/vs/workbench/contrib/output/browser/output.contribution.ts b/src/vs/workbench/contrib/output/browser/output.contribution.ts index da7cd3b026c..ad427f0da42 100644 --- a/src/vs/workbench/contrib/output/browser/output.contribution.ts +++ b/src/vs/workbench/contrib/output/browser/output.contribution.ts @@ -243,7 +243,7 @@ registerAction2(class extends Action2 { } }); -const devCategory = { value: nls.localize('developer', "Developer"), original: 'Developer' }; +const devCategory = { value: nls.localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer"), original: 'Developer' }; registerAction2(class extends Action2 { constructor() { super({ diff --git a/src/vs/workbench/contrib/output/browser/outputServices.ts b/src/vs/workbench/contrib/output/browser/outputServices.ts index ea753245ff4..7af6a57b013 100644 --- a/src/vs/workbench/contrib/output/browser/outputServices.ts +++ b/src/vs/workbench/contrib/output/browser/outputServices.ts @@ -56,7 +56,7 @@ class OutputChannel extends Disposable implements IOutputChannel { export class OutputService extends Disposable implements IOutputService, ITextModelContentProvider { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private channels: Map = new Map(); private activeChannelIdInStorage: string; diff --git a/src/vs/workbench/contrib/output/browser/outputView.ts b/src/vs/workbench/contrib/output/browser/outputView.ts index 3ee582fcc0d..2e5bd2cc7eb 100644 --- a/src/vs/workbench/contrib/output/browser/outputView.ts +++ b/src/vs/workbench/contrib/output/browser/outputView.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IAction } from 'vs/base/common/actions'; -import { IActionViewItem, SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IAction, IActionViewItem } from 'vs/base/common/actions'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -37,6 +36,7 @@ import { groupBy } from 'vs/base/common/arrays'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { editorBackground, selectBorder } from 'vs/platform/theme/common/colorRegistry'; import { addClass } from 'vs/base/browser/dom'; +import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; export class OutputViewPane extends ViewPane { @@ -157,7 +157,7 @@ export class OutputViewPane extends ViewPane { } private createInput(channel: IOutputChannel): ResourceEditorInput { - return this.instantiationService.createInstance(ResourceEditorInput, nls.localize('output model title', "{0} - Output", channel.label), nls.localize('channel', "Output channel for '{0}'", channel.label), channel.uri, undefined); + return this.instantiationService.createInstance(ResourceEditorInput, channel.uri, nls.localize('output model title', "{0} - Output", channel.label), nls.localize('channel', "Output channel for '{0}'", channel.label), undefined); } } @@ -280,7 +280,7 @@ class SwitchOutputActionViewItem extends SelectActionViewItem { @IThemeService private readonly themeService: IThemeService, @IContextViewService contextViewService: IContextViewService ) { - super(null, action, [], 0, contextViewService, { ariaLabel: nls.localize('outputChannels', 'Output Channels.') }); + super(null, action, [], 0, contextViewService, { ariaLabel: nls.localize('outputChannels', 'Output Channels.'), optionsAsChildren: true }); let outputChannelRegistry = Registry.as(Extensions.OutputChannels); this._register(outputChannelRegistry.onDidRegisterChannel(() => this.updateOtions())); diff --git a/src/vs/workbench/contrib/output/common/output.ts b/src/vs/workbench/contrib/output/common/output.ts index 483be2132b5..ded98a2afbd 100644 --- a/src/vs/workbench/contrib/output/common/output.ts +++ b/src/vs/workbench/contrib/output/common/output.ts @@ -60,7 +60,7 @@ export const IOutputService = createDecorator(OUTPUT_SERVICE_ID) * The output service to manage output from the various processes running. */ export interface IOutputService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Given the channel id returns the output channel instance. diff --git a/src/vs/workbench/contrib/performance/browser/performance.contribution.ts b/src/vs/workbench/contrib/performance/browser/performance.contribution.ts new file mode 100644 index 00000000000..01f8f6cbb24 --- /dev/null +++ b/src/vs/workbench/contrib/performance/browser/performance.contribution.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 { localize } from 'vs/nls'; +import { registerAction2, Action2 } from 'vs/platform/actions/common/actions'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; +import { Extensions as Input, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; +import { PerfviewContrib, PerfviewInput } from 'vs/workbench/contrib/performance/browser/perfviewEditor'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; + +// -- startup performance view + +Registry.as(Extensions.Workbench).registerWorkbenchContribution( + PerfviewContrib, + LifecyclePhase.Ready +); + +Registry.as(Input.EditorInputFactories).registerEditorInputFactory( + PerfviewInput.Id, + class implements IEditorInputFactory { + canSerialize(): boolean { + return true; + } + serialize(): string { + return ''; + } + deserialize(instantiationService: IInstantiationService): PerfviewInput { + return instantiationService.createInstance(PerfviewInput); + } + } +); + + +registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'perfview.show', + title: { value: localize('show.label', "Startup Performance"), original: 'Startup Performance' }, + category: { value: localize({ key: 'show.cat', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer"), original: 'Developer' }, + f1: true + }); + } + + run(accessor: ServicesAccessor) { + const editorService = accessor.get(IEditorService); + const instaService = accessor.get(IInstantiationService); + return editorService.openEditor(instaService.createInstance(PerfviewInput)); + } +}); diff --git a/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor.ts b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts similarity index 97% rename from src/vs/workbench/contrib/performance/electron-browser/perfviewEditor.ts rename to src/vs/workbench/contrib/performance/browser/perfviewEditor.ts index 9c5a93fc0d8..ec7143e8e40 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor.ts +++ b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts @@ -12,14 +12,14 @@ import { ILifecycleService, LifecyclePhase, StartupKindToString } from 'vs/platf import { IModeService } from 'vs/editor/common/services/modeService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { ITimerService, IStartupMetrics } from 'vs/workbench/services/timer/electron-browser/timerService'; +import { ITimerService, IStartupMetrics } from 'vs/workbench/services/timer/browser/timerService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import * as perf from 'vs/base/common/performance'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { writeTransientState } from 'vs/workbench/contrib/codeEditor/browser/toggleWordWrap'; import { mergeSort } from 'vs/base/common/arrays'; -import product from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -58,9 +58,9 @@ export class PerfviewInput extends ResourceEditorInput { @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService ) { super( + PerfviewInput.Uri, localize('name', "Startup Performance"), undefined, - PerfviewInput.Uri, undefined, textModelResolverService, textFileService, @@ -89,6 +89,7 @@ class PerfModelContentProvider implements ITextModelContentProvider { @ILifecycleService private readonly _lifecycleService: ILifecycleService, @ITimerService private readonly _timerService: ITimerService, @IExtensionService private readonly _extensionService: IExtensionService, + @IProductService private readonly _productService: IProductService ) { } provideTextContent(resource: URI): Promise { @@ -143,7 +144,7 @@ class PerfModelContentProvider implements ITextModelContentProvider { private _addSummary(md: MarkdownBuilder, metrics: IStartupMetrics): void { md.heading(2, 'System Info'); - md.li(`${product.nameShort}: ${product.version} (${product.commit || '0000000'})`); + md.li(`${this._productService.nameShort}: ${this._productService.version} (${this._productService.commit || '0000000'})`); md.li(`OS: ${metrics.platform}(${metrics.release})`); if (metrics.cpus) { md.li(`CPUs: ${metrics.cpus.model}(${metrics.cpus.count} x ${metrics.cpus.speed})`); @@ -178,6 +179,7 @@ class PerfModelContentProvider implements ITextModelContentProvider { table.push(['restore editors', metrics.timers.ellapsedEditorRestore, '[renderer]', `${metrics.editorIds.length}: ${metrics.editorIds.join(', ')}`]); table.push(['overall workbench load', metrics.timers.ellapsedWorkbench, '[renderer]', undefined]); table.push(['workbench ready', metrics.ellapsed, '[main->renderer]', undefined]); + table.push(['renderer ready', metrics.timers.ellapsedRenderer, '[renderer]', undefined]); table.push(['extensions registered', metrics.timers.ellapsedExtensionsReady, '[renderer]', undefined]); md.heading(2, 'Performance Marks'); @@ -217,7 +219,7 @@ class PerfModelContentProvider implements ITextModelContentProvider { md.value += `Name\tTimestamp\tDelta\tTotal\n`; let lastStartTime = -1; let total = 0; - for (const { name, timestamp: startTime } of perf.getEntries()) { + for (const { name, startTime } of perf.getEntries()) { let delta = lastStartTime !== -1 ? startTime - lastStartTime : 0; total += delta; md.value += `${name}\t${startTime}\t${delta}\t${total}\n`; @@ -420,6 +422,5 @@ class MarkdownBuilder { }); this.value += '|\n'; }); - } } diff --git a/src/vs/workbench/contrib/performance/electron-browser/performance.contribution.ts b/src/vs/workbench/contrib/performance/electron-browser/performance.contribution.ts index 23be35fff85..5a2a4182404 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/performance.contribution.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/performance.contribution.ts @@ -3,54 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { localize } from 'vs/nls'; -import { MenuRegistry } from 'vs/platform/actions/common/actions'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { Extensions as Input, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; -import { PerfviewContrib, PerfviewInput } from 'vs/workbench/contrib/performance/electron-browser/perfviewEditor'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { StartupProfiler } from './startupProfiler'; import { StartupTimings } from './startupTimings'; -// -- startup performance view - -Registry.as(Extensions.Workbench).registerWorkbenchContribution( - PerfviewContrib, - LifecyclePhase.Ready -); - -Registry.as(Input.EditorInputFactories).registerEditorInputFactory( - PerfviewInput.Id, - class implements IEditorInputFactory { - canSerialize(): boolean { - return true; - } - serialize(): string { - return ''; - } - deserialize(instantiationService: IInstantiationService): PerfviewInput { - return instantiationService.createInstance(PerfviewInput); - } - } -); - -CommandsRegistry.registerCommand('perfview.show', accessor => { - const editorService = accessor.get(IEditorService); - const instaService = accessor.get(IInstantiationService); - return editorService.openEditor(instaService.createInstance(PerfviewInput)); -}); - -MenuRegistry.addCommand({ - id: 'perfview.show', - category: localize('show.cat', "Developer"), - title: localize('show.label', "Startup Performance") -}); - - // -- startup profiler Registry.as(Extensions.Workbench).registerWorkbenchContribution( diff --git a/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts b/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts index c43d174e670..9bc3cc13ee0 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts @@ -11,14 +11,13 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import product from 'vs/platform/product/common/product'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { PerfviewInput } from 'vs/workbench/contrib/performance/electron-browser/perfviewEditor'; +import { PerfviewInput } from 'vs/workbench/contrib/performance/browser/perfviewEditor'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { URI } from 'vs/base/common/uri'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { IProductService } from 'vs/platform/product/common/productService'; export class StartupProfiler implements IWorkbenchContribution { @@ -109,14 +108,17 @@ export class StartupProfiler implements IWorkbenchContribution { } private async _createPerfIssue(files: string[]): Promise { - const reportIssueUrl = product.reportIssueUrl; + const reportIssueUrl = this._productService.reportIssueUrl; if (!reportIssueUrl) { return; } const ref = await this._textModelResolverService.createModelReference(PerfviewInput.Uri); - await this._clipboardService.writeText(ref.object.textEditorModel.getValue()); - ref.dispose(); + try { + await this._clipboardService.writeText(ref.object.textEditorModel.getValue()); + } finally { + ref.dispose(); + } const body = ` 1. :warning: We have copied additional data to your clipboard. Make sure to **paste** here. :warning: diff --git a/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts b/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts index aec39827d26..d9497593fbb 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts @@ -10,18 +10,18 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; -import { ILifecycleService, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; -import product from 'vs/platform/product/common/product'; +import { ILifecycleService, StartupKind, StartupKindToString } from 'vs/platform/lifecycle/common/lifecycle'; +import { IProductService } from 'vs/platform/product/common/productService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUpdateService } from 'vs/platform/update/common/update'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import * as files from 'vs/workbench/contrib/files/common/files'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { didUseCachedData, ITimerService } from 'vs/workbench/services/timer/electron-browser/timerService'; +import { didUseCachedData } from 'vs/workbench/services/timer/electron-browser/timerService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { getEntries } from 'vs/base/common/performance'; +import { ITimerService } from 'vs/workbench/services/timer/browser/timerService'; export class StartupTimings implements IWorkbenchContribution { @@ -34,33 +34,19 @@ export class StartupTimings implements IWorkbenchContribution { @ITelemetryService private readonly _telemetryService: ITelemetryService, @ILifecycleService private readonly _lifecycleService: ILifecycleService, @IUpdateService private readonly _updateService: IUpdateService, - @IWorkbenchEnvironmentService private readonly _envService: INativeWorkbenchEnvironmentService + @IWorkbenchEnvironmentService private readonly _envService: INativeWorkbenchEnvironmentService, + @IProductService private readonly _productService: IProductService ) { // this._report().catch(onUnexpectedError); } private async _report() { - const isStandardStartup = await this._isStandardStartup(); - this._reportStartupTimes().catch(onUnexpectedError); - this._appendStartupTimes(isStandardStartup).catch(onUnexpectedError); - this._reportPerfTicks(); + const standardStartupError = await this._isStandardStartup(); + this._appendStartupTimes(standardStartupError).catch(onUnexpectedError); } - private async _reportStartupTimes(): Promise { - const metrics = await this._timerService.startupMetrics; - - /* __GDPR__ - "startupTimeVaried" : { - "${include}": [ - "${IStartupMetrics}" - ] - } - */ - this._telemetryService.publicLog('startupTimeVaried', metrics); - } - - private async _appendStartupTimes(isStandardStartup: boolean) { + private async _appendStartupTimes(standardStartupError: string | undefined) { const appendTo = this._envService.args['prof-append-timers']; if (!appendTo) { // nothing to do @@ -73,7 +59,7 @@ export class StartupTimings implements IWorkbenchContribution { this._timerService.startupMetrics, timeout(15000), // wait: cached data creation, telemetry sending ]).then(([startupMetrics]) => { - return promisify(appendFile)(appendTo, `${startupMetrics.ellapsed}\t${product.nameShort}\t${(product.commit || '').slice(0, 10) || '0000000000'}\t${sessionId}\t${isStandardStartup ? 'standard_start' : 'NO_standard_start'}\n`); + return promisify(appendFile)(appendTo, `${startupMetrics.ellapsed}\t${this._productService.nameShort}\t${(this._productService.commit || '').slice(0, 10) || '0000000000'}\t${sessionId}\t${standardStartupError === undefined ? 'standard_start' : 'NO_standard_start : ' + standardStartupError}\n`); }).then(() => { this._electronService.quit(); }).catch(err => { @@ -82,7 +68,7 @@ export class StartupTimings implements IWorkbenchContribution { }); } - private async _isStandardStartup(): Promise { + private async _isStandardStartup(): Promise { // check for standard startup: // * new window (no reload) // * just one window @@ -90,42 +76,33 @@ export class StartupTimings implements IWorkbenchContribution { // * one text editor (not multiple, not webview, welcome etc...) // * cached data present (not rejected, not created) if (this._lifecycleService.startupKind !== StartupKind.NewWindow) { - return false; + return StartupKindToString(this._lifecycleService.startupKind); } - if (await this._electronService.getWindowCount() !== 1) { - return false; + const windowCount = await this._electronService.getWindowCount(); + if (windowCount !== 1) { + return 'Expected window count : 1, Actual : ' + windowCount; } const activeViewlet = this._viewletService.getActiveViewlet(); if (!activeViewlet || activeViewlet.getId() !== files.VIEWLET_ID) { - return false; + return 'Explorer viewlet not visible'; } const visibleEditorPanes = this._editorService.visibleEditorPanes; - if (visibleEditorPanes.length !== 1 || !isCodeEditor(visibleEditorPanes[0].getControl())) { - return false; + if (visibleEditorPanes.length !== 1) { + return 'Expected text editor count : 1, Actual : ' + visibleEditorPanes.length; } - if (this._panelService.getActivePanel()) { - return false; + if (!isCodeEditor(visibleEditorPanes[0].getControl())) { + return 'Active editor is not a text editor'; + } + const activePanel = this._panelService.getActivePanel(); + if (activePanel) { + return 'Current active panel : ' + this._panelService.getPanel(activePanel.getId())?.name; } if (!didUseCachedData()) { - return false; + return 'Either cache data is rejected or not created'; } if (!await this._updateService.isLatestVersion()) { - return false; + return 'Not on latest version, updates available'; } - return true; - } - - private _reportPerfTicks(): void { - const entries: Record = Object.create(null); - for (const entry of getEntries()) { - entries[entry.name] = entry.timestamp; - } - /* __GDPR__ - "startupRawTimers" : { - "entries": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } - } - */ - this._telemetryService.publicLog('startupRawTimers', { entries }); + return undefined; } } - diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index 8b7b0145b91..fc4533b84ea 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -12,8 +12,8 @@ import { dispose, Disposable, IDisposable, combinedDisposable, DisposableStore } import { CheckboxActionViewItem } from 'vs/base/browser/ui/checkbox/checkbox'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; -import { IAction, Action } from 'vs/base/common/actions'; -import { ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IAction, Action, Separator } from 'vs/base/common/actions'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorOptions } from 'vs/workbench/common/editor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -22,11 +22,7 @@ import { KeybindingsEditorModel, IKeybindingItemEntry, IListEntry, KEYBINDING_EN import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService, IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; import { DefineKeybindingWidget, KeybindingsSearchWidget, KeybindingsSearchOptions } from 'vs/workbench/contrib/preferences/browser/keybindingWidgets'; -import { - IKeybindingsEditorPane, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_COPY, - KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, - KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN -} from 'vs/workbench/contrib/preferences/common/preferences'; +import { IKeybindingsEditorPane, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR } from 'vs/workbench/contrib/preferences/common/preferences'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing'; import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; @@ -34,7 +30,7 @@ import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollec import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode, ResolvedKeybinding } from 'vs/base/common/keyCodes'; -import { listHighlightForeground, badgeBackground, contrastBorder, badgeForeground, listActiveSelectionForeground, listInactiveSelectionForeground, listHoverForeground, listFocusForeground, editorBackground } from 'vs/platform/theme/common/colorRegistry'; +import { listHighlightForeground, badgeBackground, contrastBorder, badgeForeground, listActiveSelectionForeground, listInactiveSelectionForeground, listHoverForeground, listFocusForeground, editorBackground, foreground, listActiveSelectionBackground, listInactiveSelectionBackground, listFocusBackground, listHoverBackground } from 'vs/platform/theme/common/colorRegistry'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; @@ -48,6 +44,9 @@ import { Emitter, Event } from 'vs/base/common/event'; import { MenuRegistry, MenuId, isIMenuItem } from 'vs/platform/actions/common/actions'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { preferencesEditIcon } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; +import { Color, RGBA } from 'vs/base/common/color'; +import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme'; +import { ThemableCheckboxActionViewItem } from 'vs/platform/browser/checkbox'; const $ = DOM.$; @@ -57,6 +56,8 @@ interface ColumnItem { width: number; } +const oddRowBackgroundColor = new Color(new RGBA(130, 130, 130, 0.04)); + export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditorPane { static readonly ID: string = 'workbench.editor.keybindings'; @@ -376,13 +377,17 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditorP const actionBar = this._register(new ActionBar(this.actionsContainer, { animated: false, actionViewItemProvider: (action: IAction) => { + let checkboxViewItem: CheckboxActionViewItem | undefined; if (action.id === this.sortByPrecedenceAction.id) { - return new CheckboxActionViewItem(null, action); + checkboxViewItem = new ThemableCheckboxActionViewItem(null, action, undefined, this.themeService); } - if (action.id === this.recordKeysAction.id) { - return new CheckboxActionViewItem(null, action); + else if (action.id === this.recordKeysAction.id) { + checkboxViewItem = new ThemableCheckboxActionViewItem(null, action, undefined, this.themeService); } - return undefined; + if (checkboxViewItem) { + + } + return checkboxViewItem; } })); @@ -1123,11 +1128,56 @@ class AccessibilityProvider implements IListAccessibilityProvider { + collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-header { background-color: ${oddRowBackgroundColor}; }`); + collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row.odd:not(.focused):not(.selected):not(:hover) { background-color: ${oddRowBackgroundColor}; }`); + collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:not(:focus) .monaco-list-row.focused.odd:not(.selected):not(:hover) { background-color: ${oddRowBackgroundColor}; }`); + collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:not(.focused) .monaco-list-row.focused.odd:not(.selected):not(:hover) { background-color: ${oddRowBackgroundColor}; }`); + + const foregroundColor = theme.getColor(foreground); + if (foregroundColor) { + const whenForegroundColor = foregroundColor.transparent(.8).makeOpaque(WORKBENCH_BACKGROUND(theme)); + collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column > .code { color: ${whenForegroundColor}; }`); + const whenForegroundColorForOddRow = foregroundColor.transparent(.8).makeOpaque(oddRowBackgroundColor); + collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row.odd > .column > .code { color: ${whenForegroundColorForOddRow}; }`); + } + + const listActiveSelectionForegroundColor = theme.getColor(listActiveSelectionForeground); + const listActiveSelectionBackgroundColor = theme.getColor(listActiveSelectionBackground); + if (listActiveSelectionForegroundColor && listActiveSelectionBackgroundColor) { + const whenForegroundColor = listActiveSelectionForegroundColor.transparent(.8).makeOpaque(listActiveSelectionBackgroundColor); + collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:focus .monaco-list-row.selected > .column > .code { color: ${whenForegroundColor}; }`); + collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:focus .monaco-list-row.odd.selected > .column > .code { color: ${whenForegroundColor}; }`); + } + + const listInactiveSelectionForegroundColor = theme.getColor(listInactiveSelectionForeground); + const listInactiveSelectionBackgroundColor = theme.getColor(listInactiveSelectionBackground); + if (listInactiveSelectionForegroundColor && listInactiveSelectionBackgroundColor) { + const whenForegroundColor = listInactiveSelectionForegroundColor.transparent(.8).makeOpaque(listInactiveSelectionBackgroundColor); + collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list .monaco-list-row.selected > .column > .code { color: ${whenForegroundColor}; }`); + collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list .monaco-list-row.odd.selected > .column > .code { color: ${whenForegroundColor}; }`); + } + + const listFocusForegroundColor = theme.getColor(listFocusForeground); + const listFocusBackgroundColor = theme.getColor(listFocusBackground); + if (listFocusForegroundColor && listFocusBackgroundColor) { + const whenForegroundColor = listFocusForegroundColor.transparent(.8).makeOpaque(listFocusBackgroundColor); + collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:focus .monaco-list-row.focused > .column > .code { color: ${whenForegroundColor}; }`); + collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:focus .monaco-list-row.odd.focused > .column > .code { color: ${whenForegroundColor}; }`); + } + + const listHoverForegroundColor = theme.getColor(listHoverForeground); + const listHoverBackgroundColor = theme.getColor(listHoverBackground); + if (listHoverForegroundColor && listHoverBackgroundColor) { + const whenForegroundColor = listHoverForegroundColor.transparent(.8).makeOpaque(listHoverBackgroundColor); + collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:focus .monaco-list-row:hover:not(.focused):not(.selected) > .column > .code { color: ${whenForegroundColor}; }`); + collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:focus .monaco-list-row.odd:hover:not(.focused):not(.selected) > .column > .code { color: ${whenForegroundColor}; }`); + } + const listHighlightForegroundColor = theme.getColor(listHighlightForeground); if (listHighlightForegroundColor) { collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .highlight { color: ${listHighlightForegroundColor}; }`); } - const listActiveSelectionForegroundColor = theme.getColor(listActiveSelectionForeground); + if (listActiveSelectionForegroundColor) { collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:focus .monaco-list-row.selected.focused > .column .monaco-keybinding-key { color: ${listActiveSelectionForegroundColor}; }`); collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:focus .monaco-list-row.selected > .column .monaco-keybinding-key { color: ${listActiveSelectionForegroundColor}; }`); @@ -1136,11 +1186,9 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) = if (listInactiveFocusAndSelectionForegroundColor) { collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list .monaco-list-row.selected > .column .monaco-keybinding-key { color: ${listInactiveFocusAndSelectionForegroundColor}; }`); } - const listHoverForegroundColor = theme.getColor(listHoverForeground); if (listHoverForegroundColor) { collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list .monaco-list-row:hover:not(.selected):not(.focused) > .column .monaco-keybinding-key { color: ${listHoverForegroundColor}; }`); } - const listFocusForegroundColor = theme.getColor(listFocusForeground); if (listFocusForegroundColor) { collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list .monaco-list-row.focused > .column .monaco-keybinding-key { color: ${listFocusForegroundColor}; }`); } diff --git a/src/vs/workbench/contrib/preferences/browser/media/keybindingsEditor.css b/src/vs/workbench/contrib/preferences/browser/media/keybindingsEditor.css index 80656037046..54ad28fd56e 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/keybindingsEditor.css +++ b/src/vs/workbench/contrib/preferences/browser/media/keybindingsEditor.css @@ -84,13 +84,6 @@ display: flex; } -.keybindings-editor > .keybindings-body > .keybindings-list-header, -.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row.odd:not(.focused):not(.selected):not(:hover), -.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:not(:focus) .monaco-list-row.focused.odd:not(.selected):not(:hover), -.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:not(.focused) .monaco-list-row.focused.odd:not(.selected):not(:hover) { - background-color: rgba(130, 130, 130, 0.04); -} - .keybindings-editor > .keybindings-body > .keybindings-list-header > .header { text-align: left; font-weight: bold; @@ -147,7 +140,6 @@ .keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column > .code { font-family: var(--monaco-monospace-font); font-size: 90%; - opacity: 0.8; display: flex; overflow: hidden; } diff --git a/src/vs/workbench/contrib/preferences/browser/media/preferences.css b/src/vs/workbench/contrib/preferences/browser/media/preferences.css index bf030348ca5..2476aeea796 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/preferences.css +++ b/src/vs/workbench/contrib/preferences/browser/media/preferences.css @@ -123,7 +123,7 @@ height: 30px; } -.vs .settings-header-widget > .settings-search-container > .settings-search-input > .monaco-inputbox { +.monaco-workbench.vs .settings-header-widget > .settings-search-container > .settings-search-input > .monaco-inputbox { border: 1px solid #ddd; } diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index 23960162819..02f464009af 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -35,14 +35,19 @@ position: relative; } -.vs .settings-editor > .settings-header > .search-container > .suggest-input-container { +.monaco-workbench.vs .settings-editor > .settings-header > .search-container > .suggest-input-container { border: 1px solid #ddd; } .settings-editor > .settings-header > .search-container > .settings-count-widget { position: absolute; right: 35px; - top: 6px; + top: 0px; + margin: 6px 0px; +} + +.settings-editor > .settings-header > .search-container > .settings-count-widget:empty { + visibility: hidden; } .settings-editor > .settings-header > .search-container > .settings-clear-widget { @@ -86,11 +91,11 @@ opacity: 1; } -.vs .settings-editor > .settings-header > .settings-header-controls { +.monaco-workbench.vs .settings-editor > .settings-header > .settings-header-controls { border-color: #cccccc; } -.vs-dark .settings-editor > .settings-header > .settings-header-controls { +.monaco-workbench.vs-dark .settings-editor > .settings-header > .settings-header-controls { border-color: #3c3c3c; } @@ -315,7 +320,8 @@ bottom: 16px; } -.settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-item-contents.is-configured .setting-item-modified-indicator { +.settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-item-contents.is-configured .setting-item-modified-indicator, +.settings-editor > .settings-body > .settings-tree-container .setting-item-list .setting-item-contents.is-configured .setting-item-modified-indicator { bottom: 23px; } @@ -431,7 +437,13 @@ font-family: var(--monaco-monospace-font); } -.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-enumDescription { +.settings-editor > .settings-body > .settings-list-container .setting-item-contents .setting-item-markdown hr { + border-bottom-width: 0px; + margin: 10px 20px; + opacity: 0.4; +} + +.settings-editor > .settings-body > .settings-list-container .setting-item-contents .setting-item-enumDescription { display: none; } @@ -439,7 +451,8 @@ display: block; } -.settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-item-contents { +.settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-item-contents, +.settings-editor > .settings-body > .settings-tree-container .setting-item-list .setting-item-contents { padding-bottom: 26px; } diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsWidgets.css b/src/vs/workbench/contrib/preferences/browser/media/settingsWidgets.css index 596728acbad..ccbcc852cbe 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsWidgets.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsWidgets.css @@ -7,14 +7,17 @@ width: 100%; } -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-value { +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-value, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-object-key { margin-right: 3px; margin-left: 2px; } /* Deal with overflow */ .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-value, -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-sibling { +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-sibling, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-object-key, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-object-value { white-space: pre; overflow: hidden; text-overflow: ellipsis; @@ -25,9 +28,28 @@ .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-sibling { max-width: 10%; } +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-object-key, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-object-input-key { + margin-left: 4px; + min-width: 40%; +} +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-object-input-key { + margin-left: 0; +} +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-object-input-value, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-object-value { + width: 100%; +} +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-row:hover .setting-list-object-value, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-row:focus .setting-list-object-value, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-row.selected .setting-list-object-value { + margin-right: 44px; +} .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-value, -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-sibling { +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-sibling, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-object-key, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-object-value { display: inline-block; line-height: 24px; } @@ -49,13 +71,31 @@ display: none; position: absolute; right: 0px; + top: 0px; } -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-row { +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-row, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-row-header { position: relative; max-height: 24px; } +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-row-header { + font-weight: bold; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-row, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-row-header { + display: flex; + padding-right: 4px; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-row-header, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-object-row:nth-child(odd):not(:hover):not(:focus):not(.selected), +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-edit-row.setting-list-object-row:nth-child(odd):hover { + background-color: rgba(130, 130, 130, 0.04); +} + .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-row:focus { outline: none; } @@ -84,14 +124,14 @@ padding: 2px 14px; } -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-item-control.setting-list-new-mode .setting-list-new-row { +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-item-control.setting-list-hide-add-button .setting-list-new-row { display: none; } .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .monaco-text-button.setting-list-addButton { display: inline-block; margin-top: 4px; - margin-right: 10px; + margin-right: 4px; } .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-edit-row { @@ -99,19 +139,32 @@ } .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-valueInput, -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-siblingInput { +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-siblingInput, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-object-input { height: 24px; max-width: 320px; - flex: 1; - margin-right: 10px; + margin-right: 4px; +} +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-object-input { + max-width: unset; +} +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-object-value-container .setting-list-object-input { + margin-right: 0; } -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-okButton { - margin-right: 10px; +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-ok-button { + margin: 0 4px; } .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-widget, -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-exclude-widget { +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-exclude-widget, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget { margin-bottom: 1px; padding: 1px; } + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-object-value-container, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-object-input select { + width: 100%; + height: 24px; +} diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index e8c4488f417..5bfbe8133ab 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -3,15 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/preferences'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; +import 'vs/css!./media/preferences'; import { Context as SuggestContext } from 'vs/editor/contrib/suggest/suggest'; import * as nls from 'vs/nls'; import { Action2, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IsMacNativeContext } from 'vs/platform/contextkey/common/contextkeys'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -19,9 +20,9 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; -import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; import { RemoteNameContext, WorkbenchStateContext } from 'vs/workbench/browser/contextkeys'; -import { IsMacNativeContext } from 'vs/platform/contextkey/common/contextkeys'; import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { EditorInput, Extensions as EditorInputExtensions, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; @@ -31,14 +32,30 @@ import { KeybindingsEditor } from 'vs/workbench/contrib/preferences/browser/keyb import { ConfigureLanguageBasedSettingsAction } from 'vs/workbench/contrib/preferences/browser/preferencesActions'; import { PreferencesEditor } from 'vs/workbench/contrib/preferences/browser/preferencesEditor'; import { SettingsEditor2 } from 'vs/workbench/contrib/preferences/browser/settingsEditor2'; -import { CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, CONTEXT_KEYBINDING_FOCUS, CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS, MODIFIED_SETTING_TAG, SETTINGS_COMMAND_OPEN_SETTINGS, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING, SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED, SETTINGS_EDITOR_COMMAND_FILTER_ONLINE, SETTINGS_EDITOR_COMMAND_FOCUS_FILE, SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST, SETTINGS_EDITOR_COMMAND_SEARCH, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU, SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON, SETTINGS_EDITOR_COMMAND_FOCUS_TOC } from 'vs/workbench/contrib/preferences/common/preferences'; +import { CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, CONTEXT_KEYBINDING_FOCUS, CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, MODIFIED_SETTING_TAG, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; import { PreferencesContribution } from 'vs/workbench/contrib/preferences/common/preferencesContribution'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { DefaultPreferencesEditorInput, KeybindingsEditorInput, PreferencesEditorInput, SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; -import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; +import { AbstractSideBySideEditorInputFactory } from 'vs/workbench/browser/parts/editor/editor.contribution'; + +const SETTINGS_EDITOR_COMMAND_SEARCH = 'settings.action.search'; + +const SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING = 'settings.action.focusNextSetting'; +const SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING = 'settings.action.focusPreviousSetting'; +const SETTINGS_EDITOR_COMMAND_FOCUS_FILE = 'settings.action.focusSettingsFile'; +const SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING = 'settings.action.editFocusedSetting'; +const SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH = 'settings.action.focusSettingsFromSearch'; +const SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST = 'settings.action.focusSettingsList'; +const SETTINGS_EDITOR_COMMAND_FOCUS_TOC = 'settings.action.focusTOC'; + +const SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON = 'settings.switchToJSON'; +const SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED = 'settings.filterByModified'; +const SETTINGS_EDITOR_COMMAND_FILTER_ONLINE = 'settings.filterByOnline'; + +const SETTINGS_COMMAND_OPEN_SETTINGS = 'workbench.action.openSettings'; Registry.as(EditorExtensions.Editors).registerEditor( EditorDescriptor.create( @@ -73,79 +90,11 @@ Registry.as(EditorExtensions.Editors).registerEditor( ] ); -interface ISerializedPreferencesEditorInput { - name: string; - description: string; - - detailsSerialized: string; - masterSerialized: string; - - detailsTypeId: string; - masterTypeId: string; -} - // Register Preferences Editor Input Factory -class PreferencesEditorInputFactory implements IEditorInputFactory { +class PreferencesEditorInputFactory extends AbstractSideBySideEditorInputFactory { - canSerialize(editorInput: EditorInput): boolean { - const input = editorInput; - - if (input.details && input.master) { - const registry = Registry.as(EditorInputExtensions.EditorInputFactories); - const detailsInputFactory = registry.getEditorInputFactory(input.details.getTypeId()); - const masterInputFactory = registry.getEditorInputFactory(input.master.getTypeId()); - - return !!(detailsInputFactory?.canSerialize(input.details) && masterInputFactory?.canSerialize(input.master)); - } - - return false; - } - - serialize(editorInput: EditorInput): string | undefined { - const input = editorInput; - - if (input.details && input.master) { - const registry = Registry.as(EditorInputExtensions.EditorInputFactories); - const detailsInputFactory = registry.getEditorInputFactory(input.details.getTypeId()); - const masterInputFactory = registry.getEditorInputFactory(input.master.getTypeId()); - - if (detailsInputFactory && masterInputFactory) { - const detailsSerialized = detailsInputFactory.serialize(input.details); - const masterSerialized = masterInputFactory.serialize(input.master); - - if (detailsSerialized && masterSerialized) { - return JSON.stringify({ - name: input.getName(), - description: input.getDescription(), - detailsSerialized, - masterSerialized, - detailsTypeId: input.details.getTypeId(), - masterTypeId: input.master.getTypeId() - }); - } - } - } - - return undefined; - } - - deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput | undefined { - const deserialized: ISerializedPreferencesEditorInput = JSON.parse(serializedEditorInput); - - const registry = Registry.as(EditorInputExtensions.EditorInputFactories); - const detailsInputFactory = registry.getEditorInputFactory(deserialized.detailsTypeId); - const masterInputFactory = registry.getEditorInputFactory(deserialized.masterTypeId); - - if (detailsInputFactory && masterInputFactory) { - const detailsInput = detailsInputFactory.deserialize(instantiationService, deserialized.detailsSerialized); - const masterInput = masterInputFactory.deserialize(instantiationService, deserialized.masterSerialized); - - if (detailsInput && masterInput) { - return new PreferencesEditorInput(deserialized.name, deserialized.description, detailsInput, masterInput); - } - } - - return undefined; + protected createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { + return new PreferencesEditorInput(name, description, secondaryInput, primaryInput); } } diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts index bce92af6be9..b0401d43adc 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts @@ -12,12 +12,12 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance import { IStringDictionary } from 'vs/base/common/collections'; import { getErrorMessage, isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { ArrayNavigator } from 'vs/base/common/navigator'; import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; -import * as strings from 'vs/base/common/strings'; +import { ArrayNavigator } from 'vs/base/common/navigator'; +import { assertIsDefined, withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { EditorExtensionsRegistry, registerEditorContribution, IEditorContributionDescription } from 'vs/editor/browser/editorExtensions'; +import { EditorExtensionsRegistry, IEditorContributionDescription, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import * as editorCommon from 'vs/editor/common/editorCommon'; @@ -29,7 +29,7 @@ import { SelectionHighlighter } from 'vs/editor/contrib/multicursor/multicursor' import * as nls from 'vs/nls'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IInstantiationService, IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation'; +import { IConstructorSignature1, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -46,13 +46,12 @@ import { EditorInput, EditorOptions, IEditorControl } from 'vs/workbench/common/ import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; import { DefaultSettingsRenderer, FolderSettingsRenderer, IPreferencesRenderer, UserSettingsRenderer, WorkspaceSettingsRenderer } from 'vs/workbench/contrib/preferences/browser/preferencesRenderers'; import { SearchWidget, SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; -import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, IPreferencesSearchService, ISearchProvider, CONTEXT_SETTINGS_JSON_EDITOR } from 'vs/workbench/contrib/preferences/common/preferences'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, IPreferencesSearchService, ISearchProvider } from 'vs/workbench/contrib/preferences/common/preferences'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IFilterResult, IPreferencesService, ISetting, ISettingsEditorModel, ISettingsGroup, SettingsEditorOptions } from 'vs/workbench/services/preferences/common/preferences'; import { DefaultPreferencesEditorInput, PreferencesEditorInput } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { DefaultSettingsEditorModel, SettingsEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; -import { withNullAsUndefined, withUndefinedAsNull, assertIsDefined } from 'vs/base/common/types'; export class PreferencesEditor extends BaseEditor { @@ -80,7 +79,7 @@ export class PreferencesEditor extends BaseEditor { set minimumWidth(value: number) { /*noop*/ } set maximumWidth(value: number) { /*noop*/ } - readonly minimumHeight = 260; + get minimumHeight() { return 260; } private _onDidCreateWidget = this._register(new Emitter<{ width: number; height: number; } | undefined>()); readonly onDidSizeConstraintsChange: Event<{ width: number; height: number; } | undefined> = this._onDidCreateWidget.event; @@ -206,7 +205,7 @@ export class PreferencesEditor extends BaseEditor { } private updateInput(newInput: PreferencesEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { - return this.sideBySidePreferencesWidget.setInput(newInput.details, newInput.master, options, token).then(({ defaultPreferencesRenderer, editablePreferencesRenderer }) => { + return this.sideBySidePreferencesWidget.setInput(newInput.secondary, newInput.primary, options, token).then(({ defaultPreferencesRenderer, editablePreferencesRenderer }) => { if (token.isCancellationRequested) { return; } @@ -692,7 +691,7 @@ class PreferencesRenderersController extends Disposable { if (nlpMetadata) { const sortedKeys = Object.keys(nlpMetadata.scoredResults).sort((a, b) => nlpMetadata.scoredResults[b].score - nlpMetadata.scoredResults[a].score); const suffix = '##' + key; - data['nlpIndex'] = arrays.firstIndex(sortedKeys, key => strings.endsWith(key, suffix)); + data['nlpIndex'] = arrays.firstIndex(sortedKeys, key => key.endsWith(suffix)); } const settingLocation = this._findSetting(this.lastFilterResult, key); diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts index 4292cab7ccb..eb525604877 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts @@ -3,9 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ContextSubMenu } from 'vs/base/browser/contextmenu'; import { EventHelper, getDomNodePagePosition } from 'vs/base/browser/dom'; -import { IAction } from 'vs/base/common/actions'; +import { IAction, SubmenuAction } from 'vs/base/common/actions'; import { Delayer } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; @@ -18,8 +17,8 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; import { IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import * as nls from 'vs/nls'; -import { ConfigurationTarget, IConfigurationService, overrideIdentifierFromKey } from 'vs/platform/configuration/common/configuration'; -import { ConfigurationScope, Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationRegistry, IConfigurationNode, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationScope, Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationRegistry, IConfigurationNode, OVERRIDE_PROPERTY_PATTERN, overrideIdentifierFromKey } from 'vs/platform/configuration/common/configurationRegistry'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -651,7 +650,7 @@ class EditSettingRenderer extends Disposable { private readonly _onUpdateSetting: Emitter<{ key: string, value: any, source: IIndexedSetting }> = new Emitter<{ key: string, value: any, source: IIndexedSetting }>(); readonly onUpdateSetting: Event<{ key: string, value: any, source: IIndexedSetting }> = this._onUpdateSetting.event; - constructor(private editor: ICodeEditor, private masterSettingsModel: ISettingsEditorModel, + constructor(private editor: ICodeEditor, private primarySettingsModel: ISettingsEditorModel, private settingHighlighter: SettingHighlighter, @IInstantiationService private readonly instantiationService: IInstantiationService, @IContextMenuService private readonly contextMenuService: IContextMenuService @@ -683,7 +682,7 @@ class EditSettingRenderer extends Disposable { } private isDefaultSettings(): boolean { - return this.masterSettingsModel instanceof DefaultSettingsEditorModel; + return this.primarySettingsModel instanceof DefaultSettingsEditorModel; } private onConfigurationChanged(): void { @@ -769,7 +768,7 @@ class EditSettingRenderer extends Disposable { return true; } if (configurationNode.type === 'boolean' || configurationNode.enum) { - if ((this.masterSettingsModel).configurationTarget !== ConfigurationTarget.WORKSPACE_FOLDER) { + if ((this.primarySettingsModel).configurationTarget !== ConfigurationTarget.WORKSPACE_FOLDER) { return true; } if (configurationNode.scope === ConfigurationScope.RESOURCE || configurationNode.scope === ConfigurationScope.LANGUAGE_OVERRIDABLE) { @@ -826,7 +825,7 @@ class EditSettingRenderer extends Disposable { const anchor = { x: e.event.posx, y: e.event.posy + 10 }; const actions = this.getSettings(editPreferenceWidget.getLine()).length === 1 ? this.getActions(editPreferenceWidget.preferences[0], this.getConfigurationsMap()[editPreferenceWidget.preferences[0].key]) - : editPreferenceWidget.preferences.map(setting => new ContextSubMenu(setting.key, this.getActions(setting, this.getConfigurationsMap()[setting.key]))); + : editPreferenceWidget.preferences.map(setting => new SubmenuAction(`preferences.submenu.${setting.key}`, setting.key, this.getActions(setting, this.getConfigurationsMap()[setting.key]))); this.contextMenuService.showContextMenu({ getAnchor: () => anchor, getActions: () => actions diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts index bcb5216f675..61441434c8f 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts @@ -33,7 +33,7 @@ export interface IEndpointDetails { } export class PreferencesSearchService extends Disposable implements IPreferencesSearchService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _installedExtensions: Promise; diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index d0f5ac5841e..592600feba1 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -5,7 +5,7 @@ import * as DOM from 'vs/base/browser/dom'; import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { ActionBar, ActionsOrientation, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { IInputOptions, InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { Widget } from 'vs/base/browser/ui/widget'; import { Action, IAction } from 'vs/base/common/actions'; @@ -36,6 +36,7 @@ import { ISettingsGroup, IPreferencesService } from 'vs/workbench/services/prefe import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { isEqual } from 'vs/base/common/resources'; import { registerIcon, Codicon } from 'vs/base/common/codicons'; +import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; export class SettingsHeaderWidget extends Widget implements IViewZone { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index f7fbfb25ba0..9b70dc5488e 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -10,15 +10,16 @@ import { Button } from 'vs/base/browser/ui/button/button'; import { ITreeElement } from 'vs/base/browser/ui/tree/tree'; import { Action } from 'vs/base/common/actions'; import * as arrays from 'vs/base/common/arrays'; -import { Delayer, ThrottledDelayer, timeout, IntervalTimer } from 'vs/base/common/async'; +import { Delayer, IntervalTimer, ThrottledDelayer, timeout } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import * as collections from 'vs/base/common/collections'; +import { fromNow } from 'vs/base/common/date'; import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors'; +import { Emitter } from 'vs/base/common/event'; import { Iterable } from 'vs/base/common/iterator'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; -import * as strings from 'vs/base/common/strings'; import { isArray, withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/settingsEditor2'; @@ -31,14 +32,13 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IProductService } from 'vs/platform/product/common/productService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { badgeBackground, badgeForeground, contrastBorder, editorForeground } from 'vs/platform/theme/common/colorRegistry'; -import { attachStylerCallback } from 'vs/platform/theme/common/styler'; +import { attachButtonStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; -import { getUserDataSyncStore, IUserDataSyncService, SyncStatus, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataAutoSyncService, IUserDataSyncService, SyncStatus } from 'vs/platform/userDataSync/common/userDataSync'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { IEditorMemento, IEditorPane } from 'vs/workbench/common/editor'; import { attachSuggestEnabledInputBoxStyler, SuggestEnabledInput } from 'vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput'; @@ -49,12 +49,11 @@ import { ISettingsEditorViewState, parseQuery, SearchResultIdx, SearchResultMode import { settingsTextInputBorder } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; import { createTOCIterator, TOCTree, TOCTreeModel } from 'vs/workbench/contrib/preferences/browser/tocTree'; import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, EXTENSION_SETTING_TAG, IPreferencesSearchService, ISearchProvider, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; -import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IPreferencesService, ISearchResult, ISettingsEditorModel, ISettingsEditorOptions, SettingsEditorOptions, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { Settings2EditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; -import { fromNow } from 'vs/base/common/date'; -import { Emitter } from 'vs/base/common/event'; +import { IUserDataSyncWorkbenchService } from 'vs/workbench/services/userDataSync/common/userDataSync'; function createGroupIterator(group: SettingsTreeGroupElement): Iterable> { return Iterable.map(group.children, g => { @@ -168,7 +167,8 @@ export class SettingsEditor2 extends BaseEditor { @IEditorGroupsService protected editorGroupService: IEditorGroupsService, @IKeybindingService private readonly keybindingService: IKeybindingService, @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, - @IProductService private readonly productService: IProductService, + @IUserDataSyncWorkbenchService private readonly userDataSyncWorkbenchService: IUserDataSyncWorkbenchService, + @IUserDataAutoSyncService private readonly userDataAutoSyncService: IUserDataAutoSyncService ) { super(SettingsEditor2.ID, telemetryService, themeService, storageService); this.delayedFilterLogging = new Delayer(1000); @@ -339,8 +339,12 @@ export class SettingsEditor2 extends BaseEditor { this.focusSearch(); } - onHide(): void { - this.searchWidget.onHide(); + protected setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void { + super.setEditorVisible(visible, group); + + if (!visible) { + this.searchWidget.onHide(); + } } focusSettings(): void { @@ -425,7 +429,7 @@ export class SettingsEditor2 extends BaseEditor { this.searchWidget = this._register(this.instantiationService.createInstance(SuggestEnabledInput, `${SettingsEditor2.ID}.searchbox`, searchContainer, { triggerCharacters: ['@'], provideResults: (query: string) => { - return SettingsEditor2.SUGGESTIONS.filter(tag => query.indexOf(tag) === -1).map(tag => strings.endsWith(tag, ':') ? tag : tag + ' '); + return SettingsEditor2.SUGGESTIONS.filter(tag => query.indexOf(tag) === -1).map(tag => tag.endsWith(':') ? tag : tag + ' '); } }, searchBoxLabel, 'settingseditor:searchinput' + SettingsEditor2.NUM_INSTANCES++, { placeholderText: searchBoxLabel, @@ -468,7 +472,7 @@ export class SettingsEditor2 extends BaseEditor { this.settingsTargetsWidget.settingsTarget = ConfigurationTarget.USER_LOCAL; this.settingsTargetsWidget.onDidTargetChange(target => this.onDidSettingsTargetChange(target)); - if (syncAllowed(this.productService, this.configurationService)) { + if (this.userDataSyncWorkbenchService.enabled && this.userDataAutoSyncService.canToggleEnablement()) { const syncControls = this._register(this.instantiationService.createInstance(SyncControls, headerControlsContainer)); this._register(syncControls.onDidChangeLastSyncedLabel(lastSyncedLabel => this.updateInputAriaLabel(lastSyncedLabel))); } @@ -642,7 +646,10 @@ export class SettingsEditor2 extends BaseEditor { this.tocTreeContainer = DOM.append(parent, $('.settings-toc-container')); this.tocTree = this._register(this.instantiationService.createInstance(TOCTree, - DOM.append(this.tocTreeContainer, $('.settings-toc-wrapper')), + DOM.append(this.tocTreeContainer, $('.settings-toc-wrapper', { + 'role': 'navigation', + 'aria-label': localize('settings', "Settings"), + })), this.viewState)); this._register(this.tocTree.onDidChangeFocus(e => { @@ -1401,22 +1408,24 @@ class SyncControls extends Disposable { container: HTMLElement, @ICommandService private readonly commandService: ICommandService, @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, - @IUserDataSyncEnablementService private readonly userDataSyncEnablementService: IUserDataSyncEnablementService + @IUserDataAutoSyncService private readonly userDataAutoSyncService: IUserDataAutoSyncService, + @IThemeService themeService: IThemeService, ) { super(); const headerRightControlsContainer = DOM.append(container, $('.settings-right-controls')); const turnOnSyncButtonContainer = DOM.append(headerRightControlsContainer, $('.turn-on-sync')); this.turnOnSyncButton = this._register(new Button(turnOnSyncButtonContainer, { title: true })); + this._register(attachButtonStyler(this.turnOnSyncButton, themeService)); this.lastSyncedLabel = DOM.append(headerRightControlsContainer, $('.last-synced-label')); DOM.hide(this.lastSyncedLabel); this.turnOnSyncButton.enabled = true; - this.turnOnSyncButton.label = localize('turnOnSyncButton', "Turn on Preferences Sync"); + this.turnOnSyncButton.label = localize('turnOnSyncButton', "Turn on Settings Sync"); DOM.hide(this.turnOnSyncButton.element); this._register(this.turnOnSyncButton.onDidClick(async () => { - await this.commandService.executeCommand('workbench.userData.actions.syncStart'); + await this.commandService.executeCommand('workbench.userDataSync.actions.turnOn'); })); this.updateLastSyncedTime(); @@ -1432,7 +1441,7 @@ class SyncControls extends Disposable { this.update(); })); - this._register(this.userDataSyncEnablementService.onDidChangeEnablement(() => { + this._register(this.userDataAutoSyncService.onDidChangeEnablement(() => { this.update(); })); } @@ -1456,7 +1465,7 @@ class SyncControls extends Disposable { return; } - if (this.userDataSyncEnablementService.isEnabled()) { + if (this.userDataAutoSyncService.isEnabled()) { DOM.show(this.lastSyncedLabel); DOM.hide(this.turnOnSyncButton.element); } else { @@ -1470,7 +1479,3 @@ interface ISettingsEditor2State { searchQuery: string; target: SettingsTarget; } - -function syncAllowed(productService: IProductService, configService: IConfigurationService): boolean { - return !!getUserDataSyncStore(productService, configService); -} diff --git a/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts b/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts index 335e4c7cedf..b66d3b8ad38 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts @@ -238,6 +238,7 @@ export const knownAcronyms = new Set(); 'ie', 'id', 'php', + 'scm', ].forEach(str => knownAcronyms.add(str)); export const knownTermMappings = new Map(); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 495a328ae6f..060d31324f6 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -8,7 +8,6 @@ import * as DOM from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; -import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { alert as ariaAlert } from 'vs/base/browser/ui/aria/aria'; import { Button } from 'vs/base/browser/ui/button/button'; import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; @@ -20,7 +19,7 @@ import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IObjectTreeOptions, ObjectTree } from 'vs/base/browser/ui/tree/objectTree'; import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel'; import { ITreeFilter, ITreeModel, ITreeNode, ITreeRenderer, TreeFilterResult, TreeVisibility } from 'vs/base/browser/ui/tree/tree'; -import { Action, IAction } from 'vs/base/common/actions'; +import { Action, IAction, Separator } from 'vs/base/common/actions'; import * as arrays from 'vs/base/common/arrays'; import { Color, RGBA } from 'vs/base/common/color'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -28,9 +27,8 @@ import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; import { isIOS } from 'vs/base/common/platform'; -import { ISpliceable } from 'vs/base/common/sequence'; -import { escapeRegExpCharacters, startsWith } from 'vs/base/common/strings'; -import { isArray } from 'vs/base/common/types'; +import { escapeRegExpCharacters } from 'vs/base/common/strings'; +import { isArray, isDefined, isUndefinedOrNull } from 'vs/base/common/types'; import { localize } from 'vs/nls'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -45,14 +43,16 @@ import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticip import { getIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; import { ITOCEntry } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; import { ISettingsEditorViewState, settingKeyToDisplayFormat, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeNewExtensionsElement, SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; -import { ExcludeSettingWidget, IListChangeEvent, IListDataItem, ListSettingWidget, settingsHeaderForeground, settingsNumberInputBackground, settingsNumberInputBorder, settingsNumberInputForeground, settingsSelectBackground, settingsSelectBorder, settingsSelectForeground, settingsSelectListBorder, settingsTextInputBackground, settingsTextInputBorder, settingsTextInputForeground } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; +import { ExcludeSettingWidget, ISettingListChangeEvent, IListDataItem, ListSettingWidget, settingsHeaderForeground, settingsNumberInputBackground, settingsNumberInputBorder, settingsNumberInputForeground, settingsSelectBackground, settingsSelectBorder, settingsSelectForeground, settingsSelectListBorder, settingsTextInputBackground, settingsTextInputBorder, settingsTextInputForeground, ObjectSettingWidget, IObjectDataItem, IObjectEnumOption, ObjectValue, IObjectValueSuggester, IObjectKeySuggester } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; import { SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ISetting, ISettingsGroup, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; -import { IUserDataSyncEnablementService, getDefaultIgnoredSettings } from 'vs/platform/userDataSync/common/userDataSync'; +import { getDefaultIgnoredSettings, IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync'; import { getInvalidTypeError } from 'vs/workbench/services/preferences/common/preferencesValidation'; import { Codicon } from 'vs/base/common/codicons'; import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { IList } from 'vs/base/browser/ui/tree/indexTreeModel'; const $ = DOM.$; @@ -75,6 +75,174 @@ function getExcludeDisplayValue(element: SettingsTreeSettingElement): IListDataI }); } +function areAllPropertiesDefined(properties: string[], itemsToDisplay: IObjectDataItem[]): boolean { + const staticProperties = new Set(properties); + itemsToDisplay.forEach(({ key }) => staticProperties.delete(key.data)); + return staticProperties.size === 0; +} + +function getEnumOptionsFromSchema(schema: IJSONSchema): IObjectEnumOption[] { + const enumDescriptions = schema.enumDescriptions ?? []; + + return (schema.enum ?? []).map((value, idx) => { + const description = idx < enumDescriptions.length + ? enumDescriptions[idx] + : undefined; + + return { value, description }; + }); +} + +function getObjectValueType(schema: IJSONSchema): ObjectValue['type'] { + if (schema.type === 'boolean') { + return 'boolean'; + } else if (schema.type === 'string' && isDefined(schema.enum) && schema.enum.length > 0) { + return 'enum'; + } else { + return 'string'; + } +} + +function getObjectDisplayValue(element: SettingsTreeSettingElement): IObjectDataItem[] { + const elementDefaultValue: Record = typeof element.defaultValue === 'object' + ? element.defaultValue ?? {} + : {}; + + const elementScopeValue: Record = typeof element.scopeValue === 'object' + ? element.scopeValue ?? {} + : {}; + + const data = element.isConfigured ? + { ...elementDefaultValue, ...elementScopeValue } : + elementDefaultValue; + + const { objectProperties, objectPatternProperties, objectAdditionalProperties } = element.setting; + const patternsAndSchemas = Object + .entries(objectPatternProperties ?? {}) + .map(([pattern, schema]) => ({ + pattern: new RegExp(pattern), + schema + })); + + const additionalValueEnums = getEnumOptionsFromSchema( + typeof objectAdditionalProperties === 'boolean' + ? {} + : objectAdditionalProperties ?? {} + ); + + const wellDefinedKeyEnumOptions = Object.entries(objectProperties ?? {}).map( + ([key, schema]) => ({ value: key, description: schema.description }) + ); + + return Object.keys(data).map(key => { + if (isDefined(objectProperties) && key in objectProperties) { + const defaultValue = elementDefaultValue[key]; + const valueEnumOptions = getEnumOptionsFromSchema(objectProperties[key]); + + return { + key: { + type: 'enum', + data: key, + options: wellDefinedKeyEnumOptions, + }, + value: { + type: getObjectValueType(objectProperties[key]), + data: data[key], + options: valueEnumOptions, + }, + removable: isUndefinedOrNull(defaultValue), + } as IObjectDataItem; + } + + const schema = patternsAndSchemas.find(({ pattern }) => pattern.test(key))?.schema; + + if (schema) { + const valueEnumOptions = getEnumOptionsFromSchema(schema); + return { + key: { type: 'string', data: key }, + value: { + type: getObjectValueType(schema), + data: data[key], + options: valueEnumOptions, + }, + removable: true, + } as IObjectDataItem; + } + + return { + key: { type: 'string', data: key }, + value: { + type: typeof objectAdditionalProperties === 'object' ? getObjectValueType(objectAdditionalProperties) : 'string', + data: data[key], + options: additionalValueEnums, + }, + removable: true, + } as IObjectDataItem; + }); +} + +function createObjectKeySuggester(element: SettingsTreeSettingElement): IObjectKeySuggester { + const { objectProperties } = element.setting; + const allStaticKeys = Object.keys(objectProperties ?? {}); + + return keys => { + const existingKeys = new Set(keys); + const enumOptions: IObjectEnumOption[] = []; + + allStaticKeys.forEach(staticKey => { + if (!existingKeys.has(staticKey)) { + enumOptions.push({ value: staticKey, description: objectProperties![staticKey].description }); + } + }); + + return enumOptions.length > 0 + ? { type: 'enum', data: enumOptions[0].value, options: enumOptions } + : undefined; + }; +} + +function createObjectValueSuggester(element: SettingsTreeSettingElement): IObjectValueSuggester { + const { objectProperties, objectPatternProperties, objectAdditionalProperties } = element.setting; + + const patternsAndSchemas = Object + .entries(objectPatternProperties ?? {}) + .map(([pattern, schema]) => ({ + pattern: new RegExp(pattern), + schema + })); + + return (key: string) => { + let suggestedSchema: IJSONSchema | undefined; + + if (isDefined(objectProperties) && key in objectProperties) { + suggestedSchema = objectProperties[key]; + } + + const patternSchema = suggestedSchema ?? patternsAndSchemas.find(({ pattern }) => pattern.test(key))?.schema; + + if (isDefined(patternSchema)) { + suggestedSchema = patternSchema; + } else if (isDefined(objectAdditionalProperties) && typeof objectAdditionalProperties === 'object') { + suggestedSchema = objectAdditionalProperties; + } + + if (isDefined(suggestedSchema)) { + const type = getObjectValueType(suggestedSchema); + + if (type === 'boolean') { + return { type, data: suggestedSchema.default ?? true }; + } else if (type === 'enum') { + const options = getEnumOptionsFromSchema(suggestedSchema); + return { type, data: suggestedSchema.default ?? options[0].value, options }; + } else { + return { type, data: suggestedSchema.default ?? '' }; + } + } + + return; + }; +} + function getListDisplayValue(element: SettingsTreeSettingElement): IListDataItem[] { if (!element.value || !isArray(element.value)) { return []; @@ -242,6 +410,10 @@ interface ISettingExcludeItemTemplate extends ISettingItemTemplate { excludeWidget: ListSettingWidget; } +interface ISettingObjectItemTemplate extends ISettingItemTemplate { + objectWidget: ObjectSettingWidget; +} + interface ISettingNewExtensionsTemplate extends IDisposableTemplate { button: Button; context?: SettingsTreeNewExtensionsElement; @@ -258,6 +430,7 @@ const SETTINGS_ENUM_TEMPLATE_ID = 'settings.enum.template'; const SETTINGS_BOOL_TEMPLATE_ID = 'settings.bool.template'; const SETTINGS_ARRAY_TEMPLATE_ID = 'settings.array.template'; const SETTINGS_EXCLUDE_TEMPLATE_ID = 'settings.exclude.template'; +const SETTINGS_OBJECT_TEMPLATE_ID = 'settings.object.template'; const SETTINGS_COMPLEX_TEMPLATE_ID = 'settings.complex.template'; const SETTINGS_NEW_EXTENSIONS_TEMPLATE_ID = 'settings.newExtensions.template'; const SETTINGS_ELEMENT_TEMPLATE_ID = 'settings.group.template'; @@ -447,7 +620,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre template.toolbar.context = element; const actions = this.disposableActionFactory(element.setting); actions.forEach(a => template.elementDisposables?.add(a)); - template.toolbar.setActions([], [...this.settingActions, ...actions])(); + template.toolbar.setActions([], [...this.settingActions, ...actions]); this.fixToolbarIcon(template.toolbar); const setting = element.setting; @@ -535,10 +708,10 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre // Rewrite `#editor.fontSize#` to link format text = fixSettingLinks(text); - const renderedMarkdown = renderMarkdown({ value: text }, { + const renderedMarkdown = renderMarkdown({ value: text, isTrusted: true }, { actionHandler: { callback: (content: string) => { - if (startsWith(content, '#')) { + if (content.startsWith('#')) { const e: ISettingLinkClickEvent = { source: element, targetKey: content.substr(1) @@ -596,6 +769,11 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre itemElement.setAttribute('role', 'combobox'); label += modifiedText; } + } else if (templateId === SETTINGS_OBJECT_TEMPLATE_ID) { + if (itemElement = (template).objectWidget.domNode) { + itemElement.setAttribute('role', 'list'); + label += modifiedText; + } } else { // Don't change attributes if we don't know what we areFunctions return ''; @@ -797,7 +975,7 @@ export class SettingArrayRenderer extends AbstractSettingRenderer implements ITr }); } - private computeNewList(template: ISettingListItemTemplate, e: IListChangeEvent): string[] | undefined | null { + private computeNewList(template: ISettingListItemTemplate, e: ISettingListChangeEvent): string[] | undefined | null { if (template.context) { let newValue: string[] = []; if (isArray(template.context.scopeValue)) { @@ -808,23 +986,23 @@ export class SettingArrayRenderer extends AbstractSettingRenderer implements ITr if (e.targetIndex !== undefined) { // Delete value - if (!e.value && e.originalValue && e.targetIndex > -1) { + if (!e.item?.value && e.originalItem.value && e.targetIndex > -1) { newValue.splice(e.targetIndex, 1); } // Update value - else if (e.value && e.originalValue) { + else if (e.item?.value && e.originalItem.value) { if (e.targetIndex > -1) { - newValue[e.targetIndex] = e.value; + newValue[e.targetIndex] = e.item.value; } // For some reason, we are updating and cannot find original value // Just append the value in this case else { - newValue.push(e.value); + newValue.push(e.item.value); } } // Add value - else if (e.value && !e.originalValue && e.targetIndex >= newValue.length) { - newValue.push(e.value); + else if (e.item?.value && !e.originalItem.value && e.targetIndex >= newValue.length) { + newValue.push(e.item.value); } } if ( @@ -860,6 +1038,117 @@ export class SettingArrayRenderer extends AbstractSettingRenderer implements ITr } } +export class SettingObjectRenderer extends AbstractSettingRenderer implements ITreeRenderer { + templateId = SETTINGS_OBJECT_TEMPLATE_ID; + + renderTemplate(container: HTMLElement): ISettingObjectItemTemplate { + const common = this.renderCommonTemplate(null, container, 'list'); + + const objectWidget = this._instantiationService.createInstance(ObjectSettingWidget, common.controlElement); + objectWidget.domNode.classList.add(AbstractSettingRenderer.CONTROL_CLASS); + common.toDispose.add(objectWidget); + + const template: ISettingObjectItemTemplate = { + ...common, + objectWidget: objectWidget, + }; + + this.addSettingElementFocusHandler(template); + + common.toDispose.add(objectWidget.onDidChangeList(e => this.onDidChangeObject(template, e))); + + return template; + } + + private onDidChangeObject(template: ISettingObjectItemTemplate, e: ISettingListChangeEvent): void { + if (template.context) { + const defaultValue: Record = typeof template.context.defaultValue === 'object' + ? template.context.defaultValue ?? {} + : {}; + + const scopeValue: Record = typeof template.context.scopeValue === 'object' + ? template.context.scopeValue ?? {} + : {}; + + const newValue: Record = {}; + const newItems: IObjectDataItem[] = []; + + template.objectWidget.items.forEach((item, idx) => { + // Item was updated + if (isDefined(e.item) && e.targetIndex === idx) { + newValue[e.item.key.data] = e.item.value.data; + newItems.push(e.item); + } + // All remaining items, but skip the one that we just updated + else if (isUndefinedOrNull(e.item) || e.item.key.data !== item.key.data) { + newValue[item.key.data] = item.value.data; + newItems.push(item); + } + }); + + // Item was deleted + if (isUndefinedOrNull(e.item)) { + delete newValue[e.originalItem.key.data]; + + const itemToDelete = newItems.findIndex(item => item.key.data === e.originalItem.key.data); + const defaultItemValue = defaultValue[e.originalItem.key.data] as string | boolean; + + // Item does not have a default + if (isUndefinedOrNull(defaultValue[e.originalItem.key.data]) && itemToDelete > -1) { + newItems.splice(itemToDelete, 1); + } else if (itemToDelete > -1) { + newItems[itemToDelete].value.data = defaultItemValue; + } + } + // New item was added + else if (template.objectWidget.isItemNew(e.originalItem) && e.item.key.data !== '') { + newValue[e.item.key.data] = e.item.value.data; + newItems.push(e.item); + } + + Object.entries(newValue).forEach(([key, value]) => { + // value from the scope has changed back to the default + if (scopeValue[key] !== value && defaultValue[key] === value) { + delete newValue[key]; + } + }); + + this._onDidChangeSetting.fire({ + key: template.context.setting.key, + value: Object.keys(newValue).length === 0 ? undefined : newValue, + type: template.context.valueType + }); + + template.objectWidget.setValue(newItems); + } + } + + renderElement(element: ITreeNode, index: number, templateData: ISettingObjectItemTemplate): void { + super.renderSettingElement(element, index, templateData); + } + + protected renderValue(dataElement: SettingsTreeSettingElement, template: ISettingObjectItemTemplate, onChange: (value: string) => void): void { + const items = getObjectDisplayValue(dataElement); + const { key, objectProperties, objectPatternProperties, objectAdditionalProperties } = dataElement.setting; + + template.objectWidget.setValue(items, { + settingKey: key, + showAddButton: objectAdditionalProperties === false + ? ( + !areAllPropertiesDefined(Object.keys(objectProperties ?? {}), items) || + isDefined(objectPatternProperties) + ) + : true, + keySuggester: createObjectKeySuggester(dataElement), + valueSuggester: createObjectValueSuggester(dataElement), + }); + + this.setElementAriaLabels(dataElement, this.templateId, template); + + template.context = dataElement; + } +} + export class SettingExcludeRenderer extends AbstractSettingRenderer implements ITreeRenderer { templateId = SETTINGS_EXCLUDE_TEMPLATE_ID; @@ -882,27 +1171,27 @@ export class SettingExcludeRenderer extends AbstractSettingRenderer implements I return template; } - private onDidChangeExclude(template: ISettingExcludeItemTemplate, e: IListChangeEvent): void { + private onDidChangeExclude(template: ISettingExcludeItemTemplate, e: ISettingListChangeEvent): void { if (template.context) { const newValue = { ...template.context.scopeValue }; // first delete the existing entry, if present - if (e.originalValue) { - if (e.originalValue in template.context.defaultValue) { + if (e.originalItem.value) { + if (e.originalItem.value in template.context.defaultValue) { // delete a default by overriding it - newValue[e.originalValue] = false; + newValue[e.originalItem.value] = false; } else { - delete newValue[e.originalValue]; + delete newValue[e.originalItem.value]; } } // then add the new or updated entry, if present - if (e.value) { - if (e.value in template.context.defaultValue && !e.sibling) { + if (e.item?.value) { + if (e.item.value in template.context.defaultValue && !e.item.sibling) { // add a default by deleting its override - delete newValue[e.value]; + delete newValue[e.item.value]; } else { - newValue[e.value] = e.sibling ? { when: e.sibling } : true; + newValue[e.item.value] = e.item.sibling ? { when: e.item.sibling } : true; } } @@ -979,7 +1268,11 @@ export class SettingTextRenderer extends AbstractSettingRenderer implements ITre template.onChange = undefined; template.inputBox.value = dataElement.value; - template.onChange = value => { renderValidations(dataElement, template, false, label); onChange(value); }; + template.onChange = value => { + if (!renderValidations(dataElement, template, false, label)) { + onChange(value); + } + }; renderValidations(dataElement, template, true, label); } @@ -1052,7 +1345,7 @@ export class SettingEnumRenderer extends AbstractSettingRenderer implements ITre }, disposeables: disposables }, - decoratorRight: (data === dataElement.defaultValue ? localize('settings.Default', "{0}", 'default') : '') + decoratorRight: (data === dataElement.defaultValue ? localize('settings.Default', "default") : '') }); template.selectBox.setOptions(displayOptions); @@ -1126,8 +1419,9 @@ export class SettingNumberRenderer extends AbstractSettingRenderer implements IT template.onChange = undefined; template.inputBox.value = dataElement.value; template.onChange = value => { - renderValidations(dataElement, template, false, label); - onChange(nullNumParseFn(value)); + if (!renderValidations(dataElement, template, false, label)) { + onChange(nullNumParseFn(value)); + } }; renderValidations(dataElement, template, true, label); @@ -1256,7 +1550,7 @@ export class SettingTreeRenderers { @IInstantiationService private readonly _instantiationService: IInstantiationService, @IContextMenuService private readonly _contextMenuService: IContextMenuService, @IContextViewService private readonly _contextViewService: IContextViewService, - @IUserDataSyncEnablementService private readonly _userDataSyncEnablementService: IUserDataSyncEnablementService, + @IUserDataAutoSyncService private readonly _userDataAutoSyncService: IUserDataAutoSyncService, ) { this.settingActions = [ new Action('settings.resetSetting', localize('resetSettingLabel', "Reset Setting"), undefined, undefined, (context: SettingsTreeSettingElement) => { @@ -1275,12 +1569,12 @@ export class SettingTreeRenderers { const settingRenderers = [ this._instantiationService.createInstance(SettingBoolRenderer, this.settingActions, actionFactory), this._instantiationService.createInstance(SettingNumberRenderer, this.settingActions, actionFactory), - this._instantiationService.createInstance(SettingBoolRenderer, this.settingActions, actionFactory), this._instantiationService.createInstance(SettingArrayRenderer, this.settingActions, actionFactory), this._instantiationService.createInstance(SettingComplexRenderer, this.settingActions, actionFactory), this._instantiationService.createInstance(SettingTextRenderer, this.settingActions, actionFactory), this._instantiationService.createInstance(SettingExcludeRenderer, this.settingActions, actionFactory), this._instantiationService.createInstance(SettingEnumRenderer, this.settingActions, actionFactory), + this._instantiationService.createInstance(SettingObjectRenderer, this.settingActions, actionFactory), ]; this.onDidClickOverrideElement = Event.any(...settingRenderers.map(r => r.onDidClickOverrideElement)); @@ -1300,7 +1594,7 @@ export class SettingTreeRenderers { } private getActionsForSetting(setting: ISetting): IAction[] { - const enableSync = this._userDataSyncEnablementService.isEnabled(); + const enableSync = this._userDataAutoSyncService.isEnabled(); return enableSync && !setting.disallowSyncIgnore ? [ new Separator(), @@ -1348,7 +1642,10 @@ export class SettingTreeRenderers { } } -function renderValidations(dataElement: SettingsTreeSettingElement, template: ISettingTextItemTemplate, calledOnStartup: boolean, originalAriaLabel: string) { +/** + * Validate and render any error message. Returns true if the value is invalid. + */ +function renderValidations(dataElement: SettingsTreeSettingElement, template: ISettingTextItemTemplate, calledOnStartup: boolean, originalAriaLabel: string): boolean { if (dataElement.setting.validator) { const errMsg = dataElement.setting.validator(template.inputBox.value); if (errMsg) { @@ -1357,12 +1654,13 @@ function renderValidations(dataElement: SettingsTreeSettingElement, template: IS const validationError = localize('validationError', "Validation Error."); template.inputBox.inputElement.parentElement!.setAttribute('aria-label', [originalAriaLabel, validationError, errMsg].join(' ')); if (!calledOnStartup) { ariaAlert(validationError + ' ' + errMsg); } - return; + return true; } else { template.inputBox.inputElement.parentElement!.setAttribute('aria-label', originalAriaLabel); } } DOM.removeClass(template.containerElement, 'invalid-input'); + return false; } function renderArrayValidations( @@ -1515,6 +1813,10 @@ class SettingsTreeDelegate extends CachedListVirtualDelegate { viewState: ISettingsEditorViewState, renderers: ITreeRenderer[], @IThemeService themeService: IThemeService, + @IConfigurationService configurationService: IConfigurationService, @IInstantiationService instantiationService: IInstantiationService, ) { super('SettingsTree', container, @@ -1583,7 +1886,8 @@ export class SettingsTree extends ObjectTree { } }, styleController: id => new DefaultStyleController(DOM.createStyleSheet(container), id), - filter: instantiationService.createInstance(SettingsTreeFilter, viewState) + filter: instantiationService.createInstance(SettingsTreeFilter, viewState), + smoothScrolling: configurationService.getValue('workbench.list.smoothScrolling'), }); this.disposables.clear(); @@ -1658,9 +1962,17 @@ export class SettingsTree extends ObjectTree { }, colors => { this.style(colors); })); + + this.disposables.add(configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('workbench.list.smoothScrolling')) { + this.updateOptions({ + smoothScrolling: configurationService.getValue('workbench.list.smoothScrolling') + }); + } + })); } - protected createModel(user: string, view: ISpliceable>, options: IObjectTreeOptions): ITreeModel { + protected createModel(user: string, view: IList>, options: IObjectTreeOptions): ITreeModel { return new NonCollapsibleObjectTreeModel(user, view, options); } } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index 28913d73a0b..f68d3c6d14a 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -5,7 +5,7 @@ import * as arrays from 'vs/base/common/arrays'; import { isFalsyOrWhitespace } from 'vs/base/common/strings'; -import { isArray, withUndefinedAsNull } from 'vs/base/common/types'; +import { isArray, withUndefinedAsNull, isUndefinedOrNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { ConfigurationTarget, IConfigurationService, IConfigurationValue } from 'vs/platform/configuration/common/configuration'; @@ -15,6 +15,7 @@ import { MODIFIED_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/pr import { IExtensionSetting, ISearchResult, ISetting, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { FOLDER_SCOPES, WORKSPACE_SCOPES, REMOTE_MACHINE_SCOPES, LOCAL_MACHINE_SCOPES } from 'vs/workbench/services/configuration/common/configuration'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; export const ONLINE_SERVICES_SETTING_TAG = 'usesOnlineServices'; @@ -217,6 +218,8 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { } else { this.valueType = SettingValueType.Complex; } + } else if (isObjectSetting(this.setting)) { + this.valueType = SettingValueType.Object; } else { this.valueType = SettingValueType.Complex; } @@ -421,7 +424,7 @@ function wordifyKey(key: string): string { match; }); - for (let [k, v] of knownTermMappings) { + for (const [k, v] of knownTermMappings) { key = key.replace(new RegExp(`\\b${k}\\b`, 'gi'), v); } @@ -465,6 +468,44 @@ export function isExcludeSetting(setting: ISetting): boolean { setting.key === 'files.watcherExclude'; } +function isObjectRenderableSchema({ type }: IJSONSchema): boolean { + return type === 'string' || type === 'boolean'; +} + +function isObjectSetting({ + type, + objectProperties, + objectPatternProperties, + objectAdditionalProperties +}: ISetting): boolean { + if (type !== 'object') { + return false; + } + + // object can have any shape + if ( + isUndefinedOrNull(objectProperties) && + isUndefinedOrNull(objectPatternProperties) && + isUndefinedOrNull(objectAdditionalProperties) + ) { + return false; + } + + // object additional properties allow it to have any shape + if (objectAdditionalProperties === true) { + return false; + } + + const schemas = [...Object.values(objectProperties ?? {}), ...Object.values(objectPatternProperties ?? {})]; + + if (typeof objectAdditionalProperties === 'object') { + schemas.push(objectAdditionalProperties); + } + + // This should not render boolean only objects + return schemas.every(isObjectRenderableSchema) && schemas.some(({ type }) => type === 'string'); +} + function settingTypeEnumRenderable(_type: string | string[]) { const enumRenderableSettingTypes = ['string', 'boolean', 'null', 'integer', 'number']; const type = isArray(_type) ? _type : [_type]; @@ -590,7 +631,7 @@ const tagRegex = /(^|\s)@tag:("([^"]*)"|[^"]\S*)/g; const extensionRegex = /(^|\s)@ext:("([^"]*)"|[^"]\S*)?/g; export function parseQuery(query: string): IParsedQuery { const tags: string[] = []; - let extensions: string[] = []; + const extensions: string[] = []; query = query.replace(tagRegex, (_, __, quotedTag, tag) => { tags.push(tag || quotedTag); return ''; @@ -602,7 +643,7 @@ export function parseQuery(query: string): IParsedQuery { }); query = query.replace(extensionRegex, (_, __, quotedExtensionId, extensionId) => { - let extensionIdQuery: string = extensionId || quotedExtensionId; + const extensionIdQuery: string = extensionId || quotedExtensionId; if (extensionIdQuery) { extensions.push(...extensionIdQuery.split(',').map(s => s.trim()).filter(s => !isFalsyOrWhitespace(s))); } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts index a7a92ddecfb..35ef37de64e 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts @@ -17,11 +17,14 @@ import 'vs/css!./media/settingsWidgets'; import { localize } from 'vs/nls'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { foreground, inputBackground, inputBorder, inputForeground, listActiveSelectionBackground, listActiveSelectionForeground, listHoverBackground, listHoverForeground, listInactiveSelectionBackground, listInactiveSelectionForeground, registerColor, selectBackground, selectBorder, selectForeground, textLinkForeground, textPreformatForeground, editorWidgetBorder, textLinkActiveForeground, simpleCheckboxBackground, simpleCheckboxForeground, simpleCheckboxBorder } from 'vs/platform/theme/common/colorRegistry'; -import { attachButtonStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler'; +import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { disposableTimeout } from 'vs/base/common/async'; -import { isUndefinedOrNull } from 'vs/base/common/types'; +import { isUndefinedOrNull, isDefined } from 'vs/base/common/types'; import { preferencesEditIcon } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; +import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; +import { isIOS } from 'vs/base/common/platform'; +import { BrowserFeatures } from 'vs/base/browser/canIUse'; const $ = DOM.$; export const settingsHeaderForeground = registerColor('settings.headerForeground', { light: '#444444', dark: '#e7e7e7', hc: '#ffffff' }, localize('headerForeground', "The foreground color for a section header or active title.")); @@ -140,15 +143,21 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) = type EditKey = 'none' | 'create' | number; -export class ListSettingListModel { - private _dataItems: IListDataItem[] = []; +type IListViewItem = TDataItem & { + editing?: boolean; + selected?: boolean; +}; + +export class ListSettingListModel { + protected _dataItems: TDataItem[] = []; private _editKey: EditKey | null = null; private _selectedIdx: number | null = null; + private _newDataItem: TDataItem; - get items(): IListViewItem[] { + get items(): IListViewItem[] { const items = this._dataItems.map((item, i) => { const editing = typeof this._editKey === 'number' && this._editKey === i; - return { + return { ...item, editing, selected: i === this._selectedIdx || editing @@ -159,23 +168,26 @@ export class ListSettingListModel { items.push({ editing: true, selected: true, - value: '', - sibling: '' + ...this._newDataItem, }); } return items; } + constructor(newItem: TDataItem) { + this._newDataItem = newItem; + } + setEditKey(key: EditKey): void { this._editKey = key; } - setValue(listData: IListDataItem[]): void { + setValue(listData: TDataItem[]): void { this._dataItems = listData; } - select(idx: number): void { + select(idx: number | null): void { this._selectedIdx = idx; } @@ -200,30 +212,34 @@ export class ListSettingListModel { } } -export interface IListChangeEvent { - originalValue: string; - value?: string; - sibling?: string; +export interface ISettingListChangeEvent { + originalItem: TDataItem; + item?: TDataItem; targetIndex?: number; } -export class ListSettingWidget extends Disposable { +abstract class AbstractListSettingWidget extends Disposable { private listElement: HTMLElement; - private readonly listDisposables = this._register(new DisposableStore()); + private rowElements: HTMLElement[] = []; - private model = new ListSettingListModel(); + protected readonly _onDidChangeList = this._register(new Emitter>()); + protected readonly model = new ListSettingListModel(this.getEmptyItem()); + protected readonly listDisposables = this._register(new DisposableStore()); - private readonly _onDidChangeList = this._register(new Emitter()); - readonly onDidChangeList: Event = this._onDidChangeList.event; + readonly onDidChangeList: Event> = this._onDidChangeList.event; get domNode(): HTMLElement { return this.listElement; } + get items(): TDataItem[] { + return this.model.items; + } + constructor( private container: HTMLElement, - @IThemeService private readonly themeService: IThemeService, - @IContextViewService private readonly contextViewService: IContextViewService + @IThemeService protected readonly themeService: IThemeService, + @IContextViewService protected readonly contextViewService: IContextViewService ) { super(); @@ -236,54 +252,138 @@ export class ListSettingWidget extends Disposable { this._register(DOM.addDisposableListener(this.listElement, DOM.EventType.CLICK, e => this.onListClick(e))); this._register(DOM.addDisposableListener(this.listElement, DOM.EventType.DBLCLICK, e => this.onListDoubleClick(e))); - this._register(DOM.addStandardDisposableListener(this.listElement, 'keydown', (e: KeyboardEvent) => { - if (e.keyCode === KeyCode.UpArrow) { - const selectedIndex = this.model.getSelected(); - this.model.selectPrevious(); - if (this.model.getSelected() !== selectedIndex) { - this.renderList(); - } - e.preventDefault(); - e.stopPropagation(); - } else if (e.keyCode === KeyCode.DownArrow) { - const selectedIndex = this.model.getSelected(); - this.model.selectNext(); - if (this.model.getSelected() !== selectedIndex) { - this.renderList(); - } - e.preventDefault(); - e.stopPropagation(); + this._register(DOM.addStandardDisposableListener(this.listElement, 'keydown', (e: StandardKeyboardEvent) => { + if (e.equals(KeyCode.UpArrow)) { + this.selectPreviousRow(); + } else if (e.equals(KeyCode.DownArrow)) { + this.selectNextRow(); + } else { + return; } + + e.preventDefault(); + e.stopPropagation(); })); } - protected getLocalizedStrings() { - return { - deleteActionTooltip: localize('removeItem', "Remove Item"), - editActionTooltip: localize('editItem', "Edit Item"), - complexEditActionTooltip: localize('editItemInSettingsJson', "Edit Item in settings.json"), - addButtonLabel: localize('addItem', "Add Item"), - inputPlaceholder: localize('itemInputPlaceholder', "String Item..."), - siblingInputPlaceholder: localize('listSiblingInputPlaceholder', "Sibling...") - }; - } - - protected getSettingListRowLocalizedStrings(value?: string, sibling?: string) { - return { - settingListRowValueHintLabel: localize('listValueHintLabel', "List item `{0}`", value), - settingListRowSiblingHintLabel: localize('listSiblingHintLabel', "List item `{0}` with sibling `${1}`", value) - }; - } - - protected getContainerClasses() { - return ['setting-list-widget']; - } - - setValue(listData: IListDataItem[]): void { + setValue(listData: TDataItem[]): void { this.model.setValue(listData); this.renderList(); } + protected abstract getEmptyItem(): TDataItem; + protected abstract getContainerClasses(): string[]; + protected abstract getActionsForItem(item: TDataItem, idx: number): IAction[]; + protected abstract renderItem(item: TDataItem): HTMLElement; + protected abstract renderEdit(item: TDataItem, idx: number): HTMLElement; + protected abstract isItemNew(item: TDataItem): boolean; + protected abstract getLocalizedRowTitle(item: TDataItem): string; + protected abstract getLocalizedStrings(): { + deleteActionTooltip: string + editActionTooltip: string + addButtonLabel: string + }; + + protected renderHeader(): HTMLElement | undefined { + return; + } + + protected isAddButtonVisible(): boolean { + return true; + } + + protected renderList(): void { + const focused = DOM.isAncestor(document.activeElement, this.listElement); + + DOM.clearNode(this.listElement); + this.listDisposables.clear(); + + const newMode = this.model.items.some(item => !!(item.editing && this.isItemNew(item))); + DOM.toggleClass(this.container, 'setting-list-hide-add-button', !this.isAddButtonVisible() || newMode); + + const header = this.renderHeader(); + const ITEM_HEIGHT = 24; + let listHeight = ITEM_HEIGHT * this.model.items.length; + + if (header) { + listHeight += ITEM_HEIGHT; + this.listElement.appendChild(header); + } + + this.rowElements = this.model.items.map((item, i) => this.renderDataOrEditItem(item, i, focused)); + this.rowElements.forEach(rowElement => this.listElement.appendChild(rowElement)); + + this.listElement.style.height = listHeight + 'px'; + } + + protected editSetting(idx: number): void { + this.model.setEditKey(idx); + this.renderList(); + } + + protected cancelEdit(): void { + this.model.setEditKey('none'); + this.renderList(); + } + + protected handleItemChange(originalItem: TDataItem, changedItem: TDataItem, idx: number) { + this.model.setEditKey('none'); + + this._onDidChangeList.fire({ + originalItem, + item: changedItem, + targetIndex: idx, + }); + + this.renderList(); + } + + private renderDataOrEditItem(item: IListViewItem, idx: number, listFocused: boolean): HTMLElement { + const rowElement = item.editing ? + this.renderEdit(item, idx) : + this.renderDataItem(item, idx, listFocused); + + rowElement.setAttribute('role', 'listitem'); + + return rowElement; + } + + private renderDataItem(item: IListViewItem, idx: number, listFocused: boolean): HTMLElement { + const rowElement = this.renderItem(item); + + rowElement.setAttribute('data-index', idx + ''); + rowElement.setAttribute('tabindex', item.selected ? '0' : '-1'); + DOM.toggleClass(rowElement, 'selected', item.selected); + + const actionBar = new ActionBar(rowElement); + this.listDisposables.add(actionBar); + + actionBar.push(this.getActionsForItem(item, idx), { icon: true, label: true }); + rowElement.title = this.getLocalizedRowTitle(item); + + if (item.selected && listFocused) { + this.listDisposables.add(disposableTimeout(() => rowElement.focus())); + } + + return rowElement; + } + + private renderAddButton(): HTMLElement { + const rowElement = $('.setting-list-new-row'); + + const startAddButton = this._register(new Button(rowElement)); + startAddButton.label = this.getLocalizedStrings().addButtonLabel; + startAddButton.element.classList.add('setting-list-addButton'); + this._register(attachButtonStyler(startAddButton, this.themeService)); + + this._register(startAddButton.onDidClick(() => { + this.model.setEditKey('create'); + this.renderList(); + })); + + return rowElement; + } + private onListClick(e: MouseEvent): void { const targetIdx = this.getClickedItemIndex(e); if (targetIdx < 0) { @@ -294,8 +394,7 @@ export class ListSettingWidget extends Disposable { return; } - this.model.select(targetIdx); - this.renderList(); + this.selectRow(targetIdx); e.preventDefault(); e.stopPropagation(); } @@ -339,131 +438,87 @@ export class ListSettingWidget extends Disposable { return targetIdx; } - private renderList(): void { - const focused = DOM.isAncestor(document.activeElement, this.listElement); + private selectRow(idx: number): void { + this.model.select(idx); + this.rowElements.forEach(row => row.classList.remove('selected')); - DOM.clearNode(this.listElement); - this.listDisposables.clear(); + const selectedRow = this.rowElements[this.model.getSelected()!]; - const newMode = this.model.items.some(item => !!(item.editing && !item.value)); - DOM.toggleClass(this.container, 'setting-list-new-mode', newMode); - - this.model.items - .map((item, i) => this.renderItem(item, i, focused)) - .forEach(itemElement => this.listElement.appendChild(itemElement)); - - const listHeight = 24 * this.model.items.length; - this.listElement.style.height = listHeight + 'px'; + selectedRow.classList.add('selected'); + selectedRow.focus(); } - private createDeleteAction(key: string, idx: number): IAction { - return { - class: 'codicon-close', - enabled: true, - id: 'workbench.action.removeListItem', - tooltip: this.getLocalizedStrings().deleteActionTooltip, - run: () => this._onDidChangeList.fire({ originalValue: key, value: undefined, targetIndex: idx }) - }; + private selectNextRow(): void { + this.model.selectNext(); + this.selectRow(this.model.getSelected()!); } - private createEditAction(idx: number): IAction { - return { - class: preferencesEditIcon.classNames, - enabled: true, - id: 'workbench.action.editListItem', - tooltip: this.getLocalizedStrings().editActionTooltip, - run: () => { - this.editSetting(idx); + private selectPreviousRow(): void { + this.model.selectPrevious(); + this.selectRow(this.model.getSelected()!); + } +} + +export interface IListDataItem { + value: string + sibling?: string +} + +export class ListSettingWidget extends AbstractListSettingWidget { + protected getEmptyItem(): IListDataItem { + return { value: '' }; + } + + protected getContainerClasses(): string[] { + return ['setting-list-widget']; + } + + protected getActionsForItem(item: IListDataItem, idx: number): IAction[] { + return [ + { + class: preferencesEditIcon.classNames, + enabled: true, + id: 'workbench.action.editListItem', + tooltip: this.getLocalizedStrings().editActionTooltip, + run: () => this.editSetting(idx) + }, + { + class: 'codicon-close', + enabled: true, + id: 'workbench.action.removeListItem', + tooltip: this.getLocalizedStrings().deleteActionTooltip, + run: () => this._onDidChangeList.fire({ originalItem: item, item: undefined, targetIndex: idx }) } - }; + ] as IAction[]; } - private editSetting(idx: number): void { - this.model.setEditKey(idx); - this.renderList(); - } - - private renderItem(item: IListViewItem, idx: number, listFocused: boolean): HTMLElement { - return item.editing ? - this.renderEditItem(item, idx) : - this.renderDataItem(item, idx, listFocused); - } - - private renderDataItem(item: IListViewItem, idx: number, listFocused: boolean): HTMLElement { + protected renderItem(item: IListDataItem): HTMLElement { const rowElement = $('.setting-list-row'); - rowElement.setAttribute('data-index', idx + ''); - rowElement.setAttribute('tabindex', item.selected ? '0' : '-1'); - DOM.toggleClass(rowElement, 'selected', item.selected); - - const actionBar = new ActionBar(rowElement); - this.listDisposables.add(actionBar); - const valueElement = DOM.append(rowElement, $('.setting-list-value')); const siblingElement = DOM.append(rowElement, $('.setting-list-sibling')); + valueElement.textContent = item.value; - siblingElement.textContent = item.sibling ? ('when: ' + item.sibling) : null; - - actionBar.push([ - this.createEditAction(idx), - this.createDeleteAction(item.value, idx) - ], { icon: true, label: false }); - - rowElement.title = item.sibling - ? this.getSettingListRowLocalizedStrings(item.value, item.sibling).settingListRowSiblingHintLabel - : this.getSettingListRowLocalizedStrings(item.value, item.sibling).settingListRowValueHintLabel; - - if (item.selected) { - if (listFocused) { - setTimeout(() => { - rowElement.focus(); - }, 10); - } - } + siblingElement.textContent = item.sibling ? `when: ${item.sibling}` : null; return rowElement; } - private renderAddButton(): HTMLElement { - const rowElement = $('.setting-list-new-row'); - - const startAddButton = this._register(new Button(rowElement)); - startAddButton.label = this.getLocalizedStrings().addButtonLabel; - startAddButton.element.classList.add('setting-list-addButton'); - this._register(attachButtonStyler(startAddButton, this.themeService)); - - this._register(startAddButton.onDidClick(() => { - this.model.setEditKey('create'); - this.renderList(); - })); - - return rowElement; - } - - private renderEditItem(item: IListViewItem, idx: number): HTMLElement { + protected renderEdit(item: IListDataItem, idx: number): HTMLElement { const rowElement = $('.setting-list-edit-row'); - const onSubmit = (edited: boolean) => { - this.model.setEditKey('none'); - const value = valueInput.value; - if (edited && !isUndefinedOrNull(value)) { - this._onDidChangeList.fire({ - originalValue: item.value, - value: value, - sibling: siblingInput && siblingInput.value, - targetIndex: idx - }); - } - this.renderList(); - }; + const updatedItem = () => ({ + value: valueInput.value, + sibling: siblingInput?.value + }); - const onKeydown = (e: StandardKeyboardEvent) => { + const onKeyDown = (e: StandardKeyboardEvent) => { if (e.equals(KeyCode.Enter)) { - onSubmit(true); + this.handleItemChange(item, updatedItem(), idx); } else if (e.equals(KeyCode.Escape)) { - onSubmit(false); + this.cancelEdit(); e.preventDefault(); } - rowElement.focus(); + rowElement?.focus(); }; const valueInput = new InputBox(rowElement, this.contextViewService, { @@ -478,10 +533,13 @@ export class ListSettingWidget extends Disposable { })); this.listDisposables.add(valueInput); valueInput.value = item.value; - this.listDisposables.add(DOM.addStandardDisposableListener(valueInput.inputElement, DOM.EventType.KEY_DOWN, onKeydown)); - let siblingInput: InputBox; - if (item.sibling) { + this.listDisposables.add( + DOM.addStandardDisposableListener(valueInput.inputElement, DOM.EventType.KEY_DOWN, onKeyDown) + ); + + let siblingInput: InputBox | undefined; + if (!isUndefinedOrNull(item.sibling)) { siblingInput = new InputBox(rowElement, this.contextViewService, { placeholder: this.getLocalizedStrings().siblingInputPlaceholder }); @@ -493,61 +551,478 @@ export class ListSettingWidget extends Disposable { inputBorder: settingsTextInputBorder })); siblingInput.value = item.sibling; - this.listDisposables.add(DOM.addStandardDisposableListener(siblingInput.inputElement, DOM.EventType.KEY_DOWN, onKeydown)); + + this.listDisposables.add( + DOM.addStandardDisposableListener(siblingInput.inputElement, DOM.EventType.KEY_DOWN, onKeyDown) + ); } const okButton = this._register(new Button(rowElement)); okButton.label = localize('okButton', "OK"); - okButton.element.classList.add('setting-list-okButton'); + okButton.element.classList.add('setting-list-ok-button'); + this.listDisposables.add(attachButtonStyler(okButton, this.themeService)); - this.listDisposables.add(okButton.onDidClick(() => onSubmit(true))); + this.listDisposables.add(okButton.onDidClick(() => this.handleItemChange(item, updatedItem(), idx))); const cancelButton = this._register(new Button(rowElement)); cancelButton.label = localize('cancelButton', "Cancel"); - cancelButton.element.classList.add('setting-list-okButton'); + cancelButton.element.classList.add('setting-list-cancel-button'); + this.listDisposables.add(attachButtonStyler(cancelButton, this.themeService)); - this.listDisposables.add(cancelButton.onDidClick(() => onSubmit(false))); + this.listDisposables.add(cancelButton.onDidClick(() => this.cancelEdit())); this.listDisposables.add( disposableTimeout(() => { valueInput.focus(); valueInput.select(); - })); + }) + ); return rowElement; } + + protected isItemNew(item: IListDataItem): boolean { + return item.value === ''; + } + + protected getLocalizedRowTitle({ value, sibling }: IListDataItem): string { + return isUndefinedOrNull(sibling) + ? localize('listValueHintLabel', "List item `{0}`", value) + : localize('listSiblingHintLabel', "List item `{0}` with sibling `${1}`", value, sibling); + } + + protected getLocalizedStrings() { + return { + deleteActionTooltip: localize('removeItem', "Remove Item"), + editActionTooltip: localize('editItem', "Edit Item"), + addButtonLabel: localize('addItem', "Add Item"), + inputPlaceholder: localize('itemInputPlaceholder', "String Item..."), + siblingInputPlaceholder: localize('listSiblingInputPlaceholder', "Sibling..."), + }; + } } export class ExcludeSettingWidget extends ListSettingWidget { + protected getContainerClasses() { + return ['setting-list-exclude-widget']; + } + + protected getLocalizedRowTitle({ value, sibling }: IListDataItem): string { + return isUndefinedOrNull(sibling) + ? localize('excludePatternHintLabel', "Exclude files matching `{0}`", value) + : localize('excludeSiblingHintLabel', "Exclude files matching `{0}`, only when a file matching `{1}` is present", value, sibling); + } + protected getLocalizedStrings() { return { deleteActionTooltip: localize('removeExcludeItem', "Remove Exclude Item"), editActionTooltip: localize('editExcludeItem', "Edit Exclude Item"), - complexEditActionTooltip: localize('editExcludeItemInSettingsJson', "Edit Exclude Item in settings.json"), addButtonLabel: localize('addPattern', "Add Pattern"), inputPlaceholder: localize('excludePatternInputPlaceholder', "Exclude Pattern..."), - siblingInputPlaceholder: localize('excludeSiblingInputPlaceholder', "When Pattern Is Present...") + siblingInputPlaceholder: localize('excludeSiblingInputPlaceholder', "When Pattern Is Present..."), }; } +} - protected getSettingListRowLocalizedStrings(pattern?: string, sibling?: string) { +interface IObjectStringData { + type: 'string'; + data: string; +} + +export interface IObjectEnumOption { + value: string; + description?: string +} + +interface IObjectEnumData { + type: 'enum'; + data: string; + options: IObjectEnumOption[]; +} + +interface IObjectBoolData { + type: 'boolean'; + data: boolean; +} + +type ObjectKey = IObjectStringData | IObjectEnumData; +export type ObjectValue = IObjectStringData | IObjectEnumData | IObjectBoolData; + +export interface IObjectDataItem { + key: ObjectKey; + value: ObjectValue; + removable: boolean; +} + +export interface IObjectValueSuggester { + (key: string): ObjectValue | undefined; +} + +export interface IObjectKeySuggester { + (existingKeys: string[]): IObjectEnumData | undefined; +} + +interface IObjectSetValueOptions { + settingKey: string; + showAddButton: boolean; + keySuggester: IObjectKeySuggester; + valueSuggester: IObjectValueSuggester; +} + +interface IObjectRenderEditWidgetOptions { + isKey: boolean; + idx: number; + readonly originalItem: IObjectDataItem; + readonly changedItem: IObjectDataItem; + update(keyOrValue: ObjectKey | ObjectValue): void; +} + +export class ObjectSettingWidget extends AbstractListSettingWidget { + private currentSettingKey: string = ''; + private showAddButton: boolean = true; + private keySuggester: IObjectKeySuggester = () => undefined; + private valueSuggester: IObjectValueSuggester = () => undefined; + + setValue(listData: IObjectDataItem[], options?: IObjectSetValueOptions): void { + this.showAddButton = options?.showAddButton ?? this.showAddButton; + this.keySuggester = options?.keySuggester ?? this.keySuggester; + this.valueSuggester = options?.valueSuggester ?? this.valueSuggester; + + if (isDefined(options) && options.settingKey !== this.currentSettingKey) { + this.model.setEditKey('none'); + this.model.select(null); + this.currentSettingKey = options.settingKey; + } + + super.setValue(listData); + } + + isItemNew(item: IObjectDataItem): boolean { + return item.key.data === '' && item.value.data === ''; + } + + protected isAddButtonVisible(): boolean { + return this.showAddButton; + } + + protected getEmptyItem(): IObjectDataItem { return { - settingListRowValueHintLabel: localize('excludePatternHintLabel', "Exclude files matching `{0}`", pattern), - settingListRowSiblingHintLabel: localize('excludeSiblingHintLabel', "Exclude files matching `{0}`, only when a file matching `{1}` is present", pattern, sibling) + key: { type: 'string', data: '' }, + value: { type: 'string', data: '' }, + removable: true, }; } protected getContainerClasses() { - return ['setting-list-exclude-widget']; + return ['setting-list-object-widget']; + } + + protected getActionsForItem(item: IObjectDataItem, idx: number): IAction[] { + const actions = [ + { + class: preferencesEditIcon.classNames, + enabled: true, + id: 'workbench.action.editListItem', + tooltip: this.getLocalizedStrings().editActionTooltip, + run: () => this.editSetting(idx) + }, + ] as IAction[]; + + if (item.removable) { + actions.push({ + class: 'codicon-close', + enabled: true, + id: 'workbench.action.removeListItem', + tooltip: this.getLocalizedStrings().deleteActionTooltip, + run: () => this._onDidChangeList.fire({ originalItem: item, item: undefined, targetIndex: idx }) + } as IAction); + } else { + actions.push({ + class: 'codicon-discard', + enabled: true, + id: 'workbench.action.resetListItem', + tooltip: this.getLocalizedStrings().resetActionTooltip, + run: () => this._onDidChangeList.fire({ originalItem: item, item: undefined, targetIndex: idx }) + } as IAction); + } + + return actions; + } + + protected renderHeader() { + const header = $('.setting-list-row-header'); + const keyHeader = DOM.append(header, $('.setting-list-object-key')); + const valueHeader = DOM.append(header, $('.setting-list-object-value')); + const { keyHeaderText, valueHeaderText } = this.getLocalizedStrings(); + + keyHeader.textContent = keyHeaderText; + valueHeader.textContent = valueHeaderText; + + return header; + } + + protected renderItem(item: IObjectDataItem): HTMLElement { + const rowElement = $('.setting-list-row'); + rowElement.classList.add('setting-list-object-row'); + + const keyElement = DOM.append(rowElement, $('.setting-list-object-key')); + const valueElement = DOM.append(rowElement, $('.setting-list-object-value')); + + keyElement.textContent = item.key.data; + valueElement.textContent = item.value.data.toString(); + + return rowElement; + } + + protected renderEdit(item: IObjectDataItem, idx: number): HTMLElement { + const rowElement = $('.setting-list-edit-row.setting-list-object-row'); + + const changedItem = { ...item }; + const onKeyChange = (key: ObjectKey) => { + changedItem.key = key; + + const suggestedValue = this.valueSuggester(key.data) ?? item.value; + + if (this.shouldUseSuggestion(item.value, changedItem.value, suggestedValue)) { + onValueChange(suggestedValue); + renderLatestValue(); + } + }; + const onValueChange = (value: ObjectValue) => { + changedItem.value = value; + }; + + let keyWidget: InputBox | SelectBox | undefined; + let keyElement: HTMLElement; + + if (this.showAddButton) { + if (this.isItemNew(item)) { + const suggestedKey = this.keySuggester(this.model.items.map(({ key: { data } }) => data)); + + if (isDefined(suggestedKey)) { + changedItem.key = suggestedKey; + const suggestedValue = this.valueSuggester(changedItem.key.data); + onValueChange(suggestedValue ?? changedItem.value); + } + } + + const { widget, element } = this.renderEditWidget(changedItem.key, { + idx, + isKey: true, + originalItem: item, + changedItem, + update: onKeyChange, + }); + keyWidget = widget; + keyElement = element; + } else { + keyElement = $('.setting-list-object-key'); + keyElement.textContent = item.key.data; + } + + let valueWidget: InputBox | SelectBox; + const valueContainer = $('.setting-list-object-value-container'); + + const renderLatestValue = () => { + const { widget, element } = this.renderEditWidget(changedItem.value, { + idx, + isKey: false, + originalItem: item, + changedItem, + update: onValueChange, + }); + + valueWidget = widget; + + DOM.clearNode(valueContainer); + valueContainer.append(element); + }; + + renderLatestValue(); + + rowElement.append(keyElement, valueContainer); + + const okButton = this._register(new Button(rowElement)); + okButton.label = localize('okButton', "OK"); + okButton.element.classList.add('setting-list-ok-button'); + + this.listDisposables.add(attachButtonStyler(okButton, this.themeService)); + this.listDisposables.add(okButton.onDidClick(() => this.handleItemChange(item, changedItem, idx))); + + const cancelButton = this._register(new Button(rowElement)); + cancelButton.label = localize('cancelButton', "Cancel"); + cancelButton.element.classList.add('setting-list-cancel-button'); + + this.listDisposables.add(attachButtonStyler(cancelButton, this.themeService)); + this.listDisposables.add(cancelButton.onDidClick(() => this.cancelEdit())); + + this.listDisposables.add( + disposableTimeout(() => { + const widget = keyWidget ?? valueWidget; + + widget.focus(); + + if (widget instanceof InputBox) { + widget.select(); + } + }) + ); + + return rowElement; + } + + private renderEditWidget( + keyOrValue: ObjectKey | ObjectValue, + options: IObjectRenderEditWidgetOptions, + ) { + switch (keyOrValue.type) { + case 'string': + return this.renderStringEditWidget(keyOrValue, options); + case 'enum': + return this.renderEnumEditWidget(keyOrValue, options); + case 'boolean': + return this.renderEnumEditWidget( + { + type: 'enum', + data: keyOrValue.data.toString(), + options: [{ value: 'true' }, { value: 'false' }], + }, + options, + ); + } + } + + private renderStringEditWidget( + keyOrValue: IObjectStringData, + { idx, isKey, originalItem, changedItem, update }: IObjectRenderEditWidgetOptions, + ) { + const wrapper = $(isKey ? '.setting-list-object-input-key' : '.setting-list-object-input-value'); + const inputBox = new InputBox(wrapper, this.contextViewService, { + placeholder: isKey + ? localize('objectKeyInputPlaceholder', "Key") + : localize('objectValueInputPlaceholder', "Value"), + }); + + inputBox.element.classList.add('setting-list-object-input'); + + this.listDisposables.add(attachInputBoxStyler(inputBox, this.themeService, { + inputBackground: settingsTextInputBackground, + inputForeground: settingsTextInputForeground, + inputBorder: settingsTextInputBorder + })); + this.listDisposables.add(inputBox); + inputBox.value = keyOrValue.data; + + this.listDisposables.add(inputBox.onDidChange(value => update({ ...keyOrValue, data: value }))); + + const onKeyDown = (e: StandardKeyboardEvent) => { + if (e.equals(KeyCode.Enter)) { + this.handleItemChange(originalItem, changedItem, idx); + } else if (e.equals(KeyCode.Escape)) { + this.cancelEdit(); + e.preventDefault(); + } + }; + + this.listDisposables.add( + DOM.addStandardDisposableListener(inputBox.inputElement, DOM.EventType.KEY_DOWN, onKeyDown) + ); + + return { widget: inputBox, element: wrapper }; + } + + private renderEnumEditWidget( + keyOrValue: IObjectEnumData, + { isKey, originalItem, update }: IObjectRenderEditWidgetOptions, + ) { + const selectBoxOptions = keyOrValue.options.map(({ value, description }) => ({ text: value, description })); + const selected = keyOrValue.options.findIndex(option => keyOrValue.data === option.value); + + const selectBox = new SelectBox(selectBoxOptions, selected, this.contextViewService, undefined, { + useCustomDrawn: !(isIOS && BrowserFeatures.pointerEvents) + }); + + this.listDisposables.add(attachSelectBoxStyler(selectBox, this.themeService, { + selectBackground: settingsSelectBackground, + selectForeground: settingsSelectForeground, + selectBorder: settingsSelectBorder, + selectListBorder: settingsSelectListBorder + })); + + const originalKeyOrValue = isKey ? originalItem.key : originalItem.value; + + this.listDisposables.add( + selectBox.onDidSelect(({ selected }) => + update( + originalKeyOrValue.type === 'boolean' + ? { ...originalKeyOrValue, data: selected === 'true' ? true : false } + : { ...originalKeyOrValue, data: selected }, + ) + ) + ); + + const wrapper = $('.setting-list-object-input'); + wrapper.classList.add( + isKey ? 'setting-list-object-input-key' : 'setting-list-object-input-value', + ); + + selectBox.render(wrapper); + + return { widget: selectBox, element: wrapper }; + } + + private shouldUseSuggestion(originalValue: ObjectValue, previousValue: ObjectValue, newValue: ObjectValue): boolean { + // suggestion is exactly the same + if (newValue.type !== 'enum' && newValue.type === previousValue.type && newValue.data === previousValue.data) { + return false; + } + + // item is new, use suggestion + if (originalValue.data === '') { + return true; + } + + if (previousValue.type === newValue.type && newValue.type !== 'enum') { + return false; + } + + // check if all enum options are the same + if (previousValue.type === 'enum' && newValue.type === 'enum') { + const previousEnums = new Set(previousValue.options.map(({ value }) => value)); + newValue.options.forEach(({ value }) => previousEnums.delete(value)); + + // all options are the same + if (previousEnums.size === 0) { + return false; + } + } + + return true; + } + + protected getLocalizedRowTitle(item: IObjectDataItem): string { + let enumDescription = item.key.type === 'enum' + ? item.key.options.find(({ value }) => item.key.data === value)?.description + : undefined; + + // avoid rendering double '.' + if (isDefined(enumDescription) && enumDescription.endsWith('.')) { + enumDescription = enumDescription.slice(0, enumDescription.length - 1); + } + + return isDefined(enumDescription) + ? `${enumDescription}. Currently set to ${item.value.data}.` + : localize('objectPairHintLabel', "The property `{0}` is set to `{1}`.", item.key.data, item.value.data); + } + + protected getLocalizedStrings() { + return { + deleteActionTooltip: localize('removeItem', "Remove Item"), + resetActionTooltip: localize('resetItem', "Reset Item"), + editActionTooltip: localize('editItem', "Edit Item"), + addButtonLabel: localize('addItem', "Add Item"), + keyHeaderText: localize('objectKeyHeader', "Item"), + valueHeaderText: localize('objectValueHeader', "Value"), + }; } } - -export interface IListDataItem { - value: string; - sibling?: string; -} - -interface IListViewItem extends IListDataItem { - editing?: boolean; - selected?: boolean; -} diff --git a/src/vs/workbench/contrib/preferences/browser/tocTree.ts b/src/vs/workbench/contrib/preferences/browser/tocTree.ts index 70189937d1c..b8bc079ae71 100644 --- a/src/vs/workbench/contrib/preferences/browser/tocTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/tocTree.ts @@ -157,7 +157,11 @@ export function createTOCIterator(model: TOCTreeModel | SettingsTreeGroupElement class SettingsAccessibilityProvider implements IListAccessibilityProvider { getWidgetAriaLabel(): string { - return localize('settingsTOC', "Settings Table of Contents"); + return localize({ + key: 'settingsTOC', + comment: ['A label for the table of contents for the full settings list'] + }, + "Settings Table of Contents"); } getAriaLabel(element: SettingsTreeElement): string { diff --git a/src/vs/workbench/contrib/preferences/common/preferences.ts b/src/vs/workbench/contrib/preferences/common/preferences.ts index df315b95efa..b318d190d27 100644 --- a/src/vs/workbench/contrib/preferences/common/preferences.ts +++ b/src/vs/workbench/contrib/preferences/common/preferences.ts @@ -33,7 +33,7 @@ export interface IEndpointDetails { export const IPreferencesSearchService = createDecorator('preferencesSearchService'); export interface IPreferencesSearchService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; getLocalSearchProvider(filter: string): ISearchProvider; getRemoteSearchProvider(filter: string, newExtensionsOnly?: boolean): ISearchProvider | undefined; @@ -66,6 +66,9 @@ export interface IKeybindingsEditorPane extends IEditorPane { showSimilarKeybindings(keybindingEntry: IKeybindingItemEntry): void; } +export const SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS = 'settings.action.clearSearchResults'; +export const SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU = 'settings.action.showContextMenu'; + export const CONTEXT_SETTINGS_EDITOR = new RawContextKey('inSettingsEditor', false); export const CONTEXT_SETTINGS_JSON_EDITOR = new RawContextKey('inSettingsJSONEditor', false); export const CONTEXT_SETTINGS_SEARCH_FOCUS = new RawContextKey('inSettingsSearch', false); @@ -74,21 +77,6 @@ export const CONTEXT_KEYBINDINGS_EDITOR = new RawContextKey('inKeybindi export const CONTEXT_KEYBINDINGS_SEARCH_FOCUS = new RawContextKey('inKeybindingsSearch', false); export const CONTEXT_KEYBINDING_FOCUS = new RawContextKey('keybindingFocus', false); -export const SETTINGS_EDITOR_COMMAND_SEARCH = 'settings.action.search'; -export const SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS = 'settings.action.clearSearchResults'; -export const SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING = 'settings.action.focusNextSetting'; -export const SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING = 'settings.action.focusPreviousSetting'; -export const SETTINGS_EDITOR_COMMAND_FOCUS_FILE = 'settings.action.focusSettingsFile'; -export const SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING = 'settings.action.editFocusedSetting'; -export const SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH = 'settings.action.focusSettingsFromSearch'; -export const SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST = 'settings.action.focusSettingsList'; -export const SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU = 'settings.action.showContextMenu'; -export const SETTINGS_EDITOR_COMMAND_FOCUS_TOC = 'settings.action.focusTOC'; - -export const SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON = 'settings.switchToJSON'; -export const SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED = 'settings.filterByModified'; -export const SETTINGS_EDITOR_COMMAND_FILTER_ONLINE = 'settings.filterByOnline'; - export const KEYBINDINGS_EDITOR_COMMAND_SEARCH = 'keybindings.editor.searchKeybindings'; export const KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS = 'keybindings.editor.clearSearchResults'; export const KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS = 'keybindings.editor.recordSearchKeys'; @@ -101,15 +89,10 @@ export const KEYBINDINGS_EDITOR_COMMAND_COPY = 'keybindings.editor.copyKeybindin export const KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND = 'keybindings.editor.copyCommandKeybindingEntry'; export const KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR = 'keybindings.editor.showConflicts'; export const KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS = 'keybindings.editor.focusKeybindings'; -export const KEYBINDINGS_EDITOR_CLEAR_INPUT = 'keybindings.editor.showDefaultKeybindings'; export const KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS = 'keybindings.editor.showDefaultKeybindings'; export const KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS = 'keybindings.editor.showUserKeybindings'; -export const DEFAULT_SETTINGS_EDITOR_SETTING = 'workbench.settings.openDefaultSettings'; - export const MODIFIED_SETTING_TAG = 'modified'; export const EXTENSION_SETTING_TAG = 'ext:'; -export const SETTINGS_COMMAND_OPEN_SETTINGS = 'workbench.action.openSettings'; - export const KEYBOARD_LAYOUT_OPEN_PICKER = 'workbench.action.openKeyboardLayoutPicker'; diff --git a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts index 04945ccdee6..1873c7bdc83 100644 --- a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts +++ b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; -import { dispose, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { isEqual } from 'vs/base/common/resources'; -import { endsWith } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import * as nls from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationScope, Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import * as JSONContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; @@ -20,10 +20,10 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IEditorInput } from 'vs/workbench/common/editor'; -import { IEditorService, IOpenEditorOverride } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorService, IOpenEditorOverride } from 'vs/workbench/services/editor/common/editorService'; import { FOLDER_SETTINGS_PATH, IPreferencesService, USE_SPLIT_JSON_SETTING } from 'vs/workbench/services/preferences/common/preferences'; -import { Extensions, IConfigurationRegistry, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; const schemaRegistry = Registry.as(JSONContributionRegistry.Extensions.JSONContribution); @@ -68,7 +68,7 @@ export class PreferencesContribution implements IWorkbenchContribution { const resource = editor.resource; if ( !resource || - !endsWith(resource.path, 'settings.json') || // resource must end in settings.json + !resource.path.endsWith('settings.json') || // resource must end in settings.json !this.configurationService.getValue(USE_SPLIT_JSON_SETTING) // user has not disabled default settings editor ) { return undefined; @@ -154,6 +154,7 @@ export class PreferencesContribution implements IWorkbenchContribution { const registry = Registry.as(Extensions.Configuration); registry.registerConfiguration({ + ...workbenchConfigurationNodeBase, 'properties': { 'workbench.settings.enableNaturalLanguageSearch': { 'type': 'boolean', diff --git a/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts b/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts index f15b64530ef..85b04f7bd17 100644 --- a/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts +++ b/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts @@ -6,7 +6,7 @@ import { localize } from 'vs/nls'; import { ICommandQuickPick, CommandsHistory } from 'vs/platform/quickinput/browser/commandsQuickAccess'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IMenuService, MenuId, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; +import { IMenuService, MenuId, MenuItemAction, SubmenuItemAction, Action2 } from 'vs/platform/actions/common/actions'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { CancellationToken } from 'vs/base/common/cancellation'; import { timeout } from 'vs/base/common/async'; @@ -15,7 +15,7 @@ import { DisposableStore, toDisposable, dispose } from 'vs/base/common/lifecycle import { AbstractEditorCommandsQuickAccessProvider } from 'vs/editor/contrib/quickAccess/commandsQuickAccess'; import { IEditor } from 'vs/editor/common/editorCommon'; import { Language } from 'vs/base/common/platform'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -24,9 +24,10 @@ import { DefaultQuickAccessFilterValue } from 'vs/platform/quickinput/common/qui import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchQuickAccessConfiguration } from 'vs/workbench/browser/quickaccess'; import { stripCodicons } from 'vs/base/common/codicons'; -import { Action } from 'vs/base/common/actions'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAccessProvider { @@ -138,42 +139,46 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce //#region Actions -export class ShowAllCommandsAction extends Action { +export class ShowAllCommandsAction extends Action2 { static readonly ID = 'workbench.action.showCommands'; - static readonly LABEL = localize('showTriggerActions', "Show All Commands"); - constructor( - id: string, - label: string, - @IQuickInputService private readonly quickInputService: IQuickInputService - ) { - super(id, label); + constructor() { + super({ + id: ShowAllCommandsAction.ID, + title: { value: localize('showTriggerActions', "Show All Commands"), original: 'Show All Commands' }, + f1: true, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + when: undefined, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_P, + secondary: [KeyCode.F1] + } + }); } - async run(): Promise { - this.quickInputService.quickAccess.show(CommandsQuickAccessProvider.PREFIX); + async run(accessor: ServicesAccessor): Promise { + accessor.get(IQuickInputService).quickAccess.show(CommandsQuickAccessProvider.PREFIX); } } -export class ClearCommandHistoryAction extends Action { +export class ClearCommandHistoryAction extends Action2 { - static readonly ID = 'workbench.action.clearCommandHistory'; - static readonly LABEL = localize('clearCommandHistory', "Clear Command History"); - - constructor( - id: string, - label: string, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IStorageService private readonly storageService: IStorageService - ) { - super(id, label); + constructor() { + super({ + id: 'workbench.action.clearCommandHistory', + title: { value: localize('clearCommandHistory', "Clear Command History"), original: 'Clear Command History' }, + f1: true + }); } - async run(): Promise { - const commandHistoryLength = CommandsHistory.getConfiguredCommandHistoryLength(this.configurationService); + async run(accessor: ServicesAccessor): Promise { + const configurationService = accessor.get(IConfigurationService); + const storageService = accessor.get(IStorageService); + + const commandHistoryLength = CommandsHistory.getConfiguredCommandHistoryLength(configurationService); if (commandHistoryLength > 0) { - CommandsHistory.clearHistory(this.configurationService, this.storageService); + CommandsHistory.clearHistory(configurationService, storageService); } } } diff --git a/src/vs/workbench/contrib/quickaccess/browser/quickAccess.contribution.ts b/src/vs/workbench/contrib/quickaccess/browser/quickAccess.contribution.ts index f00ec822365..9e2ae84e031 100644 --- a/src/vs/workbench/contrib/quickaccess/browser/quickAccess.contribution.ts +++ b/src/vs/workbench/contrib/quickaccess/browser/quickAccess.contribution.ts @@ -9,9 +9,8 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { HelpQuickAccessProvider } from 'vs/platform/quickinput/browser/helpQuickAccess'; import { ViewQuickAccessProvider, OpenViewPickerAction, QuickAccessViewPickerAction } from 'vs/workbench/contrib/quickaccess/browser/viewQuickAccess'; import { CommandsQuickAccessProvider, ShowAllCommandsAction, ClearCommandHistoryAction } from 'vs/workbench/contrib/quickaccess/browser/commandsQuickAccess'; -import { MenuRegistry, MenuId, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; -import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { MenuRegistry, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { KeyMod } from 'vs/base/common/keyCodes'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { inQuickPickContext, getQuickNavigateHandler } from 'vs/workbench/browser/quickaccess'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -109,21 +108,14 @@ MenuRegistry.appendMenuItem(MenuId.EditorContext, { //#region Workbench actions and commands -const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ClearCommandHistoryAction), 'Clear Command History'); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ShowAllCommandsAction, { - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_P, - secondary: [KeyCode.F1] -}), 'Show All Commands'); +registerAction2(ClearCommandHistoryAction); +registerAction2(ShowAllCommandsAction); +registerAction2(OpenViewPickerAction); +registerAction2(QuickAccessViewPickerAction); const inViewsPickerContextKey = 'inViewsPicker'; const inViewsPickerContext = ContextKeyExpr.and(inQuickPickContext, ContextKeyExpr.has(inViewsPickerContextKey)); - -const viewPickerKeybinding = { primary: KeyMod.CtrlCmd | KeyCode.KEY_Q, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_Q }, linux: { primary: 0 } }; - -const viewCategory = localize('view', "View"); -registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenViewPickerAction), 'View: Open View', viewCategory); -registry.registerWorkbenchAction(SyncActionDescriptor.from(QuickAccessViewPickerAction, viewPickerKeybinding), 'View: Quick Open View', viewCategory); +const viewPickerKeybinding = QuickAccessViewPickerAction.KEYBINDING; const quickAccessNavigateNextInViewPickerId = 'workbench.action.quickOpenNavigateNextInViewPicker'; KeybindingsRegistry.registerCommandAndKeybindingRule({ diff --git a/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess.ts b/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess.ts index d7c203b46a2..82a41fd60e5 100644 --- a/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess.ts +++ b/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess.ts @@ -17,7 +17,10 @@ import { matchesFuzzy } from 'vs/base/common/filters'; import { fuzzyContains } from 'vs/base/common/strings'; import { withNullAsUndefined } from 'vs/base/common/types'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { Action } from 'vs/base/common/actions'; +import { Action2 } from 'vs/platform/actions/common/actions'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; interface IViewQuickPickItem extends IPickerQuickAccessItem { containerLabel: string; @@ -185,42 +188,56 @@ export class ViewQuickAccessProvider extends PickerQuickAccessProvider { - this.quickInputService.quickAccess.show(ViewQuickAccessProvider.PREFIX); + async run(accessor: ServicesAccessor): Promise { + accessor.get(IQuickInputService).quickAccess.show(ViewQuickAccessProvider.PREFIX); } } -export class QuickAccessViewPickerAction extends Action { +export class QuickAccessViewPickerAction extends Action2 { static readonly ID = 'workbench.action.quickOpenView'; - static readonly LABEL = localize('quickOpenView', "Quick Open View"); + static readonly KEYBINDING = { + primary: KeyMod.CtrlCmd | KeyCode.KEY_Q, + mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_Q }, + linux: { primary: 0 } + }; - constructor( - id: string, - label: string, - @IQuickInputService private readonly quickInputService: IQuickInputService, - @IKeybindingService private readonly keybindingService: IKeybindingService - ) { - super(id, label); + constructor() { + super({ + id: QuickAccessViewPickerAction.ID, + title: { value: localize('quickOpenView', "Quick Open View"), original: 'Quick Open View' }, + category: viewCategory, + f1: true, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + when: undefined, + ...QuickAccessViewPickerAction.KEYBINDING + } + }); } - async run(): Promise { - const keys = this.keybindingService.lookupKeybindings(this.id); + async run(accessor: ServicesAccessor): Promise { + const keybindingService = accessor.get(IKeybindingService); + const quickInputService = accessor.get(IQuickInputService); - this.quickInputService.quickAccess.show(ViewQuickAccessProvider.PREFIX, { quickNavigateConfiguration: { keybindings: keys }, itemActivation: ItemActivation.FIRST }); + const keys = keybindingService.lookupKeybindings(QuickAccessViewPickerAction.ID); + + quickInputService.quickAccess.show(ViewQuickAccessProvider.PREFIX, { quickNavigateConfiguration: { keybindings: keys }, itemActivation: ItemActivation.FIRST }); } } diff --git a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts index 8ff6aff2343..7886563a6ad 100644 --- a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts +++ b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts @@ -24,7 +24,6 @@ import { IProductService } from 'vs/platform/product/common/productService'; interface IConfiguration extends IWindowsConfiguration { update: { mode: string; }; telemetry: { enableCrashReporter: boolean }; - workbench: { list: { horizontalScrolling: boolean } }; debug: { console: { wordWrap: boolean } }; editor: { accessibilitySupport: 'on' | 'off' | 'auto' }; } @@ -37,7 +36,6 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo private clickThroughInactive: boolean | undefined; private updateMode: string | undefined; private enableCrashReporter: boolean | undefined; - private treeHorizontalScrolling: boolean | undefined; private debugConsoleWordWrap: boolean | undefined; private accessibilitySupport: 'on' | 'off' | 'auto' | undefined; @@ -56,12 +54,6 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo private onConfigurationChange(config: IConfiguration, notify: boolean): void { let changed = false; - // Tree horizontal scrolling support - if (typeof config.workbench?.list?.horizontalScrolling === 'boolean' && config.workbench.list.horizontalScrolling !== this.treeHorizontalScrolling) { - this.treeHorizontalScrolling = config.workbench.list.horizontalScrolling; - changed = true; - } - // Debug console word wrap if (typeof config.debug?.console.wordWrap === 'boolean' && config.debug.console.wordWrap !== this.debugConsoleWordWrap) { this.debugConsoleWordWrap = config.debug.console.wordWrap; diff --git a/src/vs/workbench/contrib/remote/browser/explorerViewItems.ts b/src/vs/workbench/contrib/remote/browser/explorerViewItems.ts index 3987abf9388..d8021dd0c07 100644 --- a/src/vs/workbench/contrib/remote/browser/explorerViewItems.ts +++ b/src/vs/workbench/contrib/remote/browser/explorerViewItems.ts @@ -6,13 +6,10 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; -import { IActionRunner, IAction, Action } from 'vs/base/common/actions'; -import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IAction, Action } from 'vs/base/common/actions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; +import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; -import { selectBorder } from 'vs/platform/theme/common/colorRegistry'; import { IRemoteExplorerService, REMOTE_EXPLORER_TYPE_KEY } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; import { IViewDescriptor } from 'vs/workbench/common/views'; @@ -21,6 +18,7 @@ import { isStringArray } from 'vs/base/common/types'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; export interface IRemoteSelectItem extends ISelectOptionItem { authority: string[]; @@ -28,21 +26,17 @@ export interface IRemoteSelectItem extends ISelectOptionItem { export class SwitchRemoteViewItem extends SelectActionViewItem { - actionRunner!: IActionRunner; - constructor( action: IAction, private readonly optionsItems: IRemoteSelectItem[], - @IThemeService private readonly themeService: IThemeService, + @IThemeService themeService: IThemeService, @IContextViewService contextViewService: IContextViewService, @IRemoteExplorerService remoteExplorerService: IRemoteExplorerService, @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @IStorageService private readonly storageService: IStorageService ) { super(null, action, optionsItems, 0, contextViewService, { ariaLabel: nls.localize('remotes', 'Switch Remote') }); - this._register(attachSelectBoxStyler(this.selectBox, themeService, { - selectBackground: SIDE_BAR_BACKGROUND - })); + this._register(attachSelectBoxStyler(this.selectBox, themeService)); this.setSelectionForConnection(optionsItems, environmentService, remoteExplorerService); } @@ -77,11 +71,10 @@ export class SwitchRemoteViewItem extends SelectActionViewItem { } render(container: HTMLElement) { - super.render(container); - dom.addClass(container, 'switch-remote'); - this._register(attachStylerCallback(this.themeService, { selectBorder }, colors => { - container.style.border = colors.selectBorder ? `1px solid ${colors.selectBorder}` : ''; - })); + if (this.optionsItems.length > 1) { + super.render(container); + dom.addClass(container, 'switch-remote'); + } } protected getActionContext(_: string, index: number): any { diff --git a/src/vs/workbench/contrib/remote/browser/media/remoteViewlet.css b/src/vs/workbench/contrib/remote/browser/media/remoteViewlet.css index f9b9d5cc1b8..d6709d5d8d9 100644 --- a/src/vs/workbench/contrib/remote/browser/media/remoteViewlet.css +++ b/src/vs/workbench/contrib/remote/browser/media/remoteViewlet.css @@ -57,5 +57,5 @@ } .monaco-workbench .part > .title > .title-actions .switch-remote > .monaco-select-box { - padding: 0 22px 0 6px; + padding: 1px 22px 2px 6px; } diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index e337f914393..6f0a32f639a 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -44,12 +44,11 @@ import { Action, IActionViewItem, IAction } from 'vs/base/common/actions'; import { isStringArray } from 'vs/base/common/types'; import { IRemoteExplorerService } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { startsWith } from 'vs/base/common/strings'; import { TunnelPanelDescriptor, TunnelViewModel, forwardedPortsViewEnabled } from 'vs/workbench/contrib/remote/browser/tunnelView'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ITreeRenderer, ITreeNode, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; -import { WorkbenchAsyncDataTree, TreeResourceNavigator } from 'vs/platform/list/browser/listService'; +import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Event } from 'vs/base/common/event'; import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; @@ -74,19 +73,19 @@ const remoteHelpExtPoint = ExtensionsRegistry.registerExtensionPointObject.create(null); data.parent = container; @@ -127,7 +126,7 @@ class HelpTreeRenderer implements ITreeRenderer, index: number, templateData: IHelpItemTemplateData, height: number | undefined): void { const container = templateData.parent; dom.append(container, templateData.icon); - dom.addClasses(templateData.icon, ...element.element.iconClasses); + templateData.icon.classList.add(...element.element.iconClasses); const labelContainer = dom.append(container, dom.$('.help-item-label')); labelContainer.innerText = element.element.label; } @@ -182,11 +181,11 @@ class HelpModel { helpItems.push(new HelpItem( getStartedIcon, nls.localize('remote.help.getStarted', "Get Started"), - getStarted.map((info: HelpInformation) => ({ - extensionDescription: info.extensionDescription, - url: info.getStarted!, - remoteAuthority: (typeof info.remoteName === 'string') ? [info.remoteName] : info.remoteName - })), + getStarted.map((info: HelpInformation) => (new HelpItemValue(commandService, + info.extensionDescription, + (typeof info.remoteName === 'string') ? [info.remoteName] : info.remoteName, + info.getStarted!) + )), quickInputService, environmentService, openerService, @@ -200,11 +199,11 @@ class HelpModel { helpItems.push(new HelpItem( documentationIcon, nls.localize('remote.help.documentation', "Read Documentation"), - documentation.map((info: HelpInformation) => ({ - extensionDescription: info.extensionDescription, - url: info.documentation!, - remoteAuthority: (typeof info.remoteName === 'string') ? [info.remoteName] : info.remoteName - })), + documentation.map((info: HelpInformation) => (new HelpItemValue(commandService, + info.extensionDescription, + (typeof info.remoteName === 'string') ? [info.remoteName] : info.remoteName, + info.documentation!) + )), quickInputService, environmentService, openerService, @@ -218,11 +217,11 @@ class HelpModel { helpItems.push(new HelpItem( feedbackIcon, nls.localize('remote.help.feedback', "Provide Feedback"), - feedback.map((info: HelpInformation) => ({ - extensionDescription: info.extensionDescription, - url: info.feedback!, - remoteAuthority: (typeof info.remoteName === 'string') ? [info.remoteName] : info.remoteName - })), + feedback.map((info: HelpInformation) => (new HelpItemValue(commandService, + info.extensionDescription, + (typeof info.remoteName === 'string') ? [info.remoteName] : info.remoteName, + info.feedback!) + )), quickInputService, environmentService, openerService, @@ -236,11 +235,11 @@ class HelpModel { helpItems.push(new HelpItem( reviewIssuesIcon, nls.localize('remote.help.issues', "Review Issues"), - issues.map((info: HelpInformation) => ({ - extensionDescription: info.extensionDescription, - url: info.issues!, - remoteAuthority: (typeof info.remoteName === 'string') ? [info.remoteName] : info.remoteName - })), + issues.map((info: HelpInformation) => (new HelpItemValue(commandService, + info.extensionDescription, + (typeof info.remoteName === 'string') ? [info.remoteName] : info.remoteName, + info.issues!) + )), quickInputService, environmentService, openerService, @@ -252,10 +251,10 @@ class HelpModel { helpItems.push(new IssueReporterItem( reportIssuesIcon, nls.localize('remote.help.report', "Report Issue"), - viewModel.helpInformation.map(info => ({ - extensionDescription: info.extensionDescription, - remoteAuthority: (typeof info.remoteName === 'string') ? [info.remoteName] : info.remoteName - })), + viewModel.helpInformation.map(info => (new HelpItemValue(commandService, + info.extensionDescription, + (typeof info.remoteName === 'string') ? [info.remoteName] : info.remoteName + ))), quickInputService, environmentService, commandService, @@ -269,33 +268,55 @@ class HelpModel { } } +class HelpItemValue { + private _url: string | undefined; + constructor(private commandService: ICommandService, public extensionDescription: IExtensionDescription, public remoteAuthority: string[] | undefined, private urlOrCommand?: string) { } + + get url(): Promise { + return new Promise(async (resolve) => { + if (this._url === undefined) { + if (this.urlOrCommand) { + let url = URI.parse(this.urlOrCommand); + if (url.authority) { + this._url = this.urlOrCommand; + } else { + this._url = await this.commandService.executeCommand(this.urlOrCommand); + } + } else { + this._url = ''; + } + } + resolve(this._url); + }); + } +} + abstract class HelpItemBase implements IHelpItem { public iconClasses: string[] = []; constructor( public icon: Codicon, public label: string, - public values: { extensionDescription: IExtensionDescription, url?: string, remoteAuthority: string[] | undefined }[], + public values: HelpItemValue[], private quickInputService: IQuickInputService, private environmentService: IWorkbenchEnvironmentService, private remoteExplorerService: IRemoteExplorerService ) { - this.iconClasses.push(icon.classNames); + this.iconClasses.push(...icon.classNamesArray); this.iconClasses.push('remote-help-tree-node-item-icon'); } async handleClick() { const remoteAuthority = this.environmentService.configuration.remoteAuthority; - if (!remoteAuthority) { - return; - } - for (let i = 0; i < this.remoteExplorerService.targetType.length; i++) { - if (startsWith(remoteAuthority, this.remoteExplorerService.targetType[i])) { - for (let value of this.values) { - if (value.remoteAuthority) { - for (let authority of value.remoteAuthority) { - if (startsWith(remoteAuthority, authority)) { - await this.takeAction(value.extensionDescription, value.url); - return; + if (remoteAuthority) { + for (let i = 0; i < this.remoteExplorerService.targetType.length; i++) { + if (remoteAuthority.startsWith(this.remoteExplorerService.targetType[i])) { + for (let value of this.values) { + if (value.remoteAuthority) { + for (let authority of value.remoteAuthority) { + if (remoteAuthority.startsWith(authority)) { + await this.takeAction(value.extensionDescription, await value.url); + return; + } } } } @@ -304,13 +325,13 @@ abstract class HelpItemBase implements IHelpItem { } if (this.values.length > 1) { - let actions = this.values.map(value => { + let actions = await Promise.all(this.values.map(async (value) => { return { label: value.extensionDescription.displayName || value.extensionDescription.identifier.value, - description: value.url, + description: await value.url, extensionDescription: value.extensionDescription }; - }); + })); const action = await this.quickInputService.pick(actions, { placeHolder: nls.localize('pickRemoteExtension', "Select url to open") }); @@ -318,7 +339,7 @@ abstract class HelpItemBase implements IHelpItem { await this.takeAction(action.extensionDescription, action.description); } } else { - await this.takeAction(this.values[0].extensionDescription, this.values[0].url); + await this.takeAction(this.values[0].extensionDescription, await this.values[0].url); } } @@ -329,7 +350,7 @@ class HelpItem extends HelpItemBase { constructor( icon: Codicon, label: string, - values: { extensionDescription: IExtensionDescription; url: string, remoteAuthority: string[] | undefined }[], + values: HelpItemValue[], quickInputService: IQuickInputService, environmentService: IWorkbenchEnvironmentService, private openerService: IOpenerService, @@ -347,7 +368,7 @@ class IssueReporterItem extends HelpItemBase { constructor( icon: Codicon, label: string, - values: { extensionDescription: IExtensionDescription; remoteAuthority: string[] | undefined }[], + values: HelpItemValue[], quickInputService: IQuickInputService, environmentService: IWorkbenchEnvironmentService, private commandService: ICommandService, @@ -389,9 +410,9 @@ class HelpPanel extends ViewPane { protected renderBody(container: HTMLElement): void { super.renderBody(container); - dom.addClass(container, 'remote-help'); + container.classList.add('remote-help'); const treeContainer = document.createElement('div'); - dom.addClass(treeContainer, 'remote-help-content'); + treeContainer.classList.add('remote-help-content'); container.appendChild(treeContainer); this.tree = this.instantiationService.createInstance(WorkbenchAsyncDataTree, @@ -415,9 +436,7 @@ class HelpPanel extends ViewPane { this.tree.setInput(model); - const helpItemNavigator = this._register(new TreeResourceNavigator(this.tree, { openOnFocus: false, openOnSelection: false })); - - this._register(Event.debounce(helpItemNavigator.onDidOpenResource, (last, event) => event, 75, true)(e => { + this._register(Event.debounce(this.tree.onDidOpen, (last, event) => event, 75, true)(e => { e.element.handleClick(); })); } diff --git a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts index 9194ba3e37f..7aec94c4f7e 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts @@ -55,7 +55,7 @@ export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenc this.windowCommandMenu = this.menuService.createMenu(MenuId.StatusBarWindowIndicatorMenu, this.contextKeyService); this._register(this.windowCommandMenu); - const category = nls.localize('remote.category', "Remote"); + const category = { value: nls.localize('remote.category', "Remote"), original: 'Remote' }; const that = this; registerAction2(class extends Action2 { constructor() { diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index a9cbf589a7a..930d4e799cc 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -7,7 +7,7 @@ import 'vs/css!./media/tunnelView'; import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { IViewDescriptor, IEditableData, IViewsService, IViewDescriptorService } from 'vs/workbench/common/views'; -import { WorkbenchAsyncDataTree, TreeResourceNavigator } from 'vs/platform/list/browser/listService'; +import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IContextKeyService, IContextKey, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -21,11 +21,11 @@ import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ITreeRenderer, ITreeNode, IAsyncDataSource, ITreeContextMenuEvent, ITreeMouseEvent } from 'vs/base/browser/ui/tree/tree'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { Disposable, IDisposable, toDisposable, MutableDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle'; -import { ActionBar, ActionViewItem, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { ActionRunner, IAction } from 'vs/base/common/actions'; -import { IMenuService, MenuId, IMenu, MenuRegistry, MenuItemAction, ILocalizedString } from 'vs/platform/actions/common/actions'; -import { createAndFillInContextMenuActions, createAndFillInActionBarActions, ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenuService, MenuId, IMenu, MenuRegistry, MenuItemAction, ILocalizedString, SubmenuItemAction } from 'vs/platform/actions/common/actions'; +import { createAndFillInContextMenuActions, createAndFillInActionBarActions, MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IRemoteExplorerService, TunnelModel, MakeAddress, TunnelType, ITunnelItem, Tunnel } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; @@ -41,6 +41,7 @@ import { RemoteTunnel } from 'vs/platform/remote/common/tunnel'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; export const forwardedPortsViewEnabled = new RawContextKey('forwardedPortsViewEnabled', false); @@ -157,8 +158,18 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { get candidates(): TunnelItem[] { const candidates: TunnelItem[] = []; this._candidates.forEach(value => { - const key = MakeAddress(value.host, value.port); + let key = MakeAddress(value.host, value.port); if (!this.model.forwarded.has(key) && !this.model.detected.has(key)) { + // The host:port hasn't been forwarded or detected. However, if the candidate is 0.0.0.0, + // also check that the port hasn't already been forwarded with localhost, and vice versa. + // For example: no need to show 0.0.0.0:3000 as a candidate if localhost:3000 is already forwarded. + const otherHost = value.host === '0.0.0.0' ? 'localhost' : (value.host === 'localhost' ? '0.0.0.0' : undefined); + if (otherHost) { + key = MakeAddress(otherHost, value.port); + if (this.model.forwarded.has(key) || this.model.detected.has(key)) { + return; + } + } candidates.push(new TunnelItem(TunnelType.Candidate, value.host, value.port, undefined, undefined, false, undefined, value.detail)); } }); @@ -208,15 +219,16 @@ class TunnelTreeRenderer extends Disposable implements ITreeRenderer { if (action instanceof MenuItemAction) { - return this.instantiationService.createInstance(ContextAwareMenuEntryActionViewItem, action); + return this.instantiationService.createInstance(MenuEntryActionViewItem, action); + } else if (action instanceof SubmenuItemAction) { + return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action); } return undefined; @@ -451,7 +463,6 @@ export class TunnelPanel extends ViewPane { @IQuickInputService protected quickInputService: IQuickInputService, @ICommandService protected commandService: ICommandService, @IMenuService private readonly menuService: IMenuService, - @INotificationService private readonly notificationService: INotificationService, @IContextViewService private readonly contextViewService: IContextViewService, @IThemeService themeService: IThemeService, @IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService, @@ -488,8 +499,7 @@ export class TunnelPanel extends ViewPane { const panelContainer = dom.append(container, dom.$('.tree-explorer-viewlet-tree-view')); const treeContainer = dom.append(panelContainer, dom.$('.customview-tree')); - dom.addClass(treeContainer, 'file-icon-themable-tree'); - dom.addClass(treeContainer, 'show-file-icons'); + treeContainer.classList.add('file-icon-themable-tree', 'show-file-icons'); const renderer = new TunnelTreeRenderer(TunnelPanel.ID, this.menuService, this.contextKeyService, this.instantiationService, this.contextViewService, this.themeService, this.remoteExplorerService); this.tree = this.instantiationService.createInstance(TunnelDataTree, @@ -538,9 +548,7 @@ export class TunnelPanel extends ViewPane { this.tree.updateChildren(undefined, true); })); - const navigator = this._register(new TreeResourceNavigator(this.tree, { openOnFocus: false, openOnSelection: false })); - - this._register(Event.debounce(navigator.onDidOpenResource, (last, event) => event, 75, true)(e => { + this._register(Event.debounce(this.tree.onDidOpen, (last, event) => event, 75, true)(e => { if (e.element && (e.element.tunnelType === TunnelType.Add)) { this.commandService.executeCommand(ForwardPortAction.INLINE_ID); } @@ -550,13 +558,13 @@ export class TunnelPanel extends ViewPane { const isEditing = !!this.remoteExplorerService.getEditableData(e); if (!isEditing) { - dom.removeClass(treeContainer, 'highlight'); + treeContainer.classList.remove('highlight'); } await this.tree.updateChildren(undefined, false); if (isEditing) { - dom.addClass(treeContainer, 'highlight'); + treeContainer.classList.add('highlight'); if (!e) { // When we are in editing mode for a new forward, rather than updating an existing one we need to reveal the input box since it might be out of view. this.tree.reveal(this.viewModel.input); @@ -650,10 +658,6 @@ export class TunnelPanel extends ViewPane { super.layoutBody(height, width); this.tree.layout(height, width); } - - getActionViewItem(action: IAction): IActionViewItem | undefined { - return action instanceof MenuItemAction ? new ContextAwareMenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService) : undefined; - } } export class TunnelPanelDescriptor implements IViewDescriptor { diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index dbd34cdc3d8..fd8ee7ee9a2 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -97,11 +97,13 @@ const extensionKindSchema: IJSONSchema = { type: 'string', enum: [ 'ui', - 'workspace' + 'workspace', + 'web' ], enumDescriptions: [ localize('ui', "UI extension kind. In a remote window, such extensions are enabled only when available on the local machine."), - localize('workspace', "Workspace extension kind. In a remote window, such extensions are enabled only when available on the remote.") + localize('workspace', "Workspace extension kind. In a remote window, such extensions are enabled only when available on the remote."), + localize('web', "Web worker extension kind. Such an extension can execute in a web worker extension host.") ], }; diff --git a/src/vs/workbench/contrib/remote/common/showCandidate.ts b/src/vs/workbench/contrib/remote/common/showCandidate.ts index 7ab26b73803..6fa5f01943c 100644 --- a/src/vs/workbench/contrib/remote/common/showCandidate.ts +++ b/src/vs/workbench/contrib/remote/common/showCandidate.ts @@ -14,9 +14,10 @@ export class ShowCandidateContribution extends Disposable implements IWorkbenchC @IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService, ) { super(); - if (workbenchEnvironmentService.options && workbenchEnvironmentService.options.showCandidate) { + const showPortCandidate = workbenchEnvironmentService.options?.tunnelProvider?.showPortCandidate; + if (showPortCandidate) { this._register(remoteExplorerService.setCandidateFilter(async (candidates: { host: string, port: number, detail: string }[]): Promise<{ host: string, port: number, detail: string }[]> => { - const filters: boolean[] = await Promise.all(candidates.map(candidate => workbenchEnvironmentService.options!.showCandidate!(candidate.host, candidate.port, candidate.detail))); + const filters: boolean[] = await Promise.all(candidates.map(candidate => showPortCandidate(candidate.host, candidate.port, candidate.detail))); const filteredCandidates: { host: string, port: number, detail: string }[] = []; if (filters.length !== candidates.length) { return candidates; diff --git a/src/vs/workbench/contrib/remote/common/tunnelFactory.ts b/src/vs/workbench/contrib/remote/common/tunnelFactory.ts index 9414ee117d4..2562cb661fe 100644 --- a/src/vs/workbench/contrib/remote/common/tunnelFactory.ts +++ b/src/vs/workbench/contrib/remote/common/tunnelFactory.ts @@ -14,10 +14,11 @@ export class TunnelFactoryContribution extends Disposable implements IWorkbenchC @IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService, ) { super(); - if (workbenchEnvironmentService.options && workbenchEnvironmentService.options.tunnelFactory) { + const tunnelFactory = workbenchEnvironmentService.options?.tunnelProvider?.tunnelFactory; + if (tunnelFactory) { this._register(tunnelService.setTunnelProvider({ forwardPort: (tunnelOptions: TunnelOptions): Promise | undefined => { - const tunnelPromise = workbenchEnvironmentService.options!.tunnelFactory!(tunnelOptions); + const tunnelPromise = tunnelFactory(tunnelOptions); if (!tunnelPromise) { return undefined; } diff --git a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts index bed450069dc..bf829f2aaed 100644 --- a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts @@ -21,7 +21,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { DialogChannel } from 'vs/platform/dialogs/electron-browser/dialogIpc'; import { DownloadServiceChannel } from 'vs/platform/download/common/downloadIpc'; import { LoggerChannel } from 'vs/platform/log/common/logIpc'; -import { ipcRenderer as ipc } from 'electron'; +import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { IDiagnosticInfoOptions, IRemoteDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; @@ -55,7 +55,7 @@ class RemoteAgentDiagnosticListener implements IWorkbenchContribution { @IRemoteAgentService remoteAgentService: IRemoteAgentService, @ILabelService labelService: ILabelService ) { - ipc.on('vscode:getDiagnosticInfo', (event: Event, request: { replyChannel: string, args: IDiagnosticInfoOptions }): void => { + ipcRenderer.on('vscode:getDiagnosticInfo', (event: unknown, request: { replyChannel: string, args: IDiagnosticInfoOptions }): void => { const connection = remoteAgentService.getConnection(); if (connection) { const hostName = labelService.getHostLabel(REMOTE_HOST_SCHEME, connection.remoteAuthority); @@ -65,14 +65,14 @@ class RemoteAgentDiagnosticListener implements IWorkbenchContribution { (info as IRemoteDiagnosticInfo).hostName = hostName; } - ipc.send(request.replyChannel, info); + ipcRenderer.send(request.replyChannel, info); }) .catch(e => { const errorMessage = e && e.message ? `Fetching remote diagnostics for '${hostName}' failed: ${e.message}` : `Fetching remote diagnostics for '${hostName}' failed.`; - ipc.send(request.replyChannel, { hostName, errorMessage }); + ipcRenderer.send(request.replyChannel, { hostName, errorMessage }); }); } else { - ipc.send(request.replyChannel); + ipcRenderer.send(request.replyChannel); } }); } diff --git a/src/vs/workbench/contrib/sash/browser/sash.contribution.ts b/src/vs/workbench/contrib/sash/browser/sash.contribution.ts new file mode 100644 index 00000000000..1dfb6be879b --- /dev/null +++ b/src/vs/workbench/contrib/sash/browser/sash.contribution.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 { localize } from 'vs/nls'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { SashSizeController, minSize, maxSize } from 'vs/workbench/contrib/sash/browser/sash'; +import { isIPad } from 'vs/base/browser/browser'; + +// Sash size contribution +Registry.as(WorkbenchExtensions.Workbench) + .registerWorkbenchContribution(SashSizeController, LifecyclePhase.Restored); + +// Sash size configuration contribution +Registry.as(ConfigurationExtensions.Configuration) + .registerConfiguration({ + ...workbenchConfigurationNodeBase, + 'properties': { + 'workbench.sash.size': { + 'type': 'number', + 'default': isIPad ? maxSize : minSize, + 'minimum': minSize, + 'maximum': maxSize, + 'description': localize('sashSize', "Controls the feedback area size in pixels of the dragging area in between views/editors. Set it to a larger value if you feel it's hard to resize views using the mouse.") + }, + } + }); diff --git a/src/vs/workbench/contrib/sash/browser/sash.ts b/src/vs/workbench/contrib/sash/browser/sash.ts new file mode 100644 index 00000000000..f2884080763 --- /dev/null +++ b/src/vs/workbench/contrib/sash/browser/sash.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 { clamp } from 'vs/base/common/numbers'; +import { createStyleSheet } from 'vs/base/browser/dom'; +import { setGlobalSashSize } from 'vs/base/browser/ui/sash/sash'; +import { Event } from 'vs/base/common/event'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; + +export const minSize = 4; +export const maxSize = 20; // see also https://ux.stackexchange.com/questions/39023/what-is-the-optimum-button-size-of-touch-screen-applications + +export class SashSizeController extends Disposable implements IWorkbenchContribution { + private readonly configurationName = 'workbench.sash.size'; + private stylesheet: HTMLStyleElement; + + constructor( + @IConfigurationService private readonly configurationService: IConfigurationService + ) { + super(); + + this.stylesheet = createStyleSheet(); + this._register(toDisposable(() => this.stylesheet.remove())); + + const onDidChangeSizeConfiguration = Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(this.configurationName)); + this._register(onDidChangeSizeConfiguration(this.onDidChangeSizeConfiguration, this)); + this.onDidChangeSizeConfiguration(); + } + + private onDidChangeSizeConfiguration(): void { + const size = clamp(this.configurationService.getValue(this.configurationName) ?? minSize, minSize, maxSize); + + // Update styles + this.stylesheet.innerHTML = ` + .monaco-sash.vertical { cursor: ew-resize; top: 0; width: ${size}px; height: 100%; } + .monaco-sash.horizontal { cursor: ns-resize; left: 0; width: 100%; height: ${size}px; } + .monaco-sash:not(.disabled).orthogonal-start::before, .monaco-sash:not(.disabled).orthogonal-end::after { content: ' '; height: ${size * 2}px; width: ${size * 2}px; z-index: 100; display: block; cursor: all-scroll; position: absolute; } + .monaco-sash.orthogonal-start.vertical::before { left: -${size / 2}px; top: -${size}px; } + .monaco-sash.orthogonal-end.vertical::after { left: -${size / 2}px; bottom: -${size}px; } + .monaco-sash.orthogonal-start.horizontal::before { top: -${size / 2}px; left: -${size}px; } + .monaco-sash.orthogonal-end.horizontal::after { top: -${size / 2}px; right: -${size}px; }`; + + // Update behavor + setGlobalSashSize(size); + } +} diff --git a/src/vs/workbench/contrib/scm/browser/activity.ts b/src/vs/workbench/contrib/scm/browser/activity.ts index 04600cfa0eb..b5b9adcdb5d 100644 --- a/src/vs/workbench/contrib/scm/browser/activity.ts +++ b/src/vs/workbench/contrib/scm/browser/activity.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import { basename, relativePath } from 'vs/base/common/resources'; import { IDisposable, dispose, Disposable, DisposableStore, combinedDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; -import { VIEWLET_ID, ISCMService, ISCMRepository } from 'vs/workbench/contrib/scm/common/scm'; +import { VIEW_PANE_ID, ISCMService, ISCMRepository } from 'vs/workbench/contrib/scm/common/scm'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -92,7 +92,7 @@ export class SCMStatusController implements IWorkbenchContribution { } private onDidAddRepository(repository: ISCMRepository): void { - const focusDisposable = repository.onDidFocus(() => this.focusRepository(repository)); + const selectedDisposable = Event.filter(repository.onDidChangeSelection, selected => selected)(() => this.focusRepository(repository)); const onDidChange = Event.any(repository.provider.onDidChange, repository.provider.onDidChangeResources); const changeDisposable = onDidChange(() => this.renderActivityCount()); @@ -104,14 +104,12 @@ export class SCMStatusController implements IWorkbenchContribution { if (this.scmService.repositories.length === 0) { this.focusRepository(undefined); - } else if (this.focusedRepository === repository) { - this.scmService.repositories[0].focus(); } this.renderActivityCount(); }); - const disposable = combinedDisposable(focusDisposable, changeDisposable, removeDisposable); + const disposable = combinedDisposable(selectedDisposable, changeDisposable, removeDisposable); this.disposables.push(disposable); if (this.focusedRepository) { @@ -155,14 +153,14 @@ export class SCMStatusController implements IWorkbenchContribution { : repository.provider.label; const disposables = new DisposableStore(); - for (const c of commands) { - const tooltip = `${label} - ${c.tooltip}`; + for (const command of commands) { + const tooltip = `${label} - ${command.tooltip}`; disposables.add(this.statusbarService.addEntry({ - text: c.title, - ariaLabel: c.tooltip || label, + text: command.title, + ariaLabel: command.tooltip || label, tooltip, - command: c + command: command.id ? command : undefined }, 'status.scm', localize('status.scm', "Source Control"), MainThreadStatusBarAlignment.LEFT, 10000)); } @@ -184,7 +182,7 @@ export class SCMStatusController implements IWorkbenchContribution { if (count > 0) { const badge = new NumberBadge(count, num => localize('scmPendingChangesBadge', '{0} pending changes', num)); - this.badgeDisposable.value = this.activityService.showViewContainerActivity(VIEWLET_ID, { badge, clazz: 'scm-viewlet-label' }); + this.badgeDisposable.value = this.activityService.showViewActivity(VIEW_PANE_ID, { badge, clazz: 'scm-viewlet-label' }); } else { this.badgeDisposable.clear(); } diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index f1845d3a4df..450448112bb 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -34,20 +34,20 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { IDiffEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions'; import { Action, IAction, ActionRunner } from 'vs/base/common/actions'; -import { IActionBarOptions, ActionsOrientation, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IActionBarOptions, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { basename, isEqualOrParent } from 'vs/base/common/resources'; import { MenuId, IMenuService, IMenu, MenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions'; -import { createAndFillInActionBarActions, ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IChange, IEditorModel, ScrollType, IEditorContribution, IDiffEditorModel } from 'vs/editor/common/editorCommon'; import { OverviewRulerLane, ITextModel, IModelDecorationOptions, MinimapPosition } from 'vs/editor/common/model'; import { sortedDiff, firstIndex } from 'vs/base/common/arrays'; import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { ISplice } from 'vs/base/common/sequence'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import { createStyleSheet } from 'vs/base/browser/dom'; +import { ITextFileEditorModel, IResolvedTextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { EncodingMode } from 'vs/workbench/common/editor'; class DiffActionRunner extends ActionRunner { @@ -174,14 +174,11 @@ class DirtyDiffWidget extends PeekViewWidget { editor: ICodeEditor, private model: DirtyDiffModel, @IThemeService private readonly themeService: IThemeService, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IMenuService menuService: IMenuService, - @IKeybindingService private readonly keybindingService: IKeybindingService, - @INotificationService private readonly notificationService: INotificationService, - @IContextKeyService contextKeyService: IContextKeyService, - @IContextMenuService private readonly contextMenuService: IContextMenuService + @IContextKeyService contextKeyService: IContextKeyService ) { - super(editor, { isResizeable: true, frameWidth: 1, keepEditorSelection: true }); + super(editor, { isResizeable: true, frameWidth: 1, keepEditorSelection: true }, instantiationService); this._disposables.add(themeService.onDidColorThemeChange(this._applyTheme, this)); this._applyTheme(themeService.getColorTheme()); @@ -272,20 +269,12 @@ class DirtyDiffWidget extends PeekViewWidget { }); return { + ...super._getActionBarOptions(), actionRunner, - actionViewItemProvider: action => this.getActionViewItem(action), orientation: ActionsOrientation.HORIZONTAL_REVERSE }; } - getActionViewItem(action: IAction): IActionViewItem | undefined { - if (!(action instanceof MenuItemAction)) { - return undefined; - } - - return new ContextAwareMenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService); - } - protected _fillBody(container: HTMLElement): void { const options: IDiffEditorOptions = { scrollBeyondLastLine: true, @@ -1012,14 +1001,16 @@ function createProviderComparer(uri: URI): (a: ISCMProvider, b: ISCMProvider) => export class DirtyDiffModel extends Disposable { - private _originalModel: ITextModel | null = null; - get original(): ITextModel | null { return this._originalModel; } - get modified(): ITextModel | null { return this._editorModel; } + private _originalModel: IResolvedTextFileEditorModel | null = null; + private _model: ITextFileEditorModel; + get original(): ITextModel | null { return this._originalModel?.textEditorModel || null; } + get modified(): ITextModel | null { return this._model.textEditorModel || null; } - private diffDelayer: ThrottledDelayer | null; + private diffDelayer = new ThrottledDelayer(200); private _originalURIPromise?: Promise; private repositoryDisposables = new Set(); private readonly originalModelDisposables = this._register(new DisposableStore()); + private _disposed = false; private readonly _onDidChange = new Emitter<{ changes: IChange[], diff: ISplice[] }>(); readonly onDidChange: Event<{ changes: IChange[], diff: ISplice[] }> = this._onDidChange.event; @@ -1027,22 +1018,27 @@ export class DirtyDiffModel extends Disposable { private _changes: IChange[] = []; get changes(): IChange[] { return this._changes; } - private _editorModel: ITextModel | null; - constructor( - editorModel: ITextModel, + textFileModel: IResolvedTextFileEditorModel, @ISCMService private readonly scmService: ISCMService, @IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService, @ITextModelService private readonly textModelResolverService: ITextModelService ) { super(); - this._editorModel = editorModel; - this.diffDelayer = new ThrottledDelayer(200); + this._model = textFileModel; - this._register(editorModel.onDidChangeContent(() => this.triggerDiff())); + this._register(textFileModel.textEditorModel.onDidChangeContent(() => this.triggerDiff())); this._register(scmService.onDidAddRepository(this.onDidAddRepository, this)); scmService.repositories.forEach(r => this.onDidAddRepository(r)); + this._register(this._model.onDidChangeEncoding(() => { + this.diffDelayer.cancel(); + this._originalModel = null; + this._originalURIPromise = undefined; + this.setChanges([]); + this.triggerDiff(); + })); + this.triggerDiff(); } @@ -1069,11 +1065,11 @@ export class DirtyDiffModel extends Disposable { return this.diffDelayer .trigger(() => this.diff()) .then((changes: IChange[] | null) => { - if (!this._editorModel || this._editorModel.isDisposed() || !this._originalModel || this._originalModel.isDisposed()) { + if (this._disposed || this._model.isDisposed() || !this._originalModel || this._originalModel.isDisposed()) { return; // disposed } - if (this._originalModel.getValueLength() === 0) { + if (this._originalModel.textEditorModel.getValueLength() === 0) { changes = []; } @@ -1081,23 +1077,27 @@ export class DirtyDiffModel extends Disposable { changes = []; } - const diff = sortedDiff(this._changes, changes, compareChanges); - this._changes = changes; - this._onDidChange.fire({ changes, diff }); + this.setChanges(changes); }); } + private setChanges(changes: IChange[]): void { + const diff = sortedDiff(this._changes, changes, compareChanges); + this._changes = changes; + this._onDidChange.fire({ changes, diff }); + } + private diff(): Promise { return this.getOriginalURIPromise().then(originalURI => { - if (!this._editorModel || this._editorModel.isDisposed() || !originalURI) { + if (this._disposed || this._model.isDisposed() || !originalURI) { return Promise.resolve([]); // disposed } - if (!this.editorWorkerService.canComputeDirtyDiff(originalURI, this._editorModel.uri)) { + if (!this.editorWorkerService.canComputeDirtyDiff(originalURI, this._model.resource)) { return Promise.resolve([]); // Files too large } - return this.editorWorkerService.computeDirtyDiff(originalURI, this._editorModel.uri, false); + return this.editorWorkerService.computeDirtyDiff(originalURI, this._model.resource, false); }); } @@ -1107,7 +1107,7 @@ export class DirtyDiffModel extends Disposable { } this._originalURIPromise = this.getOriginalResource().then(originalUri => { - if (!this._editorModel) { // disposed + if (this._disposed) { // disposed return null; } @@ -1116,16 +1116,23 @@ export class DirtyDiffModel extends Disposable { return null; } - if (this._originalModel && this._originalModel.uri.toString() === originalUri.toString()) { + if (this._originalModel && this._originalModel.resource.toString() === originalUri.toString()) { return originalUri; } return this.textModelResolverService.createModelReference(originalUri).then(ref => { - if (!this._editorModel) { // disposed + if (this._disposed) { // disposed + ref.dispose(); return null; } - this._originalModel = ref.object.textEditorModel; + this._originalModel = ref.object as IResolvedTextFileEditorModel; + + const encoding = this._model.getEncoding(); + + if (encoding) { + this._originalModel.setEncoding(encoding, EncodingMode.Decode); + } this.originalModelDisposables.clear(); this.originalModelDisposables.add(ref); @@ -1143,11 +1150,11 @@ export class DirtyDiffModel extends Disposable { } private async getOriginalResource(): Promise { - if (!this._editorModel) { + if (this._disposed) { return Promise.resolve(null); } - const uri = this._editorModel.uri; + const uri = this._model.resource; const providers = this.scmService.repositories.map(r => r.provider); const rootedProviders = providers.filter(p => !!p.rootUri); @@ -1202,14 +1209,9 @@ export class DirtyDiffModel extends Disposable { dispose(): void { super.dispose(); - this._editorModel = null; + this._disposed = true; this._originalModel = null; - - if (this.diffDelayer) { - this.diffDelayer.cancel(); - this.diffDelayer = null; - } - + this.diffDelayer.cancel(); this.repositoryDisposables.forEach(d => dispose(d)); this.repositoryDisposables.clear(); } @@ -1234,15 +1236,15 @@ export class DirtyDiffWorkbenchController extends Disposable implements ext.IWor private enabled = false; private viewState: IViewState = { width: 3, visibility: 'always' }; - private models: ITextModel[] = []; - private items: { [modelId: string]: DirtyDiffItem; } = Object.create(null); + private items = new Map(); private readonly transientDisposables = this._register(new DisposableStore()); private stylesheet: HTMLStyleElement; constructor( @IEditorService private readonly editorService: IEditorService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IConfigurationService private readonly configurationService: IConfigurationService + @IConfigurationService private readonly configurationService: IConfigurationService, + @ITextFileService private readonly textFileService: ITextFileService ) { super(); this.stylesheet = createStyleSheet(); @@ -1312,9 +1314,12 @@ export class DirtyDiffWorkbenchController extends Disposable implements ext.IWor } this.transientDisposables.clear(); - this.models.forEach(m => this.items[m.id].dispose()); - this.models = []; - this.items = Object.create(null); + + for (const [, dirtyDiff] of this.items) { + dirtyDiff.dispose(); + } + + this.items.clear(); this.enabled = false; } @@ -1336,37 +1341,39 @@ export class DirtyDiffWorkbenchController extends Disposable implements ext.IWor }) // remove nulls and duplicates - .filter((m, i, a) => !!m && !!m.uri && a.indexOf(m, i + 1) === -1) as ITextModel[]; + .filter((m, i, a) => !!m && !!m.uri && a.indexOf(m, i + 1) === -1) - const newModels = models.filter(o => this.models.every(m => o !== m)); - const oldModels = this.models.filter(m => models.every(o => o !== m)); + // only want resolved text file service models + .map(m => this.textFileService.files.get(m!.uri)) + .filter(m => m?.isResolved()) as IResolvedTextFileEditorModel[]; + + const set = new Set(models); + const newModels = models.filter(o => !this.items.has(o)); + const oldModels = [...this.items.keys()].filter(m => !set.has(m)); oldModels.forEach(m => this.onModelInvisible(m)); newModels.forEach(m => this.onModelVisible(m)); - - this.models = models; } - private onModelVisible(editorModel: ITextModel): void { - const model = this.instantiationService.createInstance(DirtyDiffModel, editorModel); - const decorator = new DirtyDiffDecorator(editorModel, model, this.configurationService); - - this.items[editorModel.id] = new DirtyDiffItem(model, decorator); + private onModelVisible(textFileModel: IResolvedTextFileEditorModel): void { + const model = this.instantiationService.createInstance(DirtyDiffModel, textFileModel); + const decorator = new DirtyDiffDecorator(textFileModel.textEditorModel, model, this.configurationService); + this.items.set(textFileModel, new DirtyDiffItem(model, decorator)); } - private onModelInvisible(editorModel: ITextModel): void { - this.items[editorModel.id].dispose(); - delete this.items[editorModel.id]; + private onModelInvisible(textFileModel: IResolvedTextFileEditorModel): void { + this.items.get(textFileModel)!.dispose(); + this.items.delete(textFileModel); } getModel(editorModel: ITextModel): DirtyDiffModel | null { - const item = this.items[editorModel.id]; - - if (!item) { - return null; + for (const [model, diff] of this.items) { + if (model.textEditorModel.id === editorModel.id) { + return diff.model; + } } - return item.model; + return null; } dispose(): void { diff --git a/src/vs/workbench/contrib/scm/browser/mainPane.ts b/src/vs/workbench/contrib/scm/browser/mainPane.ts deleted file mode 100644 index 3eb3627812f..00000000000 --- a/src/vs/workbench/contrib/scm/browser/mainPane.ts +++ /dev/null @@ -1,362 +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/scmViewlet'; -import { localize } from 'vs/nls'; -import { Event, Emitter } from 'vs/base/common/event'; -import { basename } from 'vs/base/common/resources'; -import { IDisposable, dispose, Disposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle'; -import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; -import { append, $, toggleClass } from 'vs/base/browser/dom'; -import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; -import { ISCMRepository } from 'vs/workbench/contrib/scm/common/scm'; -import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; -import { IAction, Action } from 'vs/base/common/actions'; -import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { ActionBar, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; -import { Command } from 'vs/editor/common/modes'; -import { renderCodicons, Codicon } from 'vs/base/common/codicons'; -import { escape } from 'vs/base/common/strings'; -import { WorkbenchList } from 'vs/platform/list/browser/listService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IViewDescriptor, IViewDescriptorService } from 'vs/workbench/common/views'; -import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; -import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; - -export interface ISpliceEvent { - index: number; - deleteCount: number; - elements: T[]; -} - -export interface IViewModel { - readonly repositories: ISCMRepository[]; - readonly onDidSplice: Event>; - - readonly visibleRepositories: ISCMRepository[]; - readonly onDidChangeVisibleRepositories: Event; - setVisibleRepositories(repositories: ISCMRepository[]): void; - - isVisible(): boolean; - readonly onDidChangeVisibility: Event; -} - -class ProvidersListDelegate implements IListVirtualDelegate { - - getHeight(): number { - return 22; - } - - getTemplateId(): string { - return 'provider'; - } -} - -class StatusBarAction extends Action { - - constructor( - private command: Command, - private commandService: ICommandService - ) { - super(`statusbaraction{${command.id}}`, command.title, '', true); - this.tooltip = command.tooltip || ''; - } - - run(): Promise { - return this.commandService.executeCommand(this.command.id, ...(this.command.arguments || [])); - } -} - -class StatusBarActionViewItem extends ActionViewItem { - - constructor(action: StatusBarAction) { - super(null, action, {}); - } - - updateLabel(): void { - if (this.options.label && this.label) { - this.label.innerHTML = renderCodicons(escape(this.getAction().label)); - } - } -} - -interface RepositoryTemplateData { - title: HTMLElement; - type: HTMLElement; - countContainer: HTMLElement; - count: CountBadge; - actionBar: ActionBar; - disposable: IDisposable; - templateDisposable: IDisposable; -} - -class ProviderRenderer implements IListRenderer { - - readonly templateId = 'provider'; - - private readonly _onDidRenderElement = new Emitter(); - readonly onDidRenderElement = this._onDidRenderElement.event; - - constructor( - @ICommandService protected commandService: ICommandService, - @IThemeService protected themeService: IThemeService - ) { } - - renderTemplate(container: HTMLElement): RepositoryTemplateData { - const provider = append(container, $('.scm-provider')); - const name = append(provider, $('.name')); - const title = append(name, $('span.title')); - const type = append(name, $('span.type')); - const countContainer = append(provider, $('.count')); - const count = new CountBadge(countContainer); - const badgeStyler = attachBadgeStyler(count, this.themeService); - const actionBar = new ActionBar(provider, { actionViewItemProvider: a => new StatusBarActionViewItem(a as StatusBarAction) }); - const disposable = Disposable.None; - const templateDisposable = combinedDisposable(actionBar, badgeStyler); - - return { title, type, countContainer, count, actionBar, disposable, templateDisposable }; - } - - renderElement(repository: ISCMRepository, index: number, templateData: RepositoryTemplateData): void { - templateData.disposable.dispose(); - const disposables = new DisposableStore(); - - if (repository.provider.rootUri) { - templateData.title.textContent = basename(repository.provider.rootUri); - templateData.type.textContent = repository.provider.label; - } else { - templateData.title.textContent = repository.provider.label; - templateData.type.textContent = ''; - } - - const actions: IAction[] = []; - const disposeActions = () => dispose(actions); - disposables.add({ dispose: disposeActions }); - - const update = () => { - disposeActions(); - - const commands = repository.provider.statusBarCommands || []; - actions.splice(0, actions.length, ...commands.map(c => new StatusBarAction(c, this.commandService))); - templateData.actionBar.clear(); - templateData.actionBar.push(actions); - - const count = repository.provider.count || 0; - toggleClass(templateData.countContainer, 'hidden', count === 0); - templateData.count.setCount(count); - - this._onDidRenderElement.fire(repository); - }; - - disposables.add(repository.provider.onDidChange(update, null)); - update(); - - templateData.disposable = disposables; - } - - disposeTemplate(templateData: RepositoryTemplateData): void { - templateData.disposable.dispose(); - templateData.templateDisposable.dispose(); - } -} - -export class MainPane extends ViewPane { - - static readonly ID = 'scm.mainPane'; - static readonly TITLE = localize('scm providers', "Source Control Providers"); - - private list!: WorkbenchList; - - constructor( - protected viewModel: IViewModel, - options: IViewPaneOptions, - @IKeybindingService protected keybindingService: IKeybindingService, - @IContextMenuService protected contextMenuService: IContextMenuService, - @IInstantiationService instantiationService: IInstantiationService, - @IViewDescriptorService viewDescriptorService: IViewDescriptorService, - @IContextKeyService contextKeyService: IContextKeyService, - @IMenuService private readonly menuService: IMenuService, - @IConfigurationService configurationService: IConfigurationService, - @IOpenerService openerService: IOpenerService, - @IThemeService themeService: IThemeService, - @ITelemetryService telemetryService: ITelemetryService, - @ICommandService private readonly commandService: ICommandService - ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); - } - - protected renderBody(container: HTMLElement): void { - super.renderBody(container); - - const delegate = new ProvidersListDelegate(); - const renderer = this.instantiationService.createInstance(ProviderRenderer); - const identityProvider = { getId: (r: ISCMRepository) => r.provider.id }; - - this.list = this.instantiationService.createInstance(WorkbenchList, `SCM Main`, container, delegate, [renderer], { - identityProvider, - horizontalScrolling: false, - overrideStyles: { - listBackground: SIDE_BAR_BACKGROUND - }, - accessibilityProvider: { - getAriaLabel(r: ISCMRepository) { - return r.provider.label; - }, - getWidgetAriaLabel() { - return MainPane.TITLE; - } - } - }) as WorkbenchList; - - this._register(renderer.onDidRenderElement(e => this.list.updateWidth(this.viewModel.repositories.indexOf(e)), null)); - this._register(this.list.onDidChangeSelection(this.onListSelectionChange, this)); - this._register(this.list.onDidChangeFocus(this.onListFocusChange, this)); - this._register(this.list.onContextMenu(this.onListContextMenu, this)); - - this._register(this.viewModel.onDidChangeVisibleRepositories(this.updateListSelection, this)); - - this._register(this.viewModel.onDidSplice(({ index, deleteCount, elements }) => this.splice(index, deleteCount, elements), null)); - this.splice(0, 0, this.viewModel.repositories); - - this._register(this.list); - - this._register(this.configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('scm.providers.visible')) { - this.updateBodySize(); - } - })); - - this.updateListSelection(); - } - - private splice(index: number, deleteCount: number, repositories: ISCMRepository[] = []): void { - this.list.splice(index, deleteCount, repositories); - - const empty = this.list.length === 0; - toggleClass(this.element, 'empty', empty); - - this.updateBodySize(); - } - - focus(): void { - this.list.domFocus(); - } - - protected layoutBody(height: number, width: number): void { - super.layoutBody(height, width); - this.list.layout(height, width); - } - - private updateBodySize(): void { - const visibleCount = this.configurationService.getValue('scm.providers.visible'); - const empty = this.list.length === 0; - const size = Math.min(this.viewModel.repositories.length, visibleCount) * 22; - - this.minimumBodySize = visibleCount === 0 ? 22 : size; - this.maximumBodySize = visibleCount === 0 ? Number.POSITIVE_INFINITY : empty ? Number.POSITIVE_INFINITY : size; - } - - private onListContextMenu(e: IListContextMenuEvent): void { - if (!e.element) { - return; - } - - const repository = e.element; - const contextKeyService = this.contextKeyService.createScoped(); - const scmProviderKey = contextKeyService.createKey('scmProvider', undefined); - scmProviderKey.set(repository.provider.contextValue); - - const menu = this.menuService.createMenu(MenuId.SCMSourceControl, contextKeyService); - const primary: IAction[] = []; - const secondary: IAction[] = []; - const result = { primary, secondary }; - - const disposable = createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, this.contextMenuService, g => g === 'inline'); - - menu.dispose(); - contextKeyService.dispose(); - - if (repository.provider.rootUri) { - secondary.push(new Action('_openInTerminal', localize('open in terminal', "Open In Terminal"), undefined, true, async () => { - await this.commandService.executeCommand('openInTerminal', repository.provider.rootUri); - })); - } - - if (secondary.length === 0) { - return; - } - - this.contextMenuService.showContextMenu({ - getAnchor: () => e.anchor, - getActions: () => secondary, - getActionsContext: () => repository.provider - }); - - disposable.dispose(); - } - - private onListSelectionChange(e: IListEvent): void { - if (e.browserEvent && e.elements.length > 0) { - const scrollTop = this.list.scrollTop; - this.viewModel.setVisibleRepositories(e.elements); - this.list.scrollTop = scrollTop; - } - } - - private onListFocusChange(e: IListEvent): void { - if (e.browserEvent && e.elements.length > 0) { - e.elements[0].focus(); - } - } - - private updateListSelection(): void { - const set = new Set(); - - for (const repository of this.viewModel.visibleRepositories) { - set.add(repository); - } - - const selection: number[] = []; - - for (let i = 0; i < this.list.length; i++) { - if (set.has(this.list.element(i))) { - selection.push(i); - } - } - - this.list.setSelection(selection); - - if (selection.length > 0) { - this.list.setFocus([selection[0]]); - } - } -} - -export class MainPaneDescriptor implements IViewDescriptor { - - readonly id = MainPane.ID; - readonly name = MainPane.TITLE; - readonly containerIcon = Codicon.sourceControl.classNames; - readonly ctorDescriptor: SyncDescriptor; - readonly canToggleVisibility = true; - readonly hideByDefault = false; - readonly order = -1000; - readonly workspace = true; - readonly when = ContextKeyExpr.or(ContextKeyExpr.equals('config.scm.alwaysShowProviders', true), ContextKeyExpr.and(ContextKeyExpr.notEquals('scm.providerCount', 0), ContextKeyExpr.notEquals('scm.providerCount', 1))); - - constructor(viewModel: IViewModel) { - this.ctorDescriptor = new SyncDescriptor(MainPane, [viewModel]); - } -} diff --git a/src/vs/workbench/contrib/scm/browser/media/scm.css b/src/vs/workbench/contrib/scm/browser/media/scm.css new file mode 100644 index 00000000000..9947f240bf2 --- /dev/null +++ b/src/vs/workbench/contrib/scm/browser/media/scm.css @@ -0,0 +1,234 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.scm-view { + height: 100%; + position: relative; +} + +.scm-view .monaco-tl-contents > div { + margin-right: 12px; + overflow: hidden; +} + +.scm-view .count { + display: flex; + margin-left: 6px; +} + +.scm-view .count.hidden { + display: none; +} + +.scm-view .scm-provider { + display: flex; + flex-direction: column; + height: 100%; + align-items: center; + flex-flow: nowrap; +} + +.scm-view.hide-provider-counts .scm-provider > .count, +.scm-view.auto-provider-counts .scm-provider > .count[data-count="0"] { + display: none; +} + +.scm-view .scm-provider > .label { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; +} + +.scm-view .scm-provider > .label > .name { + font-weight: bold; +} + +.scm-view .scm-provider > .label > .description { + opacity: 0.7; + margin-left: 0.5em; + font-size: 0.9em; +} + +.scm-view .scm-provider > .actions { + overflow: hidden; + justify-content: flex-end; +} + +/** + * The following rules are very specific because of inline drop down menus + * https://github.com/microsoft/vscode/issues/101410 + */ +.scm-view .scm-provider > .actions > .monaco-toolbar > .monaco-action-bar > .actions-container > .action-item { + padding-left: 4px; + display: flex; + align-items: center; + min-width: 16px; +} + +.scm-view .scm-provider > .actions > .monaco-toolbar > .monaco-action-bar > .actions-container > .action-item > .action-label, +.scm-view .scm-provider > .actions > .monaco-toolbar > .monaco-action-bar > .actions-container > .action-item > .monaco-dropdown > .dropdown-label > .action-label { + display: flex; + align-items: center; + overflow: hidden; + min-width: 16px; /* for flex */ + height: 100%; + margin: 0; + background-repeat: no-repeat; + background-position: center; +} + +.scm-view .scm-provider > .actions > .monaco-toolbar > .monaco-action-bar > .actions-container > .action-item > .action-label > .codicon { + font-size: 12px; + padding-right: 2px; +} + +.scm-view .scm-provider > .actions > .monaco-toolbar > .monaco-action-bar > .actions-container > .action-item:last-of-type { + padding-right: 0; +} + +.scm-view .scm-provider > .body { + flex-grow: 1; +} + +.scm-view .monaco-list-row { + line-height: 22px; +} + +.scm-view .monaco-list-row .resource-group { + display: flex; + height: 100%; + align-items: center; +} + +.scm-view .monaco-list-row .resource-group > .name { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; +} + +.scm-view .monaco-list-row .resource { + display: flex; + height: 100%; +} + +.scm-view .monaco-list-row .resource.faded { + opacity: 0.7; +} + +.scm-view .monaco-list-row .resource > .name { + flex: 1; + overflow: hidden; +} + +.scm-view .monaco-list-row .resource > .name > .monaco-icon-label::after { + padding-right: 3px; +} + +.scm-view .monaco-list-row .resource > .name.strike-through > .monaco-icon-label > .monaco-icon-label-container > .monaco-icon-name-container > .label-name { + text-decoration: line-through; +} + +.scm-view .monaco-list-row .resource > .decoration-icon { + width: 16px; + height: 100%; + background-repeat: no-repeat; + background-position: 50% 50%; + margin-right: 8px; +} + +.scm-view .monaco-list .monaco-list-row .resource > .name > .monaco-icon-label > .actions { + flex-grow: 100; +} + +.scm-view .monaco-list .monaco-list-row .resource-group > .actions, +.scm-view .monaco-list .monaco-list-row .resource > .name > .monaco-icon-label > .actions { + display: none; + max-width: fit-content; +} + +.scm-view .monaco-list .monaco-list-row:hover .resource-group > .actions, +.scm-view .monaco-list .monaco-list-row.selected .resource-group > .actions, +.scm-view .monaco-list .monaco-list-row.focused .resource-group > .actions, +.scm-view .monaco-list .monaco-list-row:hover .resource > .name > .monaco-icon-label > .actions, +.scm-view .monaco-list .monaco-list-row.selected .resource > .name > .monaco-icon-label > .actions, +.scm-view .monaco-list .monaco-list-row.focused .resource > .name > .monaco-icon-label > .actions, +.scm-view .monaco-list:not(.selection-multiple) .monaco-list-row .resource:hover > .actions { + display: block; +} + +.scm-view.show-actions .scm-provider > .actions, +.scm-view.show-actions > .monaco-list .monaco-list-row .resource-group > .actions, +.scm-view.show-actions > .monaco-list .monaco-list-row .resource > .name > .monaco-icon-label > .actions { + display: block; +} + +.scm-view .monaco-list-row .resource > .name > .monaco-icon-label > .actions .action-label, +.scm-view .monaco-list-row .resource-group > .actions .action-label { + width: 16px; + height: 100%; + background-position: 50% 50%; + background-repeat: no-repeat; +} + +.scm-view .monaco-list-row .resource > .name > .monaco-icon-label > .actions .action-label.codicon { + line-height: 22px; +} + +.scm-view .scm-input { + height: 100%; + margin-left: 9px; +} + +.scm-view .scm-editor { + box-sizing: border-box; + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; +} + +.scm-view .scm-editor.hidden { + display: none; +} + +.scm-view .scm-editor-container { + position: relative; + box-sizing: border-box; + padding: 1px; + outline-offset: -1px; +} + +.scm-editor-validation { + box-sizing: border-box; + font-size: 0.9em; + padding: 1px 3px; + display: block; + border-width: 1px; + border-style: solid; + border-top: none; +} + +.scm-view .scm-editor-placeholder { + position: absolute; + pointer-events: none; + z-index: 1; + padding: 2px 4px 3px 4px; + box-sizing: border-box; + width: 100%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.scm-view .scm-editor-placeholder.hidden { + display: none; +} + +.scm-view .scm-editor-container .monaco-editor-background, +.scm-view .scm-editor-container .monaco-editor, +.scm-view .scm-editor-container .mtk1 { + color: inherit; +} diff --git a/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css b/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css deleted file mode 100644 index 5ea3fd924b9..00000000000 --- a/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css +++ /dev/null @@ -1,221 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -.monaco-workbench .viewlet.scm-viewlet .collapsible.header .actions { - width: initial; - flex: 1; -} - -.scm-viewlet .scm-status { - height: 100%; - position: relative; -} - -.scm-viewlet .monaco-list-row > .scm-provider { - display: flex; - align-items: center; - flex-wrap: wrap; - height: 100%; - padding: 0 12px 0 20px; -} - -.scm-viewlet .monaco-list-row > .scm-provider > .monaco-action-bar { - flex: 1; -} - -.scm-viewlet .monaco-list-row > .scm-provider > .monaco-action-bar .action-item { - padding: 0 4px; - overflow: hidden; - text-overflow: ellipsis; - display: flex; - align-items: center; - min-width: 14px; -} - -.scm-viewlet .monaco-list-row > .scm-provider > .monaco-action-bar .action-label { - text-overflow: ellipsis; - overflow: hidden; - min-width: 14px; /* minimum size of icons */ -} - -.scm-viewlet .monaco-list-row > .scm-provider > .monaco-action-bar .action-label .codicon { - font-size: 14px; - vertical-align: sub; - display: inline-flex; -} - -.scm-viewlet .monaco-list-row > .scm-provider > .monaco-action-bar .action-item:last-of-type { - padding-right: 0; -} - -.scm-viewlet .scm-provider > .name, -.scm-viewlet .scm-provider > .count { - display: flex; - align-items: center; -} - -.scm-viewlet .scm-provider > .count { - margin: 0 0.5em; -} - -.scm-viewlet .scm-provider > .count.hidden { - display: none; -} - -.scm-viewlet .scm-provider > .type, -.scm-viewlet .scm-provider > .name > .type { - opacity: 0.7; - margin-left: 0.5em; - font-size: 0.9em; -} - -.scm-viewlet .monaco-list-row { - line-height: 22px; -} - -.scm-viewlet .monaco-list-row .resource-group { - display: flex; - height: 100%; - align-items: center; -} - -.scm-viewlet .monaco-list-row .resource-group > .name { - flex: 1; - font-size: 11px; - font-weight: bold; - overflow: hidden; - text-overflow: ellipsis; -} - -.scm-viewlet .monaco-list-row .resource { - display: flex; - height: 100%; -} - -.scm-viewlet .monaco-list-row .resource.faded { - opacity: 0.7; -} - -.scm-viewlet .monaco-list-row .resource > .name { - flex: 1; - overflow: hidden; -} - -.scm-viewlet .monaco-list-row .resource > .name.strike-through > .monaco-icon-label > .monaco-icon-label-container > .monaco-icon-name-container > .label-name { - text-decoration: line-through; -} - -.scm-viewlet .monaco-list-row .resource-group > .count { - padding: 0 12px 0 8px; - display: flex; -} - -.scm-viewlet .monaco-list-row .resource > .decoration-icon { - width: 16px; - height: 100%; - background-repeat: no-repeat; - background-position: 50% 50%; - margin-right: 8px; -} - -.scm-viewlet .monaco-list .monaco-list-row .resource > .name > .monaco-icon-label > .actions { - flex-grow: 100; -} - -.scm-viewlet .monaco-list .monaco-list-row .resource-group > .actions, -.scm-viewlet .monaco-list .monaco-list-row .resource > .name > .monaco-icon-label > .actions { - display: none; - max-width: fit-content; -} - -.scm-viewlet .monaco-list .monaco-list-row:hover .resource-group > .actions, -.scm-viewlet .monaco-list .monaco-list-row:hover .resource > .name > .monaco-icon-label > .actions, -.scm-viewlet .monaco-list .monaco-list-row.selected .resource-group > .actions, -.scm-viewlet .monaco-list .monaco-list-row.focused .resource-group > .actions, -.scm-viewlet .monaco-list .monaco-list-row.selected .resource > .name > .monaco-icon-label > .actions, -.scm-viewlet .monaco-list .monaco-list-row.focused .resource > .name > .monaco-icon-label > .actions, -.scm-viewlet .monaco-list:not(.selection-multiple) .monaco-list-row .resource:hover > .actions { - display: block; -} - -.scm-viewlet .scm-status.show-actions > .monaco-list .monaco-list-row .resource-group > .actions, -.scm-viewlet .scm-status.show-actions > .monaco-list .monaco-list-row .resource > .name > .monaco-icon-label > .actions { - display: block; -} - -.scm-viewlet .monaco-list-row .resource > .name > .monaco-icon-label > .actions .action-label, -.scm-viewlet .monaco-list-row .resource-group > .actions .action-label { - width: 16px; - height: 100%; - background-position: 50% 50%; - background-repeat: no-repeat; -} - -.scm-viewlet .monaco-list-row .resource > .name > .monaco-icon-label > .actions .action-label.codicon { - line-height: 22px; -} - -.scm-viewlet .scm-editor { - box-sizing: border-box; - padding: 5px 12px 5px 16px; -} - -.scm-viewlet .scm-editor.hidden { - display: none; -} - -.scm-viewlet .scm-editor-container { - padding: 1px; - position: relative; - outline-offset: -1px; -} - -.scm-viewlet .scm-editor-container > .scm-editor-validation { - position: absolute; - width: 100%; - z-index: 10; - left: 0px; - box-sizing: border-box; - font-size: 0.9em; - padding: 1px 3px; - display: none; -} - -.scm-viewlet .scm-editor-container.synthetic-focus > .scm-editor-validation { - display: block; -} - -.scm-viewlet .scm-editor-placeholder { - position: absolute; - pointer-events: none; - z-index: 1; - margin: 1px; - padding: 3px 4px; - box-sizing: border-box; - width: 100%; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; -} - -.scm-viewlet .scm-editor-placeholder.hidden { - display: none; -} - -.scm-viewlet .scm-editor-container .monaco-editor-background, -.scm-viewlet .scm-editor-container .monaco-editor, -.scm-viewlet .scm-editor-container .mtk1 { - color: inherit; -} - -.scm-viewlet .list-view-mode .monaco-tl-twistie:not(.force-twistie):not(.collapsible) { - background-image: none !important; - width: 8px !important; - margin-right: 0 !important; -} - -.scm-viewlet .scm-status.show-file-icons.hide-arrows.tree-view-mode .monaco-tl-indent .indent-guide:first-child { - border: none; -} diff --git a/src/vs/workbench/contrib/scm/browser/menus.ts b/src/vs/workbench/contrib/scm/browser/menus.ts index 96b01219235..fbf256d8a04 100644 --- a/src/vs/workbench/contrib/scm/browser/menus.ts +++ b/src/vs/workbench/contrib/scm/browser/menus.ts @@ -3,18 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/scmViewlet'; +import 'vs/css!./media/scm'; import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, Disposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; -import { IAction } from 'vs/base/common/actions'; +import { IAction, Action } from 'vs/base/common/actions'; import { createAndFillInContextMenuActions, createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { ISCMProvider, ISCMResource, ISCMResourceGroup } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMResource, ISCMResourceGroup, ISCMProvider, ISCMRepository } from 'vs/workbench/contrib/scm/common/scm'; import { isSCMResource } from './util'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { equals } from 'vs/base/common/arrays'; -import { ISplice } from 'vs/base/common/sequence'; +import { ISplice, ISequence } from 'vs/base/common/sequence'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { localize } from 'vs/nls'; +import { ICommandService } from 'vs/platform/commands/common/commands'; function actionEquals(a: IAction, b: IAction): boolean { return a.id === b.id; @@ -39,11 +42,11 @@ export function getSCMResourceContextKey(resource: ISCMResourceGroup | ISCMResou return isSCMResource(resource) ? resource.resourceGroup.id : resource.id; } -export class SCMMenus implements IDisposable { +export class SCMRepositoryMenus implements IDisposable { private contextKeyService: IContextKeyService; - private titleMenu: IMenu; + readonly titleMenu: IMenu; private titleActionDisposable: IDisposable = Disposable.None; private titleActions: IAction[] = []; private titleSecondaryActions: IAction[] = []; @@ -57,8 +60,9 @@ export class SCMMenus implements IDisposable { private readonly disposables = new DisposableStore(); constructor( - provider: ISCMProvider | undefined, + readonly provider: ISCMProvider | undefined, @IContextKeyService contextKeyService: IContextKeyService, + @ICommandService private readonly commandService: ICommandService, @IMenuService private readonly menuService: IMenuService, @IContextMenuService private readonly contextMenuService: IContextMenuService ) { @@ -67,15 +71,14 @@ export class SCMMenus implements IDisposable { if (provider) { scmProviderKey.set(provider.contextValue); - this.onDidSpliceGroups({ start: 0, deleteCount: 0, toInsert: provider.groups.elements }); provider.groups.onDidSplice(this.onDidSpliceGroups, this, this.disposables); + this.onDidSpliceGroups({ start: 0, deleteCount: 0, toInsert: provider.groups.elements }); } else { scmProviderKey.set(''); } this.titleMenu = this.menuService.createMenu(MenuId.SCMTitle, this.contextKeyService); this.disposables.add(this.titleMenu); - this.titleMenu.onDidChange(this.updateTitleActions, this, this.disposables); this.updateTitleActions(); } @@ -107,6 +110,33 @@ export class SCMMenus implements IDisposable { return this.titleSecondaryActions; } + getRepositoryContextActions(): IAction[] { + if (!this.provider) { + return []; + } + + const contextKeyService = this.contextKeyService.createScoped(); + const scmProviderKey = contextKeyService.createKey('scmProvider', undefined); + scmProviderKey.set(this.provider.contextValue); + + const menu = this.menuService.createMenu(MenuId.SCMSourceControl, contextKeyService); + const primary: IAction[] = []; + const secondary: IAction[] = []; + const result = { primary, secondary }; + const disposable = createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, this.contextMenuService, g => g === 'inline'); + + disposable.dispose(); + menu.dispose(); + + if (this.provider.rootUri) { + secondary.push(new Action('_openInTerminal', localize('open in terminal', "Open In Terminal"), undefined, true, async () => { + await this.commandService.executeCommand('openInTerminal', this.provider!.rootUri); + })); + } + + return secondary; + } + getResourceGroupContextActions(group: ISCMResourceGroup): IAction[] { return this.getActions(MenuId.SCMResourceGroupContext, group).secondary; } @@ -133,7 +163,6 @@ export class SCMMenus implements IDisposable { createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, this.contextMenuService, g => /^inline/.test(g)); menu.dispose(); - contextKeyService.dispose(); return result; } @@ -212,3 +241,49 @@ export class SCMMenus implements IDisposable { this.resourceGroupMenuEntries.forEach(e => e.disposable.dispose()); } } + +export class SCMMenus { + + private readonly disposables = new DisposableStore(); + private readonly entries: { repository: ISCMRepository, dispose: () => void }[] = []; + private readonly menus = new Map(); + + constructor( + repositories: ISequence, + @IInstantiationService private instantiationService: IInstantiationService + ) { + repositories.onDidSplice(this.onDidSplice, this, this.disposables); + this.onDidSplice({ start: 0, deleteCount: 0, toInsert: repositories.elements }); + } + + getRepositoryMenus(provider: ISCMProvider): SCMRepositoryMenus { + if (!this.menus.has(provider)) { + throw new Error('SCM Repository menu not found'); + } + + return this.menus.get(provider)!; + } + + private onDidSplice({ start, deleteCount, toInsert }: ISplice): void { + const entriesToInsert = toInsert.map(repository => { + const menus = this.instantiationService.createInstance(SCMRepositoryMenus, repository.provider); + const dispose = () => { + menus.dispose(); + this.menus.delete(repository.provider); + }; + + this.menus.set(repository.provider, menus); + return { repository, dispose }; + }); + + const deletedEntries = this.entries.splice(start, deleteCount, ...entriesToInsert); + + for (const entry of deletedEntries) { + entry.dispose(); + } + } + + dispose(): void { + this.disposables.dispose(); + } +} diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts deleted file mode 100644 index 2b431ba7464..00000000000 --- a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts +++ /dev/null @@ -1,1235 +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/scmViewlet'; -import { Event, Emitter } from 'vs/base/common/event'; -import { basename, dirname, isEqual } from 'vs/base/common/resources'; -import { IDisposable, Disposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle'; -import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; -import { append, $, addClass, toggleClass, trackFocus, removeClass } from 'vs/base/browser/dom'; -import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { ISCMRepository, ISCMResourceGroup, ISCMResource, InputValidationType } from 'vs/workbench/contrib/scm/common/scm'; -import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; -import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { MenuItemAction, IMenuService } from 'vs/platform/actions/common/actions'; -import { IAction, IActionViewItem, ActionRunner, Action } from 'vs/base/common/actions'; -import { ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { SCMMenus } from './menus'; -import { ActionBar, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IThemeService, LIGHT, registerThemingParticipant, IFileIconTheme } from 'vs/platform/theme/common/themeService'; -import { isSCMResource, isSCMResourceGroup, connectPrimaryMenuToInlineActionBar } from './util'; -import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; -import { WorkbenchCompressibleObjectTree, TreeResourceNavigator, IOpenEvent } from 'vs/platform/list/browser/listService'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { disposableTimeout, ThrottledDelayer } from 'vs/base/common/async'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { ITreeNode, ITreeFilter, ITreeSorter, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; -import { ResourceTree, IResourceNode } from 'vs/base/common/resourceTree'; -import { ISequence, ISplice } from 'vs/base/common/sequence'; -import { ICompressibleTreeRenderer, ICompressibleKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/tree/objectTree'; -import { Iterable } from 'vs/base/common/iterator'; -import { ICompressedTreeNode, ICompressedTreeElement } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; -import { URI } from 'vs/base/common/uri'; -import { FileKind } from 'vs/platform/files/common/files'; -import { compareFileNames } from 'vs/base/common/comparers'; -import { FuzzyScore, createMatches } from 'vs/base/common/filters'; -import { IViewDescriptor, IViewDescriptorService } from 'vs/workbench/common/views'; -import { localize } from 'vs/nls'; -import { flatten, find } from 'vs/base/common/arrays'; -import { memoize } from 'vs/base/common/decorators'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { toResource, SideBySideEditor } from 'vs/workbench/common/editor'; -import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; -import { Hasher } from 'vs/base/common/hash'; -import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; -import { ITextModel } from 'vs/editor/common/model'; -import { IEditorConstructionOptions } from 'vs/editor/common/config/editorOptions'; -import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; -import { MenuPreventer } from 'vs/workbench/contrib/codeEditor/browser/menuPreventer'; -import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard'; -import { ContextMenuController } from 'vs/editor/contrib/contextmenu/contextmenu'; -import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import * as platform from 'vs/base/common/platform'; -import { format } from 'vs/base/common/strings'; -import { inputPlaceholderForeground, inputValidationInfoBorder, inputValidationWarningBorder, inputValidationErrorBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationErrorBackground, inputValidationErrorForeground, inputBackground, inputForeground, inputBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry'; -import { SuggestController } from 'vs/editor/contrib/suggest/suggestController'; -import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; -import { Schemas } from 'vs/base/common/network'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { ModesHoverController } from 'vs/editor/contrib/hover/hover'; -import { ColorDetector } from 'vs/editor/contrib/colorPicker/colorDetector'; -import { LinkDetector } from 'vs/editor/contrib/links/links'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; -import { IModeService } from 'vs/editor/common/services/modeService'; -import { ILabelService } from 'vs/platform/label/common/label'; - -type TreeElement = ISCMResourceGroup | IResourceNode | ISCMResource; - -interface ResourceGroupTemplate { - readonly name: HTMLElement; - readonly count: CountBadge; - readonly actionBar: ActionBar; - elementDisposables: IDisposable; - readonly disposables: IDisposable; -} - -class ResourceGroupRenderer implements ICompressibleTreeRenderer { - - static readonly TEMPLATE_ID = 'resource group'; - get templateId(): string { return ResourceGroupRenderer.TEMPLATE_ID; } - - constructor( - private actionViewItemProvider: IActionViewItemProvider, - private themeService: IThemeService, - private menus: SCMMenus - ) { } - - renderTemplate(container: HTMLElement): ResourceGroupTemplate { - // hack - addClass(container.parentElement!.parentElement!.querySelector('.monaco-tl-twistie')! as HTMLElement, 'force-twistie'); - - const element = append(container, $('.resource-group')); - const name = append(element, $('.name')); - const actionsContainer = append(element, $('.actions')); - const actionBar = new ActionBar(actionsContainer, { actionViewItemProvider: this.actionViewItemProvider }); - const countContainer = append(element, $('.count')); - const count = new CountBadge(countContainer); - const styler = attachBadgeStyler(count, this.themeService); - const elementDisposables = Disposable.None; - const disposables = combinedDisposable(actionBar, styler); - - return { name, count, actionBar, elementDisposables, disposables }; - } - - renderElement(node: ITreeNode, index: number, template: ResourceGroupTemplate): void { - template.elementDisposables.dispose(); - - const group = node.element; - template.name.textContent = group.label; - template.actionBar.clear(); - template.actionBar.context = group; - template.count.setCount(group.elements.length); - - const disposables = new DisposableStore(); - disposables.add(connectPrimaryMenuToInlineActionBar(this.menus.getResourceGroupMenu(group), template.actionBar)); - - template.elementDisposables = disposables; - } - - renderCompressedElements(node: ITreeNode, FuzzyScore>, index: number, templateData: ResourceGroupTemplate, height: number | undefined): void { - throw new Error('Should never happen since node is incompressible'); - } - - disposeElement(group: ITreeNode, index: number, template: ResourceGroupTemplate): void { - template.elementDisposables.dispose(); - } - - disposeTemplate(template: ResourceGroupTemplate): void { - template.elementDisposables.dispose(); - template.disposables.dispose(); - } -} - -interface ResourceTemplate { - element: HTMLElement; - name: HTMLElement; - fileLabel: IResourceLabel; - decorationIcon: HTMLElement; - actionBar: ActionBar; - elementDisposables: IDisposable; - disposables: IDisposable; -} - -class RepositoryPaneActionRunner extends ActionRunner { - - constructor(private getSelectedResources: () => (ISCMResource | IResourceNode)[]) { - super(); - } - - async runAction(action: IAction, context: ISCMResource | IResourceNode): Promise { - if (!(action instanceof MenuItemAction)) { - return super.runAction(action, context); - } - - const selection = this.getSelectedResources(); - const contextIsSelected = selection.some(s => s === context); - const actualContext = contextIsSelected ? selection : [context]; - const args = flatten(actualContext.map(e => ResourceTree.isResourceNode(e) ? ResourceTree.collect(e) : [e])); - await action.run(...args); - } -} - -class ResourceRenderer implements ICompressibleTreeRenderer, FuzzyScore, ResourceTemplate> { - - static readonly TEMPLATE_ID = 'resource'; - get templateId(): string { return ResourceRenderer.TEMPLATE_ID; } - - constructor( - private viewModelProvider: () => ViewModel, - private labels: ResourceLabels, - private actionViewItemProvider: IActionViewItemProvider, - private actionRunner: ActionRunner, - private themeService: IThemeService, - private menus: SCMMenus - ) { } - - renderTemplate(container: HTMLElement): ResourceTemplate { - const element = append(container, $('.resource')); - const name = append(element, $('.name')); - const fileLabel = this.labels.create(name, { supportHighlights: true }); - const actionsContainer = append(fileLabel.element, $('.actions')); - const actionBar = new ActionBar(actionsContainer, { - actionViewItemProvider: this.actionViewItemProvider, - actionRunner: this.actionRunner - }); - - const decorationIcon = append(element, $('.decoration-icon')); - const disposables = combinedDisposable(actionBar, fileLabel); - - return { element, name, fileLabel, decorationIcon, actionBar, elementDisposables: Disposable.None, disposables }; - } - - renderElement(node: ITreeNode | ITreeNode, FuzzyScore>, index: number, template: ResourceTemplate): void { - template.elementDisposables.dispose(); - - const elementDisposables = new DisposableStore(); - const resourceOrFolder = node.element; - const theme = this.themeService.getColorTheme(); - const iconResource = ResourceTree.isResourceNode(resourceOrFolder) ? resourceOrFolder.element : resourceOrFolder; - const icon = iconResource && (theme.type === LIGHT ? iconResource.decorations.icon : iconResource.decorations.iconDark); - - const uri = ResourceTree.isResourceNode(resourceOrFolder) ? resourceOrFolder.uri : resourceOrFolder.sourceUri; - const fileKind = ResourceTree.isResourceNode(resourceOrFolder) ? FileKind.FOLDER : FileKind.FILE; - const viewModel = this.viewModelProvider(); - - template.fileLabel.setFile(uri, { - fileDecorations: { colors: false, badges: !icon }, - hidePath: viewModel.mode === ViewModelMode.Tree, - fileKind, - matches: createMatches(node.filterData) - }); - - template.actionBar.clear(); - template.actionBar.context = resourceOrFolder; - - if (ResourceTree.isResourceNode(resourceOrFolder)) { - if (resourceOrFolder.element) { - elementDisposables.add(connectPrimaryMenuToInlineActionBar(this.menus.getResourceMenu(resourceOrFolder.element.resourceGroup, resourceOrFolder.element), template.actionBar)); - toggleClass(template.name, 'strike-through', resourceOrFolder.element.decorations.strikeThrough); - toggleClass(template.element, 'faded', resourceOrFolder.element.decorations.faded); - } else { - elementDisposables.add(connectPrimaryMenuToInlineActionBar(this.menus.getResourceFolderMenu(resourceOrFolder.context), template.actionBar)); - removeClass(template.name, 'strike-through'); - removeClass(template.element, 'faded'); - } - } else { - elementDisposables.add(connectPrimaryMenuToInlineActionBar(this.menus.getResourceMenu(resourceOrFolder.resourceGroup, resourceOrFolder), template.actionBar)); - toggleClass(template.name, 'strike-through', resourceOrFolder.decorations.strikeThrough); - toggleClass(template.element, 'faded', resourceOrFolder.decorations.faded); - } - - const tooltip = !ResourceTree.isResourceNode(resourceOrFolder) && resourceOrFolder.decorations.tooltip || ''; - - if (icon) { - template.decorationIcon.style.display = ''; - template.decorationIcon.style.backgroundImage = `url('${icon}')`; - template.decorationIcon.title = tooltip; - } else { - template.decorationIcon.style.display = 'none'; - template.decorationIcon.style.backgroundImage = ''; - template.decorationIcon.title = ''; - } - - template.element.setAttribute('data-tooltip', tooltip); - template.elementDisposables = elementDisposables; - } - - disposeElement(resource: ITreeNode | ITreeNode, FuzzyScore>, index: number, template: ResourceTemplate): void { - template.elementDisposables.dispose(); - } - - renderCompressedElements(node: ITreeNode | ICompressedTreeNode>, FuzzyScore>, index: number, template: ResourceTemplate, height: number | undefined): void { - template.elementDisposables.dispose(); - - const elementDisposables = new DisposableStore(); - const compressed = node.element as ICompressedTreeNode>; - const folder = compressed.elements[compressed.elements.length - 1]; - - const label = compressed.elements.map(e => e.name).join('/'); - const fileKind = FileKind.FOLDER; - - template.fileLabel.setResource({ resource: folder.uri, name: label }, { - fileDecorations: { colors: false, badges: true }, - fileKind, - matches: createMatches(node.filterData) - }); - - template.actionBar.clear(); - template.actionBar.context = folder; - - elementDisposables.add(connectPrimaryMenuToInlineActionBar(this.menus.getResourceFolderMenu(folder.context), template.actionBar)); - - removeClass(template.name, 'strike-through'); - removeClass(template.element, 'faded'); - template.decorationIcon.style.display = 'none'; - template.decorationIcon.style.backgroundImage = ''; - - template.element.setAttribute('data-tooltip', ''); - template.elementDisposables = elementDisposables; - } - - disposeCompressedElements(node: ITreeNode | ICompressedTreeNode>, FuzzyScore>, index: number, template: ResourceTemplate, height: number | undefined): void { - template.elementDisposables.dispose(); - } - - disposeTemplate(template: ResourceTemplate): void { - template.elementDisposables.dispose(); - template.disposables.dispose(); - } -} - -class ProviderListDelegate implements IListVirtualDelegate { - - getHeight() { return 22; } - - getTemplateId(element: TreeElement) { - if (ResourceTree.isResourceNode(element) || isSCMResource(element)) { - return ResourceRenderer.TEMPLATE_ID; - } else { - return ResourceGroupRenderer.TEMPLATE_ID; - } - } -} - -class SCMTreeFilter implements ITreeFilter { - - filter(element: TreeElement): boolean { - if (ResourceTree.isResourceNode(element)) { - return true; - } else if (isSCMResourceGroup(element)) { - return element.elements.length > 0 || !element.hideWhenEmpty; - } else { - return true; - } - } -} - -export class SCMTreeSorter implements ITreeSorter { - - @memoize - private get viewModel(): ViewModel { return this.viewModelProvider(); } - - constructor(private viewModelProvider: () => ViewModel) { } - - compare(one: TreeElement, other: TreeElement): number { - if (this.viewModel.mode === ViewModelMode.List) { - return 0; - } - - if (isSCMResourceGroup(one) && isSCMResourceGroup(other)) { - return 0; - } - - const oneIsDirectory = ResourceTree.isResourceNode(one); - const otherIsDirectory = ResourceTree.isResourceNode(other); - - if (oneIsDirectory !== otherIsDirectory) { - return oneIsDirectory ? -1 : 1; - } - - const oneName = ResourceTree.isResourceNode(one) ? one.name : basename((one as ISCMResource).sourceUri); - const otherName = ResourceTree.isResourceNode(other) ? other.name : basename((other as ISCMResource).sourceUri); - - return compareFileNames(oneName, otherName); - } -} - -export class SCMTreeKeyboardNavigationLabelProvider implements ICompressibleKeyboardNavigationLabelProvider { - - getKeyboardNavigationLabel(element: TreeElement): { toString(): string; } | undefined { - if (ResourceTree.isResourceNode(element)) { - return element.name; - } else if (isSCMResourceGroup(element)) { - return element.label; - } else { - return basename(element.sourceUri); - } - } - - getCompressedNodeKeyboardNavigationLabel(elements: TreeElement[]): { toString(): string | undefined; } | undefined { - const folders = elements as IResourceNode[]; - return folders.map(e => e.name).join('/'); - } -} - -class SCMResourceIdentityProvider implements IIdentityProvider { - - getId(element: TreeElement): string { - if (ResourceTree.isResourceNode(element)) { - const group = element.context; - return `${group.provider.contextValue}/${group.id}/$FOLDER/${element.uri.toString()}`; - } else if (isSCMResource(element)) { - const group = element.resourceGroup; - const provider = group.provider; - return `${provider.contextValue}/${group.id}/${element.sourceUri.toString()}`; - } else { - const provider = element.provider; - return `${provider.contextValue}/${element.id}`; - } - } -} - -export class SCMAccessibilityProvider implements IListAccessibilityProvider { - - constructor(@ILabelService private readonly labelService: ILabelService) { } - - getWidgetAriaLabel(): string { - return localize('scm', "Source Control Management"); - } - - getAriaLabel(element: TreeElement): string { - if (ResourceTree.isResourceNode(element)) { - return this.labelService.getUriLabel(element.uri, { relative: true, noPrefix: true }) || element.name; - } else if (isSCMResourceGroup(element)) { - return element.label; - } else { - const result: string[] = []; - - result.push(basename(element.sourceUri)); - - if (element.decorations.tooltip) { - result.push(element.decorations.tooltip); - } - - const path = this.labelService.getUriLabel(dirname(element.sourceUri), { relative: true, noPrefix: true }); - - if (path) { - result.push(path); - } - - return result.join(', '); - } - } -} - -interface IGroupItem { - readonly group: ISCMResourceGroup; - readonly resources: ISCMResource[]; - readonly tree: ResourceTree; - readonly disposable: IDisposable; -} - -function groupItemAsTreeElement(item: IGroupItem, mode: ViewModelMode): ICompressedTreeElement { - const children = mode === ViewModelMode.List - ? Iterable.map(item.resources, element => ({ element, incompressible: true })) - : Iterable.map(item.tree.root.children, node => asTreeElement(node, true)); - - return { element: item.group, children, incompressible: true, collapsible: true }; -} - -function asTreeElement(node: IResourceNode, forceIncompressible: boolean): ICompressedTreeElement { - return { - element: (node.childrenCount === 0 && node.element) ? node.element : node, - children: Iterable.map(node.children, node => asTreeElement(node, false)), - incompressible: !!node.element || forceIncompressible - }; -} - -const enum ViewModelMode { - List = 'list', - Tree = 'tree' -} - -class ViewModel { - - private readonly _onDidChangeMode = new Emitter(); - readonly onDidChangeMode = this._onDidChangeMode.event; - - get mode(): ViewModelMode { return this._mode; } - set mode(mode: ViewModelMode) { - this._mode = mode; - - for (const item of this.items) { - item.tree.clear(); - - if (mode === ViewModelMode.Tree) { - for (const resource of item.resources) { - item.tree.add(resource.sourceUri, resource); - } - } - } - - this.refresh(); - this._onDidChangeMode.fire(mode); - } - - private items: IGroupItem[] = []; - private visibilityDisposables = new DisposableStore(); - private scrollTop: number | undefined; - private firstVisible = true; - private disposables = new DisposableStore(); - - constructor( - private groups: ISequence, - private tree: WorkbenchCompressibleObjectTree, - private _mode: ViewModelMode, - @IEditorService protected editorService: IEditorService, - @IConfigurationService protected configurationService: IConfigurationService, - ) { } - - private onDidSpliceGroups({ start, deleteCount, toInsert }: ISplice): void { - const itemsToInsert: IGroupItem[] = []; - - for (const group of toInsert) { - const tree = new ResourceTree(group, group.provider.rootUri || URI.file('/')); - const resources: ISCMResource[] = [...group.elements]; - const disposable = combinedDisposable( - group.onDidChange(() => this.tree.refilter()), - group.onDidSplice(splice => this.onDidSpliceGroup(item, splice)) - ); - - const item: IGroupItem = { group, resources, tree, disposable }; - - if (this._mode === ViewModelMode.Tree) { - for (const resource of resources) { - item.tree.add(resource.sourceUri, resource); - } - } - - itemsToInsert.push(item); - } - - const itemsToDispose = this.items.splice(start, deleteCount, ...itemsToInsert); - - for (const item of itemsToDispose) { - item.disposable.dispose(); - } - - this.refresh(); - } - - private onDidSpliceGroup(item: IGroupItem, { start, deleteCount, toInsert }: ISplice): void { - const deleted = item.resources.splice(start, deleteCount, ...toInsert); - - if (this._mode === ViewModelMode.Tree) { - for (const resource of deleted) { - item.tree.delete(resource.sourceUri); - } - - for (const resource of toInsert) { - item.tree.add(resource.sourceUri, resource); - } - } - - this.refresh(item); - } - - setVisible(visible: boolean): void { - if (visible) { - this.visibilityDisposables = new DisposableStore(); - this.groups.onDidSplice(this.onDidSpliceGroups, this, this.visibilityDisposables); - this.onDidSpliceGroups({ start: 0, deleteCount: this.items.length, toInsert: this.groups.elements }); - - if (typeof this.scrollTop === 'number') { - this.tree.scrollTop = this.scrollTop; - this.scrollTop = undefined; - } - - this.editorService.onDidActiveEditorChange(this.onDidActiveEditorChange, this, this.visibilityDisposables); - this.onDidActiveEditorChange(); - } else { - this.visibilityDisposables.dispose(); - this.onDidSpliceGroups({ start: 0, deleteCount: this.items.length, toInsert: [] }); - this.scrollTop = this.tree.scrollTop; - } - } - - private refresh(item?: IGroupItem): void { - if (item) { - this.tree.setChildren(item.group, groupItemAsTreeElement(item, this.mode).children); - } else { - this.tree.setChildren(null, this.items.map(item => groupItemAsTreeElement(item, this.mode))); - } - } - - private onDidActiveEditorChange(): void { - if (!this.configurationService.getValue('scm.autoReveal')) { - return; - } - - if (this.firstVisible) { - this.firstVisible = false; - this.visibilityDisposables.add(disposableTimeout(() => this.onDidActiveEditorChange(), 250)); - return; - } - - const editor = this.editorService.activeEditor; - - if (!editor) { - return; - } - - const uri = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }); - - if (!uri) { - return; - } - - // go backwards from last group - for (let i = this.items.length - 1; i >= 0; i--) { - const item = this.items[i]; - const resource = this.mode === ViewModelMode.Tree - ? item.tree.getNode(uri)?.element - : find(item.resources, r => isEqual(r.sourceUri, uri)); - - if (resource) { - this.tree.reveal(resource); - this.tree.setSelection([resource]); - this.tree.setFocus([resource]); - return; - } - } - } - - dispose(): void { - this.visibilityDisposables.dispose(); - this.disposables.dispose(); - - for (const item of this.items) { - item.disposable.dispose(); - } - - this.items = []; - } -} - -export class ToggleViewModeAction extends Action { - - static readonly ID = 'workbench.scm.action.toggleViewMode'; - static readonly LABEL = localize('toggleViewMode', "Toggle View Mode"); - - constructor(private viewModel: ViewModel) { - super(ToggleViewModeAction.ID, ToggleViewModeAction.LABEL); - - this._register(this.viewModel.onDidChangeMode(this.onDidChangeMode, this)); - this.onDidChangeMode(this.viewModel.mode); - } - - async run(): Promise { - this.viewModel.mode = this.viewModel.mode === ViewModelMode.List ? ViewModelMode.Tree : ViewModelMode.List; - } - - private onDidChangeMode(mode: ViewModelMode): void { - const iconClass = mode === ViewModelMode.List ? 'codicon-list-tree' : 'codicon-list-flat'; - this.class = `scm-action toggle-view-mode ${iconClass}`; - } -} - -export class RepositoryPane extends ViewPane { - private readonly defaultInputFontFamily = 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif'; - - private cachedHeight: number | undefined = undefined; - private cachedWidth: number | undefined = undefined; - private inputContainer!: HTMLElement; - private validationContainer!: HTMLElement; - private inputEditor!: CodeEditorWidget; - private inputModel!: ITextModel; - private listContainer!: HTMLElement; - private tree!: WorkbenchCompressibleObjectTree; - private viewModel!: ViewModel; - private listLabels!: ResourceLabels; - private menus: SCMMenus; - private toggleViewModelModeAction: ToggleViewModeAction | undefined; - protected contextKeyService: IContextKeyService; - private commitTemplate = ''; - - constructor( - readonly repository: ISCMRepository, - options: IViewPaneOptions, - @IKeybindingService protected keybindingService: IKeybindingService, - @IThemeService protected themeService: IThemeService, - @IContextMenuService protected contextMenuService: IContextMenuService, - @IContextViewService protected contextViewService: IContextViewService, - @ICommandService protected commandService: ICommandService, - @INotificationService private readonly notificationService: INotificationService, - @IEditorService protected editorService: IEditorService, - @IInstantiationService protected instantiationService: IInstantiationService, - @IViewDescriptorService viewDescriptorService: IViewDescriptorService, - @IConfigurationService protected configurationService: IConfigurationService, - @IContextKeyService contextKeyService: IContextKeyService, - @IMenuService protected menuService: IMenuService, - @IStorageService private storageService: IStorageService, - @IModelService private modelService: IModelService, - @IModeService private modeService: IModeService, - @IOpenerService openerService: IOpenerService, - @ITelemetryService telemetryService: ITelemetryService, - ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); - - this.menus = instantiationService.createInstance(SCMMenus, this.repository.provider); - this._register(this.menus); - this._register(this.menus.onDidChangeTitle(this._onDidChangeTitleArea.fire, this._onDidChangeTitleArea)); - - this.contextKeyService = contextKeyService.createScoped(this.element); - this.contextKeyService.createKey('scmRepository', this.repository); - } - - render(): void { - super.render(); - this._register(this.menus.onDidChangeTitle(this.updateActions, this)); - } - - protected renderHeaderTitle(container: HTMLElement): void { - let title: string; - let type: string; - - if (this.repository.provider.rootUri) { - title = basename(this.repository.provider.rootUri); - type = this.repository.provider.label; - } else { - title = this.repository.provider.label; - type = ''; - } - - super.renderHeaderTitle(container, title); - addClass(container, 'scm-provider'); - append(container, $('span.type', undefined, type)); - } - - protected renderBody(container: HTMLElement): void { - super.renderBody(container); - - const focusTracker = trackFocus(container); - this._register(focusTracker.onDidFocus(() => this.repository.focus())); - this._register(focusTracker); - - // Input - this.inputContainer = append(container, $('.scm-editor')); - const editorContainer = append(this.inputContainer, $('.scm-editor-container')); - - const placeholderTextContainer = append(editorContainer, $('.scm-editor-placeholder')); - const updatePlaceholder = () => { - const binding = this.keybindingService.lookupKeybinding('scm.acceptInput'); - const label = binding ? binding.getLabel() : (platform.isMacintosh ? 'Cmd+Enter' : 'Ctrl+Enter'); - const placeholderText = format(this.repository.input.placeholder, label); - - this.inputEditor.updateOptions({ ariaLabel: placeholderText }); - placeholderTextContainer.textContent = placeholderText; - }; - - this.validationContainer = append(editorContainer, $('.scm-editor-validation')); - - const validationDelayer = new ThrottledDelayer(200); - const validate = () => { - const position = this.inputEditor.getSelection()?.getStartPosition(); - const offset = position && this.inputModel.getOffsetAt(position); - const value = this.inputModel.getValue(); - - return this.repository.input.validateInput(value, offset || 0).then(result => { - if (!result) { - removeClass(editorContainer, 'validation-info'); - removeClass(editorContainer, 'validation-warning'); - removeClass(editorContainer, 'validation-error'); - removeClass(this.validationContainer, 'validation-info'); - removeClass(this.validationContainer, 'validation-warning'); - removeClass(this.validationContainer, 'validation-error'); - this.validationContainer.textContent = null; - } else { - toggleClass(editorContainer, 'validation-info', result.type === InputValidationType.Information); - toggleClass(editorContainer, 'validation-warning', result.type === InputValidationType.Warning); - toggleClass(editorContainer, 'validation-error', result.type === InputValidationType.Error); - toggleClass(this.validationContainer, 'validation-info', result.type === InputValidationType.Information); - toggleClass(this.validationContainer, 'validation-warning', result.type === InputValidationType.Warning); - toggleClass(this.validationContainer, 'validation-error', result.type === InputValidationType.Error); - this.validationContainer.textContent = result.message; - } - }); - }; - - const triggerValidation = () => validationDelayer.trigger(validate); - - const editorOptions: IEditorConstructionOptions = { - ...getSimpleEditorOptions(), - lineDecorationsWidth: 4, - dragAndDrop: false, - cursorWidth: 1, - fontSize: 13, - lineHeight: 20, - fontFamily: this.getInputEditorFontFamily(), - wrappingStrategy: 'advanced', - wrappingIndent: 'none', - padding: { top: 3, bottom: 3 }, - quickSuggestions: false - }; - const codeEditorWidgetOptions: ICodeEditorWidgetOptions = { - isSimpleWidget: true, - contributions: EditorExtensionsRegistry.getSomeEditorContributions([ - SuggestController.ID, - SnippetController2.ID, - MenuPreventer.ID, - SelectionClipboardContributionID, - ContextMenuController.ID, - ColorDetector.ID, - ModesHoverController.ID, - LinkDetector.ID - ]) - }; - - const services = new ServiceCollection([IContextKeyService, this.contextKeyService]); - const instantiationService = this.instantiationService.createChild(services); - this.inputEditor = instantiationService.createInstance(CodeEditorWidget, editorContainer, editorOptions, codeEditorWidgetOptions); - - this._register(this.inputEditor); - - this._register(this.inputEditor.onDidFocusEditorText(() => addClass(editorContainer, 'synthetic-focus'))); - this._register(this.inputEditor.onDidBlurEditorText(() => removeClass(editorContainer, 'synthetic-focus'))); - - const onInputFontFamilyChanged = Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.inputFontFamily')); - this._register(onInputFontFamilyChanged(() => this.inputEditor.updateOptions({ fontFamily: this.getInputEditorFontFamily() }))); - - let query: string | undefined; - - if (this.repository.provider.rootUri) { - query = `rootUri=${encodeURIComponent(this.repository.provider.rootUri.toString())}`; - } - - const uri = URI.from({ - scheme: Schemas.vscode, - path: `scm/${this.repository.provider.contextValue}/${this.repository.provider.id}/input`, - query - }); - - this.configurationService.updateValue('editor.wordBasedSuggestions', false, { resource: uri }, ConfigurationTarget.MEMORY); - - const mode = this.modeService.create('scminput'); - this.inputModel = this.modelService.getModel(uri) || this.modelService.createModel('', mode, uri); - this.inputEditor.setModel(this.inputModel); - - this._register(this.inputEditor.onDidChangeCursorPosition(triggerValidation)); - - // Keep model in sync with API - this.inputModel.setValue(this.repository.input.value); - this._register(this.repository.input.onDidChange(value => { - if (value === this.inputModel.getValue()) { - return; - } - this.inputModel.setValue(value); - this.inputEditor.setPosition(this.inputModel.getFullModelRange().getEndPosition()); - })); - - // Keep API in sync with model and update placeholder and validation - toggleClass(placeholderTextContainer, 'hidden', this.inputModel.getValueLength() > 0); - this.inputModel.onDidChangeContent(() => { - this.repository.input.value = this.inputModel.getValue(); - toggleClass(placeholderTextContainer, 'hidden', this.inputModel.getValueLength() > 0); - triggerValidation(); - }); - - updatePlaceholder(); - this._register(this.repository.input.onDidChangePlaceholder(updatePlaceholder, null)); - this._register(this.keybindingService.onDidUpdateKeybindings(updatePlaceholder, null)); - - const onDidChangeContentHeight = Event.filter(this.inputEditor.onDidContentSizeChange, e => e.contentHeightChanged); - this._register(onDidChangeContentHeight(() => this.layoutBody())); - - this._register(this.repository.provider.onDidChangeCommitTemplate(this.onDidChangeCommitTemplate, this)); - - this.onDidChangeCommitTemplate(); - - // Input box visibility - this._register(this.repository.input.onDidChangeVisibility(this.updateInputBoxVisibility, this)); - this.updateInputBoxVisibility(); - - // List - this.listContainer = append(container, $('.scm-status.show-file-icons')); - - const updateActionsVisibility = () => toggleClass(this.listContainer, 'show-actions', this.configurationService.getValue('scm.alwaysShowActions')); - Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.alwaysShowActions'))(updateActionsVisibility); - updateActionsVisibility(); - - const delegate = new ProviderListDelegate(); - - const actionViewItemProvider = (action: IAction) => this.getActionViewItem(action); - - this.listLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility }); - this._register(this.listLabels); - - const actionRunner = new RepositoryPaneActionRunner(() => this.getSelectedResources()); - this._register(actionRunner); - this._register(actionRunner.onDidBeforeRun(() => this.tree.domFocus())); - - const renderers = [ - new ResourceGroupRenderer(actionViewItemProvider, this.themeService, this.menus), - new ResourceRenderer(() => this.viewModel, this.listLabels, actionViewItemProvider, actionRunner, this.themeService, this.menus) - ]; - - const filter = new SCMTreeFilter(); - const sorter = new SCMTreeSorter(() => this.viewModel); - const keyboardNavigationLabelProvider = new SCMTreeKeyboardNavigationLabelProvider(); - const identityProvider = new SCMResourceIdentityProvider(); - - this.tree = this.instantiationService.createInstance( - WorkbenchCompressibleObjectTree, - 'SCM Tree Repo', - this.listContainer, - delegate, - renderers, - { - identityProvider, - horizontalScrolling: false, - filter, - sorter, - keyboardNavigationLabelProvider, - overrideStyles: { - listBackground: SIDE_BAR_BACKGROUND - }, - accessibilityProvider: this.instantiationService.createInstance(SCMAccessibilityProvider) - }) as WorkbenchCompressibleObjectTree; - - const navigator = this._register(new TreeResourceNavigator(this.tree, { openOnSelection: false })); - this._register(navigator.onDidOpenResource(this.open, this)); - - this._register(Event.chain(this.tree.onDidPin) - .map(e => e.elements[0]) - .filter(e => !!e && !isSCMResourceGroup(e) && !ResourceTree.isResourceNode(e)) - .on(this.pin, this)); - - this._register(this.tree.onContextMenu(this.onListContextMenu, this)); - this._register(this.tree); - - let viewMode = this.configurationService.getValue<'tree' | 'list'>('scm.defaultViewMode') === 'list' ? ViewModelMode.List : ViewModelMode.Tree; - - const rootUri = this.repository.provider.rootUri; - - if (typeof rootUri !== 'undefined') { - const storageMode = this.storageService.get(`scm.repository.viewMode:${rootUri.toString()}`, StorageScope.WORKSPACE) as ViewModelMode; - - if (typeof storageMode === 'string') { - viewMode = storageMode; - } - } - - this.viewModel = this.instantiationService.createInstance(ViewModel, this.repository.provider.groups, this.tree, viewMode); - this._register(this.viewModel); - - addClass(this.listContainer, 'file-icon-themable-tree'); - addClass(this.listContainer, 'show-file-icons'); - - this.updateIndentStyles(this.themeService.getFileIconTheme()); - this._register(this.themeService.onDidFileIconThemeChange(this.updateIndentStyles, this)); - this._register(this.viewModel.onDidChangeMode(this.onDidChangeMode, this)); - - this.toggleViewModelModeAction = new ToggleViewModeAction(this.viewModel); - this._register(this.toggleViewModelModeAction); - - this._register(this.onDidChangeBodyVisibility(this._onDidChangeVisibility, this)); - - this.updateActions(); - } - - private updateIndentStyles(theme: IFileIconTheme): void { - toggleClass(this.listContainer, 'list-view-mode', this.viewModel.mode === ViewModelMode.List); - toggleClass(this.listContainer, 'tree-view-mode', this.viewModel.mode === ViewModelMode.Tree); - toggleClass(this.listContainer, 'align-icons-and-twisties', this.viewModel.mode === ViewModelMode.Tree && theme.hasFileIcons && !theme.hasFolderIcons); - toggleClass(this.listContainer, 'hide-arrows', this.viewModel.mode === ViewModelMode.Tree && theme.hidesExplorerArrows === true); - } - - private onDidChangeMode(): void { - this.updateIndentStyles(this.themeService.getFileIconTheme()); - - const rootUri = this.repository.provider.rootUri; - - if (typeof rootUri === 'undefined') { - return; - } - - this.storageService.store(`scm.repository.viewMode:${rootUri.toString()}`, this.viewModel.mode, StorageScope.WORKSPACE); - } - - layoutBody(height: number | undefined = this.cachedHeight, width: number | undefined = this.cachedWidth): void { - if (height === undefined) { - return; - } - - if (width !== undefined) { - super.layoutBody(height, width); - } - - this.cachedHeight = height; - this.cachedWidth = width; - - if (this.repository.input.visible) { - removeClass(this.inputContainer, 'hidden'); - - const editorContentHeight = this.inputEditor.getContentHeight(); - const editorHeight = Math.min(editorContentHeight, 134); - this.inputEditor.layout({ height: editorHeight, width: width! - 12 - 16 - 2 }); - - this.validationContainer.style.top = `${editorHeight + 1}px`; - - const listHeight = height - (editorHeight + 5 + 2 + 5); - this.listContainer.style.height = `${listHeight}px`; - this.tree.layout(listHeight, width); - } else { - addClass(this.inputContainer, 'hidden'); - - this.inputEditor.onHide(); - this.listContainer.style.height = `${height}px`; - this.tree.layout(height, width); - } - } - - focus(): void { - super.focus(); - - if (this.isExpanded()) { - if (this.repository.input.visible) { - this.inputEditor.focus(); - } else { - this.tree.domFocus(); - } - - this.repository.focus(); - } - } - - private _onDidChangeVisibility(visible: boolean): void { - this.viewModel.setVisible(visible); - - if (this.repository.input.visible && visible) { - this.inputEditor.onVisible(); - } else { - this.inputEditor.onHide(); - } - } - - getActions(): IAction[] { - if (this.toggleViewModelModeAction) { - - return [ - this.toggleViewModelModeAction, - ...this.menus.getTitleActions() - ]; - } else { - return this.menus.getTitleActions(); - } - } - - getSecondaryActions(): IAction[] { - return this.menus.getTitleSecondaryActions(); - } - - getActionViewItem(action: IAction): IActionViewItem | undefined { - if (!(action instanceof MenuItemAction)) { - return undefined; - } - - return new ContextAwareMenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService); - } - - getActionsContext(): any { - return this.repository.provider; - } - - private open(e: IOpenEvent): void { - if (!e.element || isSCMResourceGroup(e.element) || ResourceTree.isResourceNode(e.element)) { - return; - } - - e.element.open(!!e.editorOptions.preserveFocus); - } - - private pin(): void { - const activeEditorPane = this.editorService.activeEditorPane; - - if (activeEditorPane) { - activeEditorPane.group.pinEditor(activeEditorPane.input); - } - } - - private onListContextMenu(e: ITreeContextMenuEvent): void { - if (!e.element) { - return; - } - - const element = e.element; - let actions: IAction[] = []; - - if (isSCMResourceGroup(element)) { - actions = this.menus.getResourceGroupContextActions(element); - } else if (ResourceTree.isResourceNode(element)) { - if (element.element) { - actions = this.menus.getResourceContextActions(element.element); - } else { - actions = this.menus.getResourceFolderContextActions(element.context); - } - } else { - actions = this.menus.getResourceContextActions(element); - } - - const actionRunner = new RepositoryPaneActionRunner(() => this.getSelectedResources()); - actionRunner.onDidBeforeRun(() => this.tree.domFocus()); - - this.contextMenuService.showContextMenu({ - getAnchor: () => e.anchor, - getActions: () => actions, - getActionsContext: () => element, - actionRunner - }); - } - - private getSelectedResources(): (ISCMResource | IResourceNode)[] { - return this.tree.getSelection() - .filter(r => !!r && !isSCMResourceGroup(r))! as any; - } - - private onDidChangeCommitTemplate(): void { - if (typeof this.repository.provider.commitTemplate === 'undefined' || !this.repository.input.visible) { - return; - } - - const oldCommitTemplate = this.commitTemplate; - this.commitTemplate = this.repository.provider.commitTemplate; - - const value = this.inputModel.getValue(); - - if (value && value !== oldCommitTemplate) { - return; - } - - this.inputModel.setValue(this.commitTemplate); - } - - private updateInputBoxVisibility(): void { - if (this.cachedHeight) { - this.layoutBody(this.cachedHeight); - } - } - - private getInputEditorFontFamily(): string { - const inputFontFamily = this.configurationService.getValue('scm.inputFontFamily'); - - if (inputFontFamily.toLowerCase() === 'editor') { - return this.configurationService.getValue('editor.fontFamily'); - } - - if (inputFontFamily.length !== 0 && inputFontFamily.toLowerCase() !== 'default') { - return inputFontFamily; - } - - return this.defaultInputFontFamily; - } -} - -export class RepositoryViewDescriptor implements IViewDescriptor { - - private static counter = 0; - - readonly id: string; - readonly name: string; - readonly ctorDescriptor: SyncDescriptor; - readonly canToggleVisibility = true; - readonly order = -500; - readonly workspace = true; - - constructor(readonly repository: ISCMRepository, readonly hideByDefault: boolean) { - const repoId = repository.provider.rootUri ? repository.provider.rootUri.toString() : `#${RepositoryViewDescriptor.counter++}`; - const hasher = new Hasher(); - hasher.hash(repository.provider.label); - hasher.hash(repoId); - this.id = `scm:repository:${hasher.value}`; - this.name = repository.provider.rootUri ? basename(repository.provider.rootUri) : repository.provider.label; - - this.ctorDescriptor = new SyncDescriptor(RepositoryPane, [repository]); - } -} - -registerThemingParticipant((theme, collector) => { - const inputBackgroundColor = theme.getColor(inputBackground); - if (inputBackgroundColor) { - collector.addRule(`.scm-viewlet .scm-editor-container .monaco-editor-background, - .scm-viewlet .scm-editor-container .monaco-editor, - .scm-viewlet .scm-editor-container .monaco-editor .margin - { background-color: ${inputBackgroundColor}; }`); - } - - const inputForegroundColor = theme.getColor(inputForeground); - if (inputForegroundColor) { - collector.addRule(`.scm-viewlet .scm-editor-container .mtk1 { color: ${inputForegroundColor}; }`); - } - - const inputBorderColor = theme.getColor(inputBorder); - if (inputBorderColor) { - collector.addRule(`.scm-viewlet .scm-editor-container { outline: 1px solid ${inputBorderColor}; }`); - } - - const focusBorderColor = theme.getColor(focusBorder); - if (focusBorderColor) { - collector.addRule(`.scm-viewlet .scm-editor-container.synthetic-focus { outline: 1px solid ${focusBorderColor}; }`); - } - - const inputPlaceholderForegroundColor = theme.getColor(inputPlaceholderForeground); - if (inputPlaceholderForegroundColor) { - collector.addRule(`.scm-viewlet .scm-editor-placeholder { color: ${inputPlaceholderForegroundColor}; }`); - } - - const inputValidationInfoBorderColor = theme.getColor(inputValidationInfoBorder); - if (inputValidationInfoBorderColor) { - collector.addRule(`.scm-viewlet .scm-editor-container.validation-info { outline: 1px solid ${inputValidationInfoBorderColor}; }`); - collector.addRule(`.scm-viewlet .scm-editor-validation.validation-info { border: 1px solid ${inputValidationInfoBorderColor}; }`); - } - - const inputValidationInfoBackgroundColor = theme.getColor(inputValidationInfoBackground); - if (inputValidationInfoBackgroundColor) { - collector.addRule(`.scm-viewlet .scm-editor-validation.validation-info { background-color: ${inputValidationInfoBackgroundColor}; }`); - } - - const inputValidationInfoForegroundColor = theme.getColor(inputValidationInfoForeground); - if (inputValidationInfoForegroundColor) { - collector.addRule(`.scm-viewlet .scm-editor-validation.validation-info { color: ${inputValidationInfoForegroundColor}; }`); - } - - const inputValidationWarningBorderColor = theme.getColor(inputValidationWarningBorder); - if (inputValidationWarningBorderColor) { - collector.addRule(`.scm-viewlet .scm-editor-container.validation-warning { outline: 1px solid ${inputValidationWarningBorderColor}; }`); - collector.addRule(`.scm-viewlet .scm-editor-validation.validation-warning { border: 1px solid ${inputValidationWarningBorderColor}; }`); - } - - const inputValidationWarningBackgroundColor = theme.getColor(inputValidationWarningBackground); - if (inputValidationWarningBackgroundColor) { - collector.addRule(`.scm-viewlet .scm-editor-validation.validation-warning { background-color: ${inputValidationWarningBackgroundColor}; }`); - } - - const inputValidationWarningForegroundColor = theme.getColor(inputValidationWarningForeground); - if (inputValidationWarningForegroundColor) { - collector.addRule(`.scm-viewlet .scm-editor-validation.validation-warning { color: ${inputValidationWarningForegroundColor}; }`); - } - - const inputValidationErrorBorderColor = theme.getColor(inputValidationErrorBorder); - if (inputValidationErrorBorderColor) { - collector.addRule(`.scm-viewlet .scm-editor-container.validation-error { outline: 1px solid ${inputValidationErrorBorderColor}; }`); - collector.addRule(`.scm-viewlet .scm-editor-validation.validation-error { border: 1px solid ${inputValidationErrorBorderColor}; }`); - } - - const inputValidationErrorBackgroundColor = theme.getColor(inputValidationErrorBackground); - if (inputValidationErrorBackgroundColor) { - collector.addRule(`.scm-viewlet .scm-editor-validation.validation-error { background-color: ${inputValidationErrorBackgroundColor}; }`); - } - - const inputValidationErrorForegroundColor = theme.getColor(inputValidationErrorForeground); - if (inputValidationErrorForegroundColor) { - collector.addRule(`.scm-viewlet .scm-editor-validation.validation-error { color: ${inputValidationErrorForegroundColor}; }`); - } -}); diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index 6020e1c282d..0f3f696791e 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -7,37 +7,23 @@ import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { DirtyDiffWorkbenchController } from './dirtydiffDecorator'; -import { ShowViewletAction } from 'vs/workbench/browser/viewlet'; -import { VIEWLET_ID, ISCMRepository, ISCMService } from 'vs/workbench/contrib/scm/common/scm'; -import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; +import { VIEWLET_ID, ISCMRepository, ISCMService, VIEW_PANE_ID } from 'vs/workbench/contrib/scm/common/scm'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; -import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { SCMStatusController } from './activity'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; -import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { SCMService } from 'vs/workbench/contrib/scm/common/scmService'; -import { IViewContainersRegistry, ViewContainerLocation, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views'; -import { SCMViewPaneContainer } from 'vs/workbench/contrib/scm/browser/scmViewlet'; +import { IViewContainersRegistry, ViewContainerLocation, Extensions as ViewContainerExtensions, IViewsRegistry, IViewsService } from 'vs/workbench/common/views'; +import { SCMViewPaneContainer } from 'vs/workbench/contrib/scm/browser/scmViewPaneContainer'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; import { Codicon } from 'vs/base/common/codicons'; - -class OpenSCMViewletAction extends ShowViewletAction { - - static readonly ID = VIEWLET_ID; - static readonly LABEL = localize('toggleSCMViewlet', "Show SCM"); - - constructor(id: string, label: string, @IViewletService viewletService: IViewletService, @IEditorGroupsService editorGroupService: IEditorGroupsService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService) { - super(id, label, VIEWLET_ID, viewletService, editorGroupService, layoutService); - } -} +import { SCMViewPane } from 'vs/workbench/contrib/scm/browser/scmViewPane'; ModesRegistry.registerLanguage({ id: 'scminput', @@ -48,30 +34,56 @@ ModesRegistry.registerLanguage({ Registry.as(WorkbenchExtensions.Workbench) .registerWorkbenchContribution(DirtyDiffWorkbenchController, LifecyclePhase.Restored); -Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ +const viewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: VIEWLET_ID, name: localize('source control', "Source Control"), ctorDescriptor: new SyncDescriptor(SCMViewPaneContainer), storageId: 'workbench.scm.views.state', icon: Codicon.sourceControl.classNames, alwaysUseContainerInfo: true, - order: 2 + order: 2, + hideIfEmpty: true }, ViewContainerLocation.Sidebar); +const viewsRegistry = Registry.as(ViewContainerExtensions.ViewsRegistry); + +viewsRegistry.registerViewWelcomeContent(VIEW_PANE_ID, { + content: localize('no open repo', "No source control providers registered."), + when: 'default' +}); + +viewsRegistry.registerViews([{ + id: VIEW_PANE_ID, + name: localize('source control', "Source Control"), + ctorDescriptor: new SyncDescriptor(SCMViewPane), + canToggleVisibility: true, + workspace: true, + canMoveView: true, + containerIcon: Codicon.sourceControl.classNames +}], viewContainer); + Registry.as(WorkbenchExtensions.Workbench) .registerWorkbenchContribution(SCMStatusController, LifecyclePhase.Restored); -// Register Action to Open Viewlet -Registry.as(WorkbenchActionExtensions.WorkbenchActions).registerWorkbenchAction( - SyncActionDescriptor.from(OpenSCMViewletAction, { - primary: 0, - win: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G }, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G }, - mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_G } - }), - 'View: Show SCM', - localize('view', "View") -); +// Register Action to Open View +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: VIEWLET_ID, + description: { description: localize('toggleSCMViewlet', "Show SCM"), args: [] }, + weight: KeybindingWeight.WorkbenchContrib, + primary: 0, + win: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G }, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G }, + mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_G }, + handler: accessor => { + const viewsService = accessor.get(IViewsService); + + if (viewsService.isViewVisible(VIEW_PANE_ID)) { + viewsService.closeView(VIEW_PANE_ID); + } else { + viewsService.openView(VIEW_PANE_ID); + } + } +}); Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ id: 'scm', @@ -80,16 +92,6 @@ Registry.as(ConfigurationExtensions.Configuration).regis type: 'object', scope: ConfigurationScope.RESOURCE, properties: { - 'scm.alwaysShowProviders': { - type: 'boolean', - description: localize('alwaysShowProviders', "Controls whether to show the Source Control Provider section even when there's only one Provider registered."), - default: false - }, - 'scm.providers.visible': { - type: 'number', - description: localize('providersVisible', "Controls how many providers are visible in the Source Control Provider section. Set to `0` to be able to manually resize the view."), - default: 10 - }, 'scm.diffDecorations': { type: 'string', enum: ['all', 'gutter', 'overview', 'minimap', 'none'], @@ -128,13 +130,24 @@ Registry.as(ConfigurationExtensions.Configuration).regis type: 'string', enum: ['all', 'focused', 'off'], enumDescriptions: [ - localize('scm.countBadge.all', "Show the sum of all Source Control Providers count badges."), + localize('scm.countBadge.all', "Show the sum of all Source Control Provider count badges."), localize('scm.countBadge.focused', "Show the count badge of the focused Source Control Provider."), localize('scm.countBadge.off', "Disable the Source Control count badge.") ], - description: localize('scm.countBadge', "Controls the Source Control count badge."), + description: localize('scm.countBadge', "Controls the count badge on the Source Control icon on the Activity Bar."), default: 'all' }, + 'scm.providerCountBadge': { + type: 'string', + enum: ['hidden', 'auto', 'visible'], + enumDescriptions: [ + localize('scm.providerCountBadge.hidden', "Hide Source Control Provider count badges."), + localize('scm.providerCountBadge.auto', "Only show count badge for Source Control Provider when non-zero."), + localize('scm.providerCountBadge.visible', "Show Source Control Provider count badges.") + ], + description: localize('scm.providerCountBadge', "Controls the count badges on Source Control Provider headers. These headers only appear when there is more than one provider."), + default: 'hidden' + }, 'scm.defaultViewMode': { type: 'string', enum: ['tree', 'list'], @@ -154,6 +167,11 @@ Registry.as(ConfigurationExtensions.Configuration).regis type: 'string', markdownDescription: localize('inputFontFamily', "Controls the font for the input message. Use `default` for the workbench user interface font family, `editor` for the `#editor.fontFamily#`'s value, or a custom font family."), default: 'default' + }, + 'scm.alwaysShowRepositories': { + type: 'boolean', + markdownDescription: localize('alwaysShowRepository', "Controls whether repositories should always be visible in the SCM view."), + default: false } } }); diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts new file mode 100644 index 00000000000..7583dc8f168 --- /dev/null +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -0,0 +1,2013 @@ +/*--------------------------------------------------------------------------------------------- + * 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/scm'; +import { Event, Emitter } from 'vs/base/common/event'; +import { basename, dirname, isEqual } from 'vs/base/common/resources'; +import { IDisposable, Disposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle'; +import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { append, $, addClass, toggleClass, removeClass, Dimension } from 'vs/base/browser/dom'; +import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; +import { ISCMResourceGroup, ISCMResource, InputValidationType, ISCMService, ISCMRepository, ISCMInput, IInputValidation } from 'vs/workbench/contrib/scm/common/scm'; +import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; +import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { MenuItemAction, IMenuService } from 'vs/platform/actions/common/actions'; +import { IAction, IActionViewItem, ActionRunner, Action, RadioGroup, Separator, SubmenuAction, IActionViewItemProvider } from 'vs/base/common/actions'; +import { SCMMenus } from './menus'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IThemeService, LIGHT, registerThemingParticipant, IFileIconTheme } from 'vs/platform/theme/common/themeService'; +import { isSCMResource, isSCMResourceGroup, connectPrimaryMenuToInlineActionBar, isSCMRepository, isSCMInput, connectPrimaryMenu } from './util'; +import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; +import { WorkbenchCompressibleObjectTree, IOpenEvent } from 'vs/platform/list/browser/listService'; +import { IConfigurationService, ConfigurationTarget, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; +import { disposableTimeout, ThrottledDelayer } from 'vs/base/common/async'; +import { ITreeNode, ITreeFilter, ITreeSorter, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; +import { ResourceTree, IResourceNode } from 'vs/base/common/resourceTree'; +import { ISequence, ISplice, SimpleSequence } from 'vs/base/common/sequence'; +import { ICompressibleTreeRenderer, ICompressibleKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/tree/objectTree'; +import { Iterable } from 'vs/base/common/iterator'; +import { ICompressedTreeNode, ICompressedTreeElement } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; +import { URI } from 'vs/base/common/uri'; +import { FileKind } from 'vs/platform/files/common/files'; +import { compareFileNames, comparePaths } from 'vs/base/common/comparers'; +import { FuzzyScore, createMatches, IMatch } from 'vs/base/common/filters'; +import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; +import { localize } from 'vs/nls'; +import { flatten } from 'vs/base/common/arrays'; +import { memoize } from 'vs/base/common/decorators'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { toResource, SideBySideEditor } from 'vs/workbench/common/editor'; +import { SIDE_BAR_BACKGROUND, SIDE_BAR_BORDER, PANEL_BACKGROUND, PANEL_INPUT_BORDER } from 'vs/workbench/common/theme'; +import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; +import { ITextModel } from 'vs/editor/common/model'; +import { IEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; +import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; +import { MenuPreventer } from 'vs/workbench/contrib/codeEditor/browser/menuPreventer'; +import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard'; +import { ContextMenuController } from 'vs/editor/contrib/contextmenu/contextmenu'; +import * as platform from 'vs/base/common/platform'; +import { escape, compare, format } from 'vs/base/common/strings'; +import { inputPlaceholderForeground, inputValidationInfoBorder, inputValidationWarningBorder, inputValidationErrorBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationErrorBackground, inputValidationErrorForeground, inputBackground, inputForeground, inputBorder, focusBorder, registerColor, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; +import { SuggestController } from 'vs/editor/contrib/suggest/suggestController'; +import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; +import { Schemas } from 'vs/base/common/network'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { ModesHoverController } from 'vs/editor/contrib/hover/hover'; +import { ColorDetector } from 'vs/editor/contrib/colorPicker/colorDetector'; +import { LinkDetector } from 'vs/editor/contrib/links/links'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { DEFAULT_FONT_FAMILY } from 'vs/workbench/browser/style'; +import { Command } from 'vs/editor/common/modes'; +import { renderCodicons, Codicon } from 'vs/base/common/codicons'; +import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; +import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; + +type TreeElement = ISCMRepository | ISCMInput | ISCMResourceGroup | IResourceNode | ISCMResource; + +function splitMatches(uri: URI, filterData: FuzzyScore | undefined): [IMatch[] | undefined, IMatch[] | undefined] { + let matches: IMatch[] | undefined; + let descriptionMatches: IMatch[] | undefined; + + if (filterData) { + matches = []; + descriptionMatches = []; + + const fileName = basename(uri); + const allMatches = createMatches(filterData); + + for (const match of allMatches) { + if (match.start < fileName.length) { + matches!.push( + { + start: match.start, + end: Math.min(match.end, fileName.length) + } + ); + } else { + descriptionMatches!.push( + { + start: match.start - (fileName.length + 1), + end: match.end - (fileName.length + 1) + } + ); + } + } + } + + return [matches, descriptionMatches]; +} + +class StatusBarAction extends Action { + + constructor( + private command: Command, + private commandService: ICommandService + ) { + super(`statusbaraction{${command.id}}`, command.title, '', true); + this.tooltip = command.tooltip || ''; + } + + run(): Promise { + return this.commandService.executeCommand(this.command.id, ...(this.command.arguments || [])); + } +} + +class StatusBarActionViewItem extends ActionViewItem { + + constructor(action: StatusBarAction) { + super(null, action, {}); + } + + updateLabel(): void { + if (this.options.label && this.label) { + this.label.innerHTML = renderCodicons(escape(this.getAction().label)); + } + } +} + +interface ISCMLayout { + height: number | undefined; + width: number | undefined; + readonly onDidChange: Event; +} + +interface RepositoryTemplate { + readonly label: HTMLElement; + readonly name: HTMLElement; + readonly description: HTMLElement; + readonly countContainer: HTMLElement; + readonly count: CountBadge; + readonly toolBar: ToolBar; + disposable: IDisposable; + readonly templateDisposable: IDisposable; +} + +class RepositoryRenderer implements ICompressibleTreeRenderer { + + static readonly TEMPLATE_ID = 'repository'; + get templateId(): string { return RepositoryRenderer.TEMPLATE_ID; } + + constructor( + private actionViewItemProvider: IActionViewItemProvider, + private menus: SCMMenus, + @ICommandService private commandService: ICommandService, + @IContextMenuService private contextMenuService: IContextMenuService, + @IThemeService private themeService: IThemeService + ) { } + + renderTemplate(container: HTMLElement): RepositoryTemplate { + // hack + addClass(container.parentElement!.parentElement!.querySelector('.monaco-tl-twistie')! as HTMLElement, 'force-twistie'); + + const provider = append(container, $('.scm-provider')); + const label = append(provider, $('.label')); + const name = append(label, $('span.name')); + const description = append(label, $('span.description')); + const actions = append(provider, $('.actions')); + const toolBar = new ToolBar(actions, this.contextMenuService, { actionViewItemProvider: this.actionViewItemProvider }); + const countContainer = append(provider, $('.count')); + const count = new CountBadge(countContainer); + const badgeStyler = attachBadgeStyler(count, this.themeService); + const visibilityDisposable = toolBar.onDidChangeDropdownVisibility(e => toggleClass(provider, 'active', e)); + + const disposable = Disposable.None; + const templateDisposable = combinedDisposable(visibilityDisposable, toolBar, badgeStyler); + + return { label, name, description, countContainer, count, toolBar, disposable, templateDisposable }; + } + + renderElement(node: ITreeNode, index: number, templateData: RepositoryTemplate, height: number | undefined): void { + templateData.disposable.dispose(); + + const disposables = new DisposableStore(); + const repository = node.element; + + if (repository.provider.rootUri) { + templateData.label.title = `${repository.provider.label}: ${repository.provider.rootUri.fsPath}`; + templateData.name.textContent = basename(repository.provider.rootUri); + templateData.description.textContent = repository.provider.label; + } else { + templateData.label.title = repository.provider.label; + templateData.name.textContent = repository.provider.label; + templateData.description.textContent = ''; + } + + let statusPrimaryActions: IAction[] = []; + let menuPrimaryActions: IAction[] = []; + let menuSecondaryActions: IAction[] = []; + const updateToolbar = () => { + templateData.toolBar.setActions([...statusPrimaryActions, ...menuPrimaryActions], menuSecondaryActions); + }; + + const onDidChangeProvider = () => { + const commands = repository.provider.statusBarCommands || []; + statusPrimaryActions = commands.map(c => new StatusBarAction(c, this.commandService)); + updateToolbar(); + + const count = repository.provider.count || 0; + templateData.countContainer.setAttribute('data-count', String(count)); + templateData.count.setCount(count); + }; + disposables.add(repository.provider.onDidChange(onDidChangeProvider, null)); + onDidChangeProvider(); + + const menus = this.menus.getRepositoryMenus(repository.provider); + disposables.add(connectPrimaryMenu(menus.titleMenu, (primary, secondary) => { + menuPrimaryActions = primary; + menuSecondaryActions = secondary; + updateToolbar(); + })); + templateData.toolBar.context = repository.provider; + + templateData.disposable = disposables; + } + + renderCompressedElements(): void { + throw new Error('Should never happen since node is incompressible'); + } + + disposeElement(group: ITreeNode, index: number, template: RepositoryTemplate): void { + template.disposable.dispose(); + } + + disposeTemplate(templateData: RepositoryTemplate): void { + templateData.disposable.dispose(); + templateData.templateDisposable.dispose(); + } +} + +interface InputTemplate { + readonly inputWidget: SCMInputWidget; + disposable: IDisposable; + readonly templateDisposable: IDisposable; +} + +class InputRenderer implements ICompressibleTreeRenderer { + + static readonly DEFAULT_HEIGHT = 26; + + static readonly TEMPLATE_ID = 'input'; + get templateId(): string { return InputRenderer.TEMPLATE_ID; } + + private inputWidgets = new Map(); + private contentHeights = new WeakMap(); + + constructor( + private outerLayout: ISCMLayout, + private updateHeight: (input: ISCMInput, height: number) => void, + @IInstantiationService private instantiationService: IInstantiationService, + ) { } + + renderTemplate(container: HTMLElement): InputTemplate { + // hack + addClass(container.parentElement!.parentElement!.querySelector('.monaco-tl-twistie')! as HTMLElement, 'force-no-twistie'); + + const disposables = new DisposableStore(); + const inputElement = append(container, $('.scm-input')); + const inputWidget = this.instantiationService.createInstance(SCMInputWidget, inputElement); + disposables.add(inputWidget); + + return { inputWidget, disposable: Disposable.None, templateDisposable: disposables }; + } + + renderElement(node: ITreeNode, index: number, templateData: InputTemplate): void { + templateData.disposable.dispose(); + + const disposables = new DisposableStore(); + const input = node.element; + templateData.inputWidget.input = input; + + // Remember widget + this.inputWidgets.set(input, templateData.inputWidget); + disposables.add({ dispose: () => this.inputWidgets.delete(input) }); + + // Rerender the element whenever the editor content height changes + const onDidChangeContentHeight = () => { + const contentHeight = templateData.inputWidget.getContentHeight(); + const lastContentHeight = this.contentHeights.get(input)!; + this.contentHeights.set(input, contentHeight); + + if (lastContentHeight !== contentHeight) { + this.updateHeight(input, contentHeight + 10); + templateData.inputWidget.layout(); + } + }; + + const startListeningContentHeightChange = () => { + disposables.add(templateData.inputWidget.onDidChangeContentHeight(onDidChangeContentHeight)); + onDidChangeContentHeight(); + }; + + // Setup height change listener on next tick + const timeout = disposableTimeout(startListeningContentHeightChange, 0); + disposables.add(timeout); + + // Layout the editor whenever the outer layout happens + const layoutEditor = () => templateData.inputWidget.layout(); + disposables.add(this.outerLayout.onDidChange(layoutEditor)); + layoutEditor(); + + templateData.disposable = disposables; + } + + renderCompressedElements(): void { + throw new Error('Should never happen since node is incompressible'); + } + + disposeElement(group: ITreeNode, index: number, template: InputTemplate): void { + template.disposable.dispose(); + } + + disposeTemplate(templateData: InputTemplate): void { + templateData.disposable.dispose(); + templateData.templateDisposable.dispose(); + } + + getHeight(input: ISCMInput): number { + return (this.contentHeights.get(input) ?? InputRenderer.DEFAULT_HEIGHT) + 10; + } + + getRenderedInputWidget(input: ISCMInput): SCMInputWidget | undefined { + return this.inputWidgets.get(input); + } + + getFocusedInput(): ISCMInput | undefined { + for (const [input, inputWidget] of this.inputWidgets) { + if (inputWidget.hasFocus()) { + return input; + } + } + + return undefined; + } +} + +interface ResourceGroupTemplate { + readonly name: HTMLElement; + readonly count: CountBadge; + readonly actionBar: ActionBar; + elementDisposables: IDisposable; + readonly disposables: IDisposable; +} + +class ResourceGroupRenderer implements ICompressibleTreeRenderer { + + static readonly TEMPLATE_ID = 'resource group'; + get templateId(): string { return ResourceGroupRenderer.TEMPLATE_ID; } + + constructor( + private actionViewItemProvider: IActionViewItemProvider, + private menus: SCMMenus, + @IThemeService private themeService: IThemeService, + ) { } + + renderTemplate(container: HTMLElement): ResourceGroupTemplate { + // hack + addClass(container.parentElement!.parentElement!.querySelector('.monaco-tl-twistie')! as HTMLElement, 'force-twistie'); + + const element = append(container, $('.resource-group')); + const name = append(element, $('.name')); + const actionsContainer = append(element, $('.actions')); + const actionBar = new ActionBar(actionsContainer, { actionViewItemProvider: this.actionViewItemProvider }); + const countContainer = append(element, $('.count')); + const count = new CountBadge(countContainer); + const styler = attachBadgeStyler(count, this.themeService); + const elementDisposables = Disposable.None; + const disposables = combinedDisposable(actionBar, styler); + + return { name, count, actionBar, elementDisposables, disposables }; + } + + renderElement(node: ITreeNode, index: number, template: ResourceGroupTemplate): void { + template.elementDisposables.dispose(); + + const group = node.element; + template.name.textContent = group.label; + template.actionBar.clear(); + template.actionBar.context = group; + template.count.setCount(group.elements.length); + + const disposables = new DisposableStore(); + const menus = this.menus.getRepositoryMenus(group.provider); + disposables.add(connectPrimaryMenuToInlineActionBar(menus.getResourceGroupMenu(group), template.actionBar)); + + template.elementDisposables = disposables; + } + + renderCompressedElements(node: ITreeNode, FuzzyScore>, index: number, templateData: ResourceGroupTemplate, height: number | undefined): void { + throw new Error('Should never happen since node is incompressible'); + } + + disposeElement(group: ITreeNode, index: number, template: ResourceGroupTemplate): void { + template.elementDisposables.dispose(); + } + + disposeTemplate(template: ResourceGroupTemplate): void { + template.elementDisposables.dispose(); + template.disposables.dispose(); + } +} + +interface ResourceTemplate { + element: HTMLElement; + name: HTMLElement; + fileLabel: IResourceLabel; + decorationIcon: HTMLElement; + actionBar: ActionBar; + elementDisposables: IDisposable; + disposables: IDisposable; +} + +class RepositoryPaneActionRunner extends ActionRunner { + + constructor(private getSelectedResources: () => (ISCMResource | IResourceNode)[]) { + super(); + } + + async runAction(action: IAction, context: ISCMResource | IResourceNode): Promise { + if (!(action instanceof MenuItemAction)) { + return super.runAction(action, context); + } + + const selection = this.getSelectedResources(); + const contextIsSelected = selection.some(s => s === context); + const actualContext = contextIsSelected ? selection : [context]; + const args = flatten(actualContext.map(e => ResourceTree.isResourceNode(e) ? ResourceTree.collect(e) : [e])); + await action.run(...args); + } +} + +class ResourceRenderer implements ICompressibleTreeRenderer, FuzzyScore, ResourceTemplate> { + + static readonly TEMPLATE_ID = 'resource'; + get templateId(): string { return ResourceRenderer.TEMPLATE_ID; } + + constructor( + private viewModelProvider: () => ViewModel, + private labels: ResourceLabels, + private actionViewItemProvider: IActionViewItemProvider, + private actionRunner: ActionRunner, + private menus: SCMMenus, + @IThemeService private themeService: IThemeService + ) { } + + renderTemplate(container: HTMLElement): ResourceTemplate { + const element = append(container, $('.resource')); + const name = append(element, $('.name')); + const fileLabel = this.labels.create(name, { supportDescriptionHighlights: true, supportHighlights: true }); + const actionsContainer = append(fileLabel.element, $('.actions')); + const actionBar = new ActionBar(actionsContainer, { + actionViewItemProvider: this.actionViewItemProvider, + actionRunner: this.actionRunner + }); + + const decorationIcon = append(element, $('.decoration-icon')); + const disposables = combinedDisposable(actionBar, fileLabel); + + return { element, name, fileLabel, decorationIcon, actionBar, elementDisposables: Disposable.None, disposables }; + } + + renderElement(node: ITreeNode | ITreeNode, FuzzyScore>, index: number, template: ResourceTemplate): void { + template.elementDisposables.dispose(); + + const elementDisposables = new DisposableStore(); + const resourceOrFolder = node.element; + const theme = this.themeService.getColorTheme(); + const iconResource = ResourceTree.isResourceNode(resourceOrFolder) ? resourceOrFolder.element : resourceOrFolder; + const icon = iconResource && (theme.type === LIGHT ? iconResource.decorations.icon : iconResource.decorations.iconDark); + + const uri = ResourceTree.isResourceNode(resourceOrFolder) ? resourceOrFolder.uri : resourceOrFolder.sourceUri; + const fileKind = ResourceTree.isResourceNode(resourceOrFolder) ? FileKind.FOLDER : FileKind.FILE; + const viewModel = this.viewModelProvider(); + + const [matches, descriptionMatches] = splitMatches(uri, node.filterData); + template.fileLabel.setFile(uri, { + fileDecorations: { colors: false, badges: !icon }, + hidePath: viewModel.mode === ViewModelMode.Tree, + fileKind, + matches, + descriptionMatches + }); + + template.actionBar.clear(); + template.actionBar.context = resourceOrFolder; + + if (ResourceTree.isResourceNode(resourceOrFolder)) { + if (resourceOrFolder.element) { + const menus = this.menus.getRepositoryMenus(resourceOrFolder.element.resourceGroup.provider); + elementDisposables.add(connectPrimaryMenuToInlineActionBar(menus.getResourceMenu(resourceOrFolder.element.resourceGroup, resourceOrFolder.element), template.actionBar)); + toggleClass(template.name, 'strike-through', resourceOrFolder.element.decorations.strikeThrough); + toggleClass(template.element, 'faded', resourceOrFolder.element.decorations.faded); + } else { + const menus = this.menus.getRepositoryMenus(resourceOrFolder.context.provider); + elementDisposables.add(connectPrimaryMenuToInlineActionBar(menus.getResourceFolderMenu(resourceOrFolder.context), template.actionBar)); + removeClass(template.name, 'strike-through'); + removeClass(template.element, 'faded'); + } + } else { + const menus = this.menus.getRepositoryMenus(resourceOrFolder.resourceGroup.provider); + elementDisposables.add(connectPrimaryMenuToInlineActionBar(menus.getResourceMenu(resourceOrFolder.resourceGroup, resourceOrFolder), template.actionBar)); + toggleClass(template.name, 'strike-through', resourceOrFolder.decorations.strikeThrough); + toggleClass(template.element, 'faded', resourceOrFolder.decorations.faded); + } + + const tooltip = !ResourceTree.isResourceNode(resourceOrFolder) && resourceOrFolder.decorations.tooltip || ''; + + if (icon) { + template.decorationIcon.style.display = ''; + template.decorationIcon.style.backgroundImage = `url('${icon}')`; + template.decorationIcon.title = tooltip; + } else { + template.decorationIcon.style.display = 'none'; + template.decorationIcon.style.backgroundImage = ''; + template.decorationIcon.title = ''; + } + + template.element.setAttribute('data-tooltip', tooltip); + template.elementDisposables = elementDisposables; + } + + disposeElement(resource: ITreeNode | ITreeNode, FuzzyScore>, index: number, template: ResourceTemplate): void { + template.elementDisposables.dispose(); + } + + renderCompressedElements(node: ITreeNode | ICompressedTreeNode>, FuzzyScore>, index: number, template: ResourceTemplate, height: number | undefined): void { + template.elementDisposables.dispose(); + + const elementDisposables = new DisposableStore(); + const compressed = node.element as ICompressedTreeNode>; + const folder = compressed.elements[compressed.elements.length - 1]; + + const label = compressed.elements.map(e => e.name).join('/'); + const fileKind = FileKind.FOLDER; + + const [matches, descriptionMatches] = splitMatches(folder.uri, node.filterData); + template.fileLabel.setResource({ resource: folder.uri, name: label }, { + fileDecorations: { colors: false, badges: true }, + fileKind, + matches, + descriptionMatches + }); + + template.actionBar.clear(); + template.actionBar.context = folder; + + const menus = this.menus.getRepositoryMenus(folder.context.provider); + elementDisposables.add(connectPrimaryMenuToInlineActionBar(menus.getResourceFolderMenu(folder.context), template.actionBar)); + + removeClass(template.name, 'strike-through'); + removeClass(template.element, 'faded'); + template.decorationIcon.style.display = 'none'; + template.decorationIcon.style.backgroundImage = ''; + + template.element.setAttribute('data-tooltip', ''); + template.elementDisposables = elementDisposables; + } + + disposeCompressedElements(node: ITreeNode | ICompressedTreeNode>, FuzzyScore>, index: number, template: ResourceTemplate, height: number | undefined): void { + template.elementDisposables.dispose(); + } + + disposeTemplate(template: ResourceTemplate): void { + template.elementDisposables.dispose(); + template.disposables.dispose(); + } +} + +class ProviderListDelegate implements IListVirtualDelegate { + + constructor(private readonly inputRenderer: InputRenderer) { } + + getHeight(element: TreeElement) { + if (isSCMInput(element)) { + return this.inputRenderer.getHeight(element); + } else { + return 22; + } + } + + getTemplateId(element: TreeElement) { + if (isSCMRepository(element)) { + return RepositoryRenderer.TEMPLATE_ID; + } else if (isSCMInput(element)) { + return InputRenderer.TEMPLATE_ID; + } else if (ResourceTree.isResourceNode(element) || isSCMResource(element)) { + return ResourceRenderer.TEMPLATE_ID; + } else { + return ResourceGroupRenderer.TEMPLATE_ID; + } + } +} + +class SCMTreeFilter implements ITreeFilter { + + filter(element: TreeElement): boolean { + if (ResourceTree.isResourceNode(element)) { + return true; + } else if (isSCMResourceGroup(element)) { + return element.elements.length > 0 || !element.hideWhenEmpty; + } else { + return true; + } + } +} + +export class SCMTreeSorter implements ITreeSorter { + + @memoize + private get viewModel(): ViewModel { return this.viewModelProvider(); } + + constructor(private viewModelProvider: () => ViewModel) { } + + compare(one: TreeElement, other: TreeElement): number { + if (isSCMRepository(one)) { + if (!isSCMRepository(other)) { + throw new Error('Invalid comparison'); + } + + return 0; + } + + if (isSCMInput(one)) { + return -1; + } else if (isSCMInput(other)) { + return 1; + } + + if (isSCMResourceGroup(one)) { + if (!isSCMResourceGroup(other)) { + throw new Error('Invalid comparison'); + } + + return 0; + } + + // List + if (this.viewModel.mode === ViewModelMode.List) { + // FileName + if (this.viewModel.sortKey === ViewModelSortKey.Name) { + const oneName = basename((one as ISCMResource).sourceUri); + const otherName = basename((other as ISCMResource).sourceUri); + + return compareFileNames(oneName, otherName); + } + + // Status + if (this.viewModel.sortKey === ViewModelSortKey.Status) { + const oneTooltip = (one as ISCMResource).decorations.tooltip ?? ''; + const otherTooltip = (other as ISCMResource).decorations.tooltip ?? ''; + + if (oneTooltip !== otherTooltip) { + return compare(oneTooltip, otherTooltip); + } + } + + // Path (default) + const onePath = (one as ISCMResource).sourceUri.fsPath; + const otherPath = (other as ISCMResource).sourceUri.fsPath; + + return comparePaths(onePath, otherPath); + } + + // Tree + const oneIsDirectory = ResourceTree.isResourceNode(one); + const otherIsDirectory = ResourceTree.isResourceNode(other); + + if (oneIsDirectory !== otherIsDirectory) { + return oneIsDirectory ? -1 : 1; + } + + const oneName = ResourceTree.isResourceNode(one) ? one.name : basename((one as ISCMResource).sourceUri); + const otherName = ResourceTree.isResourceNode(other) ? other.name : basename((other as ISCMResource).sourceUri); + + return compareFileNames(oneName, otherName); + } +} + +export class SCMTreeKeyboardNavigationLabelProvider implements ICompressibleKeyboardNavigationLabelProvider { + + constructor(@ILabelService private readonly labelService: ILabelService) { } + + getKeyboardNavigationLabel(element: TreeElement): { toString(): string; } | undefined { + if (ResourceTree.isResourceNode(element)) { + return element.name; + } else if (isSCMRepository(element)) { + return undefined; + } else if (isSCMInput(element)) { + return undefined; + } else if (isSCMResourceGroup(element)) { + return element.label; + } else { + // Since a match in the file name takes precedence over a match + // in the folder name we are returning the label as file/folder. + const fileName = basename(element.sourceUri); + const filePath = this.labelService.getUriLabel(dirname(element.sourceUri), { relative: true }); + + return filePath.length !== 0 ? `${fileName} ${filePath}` : fileName; + } + } + + getCompressedNodeKeyboardNavigationLabel(elements: TreeElement[]): { toString(): string | undefined; } | undefined { + const folders = elements as IResourceNode[]; + return folders.map(e => e.name).join('/'); + } +} + +class SCMResourceIdentityProvider implements IIdentityProvider { + + getId(element: TreeElement): string { + if (ResourceTree.isResourceNode(element)) { + const group = element.context; + return `folder:${group.provider.id}/${group.id}/$FOLDER/${element.uri.toString()}`; + } else if (isSCMRepository(element)) { + const provider = element.provider; + return `repo:${provider.id}`; + } else if (isSCMInput(element)) { + const provider = element.repository.provider; + return `input:${provider.id}`; + } else if (isSCMResource(element)) { + const group = element.resourceGroup; + const provider = group.provider; + return `resource:${provider.id}/${group.id}/${element.sourceUri.toString()}`; + } else { + const provider = element.provider; + return `group:${provider.id}/${element.id}`; + } + } +} + +export class SCMAccessibilityProvider implements IListAccessibilityProvider { + + constructor(@ILabelService private readonly labelService: ILabelService) { } + + getWidgetAriaLabel(): string { + return localize('scm', "Source Control Management"); + } + + getAriaLabel(element: TreeElement): string { + if (ResourceTree.isResourceNode(element)) { + return this.labelService.getUriLabel(element.uri, { relative: true, noPrefix: true }) || element.name; + } else if (isSCMRepository(element)) { + return element.provider.label; + } else if (isSCMInput(element)) { + return localize('input', "Source Control Input"); + } else if (isSCMResourceGroup(element)) { + return element.label; + } else { + const result: string[] = []; + + result.push(basename(element.sourceUri)); + + if (element.decorations.tooltip) { + result.push(element.decorations.tooltip); + } + + const path = this.labelService.getUriLabel(dirname(element.sourceUri), { relative: true, noPrefix: true }); + + if (path) { + result.push(path); + } + + return result.join(', '); + } + } +} + +interface IGroupItem { + readonly element: ISCMResourceGroup; + readonly resources: ISCMResource[]; + readonly tree: ResourceTree; + readonly disposable: IDisposable; +} + +interface IRepositoryItem { + readonly element: ISCMRepository; + readonly groupItems: IGroupItem[]; + readonly disposable: IDisposable; +} + +function isRepositoryItem(item: IRepositoryItem | IGroupItem): item is IRepositoryItem { + return Array.isArray((item as IRepositoryItem).groupItems); +} + +function asTreeElement(node: IResourceNode, forceIncompressible: boolean): ICompressedTreeElement { + return { + element: (node.childrenCount === 0 && node.element) ? node.element : node, + children: Iterable.map(node.children, node => asTreeElement(node, false)), + incompressible: !!node.element || forceIncompressible + }; +} + +const enum ViewModelMode { + List = 'list', + Tree = 'tree' +} + +const enum ViewModelSortKey { + Path, + Name, + Status +} + +class ViewModel { + + private readonly _onDidChangeMode = new Emitter(); + readonly onDidChangeMode = this._onDidChangeMode.event; + + private _onDidChangeRepositoryCollapseState = new Emitter(); + readonly onDidChangeRepositoryCollapseState: Event; + private visible: boolean = false; + + get mode(): ViewModelMode { return this._mode; } + set mode(mode: ViewModelMode) { + this._mode = mode; + + for (const item of this.items) { + for (const groupItem of item.groupItems) { + groupItem.tree.clear(); + + if (mode === ViewModelMode.Tree) { + for (const resource of groupItem.resources) { + groupItem.tree.add(resource.sourceUri, resource); + } + } + } + } + + this.refresh(); + this._onDidChangeMode.fire(mode); + } + + get sortKey(): ViewModelSortKey { return this._sortKey; } + set sortKey(sortKey: ViewModelSortKey) { + if (sortKey !== this._sortKey) { + this._sortKey = sortKey; + this.refresh(); + } + } + + private items: IRepositoryItem[] = []; + private visibilityDisposables = new DisposableStore(); + private scrollTop: number | undefined; + private alwaysShowRepositories = false; + private firstVisible = true; + private repositoryCollapseStates: Map | undefined; + private disposables = new DisposableStore(); + + constructor( + readonly repositories: ISequence, + private tree: WorkbenchCompressibleObjectTree, + private menus: SCMMenus, + private inputRenderer: InputRenderer, + private _mode: ViewModelMode, + private _sortKey: ViewModelSortKey, + @IEditorService protected editorService: IEditorService, + @IConfigurationService protected configurationService: IConfigurationService, + ) { + this.onDidChangeRepositoryCollapseState = Event.any( + this._onDidChangeRepositoryCollapseState.event, + Event.signal(Event.filter(this.tree.onDidChangeCollapseState, e => isSCMRepository(e.node.element))) + ); + + configurationService.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables); + this.onDidChangeConfiguration(); + } + + private onDidChangeConfiguration(e?: IConfigurationChangeEvent): void { + if (!e || e.affectsConfiguration('scm.alwaysShowRepositories')) { + this.alwaysShowRepositories = this.configurationService.getValue('scm.alwaysShowRepositories'); + this.refresh(); + } + } + + private _onDidSpliceRepositories({ start, deleteCount, toInsert }: ISplice): void { + const itemsToInsert = toInsert.map(repository => { + const disposable = combinedDisposable( + repository.provider.groups.onDidSplice(splice => this._onDidSpliceGroups(item, splice)), + repository.input.onDidChangeVisibility(() => this.refresh(item)) + ); + const groupItems = repository.provider.groups.elements.map(group => this.createGroupItem(group)); + const item: IRepositoryItem = { element: repository, groupItems, disposable }; + + return item; + }); + + const itemsToDispose = this.items.splice(start, deleteCount, ...itemsToInsert); + + for (const item of itemsToDispose) { + for (const groupItem of item.groupItems) { + groupItem.disposable.dispose(); + } + + item.disposable.dispose(); + } + + this.refresh(); + } + + private _onDidSpliceGroups(item: IRepositoryItem, { start, deleteCount, toInsert }: ISplice): void { + const itemsToInsert: IGroupItem[] = toInsert.map(group => this.createGroupItem(group)); + const itemsToDispose = item.groupItems.splice(start, deleteCount, ...itemsToInsert); + + for (const item of itemsToDispose) { + item.disposable.dispose(); + } + + this.refresh(); + } + + private createGroupItem(group: ISCMResourceGroup): IGroupItem { + const tree = new ResourceTree(group, group.provider.rootUri || URI.file('/')); + const resources: ISCMResource[] = [...group.elements]; + const disposable = combinedDisposable( + group.onDidChange(() => this.tree.refilter()), + group.onDidSplice(splice => this._onDidSpliceGroup(item, splice)) + ); + + const item: IGroupItem = { element: group, resources, tree, disposable }; + + if (this._mode === ViewModelMode.Tree) { + for (const resource of resources) { + item.tree.add(resource.sourceUri, resource); + } + } + + return item; + } + + private _onDidSpliceGroup(item: IGroupItem, { start, deleteCount, toInsert }: ISplice): void { + const before = item.resources.length; + const deleted = item.resources.splice(start, deleteCount, ...toInsert); + const after = item.resources.length; + + if (this._mode === ViewModelMode.Tree) { + for (const resource of deleted) { + item.tree.delete(resource.sourceUri); + } + + for (const resource of toInsert) { + item.tree.add(resource.sourceUri, resource); + } + } + + if (before !== after && (before === 0 || after === 0)) { + this.refresh(); + } else { + this.refresh(item); + } + } + + setVisible(visible: boolean): void { + if (visible) { + this.visibilityDisposables = new DisposableStore(); + this.repositories.onDidSplice(this._onDidSpliceRepositories, this, this.visibilityDisposables); + this._onDidSpliceRepositories({ start: 0, deleteCount: 0, toInsert: this.repositories.elements }); + this.repositoryCollapseStates = undefined; + + if (typeof this.scrollTop === 'number') { + this.tree.scrollTop = this.scrollTop; + this.scrollTop = undefined; + } + + this.editorService.onDidActiveEditorChange(this.onDidActiveEditorChange, this, this.visibilityDisposables); + this.onDidActiveEditorChange(); + } else { + if (this.items.length > 1) { + this.repositoryCollapseStates = new Map(); + + for (const item of this.items) { + this.repositoryCollapseStates.set(item.element, this.tree.isCollapsed(item.element)); + } + } + + this.visibilityDisposables.dispose(); + this._onDidSpliceRepositories({ start: 0, deleteCount: this.items.length, toInsert: [] }); + this.scrollTop = this.tree.scrollTop; + } + + this.visible = visible; + this._onDidChangeRepositoryCollapseState.fire(); + } + + private refresh(item?: IRepositoryItem | IGroupItem): void { + const focusedInput = this.inputRenderer.getFocusedInput(); + + if (!this.alwaysShowRepositories && (this.items.length === 1 && (!item || isRepositoryItem(item)))) { + this.tree.setChildren(null, this.render(this.items[0]).children); + } else if (item) { + this.tree.setChildren(item.element, this.render(item).children); + } else { + this.tree.setChildren(null, this.items.map(item => this.render(item))); + } + + if (focusedInput) { + const inputWidget = this.inputRenderer.getRenderedInputWidget(focusedInput); + + if (inputWidget) { + inputWidget.focus(); + } + } + + this._onDidChangeRepositoryCollapseState.fire(); + } + + private render(item: IRepositoryItem | IGroupItem): ICompressedTreeElement { + if (isRepositoryItem(item)) { + const children: ICompressedTreeElement[] = []; + const hasSomeChanges = item.groupItems.some(item => item.element.elements.length > 0); + + if (this.items.length === 1 || hasSomeChanges) { + if (item.element.input.visible) { + children.push({ element: item.element.input, incompressible: true, collapsible: false }); + } + + children.push(...item.groupItems.map(i => this.render(i))); + } + + const collapsed = this.repositoryCollapseStates?.get(item.element); + return { element: item.element, children, incompressible: true, collapsed, collapsible: true }; + } else { + const children = this.mode === ViewModelMode.List + ? Iterable.map(item.resources, element => ({ element, incompressible: true })) + : Iterable.map(item.tree.root.children, node => asTreeElement(node, true)); + + return { element: item.element, children, incompressible: true, collapsible: true }; + } + } + + private onDidActiveEditorChange(): void { + if (!this.configurationService.getValue('scm.autoReveal')) { + return; + } + + if (this.firstVisible) { + this.firstVisible = false; + this.visibilityDisposables.add(disposableTimeout(() => this.onDidActiveEditorChange(), 250)); + return; + } + + const editor = this.editorService.activeEditor; + + if (!editor) { + return; + } + + const uri = toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY }); + + if (!uri) { + return; + } + + for (let i = 0; i < this.items.length; i++) { + const item = this.items[i]; + // go backwards from last group + for (let j = item.groupItems.length - 1; j >= 0; j--) { + const groupItem = item.groupItems[j]; + const resource = this.mode === ViewModelMode.Tree + ? groupItem.tree.getNode(uri)?.element + : groupItem.resources.find(r => isEqual(r.sourceUri, uri)); + + if (resource) { + this.tree.reveal(resource); + this.tree.setSelection([resource]); + this.tree.setFocus([resource]); + return; + } + } + } + } + + focus() { + for (const repository of this.repositories.elements) { + const widget = this.inputRenderer.getRenderedInputWidget(repository.input); + + if (widget) { + widget.focus(); + return; + } + } + + this.tree.domFocus(); + } + + getViewActions(): IAction[] { + if (this.repositories.elements.length === 0) { + return []; + } + + if (this.alwaysShowRepositories || this.repositories.elements.length !== 1) { + return []; + } + + const menus = this.menus.getRepositoryMenus(this.repositories.elements[0].provider); + return menus.getTitleActions(); + } + + getViewSecondaryActions(): IAction[] { + if (this.repositories.elements.length === 0) { + return []; + } + + const viewAction = new SCMViewSubMenuAction(this); + + if (this.alwaysShowRepositories || this.repositories.elements.length !== 1) { + return Array.isArray(viewAction.actions) ? viewAction.actions : viewAction.actions(); + } + + const menus = this.menus.getRepositoryMenus(this.repositories.elements[0].provider); + const secondaryActions = menus.getTitleSecondaryActions(); + + if (secondaryActions.length === 0) { + return [viewAction]; + } + + return [viewAction, new Separator(), ...secondaryActions]; + } + + getViewActionsContext(): any { + if (this.repositories.elements.length === 0) { + return []; + } + + if (this.alwaysShowRepositories || this.repositories.elements.length !== 1) { + return undefined; + } + + return this.repositories.elements[0].provider; + } + + collapseAllProviders(): void { + for (const repository of this.repositories.elements) { + if (this.tree.isCollapsible(repository)) { + this.tree.collapse(repository); + } + } + } + + expandAllProviders(): void { + for (const repository of this.repositories.elements) { + if (this.tree.isCollapsible(repository)) { + this.tree.expand(repository); + } + } + } + + isAnyProviderCollapsible(): boolean { + if (!this.visible || this.repositories.elements.length === 1) { + return false; + } + + return this.repositories.elements.some(r => this.tree.hasElement(r) && this.tree.isCollapsible(r)); + } + + areAllProvidersCollapsed(): boolean { + if (!this.visible || this.repositories.elements.length === 1) { + return false; + } + + return this.repositories.elements.every(r => this.tree.hasElement(r) && (!this.tree.isCollapsible(r) || this.tree.isCollapsed(r))); + } + + dispose(): void { + this.visibilityDisposables.dispose(); + this.disposables.dispose(); + + for (const item of this.items) { + item.disposable.dispose(); + } + + this.items = []; + } +} + +class SCMViewSubMenuAction extends SubmenuAction { + constructor(viewModel: ViewModel) { + super( + 'scm.viewsort', + localize('sortAction', "View & Sort"), + [ + ...new RadioGroup([ + new SCMViewModeListAction(viewModel), + new SCMViewModeTreeAction(viewModel) + ]).actions, + new Separator(), + ...new RadioGroup([ + new SCMSortByNameAction(viewModel), + new SCMSortByPathAction(viewModel), + new SCMSortByStatusAction(viewModel) + ]).actions + ] + ); + } +} + +abstract class SCMViewModeAction extends Action { + constructor(id: string, label: string, private viewModel: ViewModel, private viewMode: ViewModelMode) { + super(id, label); + + this.checked = this.viewModel.mode === this.viewMode; + } + + async run(): Promise { + if (this.viewMode !== this.viewModel.mode) { + this.checked = !this.checked; + this.viewModel.mode = this.viewMode; + } + } +} + +class SCMViewModeListAction extends SCMViewModeAction { + static readonly ID = 'workbench.scm.action.viewModeList'; + static readonly LABEL = localize('viewModeList', "View as List"); + + constructor(viewModel: ViewModel) { + super(SCMViewModeListAction.ID, SCMViewModeListAction.LABEL, viewModel, ViewModelMode.List); + } +} + +class SCMViewModeTreeAction extends SCMViewModeAction { + static readonly ID = 'workbench.scm.action.viewModeTree'; + static readonly LABEL = localize('viewModeTree', "View as Tree"); + + constructor(viewModel: ViewModel) { + super(SCMViewModeTreeAction.ID, SCMViewModeTreeAction.LABEL, viewModel, ViewModelMode.Tree); + } +} + +abstract class SCMSortAction extends Action { + + private readonly _listener: IDisposable; + + constructor(id: string, label: string, private viewModel: ViewModel, private sortKey: ViewModelSortKey) { + super(id, label); + + this.checked = this.sortKey === ViewModelSortKey.Path; + this.enabled = this.viewModel?.mode === ViewModelMode.List ?? false; + this._listener = viewModel?.onDidChangeMode(e => this.enabled = e === ViewModelMode.List); + } + + async run(): Promise { + if (this.sortKey !== this.viewModel.sortKey) { + this.checked = !this.checked; + this.viewModel.sortKey = this.sortKey; + } + } + + dispose(): void { + this._listener.dispose(); + super.dispose(); + } +} + +class SCMSortByNameAction extends SCMSortAction { + static readonly ID = 'workbench.scm.action.sortByName'; + static readonly LABEL = localize('sortByName', "Sort by Name"); + + constructor(viewModel: ViewModel) { + super(SCMSortByNameAction.ID, SCMSortByNameAction.LABEL, viewModel, ViewModelSortKey.Name); + } +} + +class SCMSortByPathAction extends SCMSortAction { + static readonly ID = 'workbench.scm.action.sortByPath'; + static readonly LABEL = localize('sortByPath', "Sort by Path"); + + constructor(viewModel: ViewModel) { + super(SCMSortByPathAction.ID, SCMSortByPathAction.LABEL, viewModel, ViewModelSortKey.Path); + } +} + +class SCMSortByStatusAction extends SCMSortAction { + static readonly ID = 'workbench.scm.action.sortByStatus'; + static readonly LABEL = localize('sortByStatus', "Sort by Status"); + + constructor(viewModel: ViewModel) { + super(SCMSortByStatusAction.ID, SCMSortByStatusAction.LABEL, viewModel, ViewModelSortKey.Status); + } +} + +class SCMInputWidget extends Disposable { + + private readonly defaultInputFontFamily = DEFAULT_FONT_FAMILY; + + private element: HTMLElement; + private editorContainer: HTMLElement; + private placeholderTextContainer: HTMLElement; + private inputEditor: CodeEditorWidget; + + private model: { readonly input: ISCMInput; readonly textModel: ITextModel; } | undefined; + private repositoryContextKey: IContextKey; + private repositoryDisposables = new DisposableStore(); + + private validation: IInputValidation | undefined; + private validationDisposable: IDisposable = Disposable.None; + + readonly onDidChangeContentHeight: Event; + + get input(): ISCMInput | undefined { + return this.model?.input; + } + + set input(input: ISCMInput | undefined) { + if (input === this.input) { + return; + } + + this.validationDisposable.dispose(); + removeClass(this.editorContainer, 'synthetic-focus'); + + this.repositoryDisposables.dispose(); + this.repositoryDisposables = new DisposableStore(); + this.repositoryContextKey.set(input?.repository); + + if (!input) { + this.model?.textModel.dispose(); + this.inputEditor.setModel(undefined); + this.model = undefined; + return; + } + + let query: string | undefined; + + if (input.repository.provider.rootUri) { + query = `rootUri=${encodeURIComponent(input.repository.provider.rootUri.toString())}`; + } + + const uri = URI.from({ + scheme: Schemas.vscode, + path: `scm/${input.repository.provider.contextValue}/${input.repository.provider.id}/input`, + query + }); + + this.configurationService.updateValue('editor.wordBasedSuggestions', false, { resource: uri }, ConfigurationTarget.MEMORY); + + const mode = this.modeService.create('scminput'); + const textModel = this.modelService.getModel(uri) || this.modelService.createModel('', mode, uri); + this.inputEditor.setModel(textModel); + + // Validation + const validationDelayer = new ThrottledDelayer(200); + const validate = async () => { + const position = this.inputEditor.getSelection()?.getStartPosition(); + const offset = position && textModel.getOffsetAt(position); + const value = textModel.getValue(); + + this.validation = await input.validateInput(value, offset || 0); + this.renderValidation(); + }; + + const triggerValidation = () => validationDelayer.trigger(validate); + this.repositoryDisposables.add(validationDelayer); + this.repositoryDisposables.add(this.inputEditor.onDidChangeCursorPosition(triggerValidation)); + + // Adaptive indentation rules + const opts = this.modelService.getCreationOptions(textModel.getLanguageIdentifier().language, textModel.uri, textModel.isForSimpleWidget); + const onEnter = Event.filter(this.inputEditor.onKeyDown, e => e.keyCode === KeyCode.Enter); + this.repositoryDisposables.add(onEnter(() => textModel.detectIndentation(opts.insertSpaces, opts.tabSize))); + + // Keep model in sync with API + textModel.setValue(input.value); + this.repositoryDisposables.add(input.onDidChange(value => { + if (value === textModel.getValue()) { // circuit breaker + return; + } + textModel.setValue(value); + this.inputEditor.setPosition(textModel.getFullModelRange().getEndPosition()); + })); + + // Keep API in sync with model, update placeholder visibility and validate + const updatePlaceholderVisibility = () => toggleClass(this.placeholderTextContainer, 'hidden', textModel.getValueLength() > 0); + this.repositoryDisposables.add(textModel.onDidChangeContent(() => { + input.value = textModel.getValue(); + updatePlaceholderVisibility(); + triggerValidation(); + })); + updatePlaceholderVisibility(); + + // Update placeholder text + const updatePlaceholderText = () => { + const binding = this.keybindingService.lookupKeybinding('scm.acceptInput'); + const label = binding ? binding.getLabel() : (platform.isMacintosh ? 'Cmd+Enter' : 'Ctrl+Enter'); + const placeholderText = format(input.placeholder, label); + + this.inputEditor.updateOptions({ ariaLabel: placeholderText }); + this.placeholderTextContainer.textContent = placeholderText; + }; + this.repositoryDisposables.add(input.onDidChangePlaceholder(updatePlaceholderText)); + this.repositoryDisposables.add(this.keybindingService.onDidUpdateKeybindings(updatePlaceholderText)); + updatePlaceholderText(); + + // Update input template + let commitTemplate = ''; + const updateTemplate = () => { + if (typeof input.repository.provider.commitTemplate === 'undefined' || !input.visible) { + return; + } + + const oldCommitTemplate = commitTemplate; + commitTemplate = input.repository.provider.commitTemplate; + + const value = textModel.getValue(); + + if (value && value !== oldCommitTemplate) { + return; + } + + textModel.setValue(commitTemplate); + }; + this.repositoryDisposables.add(input.repository.provider.onDidChangeCommitTemplate(updateTemplate, this)); + updateTemplate(); + + // Save model + this.model = { input, textModel }; + } + + constructor( + container: HTMLElement, + @IContextKeyService contextKeyService: IContextKeyService, + @IModelService private modelService: IModelService, + @IModeService private modeService: IModeService, + @IKeybindingService private keybindingService: IKeybindingService, + @IConfigurationService private configurationService: IConfigurationService, + @IInstantiationService instantiationService: IInstantiationService, + @IContextViewService private readonly contextViewService: IContextViewService, + ) { + super(); + + this.element = append(container, $('.scm-editor')); + this.editorContainer = append(this.element, $('.scm-editor-container')); + this.placeholderTextContainer = append(this.editorContainer, $('.scm-editor-placeholder')); + + const contextKeyService2 = contextKeyService.createScoped(this.element); + this.repositoryContextKey = contextKeyService2.createKey('scmRepository', undefined); + + const editorOptions: IEditorConstructionOptions = { + ...getSimpleEditorOptions(), + lineDecorationsWidth: 4, + dragAndDrop: false, + cursorWidth: 1, + fontSize: 13, + lineHeight: 20, + fontFamily: this.getInputEditorFontFamily(), + wrappingStrategy: 'advanced', + wrappingIndent: 'none', + padding: { top: 3, bottom: 3 }, + quickSuggestions: false + }; + + const codeEditorWidgetOptions: ICodeEditorWidgetOptions = { + isSimpleWidget: true, + contributions: EditorExtensionsRegistry.getSomeEditorContributions([ + SuggestController.ID, + SnippetController2.ID, + MenuPreventer.ID, + SelectionClipboardContributionID, + ContextMenuController.ID, + ColorDetector.ID, + ModesHoverController.ID, + LinkDetector.ID + ]) + }; + + const services = new ServiceCollection([IContextKeyService, contextKeyService2]); + const instantiationService2 = instantiationService.createChild(services); + this.inputEditor = instantiationService2.createInstance(CodeEditorWidget, this.editorContainer, editorOptions, codeEditorWidgetOptions); + this._register(this.inputEditor); + + this._register(this.inputEditor.onDidFocusEditorText(() => { + this.input?.repository.setSelected(true); // TODO@joao: remove + addClass(this.editorContainer, 'synthetic-focus'); + this.renderValidation(); + })); + this._register(this.inputEditor.onDidBlurEditorText(() => { + removeClass(this.editorContainer, 'synthetic-focus'); + this.validationDisposable.dispose(); + })); + + const onInputFontFamilyChanged = Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.inputFontFamily')); + this._register(onInputFontFamilyChanged(() => this.inputEditor.updateOptions({ fontFamily: this.getInputEditorFontFamily() }))); + + this.onDidChangeContentHeight = Event.signal(Event.filter(this.inputEditor.onDidContentSizeChange, e => e.contentHeightChanged)); + } + + getContentHeight(): number { + const editorContentHeight = this.inputEditor.getContentHeight(); + return Math.min(editorContentHeight, 134); + } + + layout(): void { + const editorHeight = this.getContentHeight(); + const dimension: Dimension = { + width: this.element.clientWidth - 2, + height: editorHeight, + }; + + this.inputEditor.layout(dimension); + this.renderValidation(); + } + + focus(): void { + this.inputEditor.focus(); + addClass(this.editorContainer, 'synthetic-focus'); + } + + hasFocus(): boolean { + return this.inputEditor.hasWidgetFocus(); + } + + private renderValidation(): void { + this.validationDisposable.dispose(); + + toggleClass(this.editorContainer, 'validation-info', this.validation?.type === InputValidationType.Information); + toggleClass(this.editorContainer, 'validation-warning', this.validation?.type === InputValidationType.Warning); + toggleClass(this.editorContainer, 'validation-error', this.validation?.type === InputValidationType.Error); + + if (!this.validation || !this.inputEditor.hasTextFocus()) { + return; + } + + this.validationDisposable = this.contextViewService.showContextView({ + getAnchor: () => this.editorContainer, + render: container => { + const element = append(container, $('.scm-editor-validation')); + toggleClass(element, 'validation-info', this.validation!.type === InputValidationType.Information); + toggleClass(element, 'validation-warning', this.validation!.type === InputValidationType.Warning); + toggleClass(element, 'validation-error', this.validation!.type === InputValidationType.Error); + element.style.width = `${this.editorContainer.clientWidth}px`; + element.textContent = this.validation!.message; + return Disposable.None; + }, + anchorAlignment: AnchorAlignment.LEFT + }); + } + + private getInputEditorFontFamily(): string { + const inputFontFamily = this.configurationService.getValue('scm.inputFontFamily').trim(); + + if (inputFontFamily.toLowerCase() === 'editor') { + return this.configurationService.getValue('editor.fontFamily').trim(); + } + + if (inputFontFamily.length !== 0 && inputFontFamily.toLowerCase() !== 'default') { + return inputFontFamily; + } + + return this.defaultInputFontFamily; + } + + dispose(): void { + this.input = undefined; + this.repositoryDisposables.dispose(); + this.validationDisposable.dispose(); + super.dispose(); + } +} + +class SCMCollapseAction extends Action { + + private allCollapsed = false; + + constructor(private viewModel: ViewModel) { + super('scm.collapse', undefined, undefined, true); + this._register(viewModel.onDidChangeRepositoryCollapseState(this.update, this)); + this.update(); + } + + async run(): Promise { + if (this.allCollapsed) { + this.viewModel.expandAllProviders(); + } else { + this.viewModel.collapseAllProviders(); + } + } + + private update(): void { + const isAnyProviderCollapsible = this.viewModel.isAnyProviderCollapsible(); + + this.enabled = isAnyProviderCollapsible; + this.allCollapsed = isAnyProviderCollapsible && this.viewModel.areAllProvidersCollapsed(); + this.label = this.allCollapsed ? localize('expand all', "Expand All Providers") : localize('collapse all', "Collapse All Providers"); + this.class = this.allCollapsed ? Codicon.expandAll.classNames : Codicon.collapseAll.classNames; + } +} + +export class SCMViewPane extends ViewPane { + + private _onDidLayout = new Emitter(); + private layoutCache: ISCMLayout = { + height: undefined, + width: undefined, + onDidChange: this._onDidLayout.event + }; + + private listContainer!: HTMLElement; + private tree!: WorkbenchCompressibleObjectTree; + private viewModel!: ViewModel; + private listLabels!: ResourceLabels; + private menus!: SCMMenus; + private inputRenderer!: InputRenderer; + + constructor( + options: IViewPaneOptions, + @ISCMService private scmService: ISCMService, + @IKeybindingService protected keybindingService: IKeybindingService, + @IThemeService protected themeService: IThemeService, + @IContextMenuService protected contextMenuService: IContextMenuService, + @IContextViewService protected contextViewService: IContextViewService, + @ICommandService protected commandService: ICommandService, + @IEditorService protected editorService: IEditorService, + @IInstantiationService protected instantiationService: IInstantiationService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @IConfigurationService protected configurationService: IConfigurationService, + @IContextKeyService contextKeyService: IContextKeyService, + @IMenuService protected menuService: IMenuService, + @IStorageService private storageService: IStorageService, + @IOpenerService openerService: IOpenerService, + @ITelemetryService telemetryService: ITelemetryService, + ) { + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); + this._register(Event.any(this.scmService.onDidAddRepository, this.scmService.onDidRemoveRepository)(() => this._onDidChangeViewWelcomeState.fire())); + } + + protected renderBody(container: HTMLElement): void { + super.renderBody(container); + + // List + this.listContainer = append(container, $('.scm-view.show-file-icons')); + + const updateActionsVisibility = () => toggleClass(this.listContainer, 'show-actions', this.configurationService.getValue('scm.alwaysShowActions')); + this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.alwaysShowActions'))(updateActionsVisibility)); + updateActionsVisibility(); + + const updateProviderCountVisibility = () => { + const value = this.configurationService.getValue<'hidden' | 'auto' | 'visible'>('scm.providerCountBadge'); + toggleClass(this.listContainer, 'hide-provider-counts', value === 'hidden'); + toggleClass(this.listContainer, 'auto-provider-counts', value === 'auto'); + }; + this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.providerCountBadge'))(updateProviderCountVisibility)); + updateProviderCountVisibility(); + + const repositories = new SimpleSequence(this.scmService.repositories, this.scmService.onDidAddRepository, this.scmService.onDidRemoveRepository); + this._register(repositories); + + this.menus = this.instantiationService.createInstance(SCMMenus, repositories); + this._register(this.menus); + + this._register(repositories.onDidSplice(() => this.updateActions())); + + this.inputRenderer = this.instantiationService.createInstance(InputRenderer, this.layoutCache, (input, height) => this.tree.updateElementHeight(input, height)); + const delegate = new ProviderListDelegate(this.inputRenderer); + + const actionViewItemProvider = (action: IAction) => this.getActionViewItem(action); + + this.listLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility }); + this._register(this.listLabels); + + const actionRunner = new RepositoryPaneActionRunner(() => this.getSelectedResources()); + this._register(actionRunner); + this._register(actionRunner.onDidBeforeRun(() => this.tree.domFocus())); + + const renderers = [ + this.instantiationService.createInstance(RepositoryRenderer, actionViewItemProvider, this.menus), + this.inputRenderer, + this.instantiationService.createInstance(ResourceGroupRenderer, actionViewItemProvider, this.menus), + this.instantiationService.createInstance(ResourceRenderer, () => this.viewModel, this.listLabels, actionViewItemProvider, actionRunner, this.menus) + ]; + + const filter = new SCMTreeFilter(); + const sorter = new SCMTreeSorter(() => this.viewModel); + const keyboardNavigationLabelProvider = this.instantiationService.createInstance(SCMTreeKeyboardNavigationLabelProvider); + const identityProvider = new SCMResourceIdentityProvider(); + + this.tree = this.instantiationService.createInstance( + WorkbenchCompressibleObjectTree, + 'SCM Tree Repo', + this.listContainer, + delegate, + renderers, + { + identityProvider, + horizontalScrolling: false, + setRowLineHeight: false, + filter, + sorter, + keyboardNavigationLabelProvider, + transformOptimization: false, + overrideStyles: { + listBackground: this.viewDescriptorService.getViewLocationById(this.id) === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND + }, + accessibilityProvider: this.instantiationService.createInstance(SCMAccessibilityProvider) + }) as WorkbenchCompressibleObjectTree; + + this._register(this.tree.onDidOpen(this.open, this)); + + this._register(this.tree.onContextMenu(this.onListContextMenu, this)); + this._register(this.tree); + + let viewMode = this.configurationService.getValue<'tree' | 'list'>('scm.defaultViewMode') === 'list' ? ViewModelMode.List : ViewModelMode.Tree; + const storageMode = this.storageService.get(`scm.viewMode`, StorageScope.WORKSPACE) as ViewModelMode; + + if (typeof storageMode === 'string') { + viewMode = storageMode; + } + + this.viewModel = this.instantiationService.createInstance(ViewModel, repositories, this.tree, this.menus, this.inputRenderer, viewMode, ViewModelSortKey.Path); + this._register(this.viewModel); + + addClass(this.listContainer, 'file-icon-themable-tree'); + addClass(this.listContainer, 'show-file-icons'); + + this.updateIndentStyles(this.themeService.getFileIconTheme()); + this._register(this.themeService.onDidFileIconThemeChange(this.updateIndentStyles, this)); + this._register(this.viewModel.onDidChangeMode(this.onDidChangeMode, this)); + + this._register(this.onDidChangeBodyVisibility(this.viewModel.setVisible, this.viewModel)); + + this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.alwaysShowRepositories'))(this.updateActions, this)); + this.updateActions(); + } + + private updateIndentStyles(theme: IFileIconTheme): void { + toggleClass(this.listContainer, 'list-view-mode', this.viewModel.mode === ViewModelMode.List); + toggleClass(this.listContainer, 'tree-view-mode', this.viewModel.mode === ViewModelMode.Tree); + toggleClass(this.listContainer, 'align-icons-and-twisties', (this.viewModel.mode === ViewModelMode.List && theme.hasFileIcons) || (theme.hasFileIcons && !theme.hasFolderIcons)); + toggleClass(this.listContainer, 'hide-arrows', this.viewModel.mode === ViewModelMode.Tree && theme.hidesExplorerArrows === true); + } + + private onDidChangeMode(): void { + this.updateIndentStyles(this.themeService.getFileIconTheme()); + this.storageService.store(`scm.viewMode`, this.viewModel.mode, StorageScope.WORKSPACE); + } + + layoutBody(height: number | undefined = this.layoutCache.height, width: number | undefined = this.layoutCache.width): void { + if (height === undefined) { + return; + } + + if (width !== undefined) { + super.layoutBody(height, width); + } + + this.layoutCache.height = height; + this.layoutCache.width = width; + this._onDidLayout.fire(); + + this.listContainer.style.height = `${height}px`; + this.tree.layout(height, width); + } + + focus(): void { + super.focus(); + + if (this.isExpanded()) { + this.viewModel.focus(); + } + } + + getActions(): IAction[] { + if (!this.viewModel) { + return []; + } + + if (this.viewModel.repositories.elements.length < 2) { + return this.viewModel.getViewActions(); + } + + return [ + new SCMCollapseAction(this.viewModel), + ...this.viewModel.getViewActions() + ]; + } + + getSecondaryActions(): IAction[] { + if (!this.viewModel) { + return []; + } + + return this.viewModel.getViewSecondaryActions(); + } + + getActionViewItem(action: IAction): IActionViewItem | undefined { + if (action instanceof StatusBarAction) { + return new StatusBarActionViewItem(action); + } + + return super.getActionViewItem(action); + } + + getActionsContext(): any { + if (!this.viewModel) { + return []; + } + + return this.viewModel.getViewActionsContext(); + } + + private async open(e: IOpenEvent): Promise { + if (!e.element) { + return; + } else if (isSCMRepository(e.element)) { // TODO@joao: remove + e.element.setSelected(true); + return; + } else if (isSCMResourceGroup(e.element)) { // TODO@joao: remove + const provider = e.element.provider; + const repository = this.scmService.repositories.find(r => r.provider === provider); + repository?.setSelected(true); + return; + } else if (ResourceTree.isResourceNode(e.element)) { // TODO@joao: remove + const provider = e.element.context.provider; + const repository = this.scmService.repositories.find(r => r.provider === provider); + repository?.setSelected(true); + return; + } else if (isSCMInput(e.element)) { + e.element.repository.setSelected(true); // TODO@joao: remove + + const widget = this.inputRenderer.getRenderedInputWidget(e.element); + + if (widget) { + widget.focus(); + + const selection = this.tree.getSelection(); + + if (selection.length === 1 && selection[0] === e.element) { + setTimeout(() => this.tree.setSelection([])); + } + } + + return; + } + + // ISCMResource + await e.element.open(!!e.editorOptions.preserveFocus); + + if (e.editorOptions.pinned) { + const activeEditorPane = this.editorService.activeEditorPane; + + if (activeEditorPane) { + activeEditorPane.group.pinEditor(activeEditorPane.input); + } + } + + // TODO@joao: remove + const provider = e.element.resourceGroup.provider; + const repository = this.scmService.repositories.find(r => r.provider === provider); + repository?.setSelected(true); + } + + private onListContextMenu(e: ITreeContextMenuEvent): void { + if (!e.element) { + return; + } + + const element = e.element; + let context: any = element; + let actions: IAction[] = []; + + if (isSCMRepository(element)) { + const menus = this.menus.getRepositoryMenus(element.provider); + context = element.provider; + actions = menus.getRepositoryContextActions(); + } else if (isSCMInput(element)) { + // noop + } else if (isSCMResourceGroup(element)) { + const menus = this.menus.getRepositoryMenus(element.provider); + actions = menus.getResourceGroupContextActions(element); + } else if (ResourceTree.isResourceNode(element)) { + if (element.element) { + const menus = this.menus.getRepositoryMenus(element.element.resourceGroup.provider); + actions = menus.getResourceContextActions(element.element); + } else { + const menus = this.menus.getRepositoryMenus(element.context.provider); + actions = menus.getResourceFolderContextActions(element.context); + } + } else { + const menus = this.menus.getRepositoryMenus(element.resourceGroup.provider); + actions = menus.getResourceContextActions(element); + } + + const actionRunner = new RepositoryPaneActionRunner(() => this.getSelectedResources()); + actionRunner.onDidBeforeRun(() => this.tree.domFocus()); + + this.contextMenuService.showContextMenu({ + getAnchor: () => e.anchor, + getActions: () => actions, + getActionsContext: () => context, + actionRunner + }); + } + + private getSelectedResources(): (ISCMResource | IResourceNode)[] { + return this.tree.getSelection() + .filter(r => !!r && !isSCMResourceGroup(r))! as any; + } + + shouldShowWelcome(): boolean { + return this.scmService.repositories.length === 0; + } +} + +export const scmProviderSeparatorBorderColor = registerColor('scm.providerBorder', { dark: '#454545', light: '#C8C8C8', hc: contrastBorder }, localize('scm.providerBorder', "SCM Provider separator border.")); + +registerThemingParticipant((theme, collector) => { + const inputBackgroundColor = theme.getColor(inputBackground); + if (inputBackgroundColor) { + collector.addRule(`.scm-view .scm-editor-container .monaco-editor-background, + .scm-view .scm-editor-container .monaco-editor, + .scm-view .scm-editor-container .monaco-editor .margin + { background-color: ${inputBackgroundColor} !important; }`); + } + + const inputForegroundColor = theme.getColor(inputForeground); + if (inputForegroundColor) { + collector.addRule(`.scm-view .scm-editor-container .mtk1 { color: ${inputForegroundColor}; }`); + } + + const inputBorderColor = theme.getColor(inputBorder); + if (inputBorderColor) { + collector.addRule(`.scm-view .scm-editor-container { outline: 1px solid ${inputBorderColor}; }`); + } + + const panelInputBorder = theme.getColor(PANEL_INPUT_BORDER); + if (panelInputBorder) { + collector.addRule(`.monaco-workbench .part.panel .scm-view .scm-editor-container { outline: 1px solid ${panelInputBorder}; }`); + } + + const focusBorderColor = theme.getColor(focusBorder); + if (focusBorderColor) { + collector.addRule(`.scm-view .scm-editor-container.synthetic-focus { outline: 1px solid ${focusBorderColor}; }`); + } + + const inputPlaceholderForegroundColor = theme.getColor(inputPlaceholderForeground); + if (inputPlaceholderForegroundColor) { + collector.addRule(`.scm-view .scm-editor-placeholder { color: ${inputPlaceholderForegroundColor}; }`); + } + + const inputValidationInfoBorderColor = theme.getColor(inputValidationInfoBorder); + if (inputValidationInfoBorderColor) { + collector.addRule(`.scm-view .scm-editor-container.validation-info { outline: 1px solid ${inputValidationInfoBorderColor} !important; }`); + collector.addRule(`.scm-editor-validation.validation-info { border-color: ${inputValidationInfoBorderColor}; }`); + } + + const inputValidationInfoBackgroundColor = theme.getColor(inputValidationInfoBackground); + if (inputValidationInfoBackgroundColor) { + collector.addRule(`.scm-editor-validation.validation-info { background-color: ${inputValidationInfoBackgroundColor}; }`); + } + + const inputValidationInfoForegroundColor = theme.getColor(inputValidationInfoForeground); + if (inputValidationInfoForegroundColor) { + collector.addRule(`.scm-editor-validation.validation-info { color: ${inputValidationInfoForegroundColor}; }`); + } + + const inputValidationWarningBorderColor = theme.getColor(inputValidationWarningBorder); + if (inputValidationWarningBorderColor) { + collector.addRule(`.scm-view .scm-editor-container.validation-warning { outline: 1px solid ${inputValidationWarningBorderColor} !important; }`); + collector.addRule(`.scm-editor-validation.validation-warning { border-color: ${inputValidationWarningBorderColor}; }`); + } + + const inputValidationWarningBackgroundColor = theme.getColor(inputValidationWarningBackground); + if (inputValidationWarningBackgroundColor) { + collector.addRule(`.scm-editor-validation.validation-warning { background-color: ${inputValidationWarningBackgroundColor}; }`); + } + + const inputValidationWarningForegroundColor = theme.getColor(inputValidationWarningForeground); + if (inputValidationWarningForegroundColor) { + collector.addRule(`.scm-editor-validation.validation-warning { color: ${inputValidationWarningForegroundColor}; }`); + } + + const inputValidationErrorBorderColor = theme.getColor(inputValidationErrorBorder); + if (inputValidationErrorBorderColor) { + collector.addRule(`.scm-view .scm-editor-container.validation-error { outline: 1px solid ${inputValidationErrorBorderColor} !important; }`); + collector.addRule(`.scm-editor-validation.validation-error { border-color: ${inputValidationErrorBorderColor}; }`); + } + + const inputValidationErrorBackgroundColor = theme.getColor(inputValidationErrorBackground); + if (inputValidationErrorBackgroundColor) { + collector.addRule(`.scm-editor-validation.validation-error { background-color: ${inputValidationErrorBackgroundColor}; }`); + } + + const inputValidationErrorForegroundColor = theme.getColor(inputValidationErrorForeground); + if (inputValidationErrorForegroundColor) { + collector.addRule(`.scm-editor-validation.validation-error { color: ${inputValidationErrorForegroundColor}; }`); + } + + const repositoryStatusActionsBorderColor = theme.getColor(SIDE_BAR_BORDER); + if (repositoryStatusActionsBorderColor) { + collector.addRule(`.scm-view .scm-provider > .status > .monaco-action-bar > .actions-container { border-color: ${repositoryStatusActionsBorderColor}; }`); + } +}); diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPaneContainer.ts b/src/vs/workbench/contrib/scm/browser/scmViewPaneContainer.ts new file mode 100644 index 00000000000..b1efece39ac --- /dev/null +++ b/src/vs/workbench/contrib/scm/browser/scmViewPaneContainer.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * 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/scm'; +import { localize } from 'vs/nls'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { VIEWLET_ID, ISCMService } from 'vs/workbench/contrib/scm/common/scm'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { SCMRepositoryMenus } from './menus'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IViewDescriptorService } from 'vs/workbench/common/views'; +import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { addClass } from 'vs/base/browser/dom'; + +export class SCMViewPaneContainer extends ViewPaneContainer { + + constructor( + @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, + @ITelemetryService telemetryService: ITelemetryService, + @ISCMService protected scmService: ISCMService, + @IInstantiationService protected instantiationService: IInstantiationService, + @IContextViewService protected contextViewService: IContextViewService, + @IKeybindingService protected keybindingService: IKeybindingService, + @INotificationService protected notificationService: INotificationService, + @IContextMenuService protected contextMenuService: IContextMenuService, + @IThemeService protected themeService: IThemeService, + @ICommandService protected commandService: ICommandService, + @IStorageService storageService: IStorageService, + @IConfigurationService configurationService: IConfigurationService, + @IExtensionService extensionService: IExtensionService, + @IWorkspaceContextService protected contextService: IWorkspaceContextService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService + ) { + super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService); + + const menus = instantiationService.createInstance(SCMRepositoryMenus, undefined); + this._register(menus); + this._register(menus.onDidChangeTitle(this.updateTitleArea, this)); + } + + create(parent: HTMLElement): void { + super.create(parent); + addClass(parent, 'scm-viewlet'); + } + + getOptimalWidth(): number { + return 400; + } + + getTitle(): string { + return localize('source control', "Source Control"); + } +} diff --git a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts deleted file mode 100644 index 30b7511a215..00000000000 --- a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts +++ /dev/null @@ -1,313 +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/scmViewlet'; -import { localize } from 'vs/nls'; -import { Event, Emitter } from 'vs/base/common/event'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { VIEWLET_ID, ISCMService, ISCMRepository } from 'vs/workbench/contrib/scm/common/scm'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { MenuItemAction } from 'vs/platform/actions/common/actions'; -import { IAction, IActionViewItem } from 'vs/base/common/actions'; -import { ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { SCMMenus } from './menus'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IStorageService } from 'vs/platform/storage/common/storage'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IViewsRegistry, Extensions, IViewDescriptorService, IViewDescriptor, IAddedViewDescriptorRef, IViewDescriptorRef } from 'vs/workbench/common/views'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { RepositoryPane, RepositoryViewDescriptor } from 'vs/workbench/contrib/scm/browser/repositoryPane'; -import { MainPaneDescriptor, MainPane, IViewModel } from 'vs/workbench/contrib/scm/browser/mainPane'; -import { ViewPaneContainer, IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; -import { debounce } from 'vs/base/common/decorators'; -import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { addClass } from 'vs/base/browser/dom'; -import { Codicon } from 'vs/base/common/codicons'; - -export interface ISpliceEvent { - index: number; - deleteCount: number; - elements: T[]; -} - -export class EmptyPane extends ViewPane { - - static readonly ID = 'workbench.scm'; - static readonly TITLE = localize('scm', "Source Control"); - - constructor( - options: IViewPaneOptions, - @IKeybindingService keybindingService: IKeybindingService, - @IContextMenuService contextMenuService: IContextMenuService, - @IConfigurationService configurationService: IConfigurationService, - @IContextKeyService contextKeyService: IContextKeyService, - @IViewDescriptorService viewDescriptorService: IViewDescriptorService, - @IInstantiationService instantiationService: IInstantiationService, - @IOpenerService openerService: IOpenerService, - @IThemeService themeService: IThemeService, - @ITelemetryService telemetryService: ITelemetryService, - ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); - } - - shouldShowWelcome(): boolean { - return true; - } -} - -export class EmptyPaneDescriptor implements IViewDescriptor { - readonly id = EmptyPane.ID; - readonly name = EmptyPane.TITLE; - readonly containerIcon = Codicon.sourceControl.classNames; - readonly ctorDescriptor = new SyncDescriptor(EmptyPane); - readonly canToggleVisibility = true; - readonly hideByDefault = false; - readonly order = -1000; - readonly workspace = true; - readonly when = ContextKeyExpr.equals('scm.providerCount', 0); -} - -export class SCMViewPaneContainer extends ViewPaneContainer implements IViewModel { - - private menus: SCMMenus; - private _repositories: ISCMRepository[] = []; - - private repositoryCountKey: IContextKey; - private viewDescriptors: RepositoryViewDescriptor[] = []; - - private readonly _onDidSplice = new Emitter>(); - readonly onDidSplice: Event> = this._onDidSplice.event; - - private _height: number | undefined = undefined; - get height(): number | undefined { return this._height; } - - get repositories(): ISCMRepository[] { - return this._repositories; - } - - get visibleRepositories(): ISCMRepository[] { - return this.panes.filter(pane => pane instanceof RepositoryPane) - .map(pane => (pane as RepositoryPane).repository); - } - - get onDidChangeVisibleRepositories(): Event { - const modificationEvent = Event.debounce(Event.any(this.viewContainerModel.onDidAddVisibleViewDescriptors, this.viewContainerModel.onDidRemoveVisibleViewDescriptors), () => null, 0); - return Event.map(modificationEvent, () => this.visibleRepositories); - } - - constructor( - @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, - @ITelemetryService telemetryService: ITelemetryService, - @ISCMService protected scmService: ISCMService, - @IInstantiationService protected instantiationService: IInstantiationService, - @IContextViewService protected contextViewService: IContextViewService, - @IKeybindingService protected keybindingService: IKeybindingService, - @INotificationService protected notificationService: INotificationService, - @IContextMenuService protected contextMenuService: IContextMenuService, - @IThemeService protected themeService: IThemeService, - @ICommandService protected commandService: ICommandService, - @IStorageService storageService: IStorageService, - @IConfigurationService configurationService: IConfigurationService, - @IExtensionService extensionService: IExtensionService, - @IWorkspaceContextService protected contextService: IWorkspaceContextService, - @IContextKeyService contextKeyService: IContextKeyService, - @IViewDescriptorService viewDescriptorService: IViewDescriptorService - ) { - super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService); - - this.menus = instantiationService.createInstance(SCMMenus, undefined); - this._register(this.menus.onDidChangeTitle(this.updateTitleArea, this)); - - const viewsRegistry = Registry.as(Extensions.ViewsRegistry); - - viewsRegistry.registerViewWelcomeContent(EmptyPane.ID, { - content: localize('no open repo', "No source control providers registered."), - when: 'default' - }); - - viewsRegistry.registerViews([new EmptyPaneDescriptor()], this.viewContainer); - viewsRegistry.registerViews([new MainPaneDescriptor(this)], this.viewContainer); - - this._register(configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('scm.alwaysShowProviders') && configurationService.getValue('scm.alwaysShowProviders')) { - this.viewContainerModel.setVisible(MainPane.ID, true); - } - })); - - this.repositoryCountKey = contextKeyService.createKey('scm.providerCount', 0); - - this._register(this.viewContainerModel.onDidAddVisibleViewDescriptors(this.onDidShowView, this)); - this._register(this.viewContainerModel.onDidRemoveVisibleViewDescriptors(this.onDidHideView, this)); - } - - create(parent: HTMLElement): void { - super.create(parent); - addClass(parent, 'scm-viewlet'); - this._register(this.scmService.onDidAddRepository(this.onDidAddRepository, this)); - this._register(this.scmService.onDidRemoveRepository(this.onDidRemoveRepository, this)); - this.scmService.repositories.forEach(r => this.onDidAddRepository(r)); - } - - private onDidAddRepository(repository: ISCMRepository): void { - const index = this._repositories.length; - this._repositories.push(repository); - - const viewDescriptor = new RepositoryViewDescriptor(repository, false); - Registry.as(Extensions.ViewsRegistry).registerViews([viewDescriptor], this.viewContainer); - this.viewDescriptors.push(viewDescriptor); - - this._onDidSplice.fire({ index, deleteCount: 0, elements: [repository] }); - this.updateTitleArea(); - - this.onDidChangeRepositories(); - } - - private onDidRemoveRepository(repository: ISCMRepository): void { - const index = this._repositories.indexOf(repository); - - if (index === -1) { - return; - } - - Registry.as(Extensions.ViewsRegistry).deregisterViews([this.viewDescriptors[index]], this.viewContainer); - - this._repositories.splice(index, 1); - this.viewDescriptors.splice(index, 1); - - this._onDidSplice.fire({ index, deleteCount: 1, elements: [] }); - this.updateTitleArea(); - - this.onDidChangeRepositories(); - } - - private onDidChangeRepositories(): void { - this.repositoryCountKey.set(this.repositories.length); - } - - private onDidShowView(e: IAddedViewDescriptorRef[]): void { - for (const ref of e) { - if (ref.viewDescriptor instanceof RepositoryViewDescriptor) { - ref.viewDescriptor.repository.setSelected(true); - } - } - } - - private onDidHideView(e: IViewDescriptorRef[]): void { - for (const ref of e) { - if (ref.viewDescriptor instanceof RepositoryViewDescriptor) { - ref.viewDescriptor.repository.setSelected(false); - } - } - - this.afterOnDidHideView(); - } - - @debounce(0) - private afterOnDidHideView(): void { - if (this.repositoryCountKey.get()! > 0 && this.viewDescriptors.every(d => !this.viewContainerModel.isVisible(d.id))) { - this.viewContainerModel.setVisible(this.viewDescriptors[0].id, true); - } - } - - focus(): void { - const repository = this.visibleRepositories[0]; - - if (repository) { - const pane = this.panes - .filter(pane => pane instanceof RepositoryPane && pane.repository === repository)[0] as RepositoryPane | undefined; - - if (pane) { - pane.focus(); - } else { - super.focus(); - } - } else { - super.focus(); - } - } - - getOptimalWidth(): number { - return 400; - } - - getTitle(): string { - const title = localize('source control', "Source Control"); - - if (this.visibleRepositories.length === 1) { - const [repository] = this.repositories; - return localize('viewletTitle', "{0}: {1}", title, repository.provider.label); - } else { - return title; - } - } - - getActionViewItem(action: IAction): IActionViewItem | undefined { - if (!(action instanceof MenuItemAction)) { - return undefined; - } - - return new ContextAwareMenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService); - } - - getActions(): IAction[] { - if (this.repositories.length > 0) { - return super.getActions(); - } - - return this.menus.getTitleActions(); - } - - getSecondaryActions(): IAction[] { - if (this.repositories.length > 0) { - return super.getSecondaryActions(); - } - - return this.menus.getTitleSecondaryActions(); - } - - getActionsContext(): any { - if (this.visibleRepositories.length === 1) { - return this.repositories[0].provider; - } - } - - setVisibleRepositories(repositories: ISCMRepository[]): void { - const visibleViewDescriptors = this.viewContainerModel.visibleViewDescriptors; - - const toSetVisible = this.viewContainerModel.activeViewDescriptors - .filter((d): d is RepositoryViewDescriptor => d instanceof RepositoryViewDescriptor && repositories.indexOf(d.repository) > -1 && visibleViewDescriptors.indexOf(d) === -1); - - const toSetInvisible = visibleViewDescriptors - .filter((d): d is RepositoryViewDescriptor => d instanceof RepositoryViewDescriptor && repositories.indexOf(d.repository) === -1); - - let size: number | undefined; - const oneToOne = toSetVisible.length === 1 && toSetInvisible.length === 1; - - for (const viewDescriptor of toSetInvisible) { - if (oneToOne) { - const pane = this.panes.filter(pane => pane.id === viewDescriptor.id)[0]; - - if (pane) { - size = this.getPaneSize(pane); - } - } - - this.viewContainerModel.setVisible(viewDescriptor.id, false); - } - - for (const viewDescriptor of toSetVisible) { - this.viewContainerModel.setVisible(viewDescriptor.id, true, size); - } - } -} diff --git a/src/vs/workbench/contrib/scm/browser/util.ts b/src/vs/workbench/contrib/scm/browser/util.ts index 97a4c1eb80a..08a7e69ce3a 100644 --- a/src/vs/workbench/contrib/scm/browser/util.ts +++ b/src/vs/workbench/contrib/scm/browser/util.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ISCMResource, ISCMRepository, ISCMResourceGroup } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMResource, ISCMRepository, ISCMResourceGroup, ISCMInput } from 'vs/workbench/contrib/scm/common/scm'; import { IMenu } from 'vs/platform/actions/common/actions'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { IDisposable, Disposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -15,6 +15,10 @@ export function isSCMRepository(element: any): element is ISCMRepository { return !!(element as ISCMRepository).provider && typeof (element as ISCMRepository).setSelected === 'function'; } +export function isSCMInput(element: any): element is ISCMInput { + return !!(element as ISCMInput).validateInput && typeof (element as ISCMInput).value === 'string'; +} + export function isSCMResourceGroup(element: any): element is ISCMResourceGroup { return !!(element as ISCMResourceGroup).provider && !!(element as ISCMResourceGroup).elements; } @@ -23,31 +27,42 @@ export function isSCMResource(element: any): element is ISCMResource { return !!(element as ISCMResource).sourceUri && isSCMResourceGroup((element as ISCMResource).resourceGroup); } -export function connectPrimaryMenuToInlineActionBar(menu: IMenu, actionBar: ActionBar): IDisposable { +const compareActions = (a: IAction, b: IAction) => a.id === b.id; + +export function connectPrimaryMenu(menu: IMenu, callback: (primary: IAction[], secondary: IAction[]) => void, isPrimaryGroup?: (group: string) => boolean): IDisposable { let cachedDisposable: IDisposable = Disposable.None; let cachedPrimary: IAction[] = []; + let cachedSecondary: IAction[] = []; const updateActions = () => { const primary: IAction[] = []; const secondary: IAction[] = []; - const disposable = createAndFillInActionBarActions(menu, { shouldForwardArgs: true }, { primary, secondary }, g => /^inline/.test(g)); + const disposable = createAndFillInActionBarActions(menu, { shouldForwardArgs: true }, { primary, secondary }, isPrimaryGroup); - if (equals(cachedPrimary, primary, (a, b) => a.id === b.id)) { + if (equals(cachedPrimary, primary, compareActions) && equals(cachedSecondary, secondary, compareActions)) { disposable.dispose(); return; } cachedDisposable = disposable; cachedPrimary = primary; + cachedSecondary = secondary; - actionBar.clear(); - actionBar.push(primary, { icon: true, label: false }); + callback(primary, secondary); }; updateActions(); - return combinedDisposable(menu.onDidChange(updateActions), toDisposable(() => { - cachedDisposable.dispose(); - })); + return combinedDisposable( + menu.onDidChange(updateActions), + toDisposable(() => cachedDisposable.dispose()) + ); +} + +export function connectPrimaryMenuToInlineActionBar(menu: IMenu, actionBar: ActionBar): IDisposable { + return connectPrimaryMenu(menu, (primary) => { + actionBar.clear(); + actionBar.push(primary, { icon: true, label: false }); + }, g => /^inline/.test(g)); } diff --git a/src/vs/workbench/contrib/scm/common/scm.ts b/src/vs/workbench/contrib/scm/common/scm.ts index f9783b67bd9..5ebbcbd872f 100644 --- a/src/vs/workbench/contrib/scm/common/scm.ts +++ b/src/vs/workbench/contrib/scm/common/scm.ts @@ -11,6 +11,7 @@ import { Command } from 'vs/editor/common/modes'; import { ISequence } from 'vs/base/common/sequence'; export const VIEWLET_ID = 'workbench.view.scm'; +export const VIEW_PANE_ID = 'workbench.scm'; export interface IBaselineResourceProvider { getBaselineResource(resource: URI): Promise; @@ -80,6 +81,8 @@ export interface IInputValidator { } export interface ISCMInput { + readonly repository: ISCMRepository; + value: string; readonly onDidChange: Event; @@ -94,12 +97,10 @@ export interface ISCMInput { } export interface ISCMRepository extends IDisposable { - readonly onDidFocus: Event; readonly selected: boolean; readonly onDidChangeSelection: Event; readonly provider: ISCMProvider; readonly input: ISCMInput; - focus(): void; setSelected(selected: boolean): void; } @@ -108,10 +109,7 @@ export interface ISCMService { readonly _serviceBrand: undefined; readonly onDidAddRepository: Event; readonly onDidRemoveRepository: Event; - readonly repositories: ISCMRepository[]; - readonly selectedRepositories: ISCMRepository[]; - readonly onDidChangeSelectedRepositories: Event; registerSCMProvider(provider: ISCMProvider): ISCMRepository; } diff --git a/src/vs/workbench/contrib/scm/common/scmService.ts b/src/vs/workbench/contrib/scm/common/scmService.ts index 62c5d4f023e..fac8be966b7 100644 --- a/src/vs/workbench/contrib/scm/common/scmService.ts +++ b/src/vs/workbench/contrib/scm/common/scmService.ts @@ -7,7 +7,6 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { ISCMService, ISCMProvider, ISCMInput, ISCMRepository, IInputValidator } from './scm'; import { ILogService } from 'vs/platform/log/common/log'; -import { equals } from 'vs/base/common/arrays'; class SCMInput implements ISCMInput { @@ -70,13 +69,12 @@ class SCMInput implements ISCMInput { private readonly _onDidChangeValidateInput = new Emitter(); readonly onDidChangeValidateInput: Event = this._onDidChangeValidateInput.event; + + constructor(readonly repository: ISCMRepository) { } } class SCMRepository implements ISCMRepository { - private readonly _onDidFocus = new Emitter(); - readonly onDidFocus: Event = this._onDidFocus.event; - private _selected = false; get selected(): boolean { return this._selected; @@ -85,17 +83,13 @@ class SCMRepository implements ISCMRepository { private readonly _onDidChangeSelection = new Emitter(); readonly onDidChangeSelection: Event = this._onDidChangeSelection.event; - readonly input: ISCMInput = new SCMInput(); + readonly input: ISCMInput = new SCMInput(this); constructor( public readonly provider: ISCMProvider, private disposable: IDisposable ) { } - focus(): void { - this._onDidFocus.fire(); - } - setSelected(selected: boolean): void { if (this._selected === selected) { return; @@ -113,17 +107,16 @@ class SCMRepository implements ISCMRepository { export class SCMService implements ISCMService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _providerIds = new Set(); private _repositories: ISCMRepository[] = []; get repositories(): ISCMRepository[] { return [...this._repositories]; } - private _selectedRepositories: ISCMRepository[] = []; - get selectedRepositories(): ISCMRepository[] { return [...this._selectedRepositories]; } + private _selectedRepository: ISCMRepository | undefined; - private readonly _onDidChangeSelectedRepositories = new Emitter(); - readonly onDidChangeSelectedRepositories: Event = this._onDidChangeSelectedRepositories.event; + private readonly _onDidSelectRepository = new Emitter(); + readonly onDidSelectRepository: Event = this._onDidSelectRepository.event; private readonly _onDidAddProvider = new Emitter(); readonly onDidAddRepository: Event = this._onDidAddProvider.event; @@ -153,26 +146,31 @@ export class SCMService implements ISCMService { this._providerIds.delete(provider.id); this._repositories.splice(index, 1); this._onDidRemoveProvider.fire(repository); - this.onDidChangeSelection(); + + if (this._selectedRepository === repository) { + this.select(this._repositories[0]); + } }); const repository = new SCMRepository(provider, disposable); - const selectedDisposable = repository.onDidChangeSelection(this.onDidChangeSelection, this); + const selectedDisposable = Event.map(Event.filter(repository.onDidChangeSelection, selected => selected), _ => repository)(this.select, this); this._repositories.push(repository); this._onDidAddProvider.fire(repository); + if (!this._selectedRepository) { + repository.setSelected(true); + } + return repository; } - private onDidChangeSelection(): void { - const selectedRepositories = this._repositories.filter(r => r.selected); - - if (equals(this._selectedRepositories, selectedRepositories)) { - return; + private select(repository: ISCMRepository | undefined): void { + if (this._selectedRepository) { + this._selectedRepository.setSelected(false); } - this._selectedRepositories = this._repositories.filter(r => r.selected); - this._onDidChangeSelectedRepositories.fire(this.selectedRepositories); + this._selectedRepository = repository; + this._onDidSelectRepository.fire(this._selectedRepository); } } diff --git a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts index 2ffee859696..adb459fb155 100644 --- a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts @@ -67,6 +67,10 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider= 0; + if (!activePickIsEditorSymbol && !activePickIsNoResultsInEditorSymbols) { this.pickState.lastGlobalPicks = { items: picks, active: activePick @@ -491,22 +495,34 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider; if (absolutePathResult) { - fileMatches = [absolutePathResult]; + if (excludes.has(absolutePathResult)) { + return []; // excluded + } + + // Create a single result pick and make sure to apply full + // highlights to ensure the pick is displayed. Since a + // ~ might have been used for searching, our fuzzy scorer + // may otherwise not properly respect the pick as a result + const absolutePathPick = this.createAnythingPick(absolutePathResult, this.configuration); + absolutePathPick.highlights = { + label: [{ start: 0, end: absolutePathPick.label.length }], + description: absolutePathPick.description ? [{ start: 0, end: absolutePathPick.description.length }] : undefined + }; + + return [absolutePathPick]; } // Otherwise run the file search (with a delayer if cache is not ready yet) - else { - if (this.pickState.fileQueryCache?.isLoaded) { - fileMatches = await this.doFileSearch(query, token); - } else { - fileMatches = await this.fileQueryDelayer.trigger(async () => { - if (token.isCancellationRequested) { - return []; - } + if (this.pickState.fileQueryCache?.isLoaded) { + fileMatches = await this.doFileSearch(query, token); + } else { + fileMatches = await this.fileQueryDelayer.trigger(async () => { + if (token.isCancellationRequested) { + return []; + } - return this.doFileSearch(query, token); - }); - } + return this.doFileSearch(query, token); + }); } if (token.isCancellationRequested) { @@ -631,7 +647,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider { - const contextService = accessor.get(IContextKeyService).getContext(document.activeElement); - if (contextService.getValue(SearchEditorConstants.InSearchEditor.serialize())) { - (accessor.get(IEditorService).activeEditorPane as SearchEditor).deleteResultBlock(); - } - } -}); - KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.FocusSearchFromResults, weight: KeybindingWeight.WorkbenchContrib, @@ -381,10 +368,9 @@ MenuRegistry.appendMenuItem(MenuId.SearchContext, { order: 1 }); -const clearSearchHistoryLabel = nls.localize('clearSearchHistoryLabel', "Clear Search History"); const ClearSearchHistoryCommand: ICommandAction = { id: Constants.ClearSearchHistoryCommandId, - title: clearSearchHistoryLabel, + title: { value: nls.localize('clearSearchHistoryLabel', "Clear Search History"), original: 'Clear Search History' }, category }; MenuRegistry.addCommand(ClearSearchHistoryCommand); @@ -394,10 +380,9 @@ CommandsRegistry.registerCommand({ handler: focusSearchListCommand }); -const focusSearchListCommandLabel = nls.localize('focusSearchListCommandLabel', "Focus List"); const FocusSearchListCommand: ICommandAction = { id: Constants.FocusSearchListCommandID, - title: focusSearchListCommandLabel, + title: { value: nls.localize('focusSearchListCommandLabel', "Focus List"), original: 'Focus List' }, category }; MenuRegistry.addCommand(FocusSearchListCommand); @@ -559,6 +544,27 @@ const registry = Registry.as(ActionExtensions.Workbenc // Show Search 'when' is redundant but if the two conflict with exactly the same keybinding and 'when' clause, then they can show up as "unbound" - #51780 registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenSearchViewletAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F }, Constants.SearchViewVisibleKey.toNegated()), 'View: Show Search', nls.localize('view', "View")); KeybindingsRegistry.registerCommandAndKeybindingRule({ + description: { + description: nls.localize('findInFiles.description', "Open the search viewlet"), + args: [ + { + name: nls.localize('findInFiles.args', "A set of options for the search viewlet"), + schema: { + type: 'object', + properties: { + query: { 'type': 'string' }, + replace: { 'type': 'string' }, + triggerSearch: { 'type': 'boolean' }, + filesToInclude: { 'type': 'string' }, + filesToExclude: { 'type': 'string' }, + isRegex: { 'type': 'boolean' }, + isCaseSensitive: { 'type': 'boolean' }, + matchWholeWord: { 'type': 'boolean' }, + } + } + }, + ] + }, id: Constants.FindInFilesActionId, weight: KeybindingWeight.WorkbenchContrib, when: null, @@ -575,10 +581,10 @@ MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { order: 1 }); -registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusNextSearchResultAction, { primary: KeyCode.F4 }, ContextKeyExpr.or(Constants.HasSearchResults, SearchEditorConstants.InSearchEditor)), 'Focus Next Search Result', category); -registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusPreviousSearchResultAction, { primary: KeyMod.Shift | KeyCode.F4 }, ContextKeyExpr.or(Constants.HasSearchResults, SearchEditorConstants.InSearchEditor)), 'Focus Previous Search Result', category); +registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusNextSearchResultAction, { primary: KeyCode.F4 }), 'Search: Focus Next Search Result', category.value, ContextKeyExpr.or(Constants.HasSearchResults, SearchEditorConstants.InSearchEditor)); +registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusPreviousSearchResultAction, { primary: KeyMod.Shift | KeyCode.F4 }), 'Search: Focus Previous Search Result', category.value, ContextKeyExpr.or(Constants.HasSearchResults, SearchEditorConstants.InSearchEditor)); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ReplaceInFilesAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_H }), 'Replace in Files', category); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ReplaceInFilesAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_H }), 'Search: Replace in Files', category.value); MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { group: '4_find_global', command: { @@ -633,12 +639,12 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); -registry.registerWorkbenchAction(SyncActionDescriptor.from(CollapseDeepestExpandedLevelAction), 'Search: Collapse All', category); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ExpandAllAction), 'Search: Expand All', category); +registry.registerWorkbenchAction(SyncActionDescriptor.from(CollapseDeepestExpandedLevelAction), 'Search: Collapse All', category.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ExpandAllAction), 'Search: Expand All', category.value); registry.registerWorkbenchAction(SyncActionDescriptor.from(ShowAllSymbolsAction, { primary: KeyMod.CtrlCmd | KeyCode.KEY_T }), 'Go to Symbol in Workspace...'); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleSearchOnTypeAction), 'Search: Toggle Search on Type', category); -registry.registerWorkbenchAction(SyncActionDescriptor.from(RefreshAction), 'Search: Refresh', category); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ClearSearchResultsAction), 'Search: Clear Search Results', category); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleSearchOnTypeAction), 'Search: Toggle Search on Type', category.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(RefreshAction), 'Search: Refresh', category.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ClearSearchResultsAction), 'Search: Clear Search Results', category.value); // Register Quick Access Handler const quickAccessRegistry = Registry.as(QuickAccessExtensions.Quickaccess); @@ -756,7 +762,7 @@ configurationRegistry.registerConfiguration({ enum: ['sidebar', 'panel'], default: 'sidebar', description: nls.localize('search.location', "Controls whether the search will be shown as a view in the sidebar or as a panel in the panel area for more horizontal space."), - deprecationMessage: nls.localize('search.location.deprecationMessage', "This setting is deprecated. Please use the search view's context menu instead.") + deprecationMessage: nls.localize('search.location.deprecationMessage', "This setting is deprecated. Please use drag and drop instead by dragging the search icon.") }, 'search.collapseResults': { type: 'string', @@ -829,19 +835,24 @@ configurationRegistry.registerConfiguration({ 'search.searchEditor.reusePriorSearchConfiguration': { type: 'boolean', default: false, - markdownDescription: nls.localize('search.searchEditor.reusePriorSearchConfiguration', "When enabled, new Search Editors will reuse the includes, excludes, and flags of the previously opened Search Editor") + markdownDescription: nls.localize({ key: 'search.searchEditor.reusePriorSearchConfiguration', comment: ['"Search Editor" is a type that editor that can display search results. "includes, excludes, and flags" just refers to settings that affect search. For example, the "search.exclude" setting.'] }, "When enabled, new Search Editors will reuse the includes, excludes, and flags of the previously opened Search Editor") + }, + 'search.searchEditor.defaultNumberOfContextLines': { + type: ['number', 'null'], + default: 1, + markdownDescription: nls.localize('search.searchEditor.defaultNumberOfContextLines', "The default number of surrounding context lines to use when creating new Search Editors. If using `#search.searchEditor.reusePriorSearchConfiguration#`, this can be set to `null` (empty) to use the prior Search Editor's configuration.") }, 'search.sortOrder': { 'type': 'string', 'enum': [SearchSortOrder.Default, SearchSortOrder.FileNames, SearchSortOrder.Type, SearchSortOrder.Modified, SearchSortOrder.CountDescending, SearchSortOrder.CountAscending], 'default': SearchSortOrder.Default, 'enumDescriptions': [ - nls.localize('searchSortOrder.default', 'Results are sorted by folder and file names, in alphabetical order.'), - nls.localize('searchSortOrder.filesOnly', 'Results are sorted by file names ignoring folder order, in alphabetical order.'), - nls.localize('searchSortOrder.type', 'Results are sorted by file extensions, in alphabetical order.'), - nls.localize('searchSortOrder.modified', 'Results are sorted by file last modified date, in descending order.'), - nls.localize('searchSortOrder.countDescending', 'Results are sorted by count per file, in descending order.'), - nls.localize('searchSortOrder.countAscending', 'Results are sorted by count per file, in ascending order.') + nls.localize('searchSortOrder.default', "Results are sorted by folder and file names, in alphabetical order."), + nls.localize('searchSortOrder.filesOnly', "Results are sorted by file names ignoring folder order, in alphabetical order."), + nls.localize('searchSortOrder.type', "Results are sorted by file extensions, in alphabetical order."), + nls.localize('searchSortOrder.modified', "Results are sorted by file last modified date, in descending order."), + nls.localize('searchSortOrder.countDescending', "Results are sorted by count per file, in descending order."), + nls.localize('searchSortOrder.countAscending', "Results are sorted by count per file, in ascending order.") ], 'description': nls.localize('search.sortOrder', "Controls sorting order of search results.") }, diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index 96c16f7b7e3..01d1831bb0a 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -446,7 +446,7 @@ export class ClearSearchResultsAction extends Action { update(): void { const searchView = getSearchView(this.viewsService); - this.enabled = !!searchView && (!searchView.allSearchFieldsClear() || searchView.hasSearchResults()); + this.enabled = !!searchView && (!searchView.allSearchFieldsClear() || searchView.hasSearchResults() || !searchView.allFilePatternFieldsClear()); } run(): Promise { diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 543ad701953..ad1ba249fe8 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -31,7 +31,7 @@ import { IContextMenuService, IContextViewService } from 'vs/platform/contextvie import { IConfirmation, IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { TreeResourceNavigator, WorkbenchObjectTree, getSelectionKeyboardEvent } from 'vs/platform/list/browser/listService'; +import { WorkbenchObjectTree, getSelectionKeyboardEvent } from 'vs/platform/list/browser/listService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IProgressService, IProgressStep, IProgress } from 'vs/platform/progress/common/progress'; import { IPatternInfo, ISearchComplete, ISearchConfiguration, ISearchConfigurationProperties, ITextQuery, SearchSortOrder, SearchCompletionExitCode } from 'vs/workbench/services/search/common/search'; @@ -718,6 +718,7 @@ export class SearchView extends ViewPane { accessibilityProvider: this.treeAccessibilityProvider, dnd: this.instantiationService.createInstance(SearchDND), multipleSelectionSupport: false, + openOnFocus: true, overrideStyles: { listBackground: this.getBackgroundColor() } @@ -727,8 +728,7 @@ export class SearchView extends ViewPane { this.toggleCollapseStateDelayer.trigger(() => this.toggleCollapseAction.onTreeCollapseStateChange()) )); - const resourceNavigator = this._register(new TreeResourceNavigator(this.tree, { openOnFocus: true, openOnSelection: false })); - this._register(Event.debounce(resourceNavigator.onDidOpenResource, (last, event) => event, 75, true)(options => { + this._register(Event.debounce(this.tree.onDidOpen, (last, event) => event, 75, true)(options => { if (options.element instanceof Match) { const selectedMatch: Match = options.element; if (this.currentSelectedFileMatch) { @@ -785,6 +785,10 @@ export class SearchView extends ViewPane { } selectNextMatch(): void { + if (!this.hasSearchResults()) { + return; + } + const [selected] = this.tree.getSelection(); // Expand the initial selected node, if needed @@ -794,7 +798,7 @@ export class SearchView extends ViewPane { } } - let navigator = this.tree.navigate(selected); + const navigator = this.tree.navigate(selected); let next = navigator.next(); if (!next) { @@ -824,6 +828,10 @@ export class SearchView extends ViewPane { } selectPreviousMatch(): void { + if (!this.hasSearchResults()) { + return; + } + const [selected] = this.tree.getSelection(); let navigator = this.tree.navigate(selected); @@ -831,7 +839,13 @@ export class SearchView extends ViewPane { // Select previous until find a Match or a collapsed item while (!prev || (!(prev instanceof Match) && !this.tree.isCollapsed(prev))) { - prev = prev ? navigator.previous() : navigator.last(); + const nextPrev = prev ? navigator.previous() : navigator.last(); + + if (!prev && !nextPrev) { + return; + } + + prev = nextPrev; } // Expand until last child is a Match @@ -959,7 +973,7 @@ export class SearchView extends ViewPane { } private reLayout(): void { - if (this.isDisposed) { + if (this.isDisposed || !this.size) { return; } @@ -1003,6 +1017,11 @@ export class SearchView extends ViewPane { this.searchWidget.searchInput.getValue() === ''; } + allFilePatternFieldsClear(): boolean { + return this.searchExcludePattern.getValue() === '' && + this.searchIncludePattern.getValue() === ''; + } + hasSearchResults(): boolean { return !this.viewModel.searchResult.isEmpty(); } @@ -1018,6 +1037,9 @@ export class SearchView extends ViewPane { this.showSearchWithoutFolderMessage(); } if (clearInput) { + if (this.allSearchFieldsClear()) { + this.clearFilePatternFields(); + } this.searchWidget.clear(); } this.viewModel.cancelSearch(); @@ -1027,6 +1049,11 @@ export class SearchView extends ViewPane { aria.status(nls.localize('ariaSearchResultsClearStatus', "The search results have been cleared")); } + clearFilePatternFields(): void { + this.searchExcludePattern.clear(); + this.searchIncludePattern.clear(); + } + cancelSearch(focus: boolean = true): boolean { if (this.viewModel.cancelSearch()) { if (focus) { this.searchWidget.focus(); } @@ -1695,7 +1722,7 @@ export class SearchView extends ViewPane { const selections = fileMatch.matches().map(m => new Selection(m.range().startLineNumber, m.range().startColumn, m.range().endLineNumber, m.range().endColumn)); const codeEditor = getCodeEditor(editor.getControl()); if (codeEditor) { - let multiCursorController = MultiCursorSelectionController.get(codeEditor); + const multiCursorController = MultiCursorSelectionController.get(codeEditor); multiCursorController.selectAllUsingSelections(selections); } } diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 9c21aabdfdd..61b1666716c 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -329,12 +329,12 @@ export class SearchWidget extends Widget { })); this.searchInputFocusTracker = this._register(dom.trackFocus(this.searchInput.inputBox.inputElement)); - this._register(this.searchInputFocusTracker.onDidFocus(() => { + this._register(this.searchInputFocusTracker.onDidFocus(async () => { this.searchInputBoxFocused.set(true); const useGlobalFindBuffer = this.searchConfiguration.globalFindClipboard; if (!this.ignoreGlobalFindBufferOnNextFocus && useGlobalFindBuffer) { - const globalBufferText = this.clipboardServce.readFindText(); + const globalBufferText = await this.clipboardServce.readFindText(); if (this.previousGlobalFindBufferValue !== globalBufferText) { this.searchInput.inputBox.addToHistory(); this.searchInput.setValue(globalBufferText); @@ -355,7 +355,7 @@ export class SearchWidget extends Widget { if (options.showContextToggle) { this.contextLinesInput = new InputBox(searchInputContainer, this.contextViewService, { type: 'number' }); dom.addClass(this.contextLinesInput.element, 'context-lines-input'); - this.contextLinesInput.value = '2'; + this.contextLinesInput.value = '' + (this.configurationService.getValue('search').searchEditor.defaultNumberOfContextLines ?? 1); this._register(this.contextLinesInput.onDidChange(() => this.onContextLinesChanged())); this._register(attachInputBoxStyler(this.contextLinesInput, this.themeService)); dom.append(searchInputContainer, this.showContextCheckbox.domNode); @@ -614,7 +614,7 @@ export class SearchWidget extends Widget { } } - private submitSearch(triggeredOnType = false, delay: number = 0): void { + private async submitSearch(triggeredOnType = false, delay: number = 0): Promise { this.searchInput.validate(); if (!this.searchInput.inputBox.isInputValid()) { return; @@ -623,7 +623,7 @@ export class SearchWidget extends Widget { const value = this.searchInput.getValue(); const useGlobalFindBuffer = this.searchConfiguration.globalFindClipboard; if (value && useGlobalFindBuffer) { - this.clipboardServce.writeFindText(value); + await this.clipboardServce.writeFindText(value); } this._onSearchSubmit.fire({ triggeredOnType, delay }); } diff --git a/src/vs/workbench/contrib/search/common/queryBuilder.ts b/src/vs/workbench/contrib/search/common/queryBuilder.ts index 3071811ee33..47885cbfad0 100644 --- a/src/vs/workbench/contrib/search/common/queryBuilder.ts +++ b/src/vs/workbench/contrib/search/common/queryBuilder.ts @@ -7,7 +7,6 @@ import * as arrays from 'vs/base/common/arrays'; import * as collections from 'vs/base/common/collections'; import * as glob from 'vs/base/common/glob'; import { untildify } from 'vs/base/common/labels'; -import { values } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; import * as path from 'vs/base/common/path'; import { isEqual } from 'vs/base/common/resources'; @@ -324,7 +323,7 @@ export class QueryBuilder { } }); - return values(searchPathPatternMap); + return Array.from(searchPathPatternMap.values()); } /** @@ -350,7 +349,7 @@ export class QueryBuilder { const workspaceUri = this.workspaceContextService.getWorkspace().folders[0].uri; searchPath = normalizeSlashes(searchPath); - if (strings.startsWith(searchPath, '../') || searchPath === '..') { + if (searchPath.startsWith('../') || searchPath === '..') { const resolvedPath = path.posix.resolve(workspaceUri.path, searchPath); return [{ searchPath: workspaceUri.with({ path: resolvedPath }) @@ -401,7 +400,7 @@ export class QueryBuilder { pattern }]; - if (pattern && !strings.endsWith(pattern, '**')) { + if (pattern && !pattern.endsWith('**')) { results.push({ searchPath: oneExpandedResult.searchPath, pattern: pattern + '/**' diff --git a/src/vs/workbench/contrib/search/common/replace.ts b/src/vs/workbench/contrib/search/common/replace.ts index d3d94df9371..8c1320c1f9c 100644 --- a/src/vs/workbench/contrib/search/common/replace.ts +++ b/src/vs/workbench/contrib/search/common/replace.ts @@ -11,7 +11,7 @@ export const IReplaceService = createDecorator('replaceService' export interface IReplaceService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Replaces the given match in the file that match belongs to diff --git a/src/vs/workbench/contrib/search/common/search.ts b/src/vs/workbench/contrib/search/common/search.ts index b63a4223a13..54a624656d2 100644 --- a/src/vs/workbench/contrib/search/common/search.ts +++ b/src/vs/workbench/contrib/search/common/search.ts @@ -96,7 +96,7 @@ export function getOutOfWorkspaceEditorResources(accessor: ServicesAccessor): UR const fileService = accessor.get(IFileService); const resources = editorService.editors - .map(editor => toResource(editor, { supportSideBySide: SideBySideEditor.MASTER })) + .map(editor => toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY })) .filter(resource => !!resource && !contextService.isInsideWorkspace(resource) && fileService.canHandleResource(resource)); return resources as URI[]; diff --git a/src/vs/workbench/contrib/search/common/searchHistoryService.ts b/src/vs/workbench/contrib/search/common/searchHistoryService.ts index 5f5496fb9d2..bb5a9209e51 100644 --- a/src/vs/workbench/contrib/search/common/searchHistoryService.ts +++ b/src/vs/workbench/contrib/search/common/searchHistoryService.ts @@ -9,7 +9,7 @@ import { isEmptyObject } from 'vs/base/common/types'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export interface ISearchHistoryService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; onDidClearHistory: Event; clearHistory(): void; load(): ISearchHistoryValues; @@ -26,7 +26,7 @@ export interface ISearchHistoryValues { } export class SearchHistoryService implements ISearchHistoryService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private static readonly SEARCH_HISTORY_KEY = 'workbench.search.history'; @@ -64,4 +64,4 @@ export class SearchHistoryService implements ISearchHistoryService { this.storageService.store(SearchHistoryService.SEARCH_HISTORY_KEY, JSON.stringify(history), StorageScope.WORKSPACE); } } -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index 9f90d468cf9..e5bbd712833 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -9,7 +9,7 @@ import * as errors from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { getBaseLabel } from 'vs/base/common/labels'; import { Disposable, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { ResourceMap, TernarySearchTree, values } from 'vs/base/common/map'; +import { ResourceMap, TernarySearchTree } from 'vs/base/common/map'; import * as objects from 'vs/base/common/objects'; import { lcut } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; @@ -293,7 +293,7 @@ export class FileMatch extends Disposable implements IFileMatch { endLineNumber: lineNumber, endColumn: this._model.getLineMaxColumn(lineNumber) }; - const oldMatches = values(this._matches).filter(match => match.range().startLineNumber === lineNumber); + const oldMatches = Array.from(this._matches.values()).filter(match => match.range().startLineNumber === lineNumber); oldMatches.forEach(match => this._matches.delete(match.id())); const wordSeparators = this._query.isWordMatch && this._query.wordSeparators ? this._query.wordSeparators : null; @@ -351,7 +351,7 @@ export class FileMatch extends Disposable implements IFileMatch { } matches(): Match[] { - return values(this._matches); + return Array.from(this._matches.values()); } remove(match: Match): void { @@ -605,7 +605,7 @@ export class FolderMatch extends Disposable { fileMatches = [fileMatches]; } - for (let match of fileMatches as FileMatch[]) { + for (const match of fileMatches as FileMatch[]) { this._fileMatches.delete(match.resource); if (dispose) { match.dispose(); @@ -1181,7 +1181,7 @@ export type RenderableMatch = FolderMatch | FolderMatchWithResource | FileMatch export class SearchWorkbenchService implements ISearchWorkbenchService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _searchModel: SearchModel | null = null; constructor(@IInstantiationService private readonly instantiationService: IInstantiationService) { @@ -1198,7 +1198,7 @@ export class SearchWorkbenchService implements ISearchWorkbenchService { export const ISearchWorkbenchService = createDecorator('searchWorkbenchService'); export interface ISearchWorkbenchService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly searchModel: SearchModel; } diff --git a/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts b/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts index 39499ce4639..363eab383c1 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts @@ -28,7 +28,7 @@ suite('Search - Viewlet', () => { }); test('Data Source', function () { - let result: SearchResult = instantiation.createInstance(SearchResult, null); + const result: SearchResult = instantiation.createInstance(SearchResult, null); result.query = { type: QueryType.Text, contentPattern: { pattern: 'foo' }, @@ -58,20 +58,20 @@ suite('Search - Viewlet', () => { }] }]); - let fileMatch = result.matches()[0]; - let lineMatch = fileMatch.matches()[0]; + const fileMatch = result.matches()[0]; + const lineMatch = fileMatch.matches()[0]; assert.equal(fileMatch.id(), 'file:///c%3A/foo'); assert.equal(lineMatch.id(), 'file:///c%3A/foo>[2,1 -> 2,2]b'); }); test('Comparer', () => { - let fileMatch1 = aFileMatch(isWindows ? 'C:\\foo' : '/c/foo'); - let fileMatch2 = aFileMatch(isWindows ? 'C:\\with\\path' : '/c/with/path'); - let fileMatch3 = aFileMatch(isWindows ? 'C:\\with\\path\\foo' : '/c/with/path/foo'); - let lineMatch1 = new Match(fileMatch1, ['bar'], new OneLineRange(0, 1, 1), new OneLineRange(0, 1, 1)); - let lineMatch2 = new Match(fileMatch1, ['bar'], new OneLineRange(0, 1, 1), new OneLineRange(2, 1, 1)); - let lineMatch3 = new Match(fileMatch1, ['bar'], new OneLineRange(0, 1, 1), new OneLineRange(2, 1, 1)); + const fileMatch1 = aFileMatch(isWindows ? 'C:\\foo' : '/c/foo'); + const fileMatch2 = aFileMatch(isWindows ? 'C:\\with\\path' : '/c/with/path'); + const fileMatch3 = aFileMatch(isWindows ? 'C:\\with\\path\\foo' : '/c/with/path/foo'); + const lineMatch1 = new Match(fileMatch1, ['bar'], new OneLineRange(0, 1, 1), new OneLineRange(0, 1, 1)); + const lineMatch2 = new Match(fileMatch1, ['bar'], new OneLineRange(0, 1, 1), new OneLineRange(2, 1, 1)); + const lineMatch3 = new Match(fileMatch1, ['bar'], new OneLineRange(0, 1, 1), new OneLineRange(2, 1, 1)); assert(searchMatchComparer(fileMatch1, fileMatch2) < 0); assert(searchMatchComparer(fileMatch2, fileMatch1) > 0); @@ -84,10 +84,10 @@ suite('Search - Viewlet', () => { }); test('Advanced Comparer', () => { - let fileMatch1 = aFileMatch(isWindows ? 'C:\\with\\path\\foo10' : '/c/with/path/foo10'); - let fileMatch2 = aFileMatch(isWindows ? 'C:\\with\\path2\\foo1' : '/c/with/path2/foo1'); - let fileMatch3 = aFileMatch(isWindows ? 'C:\\with\\path2\\bar.a' : '/c/with/path2/bar.a'); - let fileMatch4 = aFileMatch(isWindows ? 'C:\\with\\path2\\bar.b' : '/c/with/path2/bar.b'); + const fileMatch1 = aFileMatch(isWindows ? 'C:\\with\\path\\foo10' : '/c/with/path/foo10'); + const fileMatch2 = aFileMatch(isWindows ? 'C:\\with\\path2\\foo1' : '/c/with/path2/foo1'); + const fileMatch3 = aFileMatch(isWindows ? 'C:\\with\\path2\\bar.a' : '/c/with/path2/bar.a'); + const fileMatch4 = aFileMatch(isWindows ? 'C:\\with\\path2\\bar.b' : '/c/with/path2/bar.b'); // By default, path < path2 assert(searchMatchComparer(fileMatch1, fileMatch2) < 0); @@ -98,7 +98,7 @@ suite('Search - Viewlet', () => { }); function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ITextSearchMatch[]): FileMatch { - let rawMatch: IFileMatch = { + const rawMatch: IFileMatch = { resource: uri.file(path), results: lineMatches }; diff --git a/src/vs/workbench/contrib/search/test/common/extractRange.test.ts b/src/vs/workbench/contrib/search/test/common/extractRange.test.ts index bd96b29baa4..a60f8eea965 100644 --- a/src/vs/workbench/contrib/search/test/common/extractRange.test.ts +++ b/src/vs/workbench/contrib/search/test/common/extractRange.test.ts @@ -36,7 +36,7 @@ suite('extractRangeFromFilter', () => { }); test('allow space after path', async function () { - let res = extractRangeFromFilter('/some/path/file.txt (19,20)'); + const res = extractRangeFromFilter('/some/path/file.txt (19,20)'); assert.equal(res?.filter, '/some/path/file.txt'); assert.equal(res?.range.startLineNumber, 19); @@ -44,7 +44,7 @@ suite('extractRangeFromFilter', () => { }); test('unless', async function () { - let res = extractRangeFromFilter('/some/path/file.txt@ (19,20)', ['@']); + const res = extractRangeFromFilter('/some/path/file.txt@ (19,20)', ['@']); assert.ok(!res); }); diff --git a/src/vs/workbench/contrib/searchEditor/browser/constants.ts b/src/vs/workbench/contrib/searchEditor/browser/constants.ts index cc24ea515f6..b37c41a7a2d 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/constants.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/constants.ts @@ -14,3 +14,4 @@ export const SearchEditorFindMatchClass = 'seaarchEditorFindMatch'; export const SearchEditorID = 'workbench.editor.searchEditor'; export const OpenNewEditorCommandId = 'search.action.openNewEditor'; +export const OpenEditorCommandId = 'search.action.openEditor'; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts index d3c445104db..711a8b6a5d9 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts @@ -5,7 +5,7 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import * as objects from 'vs/base/common/objects'; -import { endsWith } from 'vs/base/common/strings'; +import { extname } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { Range } from 'vs/editor/common/core/range'; @@ -30,7 +30,7 @@ import * as SearchConstants from 'vs/workbench/contrib/search/common/constants'; import * as SearchEditorConstants from 'vs/workbench/contrib/searchEditor/browser/constants'; import { SearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditor'; import { createEditorFromSearchResult, modifySearchEditorContextLinesCommand, openNewSearchEditor, selectAllSearchEditorMatchesCommand, toggleSearchEditorCaseSensitiveCommand, toggleSearchEditorContextLinesCommand, toggleSearchEditorRegexCommand, toggleSearchEditorWholeWordCommand } from 'vs/workbench/contrib/searchEditor/browser/searchEditorActions'; -import { getOrMakeSearchEditorInput, SearchConfiguration, SearchEditorInput } from 'vs/workbench/contrib/searchEditor/browser/searchEditorInput'; +import { getOrMakeSearchEditorInput, SearchConfiguration, SearchEditorInput, SEARCH_EDITOR_EXT } from 'vs/workbench/contrib/searchEditor/browser/searchEditorInput'; import { parseSavedSearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditorSerialization'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -79,11 +79,11 @@ class SearchEditorContribution implements IWorkbenchContribution { const resource = editor.resource; if (!resource) { return undefined; } - if (!endsWith(resource.path, '.code-search')) { + if (extname(resource) !== SEARCH_EDITOR_EXT) { return undefined; } - if (group.isOpened(editor) && editor instanceof SearchEditorInput) { + if (editor instanceof SearchEditorInput && group.isOpened(editor)) { return undefined; } @@ -93,7 +93,7 @@ class SearchEditorContribution implements IWorkbenchContribution { override: (async () => { const { config } = await instantiationService.invokeFunction(parseSavedSearchEditor, resource); const input = instantiationService.invokeFunction(getOrMakeSearchEditorInput, { backingUri: resource, config }); - return editorService.openEditor(input, { ...options, ignoreOverrides: true }, group); + return editorService.openEditor(input, { ...options, override: false }, group); })() }; } @@ -109,7 +109,9 @@ workbenchContributionsRegistry.registerWorkbenchContribution(SearchEditorContrib type SerializedSearchEditor = { modelUri: string, dirty: boolean, config: SearchConfiguration, name: string, matchRanges: Range[], backingUri: string }; class SearchEditorInputFactory implements IEditorInputFactory { - canSerialize() { return true; } + canSerialize(input: SearchEditorInput) { + return !input.isDisposed(); + } serialize(input: SearchEditorInput) { let modelUri = undefined; @@ -209,8 +211,9 @@ CommandsRegistry.registerCommand( //#endregion //#region Actions -const category = localize('search', "Search Editor"); +const category = { value: localize('search', "Search Editor"), original: 'Search Editor' }; +export type OpenSearchEditorArgs = Partial; const openArgDescription = { description: 'Open a new search editor. Arguments passed can include variables like ${relativeFileDirname}.', args: [{ @@ -226,23 +229,64 @@ const openArgDescription = { regexp: { type: 'boolean' }, useIgnores: { type: 'boolean' }, showIncludesExcludes: { type: 'boolean' }, + triggerSearch: { type: 'boolean' }, + focusResults: { type: 'boolean' }, } } }] } as const; +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'search.searchEditor.action.deleteFileResults', + title: { value: localize('searchEditor.deleteResultBlock', "Delete File Results"), original: 'Delete File Results' }, + keybinding: { + weight: KeybindingWeight.EditorContrib, + when: SearchEditorConstants.InSearchEditor, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Backspace, + }, + precondition: SearchEditorConstants.InSearchEditor, + category, + f1: true, + }); + } + + async run(accessor: ServicesAccessor) { + const contextService = accessor.get(IContextKeyService).getContext(document.activeElement); + if (contextService.getValue(SearchEditorConstants.InSearchEditor.serialize())) { + (accessor.get(IEditorService).activeEditorPane as SearchEditor).deleteResultBlock(); + } + } +}); + registerAction2(class extends Action2 { constructor() { super({ id: SearchEditorConstants.OpenNewEditorCommandId, - title: localize('search.openNewSearchEditor', "Open new Search Editor"), + title: { value: localize('search.openNewSearchEditor', "New Search Editor"), original: 'New Search Editor' }, category, f1: true, description: openArgDescription }); } - async run(accessor: ServicesAccessor, args: Partial) { - await accessor.get(IInstantiationService).invokeFunction(openNewSearchEditor, args); + async run(accessor: ServicesAccessor, args: OpenSearchEditorArgs) { + await accessor.get(IInstantiationService).invokeFunction(openNewSearchEditor, { ...args, location: 'new' }); + } +}); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: SearchEditorConstants.OpenEditorCommandId, + title: { value: localize('search.openSearchEditor', "Open Search Editor"), original: 'Open Search Editor' }, + category, + f1: true, + description: openArgDescription + }); + } + async run(accessor: ServicesAccessor, args: OpenSearchEditorArgs) { + await accessor.get(IInstantiationService).invokeFunction(openNewSearchEditor, { ...args, location: 'reuse' }); } }); @@ -250,13 +294,13 @@ registerAction2(class extends Action2 { constructor() { super({ id: OpenNewEditorToSideCommandId, - title: localize('search.openNewEditorToSide', "Open new Search Editor to the Side"), + title: { value: localize('search.openNewEditorToSide', "Open new Search Editor to the Side"), original: 'Open new Search Editor to the Side' }, category, f1: true, description: openArgDescription }); } - async run(accessor: ServicesAccessor, args: Partial) { + async run(accessor: ServicesAccessor, args: OpenSearchEditorArgs) { await accessor.get(IInstantiationService).invokeFunction(openNewSearchEditor, args, true); } }); @@ -265,13 +309,16 @@ registerAction2(class extends Action2 { constructor() { super({ id: OpenInEditorCommandId, - title: localize('search.openResultsInEditor', "Open Results in Editor"), + title: { value: localize('search.openResultsInEditor', "Open Results in Editor"), original: 'Open Results in Editor' }, category, f1: true, keybinding: { - primary: KeyMod.CtrlCmd | KeyCode.Enter, + primary: KeyMod.Alt | KeyCode.Enter, when: ContextKeyExpr.and(SearchConstants.HasSearchResults, SearchConstants.SearchViewFocusedKey), - weight: KeybindingWeight.WorkbenchContrib + weight: KeybindingWeight.WorkbenchContrib, + mac: { + primary: KeyMod.CtrlCmd | KeyCode.Enter + } }, }); } @@ -289,7 +336,7 @@ registerAction2(class extends Action2 { constructor() { super({ id: RerunSearchEditorSearchCommandId, - title: localize('search.rerunSearchInEditor', "Search Again"), + title: { value: localize('search.rerunSearchInEditor', "Search Again"), original: 'Search Again' }, category, keybinding: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_R, @@ -321,7 +368,7 @@ registerAction2(class extends Action2 { constructor() { super({ id: FocusQueryEditorWidgetCommandId, - title: localize('search.action.focusQueryEditorWidget', "Focus Search Editor Input"), + title: { value: localize('search.action.focusQueryEditorWidget', "Focus Search Editor Input"), original: 'Focus Search Editor Input' }, category, menu: { id: MenuId.CommandPalette, diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index 722808a599d..ae1538b3ec8 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -90,7 +90,7 @@ export class SearchEditor extends BaseTextEditor { @IModelService private readonly modelService: IModelService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @ILabelService private readonly labelService: ILabelService, - @IInstantiationService readonly instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IContextViewService private readonly contextViewService: IContextViewService, @ICommandService private readonly commandService: ICommandService, @IContextKeyService readonly contextKeyService: IContextKeyService, @@ -389,7 +389,7 @@ export class SearchEditor extends BaseTextEditor { const matchText = model.getValueInRange(matchRange); let file = ''; for (let line = matchRange.startLineNumber; line >= 1; line--) { - let lineText = model.getValueInRange(new Range(line, 1, line, 2)); + const lineText = model.getValueInRange(new Range(line, 1, line, 2)); if (lineText !== ' ') { file = model.getLineContent(line); break; } } alert(localize('searchResultItem', "Matched {0} at {1} in file {2}", matchText, matchLineText, file.slice(0, file.length - 1))); @@ -410,7 +410,7 @@ export class SearchEditor extends BaseTextEditor { this.searchResultEditor.focus(); } - async triggerSearch(_options?: { resetCursor?: boolean; delay?: number; }) { + async triggerSearch(_options?: { resetCursor?: boolean; delay?: number; focusResults?: boolean }) { const options = { resetCursor: true, delay: 0, ..._options }; if (!this.pauseSearching) { @@ -421,6 +421,9 @@ export class SearchEditor extends BaseTextEditor { this.searchResultEditor.setPosition(new Position(1, 1)); this.searchResultEditor.setScrollPosition({ scrollTop: 0, scrollLeft: 0 }); } + if (options.focusResults) { + this.searchResultEditor.focus(); + } }, options.delay); } } @@ -498,10 +501,11 @@ export class SearchEditor extends BaseTextEditor { return; } + const sortOrder = this.configurationService.getValue('search').sortOrder; const controller = ReferencesController.get(this.searchResultEditor); controller.closeWidget(false); const labelFormatter = (uri: URI): string => this.labelService.getUriLabel(uri, { relative: true }); - const results = serializeSearchResultForEditor(this.searchModel.searchResult, config.includes, config.excludes, config.contextLines, labelFormatter); + const results = serializeSearchResultForEditor(this.searchModel.searchResult, config.includes, config.excludes, config.contextLines, labelFormatter, sortOrder); const { body } = await input.getModels(); this.modelService.updateModel(body, results.text); input.config = config; @@ -540,8 +544,10 @@ export class SearchEditor extends BaseTextEditor { this.saveViewState(); await super.setInput(newInput, options, token); + if (token.isCancellationRequested) { return; } const { body, config } = await newInput.getModels(); + if (token.isCancellationRequested) { return; } this.searchResultEditor.setModel(body); this.pauseSearching = true; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts index 19acdf21da1..0863b0b1c71 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts @@ -14,7 +14,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { SearchResult } from 'vs/workbench/contrib/search/common/searchModel'; import { SearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditor'; -import { getOrMakeSearchEditorInput, SearchEditorInput, SearchConfiguration } from 'vs/workbench/contrib/searchEditor/browser/searchEditorInput'; +import { getOrMakeSearchEditorInput, SearchEditorInput } from 'vs/workbench/contrib/searchEditor/browser/searchEditorInput'; import { serializeSearchResultForEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditorSerialization'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { ISearchConfigurationProperties } from 'vs/workbench/services/search/common/search'; @@ -23,8 +23,11 @@ import { IConfigurationResolverService } from 'vs/workbench/services/configurati import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { Schemas } from 'vs/base/common/network'; -import { withNullAsUndefined } from 'vs/base/common/types'; +import { withNullAsUndefined, assertIsDefined } from 'vs/base/common/types'; import { OpenNewEditorCommandId } from 'vs/workbench/contrib/searchEditor/browser/constants'; +import { OpenSearchEditorArgs } from 'vs/workbench/contrib/searchEditor/browser/searchEditor.contribution'; +import { EditorsOrder } from 'vs/workbench/common/editor'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; export const toggleSearchEditorCaseSensitiveCommand = (accessor: ServicesAccessor) => { const editorService = accessor.get(IEditorService); @@ -99,8 +102,9 @@ export class OpenSearchEditorAction extends Action { } export const openNewSearchEditor = - async (accessor: ServicesAccessor, args: Partial = {}, toSide = false) => { + async (accessor: ServicesAccessor, _args: OpenSearchEditorArgs = {}, toSide = false) => { const editorService = accessor.get(IEditorService); + const editorGroupsService = accessor.get(IEditorGroupsService); const telemetryService = accessor.get(ITelemetryService); const instantiationService = accessor.get(IInstantiationService); const configurationService = accessor.get(IConfigurationService); @@ -111,11 +115,6 @@ export const openNewSearchEditor = const activeWorkspaceRootUri = historyService.getLastActiveWorkspaceRoot(Schemas.file); const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? withNullAsUndefined(workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined; - const resolvedArgs: Record = {}; - Object.entries(args).forEach(([name, value]) => { - resolvedArgs[name as any] = (typeof value === 'string') ? configurationResolverService.resolve(lastActiveWorkspaceRoot, value) : value; - }); - const activeEditorControl = editorService.activeTextEditorControl; let activeModel: ICodeEditor | undefined; let selected = ''; @@ -140,11 +139,27 @@ export const openNewSearchEditor = telemetryService.publicLog2('searchEditor/openNewSearchEditor'); - const input = instantiationService.invokeFunction(getOrMakeSearchEditorInput, { config: { query: selected, ...resolvedArgs }, text: '' }); - const editor = await editorService.openEditor(input, { pinned: true }, toSide ? SIDE_GROUP : ACTIVE_GROUP) as SearchEditor; + const args: OpenSearchEditorArgs = { query: selected }; + Object.entries(_args).forEach(([name, value]) => { + (args as any)[name as any] = (typeof value === 'string') ? configurationResolverService.resolve(lastActiveWorkspaceRoot, value) : value; + }); + const existing = editorService.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE).find(id => id.editor.getTypeId() === SearchEditorInput.ID); + let editor: SearchEditor; + if (existing && args.location === 'reuse') { + const input = existing.editor as SearchEditorInput; + editor = assertIsDefined(await assertIsDefined(editorGroupsService.getGroup(existing.groupId)).openEditor(input)) as SearchEditor; + editor.focusSearchInput(); + } else { + const input = instantiationService.invokeFunction(getOrMakeSearchEditorInput, { config: args, text: '' }); + editor = await editorService.openEditor(input, { pinned: true }, toSide ? SIDE_GROUP : ACTIVE_GROUP) as SearchEditor; + } - if (selected && configurationService.getValue('search').searchOnType) { - editor.triggerSearch(); + const searchOnType = configurationService.getValue('search').searchOnType; + if ( + args.triggerSearch === true || + args.triggerSearch !== false && searchOnType && args.query + ) { + editor.triggerSearch({ focusResults: args.focusResults !== false }); } }; @@ -159,15 +174,24 @@ export const createEditorFromSearchResult = const telemetryService = accessor.get(ITelemetryService); const instantiationService = accessor.get(IInstantiationService); const labelService = accessor.get(ILabelService); + const configurationService = accessor.get(IConfigurationService); + const sortOrder = configurationService.getValue('search').sortOrder; telemetryService.publicLog2('searchEditor/createEditorFromSearchResult'); const labelFormatter = (uri: URI): string => labelService.getUriLabel(uri, { relative: true }); - const { text, matchRanges, config } = serializeSearchResultForEditor(searchResult, rawIncludePattern, rawExcludePattern, 0, labelFormatter); + const { text, matchRanges, config } = serializeSearchResultForEditor(searchResult, rawIncludePattern, rawExcludePattern, 0, labelFormatter, sortOrder); + const contextLines = configurationService.getValue('search').searchEditor.defaultNumberOfContextLines; - const input = instantiationService.invokeFunction(getOrMakeSearchEditorInput, { text, config }); - await editorService.openEditor(input, { pinned: true }); - input.setMatchRanges(matchRanges); + if (searchResult.isDirty || contextLines === 0 || contextLines === null) { + const input = instantiationService.invokeFunction(getOrMakeSearchEditorInput, { text, config }); + await editorService.openEditor(input, { pinned: true }); + input.setMatchRanges(matchRanges); + } else { + const input = instantiationService.invokeFunction(getOrMakeSearchEditorInput, { text: '', config: { ...config, contextLines } }); + const editor = await editorService.openEditor(input, { pinned: true }) as SearchEditor; + editor.triggerSearch({ focusResults: true }); + } }; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts index b2d8dc6834c..c884a84be2b 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Registry } from 'vs/platform/registry/common/platform'; import { Emitter, Event } from 'vs/base/common/event'; import * as network from 'vs/base/common/network'; import { basename } from 'vs/base/common/path'; @@ -10,29 +11,26 @@ import { extname, isEqual, joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/searchEditor'; import { Range } from 'vs/editor/common/core/range'; -import { DefaultEndOfLine, ITextModel, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { ITextModel, TrackedRangeStickiness } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { IModeService } from 'vs/editor/common/services/modeService'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { EditorInput, GroupIdentifier, IEditorInput, IMoveResult, IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor'; +import { EditorInput, GroupIdentifier, IEditorInput, IMoveResult, IRevertOptions, ISaveOptions, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor'; import { Memento } from 'vs/workbench/common/memento'; -import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { SearchEditorFindMatchClass, SearchEditorScheme } from 'vs/workbench/contrib/searchEditor/browser/constants'; +import { SearchEditorModel } from 'vs/workbench/contrib/searchEditor/browser/searchEditorModel'; import { defaultSearchConfig, extractSearchQueryFromModel, parseSavedSearchEditor, serializeSearchConfiguration } from 'vs/workbench/contrib/searchEditor/browser/searchEditorSerialization'; -import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { AutoSaveMode, IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { ISearchConfigurationProperties } from 'vs/workbench/services/search/common/search'; -import { ITextFileSaveOptions, ITextFileService, snapshotToString, stringToSnapshot } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileSaveOptions, ITextFileService, stringToSnapshot } from 'vs/workbench/services/textfile/common/textfiles'; import { IWorkingCopy, IWorkingCopyBackup, IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; - export type SearchConfiguration = { query: string, includes: string, @@ -45,7 +43,7 @@ export type SearchConfiguration = { showIncludesExcludes: boolean, }; -const SEARCH_EDITOR_EXT = '.code-search'; +export const SEARCH_EDITOR_EXT = '.code-search'; export class SearchEditorInput extends EditorInput { static readonly ID: string = 'workbench.editorinputs.searchEditorInput'; @@ -53,7 +51,10 @@ export class SearchEditorInput extends EditorInput { private memento: Memento; private dirty: boolean = false; - private model: Promise; + private get model(): Promise { + return this.searchEditorModel.resolve(); + } + private _cachedModel: ITextModel | undefined; private readonly _onDidChangeContent = this._register(new Emitter()); @@ -69,6 +70,8 @@ export class SearchEditorInput extends EditorInput { this._onDidChangeLabel.fire(); } + private readonly fileEditorInputFactory = Registry.as(EditorInputExtensions.EditorInputFactories).getFileEditorInputFactory(); + get resource() { return this.backingUri || this.modelUri; } @@ -76,8 +79,7 @@ export class SearchEditorInput extends EditorInput { constructor( public readonly modelUri: URI, public readonly backingUri: URI | undefined, - config: Readonly, - getModel: () => Promise, + private searchEditorModel: SearchEditorModel, @IModelService private readonly modelService: IModelService, @ITextFileService protected readonly textFileService: ITextFileService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @@ -91,13 +93,12 @@ export class SearchEditorInput extends EditorInput { ) { super(); - this._config = config; - this.model = getModel() + this._config = searchEditorModel.config; + searchEditorModel.onModelResolved .then(model => { this._register(model.onDidChangeContent(() => this._onDidChangeContent.fire())); this._register(model); this._cachedModel = model; - return model; }); if (this.modelUri.scheme !== SearchEditorScheme) { @@ -179,10 +180,6 @@ export class SearchEditorInput extends EditorInput { return localize('searchTitle', "Search"); } - async resolve() { - return null; - } - setDirty(dirty: boolean) { this.dirty = dirty; this._onDidChangeDirty.fire(); @@ -216,7 +213,7 @@ export class SearchEditorInput extends EditorInput { return !this.backingUri; } - move(group: GroupIdentifier, target: URI): IMoveResult | undefined { + rename(group: GroupIdentifier, target: URI): IMoveResult | undefined { if (this._cachedModel && extname(target) === SEARCH_EDITOR_EXT) { return { editor: this.instantiationService.invokeFunction(getOrMakeSearchEditorInput, { config: this.config, text: this._cachedModel.getValue(), backingUri: target }) @@ -236,7 +233,7 @@ export class SearchEditorInput extends EditorInput { if (other instanceof SearchEditorInput) { return !!(other.modelUri.fragment && other.modelUri.fragment === this.modelUri.fragment); - } else if (other instanceof FileEditorInput) { + } else if (this.fileEditorInputFactory.isFileEditorInput(other)) { return other.resource?.toString() === this.backingUri?.toString(); } return false; @@ -250,12 +247,11 @@ export class SearchEditorInput extends EditorInput { } async setMatchRanges(ranges: Range[]) { - this.oldDecorationsIDs = (await this.model).deltaDecorations(this.oldDecorationsIDs, ranges.map(range => + this.oldDecorationsIDs = (await this.searchEditorModel.onModelResolved).deltaDecorations(this.oldDecorationsIDs, ranges.map(range => ({ range, options: { className: SearchEditorFindMatchClass, stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); } async revert(group: GroupIdentifier, options?: IRevertOptions) { - // TODO: this should actually revert the contents. But it needs to set dirty false. if (this.backingUri) { const { config, text } = await this.instantiationService.invokeFunction(parseSavedSearchEditor, this.backingUri); (await this.model).setValue(text); @@ -284,7 +280,7 @@ export class SearchEditorInput extends EditorInput { const remoteAuthority = this.environmentService.configuration.remoteAuthority; const schemeFilter = remoteAuthority ? network.Schemas.vscodeRemote : network.Schemas.file; - return joinPath(this.fileDialogService.defaultFilePath(schemeFilter) || (await this.pathService.userHome), searchFileName); + return joinPath(this.fileDialogService.defaultFilePath(schemeFilter) || (await this.pathService.userHome()), searchFileName); } } @@ -298,16 +294,26 @@ export const getOrMakeSearchEditorInput = ( ): SearchEditorInput => { const instantiationService = accessor.get(IInstantiationService); - const modelService = accessor.get(IModelService); - const backupService = accessor.get(IBackupFileService); - const modeService = accessor.get(IModeService); const storageService = accessor.get(IStorageService); const configurationService = accessor.get(IConfigurationService); - const reuseOldSettings = configurationService.getValue('search').searchEditor?.reusePriorSearchConfiguration; + const searchEditorSettings = configurationService.getValue('search').searchEditor; + + const reuseOldSettings = searchEditorSettings.reusePriorSearchConfiguration; + const defaultNumberOfContextLines = searchEditorSettings.defaultNumberOfContextLines; + const priorConfig: SearchConfiguration = reuseOldSettings ? new Memento(SearchEditorInput.ID, storageService).getMemento(StorageScope.WORKSPACE).searchConfig : {}; const defaultConfig = defaultSearchConfig(); - let config = { ...defaultConfig, ...priorConfig, ...existingData.config }; + + const config = { ...defaultConfig, ...priorConfig, ...existingData.config }; + + if (defaultNumberOfContextLines !== null && defaultNumberOfContextLines !== undefined) { + config.contextLines = existingData.config.contextLines ?? defaultNumberOfContextLines; + } + + if (existingData.text) { + config.contextLines = 0; + } const modelUri = existingData.modelUri ?? URI.from({ scheme: SearchEditorScheme, fragment: `${Math.random()}` }); @@ -317,29 +323,8 @@ export const getOrMakeSearchEditorInput = ( return existing; } - const getModel = async () => { - let contents: string; - - const backup = await backupService.resolve(modelUri); - if (backup) { - // this way of stringifying a TextBufferFactory seems needlessly complicated... - contents = snapshotToString(backup.value.create(DefaultEndOfLine.LF).createSnapshot(true)); - } else if (existingData.text !== undefined) { - contents = existingData.text; - } else if (existingData.backingUri !== undefined) { - const { text } = await instantiationService.invokeFunction(parseSavedSearchEditor, existingData.backingUri); - contents = text; - } else if (config !== undefined) { - contents = ''; - } else { - throw new Error('no initial contents for search editor'); - } - backupService.discardBackup(modelUri); - - return modelService.getModel(modelUri) ?? modelService.createModel(contents, modeService.create('search-result'), modelUri); - }; - - const input = instantiationService.createInstance(SearchEditorInput, modelUri, existingData.backingUri, config, getModel); + const model = instantiationService.createInstance(SearchEditorModel, modelUri, config, existingData); + const input = instantiationService.createInstance(SearchEditorInput, modelUri, existingData.backingUri, model); inputs.set(cacheKey, input); input.onDispose(() => inputs.delete(cacheKey)); diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorModel.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorModel.ts new file mode 100644 index 00000000000..fd868994e08 --- /dev/null +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorModel.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 { URI } from 'vs/base/common/uri'; +import { ITextModel } from 'vs/editor/common/model'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { parseSavedSearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditorSerialization'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { SearchConfiguration } from './searchEditorInput'; +import { assertIsDefined } from 'vs/base/common/types'; + + +export class SearchEditorModel { + private cachedContentsModel: ITextModel | undefined = undefined; + private resolveContents!: (model: ITextModel) => void; + public onModelResolved: Promise; + + private ongoingResolve = Promise.resolve(undefined); + + constructor( + private modelUri: URI, + public config: SearchConfiguration, + private existingData: ({ config: Partial; backingUri?: URI; } & + ({ modelUri: URI; text?: never; } | + { text: string; modelUri?: never; } | + { backingUri: URI; text?: never; modelUri?: never; })), + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IBackupFileService readonly backupService: IBackupFileService, + @IModelService private readonly modelService: IModelService, + @IModeService private readonly modeService: IModeService) { + this.onModelResolved = new Promise(resolve => this.resolveContents = resolve); + this.onModelResolved.then(model => this.cachedContentsModel = model); + this.ongoingResolve = backupService.resolve(modelUri) + .then(backup => modelService.getModel(modelUri) ?? (backup ? modelService.createModel(backup.value, modeService.create('search-result'), modelUri) : undefined)) + .then(model => { if (model) { this.resolveContents(model); } }); + } + + async resolve(): Promise { + await (this.ongoingResolve = this.ongoingResolve.then(() => this.cachedContentsModel || this.createModel())); + return assertIsDefined(this.cachedContentsModel); + } + + private async createModel() { + const getContents = async () => { + if (this.existingData.text !== undefined) { + return this.existingData.text; + } + else if (this.existingData.backingUri !== undefined) { + return (await this.instantiationService.invokeFunction(parseSavedSearchEditor, this.existingData.backingUri)).text; + } + else { + return ''; + } + }; + + const contents = await getContents(); + const model = this.modelService.getModel(this.modelUri) ?? this.modelService.createModel(contents, this.modeService.create('search-result'), this.modelUri); + this.resolveContents(model); + return model; + } +} diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts index eccc82f3e84..1bb2abc19ab 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts @@ -11,9 +11,9 @@ import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { Range } from 'vs/editor/common/core/range'; import type { ITextModel } from 'vs/editor/common/model'; import { localize } from 'vs/nls'; -import { FileMatch, Match, searchMatchComparer, SearchResult } from 'vs/workbench/contrib/search/common/searchModel'; +import { FileMatch, Match, searchMatchComparer, SearchResult, FolderMatch } from 'vs/workbench/contrib/search/common/searchModel'; import type { SearchConfiguration } from 'vs/workbench/contrib/searchEditor/browser/searchEditorInput'; -import { ITextQuery } from 'vs/workbench/services/search/common/search'; +import { ITextQuery, SearchSortOrder } from 'vs/workbench/services/search/common/search'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; // Using \r\n on Windows inserts an extra newline between results. @@ -66,8 +66,8 @@ function fileMatchToSearchResultFormat(fileMatch: FileMatch, labelFormatter: (x: const serializedMatches = flatten(sortedMatches.map(match => matchToSearchResultFormat(match, longestLineNumber))); const uriString = labelFormatter(fileMatch.resource); - let text: string[] = [`${uriString}:`]; - let matchRanges: Range[] = []; + const text: string[] = [`${uriString}:`]; + const matchRanges: Range[] = []; const targetLineNumberToOffset: Record = {}; @@ -208,7 +208,7 @@ export const extractSearchQueryFromLines = (lines: string[]): SearchConfiguratio }; export const serializeSearchResultForEditor = - (searchResult: SearchResult, rawIncludePattern: string, rawExcludePattern: string, contextLines: number, labelFormatter: (x: URI) => string): { matchRanges: Range[], text: string, config: Partial } => { + (searchResult: SearchResult, rawIncludePattern: string, rawExcludePattern: string, contextLines: number, labelFormatter: (x: URI) => string, sortOrder: SearchSortOrder): { matchRanges: Range[], text: string, config: Partial } => { if (!searchResult.query) { throw Error('Internal Error: Expected query, got null'); } const config = contentPatternToSearchConfiguration(searchResult.query, rawIncludePattern, rawExcludePattern, contextLines); @@ -221,11 +221,13 @@ export const serializeSearchResultForEditor = : localize('noResults', "No Results"), '']; + const matchComparer = (a: FileMatch | FolderMatch, b: FileMatch | FolderMatch) => searchMatchComparer(a, b, sortOrder); + const allResults = flattenSearchResultSerializations( flatten( - searchResult.folderMatches().sort(searchMatchComparer) - .map(folderMatch => folderMatch.matches().sort(searchMatchComparer) + searchResult.folderMatches().sort(matchComparer) + .map(folderMatch => folderMatch.matches().sort(matchComparer) .map(fileMatch => fileMatchToSearchResultFormat(fileMatch, labelFormatter))))); return { @@ -236,8 +238,8 @@ export const serializeSearchResultForEditor = }; const flattenSearchResultSerializations = (serializations: SearchResultSerialization[]): SearchResultSerialization => { - let text: string[] = []; - let matchRanges: Range[] = []; + const text: string[] = []; + const matchRanges: Range[] = []; serializations.forEach(serialized => { serialized.matchRanges.map(translateRangeLines(text.length)).forEach(range => matchRanges.push(range)); diff --git a/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts b/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts index e15578264ec..5b5c430ac30 100644 --- a/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts +++ b/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts @@ -80,20 +80,21 @@ class InsertSnippetAction extends EditorAction { }); } - run(accessor: ServicesAccessor, editor: ICodeEditor, arg: any): Promise | undefined { + async run(accessor: ServicesAccessor, editor: ICodeEditor, arg: any): Promise { const modeService = accessor.get(IModeService); const snippetService = accessor.get(ISnippetsService); if (!editor.hasModel()) { - return undefined; + return; } const clipboardService = accessor.get(IClipboardService); const quickInputService = accessor.get(IQuickInputService); - const { lineNumber, column } = editor.getPosition(); - let { snippet, name, langId } = Args.fromUser(arg); - return new Promise(async (resolve, reject) => { + const snippet = await new Promise(async (resolve, reject) => { + + const { lineNumber, column } = editor.getPosition(); + let { snippet, name, langId } = Args.fromUser(arg); if (snippet) { return resolve(new Snippet( @@ -167,16 +168,16 @@ class InsertSnippetAction extends EditorAction { } return quickInputService.pick(picks, { matchOnDetail: true }).then(pick => resolve(pick && pick.snippet), reject); } - }).then(async snippet => { - if (!snippet) { - return; - } - let clipboardText: string | undefined; - if (snippet.needsClipboard) { - clipboardText = await clipboardService.readText(); - } - SnippetController2.get(editor).insert(snippet.codeSnippet, { clipboardText }); }); + + if (!snippet) { + return; + } + let clipboardText: string | undefined; + if (snippet.needsClipboard) { + clipboardText = await clipboardService.readText(); + } + SnippetController2.get(editor).insert(snippet.codeSnippet, { clipboardText }); } } diff --git a/src/vs/workbench/contrib/snippets/browser/snippets.contribution.ts b/src/vs/workbench/contrib/snippets/browser/snippets.contribution.ts index 6c197f94cac..c7adf5c4fcc 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippets.contribution.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippets.contribution.ts @@ -15,7 +15,7 @@ export const ISnippetsService = createDecorator('snippetServic export interface ISnippetsService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; getSnippetFiles(): Promise>; @@ -28,7 +28,7 @@ const languageScopeSchemaId = 'vscode://schemas/snippets'; const snippetSchemaProperties: IJSONSchemaMap = { prefix: { - description: nls.localize('snippetSchema.json.prefix', 'The prefix to used when selecting the snippet in intellisense'), + description: nls.localize('snippetSchema.json.prefix', 'The prefix to use when selecting the snippet in intellisense'), type: ['string', 'array'] }, body: { diff --git a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts index c0eada3f0a3..8e79fd75fcf 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts @@ -174,12 +174,12 @@ class SnippetsService implements ISnippetsService { const languageIdentifier = this._modeService.getLanguageIdentifier(languageId); if (languageIdentifier) { const langName = languageIdentifier.language; - this._files.forEach(file => { + for (const file of this._files.values()) { promises.push(file.load() .then(file => file.select(langName, result)) .catch(err => this._logService.error(err, file.location.toString())) ); - }); + } } return Promise.all(promises).then(() => result); }); @@ -190,12 +190,12 @@ class SnippetsService implements ISnippetsService { const languageIdentifier = this._modeService.getLanguageIdentifier(languageId); if (languageIdentifier) { const langName = languageIdentifier.language; - this._files.forEach(file => { + for (const file of this._files.values()) { // kick off loading (which is a noop in case it's already loaded) // and optimistically collect snippets file.load().catch(err => { /*ignore*/ }); file.select(langName, result); - }); + } } return result; } @@ -205,11 +205,11 @@ class SnippetsService implements ISnippetsService { private _initExtensionSnippets(): void { snippetExt.point.setHandler(extensions => { - this._files.forEach((value, key) => { + for (let [key, value] of this._files) { if (value.source === SnippetSource.Extension) { this._files.delete(key); } - }); + } for (const extension of extensions) { for (const contribution of extension.value) { diff --git a/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts b/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts index 1506f076764..1b21a410386 100644 --- a/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts +++ b/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts @@ -19,6 +19,8 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Snippet } from './snippetsFile'; import { SnippetCompletion } from './snippetCompletionProvider'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { EditorState, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState'; export class TabCompletionController implements IEditorContribution { @@ -38,6 +40,7 @@ export class TabCompletionController implements IEditorContribution { constructor( private readonly _editor: ICodeEditor, @ISnippetsService private readonly _snippetService: ISnippetsService, + @IClipboardService private readonly _clipboardService: IClipboardService, @IContextKeyService contextKeyService: IContextKeyService, ) { this._hasSnippets = TabCompletionController.ContextKey.bindTo(contextKeyService); @@ -117,7 +120,7 @@ export class TabCompletionController implements IEditorContribution { this._hasSnippets.set(this._activeSnippets.length > 0); } - performSnippetCompletions(): void { + async performSnippetCompletions() { if (!this._editor.hasModel()) { return; } @@ -125,7 +128,22 @@ export class TabCompletionController implements IEditorContribution { if (this._activeSnippets.length === 1) { // one -> just insert const [snippet] = this._activeSnippets; - SnippetController2.get(this._editor).insert(snippet.codeSnippet, { overwriteBefore: snippet.prefix.length, overwriteAfter: 0 }); + + // async clipboard access might be required and in that case + // we need to check if the editor has changed in flight and then + // bail out (or be smarter than that) + let clipboardText: string | undefined; + if (snippet.needsClipboard) { + const state = new EditorState(this._editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position); + clipboardText = await this._clipboardService.readText(); + if (!state.validate(this._editor)) { + return; + } + } + SnippetController2.get(this._editor).insert(snippet.codeSnippet, { + overwriteBefore: snippet.prefix.length, overwriteAfter: 0, + clipboardText + }); } else if (this._activeSnippets.length > 1) { // two or more -> show IntelliSense box diff --git a/src/vs/workbench/contrib/snippets/test/browser/snippetFile.test.ts b/src/vs/workbench/contrib/snippets/test/browser/snippetFile.test.ts index a804f261f37..25d373fd0b9 100644 --- a/src/vs/workbench/contrib/snippets/test/browser/snippetFile.test.ts +++ b/src/vs/workbench/contrib/snippets/test/browser/snippetFile.test.ts @@ -6,6 +6,7 @@ import * as assert from 'assert'; import { SnippetFile, Snippet, SnippetSource } from 'vs/workbench/contrib/snippets/browser/snippetsFile'; import { URI } from 'vs/base/common/uri'; +import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser'; suite('Snippets', function () { @@ -70,6 +71,8 @@ suite('Snippets', function () { function assertNeedsClipboard(body: string, expected: boolean): void { let snippet = new Snippet(['foo'], 'FooSnippet1', 'foo', '', body, 'test', SnippetSource.User); assert.equal(snippet.needsClipboard, expected); + + assert.equal(SnippetParser.guessNeedsClipboard(body), expected); } assertNeedsClipboard('foo$CLIPBOARD', true); diff --git a/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts b/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts index 70d7065de59..91ad6206651 100644 --- a/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts +++ b/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts @@ -15,7 +15,7 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo import { CompletionContext, CompletionTriggerKind } from 'vs/editor/common/modes'; class SimpleSnippetService implements ISnippetsService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(readonly snippets: Snippet[]) { } getSnippets() { diff --git a/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts b/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts index 8fe4ddd1fc2..8bf519fd308 100644 --- a/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts +++ b/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ipcRenderer as ipc } from 'electron'; +import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { join } from 'vs/base/common/path'; import { onDidChangeFullscreen, isFullscreen } from 'vs/base/browser/browser'; import { getTotalHeight, getTotalWidth } from 'vs/base/browser/dom'; @@ -26,6 +26,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import * as perf from 'vs/base/common/performance'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; import { assertIsDefined } from 'vs/base/common/types'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; class PartsSplash { @@ -45,6 +46,7 @@ class PartsSplash { @ILifecycleService lifecycleService: ILifecycleService, @IEditorGroupsService editorGroupsService: IEditorGroupsService, @IConfigurationService configService: IConfigurationService, + @IElectronService private readonly _electronService: IElectronService ) { lifecycleService.when(LifecyclePhase.Restored).then(_ => { this._removePartsSplash(); @@ -112,7 +114,7 @@ class PartsSplash { // the color needs to be in hex const backgroundColor = this._themeService.getColorTheme().getColor(editorBackground) || themes.WORKBENCH_BACKGROUND(this._themeService.getColorTheme()); const payload = JSON.stringify({ baseTheme, background: Color.Format.CSS.formatHex(backgroundColor) }); - ipc.send('vscode:changeColorTheme', this._envService.configuration.windowId, payload); + ipcRenderer.send('vscode:changeColorTheme', this._electronService.windowId, payload); } } diff --git a/src/vs/workbench/contrib/tags/browser/workspaceTagsService.ts b/src/vs/workbench/contrib/tags/browser/workspaceTagsService.ts index d21d6688d7a..2aecc6350ce 100644 --- a/src/vs/workbench/contrib/tags/browser/workspaceTagsService.ts +++ b/src/vs/workbench/contrib/tags/browser/workspaceTagsService.ts @@ -10,7 +10,7 @@ import { IWorkspaceTagsService, Tags } from 'vs/workbench/contrib/tags/common/wo export class NoOpWorkspaceTagsService implements IWorkspaceTagsService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; getTags(): Promise { return Promise.resolve({}); diff --git a/src/vs/workbench/contrib/tags/common/workspaceTags.ts b/src/vs/workbench/contrib/tags/common/workspaceTags.ts index 6814b5cdf3f..d1683b97726 100644 --- a/src/vs/workbench/contrib/tags/common/workspaceTags.ts +++ b/src/vs/workbench/contrib/tags/common/workspaceTags.ts @@ -12,7 +12,7 @@ export type Tags = { [index: string]: boolean | number | string | undefined }; export const IWorkspaceTagsService = createDecorator('workspaceTagsService'); export interface IWorkspaceTagsService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; getTags(): Promise; diff --git a/src/vs/workbench/contrib/tags/electron-browser/workspaceTags.ts b/src/vs/workbench/contrib/tags/electron-browser/workspaceTags.ts index 9c1254049f8..4737453d673 100644 --- a/src/vs/workbench/contrib/tags/electron-browser/workspaceTags.ts +++ b/src/vs/workbench/contrib/tags/electron-browser/workspaceTags.ts @@ -16,7 +16,7 @@ import { IWorkspaceTagsService, Tags } from 'vs/workbench/contrib/tags/common/wo import { IWorkspaceInformation } from 'vs/platform/diagnostics/common/diagnostics'; import { IRequestService } from 'vs/platform/request/common/request'; import { isWindows } from 'vs/base/common/platform'; -import { getRemotes, SecondLevelDomainWhitelist, getDomainsOfRemotes } from 'vs/platform/extensionManagement/common/configRemotes'; +import { getRemotes, AllowedSecondLevelDomains, getDomainsOfRemotes } from 'vs/platform/extensionManagement/common/configRemotes'; export function getHashedRemotesFromConfig(text: string, stripEndingDotGit: boolean = false): string[] { return getRemotes(text, stripEndingDotGit).map(r => { @@ -111,7 +111,7 @@ export class WorkspaceTags implements IWorkbenchContribution { return []; } return this.textFileService.read(uri, { acceptTextOnly: true }).then( - content => getDomainsOfRemotes(content.value, SecondLevelDomainWhitelist), + content => getDomainsOfRemotes(content.value, AllowedSecondLevelDomains), err => [] // ignore missing or binary file ); }); diff --git a/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService.ts b/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService.ts index f0a1de61d5c..a196460434f 100644 --- a/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService.ts +++ b/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService.ts @@ -22,6 +22,19 @@ import { IWorkspaceTagsService, Tags } from 'vs/workbench/contrib/tags/common/wo import { getHashedRemotesFromConfig } from 'vs/workbench/contrib/tags/electron-browser/workspaceTags'; import { IProductService } from 'vs/platform/product/common/productService'; +const MetaModulesToLookFor = [ + // Azure packages + '@azure', + '@azure/ai', + '@azure/core', + '@azure/cosmos', + '@azure/event', + '@azure/identity', + '@azure/keyvault', + '@azure/search', + '@azure/storage' +]; + const ModulesToLookFor = [ // Packages that suggest a node server 'express', @@ -33,6 +46,8 @@ const ModulesToLookFor = [ // JS frameworks 'react', 'react-native', + 'react-native-macos', + 'react-native-windows', 'rnpm-plugin-windows', '@angular/core', '@ionic', @@ -48,6 +63,7 @@ const ModulesToLookFor = [ '@google-cloud/common', 'heroku-cli', //Office and Sharepoint packages + '@microsoft/teams-js', '@microsoft/office-js', '@microsoft/office-js-helpers', '@types/office-js', @@ -61,29 +77,43 @@ const ModulesToLookFor = [ '@microsoft/rush', 'lerna', 'just-task', - 'beachball' + 'beachball', + // Playwright packages + 'playwright', + 'playwright-core', + 'playwright-chromium', + 'playwright-firefox', + 'playwright-webkit' ]; + +const PyMetaModulesToLookFor = [ + 'azure-ai', + 'azure-cognitiveservices', + 'azure-core', + 'azure-cosmos', + 'azure-event', + 'azure-identity', + 'azure-keyvault', + 'azure-mgmt', + 'azure-ml', + 'azure-search', + 'azure-storage' +]; + const PyModulesToLookFor = [ 'azure', - 'azure-storage-common', - 'azure-storage-blob', - 'azure-storage-file', - 'azure-storage-queue', - 'azure-shell', - 'azure-cosmos', 'azure-devtools', 'azure-elasticluster', 'azure-eventgrid', 'azure-functions', 'azure-graphrbac', - 'azure-keyvault', + 'azure-iothub-device-client', 'azure-loganalytics', 'azure-monitor', 'azure-servicebus', 'azure-servicefabric', - 'azure-storage', + 'azure-shell', 'azure-translator', - 'azure-iothub-device-client', 'adal', 'pydocumentdb', 'botbuilder-core', @@ -92,7 +122,7 @@ const PyModulesToLookFor = [ ]; export class WorkspaceTagsService implements IWorkspaceTagsService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _tags: Tags | undefined; constructor( @@ -180,11 +210,21 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { "workspace.npm.vue" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.aws-sdk" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.aws-amplify-sdk" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/ai" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/core" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/cosmos" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/event" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/identity" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/keyvault" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/search" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/storage" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.azure" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.azure-storage" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.@google-cloud/common" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.firebase" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.heroku-cli" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@microsoft/teams-js" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.@microsoft/office-js" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.@microsoft/office-js-helpers" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.@types/office-js" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, @@ -200,6 +240,13 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { "workspace.npm.just-task" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.beachball" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.electron" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.playwright" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.playwright-core" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.playwright-chromium" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.playwright-firefox" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.playwright-webkit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.react-native-macos" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.react-native-windows" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.bower" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.yeoman.code.ext" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.cordova.high" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, @@ -216,30 +263,31 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { "workspace.py.Pipfile" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.conda" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.any-azure" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspace.py.azure" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspace.py.azure-storage-common" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspace.py.azure-storage-blob" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspace.py.azure-storage-file" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspace.py.azure-storage-queue" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspace.py.azure-mgmt" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspace.py.azure-shell" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.pulumi-azure" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.py.azure" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.py.azure-ai" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.py.azure-cognitiveservices" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.py.azure-core" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-cosmos" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-devtools" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-elasticluster" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.py.azure-event" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-eventgrid" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-functions" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-graphrbac" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.py.azure-identity" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.py.azure-iothub-device-client" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-keyvault" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-loganalytics" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.py.azure-mgmt" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.py.azure-ml" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-monitor" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.py.azure-search" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-servicebus" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-servicefabric" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.py.azure-shell" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-storage" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-translator" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspace.py.azure-iothub-device-client" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspace.py.azure-ml" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspace.py.azure-cognitiveservices" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.adal" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.pydocumentdb" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.botbuilder-core" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, @@ -359,16 +407,13 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { if (PyModulesToLookFor.indexOf(packageName) > -1) { tags['workspace.py.' + packageName] = true; } - // cognitive services has a lot of tiny packages. e.g. 'azure-cognitiveservices-search-autosuggest' - if (packageName.indexOf('azure-cognitiveservices') > -1) { - tags['workspace.py.azure-cognitiveservices'] = true; - } - if (packageName.indexOf('azure-mgmt') > -1) { - tags['workspace.py.azure-mgmt'] = true; - } - if (packageName.indexOf('azure-ml') > -1) { - tags['workspace.py.azure-ml'] = true; + + for (const metaModule of PyMetaModulesToLookFor) { + if (packageName.startsWith(metaModule)) { + tags['workspace.py.' + metaModule] = true; + } } + if (!tags['workspace.py.any-azure']) { tags['workspace.py.any-azure'] = /azure/i.test(packageName); } @@ -408,24 +453,23 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { const packageJsonPromises = getFilePromises('package.json', this.fileService, this.textFileService, content => { try { const packageJsonContents = JSON.parse(content.value); - let dependencies = packageJsonContents['dependencies']; - let devDependencies = packageJsonContents['devDependencies']; - for (let module of ModulesToLookFor) { - if ('react-native' === module) { - if ((dependencies && dependencies[module]) || (devDependencies && devDependencies[module])) { - tags['workspace.reactNative'] = true; - } - } else if ('tns-core-modules' === module) { - if ((dependencies && dependencies[module]) || (devDependencies && devDependencies[module])) { - tags['workspace.nativescript'] = true; - } + let dependencies = Object.keys(packageJsonContents['dependencies'] || {}).concat(Object.keys(packageJsonContents['devDependencies'] || {})); + + for (let dependency of dependencies) { + if ('react-native' === dependency) { + tags['workspace.reactNative'] = true; + } else if ('tns-core-modules' === dependency) { + tags['workspace.nativescript'] = true; + } else if (ModulesToLookFor.indexOf(dependency) > -1) { + tags['workspace.npm.' + dependency] = true; } else { - if ((dependencies && dependencies[module]) || (devDependencies && devDependencies[module])) { - tags['workspace.npm.' + module] = true; + for (const metaModule of MetaModulesToLookFor) { + if (dependency.startsWith(metaModule)) { + tags['workspace.npm.' + metaModule] = true; + } } } } - } catch (e) { // Ignore errors when resolving file or parsing file contents diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 58b81d426c1..e757824eb43 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -11,11 +11,10 @@ import * as json from 'vs/base/common/json'; import { URI } from 'vs/base/common/uri'; import { IStringDictionary } from 'vs/base/common/collections'; import { Action } from 'vs/base/common/actions'; -import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable, IReference } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import * as Types from 'vs/base/common/types'; import { TerminateResponseCode } from 'vs/base/common/processes'; -import * as strings from 'vs/base/common/strings'; import { ValidationStatus, ValidationState } from 'vs/base/common/parsers'; import * as UUID from 'vs/base/common/uuid'; import * as Platform from 'vs/base/common/platform'; @@ -57,7 +56,7 @@ import { TaskSorter, TaskIdentifier, KeyedTaskIdentifier, TASK_RUNNING_STATE, TaskRunSource, KeyedTaskIdentifier as NKeyedTaskIdentifier, TaskDefinition } from 'vs/workbench/contrib/tasks/common/tasks'; -import { ITaskService, ITaskProvider, ProblemMatcherRunOptions, CustomizationProperties, TaskFilter, WorkspaceFolderTaskResult, USER_TASKS_GROUP_KEY } from 'vs/workbench/contrib/tasks/common/taskService'; +import { ITaskService, ITaskProvider, ProblemMatcherRunOptions, CustomizationProperties, TaskFilter, WorkspaceFolderTaskResult, USER_TASKS_GROUP_KEY, CustomExecutionSupportedContext, ShellExecutionSupportedContext, ProcessExecutionSupportedContext } from 'vs/workbench/contrib/tasks/common/taskService'; import { getTemplates as getTaskTemplates } from 'vs/workbench/contrib/tasks/common/taskTemplates'; import * as TaskConfig from '../common/taskConfiguration'; @@ -72,15 +71,16 @@ import { RunAutomaticTasks } from 'vs/workbench/contrib/tasks/browser/runAutomat import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { format } from 'vs/base/common/jsonFormatter'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { applyEdits } from 'vs/base/common/jsonEdit'; -import { ITextEditorPane, SaveReason } from 'vs/workbench/common/editor'; +import { SaveReason } from 'vs/workbench/common/editor'; import { ITextEditorSelection, TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; -import { find } from 'vs/base/common/arrays'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { IViewsService, IViewDescriptorService } from 'vs/workbench/common/views'; import { isWorkspaceFolder, TaskQuickPickEntry, QUICKOPEN_DETAIL_CONFIG, TaskQuickPick, QUICKOPEN_SKIP_CONFIG } from 'vs/workbench/contrib/tasks/browser/taskQuickPick'; +import { ILogService } from 'vs/platform/log/common/log'; +import { once } from 'vs/base/common/functional'; const QUICKOPEN_HISTORY_LIMIT_CONFIG = 'task.quickOpen.history'; const PROBLEM_MATCHER_NEVER_CONFIG = 'task.problemMatchers.neverPrompt'; @@ -224,6 +224,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer protected _outputChannel: IOutputChannel; protected readonly _onDidStateChange: Emitter; + private _waitForSupportedExecutions: Promise; + private _onDidRegisterSupportedExecutions: Emitter = new Emitter(); constructor( @IConfigurationService private readonly configurationService: IConfigurationService, @@ -249,13 +251,14 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer @IHostService private readonly _hostService: IHostService, @IDialogService private readonly dialogService: IDialogService, @INotificationService private readonly notificationService: INotificationService, - @IContextKeyService contextKeyService: IContextKeyService, + @IContextKeyService protected readonly contextKeyService: IContextKeyService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @ITerminalInstanceService private readonly terminalInstanceService: ITerminalInstanceService, @IPathService private readonly pathService: IPathService, @ITextModelService private readonly textModelResolverService: ITextModelService, @IPreferencesService private readonly preferencesService: IPreferencesService, - @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, + @ILogService private readonly logService: ILogService ) { super(); @@ -331,6 +334,26 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } return task._label; }); + + this._waitForSupportedExecutions = new Promise(resolve => { + once(this._onDidRegisterSupportedExecutions.event)(() => resolve()); + }); + } + + public registerSupportedExecutions(custom?: boolean, shell?: boolean, process?: boolean) { + if (custom !== undefined) { + const customContext = CustomExecutionSupportedContext.bindTo(this.contextKeyService); + customContext.set(custom); + } + if (shell !== undefined) { + const shellContext = ShellExecutionSupportedContext.bindTo(this.contextKeyService); + shellContext.set(shell); + } + if (process !== undefined) { + const processContext = ProcessExecutionSupportedContext.bindTo(this.contextKeyService); + processContext.set(process); + } + this._onDidRegisterSupportedExecutions.fire(); } public get onDidStateChange(): Event { @@ -549,20 +572,34 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (!values) { return undefined; } - return find(values, task => task.matches(key, compareId)); + return values.find(task => task.matches(key, compareId)); }); } public async tryResolveTask(configuringTask: ConfiguringTask): Promise { + await Promise.all([this.extensionService.activateByEvent('onCommand:workbench.action.tasks.runTask'), this.extensionService.whenInstalledExtensionsRegistered()]); let matchingProvider: ITaskProvider | undefined; + let matchingProviderUnavailable: boolean = false; for (const [handle, provider] of this._providers) { - if (configuringTask.type === this._providerTypes.get(handle)) { + const providerType = this._providerTypes.get(handle); + if (configuringTask.type === providerType) { + if (providerType && !this.isTaskProviderEnabled(providerType)) { + matchingProviderUnavailable = true; + continue; + } matchingProvider = provider; break; } } if (!matchingProvider) { + if (matchingProviderUnavailable) { + this._outputChannel.append(nls.localize( + 'TaskService.providerUnavailable', + 'Warning: {0} tasks are unavailable in the current environment.\n', + configuringTask.configures.type + )); + } return; } @@ -623,7 +660,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (this.isProvideTasksEnabled()) { for (const [handle] of this._providers) { const type = this._providerTypes.get(handle); - if (type) { + if (type && this.isTaskProviderEnabled(type)) { types.push(type); } } @@ -807,7 +844,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this.openerService.open(URI.parse('https://go.microsoft.com/fwlink/?LinkId=733558')); } - public build(): Promise { + public async build(): Promise { return this.getGroupedTasks().then((tasks) => { let runnable = this.createRunnableTask(tasks, TaskGroup.Build); if (!runnable || !runnable.task) { @@ -817,7 +854,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer throw new TaskError(Severity.Info, nls.localize('TaskService.noBuildTask2', 'No build task defined. Mark a task with as a \'build\' group in the tasks.json file.'), TaskErrors.NoBuildTask); } } - return this.executeTask(runnable.task, runnable.resolver); + return this.executeTask(runnable.task, runnable.resolver, TaskRunSource.User); }).then(value => value, (error) => { this.handleError(error); return Promise.reject(error); @@ -834,7 +871,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer throw new TaskError(Severity.Info, nls.localize('TaskService.noTestTask2', 'No test task defined. Mark a task with as a \'test\' group in the tasks.json file.'), TaskErrors.NoTestTask); } } - return this.executeTask(runnable.task, runnable.resolver); + return this.executeTask(runnable.task, runnable.resolver, TaskRunSource.User); }).then(value => value, (error) => { this.handleError(error); return Promise.reject(error); @@ -851,12 +888,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (options && options.attachProblemMatcher && this.shouldAttachProblemMatcher(task) && !InMemoryTask.is(task)) { const toExecute = await this.attachProblemMatcher(task); if (toExecute) { - resolve(this.executeTask(toExecute, resolver)); + resolve(this.executeTask(toExecute, resolver, runSource)); } else { resolve(undefined); } } else { - resolve(this.executeTask(task, resolver)); + resolve(this.executeTask(task, resolver, runSource)); } }).then((value) => { if (runSource === TaskRunSource.User) { @@ -1051,30 +1088,52 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return false; } - private openEditorAtTask(resource: URI | undefined, task: TaskConfig.CustomTask | TaskConfig.ConfiguringTask | string | undefined): Promise { + private async formatTaskForJson(resource: URI, task: TaskConfig.CustomTask | TaskConfig.ConfiguringTask): Promise { + let reference: IReference | undefined; + let stringValue: string = ''; + try { + reference = await this.textModelResolverService.createModelReference(resource); + const model = reference.object.textEditorModel; + const { tabSize, insertSpaces } = model.getOptions(); + const eol = model.getEOL(); + const edits = format(JSON.stringify(task), undefined, { eol, tabSize, insertSpaces }); + let stringified = applyEdits(JSON.stringify(task), edits); + const regex = new RegExp(eol + (insertSpaces ? ' '.repeat(tabSize) : '\\t'), 'g'); + stringified = stringified.replace(regex, eol + (insertSpaces ? ' '.repeat(tabSize * 3) : '\t\t\t')); + const twoTabs = insertSpaces ? ' '.repeat(tabSize * 2) : '\t\t'; + stringValue = twoTabs + stringified.slice(0, stringified.length - 1) + twoTabs + stringified.slice(stringified.length - 1); + } finally { + if (reference) { + reference.dispose(); + } + } + return stringValue; + } + + private openEditorAtTask(resource: URI | undefined, task: TaskConfig.CustomTask | TaskConfig.ConfiguringTask | string | undefined, configIndex: number = -1): Promise { if (resource === undefined) { - return Promise.resolve(undefined); + return Promise.resolve(false); } let selection: ITextEditorSelection | undefined; return this.fileService.readFile(resource).then(content => content.value).then(async content => { if (!content) { - return undefined; + return false; } if (task) { const contentValue = content.toString(); - let stringValue: string; - if (typeof task === 'string') { - stringValue = task; - } else { - const model = (await this.textModelResolverService.createModelReference(resource)).object.textEditorModel; - const { tabSize, insertSpaces } = model.getOptions(); - const eol = model.getEOL(); - const edits = format(JSON.stringify(task), undefined, { eol, tabSize, insertSpaces }); - let stringified = applyEdits(JSON.stringify(task), edits); - const regex = new RegExp(eol + '\\t', 'g'); - stringified = stringified.replace(regex, eol + '\t\t\t'); - const twoTabs = '\t\t'; - stringValue = twoTabs + stringified.slice(0, stringified.length - 1) + twoTabs + stringified.slice(stringified.length - 1); + let stringValue: string | undefined; + if (configIndex !== -1) { + const json: TaskConfig.ExternalTaskRunnerConfiguration = this.configurationService.getValue('tasks', { resource }); + if (json.tasks && (json.tasks.length > configIndex)) { + stringValue = await this.formatTaskForJson(resource, json.tasks[configIndex]); + } + } + if (!stringValue) { + if (typeof task === 'string') { + stringValue = task; + } else { + stringValue = await this.formatTaskForJson(resource, task); + } } const index = contentValue.indexOf(stringValue); @@ -1101,19 +1160,19 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer selection, selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport } - }); + }).then(() => !!selection); }); } - private createCustomizableTask(task: ContributedTask | CustomTask): TaskConfig.CustomTask | TaskConfig.ConfiguringTask | undefined { + private createCustomizableTask(task: ContributedTask | CustomTask | ConfiguringTask): TaskConfig.CustomTask | TaskConfig.ConfiguringTask | undefined { let toCustomize: TaskConfig.CustomTask | TaskConfig.ConfiguringTask | undefined; - let taskConfig = CustomTask.is(task) ? task._source.config : undefined; + let taskConfig = CustomTask.is(task) || ConfiguringTask.is(task) ? task._source.config : undefined; if (taskConfig && taskConfig.element) { toCustomize = { ...(taskConfig.element) }; } else if (ContributedTask.is(task)) { toCustomize = { }; - let identifier: TaskConfig.TaskIdentifier = Objects.assign(Object.create(null), task.defines); + let identifier: TaskConfig.TaskIdentifier = Object.assign(Object.create(null), task.defines); delete identifier['_key']; Object.keys(identifier).forEach(key => (toCustomize)![key] = identifier[key]); if (task.configurationProperties.problemMatchers && task.configurationProperties.problemMatchers.length > 0 && Types.isStringArray(task.configurationProperties.problemMatchers)) { @@ -1138,7 +1197,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return toCustomize; } - public customize(task: ContributedTask | CustomTask, properties?: CustomizationProperties, openConfig?: boolean): Promise { + public customize(task: ContributedTask | CustomTask | ConfiguringTask, properties?: CustomizationProperties, openConfig?: boolean): Promise { const workspaceFolder = task.getWorkspaceFolder(); if (!workspaceFolder) { return Promise.resolve(undefined); @@ -1176,7 +1235,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer ].join('\n') + JSON.stringify(value, null, '\t').substr(1); let editorConfig = this.configurationService.getValue(); if (editorConfig.editor.insertSpaces) { - content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + strings.repeat(' ', s2.length * editorConfig.editor.tabSize)); + content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + ' '.repeat(s2.length * editorConfig.editor.tabSize)); } promise = this.textFileService.create(workspaceFolder.toResource('.vscode/tasks.json'), content).then(() => { }); } else { @@ -1272,14 +1331,14 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } - public openConfig(task: CustomTask | ConfiguringTask | undefined): Promise { + public async openConfig(task: CustomTask | ConfiguringTask | undefined): Promise { let resource: URI | undefined; if (task) { resource = this.getResourceForTask(task); } else { resource = (this._workspaceFolders && (this._workspaceFolders.length > 0)) ? this._workspaceFolders[0].toResource('.vscode/tasks.json') : undefined; } - return this.openEditorAtTask(resource, task ? task._label : undefined).then(() => undefined); + return this.openEditorAtTask(resource, task ? task._label : undefined, task ? task._source.config.index : -1); } private createRunnableTask(tasks: TaskMap, group: TaskGroup): { task: Task; resolver: ITaskResolver } | undefined { @@ -1403,7 +1462,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }; } - private executeTask(task: Task, resolver: ITaskResolver): Promise { + private executeTask(task: Task, resolver: ITaskResolver, runSource: TaskRunSource): Promise { enum SaveBeforeRunConfigOptions { Always = 'always', Never = 'never', @@ -1415,7 +1474,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer const execTask = async (task: Task, resolver: ITaskResolver): Promise => { return ProblemMatcherRegistry.onReady().then(() => { let executeResult = this.getTaskSystem().run(task, resolver); - return this.handleExecuteResult(executeResult); + return this.handleExecuteResult(executeResult, runSource); }); }; @@ -1452,7 +1511,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } - private async handleExecuteResult(executeResult: ITaskExecuteResult): Promise { + private async handleExecuteResult(executeResult: ITaskExecuteResult, runSource?: TaskRunSource): Promise { if (executeResult.task.taskLoadMessages && executeResult.task.taskLoadMessages.length > 0) { executeResult.task.taskLoadMessages.forEach(loadMessage => { this._outputChannel.append(loadMessage + '\n'); @@ -1460,7 +1519,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this.showOutput(); } - await this.setRecentlyUsedTask(executeResult.task); + if (runSource === TaskRunSource.User) { + await this.setRecentlyUsedTask(executeResult.task); + } if (executeResult.kind === TaskExecuteKind.Active) { let active = executeResult.active; if (active && active.same) { @@ -1524,7 +1585,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this.modelService, this.configurationResolverService, this.telemetryService, this.contextService, this.environmentService, AbstractTaskService.OutputChannelId, this.fileService, this.terminalInstanceService, - this.pathService, this.viewDescriptorService, + this.pathService, this.viewDescriptorService, this.logService, (workspaceFolder: IWorkspaceFolder) => { if (!workspaceFolder) { return undefined; @@ -1536,6 +1597,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer protected abstract getTaskSystem(): ITaskSystem; + private isTaskProviderEnabled(type: string) { + const definition = TaskDefinitionRegistry.get(type); + return !definition || !definition.when || this.contextKeyService.contextMatchesRules(definition.when); + } + private getGroupedTasks(type?: string): Promise { const needsRecentTasksMigration = this.needsRecentTasksMigration(); return Promise.all([this.extensionService.activateByEvent('onCommand:workbench.action.tasks.runTask'), this.extensionService.whenInstalledExtensionsRegistered()]).then(() => { @@ -1573,7 +1639,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }; if (this.isProvideTasksEnabled() && (this.schemaVersion === JsonSchemaVersion.V2_0_0) && (this._providers.size > 0)) { for (const [handle, provider] of this._providers) { - if ((type === undefined) || (type === this._providerTypes.get(handle))) { + const providerType = this._providerTypes.get(handle); + if ((type === undefined) || (type === providerType)) { + if (providerType && !this.isTaskProviderEnabled(providerType)) { + continue; + } counter++; provider.provideTasks(validTypes).then((taskSet: TaskSet) => { // Check that the tasks provided are of the correct type @@ -1676,8 +1746,16 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return; } + let requiredTaskProviderUnavailable: boolean = false; + for (const [handle, provider] of this._providers) { - if (configuringTask.type === this._providerTypes.get(handle)) { + const providerType = this._providerTypes.get(handle); + if (configuringTask.type === providerType) { + if (providerType && !this.isTaskProviderEnabled(providerType)) { + requiredTaskProviderUnavailable = true; + continue; + } + try { const resolvedTask = await provider.resolveTask(configuringTask); if (resolvedTask && (resolvedTask._id === configuringTask._id)) { @@ -1690,13 +1768,21 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } - this._outputChannel.append(nls.localize( - 'TaskService.noConfiguration', - 'Error: The {0} task detection didn\'t contribute a task for the following configuration:\n{1}\nThe task will be ignored.\n', - configuringTask.configures.type, - JSON.stringify(configuringTask._source.config.element, undefined, 4) - )); - this.showOutput(); + if (requiredTaskProviderUnavailable) { + this._outputChannel.append(nls.localize( + 'TaskService.providerUnavailable', + 'Warning: {0} tasks are unavailable in the current environment.\n', + configuringTask.configures.type + )); + } else { + this._outputChannel.append(nls.localize( + 'TaskService.noConfiguration', + 'Error: The {0} task detection didn\'t contribute a task for the following configuration:\n{1}\nThe task will be ignored.\n', + configuringTask.configures.type, + JSON.stringify(configuringTask._source.config.element, undefined, 4) + )); + this.showOutput(); + } }); await Promise.all(unUsedConfigurationPromises); @@ -1755,7 +1841,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return result; } - public getWorkspaceTasks(runSource: TaskRunSource = TaskRunSource.User): Promise> { + public async getWorkspaceTasks(runSource: TaskRunSource = TaskRunSource.User): Promise> { + await this._waitForSupportedExecutions; if (this._workspaceTasksPromise) { return this._workspaceTasksPromise; } @@ -1808,9 +1895,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return ProblemMatcherRegistry.onReady().then(async (): Promise => { let taskSystemInfo: TaskSystemInfo | undefined = this._taskSystemInfos.get(workspaceFolder.uri.scheme); let problemReporter = new ProblemReporter(this._outputChannel); - let parseResult = TaskConfig.parse(workspaceFolder, undefined, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, workspaceFolderConfiguration.config!, problemReporter, TaskConfig.TaskConfigSource.TasksJson); + let parseResult = TaskConfig.parse(workspaceFolder, undefined, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, workspaceFolderConfiguration.config!, problemReporter, TaskConfig.TaskConfigSource.TasksJson, this.contextKeyService); let hasErrors = false; - if (!parseResult.validationStatus.isOK()) { + if (!parseResult.validationStatus.isOK() && (parseResult.validationStatus.state !== ValidationState.Info)) { hasErrors = true; this.showOutput(runSource); } @@ -1849,7 +1936,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } if (isAffected) { - this._outputChannel.append(nls.localize('TaskSystem.invalidTaskJsonOther', 'Error: The content of the tasks json in {0} has syntax errors. Please correct them before executing a task.\n', location)); + this._outputChannel.append(nls.localize({ key: 'TaskSystem.invalidTaskJsonOther', comment: ['Message notifies of an error in one of several places there is tasks related json, not necessarily in a file named tasks.json'] }, 'Error: The content of the tasks json in {0} has syntax errors. Please correct them before executing a task.\n', location)); this.showOutput(); return { config, hasParseErrors: true }; } @@ -1905,9 +1992,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } let taskSystemInfo: TaskSystemInfo | undefined = workspaceFolder ? this._taskSystemInfos.get(workspaceFolder.uri.scheme) : undefined; let problemReporter = new ProblemReporter(this._outputChannel); - let parseResult = TaskConfig.parse(workspaceFolder, this._workspace, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, config, problemReporter, source, isRecentTask); + let parseResult = TaskConfig.parse(workspaceFolder, this._workspace, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, config, problemReporter, source, this.contextKeyService, isRecentTask); let hasErrors = false; - if (!parseResult.validationStatus.isOK()) { + if (!parseResult.validationStatus.isOK() && (parseResult.validationStatus.state !== ValidationState.Info)) { this.showOutput(runSource); hasErrors = true; } @@ -2365,10 +2452,13 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (identifier !== undefined) { this.getGroupedTasks().then(async (grouped) => { let resolver = this.createResolver(grouped); - let folders: (IWorkspaceFolder | string)[] = this.contextService.getWorkspace().folders; - folders = folders.concat([USER_TASKS_GROUP_KEY]); - for (let folder of folders) { - let task = await resolver.resolve(typeof folder === 'string' ? folder : folder.uri, identifier); + let folderURIs: (URI | string)[] = this.contextService.getWorkspace().folders.map(folder => folder.uri); + if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { + folderURIs.push(this.contextService.getWorkspace().configuration!); + } + folderURIs.push(USER_TASKS_GROUP_KEY); + for (let uri of folderURIs) { + let task = await resolver.resolve(uri, identifier); if (task) { this.run(task).then(undefined, reason => { // eat the error, it has already been surfaced to the user and we don't care about it here @@ -2504,38 +2594,58 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer location: ProgressLocation.Window, title: nls.localize('TaskService.fetchingBuildTasks', 'Fetching build tasks...') }; - let promise = this.getTasksForGroup(TaskGroup.Build).then((tasks) => { - if (tasks.length > 0) { - let { defaults, users } = this.splitPerGroupType(tasks); - if (defaults.length === 1) { - this.run(defaults[0]).then(undefined, reason => { - // eat the error, it has already been surfaced to the user and we don't care about it here - }); - return; - } else if (defaults.length + users.length > 0) { - tasks = defaults.concat(users); + let promise = this.getWorkspaceTasks().then(tasks => { + const buildTasks: ConfiguringTask[] = []; + for (const taskSource of tasks) { + for (const task in taskSource[1].configurations?.byIdentifier) { + if ((taskSource[1].configurations?.byIdentifier[task].configurationProperties.group === TaskGroup.Build) && + (taskSource[1].configurations?.byIdentifier[task].configurationProperties.groupType === GroupType.default)) { + buildTasks.push(taskSource[1].configurations.byIdentifier[task]); + } } } - this.showIgnoredFoldersMessage().then(() => { - this.showQuickPick(tasks, - nls.localize('TaskService.pickBuildTask', 'Select the build task to run'), - { - label: nls.localize('TaskService.noBuildTask', 'No build task to run found. Configure Build Task...'), - task: null - }, - true).then((entry) => { - let task: Task | undefined | null = entry ? entry.task : undefined; - if (task === undefined) { - return; - } - if (task === null) { - this.runConfigureDefaultBuildTask(); - return; - } - this.run(task, { attachProblemMatcher: true }).then(undefined, reason => { + if (buildTasks.length === 1) { + this.tryResolveTask(buildTasks[0]).then(resolvedTask => { + this.run(resolvedTask).then(undefined, reason => { + // eat the error, it has already been surfaced to the user and we don't care about it here + }); + }); + return; + } + + return this.getTasksForGroup(TaskGroup.Build).then((tasks) => { + if (tasks.length > 0) { + let { defaults, users } = this.splitPerGroupType(tasks); + if (defaults.length === 1) { + this.run(defaults[0]).then(undefined, reason => { // eat the error, it has already been surfaced to the user and we don't care about it here }); - }); + return; + } else if (defaults.length + users.length > 0) { + tasks = defaults.concat(users); + } + } + this.showIgnoredFoldersMessage().then(() => { + this.showQuickPick(tasks, + nls.localize('TaskService.pickBuildTask', 'Select the build task to run'), + { + label: nls.localize('TaskService.noBuildTask', 'No build task to run found. Configure Build Task...'), + task: null + }, + true).then((entry) => { + let task: Task | undefined | null = entry ? entry.task : undefined; + if (task === undefined) { + return; + } + if (task === null) { + this.runConfigureDefaultBuildTask(); + return; + } + this.run(task, { attachProblemMatcher: true }).then(undefined, reason => { + // eat the error, it has already been surfaced to the user and we don't care about it here + }); + }); + }); }); }); this.progressService.withProgress(options, () => promise); @@ -2743,7 +2853,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer content = pickTemplateResult.content; let editorConfig = this.configurationService.getValue(); if (editorConfig.editor.insertSpaces) { - content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + strings.repeat(' ', s2.length * editorConfig.editor.tabSize)); + content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + ' '.repeat(s2.length * editorConfig.editor.tabSize)); } configFileCreated = true; type TaskServiceTemplateClassification = { diff --git a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts index ef27420ed41..6418a23ec93 100644 --- a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts @@ -37,13 +37,13 @@ import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys'; import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/platform/quickinput/common/quickAccess'; import { TasksQuickAccessProvider } from 'vs/workbench/contrib/tasks/browser/tasksQuickAccess'; -let tasksCategory = nls.localize('tasksCategory', "Tasks"); +let tasksCategory = { value: nls.localize('tasksCategory', "Tasks"), original: 'Tasks' }; const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(RunAutomaticTasks, LifecyclePhase.Eventually); const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ManageAutomaticTaskRunning), 'Tasks: Manage Automatic Tasks in Folder', tasksCategory); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ManageAutomaticTaskRunning), 'Tasks: Manage Automatic Tasks in Folder', tasksCategory.value); export class TaskStatusBarContributions extends Disposable implements IWorkbenchContribution { private runningTasksStatusItem: IStatusbarEntryAccessor | undefined; @@ -226,24 +226,24 @@ MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { MenuRegistry.appendMenuItem(MenuId.CommandPalette, ({ command: { id: 'workbench.action.tasks.openWorkspaceFileTasks', - title: nls.localize('workbench.action.tasks.openWorkspaceFileTasks', "Open Workspace Tasks"), + title: { value: nls.localize('workbench.action.tasks.openWorkspaceFileTasks', "Open Workspace Tasks"), original: 'Open Workspace Tasks' }, category: tasksCategory }, when: WorkbenchStateContext.isEqualTo('workspace') })); -MenuRegistry.addCommand({ id: ConfigureTaskAction.ID, title: { value: ConfigureTaskAction.TEXT, original: 'Configure Task' }, category: { value: tasksCategory, original: 'Tasks' } }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.showLog', title: { value: nls.localize('ShowLogAction.label', "Show Task Log"), original: 'Show Task Log' }, category: { value: tasksCategory, original: 'Tasks' } }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.runTask', title: { value: nls.localize('RunTaskAction.label', "Run Task"), original: 'Run Task' }, category: { value: tasksCategory, original: 'Tasks' } }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.reRunTask', title: { value: nls.localize('ReRunTaskAction.label', "Rerun Last Task"), original: 'Rerun Last Task' }, category: { value: tasksCategory, original: 'Tasks' } }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.restartTask', title: { value: nls.localize('RestartTaskAction.label', "Restart Running Task"), original: 'Restart Running Task' }, category: { value: tasksCategory, original: 'Tasks' } }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.showTasks', title: { value: nls.localize('ShowTasksAction.label', "Show Running Tasks"), original: 'Show Running Tasks' }, category: { value: tasksCategory, original: 'Tasks' } }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.terminate', title: { value: nls.localize('TerminateAction.label', "Terminate Task"), original: 'Terminate Task' }, category: { value: tasksCategory, original: 'Tasks' } }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.build', title: { value: nls.localize('BuildAction.label', "Run Build Task"), original: 'Run Build Task' }, category: { value: tasksCategory, original: 'Tasks' } }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.test', title: { value: nls.localize('TestAction.label', "Run Test Task"), original: 'Run Test Task' }, category: { value: tasksCategory, original: 'Tasks' } }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.configureDefaultBuildTask', title: { value: nls.localize('ConfigureDefaultBuildTask.label', "Configure Default Build Task"), original: 'Configure Default Build Task' }, category: { value: tasksCategory, original: 'Tasks' } }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.configureDefaultTestTask', title: { value: nls.localize('ConfigureDefaultTestTask.label', "Configure Default Test Task"), original: 'Configure Default Test Task' }, category: { value: tasksCategory, original: 'Tasks' } }); -MenuRegistry.addCommand({ id: 'workbench.action.tasks.openUserTasks', title: { value: nls.localize('workbench.action.tasks.openUserTasks', "Open User Tasks"), original: 'Open User Tasks' }, category: { value: tasksCategory, original: 'Tasks' } }); +MenuRegistry.addCommand({ id: ConfigureTaskAction.ID, title: { value: ConfigureTaskAction.TEXT, original: 'Configure Task' }, category: tasksCategory }); +MenuRegistry.addCommand({ id: 'workbench.action.tasks.showLog', title: { value: nls.localize('ShowLogAction.label', "Show Task Log"), original: 'Show Task Log' }, category: tasksCategory }); +MenuRegistry.addCommand({ id: 'workbench.action.tasks.runTask', title: { value: nls.localize('RunTaskAction.label', "Run Task"), original: 'Run Task' }, category: tasksCategory }); +MenuRegistry.addCommand({ id: 'workbench.action.tasks.reRunTask', title: { value: nls.localize('ReRunTaskAction.label', "Rerun Last Task"), original: 'Rerun Last Task' }, category: tasksCategory }); +MenuRegistry.addCommand({ id: 'workbench.action.tasks.restartTask', title: { value: nls.localize('RestartTaskAction.label', "Restart Running Task"), original: 'Restart Running Task' }, category: tasksCategory }); +MenuRegistry.addCommand({ id: 'workbench.action.tasks.showTasks', title: { value: nls.localize('ShowTasksAction.label', "Show Running Tasks"), original: 'Show Running Tasks' }, category: tasksCategory }); +MenuRegistry.addCommand({ id: 'workbench.action.tasks.terminate', title: { value: nls.localize('TerminateAction.label', "Terminate Task"), original: 'Terminate Task' }, category: tasksCategory }); +MenuRegistry.addCommand({ id: 'workbench.action.tasks.build', title: { value: nls.localize('BuildAction.label', "Run Build Task"), original: 'Run Build Task' }, category: tasksCategory }); +MenuRegistry.addCommand({ id: 'workbench.action.tasks.test', title: { value: nls.localize('TestAction.label', "Run Test Task"), original: 'Run Test Task' }, category: tasksCategory }); +MenuRegistry.addCommand({ id: 'workbench.action.tasks.configureDefaultBuildTask', title: { value: nls.localize('ConfigureDefaultBuildTask.label', "Configure Default Build Task"), original: 'Configure Default Build Task' }, category: tasksCategory }); +MenuRegistry.addCommand({ id: 'workbench.action.tasks.configureDefaultTestTask', title: { value: nls.localize('ConfigureDefaultTestTask.label', "Configure Default Test Task"), original: 'Configure Default Test Task' }, category: tasksCategory }); +MenuRegistry.addCommand({ id: 'workbench.action.tasks.openUserTasks', title: { value: nls.localize('workbench.action.tasks.openUserTasks', "Open User Tasks"), original: 'Open User Tasks' }, category: tasksCategory }); // MenuRegistry.addCommand( { id: 'workbench.action.tasks.rebuild', title: nls.localize('RebuildAction.label', 'Run Rebuild Task'), category: tasksCategory }); // MenuRegistry.addCommand( { id: 'workbench.action.tasks.clean', title: nls.localize('CleanAction.label', 'Run Clean Task'), category: tasksCategory }); diff --git a/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts b/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts index f0519eda5a8..cd19578ce11 100644 --- a/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts +++ b/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts @@ -105,9 +105,12 @@ export class TaskQuickPick extends Disposable { for (let j = 0; j < configuredTasks.length; j++) { const workspaceFolder = configuredTasks[j].getWorkspaceFolder()?.uri.toString(); const definition = configuredTasks[j].getDefinition()?._key; + const type = configuredTasks[j].type; + const label = configuredTasks[j]._label; const recentKey = configuredTasks[j].getRecentlyUsedKey(); const findIndex = recentTasks.findIndex((value) => { - return (workspaceFolder && definition && value.getWorkspaceFolder()?.uri.toString() === workspaceFolder && value.getDefinition()?._key === definition) + return (workspaceFolder && definition && value.getWorkspaceFolder()?.uri.toString() === workspaceFolder + && ((value.getDefinition()?._key === definition) || (value.type === type && value._label === label))) || (recentKey && value.getRecentlyUsedKey() === recentKey); }); if (findIndex === -1) { @@ -166,13 +169,15 @@ export class TaskQuickPick extends Disposable { picker.ignoreFocusOut = false; picker.show(); - picker.onDidTriggerItemButton(context => { + picker.onDidTriggerItemButton(async (context) => { let task = context.item.task; this.quickInputService.cancel(); if (ContributedTask.is(task)) { this.taskService.customize(task, undefined, true); } else if (CustomTask.is(task) || ConfiguringTask.is(task)) { - this.taskService.openConfig(task); + if (!(await this.taskService.openConfig(task))) { + this.taskService.customize(task, undefined, true); + } } }); diff --git a/src/vs/workbench/contrib/tasks/browser/tasksQuickAccess.ts b/src/vs/workbench/contrib/tasks/browser/tasksQuickAccess.ts index 89b4ff97692..3b768e712a5 100644 --- a/src/vs/workbench/contrib/tasks/browser/tasksQuickAccess.ts +++ b/src/vs/workbench/contrib/tasks/browser/tasksQuickAccess.ts @@ -75,7 +75,10 @@ export class TasksQuickAccessProvider extends PickerQuickAccessProvider { if (isString(task)) { // switch to quick pick and show second level - taskQuickPick.show(localize('TaskService.pickRunTask', 'Select the task to run'), undefined, task); + const showResult = await taskQuickPick.show(localize('TaskService.pickRunTask', 'Select the task to run'), undefined, task); + if (showResult) { + this.taskService.run(showResult, { attachProblemMatcher: true }); + } } else { this.taskService.run(await this.toTask(task), { attachProblemMatcher: true }); } diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index c301d2fdb11..f6993a12c3c 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -46,6 +46,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { env as processEnv, cwd as processCwd } from 'vs/base/common/process'; import { IViewsService, IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; +import { ILogService } from 'vs/platform/log/common/log'; interface TerminalData { terminal: ITerminalInstance; @@ -202,6 +203,7 @@ export class TerminalTaskSystem implements ITaskSystem { private terminalInstanceService: ITerminalInstanceService, private pathService: IPathService, private viewDescriptorService: IViewDescriptorService, + private logService: ILogService, taskSystemInfoResolver: TaskSystemInfoResolver, ) { @@ -444,7 +446,7 @@ export class TerminalTaskSystem implements ITaskSystem { let promise = this.activeTasks[key] ? this.activeTasks[key].promise : undefined; if (!promise) { this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.DependsOnStarted, task)); - promise = this.executeTask(dependencyTask, resolver, trigger, alreadyResolved); + promise = this.executeDependencyTask(dependencyTask, resolver, trigger, alreadyResolved); } promises.push(promise); if (task.configurationProperties.dependsOrder === DependsOrder.sequence) { @@ -494,12 +496,33 @@ export class TerminalTaskSystem implements ITaskSystem { } } - private resolveAndFindExecutable(workspaceFolder: IWorkspaceFolder | undefined, task: CustomTask | ContributedTask, cwd: string | undefined, envPath: string | undefined): Promise { - return this.findExecutable( - this.configurationResolverService.resolve(workspaceFolder, CommandString.value(task.command.name!)), - cwd ? this.configurationResolverService.resolve(workspaceFolder, cwd) : undefined, - envPath ? envPath.split(path.delimiter).map(p => this.configurationResolverService.resolve(workspaceFolder, p)) : undefined - ); + private async executeDependencyTask(task: Task, resolver: ITaskResolver, trigger: string, alreadyResolved?: Map): Promise { + // If the task is a background task with a watching problem matcher, we don't wait for the whole task to finish, + // just for the problem matcher to go inactive. + if (!task.configurationProperties.isBackground) { + return this.executeTask(task, resolver, trigger, alreadyResolved); + } + + const inactivePromise = new Promise(resolve => { + const taskInactiveDisposable = this._onDidStateChange.event(taskEvent => { + if ((taskEvent.kind === TaskEventKind.Inactive) && (taskEvent.__task === task)) { + taskInactiveDisposable.dispose(); + resolve({ exitCode: 0 }); + } + }); + }); + return Promise.race([inactivePromise, this.executeTask(task, resolver, trigger, alreadyResolved)]); + } + + private async resolveAndFindExecutable(systemInfo: TaskSystemInfo | undefined, workspaceFolder: IWorkspaceFolder | undefined, task: CustomTask | ContributedTask, cwd: string | undefined, envPath: string | undefined): Promise { + const command = this.configurationResolverService.resolve(workspaceFolder, CommandString.value(task.command.name!)); + cwd = cwd ? this.configurationResolverService.resolve(workspaceFolder, cwd) : undefined; + const paths = envPath ? envPath.split(path.delimiter).map(p => this.configurationResolverService.resolve(workspaceFolder, p)) : undefined; + let foundExecutable = await systemInfo?.findExecutable(command, cwd, paths); + if (!foundExecutable) { + foundExecutable = await this.findExecutable(command, cwd, paths); + } + return foundExecutable; } private findUnresolvedVariables(variables: Set, alreadyResolved: Map): Set { @@ -523,7 +546,7 @@ export class TerminalTaskSystem implements ITaskSystem { } } - private resolveVariablesFromSet(taskSystemInfo: TaskSystemInfo | undefined, workspaceFolder: IWorkspaceFolder | undefined, task: CustomTask | ContributedTask, variables: Set, alreadyResolved: Map): Promise { + private resolveVariablesFromSet(taskSystemInfo: TaskSystemInfo | undefined, workspaceFolder: IWorkspaceFolder | undefined, task: CustomTask | ContributedTask, variables: Set, alreadyResolved: Map): Promise { let isProcess = task.command && task.command.runtime === RuntimeType.Process; let options = task.command && task.command.options ? task.command.options : undefined; let cwd = options ? options.cwd : undefined; @@ -539,7 +562,7 @@ export class TerminalTaskSystem implements ITaskSystem { } } const unresolved = this.findUnresolvedVariables(variables, alreadyResolved); - let resolvedVariables: Promise; + let resolvedVariables: Promise; if (taskSystemInfo && workspaceFolder) { let resolveSet: ResolveSet = { variables: unresolved @@ -555,23 +578,27 @@ export class TerminalTaskSystem implements ITaskSystem { } } resolvedVariables = taskSystemInfo.resolveVariables(workspaceFolder, resolveSet, TaskSourceKind.toConfigurationTarget(task._source.kind)).then(async (resolved) => { + if (!resolved) { + return undefined; + } + this.mergeMaps(alreadyResolved, resolved.variables); resolved.variables = new Map(alreadyResolved); if (isProcess) { let process = CommandString.value(task.command.name!); if (taskSystemInfo.platform === Platform.Platform.Windows) { - process = await this.resolveAndFindExecutable(workspaceFolder, task, cwd, envPath); + process = await this.resolveAndFindExecutable(taskSystemInfo, workspaceFolder, task, cwd, envPath); } resolved.variables.set(TerminalTaskSystem.ProcessVarName, process); } - return Promise.resolve(resolved); + return resolved; }); return resolvedVariables; } else { let variablesArray = new Array(); unresolved.forEach(variable => variablesArray.push(variable)); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { this.configurationResolverService.resolveWithInteraction(workspaceFolder, variablesArray, 'tasks', undefined, TaskSourceKind.toConfigurationTarget(task._source.kind)).then(async (resolvedVariablesMap: Map | undefined) => { if (resolvedVariablesMap) { this.mergeMaps(alreadyResolved, resolvedVariablesMap); @@ -579,7 +606,7 @@ export class TerminalTaskSystem implements ITaskSystem { if (isProcess) { let processVarValue: string; if (Platform.isWindows) { - processVarValue = await this.resolveAndFindExecutable(workspaceFolder, task, cwd, envPath); + processVarValue = await this.resolveAndFindExecutable(taskSystemInfo, workspaceFolder, task, cwd, envPath); } else { processVarValue = this.configurationResolverService.resolve(workspaceFolder, CommandString.value(task.command.name!)); } @@ -652,6 +679,9 @@ export class TerminalTaskSystem implements ITaskSystem { if (!hasAllVariables) { return this.resolveVariablesFromSet(lastTask.getVerifiedTask().systemInfo, lastTask.getVerifiedTask().workspaceFolder, task, variables, alreadyResolved).then((resolvedVariables) => { + if (!resolvedVariables) { + return { exitCode: 0 }; + } this.currentTask.resolvedVariables = resolvedVariables; return this.executeInTerminal(task, trigger, new VariableResolver(lastTask.getVerifiedTask().workspaceFolder, lastTask.getVerifiedTask().systemInfo, resolvedVariables.variables, this.configurationResolverService), workspaceFolder!); }, reason => { @@ -722,10 +752,9 @@ export class TerminalTaskSystem implements ITaskSystem { processStartedSignaled = true; } }, (_error) => { - // The process never got ready. Need to think how to handle this. + this.logService.error('Task terminal process never got ready'); }); this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Start, task, terminal.id)); - const registeredLinkMatchers = this.registerLinkMatchers(terminal, problemMatchers); let skipLine: boolean = (!!task.command.presentation && task.command.presentation.echo); const onData = terminal.onLineData((line) => { if (skipLine) { @@ -765,12 +794,16 @@ export class TerminalTaskSystem implements ITaskSystem { let reveal = task.command.presentation!.reveal; if ((reveal === RevealKind.Silent) && ((exitCode !== 0) || (watchingProblemMatcher.numberOfMatches > 0) && watchingProblemMatcher.maxMarkerSeverity && (watchingProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error))) { - this.terminalService.setActiveInstance(terminal!); - this.terminalService.showPanel(false); + try { + this.terminalService.setActiveInstance(terminal!); + this.terminalService.showPanel(false); + } catch (e) { + // If the terminal has already been disposed, then setting the active instance will fail. #99828 + // There is nothing else to do here. + } } watchingProblemMatcher.done(); watchingProblemMatcher.dispose(); - registeredLinkMatchers.forEach(handle => terminal!.deregisterLinkMatcher(handle)); if (!processStartedSignaled) { this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal!.processId!)); processStartedSignaled = true; @@ -813,7 +846,6 @@ export class TerminalTaskSystem implements ITaskSystem { this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Active, task)); let problemMatchers = this.resolveMatchers(resolver, task.configurationProperties.problemMatchers); let startStopProblemMatcher = new StartStopProblemCollector(problemMatchers, this.markerService, this.modelService, ProblemHandlingStrategy.Clean, this.fileService); - const registeredLinkMatchers = this.registerLinkMatchers(terminal, problemMatchers); let skipLine: boolean = (!!task.command.presentation && task.command.presentation.echo); const onData = terminal.onLineData((line) => { if (skipLine) { @@ -847,16 +879,16 @@ export class TerminalTaskSystem implements ITaskSystem { this.viewsService.openView(Constants.MARKERS_VIEW_ID); } else if (terminal && (reveal === RevealKind.Silent) && ((exitCode !== 0) || (startStopProblemMatcher.numberOfMatches > 0) && startStopProblemMatcher.maxMarkerSeverity && (startStopProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error))) { - this.terminalService.setActiveInstance(terminal); - this.terminalService.showPanel(false); + try { + this.terminalService.setActiveInstance(terminal); + this.terminalService.showPanel(false); + } catch (e) { + // If the terminal has already been disposed, then setting the active instance will fail. #99828 + // There is nothing else to do here. + } } startStopProblemMatcher.done(); startStopProblemMatcher.dispose(); - registeredLinkMatchers.forEach(handle => { - if (terminal) { - terminal.deregisterLinkMatcher(handle); - } - }); if (!processStartedSignaled && terminal) { this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId!)); processStartedSignaled = true; @@ -961,7 +993,7 @@ export class TerminalTaskSystem implements ITaskSystem { windowsShellArgs = true; let basename = path.basename(shellLaunchConfig.executable!).toLowerCase(); // If we don't have a cwd, then the terminal uses the home dir. - const userHome = await this.pathService.userHome; + const userHome = await this.pathService.userHome(); if (basename === 'cmd.exe' && ((options.cwd && isUNC(options.cwd)) || (!options.cwd && isUNC(userHome.fsPath)))) { return undefined; } @@ -1012,7 +1044,7 @@ export class TerminalTaskSystem implements ITaskSystem { } else { let commandExecutable = (task.command.runtime !== RuntimeType.CustomExecution) ? CommandString.value(command) : undefined; let executable = !isShellCommand - ? this.resolveVariable(variableResolver, '${' + TerminalTaskSystem.ProcessVarName + '}') + ? this.resolveVariable(variableResolver, this.resolveVariable(variableResolver, '${' + TerminalTaskSystem.ProcessVarName + '}')) : commandExecutable; // When we have a process task there is no need to quote arguments. So we go ahead and take the string value. @@ -1048,7 +1080,7 @@ export class TerminalTaskSystem implements ITaskSystem { } } // This must be normalized to the OS - shellLaunchConfig.cwd = resources.toLocalResource(URI.from({ scheme: Schemas.file, path: cwd }), this.environmentService.configuration.remoteAuthority); + shellLaunchConfig.cwd = isUNC(cwd) ? cwd : resources.toLocalResource(URI.from({ scheme: Schemas.file, path: cwd }), this.environmentService.configuration.remoteAuthority); } if (options.env) { shellLaunchConfig.env = options.env; @@ -1279,14 +1311,11 @@ export class TerminalTaskSystem implements ITaskSystem { if (platform === Platform.Platform.Windows) { if (basename === 'cmd' && commandQuoted && argQuoted) { commandLine = '"' + commandLine + '"'; - } else if (basename === 'powershell' && commandQuoted) { + } else if ((basename === 'powershell' || basename === 'pwsh') && commandQuoted) { commandLine = '& ' + commandLine; } } - if (basename === 'cmd' && platform === Platform.Platform.Windows && commandQuoted && argQuoted) { - commandLine = '"' + commandLine + '"'; - } return commandLine; } @@ -1477,35 +1506,6 @@ export class TerminalTaskSystem implements ITaskSystem { return result; } - private registerLinkMatchers(terminal: ITerminalInstance, problemMatchers: ProblemMatcher[]): number[] { - let result: number[] = []; - /* - let handlePattern = (matcher: ProblemMatcher, pattern: ProblemPattern): void => { - if (pattern.regexp instanceof RegExp && Types.isNumber(pattern.file)) { - result.push(terminal.registerLinkMatcher(pattern.regexp, (match: string) => { - let resource: URI = getResource(match, matcher); - if (resource) { - this.workbenchEditorService.openEditor({ - resource: resource - }); - } - }, 0)); - } - }; - - for (let problemMatcher of problemMatchers) { - if (Array.isArray(problemMatcher.pattern)) { - for (let pattern of problemMatcher.pattern) { - handlePattern(problemMatcher, pattern); - } - } else if (problemMatcher.pattern) { - handlePattern(problemMatcher, problemMatcher.pattern); - } - } - */ - return result; - } - private static WellKnowCommands: IStringDictionary = { 'ant': true, 'cmake': true, diff --git a/src/vs/workbench/contrib/tasks/common/jsonSchemaCommon.ts b/src/vs/workbench/contrib/tasks/common/jsonSchemaCommon.ts index 70ccd46ec32..9cae427159b 100644 --- a/src/vs/workbench/contrib/tasks/common/jsonSchemaCommon.ts +++ b/src/vs/workbench/contrib/tasks/common/jsonSchemaCommon.ts @@ -38,6 +38,7 @@ const schema: IJSONSchema = { oneOf: [ { type: 'string', + errorMessage: nls.localize('JsonSchema.tasks.matcherError', 'Unrecognized problem matcher. Is the extension that contributes this problem matcher installed?') }, Schemas.LegacyProblemMatcher, { @@ -46,6 +47,7 @@ const schema: IJSONSchema = { anyOf: [ { type: 'string', + errorMessage: nls.localize('JsonSchema.tasks.matcherError', 'Unrecognized problem matcher. Is the extension that contributes this problem matcher installed?') }, Schemas.LegacyProblemMatcher ] diff --git a/src/vs/workbench/contrib/tasks/common/problemMatcher.ts b/src/vs/workbench/contrib/tasks/common/problemMatcher.ts index ae93c9ae103..f4d95ecf294 100644 --- a/src/vs/workbench/contrib/tasks/common/problemMatcher.ts +++ b/src/vs/workbench/contrib/tasks/common/problemMatcher.ts @@ -380,7 +380,7 @@ abstract class AbstractLineMatcher implements ILineMatcher { if (startColumn !== undefined) { return { startLineNumber: startLine, startCharacter: startColumn, endLineNumber: startLine, endCharacter: startColumn }; } - return { startLineNumber: startLine, startCharacter: 1, endLineNumber: startLine, endCharacter: Number.MAX_VALUE }; + return { startLineNumber: startLine, startCharacter: 1, endLineNumber: startLine, endCharacter: 2 ** 31 - 1 }; // See https://github.com/microsoft/vscode/issues/80288#issuecomment-650636442 for discussion } private getSeverity(data: ProblemData): MarkerSeverity { @@ -1614,7 +1614,7 @@ export namespace Schemas { } } ], - description: localize('ProblemMatcherSchema.fileLocation', 'Defines how file names reported in a problem pattern should be interpreted.') + description: localize('ProblemMatcherSchema.fileLocation', 'Defines how file names reported in a problem pattern should be interpreted. A relative fileLocation may be an array, where the second element of the array is the path the relative file location.') }, background: { type: 'object', diff --git a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts index 4ff57ccec89..28d45ddf306 100644 --- a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts +++ b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts @@ -23,8 +23,8 @@ import * as Tasks from './tasks'; import { TaskDefinitionRegistry } from './taskDefinitionRegistry'; import { ConfiguredInput } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { URI } from 'vs/base/common/uri'; -import { USER_TASKS_GROUP_KEY } from 'vs/workbench/contrib/tasks/common/taskService'; - +import { USER_TASKS_GROUP_KEY, ShellExecutionSupportedContext, ProcessExecutionSupportedContext } from 'vs/workbench/contrib/tasks/common/taskService'; +import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; export const enum ShellQuoting { /** @@ -709,6 +709,7 @@ interface ParseContext { schemaVersion: Tasks.JsonSchemaVersion; platform: Platform; taskLoadIssues: string[]; + contextKeyService: IContextKeyService; } @@ -1433,15 +1434,15 @@ namespace ConfiguringTask { let taskSource: Tasks.FileBasedTaskSource; switch (source) { case TaskConfigSource.User: { - taskSource = Objects.assign({} as Tasks.UserTaskSource, partialSource, { kind: Tasks.TaskSourceKind.User, config: configElement }); + taskSource = Object.assign({} as Tasks.UserTaskSource, partialSource, { kind: Tasks.TaskSourceKind.User, config: configElement }); break; } case TaskConfigSource.WorkspaceFile: { - taskSource = Objects.assign({} as Tasks.WorkspaceFileTaskSource, partialSource, { kind: Tasks.TaskSourceKind.WorkspaceFile, config: configElement }); + taskSource = Object.assign({} as Tasks.WorkspaceFileTaskSource, partialSource, { kind: Tasks.TaskSourceKind.WorkspaceFile, config: configElement }); break; } default: { - taskSource = Objects.assign({} as Tasks.WorkspaceTaskSource, partialSource, { kind: Tasks.TaskSourceKind.Workspace, config: configElement }); + taskSource = Object.assign({} as Tasks.WorkspaceTaskSource, partialSource, { kind: Tasks.TaskSourceKind.Workspace, config: configElement }); break; } } @@ -1456,7 +1457,7 @@ namespace ConfiguringTask { ); let configuration = ConfigurationProperties.from(external, context, true, source, typeDeclaration.properties); if (configuration) { - result.configurationProperties = Objects.assign(result.configurationProperties, configuration); + result.configurationProperties = Object.assign(result.configurationProperties, configuration); if (result.configurationProperties.name) { result._label = result.configurationProperties.name; } else { @@ -1505,15 +1506,15 @@ namespace CustomTask { let taskSource: Tasks.FileBasedTaskSource; switch (source) { case TaskConfigSource.User: { - taskSource = Objects.assign({} as Tasks.UserTaskSource, partialSource, { kind: Tasks.TaskSourceKind.User, config: { index, element: external, file: '.vscode/tasks.json', workspaceFolder: context.workspaceFolder } }); + taskSource = Object.assign({} as Tasks.UserTaskSource, partialSource, { kind: Tasks.TaskSourceKind.User, config: { index, element: external, file: '.vscode/tasks.json', workspaceFolder: context.workspaceFolder } }); break; } case TaskConfigSource.WorkspaceFile: { - taskSource = Objects.assign({} as Tasks.WorkspaceFileTaskSource, partialSource, { kind: Tasks.TaskSourceKind.WorkspaceFile, config: { index, element: external, file: '.vscode/tasks.json', workspaceFolder: context.workspaceFolder, workspace: context.workspace } }); + taskSource = Object.assign({} as Tasks.WorkspaceFileTaskSource, partialSource, { kind: Tasks.TaskSourceKind.WorkspaceFile, config: { index, element: external, file: '.vscode/tasks.json', workspaceFolder: context.workspaceFolder, workspace: context.workspace } }); break; } default: { - taskSource = Objects.assign({} as Tasks.WorkspaceTaskSource, partialSource, { kind: Tasks.TaskSourceKind.Workspace, config: { index, element: external, file: '.vscode/tasks.json', workspaceFolder: context.workspaceFolder } }); + taskSource = Object.assign({} as Tasks.WorkspaceTaskSource, partialSource, { kind: Tasks.TaskSourceKind.Workspace, config: { index, element: external, file: '.vscode/tasks.json', workspaceFolder: context.workspaceFolder } }); break; } } @@ -1533,7 +1534,7 @@ namespace CustomTask { ); let configuration = ConfigurationProperties.from(external, context, false, source); if (configuration) { - result.configurationProperties = Objects.assign(result.configurationProperties, configuration); + result.configurationProperties = Object.assign(result.configurationProperties, configuration); } let supportLegacy: boolean = true; //context.schemaVersion === Tasks.JsonSchemaVersion.V2_0_0; if (supportLegacy) { @@ -1596,7 +1597,7 @@ namespace CustomTask { export function createCustomTask(contributedTask: Tasks.ContributedTask, configuredProps: Tasks.ConfiguringTask | Tasks.CustomTask): Tasks.CustomTask { let result: Tasks.CustomTask = new Tasks.CustomTask( configuredProps._id, - Objects.assign({}, configuredProps._source, { customizes: contributedTask.defines }), + Object.assign({}, configuredProps._source, { customizes: contributedTask.defines }), configuredProps.configurationProperties.name || contributedTask._label, Tasks.CUSTOMIZED_TASK_TYPE, contributedTask.command, @@ -1656,6 +1657,11 @@ namespace TaskParser { return customize === undefined && (type === undefined || type === null || type === Tasks.CUSTOMIZED_TASK_TYPE || type === 'shell' || type === 'process'); } + const builtinTypeContextMap: IStringDictionary> = { + shell: ShellExecutionSupportedContext, + process: ProcessExecutionSupportedContext + }; + export function from(this: void, externals: Array | undefined, globals: Globals, context: ParseContext, source: TaskConfigSource): TaskParseResult { let result: TaskParseResult = { custom: [], configured: [] }; if (!externals) { @@ -1667,6 +1673,27 @@ namespace TaskParser { const baseLoadIssues = Objects.deepClone(context.taskLoadIssues); for (let index = 0; index < externals.length; index++) { let external = externals[index]; + const definition = external.type ? TaskDefinitionRegistry.get(external.type) : undefined; + let typeNotSupported: boolean = false; + if (definition && definition.when && !context.contextKeyService.contextMatchesRules(definition.when)) { + typeNotSupported = true; + } else if (!definition && external.type) { + for (const key of Object.keys(builtinTypeContextMap)) { + if (external.type === key) { + typeNotSupported = !ShellExecutionSupportedContext.evaluate(context.contextKeyService.getContext(null)); + break; + } + } + } + + if (typeNotSupported) { + context.problemReporter.info(nls.localize( + 'taskConfiguration.providerUnavailable', 'Warning: {0} tasks are unavailable in the current environment.\n', + external.type + )); + continue; + } + if (isCustomTask(external)) { let customTask = CustomTask.from(external, context, index, source); if (customTask) { @@ -1976,7 +2003,7 @@ class ConfigurationParser { this.uuidMap = uuidMap; } - public run(fileConfig: ExternalTaskRunnerConfiguration, source: TaskConfigSource): ParseResult { + public run(fileConfig: ExternalTaskRunnerConfiguration, source: TaskConfigSource, contextKeyService: IContextKeyService): ParseResult { let engine = ExecutionEngine.from(fileConfig); let schemaVersion = JsonSchemaVersion.from(fileConfig); let context: ParseContext = { @@ -1988,7 +2015,8 @@ class ConfigurationParser { engine, schemaVersion, platform: this.platform, - taskLoadIssues: [] + taskLoadIssues: [], + contextKeyService }; let taskParseResult = this.createTaskRunnerConfiguration(fileConfig, context, source); return { @@ -2024,7 +2052,7 @@ class ConfigurationParser { } context.problemReporter.error( nls.localize( - 'TaskParse.noOsSpecificGlobalTasks', + { key: 'TaskParse.noOsSpecificGlobalTasks', comment: ['\"Task version 2.0.0\" refers to the 2.0.0 version of the task system. The \"version 2.0.0\" is not localizable as it is a json key and value.'] }, 'Task version 2.0.0 doesn\'t support global OS specific tasks. Convert them to a task with a OS specific command. Affected tasks are:\n{0}', taskContent.join('\n')) ); } @@ -2043,7 +2071,7 @@ class ConfigurationParser { let name = Tasks.CommandString.value(globals.command.name); let task: Tasks.CustomTask = new Tasks.CustomTask( context.uuidMap.getUUID(name), - Objects.assign({} as Tasks.WorkspaceTaskSource, source, { config: { index: -1, element: fileConfig, workspaceFolder: context.workspaceFolder } }), + Object.assign({} as Tasks.WorkspaceTaskSource, source, { config: { index: -1, element: fileConfig, workspaceFolder: context.workspaceFolder } }), name, Tasks.CUSTOMIZED_TASK_TYPE, { @@ -2081,7 +2109,7 @@ class ConfigurationParser { let uuidMaps: Map> = new Map(); let recentUuidMaps: Map> = new Map(); -export function parse(workspaceFolder: IWorkspaceFolder, workspace: IWorkspace | undefined, platform: Platform, configuration: ExternalTaskRunnerConfiguration, logger: IProblemReporter, source: TaskConfigSource, isRecents: boolean = false): ParseResult { +export function parse(workspaceFolder: IWorkspaceFolder, workspace: IWorkspace | undefined, platform: Platform, configuration: ExternalTaskRunnerConfiguration, logger: IProblemReporter, source: TaskConfigSource, contextKeyService: IContextKeyService, isRecents: boolean = false): ParseResult { let recentOrOtherMaps = isRecents ? recentUuidMaps : uuidMaps; let selectedUuidMaps = recentOrOtherMaps.get(source); if (!selectedUuidMaps) { @@ -2095,7 +2123,7 @@ export function parse(workspaceFolder: IWorkspaceFolder, workspace: IWorkspace | } try { uuidMap.start(); - return (new ConfigurationParser(workspaceFolder, workspace, platform, logger, uuidMap)).run(configuration, source); + return (new ConfigurationParser(workspaceFolder, workspace, platform, logger, uuidMap)).run(configuration, source, contextKeyService); } finally { uuidMap.finish(); } diff --git a/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts b/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts index 1526a62d12b..4ec3b1db744 100644 --- a/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts +++ b/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts @@ -13,6 +13,7 @@ import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/workbench/serv import * as Tasks from 'vs/workbench/contrib/tasks/common/tasks'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; const taskDefinitionSchema: IJSONSchema = { @@ -35,6 +36,10 @@ const taskDefinitionSchema: IJSONSchema = { additionalProperties: { $ref: 'http://json-schema.org/draft-07/schema#' } + }, + when: { + type: 'string', + markdownDescription: nls.localize('TaskDefinition.when', 'Condition when the task definition is valid. Consider using `shellExecutionSupported`, `processExecutionSupported`, and `customExecutionSupported` as appropriate for this task definition.') } } }; @@ -44,6 +49,7 @@ namespace Configuration { type?: string; required?: string[]; properties?: IJSONSchemaMap; + when?: string; } export function from(value: TaskDefinition, extensionId: ExtensionIdentifier, messageCollector: ExtensionMessageCollector): Tasks.TaskDefinition | undefined { @@ -63,7 +69,12 @@ namespace Configuration { } } } - return { extensionId: extensionId.value, taskType, required: required, properties: value.properties ? Objects.deepClone(value.properties) : {} }; + return { + extensionId: extensionId.value, + taskType, required: required, + properties: value.properties ? Objects.deepClone(value.properties) : {}, + when: value.when ? ContextKeyExpr.deserialize(value.when) : undefined + }; } } diff --git a/src/vs/workbench/contrib/tasks/common/taskService.ts b/src/vs/workbench/contrib/tasks/common/taskService.ts index 38fd29f0995..7f395bdf770 100644 --- a/src/vs/workbench/contrib/tasks/common/taskService.ts +++ b/src/vs/workbench/contrib/tasks/common/taskService.ts @@ -13,9 +13,14 @@ import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/works import { Task, ContributedTask, CustomTask, TaskSet, TaskSorter, TaskEvent, TaskIdentifier, ConfiguringTask, TaskRunSource } from 'vs/workbench/contrib/tasks/common/tasks'; import { ITaskSummary, TaskTerminateResponse, TaskSystemInfo } from 'vs/workbench/contrib/tasks/common/taskSystem'; import { IStringDictionary } from 'vs/base/common/collections'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; export { ITaskSummary, Task, TaskTerminateResponse }; +export const CustomExecutionSupportedContext = new RawContextKey('customExecutionSupported', true); +export const ShellExecutionSupportedContext = new RawContextKey('shellExecutionSupported', false); +export const ProcessExecutionSupportedContext = new RawContextKey('processExecutionSupported', false); + export const ITaskService = createDecorator('taskService'); export interface ITaskProvider { @@ -53,7 +58,7 @@ export interface WorkspaceFolderTaskResult extends WorkspaceTaskResult { export const USER_TASKS_GROUP_KEY = 'settings'; export interface ITaskService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; onDidStateChange: Event; supportsMultipleTaskExecutions: boolean; @@ -84,12 +89,13 @@ export interface ITaskService { getTaskDescription(task: Task | ConfiguringTask): string | undefined; canCustomize(task: ContributedTask | CustomTask): boolean; - customize(task: ContributedTask | CustomTask, properties?: {}, openConfig?: boolean): Promise; - openConfig(task: CustomTask | ConfiguringTask | undefined): Promise; + customize(task: ContributedTask | CustomTask | ConfiguringTask, properties?: {}, openConfig?: boolean): Promise; + openConfig(task: CustomTask | ConfiguringTask | undefined): Promise; registerTaskProvider(taskProvider: ITaskProvider, type: string): IDisposable; registerTaskSystem(scheme: string, taskSystemInfo: TaskSystemInfo): void; + registerSupportedExecutions(custom?: boolean, shell?: boolean, process?: boolean): void; setJsonTasksSupported(areSuppored: Promise): void; extensionCallbackTaskComplete(task: Task, result: number | undefined): Promise; diff --git a/src/vs/workbench/contrib/tasks/common/taskSystem.ts b/src/vs/workbench/contrib/tasks/common/taskSystem.ts index 482eb62461e..7f5b1758e18 100644 --- a/src/vs/workbench/contrib/tasks/common/taskSystem.ts +++ b/src/vs/workbench/contrib/tasks/common/taskSystem.ts @@ -119,8 +119,9 @@ export interface TaskSystemInfo { platform: Platform; context: any; uriProvider: (this: void, path: string) => URI; - resolveVariables(workspaceFolder: IWorkspaceFolder, toResolve: ResolveSet, target: ConfigurationTarget): Promise; + resolveVariables(workspaceFolder: IWorkspaceFolder, toResolve: ResolveSet, target: ConfigurationTarget): Promise; getDefaultShellAndArgs(): Promise<{ shell: string, args: string[] | string | undefined }>; + findExecutable(command: string, cwd?: string, paths?: string[]): Promise; } export interface TaskSystemInfoResolver { diff --git a/src/vs/workbench/contrib/tasks/common/tasks.ts b/src/vs/workbench/contrib/tasks/common/tasks.ts index 754c69442c7..ec40a24a548 100644 --- a/src/vs/workbench/contrib/tasks/common/tasks.ts +++ b/src/vs/workbench/contrib/tasks/common/tasks.ts @@ -12,7 +12,7 @@ import { UriComponents, URI } from 'vs/base/common/uri'; import { ProblemMatcher } from 'vs/workbench/contrib/tasks/common/problemMatcher'; import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace'; -import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { RawContextKey, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; import { TaskDefinitionRegistry } from 'vs/workbench/contrib/tasks/common/taskDefinitionRegistry'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; @@ -601,7 +601,7 @@ export abstract class CommonTask { } public clone(): Task { - return this.fromObject(Objects.assign({}, this)); + return this.fromObject(Object.assign({}, this)); } protected abstract fromObject(object: any): Task; @@ -1002,6 +1002,7 @@ export interface TaskDefinition { taskType: string; required: string[]; properties: IJSONSchemaMap; + when?: ContextKeyExpression; } export class TaskSorter { @@ -1124,7 +1125,7 @@ export namespace KeyedTaskIdentifier { export function create(value: TaskIdentifier): KeyedTaskIdentifier { const resultKey = sortedStringify(value); let result = { _key: resultKey, type: value.taskType }; - Objects.assign(result, value); + Object.assign(result, value); return result; } } diff --git a/src/vs/workbench/contrib/tasks/node/processRunnerDetector.ts b/src/vs/workbench/contrib/tasks/node/processRunnerDetector.ts index 538cb57ad6c..e95e72a460c 100644 --- a/src/vs/workbench/contrib/tasks/node/processRunnerDetector.ts +++ b/src/vs/workbench/contrib/tasks/node/processRunnerDetector.ts @@ -363,7 +363,7 @@ export class ProcessRunnerDetector { if (taskName === build) { taskInfo.index = index; taskInfo.exact = 4; - } else if ((Strings.startsWith(taskName, build) || Strings.endsWith(taskName, build)) && taskInfo.exact < 4) { + } else if ((taskName.startsWith(build) || taskName.endsWith(build)) && taskInfo.exact < 4) { taskInfo.index = index; taskInfo.exact = 3; } else if (taskName.indexOf(build) !== -1 && taskInfo.exact < 3) { diff --git a/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts b/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts index ebc9c929933..61ad100db14 100644 --- a/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts @@ -147,7 +147,7 @@ export class ProcessTaskSystem implements ITaskSystem { if (this.childProcess) { let task = this.activeTask; return this.childProcess.terminate().then((response) => { - let result: TaskTerminateResponse = Objects.assign({ task: task! }, response); + let result: TaskTerminateResponse = Object.assign({ task: task! }, response); this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Terminated, task!)); return [result]; }); diff --git a/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts b/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts index 766b2115a83..7784109487f 100644 --- a/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts +++ b/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts @@ -14,6 +14,8 @@ import { WorkspaceFolder, Workspace, IWorkspace } from 'vs/platform/workspace/co import * as Tasks from 'vs/workbench/contrib/tasks/common/tasks'; import { parse, ParseResult, IProblemReporter, ExternalTaskRunnerConfiguration, CustomTask, TaskConfigSource } from 'vs/workbench/contrib/tasks/common/taskConfiguration'; +import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; +import { IContext } from 'vs/platform/contextkey/common/contextkey'; const workspaceFolder: WorkspaceFolder = new WorkspaceFolder({ uri: URI.file('/workspace/folderOne'), @@ -357,9 +359,19 @@ class PatternBuilder { } } +class TasksMockContextKeyService extends MockContextKeyService { + public getContext(domNode: HTMLElement): IContext { + return { + getValue: (_key: string) => { + return true; + } + }; + } +} + function testDefaultProblemMatcher(external: ExternalTaskRunnerConfiguration, resolved: number) { let reporter = new ProblemReporter(); - let result = parse(workspaceFolder, workspace, Platform.platform, external, reporter, TaskConfigSource.TasksJson); + let result = parse(workspaceFolder, workspace, Platform.platform, external, reporter, TaskConfigSource.TasksJson, new TasksMockContextKeyService()); assert.ok(!reporter.receivedMessage); assert.strictEqual(result.custom.length, 1); let task = result.custom[0]; @@ -370,7 +382,7 @@ function testDefaultProblemMatcher(external: ExternalTaskRunnerConfiguration, re function testConfiguration(external: ExternalTaskRunnerConfiguration, builder: ConfiguationBuilder): void { builder.done(); let reporter = new ProblemReporter(); - let result = parse(workspaceFolder, workspace, Platform.platform, external, reporter, TaskConfigSource.TasksJson); + let result = parse(workspaceFolder, workspace, Platform.platform, external, reporter, TaskConfigSource.TasksJson, new TasksMockContextKeyService()); if (reporter.receivedMessage) { assert.ok(false, reporter.lastMessage); } diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalExternalLinkProviderAdapter.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalExternalLinkProviderAdapter.ts new file mode 100644 index 00000000000..40478f7f7f9 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalExternalLinkProviderAdapter.ts @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Terminal, IViewportRange, IBufferLine } from 'xterm'; +import { getXtermLineContent, convertLinkRangeToBuffer } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers'; +import { TerminalLink } from 'vs/workbench/contrib/terminal/browser/links/terminalLink'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { TerminalBaseLinkProvider } from 'vs/workbench/contrib/terminal/browser/links/terminalBaseLinkProvider'; +import { ITerminalExternalLinkProvider, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { XtermLinkMatcherHandler } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager'; + +/** + * An adapter to convert a simple external link provider into an internal link provider that + * manages link lifecycle, hovers, etc. and gets registered in xterm.js. + */ +export class TerminalExternalLinkProviderAdapter extends TerminalBaseLinkProvider { + + constructor( + private readonly _xterm: Terminal, + private readonly _instance: ITerminalInstance, + private readonly _externalLinkProvider: ITerminalExternalLinkProvider, + private readonly _wrapLinkHandler: (handler: (event: MouseEvent | undefined, link: string) => void) => XtermLinkMatcherHandler, + private readonly _tooltipCallback: (link: TerminalLink, viewportRange: IViewportRange, modifierDownCallback?: () => void, modifierUpCallback?: () => void) => void, + @IInstantiationService private readonly _instantiationService: IInstantiationService + ) { + super(); + } + + protected async _provideLinks(y: number): Promise { + let startLine = y - 1; + let endLine = startLine; + + const lines: IBufferLine[] = [ + this._xterm.buffer.active.getLine(startLine)! + ]; + + while (this._xterm.buffer.active.getLine(startLine)?.isWrapped) { + lines.unshift(this._xterm.buffer.active.getLine(startLine - 1)!); + startLine--; + } + + while (this._xterm.buffer.active.getLine(endLine + 1)?.isWrapped) { + lines.push(this._xterm.buffer.active.getLine(endLine + 1)!); + endLine++; + } + + const lineContent = getXtermLineContent(this._xterm.buffer.active, startLine, endLine, this._xterm.cols); + if (lineContent.trim().length === 0) { + return []; + } + + const externalLinks = await this._externalLinkProvider.provideLinks(this._instance, lineContent); + if (!externalLinks) { + return []; + } + + return externalLinks.map(link => { + const bufferRange = convertLinkRangeToBuffer(lines, this._xterm.cols, { + startColumn: link.startIndex + 1, + startLineNumber: 1, + endColumn: link.startIndex + link.length + 1, + endLineNumber: 1 + }, startLine); + const matchingText = lineContent.substr(link.startIndex, link.length) || ''; + const activateLink = this._wrapLinkHandler((_, text) => link.activate(text)); + return this._instantiationService.createInstance(TerminalLink, bufferRange, matchingText, this._xterm.buffer.active.viewportY, activateLink, this._tooltipCallback, true, link.label); + }); + } +} diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLink.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLink.ts index 2754618238f..d0d122fae55 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLink.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLink.ts @@ -68,20 +68,24 @@ export class TerminalLink extends DisposableStore implements ILink { } })); - const timeout = this._configurationService.getValue('editor.hover.delay'); - this._tooltipScheduler = new RunOnceScheduler(() => { - this._tooltipCallback( - this, - convertBufferRangeToViewport(this.range, this._viewportY), - this._isHighConfidenceLink ? () => this._enableDecorations() : undefined, - this._isHighConfidenceLink ? () => this._disableDecorations() : undefined - ); - // Clear out scheduler until next hover event - this._tooltipScheduler?.dispose(); - this._tooltipScheduler = undefined; - }, timeout); - this.add(this._tooltipScheduler); - this._tooltipScheduler.schedule(); + // Only show the tooltip and highlight for high confidence links (not word/search workspace + // links). Feedback was that this makes using the terminal overly noisy. + if (this._isHighConfidenceLink) { + const timeout = this._configurationService.getValue('editor.hover.delay'); + this._tooltipScheduler = new RunOnceScheduler(() => { + this._tooltipCallback( + this, + convertBufferRangeToViewport(this.range, this._viewportY), + this._isHighConfidenceLink ? () => this._enableDecorations() : undefined, + this._isHighConfidenceLink ? () => this._disableDecorations() : undefined + ); + // Clear out scheduler until next hover event + this._tooltipScheduler?.dispose(); + this._tooltipScheduler = undefined; + }, timeout); + this.add(this._tooltipScheduler); + this._tooltipScheduler.schedule(); + } const origin = { x: event.pageX, y: event.pageY }; this._hoverListeners = new DisposableStore(); diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers.ts index 1514b48a940..de8cb63d107 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers.ts @@ -93,12 +93,17 @@ export function convertBufferRangeToViewport(bufferRange: IBufferRange, viewport }; } -export function getXtermLineContent(buffer: IBuffer, lineStart: number, lineEnd: number): string { - let line = ''; +export function getXtermLineContent(buffer: IBuffer, lineStart: number, lineEnd: number, cols: number): string { + let content = ''; for (let i = lineStart; i <= lineEnd; i++) { - line += buffer.getLine(i)?.translateToString(true); + // Make sure only 0 to cols are considered as resizing when windows mode is enabled will + // retain buffer data outside of the terminal width as reflow is disabled. + const line = buffer.getLine(i); + if (line) { + content += line.translateToString(true, 0, cols); + } } - return line; + return content; } export function positionIsInRange(position: IBufferCellPosition, range: IBufferRange): boolean { diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts index c8e0235f257..64434e13548 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts @@ -9,64 +9,26 @@ import { DisposableStore, IDisposable, dispose } from 'vs/base/common/lifecycle' import { IOpenerService } from 'vs/platform/opener/common/opener'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ITerminalProcessManager, ITerminalConfigHelper, ITerminalConfiguration, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalProcessManager, ITerminalConfiguration, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal'; import { ITextEditorSelection } from 'vs/platform/editor/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IFileService } from 'vs/platform/files/common/files'; -import { Terminal, ILinkMatcherOptions, IViewportRange, ITerminalAddon } from 'xterm'; +import { Terminal, IViewportRange, ILinkProvider } from 'xterm'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { posix, win32 } from 'vs/base/common/path'; -import { ITerminalInstanceService, ITerminalBeforeHandleLinkEvent, LINK_INTERCEPT_THRESHOLD } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalBeforeHandleLinkEvent, LINK_INTERCEPT_THRESHOLD, ITerminalExternalLinkProvider, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { OperatingSystem, isMacintosh, OS } from 'vs/base/common/platform'; import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; import { Emitter, Event } from 'vs/base/common/event'; import { ILogService } from 'vs/platform/log/common/log'; import { TerminalProtocolLinkProvider } from 'vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider'; -import { TerminalValidatedLocalLinkProvider } from 'vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider'; +import { TerminalValidatedLocalLinkProvider, lineAndColumnClause, unixLocalLinkClause, winLocalLinkClause, winDrivePrefix, winLineAndColumnMatchIndex, unixLineAndColumnMatchIndex, lineAndColumnClauseGroupCount } from 'vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider'; import { TerminalWordLinkProvider } from 'vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { XTermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private'; import { TerminalHover, ILinkHoverTargetOptions } from 'vs/workbench/contrib/terminal/browser/widgets/terminalHoverWidget'; import { TerminalLink } from 'vs/workbench/contrib/terminal/browser/links/terminalLink'; - -const pathPrefix = '(\\.\\.?|\\~)'; -const pathSeparatorClause = '\\/'; -// '":; are allowed in paths but they are often separators so ignore them -// Also disallow \\ to prevent a catastropic backtracking case #24798 -const excludedPathCharactersClause = '[^\\0\\s!$`&*()\\[\\]+\'":;\\\\]'; -/** A regex that matches paths in the form /foo, ~/foo, ./foo, ../foo, foo/bar */ -const unixLocalLinkClause = '((' + pathPrefix + '|(' + excludedPathCharactersClause + ')+)?(' + pathSeparatorClause + '(' + excludedPathCharactersClause + ')+)+)'; - -// Valid absolute formats: C:, \\?\C: and \\?\%VAR% -const winDrivePrefix = '(?:\\\\\\\\\\?\\\\)?[a-zA-Z]:'; -const winPathPrefix = '(' + winDrivePrefix + '|\\.\\.?|\\~)'; -const winPathSeparatorClause = '(\\\\|\\/)'; -const winExcludedPathCharactersClause = '[^\\0<>\\?\\|\\/\\s!$`&*()\\[\\]+\'":;]'; -/** A regex that matches paths in the form \\?\c:\foo c:\foo, ~\foo, .\foo, ..\foo, foo\bar */ -const winLocalLinkClause = '((' + winPathPrefix + '|(' + winExcludedPathCharactersClause + ')+)?(' + winPathSeparatorClause + '(' + winExcludedPathCharactersClause + ')+)+)'; - -/** As xterm reads from DOM, space in that case is nonbreaking char ASCII code - 160, -replacing space with nonBreakningSpace or space ASCII code - 32. */ -const lineAndColumnClause = [ - '((\\S*)", line ((\\d+)( column (\\d+))?))', // "(file path)", line 45 [see #40468] - '((\\S*)",((\\d+)(:(\\d+))?))', // "(file path)",45 [see #78205] - '((\\S*) on line ((\\d+)(, column (\\d+))?))', // (file path) on line 8, column 13 - '((\\S*):line ((\\d+)(, column (\\d+))?))', // (file path):line 8, column 13 - '(([^\\s\\(\\)]*)(\\s?[\\(\\[](\\d+)(,\\s?(\\d+))?)[\\)\\]])', // (file path)(45), (file path) (45), (file path)(45,18), (file path) (45,18), (file path)(45, 18), (file path) (45, 18), also with [] - '(([^:\\s\\(\\)<>\'\"\\[\\]]*)(:(\\d+))?(:(\\d+))?)' // (file path):336, (file path):336:9 -].join('|').replace(/ /g, `[${'\u00A0'} ]`); - -// Changing any regex may effect this value, hence changes this as well if required. -const winLineAndColumnMatchIndex = 12; -const unixLineAndColumnMatchIndex = 11; - -// Each line and column clause have 6 groups (ie no. of expressions in round brackets) -const lineAndColumnClauseGroupCount = 6; - -/** Higher than local link, lower than hypertext */ -const CUSTOM_LINK_PRIORITY = -1; -/** Lowest */ -const LOCAL_LINK_PRIORITY = -2; +import { TerminalExternalLinkProviderAdapter } from 'vs/workbench/contrib/terminal/browser/links/terminalExternalLinkProviderAdapter'; export type XtermLinkMatcherHandler = (event: MouseEvent | undefined, link: string) => Promise; export type XtermLinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void; @@ -82,12 +44,9 @@ interface IPath { export class TerminalLinkManager extends DisposableStore { private _widgetManager: TerminalWidgetManager | undefined; private _processCwd: string | undefined; - private _gitDiffPreImagePattern: RegExp; - private _gitDiffPostImagePattern: RegExp; - private _linkMatchers: number[] = []; - private _webLinksAddon: ITerminalAddon | undefined; - private _linkProviders: IDisposable[] = []; private _hasBeforeHandleLinkListeners = false; + private _standardLinkProviders: ILinkProvider[] = []; + private _standardLinkProvidersDisposables: IDisposable[] = []; protected static _LINK_INTERCEPT_THRESHOLD = LINK_INTERCEPT_THRESHOLD; public static readonly LINK_INTERCEPT_THRESHOLD = TerminalLinkManager._LINK_INTERCEPT_THRESHOLD; @@ -106,62 +65,38 @@ export class TerminalLinkManager extends DisposableStore { constructor( private _xterm: Terminal, private readonly _processManager: ITerminalProcessManager, - private readonly _configHelper: ITerminalConfigHelper, @IOpenerService private readonly _openerService: IOpenerService, @IEditorService private readonly _editorService: IEditorService, @IConfigurationService private readonly _configurationService: IConfigurationService, - @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService, @IFileService private readonly _fileService: IFileService, @ILogService private readonly _logService: ILogService, @IInstantiationService private readonly _instantiationService: IInstantiationService ) { super(); - // Matches '--- a/src/file1', capturing 'src/file1' in group 1 - this._gitDiffPreImagePattern = /^--- a\/(\S*)/; - // Matches '+++ b/src/file1', capturing 'src/file1' in group 1 - this._gitDiffPostImagePattern = /^\+\+\+ b\/(\S*)/; + // Protocol links + const wrappedActivateCallback = this._wrapLinkHandler((_, link) => this._handleProtocolLink(link)); + const protocolProvider = this._instantiationService.createInstance(TerminalProtocolLinkProvider, this._xterm, wrappedActivateCallback, this._tooltipCallback2.bind(this)); + this._standardLinkProviders.push(protocolProvider); - if (this._configHelper.config.experimentalLinkProvider) { - this.registerLinkProvider(); - } else { - this._registerLinkMatchers(); + // Validated local links + if (this._configurationService.getValue(TERMINAL_CONFIG_SECTION).enableFileLinks) { + const wrappedTextLinkActivateCallback = this._wrapLinkHandler((_, link) => this._handleLocalLink(link)); + const validatedProvider = this._instantiationService.createInstance(TerminalValidatedLocalLinkProvider, + this._xterm, + this._processManager.os || OS, + wrappedTextLinkActivateCallback, + this._wrapLinkHandler.bind(this), + this._tooltipCallback2.bind(this), + async (link, cb) => cb(await this._resolvePath(link))); + this._standardLinkProviders.push(validatedProvider); } - this._configurationService?.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('terminal.integrated.experimentalLinkProvider')) { - if (this._configHelper.config.experimentalLinkProvider) { - this._deregisterLinkMatchers(); - this.registerLinkProvider(); - } else { - dispose(this._linkProviders); - this._linkProviders.length = 0; - this._registerLinkMatchers(); - } - } - }); - } + // Word links + const wordProvider = this._instantiationService.createInstance(TerminalWordLinkProvider, this._xterm, this._wrapLinkHandler.bind(this), this._tooltipCallback2.bind(this)); + this._standardLinkProviders.push(wordProvider); - private _tooltipCallback(linkText: string, viewportRange: IViewportRange, linkHandler: (url: string) => void) { - if (!this._widgetManager) { - return; - } - - const core = (this._xterm as any)._core as XTermCore; - const cellDimensions = { - width: core._renderService.dimensions.actualCellWidth, - height: core._renderService.dimensions.actualCellHeight - }; - const terminalDimensions = { - width: this._xterm.cols, - height: this._xterm.rows - }; - - this._showHover({ - viewportRange, - cellDimensions, - terminalDimensions - }, this._getLinkHoverString(linkText, undefined), linkHandler); + this._registerStandardLinkProviders(); } private _tooltipCallback2(link: TerminalLink, viewportRange: IViewportRange, modifierDownCallback?: () => void, modifierUpCallback?: () => void) { @@ -204,24 +139,6 @@ export class TerminalLinkManager extends DisposableStore { } } - private _registerLinkMatchers() { - this.registerWebLinkHandler(); - if (this._processManager) { - if (this._configHelper.config.enableFileLinks) { - this.registerLocalLinkHandler(); - } - this.registerGitDiffLinkHandlers(); - } - } - - private _deregisterLinkMatchers() { - this._webLinksAddon?.dispose(); - - this._linkMatchers.forEach(matcherId => { - this._xterm.deregisterLinkMatcher(matcherId); - }); - } - public setWidgetManager(widgetManager: TerminalWidgetManager): void { this._widgetManager = widgetManager; } @@ -230,93 +147,20 @@ export class TerminalLinkManager extends DisposableStore { this._processCwd = processCwd; } - public registerCustomLinkHandler(regex: RegExp, handler: (event: MouseEvent | undefined, uri: string) => void, matchIndex?: number, validationCallback?: XtermLinkMatcherValidationCallback): number { - const tooltipCallback = (_: MouseEvent, linkText: string, location: IViewportRange) => { - this._tooltipCallback(linkText, location, text => handler(undefined, text)); - }; - const options: ILinkMatcherOptions = { - matchIndex, - tooltipCallback, - willLinkActivate: (e: MouseEvent) => this._isLinkActivationModifierDown(e), - priority: CUSTOM_LINK_PRIORITY - }; - if (validationCallback) { - options.validationCallback = (uri: string, callback: (isValid: boolean) => void) => validationCallback(uri, callback); + private _registerStandardLinkProviders(): void { + dispose(this._standardLinkProvidersDisposables); + this._standardLinkProvidersDisposables = []; + for (const p of this._standardLinkProviders) { + this._standardLinkProvidersDisposables.push(this._xterm.registerLinkProvider(p)); } - return this._xterm.registerLinkMatcher(regex, this._wrapLinkHandler(handler), options); } - public registerWebLinkHandler(): void { - this._terminalInstanceService.getXtermWebLinksConstructor().then((WebLinksAddon) => { - if (!this._xterm) { - return; - } - const wrappedHandler = this._wrapLinkHandler((_, link) => this._handleHypertextLink(link)); - const tooltipCallback = (_: MouseEvent, linkText: string, location: IViewportRange) => { - this._tooltipCallback(linkText, location, this._handleHypertextLink.bind(this)); - }; - this._webLinksAddon = new WebLinksAddon(wrappedHandler, { - validationCallback: (uri: string, callback: (isValid: boolean) => void) => this._validateWebLink(callback), - tooltipCallback, - willLinkActivate: (e: MouseEvent) => this._isLinkActivationModifierDown(e) - }); - this._xterm.loadAddon(this._webLinksAddon); - }); - } - - public registerLocalLinkHandler(): void { - const wrappedHandler = this._wrapLinkHandler((_, url) => this._handleLocalLink(url)); - const tooltipCallback = (event: MouseEvent, linkText: string, location: IViewportRange) => { - this._tooltipCallback(linkText, location, this._handleLocalLink.bind(this)); - }; - this._linkMatchers.push(this._xterm.registerLinkMatcher(this._localLinkRegex, wrappedHandler, { - validationCallback: (uri: string, callback: (isValid: boolean) => void) => this._validateLocalLink(uri, callback), - tooltipCallback, - willLinkActivate: (e: MouseEvent) => this._isLinkActivationModifierDown(e), - priority: LOCAL_LINK_PRIORITY - })); - } - - public registerGitDiffLinkHandlers(): void { - const wrappedHandler = this._wrapLinkHandler((_, url) => { - this._handleLocalLink(url); - }); - const tooltipCallback = (event: MouseEvent, linkText: string, location: IViewportRange) => { - this._tooltipCallback(linkText, location, this._handleLocalLink.bind(this)); - }; - const options = { - matchIndex: 1, - validationCallback: (uri: string, callback: (isValid: boolean) => void) => this._validateLocalLink(uri, callback), - tooltipCallback, - willLinkActivate: (e: MouseEvent) => this._isLinkActivationModifierDown(e), - priority: LOCAL_LINK_PRIORITY - }; - this._linkMatchers.push(this._xterm.registerLinkMatcher(this._gitDiffPreImagePattern, wrappedHandler, options)); - this._linkMatchers.push(this._xterm.registerLinkMatcher(this._gitDiffPostImagePattern, wrappedHandler, options)); - } - - public registerLinkProvider(): void { - // Protocol links - const wrappedActivateCallback = this._wrapLinkHandler((_, link) => this._handleProtocolLink(link)); - const protocolProvider = this._instantiationService.createInstance(TerminalProtocolLinkProvider, this._xterm, wrappedActivateCallback, this._tooltipCallback2.bind(this)); - this._linkProviders.push(this._xterm.registerLinkProvider(protocolProvider)); - - // Validated local links - if (this._configurationService.getValue(TERMINAL_CONFIG_SECTION).enableFileLinks) { - const wrappedTextLinkActivateCallback = this._wrapLinkHandler((_, link) => this._handleLocalLink(link)); - const validatedProvider = this._instantiationService.createInstance(TerminalValidatedLocalLinkProvider, - this._xterm, - this._processManager.os || OS, - wrappedTextLinkActivateCallback, - this._wrapLinkHandler.bind(this), - this._tooltipCallback2.bind(this), - async (link, cb) => cb(await this._resolvePath(link))); - this._linkProviders.push(this._xterm.registerLinkProvider(validatedProvider)); - } - - // Word links - const wordProvider = this._instantiationService.createInstance(TerminalWordLinkProvider, this._xterm, this._wrapLinkHandler.bind(this), this._tooltipCallback2.bind(this)); - this._linkProviders.push(this._xterm.registerLinkProvider(wordProvider)); + public registerExternalLinkProvider(instance: ITerminalInstance, linkProvider: ITerminalExternalLinkProvider): IDisposable { + const wrappedLinkProvider = this._instantiationService.createInstance(TerminalExternalLinkProviderAdapter, this._xterm, instance, linkProvider, this._wrapLinkHandler.bind(this), this._tooltipCallback2.bind(this)); + const newLinkProvider = this._xterm.registerLinkProvider(wrappedLinkProvider); + // Re-register the standard link providers so they are a lower priority that the new one + this._registerStandardLinkProviders(); + return newLinkProvider; } protected _wrapLinkHandler(handler: (event: MouseEvent | undefined, link: string) => void): XtermLinkMatcherHandler { @@ -370,14 +214,6 @@ export class TerminalLinkManager extends DisposableStore { return new RegExp(`${baseLocalLinkClause}(${lineAndColumnClause})`); } - protected get _gitDiffPreImageRegex(): RegExp { - return this._gitDiffPreImagePattern; - } - - protected get _gitDiffPostImageRegex(): RegExp { - return this._gitDiffPostImagePattern; - } - private async _handleLocalLink(link: string): Promise { // TODO: This gets resolved again but doesn't need to as it's already validated const resolvedLink = await this._resolvePath(link); @@ -392,14 +228,6 @@ export class TerminalLinkManager extends DisposableStore { await this._editorService.openEditor({ resource: resolvedLink.uri, options: { pinned: true, selection } }); } - private _validateLocalLink(link: string, callback: (isValid: boolean) => void): void { - this._resolvePath(link).then(resolvedLink => callback(!!resolvedLink)); - } - - private _validateWebLink(callback: (isValid: boolean) => void): void { - callback(true); - } - private _handleHypertextLink(url: string): void { this._openerService.open(url, { allowTunneling: !!(this._processManager && this._processManager.remoteAuthority) }); } @@ -443,7 +271,8 @@ export class TerminalLinkManager extends DisposableStore { } } - return new MarkdownString(`[${label || nls.localize('followLink', "Follow Link")}](${uri}) (${clickLabel})`, true); + // Use 'undefined' when uri is '' so the link displays correctly + return new MarkdownString(`[${label || nls.localize('followLink', "Follow Link")}](${uri || 'undefined'}) (${clickLabel})`, true); } private get osPath(): IPath { diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider.ts index 6179c58e7bf..2e47dfe72ef 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider.ts @@ -69,6 +69,6 @@ class TerminalLinkAdapter implements ILinkComputerTarget { } getLineContent(): string { - return getXtermLineContent(this._xterm.buffer.active, this._lineStart, this._lineEnd); + return getXtermLineContent(this._xterm.buffer.active, this._lineStart, this._lineEnd, this._xterm.cols); } } diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts index 8f9b10962a4..287afdf9e1a 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts @@ -22,18 +22,18 @@ const pathSeparatorClause = '\\/'; // Also disallow \\ to prevent a catastropic backtracking case #24798 const excludedPathCharactersClause = '[^\\0\\s!$`&*()\\[\\]+\'":;\\\\]'; /** A regex that matches paths in the form /foo, ~/foo, ./foo, ../foo, foo/bar */ -const unixLocalLinkClause = '((' + pathPrefix + '|(' + excludedPathCharactersClause + ')+)?(' + pathSeparatorClause + '(' + excludedPathCharactersClause + ')+)+)'; +export const unixLocalLinkClause = '((' + pathPrefix + '|(' + excludedPathCharactersClause + ')+)?(' + pathSeparatorClause + '(' + excludedPathCharactersClause + ')+)+)'; -const winDrivePrefix = '(?:\\\\\\\\\\?\\\\)?[a-zA-Z]:'; +export const winDrivePrefix = '(?:\\\\\\\\\\?\\\\)?[a-zA-Z]:'; const winPathPrefix = '(' + winDrivePrefix + '|\\.\\.?|\\~)'; const winPathSeparatorClause = '(\\\\|\\/)'; const winExcludedPathCharactersClause = '[^\\0<>\\?\\|\\/\\s!$`&*()\\[\\]+\'":;]'; /** A regex that matches paths in the form \\?\c:\foo c:\foo, ~\foo, .\foo, ..\foo, foo\bar */ -const winLocalLinkClause = '((' + winPathPrefix + '|(' + winExcludedPathCharactersClause + ')+)?(' + winPathSeparatorClause + '(' + winExcludedPathCharactersClause + ')+)+)'; +export const winLocalLinkClause = '((' + winPathPrefix + '|(' + winExcludedPathCharactersClause + ')+)?(' + winPathSeparatorClause + '(' + winExcludedPathCharactersClause + ')+)+)'; /** As xterm reads from DOM, space in that case is nonbreaking char ASCII code - 160, replacing space with nonBreakningSpace or space ASCII code - 32. */ -const lineAndColumnClause = [ +export const lineAndColumnClause = [ '((\\S*)", line ((\\d+)( column (\\d+))?))', // "(file path)", line 45 [see #40468] '((\\S*)",((\\d+)(:(\\d+))?))', // "(file path)",45 [see #78205] '((\\S*) on line ((\\d+)(, column (\\d+))?))', // (file path) on line 8, column 13 @@ -42,6 +42,13 @@ const lineAndColumnClause = [ '(([^:\\s\\(\\)<>\'\"\\[\\]]*)(:(\\d+))?(:(\\d+))?)' // (file path):336, (file path):336:9 ].join('|').replace(/ /g, `[${'\u00A0'} ]`); +// Changing any regex may effect this value, hence changes this as well if required. +export const winLineAndColumnMatchIndex = 12; +export const unixLineAndColumnMatchIndex = 11; + +// Each line and column clause have 6 groups (ie no. of expressions in round brackets) +export const lineAndColumnClauseGroupCount = 6; + export class TerminalValidatedLocalLinkProvider extends TerminalBaseLinkProvider { constructor( private readonly _xterm: Terminal, @@ -77,7 +84,7 @@ export class TerminalValidatedLocalLinkProvider extends TerminalBaseLinkProvider endLine++; } - const text = getXtermLineContent(this._xterm.buffer.active, startLine, endLine); + const text = getXtermLineContent(this._xterm.buffer.active, startLine, endLine, this._xterm.cols); // clone regex to do a global search on text const rex = new RegExp(this._localLinkRegex, 'g'); diff --git a/src/vs/workbench/contrib/terminal/browser/media/scrollbar.css b/src/vs/workbench/contrib/terminal/browser/media/scrollbar.css index aaa2d80f59e..da4ecfc43b8 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/scrollbar.css +++ b/src/vs/workbench/contrib/terminal/browser/media/scrollbar.css @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ .monaco-workbench .pane-body.integrated-terminal .xterm-viewport { - /* Use the hack presented in http://stackoverflow.com/a/38748186/1156119 to get opacity transitions working on the scrollbar */ + /* Use the hack presented in https://stackoverflow.com/a/38748186/1156119 to get opacity transitions working on the scrollbar */ -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 0f4639d7526..43de91e60fb 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -119,8 +119,8 @@ outline: none; } -.hc-black .monaco-workbench .pane-body.integrated-terminal .xterm.focus::before, -.hc-black .monaco-workbench .pane-body.integrated-terminal .xterm:focus::before { +.monaco-workbench.hc-black .pane-body.integrated-terminal .xterm.focus::before, +.monaco-workbench.hc-black .pane-body.integrated-terminal .xterm:focus::before { display: block; content: ""; border: 1px solid; @@ -132,8 +132,8 @@ z-index: 10; } -.hc-black .monaco-workbench .pane-body.integrated-terminal .monaco-split-view2.horizontal .split-view-view:not(:only-child) .xterm.focus::before, -.hc-black .monaco-workbench .pane-body.integrated-terminal .monaco-split-view2.horizontal .split-view-view:not(:only-child) .xterm:focus::before { +.monaco-workbench.hc-black .pane-body.integrated-terminal .monaco-split-view2.horizontal .split-view-view:not(:only-child) .xterm.focus::before, +.monaco-workbench.hc-black .pane-body.integrated-terminal .monaco-split-view2.horizontal .split-view-view:not(:only-child) .xterm:focus::before { right: 0; } @@ -147,8 +147,8 @@ opacity: 0 !important; } -.vs-dark .monaco-workbench.mac .pane-body.integrated-terminal .terminal-outer-container:not(.alt-active) .terminal:not(.enable-mouse-events), -.hc-black .monaco-workbench.mac .pane-body.integrated-terminal .terminal-outer-container:not(.alt-active) .terminal:not(.enable-mouse-events) { +.monaco-workbench.vs-dark.mac .pane-body.integrated-terminal .terminal-outer-container:not(.alt-active) .terminal:not(.enable-mouse-events), +.monaco-workbench.hc-black.mac .pane-body.integrated-terminal .terminal-outer-container:not(.alt-active) .terminal:not(.enable-mouse-events) { 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/workbench/contrib/terminal/browser/media/widgets.css b/src/vs/workbench/contrib/terminal/browser/media/widgets.css index ca64da196e6..51a9ab8eff2 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/widgets.css +++ b/src/vs/workbench/contrib/terminal/browser/media/widgets.css @@ -12,29 +12,6 @@ overflow: visible; } -.monaco-workbench .terminal-hover-widget { - position: fixed; - font-size: 14px; - line-height: 19px; - animation: fadein 100ms linear; - /* Must be higher than sash's z-index and terminal canvases */ - z-index: 40; - overflow: hidden; -} - -.monaco-workbench .terminal-hover-widget a { - color: #3794ff; -} - -.monaco-workbench .terminal-hover-widget.right-aligned .hover-row.status-bar .actions { - flex-direction: row-reverse; -} - -.monaco-workbench .terminal-hover-widget.right-aligned .hover-row.status-bar .actions .action-container { - margin-right: 0; - margin-left: 16px; -} - .monaco-workbench .terminal-overlay-widget { position: absolute; left: 0; @@ -58,15 +35,16 @@ opacity: 0.5; } +.monaco-workbench .terminal-env-var-info:hover, +.monaco-workbench .terminal-env-var-info.requires-action { + opacity: 1; +} + .monaco-workbench .pane-body.integrated-terminal .monaco-split-view2.horizontal .split-view-view:last-child .terminal-env-var-info { /* Adjust for reduced margin in splits */ right: -8px; } -.monaco-workbench .terminal-env-var-info:hover { - opacity: 1; -} - .monaco-workbench .terminal-env-var-info.codicon { line-height: 28px; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 68c6d3fb1b6..a2915b88d67 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -141,6 +141,9 @@ function registerSendSequenceKeybinding(text: string, rule: { when?: ContextKeyE }); } +// The text representation of `^` is `'A'.charCodeAt(0) + 1`. +const CTRL_LETTER_OFFSET = 64; + if (BrowserFeatures.clipboard.readText) { actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(TerminalPasteAction, { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, @@ -152,7 +155,7 @@ if (BrowserFeatures.clipboard.readText) { // disabled in accessibility mode as PowerShell does not run PSReadLine when it detects a screen // reader. if (platform.isWindows) { - registerSendSequenceKeybinding(String.fromCharCode('V'.charCodeAt(0) - 64), { // ctrl+v + registerSendSequenceKeybinding(String.fromCharCode('V'.charCodeAt(0) - CTRL_LETTER_OFFSET), { // ctrl+v when: ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, ContextKeyExpr.equals(KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, WindowsShellType.PowerShell), CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate()), primary: KeyMod.CtrlCmd | KeyCode.KEY_V }); @@ -160,10 +163,18 @@ if (BrowserFeatures.clipboard.readText) { } // Delete word left: ctrl+w -registerSendSequenceKeybinding(String.fromCharCode('W'.charCodeAt(0) - 64), { +registerSendSequenceKeybinding(String.fromCharCode('W'.charCodeAt(0) - CTRL_LETTER_OFFSET), { primary: KeyMod.CtrlCmd | KeyCode.Backspace, mac: { primary: KeyMod.Alt | KeyCode.Backspace } }); +if (platform.isWindows) { + // Delete word left: ctrl+h + // Windows cmd.exe requires ^H to delete full word left + registerSendSequenceKeybinding(String.fromCharCode('H'.charCodeAt(0) - CTRL_LETTER_OFFSET), { + when: ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, ContextKeyExpr.equals(KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, WindowsShellType.CommandPrompt)), + primary: KeyMod.CtrlCmd | KeyCode.Backspace, + }); +} // Delete word right: alt+d registerSendSequenceKeybinding('\x1bd', { primary: KeyMod.CtrlCmd | KeyCode.Delete, diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 7cdaa84832d..39098571234 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -6,9 +6,8 @@ import { Terminal as XTermTerminal } from 'xterm'; import { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; import { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; -import { WebLinksAddon as XTermWebLinksAddon } from 'xterm-addon-web-links'; import { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl'; -import { IWindowsShellHelper, ITerminalConfigHelper, ITerminalChildProcess, IShellLaunchConfig, IDefaultShellAndArgsRequest, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, ITerminalProcessExtHostProxy, ICommandTracker, INavigationMode, TitleEventSource, ITerminalDimensions } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IWindowsShellHelper, ITerminalConfigHelper, ITerminalChildProcess, IShellLaunchConfig, IDefaultShellAndArgsRequest, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, ITerminalProcessExtHostProxy, ICommandTracker, INavigationMode, TitleEventSource, ITerminalDimensions, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProcessEnvironment, Platform } from 'vs/base/common/platform'; import { Event } from 'vs/base/common/event'; @@ -25,7 +24,7 @@ export const ITerminalInstanceService = createDecorator; @@ -33,7 +32,6 @@ export interface ITerminalInstanceService { getXtermConstructor(): Promise; getXtermSearchConstructor(): Promise; getXtermUnicode11Constructor(): Promise; - getXtermWebLinksConstructor(): Promise; getXtermWebglConstructor(): Promise; createWindowsShellHelper(shellProcessId: number, xterm: XTermTerminal): IWindowsShellHelper; createTerminalProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean): ITerminalChildProcess; @@ -72,7 +70,7 @@ export interface ITerminalTab { } export interface ITerminalService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; activeTabIndex: number; configHelper: ITerminalConfigHelper; @@ -146,6 +144,14 @@ export interface ITerminalService { */ addLinkHandler(key: string, callback: TerminalLinkHandlerCallback): IDisposable; + /** + * Registers a link provider that enables integrators to add links to the terminal. + * @param linkProvider When registered, the link provider is asked whenever a cell is hovered + * for links at that position. This lets the terminal know all links at a given area and also + * labels for what these links are going to do. + */ + registerLinkProvider(linkProvider: ITerminalExternalLinkProvider): IDisposable; + selectDefaultShell(): Promise; setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; @@ -163,26 +169,41 @@ export interface ITerminalService { preparePathForTerminalAsync(path: string, executable: string | undefined, title: string, shellType: TerminalShellType): Promise; extHostReady(remoteAuthority: string): void; - requestSpawnExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void; - requestStartExtensionTerminal(proxy: ITerminalProcessExtHostProxy, cols: number, rows: number): void; + requestSpawnExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise; + requestStartExtensionTerminal(proxy: ITerminalProcessExtHostProxy, cols: number, rows: number): Promise; +} + +/** + * Similar to xterm.js' ILinkProvider but using promises and hides xterm.js internals (like buffer + * positions, decorations, etc.) from the rest of vscode. This is the interface to use for + * workbench integrations. + */ +export interface ITerminalExternalLinkProvider { + provideLinks(instance: ITerminalInstance, line: string): Promise; +} + +export interface ITerminalLink { + /** The startIndex of the link in the line. */ + startIndex: number; + /** The length of the link in the line. */ + length: number; + /** The descriptive label for what the link does when activated. */ + label?: string; + /** + * Activates the link. + * @param text The text of the link. + */ + activate(text: string): void; } export interface ISearchOptions { - /** - * Whether the find should be done as a regex. - */ + /** Whether the find should be done as a regex. */ regex?: boolean; - /** - * Whether only whole words should match. - */ + /** Whether only whole words should match. */ wholeWord?: boolean; - /** - * Whether find should pay attention to case. - */ + /** Whether find should pay attention to case. */ caseSensitive?: boolean; - /** - * Whether the search should start at the current search position (not the next row) - */ + /** Whether the search should start at the current search position (not the next row). */ incremental?: boolean; } @@ -236,6 +257,7 @@ export interface ITerminalInstance { onFocused: Event; onProcessIdReady: Event; + onLinksReady: Event; onRequestExtHostProcess: Event; onDimensionsChanged: Event; onMaximumDimensionsChanged: Event; @@ -274,6 +296,9 @@ export interface ITerminalInstance { readonly exitCode: number | undefined; + readonly areLinksReady: boolean; + + /** A promise that resolves when the terminal's pty/process have been created. */ processReady: Promise; /** @@ -338,26 +363,6 @@ export interface ITerminalInstance { */ forceRedraw(): void; - /** - * Registers a link matcher, allowing custom link patterns to be matched and handled. - * @param regex The regular expression the 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 matchIndex The index of the link from the regex.match(html) call. This defaults to 0 - * (for regular expressions without capture groups). - * @param validationCallback A callback which can be used to validate the link after it has been - * added to the DOM. - * @return The ID of the new matcher, this can be used to deregister. - */ - registerLinkMatcher(regex: RegExp, handler: (url: string) => void, matchIndex?: number, validationCallback?: (uri: string, callback: (isValid: boolean) => void) => void): number; - - /** - * Deregisters a link matcher if it has been registered. - * @param matcherId The link matcher's ID (returned after register) - * @return Whether a link matcher was found and deregistered. - */ - deregisterLinkMatcher(matcherId: number): void; - /** * Check if anything is selected in terminal. */ @@ -502,4 +507,9 @@ export interface ITerminalInstance { getInitialCwd(): Promise; getCwd(): Promise; + + /** + * @throws when called before xterm.js is ready. + */ + registerLinkProvider(provider: ITerminalExternalLinkProvider): IDisposable; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index dfaef3ea7e4..dd4b2216ff2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -7,7 +7,6 @@ import { Action, IAction } from 'vs/base/common/actions'; import { EndOfLinePreference } from 'vs/editor/common/model'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { TERMINAL_VIEW_ID, ITerminalConfigHelper, TitleEventSource, TERMINAL_COMMAND_ID, KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED, TERMINAL_ACTION_CATEGORY, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FIND_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS } from 'vs/workbench/contrib/terminal/common/terminal'; -import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -26,7 +25,7 @@ import { URI } from 'vs/base/common/uri'; import { isWindows } from 'vs/base/common/platform'; import { withNullAsUndefined } from 'vs/base/common/types'; import { ITerminalInstance, ITerminalService, Direction } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; +import { Action2, registerAction2, ILocalizedString } from 'vs/platform/actions/common/actions'; import { TerminalQuickAccessProvider } from 'vs/workbench/contrib/terminal/browser/terminalQuickAccess'; import { ToggleViewAction } from 'vs/workbench/browser/actions/layoutActions'; import { IViewsService, IViewDescriptorService } from 'vs/workbench/common/views'; @@ -37,6 +36,9 @@ import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { localize } from 'vs/nls'; import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ITerminalContributionService } from 'vs/workbench/contrib/terminal/common/terminalExtensionPoints'; +import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; async function getCwdForSplit(configHelper: ITerminalConfigHelper, instance: ITerminalInstance, folders?: IWorkspaceFolder[], commandService?: ICommandService): Promise { switch (configHelper.config.splitCwd) { @@ -303,6 +305,8 @@ export class SelectDefaultShellWindowsTerminalAction extends Action { } } +const terminalIndexRe = /^([0-9]+): /; + export class SwitchTerminalAction extends Action { public static readonly ID = TERMINAL_COMMAND_ID.SWITCH_TERMINAL; @@ -310,7 +314,9 @@ export class SwitchTerminalAction extends Action { constructor( id: string, label: string, - @ITerminalService private readonly _terminalService: ITerminalService + @ITerminalService private readonly _terminalService: ITerminalService, + @ITerminalContributionService private readonly _contributions: ITerminalContributionService, + @ICommandService private readonly _commands: ICommandService, ) { super(id, label, 'terminal-action switch-terminal'); } @@ -327,9 +333,20 @@ export class SwitchTerminalAction extends Action { this._terminalService.refreshActiveTab(); return this._terminalService.selectDefaultShell(); } - const selectedTabIndex = parseInt(item.split(':')[0], 10) - 1; - this._terminalService.setActiveTabByIndex(selectedTabIndex); - return this._terminalService.showPanel(true); + + const indexMatches = terminalIndexRe.exec(item); + if (indexMatches) { + this._terminalService.setActiveTabByIndex(Number(indexMatches[1]) - 1); + return this._terminalService.showPanel(true); + } + + const customType = this._contributions.terminalTypes.find(t => t.title === item); + if (customType) { + return this._commands.executeCommand(customType.command); + } + + console.warn(`Unmatched terminal item: "${item}"`); + return Promise.resolve(); } } @@ -341,9 +358,10 @@ export class SwitchTerminalActionViewItem extends SelectActionViewItem { action: IAction, @ITerminalService private readonly _terminalService: ITerminalService, @IThemeService private readonly _themeService: IThemeService, - @IContextViewService contextViewService: IContextViewService + @ITerminalContributionService private readonly _contributions: ITerminalContributionService, + @IContextViewService contextViewService: IContextViewService, ) { - super(null, action, getTerminalSelectOpenItems(_terminalService), _terminalService.activeTabIndex, contextViewService, { ariaLabel: localize('terminals', 'Open Terminals.') }); + super(null, action, getTerminalSelectOpenItems(_terminalService, _contributions), _terminalService.activeTabIndex, contextViewService, { ariaLabel: localize('terminals', 'Open Terminals.'), optionsAsChildren: true }); this._register(_terminalService.onInstancesChanged(this._updateItems, this)); this._register(_terminalService.onActiveTabChanged(this._updateItems, this)); @@ -361,13 +379,18 @@ export class SwitchTerminalActionViewItem extends SelectActionViewItem { } private _updateItems(): void { - this.setOptions(getTerminalSelectOpenItems(this._terminalService), this._terminalService.activeTabIndex); + this.setOptions(getTerminalSelectOpenItems(this._terminalService, this._contributions), this._terminalService.activeTabIndex); } } -function getTerminalSelectOpenItems(terminalService: ITerminalService): ISelectOptionItem[] { +function getTerminalSelectOpenItems(terminalService: ITerminalService, contributions: ITerminalContributionService): ISelectOptionItem[] { const items = terminalService.getTabLabels().map(label => { text: label }); items.push({ text: SwitchTerminalActionViewItem.SEPARATOR, isDisabled: true }); + + for (const contributed of contributions.terminalTypes) { + items.push({ text: contributed.title }); + } + items.push({ text: SelectDefaultShellWindowsTerminalAction.LABEL }); return items; } @@ -392,14 +415,27 @@ export class ClearTerminalAction extends Action { } } +export class TerminalLaunchHelpAction extends Action { + + constructor( + @IOpenerService private readonly _openerService: IOpenerService + ) { + super('workbench.action.terminal.launchHelp', localize('terminalLaunchHelp', "Open Help")); + } + + async run(): Promise { + this._openerService.open('https://aka.ms/vscode-troubleshoot-terminal-launch'); + } +} + export function registerTerminalActions() { - const category = TERMINAL_ACTION_CATEGORY; + const category: ILocalizedString = { value: TERMINAL_ACTION_CATEGORY, original: 'Terminal' }; registerAction2(class extends Action2 { constructor() { super({ id: TERMINAL_COMMAND_ID.NEW_IN_ACTIVE_WORKSPACE, - title: localize('workbench.action.terminal.newInActiveWorkspace', "Create New Integrated Terminal (In Active Workspace)"), + title: { value: localize('workbench.action.terminal.newInActiveWorkspace', "Create New Integrated Terminal (In Active Workspace)"), original: 'Create New Integrated Terminal (In Active Workspace)' }, f1: true, category }); @@ -418,7 +454,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.FOCUS_PREVIOUS_PANE, - title: localize('workbench.action.terminal.focusPreviousPane', "Focus Previous Pane"), + title: { value: localize('workbench.action.terminal.focusPreviousPane', "Focus Previous Pane"), original: 'Focus Previous Pane' }, f1: true, category, keybinding: { @@ -443,7 +479,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.FOCUS_NEXT_PANE, - title: localize('workbench.action.terminal.focusNextPane', "Focus Next Pane"), + title: { value: localize('workbench.action.terminal.focusNextPane', "Focus Next Pane"), original: 'Focus Next Pane' }, f1: true, category, keybinding: { @@ -468,7 +504,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.RESIZE_PANE_LEFT, - title: localize('workbench.action.terminal.resizePaneLeft', "Resize Pane Left"), + title: { value: localize('workbench.action.terminal.resizePaneLeft', "Resize Pane Left"), original: 'Resize Pane Left' }, f1: true, category, keybinding: { @@ -487,7 +523,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.RESIZE_PANE_RIGHT, - title: localize('workbench.action.terminal.resizePaneRight', "Resize Pane Right"), + title: { value: localize('workbench.action.terminal.resizePaneRight', "Resize Pane Right"), original: 'Resize Pane Right' }, f1: true, category, keybinding: { @@ -506,7 +542,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.RESIZE_PANE_UP, - title: localize('workbench.action.terminal.resizePaneUp', "Resize Pane Up"), + title: { value: localize('workbench.action.terminal.resizePaneUp', "Resize Pane Up"), original: 'Resize Pane Up' }, f1: true, category, keybinding: { @@ -524,7 +560,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.RESIZE_PANE_DOWN, - title: localize('workbench.action.terminal.resizePaneDown', "Resize Pane Down"), + title: { value: localize('workbench.action.terminal.resizePaneDown', "Resize Pane Down"), original: 'Resize Pane Down' }, f1: true, category, keybinding: { @@ -542,7 +578,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.FOCUS, - title: localize('workbench.action.terminal.focus', "Focus Terminal"), + title: { value: localize('workbench.action.terminal.focus', "Focus Terminal"), original: 'Focus Terminal' }, f1: true, category }); @@ -561,7 +597,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.FOCUS_NEXT, - title: localize('workbench.action.terminal.focusNext', "Focus Next Terminal"), + title: { value: localize('workbench.action.terminal.focusNext', "Focus Next Terminal"), original: 'Focus Next Terminal' }, f1: true, category }); @@ -576,7 +612,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.FOCUS_PREVIOUS, - title: localize('workbench.action.terminal.focusPrevious', "Focus Previous Terminal"), + title: { value: localize('workbench.action.terminal.focusPrevious', "Focus Previous Terminal"), original: 'Focus Previous Terminal' }, f1: true, category }); @@ -591,7 +627,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.RUN_SELECTED_TEXT, - title: localize('workbench.action.terminal.runSelectedText', "Run Selected Text In Active Terminal"), + title: { value: localize('workbench.action.terminal.runSelectedText', "Run Selected Text In Active Terminal"), original: 'Run Selected Text In Active Terminal' }, f1: true, category }); @@ -621,7 +657,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.RUN_ACTIVE_FILE, - title: localize('workbench.action.terminal.runActiveFile', "Run Active File In Active Terminal"), + title: { value: localize('workbench.action.terminal.runActiveFile', "Run Active File In Active Terminal"), original: 'Run Active File In Active Terminal' }, f1: true, category }); @@ -655,7 +691,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.SCROLL_DOWN_LINE, - title: localize('workbench.action.terminal.scrollDown', "Scroll Down (Line)"), + title: { value: localize('workbench.action.terminal.scrollDown', "Scroll Down (Line)"), original: 'Scroll Down (Line)' }, f1: true, category, keybinding: { @@ -674,7 +710,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.SCROLL_DOWN_PAGE, - title: localize('workbench.action.terminal.scrollDownPage', "Scroll Down (Page)"), + title: { value: localize('workbench.action.terminal.scrollDownPage', "Scroll Down (Page)"), original: 'Scroll Down (Page)' }, f1: true, category, keybinding: { @@ -693,7 +729,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.SCROLL_TO_BOTTOM, - title: localize('workbench.action.terminal.scrollToBottom', "Scroll to Bottom"), + title: { value: localize('workbench.action.terminal.scrollToBottom', "Scroll to Bottom"), original: 'Scroll to Bottom' }, f1: true, category, keybinding: { @@ -712,7 +748,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.SCROLL_UP_LINE, - title: localize('workbench.action.terminal.scrollUp', "Scroll Up (Line)"), + title: { value: localize('workbench.action.terminal.scrollUp', "Scroll Up (Line)"), original: 'Scroll Up (Line)' }, f1: true, category, keybinding: { @@ -731,7 +767,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.SCROLL_UP_PAGE, - title: localize('workbench.action.terminal.scrollUpPage', "Scroll Up (Page)"), + title: { value: localize('workbench.action.terminal.scrollUpPage', "Scroll Up (Page)"), original: 'Scroll Up (Page)' }, f1: true, category, keybinding: { @@ -750,7 +786,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.SCROLL_TO_TOP, - title: localize('workbench.action.terminal.scrollToTop', "Scroll to Top"), + title: { value: localize('workbench.action.terminal.scrollToTop', "Scroll to Top"), original: 'Scroll to Top' }, f1: true, category, keybinding: { @@ -769,7 +805,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.NAVIGATION_MODE_EXIT, - title: localize('workbench.action.terminal.navigationModeExit', "Exit Navigation Mode"), + title: { value: localize('workbench.action.terminal.navigationModeExit', "Exit Navigation Mode"), original: 'Exit Navigation Mode' }, f1: true, category, keybinding: { @@ -787,7 +823,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.NAVIGATION_MODE_FOCUS_PREVIOUS, - title: localize('workbench.action.terminal.navigationModeFocusPrevious', "Focus Previous Line (Navigation Mode)"), + title: { value: localize('workbench.action.terminal.navigationModeFocusPrevious', "Focus Previous Line (Navigation Mode)"), original: 'Focus Previous Line (Navigation Mode)' }, f1: true, category, keybinding: { @@ -808,7 +844,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.NAVIGATION_MODE_FOCUS_NEXT, - title: localize('workbench.action.terminal.navigationModeFocusNext', "Focus Next Line (Navigation Mode)"), + title: { value: localize('workbench.action.terminal.navigationModeFocusNext', "Focus Next Line (Navigation Mode)"), original: 'Focus Next Line (Navigation Mode)' }, f1: true, category, keybinding: { @@ -829,7 +865,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.CLEAR_SELECTION, - title: localize('workbench.action.terminal.clearSelection', "Clear Selection"), + title: { value: localize('workbench.action.terminal.clearSelection', "Clear Selection"), original: 'Clear Selection' }, f1: true, category, keybinding: { @@ -850,7 +886,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.MANAGE_WORKSPACE_SHELL_PERMISSIONS, - title: localize('workbench.action.terminal.manageWorkspaceShellPermissions', "Manage Workspace Shell Permissions"), + title: { value: localize('workbench.action.terminal.manageWorkspaceShellPermissions', "Manage Workspace Shell Permissions"), original: 'Manage Workspace Shell Permissions' }, f1: true, category }); @@ -863,7 +899,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.RENAME, - title: localize('workbench.action.terminal.rename', "Rename"), + title: { value: localize('workbench.action.terminal.rename', "Rename"), original: 'Rename' }, f1: true, category }); @@ -884,7 +920,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.FIND_FOCUS, - title: localize('workbench.action.terminal.focusFind', "Focus Find"), + title: { value: localize('workbench.action.terminal.focusFind', "Focus Find"), original: 'Focus Find' }, f1: true, category, keybinding: { @@ -902,7 +938,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.FIND_HIDE, - title: localize('workbench.action.terminal.hideFind', "Hide Find"), + title: { value: localize('workbench.action.terminal.hideFind', "Hide Find"), original: 'Hide Find' }, f1: true, category, keybinding: { @@ -921,7 +957,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.QUICK_OPEN_TERM, - title: localize('quickAccessTerminal', "Switch Active Terminal"), + title: { value: localize('quickAccessTerminal', "Switch Active Terminal"), original: 'Switch Active Terminal' }, f1: true, category }); @@ -934,7 +970,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.SCROLL_TO_PREVIOUS_COMMAND, - title: localize('workbench.action.terminal.scrollToPreviousCommand', "Scroll To Previous Command"), + title: { value: localize('workbench.action.terminal.scrollToPreviousCommand', "Scroll To Previous Command"), original: 'Scroll To Previous Command' }, f1: true, category, keybinding: { @@ -955,7 +991,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.SCROLL_TO_NEXT_COMMAND, - title: localize('workbench.action.terminal.scrollToNextCommand', "Scroll To Next Command"), + title: { value: localize('workbench.action.terminal.scrollToNextCommand', "Scroll To Next Command"), original: 'Scroll To Next Command' }, f1: true, category, keybinding: { @@ -976,7 +1012,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.SELECT_TO_PREVIOUS_COMMAND, - title: localize('workbench.action.terminal.selectToPreviousCommand', "Select To Previous Command"), + title: { value: localize('workbench.action.terminal.selectToPreviousCommand', "Select To Previous Command"), original: 'Select To Previous Command' }, f1: true, category, keybinding: { @@ -997,7 +1033,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.SELECT_TO_NEXT_COMMAND, - title: localize('workbench.action.terminal.selectToNextCommand', "Select To Next Command"), + title: { value: localize('workbench.action.terminal.selectToNextCommand', "Select To Next Command"), original: 'Select To Next Command' }, f1: true, category, keybinding: { @@ -1018,7 +1054,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.SELECT_TO_PREVIOUS_LINE, - title: localize('workbench.action.terminal.selectToPreviousLine', "Select To Previous Line"), + title: { value: localize('workbench.action.terminal.selectToPreviousLine', "Select To Previous Line"), original: 'Select To Previous Line' }, f1: true, category }); @@ -1034,7 +1070,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.SELECT_TO_NEXT_LINE, - title: localize('workbench.action.terminal.selectToNextLine', "Select To Next Line"), + title: { value: localize('workbench.action.terminal.selectToNextLine', "Select To Next Line"), original: 'Select To Next Line' }, f1: true, category }); @@ -1050,7 +1086,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.TOGGLE_ESCAPE_SEQUENCE_LOGGING, - title: localize('workbench.action.terminal.toggleEscapeSequenceLogging', "Toggle Escape Sequence Logging"), + title: { value: localize('workbench.action.terminal.toggleEscapeSequenceLogging', "Toggle Escape Sequence Logging"), original: 'Toggle Escape Sequence Logging' }, f1: true, category }); @@ -1064,7 +1100,7 @@ export function registerTerminalActions() { const title = localize('workbench.action.terminal.sendSequence', "Send Custom Sequence To Terminal"); super({ id: TERMINAL_COMMAND_ID.SEND_SEQUENCE, - title, + title: { value: title, original: 'Send Custom Sequence To Terminal' }, category, description: { description: title, @@ -1090,7 +1126,7 @@ export function registerTerminalActions() { const title = localize('workbench.action.terminal.newWithCwd', "Create New Integrated Terminal Starting in a Custom Working Directory"); super({ id: TERMINAL_COMMAND_ID.NEW_WITH_CWD, - title, + title: { value: title, original: 'Create New Integrated Terminal Starting in a Custom Working Directory' }, category, description: { description: title, @@ -1125,7 +1161,7 @@ export function registerTerminalActions() { const title = localize('workbench.action.terminal.renameWithArg', "Rename the Currently Active Terminal"); super({ id: TERMINAL_COMMAND_ID.RENAME_WITH_ARG, - title, + title: { value: title, original: 'Rename the Currently Active Terminal' }, category, description: { description: title, @@ -1159,7 +1195,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.TOGGLE_FIND_REGEX, - title: localize('workbench.action.terminal.toggleFindRegex', "Toggle Find Using Regex"), + title: { value: localize('workbench.action.terminal.toggleFindRegex', "Toggle Find Using Regex"), original: 'Toggle Find Using Regex' }, f1: true, category, keybinding: { @@ -1179,7 +1215,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.TOGGLE_FIND_WHOLE_WORD, - title: localize('workbench.action.terminal.toggleFindWholeWord', "Toggle Find Using Whole Word"), + title: { value: localize('workbench.action.terminal.toggleFindWholeWord', "Toggle Find Using Whole Word"), original: 'Toggle Find Using Whole Word' }, f1: true, category, keybinding: { @@ -1199,7 +1235,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.TOGGLE_FIND_CASE_SENSITIVE, - title: localize('workbench.action.terminal.toggleFindCaseSensitive', "Toggle Find Using Case Sensitive"), + title: { value: localize('workbench.action.terminal.toggleFindCaseSensitive', "Toggle Find Using Case Sensitive"), original: 'Toggle Find Using Case Sensitive' }, f1: true, category, keybinding: { @@ -1219,7 +1255,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.FIND_NEXT, - title: localize('workbench.action.terminal.findNext', "Find Next"), + title: { value: localize('workbench.action.terminal.findNext', "Find Next"), original: 'Find Next' }, f1: true, category, keybinding: [ @@ -1245,7 +1281,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.FIND_PREVIOUS, - title: localize('workbench.action.terminal.findPrevious', "Find Previous"), + title: { value: localize('workbench.action.terminal.findPrevious', "Find Previous"), original: 'Find Previous' }, f1: true, category, keybinding: [ @@ -1271,7 +1307,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.RELAUNCH, - title: localize('workbench.action.terminal.relaunch', "Relaunch Active Terminal"), + title: { value: localize('workbench.action.terminal.relaunch', "Relaunch Active Terminal"), original: 'Relaunch Active Terminal' }, f1: true, category }); @@ -1284,7 +1320,7 @@ export function registerTerminalActions() { constructor() { super({ id: TERMINAL_COMMAND_ID.SHOW_ENVIRONMENT_INFORMATION, - title: localize('workbench.action.terminal.showEnvironmentInformation', "Show Environment Information"), + title: { value: localize('workbench.action.terminal.showEnvironmentInformation', "Show Environment Information"), original: 'Show Environment Information' }, f1: true, category }); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts index 71f2e6af2d7..82f060e64cc 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts @@ -214,7 +214,7 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { const shellArgsConfigValue = this._configurationService.inspect(`terminal.integrated.shellArgs.${platformKey}`); const envConfigValue = this._configurationService.inspect<{ [key: string]: string }>(`terminal.integrated.env.${platformKey}`); - // Check if workspace setting exists and whether it's whitelisted + // Check if workspace setting exists and whether it's allowed let isWorkspaceShellAllowed: boolean | undefined = false; if (shellConfigValue.workspaceValue !== undefined || shellArgsConfigValue.workspaceValue !== undefined || envConfigValue.workspaceValue !== undefined) { isWorkspaceShellAllowed = this.isWorkspaceShellAllowed(undefined); @@ -226,7 +226,7 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { isWorkspaceShellAllowed = true; } - // Check if the value is neither blacklisted (false) or whitelisted (true) and ask for + // Check if the value is neither on the blocklist (false) or allowlist (true) and ask for // permission if (isWorkspaceShellAllowed === undefined) { let shellString: string | undefined; @@ -294,7 +294,7 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { if (!exeBasedExtensionTips || !exeBasedExtensionTips.wsl) { return; } - const extId = exeBasedExtensionTips.wsl.recommendations[0]; + const extId = Object.keys(exeBasedExtensionTips.wsl.recommendations).find(extId => exeBasedExtensionTips.wsl.recommendations[extId].important); if (extId && ! await this.isExtensionInstalled(extId)) { this._notificationService.prompt( Severity.Info, diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 4068deb39ba..6ca70138147 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -25,12 +25,12 @@ import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderB import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; -import { IShellLaunchConfig, ITerminalDimensions, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_VIEW_ID, IWindowsShellHelper, SHELL_PATH_INVALID_EXIT_CODE, SHELL_PATH_DIRECTORY_EXIT_CODE, SHELL_CWD_INVALID_EXIT_CODE, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode, TitleEventSource, LEGACY_CONSOLE_MODE_EXIT_CODE, DEFAULT_COMMANDS_TO_SKIP_SHELL } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalDimensions, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_VIEW_ID, IWindowsShellHelper, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode, TitleEventSource, DEFAULT_COMMANDS_TO_SKIP_SHELL, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TerminalLinkManager } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; -import { ITerminalInstanceService, ITerminalInstance, TerminalShellType, WindowsShellType, ITerminalBeforeHandleLinkEvent } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalInstanceService, ITerminalInstance, TerminalShellType, WindowsShellType, ITerminalBeforeHandleLinkEvent, ITerminalExternalLinkProvider } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/browser/terminalProcessManager'; import { Terminal as XTermTerminal, IBuffer, ITerminalAddon } from 'xterm'; import { SearchAddon, ISearchOptions } from 'xterm-addon-search'; @@ -42,6 +42,7 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IViewsService, IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; import { EnvironmentVariableInfoWidget } from 'vs/workbench/contrib/terminal/browser/widgets/environmentVariableInfoWidget'; import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable'; +import { TerminalLaunchHelpAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; // How long in milliseconds should an average frame take to render for a notification to appear // which suggests the fallback DOM-based renderer @@ -71,6 +72,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { private _pressAnyKeyToCloseListener: IDisposable | undefined; private _id: number; + private _latestXtermWriteData: number = 0; + private _latestXtermParseData: number = 0; private _isExiting: boolean; private _hadFocusOnExit: boolean; private _isVisible: boolean; @@ -94,6 +97,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { private _xtermReadyPromise: Promise; private _titleReadyPromise: Promise; private _titleReadyComplete: ((title: string) => any) | undefined; + private _areLinksReady: boolean = false; private _messageTitleDisposable: IDisposable | undefined; @@ -103,6 +107,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { private _commandTrackerAddon: CommandTrackerAddon | undefined; private _navigationModeAddon: INavigationMode & ITerminalAddon | undefined; + private _timeoutDimension: dom.Dimension | undefined; + public disableLayout: boolean; public get id(): number { return this._id; } public get cols(): number { @@ -124,6 +130,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // TODO: How does this work with detached processes? // TODO: Should this be an event as it can fire twice? public get processReady(): Promise { return this._processManager.ptyProcessReady; } + public get areLinksReady(): boolean { return this._areLinksReady; } public get exitCode(): number | undefined { return this._exitCode; } public get title(): string { return this._title; } public get hadFocusOnExit(): boolean { return this._hadFocusOnExit; } @@ -141,6 +148,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { public get onFocused(): Event { return this._onFocused.event; } private readonly _onProcessIdReady = new Emitter(); public get onProcessIdReady(): Event { return this._onProcessIdReady.event; } + private readonly _onLinksReady = new Emitter(); + public get onLinksReady(): Event { return this._onLinksReady.event; } private readonly _onTitleChanged = new Emitter(); public get onTitleChanged(): Event { return this._onTitleChanged.event; } private readonly _onData = new Emitter(); @@ -198,7 +207,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._logService.trace(`terminalInstance#ctor (id: ${this.id})`, this._shellLaunchConfig); this._initDimensions(); - this._createProcess(); + this._createProcessManager(); this._xtermReadyPromise = this._createXterm(); this._xtermReadyPromise.then(() => { @@ -206,6 +215,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { if (_container) { this._attachToElement(_container); } + this._createProcess(); }); this.addDisposable(this._configurationService.onDidChangeConfiguration(e => { @@ -408,11 +418,13 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { return false; }); } - this._linkManager = this._instantiationService.createInstance(TerminalLinkManager, xterm, this._processManager!, this._configHelper); + this._linkManager = this._instantiationService.createInstance(TerminalLinkManager, xterm, this._processManager!); this._linkManager.onBeforeHandleLink(e => { e.terminal = this; this._onBeforeHandleLink.fire(e); }); + this._areLinksReady = true; + this._onLinksReady.fire(this); }); this._commandTrackerAddon = new CommandTrackerAddon(); @@ -483,8 +495,13 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { throw new Error('xterm elements not set after open'); } + // Check if custom Terminal title exists and set same + if (this._title.length > 0) { + xterm.textarea.setAttribute('aria-label', nls.localize('terminalTextBoxAriaLabelNumberAndTitle', "Terminal {0}, {1}", this._id, this._title)); + } else { + xterm.textarea.setAttribute('aria-label', nls.localize('terminalTextBoxAriaLabel', "Terminal {0}", this._id)); + } - xterm.textarea.setAttribute('aria-label', nls.localize('terminalTextBoxAriaLabel', "Terminal {0}", this._id)); xterm.textarea.addEventListener('focus', () => this._onFocus.fire(this)); xterm.attachCustomKeyEventHandler((event: KeyboardEvent): boolean => { // Disable all input if the terminal is exiting @@ -547,20 +564,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { setTimeout(() => this._refreshSelectionContextKey(), 0); })); - const xtermHelper: HTMLElement = xterm.element.querySelector('.xterm-helpers'); - const focusTrap: HTMLElement = document.createElement('div'); - focusTrap.setAttribute('tabindex', '0'); - dom.addClass(focusTrap, 'focus-trap'); - this._register(dom.addDisposableListener(focusTrap, 'focus', () => { - let currentElement = focusTrap; - while (!dom.hasClass(currentElement, 'part')) { - currentElement = currentElement.parentElement!; - } - const hidePanelElement = currentElement.querySelector('.hide-panel-action'); - hidePanelElement?.focus(); - })); - xtermHelper.insertBefore(focusTrap, xterm.textarea); - this._register(dom.addDisposableListener(xterm.textarea, 'focus', () => { this._terminalFocusContextKey.set(true); if (this.shellType) { @@ -656,14 +659,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { }; } - public registerLinkMatcher(regex: RegExp, handler: (url: string) => void, matchIndex?: number, validationCallback?: (uri: string, callback: (isValid: boolean) => void) => void): number { - return this._linkManager!.registerCustomLinkHandler(regex, (_, url) => handler(url), matchIndex, validationCallback); - } - - public deregisterLinkMatcher(linkMatcherId: number): void { - this._xtermReadyPromise.then(xterm => xterm.deregisterLinkMatcher(linkMatcherId)); - } - public hasSelection(): boolean { return this._xterm ? this._xterm.hasSelection() : false; } @@ -829,9 +824,13 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // HACK: Trigger another async layout to ensure xterm's CharMeasure is ready to use, // this hack can be removed when https://github.com/xtermjs/xterm.js/issues/702 is // supported. - setTimeout(() => this.layout(new dom.Dimension(width, height)), 0); + this._timeoutDimension = new dom.Dimension(width, height); + setTimeout(() => this.layout(this._timeoutDimension!), 0); } } + if (!visible) { + this._widgetManager.hideHovers(); + } } public scrollDownLine(): void { @@ -867,9 +866,11 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._terminalHasTextContextKey.set(isActive && this.hasSelection()); } - protected _createProcess(): void { + protected _createProcessManager(): void { this._processManager = this._instantiationService.createInstance(TerminalProcessManager, this._id, this._configHelper); - this._processManager.onProcessReady(() => this._onProcessIdReady.fire(this)); + this._processManager.onProcessReady(() => { + this._onProcessIdReady.fire(this); + }); this._processManager.onProcessExit(exitCode => this._onProcessExit(exitCode)); this._processManager.onProcessData(data => this._onData.fire(data)); this._processManager.onProcessOverrideDimensions(e => this.setDimensions(e)); @@ -905,7 +906,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._windowsShellHelper = this._terminalInstanceService.createWindowsShellHelper(this._processManager.shellProcessId, xterm); this._windowsShellHelper.onShellNameChange(title => { this.setShellType(this.getShellType(title)); - if (this.isTitleSetByProcess) { + if (this.isTitleSetByProcess && !this._configHelper.config.experimentalUseTitleEvent) { this.setTitle(title, TitleEventSource.Process); } }); @@ -913,12 +914,14 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { }); }); } + } - // Create the process asynchronously to allow the terminal's container - // to be created so dimensions are accurate - setTimeout(() => { - this._processManager.createProcess(this._shellLaunchConfig, this._cols, this._rows, this._accessibilityService.isScreenReaderOptimized()); - }, 0); + private _createProcess(): void { + this._processManager.createProcess(this._shellLaunchConfig, this._cols, this._rows, this._accessibilityService.isScreenReaderOptimized()).then(error => { + if (error) { + this._onProcessExit(error); + } + }); } private getShellType(executable: string): TerminalShellType { @@ -944,7 +947,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } private _onProcessData(data: string): void { - this._xterm?.write(data); + const messageId = ++this._latestXtermWriteData; + this._xterm?.write(data, () => this._latestXtermParseData = messageId); } /** @@ -953,48 +957,56 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { * @param exitCode The exit code of the process, this is undefined when the terminal was exited * through user action. */ - private _onProcessExit(exitCode?: number): void { + private async _onProcessExit(exitCodeOrError?: number | ITerminalLaunchError): Promise { // Prevent dispose functions being triggered multiple times if (this._isExiting) { return; } - this._logService.debug(`Terminal process exit (id: ${this.id}) with code ${exitCode}`); - - this._exitCode = exitCode; this._isExiting = true; + + await this._flushXtermData(); + this._logService.debug(`Terminal process exit (id: ${this.id}) with code ${this._exitCode}`); + let exitCodeMessage: string | undefined; // Create exit code message - if (exitCode) { - if (exitCode === SHELL_PATH_INVALID_EXIT_CODE) { - exitCodeMessage = nls.localize('terminal.integrated.exitedWithInvalidPath', 'The terminal shell path "{0}" does not exist', this._shellLaunchConfig.executable); - } else if (exitCode === SHELL_PATH_DIRECTORY_EXIT_CODE) { - exitCodeMessage = nls.localize('terminal.integrated.exitedWithInvalidPathDirectory', 'The terminal shell path "{0}" is a directory', this._shellLaunchConfig.executable); - } else if (exitCode === SHELL_CWD_INVALID_EXIT_CODE && this._shellLaunchConfig.cwd) { - exitCodeMessage = nls.localize('terminal.integrated.exitedWithInvalidCWD', 'The terminal shell CWD "{0}" does not exist', this._shellLaunchConfig.cwd.toString()); - } else if (exitCode === LEGACY_CONSOLE_MODE_EXIT_CODE) { - exitCodeMessage = nls.localize('terminal.integrated.legacyConsoleModeError', 'The terminal failed to launch properly because your system has legacy console mode enabled, uncheck "Use legacy console" cmd.exe\'s properties to fix this.'); - } else if (this._processManager.processState === ProcessState.KILLED_DURING_LAUNCH) { - let args = ''; - if (typeof this._shellLaunchConfig.args === 'string') { - args = ` ${this._shellLaunchConfig.args}`; - } else if (this._shellLaunchConfig.args && this._shellLaunchConfig.args.length) { - args = ' ' + this._shellLaunchConfig.args.map(a => { - if (typeof a === 'string' && a.indexOf(' ') !== -1) { - return `'${a}'`; - } - return a; - }).join(' '); + switch (typeof exitCodeOrError) { + case 'number': + // Only show the error if the exit code is non-zero + this._exitCode = exitCodeOrError; + if (this._exitCode === 0) { + break; } + + let commandLine: string | undefined = undefined; if (this._shellLaunchConfig.executable) { - exitCodeMessage = nls.localize('terminal.integrated.launchFailed', 'The terminal process command \'{0}{1}\' failed to launch (exit code: {2})', this._shellLaunchConfig.executable, args, exitCode); - } else { - exitCodeMessage = nls.localize('terminal.integrated.launchFailedExtHost', 'The terminal process failed to launch (exit code: {0})', exitCode); + commandLine = this._shellLaunchConfig.executable; + if (typeof this._shellLaunchConfig.args === 'string') { + commandLine += ` ${this._shellLaunchConfig.args}`; + } else if (this._shellLaunchConfig.args && this._shellLaunchConfig.args.length) { + commandLine += this._shellLaunchConfig.args.map(a => ` '${a}'`).join(); + } } - } else { - exitCodeMessage = nls.localize('terminal.integrated.exitedWithCode', 'The terminal process terminated with exit code: {0}', exitCode); - } + + if (this._processManager.processState === ProcessState.KILLED_DURING_LAUNCH) { + if (commandLine) { + exitCodeMessage = nls.localize('launchFailed.exitCodeAndCommandLine', "The terminal process \"{0}\" failed to launch (exit code: {1}).", commandLine, this._exitCode); + break; + } + exitCodeMessage = nls.localize('launchFailed.exitCodeOnly', "The terminal process failed to launch (exit code: {0}).", this._exitCode); + break; + } + if (commandLine) { + exitCodeMessage = nls.localize('terminated.exitCodeAndCommandLine', "The terminal process \"{0}\" terminated with exit code: {1}.", commandLine, this._exitCode); + break; + } + exitCodeMessage = nls.localize('terminated.exitCodeOnly', "The terminal process terminated with exit code: {0}.", this._exitCode); + break; + case 'object': + this._exitCode = exitCodeOrError.code; + exitCodeMessage = nls.localize('launchFailed.errorMessage', "The terminal process failed to launch: {0}.", exitCodeOrError.message); + break; } this._logService.debug(`Terminal process exit (id: ${this.id}) state ${this._processManager.processState}`); @@ -1021,19 +1033,41 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } else { this.dispose(); if (exitCodeMessage) { - if (this._processManager.processState === ProcessState.KILLED_DURING_LAUNCH) { - this._notificationService.error(exitCodeMessage); + const failedDuringLaunch = this._processManager.processState === ProcessState.KILLED_DURING_LAUNCH; + if (failedDuringLaunch || this._configHelper.config.showExitAlert) { + // Always show launch failures + this._notificationService.notify({ + message: exitCodeMessage, + severity: Severity.Error, + actions: { primary: [this._instantiationService.createInstance(TerminalLaunchHelpAction)] } + }); } else { - if (this._configHelper.config.showExitAlert) { - this._notificationService.error(exitCodeMessage); - } else { - console.warn(exitCodeMessage); - } + // Log to help surface the error in case users report issues with showExitAlert + // disabled + this._logService.warn(exitCodeMessage); } } } - this._onExit.fire(exitCode); + this._onExit.fire(this._exitCode); + } + + /** + * Ensure write calls to xterm.js have finished before resolving. + */ + private _flushXtermData(): Promise { + if (this._latestXtermWriteData === this._latestXtermParseData) { + return Promise.resolve(); + } + let retries = 0; + return new Promise(r => { + const interval = setInterval(() => { + if (this._latestXtermWriteData === this._latestXtermParseData || ++retries === 5) { + clearInterval(interval); + r(); + } + }, 20); + }); } private _attachPressAnyKeyToCloseListener(xterm: XTermTerminal) { @@ -1095,13 +1129,14 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // Launch the process unless this is only a renderer. // In the renderer only cases, we still need to set the title correctly. const oldTitle = this._title; - this._createProcess(); + this._createProcessManager(); if (oldTitle !== this._title) { this.setTitle(this._title, TitleEventSource.Process); } this._processManager.onProcessData(data => this._onProcessData(data)); + this._createProcess(); } public relaunch(): void { @@ -1277,6 +1312,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { return; } + this._timeoutDimension = new dom.Dimension(dimension.width, dimension.height); + if (this._xterm && this._xterm.element) { this._xterm.element.style.width = terminalWidth + 'px'; } @@ -1477,6 +1514,13 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { public getCwd(): Promise { return this._processManager.getCwd(); } + + public registerLinkProvider(provider: ITerminalExternalLinkProvider): IDisposable { + if (!this._linkManager) { + throw new Error('TerminalInstance.registerLinkProvider before link manager was ready'); + } + return this._linkManager.registerExternalLinkProvider(this, provider); + } } registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { @@ -1484,8 +1528,8 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) = const border = theme.getColor(activeContrastBorder); if (border) { collector.addRule(` - .hc-black .monaco-workbench .pane-body.integrated-terminal .xterm.focus::before, - .hc-black .monaco-workbench .pane-body.integrated-terminal .xterm:focus::before { border-color: ${border}; }` + .monaco-workbench.hc-black .pane-body.integrated-terminal .xterm.focus::before, + .monaco-workbench.hc-black .pane-body.integrated-terminal .xterm:focus::before { border-color: ${border}; }` ); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts index e714542e72c..52b03f24ccb 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts @@ -8,7 +8,6 @@ import { IWindowsShellHelper, ITerminalChildProcess, IDefaultShellAndArgsRequest import { Terminal as XTermTerminal } from 'xterm'; import { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; import { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; -import { WebLinksAddon as XTermWebLinksAddon } from 'xterm-addon-web-links'; import { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { Emitter, Event } from 'vs/base/common/event'; @@ -17,7 +16,6 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; let Terminal: typeof XTermTerminal; let SearchAddon: typeof XTermSearchAddon; let Unicode11Addon: typeof XTermUnicode11Addon; -let WebLinksAddon: typeof XTermWebLinksAddon; let WebglAddon: typeof XTermWebglAddon; export class TerminalInstanceService implements ITerminalInstanceService { @@ -47,13 +45,6 @@ export class TerminalInstanceService implements ITerminalInstanceService { return Unicode11Addon; } - public async getXtermWebLinksConstructor(): Promise { - if (!WebLinksAddon) { - WebLinksAddon = (await import('xterm-addon-web-links')).WebLinksAddon; - } - return WebLinksAddon; - } - public async getXtermWebglConstructor(): Promise { if (!WebglAddon) { WebglAddon = (await import('xterm-addon-webgl')).WebglAddon; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts index eee8ddafca5..7d7623c1a0d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; -import { ITerminalProcessExtHostProxy, IShellLaunchConfig, ITerminalChildProcess, ITerminalConfigHelper, ITerminalDimensions } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalProcessExtHostProxy, IShellLaunchConfig, ITerminalChildProcess, ITerminalConfigHelper, ITerminalDimensions, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import * as nls from 'vs/nls'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; -let hasReceivedResponse: boolean = false; +let hasReceivedResponseFromRemoteExtHost: boolean = false; export class TerminalProcessExtHostProxy extends Disposable implements ITerminalChildProcess, ITerminalProcessExtHostProxy { @@ -28,6 +28,8 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal private readonly _onProcessResolvedShellLaunchConfig = this._register(new Emitter()); public get onProcessResolvedShellLaunchConfig(): Event { return this._onProcessResolvedShellLaunchConfig.event; } + private readonly _onStart = this._register(new Emitter()); + public readonly onStart: Event = this._onStart.event; private readonly _onInput = this._register(new Emitter()); public readonly onInput: Event = this._onInput.event; private readonly _onResize: Emitter<{ cols: number, rows: number }> = this._register(new Emitter<{ cols: number, rows: number }>()); @@ -47,31 +49,15 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal constructor( public terminalId: number, - shellLaunchConfig: IShellLaunchConfig, - activeWorkspaceRootUri: URI | undefined, - cols: number, - rows: number, - configHelper: ITerminalConfigHelper, + private _shellLaunchConfig: IShellLaunchConfig, + private _activeWorkspaceRootUri: URI | undefined, + private _cols: number, + private _rows: number, + private _configHelper: ITerminalConfigHelper, @ITerminalService private readonly _terminalService: ITerminalService, - @IRemoteAgentService readonly remoteAgentService: IRemoteAgentService + @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService ) { super(); - - // Request a process if needed, if this is a virtual process this step can be skipped as - // there is no real "process" and we know it's ready on the ext host already. - if (shellLaunchConfig.isExtensionTerminal) { - this._terminalService.requestStartExtensionTerminal(this, cols, rows); - } else { - remoteAgentService.getEnvironment().then(env => { - if (!env) { - throw new Error('Could not fetch environment'); - } - this._terminalService.requestSpawnExtHostProcess(this, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, configHelper.checkWorkspaceShellPermissions(env.os)); - }); - if (!hasReceivedResponse) { - setTimeout(() => this._onProcessTitleChanged.fire(nls.localize('terminal.integrated.starting', "Starting...")), 0); - } - } } public emitData(data: string): void { @@ -79,7 +65,7 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal } public emitTitle(title: string): void { - hasReceivedResponse = true; + hasReceivedResponseFromRemoteExtHost = true; this._onProcessTitleChanged.fire(title); } @@ -118,6 +104,29 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal } } + public async start(): Promise { + // Request a process if needed, if this is a virtual process this step can be skipped as + // there is no real "process" and we know it's ready on the ext host already. + if (this._shellLaunchConfig.isExtensionTerminal) { + return this._terminalService.requestStartExtensionTerminal(this, this._cols, this._rows); + } + + // Add a loading title if the extension host has not started yet as there could be a + // decent wait for the user + if (!hasReceivedResponseFromRemoteExtHost) { + setTimeout(() => this._onProcessTitleChanged.fire(nls.localize('terminal.integrated.starting', "Starting...")), 0); + } + + // Fetch the environment to check shell permissions + const env = await this._remoteAgentService.getEnvironment(); + if (!env) { + // Extension host processes are only allowed in remote extension hosts currently + throw new Error('Could not fetch remote environment'); + } + + return this._terminalService.requestSpawnExtHostProcess(this, this._shellLaunchConfig, this._activeWorkspaceRootUri, this._cols, this._rows, this._configHelper.checkWorkspaceShellPermissions(env.os)); + } + public shutdown(immediate: boolean): void { this._onShutdown.fire(immediate); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 59635ff0609..9d4efdec9d8 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -6,7 +6,7 @@ import * as platform from 'vs/base/common/platform'; import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { env as processEnv } from 'vs/base/common/process'; -import { ProcessState, ITerminalProcessManager, IShellLaunchConfig, ITerminalConfigHelper, ITerminalChildProcess, IBeforeProcessDataEvent, ITerminalEnvironment, ITerminalDimensions } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ProcessState, ITerminalProcessManager, IShellLaunchConfig, ITerminalConfigHelper, ITerminalChildProcess, IBeforeProcessDataEvent, ITerminalEnvironment, ITerminalDimensions, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; import { ILogService } from 'vs/platform/log/common/log'; import { Emitter, Event } from 'vs/base/common/event'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; @@ -45,8 +45,8 @@ enum ProcessType { * * Internal definitions: * - Process: The process launched with the terminalProcess.ts file, or the pty as a whole - * - Pty Process: The pseudoterminal master process (or the winpty agent process) - * - Shell Process: The pseudoterminal slave process (ie. the shell) + * - Pty Process: The pseudoterminal parent process (or the conpty/winpty agent process) + * - Shell Process: The pseudoterminal child process (ie. the shell) */ export class TerminalProcessManager extends Disposable implements ITerminalProcessManager { public processState: ProcessState = ProcessState.UNINITIALIZED; @@ -127,7 +127,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce cols: number, rows: number, isScreenReaderModeEnabled: boolean - ): Promise { + ): Promise { if (shellLaunchConfig.isExtensionTerminal) { this._processType = ProcessType.ExtensionTerminal; this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy, this._terminalId, shellLaunchConfig, undefined, cols, rows, this._configHelper); @@ -146,7 +146,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce this.userHome = this._pathService.resolvedUserHome?.fsPath; this.os = platform.OS; if (launchRemotely) { - const userHomeUri = await this._pathService.userHome; + const userHomeUri = await this._pathService.userHome(); this.userHome = userHomeUri.path; if (hasRemoteAuthority) { const remoteEnv = await this._remoteAgentService.getEnvironment(); @@ -162,6 +162,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce this._process = await this._launchProcess(shellLaunchConfig, cols, rows, this.userHome, isScreenReaderModeEnabled); } } + this.processState = ProcessState.LAUNCHING; this._process.onProcessData(data => { @@ -198,6 +199,13 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce this.processState = ProcessState.RUNNING; } }, LAUNCHING_DURATION); + + const error = await this._process.start(); + if (error) { + return error; + } + + return undefined; } private async _launchProcess( diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 40fdb32a03d..cb0c8c2fca0 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { TERMINAL_VIEW_ID, IShellLaunchConfig, ITerminalConfigHelper, ITerminalNativeService, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, ITerminalProcessExtHostProxy, IShellDefinition, LinuxDistro, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TERMINAL_VIEW_ID, IShellLaunchConfig, ITerminalConfigHelper, ITerminalNativeService, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, ITerminalProcessExtHostProxy, IShellDefinition, LinuxDistro, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; @@ -14,7 +14,7 @@ import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; -import { ITerminalService, ITerminalInstance, ITerminalTab, TerminalShellType, WindowsShellType, TerminalLinkHandlerCallback, LINK_INTERCEPT_THRESHOLD } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalService, ITerminalInstance, ITerminalTab, TerminalShellType, WindowsShellType, TerminalLinkHandlerCallback, LINK_INTERCEPT_THRESHOLD, ITerminalExternalLinkProvider } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { IQuickInputService, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput'; @@ -25,9 +25,6 @@ import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { isWindows, isMacintosh, OperatingSystem } from 'vs/base/common/platform'; import { basename } from 'vs/base/common/path'; -// TODO@daniel code layering -// eslint-disable-next-line code-layering, code-import-patterns -import { INativeOpenFileRequest } from 'vs/platform/windows/node/window'; import { find } from 'vs/base/common/arrays'; import { timeout } from 'vs/base/common/async'; import { IViewsService, ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views'; @@ -54,6 +51,8 @@ export class TerminalService implements ITerminalService { private _extHostsReady: { [authority: string]: IExtHostReadyEntry | undefined } = {}; private _activeTabIndex: number; private _linkHandlers: { [key: string]: TerminalLinkHandlerCallback } = {}; + private _linkProviders: Set = new Set(); + private _linkProviderDisposables: Map = new Map(); public get activeTabIndex(): number { return this._activeTabIndex; } public get terminalInstances(): ITerminalInstance[] { return this._terminalInstances; } @@ -72,6 +71,8 @@ export class TerminalService implements ITerminalService { public get onInstanceDisposed(): Event { return this._onInstanceDisposed.event; } private readonly _onInstanceProcessIdReady = new Emitter(); public get onInstanceProcessIdReady(): Event { return this._onInstanceProcessIdReady.event; } + private readonly _onInstanceLinksReady = new Emitter(); + public get onInstanceLinksReady(): Event { return this._onInstanceLinksReady.event; } private readonly _onInstanceRequestSpawnExtHostProcess = new Emitter(); public get onInstanceRequestSpawnExtHostProcess(): Event { return this._onInstanceRequestSpawnExtHostProcess.event; } private readonly _onInstanceRequestStartExtensionTerminal = new Emitter(); @@ -105,6 +106,8 @@ export class TerminalService implements ITerminalService { @IConfigurationService private _configurationService: IConfigurationService, @IViewsService private _viewsService: IViewsService, @IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService, + // HACK: Ideally TerminalNativeService would depend on TerminalService and inject the + // additional native functionality into it. @optional(ITerminalNativeService) terminalNativeService: ITerminalNativeService ) { // @optional could give undefined and properly typing it breaks service registration @@ -116,7 +119,14 @@ export class TerminalService implements ITerminalService { lifecycleService.onBeforeShutdown(async event => event.veto(this._onBeforeShutdown())); lifecycleService.onShutdown(() => this._onShutdown()); if (this._terminalNativeService) { - this._terminalNativeService.onOpenFileRequest(e => this._onOpenFileRequest(e)); + this._terminalNativeService.onRequestFocusActiveInstance(() => { + if (this.terminalInstances.length > 0) { + const terminal = this.getActiveInstance(); + if (terminal) { + terminal.focus(); + } + } + }); this._terminalNativeService.onOsResume(() => this._onOsResume()); } this._terminalFocusContextKey = KEYBINDING_CONTEXT_TERMINAL_FOCUS.bindTo(this._contextKeyService); @@ -128,6 +138,7 @@ export class TerminalService implements ITerminalService { const instance = this.getActiveInstance(); this._onActiveInstanceChanged.fire(instance ? instance : undefined); }); + this.onInstanceLinksReady(instance => this._setInstanceLinkProviders(instance)); this._handleContextKeys(); } @@ -147,18 +158,23 @@ export class TerminalService implements ITerminalService { return activeInstance ? activeInstance : this.createTerminal(undefined); } - public async requestSpawnExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise { + public async requestSpawnExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise { await this._extensionService.whenInstalledExtensionsRegistered(); // Wait for the remoteAuthority to be ready (and listening for events) before firing // the event to spawn the ext host process const conn = this._remoteAgentService.getConnection(); const remoteAuthority = conn ? conn.remoteAuthority : 'null'; await this._whenExtHostReady(remoteAuthority); - this._onInstanceRequestSpawnExtHostProcess.fire({ proxy, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, isWorkspaceShellAllowed }); + return new Promise(callback => { + this._onInstanceRequestSpawnExtHostProcess.fire({ proxy, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, isWorkspaceShellAllowed, callback }); + }); } - public requestStartExtensionTerminal(proxy: ITerminalProcessExtHostProxy, cols: number, rows: number): void { - this._onInstanceRequestStartExtensionTerminal.fire({ proxy, cols, rows }); + public requestStartExtensionTerminal(proxy: ITerminalProcessExtHostProxy, cols: number, rows: number): Promise { + // The initial request came from the extension host, no need to wait for it + return new Promise(callback => { + this._onInstanceRequestStartExtensionTerminal.fire({ proxy, cols, rows, callback }); + }); } public async extHostReady(remoteAuthority: string): Promise { @@ -210,22 +226,6 @@ export class TerminalService implements ITerminalService { this.terminalInstances.forEach(instance => instance.dispose(true)); } - private async _onOpenFileRequest(request: INativeOpenFileRequest): Promise { - // if the request to open files is coming in from the integrated terminal (identified though - // the termProgram variable) and we are instructed to wait for editors close, wait for the - // marker file to get deleted and then focus back to the integrated terminal. - if (request.termProgram === 'vscode' && request.filesToWait && this._terminalNativeService) { - const waitMarkerFileUri = URI.revive(request.filesToWait.waitMarkerFileUri); - await this._terminalNativeService.whenFileDeleted(waitMarkerFileUri); - if (this.terminalInstances.length > 0) { - const terminal = this.getActiveInstance(); - if (terminal) { - terminal.focus(); - } - } - } - } - private _onOsResume(): void { const activeTab = this.getActiveTab(); if (!activeTab) { @@ -424,6 +424,7 @@ export class TerminalService implements ITerminalService { instance.addDisposable(instance.onDisposed(this._onInstanceDisposed.fire, this._onInstanceDisposed)); instance.addDisposable(instance.onTitleChanged(this._onInstanceTitleChanged.fire, this._onInstanceTitleChanged)); instance.addDisposable(instance.onProcessIdReady(this._onInstanceProcessIdReady.fire, this._onInstanceProcessIdReady)); + instance.addDisposable(instance.onLinksReady(this._onInstanceLinksReady.fire, this._onInstanceLinksReady)); instance.addDisposable(instance.onDimensionsChanged(() => this._onInstanceDimensionsChanged.fire(instance))); instance.addDisposable(instance.onMaximumDimensionsChanged(() => this._onInstanceMaximumDimensionsChanged.fire(instance))); instance.addDisposable(instance.onFocus(this._onActiveInstanceChanged.fire, this._onActiveInstanceChanged)); @@ -473,6 +474,34 @@ export class TerminalService implements ITerminalService { }; } + public registerLinkProvider(linkProvider: ITerminalExternalLinkProvider): IDisposable { + const disposables: IDisposable[] = []; + this._linkProviders.add(linkProvider); + for (const instance of this.terminalInstances) { + if (instance.areLinksReady) { + disposables.push(instance.registerLinkProvider(linkProvider)); + } + } + this._linkProviderDisposables.set(linkProvider, disposables); + return { + dispose: () => { + const disposables = this._linkProviderDisposables.get(linkProvider) || []; + for (const disposable of disposables) { + disposable.dispose(); + } + this._linkProviders.delete(linkProvider); + } + }; + } + + private _setInstanceLinkProviders(instance: ITerminalInstance): void { + for (const linkProvider of this._linkProviders) { + const disposables = this._linkProviderDisposables.get(linkProvider); + const provider = instance.registerLinkProvider(linkProvider); + disposables?.push(provider); + } + } + private _getTabForInstance(instance: ITerminalInstance): ITerminalTab | undefined { return find(this._terminalTabs, tab => tab.terminalInstances.indexOf(instance) !== -1); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 2eeb515b6f0..55d0d5e1865 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -6,8 +6,7 @@ import * as dom from 'vs/base/browser/dom'; import * as nls from 'vs/nls'; import * as platform from 'vs/base/common/platform'; -import { Action, IAction } from 'vs/base/common/actions'; -import { IActionViewItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Action, IAction, Separator, IActionViewItem } from 'vs/base/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -110,6 +109,8 @@ export class TerminalViewPane extends ViewPane { } else { this.layoutBody(this._bodyDimensions.height, this._bodyDimensions.width); } + } else { + this._terminalService.getActiveTab()?.setVisible(false); } })); diff --git a/src/vs/workbench/contrib/terminal/browser/widgets/environmentVariableInfoWidget.ts b/src/vs/workbench/contrib/terminal/browser/widgets/environmentVariableInfoWidget.ts index 55f3f2b7970..3bd49844533 100644 --- a/src/vs/workbench/contrib/terminal/browser/widgets/environmentVariableInfoWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/widgets/environmentVariableInfoWidget.ts @@ -6,28 +6,27 @@ import { Widget } from 'vs/base/browser/ui/widget'; import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { MarkdownString } from 'vs/base/common/htmlContent'; -import { ITerminalWidget, IHoverTarget, IHoverAnchor, HorizontalAnchorSide, VerticalAnchorSide } from 'vs/workbench/contrib/terminal/browser/widgets/widgets'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { HoverWidget } from 'vs/workbench/contrib/terminal/browser/widgets/hoverWidget'; +import { ITerminalWidget } from 'vs/workbench/contrib/terminal/browser/widgets/widgets'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import * as dom from 'vs/base/browser/dom'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { IHoverService, IHoverOptions } from 'vs/workbench/services/hover/browser/hover'; export class EnvironmentVariableInfoWidget extends Widget implements ITerminalWidget { readonly id = 'env-var-info'; private _domNode: HTMLElement | undefined; private _container: HTMLElement | undefined; - private _hoverWidget: HoverWidget | undefined; private _mouseMoveListener: IDisposable | undefined; + private _hoverOptions: IHoverOptions | undefined; get requiresAction() { return this._info.requiresAction; } constructor( private _info: IEnvironmentVariableInfo, - @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IConfigurationService private readonly _configurationService: IConfigurationService + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IHoverService private readonly _hoverService: IHoverService ) { super(); } @@ -36,9 +35,11 @@ export class EnvironmentVariableInfoWidget extends Widget implements ITerminalWi this._container = container; this._domNode = document.createElement('div'); this._domNode.classList.add('terminal-env-var-info', 'codicon', `codicon-${this._info.getIcon()}`); + if (this.requiresAction) { + this._domNode.classList.add('requires-action'); + } container.appendChild(this._domNode); - const timeout = this._configurationService.getValue('editor.hover.delay'); const scheduler: RunOnceScheduler = new RunOnceScheduler(() => this._showHover(), timeout); this._register(scheduler); @@ -71,42 +72,21 @@ export class EnvironmentVariableInfoWidget extends Widget implements ITerminalWi } focus() { - this._showHover(); - this._hoverWidget?.focus(); + this._showHover(true); } - private _showHover() { - if (!this._domNode || !this._container || this._hoverWidget) { + private _showHover(focus?: boolean) { + if (!this._domNode || !this._container) { return; } - const target = new ElementHoverTarget(this._domNode); - const actions = this._info.getActions ? this._info.getActions() : undefined; - this._hoverWidget = this._instantiationService.createInstance(HoverWidget, this._container, target, new MarkdownString(this._info.getInfo()), () => { }, actions); - this._register(this._hoverWidget); - this._register(this._hoverWidget.onDispose(() => this._hoverWidget = undefined)); - } -} - -class ElementHoverTarget implements IHoverTarget { - readonly targetElements: readonly HTMLElement[]; - - constructor( - private _element: HTMLElement - ) { - this.targetElements = [this._element]; - } - - get anchor(): IHoverAnchor { - const position = dom.getDomNodePagePosition(this._element); - return { - x: position.left, - horizontalAnchorSide: HorizontalAnchorSide.Left, - y: document.documentElement.clientHeight - position.top - 1, - verticalAnchorSide: VerticalAnchorSide.Bottom, - fallbackY: position.top + position.height - }; - } - - dispose(): void { + if (!this._hoverOptions) { + const actions = this._info.getActions ? this._info.getActions() : undefined; + this._hoverOptions = { + target: this._domNode, + text: new MarkdownString(this._info.getInfo()), + actions + }; + } + this._hoverService.showHover(this._hoverOptions, focus); } } diff --git a/src/vs/workbench/contrib/terminal/browser/widgets/hoverWidget.ts b/src/vs/workbench/contrib/terminal/browser/widgets/hoverWidget.ts deleted file mode 100644 index 004d554f843..00000000000 --- a/src/vs/workbench/contrib/terminal/browser/widgets/hoverWidget.ts +++ /dev/null @@ -1,266 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; -import { IMarkdownString } from 'vs/base/common/htmlContent'; -import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; -import { Widget } from 'vs/base/browser/ui/widget'; -import { Event, Emitter } from 'vs/base/common/event'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { editorHoverHighlight, editorHoverBackground, editorHoverBorder, textLinkForeground, editorHoverForeground, editorHoverStatusBarBackground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry'; -import * as dom from 'vs/base/browser/dom'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IHoverTarget, HorizontalAnchorSide, VerticalAnchorSide } from 'vs/workbench/contrib/terminal/browser/widgets/widgets'; -import { KeyCode } from 'vs/base/common/keyCodes'; -import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions'; - -const $ = dom.$; - -export class HoverWidget extends Widget { - private readonly _containerDomNode: HTMLElement; - private readonly _domNode: HTMLElement; - private readonly _messageListeners = new DisposableStore(); - private readonly _mouseTracker: CompositeMouseTracker; - private readonly _scrollbar: DomScrollableElement; - - private _isDisposed: boolean = false; - - get isDisposed(): boolean { return this._isDisposed; } - get domNode(): HTMLElement { return this._containerDomNode; } - - private readonly _onDispose = new Emitter(); - get onDispose(): Event { return this._onDispose.event; } - - constructor( - private _container: HTMLElement, - private _target: IHoverTarget, - private _text: IMarkdownString, - private _linkHandler: (url: string) => void, - private _actions: { label: string, iconClass?: string, run: (target: HTMLElement) => void, commandId: string }[] | undefined, - @IKeybindingService private readonly _keybindingService: IKeybindingService, - @IConfigurationService private readonly _configurationService: IConfigurationService - ) { - super(); - this._containerDomNode = document.createElement('div'); - this._containerDomNode.classList.add('terminal-hover-widget', 'fadeIn', 'monaco-editor-hover', 'xterm-hover'); - this._containerDomNode.tabIndex = 0; - this._containerDomNode.setAttribute('role', 'tooltip'); - - 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()); - - // Don't allow mousedown out of the widget, otherwise preventDefault will call and text will - // not be selected. - this.onmousedown(this._containerDomNode, e => e.stopPropagation()); - - // Hide hover on escape - this.onkeydown(this._containerDomNode, e => { - if (e.equals(KeyCode.Escape)) { - this.dispose(); - } - }); - - const rowElement = $('div.hover-row.markdown-hover'); - const contentsElement = $('div.hover-contents'); - const markdownElement = renderMarkdown(this._text, { - actionHandler: { - callback: (content) => this._linkHandler(content), - disposeables: this._messageListeners - }, - codeBlockRenderer: async (_, value) => { - const fontFamily = this._configurationService.getValue('editor').fontFamily || EDITOR_FONT_DEFAULTS.fontFamily; - return `${value.replace(/\n/g, '
')}
`; - }, - codeBlockRenderCallback: () => { - contentsElement.classList.add('code-hover-contents'); - this.layout(); - } - }); - contentsElement.appendChild(markdownElement); - rowElement.appendChild(contentsElement); - this._domNode.appendChild(rowElement); - - if (this._actions && this._actions.length > 0) { - const statusBarElement = $('div.hover-row.status-bar'); - const actionsElement = $('div.actions'); - this._actions.forEach(action => this._renderAction(actionsElement, action)); - statusBarElement.appendChild(actionsElement); - this._containerDomNode.appendChild(statusBarElement); - } - - this._mouseTracker = new CompositeMouseTracker([this._containerDomNode, ..._target.targetElements]); - this._register(this._mouseTracker.onMouseOut(() => this.dispose())); - this._register(this._mouseTracker); - - this._container.appendChild(this._containerDomNode); - - this.layout(); - } - - 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')); - action.setAttribute('href', '#'); - action.setAttribute('role', 'button'); - if (actionOptions.iconClass) { - dom.append(action, $(`span.icon.${actionOptions.iconClass}`)); - } - const label = dom.append(action, $('span')); - const keybinding = this._keybindingService.lookupKeybinding(actionOptions.commandId); - const keybindingLabel = keybinding ? keybinding.getLabel() : null; - label.textContent = keybindingLabel ? `${actionOptions.label} (${keybindingLabel})` : actionOptions.label; - return dom.addDisposableListener(actionContainer, dom.EventType.CLICK, e => { - e.stopPropagation(); - e.preventDefault(); - actionOptions.run(actionContainer); - }); - } - - public layout(): void { - const anchor = this._target.anchor; - - this._containerDomNode.classList.remove('right-aligned'); - this._domNode.style.maxHeight = ''; - if (anchor.horizontalAnchorSide === HorizontalAnchorSide.Left) { - if (anchor.x + this._containerDomNode.clientWidth > document.documentElement.clientWidth) { - // Shift the hover to the left when part of it would get cut off - const width = Math.round(this._containerDomNode.clientWidth); - this._containerDomNode.style.width = `${width - 1}px`; - this._containerDomNode.style.maxWidth = ''; - const left = document.documentElement.clientWidth - width - 1; - this._containerDomNode.style.left = `${left}px`; - // Right align if the right edge is closer to the anchor than the left edge - if (left + width / 2 < anchor.x) { - this._containerDomNode.classList.add('right-aligned'); - } - } else { - this._containerDomNode.style.width = ''; - this._containerDomNode.style.maxWidth = `${document.documentElement.clientWidth - anchor.x - 1}px`; - this._containerDomNode.style.left = `${anchor.x}px`; - } - } else { - this._containerDomNode.style.right = `${anchor.x}px`; - } - // Use fallback y value if there is not enough vertical space - if (anchor.verticalAnchorSide === VerticalAnchorSide.Bottom) { - if (anchor.y + this._containerDomNode.clientHeight > document.documentElement.clientHeight) { - this._containerDomNode.style.top = `${anchor.fallbackY}px`; - this._domNode.style.maxHeight = `${document.documentElement.clientHeight - anchor.fallbackY}px`; - } else { - this._containerDomNode.style.bottom = `${anchor.y}px`; - this._containerDomNode.style.maxHeight = ''; - } - } else { - if (anchor.y + this._containerDomNode.clientHeight > document.documentElement.clientHeight) { - this._containerDomNode.style.bottom = `${anchor.fallbackY}px`; - } else { - this._containerDomNode.style.top = `${anchor.y}px`; - } - } - this._scrollbar.scanDomNode(); - } - - public focus() { - this._containerDomNode.focus(); - } - - public dispose(): void { - if (!this._isDisposed) { - this._onDispose.fire(); - this._containerDomNode.parentElement?.removeChild(this.domNode); - this._messageListeners.dispose(); - this._target.dispose(); - super.dispose(); - } - this._isDisposed = true; - } -} - -class CompositeMouseTracker extends Widget { - private _isMouseIn: boolean = false; - private _mouseTimeout: number | undefined; - - private readonly _onMouseOut = new Emitter(); - get onMouseOut(): Event { return this._onMouseOut.event; } - - constructor( - private _elements: HTMLElement[] - ) { - super(); - this._elements.forEach(n => this.onmouseover(n, () => this._onTargetMouseOver())); - this._elements.forEach(n => this.onnonbubblingmouseout(n, () => this._onTargetMouseOut())); - } - - private _onTargetMouseOver(): void { - this._isMouseIn = true; - this._clearEvaluateMouseStateTimeout(); - } - - private _onTargetMouseOut(): void { - this._isMouseIn = false; - this._evaluateMouseState(); - } - - private _evaluateMouseState(): void { - this._clearEvaluateMouseStateTimeout(); - // Evaluate whether the mouse is still outside asynchronously such that other mouse targets - // have the opportunity to first their mouse in event. - this._mouseTimeout = window.setTimeout(() => this._fireIfMouseOutside(), 0); - } - - private _clearEvaluateMouseStateTimeout(): void { - if (this._mouseTimeout) { - clearTimeout(this._mouseTimeout); - this._mouseTimeout = undefined; - } - } - - private _fireIfMouseOutside(): void { - if (!this._isMouseIn) { - this._onMouseOut.fire(); - } - } -} - - -registerThemingParticipant((theme, collector) => { - const editorHoverHighlightColor = theme.getColor(editorHoverHighlight); - if (editorHoverHighlightColor) { - collector.addRule(`.integrated-terminal .hoverHighlight { background-color: ${editorHoverHighlightColor}; }`); - } - const hoverBackground = theme.getColor(editorHoverBackground); - if (hoverBackground) { - collector.addRule(`.integrated-terminal .monaco-editor-hover { background-color: ${hoverBackground}; }`); - } - const hoverBorder = theme.getColor(editorHoverBorder); - if (hoverBorder) { - collector.addRule(`.integrated-terminal .monaco-editor-hover { border: 1px solid ${hoverBorder}; }`); - collector.addRule(`.integrated-terminal .monaco-editor-hover .hover-row:not(:first-child):not(:empty) { border-top: 1px solid ${hoverBorder.transparent(0.5)}; }`); - collector.addRule(`.integrated-terminal .monaco-editor-hover hr { border-top: 1px solid ${hoverBorder.transparent(0.5)}; }`); - collector.addRule(`.integrated-terminal .monaco-editor-hover hr { border-bottom: 0px solid ${hoverBorder.transparent(0.5)}; }`); - } - const link = theme.getColor(textLinkForeground); - if (link) { - collector.addRule(`.integrated-terminal .monaco-editor-hover a { color: ${link}; }`); - } - const hoverForeground = theme.getColor(editorHoverForeground); - if (hoverForeground) { - collector.addRule(`.integrated-terminal .monaco-editor-hover { color: ${hoverForeground}; }`); - } - const actionsBackground = theme.getColor(editorHoverStatusBarBackground); - if (actionsBackground) { - collector.addRule(`.integrated-terminal .monaco-editor-hover .hover-row .actions { background-color: ${actionsBackground}; }`); - } - const codeBackground = theme.getColor(textCodeBlockBackground); - if (codeBackground) { - collector.addRule(`.integrated-terminal .monaco-editor-hover code { background-color: ${codeBackground}; }`); - } -}); diff --git a/src/vs/workbench/contrib/terminal/browser/widgets/terminalHoverWidget.ts b/src/vs/workbench/contrib/terminal/browser/widgets/terminalHoverWidget.ts index 438ae87c290..121573ea0f5 100644 --- a/src/vs/workbench/contrib/terminal/browser/widgets/terminalHoverWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/widgets/terminalHoverWidget.ts @@ -6,11 +6,12 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { Widget } from 'vs/base/browser/ui/widget'; -import { ITerminalWidget, IHoverAnchor, IHoverTarget, HorizontalAnchorSide, VerticalAnchorSide } from 'vs/workbench/contrib/terminal/browser/widgets/widgets'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { HoverWidget } from 'vs/workbench/contrib/terminal/browser/widgets/hoverWidget'; +import { ITerminalWidget } from 'vs/workbench/contrib/terminal/browser/widgets/widgets'; import * as dom from 'vs/base/browser/dom'; import { IViewportRange } from 'xterm'; +import { IHoverTarget, IHoverService } from 'vs/workbench/services/hover/browser/hover'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { editorHoverHighlight } from 'vs/platform/theme/common/colorRegistry'; const $ = dom.$; @@ -28,8 +29,8 @@ export class TerminalHover extends Disposable implements ITerminalWidget { constructor( private readonly _targetOptions: ILinkHoverTargetOptions, private readonly _text: IMarkdownString, - private readonly _linkHandler: (url: string) => void, - @IInstantiationService private readonly _instantiationService: IInstantiationService + private readonly _linkHandler: (url: string) => any, + @IHoverService private readonly _hoverService: IHoverService ) { super(); } @@ -40,92 +41,91 @@ export class TerminalHover extends Disposable implements ITerminalWidget { attach(container: HTMLElement): void { const target = new CellHoverTarget(container, this._targetOptions); - this._register(this._instantiationService.createInstance(HoverWidget, container, target, this._text, this._linkHandler, [])); + this._hoverService.showHover({ + target, + text: this._text, + linkHandler: this._linkHandler, + // .xterm-hover lets xterm know that the hover is part of a link + additionalClasses: ['xterm-hover'] + }); } } class CellHoverTarget extends Widget implements IHoverTarget { - private _domNode: HTMLElement; - private _isDisposed: boolean = false; + private _domNode: HTMLElement | undefined; + private readonly _targetElements: HTMLElement[] = []; - readonly targetElements: readonly HTMLElement[]; + get targetElements(): readonly HTMLElement[] { return this._targetElements; } constructor( - private readonly _container: HTMLElement, - o: ILinkHoverTargetOptions + container: HTMLElement, + private readonly _options: ILinkHoverTargetOptions ) { super(); - this._domNode = $('div.terminal-hover-targets'); - const targets: HTMLElement[] = []; - const rowCount = o.viewportRange.end.y - o.viewportRange.start.y + 1; + this._domNode = $('div.terminal-hover-targets.xterm-hover'); + const rowCount = this._options.viewportRange.end.y - this._options.viewportRange.start.y + 1; // Add top target row - const width = (o.viewportRange.end.y > o.viewportRange.start.y ? o.terminalDimensions.width - o.viewportRange.start.x : o.viewportRange.end.x - o.viewportRange.start.x + 1) * o.cellDimensions.width; + const width = (this._options.viewportRange.end.y > this._options.viewportRange.start.y ? this._options.terminalDimensions.width - this._options.viewportRange.start.x : this._options.viewportRange.end.x - this._options.viewportRange.start.x + 1) * this._options.cellDimensions.width; const topTarget = $('div.terminal-hover-target.hoverHighlight'); - topTarget.style.left = `${o.viewportRange.start.x * o.cellDimensions.width}px`; - topTarget.style.bottom = `${(o.terminalDimensions.height - o.viewportRange.start.y - 1) * o.cellDimensions.height}px`; + topTarget.style.left = `${this._options.viewportRange.start.x * this._options.cellDimensions.width}px`; + topTarget.style.bottom = `${(this._options.terminalDimensions.height - this._options.viewportRange.start.y - 1) * this._options.cellDimensions.height}px`; topTarget.style.width = `${width}px`; - topTarget.style.height = `${o.cellDimensions.height}px`; - targets.push(this._domNode.appendChild(topTarget)); + topTarget.style.height = `${this._options.cellDimensions.height}px`; + this._targetElements.push(this._domNode.appendChild(topTarget)); // Add middle target rows if (rowCount > 2) { const middleTarget = $('div.terminal-hover-target.hoverHighlight'); middleTarget.style.left = `0px`; - middleTarget.style.bottom = `${(o.terminalDimensions.height - o.viewportRange.start.y - 1 - (rowCount - 2)) * o.cellDimensions.height}px`; - middleTarget.style.width = `${o.terminalDimensions.width * o.cellDimensions.width}px`; - middleTarget.style.height = `${(rowCount - 2) * o.cellDimensions.height}px`; - targets.push(this._domNode.appendChild(middleTarget)); + middleTarget.style.bottom = `${(this._options.terminalDimensions.height - this._options.viewportRange.start.y - 1 - (rowCount - 2)) * this._options.cellDimensions.height}px`; + middleTarget.style.width = `${this._options.terminalDimensions.width * this._options.cellDimensions.width}px`; + middleTarget.style.height = `${(rowCount - 2) * this._options.cellDimensions.height}px`; + this._targetElements.push(this._domNode.appendChild(middleTarget)); } // Add bottom target row if (rowCount > 1) { const bottomTarget = $('div.terminal-hover-target.hoverHighlight'); bottomTarget.style.left = `0px`; - bottomTarget.style.bottom = `${(o.terminalDimensions.height - o.viewportRange.end.y - 1) * o.cellDimensions.height}px`; - bottomTarget.style.width = `${(o.viewportRange.end.x + 1) * o.cellDimensions.width}px`; - bottomTarget.style.height = `${o.cellDimensions.height}px`; - targets.push(this._domNode.appendChild(bottomTarget)); + bottomTarget.style.bottom = `${(this._options.terminalDimensions.height - this._options.viewportRange.end.y - 1) * this._options.cellDimensions.height}px`; + bottomTarget.style.width = `${(this._options.viewportRange.end.x + 1) * this._options.cellDimensions.width}px`; + bottomTarget.style.height = `${this._options.cellDimensions.height}px`; + this._targetElements.push(this._domNode.appendChild(bottomTarget)); } - this.targetElements = targets; - - if (o.modifierDownCallback && o.modifierUpCallback) { + if (this._options.modifierDownCallback && this._options.modifierUpCallback) { let down = false; this._register(dom.addDisposableListener(document, 'keydown', e => { if (e.ctrlKey && !down) { down = true; - o.modifierDownCallback!(); + this._options.modifierDownCallback!(); } })); this._register(dom.addDisposableListener(document, 'keyup', e => { if (!e.ctrlKey) { down = false; - o.modifierUpCallback!(); + this._options.modifierUpCallback!(); } })); } - this._container.appendChild(this._domNode); + container.appendChild(this._domNode); } dispose(): void { - if (!this._isDisposed) { - this._container.removeChild(this._domNode); - } - this._isDisposed = true; + this._domNode?.parentElement?.removeChild(this._domNode); super.dispose(); } - - get anchor(): IHoverAnchor { - const firstPosition = dom.getDomNodePagePosition(this.targetElements[0]); - return { - x: firstPosition.left, - horizontalAnchorSide: HorizontalAnchorSide.Left, - y: document.documentElement.clientHeight - firstPosition.top - 1, - verticalAnchorSide: VerticalAnchorSide.Bottom, - fallbackY: firstPosition.top + firstPosition.height - 1 - }; - } } + +registerThemingParticipant((theme, collector) => { + let editorHoverHighlightColor = theme.getColor(editorHoverHighlight); + if (editorHoverHighlightColor) { + if (editorHoverHighlightColor.isOpaque()) { + editorHoverHighlightColor = editorHoverHighlightColor.transparent(0.5); + } + collector.addRule(`.integrated-terminal .hoverHighlight { background-color: ${editorHoverHighlightColor}; }`); + } +}); diff --git a/src/vs/workbench/contrib/terminal/browser/widgets/widgetManager.ts b/src/vs/workbench/contrib/terminal/browser/widgets/widgetManager.ts index 032610dbea7..b5bf843ffc7 100644 --- a/src/vs/workbench/contrib/terminal/browser/widgets/widgetManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/widgets/widgetManager.ts @@ -5,11 +5,15 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { ITerminalWidget } from 'vs/workbench/contrib/terminal/browser/widgets/widgets'; +import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; export class TerminalWidgetManager implements IDisposable { private _container: HTMLElement | undefined; private _attached: Map = new Map(); + constructor(@IHoverService private readonly _hoverService: IHoverService) { + } + attachToElement(terminalWrapper: HTMLElement) { if (!this._container) { this._container = document.createElement('div'); @@ -25,6 +29,10 @@ export class TerminalWidgetManager implements IDisposable { } } + hideHovers(): void { + this._hoverService.hideHover(); + } + attachWidget(widget: ITerminalWidget): IDisposable | undefined { if (!this._container) { return; diff --git a/src/vs/workbench/contrib/terminal/browser/widgets/widgets.ts b/src/vs/workbench/contrib/terminal/browser/widgets/widgets.ts index d9071035e97..50bc2ee7e5e 100644 --- a/src/vs/workbench/contrib/terminal/browser/widgets/widgets.ts +++ b/src/vs/workbench/contrib/terminal/browser/widgets/widgets.ts @@ -12,32 +12,3 @@ export interface ITerminalWidget extends IDisposable { id: string; attach(container: HTMLElement): void; } - -export enum HorizontalAnchorSide { - Left, - Right -} - -export enum VerticalAnchorSide { - Top, - Bottom -} - -export interface IHoverAnchor { - x: number; - y: number; - horizontalAnchorSide: HorizontalAnchorSide; - verticalAnchorSide: VerticalAnchorSide; - /** - * Fallback Y value to try with opposite VerticalAlignment if the hover does not fit vertically. - */ - fallbackY: number; -} - -/** - * A target for a hover which can know about domain-specific locations. - */ -export interface IHoverTarget extends IDisposable { - readonly targetElements: readonly HTMLElement[]; - readonly anchor: IHoverAnchor; -} diff --git a/src/vs/workbench/contrib/terminal/common/environmentVariable.ts b/src/vs/workbench/contrib/terminal/common/environmentVariable.ts index 655cdf158b5..bb32eafc81d 100644 --- a/src/vs/workbench/contrib/terminal/common/environmentVariable.ts +++ b/src/vs/workbench/contrib/terminal/common/environmentVariable.ts @@ -61,7 +61,7 @@ export interface IMergedEnvironmentVariableCollection { * Tracks and persists environment variable collections as defined by extensions. */ export interface IEnvironmentVariableService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Gets a single collection constructed by merging all environment variable collections into diff --git a/src/vs/workbench/contrib/terminal/common/environmentVariableService.ts b/src/vs/workbench/contrib/terminal/common/environmentVariableService.ts index f4cda20ee28..8373296bf55 100644 --- a/src/vs/workbench/contrib/terminal/common/environmentVariableService.ts +++ b/src/vs/workbench/contrib/terminal/common/environmentVariableService.ts @@ -22,7 +22,7 @@ interface ISerializableExtensionEnvironmentVariableCollection { * Tracks and persists environment variable collections as defined by extensions. */ export class EnvironmentVariableService implements IEnvironmentVariableService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; collections: Map = new Map(); mergedCollection: IMergedEnvironmentVariableCollection; diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index b498ad74639..e0d0fb53029 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -10,8 +10,8 @@ import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; import { OperatingSystem } from 'vs/base/common/platform'; -import { IOpenFileRequest } from 'vs/platform/windows/common/windows'; import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable'; +import { IExtensionPointDescriptor } from 'vs/workbench/services/extensions/common/extensionsRegistry'; export const TERMINAL_VIEW_ID = 'workbench.panel.terminal'; @@ -70,10 +70,6 @@ export const TERMINAL_ACTION_CATEGORY = nls.localize('terminalCategory', "Termin export const DEFAULT_LETTER_SPACING = 0; export const MINIMUM_LETTER_SPACING = -5; export const DEFAULT_LINE_HEIGHT = 1; -export const SHELL_PATH_INVALID_EXIT_CODE = -1; -export const SHELL_PATH_DIRECTORY_EXIT_CODE = -2; -export const SHELL_CWD_INVALID_EXIT_CODE = -3; -export const LEGACY_CONSOLE_MODE_EXIT_CODE = 3221225786; // microsoft/vscode#73790 export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'; @@ -237,11 +233,11 @@ export interface IShellLaunchConfig { * Provides access to native or electron APIs to other terminal services. */ export interface ITerminalNativeService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly linuxDistro: LinuxDistro; - readonly onOpenFileRequest: Event; + readonly onRequestFocusActiveInstance: Event; readonly onOsResume: Event; getWindowsBuildNumber(): number; @@ -308,7 +304,7 @@ export interface ITerminalProcessManager extends IDisposable { readonly onEnvironmentVariableInfoChanged: Event; dispose(immediate?: boolean): void; - createProcess(shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number, isScreenReaderModeEnabled: boolean): Promise; + createProcess(shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number, isScreenReaderModeEnabled: boolean): Promise; write(data: string): void; setDimensions(cols: number, rows: number): void; @@ -364,12 +360,14 @@ export interface ISpawnExtHostProcessRequest { cols: number; rows: number; isWorkspaceShellAllowed: boolean; + callback: (error: ITerminalLaunchError | undefined) => void; } export interface IStartExtensionTerminalRequest { proxy: ITerminalProcessExtHostProxy; cols: number; rows: number; + callback: (error: ITerminalLaunchError | undefined) => void; } export interface IAvailableShellsRequest { @@ -402,6 +400,11 @@ export interface IWindowsShellHelper extends IDisposable { getShellName(): Promise; } +export interface ITerminalLaunchError { + message: string; + code?: number; +} + /** * An interface representing a raw terminal child process, this contains a subset of the * child_process.ChildProcess node.js interface. @@ -414,6 +417,14 @@ export interface ITerminalChildProcess { onProcessOverrideDimensions?: Event; onProcessResolvedShellLaunchConfig?: Event; + /** + * Starts the process. + * + * @returns undefined when the process was successfully started, otherwise an object containing + * information on what went wrong. + */ + start(): Promise; + /** * Shutdown the terminal process. * @@ -614,3 +625,41 @@ export const DEFAULT_COMMANDS_TO_SKIP_SHELL: string[] = [ 'workbench.action.quickOpenView', 'workbench.action.toggleMaximizedPanel' ]; + +export interface ITerminalContributions { + types?: ITerminalTypeContribution[]; +} + +export interface ITerminalTypeContribution { + title: string; + command: string; +} + +export const terminalContributionsDescriptor: IExtensionPointDescriptor = { + extensionPoint: 'terminal', + defaultExtensionKind: 'workspace', + jsonSchema: { + description: nls.localize('vscode.extension.contributes.terminal', 'Contributes terminal functionality.'), + type: 'object', + properties: { + types: { + type: 'array', + description: nls.localize('vscode.extension.contributes.terminal.types', "Defines additional terminal types that the user can create."), + items: { + type: 'object', + required: ['command', 'title'], + properties: { + command: { + description: nls.localize('vscode.extension.contributes.terminal.types.command', "Command to execute when the user creates this type of terminal."), + type: 'string', + }, + title: { + description: nls.localize('vscode.extension.contributes.terminal.types.title', "Title for this type of terminal."), + type: 'string', + }, + }, + }, + }, + }, + }, +}; diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index f369566829c..f38353bcb8d 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -16,17 +16,26 @@ export const terminalConfiguration: IConfigurationNode = { type: 'object', properties: { 'terminal.integrated.automationShell.linux': { - markdownDescription: localize('terminal.integrated.automationShell.linux', "A path that when set will override {0} and ignore {1} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.linux`', '`shellArgs`'), + markdownDescription: localize({ + key: 'terminal.integrated.automationShell.linux', + comment: ['{0} and {1} are the `shell` and `shellArgs` settings keys'] + }, "A path that when set will override {0} and ignore {1} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.linux`', '`shellArgs`'), type: ['string', 'null'], default: null }, 'terminal.integrated.automationShell.osx': { - markdownDescription: localize('terminal.integrated.automationShell.osx', "A path that when set will override {0} and ignore {1} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.osx`', '`shellArgs`'), + markdownDescription: localize({ + key: 'terminal.integrated.automationShell.osx', + comment: ['{0} and {1} are the `shell` and `shellArgs` settings keys'] + }, "A path that when set will override {0} and ignore {1} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.osx`', '`shellArgs`'), type: ['string', 'null'], default: null }, 'terminal.integrated.automationShell.windows': { - markdownDescription: localize('terminal.integrated.automationShell.windows', "A path that when set will override {0} and ignore {1} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.windows`', '`shellArgs`'), + markdownDescription: localize({ + key: 'terminal.integrated.automationShell.windows', + comment: ['{0} and {1} are the `shell` and `shellArgs` settings keys'] + }, "A path that when set will override {0} and ignore {1} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.windows`', '`shellArgs`'), type: ['string', 'null'], default: null }, @@ -209,7 +218,7 @@ export const terminalConfiguration: IConfigurationNode = { default: false }, 'terminal.integrated.commandsToSkipShell': { - markdownDescription: localize('terminal.integrated.commandsToSkipShell', "A set of command IDs whose keybindings will not be sent to the shell and instead always be handled by Code. This allows the use of keybindings that would normally be consumed by the shell to act the same as when the terminal is not focused, for example ctrl+p to launch Quick Open. Use the command prefixed with `-` to remove default commands from the list.\nDefault Skipped Commands:\n\n{0}", DEFAULT_COMMANDS_TO_SKIP_SHELL.sort().map(command => `- ${command}`).join('\n')), + markdownDescription: localize('terminal.integrated.commandsToSkipShell', "A set of command IDs whose keybindings will not be sent to the shell but instead always be handled by VS Code. This allows keybindings that would normally be consumed by the shell to act instead the same as when the terminal is not focused, for example `Ctrl+P` to launch Quick Open.\n\n \n\nMany commands are skipped by default. To override a default and pass that command's keybinding to the shell instead, add the command prefixed with the `-` character. For example add `-workbench.action.quickOpen` to allow `Ctrl+P` to reach the shell.\n\n \n\nThe following list of default skipped commands is truncated when viewed in Settings Editor. To see the full list, [open the default settings JSON](command:workbench.action.openRawDefaultSettings 'Open Default Settings (JSON)') and search for the first command from the list below.\n\n \n\nDefault Skipped Commands:\n\n{0}", DEFAULT_COMMANDS_TO_SKIP_SHELL.sort().map(command => `- ${command}`).join('\n')), type: 'array', items: { type: 'string' diff --git a/src/vs/workbench/contrib/terminal/common/terminalExtensionPoints.contribution.ts b/src/vs/workbench/contrib/terminal/common/terminalExtensionPoints.contribution.ts new file mode 100644 index 00000000000..86183be9c77 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/common/terminalExtensionPoints.contribution.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. + *--------------------------------------------------------------------------------------------*/ + +import { ITerminalContributionService, TerminalContributionService } from './terminalExtensionPoints'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; + +registerSingleton(ITerminalContributionService, TerminalContributionService, true); diff --git a/src/vs/workbench/contrib/terminal/common/terminalExtensionPoints.ts b/src/vs/workbench/contrib/terminal/common/terminalExtensionPoints.ts new file mode 100644 index 00000000000..3bd6a63a328 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/common/terminalExtensionPoints.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. + *--------------------------------------------------------------------------------------------*/ + +import * as extensionsRegistry from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { ITerminalTypeContribution, ITerminalContributions, terminalContributionsDescriptor } from 'vs/workbench/contrib/terminal/common/terminal'; +import { flatten } from 'vs/base/common/arrays'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +// terminal extension point +export const terminalsExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint(terminalContributionsDescriptor); + +export interface ITerminalContributionService { + readonly _serviceBrand: undefined; + + readonly terminalTypes: ReadonlyArray; +} + +export const ITerminalContributionService = createDecorator('terminalContributionsService'); + +export class TerminalContributionService implements ITerminalContributionService { + public readonly _serviceBrand = undefined; + + private _terminalTypes: ReadonlyArray = []; + + public get terminalTypes() { + return this._terminalTypes; + } + + constructor() { + terminalsExtPoint.setHandler(contributions => { + this._terminalTypes = flatten(contributions.filter(c => c.description.enableProposedApi).map(c => c.value?.types ?? [])); + }); + } +} diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts index 31034b572e2..2dbb6a0f2be 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts @@ -13,7 +13,6 @@ import { getSystemShell } from 'vs/workbench/contrib/terminal/node/terminal'; import { Terminal as XTermTerminal } from 'xterm'; import { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; import { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; -import { WebLinksAddon as XTermWebLinksAddon } from 'xterm-addon-web-links'; import { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { getDefaultShell, getDefaultShellArgs } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; @@ -27,7 +26,6 @@ import { ILogService } from 'vs/platform/log/common/log'; let Terminal: typeof XTermTerminal; let SearchAddon: typeof XTermSearchAddon; let Unicode11Addon: typeof XTermUnicode11Addon; -let WebLinksAddon: typeof XTermWebLinksAddon; let WebglAddon: typeof XTermWebglAddon; export class TerminalInstanceService implements ITerminalInstanceService { @@ -51,13 +49,6 @@ export class TerminalInstanceService implements ITerminalInstanceService { return Terminal; } - public async getXtermWebLinksConstructor(): Promise { - if (!WebLinksAddon) { - WebLinksAddon = (await import('xterm-addon-web-links')).WebLinksAddon; - } - return WebLinksAddon; - } - public async getXtermSearchConstructor(): Promise { if (!SearchAddon) { SearchAddon = (await import('xterm-addon-search')).SearchAddon; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts index cc02eddf1ea..84030ff44a7 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ipcRenderer as ipc } from 'electron'; +import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { IOpenFileRequest } from 'vs/platform/windows/common/windows'; import { ITerminalNativeService, LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; import { URI } from 'vs/base/common/uri'; @@ -15,24 +15,30 @@ import { Emitter, Event } from 'vs/base/common/event'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { registerRemoteContributions } from 'vs/workbench/contrib/terminal/electron-browser/terminalRemote'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { INativeOpenFileRequest } from 'vs/platform/windows/node/window'; -export class TerminalNativeService implements ITerminalNativeService { +export class TerminalNativeService extends Disposable implements ITerminalNativeService { public _serviceBrand: undefined; public get linuxDistro(): LinuxDistro { return linuxDistro; } - private readonly _onOpenFileRequest = new Emitter(); - public get onOpenFileRequest(): Event { return this._onOpenFileRequest.event; } - private readonly _onOsResume = new Emitter(); + private readonly _onRequestFocusActiveInstance = this._register(new Emitter()); + public get onRequestFocusActiveInstance(): Event { return this._onRequestFocusActiveInstance.event; } + private readonly _onOsResume = this._register(new Emitter()); public get onOsResume(): Event { return this._onOsResume.event; } constructor( @IFileService private readonly _fileService: IFileService, @IInstantiationService readonly instantiationService: IInstantiationService, - @IRemoteAgentService remoteAgentService: IRemoteAgentService + @IRemoteAgentService remoteAgentService: IRemoteAgentService, + @IElectronService electronService: IElectronService ) { - ipc.on('vscode:openFiles', (_event: any, request: IOpenFileRequest) => this._onOpenFileRequest.fire(request)); - ipc.on('vscode:osResume', () => this._onOsResume.fire()); + super(); + + ipcRenderer.on('vscode:openFiles', (event: unknown, request: IOpenFileRequest) => this._onOpenFileRequest(request)); + this._register(electronService.onOSResume(() => this._onOsResume.fire())); const connection = remoteAgentService.getConnection(); if (connection && connection.remoteAuthority) { @@ -40,6 +46,17 @@ export class TerminalNativeService implements ITerminalNativeService { } } + private async _onOpenFileRequest(request: INativeOpenFileRequest): Promise { + // if the request to open files is coming in from the integrated terminal (identified though + // the termProgram variable) and we are instructed to wait for editors close, wait for the + // marker file to get deleted and then focus back to the integrated terminal. + if (request.termProgram === 'vscode' && request.filesToWait) { + const waitMarkerFileUri = URI.revive(request.filesToWait.waitMarkerFileUri); + await this.whenFileDeleted(waitMarkerFileUri); + this._onRequestFocusActiveInstance.fire(); + } + } + public whenFileDeleted(path: URI): Promise { // Complete when wait marker file is deleted return new Promise(resolve => { diff --git a/src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts index a362d720738..33aca264cf7 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts @@ -48,7 +48,7 @@ export async function getMainProcessParentEnv(): Promise { // For macOS we want the "root" environment as shells by default run as login shells. It // doesn't appear to be possible to get the "root" environment as `ps eww -o command` for // PID 1 (the parent of the main process when launched from the dock/finder) returns no - // environment, because of this we will fill in the root environment using a whitelist of + // environment, because of this we will fill in the root environment using a allowlist of // environment variables that we have. if (isMacintosh) { mainProcessParentEnv = {}; @@ -129,4 +129,4 @@ export async function findExecutable(command: string, cwd?: string, paths?: stri } const fullPath = path.join(cwd, command); return await exists(fullPath) ? fullPath : undefined; -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts index 1c04b6137fd..c59d62f463e 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts +++ b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as os from 'os'; import * as path from 'vs/base/common/path'; import * as platform from 'vs/base/common/platform'; import * as pty from 'node-pty'; @@ -11,22 +10,27 @@ import * as fs from 'fs'; import { Event, Emitter } from 'vs/base/common/event'; import { getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/terminal'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IShellLaunchConfig, ITerminalChildProcess, SHELL_PATH_INVALID_EXIT_CODE, SHELL_PATH_DIRECTORY_EXIT_CODE, SHELL_CWD_INVALID_EXIT_CODE } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalChildProcess, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; import { exec } from 'child_process'; import { ILogService } from 'vs/platform/log/common/log'; import { stat } from 'vs/base/node/pfs'; import { findExecutable } from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; import { URI } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; export class TerminalProcess extends Disposable implements ITerminalChildProcess { private _exitCode: number | undefined; + private _exitMessage: string | undefined; private _closeTimeout: any; private _ptyProcess: pty.IPty | undefined; private _currentTitle: string = ''; private _processStartupComplete: Promise | undefined; private _isDisposed: boolean = false; private _titleInterval: NodeJS.Timer | null = null; - private _initialCwd: string; + private readonly _initialCwd: string; + private readonly _ptyOptions: pty.IPtyForkOptions | pty.IWindowsPtyForkOptions; + + public get exitMessage(): string | undefined { return this._exitMessage; } private readonly _onProcessData = this._register(new Emitter()); public get onProcessData(): Event { return this._onProcessData.event; } @@ -38,7 +42,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess public get onProcessTitleChanged(): Event { return this._onProcessTitleChanged.event; } constructor( - shellLaunchConfig: IShellLaunchConfig, + private readonly _shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, @@ -47,73 +51,80 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess @ILogService private readonly _logService: ILogService ) { super(); - let shellName: string; - if (os.platform() === 'win32') { - shellName = path.basename(shellLaunchConfig.executable || ''); + let name: string; + if (platform.isWindows) { + name = path.basename(this._shellLaunchConfig.executable || ''); } else { // Using 'xterm-256color' here helps ensure that the majority of Linux distributions will use a // color prompt as defined in the default ~/.bashrc file. - shellName = 'xterm-256color'; + name = 'xterm-256color'; } - this._initialCwd = cwd; - const useConpty = windowsEnableConpty && process.platform === 'win32' && getWindowsBuildNumber() >= 18309; - const options: pty.IPtyForkOptions | pty.IWindowsPtyForkOptions = { - name: shellName, + this._ptyOptions = { + name, cwd, env, cols, rows, useConpty, // This option will force conpty to not redraw the whole viewport on launch - conptyInheritCursor: useConpty && !!shellLaunchConfig.initialText + conptyInheritCursor: useConpty && !!_shellLaunchConfig.initialText }; - - // TODO: Pull verification out into its own function - const cwdVerification = stat(cwd).then(async stat => { - if (!stat.isDirectory()) { - return Promise.reject(SHELL_CWD_INVALID_EXIT_CODE); - } - return undefined; - }, async err => { - if (err && err.code === 'ENOENT') { - // So we can include in the error message the specified CWD - shellLaunchConfig.cwd = cwd; - return Promise.reject(SHELL_CWD_INVALID_EXIT_CODE); - } - return undefined; - }); - - const executableVerification = stat(shellLaunchConfig.executable!).then(async stat => { - if (!stat.isFile() && !stat.isSymbolicLink()) { - return Promise.reject(stat.isDirectory() ? SHELL_PATH_DIRECTORY_EXIT_CODE : SHELL_PATH_INVALID_EXIT_CODE); - } - return undefined; - }, async (err) => { - if (err && err.code === 'ENOENT') { - let cwd = shellLaunchConfig.cwd instanceof URI ? shellLaunchConfig.cwd.path : shellLaunchConfig.cwd!; - // Try to get path - const envPaths: string[] | undefined = (shellLaunchConfig.env && shellLaunchConfig.env.PATH) ? shellLaunchConfig.env.PATH.split(path.delimiter) : undefined; - const executable = await findExecutable(shellLaunchConfig.executable!, cwd, envPaths); - if (!executable) { - return Promise.reject(SHELL_PATH_INVALID_EXIT_CODE); - } - } - return undefined; - }); - - Promise.all([cwdVerification, executableVerification]).then(() => { - this.setupPtyProcess(shellLaunchConfig, options); - }).catch((exitCode: number) => { - return this._launchFailed(exitCode); - }); } - private _launchFailed(exitCode: number): void { - this._exitCode = exitCode; - this._queueProcessExit(); - this._processStartupComplete = Promise.resolve(undefined); + public async start(): Promise { + const results = await Promise.all([this._validateCwd(), this._validateExecutable()]); + const firstError = results.find(r => r !== undefined); + if (firstError) { + return firstError; + } + + try { + this.setupPtyProcess(this._shellLaunchConfig, this._ptyOptions); + return undefined; + } catch (err) { + this._logService.trace('IPty#spawn native exception', err); + return { message: `A native exception occurred during launch (${err.message})` }; + } + } + + private async _validateCwd(): Promise { + try { + const result = await stat(this._initialCwd); + if (!result.isDirectory()) { + return { message: localize('launchFail.cwdNotDirectory', "Starting directory (cwd) \"{0}\" is not a directory", this._initialCwd.toString()) }; + } + } catch (err) { + if (err?.code === 'ENOENT') { + return { message: localize('launchFail.cwdDoesNotExist', "Starting directory (cwd) \"{0}\" does not exist", this._initialCwd.toString()) }; + } + } + return undefined; + } + + private async _validateExecutable(): Promise { + const slc = this._shellLaunchConfig; + if (!slc.executable) { + throw new Error('IShellLaunchConfig.executable not set'); + } + try { + const result = await stat(slc.executable); + if (!result.isFile() && !result.isSymbolicLink()) { + return { message: localize('launchFail.executableIsNotFileOrSymlink', "Path to shell executable \"{0}\" is not a file of a symlink", slc.executable) }; + } + } catch (err) { + if (err?.code === 'ENOENT') { + // The executable isn't an absolute path, try find it on the PATH or CWD + let cwd = slc.cwd instanceof URI ? slc.cwd.path : slc.cwd!; + const envPaths: string[] | undefined = (slc.env && slc.env.PATH) ? slc.env.PATH.split(path.delimiter) : undefined; + const executable = await findExecutable(slc.executable!, cwd, envPaths); + if (!executable) { + return { message: localize('launchFail.executableDoesNotExist', "Path to shell executable \"{0}\" does not exist", slc.executable) }; + } + } + } + return undefined; } private setupPtyProcess(shellLaunchConfig: IShellLaunchConfig, options: pty.IPtyForkOptions): void { @@ -124,22 +135,19 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess this._processStartupComplete = new Promise(c => { this.onProcessReady(() => c()); }); - ptyProcess.on('data', data => { + ptyProcess.onData(data => { this._onProcessData.fire(data); if (this._closeTimeout) { clearTimeout(this._closeTimeout); this._queueProcessExit(); } }); - ptyProcess.on('exit', code => { - this._exitCode = code; + ptyProcess.onExit(e => { + this._exitCode = e.exitCode; this._queueProcessExit(); }); this._setupTitlePolling(ptyProcess); - // TODO: We should no longer need to delay this since pty.spawn is sync - setTimeout(() => { - this._sendProcessId(ptyProcess); - }, 500); + this._sendProcessId(ptyProcess.pid); } public dispose(): void { @@ -200,8 +208,8 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess this.dispose(); } - private _sendProcessId(ptyProcess: pty.IPty) { - this._onProcessReady.fire({ pid: ptyProcess.pid, cwd: this._initialCwd }); + private _sendProcessId(pid: number) { + this._onProcessReady.fire({ pid, cwd: this._initialCwd }); } private _sendProcessTitle(ptyProcess: pty.IPty): void { @@ -245,7 +253,8 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess this._ptyProcess.resize(cols, rows); } catch (e) { // Swallow error if the pty has already exited - if (this._exitCode !== undefined) { + this._logService.trace('IPty#resize exception ' + e.message); + if (this._exitCode !== undefined && e.message !== 'ioctl(2) failed, EBADF') { throw e; } } diff --git a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkManager.test.ts b/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkManager.test.ts index b5e5a01c9a0..2223e0d082a 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkManager.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkManager.test.ts @@ -5,11 +5,10 @@ import * as assert from 'assert'; import { OperatingSystem } from 'vs/base/common/platform'; -import { TerminalLinkManager, LineColumnInfo, XtermLinkMatcherHandler } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager'; -import * as strings from 'vs/base/common/strings'; +import { TerminalLinkManager, XtermLinkMatcherHandler } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager'; +import { Terminal as XtermTerminal } from 'xterm'; import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { Event } from 'vs/base/common/event'; -import { ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { TestPathService, TestEnvironmentService } from 'vs/workbench/test/browser/workbenchTestServices'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; @@ -21,12 +20,6 @@ class TestTerminalLinkManager extends TerminalLinkManager { public get localLinkRegex(): RegExp { return this._localLinkRegex; } - public get gitDiffLinkPreImageRegex(): RegExp { - return this._gitDiffPreImageRegex; - } - public get gitDiffLinkPostImageRegex(): RegExp { - return this._gitDiffPostImageRegex; - } public preprocessPath(link: string): string | null { return this._preprocessPath(link); } @@ -39,17 +32,12 @@ class TestTerminalLinkManager extends TerminalLinkManager { } } -class TestXterm { - public loadAddon() { } - public registerLinkMatcher() { } -} - class MockTerminalInstanceService implements ITerminalInstanceService { onRequestDefaultShellAndArgs?: Event | undefined; getDefaultShellAndArgs(): Promise<{ shell: string; args: string | string[] | undefined; }> { throw new Error('Method not implemented.'); } - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; getXtermConstructor(): Promise { throw new Error('Method not implemented.'); } @@ -59,9 +47,6 @@ class MockTerminalInstanceService implements ITerminalInstanceService { getXtermUnicode11Constructor(): Promise { throw new Error('Method not implemented.'); } - async getXtermWebLinksConstructor(): Promise { - return (await import('xterm-addon-web-links')).WebLinksAddon; - } getXtermWebglConstructor(): Promise { throw new Error('Method not implemented.'); } @@ -76,180 +61,26 @@ class MockTerminalInstanceService implements ITerminalInstanceService { } } -interface LinkFormatInfo { - urlFormat: string; - line?: string; - column?: string; -} - -const testConfigHelper: ITerminalConfigHelper = { - config: { - enableFileLinks: true - } -}; - -suite('Workbench - TerminalLinkHandler', () => { +suite('Workbench - TerminalLinkManager', () => { let instantiationService: TestInstantiationService; - setup(() => { + setup(async () => { + const configurationService = new TestConfigurationService(); + await configurationService.setUserConfiguration('terminal', { integrated: { enableFileLinks: true } }); + instantiationService = new TestInstantiationService(); instantiationService.stub(IEnvironmentService, TestEnvironmentService); instantiationService.stub(IPathService, new TestPathService()); instantiationService.stub(ITerminalInstanceService, new MockTerminalInstanceService()); - instantiationService.stub(IConfigurationService, new TestConfigurationService()); - }); - - suite('localLinkRegex', () => { - test('Windows', () => { - const terminalLinkHandler: TestTerminalLinkManager = instantiationService.createInstance(TestTerminalLinkManager, new TestXterm(), { - os: OperatingSystem.Windows, - userHome: '' - } as any, testConfigHelper); - function testLink(link: string, linkUrl: string, lineNo?: string, columnNo?: string) { - assert.equal(terminalLinkHandler.extractLinkUrl(link), linkUrl); - assert.equal(terminalLinkHandler.extractLinkUrl(`:${link}:`), linkUrl); - assert.equal(terminalLinkHandler.extractLinkUrl(`;${link};`), linkUrl); - assert.equal(terminalLinkHandler.extractLinkUrl(`(${link})`), linkUrl); - - if (lineNo) { - const lineColumnInfo: LineColumnInfo = terminalLinkHandler.extractLineColumnInfo(link); - assert.equal(lineColumnInfo.lineNumber, lineNo, `For link ${link}, expected line number ${lineNo}, actual ${lineColumnInfo.lineNumber}`); - - if (columnNo) { - assert.equal(lineColumnInfo.columnNumber, columnNo, `For link ${link}, expected column number ${columnNo}, actual ${lineColumnInfo.columnNumber}`); - } - } - } - - function generateAndTestLinks() { - const linkUrls = [ - 'c:\\foo', - '\\\\?\\c:\\foo', - 'c:/foo', - '.\\foo', - './foo', - '..\\foo', - '~\\foo', - '~/foo', - 'c:/a/long/path', - 'c:\\a\\long\\path', - 'c:\\mixed/slash\\path', - 'a/relative/path', - 'plain/path', - 'plain\\path' - ]; - - const supportedLinkFormats: LinkFormatInfo[] = [ - { urlFormat: '{0}' }, - { urlFormat: '{0} on line {1}', line: '5' }, - { urlFormat: '{0} on line {1}, column {2}', line: '5', column: '3' }, - { urlFormat: '{0}:line {1}', line: '5' }, - { urlFormat: '{0}:line {1}, column {2}', line: '5', column: '3' }, - { urlFormat: '{0}({1})', line: '5' }, - { urlFormat: '{0} ({1})', line: '5' }, - { urlFormat: '{0}({1},{2})', line: '5', column: '3' }, - { urlFormat: '{0} ({1},{2})', line: '5', column: '3' }, - { urlFormat: '{0}({1}, {2})', line: '5', column: '3' }, - { urlFormat: '{0} ({1}, {2})', line: '5', column: '3' }, - { urlFormat: '{0}:{1}', line: '5' }, - { urlFormat: '{0}:{1}:{2}', line: '5', column: '3' }, - { urlFormat: '{0}[{1}]', line: '5' }, - { urlFormat: '{0} [{1}]', line: '5' }, - { urlFormat: '{0}[{1},{2}]', line: '5', column: '3' }, - { urlFormat: '{0} [{1},{2}]', line: '5', column: '3' }, - { urlFormat: '{0}[{1}, {2}]', line: '5', column: '3' }, - { urlFormat: '{0} [{1}, {2}]', line: '5', column: '3' }, - { urlFormat: '"{0}",{1}', line: '5' } - ]; - - linkUrls.forEach(linkUrl => { - supportedLinkFormats.forEach(linkFormatInfo => { - testLink( - strings.format(linkFormatInfo.urlFormat, linkUrl, linkFormatInfo.line, linkFormatInfo.column), - linkUrl, - linkFormatInfo.line, - linkFormatInfo.column - ); - }); - }); - } - - generateAndTestLinks(); - }); - - test('Linux', () => { - const terminalLinkHandler: TestTerminalLinkManager = instantiationService.createInstance(TestTerminalLinkManager, new TestXterm(), { - os: OperatingSystem.Linux, - userHome: '' - } as any, testConfigHelper); - function testLink(link: string, linkUrl: string, lineNo?: string, columnNo?: string) { - assert.equal(terminalLinkHandler.extractLinkUrl(link), linkUrl); - assert.equal(terminalLinkHandler.extractLinkUrl(`:${link}:`), linkUrl); - assert.equal(terminalLinkHandler.extractLinkUrl(`;${link};`), linkUrl); - assert.equal(terminalLinkHandler.extractLinkUrl(`(${link})`), linkUrl); - - if (lineNo) { - const lineColumnInfo: LineColumnInfo = terminalLinkHandler.extractLineColumnInfo(link); - assert.equal(lineColumnInfo.lineNumber, lineNo, `For link ${link}, expected line number ${lineNo}, actual ${lineColumnInfo.lineNumber}`); - - if (columnNo) { - assert.equal(lineColumnInfo.columnNumber, columnNo, `For link ${link}, expected column number ${columnNo}, actual ${lineColumnInfo.columnNumber}`); - } - } - } - - function generateAndTestLinks() { - const linkUrls = [ - '/foo', - '~/foo', - './foo', - '../foo', - '/a/long/path', - 'a/relative/path' - ]; - - const supportedLinkFormats: LinkFormatInfo[] = [ - { urlFormat: '{0}' }, - { urlFormat: '{0} on line {1}', line: '5' }, - { urlFormat: '{0} on line {1}, column {2}', line: '5', column: '3' }, - { urlFormat: '{0}:line {1}', line: '5' }, - { urlFormat: '{0}:line {1}, column {2}', line: '5', column: '3' }, - { urlFormat: '{0}({1})', line: '5' }, - { urlFormat: '{0} ({1})', line: '5' }, - { urlFormat: '{0}({1},{2})', line: '5', column: '3' }, - { urlFormat: '{0} ({1},{2})', line: '5', column: '3' }, - { urlFormat: '{0}:{1}', line: '5' }, - { urlFormat: '{0}:{1}:{2}', line: '5', column: '3' }, - { urlFormat: '{0}[{1}]', line: '5' }, - { urlFormat: '{0} [{1}]', line: '5' }, - { urlFormat: '{0}[{1},{2}]', line: '5', column: '3' }, - { urlFormat: '{0} [{1},{2}]', line: '5', column: '3' }, - { urlFormat: '"{0}",{1}', line: '5' } - ]; - - linkUrls.forEach(linkUrl => { - supportedLinkFormats.forEach(linkFormatInfo => { - // console.log('linkFormatInfo: ', linkFormatInfo); - testLink( - strings.format(linkFormatInfo.urlFormat, linkUrl, linkFormatInfo.line, linkFormatInfo.column), - linkUrl, - linkFormatInfo.line, - linkFormatInfo.column - ); - }); - }); - } - - generateAndTestLinks(); - }); + instantiationService.stub(IConfigurationService, configurationService); }); suite('preprocessPath', () => { test('Windows', () => { - const linkHandler: TestTerminalLinkManager = instantiationService.createInstance(TestTerminalLinkManager, new TestXterm() as any, { + const linkHandler: TestTerminalLinkManager = instantiationService.createInstance(TestTerminalLinkManager, new XtermTerminal() as any, { os: OperatingSystem.Windows, userHome: 'C:\\Users\\Me' - } as any, testConfigHelper); + } as any); linkHandler.processCwd = 'C:\\base'; assert.equal(linkHandler.preprocessPath('./src/file1'), 'C:\\base\\src\\file1'); @@ -260,10 +91,10 @@ suite('Workbench - TerminalLinkHandler', () => { assert.equal(linkHandler.preprocessPath('\\\\?\\C:\\absolute\\path\\extended\\file6'), 'C:\\absolute\\path\\extended\\file6'); }); test('Windows - spaces', () => { - const linkHandler: TestTerminalLinkManager = instantiationService.createInstance(TestTerminalLinkManager, new TestXterm() as any, { + const linkHandler: TestTerminalLinkManager = instantiationService.createInstance(TestTerminalLinkManager, new XtermTerminal() as any, { os: OperatingSystem.Windows, userHome: 'C:\\Users\\M e' - } as any, testConfigHelper); + } as any); linkHandler.processCwd = 'C:\\base dir'; assert.equal(linkHandler.preprocessPath('./src/file1'), 'C:\\base dir\\src\\file1'); @@ -274,10 +105,10 @@ suite('Workbench - TerminalLinkHandler', () => { }); test('Linux', () => { - const linkHandler: TestTerminalLinkManager = instantiationService.createInstance(TestTerminalLinkManager, new TestXterm() as any, { + const linkHandler: TestTerminalLinkManager = instantiationService.createInstance(TestTerminalLinkManager, new XtermTerminal() as any, { os: OperatingSystem.Linux, userHome: '/home/me' - } as any, testConfigHelper); + } as any); linkHandler.processCwd = '/base'; assert.equal(linkHandler.preprocessPath('./src/file1'), '/base/src/file1'); @@ -287,10 +118,10 @@ suite('Workbench - TerminalLinkHandler', () => { }); test('No Workspace', () => { - const linkHandler: TestTerminalLinkManager = instantiationService.createInstance(TestTerminalLinkManager, new TestXterm() as any, { + const linkHandler: TestTerminalLinkManager = instantiationService.createInstance(TestTerminalLinkManager, new XtermTerminal() as any, { os: OperatingSystem.Linux, userHome: '/home/me' - } as any, testConfigHelper); + } as any); assert.equal(linkHandler.preprocessPath('./src/file1'), null); assert.equal(linkHandler.preprocessPath('src/file2'), null); @@ -299,43 +130,14 @@ suite('Workbench - TerminalLinkHandler', () => { }); }); - test('gitDiffLinkRegex', () => { - // The platform is irrelevant because the links generated by Git are the same format regardless of platform - const linkHandler: TestTerminalLinkManager = instantiationService.createInstance(TestTerminalLinkManager, new TestXterm() as any, { - os: OperatingSystem.Linux, - userHome: '' - } as any, testConfigHelper); - - function assertAreGoodMatches(matches: RegExpMatchArray | null) { - if (matches) { - assert.equal(matches.length, 2); - assert.equal(matches[1], 'src/file1'); - } else { - assert.fail(); - } - } - - // Happy cases - assertAreGoodMatches('--- a/src/file1'.match(linkHandler.gitDiffLinkPreImageRegex)); - assertAreGoodMatches('--- a/src/file1 '.match(linkHandler.gitDiffLinkPreImageRegex)); - assertAreGoodMatches('+++ b/src/file1'.match(linkHandler.gitDiffLinkPostImageRegex)); - assertAreGoodMatches('+++ b/src/file1 '.match(linkHandler.gitDiffLinkPostImageRegex)); - - // Make sure /dev/null isn't a match - assert.equal(linkHandler.gitDiffLinkPreImageRegex.test('--- /dev/null'), false); - assert.equal(linkHandler.gitDiffLinkPreImageRegex.test('--- /dev/null '), false); - assert.equal(linkHandler.gitDiffLinkPostImageRegex.test('+++ /dev/null'), false); - assert.equal(linkHandler.gitDiffLinkPostImageRegex.test('+++ /dev/null '), false); - }); - suite('wrapLinkHandler', () => { const nullMouseEvent: any = Object.freeze({ preventDefault: () => { } }); test('should allow intercepting of links with onBeforeHandleLink', async () => { - const linkHandler: TestTerminalLinkManager = instantiationService.createInstance(TestTerminalLinkManager, new TestXterm() as any, { + const linkHandler: TestTerminalLinkManager = instantiationService.createInstance(TestTerminalLinkManager, new XtermTerminal() as any, { os: OperatingSystem.Linux, userHome: '' - } as any, testConfigHelper); + } as any); linkHandler.onBeforeHandleLink(e => { if (e.link === 'https://www.microsoft.com') { intercepted = true; diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts index d38285c04f7..ff3a71ec746 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts @@ -343,7 +343,7 @@ const productIconThemeDescriptor = SyncActionDescriptor.from(SelectProductIconTh Registry.as(Extensions.WorkbenchActions).registerWorkbenchAction(productIconThemeDescriptor, 'Preferences: Product Icon Theme', category); -const developerCategory = localize('developer', "Developer"); +const developerCategory = localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer"); const generateColorThemeDescriptor = SyncActionDescriptor.from(GenerateColorThemeAction); Registry.as(Extensions.WorkbenchActions).registerWorkbenchAction(generateColorThemeDescriptor, 'Developer: Generate Color Theme From Current Settings', developerCategory); diff --git a/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts b/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts index 6a14c04bd8f..b7ca9dbb216 100644 --- a/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts +++ b/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts @@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IViewsRegistry, IViewDescriptor, Extensions as ViewExtensions } from 'vs/workbench/common/views'; import { VIEW_CONTAINER } from 'vs/workbench/contrib/files/browser/explorerViewlet'; import { ITimelineService, TimelinePaneId } from 'vs/workbench/contrib/timeline/common/timeline'; -import { TimelineService } from 'vs/workbench/contrib/timeline/common/timelineService'; +import { TimelineHasProviderContext, TimelineService } from 'vs/workbench/contrib/timeline/common/timelineService'; import { TimelinePane } from './timelinePane'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -30,6 +30,7 @@ export class TimelinePaneDescriptor implements IViewDescriptor { readonly canToggleVisibility = true; readonly hideByDefault = false; readonly canMoveView = true; + readonly when = TimelineHasProviderContext; focusCommand = { id: 'timeline.focus' }; } @@ -43,19 +44,22 @@ configurationRegistry.registerConfiguration({ type: 'object', properties: { 'timeline.excludeSources': { - type: 'array', + type: [ + 'array', + 'null' + ], + default: null, description: localize('timeline.excludeSources', "An array of Timeline sources that should be excluded from the Timeline view"), - default: null }, 'timeline.pageSize': { - type: 'number', + type: ['number', 'null'], + default: null, markdownDescription: localize('timeline.pageSize', "The number of items to show in the Timeline view by default and when loading more items. Setting to `null` (the default) will automatically choose a page size based on the visible area of the Timeline view"), - default: null }, 'timeline.pageOnScroll': { type: 'boolean', + default: false, description: localize('timeline.pageOnScroll', "Experimental. Controls whether the Timeline view will load the next page of items when you scroll to the end of the list"), - default: false }, } }); diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index 6ddc7254512..153a46de95e 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/timelinePane'; import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; -import { IAction, ActionRunner } from 'vs/base/common/actions'; +import { IAction, ActionRunner, IActionViewItemProvider } from 'vs/base/common/actions'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { fromNow } from 'vs/base/common/date'; import { debounce } from 'vs/base/common/decorators'; @@ -22,7 +22,7 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { IListVirtualDelegate, IIdentityProvider, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; import { ITreeNode, ITreeRenderer, ITreeContextMenuEvent, ITreeElement } from 'vs/base/browser/ui/tree/tree'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; -import { TreeResourceNavigator, WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; +import { WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ContextKeyExpr, IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -36,10 +36,11 @@ import { IThemeService, LIGHT, ThemeIcon } from 'vs/platform/theme/common/themeS import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IActionViewItemProvider, ActionBar, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; -import { ContextAwareMenuEntryActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { MenuItemAction, IMenuService, MenuId, registerAction2, Action2, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { MenuEntryActionViewItem, createAndFillInContextMenuActions, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { MenuItemAction, IMenuService, MenuId, registerAction2, Action2, MenuRegistry, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; const ItemHeight = 22; @@ -293,8 +294,7 @@ export class TimelinePane extends ViewPane { get pageSize() { let pageSize = this.configurationService.getValue('timeline.pageSize'); - // eslint-disable-next-line eqeqeq - if (pageSize == null) { + if (pageSize === undefined || pageSize === null) { // If we are paging when scrolling, then add an extra item to the end to make sure the "Load more" item is out of view pageSize = Math.max(20, Math.floor((this.tree.renderHeight / ItemHeight) + (this.pageOnScroll ? 1 : -1))); } @@ -347,7 +347,7 @@ export class TimelinePane extends ViewPane { const editor = this.editorService.activeEditor; if (editor) { - uri = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }); + uri = toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY }); } if ((uri?.toString(true) === this.uri?.toString(true) && uri !== undefined) || @@ -883,7 +883,13 @@ export class TimelinePane extends ViewPane { if (isLoadMoreCommand(element)) { return element.ariaLabel; } - return element.ariaLabel ?? localize('timeline.aria.item', "{0}: {1}", element.relativeTime ?? '', element.label); + return element.accessibilityInformation ? element.accessibilityInformation.label : localize('timeline.aria.item', "{0}: {1}", element.relativeTime ?? '', element.label); + }, + getRole(element: TreeElement): string { + if (isLoadMoreCommand(element)) { + return 'treeitem'; + } + return element.accessibilityInformation && element.accessibilityInformation.role ? element.accessibilityInformation.role : 'treeitem'; }, getWidgetAriaLabel(): string { return localize('timeline', "Timeline"); @@ -892,37 +898,30 @@ export class TimelinePane extends ViewPane { keyboardNavigationLabelProvider: new TimelineKeyboardNavigationLabelProvider(), overrideStyles: { listBackground: this.getBackgroundColor(), - } }); - const customTreeNavigator = new TreeResourceNavigator(this.tree, { openOnFocus: false, openOnSelection: false }); - this._register(customTreeNavigator); this._register(this.tree.onContextMenu(e => this.onContextMenu(this.commands, e))); this._register(this.tree.onDidChangeSelection(e => this.ensureValidItems())); - this._register( - customTreeNavigator.onDidOpenResource(e => { - if (!e.browserEvent || !this.ensureValidItems()) { - return; - } + this._register(this.tree.onDidOpen(e => { + if (!e.browserEvent || !this.ensureValidItems()) { + return; + } - const selection = this.tree.getSelection(); - const item = selection.length === 1 ? selection[0] : undefined; - // eslint-disable-next-line eqeqeq - if (item == null) { - return; - } + const item = e.element; + if (item === null) { + return; + } - if (isTimelineItem(item)) { - if (item.command) { - this.commandService.executeCommand(item.command.id, ...(item.command.arguments || [])); - } + if (isTimelineItem(item)) { + if (item.command) { + this.commandService.executeCommand(item.command.id, ...(item.command.arguments || [])); } - else if (isLoadMoreCommand(item)) { - this.loadMore(item); - } - }) - ); + } + else if (isLoadMoreCommand(item)) { + this.loadMore(item); + } + })); } private loadMore(item: LoadMoreCommand) { @@ -1094,9 +1093,15 @@ class TimelineTreeRenderer implements ITreeRenderer action instanceof MenuItemAction - ? this.instantiationService.createInstance(ContextAwareMenuEntryActionViewItem, action) - : undefined; + this.actionViewItemProvider = (action: IAction) => { + if (action instanceof MenuItemAction) { + return this.instantiationService.createInstance(MenuEntryActionViewItem, action); + } else if (action instanceof SubmenuItemAction) { + return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action); + } + + return undefined; + }; } private uri: URI | undefined; @@ -1241,7 +1246,6 @@ class TimelinePaneCommands extends Disposable { createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, this.contextMenuService, g => /^inline/.test(g)); menu.dispose(); - scoped.dispose(); return result; } diff --git a/src/vs/workbench/contrib/timeline/common/timeline.ts b/src/vs/workbench/contrib/timeline/common/timeline.ts index 8f2823c6b7b..c63e6ad52a1 100644 --- a/src/vs/workbench/contrib/timeline/common/timeline.ts +++ b/src/vs/workbench/contrib/timeline/common/timeline.ts @@ -10,6 +10,7 @@ import { URI } from 'vs/base/common/uri'; import { Command } from 'vs/editor/common/modes'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility'; export function toKey(extension: ExtensionIdentifier | string, source: string) { return `${typeof extension === 'string' ? extension : ExtensionIdentifier.toKey(extension)}|${source}`; @@ -24,7 +25,7 @@ export interface TimelineItem { id?: string; timestamp: number; label: string; - ariaLabel?: string; + accessibilityInformation?: IAccessibilityInformation; icon?: URI, iconDark?: URI, themeIcon?: { id: string }, diff --git a/src/vs/workbench/contrib/timeline/common/timelineService.ts b/src/vs/workbench/contrib/timeline/common/timelineService.ts index 4fd45ce01ac..2d2d5e76d5a 100644 --- a/src/vs/workbench/contrib/timeline/common/timelineService.ts +++ b/src/vs/workbench/contrib/timeline/common/timelineService.ts @@ -11,9 +11,13 @@ import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; import { ITimelineService, TimelineChangeEvent, TimelineOptions, TimelineProvidersChangeEvent, TimelineProvider, InternalTimelineOptions, TimelinePaneId } from './timeline'; import { IViewsService } from 'vs/workbench/common/views'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; + +export const TimelineHasProviderContext = new RawContextKey('timelineHasProvider', false); export class TimelineService implements ITimelineService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _onDidChangeProviders = new Emitter(); readonly onDidChangeProviders: Event = this._onDidChangeProviders.event; @@ -23,13 +27,30 @@ export class TimelineService implements ITimelineService { private readonly _onDidChangeUri = new Emitter(); readonly onDidChangeUri: Event = this._onDidChangeUri.event; + private excludedSources: Set; + private readonly hasProviderContext: IContextKey; private readonly providers = new Map(); private readonly providerSubscriptions = new Map(); constructor( @ILogService private readonly logService: ILogService, @IViewsService protected viewsService: IViewsService, + @IConfigurationService protected configurationService: IConfigurationService, + @IContextKeyService protected contextKeyService: IContextKeyService, ) { + this.hasProviderContext = TimelineHasProviderContext.bindTo(this.contextKeyService); + + this.excludedSources = new Set(configurationService.getValue('timeline.excludeSources')); + configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('timeline.excludeSources')) { + this.excludedSources = new Set(this.configurationService.getValue('timeline.excludeSources')); + + this.updateHasProviderContext(); + } + }, this); + + this.updateHasProviderContext(); + // let source = 'fast-source'; // this.registerTimelineProvider({ // scheme: '*', @@ -213,6 +234,9 @@ export class TimelineService implements ITimelineService { } this.providers.set(id, provider); + + this.updateHasProviderContext(); + if (provider.onDidChange) { this.providerSubscriptions.set(id, provider.onDidChange(e => this._onDidChangeTimeline.fire(e))); } @@ -235,6 +259,9 @@ export class TimelineService implements ITimelineService { this.providers.delete(id); this.providerSubscriptions.delete(id); + + this.updateHasProviderContext(); + this._onDidChangeProviders.fire({ removed: [id] }); } @@ -242,4 +269,14 @@ export class TimelineService implements ITimelineService { this.viewsService.openView(TimelinePaneId, true); this._onDidChangeUri.fire(uri); } + + private updateHasProviderContext() { + if (this.providers.size === 0) { + this.hasProviderContext.set(false); + return; + } + + const hasProviders = [...this.providers.keys()].some(id => !this.excludedSources.has(id)); + this.hasProviderContext.set(hasProviders); + } } diff --git a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts index b8ce8995fde..d25b380ca89 100644 --- a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts +++ b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts @@ -95,10 +95,10 @@ export class ReleaseNotesManager { return true; } - private loadReleaseNotes(version: string): Promise { + private async loadReleaseNotes(version: string): Promise { const match = /^(\d+\.\d+)\./.exec(version); if (!match) { - return Promise.reject(new Error('not found')); + throw new Error('not found'); } const versionLabel = match[1].replace(/\./g, '_'); @@ -138,17 +138,30 @@ export class ReleaseNotesManager { .replace(/kbstyle\(([^\)]+)\)/gi, kbstyle); }; - if (!this._releaseNotesCache.has(version)) { - this._releaseNotesCache.set(version, this._requestService.request({ url }, CancellationToken.None) - .then(asText) - .then(text => { - if (!text || !/^#\s/.test(text)) { // release notes always starts with `#` followed by whitespace - return Promise.reject(new Error('Invalid release notes')); - } + const fetchReleaseNotes = async () => { + let text; + try { + text = await asText(await this._requestService.request({ url }, CancellationToken.None)); + } catch { + throw new Error('Failed to fetch release notes'); + } - return Promise.resolve(text); - }) - .then(text => patchKeybindings(text))); + if (!text || !/^#\s/.test(text)) { // release notes always starts with `#` followed by whitespace + throw new Error('Invalid release notes'); + } + + return patchKeybindings(text); + }; + + if (!this._releaseNotesCache.has(version)) { + this._releaseNotesCache.set(version, (async () => { + try { + return await fetchReleaseNotes(); + } catch (err) { + this._releaseNotesCache.delete(version); + throw err; + } + })()); } return this._releaseNotesCache.get(version)!; @@ -257,8 +270,9 @@ export class ReleaseNotesManager { } code { - font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; - font-size: 14px; + font-family: var(--vscode-editor-font-family); + font-weight: var(--vscode-editor-font-weight); + font-size: var(--vscode-editor-font-size); line-height: 19px; } diff --git a/src/vs/workbench/contrib/update/browser/update.contribution.ts b/src/vs/workbench/contrib/update/browser/update.contribution.ts index 38b453f6f5f..f842ef50965 100644 --- a/src/vs/workbench/contrib/update/browser/update.contribution.ts +++ b/src/vs/workbench/contrib/update/browser/update.contribution.ts @@ -9,7 +9,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { ShowCurrentReleaseNotesAction, ProductContribution, UpdateContribution, CheckForVSCodeUpdateAction, CONTEXT_UPDATE_STATE } from 'vs/workbench/contrib/update/browser/update'; +import { ShowCurrentReleaseNotesAction, ProductContribution, UpdateContribution, CheckForVSCodeUpdateAction, CONTEXT_UPDATE_STATE, SwitchProductQualityContribution } from 'vs/workbench/contrib/update/browser/update'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import product from 'vs/platform/product/common/product'; import { StateType } from 'vs/platform/update/common/update'; @@ -18,6 +18,7 @@ const workbench = Registry.as(WorkbenchExtensio workbench.registerWorkbenchContribution(ProductContribution, LifecyclePhase.Restored); workbench.registerWorkbenchContribution(UpdateContribution, LifecyclePhase.Restored); +workbench.registerWorkbenchContribution(SwitchProductQualityContribution, LifecyclePhase.Restored); const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); diff --git a/src/vs/workbench/contrib/update/browser/update.ts b/src/vs/workbench/contrib/update/browser/update.ts index 8739dd769d6..5d590de2e93 100644 --- a/src/vs/workbench/contrib/update/browser/update.ts +++ b/src/vs/workbench/contrib/update/browser/update.ts @@ -51,12 +51,13 @@ export class OpenLatestReleaseNotesInBrowserAction extends Action { super('update.openLatestReleaseNotes', nls.localize('releaseNotes', "Release Notes"), undefined, true); } - run(): Promise { + async run(): Promise { if (this.productService.releaseNotesUrl) { const uri = URI.parse(this.productService.releaseNotesUrl); - return this.openerService.open(uri); + await this.openerService.open(uri); + } else { + throw new Error(nls.localize('update.noReleaseNotesOnline', "This version of {0} does not have release notes online", this.productService.nameLong)); } - return Promise.resolve(false); } } @@ -71,18 +72,22 @@ export abstract class AbstractShowReleaseNotesAction extends Action { super(id, label, undefined, true); } - run(): Promise { + async run(): Promise { if (!this.enabled) { - return Promise.resolve(false); + return; } - this.enabled = false; - return showReleaseNotes(this.instantiationService, this.version) - .then(undefined, () => { - const action = this.instantiationService.createInstance(OpenLatestReleaseNotesInBrowserAction); - return action.run().then(() => false); - }); + try { + await showReleaseNotes(this.instantiationService, this.version); + } catch (err) { + const action = this.instantiationService.createInstance(OpenLatestReleaseNotesInBrowserAction); + try { + await action.run(); + } catch (err2) { + throw new Error(`${err.message} and ${err2.message}`); + } + } } } @@ -179,7 +184,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu @IActivityService private readonly activityService: IActivityService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IProductService private readonly productService: IProductService, - @IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService ) { super(); @@ -219,7 +224,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu case StateType.Idle: if (state.error) { this.onError(state.error); - } else if (this.state.type === StateType.CheckingForUpdates && this.state.context === this.workbenchEnvironmentService.configuration.sessionId) { + } else if (this.state.type === StateType.CheckingForUpdates && this.state.context === this.environmentService.configuration.sessionId) { this.onUpdateNotAvailable(); } break; @@ -404,7 +409,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu } private registerGlobalActivityActions(): void { - CommandsRegistry.registerCommand('update.check', () => this.updateService.checkForUpdates(this.workbenchEnvironmentService.configuration.sessionId)); + CommandsRegistry.registerCommand('update.check', () => this.updateService.checkForUpdates(this.environmentService.configuration.sessionId)); MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { group: '6_update', command: { @@ -479,6 +484,50 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu } } +export class SwitchProductQualityContribution extends Disposable implements IWorkbenchContribution { + + constructor( + @IProductService private readonly productService: IProductService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService + ) { + super(); + + this.registerGlobalActivityActions(); + } + + private registerGlobalActivityActions(): void { + const quality = this.productService.quality; + const productQualityChangeHandler = this.environmentService.options?.productQualityChangeHandler; + if (productQualityChangeHandler && (quality === 'stable' || quality === 'insider')) { + const newQuality = quality === 'stable' ? 'insider' : 'stable'; + const commandId = `update.switchQuality.${newQuality}`; + CommandsRegistry.registerCommand(commandId, async accessor => { + const dialogService = accessor.get(IDialogService); + + const res = await dialogService.confirm({ + type: 'info', + message: nls.localize('relaunchMessage', "Changing the version requires a reload to take effect"), + detail: newQuality === 'insider' ? + nls.localize('relaunchDetailInsiders', "Press the reload button to switch to the nightly pre-production version of VSCode.") : + nls.localize('relaunchDetailStable', "Press the reload button to switch to the monthly released stable version of VSCode."), + primaryButton: nls.localize('reload', "&&Reload") + }); + + if (res.confirmed) { + productQualityChangeHandler(newQuality); + } + }); + MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { + group: '6_update', + command: { + id: commandId, + title: newQuality === 'insider' ? nls.localize('switchToInsiders', "Switch to Insiders Version...") : nls.localize('switchToStable', "Switch to Stable Version...") + } + }); + } + } +} + export class CheckForVSCodeUpdateAction extends Action { static readonly ID = CheckForVSCodeUpdateActionId; diff --git a/src/vs/workbench/contrib/url/common/externalUriResolver.ts b/src/vs/workbench/contrib/url/browser/externalUriResolver.ts similarity index 100% rename from src/vs/workbench/contrib/url/common/externalUriResolver.ts rename to src/vs/workbench/contrib/url/browser/externalUriResolver.ts diff --git a/src/vs/workbench/contrib/url/common/trustedDomains.ts b/src/vs/workbench/contrib/url/browser/trustedDomains.ts similarity index 59% rename from src/vs/workbench/contrib/url/common/trustedDomains.ts rename to src/vs/workbench/contrib/url/browser/trustedDomains.ts index caa31e673c7..9f71f4b6f4b 100644 --- a/src/vs/workbench/contrib/url/common/trustedDomains.ts +++ b/src/vs/workbench/contrib/url/browser/trustedDomains.ts @@ -11,6 +11,12 @@ import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/commo import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; const TRUSTED_DOMAINS_URI = URI.parse('trustedDomains:/Trusted Domains'); @@ -41,10 +47,13 @@ type ConfigureTrustedDomainsChoiceClassification = { export async function configureOpenerTrustedDomainsHandler( trustedDomains: string[], domainToConfigure: string, + resource: URI, quickInputService: IQuickInputService, storageService: IStorageService, editorService: IEditorService, - telemetryService: ITelemetryService + telemetryService: ITelemetryService, + notificationService: INotificationService, + clipboardService: IClipboardService, ) { const parsedDomainToConfigure = URI.parse(domainToConfigure); const toplevelDomainSegements = parsedDomainToConfigure.authority.split('.'); @@ -88,10 +97,12 @@ export async function configureOpenerTrustedDomainsHandler( switch (pickedResult.id) { case 'manage': - editorService.openEditor({ + await editorService.openEditor({ resource: TRUSTED_DOMAINS_URI, mode: 'jsonc' }); + notificationService.prompt(Severity.Info, localize('configuringURL', "Configuring trust for: {0}", resource.toString()), + [{ label: 'Copy', run: () => clipboardService.writeText(resource.toString()) }]); return trustedDomains; case 'trustDomain': case 'trustSubdomain': @@ -108,7 +119,7 @@ export async function configureOpenerTrustedDomainsHandler( StorageScope.GLOBAL ); - return [...trustedDomains, pickedResult.id]; + return [...trustedDomains, itemToTrust]; } } } @@ -116,7 +127,49 @@ export async function configureOpenerTrustedDomainsHandler( return []; } -export function readTrustedDomains(storageService: IStorageService, productService: IProductService) { +// Exported for testing. +export function extractGitHubRemotesFromGitConfig(gitConfig: string): string[] { + const domains = new Set(); + let match: RegExpExecArray | null; + + const RemoteMatcher = /^\s*url\s*=\s*(?:git@|https:\/\/)github\.com(?::|\/)(\S*)\s*$/mg; + while (match = RemoteMatcher.exec(gitConfig)) { + const repo = match[1].replace(/\.git$/, ''); + if (repo) { + domains.add(`https://github.com/${repo}/`); + } + } + return [...domains]; +} + +async function getRemotes(fileService: IFileService, textFileService: ITextFileService, contextService: IWorkspaceContextService): Promise { + const workspaceUris = contextService.getWorkspace().folders.map(folder => folder.uri); + const domains = await Promise.race([ + new Promise(resolve => setTimeout(() => resolve([]), 2000)), + Promise.all(workspaceUris.map(async workspaceUri => { + const path = workspaceUri.path; + const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/.git/config` }); + const exists = await fileService.exists(uri); + if (!exists) { + return []; + } + const gitConfig = (await (textFileService.read(uri, { acceptTextOnly: true }).catch(() => ({ value: '' })))).value; + return extractGitHubRemotesFromGitConfig(gitConfig); + }))]); + + const set = domains.reduce((set, list) => list.reduce((set, item) => set.add(item), set), new Set()); + return [...set]; +} + +export async function readTrustedDomains(accessor: ServicesAccessor) { + + const storageService = accessor.get(IStorageService); + const productService = accessor.get(IProductService); + const authenticationService = accessor.get(IAuthenticationService); + const fileService = accessor.get(IFileService); + const textFileService = accessor.get(ITextFileService); + const workspaceContextService = accessor.get(IWorkspaceContextService); + const defaultTrustedDomains: string[] = productService.linkProtectionTrustedDomains ? [...productService.linkProtectionTrustedDomains] : []; @@ -129,8 +182,20 @@ export function readTrustedDomains(storageService: IStorageService, productServi } } catch (err) { } + const userDomains = + authenticationService.isAuthenticationProviderRegistered('github') + ? ((await authenticationService.getSessions('github')) ?? []) + .map(session => session.account.label) + .filter((v, i, a) => a.indexOf(v) === i) + .map(username => `https://github.com/${username}/`) + : []; + + const workspaceDomains = await getRemotes(fileService, textFileService, workspaceContextService); + return { defaultTrustedDomains, - trustedDomains + trustedDomains, + userDomains, + workspaceDomains }; } diff --git a/src/vs/workbench/contrib/url/common/trustedDomainsFileSystemProvider.ts b/src/vs/workbench/contrib/url/browser/trustedDomainsFileSystemProvider.ts similarity index 78% rename from src/vs/workbench/contrib/url/common/trustedDomainsFileSystemProvider.ts rename to src/vs/workbench/contrib/url/browser/trustedDomainsFileSystemProvider.ts index aba030acc3c..b34f0b5ba6f 100644 --- a/src/vs/workbench/contrib/url/common/trustedDomainsFileSystemProvider.ts +++ b/src/vs/workbench/contrib/url/browser/trustedDomainsFileSystemProvider.ts @@ -11,9 +11,10 @@ import { FileDeleteOptions, FileOverwriteOptions, FileSystemProviderCapabilities import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { VSBuffer } from 'vs/base/common/buffer'; -import { readTrustedDomains, TRUSTED_DOMAINS_CONTENT_STORAGE_KEY, TRUSTED_DOMAINS_STORAGE_KEY } from 'vs/workbench/contrib/url/common/trustedDomains'; -import { IProductService } from 'vs/platform/product/common/productService'; +import { readTrustedDomains, TRUSTED_DOMAINS_CONTENT_STORAGE_KEY, TRUSTED_DOMAINS_STORAGE_KEY } from 'vs/workbench/contrib/url/browser/trustedDomains'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { assertIsDefined } from 'vs/base/common/types'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; const TRUSTED_DOMAINS_SCHEMA = 'trustedDomains'; @@ -45,7 +46,7 @@ const CONFIG_PLACEHOLDER_TEXT = `[ // "https://microsoft.com" ]`; -function computeTrustedDomainContent(defaultTrustedDomains: string[], trustedDomains: string[]) { +function computeTrustedDomainContent(defaultTrustedDomains: string[], trustedDomains: string[], userTrustedDomains: string[], workspaceTrustedDomains: string[]) { let content = CONFIG_HELP_TEXT_PRE; if (defaultTrustedDomains.length > 0) { @@ -57,6 +58,20 @@ function computeTrustedDomainContent(defaultTrustedDomains: string[], trustedDom content += `// By default, VS Code trusts "localhost".\n`; } + if (userTrustedDomains.length) { + content += `//\n// Additionally, the following domains are trusted based on your current GitHub login:\n`; + userTrustedDomains.forEach(d => { + content += `// - "${d}"\n`; + }); + } + + if (workspaceTrustedDomains.length) { + content += `//\n// Further, the following domains are trusted based on your workspace configuration:\n`; + workspaceTrustedDomains.forEach(d => { + content += `// - "${d}"\n`; + }); + } + content += CONFIG_HELP_TEXT_AFTER; if (trustedDomains.length === 0) { @@ -77,8 +92,8 @@ export class TrustedDomainsFileSystemProvider implements IFileSystemProviderWith constructor( @IFileService private readonly fileService: IFileService, @IStorageService private readonly storageService: IStorageService, - @IProductService private readonly productService: IProductService, - @IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, ) { this.fileService.registerProvider(TRUSTED_DOMAINS_SCHEMA, this); @@ -90,24 +105,24 @@ export class TrustedDomainsFileSystemProvider implements IFileSystemProviderWith return Promise.resolve(TRUSTED_DOMAINS_STAT); } - readFile(resource: URI): Promise { + async readFile(resource: URI): Promise { let trustedDomainsContent = this.storageService.get( TRUSTED_DOMAINS_CONTENT_STORAGE_KEY, StorageScope.GLOBAL ); + const { defaultTrustedDomains, trustedDomains, userDomains, workspaceDomains } = await this.instantiationService.invokeFunction(readTrustedDomains); if ( !trustedDomainsContent || trustedDomainsContent.indexOf(CONFIG_HELP_TEXT_PRE) === -1 || - trustedDomainsContent.indexOf(CONFIG_HELP_TEXT_AFTER) === -1 + trustedDomainsContent.indexOf(CONFIG_HELP_TEXT_AFTER) === -1 || + [...defaultTrustedDomains, ...trustedDomains, ...userDomains, ...workspaceDomains].some(d => !assertIsDefined(trustedDomainsContent).includes(d)) ) { - const { defaultTrustedDomains, trustedDomains } = readTrustedDomains(this.storageService, this.productService); - - trustedDomainsContent = computeTrustedDomainContent(defaultTrustedDomains, trustedDomains); + trustedDomainsContent = computeTrustedDomainContent(defaultTrustedDomains, trustedDomains, userDomains, workspaceDomains); } const buffer = VSBuffer.fromString(trustedDomainsContent).buffer; - return Promise.resolve(buffer); + return buffer; } writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { diff --git a/src/vs/workbench/contrib/url/common/trustedDomainsValidator.ts b/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts similarity index 84% rename from src/vs/workbench/contrib/url/common/trustedDomainsValidator.ts rename to src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts index bfb96b17c42..744606326ac 100644 --- a/src/vs/workbench/contrib/url/common/trustedDomainsValidator.ts +++ b/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts @@ -16,10 +16,12 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { configureOpenerTrustedDomainsHandler, readTrustedDomains -} from 'vs/workbench/contrib/url/common/trustedDomains'; +} from 'vs/workbench/contrib/url/browser/trustedDomains'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { INotificationService } from 'vs/platform/notification/common/notification'; type TrustedDomainsDialogActionClassification = { action: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; @@ -34,7 +36,9 @@ export class OpenerValidatorContributions implements IWorkbenchContribution { @IQuickInputService private readonly _quickInputService: IQuickInputService, @IEditorService private readonly _editorService: IEditorService, @IClipboardService private readonly _clipboardService: IClipboardService, - @ITelemetryService private readonly _telemetryService: ITelemetryService + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @INotificationService private readonly _notificationService: INotificationService, ) { this._openerService.registerValidator({ shouldOpen: r => this.validateLink(r) }); } @@ -50,8 +54,8 @@ export class OpenerValidatorContributions implements IWorkbenchContribution { const { scheme, authority, path, query, fragment } = resource; const domainToOpen = `${scheme}://${authority}`; - const { defaultTrustedDomains, trustedDomains } = readTrustedDomains(this._storageService, this._productService); - const allTrustedDomains = [...defaultTrustedDomains, ...trustedDomains]; + const { defaultTrustedDomains, trustedDomains, userDomains, workspaceDomains } = await this._instantiationService.invokeFunction(readTrustedDomains); + const allTrustedDomains = [...defaultTrustedDomains, ...trustedDomains, ...userDomains, ...workspaceDomains]; if (isURLDomainTrusted(resource, allTrustedDomains)) { return true; @@ -117,10 +121,13 @@ export class OpenerValidatorContributions implements IWorkbenchContribution { const pickedDomains = await configureOpenerTrustedDomainsHandler( trustedDomains, domainToOpen, + resource, this._quickInputService, this._storageService, this._editorService, - this._telemetryService + this._telemetryService, + this._notificationService, + this._clipboardService, ); // Trust all domains if (pickedDomains.indexOf('*') !== -1) { @@ -150,15 +157,33 @@ function isLocalhostAuthority(authority: string) { return rLocalhost.test(authority) || r127.test(authority); } +/** + * Case-normalize some case-insinsitive URLs, such as github. + */ +function normalizeURL(url: string | URI): string { + const caseInsensitiveAuthorities = ['github.com']; + try { + const parsed = typeof url === 'string' ? URI.parse(url, true) : url; + if (caseInsensitiveAuthorities.includes(parsed.authority)) { + return parsed.with({ path: parsed.path.toLowerCase() }).toString(true); + } else { + return parsed.toString(true); + } + } catch { return url.toString(); } +} + /** * Check whether a domain like https://www.microsoft.com matches * the list of trusted domains. * * - Schemes must match - * - There's no subdomain matching. For example https://microsoft.com doesn't match https://www.microsoft.com + * - There's no subdomsain matching. For example https://microsoft.com doesn't match https://www.microsoft.com * - Star matches all subdomains. For example https://*.microsoft.com matches https://www.microsoft.com and https://foo.bar.microsoft.com */ export function isURLDomainTrusted(url: URI, trustedDomains: string[]) { + url = URI.parse(normalizeURL(url)); + trustedDomains = trustedDomains.map(normalizeURL); + if (isLocalhostAuthority(url.authority)) { return true; } diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/browser/url.contribution.ts similarity index 64% rename from src/vs/workbench/contrib/url/common/url.contribution.ts rename to src/vs/workbench/contrib/url/browser/url.contribution.ts index 5da6346c473..3c9bca6e6c0 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/browser/url.contribution.ts @@ -3,50 +3,46 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Action } from 'vs/base/common/actions'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; -import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { MenuId, MenuRegistry, Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { Registry } from 'vs/platform/registry/common/platform'; import { IURLService } from 'vs/platform/url/common/url'; -import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { ExternalUriResolverContribution } from 'vs/workbench/contrib/url/common/externalUriResolver'; -import { manageTrustedDomainSettingsCommand } from 'vs/workbench/contrib/url/common/trustedDomains'; -import { TrustedDomainsFileSystemProvider } from 'vs/workbench/contrib/url/common/trustedDomainsFileSystemProvider'; -import { OpenerValidatorContributions } from 'vs/workbench/contrib/url/common/trustedDomainsValidator'; +import { ExternalUriResolverContribution } from 'vs/workbench/contrib/url/browser/externalUriResolver'; +import { manageTrustedDomainSettingsCommand } from 'vs/workbench/contrib/url/browser/trustedDomains'; +import { TrustedDomainsFileSystemProvider } from 'vs/workbench/contrib/url/browser/trustedDomainsFileSystemProvider'; +import { OpenerValidatorContributions } from 'vs/workbench/contrib/url/browser/trustedDomainsValidator'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -export class OpenUrlAction extends Action { - static readonly ID = 'workbench.action.url.openUrl'; - static readonly LABEL = localize('openUrl', 'Open URL'); +class OpenUrlAction extends Action2 { - constructor( - id: string, - label: string, - @IURLService private readonly urlService: IURLService, - @IQuickInputService private readonly quickInputService: IQuickInputService - ) { - super(id, label); + constructor() { + super({ + id: 'workbench.action.url.openUrl', + title: { value: localize('openUrl', "Open URL"), original: 'Open URL' }, + category: { value: localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer"), original: 'Developer' }, + f1: true + }); } - run(): Promise { - return this.quickInputService.input({ prompt: 'URL to open' }).then(input => { + async run(accessor: ServicesAccessor): Promise { + const quickInputService = accessor.get(IQuickInputService); + const urlService = accessor.get(IURLService); + + return quickInputService.input({ prompt: localize('urlToOpen', "URL to open") }).then(input => { if (input) { const uri = URI.parse(input); - this.urlService.open(uri, { trusted: true }); + urlService.open(uri, { trusted: true }); } }); } } -Registry.as(ActionExtensions.WorkbenchActions).registerWorkbenchAction( - SyncActionDescriptor.from(OpenUrlAction), - 'Open URL', - localize('developer', 'Developer') -); +registerAction2(OpenUrlAction); /** * Trusted Domains Contribution diff --git a/src/vs/workbench/contrib/url/test/browser/trustedDomains.test.ts b/src/vs/workbench/contrib/url/test/browser/trustedDomains.test.ts index 8b69baf8039..fc84ad42ff8 100644 --- a/src/vs/workbench/contrib/url/test/browser/trustedDomains.test.ts +++ b/src/vs/workbench/contrib/url/test/browser/trustedDomains.test.ts @@ -5,8 +5,9 @@ import * as assert from 'assert'; -import { isURLDomainTrusted } from 'vs/workbench/contrib/url/common/trustedDomainsValidator'; +import { isURLDomainTrusted } from 'vs/workbench/contrib/url/browser/trustedDomainsValidator'; import { URI } from 'vs/base/common/uri'; +import { extractGitHubRemotesFromGitConfig } from 'vs/workbench/contrib/url/browser/trustedDomains'; function linkAllowedByRules(link: string, rules: string[]) { assert.ok(isURLDomainTrusted(URI.parse(link), rules), `Link\n${link}\n should be protected by rules\n${JSON.stringify(rules)}`); @@ -15,6 +16,28 @@ function linkNotAllowedByRules(link: string, rules: string[]) { assert.ok(!isURLDomainTrusted(URI.parse(link), rules), `Link\n${link}\n should NOT be protected by rules\n${JSON.stringify(rules)}`); } +suite('GitHub remote extraction', () => { + test('All known formats', () => { + assert.deepEqual( + extractGitHubRemotesFromGitConfig( + ` +[remote "1"] + url = git@github.com:sshgit/vscode.git +[remote "2"] + url = git@github.com:ssh/vscode +[remote "3"] + url = https://github.com/httpsgit/vscode.git +[remote "4"] + url = https://github.com/https/vscode`), + [ + 'https://github.com/sshgit/vscode/', + 'https://github.com/ssh/vscode/', + 'https://github.com/httpsgit/vscode/', + 'https://github.com/https/vscode/' + ]); + }); +}); + suite('Link protection domain matching', () => { test('simple', () => { linkNotAllowedByRules('https://x.org', []); @@ -78,4 +101,10 @@ suite('Link protection domain matching', () => { linkAllowedByRules('https://github.com', ['https://github.com/foo/bar', 'https://github.com']); }); + + test('case normalization', () => { + // https://github.com/microsoft/vscode/issues/99294 + linkAllowedByRules('https://github.com/Microsoft/vscode/issues/new', ['https://github.com/microsoft']); + linkAllowedByRules('https://github.com/microsoft/vscode/issues/new', ['https://github.com/Microsoft']); + }); }); diff --git a/src/vs/workbench/contrib/userDataSync/browser/media/userDataSyncViews.css b/src/vs/workbench/contrib/userDataSync/browser/media/userDataSyncViews.css new file mode 100644 index 00000000000..6213a4e9915 --- /dev/null +++ b/src/vs/workbench/contrib/userDataSync/browser/media/userDataSyncViews.css @@ -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. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .pane > .pane-body > .manual-sync-buttons-container { + display: flex; + flex-direction: column; + padding: 13px 20px 0 20px; + box-sizing: border-box; +} + +.monaco-workbench .pane > .pane-body > .manual-sync-buttons-container .monaco-button { + margin-block-start: 13px; + margin-inline-start: 0px; + margin-inline-end: 0px; + max-width: 260px; + margin-left: auto; + margin-right: auto; +} diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts index 801f7e4a6b8..9b326f949d5 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts @@ -3,33 +3,40 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IUserDataSyncService, IUserDataSyncLogService, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, IUserDataSyncLogService, IUserDataSyncResourceEnablementService, IUserDataSyncStoreService, IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync'; import { Event } from 'vs/base/common/event'; import { UserDataAutoSyncService as BaseUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication'; +import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { UserDataSyncTrigger } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; export class UserDataAutoSyncService extends BaseUserDataAutoSyncService { constructor( - @IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService, + @IUserDataSyncStoreManagementService userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, + @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, + @IUserDataSyncResourceEnablementService userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, @IUserDataSyncService userDataSyncService: IUserDataSyncService, @IUserDataSyncLogService logService: IUserDataSyncLogService, - @IAuthenticationTokenService authTokenService: IAuthenticationTokenService, + @IUserDataSyncAccountService authTokenService: IUserDataSyncAccountService, @IInstantiationService instantiationService: IInstantiationService, @IHostService hostService: IHostService, @ITelemetryService telemetryService: ITelemetryService, + @IUserDataSyncMachinesService userDataSyncMachinesService: IUserDataSyncMachinesService, + @IStorageService storageService: IStorageService, + @IEnvironmentService environmentService: IEnvironmentService, ) { - super(userDataSyncEnablementService, userDataSyncService, logService, authTokenService, telemetryService); + super(userDataSyncStoreManagementService, userDataSyncStoreService, userDataSyncResourceEnablementService, userDataSyncService, logService, authTokenService, telemetryService, userDataSyncMachinesService, storageService, environmentService); this._register(Event.debounce(Event.any( Event.map(hostService.onDidChangeFocus, () => 'windowFocus'), instantiationService.createInstance(UserDataSyncTrigger).onDidTriggerSync, - userDataSyncService.onDidChangeLocal, - ), (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerAutoSync(sources))); + ), (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, true))); } } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts index d215668d3f0..e786b7f98bc 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts @@ -3,12 +3,44 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { UserDataSyncWorkbenchContribution } from 'vs/workbench/contrib/userDataSync/browser/userDataSync'; -import { UserDataSyncViewContribution } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncView'; +import { IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode } from 'vs/platform/userDataSync/common/userDataSync'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { localize } from 'vs/nls'; +import { isWeb } from 'vs/base/common/platform'; + +class UserDataSyncReportIssueContribution extends Disposable implements IWorkbenchContribution { + + constructor( + @IUserDataAutoSyncService userDataAutoSyncService: IUserDataAutoSyncService, + @INotificationService private readonly notificationService: INotificationService, + ) { + super(); + this._register(userDataAutoSyncService.onError(error => this.onAutoSyncError(error))); + } + + private onAutoSyncError(error: UserDataSyncError): void { + switch (error.code) { + case UserDataSyncErrorCode.LocalTooManyRequests: + case UserDataSyncErrorCode.TooManyRequests: + const operationId = error.operationId ? localize('operationId', "Operation Id: {0}", error.operationId) : undefined; + const message = localize('too many requests', "Turned off syncing settings on this device because it is making too many requests."); + this.notificationService.notify({ + severity: Severity.Error, + message: operationId ? `${message} ${operationId}` : message, + }); + return; + } + } +} const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(UserDataSyncWorkbenchContribution, LifecyclePhase.Ready); -workbenchRegistry.registerWorkbenchContribution(UserDataSyncViewContribution, LifecyclePhase.Ready); + +if (isWeb) { + workbenchRegistry.registerWorkbenchContribution(UserDataSyncReportIssueContribution, LifecyclePhase.Ready); +} diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index ba8bcf7c7e7..6c3e57e55d5 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -5,7 +5,7 @@ import { Action } from 'vs/base/common/actions'; import { toErrorMessage } from 'vs/base/common/errorMessage'; -import { canceled, isPromiseCanceledError } from 'vs/base/common/errors'; +import { isPromiseCanceledError } from 'vs/base/common/errors'; import { Event } from 'vs/base/common/event'; import { Disposable, DisposableStore, dispose, MutableDisposable, toDisposable, IDisposable } from 'vs/base/common/lifecycle'; import { isEqual, basename } from 'vs/base/common/resources'; @@ -28,9 +28,9 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/ import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { - CONTEXT_SYNC_STATE, IUserDataAutoSyncService, IUserDataSyncService, registerConfiguration, - SyncResource, SyncStatus, UserDataSyncError, UserDataSyncErrorCode, USER_DATA_SYNC_SCHEME, IUserDataSyncEnablementService, CONTEXT_SYNC_ENABLEMENT, - SyncResourceConflicts, Conflict, getSyncResourceFromLocalPreview + IUserDataAutoSyncService, IUserDataSyncService, registerConfiguration, + SyncResource, SyncStatus, UserDataSyncError, UserDataSyncErrorCode, USER_DATA_SYNC_SCHEME, IUserDataSyncResourceEnablementService, + getSyncResourceFromLocalPreview, IResourcePreview, IUserDataSyncStoreManagementService, UserDataSyncStoreType } from 'vs/platform/userDataSync/common/userDataSync'; import { FloatingClickWidget } from 'vs/workbench/browser/parts/editor/editorWidgets'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -42,76 +42,65 @@ import { IActivityService, IBadge, NumberBadge, ProgressBadge } from 'vs/workben import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; -import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication'; +import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; import { fromNow } from 'vs/base/common/date'; import { IProductService } from 'vs/platform/product/common/productService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; -import { UserDataSyncAccounts, AccountStatus } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncAccount'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { Codicon } from 'vs/base/common/codicons'; +import { ViewContainerLocation, IViewContainersRegistry, Extensions, ViewContainer } from 'vs/workbench/common/views'; +import { UserDataSyncViewPaneContainer, UserDataSyncDataViews } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncViews'; +import { IUserDataSyncWorkbenchService, getSyncAreaLabel, AccountStatus, CONTEXT_SYNC_STATE, CONTEXT_SYNC_ENABLEMENT, CONTEXT_ACCOUNT_STATE, CONFIGURE_SYNC_COMMAND_ID, SHOW_SYNC_LOG_COMMAND_ID, SYNC_VIEW_CONTAINER_ID, SYNC_TITLE } from 'vs/workbench/services/userDataSync/common/userDataSync'; +import { isNative } from 'vs/base/common/platform'; const CONTEXT_CONFLICTS_SOURCES = new RawContextKey('conflictsSources', ''); type ConfigureSyncQuickPickItem = { id: SyncResource, label: string, description?: string }; -function getSyncAreaLabel(source: SyncResource): string { - switch (source) { - case SyncResource.Settings: return localize('settings', "Settings"); - case SyncResource.Keybindings: return localize('keybindings', "Keyboard Shortcuts"); - case SyncResource.Snippets: return localize('snippets', "User Snippets"); - case SyncResource.Extensions: return localize('extensions', "Extensions"); - case SyncResource.GlobalState: return localize('ui state label', "UI State"); - } -} - type SyncConflictsClassification = { source: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; action?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; }; -type FirstTimeSyncClassification = { - action: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; -}; - -const turnOnSyncCommand = { id: 'workbench.userData.actions.syncStart', title: localize('turn on sync with category', "Preferences Sync: Turn On...") }; -const stopSyncCommand = { id: 'workbench.userData.actions.stopSync', title: localize('stop sync', "Preferences Sync: Turn Off") }; -const resolveSettingsConflictsCommand = { id: 'workbench.userData.actions.resolveSettingsConflicts', title: localize('showConflicts', "Preferences Sync: Show Settings Conflicts") }; -const resolveKeybindingsConflictsCommand = { id: 'workbench.userData.actions.resolveKeybindingsConflicts', title: localize('showKeybindingsConflicts', "Preferences Sync: Show Keybindings Conflicts") }; -const resolveSnippetsConflictsCommand = { id: 'workbench.userData.actions.resolveSnippetsConflicts', title: localize('showSnippetsConflicts', "Preferences Sync: Show User Snippets Conflicts") }; -const configureSyncCommand = { id: 'workbench.userData.actions.configureSync', title: localize('configure sync', "Preferences Sync: Configure...") }; -const showSyncActivityCommand = { id: 'workbench.userData.actions.showSyncActivity', title: localize('show sync log title', "Preferences Sync: Show Log") }; +const turnOnSyncCommand = { id: 'workbench.userDataSync.actions.turnOn', title: localize('turn on sync with category', "{0}: Turn On...", SYNC_TITLE) }; +const turnOffSyncCommand = { id: 'workbench.userDataSync.actions.turnOff', title: localize('stop sync', "{0}: Turn Off", SYNC_TITLE) }; +const configureSyncCommand = { id: CONFIGURE_SYNC_COMMAND_ID, title: localize('configure sync', "{0}: Configure...", SYNC_TITLE) }; +const resolveSettingsConflictsCommand = { id: 'workbench.userDataSync.actions.resolveSettingsConflicts', title: localize('showConflicts', "{0}: Show Settings Conflicts", SYNC_TITLE) }; +const resolveKeybindingsConflictsCommand = { id: 'workbench.userDataSync.actions.resolveKeybindingsConflicts', title: localize('showKeybindingsConflicts', "{0}: Show Keybindings Conflicts", SYNC_TITLE) }; +const resolveSnippetsConflictsCommand = { id: 'workbench.userDataSync.actions.resolveSnippetsConflicts', title: localize('showSnippetsConflicts', "{0}: Show User Snippets Conflicts", SYNC_TITLE) }; const syncNowCommand = { - id: 'workbench.userData.actions.syncNow', - title: localize('sync now', "Preferences Sync: Sync Now"), + id: 'workbench.userDataSync.actions.syncNow', + title: localize('sync now', "{0}: Sync Now", SYNC_TITLE), description(userDataSyncService: IUserDataSyncService): string | undefined { if (userDataSyncService.status === SyncStatus.Syncing) { - return localize('sync is on with syncing', "syncing"); + return localize('syncing', "syncing"); } if (userDataSyncService.lastSyncTime) { - return localize('sync is on with time', "synced {0}", fromNow(userDataSyncService.lastSyncTime, true)); + return localize('synced with time', "synced {0}", fromNow(userDataSyncService.lastSyncTime, true)); } return undefined; } }; -const showSyncSettingsCommand = { id: 'workbench.userData.actions.syncSettings', title: localize('sync settings', "Preferences Sync: Show Settings"), }; +const showSyncSettingsCommand = { id: 'workbench.userDataSync.actions.settings', title: localize('sync settings', "{0}: Show Settings", SYNC_TITLE), }; +const showSyncedDataCommand = { id: 'workbench.userDataSync.actions.showSyncedData', title: localize('show synced data', "{0}: Show Synced Data", SYNC_TITLE), }; const CONTEXT_TURNING_ON_STATE = new RawContextKey('userDataSyncTurningOn', false); -export const CONTEXT_ACCOUNT_STATE = new RawContextKey('userDataSyncAccountStatus', AccountStatus.Uninitialized); export class UserDataSyncWorkbenchContribution extends Disposable implements IWorkbenchContribution { private readonly turningOnSyncContext: IContextKey; - private readonly syncEnablementContext: IContextKey; - private readonly syncStatusContext: IContextKey; - private readonly accountStatusContext: IContextKey; private readonly conflictsSources: IContextKey; - private readonly userDataSyncAccounts: UserDataSyncAccounts; - private readonly badgeDisposable = this._register(new MutableDisposable()); + private readonly globalActivityBadgeDisposable = this._register(new MutableDisposable()); + private readonly accountBadgeDisposable = this._register(new MutableDisposable()); constructor( - @IUserDataSyncEnablementService private readonly userDataSyncEnablementService: IUserDataSyncEnablementService, + @IUserDataSyncResourceEnablementService private readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, + @IUserDataSyncWorkbenchService private readonly userDataSyncWorkbenchService: IUserDataSyncWorkbenchService, @IContextKeyService contextKeyService: IContextKeyService, @IActivityService private readonly activityService: IActivityService, @INotificationService private readonly notificationService: INotificationService, @@ -119,65 +108,71 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo @IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService, @IDialogService private readonly dialogService: IDialogService, @IQuickInputService private readonly quickInputService: IQuickInputService, - @IInstantiationService instantiationService: IInstantiationService, + @IInstantiationService private readonly instantiationService: IInstantiationService, @IOutputService private readonly outputService: IOutputService, - @IAuthenticationTokenService readonly authTokenService: IAuthenticationTokenService, - @IUserDataAutoSyncService userDataAutoSyncService: IUserDataAutoSyncService, - @ITextModelService private readonly textModelResolverService: ITextModelService, + @IUserDataSyncAccountService readonly authTokenService: IUserDataSyncAccountService, + @IUserDataAutoSyncService private readonly userDataAutoSyncService: IUserDataAutoSyncService, + @ITextModelService textModelResolverService: ITextModelService, @IPreferencesService private readonly preferencesService: IPreferencesService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IProductService private readonly productService: IProductService, @IStorageService private readonly storageService: IStorageService, @IOpenerService private readonly openerService: IOpenerService, @IAuthenticationService private readonly authenticationService: IAuthenticationService, + @IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, ) { super(); this.turningOnSyncContext = CONTEXT_TURNING_ON_STATE.bindTo(contextKeyService); - this.syncEnablementContext = CONTEXT_SYNC_ENABLEMENT.bindTo(contextKeyService); - this.syncStatusContext = CONTEXT_SYNC_STATE.bindTo(contextKeyService); - this.accountStatusContext = CONTEXT_ACCOUNT_STATE.bindTo(contextKeyService); this.conflictsSources = CONTEXT_CONFLICTS_SOURCES.bindTo(contextKeyService); - this.userDataSyncAccounts = instantiationService.createInstance(UserDataSyncAccounts); - - if (this.userDataSyncAccounts.authenticationProviders.length) { + if (userDataSyncWorkbenchService.enabled) { registerConfiguration(); - this.onDidChangeSyncStatus(this.userDataSyncService.status); + this.updateAccountBadge(); + this.updateGlobalActivityBadge(); this.onDidChangeConflicts(this.userDataSyncService.conflicts); - this.onDidChangeEnablement(this.userDataSyncEnablementService.isEnabled()); - this.onDidChangeAccountStatus(this.userDataSyncAccounts.status); - this._register(Event.debounce(userDataSyncService.onDidChangeStatus, () => undefined, 500)(() => this.onDidChangeSyncStatus(this.userDataSyncService.status))); + this._register(Event.any( + Event.debounce(userDataSyncService.onDidChangeStatus, () => undefined, 500), + this.userDataAutoSyncService.onDidChangeEnablement, + this.userDataSyncWorkbenchService.onDidChangeAccountStatus + )(() => { + this.updateAccountBadge(); + this.updateGlobalActivityBadge(); + })); this._register(userDataSyncService.onDidChangeConflicts(() => this.onDidChangeConflicts(this.userDataSyncService.conflicts))); - this._register(userDataSyncService.onSyncErrors(errors => this.onSyncErrors(errors))); - this._register(this.userDataSyncEnablementService.onDidChangeEnablement(enabled => this.onDidChangeEnablement(enabled))); + this._register(userDataAutoSyncService.onDidChangeEnablement(() => this.onDidChangeConflicts(this.userDataSyncService.conflicts))); + this._register(userDataSyncService.onSyncErrors(errors => this.onSynchronizerErrors(errors))); this._register(userDataAutoSyncService.onError(error => this.onAutoSyncError(error))); - this._register(this.userDataSyncAccounts.onDidChangeStatus(status => this.onDidChangeAccountStatus(status))); - this._register(this.userDataSyncAccounts.onDidSignOut(() => this.doTurnOff(false))); + this.registerActions(); + this.registerViews(); textModelResolverService.registerTextModelContentProvider(USER_DATA_SYNC_SCHEME, instantiationService.createInstance(UserDataRemoteContentProvider)); registerEditorContribution(AcceptChangesContribution.ID, AcceptChangesContribution); + + this._register(Event.any(userDataSyncService.onDidChangeStatus, userDataAutoSyncService.onDidChangeEnablement)(() => this.turningOnSync = !userDataAutoSyncService.isEnabled() && userDataSyncService.status !== SyncStatus.Idle)); } } - private onDidChangeAccountStatus(status: AccountStatus): void { - this.accountStatusContext.set(status); - this.updateBadge(); + private get turningOnSync(): boolean { + return !!this.turningOnSyncContext.get(); } - private onDidChangeSyncStatus(status: SyncStatus) { - this.syncStatusContext.set(status); - this.updateBadge(); + private set turningOnSync(turningOn: boolean) { + this.turningOnSyncContext.set(turningOn); + this.updateGlobalActivityBadge(); } private readonly conflictsDisposables = new Map(); - private onDidChangeConflicts(conflicts: SyncResourceConflicts[]) { - this.updateBadge(); + private onDidChangeConflicts(conflicts: [SyncResource, IResourcePreview[]][]) { + if (!this.userDataAutoSyncService.isEnabled()) { + return; + } + this.updateGlobalActivityBadge(); if (conflicts.length) { - const conflictsSources: SyncResource[] = conflicts.map(conflict => conflict.syncResource); + const conflictsSources: SyncResource[] = conflicts.map(([syncResource]) => syncResource); this.conflictsSources.set(conflictsSources.join(',')); if (conflictsSources.indexOf(SyncResource.Snippets) !== -1) { this.registerShowSnippetsConflictsAction(); @@ -191,13 +186,13 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } }); - for (const { syncResource, conflicts } of this.userDataSyncService.conflicts) { + for (const [syncResource, conflicts] of this.userDataSyncService.conflicts) { const conflictsEditorInputs = this.getConflictsEditorInputs(syncResource); // close stale conflicts editor previews if (conflictsEditorInputs.length) { conflictsEditorInputs.forEach(input => { - if (!conflicts.some(({ local }) => isEqual(local, input.master.resource))) { + if (!conflicts.some(({ previewResource }) => isEqual(previewResource, input.primary.resource))) { input.dispose(); } }); @@ -216,17 +211,17 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } }, { - label: localize('accept local', "Accept Local"), + label: localize('accept merges', "Accept Merges"), run: () => { this.telemetryService.publicLog2<{ source: string, action: string }, SyncConflictsClassification>('sync/handleConflicts', { source: syncResource, action: 'acceptLocal' }); this.acceptLocal(syncResource, conflicts); } }, { - label: localize('show conflicts', "Show Conflicts"), + label: localize('show merges', "Show Merges"), run: () => { this.telemetryService.publicLog2<{ source: string, action?: string }, SyncConflictsClassification>('sync/showConflicts', { source: syncResource }); - this.handleConflicts({ syncResource, conflicts }); + this.handleConflicts([syncResource, conflicts]); } } ], @@ -257,73 +252,100 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } } - private async acceptRemote(syncResource: SyncResource, conflicts: Conflict[]) { + private async acceptRemote(syncResource: SyncResource, conflicts: IResourcePreview[]) { try { for (const conflict of conflicts) { - const modelRef = await this.textModelResolverService.createModelReference(conflict.remote); - await this.userDataSyncService.acceptConflict(conflict.remote, modelRef.object.textEditorModel.getValue()); - modelRef.dispose(); + await this.userDataSyncService.accept(syncResource, conflict.remoteResource, undefined, this.userDataAutoSyncService.isEnabled()); } } catch (e) { this.notificationService.error(e); } } - private async acceptLocal(syncResource: SyncResource, conflicts: Conflict[]): Promise { + private async acceptLocal(syncResource: SyncResource, conflicts: IResourcePreview[]): Promise { try { for (const conflict of conflicts) { - const modelRef = await this.textModelResolverService.createModelReference(conflict.local); - await this.userDataSyncService.acceptConflict(conflict.local, modelRef.object.textEditorModel.getValue()); - modelRef.dispose(); + await this.userDataSyncService.accept(syncResource, conflict.previewResource, undefined, this.userDataAutoSyncService.isEnabled()); } } catch (e) { this.notificationService.error(e); } } - private onDidChangeEnablement(enabled: boolean) { - this.syncEnablementContext.set(enabled); - this.updateBadge(); - } - private onAutoSyncError(error: UserDataSyncError): void { switch (error.code) { - case UserDataSyncErrorCode.TurnedOff: case UserDataSyncErrorCode.SessionExpired: this.notificationService.notify({ severity: Severity.Info, - message: localize('turned off', "Preferences sync was turned off from another device."), + message: localize('session expired', "Settings sync was turned off because current session is expired, please sign in again to turn on sync."), actions: { - primary: [new Action('turn on sync', localize('turn on sync', "Turn on Preferences Sync..."), undefined, true, () => this.turnOn())] + primary: [new Action('turn on sync', localize('turn on sync', "Turn on Settings Sync..."), undefined, true, () => this.turnOn())] } }); - return; + break; + case UserDataSyncErrorCode.TurnedOff: + this.notificationService.notify({ + severity: Severity.Info, + message: localize('turned off', "Settings sync was turned off from another device, please sign in again to turn on sync."), + actions: { + primary: [new Action('turn on sync', localize('turn on sync', "Turn on Settings Sync..."), undefined, true, () => this.turnOn())] + } + }); + break; case UserDataSyncErrorCode.TooLarge: if (error.resource === SyncResource.Keybindings || error.resource === SyncResource.Settings) { this.disableSync(error.resource); const sourceArea = getSyncAreaLabel(error.resource); - this.notificationService.notify({ - severity: Severity.Error, - message: localize('too large', "Disabled syncing {0} because size of the {1} file to sync is larger than {2}. Please open the file and reduce the size and enable sync", sourceArea.toLowerCase(), sourceArea.toLowerCase(), '100kb'), - actions: { - primary: [new Action('open sync file', localize('open file', "Open {0} File", sourceArea), undefined, true, - () => error.resource === SyncResource.Settings ? this.preferencesService.openGlobalSettings(true) : this.preferencesService.openGlobalKeybindingSettings(true))] - } - }); + this.handleTooLargeError(error.resource, localize('too large', "Disabled syncing {0} because size of the {1} file to sync is larger than {2}. Please open the file and reduce the size and enable sync", sourceArea.toLowerCase(), sourceArea.toLowerCase(), '100kb'), error); } - return; - case UserDataSyncErrorCode.Incompatible: - this.disableSync(); + break; + case UserDataSyncErrorCode.IncompatibleLocalContent: + case UserDataSyncErrorCode.Gone: + case UserDataSyncErrorCode.UpgradeRequired: + const message = localize('error upgrade required', "Settings sync is disabled because the current version ({0}, {1}) is not compatible with the sync service. Please update before turning on sync.", this.productService.version, this.productService.commit); + const operationId = error.operationId ? localize('operationId', "Operation Id: {0}", error.operationId) : undefined; this.notificationService.notify({ severity: Severity.Error, - message: localize('error incompatible', "Turned off sync because local data is incompatible with the data in the cloud. Please update {0} and turn on sync to continue syncing.", this.productService.nameLong), + message: operationId ? `${message} ${operationId}` : message, }); + break; + case UserDataSyncErrorCode.IncompatibleRemoteContent: + this.notificationService.notify({ + severity: Severity.Error, + message: localize('error reset required', "Settings sync is disabled because your data in the cloud is older than that of the client. Please clear your data in the cloud before turning on sync."), + actions: { + primary: [ + new Action('reset', localize('reset', "Clear Data in Cloud..."), undefined, true, () => this.userDataSyncWorkbenchService.resetSyncedData()), + new Action('show synced data', localize('show synced data action', "Show Synced Data"), undefined, true, () => this.userDataSyncWorkbenchService.showSyncActivity()) + ] + } + }); + return; + case UserDataSyncErrorCode.DefaultServiceChanged: + if (isEqual(this.userDataSyncStoreManagementService.userDataSyncStore?.url, this.userDataSyncStoreManagementService.userDataSyncStore?.insidersUrl)) { + this.notificationService.notify({ + severity: Severity.Info, + message: localize('switched to insiders', "Settings sync now uses a separate service, more information is available in the [release notes](command:update.showCurrentReleaseNotes)."), + }); + } return; } } + private handleTooLargeError(resource: SyncResource, message: string, error: UserDataSyncError): void { + const operationId = error.operationId ? localize('operationId', "Operation Id: {0}", error.operationId) : undefined; + this.notificationService.notify({ + severity: Severity.Error, + message: operationId ? `${message} ${operationId}` : message, + actions: { + primary: [new Action('open sync file', localize('open file', "Open {0} File", getSyncAreaLabel(resource)), undefined, true, + () => resource === SyncResource.Settings ? this.preferencesService.openGlobalSettings(true) : this.preferencesService.openGlobalKeybindingSettings(true))] + } + }); + } + private readonly invalidContentErrorDisposables = new Map(); - private onSyncErrors(errors: [SyncResource, UserDataSyncError][]): void { + private onSynchronizerErrors(errors: [SyncResource, UserDataSyncError][]): void { if (errors.length) { for (const [source, error] of errors) { switch (error.code) { @@ -352,14 +374,14 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo return; } const resource = source === SyncResource.Settings ? this.workbenchEnvironmentService.settingsResource : this.workbenchEnvironmentService.keybindingsResource; - if (isEqual(resource, toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }))) { + if (isEqual(resource, toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }))) { // Do not show notification if the file in error is active return; } const errorArea = getSyncAreaLabel(source); const handle = this.notificationService.notify({ severity: Severity.Error, - message: localize('errorInvalidConfiguration', "Unable to sync {0} because there are some errors/warnings in the file. Please open the file to correct errors/warnings in it.", errorArea.toLowerCase()), + message: localize('errorInvalidConfiguration', "Unable to sync {0} because the content in the file is not valid. Please open the file and correct it.", errorArea.toLowerCase()), actions: { primary: [new Action('open sync file', localize('open file', "Open {0} File", errorArea), undefined, true, () => source === SyncResource.Settings ? this.preferencesService.openGlobalSettings(true) : this.preferencesService.openGlobalKeybindingSettings(true))] @@ -372,39 +394,41 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo })); } - private async updateBadge(): Promise { - this.badgeDisposable.clear(); + private async updateGlobalActivityBadge(): Promise { + this.globalActivityBadgeDisposable.clear(); let badge: IBadge | undefined = undefined; let clazz: string | undefined; let priority: number | undefined = undefined; - if (this.userDataSyncService.status !== SyncStatus.Uninitialized && this.userDataSyncEnablementService.isEnabled() && this.userDataSyncAccounts.status === AccountStatus.Unavailable) { - badge = new NumberBadge(1, () => localize('sign in to sync preferences', "Sign in to Sync Preferences")); - } else if (this.userDataSyncService.conflicts.length) { - badge = new NumberBadge(this.userDataSyncService.conflicts.reduce((result, syncResourceConflict) => { return result + syncResourceConflict.conflicts.length; }, 0), () => localize('has conflicts', "Preferences Sync: Conflicts Detected")); + if (this.userDataSyncService.conflicts.length && this.userDataAutoSyncService.isEnabled()) { + badge = new NumberBadge(this.userDataSyncService.conflicts.reduce((result, [, conflicts]) => { return result + conflicts.length; }, 0), () => localize('has conflicts', "{0}: Conflicts Detected", SYNC_TITLE)); } else if (this.turningOnSync) { - badge = new ProgressBadge(() => localize('turning on syncing', "Turning on Preferences Sync...")); + badge = new ProgressBadge(() => localize('turning on syncing', "Turning on Settings Sync...")); clazz = 'progress-badge'; priority = 1; } if (badge) { - this.badgeDisposable.value = this.activityService.showGlobalActivity({ badge, clazz, priority }); + this.globalActivityBadgeDisposable.value = this.activityService.showGlobalActivity({ badge, clazz, priority }); } } - private get turningOnSync(): boolean { - return !!this.turningOnSyncContext.get(); - } + private async updateAccountBadge(): Promise { + this.accountBadgeDisposable.clear(); - private set turningOnSync(turningOn: boolean) { - this.turningOnSyncContext.set(turningOn); - this.updateBadge(); + let badge: IBadge | undefined = undefined; + + if (this.userDataSyncService.status !== SyncStatus.Uninitialized && this.userDataAutoSyncService.isEnabled() && this.userDataSyncWorkbenchService.accountStatus === AccountStatus.Unavailable) { + badge = new NumberBadge(1, () => localize('sign in to sync', "Sign in to Sync Settings")); + } + + if (badge) { + this.accountBadgeDisposable.value = this.activityService.showAccountsActivity({ badge, clazz: undefined, priority: undefined }); + } } private async turnOn(): Promise { - this.turningOnSync = true; try { if (!this.storageService.getBoolean('sync.donotAskPreviewConfirmation', StorageScope.GLOBAL, false)) { if (!await this.askForConfirmation()) { @@ -415,20 +439,55 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo if (!turnOn) { return; } - await this.doTurnOn(); + await this.userDataSyncWorkbenchService.turnOn(); this.storageService.store('sync.donotAskPreviewConfirmation', true, StorageScope.GLOBAL); - } finally { - this.turningOnSync = false; + } catch (e) { + if (isPromiseCanceledError(e)) { + return; + } + if (e instanceof UserDataSyncError) { + switch (e.code) { + case UserDataSyncErrorCode.TooLarge: + if (e.resource === SyncResource.Keybindings || e.resource === SyncResource.Settings) { + this.handleTooLargeError(e.resource, localize('too large while starting sync', "Settings sync cannot be turned on because size of the {0} file to sync is larger than {1}. Please open the file and reduce the size and turn on sync", getSyncAreaLabel(e.resource).toLowerCase(), '100kb'), e); + return; + } + break; + case UserDataSyncErrorCode.IncompatibleLocalContent: + case UserDataSyncErrorCode.Gone: + case UserDataSyncErrorCode.UpgradeRequired: + const message = localize('error upgrade required while starting sync', "Settings sync cannot be turned on because the current version ({0}, {1}) is not compatible with the sync service. Please update before turning on sync.", this.productService.version, this.productService.commit); + const operationId = e.operationId ? localize('operationId', "Operation Id: {0}", e.operationId) : undefined; + this.notificationService.notify({ + severity: Severity.Error, + message: operationId ? `${message} ${operationId}` : message, + }); + return; + case UserDataSyncErrorCode.IncompatibleRemoteContent: + this.notificationService.notify({ + severity: Severity.Error, + message: localize('error reset required while starting sync', "Settings sync cannot be turned on because your data in the cloud is older than that of the client. Please clear your data in the cloud before turning on sync."), + actions: { + primary: [ + new Action('reset', localize('reset', "Clear Data in Cloud..."), undefined, true, () => this.userDataSyncWorkbenchService.resetSyncedData()), + new Action('show synced data', localize('show synced data action', "Show Synced Data"), undefined, true, () => this.userDataSyncWorkbenchService.showSyncActivity()) + ] + } + }); + return; + } + } + this.notificationService.error(localize('turn on failed', "Error while starting Sync: {0}", toErrorMessage(e))); } } private async askForConfirmation(): Promise { const result = await this.dialogService.show( Severity.Info, - localize('sync preview message', "Synchronizing your preferences is a preview feature, please read the documentation before turning it on."), + localize('sync preview message', "Synchronizing your settings is a preview feature, please read the documentation before turning it on."), [ - localize('open doc', "Open Documentation"), localize('turn on', "Turn On"), + localize('open doc', "Open Documentation"), localize('cancel', "Cancel"), ], { @@ -436,7 +495,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } ); switch (result.choice) { - case 0: this.openerService.open(URI.parse('https://aka.ms/vscode-settings-sync-help')); return false; + case 1: this.openerService.open(URI.parse('https://aka.ms/vscode-settings-sync-help')); return false; case 2: return false; } return true; @@ -447,16 +506,16 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo const disposables: DisposableStore = new DisposableStore(); const quickPick = this.quickInputService.createQuickPick(); disposables.add(quickPick); - quickPick.title = localize('Preferences Sync Title', "Preferences Sync"); + quickPick.title = SYNC_TITLE; quickPick.ok = false; quickPick.customButton = true; - if (this.userDataSyncAccounts.all.length) { + if (this.userDataSyncWorkbenchService.all.length) { quickPick.customLabel = localize('turn on', "Turn On"); } else { const orTerm = localize({ key: 'or', comment: ['Here is the context where it is used - Sign in with your A or B or C account to synchronize your data across devices.'] }, "or"); - const displayName = this.userDataSyncAccounts.authenticationProviders.length === 1 - ? this.authenticationService.getDisplayName(this.userDataSyncAccounts.authenticationProviders[0].id) - : this.userDataSyncAccounts.authenticationProviders.map(({ id }) => this.authenticationService.getDisplayName(id)).join(` ${orTerm} `); + const displayName = this.userDataSyncWorkbenchService.authenticationProviders.length === 1 + ? this.authenticationService.getLabel(this.userDataSyncWorkbenchService.authenticationProviders[0].id) + : this.userDataSyncWorkbenchService.authenticationProviders.map(({ id }) => this.authenticationService.getLabel(id)).join(` ${orTerm} `); quickPick.description = localize('sign in and turn on sync detail', "Sign in with your {0} account to synchronize your data across devices.", displayName); quickPick.customLabel = localize('sign in and turn on sync', "Sign in & Turn on"); } @@ -465,7 +524,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo quickPick.ignoreFocusOut = true; const items = this.getConfigureSyncQuickPickItems(); quickPick.items = items; - quickPick.selectedItems = items.filter(item => this.userDataSyncEnablementService.isResourceEnabled(item.id)); + quickPick.selectedItems = items.filter(item => this.userDataSyncResourceEnablementService.isResourceEnabled(item.id)); let accepted: boolean = false; disposables.add(Event.any(quickPick.onDidAccept, quickPick.onDidCustom)(() => { accepted = true; @@ -487,22 +546,6 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo }); } - private async doTurnOn(): Promise { - const picked = await this.userDataSyncAccounts.pick(); - if (!picked) { - throw canceled(); - } - - // User did not pick an account or login failed - if (this.userDataSyncAccounts.status !== AccountStatus.Available) { - throw new Error(localize('no account', "No account available")); - } - - await this.handleFirstTimeSync(); - this.userDataSyncEnablementService.setEnablement(true); - this.notificationService.info(localize('sync turned on', "Preferences sync is turned on")); - } - private getConfigureSyncQuickPickItems(): ConfigureSyncQuickPickItem[] { return [{ id: SyncResource.Settings, @@ -524,10 +567,10 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private updateConfiguration(items: ConfigureSyncQuickPickItem[], selectedItems: ReadonlyArray): void { for (const item of items) { - const wasEnabled = this.userDataSyncEnablementService.isResourceEnabled(item.id); + const wasEnabled = this.userDataSyncResourceEnablementService.isResourceEnabled(item.id); const isEnabled = !!selectedItems.filter(selected => selected.id === item.id)[0]; if (wasEnabled !== isEnabled) { - this.userDataSyncEnablementService.setResourceEnablement(item.id!, isEnabled); + this.userDataSyncResourceEnablementService.setResourceEnablement(item.id!, isEnabled); } } } @@ -537,14 +580,14 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo const disposables: DisposableStore = new DisposableStore(); const quickPick = this.quickInputService.createQuickPick(); disposables.add(quickPick); - quickPick.title = localize('configure sync', "Preferences Sync: Configure..."); + quickPick.title = localize('configure sync', "{0}: Configure...", SYNC_TITLE); quickPick.placeholder = localize('configure sync placeholder', "Choose what to sync"); quickPick.canSelectMany = true; quickPick.ignoreFocusOut = true; quickPick.ok = true; const items = this.getConfigureSyncQuickPickItems(); quickPick.items = items; - quickPick.selectedItems = items.filter(item => this.userDataSyncEnablementService.isResourceEnabled(item.id)); + quickPick.selectedItems = items.filter(item => this.userDataSyncResourceEnablementService.isResourceEnabled(item.id)); disposables.add(quickPick.onDidAccept(async () => { if (quickPick.selectedItems.length) { this.updateConfiguration(items, quickPick.selectedItems); @@ -559,118 +602,60 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo }); } - private async handleFirstTimeSync(): Promise { - const isFirstSyncWithMerge = await this.userDataSyncService.isFirstTimeSyncWithMerge(); - if (!isFirstSyncWithMerge) { - return; - } - const result = await this.dialogService.show( - Severity.Info, - localize('firs time sync', "Sync"), - [ - localize('merge', "Merge"), - localize('cancel', "Cancel"), - localize('replace', "Replace Local"), - ], - { - cancelId: 1, - detail: localize('first time sync detail', "It looks like this is the first time sync is set up.\nWould you like to merge or replace with the data from the cloud?"), - } - ); - switch (result.choice) { - case 0: - this.telemetryService.publicLog2<{ action: string }, FirstTimeSyncClassification>('sync/firstTimeSync', { action: 'merge' }); - break; - case 1: - this.telemetryService.publicLog2<{ action: string }, FirstTimeSyncClassification>('sync/firstTimeSync', { action: 'cancelled' }); - throw canceled(); - case 2: - this.telemetryService.publicLog2<{ action: string }, FirstTimeSyncClassification>('sync/firstTimeSync', { action: 'replace-local' }); - await this.userDataSyncService.pull(); - break; - } - } - private async turnOff(): Promise { const result = await this.dialogService.confirm({ type: 'info', message: localize('turn off sync confirmation', "Do you want to turn off sync?"), - detail: localize('turn off sync detail', "Your settings, keybindings, extensions and UI State will no longer be synced."), + detail: localize('turn off sync detail', "Your settings, keybindings, extensions, snippets and UI State will no longer be synced."), primaryButton: localize('turn off', "Turn Off"), - checkbox: { + checkbox: this.userDataSyncWorkbenchService.accountStatus === AccountStatus.Available ? { label: localize('turn off sync everywhere', "Turn off sync on all your devices and clear the data from the cloud.") - } + } : undefined }); if (result.confirmed) { - return this.doTurnOff(!!result.checkboxChecked); + return this.userDataSyncWorkbenchService.turnoff(!!result.checkboxChecked); } } - private async doTurnOff(turnOffEveryWhere: boolean): Promise { - if (!this.userDataSyncEnablementService.isEnabled()) { - return; - } - if (!this.userDataSyncEnablementService.canToggleEnablement()) { - return; - } - if (turnOffEveryWhere) { - this.telemetryService.publicLog2('sync/turnOffEveryWhere'); - await this.userDataSyncService.reset(); - } else { - await this.userDataSyncService.resetLocal(); - } - this.disableSync(); - } - - private disableSync(source?: SyncResource): void { - if (source === undefined) { - this.userDataSyncEnablementService.setEnablement(false); - } else { - switch (source) { - case SyncResource.Settings: return this.userDataSyncEnablementService.setResourceEnablement(SyncResource.Settings, false); - case SyncResource.Keybindings: return this.userDataSyncEnablementService.setResourceEnablement(SyncResource.Keybindings, false); - case SyncResource.Snippets: return this.userDataSyncEnablementService.setResourceEnablement(SyncResource.Snippets, false); - case SyncResource.Extensions: return this.userDataSyncEnablementService.setResourceEnablement(SyncResource.Extensions, false); - case SyncResource.GlobalState: return this.userDataSyncEnablementService.setResourceEnablement(SyncResource.GlobalState, false); - } + private disableSync(source: SyncResource): void { + switch (source) { + case SyncResource.Settings: return this.userDataSyncResourceEnablementService.setResourceEnablement(SyncResource.Settings, false); + case SyncResource.Keybindings: return this.userDataSyncResourceEnablementService.setResourceEnablement(SyncResource.Keybindings, false); + case SyncResource.Snippets: return this.userDataSyncResourceEnablementService.setResourceEnablement(SyncResource.Snippets, false); + case SyncResource.Extensions: return this.userDataSyncResourceEnablementService.setResourceEnablement(SyncResource.Extensions, false); + case SyncResource.GlobalState: return this.userDataSyncResourceEnablementService.setResourceEnablement(SyncResource.GlobalState, false); } } private getConflictsEditorInputs(syncResource: SyncResource): DiffEditorInput[] { return this.editorService.editors.filter(input => { - const resource = input instanceof DiffEditorInput ? input.master.resource : input.resource; + const resource = input instanceof DiffEditorInput ? input.primary.resource : input.resource; return resource && getSyncResourceFromLocalPreview(resource!, this.workbenchEnvironmentService) === syncResource; }) as DiffEditorInput[]; } private getAllConflictsEditorInputs(): IEditorInput[] { return this.editorService.editors.filter(input => { - const resource = input instanceof DiffEditorInput ? input.master.resource : input.resource; + const resource = input instanceof DiffEditorInput ? input.primary.resource : input.resource; return resource && getSyncResourceFromLocalPreview(resource!, this.workbenchEnvironmentService) !== undefined; }); } private async handleSyncResourceConflicts(resource: SyncResource): Promise { - const syncResourceCoflicts = this.userDataSyncService.conflicts.filter(({ syncResource }) => syncResource === resource)[0]; + const syncResourceCoflicts = this.userDataSyncService.conflicts.filter(([syncResource]) => syncResource === resource)[0]; if (syncResourceCoflicts) { this.handleConflicts(syncResourceCoflicts); } } - private async handleConflicts({ syncResource, conflicts }: SyncResourceConflicts): Promise { + private async handleConflicts([syncResource, conflicts]: [SyncResource, IResourcePreview[]]): Promise { for (const conflict of conflicts) { - let label: string | undefined = undefined; - if (syncResource === SyncResource.Settings) { - label = localize('settings conflicts preview', "Settings Conflicts (Remote ↔ Local)"); - } else if (syncResource === SyncResource.Keybindings) { - label = localize('keybindings conflicts preview', "Keybindings Conflicts (Remote ↔ Local)"); - } else if (syncResource === SyncResource.Snippets) { - label = localize('snippets conflicts preview', "User Snippet Conflicts (Remote ↔ Local) - {0}", basename(conflict.local)); - } + const leftResourceName = localize({ key: 'leftResourceName', comment: ['remote as in file in cloud'] }, "{0} (Remote)", basename(conflict.remoteResource)); + const rightResourceName = localize('merges', "{0} (Merges)", basename(conflict.previewResource)); await this.editorService.openEditor({ - leftResource: conflict.remote, - rightResource: conflict.local, - label, + leftResource: conflict.remoteResource, + rightResource: conflict.previewResource, + label: localize('sideBySideLabels', "{0} ↔ {1}", leftResourceName, rightResourceName), options: { preserveFocus: false, pinned: true, @@ -684,8 +669,56 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo return this.outputService.showChannel(Constants.userDataSyncLogChannelId); } + private async switchSyncService(): Promise { + const userDataSyncStore = this.userDataSyncStoreManagementService.userDataSyncStore; + if (userDataSyncStore?.insidersUrl && userDataSyncStore?.stableUrl && ![userDataSyncStore.insidersUrl, userDataSyncStore.stableUrl].includes(userDataSyncStore.url)) { + return new Promise((c, e) => { + const disposables: DisposableStore = new DisposableStore(); + const quickPick = disposables.add(this.quickInputService.createQuickPick<{ id: UserDataSyncStoreType, label: string, description?: string }>()); + quickPick.title = localize('switchSyncService.title', "Select Settings Sync Service..."); + quickPick.placeholder = localize('choose sync service', "Choose settings sync Service to use"); + quickPick.description = isNative ? + localize('choose sync service description', "Switching settings sync service requires restarting {0}", this.productService.nameLong) : + localize('choose sync service description web', "Switching settings sync service requires reloading {0}", this.productService.nameLong); + quickPick.hideInput = true; + const getDescription = (url: URI): string | undefined => { + const isCurrent = isEqual(url, userDataSyncStore.url); + const isDefault = isEqual(url, userDataSyncStore.defaultUrl); + if (isCurrent && isDefault) { + return localize('default and current', "Default & Current"); + } + if (isDefault) { + return localize('default', "Default"); + } + if (isCurrent) { + return localize('current', "Current"); + } + return undefined; + }; + quickPick.items = [ + { + id: 'insiders', + label: localize('insiders', "Insiders"), + description: getDescription(userDataSyncStore.insidersUrl!) + }, + { + id: 'stable', + label: localize('stable', "Stable"), + description: getDescription(userDataSyncStore.stableUrl!) + } + ]; + disposables.add(quickPick.onDidAccept(() => { + this.userDataSyncWorkbenchService.switchSyncService(quickPick.selectedItems[0].id); + quickPick.hide(); + })); + disposables.add(quickPick.onDidHide(() => disposables.dispose())); + quickPick.show(); + }); + } + } + private registerActions(): void { - if (this.userDataSyncEnablementService.canToggleEnablement()) { + if (this.userDataAutoSyncService.canToggleEnablement()) { this.registerTurnOnSyncAction(); this.registerTurnOffSyncAction(); } @@ -694,29 +727,24 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo this.registerShowSettingsConflictsAction(); this.registerShowKeybindingsConflictsAction(); this.registerShowSnippetsConflictsAction(); - this.registerSyncStatusAction(); + this.registerEnableSyncViewsAction(); + this.registerManageSyncAction(); + this.registerSyncNowAction(); this.registerConfigureSyncAction(); - this.registerShowActivityAction(); this.registerShowSettingsAction(); + this.registerSwitchSyncServiceAction(); + this.registerShowLogAction(); } private registerTurnOnSyncAction(): void { const turnOnSyncWhenContext = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_ENABLEMENT.toNegated(), CONTEXT_ACCOUNT_STATE.notEqualsTo(AccountStatus.Uninitialized), CONTEXT_TURNING_ON_STATE.negate()); - CommandsRegistry.registerCommand(turnOnSyncCommand.id, async () => { - try { - await this.turnOn(); - } catch (e) { - if (!isPromiseCanceledError(e)) { - this.notificationService.error(localize('turn on failed', "Error while starting Sync: {0}", toErrorMessage(e))); - } - } - }); + CommandsRegistry.registerCommand(turnOnSyncCommand.id, () => this.turnOn()); MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { group: '5_sync', command: { id: turnOnSyncCommand.id, - title: localize('global activity turn on sync', "Turn on Preferences Sync...") + title: localize('global activity turn on sync', "Turn on Settings Sync...") }, when: turnOnSyncWhenContext, order: 1 @@ -729,7 +757,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo group: '5_sync', command: { id: turnOnSyncCommand.id, - title: localize('global activity turn on sync', "Turn on Preferences Sync...") + title: localize('global activity turn on sync', "Turn on Settings Sync...") }, when: turnOnSyncWhenContext, }); @@ -737,7 +765,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo group: '1_sync', command: { id: turnOnSyncCommand.id, - title: localize('global activity turn on sync', "Turn on Preferences Sync...") + title: localize('global activity turn on sync', "Turn on Settings Sync...") }, when: turnOnSyncWhenContext }); @@ -749,7 +777,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo constructor() { super({ id: 'workbench.userData.actions.turningOn', - title: localize('turnin on sync', "Turning on Preferences Sync..."), + title: localize('turnin on sync', "Turning on Settings Sync..."), precondition: ContextKeyExpr.false(), menu: [{ group: '5_sync', @@ -775,7 +803,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo constructor() { super({ id: 'workbench.userData.actions.signin', - title: localize('sign in global', "Sign in to Sync Preferences(1)"), + title: localize('sign in global', "Sign in to Sync Settings"), menu: { group: '5_sync', id: MenuId.GlobalActivity, @@ -786,7 +814,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } async run(): Promise { try { - await that.userDataSyncAccounts.pick(); + await that.userDataSyncWorkbenchService.signIn(); } catch (e) { that.notificationService.error(e); } @@ -796,7 +824,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo group: '1_sync', command: { id, - title: localize('sign in accounts', "Sign in to Sync Preferences"), + title: localize('sign in accounts', "Sign in to Sync Settings (1)"), }, when })); @@ -809,7 +837,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo group: '5_sync', command: { id: resolveSettingsConflictsCommand.id, - title: localize('resolveConflicts_global', "Preferences Sync: Show Settings Conflicts (1)"), + title: localize('resolveConflicts_global', "{0}: Show Settings Conflicts (1)", SYNC_TITLE), }, when: resolveSettingsConflictsWhenContext, order: 2 @@ -818,7 +846,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo group: '5_sync', command: { id: resolveSettingsConflictsCommand.id, - title: localize('resolveConflicts_global', "Preferences Sync: Show Settings Conflicts (1)"), + title: localize('resolveConflicts_global', "{0}: Show Settings Conflicts (1)", SYNC_TITLE), }, when: resolveSettingsConflictsWhenContext, order: 2 @@ -836,7 +864,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo group: '5_sync', command: { id: resolveKeybindingsConflictsCommand.id, - title: localize('resolveKeybindingsConflicts_global', "Preferences Sync: Show Keybindings Conflicts (1)"), + title: localize('resolveKeybindingsConflicts_global', "{0}: Show Keybindings Conflicts (1)", SYNC_TITLE), }, when: resolveKeybindingsConflictsWhenContext, order: 2 @@ -845,7 +873,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo group: '5_sync', command: { id: resolveKeybindingsConflictsCommand.id, - title: localize('resolveKeybindingsConflicts_global', "Preferences Sync: Show Keybindings Conflicts (1)"), + title: localize('resolveKeybindingsConflicts_global', "{0}: Show Keybindings Conflicts (1)", SYNC_TITLE), }, when: resolveKeybindingsConflictsWhenContext, order: 2 @@ -860,13 +888,13 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private registerShowSnippetsConflictsAction(): void { this._snippetsConflictsActionsDisposable.clear(); const resolveSnippetsConflictsWhenContext = ContextKeyExpr.regex(CONTEXT_CONFLICTS_SOURCES.keys()[0], /.*snippets.*/i); - const conflicts: Conflict[] | undefined = this.userDataSyncService.conflicts.filter(({ syncResource }) => syncResource === SyncResource.Snippets)[0]?.conflicts; + const conflicts: IResourcePreview[] | undefined = this.userDataSyncService.conflicts.filter(([syncResource]) => syncResource === SyncResource.Snippets)[0]?.[1]; this._snippetsConflictsActionsDisposable.add(CommandsRegistry.registerCommand(resolveSnippetsConflictsCommand.id, () => this.handleSyncResourceConflicts(SyncResource.Snippets))); this._snippetsConflictsActionsDisposable.add(MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { group: '5_sync', command: { id: resolveSnippetsConflictsCommand.id, - title: localize('resolveSnippetsConflicts_global', "Preferences Sync: Show User Snippets Conflicts ({0})", conflicts?.length || 1), + title: localize('resolveSnippetsConflicts_global', "{0}: Show User Snippets Conflicts ({1})", SYNC_TITLE, conflicts?.length || 1), }, when: resolveSnippetsConflictsWhenContext, order: 2 @@ -875,7 +903,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo group: '5_sync', command: { id: resolveSnippetsConflictsCommand.id, - title: localize('resolveSnippetsConflicts_global', "Preferences Sync: Show User Snippets Conflicts ({0})", conflicts?.length || 1), + title: localize('resolveSnippetsConflicts_global', "{0}: Show User Snippets Conflicts ({1})", SYNC_TITLE, conflicts?.length || 1), }, when: resolveSnippetsConflictsWhenContext, order: 2 @@ -886,15 +914,14 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo })); } - private registerSyncStatusAction(): void { + private registerManageSyncAction(): void { const that = this; const when = ContextKeyExpr.and(CONTEXT_SYNC_ENABLEMENT, CONTEXT_ACCOUNT_STATE.isEqualTo(AccountStatus.Available), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized)); - this.registerSyncNowAction(); this._register(registerAction2(class SyncStatusAction extends Action2 { constructor() { super({ - id: 'workbench.userData.actions.syncStatus', - title: localize('sync is on', "Preferences Sync is On"), + id: 'workbench.userDataSync.actions.manage', + title: localize('sync is on', "Settings Sync is On"), menu: [ { id: MenuId.GlobalActivity, @@ -925,7 +952,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo disposables.add(quickPick); const items: Array = []; if (that.userDataSyncService.conflicts.length) { - for (const { syncResource } of that.userDataSyncService.conflicts) { + for (const [syncResource] of that.userDataSyncService.conflicts) { switch (syncResource) { case SyncResource.Settings: items.push({ id: resolveSettingsConflictsCommand.id, label: resolveSettingsConflictsCommand.title }); @@ -942,12 +969,12 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } items.push({ id: configureSyncCommand.id, label: configureSyncCommand.title }); items.push({ id: showSyncSettingsCommand.id, label: showSyncSettingsCommand.title }); - items.push({ id: showSyncActivityCommand.id, label: showSyncActivityCommand.title }); + items.push({ id: showSyncedDataCommand.id, label: showSyncedDataCommand.title }); items.push({ type: 'separator' }); items.push({ id: syncNowCommand.id, label: syncNowCommand.title, description: syncNowCommand.description(that.userDataSyncService) }); - if (that.userDataSyncEnablementService.canToggleEnablement()) { - const account = that.userDataSyncAccounts.current; - items.push({ id: stopSyncCommand.id, label: stopSyncCommand.title, description: account ? `${account.accountName} (${that.authenticationService.getDisplayName(account.authenticationProviderId)})` : undefined }); + if (that.userDataAutoSyncService.canToggleEnablement()) { + const account = that.userDataSyncWorkbenchService.current; + items.push({ id: turnOffSyncCommand.id, label: turnOffSyncCommand.title, description: account ? `${account.accountName} (${that.authenticationService.getLabel(account.authenticationProviderId)})` : undefined }); } quickPick.items = items; disposables.add(quickPick.onDidAccept(() => { @@ -966,6 +993,28 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo })); } + private registerEnableSyncViewsAction(): void { + const that = this; + const when = ContextKeyExpr.and(CONTEXT_SYNC_ENABLEMENT, CONTEXT_ACCOUNT_STATE.isEqualTo(AccountStatus.Available), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized)); + this._register(registerAction2(class SyncStatusAction extends Action2 { + constructor() { + super({ + id: showSyncedDataCommand.id, + title: { value: localize('workbench.action.showSyncRemoteBackup', "Show Synced Data"), original: `Show Synced Data` }, + category: { value: SYNC_TITLE, original: `Settings Sync` }, + precondition: when, + menu: { + id: MenuId.CommandPalette, + when + } + }); + } + run(accessor: ServicesAccessor): Promise { + return that.userDataSyncWorkbenchService.showSyncActivity(); + } + })); + } + private registerSyncNowAction(): void { const that = this; this._register(registerAction2(class SyncNowAction extends Action2 { @@ -973,10 +1022,14 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo super({ id: syncNowCommand.id, title: syncNowCommand.title, + menu: { + id: MenuId.CommandPalette, + when: ContextKeyExpr.and(CONTEXT_SYNC_ENABLEMENT, CONTEXT_ACCOUNT_STATE.isEqualTo(AccountStatus.Available), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized)) + } }); } - run(): Promise { - return that.userDataSyncService.sync(); + run(accessor: ServicesAccessor): Promise { + return that.userDataAutoSyncService.triggerSync([syncNowCommand.id], false); } })); } @@ -986,8 +1039,8 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo this._register(registerAction2(class StopSyncAction extends Action2 { constructor() { super({ - id: stopSyncCommand.id, - title: stopSyncCommand.title, + id: turnOffSyncCommand.id, + title: turnOffSyncCommand.title, menu: { id: MenuId.CommandPalette, when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_ENABLEMENT), @@ -1008,28 +1061,29 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private registerConfigureSyncAction(): void { const that = this; - this._register(registerAction2(class ShowSyncActivityAction extends Action2 { + const when = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_ENABLEMENT); + this._register(registerAction2(class ConfigureSyncAction extends Action2 { constructor() { super({ id: configureSyncCommand.id, title: configureSyncCommand.title, menu: { id: MenuId.CommandPalette, - when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_ENABLEMENT), - }, + when + } }); } run(): any { return that.configureSyncOptions(); } })); } - private registerShowActivityAction(): void { + private registerShowLogAction(): void { const that = this; this._register(registerAction2(class ShowSyncActivityAction extends Action2 { constructor() { super({ - id: showSyncActivityCommand.id, - title: showSyncActivityCommand.title, + id: SHOW_SYNC_LOG_COMMAND_ID, + title: localize('show sync log title', "{0}: Show Log", SYNC_TITLE), menu: { id: MenuId.CommandPalette, when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized)), @@ -1058,6 +1112,51 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo })); } + private registerSwitchSyncServiceAction(): void { + const that = this; + const userDataSyncStore = this.userDataSyncStoreManagementService.userDataSyncStore; + if (userDataSyncStore?.insidersUrl && userDataSyncStore?.stableUrl && ![userDataSyncStore.insidersUrl, userDataSyncStore.stableUrl].includes(userDataSyncStore.url)) { + this._register(registerAction2(class ShowSyncSettingsAction extends Action2 { + constructor() { + super({ + id: 'workbench.userDataSync.actions.switchSyncService', + title: { value: localize('workbench.userDataSync.actions.switchSyncService', "{0}: Select Service...", SYNC_TITLE), original: 'Settings Sync: Select Service...' }, + menu: { + id: MenuId.CommandPalette, + when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized)), + }, + }); + } + run(accessor: ServicesAccessor): any { + return that.switchSyncService(); + } + })); + } + } + + private registerViews(): void { + const container = this.registerViewContainer(); + this.registerDataViews(container); + } + + private registerViewContainer(): ViewContainer { + return Registry.as(Extensions.ViewContainersRegistry).registerViewContainer( + { + id: SYNC_VIEW_CONTAINER_ID, + name: SYNC_TITLE, + ctorDescriptor: new SyncDescriptor( + UserDataSyncViewPaneContainer, + [SYNC_VIEW_CONTAINER_ID] + ), + icon: Codicon.sync.classNames, + hideIfEmpty: true, + }, ViewContainerLocation.Sidebar); + } + + private registerDataViews(container: ViewContainer): void { + this._register(this.instantiationService.createInstance(UserDataSyncDataViews, container)); + } + } class UserDataRemoteContentProvider implements ITextModelContentProvider { @@ -1094,7 +1193,8 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio @INotificationService private readonly notificationService: INotificationService, @IDialogService private readonly dialogService: IDialogService, @IConfigurationService private readonly configurationService: IConfigurationService, - @ITelemetryService private readonly telemetryService: ITelemetryService + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IUserDataAutoSyncService private readonly userDataAutoSyncService: IUserDataAutoSyncService, ) { super(); @@ -1123,16 +1223,20 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio return false; // we need a model } + if (!this.userDataAutoSyncService.isEnabled()) { + return false; + } + const syncResourceConflicts = this.getSyncResourceConflicts(model.uri); if (!syncResourceConflicts) { return false; } - if (syncResourceConflicts.conflicts.some(({ local }) => isEqual(local, model.uri))) { + if (syncResourceConflicts[1].some(({ previewResource }) => isEqual(previewResource, model.uri))) { return true; } - if (syncResourceConflicts.conflicts.some(({ remote }) => isEqual(remote, model.uri))) { + if (syncResourceConflicts[1].some(({ remoteResource }) => isEqual(remoteResource, model.uri))) { return this.configurationService.getValue('diffEditor.renderSideBySide'); } @@ -1142,33 +1246,33 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio private createAcceptChangesWidgetRenderer(): void { if (!this.acceptChangesButton) { const resource = this.editor.getModel()!.uri; - const syncResourceConflicts = this.getSyncResourceConflicts(resource)!; - const isRemote = syncResourceConflicts.conflicts.some(({ remote }) => isEqual(remote, resource)); + const [syncResource, conflicts] = this.getSyncResourceConflicts(resource)!; + const isRemote = conflicts.some(({ remoteResource }) => isEqual(remoteResource, resource)); const acceptRemoteLabel = localize('accept remote', "Accept Remote"); - const acceptLocalLabel = localize('accept local', "Accept Local"); - this.acceptChangesButton = this.instantiationService.createInstance(FloatingClickWidget, this.editor, isRemote ? acceptRemoteLabel : acceptLocalLabel, null); + const acceptMergesLabel = localize('accept merges', "Accept Merges"); + this.acceptChangesButton = this.instantiationService.createInstance(FloatingClickWidget, this.editor, isRemote ? acceptRemoteLabel : acceptMergesLabel, null); this._register(this.acceptChangesButton.onClick(async () => { const model = this.editor.getModel(); if (model) { - this.telemetryService.publicLog2<{ source: string, action: string }, SyncConflictsClassification>('sync/handleConflicts', { source: syncResourceConflicts.syncResource, action: isRemote ? 'acceptRemote' : 'acceptLocal' }); - const syncAreaLabel = getSyncAreaLabel(syncResourceConflicts.syncResource); + this.telemetryService.publicLog2<{ source: string, action: string }, SyncConflictsClassification>('sync/handleConflicts', { source: syncResource, action: isRemote ? 'acceptRemote' : 'acceptLocal' }); + const syncAreaLabel = getSyncAreaLabel(syncResource); const result = await this.dialogService.confirm({ type: 'info', title: isRemote - ? localize('Sync accept remote', "Preferences Sync: {0}", acceptRemoteLabel) - : localize('Sync accept local', "Preferences Sync: {0}", acceptLocalLabel), + ? localize('Sync accept remote', "{0}: {1}", SYNC_TITLE, acceptRemoteLabel) + : localize('Sync accept merges', "{0}: {1}", SYNC_TITLE, acceptMergesLabel), message: isRemote ? localize('confirm replace and overwrite local', "Would you like to accept remote {0} and replace local {1}?", syncAreaLabel.toLowerCase(), syncAreaLabel.toLowerCase()) - : localize('confirm replace and overwrite remote', "Would you like to accept local {0} and replace remote {1}?", syncAreaLabel.toLowerCase(), syncAreaLabel.toLowerCase()), - primaryButton: isRemote ? acceptRemoteLabel : acceptLocalLabel + : localize('confirm replace and overwrite remote', "Would you like to accept merges and replace remote {0}?", syncAreaLabel.toLowerCase()), + primaryButton: isRemote ? acceptRemoteLabel : acceptMergesLabel }); if (result.confirmed) { try { - await this.userDataSyncService.acceptConflict(model.uri, model.getValue()); + await this.userDataSyncService.accept(syncResource, model.uri, model.getValue(), true); } catch (e) { if (e instanceof UserDataSyncError && e.code === UserDataSyncErrorCode.LocalPreconditionFailed) { - const syncResourceCoflicts = this.userDataSyncService.conflicts.filter(({ syncResource }) => syncResource === syncResourceConflicts.syncResource)[0]; - if (syncResourceCoflicts && syncResourceCoflicts.conflicts.some(conflict => isEqual(conflict.local, model.uri) || isEqual(conflict.remote, model.uri))) { + const syncResourceCoflicts = this.userDataSyncService.conflicts.filter(syncResourceCoflicts => syncResourceCoflicts[0] === syncResource)[0]; + if (syncResourceCoflicts && conflicts.some(conflict => isEqual(conflict.previewResource, model.uri) || isEqual(conflict.remoteResource, model.uri))) { this.notificationService.warn(localize('update conflicts', "Could not resolve conflicts as there is new local version available. Please try again.")); } } else { @@ -1183,8 +1287,8 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio } } - private getSyncResourceConflicts(resource: URI): SyncResourceConflicts | undefined { - return this.userDataSyncService.conflicts.filter(({ conflicts }) => conflicts.some(({ local, remote }) => isEqual(local, resource) || isEqual(remote, resource)))[0]; + private getSyncResourceConflicts(resource: URI): [SyncResource, IResourcePreview[]] | undefined { + return this.userDataSyncService.conflicts.filter(([, conflicts]) => conflicts.some(({ previewResource, remoteResource }) => isEqual(previewResource, resource) || isEqual(remoteResource, resource)))[0]; } private disposeAcceptChangesWidgetRenderer(): void { diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncAccount.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncAccount.ts deleted file mode 100644 index 44b7cfc95d0..00000000000 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncAccount.ts +++ /dev/null @@ -1,341 +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 { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; -import { IQuickInputService, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; -import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication'; -import { IProductService } from 'vs/platform/product/common/productService'; -import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; -import { localize } from 'vs/nls'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { AuthenticationSession, AuthenticationSessionsChangeEvent } from 'vs/editor/common/modes'; -import { Event, Emitter } from 'vs/base/common/event'; -import { getUserDataSyncStore, IUserDataSyncEnablementService, IAuthenticationProvider, isAuthenticationProvider } from 'vs/platform/userDataSync/common/userDataSync'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { values } from 'vs/base/common/map'; -import { ILogService } from 'vs/platform/log/common/log'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { flatten } from 'vs/base/common/arrays'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; - -type UserAccountClassification = { - id: { classification: 'EndUserPseudonymizedInformation', purpose: 'BusinessInsight' }; -}; - -type UserAccountEvent = { - id: string; -}; - -type AccountQuickPickItem = { label: string, authenticationProvider: IAuthenticationProvider, account?: UserDataSyncAccount, description?: string }; - -export class UserDataSyncAccount { - - constructor(readonly authenticationProviderId: string, private readonly session: AuthenticationSession) { } - - get sessionId(): string { return this.session.id; } - get accountName(): string { return this.session.account.displayName; } - get accountId(): string { return this.session.account.id; } - getToken(): Thenable { return this.session.getAccessToken(); } -} - -export const enum AccountStatus { - Uninitialized = 'uninitialized', - Unavailable = 'unavailable', - Available = 'available', -} - -export class UserDataSyncAccounts extends Disposable { - - private static DONOT_USE_WORKBENCH_SESSION_STORAGE_KEY = 'userDataSyncAccount.donotUseWorkbenchSession'; - private static CACHED_SESSION_STORAGE_KEY = 'userDataSyncAccountPreference'; - - _serviceBrand: any; - - readonly authenticationProviders: IAuthenticationProvider[]; - - private _status: AccountStatus = AccountStatus.Uninitialized; - get status(): AccountStatus { return this._status; } - private readonly _onDidChangeStatus = this._register(new Emitter()); - readonly onDidChangeStatus = this._onDidChangeStatus.event; - - private readonly _onDidSignOut = this._register(new Emitter()); - readonly onDidSignOut = this._onDidSignOut.event; - - private _all: Map = new Map(); - get all(): UserDataSyncAccount[] { return flatten(values(this._all)); } - - get current(): UserDataSyncAccount | undefined { return this.all.filter(account => this.isCurrentAccount(account))[0]; } - - constructor( - @IAuthenticationService private readonly authenticationService: IAuthenticationService, - @IAuthenticationTokenService private readonly authenticationTokenService: IAuthenticationTokenService, - @IQuickInputService private readonly quickInputService: IQuickInputService, - @IStorageService private readonly storageService: IStorageService, - @IUserDataSyncEnablementService private readonly userDataSyncEnablementService: IUserDataSyncEnablementService, - @ITelemetryService private readonly telemetryService: ITelemetryService, - @ILogService private readonly logService: ILogService, - @IProductService productService: IProductService, - @IConfigurationService configurationService: IConfigurationService, - @IExtensionService extensionService: IExtensionService, - @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, - ) { - super(); - this.authenticationProviders = getUserDataSyncStore(productService, configurationService)?.authenticationProviders || []; - if (this.authenticationProviders.length) { - extensionService.whenInstalledExtensionsRegistered().then(() => { - if (this.authenticationProviders.every(({ id }) => authenticationService.isAuthenticationProviderRegistered(id))) { - this.initialize(); - } else { - const disposable = this.authenticationService.onDidRegisterAuthenticationProvider(() => { - if (this.authenticationProviders.every(({ id }) => authenticationService.isAuthenticationProviderRegistered(id))) { - disposable.dispose(); - this.initialize(); - } - }); - } - }); - } - } - - private async initialize(): Promise { - if (this.currentSessionId === undefined && this.useWorkbenchSessionId && this.environmentService.options?.authenticationSessionId) { - this.currentSessionId = this.environmentService.options.authenticationSessionId; - this.useWorkbenchSessionId = false; - } - - await this.update(); - - this._register( - Event.any( - Event.filter( - Event.any( - this.authenticationService.onDidRegisterAuthenticationProvider, - this.authenticationService.onDidUnregisterAuthenticationProvider, - ), authenticationProviderId => this.isSupportedAuthenticationProviderId(authenticationProviderId)), - this.authenticationTokenService.onTokenFailed) - (() => this.update())); - - this._register(Event.filter(this.authenticationService.onDidChangeSessions, e => this.isSupportedAuthenticationProviderId(e.providerId))(({ event }) => this.onDidChangeSessions(event))); - this._register(this.storageService.onDidChangeStorage(e => this.onDidChangeStorage(e))); - } - - private isSupportedAuthenticationProviderId(authenticationProviderId: string): boolean { - return this.authenticationProviders.some(({ id }) => id === authenticationProviderId); - } - - private async update(): Promise { - const allAccounts: Map = new Map(); - for (const { id } of this.authenticationProviders) { - const accounts = await this.getAccounts(id); - allAccounts.set(id, accounts); - } - - this._all = allAccounts; - const current = this.current; - await this.updateToken(current); - this.updateStatus(current); - } - - private async getAccounts(authenticationProviderId: string): Promise { - let accounts: Map = new Map(); - let currentAccount: UserDataSyncAccount | null = null; - - const sessions = await this.authenticationService.getSessions(authenticationProviderId) || []; - for (const session of sessions) { - const account: UserDataSyncAccount = new UserDataSyncAccount(authenticationProviderId, session); - accounts.set(account.accountName, account); - if (this.isCurrentAccount(account)) { - currentAccount = account; - } - } - - if (currentAccount) { - // Always use current account if available - accounts.set(currentAccount.accountName, currentAccount); - } - - return values(accounts); - } - - private async updateToken(current: UserDataSyncAccount | undefined): Promise { - let value: { token: string, authenticationProviderId: string } | undefined = undefined; - if (current) { - try { - this.logService.trace('Preferences Sync: Updating the token for the account', current.accountName); - const token = await current.getToken(); - this.logService.trace('Preferences Sync: Token updated for the account', current.accountName); - value = { token, authenticationProviderId: current.authenticationProviderId }; - } catch (e) { - this.logService.error(e); - } - } - await this.authenticationTokenService.setToken(value); - } - - private updateStatus(current: UserDataSyncAccount | undefined): void { - // set status - const status: AccountStatus = current ? AccountStatus.Available : AccountStatus.Unavailable; - - if (this._status !== status) { - const previous = this._status; - this.logService.debug('Sync account status changed', previous, status); - - if (previous === AccountStatus.Available && status === AccountStatus.Unavailable) { - this._onDidSignOut.fire(); - } - - this._status = status; - this._onDidChangeStatus.fire(status); - } - } - - private isCurrentAccount(account: UserDataSyncAccount): boolean { - return account.sessionId === this.currentSessionId; - } - - async pick(): Promise { - const result = await this.doPick(); - if (!result) { - return false; - } - let sessionId: string, accountName: string, accountId: string; - if (isAuthenticationProvider(result)) { - const session = await this.authenticationService.login(result.id, result.scopes); - sessionId = session.id; - accountName = session.account.displayName; - accountId = session.account.id; - } else { - sessionId = result.sessionId; - accountName = result.accountName; - accountId = result.accountId; - } - await this.switch(sessionId, accountName, accountId); - return true; - } - - private async doPick(): Promise { - if (this.authenticationProviders.length === 0) { - return undefined; - } - - await this.update(); - - // Single auth provider and no accounts available - if (this.authenticationProviders.length === 1 && !this.all.length) { - return this.authenticationProviders[0]; - } - - return new Promise(async (c, e) => { - let result: UserDataSyncAccount | IAuthenticationProvider | undefined; - const disposables: DisposableStore = new DisposableStore(); - const quickPick = this.quickInputService.createQuickPick(); - disposables.add(quickPick); - - quickPick.title = localize('pick an account', "Preferences Sync"); - quickPick.ok = false; - quickPick.placeholder = localize('choose account placeholder', "Select an account"); - quickPick.ignoreFocusOut = true; - quickPick.items = this.createQuickpickItems(); - - disposables.add(quickPick.onDidAccept(() => { - result = quickPick.selectedItems[0]?.account ? quickPick.selectedItems[0]?.account : quickPick.selectedItems[0]?.authenticationProvider; - quickPick.hide(); - })); - disposables.add(quickPick.onDidHide(() => { - disposables.dispose(); - c(result); - })); - quickPick.show(); - }); - } - - private createQuickpickItems(): (AccountQuickPickItem | IQuickPickSeparator)[] { - const quickPickItems: (AccountQuickPickItem | IQuickPickSeparator)[] = []; - - // Signed in Accounts - if (this.all.length) { - const authenticationProviders = [...this.authenticationProviders].sort(({ id }) => id === this.current?.authenticationProviderId ? -1 : 1); - quickPickItems.push({ type: 'separator', label: localize('signed in', "Signed in") }); - for (const authenticationProvider of authenticationProviders) { - const accounts = (this._all.get(authenticationProvider.id) || []).sort(({ sessionId }) => sessionId === this.current?.sessionId ? -1 : 1); - const providerName = this.authenticationService.getDisplayName(authenticationProvider.id); - for (const account of accounts) { - quickPickItems.push({ - label: `${account.accountName} (${providerName})`, - description: account.sessionId === this.current?.sessionId ? localize('last used', "Last Used with Sync") : undefined, - account, - authenticationProvider, - }); - } - } - quickPickItems.push({ type: 'separator', label: localize('others', "Others") }); - } - - // Account proviers - for (const authenticationProvider of this.authenticationProviders) { - const providerName = this.authenticationService.getDisplayName(authenticationProvider.id); - quickPickItems.push({ label: localize('sign in using account', "Sign in with {0}", providerName), authenticationProvider }); - } - - return quickPickItems; - } - - private async switch(sessionId: string, accountName: string, accountId: string): Promise { - const currentAccount = this.current; - if (this.userDataSyncEnablementService.isEnabled() && (currentAccount && currentAccount.accountName !== accountName)) { - // accounts are switched while sync is enabled. - } - this.currentSessionId = sessionId; - this.telemetryService.publicLog2('sync.userAccount', { id: accountId }); - await this.update(); - } - - private onDidChangeSessions(e: AuthenticationSessionsChangeEvent): void { - if (this.currentSessionId && e.removed.includes(this.currentSessionId)) { - this.currentSessionId = undefined; - } - this.update(); - } - - private onDidChangeStorage(e: IWorkspaceStorageChangeEvent): void { - if (e.key === UserDataSyncAccounts.CACHED_SESSION_STORAGE_KEY && e.scope === StorageScope.GLOBAL - && this.currentSessionId !== this.getStoredCachedSessionId() /* This checks if current window changed the value or not */) { - this._cachedCurrentSessionId = null; - this.update(); - } - } - - private _cachedCurrentSessionId: string | undefined | null = null; - private get currentSessionId(): string | undefined { - if (this._cachedCurrentSessionId === null) { - this._cachedCurrentSessionId = this.getStoredCachedSessionId(); - } - return this._cachedCurrentSessionId; - } - - private set currentSessionId(cachedSessionId: string | undefined) { - if (this._cachedCurrentSessionId !== cachedSessionId) { - this._cachedCurrentSessionId = cachedSessionId; - if (cachedSessionId === undefined) { - this.storageService.remove(UserDataSyncAccounts.CACHED_SESSION_STORAGE_KEY, StorageScope.GLOBAL); - } else { - this.storageService.store(UserDataSyncAccounts.CACHED_SESSION_STORAGE_KEY, cachedSessionId, StorageScope.GLOBAL); - } - } - } - - private getStoredCachedSessionId(): string | undefined { - return this.storageService.get(UserDataSyncAccounts.CACHED_SESSION_STORAGE_KEY, StorageScope.GLOBAL); - } - - private get useWorkbenchSessionId(): boolean { - return !this.storageService.getBoolean(UserDataSyncAccounts.DONOT_USE_WORKBENCH_SESSION_STORAGE_KEY, StorageScope.GLOBAL, false); - } - - private set useWorkbenchSessionId(useWorkbenchSession: boolean) { - this.storageService.store(UserDataSyncAccounts.DONOT_USE_WORKBENCH_SESSION_STORAGE_KEY, !useWorkbenchSession, StorageScope.GLOBAL); - } -} diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts new file mode 100644 index 00000000000..2debdfa08dc --- /dev/null +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts @@ -0,0 +1,501 @@ +/*--------------------------------------------------------------------------------------------- + * 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/userDataSyncViews'; +import { ITreeItem, TreeItemCollapsibleState, TreeViewItemHandleArg, IViewDescriptorService } from 'vs/workbench/common/views'; +import { localize } from 'vs/nls'; +import { TreeViewPane } from 'vs/workbench/browser/parts/views/treeView'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IUserDataSyncService, Change, MergeState, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; +import { registerAction2, Action2, MenuId } from 'vs/platform/actions/common/actions'; +import { ContextKeyExpr, ContextKeyEqualsExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { URI } from 'vs/base/common/uri'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable, dispose } from 'vs/base/common/lifecycle'; +import { Codicon } from 'vs/base/common/codicons'; +import { IUserDataSyncWorkbenchService, getSyncAreaLabel, IUserDataSyncPreview, IUserDataSyncResource, SYNC_MERGES_VIEW_ID } from 'vs/workbench/services/userDataSync/common/userDataSync'; +import { isEqual, basename } from 'vs/base/common/resources'; +import { IDecorationsProvider, IDecorationData, IDecorationsService } from 'vs/workbench/services/decorations/browser/decorations'; +import { IProgressService } from 'vs/platform/progress/common/progress'; +import { listWarningForeground, listDeemphasizedForeground } from 'vs/platform/theme/common/colorRegistry'; +import * as DOM from 'vs/base/browser/dom'; +import { Button } from 'vs/base/browser/ui/button/button'; +import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { attachButtonStyler } from 'vs/platform/theme/common/styler'; +import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { FloatingClickWidget } from 'vs/workbench/browser/parts/editor/editorWidgets'; +import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { Severity } from 'vs/platform/notification/common/notification'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; + +export class UserDataSyncMergesViewPane extends TreeViewPane { + + private userDataSyncPreview: IUserDataSyncPreview; + + private buttonsContainer!: HTMLElement; + private syncButton!: Button; + private cancelButton!: Button; + + private readonly treeItems = new Map(); + + constructor( + options: IViewletViewOptions, + @IEditorService private readonly editorService: IEditorService, + @IDialogService private readonly dialogService: IDialogService, + @IProgressService private readonly progressService: IProgressService, + @IUserDataSyncWorkbenchService userDataSyncWorkbenchService: IUserDataSyncWorkbenchService, + @IDecorationsService decorationsService: IDecorationsService, + @IKeybindingService keybindingService: IKeybindingService, + @IContextMenuService contextMenuService: IContextMenuService, + @IConfigurationService configurationService: IConfigurationService, + @IContextKeyService contextKeyService: IContextKeyService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @IInstantiationService instantiationService: IInstantiationService, + @IOpenerService openerService: IOpenerService, + @IThemeService themeService: IThemeService, + @ITelemetryService telemetryService: ITelemetryService, + ) { + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); + this.userDataSyncPreview = userDataSyncWorkbenchService.userDataSyncPreview; + + this._register(this.userDataSyncPreview.onDidChangeResources(() => this.updateSyncButtonEnablement())); + this._register(this.userDataSyncPreview.onDidChangeResources(() => this.treeView.refresh())); + this._register(this.userDataSyncPreview.onDidChangeResources(() => this.closeConflictEditors())); + this._register(decorationsService.registerDecorationsProvider(this._register(new UserDataSyncResourcesDecorationProvider(this.userDataSyncPreview)))); + + this.registerActions(); + } + + protected renderTreeView(container: HTMLElement): void { + super.renderTreeView(DOM.append(container, DOM.$(''))); + this.createButtons(container); + + const that = this; + this.treeView.message = localize('explanation', "Please go through each entry and merge to enable sync."); + this.treeView.dataProvider = { getChildren() { return that.getTreeItems(); } }; + } + + private createButtons(container: HTMLElement): void { + this.buttonsContainer = DOM.append(container, DOM.$('.manual-sync-buttons-container')); + + this.syncButton = this._register(new Button(this.buttonsContainer)); + this.syncButton.label = localize('turn on sync', "Turn on Settings Sync"); + this.updateSyncButtonEnablement(); + this._register(attachButtonStyler(this.syncButton, this.themeService)); + this._register(this.syncButton.onDidClick(() => this.apply())); + + this.cancelButton = this._register(new Button(this.buttonsContainer, { secondary: true })); + this.cancelButton.label = localize('cancel', "Cancel"); + this._register(attachButtonStyler(this.cancelButton, this.themeService)); + this._register(this.cancelButton.onDidClick(() => this.cancel())); + } + + protected layoutTreeView(height: number, width: number): void { + const buttonContainerHeight = 78; + this.buttonsContainer.style.height = `${buttonContainerHeight}px`; + this.buttonsContainer.style.width = `${width}px`; + + const numberOfChanges = this.userDataSyncPreview.resources.filter(r => r.syncResource !== SyncResource.GlobalState && (r.localChange !== Change.None || r.remoteChange !== Change.None)).length; + const messageHeight = 44; + super.layoutTreeView(Math.min(height - buttonContainerHeight, ((22 * numberOfChanges) + messageHeight)), width); + } + + private updateSyncButtonEnablement(): void { + this.syncButton.enabled = this.userDataSyncPreview.resources.every(c => c.syncResource === SyncResource.GlobalState || c.mergeState === MergeState.Accepted); + } + + private async getTreeItems(): Promise { + this.treeItems.clear(); + const roots: ITreeItem[] = []; + for (const resource of this.userDataSyncPreview.resources) { + if (resource.syncResource !== SyncResource.GlobalState && (resource.localChange !== Change.None || resource.remoteChange !== Change.None)) { + const handle = JSON.stringify(resource); + const treeItem = { + handle, + resourceUri: resource.remote, + label: { label: basename(resource.remote), strikethrough: resource.mergeState === MergeState.Accepted && (resource.localChange === Change.Deleted || resource.remoteChange === Change.Deleted) }, + description: getSyncAreaLabel(resource.syncResource), + collapsibleState: TreeItemCollapsibleState.None, + command: { id: `workbench.actions.sync.showChanges`, title: '', arguments: [{ $treeViewId: '', $treeItemHandle: handle }] }, + contextValue: `sync-resource-${resource.mergeState}` + }; + this.treeItems.set(handle, treeItem); + roots.push(treeItem); + } + } + return roots; + } + + private toUserDataSyncResourceGroup(handle: string): IUserDataSyncResource { + const parsed: IUserDataSyncResource = JSON.parse(handle); + return { + syncResource: parsed.syncResource, + local: URI.revive(parsed.local), + remote: URI.revive(parsed.remote), + merged: URI.revive(parsed.merged), + accepted: URI.revive(parsed.accepted), + localChange: parsed.localChange, + remoteChange: parsed.remoteChange, + mergeState: parsed.mergeState, + }; + } + + private registerActions(): void { + const that = this; + + /* accept remote change */ + this._register(registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.actions.sync.acceptRemote`, + title: localize('workbench.actions.sync.acceptRemote', "Accept Remote"), + icon: Codicon.cloudDownload, + menu: { + id: MenuId.ViewItemContext, + when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', SYNC_MERGES_VIEW_ID), ContextKeyExpr.equals('viewItem', 'sync-resource-preview')), + group: 'inline', + order: 1, + }, + }); + } + async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { + return that.acceptRemote(that.toUserDataSyncResourceGroup(handle.$treeItemHandle)); + } + })); + + /* accept local change */ + this._register(registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.actions.sync.acceptLocal`, + title: localize('workbench.actions.sync.acceptLocal', "Accept Local"), + icon: Codicon.cloudUpload, + menu: { + id: MenuId.ViewItemContext, + when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', SYNC_MERGES_VIEW_ID), ContextKeyExpr.equals('viewItem', 'sync-resource-preview')), + group: 'inline', + order: 2, + }, + }); + } + async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { + return that.acceptLocal(that.toUserDataSyncResourceGroup(handle.$treeItemHandle)); + } + })); + + /* merge */ + this._register(registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.actions.sync.merge`, + title: localize('workbench.actions.sync.merge', "Merge"), + icon: Codicon.merge, + menu: { + id: MenuId.ViewItemContext, + when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', SYNC_MERGES_VIEW_ID), ContextKeyExpr.equals('viewItem', 'sync-resource-preview')), + group: 'inline', + order: 3, + }, + }); + } + async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { + return that.mergeResource(that.toUserDataSyncResourceGroup(handle.$treeItemHandle)); + } + })); + + /* discard */ + this._register(registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.actions.sync.undo`, + title: localize('workbench.actions.sync.discard', "Discard"), + icon: Codicon.discard, + menu: { + id: MenuId.ViewItemContext, + when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', SYNC_MERGES_VIEW_ID), ContextKeyExpr.or(ContextKeyExpr.equals('viewItem', 'sync-resource-accepted'), ContextKeyExpr.equals('viewItem', 'sync-resource-conflict'))), + group: 'inline', + order: 3, + }, + }); + } + async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { + return that.discardResource(that.toUserDataSyncResourceGroup(handle.$treeItemHandle)); + } + })); + + this._register(registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.actions.sync.showChanges`, + title: localize({ key: 'workbench.actions.sync.showChanges', comment: ['This is an action title to show the changes between local and remote version of resources'] }, "Open Changes"), + }); + } + async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { + const previewResource: IUserDataSyncResource = that.toUserDataSyncResourceGroup(handle.$treeItemHandle); + return that.open(previewResource); + } + })); + } + + private async acceptLocal(userDataSyncResource: IUserDataSyncResource): Promise { + await this.withProgress(async () => { + await this.userDataSyncPreview.accept(userDataSyncResource.syncResource, userDataSyncResource.local); + }); + await this.reopen(userDataSyncResource); + } + + private async acceptRemote(userDataSyncResource: IUserDataSyncResource): Promise { + await this.withProgress(async () => { + await this.userDataSyncPreview.accept(userDataSyncResource.syncResource, userDataSyncResource.remote); + }); + await this.reopen(userDataSyncResource); + } + + private async mergeResource(previewResource: IUserDataSyncResource): Promise { + await this.withProgress(() => this.userDataSyncPreview.merge(previewResource.merged)); + previewResource = this.userDataSyncPreview.resources.find(({ local }) => isEqual(local, previewResource.local))!; + await this.reopen(previewResource); + if (previewResource.mergeState === MergeState.Conflict) { + await this.dialogService.show(Severity.Warning, localize('conflicts detected', "Conflicts Detected"), [], { + detail: localize('resolve', "Unable to merge due to conflicts. Please resolve them to continue.") + }); + } + } + + private async discardResource(previewResource: IUserDataSyncResource): Promise { + this.close(previewResource); + return this.withProgress(() => this.userDataSyncPreview.discard(previewResource.merged)); + } + + private async apply(): Promise { + this.closeAll(); + this.syncButton.label = localize('turning on', "Turning on..."); + this.syncButton.enabled = false; + this.cancelButton.enabled = false; + try { + await this.withProgress(async () => this.userDataSyncPreview.apply()); + } catch (error) { + this.syncButton.enabled = false; + this.cancelButton.enabled = true; + } + } + + private async cancel(): Promise { + for (const resource of this.userDataSyncPreview.resources) { + this.close(resource); + } + await this.userDataSyncPreview.cancel(); + } + + private async open(previewResource: IUserDataSyncResource): Promise { + if (previewResource.mergeState === MergeState.Accepted) { + if (previewResource.localChange !== Change.Deleted && previewResource.remoteChange !== Change.Deleted) { + // Do not open deleted preview + await this.editorService.openEditor({ resource: previewResource.accepted, label: localize('preview', "{0} (Preview)", basename(previewResource.accepted)) }); + } + } else { + const leftResource = previewResource.remote; + const rightResource = previewResource.mergeState === MergeState.Conflict ? previewResource.merged : previewResource.local; + const leftResourceName = localize({ key: 'leftResourceName', comment: ['remote as in file in cloud'] }, "{0} (Remote)", basename(leftResource)); + const rightResourceName = previewResource.mergeState === MergeState.Conflict ? localize('merges', "{0} (Merges)", basename(rightResource)) + : localize({ key: 'rightResourceName', comment: ['local as in file in disk'] }, "{0} (Local)", basename(rightResource)); + await this.editorService.openEditor({ + leftResource, + rightResource, + label: localize('sideBySideLabels', "{0} ↔ {1}", leftResourceName, rightResourceName), + options: { + preserveFocus: true, + revealIfVisible: true, + }, + }); + } + } + + private async reopen(previewResource: IUserDataSyncResource): Promise { + this.close(previewResource); + const resource = this.userDataSyncPreview.resources.find(({ local }) => isEqual(local, previewResource.local)); + if (resource) { + // select the resource + await this.treeView.refresh(); + this.treeView.setSelection([this.treeItems.get(JSON.stringify(resource))!]); + + await this.open(resource); + } + } + + private close(previewResource: IUserDataSyncResource): void { + for (const input of this.editorService.editors) { + if (input instanceof DiffEditorInput) { + // Close all diff editors + if (isEqual(previewResource.remote, input.secondary.resource)) { + input.dispose(); + } + } + // Close all preview editors + else if (isEqual(previewResource.accepted, input.resource)) { + input.dispose(); + } + } + } + + private closeConflictEditors() { + for (const previewResource of this.userDataSyncPreview.resources) { + if (previewResource.mergeState !== MergeState.Conflict) { + for (const input of this.editorService.editors) { + if (input instanceof DiffEditorInput) { + if (isEqual(previewResource.remote, input.secondary.resource) && isEqual(previewResource.merged, input.primary.resource)) { + input.dispose(); + } + } + } + } + } + } + + private closeAll() { + for (const previewResource of this.userDataSyncPreview.resources) { + this.close(previewResource); + } + } + + private withProgress(task: () => Promise): Promise { + return this.progressService.withProgress({ location: SYNC_MERGES_VIEW_ID, delay: 500 }, task); + } + +} + +class UserDataSyncResourcesDecorationProvider extends Disposable implements IDecorationsProvider { + + readonly label: string = localize('label', "UserDataSyncResources"); + + private readonly _onDidChange = this._register(new Emitter()); + readonly onDidChange = this._onDidChange.event; + + constructor(private readonly userDataSyncPreview: IUserDataSyncPreview) { + super(); + this._register(userDataSyncPreview.onDidChangeResources(c => this._onDidChange.fire(c.map(({ remote }) => remote)))); + } + + provideDecorations(resource: URI): IDecorationData | undefined { + const userDataSyncResource = this.userDataSyncPreview.resources.find(c => isEqual(c.remote, resource)); + if (userDataSyncResource) { + switch (userDataSyncResource.mergeState) { + case MergeState.Conflict: + return { letter: '⚠', color: listWarningForeground, tooltip: localize('conflict', "Conflicts Detected") }; + case MergeState.Accepted: + return { letter: '✓', color: listDeemphasizedForeground, tooltip: localize('accepted', "Accepted") }; + } + } + return undefined; + } +} + +type AcceptChangesClassification = { + source: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; + action: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; +}; + +class AcceptChangesContribution extends Disposable implements IEditorContribution { + + static get(editor: ICodeEditor): AcceptChangesContribution { + return editor.getContribution(AcceptChangesContribution.ID); + } + + public static readonly ID = 'editor.contrib.acceptChangesButton'; + + private acceptChangesButton: FloatingClickWidget | undefined; + + constructor( + private editor: ICodeEditor, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IUserDataSyncWorkbenchService private readonly userDataSyncWorkbenchService: IUserDataSyncWorkbenchService, + ) { + super(); + + this.update(); + this.registerListeners(); + } + + private registerListeners(): void { + this._register(this.editor.onDidChangeModel(() => this.update())); + this._register(this.userDataSyncService.onDidChangeConflicts(() => this.update())); + this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('diffEditor.renderSideBySide'))(() => this.update())); + } + + private update(): void { + if (!this.shouldShowButton(this.editor)) { + this.disposeAcceptChangesWidgetRenderer(); + return; + } + + this.createAcceptChangesWidgetRenderer(); + } + + private shouldShowButton(editor: ICodeEditor): boolean { + const model = editor.getModel(); + if (!model) { + return false; // we need a model + } + + const userDataSyncResource = this.getUserDataSyncResource(model.uri); + if (!userDataSyncResource) { + return false; + } + + return true; + } + + private createAcceptChangesWidgetRenderer(): void { + if (!this.acceptChangesButton) { + const resource = this.editor.getModel()!.uri; + const userDataSyncResource = this.getUserDataSyncResource(resource)!; + + const isRemoteResource = isEqual(userDataSyncResource.remote, resource); + const isLocalResource = isEqual(userDataSyncResource.local, resource); + const label = isRemoteResource ? localize('accept remote', "Accept Remote") + : isLocalResource ? localize('accept local', "Accept Local") + : localize('accept merges', "Accept Merges"); + + this.acceptChangesButton = this.instantiationService.createInstance(FloatingClickWidget, this.editor, label, null); + this._register(this.acceptChangesButton.onClick(async () => { + const model = this.editor.getModel(); + if (model) { + this.telemetryService.publicLog2<{ source: string, action: string }, AcceptChangesClassification>('sync/acceptChanges', { source: userDataSyncResource.syncResource, action: isRemoteResource ? 'acceptRemote' : isLocalResource ? 'acceptLocal' : 'acceptMerges' }); + await this.userDataSyncWorkbenchService.userDataSyncPreview.accept(userDataSyncResource.syncResource, model.uri, model.getValue()); + } + })); + + this.acceptChangesButton.render(); + } + } + + private getUserDataSyncResource(resource: URI): IUserDataSyncResource | undefined { + return this.userDataSyncWorkbenchService.userDataSyncPreview.resources.find(r => isEqual(resource, r.local) || isEqual(resource, r.remote) || isEqual(resource, r.merged)); + } + + private disposeAcceptChangesWidgetRenderer(): void { + dispose(this.acceptChangesButton); + this.acceptChangesButton = undefined; + } + + dispose(): void { + this.disposeAcceptChangesWidgetRenderer(); + super.dispose(); + } +} + +registerEditorContribution(AcceptChangesContribution.ID, AcceptChangesContribution); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger.ts index efb35119be5..36caf6f3db4 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger.ts @@ -12,7 +12,6 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { IEditorInput } from 'vs/workbench/common/editor'; import { IViewsService } from 'vs/workbench/common/views'; -import { VIEW_CONTAINER_ID } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncView'; export class UserDataSyncTrigger extends Disposable { @@ -29,7 +28,7 @@ export class UserDataSyncTrigger extends Disposable { Event.filter( Event.any( Event.map(editorService.onDidActiveEditorChange, () => this.getUserDataEditorInputSource(editorService.activeEditor)), - Event.map(Event.filter(viewsService.onDidChangeViewContainerVisibility, e => [VIEWLET_ID, VIEW_CONTAINER_ID].includes(e.id) && e.visible), e => e.id) + Event.map(Event.filter(viewsService.onDidChangeViewContainerVisibility, e => e.id === VIEWLET_ID && e.visible), e => e.id) ), source => source !== undefined)(source => this._onDidTriggerSync.fire(source!))); } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncView.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncView.ts deleted file mode 100644 index 6b027b310ff..00000000000 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncView.ts +++ /dev/null @@ -1,225 +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 { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { IViewsRegistry, Extensions, ITreeViewDescriptor, ITreeViewDataProvider, ITreeItem, TreeItemCollapsibleState, IViewsService, TreeViewItemHandleArg, IViewContainersRegistry, ViewContainerLocation, ViewContainer, IViewDescriptorService } from 'vs/workbench/common/views'; -import { localize } from 'vs/nls'; -import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { TreeViewPane, TreeView } from 'vs/workbench/browser/parts/views/treeView'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { ALL_SYNC_RESOURCES, SyncResource, IUserDataSyncService, ISyncResourceHandle, CONTEXT_SYNC_STATE, SyncStatus } from 'vs/platform/userDataSync/common/userDataSync'; -import { registerAction2, Action2, MenuId } from 'vs/platform/actions/common/actions'; -import { IContextKeyService, RawContextKey, ContextKeyExpr, ContextKeyEqualsExpr, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { URI } from 'vs/base/common/uri'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { FolderThemeIcon } from 'vs/platform/theme/common/themeService'; -import { fromNow } from 'vs/base/common/date'; -import { pad, uppercaseFirstLetter } from 'vs/base/common/strings'; -import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; -import { Codicon } from 'vs/base/common/codicons'; -import { CONTEXT_ACCOUNT_STATE } from 'vs/workbench/contrib/userDataSync/browser/userDataSync'; -import { AccountStatus } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncAccount'; - -export const VIEW_CONTAINER_ID = 'workbench.view.sync'; -const CONTEXT_ENABLE_VIEWS = new RawContextKey(`showUserDataSyncViews`, false); - -export class UserDataSyncViewContribution implements IWorkbenchContribution { - - private readonly viewsEnablementContext: IContextKey; - - constructor( - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, - ) { - const container = this.registerSyncViewContainer(); - this.viewsEnablementContext = CONTEXT_ENABLE_VIEWS.bindTo(this.contextKeyService); - this.registerView(container, true); - this.registerView(container, false); - } - - private registerSyncViewContainer(): ViewContainer { - return Registry.as(Extensions.ViewContainersRegistry).registerViewContainer( - { - id: VIEW_CONTAINER_ID, - name: localize('sync preferences', "Preferences Sync"), - ctorDescriptor: new SyncDescriptor( - ViewPaneContainer, - [VIEW_CONTAINER_ID, { mergeViewWithContainerWhenSingleView: true }] - ), - icon: Codicon.sync.classNames, - hideIfEmpty: true, - }, ViewContainerLocation.Sidebar); - } - - private registerView(container: ViewContainer, remote: boolean): void { - const that = this; - const id = `workbench.views.sync.${remote ? 'remote' : 'local'}DataView`; - const name = remote ? localize('remote title', "Remote Data") : localize('local title', "Local Backup"); - const treeView = this.instantiationService.createInstance(TreeView, id, name); - treeView.showCollapseAllAction = true; - treeView.showRefreshAction = true; - const disposable = treeView.onDidChangeVisibility(visible => { - if (visible && !treeView.dataProvider) { - disposable.dispose(); - treeView.dataProvider = new UserDataSyncHistoryViewDataProvider(remote, this.userDataSyncService); - } - }); - const viewsRegistry = Registry.as(Extensions.ViewsRegistry); - viewsRegistry.registerViews([{ - id, - name, - ctorDescriptor: new SyncDescriptor(TreeViewPane), - when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_ACCOUNT_STATE.isEqualTo(AccountStatus.Available), CONTEXT_ENABLE_VIEWS), - canToggleVisibility: true, - canMoveView: true, - treeView, - collapsed: false, - order: 100, - }], container); - - registerAction2(class extends Action2 { - constructor() { - super({ - id: `workbench.actions.showSync${remote ? 'Remote' : 'Local'}DataView`, - title: remote ? - { value: localize('workbench.action.showSyncRemoteBackup', "Show Remote Data"), original: `Show Remote Data` } - : { value: localize('workbench.action.showSyncLocalBackup', "Show Local Backup"), original: `Show Local Backup` }, - category: { value: localize('sync preferences', "Preferences Sync"), original: `Preferences Sync` }, - menu: { - id: MenuId.CommandPalette, - when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_ACCOUNT_STATE.isEqualTo(AccountStatus.Available)), - }, - }); - } - async run(accessor: ServicesAccessor): Promise { - const viewDescriptorService = accessor.get(IViewDescriptorService); - const viewsService = accessor.get(IViewsService); - that.viewsEnablementContext.set(true); - const viewContainer = viewDescriptorService.getViewContainerByViewId(id); - if (viewContainer) { - const model = viewDescriptorService.getViewContainerModel(viewContainer); - if (model.activeViewDescriptors.some(viewDescriptor => viewDescriptor.id === id)) { - viewsService.openView(id, true); - } else { - const disposable = model.onDidChangeActiveViewDescriptors(e => { - if (e.added.some(viewDescriptor => viewDescriptor.id === id)) { - disposable.dispose(); - viewsService.openView(id, true); - } - }); - } - } - } - }); - - this.registerActions(id); - } - - private registerActions(viewId: string) { - registerAction2(class extends Action2 { - constructor() { - super({ - id: `workbench.actions.sync.resolveResource`, - title: localize('workbench.actions.sync.resolveResourceRef', "Show raw JSON sync data"), - menu: { - id: MenuId.ViewItemContext, - when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', viewId), ContextKeyExpr.regex('viewItem', /sync-resource-.*/i)) - }, - }); - } - async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { - const editorService = accessor.get(IEditorService); - await editorService.openEditor({ resource: URI.parse(handle.$treeItemHandle) }); - } - }); - - registerAction2(class extends Action2 { - constructor() { - super({ - id: `workbench.actions.sync.commpareWithLocal`, - title: localize('workbench.actions.sync.commpareWithLocal', "Open Changes"), - }); - } - async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { - const editorService = accessor.get(IEditorService); - const { resource, comparableResource } = <{ resource: string, comparableResource?: string }>JSON.parse(handle.$treeItemHandle); - if (comparableResource) { - await editorService.openEditor({ - leftResource: URI.parse(resource), - rightResource: URI.parse(comparableResource), - options: { - preserveFocus: true, - revealIfVisible: true, - }, - }); - } else { - await editorService.openEditor({ resource: URI.parse(resource) }); - } - } - }); - } - -} - -interface SyncResourceTreeItem extends ITreeItem { - resource: SyncResource; - resourceHandle: ISyncResourceHandle; -} - -class UserDataSyncHistoryViewDataProvider implements ITreeViewDataProvider { - - constructor(private readonly remote: boolean, private userDataSyncService: IUserDataSyncService) { } - - async getChildren(element?: ITreeItem): Promise { - if (!element) { - return ALL_SYNC_RESOURCES.map(resourceKey => ({ - handle: resourceKey, - collapsibleState: TreeItemCollapsibleState.Collapsed, - label: { label: uppercaseFirstLetter(resourceKey) }, - themeIcon: FolderThemeIcon, - })); - } - const resourceKey = ALL_SYNC_RESOURCES.filter(key => key === element.handle)[0] as SyncResource; - if (resourceKey) { - const refHandles = this.remote ? await this.userDataSyncService.getRemoteSyncResourceHandles(resourceKey) : await this.userDataSyncService.getLocalSyncResourceHandles(resourceKey); - return refHandles.map(({ uri, created }) => { - return { - handle: uri.toString(), - collapsibleState: TreeItemCollapsibleState.Collapsed, - label: { label: label(new Date(created)) }, - description: fromNow(created, true), - resourceUri: uri, - resource: resourceKey, - resourceHandle: { uri, created }, - contextValue: `sync-resource-${resourceKey}` - }; - }); - } - if ((element).resourceHandle) { - const associatedResources = await this.userDataSyncService.getAssociatedResources((element).resource, (element).resourceHandle); - return associatedResources.map(({ resource, comparableResource }) => { - const handle = JSON.stringify({ resource: resource.toString(), comparableResource: comparableResource?.toString() }); - return { - handle, - collapsibleState: TreeItemCollapsibleState.None, - resourceUri: resource, - command: { id: `workbench.actions.sync.commpareWithLocal`, title: '', arguments: [{ $treeViewId: '', $treeItemHandle: handle }] }, - contextValue: `sync-associatedResource-${(element).resource}` - }; - }); - } - return []; - } -} - -function label(date: Date): string { - return date.toLocaleDateString() + - ' ' + pad(date.getHours(), 2) + - ':' + pad(date.getMinutes(), 2) + - ':' + pad(date.getSeconds(), 2); -} - diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts new file mode 100644 index 00000000000..8e43f86bde3 --- /dev/null +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts @@ -0,0 +1,539 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Registry } from 'vs/platform/registry/common/platform'; +import { IViewsRegistry, Extensions, ITreeViewDescriptor, ITreeViewDataProvider, ITreeItem, TreeItemCollapsibleState, TreeViewItemHandleArg, ViewContainer, IViewDescriptorService } from 'vs/workbench/common/views'; +import { localize } from 'vs/nls'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { TreeViewPane } from 'vs/workbench/browser/parts/views/treeView'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { ALL_SYNC_RESOURCES, SyncResource, IUserDataSyncService, ISyncResourceHandle as IResourceHandle, SyncStatus, IUserDataSyncResourceEnablementService, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode } from 'vs/platform/userDataSync/common/userDataSync'; +import { registerAction2, Action2, MenuId } from 'vs/platform/actions/common/actions'; +import { ContextKeyExpr, ContextKeyEqualsExpr } from 'vs/platform/contextkey/common/contextkey'; +import { URI } from 'vs/base/common/uri'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { FolderThemeIcon, IThemeService } from 'vs/platform/theme/common/themeService'; +import { fromNow } from 'vs/base/common/date'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { Event } from 'vs/base/common/event'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { Codicon } from 'vs/base/common/codicons'; +import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IAction, Action } from 'vs/base/common/actions'; +import { IUserDataSyncWorkbenchService, CONTEXT_SYNC_STATE, getSyncAreaLabel, CONTEXT_ACCOUNT_STATE, AccountStatus, CONTEXT_ENABLE_ACTIVITY_VIEWS, SHOW_SYNC_LOG_COMMAND_ID, CONFIGURE_SYNC_COMMAND_ID, SYNC_MERGES_VIEW_ID, CONTEXT_ENABLE_SYNC_MERGES_VIEW, SYNC_TITLE } from 'vs/workbench/services/userDataSync/common/userDataSync'; +import { IUserDataSyncMachinesService, IUserDataSyncMachine } from 'vs/platform/userDataSync/common/userDataSyncMachines'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { TreeView } from 'vs/workbench/contrib/views/browser/treeView'; +import { flatten } from 'vs/base/common/arrays'; +import { UserDataSyncMergesViewPane } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView'; + +export class UserDataSyncViewPaneContainer extends ViewPaneContainer { + + constructor( + containerId: string, + @IUserDataSyncWorkbenchService private readonly userDataSyncWorkbenchService: IUserDataSyncWorkbenchService, + @ICommandService private readonly commandService: ICommandService, + @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, + @ITelemetryService telemetryService: ITelemetryService, + @IInstantiationService instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IConfigurationService configurationService: IConfigurationService, + @IStorageService storageService: IStorageService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IContextMenuService contextMenuService: IContextMenuService, + @IExtensionService extensionService: IExtensionService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + ) { + super(containerId, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService); + } + + getActions(): IAction[] { + return [ + new Action(SHOW_SYNC_LOG_COMMAND_ID, localize('showLog', "Show Log"), Codicon.output.classNames, true, async () => this.commandService.executeCommand(SHOW_SYNC_LOG_COMMAND_ID)), + new Action(CONFIGURE_SYNC_COMMAND_ID, localize('configure', "Configure..."), Codicon.settingsGear.classNames, true, async () => this.commandService.executeCommand(CONFIGURE_SYNC_COMMAND_ID)), + ]; + } + + getSecondaryActions(): IAction[] { + return [ + new Action('workbench.actions.syncData.reset', localize('workbench.actions.syncData.reset', "Clear Data in Cloud..."), undefined, true, () => this.userDataSyncWorkbenchService.resetSyncedData()), + ]; + } + +} + +export class UserDataSyncDataViews extends Disposable { + + constructor( + container: ViewContainer, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IUserDataAutoSyncService private readonly userDataAutoSyncService: IUserDataAutoSyncService, + @IUserDataSyncResourceEnablementService private readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, + ) { + super(); + this.registerViews(container); + } + + private registerViews(container: ViewContainer): void { + this.registerMergesView(container); + + this.registerActivityView(container, true); + this.registerMachinesView(container); + + this.registerActivityView(container, false); + } + + private registerMergesView(container: ViewContainer): void { + const viewsRegistry = Registry.as(Extensions.ViewsRegistry); + const viewName = localize('merges', "Merges"); + viewsRegistry.registerViews([{ + id: SYNC_MERGES_VIEW_ID, + name: viewName, + ctorDescriptor: new SyncDescriptor(UserDataSyncMergesViewPane), + when: CONTEXT_ENABLE_SYNC_MERGES_VIEW, + canToggleVisibility: false, + canMoveView: false, + treeView: this.instantiationService.createInstance(TreeView, SYNC_MERGES_VIEW_ID, viewName), + collapsed: false, + order: 100, + }], container); + } + + private registerMachinesView(container: ViewContainer): void { + const id = `workbench.views.sync.machines`; + const name = localize('synced machines', "Synced Machines"); + const treeView = this.instantiationService.createInstance(TreeView, id, name); + const dataProvider = this.instantiationService.createInstance(UserDataSyncMachinesViewDataProvider, treeView); + treeView.showRefreshAction = true; + const disposable = treeView.onDidChangeVisibility(visible => { + if (visible && !treeView.dataProvider) { + disposable.dispose(); + treeView.dataProvider = dataProvider; + } + }); + this._register(Event.any(this.userDataSyncResourceEnablementService.onDidChangeResourceEnablement, this.userDataAutoSyncService.onDidChangeEnablement)(() => treeView.refresh())); + const viewsRegistry = Registry.as(Extensions.ViewsRegistry); + viewsRegistry.registerViews([{ + id, + name, + ctorDescriptor: new SyncDescriptor(TreeViewPane), + when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_ACCOUNT_STATE.isEqualTo(AccountStatus.Available), CONTEXT_ENABLE_ACTIVITY_VIEWS), + canToggleVisibility: true, + canMoveView: false, + treeView, + collapsed: false, + order: 300, + }], container); + + registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.actions.sync.editMachineName`, + title: localize('workbench.actions.sync.editMachineName', "Edit Name"), + icon: Codicon.edit, + menu: { + id: MenuId.ViewItemContext, + when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', id)), + group: 'inline', + }, + }); + } + async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { + const changed = await dataProvider.rename(handle.$treeItemHandle); + if (changed) { + await treeView.refresh(); + } + } + }); + + registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.actions.sync.turnOffSyncOnMachine`, + title: localize('workbench.actions.sync.turnOffSyncOnMachine', "Turn off Settings Sync"), + menu: { + id: MenuId.ViewItemContext, + when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', id), ContextKeyEqualsExpr.create('viewItem', 'sync-machine')), + }, + }); + } + async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { + if (await dataProvider.disable(handle.$treeItemHandle)) { + await treeView.refresh(); + } + } + }); + + } + + private registerActivityView(container: ViewContainer, remote: boolean): void { + const id = `workbench.views.sync.${remote ? 'remote' : 'local'}Activity`; + const name = remote ? localize('remote sync activity title', "Sync Activity (Remote)") : localize('local sync activity title', "Sync Activity (Local)"); + const treeView = this.instantiationService.createInstance(TreeView, id, name); + treeView.showCollapseAllAction = true; + treeView.showRefreshAction = true; + const disposable = treeView.onDidChangeVisibility(visible => { + if (visible && !treeView.dataProvider) { + disposable.dispose(); + treeView.dataProvider = remote ? this.instantiationService.createInstance(RemoteUserDataSyncActivityViewDataProvider) + : this.instantiationService.createInstance(LocalUserDataSyncActivityViewDataProvider); + } + }); + this._register(Event.any(this.userDataSyncResourceEnablementService.onDidChangeResourceEnablement, this.userDataAutoSyncService.onDidChangeEnablement)(() => treeView.refresh())); + const viewsRegistry = Registry.as(Extensions.ViewsRegistry); + viewsRegistry.registerViews([{ + id, + name, + ctorDescriptor: new SyncDescriptor(TreeViewPane), + when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_ACCOUNT_STATE.isEqualTo(AccountStatus.Available), CONTEXT_ENABLE_ACTIVITY_VIEWS), + canToggleVisibility: true, + canMoveView: false, + treeView, + collapsed: false, + order: remote ? 200 : 400, + hideByDefault: !remote, + }], container); + + this.registerDataViewActions(id); + } + + private registerDataViewActions(viewId: string) { + registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.actions.sync.resolveResource`, + title: localize('workbench.actions.sync.resolveResourceRef', "Show raw JSON sync data"), + menu: { + id: MenuId.ViewItemContext, + when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', viewId), ContextKeyExpr.regex('viewItem', /sync-resource-.*/i)) + }, + }); + } + async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { + const { resource } = <{ resource: string }>JSON.parse(handle.$treeItemHandle); + const editorService = accessor.get(IEditorService); + await editorService.openEditor({ resource: URI.parse(resource) }); + } + }); + + registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.actions.sync.replaceCurrent`, + title: localize('workbench.actions.sync.replaceCurrent', "Restore"), + icon: { id: 'codicon/discard' }, + menu: { + id: MenuId.ViewItemContext, + when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', viewId), ContextKeyExpr.regex('viewItem', /sync-resource-.*/i)), + group: 'inline', + }, + }); + } + async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { + const dialogService = accessor.get(IDialogService); + const userDataSyncService = accessor.get(IUserDataSyncService); + const { resource, syncResource } = <{ resource: string, syncResource: SyncResource }>JSON.parse(handle.$treeItemHandle); + const result = await dialogService.confirm({ + message: localize({ key: 'confirm replace', comment: ['A confirmation message to replace current user data (settings, extensions, keybindings, snippets) with selected version'] }, "Would you like to replace your current {0} with selected?", getSyncAreaLabel(syncResource)), + type: 'info', + title: SYNC_TITLE + }); + if (result.confirmed) { + return userDataSyncService.replace(URI.parse(resource)); + } + } + }); + + registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.actions.sync.compareWithLocal`, + title: localize({ key: 'workbench.actions.sync.compareWithLocal', comment: ['This is an action title to show the changes between local and remote version of resources'] }, "Open Changes"), + }); + } + async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { + const editorService = accessor.get(IEditorService); + const { resource, comparableResource } = <{ resource: string, comparableResource?: string }>JSON.parse(handle.$treeItemHandle); + if (comparableResource) { + await editorService.openEditor({ + leftResource: URI.parse(resource), + rightResource: URI.parse(comparableResource), + options: { + preserveFocus: true, + revealIfVisible: true, + }, + }); + } else { + await editorService.openEditor({ resource: URI.parse(resource) }); + } + } + }); + } + +} + +interface ISyncResourceHandle extends IResourceHandle { + syncResource: SyncResource +} + +interface SyncResourceHandleTreeItem extends ITreeItem { + syncResourceHandle: ISyncResourceHandle; +} + +abstract class UserDataSyncActivityViewDataProvider implements ITreeViewDataProvider { + + private syncResourceHandlesPromise: Promise | undefined; + + constructor( + @IUserDataSyncService protected readonly userDataSyncService: IUserDataSyncService, + @IUserDataAutoSyncService protected readonly userDataAutoSyncService: IUserDataAutoSyncService, + @IUserDataSyncWorkbenchService private readonly userDataSyncWorkbenchService: IUserDataSyncWorkbenchService, + @INotificationService private readonly notificationService: INotificationService, + ) { } + + async getChildren(element?: ITreeItem): Promise { + try { + if (!element) { + return await this.getRoots(); + } + if ((element).syncResourceHandle) { + return await this.getChildrenForSyncResourceTreeItem(element); + } + return []; + } catch (error) { + if (!(error instanceof UserDataSyncError)) { + error = UserDataSyncError.toUserDataSyncError(error); + } + if (error instanceof UserDataSyncError && error.code === UserDataSyncErrorCode.IncompatibleRemoteContent) { + this.notificationService.notify({ + severity: Severity.Error, + message: error.message, + actions: { + primary: [ + new Action('reset', localize('reset', "Reset Synced Data"), undefined, true, () => this.userDataSyncWorkbenchService.resetSyncedData()), + ] + } + }); + } else { + this.notificationService.error(error); + } + throw error; + } + } + + private async getRoots(): Promise { + this.syncResourceHandlesPromise = undefined; + + const syncResourceHandles = await this.getSyncResourceHandles(); + + return syncResourceHandles.map(syncResourceHandle => { + const handle = JSON.stringify({ resource: syncResourceHandle.uri.toString(), syncResource: syncResourceHandle.syncResource }); + return { + handle, + collapsibleState: TreeItemCollapsibleState.Collapsed, + label: { label: getSyncAreaLabel(syncResourceHandle.syncResource) }, + description: fromNow(syncResourceHandle.created, true), + themeIcon: FolderThemeIcon, + syncResourceHandle, + contextValue: `sync-resource-${syncResourceHandle.syncResource}` + }; + }); + } + + protected async getChildrenForSyncResourceTreeItem(element: SyncResourceHandleTreeItem): Promise { + const associatedResources = await this.userDataSyncService.getAssociatedResources((element).syncResourceHandle.syncResource, (element).syncResourceHandle); + return associatedResources.map(({ resource, comparableResource }) => { + const handle = JSON.stringify({ resource: resource.toString(), comparableResource: comparableResource?.toString() }); + return { + handle, + collapsibleState: TreeItemCollapsibleState.None, + resourceUri: resource, + command: { id: `workbench.actions.sync.compareWithLocal`, title: '', arguments: [{ $treeViewId: '', $treeItemHandle: handle }] }, + contextValue: `sync-associatedResource-${(element).syncResourceHandle.syncResource}` + }; + }); + } + + private getSyncResourceHandles(): Promise { + if (this.syncResourceHandlesPromise === undefined) { + this.syncResourceHandlesPromise = Promise.all(ALL_SYNC_RESOURCES.map(async syncResource => { + const resourceHandles = await this.getResourceHandles(syncResource); + return resourceHandles.map(resourceHandle => ({ ...resourceHandle, syncResource })); + })).then(result => flatten(result).sort((a, b) => b.created - a.created)); + } + return this.syncResourceHandlesPromise; + } + + protected abstract getResourceHandles(syncResource: SyncResource): Promise; +} + +class LocalUserDataSyncActivityViewDataProvider extends UserDataSyncActivityViewDataProvider { + + protected getResourceHandles(syncResource: SyncResource): Promise { + return this.userDataSyncService.getLocalSyncResourceHandles(syncResource); + } +} + +class RemoteUserDataSyncActivityViewDataProvider extends UserDataSyncActivityViewDataProvider { + + private machinesPromise: Promise | undefined; + + constructor( + @IUserDataSyncService userDataSyncService: IUserDataSyncService, + @IUserDataAutoSyncService userDataAutoSyncService: IUserDataAutoSyncService, + @IUserDataSyncMachinesService private readonly userDataSyncMachinesService: IUserDataSyncMachinesService, + @IUserDataSyncWorkbenchService userDataSyncWorkbenchService: IUserDataSyncWorkbenchService, + @INotificationService notificationService: INotificationService, + ) { + super(userDataSyncService, userDataAutoSyncService, userDataSyncWorkbenchService, notificationService); + } + + async getChildren(element?: ITreeItem): Promise { + if (!element) { + this.machinesPromise = undefined; + } + return super.getChildren(element); + } + + private getMachines(): Promise { + if (this.machinesPromise === undefined) { + this.machinesPromise = this.userDataSyncMachinesService.getMachines(); + } + return this.machinesPromise; + } + + protected getResourceHandles(syncResource: SyncResource): Promise { + return this.userDataSyncService.getRemoteSyncResourceHandles(syncResource); + } + + protected async getChildrenForSyncResourceTreeItem(element: SyncResourceHandleTreeItem): Promise { + const children = await super.getChildrenForSyncResourceTreeItem(element); + const machineId = await this.userDataSyncService.getMachineId(element.syncResourceHandle.syncResource, element.syncResourceHandle); + if (machineId) { + const machines = await this.getMachines(); + const machine = machines.find(({ id }) => id === machineId); + children[0].description = machine?.isCurrent ? localize({ key: 'current', comment: ['Represents current machine'] }, "Current") : machine?.name; + } + return children; + } +} + +class UserDataSyncMachinesViewDataProvider implements ITreeViewDataProvider { + + private machinesPromise: Promise | undefined; + + constructor( + private readonly treeView: TreeView, + @IUserDataSyncMachinesService private readonly userDataSyncMachinesService: IUserDataSyncMachinesService, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @INotificationService private readonly notificationService: INotificationService, + @IDialogService private readonly dialogService: IDialogService, + @IUserDataSyncWorkbenchService private readonly userDataSyncWorkbenchService: IUserDataSyncWorkbenchService, + ) { + } + + async getChildren(element?: ITreeItem): Promise { + if (!element) { + this.machinesPromise = undefined; + } + try { + let machines = await this.getMachines(); + machines = machines.filter(m => !m.disabled).sort((m1, m2) => m1.isCurrent ? -1 : 1); + this.treeView.message = machines.length ? undefined : localize('no machines', "No Machines"); + return machines.map(({ id, name, isCurrent }) => ({ + handle: id, + collapsibleState: TreeItemCollapsibleState.None, + label: { label: name }, + description: isCurrent ? localize({ key: 'current', comment: ['Current machine'] }, "Current") : undefined, + themeIcon: Codicon.vm, + contextValue: 'sync-machine' + })); + } catch (error) { + this.notificationService.error(error); + return []; + } + } + + private getMachines(): Promise { + if (this.machinesPromise === undefined) { + this.machinesPromise = this.userDataSyncMachinesService.getMachines(); + } + return this.machinesPromise; + } + + async disable(machineId: string): Promise { + const machines = await this.getMachines(); + const machine = machines.find(({ id }) => id === machineId); + if (!machine) { + throw new Error(localize('not found', "machine not found with id: {0}", machineId)); + } + + const result = await this.dialogService.confirm({ + type: 'info', + message: localize('turn off sync on machine', "Are you sure you want to turn off sync on {0}?", machine.name), + primaryButton: localize('turn off', "Turn off"), + }); + + if (!result.confirmed) { + return false; + } + + if (machine.isCurrent) { + await this.userDataSyncWorkbenchService.turnoff(false); + } else { + await this.userDataSyncMachinesService.setEnablement(machineId, false); + } + + return true; + } + + async rename(machineId: string): Promise { + const disposableStore = new DisposableStore(); + const inputBox = disposableStore.add(this.quickInputService.createInputBox()); + inputBox.placeholder = localize('placeholder', "Enter the name of the machine"); + inputBox.busy = true; + inputBox.show(); + const machines = await this.getMachines(); + const machine = machines.find(({ id }) => id === machineId); + if (!machine) { + inputBox.hide(); + disposableStore.dispose(); + throw new Error(localize('not found', "machine not found with id: {0}", machineId)); + } + inputBox.busy = false; + inputBox.value = machine.name; + const validateMachineName = (machineName: string): string | null => { + machineName = machineName.trim(); + return machineName && !machines.some(m => m.id !== machineId && m.name === machineName) ? machineName : null; + }; + disposableStore.add(inputBox.onDidChangeValue(() => + inputBox.validationMessage = validateMachineName(inputBox.value) ? '' : localize('valid message', "Machine name should be unique and not empty"))); + return new Promise((c, e) => { + disposableStore.add(inputBox.onDidAccept(async () => { + const machineName = validateMachineName(inputBox.value); + disposableStore.dispose(); + if (machineName && machineName !== machine.name) { + try { + await this.userDataSyncMachinesService.renameMachine(machineId, machineName); + c(true); + } catch (error) { + e(error); + } + } else { + c(false); + } + })); + }); + } +} diff --git a/src/vs/workbench/contrib/userDataSync/electron-browser/userDataAutoSyncService.ts b/src/vs/workbench/contrib/userDataSync/electron-browser/userDataAutoSyncService.ts index c4836a46568..35fde76ad97 100644 --- a/src/vs/workbench/contrib/userDataSync/electron-browser/userDataAutoSyncService.ts +++ b/src/vs/workbench/contrib/userDataSync/electron-browser/userDataAutoSyncService.ts @@ -3,33 +3,45 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IUserDataAutoSyncService, UserDataSyncError } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataAutoSyncService, UserDataSyncError, IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; -import { Disposable } from 'vs/base/common/lifecycle'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event } from 'vs/base/common/event'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { UserDataSyncTrigger } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger'; +import { UserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -export class UserDataAutoSyncService extends Disposable implements IUserDataAutoSyncService { +export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService implements IUserDataAutoSyncService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly channel: IChannel; get onError(): Event { return Event.map(this.channel.listen('onError'), e => UserDataSyncError.toUserDataSyncError(e)); } constructor( + @IStorageService storageService: IStorageService, + @IEnvironmentService environmentService: IEnvironmentService, + @IUserDataSyncStoreManagementService userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, @IInstantiationService instantiationService: IInstantiationService, - @ISharedProcessService sharedProcessService: ISharedProcessService + @ISharedProcessService sharedProcessService: ISharedProcessService, ) { - super(); + super(storageService, environmentService, userDataSyncStoreManagementService); this.channel = sharedProcessService.getChannel('userDataAutoSync'); - this._register(instantiationService.createInstance(UserDataSyncTrigger).onDidTriggerSync(source => this.triggerAutoSync([source]))); + this._register(instantiationService.createInstance(UserDataSyncTrigger).onDidTriggerSync(source => this.triggerSync([source], true))); } - triggerAutoSync(sources: string[]): Promise { + triggerSync(sources: string[], hasToLimitSync: boolean): Promise { + return this.channel.call('triggerSync', [sources, hasToLimitSync]); + } - return this.channel.call('triggerAutoSync', [sources]); + turnOn(): Promise { + return this.channel.call('turnOn'); + } + + turnOff(everywhere: boolean): Promise { + return this.channel.call('turnOff', [everywhere]); } } diff --git a/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution.ts b/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution.ts index 60d058a8fbd..d1ea9a1d3b4 100644 --- a/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution.ts +++ b/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IUserDataSyncUtilService, CONTEXT_SYNC_STATE, SyncStatus } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncUtilService, SyncStatus, UserDataSyncError, UserDataSyncErrorCode, IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync'; import { Registry } from 'vs/platform/registry/common/platform'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; @@ -14,7 +14,13 @@ import { localize } from 'vs/nls'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { Action } from 'vs/base/common/actions'; +import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-browser/issue'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { CONTEXT_SYNC_STATE, SHOW_SYNC_LOG_COMMAND_ID, SYNC_TITLE } from 'vs/workbench/services/userDataSync/common/userDataSync'; class UserDataSyncServicesContribution implements IWorkbenchContribution { @@ -26,15 +32,50 @@ class UserDataSyncServicesContribution implements IWorkbenchContribution { } } +class UserDataSyncReportIssueContribution extends Disposable implements IWorkbenchContribution { + + constructor( + @IUserDataAutoSyncService userDataAutoSyncService: IUserDataAutoSyncService, + @INotificationService private readonly notificationService: INotificationService, + @IWorkbenchIssueService private readonly workbenchIssueService: IWorkbenchIssueService, + @ICommandService private readonly commandService: ICommandService, + ) { + super(); + this._register(userDataAutoSyncService.onError(error => this.onAutoSyncError(error))); + } + + private onAutoSyncError(error: UserDataSyncError): void { + switch (error.code) { + case UserDataSyncErrorCode.LocalTooManyRequests: + case UserDataSyncErrorCode.TooManyRequests: + const operationId = error.operationId ? localize('operationId', "Operation Id: {0}", error.operationId) : undefined; + const message = localize({ key: 'too many requests', comment: ['Settings Sync is the name of the feature'] }, "Settings sync is disabled because the current device is making too many requests. Please report an issue by providing the sync logs."); + this.notificationService.notify({ + severity: Severity.Error, + message: operationId ? `${message} ${operationId}` : message, + source: error.operationId ? localize('settings sync', "Settings Sync. Operation Id: {0}", error.operationId) : undefined, + actions: { + primary: [ + new Action('Show Sync Logs', localize('show sync logs', "Show Log"), undefined, true, () => this.commandService.executeCommand(SHOW_SYNC_LOG_COMMAND_ID)), + new Action('Report Issue', localize('report issue', "Report Issue"), undefined, true, () => this.workbenchIssueService.openReporter()) + ] + } + }); + return; + } + } +} + const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(UserDataSyncServicesContribution, LifecyclePhase.Starting); +workbenchRegistry.registerWorkbenchContribution(UserDataSyncReportIssueContribution, LifecyclePhase.Restored); registerAction2(class OpenSyncBackupsFolder extends Action2 { constructor() { super({ id: 'workbench.userData.actions.openSyncBackupsFolder', title: { value: localize('Open Backup folder', "Open Local Backups Folder"), original: 'Open Local Backups Folder' }, - category: { value: localize('sync preferences', "Preferences Sync"), original: `Preferences Sync` }, + category: { value: SYNC_TITLE, original: `Settings Sync` }, menu: { id: MenuId.CommandPalette, when: CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), diff --git a/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSyncStoreManagementService.ts b/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSyncStoreManagementService.ts new file mode 100644 index 00000000000..4a34eee9101 --- /dev/null +++ b/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSyncStoreManagementService.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 { IUserDataSyncStoreManagementService, UserDataSyncStoreType, IUserDataSyncStore } from 'vs/platform/userDataSync/common/userDataSync'; +import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { AbstractUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { URI } from 'vs/base/common/uri'; + +export class UserDataSyncStoreManagementService extends AbstractUserDataSyncStoreManagementService implements IUserDataSyncStoreManagementService { + + private readonly channel: IChannel; + + constructor( + @IProductService productService: IProductService, + @IConfigurationService configurationService: IConfigurationService, + @IStorageService storageService: IStorageService, + @ISharedProcessService sharedProcessService: ISharedProcessService, + ) { + super(productService, configurationService, storageService); + this.channel = sharedProcessService.getChannel('userDataSyncStoreManagement'); + } + + async switch(type: UserDataSyncStoreType): Promise { + return this.channel.call('switch', [type]); + } + + async getPreviousUserDataSyncStore(): Promise { + const userDataSyncStore = await this.channel.call('getPreviousUserDataSyncStore'); + return this.revive(userDataSyncStore); + } + + private revive(userDataSyncStore: IUserDataSyncStore): IUserDataSyncStore { + return { + url: URI.revive(userDataSyncStore.url), + defaultUrl: URI.revive(userDataSyncStore.defaultUrl), + insidersUrl: URI.revive(userDataSyncStore.insidersUrl), + stableUrl: URI.revive(userDataSyncStore.stableUrl), + authenticationProviders: userDataSyncStore.authenticationProviders, + }; + } + +} diff --git a/src/vs/workbench/browser/parts/views/media/views.css b/src/vs/workbench/contrib/views/browser/media/views.css similarity index 98% rename from src/vs/workbench/browser/parts/views/media/views.css rename to src/vs/workbench/contrib/views/browser/media/views.css index e2e85381244..8b30fccc585 100644 --- a/src/vs/workbench/browser/parts/views/media/views.css +++ b/src/vs/workbench/contrib/views/browser/media/views.css @@ -7,7 +7,8 @@ .file-icon-themable-tree.align-icons-and-twisties .monaco-tl-twistie:not(.force-twistie):not(.collapsible), .file-icon-themable-tree .align-icon-with-twisty .monaco-tl-twistie:not(.force-twistie):not(.collapsible), -.file-icon-themable-tree.hide-arrows .monaco-tl-twistie:not(.force-twistie) { +.file-icon-themable-tree.hide-arrows .monaco-tl-twistie:not(.force-twistie), +.file-icon-themable-tree .monaco-tl-twistie.force-no-twistie { background-image: none !important; width: 0 !important; padding-right: 0 !important; diff --git a/src/vs/workbench/contrib/views/browser/treeView.ts b/src/vs/workbench/contrib/views/browser/treeView.ts new file mode 100644 index 00000000000..b13c36fadfb --- /dev/null +++ b/src/vs/workbench/contrib/views/browser/treeView.ts @@ -0,0 +1,1037 @@ +/*--------------------------------------------------------------------------------------------- + * 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/views'; +import { Event, Emitter } from 'vs/base/common/event'; +import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IAction, ActionRunner, IActionViewItemProvider } from 'vs/base/common/actions'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IMenuService, MenuId, MenuItemAction, registerAction2, Action2, SubmenuItemAction } from 'vs/platform/actions/common/actions'; +import { MenuEntryActionViewItem, createAndFillInContextMenuActions, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IContextKeyService, ContextKeyExpr, ContextKeyEqualsExpr, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeItemLabel, IViewDescriptorService, ViewContainer, ViewContainerLocation, ResolvableTreeItem } from 'vs/workbench/common/views'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IProgressService } from 'vs/platform/progress/common/progress'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import * as DOM from 'vs/base/browser/dom'; +import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { URI } from 'vs/base/common/uri'; +import { dirname, basename } from 'vs/base/common/resources'; +import { LIGHT, FileThemeIcon, FolderThemeIcon, registerThemingParticipant, ThemeIcon, IThemeService } from 'vs/platform/theme/common/themeService'; +import { FileKind } from 'vs/platform/files/common/files'; +import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; +import { localize } from 'vs/nls'; +import { timeout } from 'vs/base/common/async'; +import { textLinkForeground, textCodeBlockBackground, focusBorder, listFilterMatchHighlight, listFilterMatchHighlightBorder } from 'vs/platform/theme/common/colorRegistry'; +import { isString } from 'vs/base/common/types'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; +import { ITreeRenderer, ITreeNode, IAsyncDataSource, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; +import { FuzzyScore, createMatches } from 'vs/base/common/filters'; +import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults'; +import { isFalsyOrWhitespace } from 'vs/base/common/strings'; +import { SIDE_BAR_BACKGROUND, PANEL_BACKGROUND } from 'vs/workbench/common/theme'; +import { IHoverService, IHoverOptions } from 'vs/workbench/services/hover/browser/hover'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; + +class Root implements ITreeItem { + label = { label: 'root' }; + handle = '0'; + parentHandle: string | undefined = undefined; + collapsibleState = TreeItemCollapsibleState.Expanded; + children: ITreeItem[] | undefined = undefined; +} + +const noDataProviderMessage = localize('no-dataprovider', "There is no data provider registered that can provide view data."); + +class Tree extends WorkbenchAsyncDataTree { } + +export class TreeView extends Disposable implements ITreeView { + + private isVisible: boolean = false; + private _hasIconForParentNode = false; + private _hasIconForLeafNode = false; + + private readonly collapseAllContextKey: RawContextKey; + private readonly collapseAllContext: IContextKey; + private readonly refreshContextKey: RawContextKey; + private readonly refreshContext: IContextKey; + + private focused: boolean = false; + private domNode!: HTMLElement; + private treeContainer!: HTMLElement; + private _messageValue: string | undefined; + private _canSelectMany: boolean = false; + private messageElement!: HTMLDivElement; + private tree: Tree | undefined; + private treeLabels: ResourceLabels | undefined; + + private root: ITreeItem; + private elementsToRefresh: ITreeItem[] = []; + + private readonly _onDidExpandItem: Emitter = this._register(new Emitter()); + readonly onDidExpandItem: Event = this._onDidExpandItem.event; + + private readonly _onDidCollapseItem: Emitter = this._register(new Emitter()); + readonly onDidCollapseItem: Event = this._onDidCollapseItem.event; + + private _onDidChangeSelection: Emitter = this._register(new Emitter()); + readonly onDidChangeSelection: Event = this._onDidChangeSelection.event; + + private readonly _onDidChangeVisibility: Emitter = this._register(new Emitter()); + readonly onDidChangeVisibility: Event = this._onDidChangeVisibility.event; + + private readonly _onDidChangeActions: Emitter = this._register(new Emitter()); + readonly onDidChangeActions: Event = this._onDidChangeActions.event; + + private readonly _onDidChangeWelcomeState: Emitter = this._register(new Emitter()); + readonly onDidChangeWelcomeState: Event = this._onDidChangeWelcomeState.event; + + private readonly _onDidChangeTitle: Emitter = this._register(new Emitter()); + readonly onDidChangeTitle: Event = this._onDidChangeTitle.event; + + private readonly _onDidCompleteRefresh: Emitter = this._register(new Emitter()); + + constructor( + readonly id: string, + private _title: string, + @IThemeService private readonly themeService: IThemeService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ICommandService private readonly commandService: ICommandService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IProgressService protected readonly progressService: IProgressService, + @IContextMenuService private readonly contextMenuService: IContextMenuService, + @IKeybindingService private readonly keybindingService: IKeybindingService, + @INotificationService private readonly notificationService: INotificationService, + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super(); + this.root = new Root(); + this.collapseAllContextKey = new RawContextKey(`treeView.${this.id}.enableCollapseAll`, false); + this.collapseAllContext = this.collapseAllContextKey.bindTo(contextKeyService); + this.refreshContextKey = new RawContextKey(`treeView.${this.id}.enableRefresh`, false); + this.refreshContext = this.refreshContextKey.bindTo(contextKeyService); + + this._register(this.themeService.onDidFileIconThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); + this._register(this.themeService.onDidColorThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('explorer.decorations')) { + this.doRefresh([this.root]); /** soft refresh **/ + } + })); + this._register(this.viewDescriptorService.onDidChangeLocation(({ views, from, to }) => { + if (views.some(v => v.id === this.id)) { + this.tree?.updateOptions({ overrideStyles: { listBackground: this.viewLocation === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND } }); + } + })); + this.registerActions(); + + this.create(); + } + + get viewContainer(): ViewContainer { + return this.viewDescriptorService.getViewContainerByViewId(this.id)!; + } + + get viewLocation(): ViewContainerLocation { + return this.viewDescriptorService.getViewLocationById(this.id)!; + } + + private _dataProvider: ITreeViewDataProvider | undefined; + get dataProvider(): ITreeViewDataProvider | undefined { + return this._dataProvider; + } + + set dataProvider(dataProvider: ITreeViewDataProvider | undefined) { + if (this.tree === undefined) { + this.createTree(); + } + + if (dataProvider) { + this._dataProvider = new class implements ITreeViewDataProvider { + private _isEmpty: boolean = true; + private _onDidChangeEmpty: Emitter = new Emitter(); + public onDidChangeEmpty: Event = this._onDidChangeEmpty.event; + + get isTreeEmpty(): boolean { + return this._isEmpty; + } + + async getChildren(node: ITreeItem): Promise { + let children: ITreeItem[]; + if (node && node.children) { + children = node.children; + } else { + children = await (node instanceof Root ? dataProvider.getChildren() : dataProvider.getChildren(node)); + node.children = children; + } + if (node instanceof Root) { + const oldEmpty = this._isEmpty; + this._isEmpty = children.length === 0; + if (oldEmpty !== this._isEmpty) { + this._onDidChangeEmpty.fire(); + } + } + return children; + } + }; + if (this._dataProvider.onDidChangeEmpty) { + this._register(this._dataProvider.onDidChangeEmpty(() => this._onDidChangeWelcomeState.fire())); + } + this.updateMessage(); + this.refresh(); + } else { + this._dataProvider = undefined; + this.updateMessage(); + } + + this._onDidChangeWelcomeState.fire(); + } + + private _message: string | undefined; + get message(): string | undefined { + return this._message; + } + + set message(message: string | undefined) { + this._message = message; + this.updateMessage(); + this._onDidChangeWelcomeState.fire(); + } + + get title(): string { + return this._title; + } + + set title(name: string) { + this._title = name; + this._onDidChangeTitle.fire(this._title); + } + + get canSelectMany(): boolean { + return this._canSelectMany; + } + + set canSelectMany(canSelectMany: boolean) { + this._canSelectMany = canSelectMany; + } + + get hasIconForParentNode(): boolean { + return this._hasIconForParentNode; + } + + get hasIconForLeafNode(): boolean { + return this._hasIconForLeafNode; + } + + get visible(): boolean { + return this.isVisible; + } + + get showCollapseAllAction(): boolean { + return !!this.collapseAllContext.get(); + } + + set showCollapseAllAction(showCollapseAllAction: boolean) { + this.collapseAllContext.set(showCollapseAllAction); + } + + get showRefreshAction(): boolean { + return !!this.refreshContext.get(); + } + + set showRefreshAction(showRefreshAction: boolean) { + this.refreshContext.set(showRefreshAction); + } + + private registerActions() { + const that = this; + this._register(registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.actions.treeView.${that.id}.refresh`, + title: localize('refresh', "Refresh"), + menu: { + id: MenuId.ViewTitle, + when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', that.id), that.refreshContextKey), + group: 'navigation', + order: Number.MAX_SAFE_INTEGER - 1, + }, + icon: { id: 'codicon/refresh' } + }); + } + async run(): Promise { + return that.refresh(); + } + })); + this._register(registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.actions.treeView.${that.id}.collapseAll`, + title: localize('collapseAll', "Collapse All"), + menu: { + id: MenuId.ViewTitle, + when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', that.id), that.collapseAllContextKey), + group: 'navigation', + order: Number.MAX_SAFE_INTEGER, + }, + icon: { id: 'codicon/collapse-all' } + }); + } + async run(): Promise { + if (that.tree) { + return new CollapseAllAction(that.tree, true).run(); + } + } + })); + } + + setVisibility(isVisible: boolean): void { + isVisible = !!isVisible; + if (this.isVisible === isVisible) { + return; + } + + this.isVisible = isVisible; + + if (this.tree) { + if (this.isVisible) { + DOM.show(this.tree.getHTMLElement()); + } else { + DOM.hide(this.tree.getHTMLElement()); // make sure the tree goes out of the tabindex world by hiding it + } + + if (this.isVisible && this.elementsToRefresh.length) { + this.doRefresh(this.elementsToRefresh); + this.elementsToRefresh = []; + } + } + + this._onDidChangeVisibility.fire(this.isVisible); + } + + focus(reveal: boolean = true): void { + if (this.tree && this.root.children && this.root.children.length > 0) { + // Make sure the current selected element is revealed + const selectedElement = this.tree.getSelection()[0]; + if (selectedElement && reveal) { + this.tree.reveal(selectedElement, 0.5); + } + + // Pass Focus to Viewer + this.tree.domFocus(); + } else if (this.tree) { + this.tree.domFocus(); + } else { + this.domNode.focus(); + } + } + + show(container: HTMLElement): void { + DOM.append(container, this.domNode); + } + + private create() { + this.domNode = DOM.$('.tree-explorer-viewlet-tree-view'); + this.messageElement = DOM.append(this.domNode, DOM.$('.message')); + this.treeContainer = DOM.append(this.domNode, DOM.$('.customview-tree')); + this.treeContainer.classList.add('file-icon-themable-tree', 'show-file-icons'); + const focusTracker = this._register(DOM.trackFocus(this.domNode)); + this._register(focusTracker.onDidFocus(() => this.focused = true)); + this._register(focusTracker.onDidBlur(() => this.focused = false)); + } + + private createTree() { + const actionViewItemProvider = (action: IAction) => { + if (action instanceof MenuItemAction) { + return this.instantiationService.createInstance(MenuEntryActionViewItem, action); + } else if (action instanceof SubmenuItemAction) { + return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action); + } + + return undefined; + }; + const treeMenus = this._register(this.instantiationService.createInstance(TreeMenus, this.id)); + this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this)); + const dataSource = this.instantiationService.createInstance(TreeDataSource, this, (task: Promise) => this.progressService.withProgress({ location: this.id }, () => task)); + const aligner = new Aligner(this.themeService); + const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, treeMenus, this.treeLabels, actionViewItemProvider, aligner); + const widgetAriaLabel = this._title; + + this.tree = this._register(this.instantiationService.createInstance(Tree, this.id, this.treeContainer, new TreeViewDelegate(), [renderer], + dataSource, { + identityProvider: new TreeViewIdentityProvider(), + accessibilityProvider: { + getAriaLabel(element: ITreeItem): string { + if (element.accessibilityInformation) { + return element.accessibilityInformation.label; + } + + return isString(element.tooltip) ? element.tooltip : element.label ? element.label.label : ''; + }, + getRole(element: ITreeItem): string | undefined { + return element.accessibilityInformation?.role ?? 'treeitem'; + }, + getWidgetAriaLabel(): string { + return widgetAriaLabel; + } + }, + keyboardNavigationLabelProvider: { + getKeyboardNavigationLabel: (item: ITreeItem) => { + return item.label ? item.label.label : (item.resourceUri ? basename(URI.revive(item.resourceUri)) : undefined); + } + }, + expandOnlyOnTwistieClick: (e: ITreeItem) => !!e.command, + collapseByDefault: (e: ITreeItem): boolean => { + return e.collapsibleState !== TreeItemCollapsibleState.Expanded; + }, + multipleSelectionSupport: this.canSelectMany, + overrideStyles: { + listBackground: this.viewLocation === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND + } + }) as WorkbenchAsyncDataTree); + aligner.tree = this.tree; + const actionRunner = new MultipleSelectionActionRunner(this.notificationService, () => this.tree!.getSelection()); + renderer.actionRunner = actionRunner; + + this.tree.contextKeyService.createKey(this.id, true); + this._register(this.tree.onContextMenu(e => this.onContextMenu(treeMenus, e, actionRunner))); + this._register(this.tree.onDidChangeSelection(e => this._onDidChangeSelection.fire(e.elements))); + this._register(this.tree.onDidChangeCollapseState(e => { + if (!e.node.element) { + return; + } + + const element: ITreeItem = Array.isArray(e.node.element.element) ? e.node.element.element[0] : e.node.element.element; + if (e.node.collapsed) { + this._onDidCollapseItem.fire(element); + } else { + this._onDidExpandItem.fire(element); + } + })); + this.tree.setInput(this.root).then(() => this.updateContentAreas()); + + this._register(this.tree.onDidOpen(e => { + if (!e.browserEvent) { + return; + } + const selection = this.tree!.getSelection(); + if ((selection.length === 1) && selection[0].command) { + this.commandService.executeCommand(selection[0].command.id, ...(selection[0].command.arguments || [])); + } + })); + + } + + private onContextMenu(treeMenus: TreeMenus, treeEvent: ITreeContextMenuEvent, actionRunner: MultipleSelectionActionRunner): void { + const node: ITreeItem | null = treeEvent.element; + if (node === null) { + return; + } + const event: UIEvent = treeEvent.browserEvent; + + event.preventDefault(); + event.stopPropagation(); + + this.tree!.setFocus([node]); + const actions = treeMenus.getResourceContextActions(node); + if (!actions.length) { + return; + } + this.contextMenuService.showContextMenu({ + getAnchor: () => treeEvent.anchor, + + getActions: () => actions, + + getActionViewItem: (action) => { + const keybinding = this.keybindingService.lookupKeybinding(action.id); + if (keybinding) { + return new ActionViewItem(action, action, { label: true, keybinding: keybinding.getLabel() }); + } + return undefined; + }, + + onHide: (wasCancelled?: boolean) => { + if (wasCancelled) { + this.tree!.domFocus(); + } + }, + + getActionsContext: () => ({ $treeViewId: this.id, $treeItemHandle: node.handle }), + + actionRunner + }); + } + + protected updateMessage(): void { + if (this._message) { + this.showMessage(this._message); + } else if (!this.dataProvider) { + this.showMessage(noDataProviderMessage); + } else { + this.hideMessage(); + } + this.updateContentAreas(); + } + + private showMessage(message: string): void { + this.messageElement.classList.remove('hide'); + this.resetMessageElement(); + this._messageValue = message; + if (!isFalsyOrWhitespace(this._message)) { + this.messageElement.textContent = this._messageValue; + } + this.layout(this._height, this._width); + } + + private hideMessage(): void { + this.resetMessageElement(); + this.messageElement.classList.add('hide'); + this.layout(this._height, this._width); + } + + private resetMessageElement(): void { + DOM.clearNode(this.messageElement); + } + + private _height: number = 0; + private _width: number = 0; + layout(height: number, width: number) { + if (height && width) { + this._height = height; + this._width = width; + const treeHeight = height - DOM.getTotalHeight(this.messageElement); + this.treeContainer.style.height = treeHeight + 'px'; + if (this.tree) { + this.tree.layout(treeHeight, width); + } + } + } + + getOptimalWidth(): number { + if (this.tree) { + const parentNode = this.tree.getHTMLElement(); + const childNodes = ([] as HTMLElement[]).slice.call(parentNode.querySelectorAll('.outline-item-label > a')); + return DOM.getLargestChildWidth(parentNode, childNodes); + } + return 0; + } + + async refresh(elements?: ITreeItem[]): Promise { + if (this.dataProvider && this.tree) { + if (this.refreshing) { + await Event.toPromise(this._onDidCompleteRefresh.event); + } + if (!elements) { + elements = [this.root]; + // remove all waiting elements to refresh if root is asked to refresh + this.elementsToRefresh = []; + } + for (const element of elements) { + element.children = undefined; // reset children + } + if (this.isVisible) { + return this.doRefresh(elements); + } else { + if (this.elementsToRefresh.length) { + const seen: Set = new Set(); + this.elementsToRefresh.forEach(element => seen.add(element.handle)); + for (const element of elements) { + if (!seen.has(element.handle)) { + this.elementsToRefresh.push(element); + } + } + } else { + this.elementsToRefresh.push(...elements); + } + } + } + return undefined; + } + + async expand(itemOrItems: ITreeItem | ITreeItem[]): Promise { + const tree = this.tree; + if (tree) { + itemOrItems = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems]; + await Promise.all(itemOrItems.map(element => { + return tree.expand(element, false); + })); + } + } + + setSelection(items: ITreeItem[]): void { + if (this.tree) { + this.tree.setSelection(items); + } + } + + setFocus(item: ITreeItem): void { + if (this.tree) { + this.focus(); + this.tree.setFocus([item]); + } + } + + async reveal(item: ITreeItem): Promise { + if (this.tree) { + return this.tree.reveal(item); + } + } + + private refreshing: boolean = false; + private async doRefresh(elements: ITreeItem[]): Promise { + const tree = this.tree; + if (tree && this.visible) { + this.refreshing = true; + await Promise.all(elements.map(element => tree.updateChildren(element, true, true))); + this.refreshing = false; + this._onDidCompleteRefresh.fire(); + this.updateContentAreas(); + if (this.focused) { + this.focus(false); + } + } + } + + private updateContentAreas(): void { + const isTreeEmpty = !this.root.children || this.root.children.length === 0; + // Hide tree container only when there is a message and tree is empty and not refreshing + if (this._messageValue && isTreeEmpty && !this.refreshing) { + this.treeContainer.classList.add('hide'); + this.domNode.setAttribute('tabindex', '0'); + } else { + this.treeContainer.classList.remove('hide'); + this.domNode.removeAttribute('tabindex'); + } + } +} + +class TreeViewIdentityProvider implements IIdentityProvider { + getId(element: ITreeItem): { toString(): string; } { + return element.handle; + } +} + +class TreeViewDelegate implements IListVirtualDelegate { + + getHeight(element: ITreeItem): number { + return TreeRenderer.ITEM_HEIGHT; + } + + getTemplateId(element: ITreeItem): string { + return TreeRenderer.TREE_TEMPLATE_ID; + } +} + +class TreeDataSource implements IAsyncDataSource { + + constructor( + private treeView: ITreeView, + private withProgress: (task: Promise) => Promise + ) { + } + + hasChildren(element: ITreeItem): boolean { + return !!this.treeView.dataProvider && (element.collapsibleState !== TreeItemCollapsibleState.None); + } + + async getChildren(element: ITreeItem): Promise { + if (this.treeView.dataProvider) { + return this.withProgress(this.treeView.dataProvider.getChildren(element)); + } + return []; + } +} + +// todo@joh,sandy make this proper and contributable from extensions +registerThemingParticipant((theme, collector) => { + + const matchBackgroundColor = theme.getColor(listFilterMatchHighlight); + if (matchBackgroundColor) { + collector.addRule(`.file-icon-themable-tree .monaco-list-row .content .monaco-highlighted-label .highlight { color: unset !important; background-color: ${matchBackgroundColor}; }`); + collector.addRule(`.monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; background-color: ${matchBackgroundColor}; }`); + } + const matchBorderColor = theme.getColor(listFilterMatchHighlightBorder); + if (matchBorderColor) { + collector.addRule(`.file-icon-themable-tree .monaco-list-row .content .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${matchBorderColor}; box-sizing: border-box; }`); + collector.addRule(`.monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${matchBorderColor}; box-sizing: border-box; }`); + } + const link = theme.getColor(textLinkForeground); + if (link) { + collector.addRule(`.tree-explorer-viewlet-tree-view > .message a { color: ${link}; }`); + } + const focusBorderColor = theme.getColor(focusBorder); + if (focusBorderColor) { + collector.addRule(`.tree-explorer-viewlet-tree-view > .message a:focus { outline: 1px solid ${focusBorderColor}; outline-offset: -1px; }`); + } + const codeBackground = theme.getColor(textCodeBlockBackground); + if (codeBackground) { + collector.addRule(`.tree-explorer-viewlet-tree-view > .message code { background-color: ${codeBackground}; }`); + } +}); + +interface ITreeExplorerTemplateData { + elementDisposable: IDisposable; + container: HTMLElement; + resourceLabel: IResourceLabel; + icon: HTMLElement; + actionBar: ActionBar; +} + +class TreeRenderer extends Disposable implements ITreeRenderer { + static readonly ITEM_HEIGHT = 22; + static readonly TREE_TEMPLATE_ID = 'treeExplorer'; + + private _actionRunner: MultipleSelectionActionRunner | undefined; + private readonly hoverDelay: number; + + constructor( + private treeViewId: string, + private menus: TreeMenus, + private labels: ResourceLabels, + private actionViewItemProvider: IActionViewItemProvider, + private aligner: Aligner, + @IThemeService private readonly themeService: IThemeService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @ILabelService private readonly labelService: ILabelService, + @IHoverService private readonly hoverService: IHoverService + ) { + super(); + this.hoverDelay = this.configurationService.getValue('editor.hover.delay'); + } + + get templateId(): string { + return TreeRenderer.TREE_TEMPLATE_ID; + } + + set actionRunner(actionRunner: MultipleSelectionActionRunner) { + this._actionRunner = actionRunner; + } + + renderTemplate(container: HTMLElement): ITreeExplorerTemplateData { + container.classList.add('custom-view-tree-node-item'); + + const icon = DOM.append(container, DOM.$('.custom-view-tree-node-item-icon')); + + const resourceLabel = this.labels.create(container, { supportHighlights: true }); + const actionsContainer = DOM.append(resourceLabel.element, DOM.$('.actions')); + const actionBar = new ActionBar(actionsContainer, { + actionViewItemProvider: this.actionViewItemProvider + }); + + return { resourceLabel, icon, actionBar, container, elementDisposable: Disposable.None }; + } + + renderElement(element: ITreeNode, index: number, templateData: ITreeExplorerTemplateData): void { + templateData.elementDisposable.dispose(); + const node = element.element; + const resource = node.resourceUri ? URI.revive(node.resourceUri) : null; + const treeItemLabel: ITreeItemLabel | undefined = node.label ? node.label : (resource ? { label: basename(resource) } : undefined); + const description = isString(node.description) ? node.description : resource && node.description === true ? this.labelService.getUriLabel(dirname(resource), { relative: true }) : undefined; + const label = treeItemLabel ? treeItemLabel.label : undefined; + const matches = (treeItemLabel && treeItemLabel.highlights && label) ? treeItemLabel.highlights.map(([start, end]) => { + if (start < 0) { + start = label.length + start; + } + if (end < 0) { + end = label.length + end; + } + if ((start >= label.length) || (end > label.length)) { + return ({ start: 0, end: 0 }); + } + if (start > end) { + const swap = start; + start = end; + end = swap; + } + return ({ start, end }); + }) : undefined; + const icon = this.themeService.getColorTheme().type === LIGHT ? node.icon : node.iconDark; + const iconUrl = icon ? URI.revive(icon) : null; + const canResolve = node instanceof ResolvableTreeItem && node.hasResolve; + const title = node.tooltip ? (isString(node.tooltip) ? node.tooltip : undefined) : (resource ? undefined : (canResolve ? undefined : label)); + + // reset + templateData.actionBar.clear(); + + if (resource || this.isFileKindThemeIcon(node.themeIcon)) { + const fileDecorations = this.configurationService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations'); + templateData.resourceLabel.setResource({ name: label, description, resource: resource ? resource : URI.parse('missing:_icon_resource') }, { + fileKind: this.getFileKind(node), + title, + hideIcon: !!iconUrl, + fileDecorations, + extraClasses: ['custom-view-tree-node-item-resourceLabel'], + matches: matches ? matches : createMatches(element.filterData), + strikethrough: treeItemLabel?.strikethrough, + }); + } else { + templateData.resourceLabel.setResource({ name: label, description }, { + title, + hideIcon: true, + extraClasses: ['custom-view-tree-node-item-resourceLabel'], + matches: matches ? matches : createMatches(element.filterData), + strikethrough: treeItemLabel?.strikethrough, + }); + } + + templateData.icon.title = title ? title : ''; + + if (iconUrl) { + templateData.icon.className = 'custom-view-tree-node-item-icon'; + templateData.icon.style.backgroundImage = DOM.asCSSUrl(iconUrl); + + } else { + let iconClass: string | undefined; + if (node.themeIcon && !this.isFileKindThemeIcon(node.themeIcon)) { + iconClass = ThemeIcon.asClassName(node.themeIcon); + } + templateData.icon.className = iconClass ? `custom-view-tree-node-item-icon ${iconClass}` : ''; + templateData.icon.style.backgroundImage = ''; + } + + templateData.actionBar.context = { $treeViewId: this.treeViewId, $treeItemHandle: node.handle }; + templateData.actionBar.push(this.menus.getResourceActions(node), { icon: true, label: false }); + if (this._actionRunner) { + templateData.actionBar.actionRunner = this._actionRunner; + } + this.setAlignment(templateData.container, node); + const disposableStore = new DisposableStore(); + templateData.elementDisposable = disposableStore; + disposableStore.add(this.themeService.onDidFileIconThemeChange(() => this.setAlignment(templateData.container, node))); + this.setupHovers(node, templateData.container, disposableStore, label); + } + + private setupHovers(node: ITreeItem, htmlElement: HTMLElement, disposableStore: DisposableStore, label: string | undefined): void { + if (!(node instanceof ResolvableTreeItem) || (node.tooltip && isString(node.tooltip)) || (!node.tooltip && !node.hasResolve)) { + return; + } + const resolvableNode: ResolvableTreeItem = node; + const hoverService = this.hoverService; + const hoverDelay = this.hoverDelay; + let hoverOptions: IHoverOptions | undefined; + function mouseOver(this: HTMLElement, e: MouseEvent): any { + let isHovering = true; + function mouseLeave(this: HTMLElement, e: MouseEvent): any { + isHovering = false; + } + this.addEventListener(DOM.EventType.MOUSE_LEAVE, mouseLeave, { passive: true }); + setTimeout(async () => { + await resolvableNode.resolve(); + const tooltip = resolvableNode.tooltip ?? label; + if (isHovering && tooltip) { + if (!hoverOptions) { + hoverOptions = { text: isString(tooltip) ? { value: tooltip } : tooltip, target: this }; + } + hoverService.showHover(hoverOptions); + } + this.removeEventListener(DOM.EventType.MOUSE_LEAVE, mouseLeave); + }, hoverDelay); + } + htmlElement.addEventListener(DOM.EventType.MOUSE_OVER, mouseOver, { passive: true }); + disposableStore.add({ + dispose: () => { + htmlElement.removeEventListener(DOM.EventType.MOUSE_OVER, mouseOver); + } + }); + } + + private setAlignment(container: HTMLElement, treeItem: ITreeItem) { + DOM.toggleClass(container.parentElement!, 'align-icon-with-twisty', this.aligner.alignIconWithTwisty(treeItem)); + } + + private isFileKindThemeIcon(icon: ThemeIcon | undefined): boolean { + if (icon) { + return icon.id === FileThemeIcon.id || icon.id === FolderThemeIcon.id; + } else { + return false; + } + } + + private getFileKind(node: ITreeItem): FileKind { + if (node.themeIcon) { + switch (node.themeIcon.id) { + case FileThemeIcon.id: + return FileKind.FILE; + case FolderThemeIcon.id: + return FileKind.FOLDER; + } + } + return node.collapsibleState === TreeItemCollapsibleState.Collapsed || node.collapsibleState === TreeItemCollapsibleState.Expanded ? FileKind.FOLDER : FileKind.FILE; + } + + disposeElement(resource: ITreeNode, index: number, templateData: ITreeExplorerTemplateData): void { + templateData.elementDisposable.dispose(); + } + + disposeTemplate(templateData: ITreeExplorerTemplateData): void { + templateData.resourceLabel.dispose(); + templateData.actionBar.dispose(); + templateData.elementDisposable.dispose(); + } +} + +class Aligner extends Disposable { + private _tree: WorkbenchAsyncDataTree | undefined; + + constructor(private themeService: IThemeService) { + super(); + } + + set tree(tree: WorkbenchAsyncDataTree) { + this._tree = tree; + } + + public alignIconWithTwisty(treeItem: ITreeItem): boolean { + if (treeItem.collapsibleState !== TreeItemCollapsibleState.None) { + return false; + } + if (!this.hasIcon(treeItem)) { + return false; + } + + if (this._tree) { + const parent: ITreeItem = this._tree.getParentElement(treeItem) || this._tree.getInput(); + if (this.hasIcon(parent)) { + return false; + } + return !!parent.children && parent.children.every(c => c.collapsibleState === TreeItemCollapsibleState.None || !this.hasIcon(c)); + } else { + return false; + } + } + + private hasIcon(node: ITreeItem): boolean { + const icon = this.themeService.getColorTheme().type === LIGHT ? node.icon : node.iconDark; + if (icon) { + return true; + } + if (node.resourceUri || node.themeIcon) { + const fileIconTheme = this.themeService.getFileIconTheme(); + const isFolder = node.themeIcon ? node.themeIcon.id === FolderThemeIcon.id : node.collapsibleState !== TreeItemCollapsibleState.None; + if (isFolder) { + return fileIconTheme.hasFileIcons && fileIconTheme.hasFolderIcons; + } + return fileIconTheme.hasFileIcons; + } + return false; + } +} + +class MultipleSelectionActionRunner extends ActionRunner { + + constructor(notificationService: INotificationService, private getSelectedResources: (() => ITreeItem[])) { + super(); + this._register(this.onDidRun(e => { + if (e.error) { + notificationService.error(localize('command-error', 'Error running command {1}: {0}. This is likely caused by the extension that contributes {1}.', e.error.message, e.action.id)); + } + })); + } + + runAction(action: IAction, context: TreeViewItemHandleArg): Promise { + const selection = this.getSelectedResources(); + let selectionHandleArgs: TreeViewItemHandleArg[] | undefined = undefined; + let actionInSelected: boolean = false; + if (selection.length > 1) { + selectionHandleArgs = selection.map(selected => { + if (selected.handle === context.$treeItemHandle) { + actionInSelected = true; + } + return { $treeViewId: context.$treeViewId, $treeItemHandle: selected.handle }; + }); + } + + if (!actionInSelected) { + selectionHandleArgs = undefined; + } + + return action.run(...[context, selectionHandleArgs]); + } +} + +class TreeMenus extends Disposable implements IDisposable { + + constructor( + private id: string, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IMenuService private readonly menuService: IMenuService, + @IContextMenuService private readonly contextMenuService: IContextMenuService + ) { + super(); + } + + getResourceActions(element: ITreeItem): IAction[] { + return this.getActions(MenuId.ViewItemContext, { key: 'viewItem', value: element.contextValue }).primary; + } + + getResourceContextActions(element: ITreeItem): IAction[] { + return this.getActions(MenuId.ViewItemContext, { key: 'viewItem', value: element.contextValue }).secondary; + } + + private getActions(menuId: MenuId, context: { key: string, value?: string }): { primary: IAction[]; secondary: IAction[]; } { + const contextKeyService = this.contextKeyService.createScoped(); + contextKeyService.createKey('view', this.id); + contextKeyService.createKey(context.key, context.value); + + const menu = this.menuService.createMenu(menuId, contextKeyService); + const primary: IAction[] = []; + const secondary: IAction[] = []; + const result = { primary, secondary }; + createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, this.contextMenuService, g => /^inline/.test(g)); + + menu.dispose(); + + return result; + } +} + +export class CustomTreeView extends TreeView { + + private activated: boolean = false; + + constructor( + id: string, + title: string, + @IThemeService themeService: IThemeService, + @IInstantiationService instantiationService: IInstantiationService, + @ICommandService commandService: ICommandService, + @IConfigurationService configurationService: IConfigurationService, + @IProgressService progressService: IProgressService, + @IContextMenuService contextMenuService: IContextMenuService, + @IKeybindingService keybindingService: IKeybindingService, + @INotificationService notificationService: INotificationService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @IContextKeyService contextKeyService: IContextKeyService, + @IHoverService hoverService: IHoverService, + @IExtensionService private readonly extensionService: IExtensionService, + ) { + super(id, title, themeService, instantiationService, commandService, configurationService, progressService, contextMenuService, keybindingService, notificationService, viewDescriptorService, contextKeyService); + } + + setVisibility(isVisible: boolean): void { + super.setVisibility(isVisible); + if (this.visible) { + this.activate(); + } + } + + private activate() { + if (!this.activated) { + this.progressService.withProgress({ location: this.id }, () => this.extensionService.activateByEvent(`onView:${this.id}`)) + .then(() => timeout(2000)) + .then(() => { + this.updateMessage(); + }); + this.activated = true; + } + } +} diff --git a/src/vs/workbench/contrib/watermark/browser/watermark.css b/src/vs/workbench/contrib/watermark/browser/watermark.css index 15c57d04c3d..aa84eaefc72 100644 --- a/src/vs/workbench/contrib/watermark/browser/watermark.css +++ b/src/vs/workbench/contrib/watermark/browser/watermark.css @@ -60,15 +60,15 @@ color: rgba(0,0,0,.68); } -.vs-dark .monaco-workbench .part.editor > .content.empty > .watermark dt, -.vs-dark .monaco-workbench .part.editor > .content.empty > .watermark dl { +.monaco-workbench.vs-dark .part.editor > .content.empty > .watermark dt, +.monaco-workbench.vs-dark .part.editor > .content.empty > .watermark dl { color: rgba(255,255,255,.6); } -.hc-black .monaco-workbench .part.editor > .content.empty > .watermark dt { +.monaco-workbench.hc-black .part.editor > .content.empty > .watermark dt { color: #FFF; } -.hc-black .monaco-workbench .part.editor > .content.empty > .watermark dl { +.monaco-workbench.hc-black .part.editor > .content.empty > .watermark dl { color: #FFF; opacity: 1; } diff --git a/src/vs/workbench/contrib/watermark/browser/watermark.ts b/src/vs/workbench/contrib/watermark/browser/watermark.ts index de9c26428a0..a4a16842150 100644 --- a/src/vs/workbench/contrib/watermark/browser/watermark.ts +++ b/src/vs/workbench/contrib/watermark/browser/watermark.ts @@ -15,7 +15,6 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { GlobalNewUntitledFileAction } from 'vs/workbench/contrib/files/browser/fileActions'; import { OpenFolderAction, OpenFileFolderAction, OpenFileAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ShowAllCommandsAction } from 'vs/workbench/contrib/quickaccess/browser/commandsQuickAccess'; import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; @@ -28,6 +27,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { assertIsDefined } from 'vs/base/common/types'; import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; +import { NEW_UNTITLED_FILE_COMMAND_ID } from 'vs/workbench/contrib/files/browser/fileCommands'; const $ = dom.$; @@ -43,7 +43,7 @@ const openFileNonMacOnly: WatermarkEntry = { text: nls.localize('watermark.openF const openFolderNonMacOnly: WatermarkEntry = { text: nls.localize('watermark.openFolder', "Open Folder"), id: OpenFolderAction.ID, mac: false }; const openFileOrFolderMacOnly: WatermarkEntry = { text: nls.localize('watermark.openFileFolder', "Open File or Folder"), id: OpenFileFolderAction.ID, mac: true }; const openRecent: WatermarkEntry = { text: nls.localize('watermark.openRecent', "Open Recent"), id: 'workbench.action.openRecent' }; -const newUntitledFile: WatermarkEntry = { text: nls.localize('watermark.newUntitledFile', "New Untitled File"), id: GlobalNewUntitledFileAction.ID }; +const newUntitledFile: WatermarkEntry = { text: nls.localize('watermark.newUntitledFile', "New Untitled File"), id: NEW_UNTITLED_FILE_COMMAND_ID }; const newUntitledFileMacOnly: WatermarkEntry = assign({ mac: true }, newUntitledFile); const toggleTerminal: WatermarkEntry = { text: nls.localize({ key: 'watermark.toggleTerminal', comment: ['toggle is a verb here'] }, "Toggle Terminal"), id: TERMINAL_COMMAND_ID.TOGGLE }; const findInFiles: WatermarkEntry = { text: nls.localize('watermark.findInFiles', "Find in Files"), id: FindInFilesActionId }; diff --git a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts index bfe8903d4d6..0291f0c605d 100644 --- a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts @@ -7,12 +7,16 @@ import { addClass } from 'vs/base/browser/dom'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ILogService } from 'vs/platform/log/common/log'; +import { INotificationService } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { WebviewExtensionDescription, WebviewOptions, WebviewContentOptions } from 'vs/workbench/contrib/webview/browser/webview'; +import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing'; +import { WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { areWebviewInputOptionsEqual } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; -import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/common/themeing'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; export const enum WebviewMessageChannels { @@ -27,7 +31,8 @@ export const enum WebviewMessageChannels { loadResource = 'load-resource', loadLocalhost = 'load-localhost', webviewReady = 'webview-ready', - wheel = 'did-scroll-wheel' + wheel = 'did-scroll-wheel', + fatalError = 'fatal-error', } interface IKeydownEvent { @@ -47,6 +52,22 @@ interface WebviewContent { readonly state: string | undefined; } +namespace WebviewState { + export const enum Type { Initializing, Ready } + + export class Initializing { + readonly type = Type.Initializing; + + constructor( + public readonly pendingMessages: Array<{ readonly channel: string, readonly data?: any }> + ) { } + } + + export const Ready = { type: Type.Ready } as const; + + export type State = typeof Ready | Initializing; +} + export abstract class BaseWebview extends Disposable { private _element: T | undefined; @@ -55,20 +76,21 @@ export abstract class BaseWebview extends Disposable { private _focused: boolean | undefined; protected get focused(): boolean { return !!this._focused; } - private readonly _ready: Promise; + private _state: WebviewState.State = new WebviewState.Initializing([]); protected content: WebviewContent; - public extension: WebviewExtensionDescription | undefined; - constructor( // TODO: matb, this should not be protected. The only reason it needs to be is that the base class ends up using it in the call to createElement protected readonly id: string, options: WebviewOptions, contentOptions: WebviewContentOptions, + public readonly extension: WebviewExtensionDescription | undefined, private readonly webviewThemeDataProvider: WebviewThemeDataProvider, + @INotificationService notificationService: INotificationService, + @ILogService private readonly _logService: ILogService, @ITelemetryService private readonly _telemetryService: ITelemetryService, - @IEnvironmentService private readonly _environementService: IEnvironmentService, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, @IWorkbenchEnvironmentService protected readonly workbenchEnvironmentService: IWorkbenchEnvironmentService, ) { super(); @@ -79,17 +101,22 @@ export abstract class BaseWebview extends Disposable { state: undefined }; - this._element = this.createElement(options); + this._element = this.createElement(options, contentOptions); - this._ready = new Promise(resolve => { - const subscription = this._register(this.on(WebviewMessageChannels.webviewReady, () => { - if (this.element) { - addClass(this.element, 'ready'); - } - subscription.dispose(); - resolve(); - })); - }); + const subscription = this._register(this.on(WebviewMessageChannels.webviewReady, () => { + this._logService.debug(`Webview(${this.id}): webview ready`); + + if (this.element) { + addClass(this.element, 'ready'); + } + + if (this._state.type === WebviewState.Type.Initializing) { + this._state.pendingMessages.forEach(({ channel, data }) => this.doPostMessage(channel, data)); + } + this._state = WebviewState.Ready; + + subscription.dispose(); + })); this._register(this.on('no-csp-found', () => { this.handleNoCspFound(); @@ -128,6 +155,10 @@ export abstract class BaseWebview extends Disposable { this.handleFocusChange(false); })); + this._register(this.on<{ message: string }>(WebviewMessageChannels.fatalError, (e) => { + notificationService.error(localize('fatalErrorMessage', "Error loading webview: {0}", e.message)); + })); + this._register(this.on('did-keydown', (data: KeyboardEvent) => { // Electron: workaround for https://github.com/electron/electron/issues/14258 // We have to detect keyboard events in the and dispatch them to our @@ -175,23 +206,25 @@ export abstract class BaseWebview extends Disposable { private readonly _onDidBlur = this._register(new Emitter()); public readonly onDidBlur = this._onDidBlur.event; - public sendMessage(data: any): void { + public postMessage(data: any): void { this._send('message', data); } protected _send(channel: string, data?: any): void { - this._ready - .then(() => this.postMessage(channel, data)) - .catch(err => console.error(err)); + if (this._state.type === WebviewState.Type.Initializing) { + this._state.pendingMessages.push({ channel, data }); + } else { + this.doPostMessage(channel, data); + } } protected abstract readonly extraContentOptions: { readonly [key: string]: string }; - protected abstract createElement(options: WebviewOptions): T; + protected abstract createElement(options: WebviewOptions, contentOptions: WebviewContentOptions): T; protected abstract on(channel: string, handler: (data: T) => void): IDisposable; - protected abstract postMessage(channel: string, data?: any): void; + protected abstract doPostMessage(channel: string, data?: any): void; private _hasAlertedAboutMissingCsp = false; private handleNoCspFound(): void { @@ -201,7 +234,7 @@ export abstract class BaseWebview extends Disposable { this._hasAlertedAboutMissingCsp = true; if (this.extension && this.extension.id) { - if (this._environementService.isExtensionDevelopment) { + if (this._environmentService.isExtensionDevelopment) { this._onMissingCsp.fire(this.extension.id); } @@ -219,7 +252,8 @@ export abstract class BaseWebview extends Disposable { } public reload(): void { - this.doUpdateContent(); + this.doUpdateContent(this.content); + const subscription = this._register(this.on(WebviewMessageChannels.didLoad, () => { this._onDidReload.fire(); subscription.dispose(); @@ -227,25 +261,30 @@ export abstract class BaseWebview extends Disposable { } public set html(value: string) { - this.content = { + this.doUpdateContent({ html: value, options: this.content.options, state: this.content.state, - }; - this.doUpdateContent(); + }); } public set contentOptions(options: WebviewContentOptions) { + this._logService.debug(`Webview(${this.id}): will update content options`); + if (areWebviewInputOptionsEqual(options, this.content.options)) { + this._logService.debug(`Webview(${this.id}): skipping content options update`); return; } - this.content = { + this.doUpdateContent({ html: this.content.html, options: options, state: this.content.state, - }; - this.doUpdateContent(); + }); + } + + public set localResourcesRoot(resources: URI[]) { + /** no op */ } public set state(state: string | undefined) { @@ -260,7 +299,11 @@ export abstract class BaseWebview extends Disposable { this._send('initial-scroll-position', value); } - private doUpdateContent() { + private doUpdateContent(newContent: WebviewContent) { + this._logService.debug(`Webview(${this.id}): will update content`); + + this.content = newContent; + this._send('content', { contents: this.content.html, options: this.content.options, @@ -270,8 +313,8 @@ export abstract class BaseWebview extends Disposable { } protected style(): void { - const { styles, activeTheme } = this.webviewThemeDataProvider.getWebviewThemeData(); - this._send('styles', { styles, activeTheme }); + const { styles, activeTheme, themeLabel } = this.webviewThemeDataProvider.getWebviewThemeData(); + this._send('styles', { styles, activeTheme, themeName: themeLabel }); } protected handleFocusChange(isFocused: boolean): void { @@ -310,8 +353,32 @@ export abstract class BaseWebview extends Disposable { } public selectAll() { + this.execCommand('selectAll'); + } + + public copy() { + this.execCommand('copy'); + } + + public paste() { + this.execCommand('paste'); + } + + public cut() { + this.execCommand('cut'); + } + + public undo() { + this.execCommand('undo'); + } + + public redo() { + this.execCommand('redo'); + } + + private execCommand(command: string) { if (this.element) { - this._send('execCommand', 'selectAll'); + this._send('execCommand', command); } } } diff --git a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts index 2ce69211f98..ca03dc90b08 100644 --- a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts +++ b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts @@ -7,6 +7,7 @@ import { Dimension } from 'vs/base/browser/dom'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { memoize } from 'vs/base/common/decorators'; import { Emitter, Event } from 'vs/base/common/event'; +import { URI } from 'vs/base/common/uri'; import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; @@ -28,7 +29,6 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv private _html: string = ''; private _initialScrollProgress: number = 0; private _state: string | undefined = undefined; - private _extension: WebviewExtensionDescription | undefined; private _contentOptions: WebviewContentOptions; private _options: WebviewOptions; @@ -42,6 +42,7 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv private readonly id: string, initialOptions: WebviewOptions, initialContentOptions: WebviewContentOptions, + public readonly extension: WebviewExtensionDescription | undefined, @ILayoutService private readonly _layoutService: ILayoutService, @IWebviewService private readonly _webviewService: IWebviewService, @IContextKeyService private readonly _contextKeyService: IContextKeyService @@ -109,11 +110,14 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv private show() { if (!this._webview.value) { - const webview = this._webviewService.createWebviewElement(this.id, this._options, this._contentOptions); + const webview = this._webviewService.createWebviewElement(this.id, this._options, this._contentOptions, this.extension); this._webview.value = webview; webview.state = this._state; - webview.html = this._html; - webview.extension = this._extension; + + if (this._html) { + webview.html = this._html; + } + if (this._options.tryRestoreScrollPosition) { webview.initialScrollProgress = this._initialScrollProgress; } @@ -142,7 +146,7 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv this._onDidUpdateState.fire(state); })); - this._pendingMessages.forEach(msg => webview.sendMessage(msg)); + this._pendingMessages.forEach(msg => webview.postMessage(msg)); this._pendingMessages.clear(); } this.container.style.visibility = 'visible'; @@ -175,10 +179,8 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv this.withWebview(webview => webview.contentOptions = value); } - public get extension() { return this._extension; } - public set extension(value) { - this._extension = value; - this.withWebview(webview => webview.extension = value); + public set localResourcesRoot(resources: URI[]) { + this.withWebview(webview => webview.localResourcesRoot = resources); } private readonly _onDidFocus = this._register(new Emitter()); @@ -205,9 +207,9 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv private readonly _onMissingCsp = this._register(new Emitter()); public readonly onMissingCsp: Event = this._onMissingCsp.event; - sendMessage(data: any): void { + postMessage(data: any): void { if (this._webview.value) { - this._webview.value.sendMessage(data); + this._webview.value.postMessage(data); } else { this._pendingMessages.add(data); } @@ -216,6 +218,11 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv focus(): void { this.withWebview(webview => webview.focus()); } reload(): void { this.withWebview(webview => webview.reload()); } selectAll(): void { this.withWebview(webview => webview.selectAll()); } + copy(): void { this.withWebview(webview => webview.copy()); } + paste(): void { this.withWebview(webview => webview.paste()); } + cut(): void { this.withWebview(webview => webview.cut()); } + undo(): void { this.withWebview(webview => webview.undo()); } + redo(): void { this.withWebview(webview => webview.redo()); } showFind() { if (this._webview.value) { diff --git a/src/vs/workbench/contrib/webview/browser/pre/host.js b/src/vs/workbench/contrib/webview/browser/pre/host.js index d56b9d66e44..c63f6122aee 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/host.js +++ b/src/vs/workbench/contrib/webview/browser/pre/host.js @@ -5,6 +5,7 @@ // @ts-check (function () { const id = document.location.search.match(/\bid=([\w-]+)/)[1]; + const onElectron = /platform=electron/.test(document.location.search); const hostMessaging = new class HostMessaging { constructor() { @@ -35,35 +36,49 @@ } }(); + function fatalError(/** @type {string} */ message) { + console.error(`Webview fatal error: ${message}`); + hostMessaging.postMessage('fatal-error', { message }); + } + const workerReady = new Promise(async (resolveWorkerReady) => { + if (onElectron) { + return resolveWorkerReady(); + } + if (!areServiceWorkersEnabled()) { - console.log('Service Workers are not enabled. Webviews will not work properly'); + fatalError('Service Workers are not enabled in browser. Webviews will not work.'); return resolveWorkerReady(); } const expectedWorkerVersion = 1; - navigator.serviceWorker.register('service-worker.js').then(async registration => { - await navigator.serviceWorker.ready; + navigator.serviceWorker.register('service-worker.js').then( + async registration => { + await navigator.serviceWorker.ready; - const versionHandler = (event) => { - if (event.data.channel !== 'version') { - return; - } + const versionHandler = (event) => { + if (event.data.channel !== 'version') { + return; + } - navigator.serviceWorker.removeEventListener('message', versionHandler); - if (event.data.version === expectedWorkerVersion) { - return resolveWorkerReady(); - } else { - // If we have the wrong version, try once to unregister and re-register - return registration.update() - .then(() => navigator.serviceWorker.ready) - .finally(resolveWorkerReady); - } - }; - navigator.serviceWorker.addEventListener('message', versionHandler); - registration.active.postMessage({ channel: 'version' }); - }); + navigator.serviceWorker.removeEventListener('message', versionHandler); + if (event.data.version === expectedWorkerVersion) { + return resolveWorkerReady(); + } else { + // If we have the wrong version, try once to unregister and re-register + return registration.update() + .then(() => navigator.serviceWorker.ready) + .finally(resolveWorkerReady); + } + }; + navigator.serviceWorker.addEventListener('message', versionHandler); + registration.active.postMessage({ channel: 'version' }); + }, + error => { + fatalError(`Could not register service workers: ${error}.`); + resolveWorkerReady(); + }); const forwardFromHostToWorker = (channel) => { hostMessaging.onMessage(channel, event => { @@ -90,10 +105,21 @@ } } - window.createWebviewManager({ + /** @type {import('./main').WebviewHost} */ + const host = { postMessage: hostMessaging.postMessage.bind(hostMessaging), onMessage: hostMessaging.onMessage.bind(hostMessaging), ready: workerReady, - fakeLoad: true - }); -}()); \ No newline at end of file + fakeLoad: !onElectron, + rewriteCSP: onElectron + ? (csp) => { + return csp.replace(/vscode-resource:(?=(\s|;|$))/g, 'vscode-webview-resource:'); + } + : (csp, endpoint) => { + const endpointUrl = new URL(endpoint); + return csp.replace(/(vscode-webview-resource|vscode-resource):(?=(\s|;|$))/g, endpointUrl.origin); + } + }; + + (/** @type {any} */ (window)).createWebviewManager(host); +}()); diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index 8fc84cd45df..630d5d93422 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -11,7 +11,8 @@ * focusIframeOnCreate?: boolean, * ready?: Promise, * onIframeLoaded?: (iframe: HTMLIFrameElement) => void, - * fakeLoad: boolean + * fakeLoad?: boolean, + * rewriteCSP: (existingCSP: string, endpoint?: string) => string, * }} WebviewHost */ @@ -134,13 +135,14 @@ * @return {string} */ function getVsCodeApiScript(allowMultipleAPIAcquire, state) { + const encodedState = state ? encodeURIComponent(state) : undefined; return ` const acquireVsCodeApi = (function() { const originalPostMessage = window.parent.postMessage.bind(window.parent); const targetOrigin = '*'; let acquired = false; - let state = ${state ? `JSON.parse(${JSON.stringify(state)})` : undefined}; + let state = ${state ? `JSON.parse(decodeURIComponent("${encodedState}"))` : undefined}; return () => { if (acquired && !${allowMultipleAPIAcquire}) { @@ -178,7 +180,7 @@ let pendingMessages = []; const initData = { - initialScrollProgress: undefined + initialScrollProgress: undefined, }; @@ -194,6 +196,9 @@ if (body) { body.classList.remove('vscode-light', 'vscode-dark', 'vscode-high-contrast'); body.classList.add(initData.activeTheme); + + body.dataset.vscodeThemeKind = initData.activeTheme; + body.dataset.vscodeThemeName = initData.themeName || ''; } if (initData.styles) { @@ -272,6 +277,14 @@ * @param {KeyboardEvent} e */ const handleInnerKeydown = (e) => { + // If the keypress would trigger a browser event, such as copy or paste, + // make sure we block the browser from dispatching it. Instead VS Code + // handles these events and will dispatch a copy/paste back to the webview + // if needed + if (isCopyPasteOrCut(e) || isUndoRedo(e)) { + e.preventDefault(); + } + host.postMessage('did-keydown', { key: e.key, keyCode: e.keyCode, @@ -284,6 +297,24 @@ }); }; + /** + * @param {KeyboardEvent} e + * @return {boolean} + */ + function isCopyPasteOrCut(e) { + const hasMeta = e.ctrlKey || e.metaKey; + return hasMeta && ['c', 'v', 'x'].includes(e.key); + } + + /** + * @param {KeyboardEvent} e + * @return {boolean} + */ + function isUndoRedo(e) { + const hasMeta = e.ctrlKey || e.metaKey; + return hasMeta && ['z', 'y'].includes(e.key); + } + let isHandlingScroll = false; const handleWheel = (event) => { @@ -360,14 +391,10 @@ if (!csp) { host.postMessage('no-csp-found'); } else { - // Rewrite vscode-resource in csp - if (data.endpoint) { - try { - const endpointUrl = new URL(data.endpoint); - csp.setAttribute('content', csp.getAttribute('content').replace(/vscode-resource:(?=(\s|;|$))/g, endpointUrl.origin)); - } catch (e) { - console.error('Could not rewrite csp'); - } + try { + csp.setAttribute('content', host.rewriteCSP(csp.getAttribute('content'), data.endpoint)); + } catch (e) { + console.error(`Could not rewrite csp: ${e}`); } } @@ -386,6 +413,7 @@ host.onMessage('styles', (_event, data) => { initData.styles = data.styles; initData.activeTheme = data.activeTheme; + initData.themeName = data.themeName; const target = getActiveFrame(); if (!target) { @@ -468,21 +496,45 @@ newFrame.contentDocument.open(); } - newFrame.contentWindow.addEventListener('DOMContentLoaded', e => { + /** + * @param {Document} contentDocument + */ + function onFrameLoaded(contentDocument) { // Workaround for https://bugs.chromium.org/p/chromium/issues/detail?id=978325 setTimeout(() => { if (host.fakeLoad) { - newFrame.contentDocument.open(); - newFrame.contentDocument.write(newDocument); - newFrame.contentDocument.close(); + contentDocument.open(); + contentDocument.write(newDocument); + contentDocument.close(); hookupOnLoadHandlers(newFrame); } - const contentDocument = e.target ? (/** @type {HTMLDocument} */ (e.target)) : undefined; if (contentDocument) { applyStyles(contentDocument, contentDocument.body); } }, 0); - }); + } + + if (host.fakeLoad) { + // On Safari for iframes with scripts disabled, the `DOMContentLoaded` never seems to be fired. + // Use polling instead. + const interval = setInterval(() => { + // If the frame is no longer mounted, loading has stopped + if (!newFrame.parentElement) { + clearInterval(interval); + return; + } + + if (newFrame.contentDocument.readyState === 'complete') { + clearInterval(interval); + onFrameLoaded(newFrame.contentDocument); + } + }, 10); + } else { + newFrame.contentWindow.addEventListener('DOMContentLoaded', e => { + const contentDocument = e.target ? (/** @type {HTMLDocument} */ (e.target)) : undefined; + onFrameLoaded(contentDocument); + }); + } /** * @param {Document} contentDocument @@ -604,6 +656,6 @@ if (typeof module !== 'undefined') { module.exports = createWebviewManager; } else { - window.createWebviewManager = createWebviewManager; + (/** @type {any} */ (window)).createWebviewManager = createWebviewManager; } }()); diff --git a/src/vs/workbench/contrib/webview/browser/pre/service-worker.js b/src/vs/workbench/contrib/webview/browser/pre/service-worker.js index 8d26680d73a..5d043d56ff0 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/service-worker.js +++ b/src/vs/workbench/contrib/webview/browser/pre/service-worker.js @@ -2,6 +2,8 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/// + const VERSION = 1; const rootPath = self.location.pathname.replace(/\/service-worker.js$/, ''); @@ -273,4 +275,4 @@ async function getOuterIframeClient(webviewId) { const clientUrl = new URL(client.url); return (clientUrl.pathname === `${rootPath}/` || clientUrl.pathname === `${rootPath}/index.html`) && clientUrl.search.match(new RegExp('\\bid=' + webviewId)); }); -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/webview/common/themeing.ts b/src/vs/workbench/contrib/webview/browser/themeing.ts similarity index 94% rename from src/vs/workbench/contrib/webview/common/themeing.ts rename to src/vs/workbench/contrib/webview/browser/themeing.ts index 5d66fb268ef..fb21ade8e95 100644 --- a/src/vs/workbench/contrib/webview/common/themeing.ts +++ b/src/vs/workbench/contrib/webview/browser/themeing.ts @@ -10,9 +10,11 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import * as colorRegistry from 'vs/platform/theme/common/colorRegistry'; import { DARK, IColorTheme, IThemeService, LIGHT } from 'vs/platform/theme/common/themeService'; import { Emitter } from 'vs/base/common/event'; +import { DEFAULT_FONT_FAMILY } from 'vs/workbench/browser/style'; interface WebviewThemeData { readonly activeTheme: string; + readonly themeLabel: string; readonly styles: { readonly [key: string]: string | number; }; } @@ -63,7 +65,7 @@ export class WebviewThemeDataProvider extends Disposable { }, {} as { [key: string]: string; }); const styles = { - 'vscode-font-family': 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif', + 'vscode-font-family': DEFAULT_FONT_FAMILY, 'vscode-font-weight': 'normal', 'vscode-font-size': '13px', 'vscode-editor-font-family': editorFontFamily, @@ -73,7 +75,7 @@ export class WebviewThemeDataProvider extends Disposable { }; const activeTheme = ApiThemeClassName.fromTheme(theme); - return { styles, activeTheme }; + return { styles, activeTheme, themeLabel: theme.label, }; } private reset() { diff --git a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts index 7ab892450db..a03eb774545 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts @@ -3,18 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { MultiCommand, RedoCommand, SelectAllCommand, ServicesAccessor, UndoCommand } from 'vs/editor/browser/editorExtensions'; +import { CopyAction, CutAction, PasteAction } from 'vs/editor/contrib/clipboard/clipboard'; import { localize } from 'vs/nls'; -import { registerAction2, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { registerAction2 } from 'vs/platform/actions/common/actions'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; -import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; -import { webviewDeveloperCategory } from 'vs/workbench/contrib/webview/browser/webview'; +import { Webview, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewEditorInputFactory } from 'vs/workbench/contrib/webview/browser/webviewEditorInputFactory'; -import { HideWebViewEditorFindCommand, ReloadWebviewAction, ShowWebViewEditorFindWidgetAction, WebViewEditorFindNextCommand, WebViewEditorFindPreviousCommand, SelectAllWebviewEditorCommand } from '../browser/webviewCommands'; +import { getActiveWebview, HideWebViewEditorFindCommand, ReloadWebviewAction, SelectAllWebviewEditorCommand, ShowWebViewEditorFindWidgetAction, WebViewEditorFindNextCommand, WebViewEditorFindPreviousCommand } from '../browser/webviewCommands'; import { WebviewEditor } from './webviewEditor'; import { WebviewInput } from './webviewEditorInput'; import { IWebviewWorkbenchService, WebviewEditorService } from './webviewWorkbenchService'; @@ -31,32 +31,50 @@ Registry.as(EditorInputExtensions.EditorInputFactor registerSingleton(IWebviewWorkbenchService, WebviewEditorService, true); - -const webviewActiveContextKeyExpr = ContextKeyExpr.and(ContextKeyExpr.equals('activeEditor', WebviewEditor.ID), ContextKeyExpr.not('editorFocus') /* https://github.com/Microsoft/vscode/issues/58668 */)!; - -registerAction2(class extends ShowWebViewEditorFindWidgetAction { - constructor() { super(webviewActiveContextKeyExpr); } -}); - -registerAction2(class extends HideWebViewEditorFindCommand { - constructor() { super(webviewActiveContextKeyExpr); } -}); - -registerAction2(class extends WebViewEditorFindNextCommand { - constructor() { super(webviewActiveContextKeyExpr); } -}); - -registerAction2(class extends WebViewEditorFindPreviousCommand { - constructor() { super(webviewActiveContextKeyExpr); } -}); - -registerAction2(class extends SelectAllWebviewEditorCommand { - constructor() { super(webviewActiveContextKeyExpr); } -}); +registerAction2(ShowWebViewEditorFindWidgetAction); +registerAction2(HideWebViewEditorFindCommand); +registerAction2(WebViewEditorFindNextCommand); +registerAction2(WebViewEditorFindPreviousCommand); +registerAction2(SelectAllWebviewEditorCommand); +registerAction2(ReloadWebviewAction); -const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); -actionRegistry.registerWorkbenchAction( - SyncActionDescriptor.from(ReloadWebviewAction), - 'Reload Webviews', - webviewDeveloperCategory); +function getActiveElectronBasedWebview(accessor: ServicesAccessor): Webview | undefined { + const webview = getActiveWebview(accessor); + if (!webview) { + return undefined; + } + + // Make sure we are really focused on the webview + if (!['WEBVIEW', 'IFRAME'].includes(document.activeElement?.tagName ?? '')) { + return undefined; + } + + if ('getInnerWebview' in (webview as WebviewOverlay)) { + const innerWebview = (webview as WebviewOverlay).getInnerWebview(); + return innerWebview; + } + + return webview; +} + + +const PRIORITY = 100; + +function overrideCommandForWebview(command: MultiCommand | undefined, f: (webview: Webview) => void) { + command?.addImplementation(PRIORITY, accessor => { + const webview = getActiveElectronBasedWebview(accessor); + if (webview) { + f(webview); + return true; + } + return false; + }); +} + +overrideCommandForWebview(UndoCommand, webview => webview.undo()); +overrideCommandForWebview(RedoCommand, webview => webview.redo()); +overrideCommandForWebview(SelectAllCommand, webview => webview.selectAll()); +overrideCommandForWebview(CopyAction, webview => webview.copy()); +overrideCommandForWebview(PasteAction, webview => webview.paste()); +overrideCommandForWebview(CutAction, webview => webview.cut()); diff --git a/src/vs/workbench/contrib/webview/browser/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts index 610f20272be..969dd6728bc 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.ts @@ -34,24 +34,33 @@ export interface WebviewIcons { * Handles the creation of webview elements. */ export interface IWebviewService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; createWebviewElement( id: string, options: WebviewOptions, contentOptions: WebviewContentOptions, + extension: WebviewExtensionDescription | undefined, ): WebviewElement; createWebviewOverlay( id: string, options: WebviewOptions, contentOptions: WebviewContentOptions, + extension: WebviewExtensionDescription | undefined, ): WebviewOverlay; setIcons(id: string, value: WebviewIcons | undefined): void; } +export const enum WebviewContentPurpose { + NotebookRenderer = 'notebookRenderer', + CustomEditor = 'customEditor', +} + export interface WebviewOptions { + // The purpose of the webview; this is (currently) only used for filtering in js-debug + readonly purpose?: WebviewContentPurpose; readonly customClasses?: string; readonly enableFindWidget?: boolean; readonly tryRestoreScrollPosition?: boolean; @@ -71,9 +80,15 @@ export interface WebviewExtensionDescription { readonly id: ExtensionIdentifier; } +export interface IDataLinkClickEvent { + dataURL: string; + downloadName?: string; +} + export interface Webview extends IDisposable { html: string; contentOptions: WebviewContentOptions; + localResourcesRoot: URI[]; extension: WebviewExtensionDescription | undefined; initialScrollProgress: number; state: string | undefined; @@ -88,7 +103,7 @@ export interface Webview extends IDisposable { readonly onMessage: Event; readonly onMissingCsp: Event; - sendMessage(data: any): void; + postMessage(data: any): void; focus(): void; reload(): void; @@ -98,6 +113,11 @@ export interface Webview extends IDisposable { runFindAction(previous: boolean): void; selectAll(): void; + copy(): void; + paste(): void; + cut(): void; + undo(): void; + redo(): void; windowDidDragStart(): void; windowDidDragEnd(): void; @@ -127,4 +147,4 @@ export interface WebviewOverlay extends Webview { layoutWebviewOverElement(element: HTMLElement, dimension?: Dimension): void; } -export const webviewDeveloperCategory = nls.localize('developer', "Developer"); +export const webviewDeveloperCategory = { value: nls.localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer"), original: 'Developer' }; diff --git a/src/vs/workbench/contrib/webview/browser/webviewCommands.ts b/src/vs/workbench/contrib/webview/browser/webviewCommands.ts index 9f949d73926..a0b5326c295 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewCommands.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewCommands.ts @@ -3,28 +3,30 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Action } from 'vs/base/common/actions'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import * as nls from 'vs/nls'; -import { Action2 } from 'vs/platform/actions/common/actions'; -import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; +import { Action2, MenuId } from 'vs/platform/actions/common/actions'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, Webview } from 'vs/workbench/contrib/webview/browser/webview'; +import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, Webview, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/browser/webview'; +import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor'; import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +const webviewActiveContextKeyExpr = ContextKeyExpr.and(ContextKeyExpr.equals('activeEditor', WebviewEditor.ID), ContextKeyExpr.not('editorFocus') /* https://github.com/Microsoft/vscode/issues/58668 */)!; + export class ShowWebViewEditorFindWidgetAction extends Action2 { public static readonly ID = 'editor.action.webvieweditor.showFind'; public static readonly LABEL = nls.localize('editor.action.webvieweditor.showFind', "Show find"); - constructor(contextKeyExpr: ContextKeyExpression) { + constructor() { super({ id: ShowWebViewEditorFindWidgetAction.ID, title: ShowWebViewEditorFindWidgetAction.LABEL, keybinding: { - when: contextKeyExpr, + when: webviewActiveContextKeyExpr, primary: KeyMod.CtrlCmd | KeyCode.KEY_F, weight: KeybindingWeight.EditorContrib } @@ -32,7 +34,7 @@ export class ShowWebViewEditorFindWidgetAction extends Action2 { } public run(accessor: ServicesAccessor): void { - getActiveWebviewEditor(accessor)?.showFind(); + getActiveWebview(accessor)?.showFind(); } } @@ -40,12 +42,12 @@ export class HideWebViewEditorFindCommand extends Action2 { public static readonly ID = 'editor.action.webvieweditor.hideFind'; public static readonly LABEL = nls.localize('editor.action.webvieweditor.hideFind', "Stop find"); - constructor(contextKeyExpr: ContextKeyExpression) { + constructor() { super({ id: HideWebViewEditorFindCommand.ID, title: HideWebViewEditorFindCommand.LABEL, keybinding: { - when: ContextKeyExpr.and(contextKeyExpr, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE), + when: ContextKeyExpr.and(webviewActiveContextKeyExpr, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE), primary: KeyCode.Escape, weight: KeybindingWeight.EditorContrib } @@ -53,7 +55,7 @@ export class HideWebViewEditorFindCommand extends Action2 { } public run(accessor: ServicesAccessor): void { - getActiveWebviewEditor(accessor)?.hideFind(); + getActiveWebview(accessor)?.hideFind(); } } @@ -61,12 +63,12 @@ export class WebViewEditorFindNextCommand extends Action2 { public static readonly ID = 'editor.action.webvieweditor.findNext'; public static readonly LABEL = nls.localize('editor.action.webvieweditor.findNext', 'Find next'); - constructor(contextKeyExpr: ContextKeyExpression) { + constructor() { super({ id: WebViewEditorFindNextCommand.ID, title: WebViewEditorFindNextCommand.LABEL, keybinding: { - when: ContextKeyExpr.and(contextKeyExpr, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED), + when: ContextKeyExpr.and(webviewActiveContextKeyExpr, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED), primary: KeyCode.Enter, weight: KeybindingWeight.EditorContrib } @@ -74,7 +76,7 @@ export class WebViewEditorFindNextCommand extends Action2 { } public run(accessor: ServicesAccessor): void { - getActiveWebviewEditor(accessor)?.runFindAction(false); + getActiveWebview(accessor)?.runFindAction(false); } } @@ -82,12 +84,12 @@ export class WebViewEditorFindPreviousCommand extends Action2 { public static readonly ID = 'editor.action.webvieweditor.findPrevious'; public static readonly LABEL = nls.localize('editor.action.webvieweditor.findPrevious', 'Find previous'); - constructor(contextKeyExpr: ContextKeyExpression) { + constructor() { super({ id: WebViewEditorFindPreviousCommand.ID, title: WebViewEditorFindPreviousCommand.LABEL, keybinding: { - when: ContextKeyExpr.and(contextKeyExpr, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED), + when: ContextKeyExpr.and(webviewActiveContextKeyExpr, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED), primary: KeyMod.Shift | KeyCode.Enter, weight: KeybindingWeight.EditorContrib } @@ -95,7 +97,7 @@ export class WebViewEditorFindPreviousCommand extends Action2 { } public run(accessor: ServicesAccessor): void { - getActiveWebviewEditor(accessor)?.runFindAction(true); + getActiveWebview(accessor)?.runFindAction(true); } } @@ -103,8 +105,8 @@ export class SelectAllWebviewEditorCommand extends Action2 { public static readonly ID = 'editor.action.webvieweditor.selectAll'; public static readonly LABEL = nls.localize('editor.action.webvieweditor.selectAll', 'Select all'); - constructor(contextKeyExpr: ContextKeyExpression) { - const precondition = ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)); + constructor() { + const precondition = ContextKeyExpr.and(webviewActiveContextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)); super({ id: SelectAllWebviewEditorCommand.ID, title: SelectAllWebviewEditorCommand.LABEL, @@ -117,24 +119,28 @@ export class SelectAllWebviewEditorCommand extends Action2 { } public run(accessor: ServicesAccessor): void { - getActiveWebviewEditor(accessor)?.selectAll(); + getActiveWebview(accessor)?.selectAll(); } } -export class ReloadWebviewAction extends Action { +export class ReloadWebviewAction extends Action2 { static readonly ID = 'workbench.action.webview.reloadWebviewAction'; static readonly LABEL = nls.localize('refreshWebviewLabel', "Reload Webviews"); - public constructor( - id: string, - label: string, - @IEditorService private readonly _editorService: IEditorService - ) { - super(id, label); + public constructor() { + super({ + id: ReloadWebviewAction.ID, + title: { value: ReloadWebviewAction.LABEL, original: 'Reload Webviews' }, + category: webviewDeveloperCategory, + menu: [{ + id: MenuId.CommandPalette + }] + }); } - public async run(): Promise { - for (const editor of this._editorService.visibleEditors) { + public async run(accessor: ServicesAccessor): Promise { + const editorService = accessor.get(IEditorService); + for (const editor of editorService.visibleEditors) { if (editor instanceof WebviewInput) { editor.webview.reload(); } @@ -142,7 +148,7 @@ export class ReloadWebviewAction extends Action { } } -export function getActiveWebviewEditor(accessor: ServicesAccessor): Webview | undefined { +export function getActiveWebview(accessor: ServicesAccessor): Webview | undefined { const editorService = accessor.get(IEditorService); const activeEditor = editorService.activeEditor; return activeEditor instanceof WebviewInput ? activeEditor.webview : undefined; diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts index 381feea138d..2b4b56fa148 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts @@ -12,13 +12,14 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; +import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorDropService'; import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; import { WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; -import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; export class WebviewEditor extends BaseEditor { @@ -39,7 +40,8 @@ export class WebviewEditor extends BaseEditor { @IThemeService themeService: IThemeService, @IStorageService storageService: IStorageService, @IEditorService private readonly _editorService: IEditorService, - @IEditorGroupsService private readonly _editorGroupsService: IEditorGroupsService, + @IWorkbenchLayoutService private readonly _workbenchLayoutService: IWorkbenchLayoutService, + @IEditorDropService private readonly _editorDropService: IEditorDropService, @IHostService private readonly _hostService: IHostService, ) { super(WebviewEditor.ID, telemetryService, themeService, storageService); @@ -76,7 +78,7 @@ export class WebviewEditor extends BaseEditor { if (!this._onFocusWindowHandler.value && !isWeb) { // Make sure we restore focus when switching back to a VS Code window this._onFocusWindowHandler.value = this._hostService.onDidChangeFocus(focused => { - if (focused && this._editorService.activeEditorPane === this) { + if (focused && this._editorService.activeEditorPane === this && this._workbenchLayoutService.hasFocus(Parts.EDITOR_PART)) { this.focus(); } }); @@ -146,11 +148,9 @@ export class WebviewEditor extends BaseEditor { this._webviewVisibleDisposables.clear(); // Webviews are not part of the normal editor dom, so we have to register our own drag and drop handler on them. - if (this._editorGroupsService instanceof EditorPart) { - this._webviewVisibleDisposables.add(this._editorGroupsService.createEditorDropTarget(input.webview.container, { - groupContainsPredicate: (group) => this.group?.id === group.group.id - })); - } + this._webviewVisibleDisposables.add(this._editorDropService.createEditorDropTarget(input.webview.container, { + containsGroup: (group) => this.group?.id === group.group.id + })); this._webviewVisibleDisposables.add(DOM.addDisposableListener(window, DOM.EventType.DRAG_START, () => { this.webview?.windowDidDragStart(); diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts index cee6146281e..c5b4ebcd05e 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts @@ -99,10 +99,6 @@ export class WebviewInput extends EditorInput { this._group = group; } - public async resolve(): Promise { - return null; - } - public supportsSplitEditor() { return false; } diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts index e78b7edb957..7d1205a8495 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts @@ -78,6 +78,7 @@ export class WebviewEditorInputFactory implements IEditorInputFactory { extension: reviveWebviewExtensionDescription(data.extensionId, data.extensionLocation), iconPath: reviveIconPath(data.iconPath), state: reviveState(data.state), + options: reviveOptions(data.options) }; } @@ -125,6 +126,8 @@ function reviveIconPath(data: SerializedIconPath | undefined) { return light && dark ? { light, dark } : undefined; } +function reviveUri(data: string | UriComponents): URI; +function reviveUri(data: string | UriComponents | undefined): URI | undefined; function reviveUri(data: string | UriComponents | undefined): URI | undefined { if (!data) { return undefined; @@ -143,3 +146,10 @@ function reviveUri(data: string | UriComponents | undefined): URI | undefined { function reviveState(state: unknown | undefined): undefined | string { return typeof state === 'string' ? state : undefined; } + +function reviveOptions(options: WebviewInputOptions): WebviewInputOptions { + return { + ...options, + localResourceRoots: options.localResourceRoots?.map(uri => reviveUri(uri)), + }; +} diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index 03133778a0b..792d98a57bb 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -4,20 +4,25 @@ *--------------------------------------------------------------------------------------------*/ import { addDisposableListener } from 'vs/base/browser/dom'; +import { streamToBuffer } from 'vs/base/common/buffer'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { isWeb } from 'vs/base/common/platform'; +import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IFileService } from 'vs/platform/files/common/files'; -import { ITunnelService } from 'vs/platform/remote/common/tunnel'; -import { Webview, WebviewContentOptions, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; -import { WebviewPortMappingManager } from 'vs/workbench/contrib/webview/common/portMapping'; -import { loadLocalResource, WebviewResourceResponse } from 'vs/workbench/contrib/webview/common/resourceLoader'; -import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/common/themeing'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { BaseWebview, WebviewMessageChannels } from 'vs/workbench/contrib/webview/browser/baseWebviewElement'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ILogService } from 'vs/platform/log/common/log'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; +import { ITunnelService } from 'vs/platform/remote/common/tunnel'; +import { IRequestService } from 'vs/platform/request/common/request'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { loadLocalResource, WebviewResourceResponse } from 'vs/platform/webview/common/resourceLoader'; +import { WebviewPortMappingManager } from 'vs/platform/webview/common/webviewPortMapping'; +import { BaseWebview, WebviewMessageChannels } from 'vs/workbench/contrib/webview/browser/baseWebviewElement'; +import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing'; +import { Webview, WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; export class IFrameWebview extends BaseWebview implements Webview { private readonly _portMappingManager: WebviewPortMappingManager; @@ -26,22 +31,22 @@ export class IFrameWebview extends BaseWebview implements Web id: string, options: WebviewOptions, contentOptions: WebviewContentOptions, + extension: WebviewExtensionDescription | undefined, webviewThemeDataProvider: WebviewThemeDataProvider, + @INotificationService notificationService: INotificationService, @ITunnelService tunnelService: ITunnelService, @IFileService private readonly fileService: IFileService, - @IConfigurationService private readonly _configurationService: IConfigurationService, + @IRequestService private readonly requestService: IRequestService, @ITelemetryService telemetryService: ITelemetryService, - @IEnvironmentService environementService: IEnvironmentService, - @IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService, + @IEnvironmentService environmentService: IEnvironmentService, + @IWorkbenchEnvironmentService private readonly _workbenchEnvironmentService: IWorkbenchEnvironmentService, + @IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService, + @ILogService logService: ILogService, ) { - super(id, options, contentOptions, webviewThemeDataProvider, telemetryService, environementService, workbenchEnvironmentService); - - if (!this.useExternalEndpoint && (!workbenchEnvironmentService.options || typeof workbenchEnvironmentService.webviewExternalEndpoint !== 'string')) { - throw new Error('To use iframe based webviews, you must configure `environmentService.webviewExternalEndpoint`'); - } + super(id, options, contentOptions, extension, webviewThemeDataProvider, notificationService, logService, telemetryService, environmentService, _workbenchEnvironmentService); this._portMappingManager = this._register(new WebviewPortMappingManager( - () => this.extension ? this.extension.location : undefined, + () => this.extension?.location, () => this.content.options.portMapping || [], tunnelService )); @@ -56,19 +61,27 @@ export class IFrameWebview extends BaseWebview implements Web this._register(this.on(WebviewMessageChannels.loadLocalhost, (entry: any) => { this.localLocalhost(entry.origin); })); + + this.initElement(extension, options); } - protected createElement(options: WebviewOptions) { + protected createElement(options: WebviewOptions, _contentOptions: WebviewContentOptions) { + // Do not start loading the webview yet. + // Wait the end of the ctor when all listeners have been hooked up. const element = document.createElement('iframe'); element.className = `webview ${options.customClasses || ''}`; element.sandbox.add('allow-scripts', 'allow-same-origin', 'allow-forms'); - element.setAttribute('src', `${this.externalEndpoint}/index.html?id=${this.id}`); element.style.border = 'none'; element.style.width = '100%'; element.style.height = '100%'; return element; } + protected initElement(extension: WebviewExtensionDescription | undefined, options: WebviewOptions) { + // The extensionId and purpose in the URL are used for filtering in js-debug: + this.element!.setAttribute('src', `${this.externalEndpoint}/index.html?id=${this.id}&extensionId=${extension?.id.value ?? ''}&purpose=${options.purpose}`); + } + private get externalEndpoint(): string { const endpoint = this.workbenchEnvironmentService.webviewExternalEndpoint!.replace('{{uuid}}', this.id); if (endpoint[endpoint.length - 1] === '/') { @@ -77,10 +90,6 @@ export class IFrameWebview extends BaseWebview implements Web return endpoint; } - private get useExternalEndpoint(): boolean { - return isWeb || this._configurationService.getValue('webview.experimental.useExternalEndpoint'); - } - public mountTo(parent: HTMLElement) { if (this.element) { parent.appendChild(this.element); @@ -91,9 +100,15 @@ export class IFrameWebview extends BaseWebview implements Web super.html = this.preprocessHtml(value); } - private preprocessHtml(value: string): string { + protected preprocessHtml(value: string): string { return value - .replace(/(["'])vscode-resource:(\/\/([^\s\/'"]+?)(?=\/))?([^\s'"]+?)(["'])/gi, (match, startQuote, _1, scheme, path, endQuote) => { + .replace(/(["'])(?:vscode-resource):(\/\/([^\s\/'"]+?)(?=\/))?([^\s'"]+?)(["'])/gi, (match, startQuote, _1, scheme, path, endQuote) => { + if (scheme) { + return `${startQuote}${this.externalEndpoint}/vscode-resource/${scheme}${path}${endQuote}`; + } + return `${startQuote}${this.externalEndpoint}/vscode-resource/file${path}${endQuote}`; + }) + .replace(/(["'])(?:vscode-webview-resource):(\/\/[^\s\/'"]+\/([^\s\/'"]+?)(?=\/))?([^\s'"]+?)(["'])/gi, (match, startQuote, _1, scheme, path, endQuote) => { if (scheme) { return `${startQuote}${this.externalEndpoint}/vscode-resource/${scheme}${path}${endQuote}`; } @@ -101,7 +116,7 @@ export class IFrameWebview extends BaseWebview implements Web }); } - protected get extraContentOptions() { + protected get extraContentOptions(): any { return { endpoint: this.externalEndpoint, }; @@ -127,18 +142,47 @@ export class IFrameWebview extends BaseWebview implements Web private async loadResource(requestPath: string, uri: URI) { try { - const result = await loadLocalResource(uri, this.fileService, this.extension ? this.extension.location : undefined, - () => (this.content.options.localResourceRoots || [])); + const remoteAuthority = this._workbenchEnvironmentService.configuration.remoteAuthority; + const remoteConnectionData = remoteAuthority ? this._remoteAuthorityResolverService.getConnectionData(remoteAuthority) : null; + const extensionLocation = this.extension?.location; + + // If we are loading a file resource from a remote extension, rewrite the uri to go remote + let rewriteUri: undefined | ((uri: URI) => URI); + if (extensionLocation?.scheme === REMOTE_HOST_SCHEME) { + rewriteUri = (uri) => { + if (uri.scheme === Schemas.file && extensionLocation?.scheme === REMOTE_HOST_SCHEME) { + return URI.from({ + scheme: REMOTE_HOST_SCHEME, + authority: extensionLocation.authority, + path: '/vscode-resource', + query: JSON.stringify({ + requestResourcePath: uri.path + }) + }); + } + return uri; + }; + } + + const result = await loadLocalResource(uri, { + extensionLocation: extensionLocation, + roots: this.content.options.localResourceRoots || [], + remoteConnectionData, + rewriteUri, + }, { + readFileStream: (resource) => this.fileService.readFileStream(resource).then(x => x.value), + }, this.requestService); if (result.type === WebviewResourceResponse.Type.Success) { + const { buffer } = await streamToBuffer(result.stream); return this._send('did-load-resource', { status: 200, path: requestPath, mime: result.mimeType, - data: result.data.buffer + data: buffer, }); } - } catch { + } catch { // noop } @@ -149,14 +193,16 @@ export class IFrameWebview extends BaseWebview implements Web } private async localLocalhost(origin: string) { - const redirect = await this._portMappingManager.getRedirect(origin); + const authority = this._workbenchEnvironmentService.configuration.remoteAuthority; + const resolveAuthority = authority ? await this._remoteAuthorityResolverService.resolveAuthority(authority) : undefined; + const redirect = resolveAuthority ? await this._portMappingManager.getRedirect(resolveAuthority.authority, origin) : undefined; return this._send('did-load-localhost', { origin, location: redirect }); } - protected postMessage(channel: string, data?: any): void { + protected doPostMessage(channel: string, data?: any): void { if (this.element) { this.element.contentWindow!.postMessage({ channel, args: data }, '*'); } diff --git a/src/vs/workbench/contrib/webview/browser/webviewIconManager.ts b/src/vs/workbench/contrib/webview/browser/webviewIconManager.ts index ee10e07a8d6..6091fed2b34 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewIconManager.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewIconManager.ts @@ -53,8 +53,8 @@ export class WebviewIconManager { const webviewSelector = `.show-file-icons .webview-${key}-name-file-icon::before`; try { cssRules.push( - `.vs ${webviewSelector} { content: ""; background-image: ${dom.asCSSUrl(value.light)}; }`, - `.vs-dark ${webviewSelector} { content: ""; background-image: ${dom.asCSSUrl(value.dark)}; }` + `.monaco-workbench.vs ${webviewSelector} { content: ""; background-image: ${dom.asCSSUrl(value.light)}; }`, + `.monaco-workbench.vs-dark ${webviewSelector} { content: ""; background-image: ${dom.asCSSUrl(value.dark)}; }` ); } catch { // noop diff --git a/src/vs/workbench/contrib/webview/browser/webviewService.ts b/src/vs/workbench/contrib/webview/browser/webviewService.ts index 5336e2d2750..688b513948d 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewService.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewService.ts @@ -5,14 +5,14 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IWebviewService, WebviewContentOptions, WebviewOverlay, WebviewElement, WebviewIcons, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; +import { IWebviewService, WebviewContentOptions, WebviewOverlay, WebviewElement, WebviewIcons, WebviewOptions, WebviewExtensionDescription } from 'vs/workbench/contrib/webview/browser/webview'; import { IFrameWebview } from 'vs/workbench/contrib/webview/browser/webviewElement'; -import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/common/themeing'; +import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing'; import { DynamicWebviewEditorOverlay } from './dynamicWebviewEditorOverlay'; import { WebviewIconManager } from './webviewIconManager'; export class WebviewService implements IWebviewService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _webviewThemeDataProvider: WebviewThemeDataProvider; private readonly _iconManager: WebviewIconManager; @@ -27,17 +27,19 @@ export class WebviewService implements IWebviewService { createWebviewElement( id: string, options: WebviewOptions, - contentOptions: WebviewContentOptions + contentOptions: WebviewContentOptions, + extension: WebviewExtensionDescription | undefined, ): WebviewElement { - return this._instantiationService.createInstance(IFrameWebview, id, options, contentOptions, this._webviewThemeDataProvider); + return this._instantiationService.createInstance(IFrameWebview, id, options, contentOptions, extension, this._webviewThemeDataProvider); } createWebviewOverlay( id: string, options: WebviewOptions, contentOptions: WebviewContentOptions, + extension: WebviewExtensionDescription | undefined, ): WebviewOverlay { - return this._instantiationService.createInstance(DynamicWebviewEditorOverlay, id, options, contentOptions); + return this._instantiationService.createInstance(DynamicWebviewEditorOverlay, id, options, contentOptions, extension); } setIcons(id: string, iconPath: WebviewIcons | undefined): void { diff --git a/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService.ts b/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService.ts index 1f55aeb7035..df34fcf7f30 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService.ts @@ -46,7 +46,7 @@ export function areWebviewInputOptionsEqual(a: WebviewInputOptions, b: WebviewIn } export interface IWebviewWorkbenchService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; createWebview( id: string, @@ -99,9 +99,6 @@ export interface WebviewResolver { } function canRevive(reviver: WebviewResolver, webview: WebviewInput): boolean { - if (webview.isDisposed()) { - return false; - } return reviver.canResolve(webview); } @@ -173,7 +170,7 @@ class RevivalPool { export class WebviewEditorService implements IWebviewWorkbenchService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _revivers = new Set(); private readonly _revivalPool = new RevivalPool(); @@ -307,14 +304,12 @@ export class WebviewEditorService implements IWebviewWorkbenchService { private createWebviewElement( id: string, extension: WebviewExtensionDescription | undefined, - options: WebviewInputOptions + options: WebviewInputOptions, ) { - const webview = this._webviewService.createWebviewOverlay(id, { + return this._webviewService.createWebviewOverlay(id, { enableFindWidget: options.enableFindWidget, retainContextWhenHidden: options.retainContextWhenHidden - }, options); - webview.extension = extension; - return webview; + }, options, extension); } } diff --git a/src/vs/workbench/contrib/webview/common/resourceLoader.ts b/src/vs/workbench/contrib/webview/common/resourceLoader.ts deleted file mode 100644 index d3ced130584..00000000000 --- a/src/vs/workbench/contrib/webview/common/resourceLoader.ts +++ /dev/null @@ -1,107 +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 { VSBuffer } from 'vs/base/common/buffer'; -import { sep } from 'vs/base/common/path'; -import { URI } from 'vs/base/common/uri'; -import { IFileService } from 'vs/platform/files/common/files'; -import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; -import { getWebviewContentMimeType } from 'vs/workbench/contrib/webview/common/mimeTypes'; -import { isUNC } from 'vs/base/common/extpath'; - -export const WebviewResourceScheme = 'vscode-resource'; - -export namespace WebviewResourceResponse { - export enum Type { Success, Failed, AccessDenied } - - export class Success { - readonly type = Type.Success; - - constructor( - public readonly data: VSBuffer, - public readonly mimeType: string - ) { } - } - - export const Failed = { type: Type.Failed } as const; - export const AccessDenied = { type: Type.AccessDenied } as const; - - export type Response = Success | typeof Failed | typeof AccessDenied; - -} -async function resolveContent( - fileService: IFileService, - resource: URI, - mime: string -): Promise { - try { - const contents = await fileService.readFile(resource); - return new WebviewResourceResponse.Success(contents.value, mime); - } catch (err) { - console.log(err); - return WebviewResourceResponse.Failed; - } -} - -export async function loadLocalResource( - requestUri: URI, - fileService: IFileService, - extensionLocation: URI | undefined, - getRoots: () => ReadonlyArray -): Promise { - const normalizedPath = normalizeRequestPath(requestUri); - - for (const root of getRoots()) { - if (!containsResource(root, normalizedPath)) { - continue; - } - - if (extensionLocation && extensionLocation.scheme === REMOTE_HOST_SCHEME) { - const redirectedUri = URI.from({ - scheme: REMOTE_HOST_SCHEME, - authority: extensionLocation.authority, - path: '/vscode-resource', - query: JSON.stringify({ - requestResourcePath: normalizedPath.path - }) - }); - return resolveContent(fileService, redirectedUri, getWebviewContentMimeType(requestUri)); - } else { - return resolveContent(fileService, normalizedPath, getWebviewContentMimeType(normalizedPath)); - } - } - - return WebviewResourceResponse.AccessDenied; -} - -function normalizeRequestPath(requestUri: URI) { - if (requestUri.scheme !== WebviewResourceScheme) { - return requestUri; - } - - // Modern vscode-resources uris put the scheme of the requested resource as the authority - if (requestUri.authority) { - return URI.parse(`${requestUri.authority}:${encodeURIComponent(requestUri.path).replace(/%2F/g, '/')}`).with({ - query: requestUri.query, - fragment: requestUri.fragment - }); - } - - // Old style vscode-resource uris lose the scheme of the resource which means they are unable to - // load a mix of local and remote content properly. - return requestUri.with({ scheme: 'file' }); -} - -function containsResource(root: URI, resource: URI): boolean { - let rootPath = root.fsPath + (root.fsPath.endsWith(sep) ? '' : sep); - let resourceFsPath = resource.fsPath; - - if (isUNC(root.fsPath) && isUNC(resource.fsPath)) { - rootPath = rootPath.toLowerCase(); - resourceFsPath = resourceFsPath.toLowerCase(); - } - - return resourceFsPath.startsWith(rootPath); -} diff --git a/src/vs/workbench/contrib/webview/common/webviewUri.ts b/src/vs/workbench/contrib/webview/common/webviewUri.ts new file mode 100644 index 00000000000..a4e9e92e391 --- /dev/null +++ b/src/vs/workbench/contrib/webview/common/webviewUri.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 { URI } from 'vs/base/common/uri'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; + + +export function asWebviewUri( + environmentService: IWorkbenchEnvironmentService, + uuid: string, + resource: URI, +): URI { + const uri = environmentService.webviewResourceRoot + // Make sure we preserve the scheme of the resource but convert it into a normal path segment + // The scheme is important as we need to know if we are requesting a local or a remote resource. + .replace('{{resource}}', resource.scheme + withoutScheme(resource)) + .replace('{{uuid}}', uuid); + return URI.parse(uri); +} + +function withoutScheme(resource: URI): string { + return resource.toString().replace(/^\S+?:/, ''); +} diff --git a/src/vs/workbench/contrib/webview/electron-browser/iframeWebviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/iframeWebviewElement.ts new file mode 100644 index 00000000000..6e7570d370a --- /dev/null +++ b/src/vs/workbench/contrib/webview/electron-browser/iframeWebviewElement.ts @@ -0,0 +1,140 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ThrottledDelayer } from 'vs/base/common/async'; +import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ILogService } from 'vs/platform/log/common/log'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { ITunnelService } from 'vs/platform/remote/common/tunnel'; +import { IRequestService } from 'vs/platform/request/common/request'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing'; +import { WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; +import { IFrameWebview } from 'vs/workbench/contrib/webview/browser/webviewElement'; +import { rewriteVsCodeResourceUrls, WebviewResourceRequestManager } from 'vs/workbench/contrib/webview/electron-browser/resourceLoading'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; + +/** + * Webview backed by an iframe but that uses Electron APIs to power the webview. + */ +export class ElectronIframeWebview extends IFrameWebview { + + private readonly _resourceRequestManager: WebviewResourceRequestManager; + private _messagePromise = Promise.resolve(); + + private readonly _focusDelayer = this._register(new ThrottledDelayer(10)); + private _elementFocusImpl!: (options?: FocusOptions | undefined) => void; + + constructor( + id: string, + options: WebviewOptions, + contentOptions: WebviewContentOptions, + extension: WebviewExtensionDescription | undefined, + webviewThemeDataProvider: WebviewThemeDataProvider, + @ITunnelService tunnelService: ITunnelService, + @IFileService fileService: IFileService, + @IRequestService requestService: IRequestService, + @ITelemetryService telemetryService: ITelemetryService, + @IEnvironmentService environmentService: IEnvironmentService, + @IWorkbenchEnvironmentService _workbenchEnvironmentService: IWorkbenchEnvironmentService, + @IRemoteAuthorityResolverService _remoteAuthorityResolverService: IRemoteAuthorityResolverService, + @ILogService logService: ILogService, + @IInstantiationService instantiationService: IInstantiationService, + @INotificationService noficationService: INotificationService, + ) { + super(id, options, contentOptions, extension, webviewThemeDataProvider, + noficationService, tunnelService, fileService, requestService, telemetryService, environmentService, _workbenchEnvironmentService, _remoteAuthorityResolverService, logService); + + this._resourceRequestManager = this._register(instantiationService.createInstance(WebviewResourceRequestManager, id, extension, this.content.options, Promise.resolve(undefined))); + } + + protected createElement(options: WebviewOptions, contentOptions: WebviewContentOptions) { + const element = super.createElement(options, contentOptions); + this._elementFocusImpl = element.focus.bind(element); + element.focus = () => { + this.doFocus(); + }; + return element; + } + + protected initElement(extension: WebviewExtensionDescription | undefined, options: WebviewOptions) { + // The extensionId and purpose in the URL are used for filtering in js-debug: + this.element!.setAttribute('src', `${Schemas.vscodeWebview}://${this.id}/index.html?id=${this.id}&platform=electron&extensionId=${extension?.id.value ?? ''}&purpose=${options.purpose}`); + } + + public set contentOptions(options: WebviewContentOptions) { + this._resourceRequestManager.update(options); + super.contentOptions = options; + } + + public set localResourcesRoot(resources: URI[]) { + this._resourceRequestManager.update({ + ...this.contentOptions, + localResourceRoots: resources, + }); + super.localResourcesRoot = resources; + } + + protected get extraContentOptions() { + return {}; + } + + protected async doPostMessage(channel: string, data?: any): Promise { + this._messagePromise = this._messagePromise + .then(() => this._resourceRequestManager.ensureReady()) + .then(() => { + this.element?.contentWindow!.postMessage({ channel, args: data }, '*'); + }); + } + + protected preprocessHtml(value: string): string { + return rewriteVsCodeResourceUrls(this.id, value); + } + + public focus(): void { + this.doFocus(); + + // Handle focus change programmatically (do not rely on event from ) + this.handleFocusChange(true); + } + + private doFocus() { + if (!this.element) { + return; + } + + // Workaround for https://github.com/microsoft/vscode/issues/75209 + // .focus is async for imframes so for a sequence of actions such as: + // + // 1. Open webview + // 1. Show quick pick from command palette + // + // We end up focusing the webview after showing the quick pick, which causes + // the quick pick to instantly dismiss. + // + // Workaround this by debouncing the focus and making sure we are not focused on an input + // when we try to re-focus. + this._focusDelayer.trigger(async () => { + if (!this.focused || !this.element) { + return; + } + + if (document.activeElement?.tagName === 'INPUT') { + return; + } + try { + this._elementFocusImpl(); + } catch { + // noop + } + this._send('focus'); + }); + } +} diff --git a/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js b/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js index e87ab16dba4..293eecf7851 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js +++ b/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js @@ -16,7 +16,6 @@ }; }()); - // @ts-ignore const ipcRenderer = require('electron').ipcRenderer; let isInDevelopmentMode = false; @@ -62,7 +61,10 @@ isMouseDown = false; }); newFrame.contentWindow.addEventListener('mousemove', tryDispatchSyntheticMouseEvent); - } + }, + rewriteCSP: (csp) => { + return csp.replace(/vscode-resource:(?=(\s|;|$))/g, 'vscode-webview-resource:'); + }, }; host.onMessage('devtools-opened', () => { diff --git a/src/vs/workbench/contrib/webview/electron-browser/pre/index.html b/src/vs/workbench/contrib/webview/electron-browser/pre/index.html new file mode 100644 index 00000000000..591caaf7d3a --- /dev/null +++ b/src/vs/workbench/contrib/webview/electron-browser/pre/index.html @@ -0,0 +1,8 @@ + + + +Virtual Document + + + + diff --git a/src/vs/workbench/contrib/webview/electron-browser/resourceLoading.ts b/src/vs/workbench/contrib/webview/electron-browser/resourceLoading.ts new file mode 100644 index 00000000000..e2d4a407857 --- /dev/null +++ b/src/vs/workbench/contrib/webview/electron-browser/resourceLoading.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 { equals } from 'vs/base/common/arrays'; +import { streamToBuffer } from 'vs/base/common/buffer'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; +import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; +import * as modes from 'vs/editor/common/modes'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { IRequestService } from 'vs/platform/request/common/request'; +import { loadLocalResource, WebviewResourceResponse } from 'vs/platform/webview/common/resourceLoader'; +import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; +import { WebviewContentOptions, WebviewExtensionDescription } from 'vs/workbench/contrib/webview/browser/webview'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; + +/** + * Try to rewrite `vscode-resource:` urls in html + */ +export function rewriteVsCodeResourceUrls( + id: string, + html: string, +): string { + return html + .replace(/(["'])vscode-resource:(\/\/([^\s\/'"]+?)(?=\/))?([^\s'"]+?)(["'])/gi, (_match, startQuote, _1, scheme, path, endQuote) => { + if (scheme) { + return `${startQuote}${Schemas.vscodeWebviewResource}://${id}/${scheme}${path}${endQuote}`; + } + if (!path.startsWith('//')) { + // Add an empty authority if we don't already have one + path = '//' + path; + } + return `${startQuote}${Schemas.vscodeWebviewResource}://${id}/file${path}${endQuote}`; + }); +} + +/** + * Manages the loading of resources inside of a webview. + */ +export class WebviewResourceRequestManager extends Disposable { + + private readonly _webviewManagerService: IWebviewManagerService; + + private _localResourceRoots: ReadonlyArray; + private _portMappings: ReadonlyArray; + + private _ready: Promise; + + constructor( + private readonly id: string, + private readonly extension: WebviewExtensionDescription | undefined, + initialContentOptions: WebviewContentOptions, + getWebContentsId: Promise, + @ILogService private readonly _logService: ILogService, + @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IMainProcessService mainProcessService: IMainProcessService, + @IElectronService electronService: IElectronService, + @IFileService fileService: IFileService, + @IRequestService requestService: IRequestService, + ) { + super(); + + this._logService.debug(`WebviewResourceRequestManager(${this.id}): init`); + + this._webviewManagerService = createChannelSender(mainProcessService.getChannel('webview')); + + this._localResourceRoots = initialContentOptions.localResourceRoots || []; + this._portMappings = initialContentOptions.portMapping || []; + + const remoteAuthority = environmentService.configuration.remoteAuthority; + const remoteConnectionData = remoteAuthority ? remoteAuthorityResolverService.getConnectionData(remoteAuthority) : null; + + this._ready = getWebContentsId.then(async (webContentsId) => { + this._logService.debug(`WebviewResourceRequestManager(${this.id}): did-start-loading`); + await this._webviewManagerService.registerWebview(this.id, webContentsId, electronService.windowId, { + extensionLocation: this.extension?.location.toJSON(), + localResourceRoots: this._localResourceRoots.map(x => x.toJSON()), + remoteConnectionData: remoteConnectionData, + portMappings: this._portMappings, + }); + + this._logService.debug(`WebviewResourceRequestManager(${this.id}): did register`); + }); + + if (remoteAuthority) { + this._register(remoteAuthorityResolverService.onDidChangeConnectionData(() => { + const update = this._webviewManagerService.updateWebviewMetadata(this.id, { + remoteConnectionData: remoteAuthority ? remoteAuthorityResolverService.getConnectionData(remoteAuthority) : null, + }); + this._ready = this._ready.then(() => update); + })); + } + + this._register(toDisposable(() => this._webviewManagerService.unregisterWebview(this.id))); + + const loadResourceChannel = `vscode:loadWebviewResource-${id}`; + const loadResourceListener = async (_event: any, requestId: number, resource: UriComponents) => { + try { + const response = await loadLocalResource(URI.revive(resource), { + extensionLocation: this.extension?.location, + roots: this._localResourceRoots, + remoteConnectionData: remoteConnectionData, + }, { + readFileStream: (resource) => fileService.readFileStream(resource).then(x => x.value), + }, requestService); + + if (response.type === WebviewResourceResponse.Type.Success) { + const buffer = await streamToBuffer(response.stream); + return this._webviewManagerService.didLoadResource(requestId, buffer); + } + } catch { + // Noop + } + this._webviewManagerService.didLoadResource(requestId, undefined); + }; + + ipcRenderer.on(loadResourceChannel, loadResourceListener); + this._register(toDisposable(() => ipcRenderer.removeListener(loadResourceChannel, loadResourceListener))); + } + + public update(options: WebviewContentOptions) { + const localResourceRoots = options.localResourceRoots || []; + const portMappings = options.portMapping || []; + + if (!this.needsUpdate(localResourceRoots, portMappings)) { + return; + } + + this._localResourceRoots = localResourceRoots; + this._portMappings = portMappings; + + this._logService.debug(`WebviewResourceRequestManager(${this.id}): will update`); + + const update = this._webviewManagerService.updateWebviewMetadata(this.id, { + localResourceRoots: localResourceRoots.map(x => x.toJSON()), + portMappings: portMappings, + }).then(() => { + this._logService.debug(`WebviewResourceRequestManager(${this.id}): did update`); + }); + + this._ready = this._ready.then(() => update); + } + + private needsUpdate( + localResourceRoots: readonly URI[], + portMappings: readonly modes.IWebviewPortMapping[], + ): boolean { + return !( + equals(this._localResourceRoots, localResourceRoots, (a, b) => a.toString() === b.toString()) + && equals(this._portMappings, portMappings, (a, b) => a.extensionHostPort === b.extensionHostPort && a.webviewPort === b.webviewPort) + ); + } + + public ensureReady(): Promise { + return this._ready; + } +} diff --git a/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts b/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts index ed4ad60e2ed..fb85f3a039f 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts @@ -3,37 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { isMacintosh } from 'vs/base/common/platform'; -import { SyncActionDescriptor, registerAction2 } from 'vs/platform/actions/common/actions'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { registerAction2 } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; -import { IWebviewService, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/browser/webview'; -import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor'; +import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview'; import * as webviewCommands from 'vs/workbench/contrib/webview/electron-browser/webviewCommands'; import { ElectronWebviewService } from 'vs/workbench/contrib/webview/electron-browser/webviewService'; registerSingleton(IWebviewService, ElectronWebviewService, true); -const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); - -actionRegistry.registerWorkbenchAction( - SyncActionDescriptor.from(webviewCommands.OpenWebviewDeveloperToolsAction), - webviewCommands.OpenWebviewDeveloperToolsAction.ALIAS, - webviewDeveloperCategory); - -function registerWebViewCommands(editorId: string): void { - const contextKeyExpr = ContextKeyExpr.and(ContextKeyExpr.equals('activeEditor', editorId), ContextKeyExpr.not('editorFocus') /* https://github.com/Microsoft/vscode/issues/58668 */)!; - - // These commands are only needed on MacOS where we have to disable the menu bar commands - if (isMacintosh) { - registerAction2(class extends webviewCommands.CopyWebviewEditorCommand { constructor() { super(contextKeyExpr); } }); - registerAction2(class extends webviewCommands.PasteWebviewEditorCommand { constructor() { super(contextKeyExpr); } }); - registerAction2(class extends webviewCommands.CutWebviewEditorCommand { constructor() { super(contextKeyExpr); } }); - registerAction2(class extends webviewCommands.UndoWebviewEditorCommand { constructor() { super(contextKeyExpr); } }); - registerAction2(class extends webviewCommands.RedoWebviewEditorCommand { constructor() { super(contextKeyExpr); } }); - } -} - -registerWebViewCommands(WebviewEditor.ID); +registerAction2(webviewCommands.OpenWebviewDeveloperToolsAction); diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts index 1486280626c..f9872c06d82 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts @@ -4,28 +4,23 @@ *--------------------------------------------------------------------------------------------*/ import { WebviewTag } from 'electron'; -import { Action } from 'vs/base/common/actions'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import * as nls from 'vs/nls'; import { Action2 } from 'vs/platform/actions/common/actions'; -import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; -import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { WebviewOverlay, webviewHasOwnEditFunctionsContextKey } from 'vs/workbench/contrib/webview/browser/webview'; -import { getActiveWebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewCommands'; -import { ElectronWebviewBasedWebview } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; +import * as nls from 'vs/nls'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { webviewDeveloperCategory } from 'vs/workbench/contrib/webview/browser/webview'; -export class OpenWebviewDeveloperToolsAction extends Action { - static readonly ID = 'workbench.action.webview.openDeveloperTools'; - static readonly ALIAS = 'Open Webview Developer Tools'; - static readonly LABEL = nls.localize('openToolsLabel', "Open Webview Developer Tools"); +export class OpenWebviewDeveloperToolsAction extends Action2 { - public constructor(id: string, label: string) { - super(id, label); + constructor() { + super({ + id: 'workbench.action.webview.openDeveloperTools', + title: { value: nls.localize('openToolsLabel', "Open Webview Developer Tools"), original: 'Open Webview Developer Tools' }, + category: webviewDeveloperCategory, + f1: true + }); } - public async run(): Promise { + async run(accessor: ServicesAccessor): Promise { const elements = document.querySelectorAll('webview.ready'); for (let i = 0; i < elements.length; i++) { try { @@ -34,131 +29,5 @@ export class OpenWebviewDeveloperToolsAction extends Action { console.error(e); } } - return true; } } - -export class CopyWebviewEditorCommand extends Action2 { - public static readonly ID = 'editor.action.webvieweditor.copy'; - public static readonly LABEL = nls.localize('editor.action.webvieweditor.copy', "Copy2"); - - constructor(contextKeyExpr: ContextKeyExpression, readonly getActiveElectronBasedWebviewDelegate: (accessor: ServicesAccessor) => ElectronWebviewBasedWebview | undefined = getActiveElectronBasedWebview) { - super({ - id: CopyWebviewEditorCommand.ID, - title: CopyWebviewEditorCommand.LABEL, - keybinding: { - when: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), - primary: KeyMod.CtrlCmd | KeyCode.KEY_C, - weight: KeybindingWeight.EditorContrib - } - }); - } - - public run(accessor: ServicesAccessor): void { - this.getActiveElectronBasedWebviewDelegate(accessor)?.copy(); - } -} - -export class PasteWebviewEditorCommand extends Action2 { - public static readonly ID = 'editor.action.webvieweditor.paste'; - public static readonly LABEL = nls.localize('editor.action.webvieweditor.paste', 'Paste'); - - constructor(contextKeyExpr: ContextKeyExpression, readonly getActiveElectronBasedWebviewDelegate: (accessor: ServicesAccessor) => ElectronWebviewBasedWebview | undefined = getActiveElectronBasedWebview) { - super({ - id: PasteWebviewEditorCommand.ID, - title: PasteWebviewEditorCommand.LABEL, - keybinding: { - when: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), - primary: KeyMod.CtrlCmd | KeyCode.KEY_V, - weight: KeybindingWeight.EditorContrib - } - }); - } - - public run(accessor: ServicesAccessor): void { - this.getActiveElectronBasedWebviewDelegate(accessor)?.paste(); - } -} - -export class CutWebviewEditorCommand extends Action2 { - public static readonly ID = 'editor.action.webvieweditor.cut'; - public static readonly LABEL = nls.localize('editor.action.webvieweditor.cut', 'Cut'); - - constructor(contextKeyExpr: ContextKeyExpression, readonly getActiveElectronBasedWebviewDelegate: (accessor: ServicesAccessor) => ElectronWebviewBasedWebview | undefined = getActiveElectronBasedWebview) { - super({ - id: CutWebviewEditorCommand.ID, - title: CutWebviewEditorCommand.LABEL, - keybinding: { - when: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), - primary: KeyMod.CtrlCmd | KeyCode.KEY_X, - weight: KeybindingWeight.EditorContrib - } - }); - } - - public run(accessor: ServicesAccessor): void { - this.getActiveElectronBasedWebviewDelegate(accessor)?.cut(); - } -} - -export class UndoWebviewEditorCommand extends Action2 { - public static readonly ID = 'editor.action.webvieweditor.undo'; - public static readonly LABEL = nls.localize('editor.action.webvieweditor.undo', "Undo"); - - constructor(contextKeyExpr: ContextKeyExpression, readonly getActiveElectronBasedWebviewDelegate: (accessor: ServicesAccessor) => ElectronWebviewBasedWebview | undefined = getActiveElectronBasedWebview) { - super({ - id: UndoWebviewEditorCommand.ID, - title: UndoWebviewEditorCommand.LABEL, - keybinding: { - when: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey), ContextKeyExpr.not(webviewHasOwnEditFunctionsContextKey)), - primary: KeyMod.CtrlCmd | KeyCode.KEY_Z, - weight: KeybindingWeight.EditorContrib - } - }); - } - - public run(accessor: ServicesAccessor): void { - this.getActiveElectronBasedWebviewDelegate(accessor)?.undo(); - } -} - -export class RedoWebviewEditorCommand extends Action2 { - public static readonly ID = 'editor.action.webvieweditor.redo'; - public static readonly LABEL = nls.localize('editor.action.webvieweditor.redo', "Redo"); - - constructor(contextKeyExpr: ContextKeyExpression, readonly getActiveElectronBasedWebviewDelegate: (accessor: ServicesAccessor) => ElectronWebviewBasedWebview | undefined = getActiveElectronBasedWebview) { - super({ - id: RedoWebviewEditorCommand.ID, - title: RedoWebviewEditorCommand.LABEL, - keybinding: { - when: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey), ContextKeyExpr.not(webviewHasOwnEditFunctionsContextKey)), - primary: KeyMod.CtrlCmd | KeyCode.KEY_Y, - secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z], - mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z }, - weight: KeybindingWeight.EditorContrib - } - }); - } - - public run(accessor: ServicesAccessor): void { - this.getActiveElectronBasedWebviewDelegate(accessor)?.redo(); - } -} - -function getActiveElectronBasedWebview(accessor: ServicesAccessor): ElectronWebviewBasedWebview | undefined { - const webview = getActiveWebviewEditor(accessor); - if (!webview) { - return undefined; - } - - if (webview instanceof ElectronWebviewBasedWebview) { - return webview; - } else if ('getInnerWebview' in (webview as WebviewOverlay)) { - const innerWebview = (webview as WebviewOverlay).getInnerWebview(); - if (innerWebview instanceof ElectronWebviewBasedWebview) { - return innerWebview; - } - } - - return undefined; -} diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index af0a4171b46..7421ee686ff 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -3,188 +3,58 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { FindInPageOptions, OnBeforeRequestListenerDetails, OnHeadersReceivedListenerDetails, Response, WebContents, WebviewTag } from 'electron'; +import { FindInPageOptions, WebviewTag } from 'electron'; import { addDisposableListener } from 'vs/base/browser/dom'; import { ThrottledDelayer } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { once } from 'vs/base/common/functional'; -import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; import { isMacintosh } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; -import * as modes from 'vs/editor/common/modes'; +import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ITunnelService } from 'vs/platform/remote/common/tunnel'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { ILogService } from 'vs/platform/log/common/log'; +import { INotificationService } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { webviewPartitionId } from 'vs/platform/webview/common/resourceLoader'; +import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; import { BaseWebview, WebviewMessageChannels } from 'vs/workbench/contrib/webview/browser/baseWebviewElement'; +import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing'; import { Webview, WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; -import { WebviewPortMappingManager } from 'vs/workbench/contrib/webview/common/portMapping'; -import { WebviewResourceScheme } from 'vs/workbench/contrib/webview/common/resourceLoader'; -import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/common/themeing'; -import { registerFileProtocol } from 'vs/workbench/contrib/webview/electron-browser/webviewProtocols'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { WebviewFindDelegate, WebviewFindWidget } from '../browser/webviewFindWidget'; - - -class WebviewTagHandle extends Disposable { - - private _webContents: undefined | WebContents | 'destroyed'; - - public constructor( - public readonly webview: WebviewTag, - ) { - super(); - - this._register(addDisposableListener(this.webview, 'destroyed', () => { - this._webContents = 'destroyed'; - })); - - this._register(addDisposableListener(this.webview, 'did-start-loading', once(() => { - const contents = this.webContents; - if (contents) { - this._onFirstLoad.fire(contents); - this._register(toDisposable(() => { - contents.removeAllListeners(); - })); - } - }))); - } - - private readonly _onFirstLoad = this._register(new Emitter()); - public readonly onFirstLoad = this._onFirstLoad.event; - - public get webContents(): WebContents | undefined { - if (this._webContents === 'destroyed') { - return undefined; - } - if (this._webContents) { - return this._webContents; - } - this._webContents = this.webview.getWebContents(); - return this._webContents; - } -} - -type OnBeforeRequestDelegate = (details: OnBeforeRequestListenerDetails) => Promise; -type OnHeadersReceivedDelegate = (details: OnHeadersReceivedListenerDetails) => { cancel: boolean; } | undefined; - -class WebviewSession extends Disposable { - - private readonly _onBeforeRequestDelegates: Array = []; - private readonly _onHeadersReceivedDelegates: Array = []; - - public constructor( - webviewHandle: WebviewTagHandle, - ) { - super(); - - this._register(webviewHandle.onFirstLoad(contents => { - contents.session.webRequest.onBeforeRequest(async (details, callback) => { - for (const delegate of this._onBeforeRequestDelegates) { - const result = await delegate(details); - if (typeof result !== 'undefined') { - callback(result); - return; - } - } - callback({}); - }); - - contents.session.webRequest.onHeadersReceived((details, callback) => { - for (const delegate of this._onHeadersReceivedDelegates) { - const result = delegate(details); - if (typeof result !== 'undefined') { - callback(result); - return; - } - } - callback({ cancel: false }); - }); - })); - } - - public onBeforeRequest(delegate: OnBeforeRequestDelegate) { - this._onBeforeRequestDelegates.push(delegate); - } - - public onHeadersReceived(delegate: OnHeadersReceivedDelegate) { - this._onHeadersReceivedDelegates.push(delegate); - } -} - -class WebviewProtocolProvider extends Disposable { - - private _resolve!: () => void; - private _reject!: () => void; - - public readonly ready: Promise; - - constructor( - handle: WebviewTagHandle, - getExtensionLocation: () => URI | undefined, - getLocalResourceRoots: () => ReadonlyArray, - fileService: IFileService, - ) { - super(); - - this.ready = new Promise((resolve, reject) => { - this._resolve = resolve; - this._reject = reject; - }); - - this._register(handle.onFirstLoad(contents => { - try { - registerFileProtocol(contents, WebviewResourceScheme, fileService, getExtensionLocation(), getLocalResourceRoots); - this._resolve(); - } catch { - this._reject(); - } - })); - } -} - -class WebviewPortMappingProvider extends Disposable { - - constructor( - session: WebviewSession, - getExtensionLocation: () => URI | undefined, - mappings: () => ReadonlyArray, - tunnelService: ITunnelService, - ) { - super(); - const manager = this._register(new WebviewPortMappingManager(getExtensionLocation, mappings, tunnelService)); - - session.onBeforeRequest(async details => { - const redirect = await manager.getRedirect(details.url); - return redirect ? { redirectURL: redirect } : undefined; - }); - } -} +import { rewriteVsCodeResourceUrls, WebviewResourceRequestManager } from './resourceLoading'; class WebviewKeyboardHandler { - private readonly _webviews = new Set(); + private readonly _webviews = new Set(); private readonly _isUsingNativeTitleBars: boolean; - constructor(configurationService: IConfigurationService) { + private readonly webviewMainService: IWebviewManagerService; + + constructor( + configurationService: IConfigurationService, + mainProcessService: IMainProcessService, + ) { this._isUsingNativeTitleBars = configurationService.getValue('window.titleBarStyle') === 'native'; + + this.webviewMainService = createChannelSender(mainProcessService.getChannel('webview')); } - public add( - webviewHandle: WebviewTagHandle, - ): IDisposable { - this._webviews.add(webviewHandle); + public add(webview: WebviewTag): IDisposable { + this._webviews.add(webview); const disposables = new DisposableStore(); + if (this.shouldToggleMenuShortcutsEnablement) { - disposables.add(webviewHandle.onFirstLoad(() => { - this.setIgnoreMenuShortcutsForWebview(webviewHandle, true); - })); + this.setIgnoreMenuShortcutsForWebview(webview, true); } - disposables.add(addDisposableListener(webviewHandle.webview, 'ipc-message', (event) => { + disposables.add(addDisposableListener(webview, 'ipc-message', (event) => { switch (event.channel) { case 'did-focus': this.setIgnoreMenuShortcuts(true); @@ -198,7 +68,7 @@ class WebviewKeyboardHandler { return toDisposable(() => { disposables.dispose(); - this._webviews.delete(webviewHandle); + this._webviews.delete(webview); }); } @@ -212,12 +82,9 @@ class WebviewKeyboardHandler { } } - private setIgnoreMenuShortcutsForWebview(webview: WebviewTagHandle, value: boolean) { + private setIgnoreMenuShortcutsForWebview(webview: WebviewTag, value: boolean) { if (this.shouldToggleMenuShortcutsEnablement) { - const contents = webview.webContents; - if (!contents?.isDestroyed()) { - contents?.setIgnoreMenuShortcuts(value); - } + this.webviewMainService.setIgnoreMenuShortcuts(webview.getWebContentsId(), value); } } } @@ -226,9 +93,12 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme private static _webviewKeyboardHandler: WebviewKeyboardHandler | undefined; - private static getWebviewKeyboardHandler(configService: IConfigurationService) { + private static getWebviewKeyboardHandler( + configService: IConfigurationService, + mainProcessService: IMainProcessService, + ) { if (!this._webviewKeyboardHandler) { - this._webviewKeyboardHandler = new WebviewKeyboardHandler(configService); + this._webviewKeyboardHandler = new WebviewKeyboardHandler(configService, mainProcessService); } return this._webviewKeyboardHandler; } @@ -236,10 +106,9 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme private _webviewFindWidget: WebviewFindWidget | undefined; private _findStarted: boolean = false; - public extension: WebviewExtensionDescription | undefined; - private readonly _protocolProvider: WebviewProtocolProvider; + private readonly _resourceRequestManager: WebviewResourceRequestManager; + private _messagePromise = Promise.resolve(); - private readonly _domReady: Promise; private readonly _focusDelayer = this._register(new ThrottledDelayer(10)); private _elementFocusImpl!: (options?: FocusOptions | undefined) => void; @@ -247,52 +116,62 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme id: string, options: WebviewOptions, contentOptions: WebviewContentOptions, + extension: WebviewExtensionDescription | undefined, private readonly _webviewThemeDataProvider: WebviewThemeDataProvider, + @ILogService private readonly _myLogService: ILogService, @IInstantiationService instantiationService: IInstantiationService, - @IFileService fileService: IFileService, - @ITunnelService tunnelService: ITunnelService, @ITelemetryService telemetryService: ITelemetryService, - @IEnvironmentService environementService: IEnvironmentService, + @IEnvironmentService environmentService: IEnvironmentService, @IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService, @IConfigurationService configurationService: IConfigurationService, + @IMainProcessService mainProcessService: IMainProcessService, + @INotificationService noficationService: INotificationService, ) { - super(id, options, contentOptions, _webviewThemeDataProvider, telemetryService, environementService, workbenchEnvironmentService); + super(id, options, contentOptions, extension, _webviewThemeDataProvider, noficationService, _myLogService, telemetryService, environmentService, workbenchEnvironmentService); - const webviewAndContents = this._register(new WebviewTagHandle(this.element!)); - const session = this._register(new WebviewSession(webviewAndContents)); - - this._protocolProvider = new WebviewProtocolProvider(webviewAndContents, - () => this.extension?.location, - () => (this.content.options.localResourceRoots || []), - fileService); - this._register(this._protocolProvider); - - this._register(new WebviewPortMappingProvider( - session, - () => this.extension ? this.extension.location : undefined, - () => (this.content.options.portMapping || []), - tunnelService, - )); - - this._register(ElectronWebviewBasedWebview.getWebviewKeyboardHandler(configurationService).add(webviewAndContents)); - - this._domReady = new Promise(resolve => { - const subscription = this._register(this.on(WebviewMessageChannels.webviewReady, () => { - subscription.dispose(); - resolve(); - })); + /* __GDPR__ + "webview.createWebview" : { + "extension": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "enableFindWidget": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + telemetryService.publicLog('webview.createWebview', { + enableFindWidget: !!options.enableFindWidget, + extension: extension?.id.value, }); + this._myLogService.debug(`Webview(${this.id}): init`); + + const webviewId = new Promise((resolve, reject) => { + const sub = this._register(addDisposableListener(this.element!, 'dom-ready', once(() => { + if (!this.element) { + reject(); + throw new Error('No element'); + } + resolve(this.element.getWebContentsId()); + sub.dispose(); + }))); + }); + this._resourceRequestManager = this._register(instantiationService.createInstance(WebviewResourceRequestManager, id, extension, this.content.options, webviewId)); + + this._register(addDisposableListener(this.element!, 'dom-ready', once(() => { + this._register(ElectronWebviewBasedWebview.getWebviewKeyboardHandler(configurationService, mainProcessService).add(this.element!)); + }))); + this._register(addDisposableListener(this.element!, 'console-message', function (e: { level: number; message: string; line: number; sourceId: string; }) { console.log(`[Embedded Page] ${e.message}`); })); + this._register(addDisposableListener(this.element!, 'dom-ready', () => { + this._myLogService.debug(`Webview(${this.id}): dom-ready`); + // Workaround for https://github.com/electron/electron/issues/14474 if (this.element && (this.focused || document.activeElement === this.element)) { this.element.blur(); this.element.focus(); } })); + this._register(addDisposableListener(this.element!, 'crashed', () => { console.error('embedded page crashed'); })); @@ -316,6 +195,8 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme })); this._register(this.on('did-set-content', () => { + this._myLogService.debug(`Webview(${this.id}): did-set-content`); + if (this.element) { this.element.style.flex = ''; this.element.style.width = '100%'; @@ -336,16 +217,22 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme this.styledFindWidget(); } + + this.element!.preload = require.toUrl('./pre/electron-index.js'); + this.element!.src = `${Schemas.vscodeWebview}://${this.id}/electron-browser/index.html`; } protected createElement(options: WebviewOptions) { + // Do not start loading the webview yet. + // Wait the end of the ctor when all listeners have been hooked up. const element = document.createElement('webview'); this._elementFocusImpl = element.focus.bind(element); element.focus = () => { this.doFocus(); }; - element.setAttribute('partition', `webview${Date.now()}`); + + element.setAttribute('partition', webviewPartitionId); element.setAttribute('webpreferences', 'contextIsolation=yes'); element.className = `webview ${options.customClasses || ''}`; @@ -354,14 +241,31 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme element.style.height = '0'; element.style.outline = '0'; - element.preload = require.toUrl('./pre/electron-index.js'); - element.src = '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%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%20role%3D%22document%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E'; - return element; } + public set contentOptions(options: WebviewContentOptions) { + this._myLogService.debug(`Webview(${this.id}): will set content options`); + this._resourceRequestManager.update(options); + super.contentOptions = options; + } + + public set localResourcesRoot(resources: URI[]) { + this._resourceRequestManager.update({ + ...this.contentOptions, + localResourceRoots: resources, + }); + super.localResourcesRoot = resources; + } + protected readonly extraContentOptions = {}; + public set html(value: string) { + this._myLogService.debug(`Webview(${this.id}): will set html`); + + super.html = rewriteVsCodeResourceUrls(this.id, value); + } + public mountTo(parent: HTMLElement) { if (!this.element) { return; @@ -373,12 +277,15 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme parent.appendChild(this.element); } - protected async postMessage(channel: string, data?: any): Promise { - await Promise.all([ - this._protocolProvider.ready, - this._domReady, - ]); - this.element?.send(channel, data); + protected async doPostMessage(channel: string, data?: any): Promise { + this._myLogService.debug(`Webview(${this.id}): will post message on '${channel}'`); + + this._messagePromise = this._messagePromise + .then(() => this._resourceRequestManager.ensureReady()) + .then(() => { + this._myLogService.debug(`Webview(${this.id}): did post message on '${channel}'`); + return this.element?.send(channel, data); + }); } public focus(): void { @@ -402,7 +309,7 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme // We end up focusing the webview after showing the quick pick, which causes // the quick pick to instantly dismiss. // - // Workarount this by debouncing the focus and making sure we are not focused on an input + // Workaround this by debouncing the focus and making sure we are not focused on an input // when we try to re-focus. this._focusDelayer.trigger(async () => { if (!this.focused || !this.element) { @@ -526,7 +433,7 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme protected on(channel: WebviewMessageChannels | string, handler: (data: T) => void): IDisposable { if (!this.element) { - return Disposable.None; + throw new Error('Cannot add event listener. No webview element found.'); } return addDisposableListener(this.element, 'ipc-message', (event) => { if (!this.element) { diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts deleted file mode 100644 index e84ce9bf6fb..00000000000 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts +++ /dev/null @@ -1,37 +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 electron from 'electron'; -import { URI } from 'vs/base/common/uri'; -import { IFileService } from 'vs/platform/files/common/files'; -import { loadLocalResource, WebviewResourceResponse } from 'vs/workbench/contrib/webview/common/resourceLoader'; - -export function registerFileProtocol( - contents: electron.WebContents, - protocol: string, - fileService: IFileService, - extensionLocation: URI | undefined, - getRoots: () => ReadonlyArray -) { - contents.session.protocol.registerBufferProtocol(protocol, async (request, callback: any) => { - try { - const result = await loadLocalResource(URI.parse(request.url), fileService, extensionLocation, getRoots); - if (result.type === WebviewResourceResponse.Type.Success) { - return callback({ - data: Buffer.from(result.data.buffer), - mimeType: result.mimeType - }); - } - if (result.type === WebviewResourceResponse.Type.AccessDenied) { - console.error('Webview: Cannot load resource outside of protocol root'); - return callback({ error: -10 /* ACCESS_DENIED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ }); - } - } catch { - // noop - } - - return callback({ error: -2 /* FAILED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ }); - }); -} - diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts index 8e5ea202c5a..44f3d38b9e9 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts @@ -6,14 +6,14 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { DynamicWebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay'; -import { IWebviewService, WebviewContentOptions, WebviewOverlay, WebviewElement, WebviewIcons, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; -import { IFrameWebview } from 'vs/workbench/contrib/webview/browser/webviewElement'; +import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing'; +import { IWebviewService, WebviewContentOptions, WebviewElement, WebviewExtensionDescription, WebviewIcons, WebviewOptions, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewIconManager } from 'vs/workbench/contrib/webview/browser/webviewIconManager'; -import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/common/themeing'; +import { ElectronIframeWebview } from 'vs/workbench/contrib/webview/electron-browser/iframeWebviewElement'; import { ElectronWebviewBasedWebview } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; export class ElectronWebviewService implements IWebviewService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _webviewThemeDataProvider: WebviewThemeDataProvider; private readonly _iconManager: WebviewIconManager; @@ -29,22 +29,20 @@ export class ElectronWebviewService implements IWebviewService { createWebviewElement( id: string, options: WebviewOptions, - contentOptions: WebviewContentOptions + contentOptions: WebviewContentOptions, + extension: WebviewExtensionDescription | undefined, ): WebviewElement { - const useExternalEndpoint = this._configService.getValue('webview.experimental.useExternalEndpoint'); - if (useExternalEndpoint) { - return this._instantiationService.createInstance(IFrameWebview, id, options, contentOptions, this._webviewThemeDataProvider); - } else { - return this._instantiationService.createInstance(ElectronWebviewBasedWebview, id, options, contentOptions, this._webviewThemeDataProvider); - } + const useIframes = this._configService.getValue('webview.experimental.useIframes'); + return this._instantiationService.createInstance(useIframes ? ElectronIframeWebview : ElectronWebviewBasedWebview, id, options, contentOptions, extension, this._webviewThemeDataProvider); } createWebviewOverlay( id: string, options: WebviewOptions, contentOptions: WebviewContentOptions, + extension: WebviewExtensionDescription | undefined, ): WebviewOverlay { - return this._instantiationService.createInstance(DynamicWebviewEditorOverlay, id, options, contentOptions); + return this._instantiationService.createInstance(DynamicWebviewEditorOverlay, id, options, contentOptions, extension); } setIcons(id: string, iconPath: WebviewIcons | undefined): void { diff --git a/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.css b/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.css index c3287f38780..a7b446b83a9 100644 --- a/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.css +++ b/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.css @@ -26,9 +26,9 @@ opacity: .4; } -.hc-black .monaco-workbench.blur-background #workbench\.parts\.panel, -.hc-black .monaco-workbench.blur-background #workbench\.parts\.sidebar, -.hc-black .monaco-workbench.blur-background #workbench\.parts\.editor { +.monaco-workbench.hc-black.blur-background #workbench\.parts\.panel, +.monaco-workbench.hc-black.blur-background #workbench\.parts\.sidebar, +.monaco-workbench.hc-black.blur-background #workbench\.parts\.editor { opacity: .2; } @@ -154,8 +154,8 @@ background-size: 100%; } -.vs-dark .monaco-workbench > .welcomeOverlay > .commandPalettePlaceholder, -.hc-black .monaco-workbench > .welcomeOverlay > .commandPalettePlaceholder { +.monaco-workbench.vs-dark > .welcomeOverlay > .commandPalettePlaceholder, +.monaco-workbench.hc-black > .welcomeOverlay > .commandPalettePlaceholder { background-image: url('media/commandpalette-dark.svg'); } @media screen and (max-width: 880px) { @@ -169,4 +169,4 @@ top: 10px; left: 50%; } -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts b/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts index 387d0e3bfe0..33bdbf1ba9d 100644 --- a/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts +++ b/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts @@ -214,7 +214,7 @@ class WelcomeOverlay extends Disposable { } private updateProblemsKey() { - const problems = document.querySelector(`div[id="workbench.parts.statusbar"] .statusbar-item.left ${Codicon.warning.cssSelector}`); + const problems = document.querySelector(`footer[id="workbench.parts.statusbar"] .statusbar-item.left ${Codicon.warning.cssSelector}`); const key = this._overlay.querySelector('.key.problems') as HTMLElement; if (problems instanceof HTMLElement) { const target = problems.getBoundingClientRect(); diff --git a/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts b/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts index 30f321498e8..7b6e2e716b8 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts @@ -40,7 +40,7 @@ export default () => `
  • ${escape(localize('welcomePage.tipsAndTricks', "Tips and Tricks"))}
  • ${escape(localize('welcomePage.productDocumentation', "Product documentation"))}
  • ${escape(localize('welcomePage.gitHubRepository', "GitHub repository"))}
  • -
  • ${escape(localize('welcomePage.stackOverflow', "Stack Overflow"))}
  • +
  • ${escape(localize('welcomePage.stackOverflow', "Stack Overflow"))}
  • ${escape(localize('welcomePage.newsletterSignup', "Join our Newsletter"))}
  • @@ -50,11 +50,11 @@ export default () => `

    ${escape(localize('welcomePage.customize', "Customize"))}

    -
    -
    diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.css b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.css index e72292ac298..738ce140c1a 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.css +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.css @@ -76,7 +76,7 @@ display: block; } -.hc-black .monaco-workbench .part.editor > .content .welcomePage .subtitle { +.monaco-workbench.hc-black .part.editor > .content .welcomePage .subtitle { font-weight: 200; } @@ -184,7 +184,7 @@ border: none; } -.hc-black .monaco-workbench .part.editor > .content .welcomePage .commands .item button > h3 { +.monaco-workbench.hc-black .part.editor > .content .welcomePage .commands .item button > h3 { font-weight: bold; } @@ -193,12 +193,12 @@ outline-width: 1px; } -.hc-black .monaco-workbench .part.editor > .content .welcomePage .commands .item button { +.monaco-workbench.hc-black .part.editor > .content .welcomePage .commands .item button { border-width: 1px; border-style: solid; } -.hc-black .monaco-workbench .part.editor > .content .welcomePage .commands .item button:hover { +.monaco-workbench.hc-black .part.editor > .content .welcomePage .commands .item button:hover { outline-width: 1px; outline-style: dashed; outline-offset: -5px; diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index abe57616814..6378a16dddf 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -162,7 +162,7 @@ interface ExtensionSuggestion { const extensionPacks: ExtensionSuggestion[] = [ { name: localize('welcomePage.javaScript', "JavaScript"), id: 'dbaeumer.vscode-eslint' }, { name: localize('welcomePage.python', "Python"), id: 'ms-python.python' }, - // { name: localize('welcomePage.go', "Go"), id: 'lukehoban.go' }, + { name: localize('welcomePage.java', "Java"), id: 'vscjava.vscode-java-pack' }, { name: localize('welcomePage.php', "PHP"), id: 'felixfbecker.php-pack' }, { name: localize('welcomePage.azure', "Azure"), title: localize('welcomePage.showAzureExtensions', "Show Azure extensions"), id: 'workbench.extensions.action.showAzureExtensions', isCommand: true }, { name: localize('welcomePage.docker', "Docker"), id: 'ms-azuretools.vscode-docker' }, diff --git a/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-browser/telemetryOptOut.contribution.ts b/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.contribution.ts similarity index 93% rename from src/vs/workbench/contrib/welcome/telemetryOptOut/electron-browser/telemetryOptOut.contribution.ts rename to src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.contribution.ts index 44b1e266005..5c4f1ccf06e 100644 --- a/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-browser/telemetryOptOut.contribution.ts +++ b/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.contribution.ts @@ -6,6 +6,6 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { NativeTelemetryOptOut } from 'vs/workbench/contrib/welcome/telemetryOptOut/electron-browser/telemetryOptOut'; +import { NativeTelemetryOptOut } from 'vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut'; Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NativeTelemetryOptOut, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-browser/telemetryOptOut.ts b/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.ts similarity index 96% rename from src/vs/workbench/contrib/welcome/telemetryOptOut/electron-browser/telemetryOptOut.ts rename to src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.ts index d561cf83320..0adc911b514 100644 --- a/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-browser/telemetryOptOut.ts +++ b/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.ts @@ -13,7 +13,7 @@ import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common import { IProductService } from 'vs/platform/product/common/productService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { AbstractTelemetryOptOut } from 'vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; export class NativeTelemetryOptOut extends AbstractTelemetryOptOut { diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.css b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.css index 864ec283803..e2a46b3799a 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.css +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.css @@ -134,7 +134,7 @@ display: initial; } -.hc-black .monaco-workbench .part.editor > .content .walkThroughContent .monaco-editor { +.monaco-workbench.hc-black .part.editor > .content .walkThroughContent .monaco-editor { border-width: 1px; border-style: solid; } diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts index 7cc9d291293..d4378989b5f 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts @@ -309,7 +309,7 @@ export class WalkThroughPart extends BaseEditor { model.snippets.forEach((snippet, i) => { const model = snippet.textEditorModel; const id = `snippet-${model.uri.fragment}`; - const div = innerContent.querySelector(`#${id.replace(/\./g, '\\.')}`) as HTMLElement; + const div = innerContent.querySelector(`#${id.replace(/[\\.]/g, '\\$&')}`) as HTMLElement; const options = this.getEditorOptions(snippet.textEditorModel.getModeId()); const telemetryData = { diff --git a/src/vs/workbench/electron-browser/actions/developerActions.ts b/src/vs/workbench/electron-browser/actions/developerActions.ts index e4c1160ca47..1b195592a4b 100644 --- a/src/vs/workbench/electron-browser/actions/developerActions.ts +++ b/src/vs/workbench/electron-browser/actions/developerActions.ts @@ -3,64 +3,25 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; -import { IElectronService } from 'vs/platform/electron/node/electron'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -export class ToggleDevToolsAction extends Action { +class ToggleSharedProcessAction extends Action2 { - static readonly ID = 'workbench.action.toggleDevTools'; - static readonly LABEL = nls.localize('toggleDevTools', "Toggle Developer Tools"); - - constructor( - id: string, - label: string, - @IElectronService private readonly electronService: IElectronService - ) { - super(id, label); + constructor() { + super({ + id: 'workbench.action.toggleSharedProcess', + title: { value: nls.localize('toggleSharedProcess', "Toggle Shared Process"), original: 'Toggle Shared Process' }, + category: { value: nls.localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer"), original: 'Developer' }, + f1: true + }); } - run(): Promise { - return this.electronService.toggleDevTools(); + async run(accessor: ServicesAccessor): Promise { + return accessor.get(ISharedProcessService).toggleSharedProcessWindow(); } } -export class ToggleSharedProcessAction extends Action { - - static readonly ID = 'workbench.action.toggleSharedProcess'; - static readonly LABEL = nls.localize('toggleSharedProcess', "Toggle Shared Process"); - - constructor( - id: string, - label: string, - @ISharedProcessService private readonly sharedProcessService: ISharedProcessService - ) { - super(id, label); - } - - run(): Promise { - return this.sharedProcessService.toggleSharedProcessWindow(); - } -} - -export class ConfigureRuntimeArgumentsAction extends Action { - - static readonly ID = 'workbench.action.configureRuntimeArguments'; - static readonly LABEL = nls.localize('configureRuntimeArguments', "Configure Runtime Arguments"); - - constructor( - id: string, - label: string, - @IEnvironmentService private readonly environmentService: IEnvironmentService, - @IEditorService private readonly editorService: IEditorService - ) { - super(id, label); - } - - async run(): Promise { - await this.editorService.openEditor({ resource: this.environmentService.argvResource }); - } -} +registerAction2(ToggleSharedProcessAction); diff --git a/src/vs/workbench/electron-browser/desktop.main.ts b/src/vs/workbench/electron-browser/desktop.main.ts index ba9d01fbba8..06e8e0137c1 100644 --- a/src/vs/workbench/electron-browser/desktop.main.ts +++ b/src/vs/workbench/electron-browser/desktop.main.ts @@ -5,7 +5,7 @@ import * as fs from 'fs'; import * as gracefulFs from 'graceful-fs'; -import { webFrame } from 'electron'; +import { zoomLevelToZoomFactor } from 'vs/platform/windows/common/windows'; import { importEntries, mark } from 'vs/base/common/performance'; import { Workbench } from 'vs/workbench/browser/workbench'; import { NativeWindow } from 'vs/workbench/electron-browser/window'; @@ -30,7 +30,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IStorageService } from 'vs/platform/storage/common/storage'; import { Disposable } from 'vs/base/common/lifecycle'; import { registerWindowDriver } from 'vs/platform/driver/electron-browser/driver'; -import { IMainProcessService, MainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; +import { IMainProcessService, MainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-browser/remoteAuthorityResolverService'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; @@ -49,6 +49,7 @@ import product from 'vs/platform/product/common/product'; import { NativeResourceIdentityService } from 'vs/platform/resource/node/resourceIdentityServiceImpl'; import { IResourceIdentityService } from 'vs/platform/resource/common/resourceIdentityService'; import { DesktopLogService } from 'vs/workbench/services/log/electron-browser/logService'; +import { IElectronService, ElectronService } from 'vs/platform/electron/electron-sandbox/electron'; class DesktopMain extends Disposable { @@ -72,8 +73,9 @@ class DesktopMain extends Disposable { importEntries(this.environmentService.configuration.perfEntries); // Browser config - setZoomFactor(webFrame.getZoomFactor()); // Ensure others can listen to zoom level changes - setZoomLevel(webFrame.getZoomLevel(), true /* isTrusted */); // Can be trusted because we are not setting it ourselves (https://github.com/Microsoft/vscode/issues/26151) + const zoomLevel = this.configuration.zoomLevel || 0; + setZoomFactor(zoomLevelToZoomFactor(zoomLevel)); + setZoomLevel(zoomLevel, true /* isTrusted */); setFullscreen(!!this.environmentService.configuration.fullscreen); // Keyboard support @@ -177,7 +179,8 @@ class DesktopMain extends Disposable { serviceCollection.set(IWorkbenchEnvironmentService, this.environmentService); // Product - serviceCollection.set(IProductService, { _serviceBrand: undefined, ...product }); + const productService: IProductService = { _serviceBrand: undefined, ...product }; + serviceCollection.set(IProductService, productService); // Log const logService = this._register(new DesktopLogService(this.configuration.windowId, mainProcessService, this.environmentService)); @@ -191,14 +194,19 @@ class DesktopMain extends Disposable { const signService = new SignService(); serviceCollection.set(ISignService, signService); - const remoteAgentService = this._register(new RemoteAgentService(this.environmentService, remoteAuthorityResolverService, signService, logService)); + // Remote Agent + const remoteAgentService = this._register(new RemoteAgentService(this.environmentService, productService, remoteAuthorityResolverService, signService, logService)); serviceCollection.set(IRemoteAgentService, remoteAgentService); + // Electron + const electronService = new ElectronService(this.configuration.windowId, mainProcessService) as IElectronService; + serviceCollection.set(IElectronService, electronService); + // Files const fileService = this._register(new FileService(logService)); serviceCollection.set(IFileService, fileService); - const diskFileSystemProvider = this._register(new DiskFileSystemProvider(logService)); + const diskFileSystemProvider = this._register(new DiskFileSystemProvider(logService, electronService)); fileService.registerProvider(Schemas.file, diskFileSystemProvider); // User Data Provider diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 545bf31888b..793b2394597 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -8,52 +8,52 @@ import { URI } from 'vs/base/common/uri'; import * as errors from 'vs/base/common/errors'; import { equals, deepClone } from 'vs/base/common/objects'; import * as DOM from 'vs/base/browser/dom'; -import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IAction } from 'vs/base/common/actions'; +import { IAction, Separator } from 'vs/base/common/actions'; import { IFileService } from 'vs/platform/files/common/files'; import { toResource, IUntitledTextResourceEditorInput, SideBySideEditor, pathsToEditors } from 'vs/workbench/common/editor'; import { IEditorService, IResourceEditorInputType } from 'vs/workbench/services/editor/common/editorService'; import { ITelemetryService, crashReporterIdStorageKey } from 'vs/platform/telemetry/common/telemetry'; -import { IWindowSettings, IOpenFileRequest, IWindowsConfiguration, getTitleBarStyle } from 'vs/platform/windows/common/windows'; -import { IRunActionInWindowRequest, IRunKeybindingInWindowRequest, IAddFoldersRequest, INativeOpenFileRequest } from 'vs/platform/windows/node/window'; +import { IWindowSettings, IOpenFileRequest, IWindowsConfiguration, getTitleBarStyle, IAddFoldersRequest } from 'vs/platform/windows/common/windows'; +import { IRunActionInWindowRequest, IRunKeybindingInWindowRequest, INativeOpenFileRequest } from 'vs/platform/windows/node/window'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { IWorkbenchThemeService, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService'; -import * as browser from 'vs/base/browser/browser'; +import { applyZoom } from 'vs/platform/windows/electron-sandbox/window'; +import { setFullscreen, getZoomLevel } from 'vs/base/browser/browser'; import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/nativeKeymapService'; -import { ipcRenderer as ipc, webFrame, crashReporter, CrashReporterStartOptions, Event as IpcEvent } from 'electron'; +import { CrashReporterStartOptions } from 'vs/base/parts/sandbox/common/electronTypes'; +import { crashReporter, ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { IMenuService, MenuId, IMenu, MenuItemAction, ICommandAction, SubmenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { LifecyclePhase, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkspaceFolderCreationData, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity'; -import { isRootUser, isWindows, isMacintosh, isLinux } from 'vs/base/common/platform'; -import product from 'vs/platform/product/common/product'; +import { isWindows, isMacintosh, isLinux } from 'vs/base/common/platform'; +import { IProductService, IAppCenterConfiguration } from 'vs/platform/product/common/productService'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { coalesce } from 'vs/base/common/arrays'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { isEqual } from 'vs/base/common/resources'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { MenubarControl } from '../browser/parts/titlebar/menubarControl'; import { ILabelService } from 'vs/platform/label/common/label'; import { IUpdateService } from 'vs/platform/update/common/update'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IPreferencesService } from '../services/preferences/common/preferences'; -import { IMenubarService, IMenubarData, IMenubarMenu, IMenubarKeybinding, IMenubarMenuItemSubmenu, IMenubarMenuItemAction, MenubarMenuItem } from 'vs/platform/menubar/node/menubar'; +import { IMenubarData, IMenubarMenu, IMenubarKeybinding, IMenubarMenuItemSubmenu, IMenubarMenuItemAction, MenubarMenuItem } from 'vs/platform/menubar/common/menubar'; +import { IMenubarService } from 'vs/platform/menubar/electron-sandbox/menubar'; import { withNullAsUndefined, assertIsDefined } from 'vs/base/common/types'; import { IOpenerService, OpenOptions } from 'vs/platform/opener/common/opener'; import { Schemas } from 'vs/base/common/network'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { posix, dirname } from 'vs/base/common/path'; import { getBaseLabel } from 'vs/base/common/labels'; import { ITunnelService, extractLocalHostUriMetaDataForPortMapping } from 'vs/platform/remote/common/tunnel'; @@ -64,6 +64,8 @@ import { AutoSaveMode, IFilesConfigurationService } from 'vs/workbench/services/ import { Event } from 'vs/base/common/event'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; import { clearAllFontInfos } from 'vs/editor/browser/config/configuration'; +import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { IAddressProvider, IAddress } from 'vs/platform/remote/common/remoteAgentConnection'; export class NativeWindow extends Disposable { @@ -83,7 +85,7 @@ export class NativeWindow extends Disposable { private isDocumentedEdited = false; constructor( - @IEditorService private readonly editorService: EditorServiceImpl, + @IEditorService private readonly editorService: IEditorService, @IConfigurationService private readonly configurationService: IConfigurationService, @ITitleService private readonly titleService: ITitleService, @IWorkbenchThemeService protected themeService: IWorkbenchThemeService, @@ -107,6 +109,8 @@ export class NativeWindow extends Disposable { @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService, @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService, @IStorageService private readonly storageService: IStorageService, + @IProductService private readonly productService: IProductService, + @IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService, ) { super(); @@ -127,7 +131,7 @@ export class NativeWindow extends Disposable { }); // Support runAction event - ipc.on('vscode:runAction', async (event: IpcEvent, request: IRunActionInWindowRequest) => { + ipcRenderer.on('vscode:runAction', async (event: unknown, request: IRunActionInWindowRequest) => { const args: unknown[] = request.args || []; // If we run an action from the touchbar, we fill in the currently active resource @@ -135,7 +139,7 @@ export class NativeWindow extends Disposable { if (request.from === 'touchbar') { const activeEditor = this.editorService.activeEditor; if (activeEditor) { - const resource = toResource(activeEditor, { supportSideBySide: SideBySideEditor.MASTER }); + const resource = toResource(activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }); if (resource) { args.push(resource); } @@ -158,47 +162,48 @@ export class NativeWindow extends Disposable { }); // Support runKeybinding event - ipc.on('vscode:runKeybinding', (event: IpcEvent, request: IRunKeybindingInWindowRequest) => { + ipcRenderer.on('vscode:runKeybinding', (event: unknown, request: IRunKeybindingInWindowRequest) => { if (document.activeElement) { this.keybindingService.dispatchByUserSettingsLabel(request.userSettingsLabel, document.activeElement); } }); // Error reporting from main - ipc.on('vscode:reportError', (event: IpcEvent, error: string) => { + ipcRenderer.on('vscode:reportError', (event: unknown, error: string) => { if (error) { errors.onUnexpectedError(JSON.parse(error)); } }); // Support openFiles event for existing and new files - ipc.on('vscode:openFiles', (event: IpcEvent, request: IOpenFileRequest) => this.onOpenFiles(request)); + ipcRenderer.on('vscode:openFiles', (event: unknown, request: IOpenFileRequest) => this.onOpenFiles(request)); // Support addFolders event if we have a workspace opened - ipc.on('vscode:addFolders', (event: IpcEvent, request: IAddFoldersRequest) => this.onAddFoldersRequest(request)); + ipcRenderer.on('vscode:addFolders', (event: unknown, request: IAddFoldersRequest) => this.onAddFoldersRequest(request)); // Message support - ipc.on('vscode:showInfoMessage', (event: IpcEvent, message: string) => { + ipcRenderer.on('vscode:showInfoMessage', (event: unknown, message: string) => { this.notificationService.info(message); }); - ipc.on('vscode:displayChanged', (event: IpcEvent) => { + // Display change events + ipcRenderer.on('vscode:displayChanged', () => { clearAllFontInfos(); }); // Fullscreen Events - ipc.on('vscode:enterFullScreen', async () => { + ipcRenderer.on('vscode:enterFullScreen', async () => { await this.lifecycleService.when(LifecyclePhase.Ready); - browser.setFullscreen(true); + setFullscreen(true); }); - ipc.on('vscode:leaveFullScreen', async () => { + ipcRenderer.on('vscode:leaveFullScreen', async () => { await this.lifecycleService.when(LifecyclePhase.Ready); - browser.setFullscreen(false); + setFullscreen(false); }); // High Contrast Events - ipc.on('vscode:enterHighContrast', async () => { + ipcRenderer.on('vscode:enterHighContrast', async () => { const windowConfig = this.configurationService.getValue('window'); if (windowConfig?.autoDetectHighContrast) { await this.lifecycleService.when(LifecyclePhase.Ready); @@ -206,7 +211,7 @@ export class NativeWindow extends Disposable { } }); - ipc.on('vscode:leaveHighContrast', async () => { + ipcRenderer.on('vscode:leaveHighContrast', async () => { const windowConfig = this.configurationService.getValue('window'); if (windowConfig?.autoDetectHighContrast) { await this.lifecycleService.when(LifecyclePhase.Ready); @@ -215,12 +220,12 @@ export class NativeWindow extends Disposable { }); // keyboard layout changed event - ipc.on('vscode:keyboardLayoutChanged', () => { + ipcRenderer.on('vscode:keyboardLayoutChanged', () => { KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged(); }); // accessibility support changed event - ipc.on('vscode:accessibilitySupportChanged', (event: IpcEvent, accessibilitySupportEnabled: boolean) => { + ipcRenderer.on('vscode:accessibilitySupportChanged', (event: unknown, accessibilitySupportEnabled: boolean) => { this.accessibilityService.setAccessibilitySupport(accessibilitySupportEnabled ? AccessibilitySupport.Enabled : AccessibilitySupport.Disabled); }); @@ -240,16 +245,13 @@ export class NativeWindow extends Disposable { // Listen to editor closing (if we run with --wait) const filesToWait = this.environmentService.configuration.filesToWait; if (filesToWait) { - const waitMarkerFile = filesToWait.waitMarkerFileUri; - const resourcesToWaitFor = coalesce(filesToWait.paths.map(p => p.fileUri)); - - this._register(this.trackClosedWaitFiles(waitMarkerFile, resourcesToWaitFor)); + this.trackClosedWaitFiles(filesToWait.waitMarkerFileUri, coalesce(filesToWait.paths.map(path => path.fileUri))); } // macOS OS integration if (isMacintosh) { this._register(this.editorService.onDidActiveEditorChange(() => { - const file = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER, filterByScheme: Schemas.file }); + const file = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY, filterByScheme: Schemas.file }); // Represented Filename this.updateRepresentedFilename(file?.fsPath); @@ -284,8 +286,8 @@ export class NativeWindow extends Disposable { // Detect minimize / maximize this._register(Event.any( - Event.map(Event.filter(this.electronService.onWindowMaximize, id => id === this.environmentService.configuration.windowId), () => true), - Event.map(Event.filter(this.electronService.onWindowUnmaximize, id => id === this.environmentService.configuration.windowId), () => false) + Event.map(Event.filter(this.electronService.onWindowMaximize, id => id === this.electronService.windowId), () => true), + Event.map(Event.filter(this.electronService.onWindowUnmaximize, id => id === this.electronService.windowId), () => false) )(e => this.onDidChangeMaximized(e))); this.onDidChangeMaximized(this.environmentService.configuration.maximized ?? false); @@ -325,27 +327,22 @@ export class NativeWindow extends Disposable { } private updateWindowZoomLevel(): void { - const windowConfig: IWindowsConfiguration = this.configurationService.getValue(); + const windowConfig = this.configurationService.getValue(); - let newZoomLevel = 0; + let configuredZoomLevel = 0; if (windowConfig.window && typeof windowConfig.window.zoomLevel === 'number') { - newZoomLevel = windowConfig.window.zoomLevel; + configuredZoomLevel = windowConfig.window.zoomLevel; // Leave early if the configured zoom level did not change (https://github.com/Microsoft/vscode/issues/1536) - if (this.previousConfiguredZoomLevel === newZoomLevel) { + if (this.previousConfiguredZoomLevel === configuredZoomLevel) { return; } - this.previousConfiguredZoomLevel = newZoomLevel; + this.previousConfiguredZoomLevel = configuredZoomLevel; } - if (webFrame.getZoomLevel() !== newZoomLevel) { - webFrame.setZoomLevel(newZoomLevel); - 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); + if (getZoomLevel() !== configuredZoomLevel) { + applyZoom(configuredZoomLevel); } } @@ -398,27 +395,22 @@ export class NativeWindow extends Disposable { // Handle open calls this.setupOpenHandlers(); - // Emit event when vscode is ready - this.lifecycleService.when(LifecyclePhase.Ready).then(() => ipc.send('vscode:workbenchReady', this.environmentService.configuration.windowId)); + // Notify main side when window ready + this.lifecycleService.when(LifecyclePhase.Ready).then(() => this.electronService.notifyReady()); // Integrity warning this.integrityService.isPure().then(res => this.titleService.updateProperties({ isPure: res.isPure })); // Root warning this.lifecycleService.when(LifecyclePhase.Restored).then(async () => { - let isAdmin: boolean; - if (isWindows) { - isAdmin = (await import('native-is-elevated'))(); - } else { - isAdmin = isRootUser(); - } + const isAdmin = await this.electronService.isAdmin(); // Update title this.titleService.updateProperties({ isAdmin }); // Show warning message (unix only) if (isAdmin && !isWindows) { - this.notificationService.warn(nls.localize('runningAsRoot', "It is not recommended to run {0} as root user.", product.nameShort)); + this.notificationService.warn(nls.localize('runningAsRoot', "It is not recommended to run {0} as root user.", this.productService.nameShort)); } }); @@ -427,19 +419,19 @@ export class NativeWindow extends Disposable { // Crash reporter (if enabled) if (!this.environmentService.disableCrashReporter && this.configurationService.getValue('telemetry.enableCrashReporter')) { - const companyName = product.crashReporter?.companyName || 'Microsoft'; - const productName = product.crashReporter?.productName || product.nameShort; - - // With appCenter enabled, crashes will be uploaded - if (product.appCenter) { - this.setupCrashReporter(companyName, productName, product.appCenter, undefined); - } + const companyName = this.productService.crashReporter?.companyName || 'Microsoft'; + const productName = this.productService.crashReporter?.productName || this.productService.nameShort; // With a provided crash reporter directory, crashes // will be stored only locally in that folder - else if (this.environmentService.crashReporterDirectory) { + if (this.environmentService.crashReporterDirectory) { this.setupCrashReporter(companyName, productName, undefined, this.environmentService.crashReporterDirectory); } + + // With appCenter enabled, crashes will be uploaded + else if (this.productService.appCenter) { + this.setupCrashReporter(companyName, productName, this.productService.appCenter, undefined); + } } } @@ -472,7 +464,13 @@ export class NativeWindow extends Disposable { if (options?.allowTunneling) { const portMappingRequest = extractLocalHostUriMetaDataForPortMapping(uri); if (portMappingRequest) { - const tunnel = await this.tunnelService.openTunnel(undefined, portMappingRequest.port); + const remoteAuthority = this.environmentService.configuration.remoteAuthority; + const addressProvider: IAddressProvider | undefined = remoteAuthority ? { + getAddress: async (): Promise => { + return (await this.remoteAuthorityResolverService.resolveAuthority(remoteAuthority)).authority; + } + } : undefined; + const tunnel = await this.tunnelService.openTunnel(addressProvider, undefined, portMappingRequest.port); if (tunnel) { return { resolved: uri.with({ authority: `127.0.0.1:${tunnel.tunnelLocalPort}` }), @@ -552,9 +550,9 @@ export class NativeWindow extends Disposable { } } - private async setupCrashReporter(companyName: string, productName: string, appCenter: typeof product.appCenter, crashesDirectory: undefined): Promise; + private async setupCrashReporter(companyName: string, productName: string, appCenter: IAppCenterConfiguration, crashesDirectory: undefined): Promise; private async setupCrashReporter(companyName: string, productName: string, appCenter: undefined, crashesDirectory: string): Promise; - private async setupCrashReporter(companyName: string, productName: string, appCenter: typeof product.appCenter | undefined, crashesDirectory: string | undefined): Promise { + private async setupCrashReporter(companyName: string, productName: string, appCenter: IAppCenterConfiguration | undefined, crashesDirectory: string | undefined): Promise { let submitURL: string | undefined = undefined; if (appCenter) { submitURL = isWindows ? appCenter[process.arch === 'ia32' ? 'win32-ia32' : 'win32-x64'] : isLinux ? appCenter[`linux-x64`] : appCenter.darwin; @@ -569,8 +567,8 @@ export class NativeWindow extends Disposable { productName, submitURL: (submitURL?.concat('&uid=', crashReporterId, '&iid=', crashReporterId, '&sid=', info.sessionId)) || '', extra: { - vscode_version: product.version, - vscode_commit: product.commit || '' + vscode_version: this.productService.version, + vscode_commit: this.productService.commit || '' }, // If `crashesDirectory` is specified, we do not upload @@ -591,7 +589,7 @@ export class NativeWindow extends Disposable { private onAddFoldersRequest(request: IAddFoldersRequest): void { // Buffer all pending requests - this.pendingFoldersToAdd.push(...request.foldersToAdd.map(f => URI.revive(f))); + this.pendingFoldersToAdd.push(...request.foldersToAdd.map(folder => URI.revive(folder))); // Delay the adding of folders a bit to buffer in case more requests are coming if (!this.addFoldersScheduler.isScheduled()) { @@ -631,65 +629,17 @@ export class NativeWindow extends Disposable { // In wait mode, listen to changes to the editors and wait until the files // are closed that the user wants to wait for. When this happens we delete // the wait marker file to signal to the outside that editing is done. - const waitMarkerFile = URI.revive(request.filesToWait.waitMarkerFileUri); - const resourcesToWaitFor = coalesce(request.filesToWait.paths.map(p => URI.revive(p.fileUri))); - this.trackClosedWaitFiles(waitMarkerFile, resourcesToWaitFor); + this.trackClosedWaitFiles(URI.revive(request.filesToWait.waitMarkerFileUri), coalesce(request.filesToWait.paths.map(p => URI.revive(p.fileUri)))); } } - private trackClosedWaitFiles(waitMarkerFile: URI, resourcesToWaitFor: URI[]): IDisposable { - let remainingResourcesToWaitFor = resourcesToWaitFor.slice(0); + private async trackClosedWaitFiles(waitMarkerFile: URI, resourcesToWaitFor: URI[]): Promise { - // In wait mode, listen to changes to the editors and wait until the files - // are closed that the user wants to wait for. When this happens we delete - // the wait marker file to signal to the outside that editing is done. - const listener = this.editorService.onDidCloseEditor(async event => { - const detailsResource = toResource(event.editor, { supportSideBySide: SideBySideEditor.DETAILS }); - const masterResource = toResource(event.editor, { supportSideBySide: SideBySideEditor.MASTER }); + // Wait for the resources to be closed in the editor... + await this.editorService.whenClosed(resourcesToWaitFor.map(resource => ({ resource })), { waitForSaved: true }); - // Remove from resources to wait for based on the - // resources from editors that got closed - remainingResourcesToWaitFor = remainingResourcesToWaitFor.filter(resourceToWaitFor => { - if (isEqual(resourceToWaitFor, masterResource) || isEqual(resourceToWaitFor, detailsResource)) { - return false; // remove - the closing editor matches this resource - } - - return true; // keep - not yet closed - }); - - if (remainingResourcesToWaitFor.length === 0) { - // If auto save is configured with the default delay (1s) it is possible - // to close the editor while the save still continues in the background. As such - // we have to also check if the files to wait for are dirty and if so wait - // for them to get saved before deleting the wait marker file. - const dirtyFilesToWait = resourcesToWaitFor.filter(resourceToWaitFor => this.workingCopyService.isDirty(resourceToWaitFor)); - if (dirtyFilesToWait.length > 0) { - await Promise.all(dirtyFilesToWait.map(async dirtyFileToWait => await this.joinResourceSaved(dirtyFileToWait))); - } - - listener.dispose(); - await this.fileService.del(waitMarkerFile); - } - }); - - return listener; - } - - private joinResourceSaved(resource: URI): Promise { - return new Promise(resolve => { - if (!this.workingCopyService.isDirty(resource)) { - return resolve(); // return early if resource is not dirty - } - - // Otherwise resolve promise when resource is saved - const listener = this.workingCopyService.onDidChangeDirty(workingCopy => { - if (!workingCopy.isDirty() && isEqual(resource, workingCopy.resource)) { - listener.dispose(); - - resolve(); - } - }); - }); + // ...before deleting the wait marker file + await this.fileService.del(waitMarkerFile); } private async openResources(resources: Array, diffMode: boolean): Promise { @@ -726,6 +676,7 @@ class NativeMenubarControl extends MenubarControl { @IAccessibilityService accessibilityService: IAccessibilityService, @IMenubarService private readonly menubarService: IMenubarService, @IHostService hostService: IHostService, + @IElectronService private readonly electronService: IElectronService ) { super( menuService, @@ -774,7 +725,7 @@ class NativeMenubarControl extends MenubarControl { // Send menus to main process to be rendered by Electron const menubarData = { menus: {}, keybindings: {} }; if (this.getMenubarMenus(menubarData)) { - this.menubarService.updateMenubar(this.environmentService.configuration.windowId, menubarData); + this.menubarService.updateMenubar(this.electronService.windowId, menubarData); } } diff --git a/src/vs/workbench/electron-sandbox/actions/developerActions.ts b/src/vs/workbench/electron-sandbox/actions/developerActions.ts new file mode 100644 index 00000000000..6c20467143d --- /dev/null +++ b/src/vs/workbench/electron-sandbox/actions/developerActions.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Action } from 'vs/base/common/actions'; +import * as nls from 'vs/nls'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; + +export class ToggleDevToolsAction extends Action { + + static readonly ID = 'workbench.action.toggleDevTools'; + static readonly LABEL = nls.localize('toggleDevTools', "Toggle Developer Tools"); + + constructor( + id: string, + label: string, + @IElectronService private readonly electronService: IElectronService + ) { + super(id, label); + } + + run(): Promise { + return this.electronService.toggleDevTools(); + } +} + +export class ConfigureRuntimeArgumentsAction extends Action { + + static readonly ID = 'workbench.action.configureRuntimeArguments'; + static readonly LABEL = nls.localize('configureRuntimeArguments', "Configure Runtime Arguments"); + + constructor( + id: string, + label: string, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IEditorService private readonly editorService: IEditorService + ) { + super(id, label); + } + + async run(): Promise { + await this.editorService.openEditor({ resource: this.environmentService.argvResource }); + } +} diff --git a/src/vs/workbench/electron-browser/actions/media/actions.css b/src/vs/workbench/electron-sandbox/actions/media/actions.css similarity index 100% rename from src/vs/workbench/electron-browser/actions/media/actions.css rename to src/vs/workbench/electron-sandbox/actions/media/actions.css diff --git a/src/vs/workbench/electron-browser/actions/windowActions.ts b/src/vs/workbench/electron-sandbox/actions/windowActions.ts similarity index 85% rename from src/vs/workbench/electron-browser/actions/windowActions.ts rename to src/vs/workbench/electron-sandbox/actions/windowActions.ts index 255a094fa06..480474191f9 100644 --- a/src/vs/workbench/electron-browser/actions/windowActions.ts +++ b/src/vs/workbench/electron-sandbox/actions/windowActions.ts @@ -8,9 +8,9 @@ import 'vs/css!./media/actions'; import { URI } from 'vs/base/common/uri'; import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; -import * as browser from 'vs/base/browser/browser'; +import { applyZoom } from 'vs/platform/windows/electron-sandbox/window'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { webFrame } from 'electron'; +import { getZoomLevel } from 'vs/base/browser/browser'; import { FileKind } from 'vs/platform/files/common/files'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; @@ -19,9 +19,7 @@ import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { ICommandHandler } from 'vs/platform/commands/common/commands'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IElectronService } from 'vs/platform/electron/node/electron'; -import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { Codicon } from 'vs/base/common/codicons'; export class CloseCurrentWindowAction extends Action { @@ -64,18 +62,9 @@ export abstract class BaseZoomAction extends Action { return; // https://github.com/microsoft/vscode/issues/48357 } - const applyZoom = () => { - webFrame.setZoomLevel(level); - 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); - }; - await this.configurationService.updateValue(BaseZoomAction.SETTING_KEY, level); - applyZoom(); + applyZoom(level); } } @@ -93,7 +82,7 @@ export class ZoomInAction extends BaseZoomAction { } async run(): Promise { - this.setConfiguredZoomLevel(webFrame.getZoomLevel() + 1); + this.setConfiguredZoomLevel(getZoomLevel() + 1); } } @@ -111,7 +100,7 @@ export class ZoomOutAction extends BaseZoomAction { } async run(): Promise { - this.setConfiguredZoomLevel(webFrame.getZoomLevel() - 1); + this.setConfiguredZoomLevel(getZoomLevel() - 1); } } @@ -169,7 +158,6 @@ export abstract class BaseSwitchWindow extends Action { constructor( id: string, label: string, - private readonly environmentService: INativeWorkbenchEnvironmentService, private readonly quickInputService: IQuickInputService, private readonly keybindingService: IKeybindingService, private readonly modelService: IModelService, @@ -182,7 +170,7 @@ export abstract class BaseSwitchWindow extends Action { protected abstract isQuickNavigate(): boolean; async run(): Promise { - const currentWindowId = this.environmentService.configuration.windowId; + const currentWindowId = this.electronService.windowId; const windows = await this.electronService.getWindows(); const placeHolder = nls.localize('switchWindowPlaceHolder', "Select a window to switch to"); @@ -225,14 +213,13 @@ export class SwitchWindow extends BaseSwitchWindow { constructor( id: string, label: string, - @IWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService, @IQuickInputService quickInputService: IQuickInputService, @IKeybindingService keybindingService: IKeybindingService, @IModelService modelService: IModelService, @IModeService modeService: IModeService, @IElectronService electronService: IElectronService ) { - super(id, label, environmentService, quickInputService, keybindingService, modelService, modeService, electronService); + super(id, label, quickInputService, keybindingService, modelService, modeService, electronService); } protected isQuickNavigate(): boolean { @@ -248,14 +235,13 @@ export class QuickSwitchWindow extends BaseSwitchWindow { constructor( id: string, label: string, - @IWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService, @IQuickInputService quickInputService: IQuickInputService, @IKeybindingService keybindingService: IKeybindingService, @IModelService modelService: IModelService, @IModeService modeService: IModeService, @IElectronService electronService: IElectronService ) { - super(id, label, environmentService, quickInputService, keybindingService, modelService, modeService, electronService); + super(id, label, quickInputService, keybindingService, modelService, modeService, electronService); } protected isQuickNavigate(): boolean { diff --git a/src/vs/workbench/electron-browser/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts similarity index 95% rename from src/vs/workbench/electron-browser/desktop.contribution.ts rename to src/vs/workbench/electron-sandbox/desktop.contribution.ts index 7739d40e638..0296383f560 100644 --- a/src/vs/workbench/electron-browser/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -5,21 +5,20 @@ import { Registry } from 'vs/platform/registry/common/platform'; import * as nls from 'vs/nls'; -import * as os from 'os'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; -import { ToggleSharedProcessAction, ToggleDevToolsAction, ConfigureRuntimeArgumentsAction } from 'vs/workbench/electron-browser/actions/developerActions'; -import { ZoomResetAction, ZoomOutAction, ZoomInAction, CloseCurrentWindowAction, SwitchWindow, QuickSwitchWindow, ReloadWindowWithExtensionsDisabledAction, NewWindowTabHandler, ShowPreviousWindowTabHandler, ShowNextWindowTabHandler, MoveWindowTabToNewWindowHandler, MergeWindowTabsHandlerHandler, ToggleWindowTabsBarHandler } from 'vs/workbench/electron-browser/actions/windowActions'; +import { ToggleDevToolsAction, ConfigureRuntimeArgumentsAction } from 'vs/workbench/electron-sandbox/actions/developerActions'; +import { ZoomResetAction, ZoomOutAction, ZoomInAction, CloseCurrentWindowAction, SwitchWindow, QuickSwitchWindow, ReloadWindowWithExtensionsDisabledAction, NewWindowTabHandler, ShowPreviousWindowTabHandler, ShowNextWindowTabHandler, MoveWindowTabToNewWindowHandler, MergeWindowTabsHandlerHandler, ToggleWindowTabsBarHandler } from 'vs/workbench/electron-sandbox/actions/windowActions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IsDevelopmentContext, IsMacContext } from 'vs/platform/contextkey/common/contextkeys'; import { NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import product from 'vs/platform/product/common/product'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; @@ -90,8 +89,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; // Actions: Developer (function registerDeveloperActions(): void { - const developerCategory = nls.localize('developer', "Developer"); - registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleSharedProcessAction), 'Developer: Toggle Shared Process', developerCategory); + const developerCategory = nls.localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer"); registry.registerWorkbenchAction(SyncActionDescriptor.from(ReloadWindowWithExtensionsDisabledAction), 'Developer: Reload With Extensions Disabled', developerCategory); registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleDevToolsAction), 'Developer: Toggle Developer Tools', developerCategory); @@ -282,7 +280,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; 'default': false, 'scope': ConfigurationScope.APPLICATION, 'description': nls.localize('window.nativeTabs', "Enables macOS Sierra window tabs. Note that changes require a full restart to apply and that native tabs will disable a custom title bar style if configured."), - 'included': isMacintosh && parseFloat(os.release()) >= 17 // Minimum: macOS Sierra (10.13.x = darwin 17.x) + 'included': isMacintosh }, 'window.nativeFullScreen': { 'type': 'boolean', @@ -345,6 +343,13 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; 'force-color-profile': { type: 'string', markdownDescription: nls.localize('argv.forceColorProfile', 'Allows to override the color profile to use. If you experience colors appear badly, try to set this to `srgb` and restart.') + }, + 'enable-proposed-api': { + type: 'array', + description: nls.localize('argv.enebleProposedApi', "Enable proposed APIs for a list of extension ids (such as \`vscode.git\`). Proposed APIs are unstable and subject to breaking without warning at any time. This should only be set for extension development and testing purposes."), + items: { + type: 'string' + } } } }; diff --git a/src/vs/workbench/electron-browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts similarity index 96% rename from src/vs/workbench/electron-browser/parts/titlebar/titlebarPart.ts rename to src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index 66a5283bd04..ea06426d444 100644 --- a/src/vs/workbench/electron-browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as browser from 'vs/base/browser/browser'; +import { getZoomFactor } from 'vs/base/browser/browser'; import * as DOM from 'vs/base/browser/dom'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; @@ -20,7 +20,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { getTitleBarStyle } from 'vs/platform/windows/common/windows'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Codicon } from 'vs/base/common/codicons'; @@ -209,14 +209,14 @@ export class TitlebarPart extends BrowserTitleBarPart { if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { // Only prevent zooming behavior on macOS or when the menubar is not visible if (isMacintosh || this.currentMenubarVisibility === 'hidden') { - this.title.style.zoom = `${1 / browser.getZoomFactor()}`; + this.title.style.zoom = `${1 / getZoomFactor()}`; if (isWindows || isLinux) { if (this.appIcon) { - this.appIcon.style.zoom = `${1 / browser.getZoomFactor()}`; + this.appIcon.style.zoom = `${1 / getZoomFactor()}`; } if (this.windowControls) { - this.windowControls.style.zoom = `${1 / browser.getZoomFactor()}`; + this.windowControls.style.zoom = `${1 / getZoomFactor()}`; } } } else { diff --git a/src/vs/workbench/services/accessibility/electron-browser/accessibilityService.ts b/src/vs/workbench/services/accessibility/electron-browser/accessibilityService.ts index 19d44947f6b..5834d7e9023 100644 --- a/src/vs/workbench/services/accessibility/electron-browser/accessibilityService.ts +++ b/src/vs/workbench/services/accessibility/electron-browser/accessibilityService.ts @@ -27,7 +27,7 @@ type AccessibilityMetricsClassification = { export class NativeAccessibilityService extends AccessibilityService implements IAccessibilityService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private didSendTelemetry = false; @@ -81,7 +81,7 @@ class LinuxAccessibilityContribution implements IWorkbenchContribution { ) { const forceRendererAccessibility = () => { if (accessibilityService.isScreenReaderOptimized()) { - jsonEditingService.write(environmentService.argvResource, [{ key: 'force-renderer-accessibility', value: true }], true); + jsonEditingService.write(environmentService.argvResource, [{ path: ['force-renderer-accessibility'], value: true }], true); } }; forceRendererAccessibility(); diff --git a/src/vs/workbench/services/activity/browser/activityService.ts b/src/vs/workbench/services/activity/browser/activityService.ts index 0027f2b19c1..179de36e3bc 100644 --- a/src/vs/workbench/services/activity/browser/activityService.ts +++ b/src/vs/workbench/services/activity/browser/activityService.ts @@ -5,20 +5,68 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IActivityService, IActivity } from 'vs/workbench/services/activity/common/activity'; -import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; -import { GLOBAL_ACTIVITY_ID } from 'vs/workbench/common/activity'; +import { GLOBAL_ACTIVITY_ID, ACCOUNTS_ACTIIVTY_ID } from 'vs/workbench/common/activity'; +import { Event } from 'vs/base/common/event'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +class ViewContainerActivityByView extends Disposable { + + private activity: IActivity | undefined = undefined; + private activityDisposable: IDisposable = Disposable.None; + + constructor( + private readonly viewId: string, + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, + @IActivityService private readonly activityService: IActivityService, + ) { + super(); + this._register(Event.filter(this.viewDescriptorService.onDidChangeContainer, e => e.views.some(view => view.id === viewId))(() => this.update())); + this._register(Event.filter(this.viewDescriptorService.onDidChangeLocation, e => e.views.some(view => view.id === viewId))(() => this.update())); + } + + setActivity(activity: IActivity): void { + this.activity = activity; + this.update(); + } + + clearActivity(): void { + this.activity = undefined; + this.update(); + } + + private update(): void { + this.activityDisposable.dispose(); + const container = this.viewDescriptorService.getViewContainerByViewId(this.viewId); + if (container && this.activity) { + this.activityDisposable = this.activityService.showViewContainerActivity(container.id, this.activity); + } + } + + dispose() { + this.activityDisposable.dispose(); + } +} + +interface IViewActivity { + id: number; + readonly activity: ViewContainerActivityByView; +} export class ActivityService implements IActivityService { public _serviceBrand: undefined; + private viewActivities = new Map(); + constructor( @IPanelService private readonly panelService: IPanelService, @IActivityBarService private readonly activityBarService: IActivityBarService, @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, + @IInstantiationService private readonly instantiationService: IInstantiationService ) { } showViewContainerActivity(viewContainerId: string, { badge, clazz, priority }: IActivity): IDisposable { @@ -35,6 +83,36 @@ export class ActivityService implements IActivityService { return Disposable.None; } + showViewActivity(viewId: string, activity: IActivity): IDisposable { + let maybeItem = this.viewActivities.get(viewId); + + if (maybeItem) { + maybeItem.id++; + } else { + maybeItem = { + id: 1, + activity: this.instantiationService.createInstance(ViewContainerActivityByView, viewId) + }; + + this.viewActivities.set(viewId, maybeItem); + } + + const id = maybeItem.id; + maybeItem.activity.setActivity(activity); + + const item = maybeItem; + return toDisposable(() => { + if (item.id === id) { + item.activity.dispose(); + this.viewActivities.delete(viewId); + } + }); + } + + showAccountsActivity({ badge, clazz, priority }: IActivity): IDisposable { + return this.activityBarService.showActivity(ACCOUNTS_ACTIIVTY_ID, badge, clazz, priority); + } + showGlobalActivity({ badge, clazz, priority }: IActivity): IDisposable { return this.activityBarService.showActivity(GLOBAL_ACTIVITY_ID, badge, clazz, priority); } diff --git a/src/vs/workbench/services/activity/common/activity.ts b/src/vs/workbench/services/activity/common/activity.ts index d3384af09ef..3ca5c6a34db 100644 --- a/src/vs/workbench/services/activity/common/activity.ts +++ b/src/vs/workbench/services/activity/common/activity.ts @@ -3,10 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IViewDescriptorService } from 'vs/workbench/common/views'; -import { Event } from 'vs/base/common/event'; export interface IActivity { readonly badge: IBadge; @@ -18,52 +16,29 @@ export const IActivityService = createDecorator('activityServi export interface IActivityService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Show activity for the given view container */ showViewContainerActivity(viewContainerId: string, badge: IActivity): IDisposable; + /** + * Show activity for the given view + */ + showViewActivity(viewId: string, badge: IActivity): IDisposable; + + /** + * Show accounts activity + */ + showAccountsActivity(activity: IActivity): IDisposable; + /** * Show global activity */ showGlobalActivity(activity: IActivity): IDisposable; } -export class ViewContaierActivityByView extends Disposable { - - private activity: IActivity | undefined = undefined; - private activityDisposable: IDisposable = Disposable.None; - - constructor( - private readonly viewId: string, - @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, - @IActivityService private readonly activityService: IActivityService, - ) { - super(); - this._register(Event.filter(this.viewDescriptorService.onDidChangeContainer, e => e.views.some(view => view.id === viewId))(() => this.update())); - this._register(Event.filter(this.viewDescriptorService.onDidChangeLocation, e => e.views.some(view => view.id === viewId))(() => this.update())); - } - - setActivity(activity: IActivity): void { - this.activity = activity; - this.update(); - } - - private update(): void { - this.activityDisposable.dispose(); - const container = this.viewDescriptorService.getViewContainerByViewId(this.viewId); - if (container && this.activity) { - this.activityDisposable = this.activityService.showViewContainerActivity(container.id, this.activity); - } - } - - dispose() { - this.activityDisposable.dispose(); - } -} - export interface IBadge { getDescription(): string; } diff --git a/src/vs/workbench/services/activityBar/browser/activityBarService.ts b/src/vs/workbench/services/activityBar/browser/activityBarService.ts index dc76b07f76b..38fcc120b1a 100644 --- a/src/vs/workbench/services/activityBar/browser/activityBarService.ts +++ b/src/vs/workbench/services/activityBar/browser/activityBarService.ts @@ -10,7 +10,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; export const IActivityBarService = createDecorator('activityBarService'); export interface IActivityBarService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Show an activity in a viewlet. diff --git a/src/vs/workbench/services/authentication/browser/authenticationService.ts b/src/vs/workbench/services/authentication/browser/authenticationService.ts index 8f5d8f52cf5..26eccd7c66c 100644 --- a/src/vs/workbench/services/authentication/browser/authenticationService.ts +++ b/src/vs/workbench/services/authentication/browser/authenticationService.ts @@ -6,50 +6,95 @@ import * as nls from 'vs/nls'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import { AuthenticationSession, AuthenticationSessionsChangeEvent } from 'vs/editor/common/modes'; +import { AuthenticationSession, AuthenticationSessionsChangeEvent, AuthenticationProviderInformation } from 'vs/editor/common/modes'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { MainThreadAuthenticationProvider } from 'vs/workbench/api/browser/mainThreadAuthentication'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; + +export function getAuthenticationProviderActivationEvent(id: string): string { return `onAuthenticationRequest:${id}`; } export const IAuthenticationService = createDecorator('IAuthenticationService'); export interface IAuthenticationService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; isAuthenticationProviderRegistered(id: string): boolean; + getProviderIds(): string[]; registerAuthenticationProvider(id: string, provider: MainThreadAuthenticationProvider): void; unregisterAuthenticationProvider(id: string): void; + requestNewSession(id: string, scopes: string[], extensionId: string, extensionName: string): void; sessionsUpdate(providerId: string, event: AuthenticationSessionsChangeEvent): void; - readonly onDidRegisterAuthenticationProvider: Event; - readonly onDidUnregisterAuthenticationProvider: Event; + readonly onDidRegisterAuthenticationProvider: Event; + readonly onDidUnregisterAuthenticationProvider: Event; - readonly onDidChangeSessions: Event<{ providerId: string, event: AuthenticationSessionsChangeEvent }>; - getSessions(providerId: string): Promise | undefined>; - getDisplayName(providerId: string): string; + readonly onDidChangeSessions: Event<{ providerId: string, label: string, event: AuthenticationSessionsChangeEvent }>; + getSessions(providerId: string): Promise>; + getLabel(providerId: string): string; + supportsMultipleAccounts(providerId: string): boolean; login(providerId: string, scopes: string[]): Promise; - logout(providerId: string, accountId: string): Promise; + logout(providerId: string, sessionId: string): Promise; + + manageTrustedExtensionsForAccount(providerId: string, accountName: string): Promise; + signOutOfAccount(providerId: string, accountName: string): Promise; } +export interface AllowedExtension { + id: string; + name: string; +} + +export function readAllowedExtensions(storageService: IStorageService, providerId: string, accountName: string): AllowedExtension[] { + let trustedExtensions: AllowedExtension[] = []; + try { + const trustedExtensionSrc = storageService.get(`${providerId}-${accountName}`, StorageScope.GLOBAL); + if (trustedExtensionSrc) { + trustedExtensions = JSON.parse(trustedExtensionSrc); + } + } catch (err) { } + + return trustedExtensions; +} + +export interface SessionRequest { + disposables: IDisposable[]; + requestingExtensionIds: string[]; +} + +export interface SessionRequestInfo { + [scopes: string]: SessionRequest; +} + +CommandsRegistry.registerCommand('workbench.getCodeExchangeProxyEndpoints', function (accessor, _) { + const environmentService = accessor.get(IWorkbenchEnvironmentService); + return environmentService.options?.codeExchangeProxyEndpoints; +}); + export class AuthenticationService extends Disposable implements IAuthenticationService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _placeholderMenuItem: IDisposable | undefined; private _noAccountsMenuItem: IDisposable | undefined; + private _signInRequestItems = new Map(); + private _badgeDisposable: IDisposable | undefined; private _authenticationProviders: Map = new Map(); - private _onDidRegisterAuthenticationProvider: Emitter = this._register(new Emitter()); - readonly onDidRegisterAuthenticationProvider: Event = this._onDidRegisterAuthenticationProvider.event; + private _onDidRegisterAuthenticationProvider: Emitter = this._register(new Emitter()); + readonly onDidRegisterAuthenticationProvider: Event = this._onDidRegisterAuthenticationProvider.event; - private _onDidUnregisterAuthenticationProvider: Emitter = this._register(new Emitter()); - readonly onDidUnregisterAuthenticationProvider: Event = this._onDidUnregisterAuthenticationProvider.event; + private _onDidUnregisterAuthenticationProvider: Emitter = this._register(new Emitter()); + readonly onDidUnregisterAuthenticationProvider: Event = this._onDidUnregisterAuthenticationProvider.event; - private _onDidChangeSessions: Emitter<{ providerId: string, event: AuthenticationSessionsChangeEvent }> = this._register(new Emitter<{ providerId: string, event: AuthenticationSessionsChangeEvent }>()); - readonly onDidChangeSessions: Event<{ providerId: string, event: AuthenticationSessionsChangeEvent }> = this._onDidChangeSessions.event; + private _onDidChangeSessions: Emitter<{ providerId: string, label: string, event: AuthenticationSessionsChangeEvent }> = this._register(new Emitter<{ providerId: string, label: string, event: AuthenticationSessionsChangeEvent }>()); + readonly onDidChangeSessions: Event<{ providerId: string, label: string, event: AuthenticationSessionsChangeEvent }> = this._onDidChangeSessions.event; - constructor() { + constructor(@IActivityService private readonly activityService: IActivityService) { super(); this._placeholderMenuItem = MenuRegistry.appendMenuItem(MenuId.AccountsContext, { command: { @@ -60,6 +105,14 @@ export class AuthenticationService extends Disposable implements IAuthentication }); } + getProviderIds(): string[] { + const providerIds: string[] = []; + this._authenticationProviders.forEach(provider => { + providerIds.push(provider.id); + }); + return providerIds; + } + isAuthenticationProviderRegistered(id: string): boolean { return this._authenticationProviders.has(id); } @@ -89,7 +142,7 @@ export class AuthenticationService extends Disposable implements IAuthentication registerAuthenticationProvider(id: string, authenticationProvider: MainThreadAuthenticationProvider): void { this._authenticationProviders.set(id, authenticationProvider); - this._onDidRegisterAuthenticationProvider.fire(id); + this._onDidRegisterAuthenticationProvider.fire({ id, label: authenticationProvider.label }); if (this._placeholderMenuItem) { this._placeholderMenuItem.dispose(); @@ -104,7 +157,7 @@ export class AuthenticationService extends Disposable implements IAuthentication if (provider) { provider.dispose(); this._authenticationProviders.delete(id); - this._onDidUnregisterAuthenticationProvider.fire(id); + this._onDidUnregisterAuthenticationProvider.fire({ id, label: provider.label }); this.updateAccountsMenuItem(); } @@ -120,30 +173,161 @@ export class AuthenticationService extends Disposable implements IAuthentication } async sessionsUpdate(id: string, event: AuthenticationSessionsChangeEvent): Promise { - this._onDidChangeSessions.fire({ providerId: id, event: event }); const provider = this._authenticationProviders.get(id); if (provider) { + this._onDidChangeSessions.fire({ providerId: id, label: provider.label, event: event }); await provider.updateSessionItems(event); this.updateAccountsMenuItem(); + + if (event.added) { + await this.updateNewSessionRequests(provider); + } } } - getDisplayName(id: string): string { + private async updateNewSessionRequests(provider: MainThreadAuthenticationProvider): Promise { + const existingRequestsForProvider = this._signInRequestItems.get(provider.id); + if (!existingRequestsForProvider) { + return; + } + + const sessions = await provider.getSessions(); + let changed = false; + + Object.keys(existingRequestsForProvider).forEach(requestedScopes => { + if (sessions.some(session => session.scopes.slice().sort().join('') === requestedScopes)) { + // Request has been completed + changed = true; + const sessionRequest = existingRequestsForProvider[requestedScopes]; + sessionRequest?.disposables.forEach(item => item.dispose()); + + delete existingRequestsForProvider[requestedScopes]; + if (Object.keys(existingRequestsForProvider).length === 0) { + this._signInRequestItems.delete(provider.id); + } else { + this._signInRequestItems.set(provider.id, existingRequestsForProvider); + } + } + }); + + if (changed) { + if (this._signInRequestItems.size === 0) { + this._badgeDisposable?.dispose(); + this._badgeDisposable = undefined; + } else { + let numberOfRequests = 0; + this._signInRequestItems.forEach(providerRequests => { + Object.keys(providerRequests).forEach(request => { + numberOfRequests += providerRequests[request].requestingExtensionIds.length; + }); + }); + + const badge = new NumberBadge(numberOfRequests, () => nls.localize('sign in', "Sign in requested")); + this._badgeDisposable = this.activityService.showAccountsActivity({ badge }); + } + } + } + + requestNewSession(providerId: string, scopes: string[], extensionId: string, extensionName: string): void { + const provider = this._authenticationProviders.get(providerId); + if (provider) { + const providerRequests = this._signInRequestItems.get(providerId); + const scopesList = scopes.sort().join(''); + const extensionHasExistingRequest = providerRequests + && providerRequests[scopesList] + && providerRequests[scopesList].requestingExtensionIds.includes(extensionId); + + if (extensionHasExistingRequest) { + return; + } + + const menuItem = MenuRegistry.appendMenuItem(MenuId.AccountsContext, { + group: '2_signInRequests', + command: { + id: `${extensionId}signIn`, + title: nls.localize( + { + key: 'signInRequest', + comment: ['The placeholder {0} will be replaced with an extension name. (1) is to indicate that this menu item contributes to a badge count.'] + }, + "Sign in to use {0} (1)", + extensionName) + } + }); + + const signInCommand = CommandsRegistry.registerCommand({ + id: `${extensionId}signIn`, + handler: async (accessor) => { + const authenticationService = accessor.get(IAuthenticationService); + const storageService = accessor.get(IStorageService); + const session = await authenticationService.login(providerId, scopes); + + // Add extension to allow list since user explicitly signed in on behalf of it + const allowList = readAllowedExtensions(storageService, providerId, session.account.label); + if (!allowList.find(allowed => allowed.id === extensionId)) { + allowList.push({ id: extensionId, name: extensionName }); + storageService.store(`${providerId}-${session.account.label}`, JSON.stringify(allowList), StorageScope.GLOBAL); + } + + // And also set it as the preferred account for the extension + storageService.store(`${extensionName}-${providerId}`, session.id, StorageScope.GLOBAL); + } + }); + + + if (providerRequests) { + const existingRequest = providerRequests[scopesList] || { disposables: [], requestingExtensionIds: [] }; + + providerRequests[scopesList] = { + disposables: [...existingRequest.disposables, menuItem, signInCommand], + requestingExtensionIds: [...existingRequest.requestingExtensionIds, extensionId] + }; + this._signInRequestItems.set(providerId, providerRequests); + } else { + this._signInRequestItems.set(providerId, { + [scopesList]: { + disposables: [menuItem, signInCommand], + requestingExtensionIds: [extensionId] + } + }); + } + + let numberOfRequests = 0; + this._signInRequestItems.forEach(providerRequests => { + Object.keys(providerRequests).forEach(request => { + numberOfRequests += providerRequests[request].requestingExtensionIds.length; + }); + }); + + const badge = new NumberBadge(numberOfRequests, () => nls.localize('sign in', "Sign in requested")); + this._badgeDisposable = this.activityService.showAccountsActivity({ badge }); + } + } + getLabel(id: string): string { const authProvider = this._authenticationProviders.get(id); if (authProvider) { - return authProvider.displayName; + return authProvider.label; } else { throw new Error(`No authentication provider '${id}' is currently registered.`); } } - async getSessions(id: string): Promise | undefined> { + supportsMultipleAccounts(id: string): boolean { + const authProvider = this._authenticationProviders.get(id); + if (authProvider) { + return authProvider.supportsMultipleAccounts; + } else { + throw new Error(`No authentication provider '${id}' is currently registered.`); + } + } + + async getSessions(id: string): Promise> { const authProvider = this._authenticationProviders.get(id); if (authProvider) { return await authProvider.getSessions(); + } else { + throw new Error(`No authentication provider '${id}' is currently registered.`); } - - return undefined; } async login(id: string, scopes: string[]): Promise { @@ -155,10 +339,28 @@ export class AuthenticationService extends Disposable implements IAuthentication } } - async logout(id: string, accountId: string): Promise { + async logout(id: string, sessionId: string): Promise { const authProvider = this._authenticationProviders.get(id); if (authProvider) { - return authProvider.logout(accountId); + return authProvider.logout(sessionId); + } else { + throw new Error(`No authentication provider '${id}' is currently registered.`); + } + } + + async manageTrustedExtensionsForAccount(id: string, accountName: string): Promise { + const authProvider = this._authenticationProviders.get(id); + if (authProvider) { + return authProvider.manageTrustedExtensions(accountName); + } else { + throw new Error(`No authentication provider '${id}' is currently registered.`); + } + } + + async signOutOfAccount(id: string, accountName: string): Promise { + const authProvider = this._authenticationProviders.get(id); + if (authProvider) { + return authProvider.signOut(accountName); } else { throw new Error(`No authentication provider '${id}' is currently registered.`); } diff --git a/src/vs/workbench/services/authentication/electron-browser/authenticationTokenService.ts b/src/vs/workbench/services/authentication/electron-browser/authenticationTokenService.ts deleted file mode 100644 index c6186a1cce3..00000000000 --- a/src/vs/workbench/services/authentication/electron-browser/authenticationTokenService.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 { IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { Emitter, Event } from 'vs/base/common/event'; -import { IAuthenticationTokenService, IUserDataSyncAuthToken } from 'vs/platform/authentication/common/authentication'; - -export class AuthenticationTokenService extends Disposable implements IAuthenticationTokenService { - - _serviceBrand: undefined; - - private readonly channel: IChannel; - private _onDidChangeToken = this._register(new Emitter()); - readonly onDidChangeToken = this._onDidChangeToken.event; - - private _onTokenFailed: Emitter = this._register(new Emitter()); - readonly onTokenFailed: Event = this._onTokenFailed.event; - - constructor( - @ISharedProcessService sharedProcessService: ISharedProcessService, - ) { - super(); - this.channel = sharedProcessService.getChannel('authToken'); - this._register(this.channel.listen('onTokenFailed')(_ => this.sendTokenFailed())); - } - - getToken(): Promise { - return this.channel.call('getToken'); - } - - setToken(token: IUserDataSyncAuthToken | undefined): Promise { - return this.channel.call('setToken', token); - } - - sendTokenFailed(): void { - this._onTokenFailed.fire(); - } -} - -registerSingleton(IAuthenticationTokenService, AuthenticationTokenService); diff --git a/src/vs/workbench/services/backup/common/backup.ts b/src/vs/workbench/services/backup/common/backup.ts index 087ccd70e7f..3637e0d4467 100644 --- a/src/vs/workbench/services/backup/common/backup.ts +++ b/src/vs/workbench/services/backup/common/backup.ts @@ -19,7 +19,7 @@ export interface IResolvedBackup { */ export interface IBackupFileService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Finds out if there are any backups stored. diff --git a/src/vs/workbench/services/backup/common/backupFileService.ts b/src/vs/workbench/services/backup/common/backupFileService.ts index dbeafa943bc..71fcefaae3a 100644 --- a/src/vs/workbench/services/backup/common/backupFileService.ts +++ b/src/vs/workbench/services/backup/common/backupFileService.ts @@ -109,7 +109,7 @@ export class BackupFilesModel implements IBackupFilesModel { export class BackupFileService implements IBackupFileService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private impl: BackupFileServiceImpl | InMemoryBackupFileService; @@ -188,7 +188,7 @@ class BackupFileServiceImpl extends Disposable implements IBackupFileService { private static readonly PREAMBLE_META_SEPARATOR = ' '; // using a character that is know to be escaped in a URI as separator private static readonly PREAMBLE_MAX_LENGTH = 10000; - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private backupWorkspacePath!: URI; @@ -322,15 +322,16 @@ class BackupFileServiceImpl extends Disposable implements IBackupFileService { return coalesce(backups); } - private async readToMatchingString(file: URI, matchingString: string, maximumBytesToRead: number): Promise { + private async readToMatchingString(file: URI, matchingString: string, maximumBytesToRead: number): Promise { const contents = (await this.fileService.readFile(file, { length: maximumBytesToRead })).value.toString(); - const newLineIndex = contents.indexOf(matchingString); - if (newLineIndex >= 0) { - return contents.substr(0, newLineIndex); + const matchingStringIndex = contents.indexOf(matchingString); + if (matchingStringIndex >= 0) { + return contents.substr(0, matchingStringIndex); } - throw new Error(`Backup: Could not find ${JSON.stringify(matchingString)} in first ${maximumBytesToRead} bytes of ${file}`); + // Unable to find matching string in file + return undefined; } async resolve(resource: URI): Promise | undefined> { @@ -387,7 +388,7 @@ class BackupFileServiceImpl extends Disposable implements IBackupFileService { // the meta-end marker ('\n') and as such the backup can only be invalid. We bail out // here if that is the case. if (!metaEndFound) { - this.logService.error(`Backup: Could not find meta end marker in ${backupResource}. The file is probably corrupt.`); + this.logService.trace(`Backup: Could not find meta end marker in ${backupResource}. The file is probably corrupt (filesize: ${content.size}).`); return undefined; } @@ -402,7 +403,7 @@ class BackupFileServiceImpl extends Disposable implements IBackupFileService { export class InMemoryBackupFileService implements IBackupFileService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private backups: Map = new Map(); diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts index 23cc7b80b96..ca37d322d6c 100644 --- a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts @@ -3,249 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { mergeSort } from 'vs/base/common/arrays'; -import { dispose, IDisposable, IReference, toDisposable } from 'vs/base/common/lifecycle'; -import { URI } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IBulkEditOptions, IBulkEditResult, IBulkEditService, IBulkEditPreviewHandler } from 'vs/editor/browser/services/bulkEditService'; -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 { EndOfLineSequence, IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; import { WorkspaceFileEdit, WorkspaceTextEdit, WorkspaceEdit } from 'vs/editor/common/modes'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; -import { localize } from 'vs/nls'; -import { IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { IProgress, IProgressStep, Progress } from 'vs/platform/progress/common/progress'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; -import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; -import { SingleModelEditStackElement, MultiModelEditStackElement } from 'vs/editor/common/model/editStack'; - -type ValidationResult = { canApply: true } | { canApply: false, reason: URI }; - -class ModelEditTask implements IDisposable { - - public readonly model: ITextModel; - - protected _edits: IIdentifiedSingleEditOperation[]; - private _expectedModelVersionId: number | undefined; - protected _newEol: EndOfLineSequence | undefined; - - constructor(private readonly _modelReference: IReference) { - this.model = this._modelReference.object.textEditorModel; - this._edits = []; - } - - dispose() { - this._modelReference.dispose(); - } - - addEdit(resourceEdit: WorkspaceTextEdit): void { - this._expectedModelVersionId = resourceEdit.modelVersionId; - const { edit } = resourceEdit; - - if (typeof edit.eol === 'number') { - // honor eol-change - this._newEol = edit.eol; - } - if (!edit.range && !edit.text) { - // lacks both a range and the text - return; - } - if (Range.isEmpty(edit.range) && !edit.text) { - // no-op edit (replace empty range with empty text) - return; - } - - // create edit operation - let range: Range; - if (!edit.range) { - range = this.model.getFullModelRange(); - } else { - range = Range.lift(edit.range); - } - this._edits.push(EditOperation.replaceMove(range, edit.text)); - } - - validate(): ValidationResult { - if (typeof this._expectedModelVersionId === 'undefined' || this.model.getVersionId() === this._expectedModelVersionId) { - return { canApply: true }; - } - return { canApply: false, reason: this.model.uri }; - } - - getBeforeCursorState(): Selection[] | null { - return null; - } - - apply(): void { - if (this._edits.length > 0) { - this._edits = mergeSort(this._edits, (a, b) => Range.compareRangesUsingStarts(a.range, b.range)); - this.model.pushEditOperations(null, this._edits, () => null); - } - if (this._newEol !== undefined) { - this.model.pushEOL(this._newEol); - } - } -} - -class EditorEditTask extends ModelEditTask { - - private _editor: ICodeEditor; - - constructor(modelReference: IReference, editor: ICodeEditor) { - super(modelReference); - this._editor = editor; - } - - getBeforeCursorState(): Selection[] | null { - return this._editor.getSelections(); - } - - apply(): void { - if (this._edits.length > 0) { - this._edits = mergeSort(this._edits, (a, b) => Range.compareRangesUsingStarts(a.range, b.range)); - this._editor.executeEdits('', this._edits); - } - if (this._newEol !== undefined) { - if (this._editor.hasModel()) { - this._editor.getModel().pushEOL(this._newEol); - } - } - } -} - -class BulkEditModel implements IDisposable { - - private _edits = new Map(); - private _tasks: ModelEditTask[] | undefined; - - constructor( - private readonly _label: string | undefined, - private readonly _editor: ICodeEditor | undefined, - private readonly _progress: IProgress, - edits: WorkspaceTextEdit[], - @IEditorWorkerService private readonly _editorWorker: IEditorWorkerService, - @ITextModelService private readonly _textModelResolverService: ITextModelService, - @IUndoRedoService private readonly _undoRedoService: IUndoRedoService - ) { - edits.forEach(this._addEdit, this); - } - - dispose(): void { - if (this._tasks) { - dispose(this._tasks); - } - } - - private _addEdit(edit: WorkspaceTextEdit): void { - let array = this._edits.get(edit.resource.toString()); - if (!array) { - array = []; - this._edits.set(edit.resource.toString(), array); - } - array.push(edit); - } - - async prepare(): Promise { - - if (this._tasks) { - throw new Error('illegal state - already prepared'); - } - - this._tasks = []; - const promises: Promise[] = []; - - this._edits.forEach((value, key) => { - const promise = this._textModelResolverService.createModelReference(URI.parse(key)).then(async ref => { - const model = ref.object; - - if (!model || !model.textEditorModel) { - throw new Error(`Cannot load file ${key}`); - } - - let task: ModelEditTask; - let makeMinimal = false; - if (this._editor && this._editor.hasModel() && this._editor.getModel().uri.toString() === model.textEditorModel.uri.toString()) { - task = new EditorEditTask(ref, this._editor); - makeMinimal = true; - } else { - task = new ModelEditTask(ref); - } - - for (const edit of value) { - if (makeMinimal) { - const newEdits = await this._editorWorker.computeMoreMinimalEdits(edit.resource, [edit.edit]); - if (!newEdits) { - task.addEdit(edit); - } else { - for (let moreMinialEdit of newEdits) { - task.addEdit({ ...edit, edit: moreMinialEdit }); - } - } - } else { - task.addEdit(edit); - } - } - - this._tasks!.push(task); - this._progress.report(undefined); - }); - promises.push(promise); - }); - - await Promise.all(promises); - - return this; - } - - validate(): ValidationResult { - for (const task of this._tasks!) { - const result = task.validate(); - if (!result.canApply) { - return result; - } - } - return { canApply: true }; - } - - apply(): void { - const tasks = this._tasks!; - - if (tasks.length === 1) { - // This edit touches a single model => keep things simple - for (const task of tasks) { - task.model.pushStackElement(); - task.apply(); - task.model.pushStackElement(); - this._progress.report(undefined); - } - return; - } - - const multiModelEditStackElement = new MultiModelEditStackElement( - this._label || localize('workspaceEdit', "Workspace Edit"), - tasks.map(t => new SingleModelEditStackElement(t.model, t.getBeforeCursorState())) - ); - this._undoRedoService.pushElement(multiModelEditStackElement); - - for (const task of tasks) { - task.apply(); - this._progress.report(undefined); - } - - multiModelEditStackElement.close(); - } -} +import { BulkTextEdits } from 'vs/workbench/services/bulkEdit/browser/bulkTextEdits'; +import { BulkFileEdits } from 'vs/workbench/services/bulkEdit/browser/bulkFileEdits'; +import { ResourceMap } from 'vs/base/common/map'; type Edit = WorkspaceFileEdit | WorkspaceTextEdit; @@ -263,10 +34,6 @@ class BulkEdit { edits: Edit[], @IInstantiationService private readonly _instaService: IInstantiationService, @ILogService private readonly _logService: ILogService, - @IFileService private readonly _fileService: IFileService, - @ITextFileService private readonly _textFileService: ITextFileService, - @IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService, - @IConfigurationService private readonly _configurationService: IConfigurationService ) { this._label = label; this._editor = editor; @@ -288,7 +55,7 @@ class BulkEdit { async perform(): Promise { - let seen = new Set(); + let seen = new ResourceMap(); let total = 0; const groups: Edit[][] = []; @@ -305,8 +72,8 @@ class BulkEdit { if (WorkspaceFileEdit.is(edit)) { total += 1; - } else if (!seen.has(edit.resource.toString())) { - seen.add(edit.resource.toString()); + } else if (!seen.has(edit.resource)) { + seen.set(edit.resource, true); total += 2; } } @@ -329,68 +96,26 @@ class BulkEdit { private async _performFileEdits(edits: WorkspaceFileEdit[], progress: IProgress) { this._logService.debug('_performFileEdits', JSON.stringify(edits)); - for (const edit of edits) { - progress.report(undefined); - - let options = edit.options || {}; - - if (edit.newUri && edit.oldUri) { - // rename - if (options.overwrite === undefined && options.ignoreIfExists && await this._fileService.exists(edit.newUri)) { - continue; // not overwriting, but ignoring, and the target file exists - } - await this._workingCopyFileService.move(edit.oldUri, edit.newUri, options.overwrite); - - } else if (!edit.newUri && edit.oldUri) { - // delete file - if (await this._fileService.exists(edit.oldUri)) { - let useTrash = this._configurationService.getValue('files.enableTrash'); - if (useTrash && !(this._fileService.hasCapability(edit.oldUri, FileSystemProviderCapabilities.Trash))) { - useTrash = false; // not supported by provider - } - await this._workingCopyFileService.delete(edit.oldUri, { useTrash, recursive: options.recursive }); - } else if (!options.ignoreIfNotExists) { - throw new Error(`${edit.oldUri} does not exist and can not be deleted`); - } - } else if (edit.newUri && !edit.oldUri) { - // create file - if (options.overwrite === undefined && options.ignoreIfExists && await this._fileService.exists(edit.newUri)) { - continue; // not overwriting, but ignoring, and the target file exists - } - await this._textFileService.create(edit.newUri, undefined, { overwrite: options.overwrite }); - } - } + const model = this._instaService.createInstance(BulkFileEdits, this._label || localize('workspaceEdit', "Workspace Edit"), progress, edits); + await model.apply(); } private async _performTextEdits(edits: WorkspaceTextEdit[], progress: IProgress): Promise { this._logService.debug('_performTextEdits', JSON.stringify(edits)); - - const model = this._instaService.createInstance(BulkEditModel, this._label, this._editor, progress, edits); - - await model.prepare(); - - // this._throwIfConflicts(conflicts); - const validationResult = model.validate(); - if (validationResult.canApply === false) { - model.dispose(); - throw new Error(`${validationResult.reason.toString()} has changed in the meantime`); - } - - model.apply(); - model.dispose(); + const model = this._instaService.createInstance(BulkTextEdits, this._label || localize('workspaceEdit', "Workspace Edit"), this._editor, progress, edits); + await model.apply(); } } export class BulkEditService implements IBulkEditService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _previewHandler?: IBulkEditPreviewHandler; constructor( @IInstantiationService private readonly _instaService: IInstantiationService, @ILogService private readonly _logService: ILogService, - @IModelService private readonly _modelService: IModelService, @IEditorService private readonly _editorService: IEditorService, ) { } @@ -419,18 +144,6 @@ export class BulkEditService implements IBulkEditService { const { edits } = edit; let codeEditor = options?.editor; - - // First check if loaded models were not changed in the meantime - for (const edit of edits) { - if (!WorkspaceFileEdit.is(edit) && typeof edit.modelVersionId === 'number') { - let model = this._modelService.getModel(edit.resource); - if (model && model.getVersionId() !== edit.modelVersionId) { - // model changed in the meantime - return Promise.reject(new Error(`${model.uri.toString()} has changed in the meantime`)); - } - } - } - // try to find code editor if (!codeEditor) { let candidate = this._editorService.activeTextEditorControl; diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkFileEdits.ts b/src/vs/workbench/services/bulkEdit/browser/bulkFileEdits.ts new file mode 100644 index 00000000000..b5e768349cb --- /dev/null +++ b/src/vs/workbench/services/bulkEdit/browser/bulkFileEdits.ts @@ -0,0 +1,180 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +import { WorkspaceFileEdit, WorkspaceFileEditOptions } from 'vs/editor/common/modes'; +import { IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; +import { IProgress } from 'vs/platform/progress/common/progress'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; +import { IWorkspaceUndoRedoElement, UndoRedoElementType, IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; +import { URI } from 'vs/base/common/uri'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ILogService } from 'vs/platform/log/common/log'; +import { VSBuffer } from 'vs/base/common/buffer'; + +interface IFileOperation { + uris: URI[]; + perform(): Promise; +} + +class Noop implements IFileOperation { + readonly uris = []; + async perform() { return this; } +} + +class RenameOperation implements IFileOperation { + + constructor( + readonly newUri: URI, + readonly oldUri: URI, + readonly options: WorkspaceFileEditOptions, + @IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService, + @IFileService private readonly _fileService: IFileService, + ) { } + + get uris() { + return [this.newUri, this.oldUri]; + } + + async perform(): Promise { + // rename + if (this.options.overwrite === undefined && this.options.ignoreIfExists && await this._fileService.exists(this.newUri)) { + return new Noop(); // not overwriting, but ignoring, and the target file exists + } + await this._workingCopyFileService.move([{ source: this.oldUri, target: this.newUri }], { overwrite: this.options.overwrite }); + return new RenameOperation(this.oldUri, this.newUri, this.options, this._workingCopyFileService, this._fileService); + } +} + +class CreateOperation implements IFileOperation { + + constructor( + readonly newUri: URI, + readonly options: WorkspaceFileEditOptions, + readonly contents: VSBuffer | undefined, + @IFileService private readonly _fileService: IFileService, + @IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService, + @IInstantiationService private readonly _instaService: IInstantiationService, + ) { } + + get uris() { + return [this.newUri]; + } + + async perform(): Promise { + // create file + if (this.options.overwrite === undefined && this.options.ignoreIfExists && await this._fileService.exists(this.newUri)) { + return new Noop(); // not overwriting, but ignoring, and the target file exists + } + await this._workingCopyFileService.create(this.newUri, this.contents, { overwrite: this.options.overwrite }); + return this._instaService.createInstance(DeleteOperation, this.newUri, this.options); + } +} + +class DeleteOperation implements IFileOperation { + + constructor( + readonly oldUri: URI, + readonly options: WorkspaceFileEditOptions, + @IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService, + @IFileService private readonly _fileService: IFileService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IInstantiationService private readonly _instaService: IInstantiationService, + @ILogService private readonly _logService: ILogService + ) { } + + get uris() { + return [this.oldUri]; + } + + async perform(): Promise { + // delete file + if (!await this._fileService.exists(this.oldUri)) { + if (!this.options.ignoreIfNotExists) { + throw new Error(`${this.oldUri} does not exist and can not be deleted`); + } + return new Noop(); + } + + let contents: VSBuffer | undefined; + try { + contents = (await this._fileService.readFile(this.oldUri)).value; + } catch (err) { + this._logService.critical(err); + } + + const useTrash = this._fileService.hasCapability(this.oldUri, FileSystemProviderCapabilities.Trash) && this._configurationService.getValue('files.enableTrash'); + await this._workingCopyFileService.delete([this.oldUri], { useTrash, recursive: this.options.recursive }); + return this._instaService.createInstance(CreateOperation, this.oldUri, this.options, contents); + } +} + +class FileUndoRedoElement implements IWorkspaceUndoRedoElement { + + readonly type = UndoRedoElementType.Workspace; + + readonly resources: readonly URI[]; + + constructor( + readonly label: string, + readonly operations: IFileOperation[] + ) { + this.resources = ([]).concat(...operations.map(op => op.uris)); + } + + async undo(): Promise { + await this._reverse(); + } + + async redo(): Promise { + await this._reverse(); + } + + private async _reverse() { + for (let i = 0; i < this.operations.length; i++) { + const op = this.operations[i]; + const undo = await op.perform(); + this.operations[i] = undo; + } + } +} + +export class BulkFileEdits { + + constructor( + private readonly _label: string, + private readonly _progress: IProgress, + private readonly _edits: WorkspaceFileEdit[], + @IInstantiationService private readonly _instaService: IInstantiationService, + @IUndoRedoService private readonly _undoRedoService: IUndoRedoService, + ) { } + + async apply(): Promise { + const undoOperations: IFileOperation[] = []; + for (const edit of this._edits) { + this._progress.report(undefined); + + const options = edit.options || {}; + let op: IFileOperation | undefined; + if (edit.newUri && edit.oldUri) { + // rename + op = this._instaService.createInstance(RenameOperation, edit.newUri, edit.oldUri, options); + } else if (!edit.newUri && edit.oldUri) { + // delete file + op = this._instaService.createInstance(DeleteOperation, edit.oldUri, options); + } else if (edit.newUri && !edit.oldUri) { + // create file + op = this._instaService.createInstance(CreateOperation, edit.newUri, options, undefined); + } + if (op) { + const undoOp = await op.perform(); + undoOperations.push(undoOp); + } + } + + this._undoRedoService.pushElement(new FileUndoRedoElement(this._label, undoOperations)); + } +} diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkTextEdits.ts b/src/vs/workbench/services/bulkEdit/browser/bulkTextEdits.ts new file mode 100644 index 00000000000..ca9dfa7739c --- /dev/null +++ b/src/vs/workbench/services/bulkEdit/browser/bulkTextEdits.ts @@ -0,0 +1,245 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { mergeSort } from 'vs/base/common/arrays'; +import { dispose, IDisposable, IReference } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +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 { EndOfLineSequence, IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; +import { WorkspaceTextEdit } from 'vs/editor/common/modes'; +import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; +import { IProgress } from 'vs/platform/progress/common/progress'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; +import { SingleModelEditStackElement, MultiModelEditStackElement } from 'vs/editor/common/model/editStack'; +import { ResourceMap } from 'vs/base/common/map'; +import { IModelService } from 'vs/editor/common/services/modelService'; + +type ValidationResult = { canApply: true } | { canApply: false, reason: URI }; + +class ModelEditTask implements IDisposable { + + readonly model: ITextModel; + + private _expectedModelVersionId: number | undefined; + protected _edits: IIdentifiedSingleEditOperation[]; + protected _newEol: EndOfLineSequence | undefined; + + constructor(private readonly _modelReference: IReference) { + this.model = this._modelReference.object.textEditorModel; + this._edits = []; + } + + dispose() { + this._modelReference.dispose(); + } + + addEdit(resourceEdit: WorkspaceTextEdit): void { + this._expectedModelVersionId = resourceEdit.modelVersionId; + const { edit } = resourceEdit; + + if (typeof edit.eol === 'number') { + // honor eol-change + this._newEol = edit.eol; + } + if (!edit.range && !edit.text) { + // lacks both a range and the text + return; + } + if (Range.isEmpty(edit.range) && !edit.text) { + // no-op edit (replace empty range with empty text) + return; + } + + // create edit operation + let range: Range; + if (!edit.range) { + range = this.model.getFullModelRange(); + } else { + range = Range.lift(edit.range); + } + this._edits.push(EditOperation.replaceMove(range, edit.text)); + } + + validate(): ValidationResult { + if (typeof this._expectedModelVersionId === 'undefined' || this.model.getVersionId() === this._expectedModelVersionId) { + return { canApply: true }; + } + return { canApply: false, reason: this.model.uri }; + } + + getBeforeCursorState(): Selection[] | null { + return null; + } + + apply(): void { + if (this._edits.length > 0) { + this._edits = mergeSort(this._edits, (a, b) => Range.compareRangesUsingStarts(a.range, b.range)); + this.model.pushEditOperations(null, this._edits, () => null); + } + if (this._newEol !== undefined) { + this.model.pushEOL(this._newEol); + } + } +} + +class EditorEditTask extends ModelEditTask { + + private _editor: ICodeEditor; + + constructor(modelReference: IReference, editor: ICodeEditor) { + super(modelReference); + this._editor = editor; + } + + getBeforeCursorState(): Selection[] | null { + return this._editor.getSelections(); + } + + apply(): void { + if (this._edits.length > 0) { + this._edits = mergeSort(this._edits, (a, b) => Range.compareRangesUsingStarts(a.range, b.range)); + this._editor.executeEdits('', this._edits); + } + if (this._newEol !== undefined) { + if (this._editor.hasModel()) { + this._editor.getModel().pushEOL(this._newEol); + } + } + } +} + +export class BulkTextEdits { + + private readonly _edits = new ResourceMap(); + + constructor( + private readonly _label: string, + private readonly _editor: ICodeEditor | undefined, + private readonly _progress: IProgress, + edits: WorkspaceTextEdit[], + @IEditorWorkerService private readonly _editorWorker: IEditorWorkerService, + @IModelService private readonly _modelService: IModelService, + @ITextModelService private readonly _textModelResolverService: ITextModelService, + @IUndoRedoService private readonly _undoRedoService: IUndoRedoService + ) { + + for (const edit of edits) { + let array = this._edits.get(edit.resource); + if (!array) { + array = []; + this._edits.set(edit.resource, array); + } + array.push(edit); + } + } + + private _validateBeforePrepare(): void { + // First check if loaded models were not changed in the meantime + for (const array of this._edits.values()) { + for (let edit of array) { + if (typeof edit.modelVersionId === 'number') { + let model = this._modelService.getModel(edit.resource); + if (model && model.getVersionId() !== edit.modelVersionId) { + // model changed in the meantime + throw new Error(`${model.uri.toString()} has changed in the meantime`); + } + } + } + } + } + + private async _createEditsTasks(): Promise { + + const tasks: ModelEditTask[] = []; + const promises: Promise[] = []; + + for (let [key, value] of this._edits) { + const promise = this._textModelResolverService.createModelReference(key).then(async ref => { + let task: ModelEditTask; + let makeMinimal = false; + if (this._editor?.getModel()?.uri.toString() === ref.object.textEditorModel.uri.toString()) { + task = new EditorEditTask(ref, this._editor); + makeMinimal = true; + } else { + task = new ModelEditTask(ref); + } + + for (const edit of value) { + if (makeMinimal) { + const newEdits = await this._editorWorker.computeMoreMinimalEdits(edit.resource, [edit.edit]); + if (!newEdits) { + task.addEdit(edit); + } else { + for (let moreMinialEdit of newEdits) { + task.addEdit({ ...edit, edit: moreMinialEdit }); + } + } + } else { + task.addEdit(edit); + } + } + + tasks.push(task); + this._progress.report(undefined); + }); + promises.push(promise); + } + + await Promise.all(promises); + return tasks; + } + + private _validateTasks(tasks: ModelEditTask[]): ValidationResult { + for (const task of tasks) { + const result = task.validate(); + if (!result.canApply) { + return result; + } + } + return { canApply: true }; + } + + async apply(): Promise { + + this._validateBeforePrepare(); + const tasks = await this._createEditsTasks(); + + try { + + const validation = this._validateTasks(tasks); + if (!validation.canApply) { + throw new Error(`${validation.reason.toString()} has changed in the meantime`); + } + if (tasks.length === 1) { + // This edit touches a single model => keep things simple + for (const task of tasks) { + task.model.pushStackElement(); + task.apply(); + task.model.pushStackElement(); + this._progress.report(undefined); + } + } else { + // prepare multi model undo element + const multiModelEditStackElement = new MultiModelEditStackElement( + this._label, + tasks.map(t => new SingleModelEditStackElement(t.model, t.getBeforeCursorState())) + ); + this._undoRedoService.pushElement(multiModelEditStackElement); + for (const task of tasks) { + task.apply(); + this._progress.report(undefined); + } + multiModelEditStackElement.close(); + } + + } finally { + dispose(tasks); + } + } +} diff --git a/src/vs/workbench/services/clipboard/electron-browser/clipboardService.ts b/src/vs/workbench/services/clipboard/electron-sandbox/clipboardService.ts similarity index 52% rename from src/vs/workbench/services/clipboard/electron-browser/clipboardService.ts rename to src/vs/workbench/services/clipboard/electron-sandbox/clipboardService.ts index 623385c0a79..3df04d9e4cf 100644 --- a/src/vs/workbench/services/clipboard/electron-browser/clipboardService.ts +++ b/src/vs/workbench/services/clipboard/electron-sandbox/clipboardService.ts @@ -4,62 +4,63 @@ *--------------------------------------------------------------------------------------------*/ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { clipboard } from 'electron'; import { URI } from 'vs/base/common/uri'; import { isMacintosh } from 'vs/base/common/platform'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; +import { VSBuffer } from 'vs/base/common/buffer'; export class NativeClipboardService implements IClipboardService { private static readonly FILE_FORMAT = 'code/file-list'; // Clipboard format for files - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; + + constructor( + @IElectronService private readonly electronService: IElectronService + ) { } async writeText(text: string, type?: 'selection' | 'clipboard'): Promise { - clipboard.writeText(text, type); + return this.electronService.writeClipboardText(text, type); } async readText(type?: 'selection' | 'clipboard'): Promise { - return clipboard.readText(type); + return this.electronService.readClipboardText(type); } - readTextSync(): string { - return clipboard.readText(); - } - - readFindText(): string { + async readFindText(): Promise { if (isMacintosh) { - return clipboard.readFindText(); + return this.electronService.readClipboardFindText(); } return ''; } - writeFindText(text: string): void { + async writeFindText(text: string): Promise { if (isMacintosh) { - clipboard.writeFindText(text); + return this.electronService.writeClipboardFindText(text); } } - writeResources(resources: URI[]): void { + async writeResources(resources: URI[]): Promise { if (resources.length) { - clipboard.writeBuffer(NativeClipboardService.FILE_FORMAT, this.resourcesToBuffer(resources)); + return this.electronService.writeClipboardBuffer(NativeClipboardService.FILE_FORMAT, this.resourcesToBuffer(resources)); } } - readResources(): URI[] { - return this.bufferToResources(clipboard.readBuffer(NativeClipboardService.FILE_FORMAT)); + async readResources(): Promise { + return this.bufferToResources(await this.electronService.readClipboardBuffer(NativeClipboardService.FILE_FORMAT)); } - hasResources(): boolean { - return clipboard.has(NativeClipboardService.FILE_FORMAT); + async hasResources(): Promise { + return this.electronService.hasClipboard(NativeClipboardService.FILE_FORMAT); } - private resourcesToBuffer(resources: URI[]): Buffer { - return Buffer.from(resources.map(r => r.toString()).join('\n')); + private resourcesToBuffer(resources: URI[]): Uint8Array { + return VSBuffer.fromString(resources.map(r => r.toString()).join('\n')).buffer; } - private bufferToResources(buffer: Buffer): URI[] { + private bufferToResources(buffer: Uint8Array): URI[] { if (!buffer) { return []; } diff --git a/src/vs/workbench/services/commands/common/commandService.ts b/src/vs/workbench/services/commands/common/commandService.ts index 0b8653ad474..08efa010dbc 100644 --- a/src/vs/workbench/services/commands/common/commandService.ts +++ b/src/vs/workbench/services/commands/common/commandService.ts @@ -14,7 +14,7 @@ import { timeout } from 'vs/base/common/async'; export class CommandService extends Disposable implements ICommandService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _extensionHostIsReady: boolean = false; private _starActivation: Promise | null; diff --git a/src/vs/workbench/services/configuration/browser/configuration.ts b/src/vs/workbench/services/configuration/browser/configuration.ts index d7afe8cde6a..461195a59e9 100644 --- a/src/vs/workbench/services/configuration/browser/configuration.ts +++ b/src/vs/workbench/services/configuration/browser/configuration.ts @@ -437,7 +437,7 @@ export class WorkspaceConfiguration extends Disposable { setFolders(folders: IStoredWorkspaceFolder[], jsonEditingService: JSONEditingService): Promise { if (this._workspaceIdentifier) { - return jsonEditingService.write(this._workspaceIdentifier.configPath, [{ key: 'folders', value: folders }], true) + return jsonEditingService.write(this._workspaceIdentifier.configPath, [{ path: ['folders'], value: folders }], true) .then(() => this.reload()); } return Promise.resolve(); diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 4d2315b2e2d..747f5506106 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -94,8 +94,12 @@ export class WorkspaceService extends Disposable implements IConfigurationServic }); })); - this._register(Registry.as(Extensions.Configuration).onDidSchemaChange(e => this.registerConfigurationSchemas())); - this._register(Registry.as(Extensions.Configuration).onDidUpdateConfiguration(configurationProperties => this.onDefaultConfigurationChanged(configurationProperties))); + const configurationRegistry = Registry.as(Extensions.Configuration); + if (environmentService.options?.configurationDefaults) { + configurationRegistry.registerDefaultConfigurations([environmentService.options.configurationDefaults]); + } + this._register(configurationRegistry.onDidSchemaChange(e => this.registerConfigurationSchemas())); + this._register(configurationRegistry.onDidUpdateConfiguration(configurationProperties => this.onDefaultConfigurationChanged(configurationProperties))); this.workspaceEditingQueue = new Queue(); } @@ -503,10 +507,19 @@ export class WorkspaceService extends Disposable implements IConfigurationServic this._configuration.updateRemoteUserConfiguration(this.remoteUserConfiguration.reprocess()); } if (this.getWorkbenchState() === WorkbenchState.FOLDER) { - this._configuration.updateWorkspaceConfiguration(this.cachedFolderConfigs.get(this.workspace.folders[0].uri)!.reprocess()); + const folderConfiguration = this.cachedFolderConfigs.get(this.workspace.folders[0].uri); + if (folderConfiguration) { + this._configuration.updateWorkspaceConfiguration(folderConfiguration.reprocess()); + this._configuration.updateFolderConfiguration(this.workspace.folders[0].uri, folderConfiguration.reprocess()); + } } else { this._configuration.updateWorkspaceConfiguration(this.workspaceConfiguration.reprocessWorkspaceSettings()); - this.workspace.folders.forEach(folder => this._configuration.updateFolderConfiguration(folder.uri, this.cachedFolderConfigs.get(folder.uri)!.reprocess())); + for (const folder of this.workspace.folders) { + const folderConfiguration = this.cachedFolderConfigs.get(folder.uri); + if (folderConfiguration) { + this._configuration.updateFolderConfiguration(folder.uri, folderConfiguration.reprocess()); + } + } } this.triggerConfigurationChange(change, { data: previousData, workspace: this.workspace }, ConfigurationTarget.DEFAULT); } @@ -515,12 +528,13 @@ export class WorkspaceService extends Disposable implements IConfigurationServic private registerConfigurationSchemas(): void { if (this.workspace) { const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); + const defaultSettingsSchema: IJSONSchema = { additionalProperties: true, allowTrailingCommas: true, allowComments: true }; const allSettingsSchema: IJSONSchema = { properties: allSettings.properties, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true }; const userSettingsSchema: IJSONSchema = this.remoteUserConfiguration ? { properties: { ...applicationSettings.properties, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true } : allSettingsSchema; const machineSettingsSchema: IJSONSchema = { properties: { ...machineSettings.properties, ...machineOverridableSettings.properties, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true }; const workspaceSettingsSchema: IJSONSchema = { properties: { ...machineOverridableSettings.properties, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true }; - jsonRegistry.registerSchema(defaultSettingsSchemaId, allSettingsSchema); + jsonRegistry.registerSchema(defaultSettingsSchemaId, defaultSettingsSchema); jsonRegistry.registerSchema(userSettingsSchemaId, userSettingsSchema); jsonRegistry.registerSchema(machineSettingsSchemaId, machineSettingsSchema); diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index 6a76e88e019..37ef711911b 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -525,11 +525,13 @@ export class ConfigurationEditingService { const model = reference.object.textEditorModel; if (this.hasParseErrors(model, operation)) { + reference.dispose(); return this.reject(ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION, target, operation); } // Target cannot be dirty if not writing into buffer if (checkDirty && operation.resource && this.textFileService.isDirty(operation.resource)) { + reference.dispose(); return this.reject(ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY, target, operation); } return reference; diff --git a/src/vs/workbench/services/configuration/common/configurationModels.ts b/src/vs/workbench/services/configuration/common/configurationModels.ts index 5159bd43fe8..ad60a05ab72 100644 --- a/src/vs/workbench/services/configuration/common/configurationModels.ts +++ b/src/vs/workbench/services/configuration/common/configurationModels.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { equals } from 'vs/base/common/objects'; -import { toValuesTree, IConfigurationModel, IConfigurationOverrides, IConfigurationValue, IConfigurationChange, overrideIdentifierFromKey } from 'vs/platform/configuration/common/configuration'; +import { toValuesTree, IConfigurationModel, IConfigurationOverrides, IConfigurationValue, IConfigurationChange } from 'vs/platform/configuration/common/configuration'; import { Configuration as BaseConfiguration, ConfigurationModelParser, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; import { Workspace } from 'vs/platform/workspace/common/workspace'; import { ResourceMap } from 'vs/base/common/map'; import { URI } from 'vs/base/common/uri'; import { WORKSPACE_SCOPES } from 'vs/workbench/services/configuration/common/configuration'; -import { OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry'; +import { OVERRIDE_PROPERTY_PATTERN, overrideIdentifierFromKey } from 'vs/platform/configuration/common/configurationRegistry'; export class WorkspaceConfigurationModelParser extends ConfigurationModelParser { diff --git a/src/vs/workbench/services/configuration/common/jsonEditing.ts b/src/vs/workbench/services/configuration/common/jsonEditing.ts index a5068c98c3a..b5dc176eb0a 100644 --- a/src/vs/workbench/services/configuration/common/jsonEditing.ts +++ b/src/vs/workbench/services/configuration/common/jsonEditing.ts @@ -5,6 +5,7 @@ import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { JSONPath } from 'vs/base/common/json'; export const IJSONEditingService = createDecorator('jsonEditingService'); @@ -28,13 +29,13 @@ export class JSONEditingError extends Error { } export interface IJSONValue { - key: string; + path: JSONPath; value: any; } export interface IJSONEditingService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; write(resource: URI, values: IJSONValue[], save: boolean): Promise; } diff --git a/src/vs/workbench/services/configuration/common/jsonEditingService.ts b/src/vs/workbench/services/configuration/common/jsonEditingService.ts index 289c66fb2f8..7aff6207f5b 100644 --- a/src/vs/workbench/services/configuration/common/jsonEditingService.ts +++ b/src/vs/workbench/services/configuration/common/jsonEditingService.ts @@ -41,9 +41,11 @@ export class JSONEditingService implements IJSONEditingService { private async doWriteConfiguration(resource: URI, values: IJSONValue[], save: boolean): Promise { const reference = await this.resolveAndValidate(resource, save); - await this.writeToBuffer(reference.object.textEditorModel, values, save); - - reference.dispose(); + try { + await this.writeToBuffer(reference.object.textEditorModel, values, save); + } finally { + reference.dispose(); + } } private async writeToBuffer(model: ITextModel, values: IJSONValue[], save: boolean): Promise { @@ -73,10 +75,10 @@ export class JSONEditingService implements IJSONEditingService { private getEdits(model: ITextModel, configurationValue: IJSONValue): Edit[] { const { tabSize, insertSpaces } = model.getOptions(); const eol = model.getEOL(); - const { key, value } = configurationValue; + const { path, value } = configurationValue; - // Without key, the entire settings file is being replaced, so we just use JSON.stringify - if (!key) { + // With empty path the entire file is being replaced, so we just use JSON.stringify + if (!path.length) { const content = JSON.stringify(value, null, insertSpaces ? strings.repeat(' ', tabSize) : '\t'); return [{ content, @@ -85,7 +87,7 @@ export class JSONEditingService implements IJSONEditingService { }]; } - return setProperty(model.getValue(), [key], value, { tabSize, insertSpaces, eol }); + return setProperty(model.getValue(), path, value, { tabSize, insertSpaces, eol }); } private async resolveModelReference(resource: URI): Promise> { @@ -108,11 +110,13 @@ export class JSONEditingService implements IJSONEditingService { const model = reference.object.textEditorModel; if (this.hasParseErrors(model)) { + reference.dispose(); return this.reject>(JSONEditingErrorCode.ERROR_INVALID_FILE); } // Target cannot be dirty if not writing into buffer if (checkDirty && this.textFileService.isDirty(resource)) { + reference.dispose(); return this.reject>(JSONEditingErrorCode.ERROR_FILE_DIRTY); } diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts index 5ca0a7f6a8c..5091446162d 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts @@ -49,6 +49,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { timeout } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import product from 'vs/platform/product/common/product'; class TestEnvironmentService extends NativeWorkbenchEnvironmentService { @@ -111,7 +112,7 @@ suite('WorkspaceContextService - Folder', () => { const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); fileService.registerProvider(Schemas.file, diskFileSystemProvider); fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, new DiskFileSystemProvider(new NullLogService()), environmentService, new NullLogService())); - workspaceContextService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, new RemoteAgentService(environmentService, new RemoteAuthorityResolverService(), new SignService(undefined), new NullLogService())); + workspaceContextService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, new RemoteAgentService(environmentService, { _serviceBrand: undefined, ...product }, new RemoteAuthorityResolverService(), new SignService(undefined), new NullLogService())); return (workspaceContextService).initialize(convertToWorkspacePayload(URI.file(folderDir))); }); }); @@ -881,56 +882,140 @@ suite('WorkspaceConfigurationService - Folder', () => { }); }); - test('application settings are not read from workspace', () => { + test('application settings are not read from workspace', async () => { fs.writeFileSync(globalSettingsFile, '{ "configurationService.folder.applicationSetting": "userValue" }'); fs.writeFileSync(path.join(workspaceDir, '.vscode', 'settings.json'), '{ "configurationService.folder.applicationSetting": "workspaceValue" }'); - return testObject.reloadConfiguration() - .then(() => assert.equal(testObject.getValue('configurationService.folder.applicationSetting'), 'userValue')); + + await testObject.reloadConfiguration(); + + assert.equal(testObject.getValue('configurationService.folder.applicationSetting'), 'userValue'); }); - test('machine settings are not read from workspace', () => { + test('application settings are not read from workspace when workspace folder uri is passed', async () => { + fs.writeFileSync(globalSettingsFile, '{ "configurationService.folder.applicationSetting": "userValue" }'); + fs.writeFileSync(path.join(workspaceDir, '.vscode', 'settings.json'), '{ "configurationService.folder.applicationSetting": "workspaceValue" }'); + + await testObject.reloadConfiguration(); + + assert.equal(testObject.getValue('configurationService.folder.applicationSetting', { resource: workspaceService.getWorkspace().folders[0].uri }), 'userValue'); + }); + + test('machine settings are not read from workspace', async () => { fs.writeFileSync(globalSettingsFile, '{ "configurationService.folder.machineSetting": "userValue" }'); fs.writeFileSync(path.join(workspaceDir, '.vscode', 'settings.json'), '{ "configurationService.folder.machineSetting": "workspaceValue" }'); - return testObject.reloadConfiguration() - .then(() => assert.equal(testObject.getValue('configurationService.folder.machineSetting'), 'userValue')); + + await testObject.reloadConfiguration(); + + assert.equal(testObject.getValue('configurationService.folder.machineSetting', { resource: workspaceService.getWorkspace().folders[0].uri }), 'userValue'); }); - test('get application scope settings are not loaded after defaults are registered', () => { - fs.writeFileSync(path.join(workspaceDir, '.vscode', 'settings.json'), '{ "configurationService.folder.anotherApplicationSetting": "workspaceValue" }'); - return testObject.reloadConfiguration() - .then(() => { - configurationRegistry.registerConfiguration({ - 'id': '_test', - 'type': 'object', - 'properties': { - 'configurationService.folder.anotherApplicationSetting': { - 'type': 'string', - 'default': 'isSet', - scope: ConfigurationScope.APPLICATION - } - } - }); - assert.deepEqual(testObject.keys().workspace, []); - }); + test('machine settings are not read from workspace when workspace folder uri is passed', async () => { + fs.writeFileSync(globalSettingsFile, '{ "configurationService.folder.machineSetting": "userValue" }'); + fs.writeFileSync(path.join(workspaceDir, '.vscode', 'settings.json'), '{ "configurationService.folder.machineSetting": "workspaceValue" }'); + + await testObject.reloadConfiguration(); + + assert.equal(testObject.getValue('configurationService.folder.machineSetting', { resource: workspaceService.getWorkspace().folders[0].uri }), 'userValue'); }); - test('get machine scope settings are not loaded after defaults are registered', () => { - fs.writeFileSync(path.join(workspaceDir, '.vscode', 'settings.json'), '{ "configurationService.folder.anotherMachineSetting": "workspaceValue" }'); - return testObject.reloadConfiguration() - .then(() => { - configurationRegistry.registerConfiguration({ - 'id': '_test', - 'type': 'object', - 'properties': { - 'configurationService.folder.anotherMachineSetting': { - 'type': 'string', - 'default': 'isSet', - scope: ConfigurationScope.MACHINE - } - } - }); - assert.deepEqual(testObject.keys().workspace, []); - }); + test('get application scope settings are not loaded after defaults are registered', async () => { + fs.writeFileSync(globalSettingsFile, '{ "configurationService.folder.applicationSetting-2": "userValue" }'); + fs.writeFileSync(path.join(workspaceDir, '.vscode', 'settings.json'), '{ "configurationService.folder.applicationSetting-2": "workspaceValue" }'); + + await testObject.reloadConfiguration(); + assert.equal(testObject.getValue('configurationService.folder.applicationSetting-2'), 'workspaceValue'); + + configurationRegistry.registerConfiguration({ + 'id': '_test', + 'type': 'object', + 'properties': { + 'configurationService.folder.applicationSetting-2': { + 'type': 'string', + 'default': 'isSet', + scope: ConfigurationScope.APPLICATION + } + } + }); + + assert.equal(testObject.getValue('configurationService.folder.applicationSetting-2'), 'userValue'); + + await testObject.reloadConfiguration(); + assert.equal(testObject.getValue('configurationService.folder.applicationSetting-2'), 'userValue'); + }); + + test('get application scope settings are not loaded after defaults are registered when workspace folder uri is passed', async () => { + fs.writeFileSync(globalSettingsFile, '{ "configurationService.folder.applicationSetting-3": "userValue" }'); + fs.writeFileSync(path.join(workspaceDir, '.vscode', 'settings.json'), '{ "configurationService.folder.applicationSetting-3": "workspaceValue" }'); + + await testObject.reloadConfiguration(); + assert.equal(testObject.getValue('configurationService.folder.applicationSetting-3', { resource: workspaceService.getWorkspace().folders[0].uri }), 'workspaceValue'); + + configurationRegistry.registerConfiguration({ + 'id': '_test', + 'type': 'object', + 'properties': { + 'configurationService.folder.applicationSetting-3': { + 'type': 'string', + 'default': 'isSet', + scope: ConfigurationScope.APPLICATION + } + } + }); + + assert.equal(testObject.getValue('configurationService.folder.applicationSetting-3', { resource: workspaceService.getWorkspace().folders[0].uri }), 'userValue'); + + await testObject.reloadConfiguration(); + assert.equal(testObject.getValue('configurationService.folder.applicationSetting-3', { resource: workspaceService.getWorkspace().folders[0].uri }), 'userValue'); + }); + + test('get machine scope settings are not loaded after defaults are registered', async () => { + fs.writeFileSync(globalSettingsFile, '{ "configurationService.folder.machineSetting-2": "userValue" }'); + fs.writeFileSync(path.join(workspaceDir, '.vscode', 'settings.json'), '{ "configurationService.folder.machineSetting-2": "workspaceValue" }'); + + await testObject.reloadConfiguration(); + assert.equal(testObject.getValue('configurationService.folder.machineSetting-2'), 'workspaceValue'); + + configurationRegistry.registerConfiguration({ + 'id': '_test', + 'type': 'object', + 'properties': { + 'configurationService.folder.machineSetting-2': { + 'type': 'string', + 'default': 'isSet', + scope: ConfigurationScope.MACHINE + } + } + }); + + assert.equal(testObject.getValue('configurationService.folder.machineSetting-2'), 'userValue'); + + await testObject.reloadConfiguration(); + assert.equal(testObject.getValue('configurationService.folder.machineSetting-2'), 'userValue'); + }); + + test('get machine scope settings are not loaded after defaults are registered when workspace folder uri is passed', async () => { + fs.writeFileSync(globalSettingsFile, '{ "configurationService.folder.machineSetting-3": "userValue" }'); + fs.writeFileSync(path.join(workspaceDir, '.vscode', 'settings.json'), '{ "configurationService.folder.machineSetting-3": "workspaceValue" }'); + + await testObject.reloadConfiguration(); + assert.equal(testObject.getValue('configurationService.folder.machineSetting-3', { resource: workspaceService.getWorkspace().folders[0].uri }), 'workspaceValue'); + + configurationRegistry.registerConfiguration({ + 'id': '_test', + 'type': 'object', + 'properties': { + 'configurationService.folder.machineSetting-3': { + 'type': 'string', + 'default': 'isSet', + scope: ConfigurationScope.MACHINE + } + } + }); + + assert.equal(testObject.getValue('configurationService.folder.machineSetting-3', { resource: workspaceService.getWorkspace().folders[0].uri }), 'userValue'); + + await testObject.reloadConfiguration(); + assert.equal(testObject.getValue('configurationService.folder.machineSetting-3', { resource: workspaceService.getWorkspace().folders[0].uri }), 'userValue'); }); test('reload configuration emits events after global configuraiton changes', () => { @@ -1230,116 +1315,207 @@ suite('WorkspaceConfigurationService-Multiroot', () => { return undefined; }); - test('application settings are not read from workspace', () => { - fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.applicationSetting": "userValue" }'); - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'settings', value: { 'configurationService.workspace.applicationSetting': 'workspaceValue' } }], true) - .then(() => testObject.reloadConfiguration()) - .then(() => assert.equal(testObject.getValue('configurationService.workspace.applicationSetting'), 'userValue')); + test('application settings are not read from workspace', async () => { + fs.writeFileSync(globalSettingsFile, '{ "configurationService.folder.applicationSetting": "userValue" }'); + await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.applicationSetting': 'workspaceValue' } }], true); + + await testObject.reloadConfiguration(); + + assert.equal(testObject.getValue('configurationService.folder.applicationSetting'), 'userValue'); }); - test('machine settings are not read from workspace', () => { - fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.machineSetting": "userValue" }'); - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'settings', value: { 'configurationService.workspace.machineSetting': 'workspaceValue' } }], true) - .then(() => testObject.reloadConfiguration()) - .then(() => assert.equal(testObject.getValue('configurationService.workspace.machineSetting'), 'userValue')); + test('application settings are not read from workspace when folder is passed', async () => { + fs.writeFileSync(globalSettingsFile, '{ "configurationService.folder.applicationSetting": "userValue" }'); + await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.applicationSetting': 'workspaceValue' } }], true); + + await testObject.reloadConfiguration(); + + assert.equal(testObject.getValue('configurationService.folder.applicationSetting', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'userValue'); }); - test('workspace settings override user settings after defaults are registered ', () => { + test('machine settings are not read from workspace', async () => { + fs.writeFileSync(globalSettingsFile, '{ "configurationService.folder.machineSetting": "userValue" }'); + await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.machineSetting': 'workspaceValue' } }], true); + + await testObject.reloadConfiguration(); + + assert.equal(testObject.getValue('configurationService.folder.machineSetting'), 'userValue'); + }); + + test('machine settings are not read from workspace when folder is passed', async () => { + fs.writeFileSync(globalSettingsFile, '{ "configurationService.folder.machineSetting": "userValue" }'); + await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.machineSetting': 'workspaceValue' } }], true); + + await testObject.reloadConfiguration(); + + assert.equal(testObject.getValue('configurationService.folder.machineSetting', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'userValue'); + }); + + test('get application scope settings are not loaded after defaults are registered', async () => { fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.newSetting": "userValue" }'); - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'settings', value: { 'configurationService.workspace.newSetting': 'workspaceValue' } }], true) - .then(() => testObject.reloadConfiguration()) - .then(() => { - configurationRegistry.registerConfiguration({ - 'id': '_test', - 'type': 'object', - 'properties': { - 'configurationService.workspace.newSetting': { - 'type': 'string', - 'default': 'isSet' - } - } - }); - assert.equal(testObject.getValue('configurationService.workspace.newSetting'), 'workspaceValue'); - }); + await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.newSetting': 'workspaceValue' } }], true); + + await testObject.reloadConfiguration(); + assert.equal(testObject.getValue('configurationService.workspace.newSetting'), 'workspaceValue'); + + configurationRegistry.registerConfiguration({ + 'id': '_test', + 'type': 'object', + 'properties': { + 'configurationService.workspace.newSetting': { + 'type': 'string', + 'default': 'isSet', + scope: ConfigurationScope.APPLICATION + } + } + }); + + assert.equal(testObject.getValue('configurationService.workspace.newSetting'), 'userValue'); + + await testObject.reloadConfiguration(); + assert.equal(testObject.getValue('configurationService.workspace.newSetting'), 'userValue'); }); - test('workspace settings override user settings after defaults are registered for machine overridable settings ', () => { + test('get application scope settings are not loaded after defaults are registered when workspace folder is passed', async () => { + fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.newSetting-2": "userValue" }'); + await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.newSetting-2': 'workspaceValue' } }], true); + + await testObject.reloadConfiguration(); + assert.equal(testObject.getValue('configurationService.workspace.newSetting-2', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'workspaceValue'); + + configurationRegistry.registerConfiguration({ + 'id': '_test', + 'type': 'object', + 'properties': { + 'configurationService.workspace.newSetting-2': { + 'type': 'string', + 'default': 'isSet', + scope: ConfigurationScope.APPLICATION + } + } + }); + + assert.equal(testObject.getValue('configurationService.workspace.newSetting-2', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'userValue'); + + await testObject.reloadConfiguration(); + assert.equal(testObject.getValue('configurationService.workspace.newSetting-2', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'userValue'); + }); + + test('workspace settings override user settings after defaults are registered for machine overridable settings ', async () => { fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.newMachineOverridableSetting": "userValue" }'); - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'settings', value: { 'configurationService.workspace.newMachineOverridableSetting': 'workspaceValue' } }], true) - .then(() => testObject.reloadConfiguration()) - .then(() => { - configurationRegistry.registerConfiguration({ - 'id': '_test', - 'type': 'object', - 'properties': { - 'configurationService.workspace.newMachineOverridableSetting': { - 'type': 'string', - 'default': 'isSet', - scope: ConfigurationScope.MACHINE_OVERRIDABLE - } - } - }); - assert.equal(testObject.getValue('configurationService.workspace.newMachineOverridableSetting'), 'workspaceValue'); - }); + await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.newMachineOverridableSetting': 'workspaceValue' } }], true); + + await testObject.reloadConfiguration(); + assert.equal(testObject.getValue('configurationService.workspace.newMachineOverridableSetting'), 'workspaceValue'); + + configurationRegistry.registerConfiguration({ + 'id': '_test', + 'type': 'object', + 'properties': { + 'configurationService.workspace.newMachineOverridableSetting': { + 'type': 'string', + 'default': 'isSet', + scope: ConfigurationScope.MACHINE_OVERRIDABLE + } + } + }); + + assert.equal(testObject.getValue('configurationService.workspace.newMachineOverridableSetting'), 'workspaceValue'); + + await testObject.reloadConfiguration(); + assert.equal(testObject.getValue('configurationService.workspace.newMachineOverridableSetting'), 'workspaceValue'); + }); - test('application settings are not read from workspace folder', () => { + test('application settings are not read from workspace folder', async () => { fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.applicationSetting": "userValue" }'); fs.writeFileSync(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json').fsPath, '{ "configurationService.workspace.applicationSetting": "workspaceFolderValue" }'); - return testObject.reloadConfiguration() - .then(() => assert.equal(testObject.getValue('configurationService.workspace.applicationSetting'), 'userValue')); + + await testObject.reloadConfiguration(); + + assert.equal(testObject.getValue('configurationService.workspace.applicationSetting'), 'userValue'); }); - test('machine settings are not read from workspace folder', () => { + test('application settings are not read from workspace folder when workspace folder is passed', async () => { + fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.applicationSetting": "userValue" }'); + fs.writeFileSync(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json').fsPath, '{ "configurationService.workspace.applicationSetting": "workspaceFolderValue" }'); + + await testObject.reloadConfiguration(); + + assert.equal(testObject.getValue('configurationService.workspace.applicationSetting', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'userValue'); + }); + + test('machine settings are not read from workspace folder', async () => { fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.machineSetting": "userValue" }'); fs.writeFileSync(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json').fsPath, '{ "configurationService.workspace.machineSetting": "workspaceFolderValue" }'); - return testObject.reloadConfiguration() - .then(() => assert.equal(testObject.getValue('configurationService.workspace.machineSetting'), 'userValue')); + + await testObject.reloadConfiguration(); + + assert.equal(testObject.getValue('configurationService.workspace.machineSetting'), 'userValue'); }); - test('application settings are not read from workspace folder after defaults are registered', () => { + test('machine settings are not read from workspace folder when workspace folder is passed', async () => { + fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.machineSetting": "userValue" }'); + fs.writeFileSync(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json').fsPath, '{ "configurationService.workspace.machineSetting": "workspaceFolderValue" }'); + + await testObject.reloadConfiguration(); + + assert.equal(testObject.getValue('configurationService.workspace.machineSetting', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'userValue'); + }); + + test('application settings are not read from workspace folder after defaults are registered', async () => { fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.testNewApplicationSetting": "userValue" }'); fs.writeFileSync(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json').fsPath, '{ "configurationService.workspace.testNewApplicationSetting": "workspaceFolderValue" }'); - return testObject.reloadConfiguration() - .then(() => { - configurationRegistry.registerConfiguration({ - 'id': '_test', - 'type': 'object', - 'properties': { - 'configurationService.workspace.testNewApplicationSetting': { - 'type': 'string', - 'default': 'isSet', - scope: ConfigurationScope.APPLICATION - } - } - }); - assert.equal(testObject.getValue('configurationService.workspace.testNewApplicationSetting', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'userValue'); - }); + + await testObject.reloadConfiguration(); + assert.equal(testObject.getValue('configurationService.workspace.testNewApplicationSetting', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'workspaceFolderValue'); + + configurationRegistry.registerConfiguration({ + 'id': '_test', + 'type': 'object', + 'properties': { + 'configurationService.workspace.testNewApplicationSetting': { + 'type': 'string', + 'default': 'isSet', + scope: ConfigurationScope.APPLICATION + } + } + }); + + assert.equal(testObject.getValue('configurationService.workspace.testNewApplicationSetting', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'userValue'); + + await testObject.reloadConfiguration(); + assert.equal(testObject.getValue('configurationService.workspace.testNewApplicationSetting', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'userValue'); }); - test('application settings are not read from workspace folder after defaults are registered', () => { + test('application settings are not read from workspace folder after defaults are registered', async () => { fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.testNewMachineSetting": "userValue" }'); fs.writeFileSync(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json').fsPath, '{ "configurationService.workspace.testNewMachineSetting": "workspaceFolderValue" }'); - return testObject.reloadConfiguration() - .then(() => { - configurationRegistry.registerConfiguration({ - 'id': '_test', - 'type': 'object', - 'properties': { - 'configurationService.workspace.testNewMachineSetting': { - 'type': 'string', - 'default': 'isSet', - scope: ConfigurationScope.MACHINE - } - } - }); - assert.equal(testObject.getValue('configurationService.workspace.testNewMachineSetting', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'userValue'); - }); + await testObject.reloadConfiguration(); + + assert.equal(testObject.getValue('configurationService.workspace.testNewMachineSetting', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'workspaceFolderValue'); + + configurationRegistry.registerConfiguration({ + 'id': '_test', + 'type': 'object', + 'properties': { + 'configurationService.workspace.testNewMachineSetting': { + 'type': 'string', + 'default': 'isSet', + scope: ConfigurationScope.MACHINE + } + } + }); + + assert.equal(testObject.getValue('configurationService.workspace.testNewMachineSetting', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'userValue'); + + await testObject.reloadConfiguration(); + assert.equal(testObject.getValue('configurationService.workspace.testNewMachineSetting', { resource: workspaceContextService.getWorkspace().folders[0].uri }), 'userValue'); }); test('resource setting in folder is read after it is registered later', () => { fs.writeFileSync(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json').fsPath, '{ "configurationService.workspace.testNewResourceSetting2": "workspaceFolderValue" }'); - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'settings', value: { 'configurationService.workspace.testNewResourceSetting2': 'workspaceValue' } }], true) + return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.testNewResourceSetting2': 'workspaceValue' } }], true) .then(() => testObject.reloadConfiguration()) .then(() => { configurationRegistry.registerConfiguration({ @@ -1359,7 +1535,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { test('resource language setting in folder is read after it is registered later', () => { fs.writeFileSync(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json').fsPath, '{ "configurationService.workspace.testNewResourceLanguageSetting2": "workspaceFolderValue" }'); - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'settings', value: { 'configurationService.workspace.testNewResourceLanguageSetting2': 'workspaceValue' } }], true) + return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.testNewResourceLanguageSetting2': 'workspaceValue' } }], true) .then(() => testObject.reloadConfiguration()) .then(() => { configurationRegistry.registerConfiguration({ @@ -1379,7 +1555,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { test('machine overridable setting in folder is read after it is registered later', () => { fs.writeFileSync(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json').fsPath, '{ "configurationService.workspace.testNewMachineOverridableSetting2": "workspaceFolderValue" }'); - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'settings', value: { 'configurationService.workspace.testNewMachineOverridableSetting2': 'workspaceValue' } }], true) + return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.testNewMachineOverridableSetting2': 'workspaceValue' } }], true) .then(() => testObject.reloadConfiguration()) .then(() => { configurationRegistry.registerConfiguration({ @@ -1422,7 +1598,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { assert.equal(actual.workspaceFolderValue, undefined); assert.equal(actual.value, 'userValue'); - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'settings', value: { 'configurationService.workspace.testResourceSetting': 'workspaceValue' } }], true) + return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.testResourceSetting': 'workspaceValue' } }], true) .then(() => testObject.reloadConfiguration()) .then(() => { actual = testObject.inspect('configurationService.workspace.testResourceSetting'); @@ -1464,7 +1640,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { } ] }; - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'launch', value: expectedLaunchConfiguration }], true) + return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['launch'], value: expectedLaunchConfiguration }], true) .then(() => testObject.reloadConfiguration()) .then(() => { const actual = testObject.getValue('launch'); @@ -1489,7 +1665,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { } ] }; - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'launch', value: expectedLaunchConfiguration }], true) + return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['launch'], value: expectedLaunchConfiguration }], true) .then(() => testObject.reloadConfiguration()) .then(() => { const actual = testObject.inspect('launch').workspaceValue; @@ -1513,7 +1689,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { } ] }; - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'tasks', value: expectedTasksConfiguration }], true) + return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['tasks'], value: expectedTasksConfiguration }], true) .then(() => testObject.reloadConfiguration()) .then(() => { const actual = testObject.getValue('tasks'); @@ -1536,7 +1712,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { } ] }; - await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'tasks', value: expectedTasksConfiguration }], true); + await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['tasks'], value: expectedTasksConfiguration }], true); await testObject.reloadConfiguration(); const actual = testObject.inspect('tasks').workspaceValue; assert.deepEqual(actual, expectedTasksConfiguration); @@ -1660,7 +1836,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { suite('WorkspaceConfigurationService - Remote Folder', () => { - let workspaceName = `testWorkspace${uuid.generateUuid()}`, parentResource: string, workspaceDir: string, testObject: WorkspaceService, globalSettingsFile: string, remoteSettingsFile: string, instantiationService: TestInstantiationService, resolveRemoteEnvironment: () => void; + let workspaceName = `testWorkspace${uuid.generateUuid()}`, parentResource: string, workspaceDir: string, testObject: WorkspaceService, globalSettingsFile: string, remoteSettingsFile: string, remoteSettingsResource: URI, instantiationService: TestInstantiationService, resolveRemoteEnvironment: () => void; const remoteAuthority = 'configuraiton-tests'; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); @@ -1702,10 +1878,11 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { workspaceDir = folderDir; globalSettingsFile = path.join(parentDir, 'settings.json'); remoteSettingsFile = path.join(parentDir, 'remote-settings.json'); + remoteSettingsResource = URI.file(remoteSettingsFile).with({ scheme: Schemas.vscodeRemote, authority: remoteAuthority }); instantiationService = workbenchInstantiationService(); const environmentService = new TestEnvironmentService(URI.file(parentDir)); - const remoteEnvironmentPromise = new Promise>(c => resolveRemoteEnvironment = () => c({ settingsPath: URI.file(remoteSettingsFile).with({ scheme: Schemas.vscodeRemote, authority: remoteAuthority }) })); + const remoteEnvironmentPromise = new Promise>(c => resolveRemoteEnvironment = () => c({ settingsPath: remoteSettingsResource })); const remoteAgentService = instantiationService.stub(IRemoteAgentService, >{ getEnvironment: () => remoteEnvironmentPromise }); const fileService = new FileService(new NullLogService()); fileService.registerProvider(Schemas.file, diskFileSystemProvider); @@ -1805,26 +1982,26 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { return promise; }); - // test('update remote settings', async () => { - // registerRemoteFileSystemProvider(); - // resolveRemoteEnvironment(); - // await initialize(); - // assert.equal(testObject.getValue('configurationService.remote.machineSetting'), 'isSet'); - // const promise = new Promise((c, e) => { - // testObject.onDidChangeConfiguration(event => { - // try { - // assert.equal(event.source, ConfigurationTarget.USER); - // assert.deepEqual(event.affectedKeys, ['configurationService.remote.machineSetting']); - // assert.equal(testObject.getValue('configurationService.remote.machineSetting'), 'remoteValue'); - // c(); - // } catch (error) { - // e(error); - // } - // }); - // }); - // fs.writeFileSync(remoteSettingsFile, '{ "configurationService.remote.machineSetting": "remoteValue" }'); - // return promise; - // }); + test.skip('update remote settings', async () => { + registerRemoteFileSystemProvider(); + resolveRemoteEnvironment(); + await initialize(); + assert.equal(testObject.getValue('configurationService.remote.machineSetting'), 'isSet'); + const promise = new Promise((c, e) => { + testObject.onDidChangeConfiguration(event => { + try { + assert.equal(event.source, ConfigurationTarget.USER); + assert.deepEqual(event.affectedKeys, ['configurationService.remote.machineSetting']); + assert.equal(testObject.getValue('configurationService.remote.machineSetting'), 'remoteValue'); + c(); + } catch (error) { + e(error); + } + }); + }); + await instantiationService.get(IFileService).writeFile(remoteSettingsResource, VSBuffer.fromString('{ "configurationService.remote.machineSetting": "remoteValue" }')); + return promise; + }); test('machine settings in local user settings does not override defaults', async () => { fs.writeFileSync(globalSettingsFile, '{ "configurationService.remote.machineSetting": "globalValue" }'); diff --git a/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts b/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts index 33a6c5d05da..0bd4c9e6f2b 100644 --- a/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts @@ -11,7 +11,7 @@ import { ConfigurationTarget } from 'vs/platform/configuration/common/configurat export const IConfigurationResolverService = createDecorator('configurationResolverService'); export interface IConfigurationResolverService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; resolve(folder: IWorkspaceFolder | undefined, value: string): string; resolve(folder: IWorkspaceFolder | undefined, value: string[]): string[]; diff --git a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts index c29a083e759..2ad97f9bc33 100644 --- a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts @@ -28,16 +28,15 @@ export interface IVariableResolveContext { export class AbstractVariableResolverService implements IConfigurationResolverService { static readonly VARIABLE_REGEXP = /\$\{(.*?)\}/g; - static readonly VARIABLE_REGEXP_SINGLE = /\$\{(.*?)\}/; - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _context: IVariableResolveContext; private _envVariables?: IProcessEnvironment; protected _contributedVariables: Map Promise> = new Map(); - constructor(_context: IVariableResolveContext, _envVariables?: IProcessEnvironment) { + constructor(_context: IVariableResolveContext, _envVariables?: IProcessEnvironment, private _ignoreEditorVariables = false) { this._context = _context; if (_envVariables) { if (isWindows) { @@ -232,6 +231,9 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe return paths.basename(getFolderUri().fsPath); case 'lineNumber': + if (this._ignoreEditorVariables) { + return match; + } const lineNumber = this._context.getLineNumber(); if (lineNumber) { return lineNumber; @@ -239,6 +241,9 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe throw new Error(localize('canNotResolveLineNumber', "'{0}' can not be resolved. Make sure to have a line selected in the active editor.", match)); case 'selectedText': + if (this._ignoreEditorVariables) { + return match; + } const selectedText = this._context.getSelectedText(); if (selectedText) { return selectedText; @@ -246,15 +251,24 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe throw new Error(localize('canNotResolveSelectedText', "'{0}' can not be resolved. Make sure to have some text selected in the active editor.", match)); case 'file': + if (this._ignoreEditorVariables) { + return match; + } return getFilePath(); case 'relativeFile': + if (this._ignoreEditorVariables) { + return match; + } if (folderUri || argument) { return paths.normalize(paths.relative(getFolderUri().fsPath, getFilePath())); } return getFilePath(); case 'relativeFileDirname': + if (this._ignoreEditorVariables) { + return match; + } const dirname = paths.dirname(getFilePath()); if (folderUri || argument) { return paths.normalize(paths.relative(getFolderUri().fsPath, dirname)); @@ -262,15 +276,27 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe return dirname; case 'fileDirname': + if (this._ignoreEditorVariables) { + return match; + } return paths.dirname(getFilePath()); case 'fileExtname': + if (this._ignoreEditorVariables) { + return match; + } return paths.extname(getFilePath()); case 'fileBasename': + if (this._ignoreEditorVariables) { + return match; + } return paths.basename(getFilePath()); case 'fileBasenameNoExtension': + if (this._ignoreEditorVariables) { + return match; + } const basename = paths.basename(getFilePath()); return (basename.slice(0, basename.length - paths.extname(basename).length)); diff --git a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts index 9d1f709f55c..eeea87956ad 100644 --- a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts +++ b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts @@ -591,7 +591,7 @@ class MockCommandService implements ICommandService { } class MockQuickInputService implements IQuickInputService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; readonly onShow = Event.None; readonly onHide = Event.None; diff --git a/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts similarity index 83% rename from src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts rename to src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts index 70a68b7f35f..ee78a258ee3 100644 --- a/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts +++ b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts @@ -3,21 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IAction, IActionRunner, ActionRunner, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; -import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IAction, IActionRunner, ActionRunner, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification, Separator, SubmenuAction } from 'vs/base/common/actions'; import * as dom from 'vs/base/browser/dom'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { webFrame } from 'electron'; +import { getZoomFactor } from 'vs/base/browser/browser'; import { unmnemonicLabel } from 'vs/base/common/labels'; import { Event, Emitter } from 'vs/base/common/event'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IContextMenuDelegate, ContextSubMenu, IContextMenuEvent } from 'vs/base/browser/contextmenu'; +import { IContextMenuDelegate, IContextMenuEvent } from 'vs/base/browser/contextmenu'; import { once } from 'vs/base/common/functional'; import { Disposable } from 'vs/base/common/lifecycle'; import { IContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu'; -import { popup } from 'vs/base/parts/contextmenu/electron-browser/contextmenu'; +import { popup } from 'vs/base/parts/contextmenu/electron-sandbox/contextmenu'; import { getTitleBarStyle } from 'vs/platform/windows/common/windows'; import { isMacintosh } from 'vs/base/common/platform'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -26,10 +25,11 @@ import { ContextMenuService as HTMLContextMenuService } from 'vs/platform/contex import { IThemeService } from 'vs/platform/theme/common/themeService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { stripCodicons } from 'vs/base/common/codicons'; +import { coalesce } from 'vs/base/common/arrays'; export class ContextMenuService extends Disposable implements IContextMenuService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; get onDidContextMenu(): Event { return this.impl.onDidContextMenu; } @@ -64,7 +64,7 @@ export class ContextMenuService extends Disposable implements IContextMenuServic class NativeContextMenuService extends Disposable implements IContextMenuService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _onDidContextMenu = this._register(new Emitter()); readonly onDidContextMenu: Event = this._onDidContextMenu.event; @@ -90,11 +90,13 @@ class NativeContextMenuService extends Disposable implements IContextMenuService const menu = this.createMenu(delegate, actions, onHide); const anchor = delegate.getAnchor(); - let x: number, y: number; - let zoom = webFrame.getZoomFactor(); + let x: number; + let y: number; + + const zoom = getZoomFactor(); if (dom.isHTMLElement(anchor)) { - let elementPosition = dom.getDomNodePagePosition(anchor); + const elementPosition = dom.getDomNodePagePosition(anchor); x = elementPosition.left; y = elementPosition.top + elementPosition.height; @@ -118,29 +120,32 @@ class NativeContextMenuService extends Disposable implements IContextMenuService x: Math.floor(x), y: Math.floor(y), positioningItem: delegate.autoSelectFirstItem ? 0 : undefined, - onHide: () => onHide() - }); + }, () => onHide()); } } - private createMenu(delegate: IContextMenuDelegate, entries: ReadonlyArray, onHide: () => void): IContextMenuItem[] { + private createMenu(delegate: IContextMenuDelegate, entries: IAction[], onHide: () => void, submenuIds = new Set()): IContextMenuItem[] { const actionRunner = delegate.actionRunner || new ActionRunner(); - - return entries.map(entry => this.createMenuItem(delegate, entry, actionRunner, onHide)); + return coalesce(entries.map(entry => this.createMenuItem(delegate, entry, actionRunner, onHide, submenuIds))); } - private createMenuItem(delegate: IContextMenuDelegate, entry: IAction | ContextSubMenu, actionRunner: IActionRunner, onHide: () => void): IContextMenuItem { - + private createMenuItem(delegate: IContextMenuDelegate, entry: IAction, actionRunner: IActionRunner, onHide: () => void, submenuIds: Set): IContextMenuItem | undefined { // Separator if (entry instanceof Separator) { return { type: 'separator' }; } // Submenu - if (entry instanceof ContextSubMenu) { + if (entry instanceof SubmenuAction) { + if (submenuIds.has(entry.id)) { + console.warn(`Found submenu cycle: ${entry.id}`); + return undefined; + } + + const actions = Array.isArray(entry.actions) ? entry.actions : entry.actions(); return { label: unmnemonicLabel(stripCodicons(entry.label)).trim(), - submenu: this.createMenu(delegate, entry.entries, onHide) + submenu: this.createMenu(delegate, actions, onHide, new Set([...submenuIds, entry.id])) }; } diff --git a/src/vs/workbench/services/credentials/browser/credentialsService.ts b/src/vs/workbench/services/credentials/browser/credentialsService.ts index d50c058d3aa..5d64ecf1d2f 100644 --- a/src/vs/workbench/services/credentials/browser/credentialsService.ts +++ b/src/vs/workbench/services/credentials/browser/credentialsService.ts @@ -20,7 +20,7 @@ export interface ICredentialsProvider { export class BrowserCredentialsService implements ICredentialsService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private credentialsProvider: ICredentialsProvider; diff --git a/src/vs/workbench/services/credentials/common/credentials.ts b/src/vs/workbench/services/credentials/common/credentials.ts index 6fcb7dffaa0..2799abeed19 100644 --- a/src/vs/workbench/services/credentials/common/credentials.ts +++ b/src/vs/workbench/services/credentials/common/credentials.ts @@ -9,7 +9,7 @@ export const ICredentialsService = createDecorator('ICreden export interface ICredentialsService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; getPassword(service: string, account: string): Promise; setPassword(service: string, account: string, password: string): Promise; diff --git a/src/vs/workbench/services/credentials/node/credentialsService.ts b/src/vs/workbench/services/credentials/node/credentialsService.ts deleted file mode 100644 index 6ff56147902..00000000000 --- a/src/vs/workbench/services/credentials/node/credentialsService.ts +++ /dev/null @@ -1,43 +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 { ICredentialsService } from 'vs/platform/credentials/common/credentials'; -import { IdleValue } from 'vs/base/common/async'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; - -type KeytarModule = typeof import('keytar'); -export class KeytarCredentialsService implements ICredentialsService { - - _serviceBrand: undefined; - - private readonly _keytar = new IdleValue>(() => import('keytar')); - - async getPassword(service: string, account: string): Promise { - const keytar = await this._keytar.value; - return keytar.getPassword(service, account); - } - - async setPassword(service: string, account: string, password: string): Promise { - const keytar = await this._keytar.value; - return keytar.setPassword(service, account, password); - } - - async deletePassword(service: string, account: string): Promise { - const keytar = await this._keytar.value; - return keytar.deletePassword(service, account); - } - - async findPassword(service: string): Promise { - const keytar = await this._keytar.value; - return keytar.findPassword(service); - } - - async findCredentials(service: string): Promise> { - const keytar = await this._keytar.value; - return keytar.findCredentials(service); - } -} - -registerSingleton(ICredentialsService, KeytarCredentialsService, true); diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts index 325f4fd609f..7ab5ba02ff5 100644 --- a/src/vs/workbench/services/decorations/browser/decorationsService.ts +++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts @@ -208,7 +208,8 @@ class DecorationProviderWrapper { constructor( readonly provider: IDecorationsProvider, private readonly _uriEmitter: Emitter, - private readonly _flushEmitter: Emitter + private readonly _flushEmitter: Emitter, + @ILogService private readonly _logService: ILogService, ) { this._dispoable = this.provider.onDidChange(uris => { if (!uris) { @@ -238,16 +239,17 @@ class DecorationProviderWrapper { } getOrRetrieve(uri: URI, includeChildren: boolean, callback: (data: IDecorationData, isChild: boolean) => void): void { - let item = this.data.get(uri); if (item === undefined) { // unknown -> trigger request + this._logService.trace('[Decorations] getOrRetrieve -> FETCH', this.provider.label, uri); item = this._fetchData(uri); } if (item && !(item instanceof DecorationDataRequest)) { // found something (which isn't pending anymore) + this._logService.trace('[Decorations] getOrRetrieve -> RESULT', this.provider.label, uri); callback(item, false); } @@ -257,6 +259,7 @@ class DecorationProviderWrapper { if (iter) { for (let item = iter.next(); !item.done; item = iter.next()) { if (item.value && !(item.value instanceof DecorationDataRequest)) { + this._logService.trace('[Decorations] getOrRetrieve -> RESULT (children)', this.provider.label, uri); callback(item.value, true); } } @@ -269,6 +272,7 @@ class DecorationProviderWrapper { // check for pending request and cancel it const pendingRequest = this.data.get(uri); if (pendingRequest instanceof DecorationDataRequest) { + this._logService.trace('[Decorations] fetchData -> CANCEL previous', this.provider.label, uri); pendingRequest.source.cancel(); this.data.delete(uri); } @@ -297,6 +301,7 @@ class DecorationProviderWrapper { } private _keepItem(uri: URI, data: IDecorationData | undefined): IDecorationData | null { + this._logService.trace('[Decorations] keepItem -> CANCEL previous', this.provider.label, uri, data); const deco = data ? data : null; const old = this.data.set(uri, deco); if (deco || old) { @@ -309,7 +314,7 @@ class DecorationProviderWrapper { export class DecorationsService implements IDecorationsService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _data = new LinkedList(); private readonly _onDidChangeDecorationsDelayed = new Emitter(); @@ -343,7 +348,8 @@ export class DecorationsService implements IDecorationsService { const wrapper = new DecorationProviderWrapper( provider, this._onDidChangeDecorationsDelayed, - this._onDidChangeDecorations + this._onDidChangeDecorations, + this._logService ); const remove = this._data.push(wrapper); diff --git a/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts b/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts index a9ffd0323a4..64f90d318ad 100644 --- a/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts @@ -28,7 +28,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; export abstract class AbstractFileDialogService implements IFileDialogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @IHostService protected readonly hostService: IHostService, @@ -218,27 +218,23 @@ export abstract class AbstractFileDialogService implements IFileDialogService { } private pickResource(options: IOpenDialogOptions): Promise { - const simpleFileDialog = this.createSimpleFileDialog(); + const simpleFileDialog = this.instantiationService.createInstance(SimpleFileDialog); return simpleFileDialog.showOpenDialog(options); } private saveRemoteResource(options: ISaveDialogOptions): Promise { - const remoteFileDialog = this.createSimpleFileDialog(); + const remoteFileDialog = this.instantiationService.createInstance(SimpleFileDialog); return remoteFileDialog.showSaveDialog(options); } - protected createSimpleFileDialog(): SimpleFileDialog { - return this.instantiationService.createInstance(SimpleFileDialog); - } - - protected getSchemeFilterForWindow(): string { - return !this.environmentService.configuration.remoteAuthority ? Schemas.file : REMOTE_HOST_SCHEME; + protected getSchemeFilterForWindow(defaultUriScheme?: string): string { + return !this.environmentService.configuration.remoteAuthority ? (!defaultUriScheme || defaultUriScheme === Schemas.file ? Schemas.file : defaultUriScheme) : REMOTE_HOST_SCHEME; } protected getFileSystemSchema(options: { availableFileSystems?: readonly string[], defaultUri?: URI }): string { - return options.availableFileSystems && options.availableFileSystems[0] || this.getSchemeFilterForWindow(); + return options.availableFileSystems && options.availableFileSystems[0] || this.getSchemeFilterForWindow(options.defaultUri?.scheme); } abstract pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise; @@ -254,7 +250,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService { const options: ISaveDialogOptions = { defaultUri, title: nls.localize('saveAsTitle', "Save As"), - availableFileSystems, + availableFileSystems }; interface IFilter { name: string; extensions: string[]; } @@ -262,7 +258,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService { // Build the file filter by using our known languages const ext: string | undefined = defaultUri ? resources.extname(defaultUri) : undefined; let matchingFilter: IFilter | undefined; - const filters: IFilter[] = coalesce(this.modeService.getRegisteredLanguageNames().map(languageName => { + const registeredLanguageFilters: IFilter[] = coalesce(this.modeService.getRegisteredLanguageNames().map(languageName => { const extensions = this.modeService.getExtensions(languageName); if (!extensions || !extensions.length) { return null; @@ -279,21 +275,25 @@ export abstract class AbstractFileDialogService implements IFileDialogService { return filter; })); - // Filters are a bit weird on Windows, based on having a match or not: - // Match: we put the matching filter first so that it shows up selected and the all files last - // No match: we put the all files filter first - const allFilesFilter = { name: nls.localize('allFiles', "All Files"), extensions: ['*'] }; - if (matchingFilter) { - filters.unshift(matchingFilter); - filters.unshift(allFilesFilter); - } else { - filters.unshift(allFilesFilter); + // We have no matching filter, e.g. because the language + // is unknown. We still add the extension to the list of + // filters though so that it can be picked + // (https://github.com/microsoft/vscode/issues/96283) + if (!matchingFilter && ext) { + matchingFilter = { name: trim(ext, '.').toUpperCase(), extensions: [trim(ext, '.')] }; } - // Allow to save file without extension - filters.push({ name: nls.localize('noExt', "No Extension"), extensions: [''] }); - - options.filters = filters; + // Order of filters is + // - All Files (we MUST do this to fix macOS issue https://github.com/microsoft/vscode/issues/102713) + // - File Extension Match (if any) + // - All Languages + // - No Extension + options.filters = coalesce([ + { name: nls.localize('allFiles', "All Files"), extensions: ['*'] }, + matchingFilter, + ...registeredLanguageFilters, + { name: nls.localize('noExt', "No Extension"), extensions: [''] } + ]); return options; } diff --git a/src/vs/workbench/services/dialogs/browser/dialogService.ts b/src/vs/workbench/services/dialogs/browser/dialogService.ts index 6b42535bff5..bf9a892d41e 100644 --- a/src/vs/workbench/services/dialogs/browser/dialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/dialogService.ts @@ -22,7 +22,7 @@ import { fromNow } from 'vs/base/common/date'; export class DialogService implements IDialogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private allowableCommands = ['copy', 'cut']; diff --git a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts index 34b4347cfd7..46801bd7349 100644 --- a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts @@ -22,7 +22,7 @@ import { Schemas } from 'vs/base/common/network'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { equalsIgnoreCase, format, startsWithIgnoreCase, startsWith } from 'vs/base/common/strings'; +import { equalsIgnoreCase, format, startsWithIgnoreCase } from 'vs/base/common/strings'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { isValidBasename } from 'vs/base/common/extpath'; @@ -207,7 +207,7 @@ export class SimpleFileDialog { } private remoteUriFrom(path: string): URI { - if (!startsWith(path, '\\\\')) { + if (!path.startsWith('\\\\')) { path = path.replace(/\\/g, '/'); } const uri: URI = this.scheme === Schemas.file ? URI.file(path) : URI.from({ scheme: this.scheme, path }); @@ -231,8 +231,8 @@ export class SimpleFileDialog { return this.remoteAgentEnvironment; } - protected async getUserHome(): Promise { - return (await this.pathService.userHome) ?? URI.from({ scheme: this.scheme, authority: this.remoteAuthority, path: '/' }); + protected getUserHome(): Promise { + return this.pathService.userHome({ preferLocal: this.scheme === Schemas.file }); } private async pickResource(isSave: boolean = false): Promise { @@ -403,7 +403,7 @@ export class SimpleFileDialog { this.filePickBox.validationMessage = undefined; const filePickBoxUri = this.filePickBoxValue(); let updated: UpdateResult = UpdateResult.NotUpdated; - if (!resources.isEqual(this.currentFolder, filePickBoxUri, true)) { + if (!resources.extUriIgnorePathCase.isEqual(this.currentFolder, filePickBoxUri)) { updated = await this.tryUpdateItems(value, filePickBoxUri); } if (updated === UpdateResult.NotUpdated) { @@ -448,7 +448,7 @@ export class SimpleFileDialog { private filePickBoxValue(): URI { // The file pick box can't render everything, so we use the current folder to create the uri so that it is an existing path. - const directUri = this.remoteUriFrom(this.filePickBox.value); + const directUri = this.remoteUriFrom(this.filePickBox.value.trimRight()); const currentPath = this.pathFromUri(this.currentFolder); if (equalsIgnoreCase(this.filePickBox.value, currentPath)) { return this.currentFolder; @@ -541,7 +541,7 @@ export class SimpleFileDialog { value = this.pathFromUri(valueUri); await this.updateItems(valueUri, true); return UpdateResult.Updated; - } else if (!resources.isEqual(this.currentFolder, valueUri, true) && (this.endsWithSlash(value) || (!resources.isEqual(this.currentFolder, resources.dirname(valueUri), true) && resources.isEqualOrParent(this.currentFolder, resources.dirname(valueUri), true)))) { + } else if (!resources.extUriIgnorePathCase.isEqual(this.currentFolder, valueUri) && (this.endsWithSlash(value) || (!resources.extUriIgnorePathCase.isEqual(this.currentFolder, resources.dirname(valueUri)) && resources.extUriIgnorePathCase.isEqualOrParent(this.currentFolder, resources.dirname(valueUri))))) { let stat: IFileStat | undefined; try { stat = await this.fileService.resolve(valueUri); @@ -560,7 +560,7 @@ export class SimpleFileDialog { return UpdateResult.InvalidPath; } else { const inputUriDirname = resources.dirname(valueUri); - if (!resources.isEqual(resources.removeTrailingPathSeparator(this.currentFolder), inputUriDirname, true)) { + if (!resources.extUriIgnorePathCase.isEqual(resources.removeTrailingPathSeparator(this.currentFolder), inputUriDirname)) { let statWithoutTrailing: IFileStat | undefined; try { statWithoutTrailing = await this.fileService.resolve(inputUriDirname); @@ -765,6 +765,9 @@ export class SimpleFileDialog { // File or folder doesn't exist this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateNonexistentDir', 'Please enter a path that exists.'); return Promise.resolve(false); + } else if (uri.path === '/' && (await this.isWindowsOS())) { + this.filePickBox.validationMessage = nls.localize('remoteFileDialog.windowsDriveLetter', 'Please start the path with a drive letter.'); + return Promise.resolve(false); } else if (stat.isDirectory && !this.allowFolderSelection) { // Folder selected when folder selection not permitted this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateFileOnly', 'Please select a file.'); @@ -865,7 +868,7 @@ export class SimpleFileDialog { private createBackItem(currFolder: URI): FileQuickPickItem | null { const fileRepresentationCurr = this.currentFolder.with({ scheme: Schemas.file }); const fileRepresentationParent = resources.dirname(fileRepresentationCurr); - if (!resources.isEqual(fileRepresentationCurr, fileRepresentationParent, true)) { + if (!resources.isEqual(fileRepresentationCurr, fileRepresentationParent)) { const parentFolder = resources.dirname(currFolder); return { label: '..', uri: resources.addTrailingPathSeparator(parentFolder, this.separator), isFolder: true }; } @@ -878,8 +881,7 @@ export class SimpleFileDialog { const backDir = this.createBackItem(currentFolder); try { const folder = await this.fileService.resolve(currentFolder); - const fileNames = folder.children ? folder.children.map(child => child.name) : []; - const items = await Promise.all(fileNames.map(fileName => this.createItem(fileName, currentFolder, token))); + const items = folder.children ? await Promise.all(folder.children.map(child => this.createItem(child, currentFolder, token))) : []; for (let item of items) { if (item) { result.push(item); @@ -922,23 +924,18 @@ export class SimpleFileDialog { return true; } - private async createItem(filename: string, parent: URI, token: CancellationToken): Promise { + private async createItem(stat: IFileStat, parent: URI, token: CancellationToken): Promise { if (token.isCancellationRequested) { return undefined; } - let fullPath = resources.joinPath(parent, filename); - try { - const stat = await this.fileService.resolve(fullPath); - if (stat.isDirectory) { - filename = resources.basename(fullPath); - fullPath = resources.addTrailingPathSeparator(fullPath, this.separator); - return { label: filename, uri: fullPath, isFolder: true, iconClasses: getIconClasses(this.modelService, this.modeService, fullPath || undefined, FileKind.FOLDER) }; - } else if (!stat.isDirectory && this.allowFileSelection && this.filterFile(fullPath)) { - return { label: filename, uri: fullPath, isFolder: false, iconClasses: getIconClasses(this.modelService, this.modeService, fullPath || undefined) }; - } - return undefined; - } catch (e) { - return undefined; + let fullPath = resources.joinPath(parent, stat.name); + if (stat.isDirectory) { + const filename = resources.basename(fullPath); + fullPath = resources.addTrailingPathSeparator(fullPath, this.separator); + return { label: filename, uri: fullPath, isFolder: true, iconClasses: getIconClasses(this.modelService, this.modeService, fullPath || undefined, FileKind.FOLDER) }; + } else if (!stat.isDirectory && this.allowFileSelection && this.filterFile(fullPath)) { + return { label: stat.name, uri: fullPath, isFolder: false, iconClasses: getIconClasses(this.modelService, this.modeService, fullPath || undefined) }; } + return undefined; } } diff --git a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts index 969a1e5a5dc..1bb0e9959c2 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; import * as os from 'os'; -import product from 'vs/platform/product/common/product'; import Severity from 'vs/base/common/severity'; import { isLinux, isWindows } from 'vs/base/common/platform'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; @@ -21,8 +20,8 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IProductService } from 'vs/platform/product/common/productService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { IElectronService } from 'vs/platform/electron/node/electron'; -import { MessageBoxOptions } from 'electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; +import { MessageBoxOptions } from 'vs/base/parts/sandbox/common/electronTypes'; import { fromNow } from 'vs/base/common/date'; interface IMassagedMessageBoxOptions { @@ -42,7 +41,7 @@ interface IMassagedMessageBoxOptions { export class DialogService implements IDialogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private nativeImpl: IDialogService; private customImpl: IDialogService; @@ -59,7 +58,7 @@ export class DialogService implements IDialogService { @IElectronService electronService: IElectronService ) { this.customImpl = new HTMLDialogService(logService, layoutService, themeService, keybindingService, productService, clipboardService); - this.nativeImpl = new NativeDialogService(logService, sharedProcessService, electronService, clipboardService); + this.nativeImpl = new NativeDialogService(logService, sharedProcessService, electronService, productService, clipboardService); } private get useCustomDialog(): boolean { @@ -89,12 +88,13 @@ export class DialogService implements IDialogService { class NativeDialogService implements IDialogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @ILogService private readonly logService: ILogService, @ISharedProcessService sharedProcessService: ISharedProcessService, @IElectronService private readonly electronService: IElectronService, + @IProductService private readonly productService: IProductService, @IClipboardService private readonly clipboardService: IClipboardService ) { sharedProcessService.registerChannel('dialog', new DialogChannel(this)); @@ -205,15 +205,15 @@ class NativeDialogService implements IDialogService { options.buttons = buttons; options.cancelId = cancelId; options.noLink = true; - options.title = options.title || product.nameLong; + options.title = options.title || this.productService.nameLong; return { options, buttonIndexMap }; } async about(): Promise { - let version = product.version; - if (product.target) { - version = `${version} (${product.target} setup)`; + let version = this.productService.version; + if (this.productService.target) { + version = `${version} (${this.productService.target} setup)`; } const isSnap = process.platform === 'linux' && process.env.SNAP && process.env.SNAP_REVISION; @@ -222,8 +222,8 @@ class NativeDialogService implements IDialogService { return nls.localize('aboutDetail', "Version: {0}\nCommit: {1}\nDate: {2}\nElectron: {3}\nChrome: {4}\nNode.js: {5}\nV8: {6}\nOS: {7}", version, - product.commit || 'Unknown', - product.date ? `${product.date}${useAgo ? ' (' + fromNow(new Date(product.date), true) + ')' : ''}` : 'Unknown', + this.productService.commit || 'Unknown', + this.productService.date ? `${this.productService.date}${useAgo ? ' (' + fromNow(new Date(this.productService.date), true) + ')' : ''}` : 'Unknown', process.versions['electron'], process.versions['chrome'], process.versions['node'], @@ -245,9 +245,9 @@ class NativeDialogService implements IDialogService { } const result = await this.electronService.showMessageBox({ - title: product.nameLong, + title: this.productService.nameLong, type: 'info', - message: product.nameLong, + message: this.productService.nameLong, detail: `\n${detail}`, buttons, noLink: true, diff --git a/src/vs/workbench/services/dialogs/electron-browser/simpleFileDialog.ts b/src/vs/workbench/services/dialogs/electron-browser/simpleFileDialog.ts deleted file mode 100644 index ac98bcc8193..00000000000 --- a/src/vs/workbench/services/dialogs/electron-browser/simpleFileDialog.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 { URI } from 'vs/base/common/uri'; -import { SimpleFileDialog } from 'vs/workbench/services/dialogs/browser/simpleFileDialog'; -import { Schemas } from 'vs/base/common/network'; -import { IFileService } from 'vs/platform/files/common/files'; -import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { ILabelService } from 'vs/platform/label/common/label'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { IModeService } from 'vs/editor/common/services/modeService'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; -import { IPathService } from 'vs/workbench/services/path/common/pathService'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; - -export class NativeSimpleFileDialog extends SimpleFileDialog { - constructor( - @IFileService fileService: IFileService, - @IQuickInputService quickInputService: IQuickInputService, - @ILabelService labelService: ILabelService, - @IWorkspaceContextService workspaceContextService: IWorkspaceContextService, - @INotificationService notificationService: INotificationService, - @IFileDialogService fileDialogService: IFileDialogService, - @IModelService modelService: IModelService, - @IModeService modeService: IModeService, - @IWorkbenchEnvironmentService protected environmentService: INativeWorkbenchEnvironmentService, - @IRemoteAgentService remoteAgentService: IRemoteAgentService, - @IPathService protected pathService: IPathService, - @IKeybindingService keybindingService: IKeybindingService, - @IContextKeyService contextKeyService: IContextKeyService - ) { - super(fileService, quickInputService, labelService, workspaceContextService, notificationService, fileDialogService, modelService, modeService, environmentService, remoteAgentService, pathService, keybindingService, contextKeyService); - } - - protected async getUserHome(): Promise { - if (this.scheme !== Schemas.file) { - return super.getUserHome(); - } - return this.environmentService.userHome; - } -} diff --git a/src/vs/workbench/services/dialogs/electron-browser/fileDialogService.ts b/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts similarity index 92% rename from src/vs/workbench/services/dialogs/electron-browser/fileDialogService.ts rename to src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts index 6457069b1e5..f95983cf9c6 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts @@ -3,10 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SaveDialogOptions, OpenDialogOptions } from 'electron'; -import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs'; +import { SaveDialogOptions, OpenDialogOptions } from 'vs/base/parts/sandbox/common/electronTypes'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService, IDialogService, INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -16,18 +15,16 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { AbstractFileDialogService } from 'vs/workbench/services/dialogs/browser/abstractFileDialogService'; import { Schemas } from 'vs/base/common/network'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { ILabelService } from 'vs/platform/label/common/label'; -import { SimpleFileDialog } from 'vs/workbench/services/dialogs/browser/simpleFileDialog'; -import { NativeSimpleFileDialog } from 'vs/workbench/services/dialogs/electron-browser/simpleFileDialog'; export class FileDialogService extends AbstractFileDialogService implements IFileDialogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @IHostService hostService: IHostService, @@ -192,10 +189,6 @@ export class FileDialogService extends AbstractFileDialogService implements IFil // Don't allow untitled schema through. return schema === Schemas.untitled ? [Schemas.file] : (schema !== Schemas.file ? [schema, Schemas.file] : [schema]); } - - protected createSimpleFileDialog(): SimpleFileDialog { - return this.instantiationService.createInstance(NativeSimpleFileDialog); - } } registerSingleton(IFileDialogService, FileDialogService, true); diff --git a/src/vs/workbench/services/editor/browser/codeEditorService.ts b/src/vs/workbench/services/editor/browser/codeEditorService.ts index b1b93a22314..ad4d75f6caa 100644 --- a/src/vs/workbench/services/editor/browser/codeEditorService.ts +++ b/src/vs/workbench/services/editor/browser/codeEditorService.ts @@ -12,6 +12,7 @@ import { TextEditorOptions } from 'vs/workbench/common/editor'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { extUri } from 'vs/base/common/resources'; export class CodeEditorService extends CodeEditorServiceImpl { @@ -47,13 +48,13 @@ export class CodeEditorService extends CodeEditorServiceImpl { // side as separate editor. const activeTextEditorControl = this.editorService.activeTextEditorControl; if ( - !sideBySide && // we need the current active group to be the taret - isDiffEditor(activeTextEditorControl) && // we only support this for active text diff editors - input.options && // we need options to apply - input.resource && // we need a request resource to compare with - activeTextEditorControl.getModel() && // we need a target model to compare with - source === activeTextEditorControl.getModifiedEditor() && // we need the source of this request to be the modified side of the diff editor - input.resource.toString() === activeTextEditorControl.getModel()!.modified.uri.toString() // we need the input resources to match with modified side + !sideBySide && // we need the current active group to be the taret + isDiffEditor(activeTextEditorControl) && // we only support this for active text diff editors + input.options && // we need options to apply + input.resource && // we need a request resource to compare with + activeTextEditorControl.getModel() && // we need a target model to compare with + source === activeTextEditorControl.getModifiedEditor() && // we need the source of this request to be the modified side of the diff editor + extUri.isEqual(input.resource, activeTextEditorControl.getModel()!.modified.uri) // we need the input resources to match with modified side ) { const targetEditor = activeTextEditorControl.getModifiedEditor(); diff --git a/src/vs/workbench/services/editor/browser/editorDropService.ts b/src/vs/workbench/services/editor/browser/editorDropService.ts new file mode 100644 index 00000000000..c8d04f4ab35 --- /dev/null +++ b/src/vs/workbench/services/editor/browser/editorDropService.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IEditorDropTargetDelegate } from 'vs/workbench/browser/parts/editor/editorDropTarget'; + +export const IEditorDropService = createDecorator('editorDropService'); + +export interface IEditorDropService { + + readonly _serviceBrand: undefined; + + /** + * Allows to register a drag and drop target for editors. + */ + createEditorDropTarget(container: HTMLElement, delegate: IEditorDropTargetDelegate): IDisposable; +} diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index b025eadf1bf..88800a2c196 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -15,9 +15,9 @@ import { IFileService, FileOperationEvent, FileOperation, FileChangesEvent, File import { Schemas } from 'vs/base/common/network'; import { Event, Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; -import { basename, isEqualOrParent, joinPath } from 'vs/base/common/resources'; +import { basename, joinPath, extUri } from 'vs/base/common/resources'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; -import { IEditorGroupsService, IEditorGroup, GroupsOrder, IEditorReplacement, GroupChangeKind, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorGroupsService, IEditorGroup, GroupsOrder, IEditorReplacement, GroupChangeKind, preferredSideBySideGroupDirection, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IResourceEditorInputType, SIDE_GROUP, IResourceEditorReplacement, IOpenEditorOverrideHandler, IEditorService, SIDE_GROUP_TYPE, ACTIVE_GROUP_TYPE, ISaveEditorsOptions, ISaveAllEditorsOptions, IRevertAllEditorsOptions, IBaseSaveRevertAllEditorOptions, IOpenEditorOverrideEntry, ICustomEditorViewTypesHandler, ICustomEditorInfo } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Disposable, IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; @@ -34,15 +34,18 @@ import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/u import { timeout } from 'vs/base/common/async'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { indexOfPath } from 'vs/base/common/extpath'; -import { DEFAULT_CUSTOM_EDITOR, updateViewTypeSchema, editorAssociationsConfigurationNode } from 'vs/workbench/services/editor/common/editorAssociationsSetting'; +import { DEFAULT_CUSTOM_EDITOR, updateViewTypeSchema, editorAssociationsConfigurationNode } from 'vs/workbench/services/editor/common/editorOpenWith'; import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { IModelService } from 'vs/editor/common/services/modelService'; type CachedEditorInput = ResourceEditorInput | IFileEditorInput | UntitledTextEditorInput; type OpenInEditorGroup = IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE; export class EditorService extends Disposable implements EditorServiceImpl { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; //#region events @@ -63,6 +66,8 @@ export class EditorService extends Disposable implements EditorServiceImpl { //#endregion + private readonly fileEditorInputFactory = Registry.as(EditorExtensions.EditorInputFactories).getFileEditorInputFactory(); + constructor( @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @IUntitledTextEditorService private readonly untitledTextEditorService: IUntitledTextEditorService, @@ -70,7 +75,9 @@ export class EditorService extends Disposable implements EditorServiceImpl { @ILabelService private readonly labelService: ILabelService, @IFileService private readonly fileService: IFileService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IWorkspaceContextService private readonly contextService: IWorkspaceContextService + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService ) { super(); @@ -178,8 +185,8 @@ export class EditorService extends Disposable implements EditorServiceImpl { for (const editor of this.visibleEditors) { const resources = distinct(coalesce([ - toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }), - toResource(editor, { supportSideBySide: SideBySideEditor.DETAILS }) + toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY }), + toResource(editor, { supportSideBySide: SideBySideEditor.SECONDARY }) ]), resource => resource.toString()); for (const resource of resources) { @@ -235,13 +242,13 @@ export class EditorService extends Disposable implements EditorServiceImpl { for (const editor of group.editors) { const resource = editor.resource; - if (!resource || !isEqualOrParent(resource, source)) { + if (!resource || !this.uriIdentityService.extUri.isEqualOrParent(resource, source)) { continue; // not matching our resource } // Determine new resulting target resource let targetResource: URI; - if (source.toString() === resource.toString()) { + if (extUri.isEqual(source, resource)) { targetResource = target; // file got moved } else { const ignoreCase = !this.fileService.hasCapability(resource, FileSystemProviderCapabilities.PathCaseSensitive); @@ -249,8 +256,8 @@ export class EditorService extends Disposable implements EditorServiceImpl { targetResource = joinPath(target, resource.path.substr(index + source.path.length + 1)); // parent folder got moved } - // Delegate move() to editor instance - const moveResult = editor.move(group.id, targetResource); + // Delegate rename() to editor instance + const moveResult = editor.rename(group.id, targetResource); if (!moveResult) { return; // not target - ignore } @@ -295,7 +302,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { } private closeOnFileDelete: boolean = false; - private fileEditorInputFactory = Registry.as(EditorExtensions.EditorInputFactories).getFileEditorInputFactory(); + private onConfigurationUpdated(configuration: IWorkbenchEditorConfiguration): void { if (typeof configuration.workbench?.editor?.closeOnFileDelete === 'boolean') { this.closeOnFileDelete = configuration.workbench.editor.closeOnFileDelete; @@ -321,7 +328,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { // Do NOT close any opened editor that matches the resource path (either equal or being parent) of the // resource we move to (movedTo). Otherwise we would close a resource that has been renamed to the same // path but different casing. - if (movedTo && isEqualOrParent(resource, movedTo)) { + if (movedTo && this.uriIdentityService.extUri.isEqualOrParent(resource, movedTo)) { return; } @@ -329,7 +336,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { if (arg1 instanceof FileChangesEvent) { matches = arg1.contains(resource, FileChangeType.DELETED); } else { - matches = isEqualOrParent(resource, arg1); + matches = this.uriIdentityService.extUri.isEqualOrParent(resource, arg1); } if (!matches) { @@ -373,8 +380,8 @@ export class EditorService extends Disposable implements EditorServiceImpl { for (const editor of this.editors) { if (options.supportSideBySide && editor instanceof SideBySideEditorInput) { - conditionallyAddEditor(editor.master); - conditionallyAddEditor(editor.details); + conditionallyAddEditor(editor.primary); + conditionallyAddEditor(editor.secondary); } else { conditionallyAddEditor(editor); } @@ -484,22 +491,22 @@ export class EditorService extends Disposable implements EditorServiceImpl { } getEditorOverrides(resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined): [IOpenEditorOverrideHandler, IOpenEditorOverrideEntry][] { - const ret = []; + const overrides = []; for (const handler of this.openEditorHandlers) { - const handlers = handler.getEditorOverrides ? handler.getEditorOverrides(resource, options, group).map(val => { return [handler, val] as [IOpenEditorOverrideHandler, IOpenEditorOverrideEntry]; }) : []; - ret.push(...handlers); + const handlers = handler.getEditorOverrides ? handler.getEditorOverrides(resource, options, group).map(val => [handler, val] as [IOpenEditorOverrideHandler, IOpenEditorOverrideEntry]) : []; + overrides.push(...handlers); } - return ret; + return overrides; } private onGroupWillOpenEditor(group: IEditorGroup, event: IEditorOpeningEvent): void { - if (event.options && event.options.ignoreOverrides) { - return; + if (event.options?.override === false) { + return; // return early when overrides are explicitly disabled } for (const handler of this.openEditorHandlers) { - const result = handler.open(event.editor, event.options, group); + const result = handler.open(event.editor, event.options, group, event.context ?? OpenEditorContext.NEW_EDITOR); const override = result?.override; if (override) { event.prevent((() => override.then(editor => withNullAsUndefined(editor)))); @@ -682,13 +689,13 @@ export class EditorService extends Disposable implements EditorServiceImpl { async openEditors(editors: Array, group?: OpenInEditorGroup): Promise { // Convert to typed editors and options - const typedEditors: IEditorInputWithOptions[] = []; - editors.forEach(editor => { + const typedEditors = editors.map(editor => { if (isEditorInputWithOptions(editor)) { - typedEditors.push(editor); - } else { - typedEditors.push({ editor: this.createEditorInput(editor), options: TextEditorOptions.from(editor) }); + return editor; } + + const editorInput: IEditorInputWithOptions = { editor: this.createEditorInput(editor), options: TextEditorOptions.from(editor) }; + return editorInput; }); // Find target groups to open @@ -730,7 +737,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { } if (editor.resource) { - return this.editorsObserver.hasEditor(editor.resource); + return this.editorsObserver.hasEditor(this.asCanonicalEditorResource(editor.resource)); } return false; @@ -863,20 +870,29 @@ export class EditorService extends Disposable implements EditorServiceImpl { // Resource Editor Support const resourceEditorInput = input as IResourceEditorInput; if (resourceEditorInput.resource instanceof URI) { - let label = resourceEditorInput.label; - if (!label) { - label = basename(resourceEditorInput.resource); // derive the label from the path - } - return this.createOrGetCached(resourceEditorInput.resource, () => { + // Derive the label from the path if not provided explicitly + const label = resourceEditorInput.label || basename(resourceEditorInput.resource); + + // We keep track of the preferred resource this input is to be created + // with but it may be different from the canonical resource (see below) + const preferredResource = resourceEditorInput.resource; + + // From this moment on, only operate on the canonical resource + // to ensure we reduce the chance of opening the same resource + // with different resource forms (e.g. path casing on Windows) + const canonicalResource = this.asCanonicalEditorResource(preferredResource); + + + return this.createOrGetCached(canonicalResource, () => { // File - if (resourceEditorInput.forceFile /* fix for https://github.com/Microsoft/vscode/issues/48275 */ || this.fileService.canHandleResource(resourceEditorInput.resource)) { - return this.fileEditorInputFactory.createFileEditorInput(resourceEditorInput.resource, resourceEditorInput.encoding, resourceEditorInput.mode, this.instantiationService); + if (resourceEditorInput.forceFile || this.fileService.canHandleResource(canonicalResource)) { + return this.fileEditorInputFactory.createFileEditorInput(canonicalResource, preferredResource, resourceEditorInput.encoding, resourceEditorInput.mode, this.instantiationService); } // Resource - return this.instantiationService.createInstance(ResourceEditorInput, resourceEditorInput.label, resourceEditorInput.description, resourceEditorInput.resource, resourceEditorInput.mode); + return this.instantiationService.createInstance(ResourceEditorInput, canonicalResource, resourceEditorInput.label, resourceEditorInput.description, resourceEditorInput.mode); }, cachedInput => { // Untitled @@ -886,6 +902,8 @@ export class EditorService extends Disposable implements EditorServiceImpl { // Files else if (!(cachedInput instanceof ResourceEditorInput)) { + cachedInput.setPreferredResource(preferredResource); + if (resourceEditorInput.encoding) { cachedInput.setPreferredEncoding(resourceEditorInput.encoding); } @@ -915,6 +933,28 @@ export class EditorService extends Disposable implements EditorServiceImpl { throw new Error('Unknown input type'); } + private _modelService: IModelService | undefined = undefined; + private get modelService(): IModelService | undefined { + if (!this._modelService) { + this._modelService = this.instantiationService.invokeFunction(accessor => accessor.get(IModelService)); + } + + return this._modelService; + } + + private asCanonicalEditorResource(resource: URI): URI { + const canonicalResource: URI = this.uriIdentityService.asCanonicalUri(resource); + + // In the unlikely case that a model exists for the original resource but + // differs from the canonical resource, we print a warning as this means + // the model will not be able to be opened as editor. + if (!extUri.isEqual(resource, canonicalResource) && this.modelService?.getModel(resource)) { + console.warn(`EditorService: a model exists for a resource that is not canonical: ${resource.toString(true)}`); + } + + return canonicalResource; + } + private createOrGetCached(resource: URI, factoryFn: () => CachedEditorInput, cachedFn?: (input: CachedEditorInput) => void): CachedEditorInput { // Return early if already cached @@ -1107,7 +1147,9 @@ export class EditorService extends Disposable implements EditorServiceImpl { //#endregion //#region Custom View Type - private customEditorViewTypesHandlers = new Map(); + + private readonly customEditorViewTypesHandlers = new Map(); + registerCustomEditorViewTypesHandler(source: string, handler: ICustomEditorViewTypesHandler): IDisposable { if (this.customEditorViewTypesHandlers.has(source)) { throw new Error(`Use a different name for the custom editor component, ${source} is already occupied.`); @@ -1148,6 +1190,67 @@ export class EditorService extends Disposable implements EditorServiceImpl { } //#endregion + + //#region Editor Tracking + + whenClosed(editors: IResourceEditorInput[], options?: { waitForSaved: boolean }): Promise { + let remainingEditors = [...editors]; + + return new Promise(resolve => { + const listener = this.onDidCloseEditor(async event => { + const primaryResource = toResource(event.editor, { supportSideBySide: SideBySideEditor.PRIMARY }); + const secondaryResource = toResource(event.editor, { supportSideBySide: SideBySideEditor.SECONDARY }); + + // Remove from resources to wait for being closed based on the + // resources from editors that got closed + remainingEditors = remainingEditors.filter(({ resource }) => { + if (this.uriIdentityService.extUri.isEqual(resource, primaryResource) || this.uriIdentityService.extUri.isEqual(resource, secondaryResource)) { + return false; // remove - the closing editor matches this resource + } + + return true; // keep - not yet closed + }); + + // All resources to wait for being closed are closed + if (remainingEditors.length === 0) { + if (options?.waitForSaved) { + // If auto save is configured with the default delay (1s) it is possible + // to close the editor while the save still continues in the background. As such + // we have to also check if the editors to track for are dirty and if so wait + // for them to get saved. + const dirtyResources = editors.filter(({ resource }) => this.workingCopyService.isDirty(resource)).map(({ resource }) => resource); + if (dirtyResources.length > 0) { + await Promise.all(dirtyResources.map(async resource => await this.whenSaved(resource))); + } + } + + listener.dispose(); + + resolve(); + } + }); + }); + } + + private whenSaved(resource: URI): Promise { + return new Promise(resolve => { + if (!this.workingCopyService.isDirty(resource)) { + return resolve(); // return early if resource is not dirty + } + + // Otherwise resolve promise when resource is saved + const listener = this.workingCopyService.onDidChangeDirty(workingCopy => { + if (!workingCopy.isDirty() && this.uriIdentityService.extUri.isEqual(resource, workingCopy.resource)) { + listener.dispose(); + + resolve(); + } + }); + }); + } + + //#endregion + dispose(): void { super.dispose(); @@ -1173,17 +1276,13 @@ export interface IEditorOpenHandler { */ export class DelegatingEditorService implements IEditorService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( private editorOpenHandler: IEditorOpenHandler, @IEditorService private editorService: EditorService ) { } - getEditorOverrides(resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined) { - return this.editorService.getEditorOverrides(resource, options, group); - } - openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, group?: OpenInEditorGroup): Promise; openEditor(editor: IResourceEditorInput | IUntitledTextResourceEditorInput, group?: OpenInEditorGroup): Promise; openEditor(editor: IResourceDiffEditorInput, group?: OpenInEditorGroup): Promise; @@ -1214,6 +1313,7 @@ export class DelegatingEditorService implements IEditorService { get onDidActiveEditorChange(): Event { return this.editorService.onDidActiveEditorChange; } get onDidVisibleEditorsChange(): Event { return this.editorService.onDidVisibleEditorsChange; } + get onDidCloseEditor(): Event { return this.editorService.onDidCloseEditor; } get activeEditor(): IEditorInput | undefined { return this.editorService.activeEditor; } get activeEditorPane(): IVisibleEditorPane | undefined { return this.editorService.activeEditorPane; } @@ -1252,6 +1352,7 @@ export class DelegatingEditorService implements IEditorService { isOpen(editor: IEditorInput | IResourceEditorInput): boolean { return this.editorService.isOpen(editor as IResourceEditorInput /* TS fail */); } overrideOpenEditor(handler: IOpenEditorOverrideHandler): IDisposable { return this.editorService.overrideOpenEditor(handler); } + getEditorOverrides(resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined) { return this.editorService.getEditorOverrides(resource, options, group); } invokeWithinEditorContext(fn: (accessor: ServicesAccessor) => T): T { return this.editorService.invokeWithinEditorContext(fn); } @@ -1263,9 +1364,9 @@ export class DelegatingEditorService implements IEditorService { revert(editors: IEditorIdentifier | IEditorIdentifier[], options?: IRevertOptions): Promise { return this.editorService.revert(editors, options); } revertAll(options?: IRevertAllEditorsOptions): Promise { return this.editorService.revertAll(options); } - registerCustomEditorViewTypesHandler(source: string, handler: ICustomEditorViewTypesHandler): IDisposable { - throw new Error('Method not implemented.'); - } + registerCustomEditorViewTypesHandler(source: string, handler: ICustomEditorViewTypesHandler): IDisposable { return this.editorService.registerCustomEditorViewTypesHandler(source, handler); } + + whenClosed(editors: IResourceEditorInput[]): Promise { return this.editorService.whenClosed(editors); } //#endregion } diff --git a/src/vs/workbench/services/editor/common/editorAssociationsSetting.ts b/src/vs/workbench/services/editor/common/editorAssociationsSetting.ts deleted file mode 100644 index aded0a85d1e..00000000000 --- a/src/vs/workbench/services/editor/common/editorAssociationsSetting.ts +++ /dev/null @@ -1,76 +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 { IJSONSchema } from 'vs/base/common/jsonSchema'; -import * as nls from 'vs/nls'; -import { IConfigurationNode, IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; -import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { ICustomEditorInfo } from 'vs/workbench/services/editor/common/editorService'; - -export const customEditorsAssociationsSettingId = 'workbench.editorAssociations'; - -export const viewTypeSchamaAddition: IJSONSchema = { - type: 'string', - enum: [] -}; - -export type CustomEditorAssociation = { - readonly viewType: string; - readonly filenamePattern?: string; -}; - -export type CustomEditorsAssociations = readonly CustomEditorAssociation[]; - -export const editorAssociationsConfigurationNode: IConfigurationNode = { - ...workbenchConfigurationNodeBase, - properties: { - [customEditorsAssociationsSettingId]: { - type: 'array', - markdownDescription: nls.localize('editor.editorAssociations', "Configure which editor to use for specific file types."), - items: { - type: 'object', - defaultSnippets: [{ - body: { - 'viewType': '$1', - 'filenamePattern': '$2' - } - }], - properties: { - 'viewType': { - anyOf: [ - { - type: 'string', - description: nls.localize('editor.editorAssociations.viewType', "The unique id of the editor to use."), - }, - viewTypeSchamaAddition - ] - }, - 'filenamePattern': { - type: 'string', - description: nls.localize('editor.editorAssociations.filenamePattern', "Glob pattern specifying which files the editor should be used for."), - } - } - } - } - } -}; - - -const builtinProviderDisplayName = nls.localize('builtinProviderDisplayName', "Built-in"); - -export const DEFAULT_CUSTOM_EDITOR: ICustomEditorInfo = { - id: 'default', - displayName: nls.localize('promptOpenWith.defaultEditor.displayName', "Text Editor"), - providerDisplayName: builtinProviderDisplayName -}; - -export function updateViewTypeSchema(enumValues: string[], enumDescriptions: string[]): void { - viewTypeSchamaAddition.enum = enumValues; - viewTypeSchamaAddition.enumDescriptions = enumDescriptions; - - Registry.as(Extensions.Configuration) - .notifyConfigurationSchemaUpdated(editorAssociationsConfigurationNode); -} diff --git a/src/vs/workbench/services/editor/common/editorGroupsService.ts b/src/vs/workbench/services/editor/common/editorGroupsService.ts index ea3c10d78a1..c2433a8015b 100644 --- a/src/vs/workbench/services/editor/common/editorGroupsService.ts +++ b/src/vs/workbench/services/editor/common/editorGroupsService.ts @@ -5,7 +5,7 @@ import { Event } from 'vs/base/common/event'; import { createDecorator, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IEditorInput, IEditorPane, GroupIdentifier, IEditorInputWithOptions, CloseDirection, IEditorPartOptions, IEditorPartOptionsChangeEvent, EditorsOrder, IVisibleEditorPane } from 'vs/workbench/common/editor'; +import { IEditorInput, IEditorPane, GroupIdentifier, IEditorInputWithOptions, CloseDirection, IEditorPartOptions, IEditorPartOptionsChangeEvent, EditorsOrder, IVisibleEditorPane, IEditorCloseEvent } from 'vs/workbench/common/editor'; import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IDimension } from 'vs/editor/common/editorCommon'; @@ -140,7 +140,7 @@ export const enum GroupsOrder { export interface IEditorGroupsService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * An event for when the active editor group changes. The active editor @@ -359,6 +359,7 @@ export const enum GroupChangeKind { EDITOR_ACTIVE, EDITOR_LABEL, EDITOR_PIN, + EDITOR_STICKY, EDITOR_DIRTY } @@ -368,6 +369,12 @@ export interface IGroupChangeEvent { editorIndex?: number; } +export const enum OpenEditorContext { + NEW_EDITOR = 1, + MOVE_EDITOR = 2, + COPY_EDITOR = 3 +} + export interface IEditorGroup { /** @@ -380,6 +387,11 @@ export interface IEditorGroup { */ readonly onWillDispose: Event; + /** + * An event that is fired when an editor is about to close. + */ + readonly onWillCloseEditor: Event; + /** * A unique identifier of this group that remains identical even if the * group is moved to different locations. @@ -462,7 +474,7 @@ export interface IEditorGroup { * @returns a promise that resolves around an IEditor instance unless * the call failed, or the editor was not opened as active editor. */ - openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions): Promise; + openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, context?: OpenEditorContext): Promise; /** * Opens editors in this group. diff --git a/src/vs/workbench/contrib/files/common/openWith.ts b/src/vs/workbench/services/editor/common/editorOpenWith.ts similarity index 54% rename from src/vs/workbench/contrib/files/common/openWith.ts rename to src/vs/workbench/services/editor/common/editorOpenWith.ts index 265e49d5832..a824ac2e408 100644 --- a/src/vs/workbench/contrib/files/common/openWith.ts +++ b/src/vs/workbench/services/editor/common/editorOpenWith.ts @@ -3,20 +3,24 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { basename, extname, isEqual } from 'vs/base/common/resources'; -import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { IConfigurationNode, IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ICustomEditorInfo, IEditorService, IOpenEditorOverrideHandler, IOpenEditorOverrideEntry } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorInput, IEditorPane, IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; +import { ITextEditorOptions, IEditorOptions } from 'vs/platform/editor/common/editor'; +import { IEditorGroup, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { IEditorInput, IEditorPane } from 'vs/workbench/common/editor'; -import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; -import { DEFAULT_EDITOR_ID } from 'vs/workbench/contrib/files/common/files'; -import { CustomEditorAssociation, CustomEditorsAssociations, customEditorsAssociationsSettingId } from 'vs/workbench/services/editor/common/editorAssociationsSetting'; -import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IEditorService, IOpenEditorOverrideEntry, IOpenEditorOverrideHandler } from 'vs/workbench/services/editor/common/editorService'; +import { URI } from 'vs/base/common/uri'; +import { extname, basename, isEqual } from 'vs/base/common/resources'; -const builtinProviderDisplayName = nls.localize('builtinProviderDisplayName', "Built-in"); +/** + * Id of the default editor for open with. + */ +export const DEFAULT_EDITOR_ID = 'default'; /** * Try to open an resource with a given editor. @@ -38,14 +42,21 @@ export async function openEditorWith( return; } - const allEditorOverrides = getAllAvailableEditors(resource, options, group, editorService); + const overrideOptions = { ...options, override: id }; + + const allEditorOverrides = getAllAvailableEditors(resource, id, overrideOptions, group, editorService); if (!allEditorOverrides.length) { return; } - const overrideToUse = typeof id === 'string' && allEditorOverrides.find(([_, entry]) => entry.id === id); + let overrideToUse; + if (typeof id === 'string') { + overrideToUse = allEditorOverrides.find(([_, entry]) => entry.id === id); + } else if (allEditorOverrides.length === 1) { + overrideToUse = allEditorOverrides[0]; + } if (overrideToUse) { - return overrideToUse[0].open(input, options, group, id)?.override; + return overrideToUse[0].open(input, overrideOptions, group, OpenEditorContext.NEW_EDITOR)?.override; } // Prompt @@ -108,13 +119,15 @@ export async function openEditorWith( picker.show(); }); - return pickedItem?.handler.open(input!, options, group, pickedItem.id)?.override; + return pickedItem?.handler.open(input, { ...options, override: pickedItem.id }, group, OpenEditorContext.NEW_EDITOR)?.override; } +const builtinProviderDisplayName = nls.localize('builtinProviderDisplayName', "Built-in"); + export const defaultEditorOverrideEntry = Object.freeze({ id: DEFAULT_EDITOR_ID, label: nls.localize('promptOpenWith.defaultEditor.displayName', "Text Editor"), - detail: builtinProviderDisplayName, + detail: builtinProviderDisplayName }); /** @@ -122,10 +135,12 @@ export const defaultEditorOverrideEntry = Object.freeze({ */ export function getAllAvailableEditors( resource: URI, + id: string | undefined, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup, - editorService: IEditorService, + editorService: IEditorService ): Array<[IOpenEditorOverrideHandler, IOpenEditorOverrideEntry]> { + const fileEditorInputFactory = Registry.as(EditorExtensions.EditorInputFactories).getFileEditorInputFactory(); const overrides = editorService.getEditorOverrides(resource, options, group); if (!overrides.some(([_, entry]) => entry.id === DEFAULT_EDITOR_ID)) { overrides.unshift([ @@ -136,15 +151,77 @@ export function getAllAvailableEditors( } const fileEditorInput = editorService.createEditorInput({ resource: input.resource, forceFile: true }); - const textOptions = options ? { ...options, ignoreOverrides: true } : { ignoreOverrides: true }; + const textOptions: IEditorOptions | ITextEditorOptions = options ? { ...options, override: false } : { override: false }; return { override: editorService.openEditor(fileEditorInput, textOptions, group) }; } }, { ...defaultEditorOverrideEntry, - active: editorService.activeEditor instanceof FileEditorInput && isEqual(editorService.activeEditor.resource, resource), + active: fileEditorInputFactory.isFileEditorInput(editorService.activeEditor) && isEqual(editorService.activeEditor.resource, resource), }]); } + return overrides; } +export const customEditorsAssociationsSettingId = 'workbench.editorAssociations'; + +export const viewTypeSchamaAddition: IJSONSchema = { + type: 'string', + enum: [] +}; + +export type CustomEditorAssociation = { + readonly viewType: string; + readonly filenamePattern?: string; +}; + +export type CustomEditorsAssociations = readonly CustomEditorAssociation[]; + +export const editorAssociationsConfigurationNode: IConfigurationNode = { + ...workbenchConfigurationNodeBase, + properties: { + [customEditorsAssociationsSettingId]: { + type: 'array', + markdownDescription: nls.localize('editor.editorAssociations', "Configure which editor to use for specific file types."), + items: { + type: 'object', + defaultSnippets: [{ + body: { + 'viewType': '$1', + 'filenamePattern': '$2' + } + }], + properties: { + 'viewType': { + anyOf: [ + { + type: 'string', + description: nls.localize('editor.editorAssociations.viewType', "The unique id of the editor to use."), + }, + viewTypeSchamaAddition + ] + }, + 'filenamePattern': { + type: 'string', + description: nls.localize('editor.editorAssociations.filenamePattern', "Glob pattern specifying which files the editor should be used for."), + } + } + } + } + } +}; + +export const DEFAULT_CUSTOM_EDITOR: ICustomEditorInfo = { + id: 'default', + displayName: nls.localize('promptOpenWith.defaultEditor.displayName', "Text Editor"), + providerDisplayName: builtinProviderDisplayName +}; + +export function updateViewTypeSchema(enumValues: string[], enumDescriptions: string[]): void { + viewTypeSchamaAddition.enum = enumValues; + viewTypeSchamaAddition.enumDescriptions = enumDescriptions; + + Registry.as(Extensions.Configuration) + .notifyConfigurationSchemaUpdated(editorAssociationsConfigurationNode); +} diff --git a/src/vs/workbench/services/editor/common/editorService.ts b/src/vs/workbench/services/editor/common/editorService.ts index ff132192ed4..8e481661296 100644 --- a/src/vs/workbench/services/editor/common/editorService.ts +++ b/src/vs/workbench/services/editor/common/editorService.ts @@ -5,10 +5,10 @@ import { createDecorator, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IResourceEditorInput, IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor'; -import { IEditorInput, IEditorPane, GroupIdentifier, IEditorInputWithOptions, IUntitledTextResourceEditorInput, IResourceDiffEditorInput, ITextEditorPane, ITextDiffEditorPane, IEditorIdentifier, ISaveOptions, IRevertOptions, EditorsOrder, IVisibleEditorPane } from 'vs/workbench/common/editor'; +import { IEditorInput, IEditorPane, GroupIdentifier, IEditorInputWithOptions, IUntitledTextResourceEditorInput, IResourceDiffEditorInput, ITextEditorPane, ITextDiffEditorPane, IEditorIdentifier, ISaveOptions, IRevertOptions, EditorsOrder, IVisibleEditorPane, IEditorCloseEvent } from 'vs/workbench/common/editor'; import { Event } from 'vs/base/common/event'; import { IEditor, IDiffEditor } from 'vs/editor/common/editorCommon'; -import { IEditorGroup, IEditorReplacement } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorGroup, IEditorReplacement, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; @@ -35,7 +35,7 @@ export interface IOpenEditorOverrideEntry { } export interface IOpenEditorOverrideHandler { - open(editor: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup, id?: string): IOpenEditorOverride | undefined; + open(editor: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup, context: OpenEditorContext): IOpenEditorOverride | undefined; getEditorOverrides?(resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined): IOpenEditorOverrideEntry[]; } @@ -74,7 +74,6 @@ export interface ISaveAllEditorsOptions extends ISaveEditorsOptions, IBaseSaveRe export interface IRevertAllEditorsOptions extends IRevertOptions, IBaseSaveRevertAllEditorOptions { } export interface ICustomEditorInfo { - readonly id: string; readonly displayName: string; readonly providerDisplayName: string; @@ -82,12 +81,13 @@ export interface ICustomEditorInfo { export interface ICustomEditorViewTypesHandler { readonly onDidChangeViewTypes: Event; + getViewTypes(): ICustomEditorInfo[]; } export interface IEditorService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Emitted when the currently active editor changes. @@ -103,6 +103,11 @@ export interface IEditorService { */ readonly onDidVisibleEditorsChange: Event; + /** + * Emitted when an editor is closed. + */ + readonly onDidCloseEditor: Event; + /** * The currently active editor pane or `undefined` if none. The editor pane is * the workbench container for editors of any kind. @@ -240,6 +245,11 @@ export interface IEditorService { */ overrideOpenEditor(handler: IOpenEditorOverrideHandler): IDisposable; + /** + * Register handlers for custom editor view types. + * The handler will provide all available custom editors registered + * and also notify the editor service when a custom editor view type is registered/unregistered. + */ registerCustomEditorViewTypesHandler(source: string, handler: ICustomEditorViewTypesHandler): IDisposable; /** @@ -279,4 +289,13 @@ export interface IEditorService { * @returns `true` if all editors reverted and `false` otherwise. */ revertAll(options?: IRevertAllEditorsOptions): Promise; + + /** + * Track the provided editors until all have been closed. + * + * @param options use `waitForSaved: true` to wait for the resources + * being saved. If auto-save is enabled, it may be possible to close + * an editor while the save continues in the background. + */ + whenClosed(editors: IResourceEditorInput[], options?: { waitForSaved: boolean }): Promise; } diff --git a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts index d929a5c5338..b74c4cb5358 100644 --- a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts @@ -4,8 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { Event } from 'vs/base/common/event'; import { workbenchInstantiationService, registerTestEditor, TestFileEditorInput, TestEditorPart, ITestInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { GroupDirection, GroupsOrder, MergeGroupMode, GroupOrientation, GroupChangeKind, GroupLocation } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { GroupDirection, GroupsOrder, MergeGroupMode, GroupOrientation, GroupChangeKind, GroupLocation, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { EditorOptions, CloseDirection, IEditorPartOptions, EditorsOrder } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; @@ -369,8 +370,9 @@ suite('EditorGroupsService', () => { let activeEditorChangeCounter = 0; let editorDidOpenCounter = 0; - let editorCloseCounter1 = 0; + let editorCloseCounter = 0; let editorPinCounter = 0; + let editorStickyCounter = 0; const editorGroupChangeListener = group.onDidGroupChange(e => { if (e.kind === GroupChangeKind.EDITOR_OPEN) { assert.ok(e.editor); @@ -380,16 +382,19 @@ suite('EditorGroupsService', () => { activeEditorChangeCounter++; } else if (e.kind === GroupChangeKind.EDITOR_CLOSE) { assert.ok(e.editor); - editorCloseCounter1++; + editorCloseCounter++; } else if (e.kind === GroupChangeKind.EDITOR_PIN) { assert.ok(e.editor); editorPinCounter++; + } else if (e.kind === GroupChangeKind.EDITOR_STICKY) { + assert.ok(e.editor); + editorStickyCounter++; } }); - let editorCloseCounter2 = 0; + let editorCloseCounter1 = 0; const editorCloseListener = group.onDidCloseEditor(() => { - editorCloseCounter2++; + editorCloseCounter1++; }); let editorWillCloseCounter = 0; @@ -440,12 +445,20 @@ suite('EditorGroupsService', () => { await group.closeEditor(inputInactive); assert.equal(activeEditorChangeCounter, 3); + assert.equal(editorCloseCounter, 1); assert.equal(editorCloseCounter1, 1); - assert.equal(editorCloseCounter2, 1); assert.equal(editorWillCloseCounter, 1); + assert.ok(inputInactive.gotDisposed); + assert.equal(group.activeEditor, input); + assert.equal(editorStickyCounter, 0); + group.stickEditor(input); + assert.equal(editorStickyCounter, 1); + group.unstickEditor(input); + assert.equal(editorStickyCounter, 2); + editorCloseListener.dispose(); editorWillCloseListener.dispose(); editorWillOpenListener.dispose(); @@ -471,10 +484,36 @@ suite('EditorGroupsService', () => { assert.equal(group.getEditorByIndex(1), inputInactive); await group.closeEditors([input, inputInactive]); + + assert.ok(input.gotDisposed); + assert.ok(inputInactive.gotDisposed); + assert.equal(group.isEmpty, true); part.dispose(); }); + test('closeEditors (one, opened in multiple groups)', async () => { + const [part] = createPart(); + const group = part.activeGroup; + assert.equal(group.isEmpty, true); + + const rightGroup = part.addGroup(group, GroupDirection.RIGHT); + + const input = new TestFileEditorInput(URI.file('foo/bar'), TEST_EDITOR_INPUT_ID); + const inputInactive = new TestFileEditorInput(URI.file('foo/bar/inactive'), TEST_EDITOR_INPUT_ID); + + await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); + await rightGroup.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); + + await rightGroup.closeEditor(input); + + assert.ok(!input.gotDisposed); + + await group.closeEditor(input); + + assert.ok(input.gotDisposed); + }); + test('closeEditors (except one)', async () => { const [part] = createPart(); const group = part.activeGroup; @@ -1021,4 +1060,49 @@ suite('EditorGroupsService', () => { editorGroupChangeListener.dispose(); part.dispose(); }); + + test('moveEditor with context (across groups)', async () => { + const [part] = createPart(); + const group = part.activeGroup; + assert.equal(group.isEmpty, true); + + const rightGroup = part.addGroup(group, GroupDirection.RIGHT); + + const input = new TestFileEditorInput(URI.file('foo/bar'), TEST_EDITOR_INPUT_ID); + const inputInactive = new TestFileEditorInput(URI.file('foo/bar/inactive'), TEST_EDITOR_INPUT_ID); + let firstOpenEditorContext: OpenEditorContext | undefined; + Event.once(group.onWillOpenEditor)(e => { + firstOpenEditorContext = e.context; + }); + await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); + assert.equal(firstOpenEditorContext, undefined); + + const waitForEditorWillOpen = new Promise(c => { + Event.once(rightGroup.onWillOpenEditor)(e => c(e.context)); + }); + + group.moveEditor(inputInactive, rightGroup, { index: 0 }); + const context = await waitForEditorWillOpen; + assert.equal(context, OpenEditorContext.MOVE_EDITOR); + part.dispose(); + }); + + test('copyEditor with context (across groups)', async () => { + const [part] = createPart(); + const group = part.activeGroup; + assert.equal(group.isEmpty, true); + + const rightGroup = part.addGroup(group, GroupDirection.RIGHT); + const input = new TestFileEditorInput(URI.file('foo/bar'), TEST_EDITOR_INPUT_ID); + const inputInactive = new TestFileEditorInput(URI.file('foo/bar/inactive'), TEST_EDITOR_INPUT_ID); + await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); + const waitForEditorWillOpen = new Promise(c => { + Event.once(rightGroup.onWillOpenEditor)(e => c(e.context)); + }); + + group.copyEditor(inputInactive, rightGroup, { index: 0 }); + const context = await waitForEditorWillOpen; + assert.equal(context, OpenEditorContext.COPY_EDITOR); + part.dispose(); + }); }); diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts index 593a47c9fca..bec876bc4d3 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -28,6 +28,7 @@ import { UntitledTextEditorModel } from 'vs/workbench/services/untitled/common/u import { NullFileSystemProvider } from 'vs/platform/files/test/common/nullFileSystemProvider'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; +import { isLinux } from 'vs/base/common/platform'; const TEST_EDITOR_ID = 'MyTestEditorForEditorService'; const TEST_EDITOR_INPUT_ID = 'testEditorInputForEditorService'; @@ -111,7 +112,7 @@ suite('EditorService', () => { assert.equal(visibleEditorChangeEventCounter, 1); // Close input - await editor!.group!.closeEditor(input); + await editor?.group?.closeEditor(input); assert.equal(0, service.count); assert.equal(0, service.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE).length); @@ -255,13 +256,13 @@ suite('EditorService', () => { const fileEditorInput1Again = service.createEditorInput({ resource: fileResource1 }); assert.equal(fileEditorInput1Again, fileEditorInput1); - fileEditorInput1Again!.dispose(); + fileEditorInput1Again.dispose(); - assert.ok(fileEditorInput1!.isDisposed()); + assert.ok(fileEditorInput1.isDisposed()); const fileEditorInput1AgainAndAgain = service.createEditorInput({ resource: fileResource1 }); assert.notEqual(fileEditorInput1AgainAndAgain, fileEditorInput1); - assert.ok(!fileEditorInput1AgainAndAgain!.isDisposed()); + assert.ok(!fileEditorInput1AgainAndAgain.isDisposed()); // Cached Input (Resource) const resource1 = URI.from({ scheme: 'custom', path: '/foo/bar/cache1.js' }); @@ -277,13 +278,13 @@ suite('EditorService', () => { const input1Again = service.createEditorInput({ resource: resource1 }); assert.equal(input1Again, input1); - input1Again!.dispose(); + input1Again.dispose(); - assert.ok(input1!.isDisposed()); + assert.ok(input1.isDisposed()); const input1AgainAndAgain = service.createEditorInput({ resource: resource1 }); assert.notEqual(input1AgainAndAgain, input1); - assert.ok(!input1AgainAndAgain!.isDisposed()); + assert.ok(!input1AgainAndAgain.isDisposed()); }); test('createEditorInput', async function () { @@ -301,6 +302,18 @@ suite('EditorService', () => { let contentInput = input; assert.strictEqual(contentInput.resource.fsPath, toResource.call(this, '/index.html').fsPath); + // Untyped Input (file casing) + input = service.createEditorInput({ resource: toResource.call(this, '/index.html') }); + let inputDifferentCase = service.createEditorInput({ resource: toResource.call(this, '/INDEX.html') }); + + if (!isLinux) { + assert.equal(input, inputDifferentCase); + assert.equal(input.resource?.toString(), inputDifferentCase.resource?.toString()); + } else { + assert.notEqual(input, inputDifferentCase); + assert.notEqual(input.resource?.toString(), inputDifferentCase.resource?.toString()); + } + // Typed Input assert.equal(service.createEditorInput(input), input); assert.equal(service.createEditorInput({ editor: input }), input); @@ -331,7 +344,7 @@ suite('EditorService', () => { input = service.createEditorInput({ contents: 'Hello Untitled', options: { selection: { startLineNumber: 1, startColumn: 1 } } }); assert(input instanceof UntitledTextEditorInput); let model = await input.resolve() as UntitledTextEditorModel; - assert.equal(model.textEditorModel!.getValue(), 'Hello Untitled'); + assert.equal(model.textEditorModel?.getValue(), 'Hello Untitled'); // Untyped Input (untitled with mode) input = service.createEditorInput({ mode, options: { selection: { startLineNumber: 1, startColumn: 1 } } }); @@ -364,8 +377,8 @@ suite('EditorService', () => { // Untyped Input (diff) input = service.createEditorInput({ - leftResource: toResource.call(this, '/master.html'), - rightResource: toResource.call(this, '/detail.html') + leftResource: toResource.call(this, '/primary.html'), + rightResource: toResource.call(this, '/secondary.html') }); assert(input instanceof DiffEditorInput); }); @@ -390,7 +403,7 @@ suite('EditorService', () => { const ed = instantiationService.createInstance(MyEditor, 'my.editor'); - const inp = instantiationService.createInstance(ResourceEditorInput, 'name', 'description', URI.parse('my://resource-delegate'), undefined); + const inp = instantiationService.createInstance(ResourceEditorInput, URI.parse('my://resource-delegate'), 'name', 'description', undefined); const delegate = instantiationService.createInstance(DelegatingEditorService, async (delegate, group, input) => { assert.strictEqual(input, inp); @@ -446,13 +459,13 @@ suite('EditorService', () => { assert.equal(part.activeGroup, rootGroup); assert.equal(part.count, 2); - assert.equal(editor!.group, part.groups[1]); + assert.equal(editor?.group, part.groups[1]); // Open to the side uses existing neighbour group if any editor = await service.openEditor(input2, { pinned: true, preserveFocus: true }, SIDE_GROUP); assert.equal(part.activeGroup, rootGroup); assert.equal(part.count, 2); - assert.equal(editor!.group, part.groups[1]); + assert.equal(editor?.group, part.groups[1]); part.dispose(); }); @@ -469,7 +482,7 @@ suite('EditorService', () => { await service.openEditor(input1, { pinned: true }, rootGroup); let editor = await service.openEditor(input2, { pinned: true, preserveFocus: true, activation: EditorActivation.ACTIVATE }, SIDE_GROUP); - const sideGroup = editor!.group; + const sideGroup = editor?.group; assert.equal(part.activeGroup, sideGroup); @@ -527,7 +540,7 @@ suite('EditorService', () => { // 1.) open, open same, open other, close let editor = await service.openEditor(input, { pinned: true }); - const group = editor!.group!; + const group = editor?.group!; assertActiveEditorChangedEvent(true); assertVisibleEditorsChangedEvent(true); @@ -1059,4 +1072,24 @@ suite('EditorService', () => { handler.dispose(); part.dispose(); }); + + test('whenClosed', async function () { + const [part, service] = createEditorService(); + + const input1 = new TestFileEditorInput(URI.parse('file://resource1'), TEST_EDITOR_INPUT_ID); + const input2 = new TestFileEditorInput(URI.parse('file://resource2'), TEST_EDITOR_INPUT_ID); + + await part.whenRestored; + + const editor = await service.openEditor(input1, { pinned: true }); + await service.openEditor(input2, { pinned: true }); + + const whenClosed = service.whenClosed([{ resource: input1.resource }, { resource: input2.resource }]); + + editor?.group?.closeAllEditors(); + + await whenClosed; + + part.dispose(); + }); }); diff --git a/src/vs/workbench/services/electron/electron-browser/electronService.ts b/src/vs/workbench/services/electron/electron-browser/electronService.ts deleted file mode 100644 index ba25ebd672d..00000000000 --- a/src/vs/workbench/services/electron/electron-browser/electronService.ts +++ /dev/null @@ -1,25 +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 { IElectronService } from 'vs/platform/electron/node/electron'; -import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; -import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; - -export class ElectronService { - - _serviceBrand: undefined; - - constructor( - @IMainProcessService mainProcessService: IMainProcessService, - @IWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService - ) { - return createChannelSender(mainProcessService.getChannel('electron'), { context: environmentService.configuration.windowId }); - } -} - -registerSingleton(IElectronService, ElectronService, true); diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index 7ed6e9e21ad..ba2701ec54d 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -14,6 +14,8 @@ import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; import product from 'vs/platform/product/common/product'; import { memoize } from 'vs/base/common/decorators'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { LIGHT } from 'vs/platform/theme/common/themeService'; +import { parseLineAndColumnAware } from 'vs/base/common/extpath'; export class BrowserEnvironmentConfiguration implements IEnvironmentConfiguration { @@ -37,7 +39,20 @@ export class BrowserEnvironmentConfiguration implements IEnvironmentConfiguratio if (this.payload) { const fileToOpen = this.payload.get('openFile'); if (fileToOpen) { - return [{ fileUri: URI.parse(fileToOpen) }]; + const fileUri = URI.parse(fileToOpen); + + // Support: --goto parameter to open on line/col + if (this.payload.has('gotoLineMode')) { + const pathColumnAware = parseLineAndColumnAware(fileUri.path); + + return [{ + fileUri: fileUri.with({ path: pathColumnAware.path }), + lineNumber: pathColumnAware.line, + columnNumber: pathColumnAware.column + }]; + } + + return [{ fileUri }]; } } @@ -47,12 +62,12 @@ export class BrowserEnvironmentConfiguration implements IEnvironmentConfiguratio @memoize get filesToDiff(): IPath[] | undefined { if (this.payload) { - const fileToDiffDetail = this.payload.get('diffFileDetail'); - const fileToDiffMaster = this.payload.get('diffFileMaster'); - if (fileToDiffDetail && fileToDiffMaster) { + const fileToDiffPrimary = this.payload.get('diffFilePrimary'); + const fileToDiffSecondary = this.payload.get('diffFileSecondary'); + if (fileToDiffPrimary && fileToDiffSecondary) { return [ - { fileUri: URI.parse(fileToDiffDetail) }, - { fileUri: URI.parse(fileToDiffMaster) } + { fileUri: URI.parse(fileToDiffSecondary) }, + { fileUri: URI.parse(fileToDiffPrimary) } ]; } } @@ -63,6 +78,10 @@ export class BrowserEnvironmentConfiguration implements IEnvironmentConfiguratio get highContrast() { return false; // could investigate to detect high contrast theme automatically } + + get defaultThemeType() { + return LIGHT; + } } interface IBrowserWorkbenchEnvironmentConstructionOptions extends IWorkbenchConstructionOptions { @@ -80,7 +99,7 @@ interface IExtensionHostDebugEnvironment { export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironmentService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _configuration: IEnvironmentConfiguration | undefined = undefined; get configuration(): IEnvironmentConfiguration { @@ -114,6 +133,12 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment @memoize get snippetsHome(): URI { return joinPath(this.userRoamingDataHome, 'snippets'); } + @memoize + get globalStorageHome(): URI { return URI.joinPath(this.userRoamingDataHome, 'globalStorage'); } + + @memoize + get workspaceStorageHome(): URI { return URI.joinPath(this.userRoamingDataHome, 'workspaceStorage'); } + /* * In Web every workspace can potentially have scoped user-data and/or extensions and if Sync state is shared then it can make * Sync error prone - say removing extensions from another workspace. Hence scope Sync state per workspace. @@ -189,10 +214,14 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment get disableExtensions() { return this.payload?.get('disableExtensions') === 'true'; } + private get webviewEndpoint(): string { + // TODO@matt: get fallback from product.json + return this.options.webviewEndpoint || 'https://{{uuid}}.vscode-webview-test.com/{{commit}}'; + } + @memoize get webviewExternalEndpoint(): string { - // TODO@matt: get fallback from product.json - return (this.options.webviewEndpoint || 'https://{{uuid}}.vscode-webview-test.com/{{commit}}').replace('{{commit}}', product.commit || '0d728c31ebdf03869d2687d9be0b017667c9ff37'); + return (this.webviewEndpoint).replace('{{commit}}', product.commit || '0d728c31ebdf03869d2687d9be0b017667c9ff37'); } @memoize @@ -202,7 +231,8 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment @memoize get webviewCspSource(): string { - return this.webviewExternalEndpoint.replace('{{uuid}}', '*'); + const uri = URI.parse(this.webviewEndpoint.replace('{{uuid}}', '*')); + return `${uri.scheme}://${uri.authority}`; } get disableTelemetry(): boolean { return false; } @@ -250,6 +280,9 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment extensionHostDebugEnvironment.params.port = parseInt(value); extensionHostDebugEnvironment.params.break = true; break; + case 'inspect-extensions': + extensionHostDebugEnvironment.params.port = parseInt(value); + break; case 'enableProposedApi': extensionHostDebugEnvironment.extensionEnabledProposedApi = []; break; diff --git a/src/vs/workbench/services/environment/common/environmentService.ts b/src/vs/workbench/services/environment/common/environmentService.ts index bc19c3722ac..7d9a374ff8a 100644 --- a/src/vs/workbench/services/environment/common/environmentService.ts +++ b/src/vs/workbench/services/environment/common/environmentService.ts @@ -17,7 +17,7 @@ export interface IEnvironmentConfiguration extends IWindowConfiguration { export interface IWorkbenchEnvironmentService extends IEnvironmentService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly configuration: IEnvironmentConfiguration; diff --git a/src/vs/workbench/services/environment/electron-browser/environmentService.ts b/src/vs/workbench/services/environment/electron-browser/environmentService.ts index 158e03e3597..20da095a459 100644 --- a/src/vs/workbench/services/environment/electron-browser/environmentService.ts +++ b/src/vs/workbench/services/environment/electron-browser/environmentService.ts @@ -24,15 +24,13 @@ export interface INativeWorkbenchEnvironmentService extends IWorkbenchEnvironmen readonly log?: string; readonly extHostLogsPath: URI; - - readonly userHome: URI; } export interface INativeEnvironmentConfiguration extends IEnvironmentConfiguration, INativeWindowConfiguration { } export class NativeWorkbenchEnvironmentService extends EnvironmentService implements INativeWorkbenchEnvironmentService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; @memoize get webviewExternalEndpoint(): string { @@ -42,10 +40,10 @@ export class NativeWorkbenchEnvironmentService extends EnvironmentService implem } @memoize - get webviewResourceRoot(): string { return 'vscode-resource://{{resource}}'; } + get webviewResourceRoot(): string { return `${Schemas.vscodeWebviewResource}://{{uuid}}/{{resource}}`; } @memoize - get webviewCspSource(): string { return 'vscode-resource:'; } + get webviewCspSource(): string { return `${Schemas.vscodeWebviewResource}:`; } @memoize get userRoamingDataHome(): URI { return this.appSettingsHome.with({ scheme: Schemas.userData }); } diff --git a/src/vs/workbench/services/experiment/common/experimentService.ts b/src/vs/workbench/services/experiment/common/experimentService.ts new file mode 100644 index 00000000000..758c525e263 --- /dev/null +++ b/src/vs/workbench/services/experiment/common/experimentService.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const ITASExperimentService = createDecorator('TASExperimentService'); + +export interface ITASExperimentService { + readonly _serviceBrand: undefined; + getTreatment(name: string): Promise; +} diff --git a/src/vs/workbench/services/experiment/electron-browser/experimentService.ts b/src/vs/workbench/services/experiment/electron-browser/experimentService.ts new file mode 100644 index 00000000000..966ea7736f8 --- /dev/null +++ b/src/vs/workbench/services/experiment/electron-browser/experimentService.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 * as platform from 'vs/base/common/platform'; +import { IKeyValueStorage, IExperimentationTelemetry, IExperimentationFilterProvider, ExperimentationService as TASClient } from 'tas-client'; +import { MementoObject, Memento } from 'vs/workbench/common/memento'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { ITelemetryData } from 'vs/base/common/actions'; +import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; + +const storageKey = 'VSCode.ABExp.FeatureData'; +const refetchInterval = 1000 * 60 * 30; // By default it's set up to 30 minutes. + +class MementoKeyValueStorage implements IKeyValueStorage { + constructor(private mementoObj: MementoObject) { } + + async getValue(key: string, defaultValue?: T | undefined): Promise { + const value = await this.mementoObj[key]; + return value || defaultValue; + } + + setValue(key: string, value: T): void { + this.mementoObj[key] = value; + } +} + +class ExperimentServiceTelemetry implements IExperimentationTelemetry { + constructor(private telemetryService: ITelemetryService) { } + + // __GDPR__COMMON__ "VSCode.ABExp.Features" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + // __GDPR__COMMON__ "abexp.assignmentcontext" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + setSharedProperty(name: string, value: string): void { + this.telemetryService.setExperimentProperty(name, value); + } + + postEvent(eventName: string, props: Map): void { + const data: ITelemetryData = {}; + for (const [key, value] of props.entries()) { + data[key] = value; + } + + /* __GDPR__ + "query-expfeature" : { + "ABExp.queriedFeature": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog(eventName, data); + } +} + +class ExperimentServiceFilterProvider implements IExperimentationFilterProvider { + constructor( + private version: string, + private appName: string, + private machineId: string, + private targetPopulation: TargetPopulation + ) { } + + getFilterValue(filter: string): string | null { + switch (filter) { + case Filters.ApplicationVersion: + return this.version; // productService.version + case Filters.Build: + return this.appName; // productService.nameLong + case Filters.ClientId: + return this.machineId; + case Filters.Language: + return platform.language; + case Filters.ExtensionName: + return 'vscode-core'; // always return vscode-core for exp service + case Filters.TargetPopulation: + return this.targetPopulation; + default: + return ''; + } + } + + getFilters(): Map { + let filters: Map = new Map(); + let filterValues = Object.values(Filters); + for (let value of filterValues) { + filters.set(value, this.getFilterValue(value)); + } + + return filters; + } +} + +/* +Based upon the official VSCode currently existing filters in the +ExP backend for the VSCode cluster. +https://experimentation.visualstudio.com/Analysis%20and%20Experimentation/_git/AnE.ExP.TAS.TachyonHost.Configuration?path=%2FConfigurations%2Fvscode%2Fvscode.json&version=GBmaster +"X-MSEdge-Market": "detection.market", +"X-FD-Corpnet": "detection.corpnet", +"X-VSCode–AppVersion": "appversion", +"X-VSCode-Build": "build", +"X-MSEdge-ClientId": "clientid", +"X-VSCode-ExtensionName": "extensionname", +"X-VSCode-TargetPopulation": "targetpopulation", +"X-VSCode-Language": "language" +*/ + +enum Filters { + /** + * The market in which the extension is distributed. + */ + Market = 'X-MSEdge-Market', + + /** + * The corporation network. + */ + CorpNet = 'X-FD-Corpnet', + + /** + * Version of the application which uses experimentation service. + */ + ApplicationVersion = 'X-VSCode-AppVersion', + + /** + * Insiders vs Stable. + */ + Build = 'X-VSCode-Build', + + /** + * Client Id which is used as primary unit for the experimentation. + */ + ClientId = 'X-MSEdge-ClientId', + + /** + * Extension header. + */ + ExtensionName = 'X-VSCode-ExtensionName', + + /** + * The language in use by VS Code + */ + Language = 'X-VSCode-Language', + + /** + * The target population. + * This is used to separate internal, early preview, GA, etc. + */ + TargetPopulation = 'X-VSCode-TargetPopulation', +} + +enum TargetPopulation { + Team = 'team', + Internal = 'internal', + Insiders = 'insider', + Public = 'public', +} + +export class ExperimentService implements ITASExperimentService { + _serviceBrand: undefined; + private tasClient: Promise | undefined; + private static MEMENTO_ID = 'experiment.service.memento'; + + constructor( + @IProductService private productService: IProductService, + @ITelemetryService private telemetryService: ITelemetryService, + @IStorageService private storageService: IStorageService + ) { + + if (this.productService.tasConfig) { + this.tasClient = this.setupTASClient(); + } + } + + async getTreatment(name: string): Promise { + if (!this.tasClient) { + return undefined; + } + + return (await this.tasClient).getTreatmentVariable('vscode', name); + } + + private async setupTASClient(): Promise { + const telemetryInfo = await this.telemetryService.getTelemetryInfo(); + const targetPopulation = telemetryInfo.msftInternal ? TargetPopulation.Internal : (this.productService.quality === 'stable' ? TargetPopulation.Public : TargetPopulation.Insiders); + const machineId = telemetryInfo.machineId; + const filterProvider = new ExperimentServiceFilterProvider( + this.productService.version, + this.productService.nameLong, + machineId, + targetPopulation + ); + + const memento = new Memento(ExperimentService.MEMENTO_ID, this.storageService); + const keyValueStorage = new MementoKeyValueStorage(memento.getMemento(StorageScope.GLOBAL)); + + const telemetry = new ExperimentServiceTelemetry(this.telemetryService); + + const tasConfig = this.productService.tasConfig!; + const tasClient = new TASClient({ + filterProviders: [filterProvider], + telemetry: telemetry, + storageKey: storageKey, + keyValueStorage: keyValueStorage, + featuresTelemetryPropertyName: tasConfig.featuresTelemetryPropertyName, + assignmentContextTelemetryPropertyName: tasConfig.assignmentContextTelemetryPropertyName, + telemetryEventName: tasConfig.telemetryEventName, + endpoint: tasConfig.endpoint, + refetchInterval: refetchInterval, + }); + + await tasClient.initializePromise; + return tasClient; + } +} + +registerSingleton(ITASExperimentService, ExperimentService, false); + diff --git a/src/vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.ts b/src/vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.ts new file mode 100644 index 00000000000..ef657aa2114 --- /dev/null +++ b/src/vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.ts @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IBuiltinExtensionsScannerService, IScannedExtension, ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; +import { isWeb } from 'vs/base/common/platform'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { URI } from 'vs/base/common/uri'; +import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; + +interface IScannedBuiltinExtension { + extensionPath: string; + packageJSON: IExtensionManifest; + packageNLS?: any; + readmePath?: string; + changelogPath?: string; +} + +export class BuiltinExtensionsScannerService implements IBuiltinExtensionsScannerService { + + declare readonly _serviceBrand: undefined; + + private readonly builtinExtensions: IScannedExtension[] = []; + + constructor( + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IUriIdentityService uriIdentityService: IUriIdentityService, + ) { + + const builtinExtensionsServiceUrl = environmentService.options?.builtinExtensionsServiceUrl ? URI.parse(environmentService.options?.builtinExtensionsServiceUrl) : undefined; + if (isWeb && builtinExtensionsServiceUrl) { + + let scannedBuiltinExtensions: IScannedBuiltinExtension[] = []; + + if (environmentService.isBuilt) { + // Built time configuration (do NOT modify) + scannedBuiltinExtensions = [/*BUILD->INSERT_BUILTIN_EXTENSIONS*/]; + } else { + // Find builtin extensions by checking for DOM + const builtinExtensionsElement = document.getElementById('vscode-workbench-builtin-extensions'); + const builtinExtensionsElementAttribute = builtinExtensionsElement ? builtinExtensionsElement.getAttribute('data-settings') : undefined; + if (builtinExtensionsElementAttribute) { + try { + scannedBuiltinExtensions = JSON.parse(builtinExtensionsElementAttribute); + } catch (error) { /* ignore error*/ } + } + } + + this.builtinExtensions = scannedBuiltinExtensions.map(e => ({ + identifier: { id: getGalleryExtensionId(e.packageJSON.publisher, e.packageJSON.name) }, + location: uriIdentityService.extUri.joinPath(builtinExtensionsServiceUrl!, e.extensionPath), + type: ExtensionType.System, + packageJSON: e.packageJSON, + packageNLS: e.packageNLS, + readmeUrl: e.readmePath ? uriIdentityService.extUri.joinPath(builtinExtensionsServiceUrl!, e.readmePath) : undefined, + changelogUrl: e.changelogPath ? uriIdentityService.extUri.joinPath(builtinExtensionsServiceUrl!, e.changelogPath) : undefined, + })); + } + } + + async scanBuiltinExtensions(): Promise { + if (isWeb) { + return this.builtinExtensions; + } + throw new Error('not supported'); + } +} + +registerSingleton(IBuiltinExtensionsScannerService, BuiltinExtensionsScannerService); diff --git a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts index cfac383e8af..c28b1477400 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts @@ -18,12 +18,13 @@ import { getExtensionKind } from 'vs/workbench/services/extensions/common/extens import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IProductService } from 'vs/platform/product/common/productService'; import { StorageManager } from 'vs/platform/extensionManagement/common/extensionEnablementService'; +import { webWorkerExtHostConfig } from 'vs/workbench/services/extensions/common/extensions'; const SOURCE = 'IWorkbenchExtensionEnablementService'; export class ExtensionEnablementService extends Disposable implements IWorkbenchExtensionEnablementService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _onEnablementChanged = new Emitter(); public readonly onEnablementChanged: Event = this._onEnablementChanged.event; @@ -121,6 +122,10 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench return enablementState === EnablementState.EnabledWorkspace || enablementState === EnablementState.EnabledGlobally; } + isDisabledGlobally(extension: IExtension): boolean { + return this._isDisabledGlobally(extension.identifier); + } + private _isDisabledInEnv(extension: IExtension): boolean { if (this.allUserExtensionsDisabled) { return extension.type === ExtensionType.User; @@ -133,8 +138,8 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench } private _isDisabledByExtensionKind(extension: IExtension): boolean { - if (this.extensionManagementServerService.remoteExtensionManagementServer) { - const server = this.extensionManagementServerService.getExtensionManagementServer(extension.location); + if (this.extensionManagementServerService.remoteExtensionManagementServer || this.extensionManagementServerService.webExtensionManagementServer) { + const server = this.extensionManagementServerService.getExtensionManagementServer(extension); for (const extensionKind of getExtensionKind(extension.manifest, this.productService, this.configurationService)) { if (extensionKind === 'ui') { if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.localExtensionManagementServer === server) { @@ -147,8 +152,13 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench } } if (extensionKind === 'web') { - // Web extensions are not yet supported to be disabled by kind. Enable them always on web. + const enableLocalWebWorker = this.configurationService.getValue(webWorkerExtHostConfig); + if (enableLocalWebWorker) { + // Web extensions are enabled on all configurations + return false; + } if (this.extensionManagementServerService.localExtensionManagementServer === null) { + // Web extensions run only in the web return false; } } @@ -168,12 +178,16 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench return EnablementState.DisabledWorkspace; } } - if (this.globalExtensionEnablementService.getDisabledExtensions().filter(e => areSameExtensions(e, identifier))[0]) { + if (this._isDisabledGlobally(identifier)) { return EnablementState.DisabledGlobally; } return EnablementState.EnabledGlobally; } + private _isDisabledGlobally(identifier: IExtensionIdentifier): boolean { + return this.globalExtensionEnablementService.getDisabledExtensions().some(e => areSameExtensions(e, identifier)); + } + private _enableExtension(identifier: IExtensionIdentifier): Promise { this._removeFromWorkspaceDisabledExtensions(identifier); this._removeFromWorkspaceEnabledExtensions(identifier); diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts index 48f5782881a..f5e29e52322 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts @@ -6,24 +6,25 @@ import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; -import { IExtension } from 'vs/platform/extensions/common/extensions'; -import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtension, IScannedExtension, ExtensionType, ITranslatedScannedExtension } from 'vs/platform/extensions/common/extensions'; +import { IExtensionManagementService, IGalleryExtension, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkspace, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IStringDictionary } from 'vs/base/common/collections'; export const IExtensionManagementServerService = createDecorator('extensionManagementServerService'); export interface IExtensionManagementServer { - extensionManagementService: IExtensionManagementService; - authority: string; + id: string; label: string; + extensionManagementService: IExtensionManagementService; } export interface IExtensionManagementServerService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly localExtensionManagementServer: IExtensionManagementServer | null; readonly remoteExtensionManagementServer: IExtensionManagementServer | null; - getExtensionManagementServer(location: URI): IExtensionManagementServer | null; + readonly webExtensionManagementServer: IExtensionManagementServer | null; + getExtensionManagementServer(extension: IExtension): IExtensionManagementServer | null; } export const enum EnablementState { @@ -38,7 +39,7 @@ export const enum EnablementState { export const IWorkbenchExtensionEnablementService = createDecorator('extensionEnablementService'); export interface IWorkbenchExtensionEnablementService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly allUserExtensionsDisabled: boolean; @@ -62,6 +63,14 @@ export interface IWorkbenchExtensionEnablementService { */ isEnabled(extension: IExtension): boolean; + /** + * Returns `true` if the given extension identifier is disabled globally. + * Extensions can be disabled globally or in workspace or both. + * If an extension is disabled in both then enablement state shows only workspace. + * This will + */ + isDisabledGlobally(extension: IExtension): boolean; + /** * Enable or disable the given extension. * if `workspace` is `true` then enablement is done for workspace, otherwise globally. @@ -115,7 +124,7 @@ export interface IExtensionRecommendationReson { export const IExtensionRecommendationsService = createDecorator('extensionRecommendationsService'); export interface IExtensionRecommendationsService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; getAllRecommendationsWithReason(): IStringDictionary; getFileBasedRecommendations(): IExtensionRecommendation[]; @@ -128,3 +137,12 @@ export interface IExtensionRecommendationsService { getIgnoredRecommendations(): ReadonlyArray; onRecommendationChange: Event; } + +export const IWebExtensionsScannerService = createDecorator('IWebExtensionsScannerService'); +export interface IWebExtensionsScannerService { + readonly _serviceBrand: undefined; + scanExtensions(type?: ExtensionType): Promise; + scanAndTranslateExtensions(type?: ExtensionType): Promise; + addExtension(galleryExtension: IGalleryExtension): Promise; + removeExtension(identifier: IExtensionIdentifier, version?: string): Promise; +} diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts index 5507e35d911..388d6996081 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { URI } from 'vs/base/common/uri'; import { IExtensionManagementServer, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; @@ -12,34 +11,51 @@ import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILabelService } from 'vs/platform/label/common/label'; +import { isWeb } from 'vs/base/common/platform'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { WebExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/webExtensionManagementService'; +import { IExtension } from 'vs/platform/extensions/common/extensions'; export class ExtensionManagementServerService implements IExtensionManagementServerService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; readonly localExtensionManagementServer: IExtensionManagementServer | null = null; readonly remoteExtensionManagementServer: IExtensionManagementServer | null = null; + readonly webExtensionManagementServer: IExtensionManagementServer | null = null; constructor( @IRemoteAgentService remoteAgentService: IRemoteAgentService, @ILabelService labelService: ILabelService, + @IInstantiationService instantiationService: IInstantiationService, ) { const remoteAgentConnection = remoteAgentService.getConnection(); if (remoteAgentConnection) { const extensionManagementService = new ExtensionManagementChannelClient(remoteAgentConnection!.getChannel('extensions')); this.remoteExtensionManagementServer = { - authority: remoteAgentConnection.remoteAuthority, + id: 'remote', extensionManagementService, get label() { return labelService.getHostLabel(REMOTE_HOST_SCHEME, remoteAgentConnection!.remoteAuthority) || localize('remote', "Remote"); } }; } + if (isWeb) { + const extensionManagementService = instantiationService.createInstance(WebExtensionManagementService); + this.webExtensionManagementServer = { + id: 'web', + extensionManagementService, + label: localize('web', "Web") + }; + } } - getExtensionManagementServer(location: URI): IExtensionManagementServer | null { - if (location.scheme === REMOTE_HOST_SCHEME) { - return this.remoteExtensionManagementServer; + getExtensionManagementServer(extension: IExtension): IExtensionManagementServer { + if (extension.location.scheme === REMOTE_HOST_SCHEME) { + return this.remoteExtensionManagementServer!; } - return null; + if (this.webExtensionManagementServer) { + return this.webExtensionManagementServer; + } + throw new Error(`Invalid Extension ${extension.location}`); } } diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index 9f8c6ac6f5e..33eb56db3c2 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -15,14 +15,15 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { CancellationToken } from 'vs/base/common/cancellation'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { localize } from 'vs/nls'; -import { prefersExecuteOnUI, canExecuteOnWorkspace } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { prefersExecuteOnUI, canExecuteOnWorkspace, prefersExecuteOnWorkspace, canExecuteOnUI, prefersExecuteOnWeb, canExecuteOnWeb } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { IProductService } from 'vs/platform/product/common/productService'; import { Schemas } from 'vs/base/common/network'; import { IDownloadService } from 'vs/platform/download/common/download'; +import { flatten } from 'vs/base/common/arrays'; export class ExtensionManagementService extends Disposable implements IExtensionManagementService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; readonly onInstallExtension: Event; readonly onDidInstallExtension: Event; @@ -45,6 +46,9 @@ export class ExtensionManagementService extends Disposable implements IExtension if (this.extensionManagementServerService.remoteExtensionManagementServer) { this.servers.push(this.extensionManagementServerService.remoteExtensionManagementServer); } + if (this.extensionManagementServerService.webExtensionManagementServer) { + this.servers.push(this.extensionManagementServerService.webExtensionManagementServer); + } this.onInstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(server.extensionManagementService.onInstallExtension); return emitter; }, new EventMultiplexer())).event; this.onDidInstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(server.extensionManagementService.onDidInstallExtension); return emitter; }, new EventMultiplexer())).event; @@ -52,11 +56,9 @@ export class ExtensionManagementService extends Disposable implements IExtension this.onDidUninstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(server.extensionManagementService.onDidUninstallExtension); return emitter; }, new EventMultiplexer())).event; } - getInstalled(type?: ExtensionType): Promise { - const installedExtensions: ILocalExtension[] = []; - return Promise.all(this.servers.map(({ extensionManagementService }) => extensionManagementService.getInstalled(type).then(extensions => installedExtensions.push(...extensions)))) - .then(_ => installedExtensions) - .catch(e => installedExtensions); + async getInstalled(type?: ExtensionType): Promise { + const result = await Promise.all(this.servers.map(({ extensionManagementService }) => extensionManagementService.getInstalled(type))); + return flatten(result); } async uninstall(extension: ILocalExtension): Promise { @@ -64,7 +66,7 @@ export class ExtensionManagementService extends Disposable implements IExtension if (!server) { return Promise.reject(`Invalid location ${extension.location.toString()}`); } - if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) { + if (this.servers.length > 1) { if (isLanguagePackExtension(extension.manifest)) { return this.uninstallEverywhere(extension); } @@ -79,12 +81,14 @@ export class ExtensionManagementService extends Disposable implements IExtension return Promise.reject(`Invalid location ${extension.location.toString()}`); } const promise = server.extensionManagementService.uninstall(extension); - const anotherServer: IExtensionManagementServer | null = server === this.extensionManagementServerService.localExtensionManagementServer ? this.extensionManagementServerService.remoteExtensionManagementServer! : this.extensionManagementServerService.localExtensionManagementServer; - if (anotherServer) { - const installed = await anotherServer.extensionManagementService.getInstalled(ExtensionType.User); - extension = installed.filter(i => areSameExtensions(i.identifier, extension.identifier))[0]; - if (extension) { - await anotherServer.extensionManagementService.uninstall(extension); + const otherServers: IExtensionManagementServer[] = this.servers.filter(s => s !== server); + if (otherServers.length) { + for (const otherServer of otherServers) { + const installed = await otherServer.extensionManagementService.getInstalled(ExtensionType.User); + extension = installed.filter(i => areSameExtensions(i.identifier, extension.identifier))[0]; + if (extension) { + await otherServer.extensionManagementService.uninstall(extension); + } } } return promise; @@ -140,8 +144,11 @@ export class ExtensionManagementService extends Disposable implements IExtension return Promise.reject(`Invalid location ${extension.location.toString()}`); } - unzip(zipLocation: URI, type: ExtensionType): Promise { - return Promise.all(this.servers.map(({ extensionManagementService }) => extensionManagementService.unzip(zipLocation, type))).then(([extensionIdentifier]) => extensionIdentifier); + unzip(zipLocation: URI): Promise { + return Promise.all(this.servers + // Filter out web server + .filter(server => server !== this.extensionManagementServerService.webExtensionManagementServer) + .map(({ extensionManagementService }) => extensionManagementService.unzip(zipLocation))).then(([extensionIdentifier]) => extensionIdentifier); } async install(vsix: URI): Promise { @@ -149,7 +156,7 @@ export class ExtensionManagementService extends Disposable implements IExtension const manifest = await this.getManifest(vsix); if (isLanguagePackExtension(manifest)) { // Install on both servers - const [local] = await Promise.all(this.servers.map(server => this.installVSIX(vsix, server))); + const [local] = await Promise.all([this.extensionManagementServerService.localExtensionManagementServer, this.extensionManagementServerService.remoteExtensionManagementServer].map(server => this.installVSIX(vsix, server))); return local; } if (prefersExecuteOnUI(manifest, this.productService, this.configurationService)) { @@ -183,39 +190,61 @@ export class ExtensionManagementService extends Disposable implements IExtension } async installFromGallery(gallery: IGalleryExtension): Promise { - if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) { - const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None); - if (manifest) { - if (isLanguagePackExtension(manifest)) { - // Install on both servers - return Promise.all(this.servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(([local]) => local); - } - if (prefersExecuteOnUI(manifest, this.productService, this.configurationService)) { - // Install only on local server - return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery); - } - // Install only on remote server - return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(gallery); - } else { - return Promise.reject(localize('Manifest is not found', "Installing Extension {0} failed: Manifest is not found.", gallery.displayName || gallery.name)); - } - } - if (this.extensionManagementServerService.localExtensionManagementServer) { + + // Only local server, install without any checks + if (this.servers.length === 1 && this.extensionManagementServerService.localExtensionManagementServer) { return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery); } - if (this.extensionManagementServerService.remoteExtensionManagementServer) { - const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None); - if (!manifest) { - return Promise.reject(localize('Manifest is not found', "Installing Extension {0} failed: Manifest is not found.", gallery.displayName || gallery.name)); - } - if (!isLanguagePackExtension(manifest) && !canExecuteOnWorkspace(manifest, this.productService, this.configurationService)) { - const error = new Error(localize('cannot be installed', "Cannot install '{0}' because this extension has defined that it cannot run on the remote server.", gallery.displayName || gallery.name)); - error.name = INSTALL_ERROR_NOT_SUPPORTED; - return Promise.reject(error); - } + + const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None); + if (!manifest) { + return Promise.reject(localize('Manifest is not found', "Installing Extension {0} failed: Manifest is not found.", gallery.displayName || gallery.name)); + } + + // Install Language pack on all servers + if (isLanguagePackExtension(manifest)) { + return Promise.all(this.servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(([local]) => local); + } + + // 1. Install on preferred location + + // Install UI preferred extension on local server + if (prefersExecuteOnUI(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.localExtensionManagementServer) { + return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery); + } + // Install Workspace preferred extension on remote server + if (prefersExecuteOnWorkspace(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.remoteExtensionManagementServer) { return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(gallery); } - return Promise.reject('No Servers to Install'); + // Install Web preferred extension on web server + if (prefersExecuteOnWeb(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.webExtensionManagementServer) { + return this.extensionManagementServerService.webExtensionManagementServer.extensionManagementService.installFromGallery(gallery); + } + + // 2. Install on supported location + + // Install UI supported extension on local server + if (canExecuteOnUI(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.localExtensionManagementServer) { + return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery); + } + // Install Workspace supported extension on remote server + if (canExecuteOnWorkspace(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.remoteExtensionManagementServer) { + return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(gallery); + } + // Install Web supported extension on web server + if (canExecuteOnWeb(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.webExtensionManagementServer) { + return this.extensionManagementServerService.webExtensionManagementServer.extensionManagementService.installFromGallery(gallery); + } + + if (this.extensionManagementServerService.remoteExtensionManagementServer) { + const error = new Error(localize('cannot be installed', "Cannot install '{0}' because this extension has defined that it cannot run on the remote server.", gallery.displayName || gallery.name)); + error.name = INSTALL_ERROR_NOT_SUPPORTED; + return Promise.reject(error); + } + + const error = new Error(localize('cannot be installed on web', "Cannot install '{0}' because this extension has defined that it cannot run on the web server.", gallery.displayName || gallery.name)); + error.name = INSTALL_ERROR_NOT_SUPPORTED; + return Promise.reject(error); } getExtensionsReport(): Promise { @@ -229,6 +258,6 @@ export class ExtensionManagementService extends Disposable implements IExtension } private getServer(extension: ILocalExtension): IExtensionManagementServer | null { - return this.extensionManagementServerService.getExtensionManagementServer(extension.location); + return this.extensionManagementServerService.getExtensionManagementServer(extension); } } diff --git a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts new file mode 100644 index 00000000000..98a423df368 --- /dev/null +++ b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.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. + *--------------------------------------------------------------------------------------------*/ + +import { ExtensionType, IExtensionIdentifier, IExtensionManifest, ITranslatedScannedExtension } from 'vs/platform/extensions/common/extensions'; +import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, DidUninstallExtensionEvent, IGalleryExtension, IReportedExtension, IGalleryMetadata, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { Event, Emitter } from 'vs/base/common/event'; +import { URI } from 'vs/base/common/uri'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { ILogService } from 'vs/platform/log/common/log'; +import { Disposable } from 'vs/base/common/lifecycle'; + +export class WebExtensionManagementService extends Disposable implements IExtensionManagementService { + + declare readonly _serviceBrand: undefined; + + private readonly _onInstallExtension = this._register(new Emitter()); + readonly onInstallExtension: Event = this._onInstallExtension.event; + + private readonly _onDidInstallExtension = this._register(new Emitter()); + readonly onDidInstallExtension: Event = this._onDidInstallExtension.event; + + private readonly _onUninstallExtension = this._register(new Emitter()); + readonly onUninstallExtension: Event = this._onUninstallExtension.event; + + private _onDidUninstallExtension = this._register(new Emitter()); + onDidUninstallExtension: Event = this._onDidUninstallExtension.event; + + constructor( + @IWebExtensionsScannerService private readonly webExtensionsScannerService: IWebExtensionsScannerService, + @ILogService private readonly logService: ILogService, + ) { + super(); + } + + async getInstalled(type?: ExtensionType): Promise { + const extensions = await this.webExtensionsScannerService.scanAndTranslateExtensions(type); + return Promise.all(extensions.map(e => this.toLocalExtension(e))); + } + + async installFromGallery(gallery: IGalleryExtension): Promise { + this.logService.info('Installing extension:', gallery.identifier.id); + this._onInstallExtension.fire({ identifier: gallery.identifier, gallery }); + try { + const existingExtension = await this.getUserExtension(gallery.identifier); + if (existingExtension && existingExtension.manifest.version !== gallery.version) { + await this.webExtensionsScannerService.removeExtension(existingExtension.identifier, existingExtension.manifest.version); + } + const scannedExtension = await this.webExtensionsScannerService.addExtension(gallery); + const local = await this.toLocalExtension(scannedExtension); + this._onDidInstallExtension.fire({ local, identifier: gallery.identifier, operation: InstallOperation.Install, gallery }); + return local; + } catch (error) { + this._onDidInstallExtension.fire({ error, identifier: gallery.identifier, operation: InstallOperation.Install, gallery }); + throw error; + } + } + + async uninstall(extension: ILocalExtension): Promise { + this._onUninstallExtension.fire(extension.identifier); + try { + await this.webExtensionsScannerService.removeExtension(extension.identifier); + this._onDidUninstallExtension.fire({ identifier: extension.identifier }); + } catch (error) { + this.logService.error(error); + this._onDidUninstallExtension.fire({ error, identifier: extension.identifier }); + throw error; + } + } + + async updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise { + return local; + } + + private async getUserExtension(identifier: IExtensionIdentifier): Promise { + const userExtensions = await this.getInstalled(ExtensionType.User); + return userExtensions.find(e => areSameExtensions(e.identifier, identifier)); + } + + private async toLocalExtension(scannedExtension: ITranslatedScannedExtension): Promise { + return { + type: scannedExtension.type, + identifier: scannedExtension.identifier, + manifest: scannedExtension.packageJSON, + location: scannedExtension.location, + isMachineScoped: false, + publisherId: null, + publisherDisplayName: null + }; + } + + zip(extension: ILocalExtension): Promise { throw new Error('unsupported'); } + unzip(zipLocation: URI): Promise { throw new Error('unsupported'); } + getManifest(vsix: URI): Promise { throw new Error('unsupported'); } + install(vsix: URI, isMachineScoped?: boolean): Promise { throw new Error('unsupported'); } + reinstallFromGallery(extension: ILocalExtension): Promise { throw new Error('unsupported'); } + getExtensionsReport(): Promise { throw new Error('unsupported'); } + +} diff --git a/src/vs/workbench/services/extensionManagement/common/webExtensionsScannerService.ts b/src/vs/workbench/services/extensionManagement/common/webExtensionsScannerService.ts new file mode 100644 index 00000000000..d5ae5593e0f --- /dev/null +++ b/src/vs/workbench/services/extensionManagement/common/webExtensionsScannerService.ts @@ -0,0 +1,307 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as semver from 'semver-umd'; +import { IBuiltinExtensionsScannerService, IScannedExtension, ExtensionType, IExtensionIdentifier, ITranslatedScannedExtension } from 'vs/platform/extensions/common/extensions'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { isWeb } from 'vs/base/common/platform'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { joinPath } from 'vs/base/common/resources'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { IFileService } from 'vs/platform/files/common/files'; +import { Queue } from 'vs/base/common/async'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { asText, isSuccess, IRequestService } from 'vs/platform/request/common/request'; +import { ILogService } from 'vs/platform/log/common/log'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { groupByExtension, areSameExtensions, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IStaticExtension } from 'vs/workbench/workbench.web.api'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Event } from 'vs/base/common/event'; +import { localizeManifest } from 'vs/platform/extensionManagement/common/extensionNls'; + +interface IUserExtension { + identifier: IExtensionIdentifier; + version: string; + uri: URI; + readmeUri?: URI; + changelogUri?: URI; + packageNLSUri?: URI; +} + +interface IStoredUserExtension { + identifier: IExtensionIdentifier; + version: string; + uri: UriComponents; + readmeUri?: UriComponents; + changelogUri?: UriComponents; + packageNLSUri?: UriComponents; +} + +const AssetTypeWebResource = 'Microsoft.VisualStudio.Code.WebResources'; + +function getExtensionLocation(assetUri: URI): URI { return joinPath(assetUri, AssetTypeWebResource, 'extension'); } + +export class WebExtensionsScannerService extends Disposable implements IWebExtensionsScannerService { + + declare readonly _serviceBrand: undefined; + + private readonly systemExtensionsPromise: Promise = Promise.resolve([]); + private readonly defaultExtensionsPromise: Promise = Promise.resolve([]); + private readonly extensionsResource: URI | undefined = undefined; + private readonly userExtensionsResourceLimiter: Queue = new Queue(); + + private userExtensionsPromise: Promise | undefined; + + constructor( + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, + @IBuiltinExtensionsScannerService private readonly builtinExtensionsScannerService: IBuiltinExtensionsScannerService, + @IFileService private readonly fileService: IFileService, + @IRequestService private readonly requestService: IRequestService, + @ILogService private readonly logService: ILogService, + @IConfigurationService private readonly configurationService: IConfigurationService, + ) { + super(); + if (isWeb) { + this.extensionsResource = joinPath(environmentService.userRoamingDataHome, 'extensions.json'); + this.systemExtensionsPromise = this.readSystemExtensions(); + this.defaultExtensionsPromise = this.readDefaultExtensions(); + if (this.extensionsResource) { + this._register(Event.filter(this.fileService.onDidFilesChange, e => e.contains(this.extensionsResource!))(() => this.userExtensionsPromise = undefined)); + } + } + } + + private async readSystemExtensions(): Promise { + const extensions = await this.builtinExtensionsScannerService.scanBuiltinExtensions(); + return extensions.concat(this.getStaticExtensions(true)); + } + + /** + * All extensions defined via `staticExtensions` + */ + private getStaticExtensions(builtin: boolean): IScannedExtension[] { + const staticExtensions = this.environmentService.options && Array.isArray(this.environmentService.options.staticExtensions) ? this.environmentService.options.staticExtensions : []; + return ( + staticExtensions + .filter(e => Boolean(e.isBuiltin) === builtin) + .map(e => ({ + identifier: { id: getGalleryExtensionId(e.packageJSON.publisher, e.packageJSON.name) }, + location: e.extensionLocation, + type: ExtensionType.System, + packageJSON: e.packageJSON, + })) + ); + } + + private async readDefaultExtensions(): Promise { + const defaultUserWebExtensions = await this.readDefaultUserWebExtensions(); + const extensions = defaultUserWebExtensions.map(e => ({ + identifier: { id: getGalleryExtensionId(e.packageJSON.publisher, e.packageJSON.name) }, + location: e.extensionLocation, + type: ExtensionType.User, + packageJSON: e.packageJSON, + })); + return extensions.concat(this.getStaticExtensions(false)); + } + + private async readDefaultUserWebExtensions(): Promise { + const result: IStaticExtension[] = []; + const defaultUserWebExtensions = this.configurationService.getValue<{ location: string }[]>('_extensions.defaultUserWebExtensions') || []; + for (const webExtension of defaultUserWebExtensions) { + const extensionLocation = URI.parse(webExtension.location); + const manifestLocation = joinPath(extensionLocation, 'package.json'); + const context = await this.requestService.request({ type: 'GET', url: manifestLocation.toString(true) }, CancellationToken.None); + if (!isSuccess(context)) { + this.logService.warn('Skipped default user web extension as there is an error while fetching manifest', manifestLocation); + continue; + } + const content = await asText(context); + if (!content) { + this.logService.warn('Skipped default user web extension as there is manifest is not found', manifestLocation); + continue; + } + const packageJSON = JSON.parse(content); + result.push({ + packageJSON, + extensionLocation, + }); + } + return result; + } + + async scanExtensions(type?: ExtensionType): Promise { + const extensions = []; + if (type === undefined || type === ExtensionType.System) { + const systemExtensions = await this.systemExtensionsPromise; + extensions.push(...systemExtensions); + } + if (type === undefined || type === ExtensionType.User) { + const staticExtensions = await this.defaultExtensionsPromise; + extensions.push(...staticExtensions); + if (!this.userExtensionsPromise) { + this.userExtensionsPromise = this.scanUserExtensions(); + } + const userExtensions = await this.userExtensionsPromise; + extensions.push(...userExtensions); + } + return extensions; + } + + async scanAndTranslateExtensions(type?: ExtensionType): Promise { + const extensions = await this.scanExtensions(type); + return Promise.all(extensions.map((ext) => this._translateScannedExtension(ext))); + } + + private async _translateScannedExtension(scannedExtension: IScannedExtension): Promise { + let manifest = scannedExtension.packageJSON; + if (scannedExtension.packageNLS) { + // package.nls.json is inlined + try { + manifest = localizeManifest(manifest, scannedExtension.packageNLS); + } catch (error) { + console.log(error); + /* ignore */ + } + } else if (scannedExtension.packageNLSUrl) { + // package.nls.json needs to be fetched + try { + const context = await this.requestService.request({ type: 'GET', url: scannedExtension.packageNLSUrl.toString() }, CancellationToken.None); + if (isSuccess(context)) { + const content = await asText(context); + if (content) { + manifest = localizeManifest(manifest, JSON.parse(content)); + } + } + } catch (error) { /* ignore */ } + } + return { + identifier: scannedExtension.identifier, + location: scannedExtension.location, + type: scannedExtension.type, + packageJSON: manifest, + readmeUrl: scannedExtension.readmeUrl, + changelogUrl: scannedExtension.changelogUrl + }; + } + + async addExtension(galleryExtension: IGalleryExtension): Promise { + if (!galleryExtension.assetTypes.some(type => type.startsWith(AssetTypeWebResource))) { + throw new Error(`Missing ${AssetTypeWebResource} asset type`); + } + + const packageNLSUri = joinPath(getExtensionLocation(galleryExtension.assetUri), 'package.nls.json'); + const context = await this.requestService.request({ type: 'GET', url: packageNLSUri.toString() }, CancellationToken.None); + const packageNLSExists = isSuccess(context); + + const userExtensions = await this.readUserExtensions(); + const userExtension: IUserExtension = { + identifier: galleryExtension.identifier, + version: galleryExtension.version, + uri: galleryExtension.assetUri, + readmeUri: galleryExtension.assets.readme ? URI.parse(galleryExtension.assets.readme.uri) : undefined, + changelogUri: galleryExtension.assets.changelog ? URI.parse(galleryExtension.assets.changelog.uri) : undefined, + packageNLSUri: packageNLSExists ? packageNLSUri : undefined + }; + userExtensions.push(userExtension); + await this.writeUserExtensions(userExtensions); + + const scannedExtension = await this.toScannedExtension(userExtension); + if (scannedExtension) { + return scannedExtension; + } + throw new Error('Error while scanning extension'); + } + + async removeExtension(identifier: IExtensionIdentifier, version?: string): Promise { + let userExtensions = await this.readUserExtensions(); + userExtensions = userExtensions.filter(extension => !(areSameExtensions(extension.identifier, identifier) && (version ? extension.version === version : true))); + await this.writeUserExtensions(userExtensions); + } + + private async scanUserExtensions(): Promise { + let userExtensions = await this.readUserExtensions(); + const byExtension: IUserExtension[][] = groupByExtension(userExtensions, e => e.identifier); + userExtensions = byExtension.map(p => p.sort((a, b) => semver.rcompare(a.version, b.version))[0]); + const scannedExtensions: IScannedExtension[] = []; + await Promise.all(userExtensions.map(async userExtension => { + try { + const scannedExtension = await this.toScannedExtension(userExtension); + if (scannedExtension) { + scannedExtensions.push(scannedExtension); + } + } catch (error) { + this.logService.error(error, 'Error while scanning user extension', userExtension.identifier.id); + } + })); + return scannedExtensions; + } + + private async toScannedExtension(userExtension: IUserExtension): Promise { + const context = await this.requestService.request({ type: 'GET', url: joinPath(userExtension.uri, 'Microsoft.VisualStudio.Code.Manifest').toString() }, CancellationToken.None); + if (isSuccess(context)) { + const content = await asText(context); + if (content) { + const packageJSON = JSON.parse(content); + return { + identifier: userExtension.identifier, + location: getExtensionLocation(userExtension.uri), + packageJSON, + type: ExtensionType.User, + readmeUrl: userExtension.readmeUri, + changelogUrl: userExtension.changelogUri, + packageNLSUrl: userExtension.packageNLSUri, + }; + } + } + return null; + } + + private async readUserExtensions(): Promise { + if (!this.extensionsResource) { + return []; + } + return this.userExtensionsResourceLimiter.queue(async () => { + try { + const content = await this.fileService.readFile(this.extensionsResource!); + const storedUserExtensions: IStoredUserExtension[] = JSON.parse(content.value.toString()); + return storedUserExtensions.map(e => ({ + identifier: e.identifier, + version: e.version, + uri: URI.revive(e.uri), + readmeUri: URI.revive(e.readmeUri), + changelogUri: URI.revive(e.changelogUri), + packageNLSUri: URI.revive(e.packageNLSUri), + })); + } catch (error) { /* Ignore */ } + return []; + }); + } + + private writeUserExtensions(userExtensions: IUserExtension[]): Promise { + if (!this.extensionsResource) { + throw new Error('unsupported'); + } + return this.userExtensionsResourceLimiter.queue(async () => { + const storedUserExtensions: IStoredUserExtension[] = userExtensions.map(e => ({ + identifier: e.identifier, + version: e.version, + uri: e.uri.toJSON(), + readmeUri: e.readmeUri?.toJSON(), + changelogUri: e.changelogUri?.toJSON(), + packageNLSUri: e.packageNLSUri?.toJSON(), + })); + await this.fileService.writeFile(this.extensionsResource!, VSBuffer.fromString(JSON.stringify(storedUserExtensions))); + this.userExtensionsPromise = undefined; + return userExtensions; + }); + } + +} + +registerSingleton(IWebExtensionsScannerService, WebExtensionsScannerService); diff --git a/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService.ts index 3401e3bde01..0ac506857c7 100644 --- a/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService.ts @@ -5,7 +5,6 @@ import { localize } from 'vs/nls'; import { Schemas } from 'vs/base/common/network'; -import { URI } from 'vs/base/common/uri'; import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementServer, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; @@ -19,16 +18,16 @@ import { RemoteExtensionManagementChannelClient } from 'vs/workbench/services/ex import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IProductService } from 'vs/platform/product/common/productService'; import { ILabelService } from 'vs/platform/label/common/label'; - -const localExtensionManagementServerAuthority: string = 'vscode-local'; +import { IExtension } from 'vs/platform/extensions/common/extensions'; export class ExtensionManagementServerService implements IExtensionManagementServerService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; - readonly localExtensionManagementServer: IExtensionManagementServer; + private readonly _localExtensionManagementServer: IExtensionManagementServer; + public get localExtensionManagementServer(): IExtensionManagementServer { return this._localExtensionManagementServer; } readonly remoteExtensionManagementServer: IExtensionManagementServer | null = null; - readonly isSingleServer: boolean = false; + readonly webExtensionManagementServer: IExtensionManagementServer | null = null; constructor( @ISharedProcessService sharedProcessService: ISharedProcessService, @@ -41,26 +40,26 @@ export class ExtensionManagementServerService implements IExtensionManagementSer ) { const localExtensionManagementService = new ExtensionManagementChannelClient(sharedProcessService.getChannel('extensions')); - this.localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, authority: localExtensionManagementServerAuthority, label: localize('local', "Local") }; + this._localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, id: 'local', label: localize('local', "Local") }; const remoteAgentConnection = remoteAgentService.getConnection(); if (remoteAgentConnection) { const extensionManagementService = new RemoteExtensionManagementChannelClient(remoteAgentConnection.getChannel('extensions'), this.localExtensionManagementServer.extensionManagementService, galleryService, logService, configurationService, productService); this.remoteExtensionManagementServer = { - authority: remoteAgentConnection.remoteAuthority, + id: 'remote', extensionManagementService, get label() { return labelService.getHostLabel(REMOTE_HOST_SCHEME, remoteAgentConnection!.remoteAuthority) || localize('remote', "Remote"); } }; } } - getExtensionManagementServer(location: URI): IExtensionManagementServer | null { - if (location.scheme === Schemas.file) { + getExtensionManagementServer(extension: IExtension): IExtensionManagementServer { + if (extension.location.scheme === Schemas.file) { return this.localExtensionManagementServer; } - if (location.scheme === REMOTE_HOST_SCHEME) { + if (this.remoteExtensionManagementServer && extension.location.scheme === REMOTE_HOST_SCHEME) { return this.remoteExtensionManagementServer; } - return null; + throw new Error(`Invalid Extension ${extension.location}`); } } diff --git a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts index 66bdd7eefae..d7ccb3fafec 100644 --- a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts @@ -397,7 +397,7 @@ suite('ExtensionEnablementService Test', () => { assert.ok(!testObject.canChangeEnablement(extension)); }); - test('test extension is disabled when disabled in enviroment', async () => { + test('test extension is disabled when disabled in environment', async () => { const extension = aLocalExtension('pub.a'); instantiationService.stub(IWorkbenchEnvironmentService, { disableExtensions: ['pub.a'] } as IWorkbenchEnvironmentService); instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: didUninstallEvent.event, getInstalled: () => Promise.resolve([extension, aLocalExtension('pub.b')]) } as IExtensionManagementService); @@ -461,7 +461,7 @@ suite('ExtensionEnablementService Test', () => { }); test('test remote ui extension is disabled by kind when there is no local server', async () => { - instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, anExtensionManagementServer('vscode-remote', instantiationService))); + instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, anExtensionManagementServer('vscode-remote', instantiationService), null)); const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['ui'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); testObject = new TestExtensionEnablementService(instantiationService); assert.ok(!testObject.isEnabled(localWorkspaceExtension)); @@ -499,7 +499,7 @@ suite('ExtensionEnablementService Test', () => { }); test('test web extension on remote server is not disabled by kind when there is no local server', async () => { - instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, anExtensionManagementServer('vscode-remote', instantiationService))); + instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, anExtensionManagementServer('vscode-remote', instantiationService), anExtensionManagementServer('web', instantiationService))); const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); testObject = new TestExtensionEnablementService(instantiationService); assert.ok(testObject.isEnabled(localWorkspaceExtension)); @@ -507,7 +507,7 @@ suite('ExtensionEnablementService Test', () => { }); test('test web extension with no server is not disabled by kind when there is no local server', async () => { - instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, anExtensionManagementServer('vscode-remote', instantiationService))); + instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, anExtensionManagementServer('vscode-remote', instantiationService), anExtensionManagementServer('web', instantiationService))); const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.https }) }); testObject = new TestExtensionEnablementService(instantiationService); assert.ok(testObject.isEnabled(localWorkspaceExtension)); @@ -515,7 +515,7 @@ suite('ExtensionEnablementService Test', () => { }); test('test web extension with no server is not disabled by kind when there is no local and remote server', async () => { - instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, null)); + instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, null, anExtensionManagementServer('web', instantiationService))); const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.https }) }); testObject = new TestExtensionEnablementService(instantiationService); assert.ok(testObject.isEnabled(localWorkspaceExtension)); @@ -526,7 +526,7 @@ suite('ExtensionEnablementService Test', () => { function anExtensionManagementServer(authority: string, instantiationService: TestInstantiationService): IExtensionManagementServer { return { - authority, + id: authority, label: authority, extensionManagementService: instantiationService.get(IExtensionManagementService) }; @@ -535,22 +535,23 @@ function anExtensionManagementServer(authority: string, instantiationService: Te function aMultiExtensionManagementServerService(instantiationService: TestInstantiationService): IExtensionManagementServerService { const localExtensionManagementServer = anExtensionManagementServer('vscode-local', instantiationService); const remoteExtensionManagementServer = anExtensionManagementServer('vscode-remote', instantiationService); - return anExtensionManagementServerService(localExtensionManagementServer, remoteExtensionManagementServer); + return anExtensionManagementServerService(localExtensionManagementServer, remoteExtensionManagementServer, null); } -function anExtensionManagementServerService(localExtensionManagementServer: IExtensionManagementServer | null, remoteExtensionManagementServer: IExtensionManagementServer | null): IExtensionManagementServerService { +function anExtensionManagementServerService(localExtensionManagementServer: IExtensionManagementServer | null, remoteExtensionManagementServer: IExtensionManagementServer | null, webExtensionManagementServer: IExtensionManagementServer | null): IExtensionManagementServerService { return { _serviceBrand: undefined, localExtensionManagementServer, remoteExtensionManagementServer, - getExtensionManagementServer: (location: URI) => { - if (location.scheme === Schemas.file) { + webExtensionManagementServer: null, + getExtensionManagementServer: (extension: IExtension) => { + if (extension.location.scheme === Schemas.file) { return localExtensionManagementServer; } - if (location.scheme === REMOTE_HOST_SCHEME) { + if (extension.location.scheme === REMOTE_HOST_SCHEME) { return remoteExtensionManagementServer; } - return null; + return webExtensionManagementServer; } }; } diff --git a/src/vs/workbench/services/extensionResourceLoader/browser/extensionResourceLoaderService.ts b/src/vs/workbench/services/extensionResourceLoader/browser/extensionResourceLoaderService.ts index f2d816cca9e..be77f2563c6 100644 --- a/src/vs/workbench/services/extensionResourceLoader/browser/extensionResourceLoaderService.ts +++ b/src/vs/workbench/services/extensionResourceLoader/browser/extensionResourceLoaderService.ts @@ -12,7 +12,7 @@ import { Schemas } from 'vs/base/common/network'; class ExtensionResourceLoaderService implements IExtensionResourceLoaderService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @IFileService private readonly _fileService: IFileService diff --git a/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader.ts b/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader.ts index 300a54b0842..0820d426cca 100644 --- a/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader.ts +++ b/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader.ts @@ -12,7 +12,7 @@ export const IExtensionResourceLoaderService = createDecorator; constructor( @IInstantiationService instantiationService: IInstantiationService, @@ -42,9 +39,10 @@ export class ExtensionService extends AbstractExtensionService implements IExten @IWorkbenchExtensionEnablementService extensionEnablementService: IWorkbenchExtensionEnablementService, @IFileService fileService: IFileService, @IProductService productService: IProductService, + @IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService, @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, @IConfigurationService private readonly _configService: IConfigurationService, - @IStaticExtensionsService private readonly _staticExtensions: IStaticExtensionsService, + @IWebExtensionsScannerService private readonly _webExtensionsScannerService: IWebExtensionsScannerService, ) { super( instantiationService, @@ -56,6 +54,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten productService, ); + this._runningLocation = new Map(); + this._initialize(); this._initFetchFileSystem(); } @@ -71,31 +71,39 @@ export class ExtensionService extends AbstractExtensionService implements IExten this._disposables.add(this._fileService.registerProvider(Schemas.https, provider)); } - private _createProvider(remoteAuthority: string): IInitDataProvider { + private _createLocalExtensionHostDataProvider() { return { - remoteAuthority: remoteAuthority, - getInitData: () => { - return this.whenInstalledExtensionsRegistered().then(() => { - return this._remoteExtensionsEnvironmentData!; - }); + getInitData: async () => { + const allExtensions = await this.getExtensions(); + const localWebWorkerExtensions = filterByRunningLocation(allExtensions, this._runningLocation, ExtensionRunningLocation.LocalWebWorker); + return { + autoStart: true, + extensions: localWebWorkerExtensions + }; } }; } - protected _createExtensionHosts(_isInitialStart: boolean, initialActivationEvents: string[]): ExtensionHostProcessManager[] { - const result: ExtensionHostProcessManager[] = []; + private _createRemoteExtensionHostDataProvider(remoteAuthority: string): IRemoteExtensionHostDataProvider { + return { + remoteAuthority: remoteAuthority, + getInitData: async () => { + await this.whenInstalledExtensionsRegistered(); + return this._remoteInitData!; + } + }; + } - const webExtensions = this.getExtensions().then(extensions => extensions.filter(ext => canExecuteOnWeb(ext, this._productService, this._configService))); - const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, webExtensions, URI.file(this._environmentService.logsPath).with({ scheme: this._environmentService.logFile.scheme })); - const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, null, initialActivationEvents); - result.push(webHostProcessManager); + protected _createExtensionHosts(_isInitialStart: boolean): IExtensionHost[] { + const result: IExtensionHost[] = []; + + const webWorkerExtHost = this._instantiationService.createInstance(WebWorkerExtensionHost, this._createLocalExtensionHostDataProvider()); + result.push(webWorkerExtHost); const remoteAgentConnection = this._remoteAgentService.getConnection(); if (remoteAgentConnection) { - const remoteExtensions = this.getExtensions().then(extensions => extensions.filter(ext => !canExecuteOnWeb(ext, this._productService, this._configService))); - const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, remoteExtensions, this._createProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory); - const remoteExtHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, remoteExtHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); - result.push(remoteExtHostProcessManager); + const remoteExtHost = this._instantiationService.createInstance(RemoteExtensionHost, this._createRemoteExtensionHostDataProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory); + result.push(remoteExtHost); } return result; @@ -103,39 +111,39 @@ export class ExtensionService extends AbstractExtensionService implements IExten protected async _scanAndHandleExtensions(): Promise { // fetch the remote environment - let [remoteEnv, localExtensions] = await Promise.all([ + let [localExtensions, remoteEnv, remoteExtensions] = await Promise.all([ + this._webExtensionsScannerService.scanAndTranslateExtensions().then(extensions => extensions.map(parseScannedExtension)), this._remoteAgentService.getEnvironment(), - this._staticExtensions.getExtensions() + this._remoteAgentService.scanExtensions() ]); + localExtensions = this._checkEnabledAndProposedAPI(localExtensions); + remoteExtensions = this._checkEnabledAndProposedAPI(remoteExtensions); - let result: DeltaExtensionsResult; + const remoteAgentConnection = this._remoteAgentService.getConnection(); + this._runningLocation = _determineRunningLocation(this._productService, this._configService, localExtensions, remoteExtensions, Boolean(remoteEnv && remoteAgentConnection)); - // local: only enabled and web'ish extension - localExtensions = localExtensions!.filter(ext => this._isEnabled(ext) && canExecuteOnWeb(ext, this._productService, this._configService)); - this._checkEnableProposedApi(localExtensions); - - if (!remoteEnv) { - result = this._registry.deltaExtensions(localExtensions, []); - - } else { - // remote: only enabled and none-web'ish extension - remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension) && !canExecuteOnWeb(extension, this._productService, this._configService)); - this._checkEnableProposedApi(remoteEnv.extensions); - - // in case of overlap, the remote wins - const isRemoteExtension = new Set(); - remoteEnv.extensions.forEach(extension => isRemoteExtension.add(ExtensionIdentifier.toKey(extension.identifier))); - localExtensions = localExtensions.filter(extension => !isRemoteExtension.has(ExtensionIdentifier.toKey(extension.identifier))); - - // save for remote extension's init data - this._remoteExtensionsEnvironmentData = remoteEnv; - - result = this._registry.deltaExtensions(remoteEnv.extensions.concat(localExtensions), []); - } + localExtensions = filterByRunningLocation(localExtensions, this._runningLocation, ExtensionRunningLocation.LocalWebWorker); + remoteExtensions = filterByRunningLocation(remoteExtensions, this._runningLocation, ExtensionRunningLocation.Remote); + const result = this._registry.deltaExtensions(remoteExtensions.concat(localExtensions), []); if (result.removedDueToLooping.length > 0) { this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))); } + + if (remoteEnv && remoteAgentConnection) { + // save for remote extension's init data + this._remoteInitData = { + connectionData: this._remoteAuthorityResolverService.getConnectionData(remoteAgentConnection.remoteAuthority), + pid: remoteEnv.pid, + appRoot: remoteEnv.appRoot, + extensionHostLogsPath: remoteEnv.extensionHostLogsPath, + globalStorageHome: remoteEnv.globalStorageHome, + workspaceStorageHome: remoteEnv.workspaceStorageHome, + extensions: remoteExtensions, + allExtensions: this._registry.getAllExtensionDescriptions() + }; + } + this._doHandleExtensionPoints(this._registry.getAllExtensionDescriptions()); } @@ -147,4 +155,55 @@ export class ExtensionService extends AbstractExtensionService implements IExten } } +const enum ExtensionRunningLocation { + None, + LocalWebWorker, + Remote +} + +export function determineRunningLocation(localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[], allExtensionKinds: Map, hasRemote: boolean): Map { + const localExtensionsSet = new Set(); + localExtensions.forEach(ext => localExtensionsSet.add(ExtensionIdentifier.toKey(ext.identifier))); + + const remoteExtensionsSet = new Set(); + remoteExtensions.forEach(ext => remoteExtensionsSet.add(ExtensionIdentifier.toKey(ext.identifier))); + + const pickRunningLocation = (extension: IExtensionDescription): ExtensionRunningLocation => { + const isInstalledLocally = localExtensionsSet.has(ExtensionIdentifier.toKey(extension.identifier)); + const isInstalledRemotely = remoteExtensionsSet.has(ExtensionIdentifier.toKey(extension.identifier)); + const extensionKinds = allExtensionKinds.get(ExtensionIdentifier.toKey(extension.identifier)) || []; + for (const extensionKind of extensionKinds) { + if (extensionKind === 'ui' && isInstalledRemotely) { + // ui extensions run remotely if possible + return ExtensionRunningLocation.Remote; + } + if (extensionKind === 'workspace' && isInstalledRemotely) { + // workspace extensions run remotely if possible + return ExtensionRunningLocation.Remote; + } + if (extensionKind === 'web' && isInstalledLocally) { + // web worker extensions run in the local web worker if possible + return ExtensionRunningLocation.LocalWebWorker; + } + } + return ExtensionRunningLocation.None; + }; + + const runningLocation = new Map(); + localExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext))); + remoteExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext))); + return runningLocation; +} + +function _determineRunningLocation(productService: IProductService, configurationService: IConfigurationService, localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[], hasRemote: boolean): Map { + const allExtensionKinds = new Map(); + localExtensions.forEach(ext => allExtensionKinds.set(ExtensionIdentifier.toKey(ext.identifier), getExtensionKind(ext, productService, configurationService))); + remoteExtensions.forEach(ext => allExtensionKinds.set(ExtensionIdentifier.toKey(ext.identifier), getExtensionKind(ext, productService, configurationService))); + return determineRunningLocation(localExtensions, remoteExtensions, allExtensionKinds, hasRemote); +} + +function filterByRunningLocation(extensions: IExtensionDescription[], runningLocation: Map, desiredRunningLocation: ExtensionRunningLocation): IExtensionDescription[] { + return extensions.filter(ext => runningLocation.get(ExtensionIdentifier.toKey(ext.identifier)) === desiredRunningLocation); +} + registerSingleton(IExtensionService, ExtensionService); diff --git a/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts b/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts index 88beb74a747..4091dc8bb58 100644 --- a/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts @@ -4,15 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { Action } from 'vs/base/common/actions'; import { IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementService, ExtensionsLabel } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { createDecorator, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IURLHandler, IURLService, IOpenURLOptions } from 'vs/platform/url/common/url'; @@ -23,8 +22,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContribution, Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; @@ -375,39 +373,37 @@ class ExtensionUrlBootstrapHandler implements IWorkbenchContribution, IURLHandle const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(ExtensionUrlBootstrapHandler, LifecyclePhase.Ready); -export class ManageAuthorizedExtensionURIsAction extends Action { +class ManageAuthorizedExtensionURIsAction extends Action2 { - static readonly ID = 'workbench.extensions.action.manageAuthorizedExtensionURIs'; - static readonly LABEL = localize('manage', "Manage Authorized Extension URIs..."); - - private storage: ConfirmedExtensionIdStorage; - - constructor( - id = ManageAuthorizedExtensionURIsAction.ID, - label = ManageAuthorizedExtensionURIsAction.LABEL, - @IStorageService readonly storageService: IStorageService, - @IQuickInputService private readonly quickInputService: IQuickInputService - ) { - super(id, label, undefined, true); - this.storage = new ConfirmedExtensionIdStorage(storageService); + constructor() { + super({ + id: 'workbench.extensions.action.manageAuthorizedExtensionURIs', + title: { value: localize('manage', "Manage Authorized Extension URIs..."), original: 'Manage Authorized Extension URIs...' }, + category: { value: localize('extensions', "Extensions"), original: 'Extensions' }, + f1: true + }); } - async run(): Promise { - const items = this.storage.extensions.map(label => ({ label, picked: true } as IQuickPickItem)); + async run(accessor: ServicesAccessor): Promise { + const storageService = accessor.get(IStorageService); + const quickInputService = accessor.get(IQuickInputService); + + const storage = new ConfirmedExtensionIdStorage(storageService); + + const items = storage.extensions.map(label => ({ label, picked: true } as IQuickPickItem)); if (items.length === 0) { return; } - const result = await this.quickInputService.pick(items, { canPickMany: true }); + const result = await quickInputService.pick(items, { canPickMany: true }); if (!result) { return; } - this.storage.set(result.map(item => item.label)); + storage.set(result.map(item => item.label)); } } -const actionRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ManageAuthorizedExtensionURIsAction), `Extensions: Manage Authorized Extension URIs...`, ExtensionsLabel); +registerAction2(ManageAuthorizedExtensionURIsAction); diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts new file mode 100644 index 00000000000..fa43cce63be --- /dev/null +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts @@ -0,0 +1,279 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { getWorkerBootstrapUrl } from 'vs/base/worker/defaultWorkerFactory'; +import { Emitter, Event } from 'vs/base/common/event'; +import { toDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { createMessageOfType, MessageType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; +import { IInitData, UIKind } from 'vs/workbench/api/common/extHost.protocol'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import * as platform from 'vs/base/common/platform'; +import * as dom from 'vs/base/browser/dom'; +import { URI } from 'vs/base/common/uri'; +import { IExtensionHost, ExtensionHostLogFileName, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { joinPath } from 'vs/base/common/resources'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output/common/output'; +import { localize } from 'vs/nls'; +import { generateUuid } from 'vs/base/common/uuid'; +import { canceled, onUnexpectedError } from 'vs/base/common/errors'; +import { WEB_WORKER_IFRAME } from 'vs/workbench/services/extensions/common/webWorkerIframe'; + +const WRAP_IN_IFRAME = true; + +export interface IWebWorkerExtensionHostInitData { + readonly autoStart: boolean; + readonly extensions: IExtensionDescription[]; +} + +export interface IWebWorkerExtensionHostDataProvider { + getInitData(): Promise; +} + +export class WebWorkerExtensionHost extends Disposable implements IExtensionHost { + + public readonly kind = ExtensionHostKind.LocalWebWorker; + public readonly remoteAuthority = null; + + private readonly _onDidExit = this._register(new Emitter<[number, string | null]>()); + public readonly onExit: Event<[number, string | null]> = this._onDidExit.event; + + private _isTerminating: boolean; + private _protocolPromise: Promise | null; + private _protocol: IMessagePassingProtocol | null; + + private readonly _extensionHostLogsLocation: URI; + private readonly _extensionHostLogFile: URI; + + constructor( + private readonly _initDataProvider: IWebWorkerExtensionHostDataProvider, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, + @ILabelService private readonly _labelService: ILabelService, + @ILogService private readonly _logService: ILogService, + @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, + @IProductService private readonly _productService: IProductService, + ) { + super(); + this._isTerminating = false; + this._protocolPromise = null; + this._protocol = null; + this._extensionHostLogsLocation = URI.file(this._environmentService.logsPath).with({ scheme: this._environmentService.logFile.scheme }); + this._extensionHostLogFile = joinPath(this._extensionHostLogsLocation, `${ExtensionHostLogFileName}.log`); + } + + public async start(): Promise { + if (!this._protocolPromise) { + if (WRAP_IN_IFRAME && platform.isWeb) { + this._protocolPromise = this._startInsideIframe(); + } else { + this._protocolPromise = this._startOutsideIframe(); + } + this._protocolPromise.then(protocol => this._protocol = protocol); + } + return this._protocolPromise; + } + + private _startInsideIframe(): Promise { + const emitter = this._register(new Emitter()); + + const iframe = document.createElement('iframe'); + iframe.setAttribute('class', 'web-worker-ext-host-iframe'); + iframe.setAttribute('sandbox', 'allow-scripts'); + iframe.style.display = 'none'; + + const vscodeWebWorkerExtHostId = generateUuid(); + const workerUrl = require.toUrl('../worker/extensionHostWorkerMain.js'); + const workerSrc = getWorkerBootstrapUrl(workerUrl, 'WorkerExtensionHost', true); + const escapeAttribute = (value: string): string => { + return value.replace(/"/g, '"'); + }; + const html = ` + + + + + + + + + +`; + const iframeContent = `data:text/html;charset=utf-8,${encodeURIComponent(html)}`; + iframe.setAttribute('src', iframeContent); + + this._register(dom.addDisposableListener(window, 'message', (event) => { + if (event.source !== iframe.contentWindow) { + return; + } + if (event.data.vscodeWebWorkerExtHostId !== vscodeWebWorkerExtHostId) { + return; + } + if (event.data.error) { + const { name, message, stack } = event.data.error; + const err = new Error(); + err.message = message; + err.name = name; + err.stack = stack; + onUnexpectedError(err); + this._onDidExit.fire([18, err.message]); + return; + } + const { data } = event.data; + if (!(data instanceof ArrayBuffer)) { + console.warn('UNKNOWN data received', data); + this._onDidExit.fire([77, 'UNKNOWN data received']); + return; + } + emitter.fire(VSBuffer.wrap(new Uint8Array(data, 0, data.byteLength))); + })); + + const protocol: IMessagePassingProtocol = { + onMessage: emitter.event, + send: vsbuf => { + const data = vsbuf.buffer.buffer.slice(vsbuf.buffer.byteOffset, vsbuf.buffer.byteOffset + vsbuf.buffer.byteLength); + iframe.contentWindow!.postMessage({ + vscodeWebWorkerExtHostId, + data: data + }, '*', [data]); + } + }; + + document.body.appendChild(iframe); + this._register(toDisposable(() => iframe.remove())); + + return this._performHandshake(protocol); + } + + private _startOutsideIframe(): Promise { + const emitter = new Emitter(); + + const url = getWorkerBootstrapUrl(require.toUrl('../worker/extensionHostWorkerMain.js'), 'WorkerExtensionHost'); + const worker = new Worker(url, { name: 'WorkerExtensionHost' }); + + worker.onmessage = (event) => { + const { data } = event; + if (!(data instanceof ArrayBuffer)) { + console.warn('UNKNOWN data received', data); + this._onDidExit.fire([77, 'UNKNOWN data received']); + return; + } + + emitter.fire(VSBuffer.wrap(new Uint8Array(data, 0, data.byteLength))); + }; + + worker.onerror = (event) => { + console.error(event.message, event.error); + this._onDidExit.fire([81, event.message || event.error]); + }; + + // keep for cleanup + this._register(emitter); + this._register(toDisposable(() => worker.terminate())); + + const protocol: IMessagePassingProtocol = { + onMessage: emitter.event, + send: vsbuf => { + const data = vsbuf.buffer.buffer.slice(vsbuf.buffer.byteOffset, vsbuf.buffer.byteOffset + vsbuf.buffer.byteLength); + worker.postMessage(data, [data]); + } + }; + + return this._performHandshake(protocol); + } + + private async _performHandshake(protocol: IMessagePassingProtocol): Promise { + // extension host handshake happens below + // (1) <== wait for: Ready + // (2) ==> send: init data + // (3) <== wait for: Initialized + + await Event.toPromise(Event.filter(protocol.onMessage, msg => isMessageOfType(msg, MessageType.Ready))); + if (this._isTerminating) { + throw canceled(); + } + protocol.send(VSBuffer.fromString(JSON.stringify(await this._createExtHostInitData()))); + if (this._isTerminating) { + throw canceled(); + } + await Event.toPromise(Event.filter(protocol.onMessage, msg => isMessageOfType(msg, MessageType.Initialized))); + if (this._isTerminating) { + throw canceled(); + } + + // Register log channel for web worker exthost log + Registry.as(Extensions.OutputChannels).registerChannel({ id: 'webWorkerExtHostLog', label: localize('name', "Worker Extension Host"), file: this._extensionHostLogFile, log: true }); + + return protocol; + } + + public dispose(): void { + if (this._isTerminating) { + return; + } + this._isTerminating = true; + if (this._protocol) { + this._protocol.send(createMessageOfType(MessageType.Terminate)); + } + super.dispose(); + } + + getInspectPort(): number | undefined { + return undefined; + } + + enableInspectPort(): Promise { + return Promise.resolve(false); + } + + private async _createExtHostInitData(): Promise { + const [telemetryInfo, initData] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._initDataProvider.getInitData()]); + const workspace = this._contextService.getWorkspace(); + return { + commit: this._productService.commit, + version: this._productService.version, + parentPid: -1, + environment: { + isExtensionDevelopmentDebug: false, //todo@jrieken web + appName: this._productService.nameLong, + appUriScheme: this._productService.urlProtocol, + appLanguage: platform.language, + extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, + extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, + globalStorageHome: this._environmentService.globalStorageHome, + workspaceStorageHome: this._environmentService.workspaceStorageHome, + webviewResourceRoot: this._environmentService.webviewResourceRoot, + webviewCspSource: this._environmentService.webviewCspSource, + }, + workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : { + configuration: workspace.configuration || undefined, + id: workspace.id, + name: this._labelService.getWorkspaceLabel(workspace) + }, + resolvedExtensions: [], + hostExtensions: [], + extensions: initData.extensions, + telemetryInfo, + logLevel: this._logService.getLevel(), + logsLocation: this._extensionHostLogsLocation, + logFile: this._extensionHostLogFile, + autoStart: initData.autoStart, + remote: { + authority: this._environmentService.configuration.remoteAuthority, + connectionData: null, + isRemote: false + }, + uiKind: platform.isWeb ? UIKind.Web : UIKind.Desktop + }; + } +} diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts deleted file mode 100644 index 097a0487931..00000000000 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts +++ /dev/null @@ -1,168 +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 { getWorkerBootstrapUrl } from 'vs/base/worker/defaultWorkerFactory'; -import { Emitter, Event } from 'vs/base/common/event'; -import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; -import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; -import { VSBuffer } from 'vs/base/common/buffer'; -import { createMessageOfType, MessageType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; -import { IInitData, UIKind } from 'vs/workbench/api/common/extHost.protocol'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { ILabelService } from 'vs/platform/label/common/label'; -import { ILogService } from 'vs/platform/log/common/log'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import * as platform from 'vs/base/common/platform'; -import { URI } from 'vs/base/common/uri'; -import { IExtensionHostStarter, ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions'; -import { IProductService } from 'vs/platform/product/common/productService'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { joinPath } from 'vs/base/common/resources'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output/common/output'; -import { localize } from 'vs/nls'; - -export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { - - private _toDispose = new DisposableStore(); - private _isTerminating: boolean = false; - private _protocol?: IMessagePassingProtocol; - - private readonly _onDidExit = new Emitter<[number, string | null]>(); - readonly onExit: Event<[number, string | null]> = this._onDidExit.event; - - private readonly _extensionHostLogFile: URI; - - constructor( - private readonly _autoStart: boolean, - private readonly _extensions: Promise, - private readonly _extensionHostLogsLocation: URI, - @ITelemetryService private readonly _telemetryService: ITelemetryService, - @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, - @ILabelService private readonly _labelService: ILabelService, - @ILogService private readonly _logService: ILogService, - @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, - @IProductService private readonly _productService: IProductService, - ) { - this._extensionHostLogFile = joinPath(this._extensionHostLogsLocation, `${ExtensionHostLogFileName}.log`); - } - - async start(): Promise { - - if (!this._protocol) { - - const emitter = new Emitter(); - - const url = getWorkerBootstrapUrl(require.toUrl('../worker/extensionHostWorkerMain.js'), 'WorkerExtensionHost'); - const worker = new Worker(url, { name: 'WorkerExtensionHost' }); - - worker.onmessage = (event) => { - const { data } = event; - if (!(data instanceof ArrayBuffer)) { - console.warn('UNKNOWN data received', data); - this._onDidExit.fire([77, 'UNKNOWN data received']); - return; - } - - emitter.fire(VSBuffer.wrap(new Uint8Array(data, 0, data.byteLength))); - }; - - worker.onerror = (event) => { - console.error(event.message, event.error); - this._onDidExit.fire([81, event.message || event.error]); - }; - - // keep for cleanup - this._toDispose.add(emitter); - this._toDispose.add(toDisposable(() => worker.terminate())); - - const protocol: IMessagePassingProtocol = { - onMessage: emitter.event, - send: vsbuf => { - const data = vsbuf.buffer.buffer.slice(vsbuf.buffer.byteOffset, vsbuf.buffer.byteOffset + vsbuf.buffer.byteLength); - worker.postMessage(data, [data]); - } - }; - - // extension host handshake happens below - // (1) <== wait for: Ready - // (2) ==> send: init data - // (3) <== wait for: Initialized - - await Event.toPromise(Event.filter(protocol.onMessage, msg => isMessageOfType(msg, MessageType.Ready))); - protocol.send(VSBuffer.fromString(JSON.stringify(await this._createExtHostInitData()))); - await Event.toPromise(Event.filter(protocol.onMessage, msg => isMessageOfType(msg, MessageType.Initialized))); - - // Register log channel for web worker exthost log - Registry.as(Extensions.OutputChannels).registerChannel({ id: 'webWorkerExtHostLog', label: localize('name', "Worker Extension Host"), file: this._extensionHostLogFile, log: true }); - - this._protocol = protocol; - } - return this._protocol; - - } - - dispose(): void { - if (!this._protocol) { - this._toDispose.dispose(); - return; - } - if (this._isTerminating) { - return; - } - this._isTerminating = true; - this._protocol.send(createMessageOfType(MessageType.Terminate)); - setTimeout(() => this._toDispose.dispose(), 10 * 1000); - } - - getInspectPort(): number | undefined { - return undefined; - } - - enableInspectPort(): Promise { - return Promise.resolve(false); - } - - private async _createExtHostInitData(): Promise { - const [telemetryInfo, extensionDescriptions] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._extensions]); - const workspace = this._contextService.getWorkspace(); - return { - commit: this._productService.commit, - version: this._productService.version, - parentPid: -1, - environment: { - isExtensionDevelopmentDebug: false, - appName: this._productService.nameLong, - appUriScheme: this._productService.urlProtocol, - appLanguage: platform.language, - extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, - extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, - globalStorageHome: URI.parse('fake:globalStorageHome'), //todo@joh URI.file(this._environmentService.globalStorageHome), - userHome: URI.parse('fake:userHome'), //todo@joh URI.file(this._environmentService.userHome), - webviewResourceRoot: this._environmentService.webviewResourceRoot, - webviewCspSource: this._environmentService.webviewCspSource, - }, - workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : { - configuration: workspace.configuration || undefined, - id: workspace.id, - name: this._labelService.getWorkspaceLabel(workspace) - }, - resolvedExtensions: [], - hostExtensions: [], - extensions: extensionDescriptions, - telemetryInfo, - logLevel: this._logService.getLevel(), - logsLocation: this._extensionHostLogsLocation, - logFile: this._extensionHostLogFile, - autoStart: this._autoStart, - remote: { - authority: this._environmentService.configuration.remoteAuthority, - isRemote: false - }, - uiKind: platform.isWeb ? UIKind.Web : UIKind.Desktop - }; - } -} diff --git a/src/vs/workbench/services/extensions/browser/webWorkerFileSystemProvider.ts b/src/vs/workbench/services/extensions/browser/webWorkerFileSystemProvider.ts index c0e53c971a5..ee6f058e4b4 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerFileSystemProvider.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerFileSystemProvider.ts @@ -7,7 +7,7 @@ import { FileSystemProviderCapabilities, IStat, FileType, FileDeleteOptions, Fil import { Event } from 'vs/base/common/event'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { NotImplementedError } from 'vs/base/common/errors'; +import { NotSupportedError } from 'vs/base/common/errors'; export class FetchFileSystemProvider implements IFileSystemProviderWithFileReadWriteCapability { @@ -44,18 +44,18 @@ export class FetchFileSystemProvider implements IFileSystemProviderWithFileReadW // error implementations writeFile(_resource: URI, _content: Uint8Array, _opts: FileWriteOptions): Promise { - throw new NotImplementedError(); + throw new NotSupportedError(); } readdir(_resource: URI): Promise<[string, FileType][]> { - throw new NotImplementedError(); + throw new NotSupportedError(); } mkdir(_resource: URI): Promise { - throw new NotImplementedError(); + throw new NotSupportedError(); } delete(_resource: URI, _opts: FileDeleteOptions): Promise { - throw new NotImplementedError(); + throw new NotSupportedError(); } rename(_from: URI, _to: URI, _opts: FileOverwriteOptions): Promise { - throw new NotImplementedError(); + throw new NotSupportedError(); } } diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index c5f788650eb..446f74eff84 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -15,12 +15,12 @@ import { BetterMergeId } from 'vs/platform/extensionManagement/common/extensionM import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension } from 'vs/workbench/services/extensions/common/extensions'; +import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension, IExtensionHost } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol'; -import { ExtensionHostProcessManager } from 'vs/workbench/services/extensions/common/extensionHostProcessManager'; -import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager'; +import { ExtensionIdentifier, IExtensionDescription, ExtensionType, ITranslatedScannedExtension } from 'vs/platform/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -29,6 +29,16 @@ import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtens const hasOwnProperty = Object.hasOwnProperty; const NO_OP_VOID_PROMISE = Promise.resolve(undefined); +export function parseScannedExtension(extension: ITranslatedScannedExtension): IExtensionDescription { + return { + identifier: new ExtensionIdentifier(`${extension.packageJSON.publisher}.${extension.packageJSON.name}`), + isBuiltin: extension.type === ExtensionType.System, + isUnderDevelopment: false, + extensionLocation: extension.location, + ...extension.packageJSON, + }; +} + export abstract class AbstractExtensionService extends Disposable implements IExtensionService { public _serviceBrand: undefined; @@ -58,9 +68,9 @@ export abstract class AbstractExtensionService extends Disposable implements IEx protected readonly _isExtensionDevTestFromCli: boolean; // --- Members used per extension host process - protected _extensionHostProcessManagers: ExtensionHostProcessManager[]; + protected _extensionHostManagers: ExtensionHostManager[]; protected _extensionHostActiveExtensions: Map; - private _extensionHostProcessActivationTimes: Map; + private _extensionHostActivationTimes: Map; private _extensionHostExtensionRuntimeErrors: Map; constructor( @@ -85,9 +95,9 @@ export abstract class AbstractExtensionService extends Disposable implements IEx this._extensionsMessages = new Map(); this._proposedApiController = new ProposedApiController(this._environmentService, this._productService); - this._extensionHostProcessManagers = []; + this._extensionHostManagers = []; this._extensionHostActiveExtensions = new Map(); - this._extensionHostProcessActivationTimes = new Map(); + this._extensionHostActivationTimes = new Map(); this._extensionHostExtensionRuntimeErrors = new Map(); const devOpts = parseExtensionDevOptions(this._environmentService); @@ -97,7 +107,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx protected async _initialize(): Promise { perf.mark('willLoadExtensions'); - this._startExtensionHostProcess(true, []); + this._startExtensionHosts(true, []); this.whenInstalledExtensionsRegistered().then(() => perf.mark('didLoadExtensions')); await this._scanAndHandleExtensions(); this._releaseBarrier(); @@ -110,18 +120,18 @@ export abstract class AbstractExtensionService extends Disposable implements IEx this._onDidChangeExtensionsStatus.fire(this._registry.getAllExtensionDescriptions().map(e => e.identifier)); } - private _stopExtensionHostProcess(): void { + private _stopExtensionHosts(): void { let previouslyActivatedExtensionIds: ExtensionIdentifier[] = []; this._extensionHostActiveExtensions.forEach((value) => { previouslyActivatedExtensionIds.push(value); }); - for (const manager of this._extensionHostProcessManagers) { + for (const manager of this._extensionHostManagers) { manager.dispose(); } - this._extensionHostProcessManagers = []; + this._extensionHostManagers = []; this._extensionHostActiveExtensions = new Map(); - this._extensionHostProcessActivationTimes = new Map(); + this._extensionHostActivationTimes = new Map(); this._extensionHostExtensionRuntimeErrors = new Map(); if (previouslyActivatedExtensionIds.length > 0) { @@ -129,18 +139,19 @@ export abstract class AbstractExtensionService extends Disposable implements IEx } } - private _startExtensionHostProcess(isInitialStart: boolean, initialActivationEvents: string[]): void { - this._stopExtensionHostProcess(); + private _startExtensionHosts(isInitialStart: boolean, initialActivationEvents: string[]): void { + this._stopExtensionHosts(); - const processManagers = this._createExtensionHosts(isInitialStart, initialActivationEvents); - processManagers.forEach((processManager) => { + const extensionHosts = this._createExtensionHosts(isInitialStart); + extensionHosts.forEach((extensionHost) => { + const processManager = this._instantiationService.createInstance(ExtensionHostManager, extensionHost, initialActivationEvents); processManager.onDidExit(([code, signal]) => this._onExtensionHostCrashOrExit(processManager, code, signal)); processManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ isResponsive: responsiveState === ResponsiveState.Responsive }); }); - this._extensionHostProcessManagers.push(processManager); + this._extensionHostManagers.push(processManager); }); } - private _onExtensionHostCrashOrExit(extensionHost: ExtensionHostProcessManager, code: number, signal: string | null): void { + private _onExtensionHostCrashOrExit(extensionHost: ExtensionHostManager, code: number, signal: string | null): void { // Unexpected termination if (!this._isExtensionDevHost) { @@ -151,9 +162,9 @@ export abstract class AbstractExtensionService extends Disposable implements IEx this._onExtensionHostExit(code); } - protected _onExtensionHostCrashed(extensionHost: ExtensionHostProcessManager, code: number, signal: string | null): void { + protected _onExtensionHostCrashed(extensionHost: ExtensionHostManager, code: number, signal: string | null): void { console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal); - this._stopExtensionHostProcess(); + this._stopExtensionHosts(); } //#region IExtensionService @@ -167,12 +178,12 @@ export abstract class AbstractExtensionService extends Disposable implements IEx } public restartExtensionHost(): void { - this._stopExtensionHostProcess(); - this._startExtensionHostProcess(false, Array.from(this._allRequestedActivateEvents.keys())); + this._stopExtensionHosts(); + this._startExtensionHosts(false, Array.from(this._allRequestedActivateEvents.keys())); } protected startExtensionHost(): void { - this._startExtensionHostProcess(false, Array.from(this._allRequestedActivateEvents.keys())); + this._startExtensionHosts(false, Array.from(this._allRequestedActivateEvents.keys())); } public activateByEvent(activationEvent: string): Promise { @@ -200,7 +211,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx private _activateByEvent(activationEvent: string): Promise { const result = Promise.all( - this._extensionHostProcessManagers.map(extHostManager => extHostManager.activateByEvent(activationEvent)) + this._extensionHostManagers.map(extHostManager => extHostManager.activateByEvent(activationEvent)) ).then(() => { }); this._onWillActivateByEvent.fire({ event: activationEvent, @@ -248,7 +259,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx const extensionKey = ExtensionIdentifier.toKey(extension.identifier); result[extension.identifier.value] = { messages: this._extensionsMessages.get(extensionKey) || [], - activationTimes: this._extensionHostProcessActivationTimes.get(extensionKey), + activationTimes: this._extensionHostActivationTimes.get(extensionKey), runtimeErrors: this._extensionHostExtensionRuntimeErrors.get(extensionKey) || [], }; } @@ -261,7 +272,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx } public async setRemoteEnvironment(env: { [key: string]: string | null }): Promise { - await this._extensionHostProcessManagers + await this._extensionHostManagers .map(manager => manager.setRemoteEnvironment(env)); } @@ -275,6 +286,14 @@ export abstract class AbstractExtensionService extends Disposable implements IEx } } + protected _checkEnabledAndProposedAPI(extensions: IExtensionDescription[]): IExtensionDescription[] { + // enable or disable proposed API per extension + this._checkEnableProposedApi(extensions); + + // keep only enabled extensions + return extensions.filter(extension => this._isEnabled(extension)); + } + private _isExtensionUnderDevelopment(extension: IExtensionDescription): boolean { if (this._environmentService.isExtensionDevelopment) { const extDevLocs = this._environmentService.extensionDevelopmentLocationURI; @@ -291,21 +310,17 @@ export abstract class AbstractExtensionService extends Disposable implements IEx } protected _isEnabled(extension: IExtensionDescription): boolean { - return !this._isDisabled(extension); - } - - protected _isDisabled(extension: IExtensionDescription): boolean { if (this._isExtensionUnderDevelopment(extension)) { // Never disable extensions under development - return false; + return true; } if (ExtensionIdentifier.equals(extension.identifier, BetterMergeId)) { // Check if this is the better merge extension which was migrated to a built-in extension - return true; + return false; } - return !this._extensionEnablementService.isEnabled(toExtension(extension)); + return this._extensionEnablementService.isEnabled(toExtension(extension)); } protected _doHandleExtensionPoints(affectedExtensions: IExtensionDescription[]): void { @@ -413,7 +428,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx public async _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise { const results = await Promise.all( - this._extensionHostProcessManagers.map(manager => manager.activate(extensionId, reason)) + this._extensionHostManagers.map(manager => manager.activate(extensionId, reason)) ); const activated = results.some(e => e); if (!activated) { @@ -426,7 +441,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx } public _onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void { - this._extensionHostProcessActivationTimes.set(ExtensionIdentifier.toKey(extensionId), new ActivationTimes(codeLoadingTime, activateCallTime, activateResolvedTime, activationReason)); + this._extensionHostActivationTimes.set(ExtensionIdentifier.toKey(extensionId), new ActivationTimes(codeLoadingTime, activateCallTime, activateResolvedTime, activationReason)); this._onDidChangeExtensionsStatus.fire([extensionId]); } @@ -441,7 +456,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx //#endregion - protected abstract _createExtensionHosts(isInitialStart: boolean, initialActivationEvents: string[]): ExtensionHostProcessManager[]; + protected abstract _createExtensionHosts(isInitialStart: boolean): IExtensionHost[]; protected abstract _scanAndHandleExtensions(): Promise; public abstract _onExtensionHostExit(code: number): void; } diff --git a/src/vs/workbench/services/extensions/common/extensionHostMain.ts b/src/vs/workbench/services/extensions/common/extensionHostMain.ts index 93d9a1d107d..552e08e1917 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostMain.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostMain.ts @@ -132,14 +132,13 @@ export class ExtensionHostMain { private static _transform(initData: IInitData, rpcProtocol: RPCProtocol): IInitData { initData.extensions.forEach((ext) => (ext).extensionLocation = URI.revive(rpcProtocol.transformIncomingURIs(ext.extensionLocation))); initData.environment.appRoot = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.appRoot)); - initData.environment.appSettingsHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.appSettingsHome)); const extDevLocs = initData.environment.extensionDevelopmentLocationURI; if (extDevLocs) { initData.environment.extensionDevelopmentLocationURI = extDevLocs.map(url => URI.revive(rpcProtocol.transformIncomingURIs(url))); } initData.environment.extensionTestsLocationURI = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.extensionTestsLocationURI)); initData.environment.globalStorageHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.globalStorageHome)); - initData.environment.userHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.userHome)); + initData.environment.workspaceStorageHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.workspaceStorageHome)); initData.logsLocation = URI.revive(rpcProtocol.transformIncomingURIs(initData.logsLocation)); initData.logFile = URI.revive(rpcProtocol.transformIncomingURIs(initData.logFile)); initData.workspace = rpcProtocol.transformIncomingURIs(initData.workspace); diff --git a/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/common/extensionHostManager.ts similarity index 73% rename from src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts rename to src/vs/workbench/services/extensions/common/extensionHostManager.ts index 12bf5c14dc4..484444e968d 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostManager.ts @@ -21,51 +21,49 @@ import { registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { StopWatch } from 'vs/base/common/stopwatch'; import { VSBuffer } from 'vs/base/common/buffer'; -import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionHost, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator'; // Enable to see detailed message communication between window and extension host const LOG_EXTENSION_HOST_COMMUNICATION = false; const LOG_USE_COLORS = true; -const NO_OP_VOID_PROMISE = Promise.resolve(undefined); - -export class ExtensionHostProcessManager extends Disposable { +export class ExtensionHostManager extends Disposable { + public readonly kind: ExtensionHostKind; public readonly onDidExit: Event<[number, string | null]>; private readonly _onDidChangeResponsiveState: Emitter = this._register(new Emitter()); public readonly onDidChangeResponsiveState: Event = this._onDidChangeResponsiveState.event; /** - * A map of already activated events to speed things up if the same activation event is triggered multiple times. + * A map of already requested activation events to speed things up if the same activation event is triggered multiple times. */ - private readonly _extensionHostProcessFinishedActivateEvents: { [activationEvent: string]: boolean; }; - private _extensionHostProcessRPCProtocol: RPCProtocol | null; - private readonly _extensionHostProcessCustomers: IDisposable[]; - private readonly _extensionHostProcessWorker: IExtensionHostStarter; + private readonly _cachedActivationEvents: Map>; + private _rpcProtocol: RPCProtocol | null; + private readonly _customers: IDisposable[]; + private readonly _extensionHost: IExtensionHost; /** * winjs believes a proxy is a promise because it has a `then` method, so wrap the result in an object. */ - private _extensionHostProcessProxy: Promise<{ value: ExtHostExtensionServiceShape; } | null> | null; + private _proxy: Promise<{ value: ExtHostExtensionServiceShape; } | null> | null; private _resolveAuthorityAttempt: number; constructor( - public readonly isLocal: boolean, - extensionHostProcessWorker: IExtensionHostStarter, - private readonly _remoteAuthority: string | null, + extensionHost: IExtensionHost, initialActivationEvents: string[], @IInstantiationService private readonly _instantiationService: IInstantiationService, @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, ) { super(); - this._extensionHostProcessFinishedActivateEvents = Object.create(null); - this._extensionHostProcessRPCProtocol = null; - this._extensionHostProcessCustomers = []; + this._cachedActivationEvents = new Map>(); + this._rpcProtocol = null; + this._customers = []; - this._extensionHostProcessWorker = extensionHostProcessWorker; - this.onDidExit = this._extensionHostProcessWorker.onExit; - this._extensionHostProcessProxy = this._extensionHostProcessWorker.start()!.then( + this._extensionHost = extensionHost; + this.kind = this._extensionHost.kind; + this.onDidExit = this._extensionHost.onExit; + this._proxy = this._extensionHost.start()!.then( (protocol) => { return { value: this._createExtensionHostCustomers(protocol) }; }, @@ -75,7 +73,7 @@ export class ExtensionHostProcessManager extends Disposable { return null; } ); - this._extensionHostProcessProxy.then(() => { + this._proxy.then(() => { initialActivationEvents.forEach((activationEvent) => this.activateByEvent(activationEvent)); this._register(registerLatencyTestProvider({ measure: () => this.measure() @@ -85,27 +83,27 @@ export class ExtensionHostProcessManager extends Disposable { } public dispose(): void { - if (this._extensionHostProcessWorker) { - this._extensionHostProcessWorker.dispose(); + if (this._extensionHost) { + this._extensionHost.dispose(); } - if (this._extensionHostProcessRPCProtocol) { - this._extensionHostProcessRPCProtocol.dispose(); + if (this._rpcProtocol) { + this._rpcProtocol.dispose(); } - for (let i = 0, len = this._extensionHostProcessCustomers.length; i < len; i++) { - const customer = this._extensionHostProcessCustomers[i]; + for (let i = 0, len = this._customers.length; i < len; i++) { + const customer = this._customers[i]; try { customer.dispose(); } catch (err) { errors.onUnexpectedError(err); } } - this._extensionHostProcessProxy = null; + this._proxy = null; super.dispose(); } private async measure(): Promise { - const proxy = await this._getExtensionHostProcessProxy(); + const proxy = await this._getProxy(); if (!proxy) { return null; } @@ -113,18 +111,18 @@ export class ExtensionHostProcessManager extends Disposable { const down = await this._measureDown(proxy); const up = await this._measureUp(proxy); return { - remoteAuthority: this._remoteAuthority, + remoteAuthority: this._extensionHost.remoteAuthority, latency, down, up }; } - private async _getExtensionHostProcessProxy(): Promise { - if (!this._extensionHostProcessProxy) { + private async _getProxy(): Promise { + if (!this._proxy) { return null; } - const p = await this._extensionHostProcessProxy; + const p = await this._proxy; if (!p) { return null; } @@ -159,7 +157,7 @@ export class ExtensionHostProcessManager extends Disposable { const sw = StopWatch.create(true); await proxy.$test_up(buff); sw.stop(); - return ExtensionHostProcessManager._convert(SIZE, sw.elapsed()); + return ExtensionHostManager._convert(SIZE, sw.elapsed()); } private async _measureDown(proxy: ExtHostExtensionServiceShape): Promise { @@ -168,7 +166,7 @@ export class ExtensionHostProcessManager extends Disposable { const sw = StopWatch.create(true); await proxy.$test_down(SIZE); sw.stop(); - return ExtensionHostProcessManager._convert(SIZE, sw.elapsed()); + return ExtensionHostManager._convert(SIZE, sw.elapsed()); } private _createExtensionHostCustomers(protocol: IMessagePassingProtocol): ExtHostExtensionServiceShape { @@ -178,13 +176,14 @@ export class ExtensionHostProcessManager extends Disposable { logger = new RPCLogger(); } - this._extensionHostProcessRPCProtocol = new RPCProtocol(protocol, logger); - this._register(this._extensionHostProcessRPCProtocol.onDidChangeResponsiveState((responsiveState: ResponsiveState) => this._onDidChangeResponsiveState.fire(responsiveState))); + this._rpcProtocol = new RPCProtocol(protocol, logger); + this._register(this._rpcProtocol.onDidChangeResponsiveState((responsiveState: ResponsiveState) => this._onDidChangeResponsiveState.fire(responsiveState))); const extHostContext: IExtHostContext = { - remoteAuthority: this._remoteAuthority! /* TODO: alexdima, remove not-null assertion */, - getProxy: (identifier: ProxyIdentifier): T => this._extensionHostProcessRPCProtocol!.getProxy(identifier), - set: (identifier: ProxyIdentifier, instance: R): R => this._extensionHostProcessRPCProtocol!.set(identifier, instance), - assertRegistered: (identifiers: ProxyIdentifier[]): void => this._extensionHostProcessRPCProtocol!.assertRegistered(identifiers), + remoteAuthority: this._extensionHost.remoteAuthority, + getProxy: (identifier: ProxyIdentifier): T => this._rpcProtocol!.getProxy(identifier), + set: (identifier: ProxyIdentifier, instance: R): R => this._rpcProtocol!.set(identifier, instance), + assertRegistered: (identifiers: ProxyIdentifier[]): void => this._rpcProtocol!.assertRegistered(identifiers), + drain: (): Promise => this._rpcProtocol!.drain(), }; // Named customers @@ -192,26 +191,26 @@ export class ExtensionHostProcessManager extends Disposable { for (let i = 0, len = namedCustomers.length; i < len; i++) { const [id, ctor] = namedCustomers[i]; const instance = this._instantiationService.createInstance(ctor, extHostContext); - this._extensionHostProcessCustomers.push(instance); - this._extensionHostProcessRPCProtocol.set(id, instance); + this._customers.push(instance); + this._rpcProtocol.set(id, instance); } // Customers const customers = ExtHostCustomersRegistry.getCustomers(); for (const ctor of customers) { const instance = this._instantiationService.createInstance(ctor, extHostContext); - this._extensionHostProcessCustomers.push(instance); + this._customers.push(instance); } // Check that no named customers are missing const expected: ProxyIdentifier[] = Object.keys(MainContext).map((key) => (MainContext)[key]); - this._extensionHostProcessRPCProtocol.assertRegistered(expected); + this._rpcProtocol.assertRegistered(expected); - return this._extensionHostProcessRPCProtocol.getProxy(ExtHostContext.ExtHostExtensionService); + return this._rpcProtocol.getProxy(ExtHostContext.ExtHostExtensionService); } public async activate(extension: ExtensionIdentifier, reason: ExtensionActivationReason): Promise { - const proxy = await this._getExtensionHostProcessProxy(); + const proxy = await this._getProxy(); if (!proxy) { return false; } @@ -219,27 +218,31 @@ export class ExtensionHostProcessManager extends Disposable { } public activateByEvent(activationEvent: string): Promise { - if (this._extensionHostProcessFinishedActivateEvents[activationEvent] || !this._extensionHostProcessProxy) { - return NO_OP_VOID_PROMISE; + if (!this._cachedActivationEvents.has(activationEvent)) { + this._cachedActivationEvents.set(activationEvent, this._activateByEvent(activationEvent)); } - return this._extensionHostProcessProxy.then((proxy) => { - if (!proxy) { - // this case is already covered above and logged. - // i.e. the extension host could not be started - return NO_OP_VOID_PROMISE; - } - return proxy.value.$activateByEvent(activationEvent); - }).then(() => { - this._extensionHostProcessFinishedActivateEvents[activationEvent] = true; - }); + return this._cachedActivationEvents.get(activationEvent)!; + } + + private async _activateByEvent(activationEvent: string): Promise { + if (!this._proxy) { + return; + } + const proxy = await this._proxy; + if (!proxy) { + // this case is already covered above and logged. + // i.e. the extension host could not be started + return; + } + return proxy.value.$activateByEvent(activationEvent); } public async getInspectPort(tryEnableInspector: boolean): Promise { - if (this._extensionHostProcessWorker) { + if (this._extensionHost) { if (tryEnableInspector) { - await this._extensionHostProcessWorker.enableInspectPort(); + await this._extensionHost.enableInspectPort(); } - let port = this._extensionHostProcessWorker.getInspectPort(); + let port = this._extensionHost.getInspectPort(); if (port) { return port; } @@ -260,7 +263,7 @@ export class ExtensionHostProcessManager extends Disposable { } }); } - const proxy = await this._getExtensionHostProcessProxy(); + const proxy = await this._getProxy(); if (!proxy) { throw new Error(`Cannot resolve authority`); } @@ -274,7 +277,7 @@ export class ExtensionHostProcessManager extends Disposable { } public async start(enabledExtensionIds: ExtensionIdentifier[]): Promise { - const proxy = await this._getExtensionHostProcessProxy(); + const proxy = await this._getProxy(); if (!proxy) { return; } @@ -282,7 +285,7 @@ export class ExtensionHostProcessManager extends Disposable { } public async deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise { - const proxy = await this._getExtensionHostProcessProxy(); + const proxy = await this._getProxy(); if (!proxy) { return; } @@ -290,7 +293,7 @@ export class ExtensionHostProcessManager extends Disposable { } public async setRemoteEnvironment(env: { [key: string]: string | null }): Promise { - const proxy = await this._getExtensionHostProcessProxy(); + const proxy = await this._getProxy(); if (!proxy) { return; } @@ -394,7 +397,7 @@ registerAction2(class MeasureExtHostLatencyAction extends Action2 { value: nls.localize('measureExtHostLatency', "Measure Extension Host Latency"), original: 'Measure Extension Host Latency' }, - category: nls.localize('developer', "Developer"), + category: { value: nls.localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer"), original: 'Developer' }, f1: true }); } diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 074447cab22..652a2603156 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -24,6 +24,8 @@ export const nullExtensionDescription = Object.freeze({ isBuiltin: false, }); +export const webWorkerExtHostConfig = 'extensions.webWorker'; + export const IExtensionService = createDecorator('extensionService'); export interface IMessage { @@ -84,7 +86,15 @@ export interface IExtensionHostProfile { getAggregatedTimes(): Map; } -export interface IExtensionHostStarter { +export const enum ExtensionHostKind { + LocalProcess, + LocalWebWorker, + Remote +} + +export interface IExtensionHost { + readonly kind: ExtensionHostKind; + readonly remoteAuthority: string | null; readonly onExit: Event<[number, string | null]>; start(): Promise | null; @@ -131,7 +141,7 @@ export interface IResponsiveStateChangeEvent { } export interface IExtensionService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * An event emitted when extensions are registered after their extension points got handled. @@ -257,9 +267,20 @@ export function toExtension(extensionDescription: IExtensionDescription): IExten }; } +export function toExtensionDescription(extension: IExtension): IExtensionDescription { + return { + identifier: new ExtensionIdentifier(extension.identifier.id), + isBuiltin: extension.type === ExtensionType.System, + isUnderDevelopment: false, + extensionLocation: extension.location, + ...extension.manifest, + uuid: extension.identifier.uuid + }; +} + export class NullExtensionService implements IExtensionService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; onDidRegisterExtensions: Event = Event.None; onDidChangeExtensionsStatus: Event = Event.None; onDidChangeExtensions: Event = Event.None; diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index 6c71f033134..28b7f1e14b3 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -11,8 +11,8 @@ import { EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/co import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { IMessage } from 'vs/workbench/services/extensions/common/extensions'; -import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { values } from 'vs/base/common/map'; +import { ExtensionIdentifier, IExtensionDescription, EXTENSION_CATEGORIES } from 'vs/platform/extensions/common/extensions'; +import { toArray } from 'vs/base/common/arrays'; const schemaRegistry = Registry.as(Extensions.JSONContribution); export type ExtensionKind = 'workspace' | 'ui' | undefined; @@ -150,11 +150,13 @@ const extensionKindSchema: IJSONSchema = { type: 'string', enum: [ 'ui', - 'workspace' + 'workspace', + 'web' ], enumDescriptions: [ nls.localize('ui', "UI extension kind. In a remote window, such extensions are enabled only when available on the local machine."), - nls.localize('workspace', "Workspace extension kind. In a remote window, such extensions are enabled only when available on the remote.") + nls.localize('workspace', "Workspace extension kind. In a remote window, such extensions are enabled only when available on the remote."), + nls.localize('web', "Web worker extension kind. Such an extension can execute in a web worker extension host.") ], }; @@ -187,7 +189,7 @@ export const schema: IJSONSchema = { items: { oneOf: [{ type: 'string', - enum: ['Programming Languages', 'Snippets', 'Linters', 'Themes', 'Debuggers', 'Other', 'Keymaps', 'Formatters', 'Extension Packs', 'SCM Providers', 'Azure', 'Language Packs'], + enum: EXTENSION_CATEGORIES, }, { type: 'string', @@ -269,6 +271,11 @@ export const schema: IJSONSchema = { description: nls.localize('vscode.extension.activationEvents.workspaceContains', 'An activation event emitted whenever a folder is opened that contains at least a file matching the specified glob pattern.'), body: 'workspaceContains:${4:filePattern}' }, + { + label: 'onStartupFinished', + description: nls.localize('vscode.extension.activationEvents.onStartupFinished', 'An activation event emitted after the start-up finished (after all `*` activated extensions have finished activating).'), + body: 'onStartupFinished' + }, { label: 'onFileSystem', description: nls.localize('vscode.extension.activationEvents.onFileSystem', 'An activation event emitted whenever a file or folder is accessed with the given scheme.'), @@ -439,7 +446,7 @@ export class ExtensionsRegistryImpl { } public getExtensionPoints(): ExtensionPoint[] { - return values(this._extensionPoints); + return toArray(this._extensionPoints.values()); } } diff --git a/src/vs/workbench/services/extensions/common/extensionsUtil.ts b/src/vs/workbench/services/extensions/common/extensionsUtil.ts index 9e8352ac881..65e532ee58d 100644 --- a/src/vs/workbench/services/extensions/common/extensionsUtil.ts +++ b/src/vs/workbench/services/extensions/common/extensionsUtil.ts @@ -20,6 +20,11 @@ export function prefersExecuteOnWorkspace(manifest: IExtensionManifest, productS return (extensionKind.length > 0 && extensionKind[0] === 'workspace'); } +export function prefersExecuteOnWeb(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean { + const extensionKind = getExtensionKind(manifest, productService, configurationService); + return (extensionKind.length > 0 && extensionKind[0] === 'web'); +} + export function canExecuteOnUI(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean { const extensionKind = getExtensionKind(manifest, productService, configurationService); return extensionKind.some(kind => kind === 'ui'); @@ -54,18 +59,29 @@ export function getExtensionKind(manifest: IExtensionManifest, productService: I return toArray(result); } + return deduceExtensionKind(manifest); +} + +export function deduceExtensionKind(manifest: IExtensionManifest): ExtensionKind[] { // Not an UI extension if it has main if (manifest.main) { + if (manifest.browser) { + return ['workspace', 'web']; + } return ['workspace']; } - // Not an UI extension if it has dependencies or an extension pack + if (manifest.browser) { + return ['web']; + } + + // Not an UI nor web extension if it has dependencies or an extension pack if (isNonEmptyArray(manifest.extensionDependencies) || isNonEmptyArray(manifest.extensionPack)) { return ['workspace']; } if (manifest.contributes) { - // Not an UI extension if it has no ui contributions + // Not an UI nor web extension if it has no ui contributions for (const contribution of Object.keys(manifest.contributes)) { if (!isUIExtensionPoint(contribution)) { return ['workspace']; @@ -73,7 +89,7 @@ export function getExtensionKind(manifest: IExtensionManifest, productService: I } } - return ['ui', 'workspace']; + return ['ui', 'workspace', 'web']; } let _uiExtensionPoints: Set | null = null; diff --git a/src/vs/workbench/services/extensions/common/proxyIdentifier.ts b/src/vs/workbench/services/extensions/common/proxyIdentifier.ts index e0e9999a62f..edadcae9eb2 100644 --- a/src/vs/workbench/services/extensions/common/proxyIdentifier.ts +++ b/src/vs/workbench/services/extensions/common/proxyIdentifier.ts @@ -18,6 +18,11 @@ export interface IRPCProtocol { * Assert these identifiers are already registered via `.set`. */ assertRegistered(identifiers: ProxyIdentifier[]): void; + + /** + * Wait for the write buffer (if applicable) to become empty. + */ + drain(): Promise; } export class ProxyIdentifier { diff --git a/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts b/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts similarity index 70% rename from src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts rename to src/vs/workbench/services/extensions/common/remoteExtensionHost.ts index cb751b89e8f..fea56076567 100644 --- a/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts +++ b/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts @@ -13,10 +13,9 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IInitData, UIKind } from 'vs/workbench/api/common/extHost.protocol'; import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; -import { IExtensionHostStarter, ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionHost, ExtensionHostLogFileName, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions'; import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; -import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; -import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { IRemoteAuthorityResolverService, IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver'; import * as platform from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -33,25 +32,36 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output/common/output'; import { localize } from 'vs/nls'; -export interface IInitDataProvider { - readonly remoteAuthority: string; - getInitData(): Promise; +export interface IRemoteExtensionHostInitData { + readonly connectionData: IRemoteConnectionData | null; + readonly pid: number; + readonly appRoot: URI; + readonly extensionHostLogsPath: URI; + readonly globalStorageHome: URI; + readonly workspaceStorageHome: URI; + readonly extensions: IExtensionDescription[]; + readonly allExtensions: IExtensionDescription[]; } -export class RemoteExtensionHostClient extends Disposable implements IExtensionHostStarter { +export interface IRemoteExtensionHostDataProvider { + readonly remoteAuthority: string; + getInitData(): Promise; +} + +export class RemoteExtensionHost extends Disposable implements IExtensionHost { + + public readonly kind = ExtensionHostKind.Remote; + public readonly remoteAuthority: string; private _onExit: Emitter<[number, string | null]> = this._register(new Emitter<[number, string | null]>()); public readonly onExit: Event<[number, string | null]> = this._onExit.event; private _protocol: PersistentProtocol | null; - + private _terminating: boolean; private readonly _isExtensionDevHost: boolean; - private _terminating: boolean; - constructor( - private readonly _allExtensions: Promise, - private readonly _initDataProvider: IInitDataProvider, + private readonly _initDataProvider: IRemoteExtensionHostDataProvider, private readonly _socketFactory: ISocketFactory, @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, @@ -65,6 +75,7 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH @ISignService private readonly _signService: ISignService ) { super(); + this.remoteAuthority = this._initDataProvider.remoteAuthority; this._protocol = null; this._terminating = false; @@ -85,7 +96,8 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH } }, signService: this._signService, - logService: this._logService + logService: this._logService, + ipcLogger: null }; return this.remoteAuthorityResolverService.resolveAuthority(this._initDataProvider.remoteAuthority).then((resolverResult) => { @@ -189,51 +201,50 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH this._onExit.fire([0, null]); } - private _createExtHostInitData(isExtensionDevelopmentDebug: boolean): Promise { - return Promise.all([this._allExtensions, this._telemetryService.getTelemetryInfo(), this._initDataProvider.getInitData()]).then(([allExtensions, telemetryInfo, remoteExtensionHostData]) => { - // Collect all identifiers for extension ids which can be considered "resolved" - const resolvedExtensions = allExtensions.filter(extension => !extension.main).map(extension => extension.identifier); - const hostExtensions = allExtensions.filter(extension => extension.main && extension.api === 'none').map(extension => extension.identifier); - const workspace = this._contextService.getWorkspace(); - const r: IInitData = { - commit: this._productService.commit, - version: this._productService.version, - parentPid: remoteExtensionHostData.pid, - environment: { - isExtensionDevelopmentDebug, - appRoot: remoteExtensionHostData.appRoot, - appSettingsHome: remoteExtensionHostData.appSettingsHome, - appName: this._productService.nameLong, - appUriScheme: this._productService.urlProtocol, - appLanguage: platform.language, - extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, - extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, - globalStorageHome: remoteExtensionHostData.globalStorageHome, - userHome: remoteExtensionHostData.userHome, - webviewResourceRoot: this._environmentService.webviewResourceRoot, - webviewCspSource: this._environmentService.webviewCspSource, - }, - workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : { - configuration: workspace.configuration, - id: workspace.id, - name: this._labelService.getWorkspaceLabel(workspace) - }, - remote: { - isRemote: true, - authority: this._initDataProvider.remoteAuthority - }, - resolvedExtensions: resolvedExtensions, - hostExtensions: hostExtensions, - extensions: remoteExtensionHostData.extensions, - telemetryInfo, - logLevel: this._logService.getLevel(), - logsLocation: remoteExtensionHostData.extensionHostLogsPath, - logFile: joinPath(remoteExtensionHostData.extensionHostLogsPath, `${ExtensionHostLogFileName}.log`), - autoStart: true, - uiKind: platform.isWeb ? UIKind.Web : UIKind.Desktop - }; - return r; - }); + private async _createExtHostInitData(isExtensionDevelopmentDebug: boolean): Promise { + const [telemetryInfo, remoteInitData] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._initDataProvider.getInitData()]); + + // Collect all identifiers for extension ids which can be considered "resolved" + const resolvedExtensions = remoteInitData.allExtensions.filter(extension => !extension.main && !extension.browser).map(extension => extension.identifier); + const hostExtensions = remoteInitData.allExtensions.filter(extension => (extension.main || extension.browser) && extension.api === 'none').map(extension => extension.identifier); + const workspace = this._contextService.getWorkspace(); + return { + commit: this._productService.commit, + version: this._productService.version, + parentPid: remoteInitData.pid, + environment: { + isExtensionDevelopmentDebug, + appRoot: remoteInitData.appRoot, + appName: this._productService.nameLong, + appUriScheme: this._productService.urlProtocol, + appLanguage: platform.language, + extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, + extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, + globalStorageHome: remoteInitData.globalStorageHome, + workspaceStorageHome: remoteInitData.workspaceStorageHome, + webviewResourceRoot: this._environmentService.webviewResourceRoot, + webviewCspSource: this._environmentService.webviewCspSource, + }, + workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : { + configuration: workspace.configuration, + id: workspace.id, + name: this._labelService.getWorkspaceLabel(workspace) + }, + remote: { + isRemote: true, + authority: this._initDataProvider.remoteAuthority, + connectionData: remoteInitData.connectionData + }, + resolvedExtensions: resolvedExtensions, + hostExtensions: hostExtensions, + extensions: remoteInitData.extensions, + telemetryInfo, + logLevel: this._logService.getLevel(), + logsLocation: remoteInitData.extensionHostLogsPath, + logFile: joinPath(remoteInitData.extensionHostLogsPath, `${ExtensionHostLogFileName}.log`), + autoStart: true, + uiKind: platform.isWeb ? UIKind.Web : UIKind.Desktop + }; } getInspectPort(): number | undefined { diff --git a/src/vs/workbench/services/extensions/common/rpcProtocol.ts b/src/vs/workbench/services/extensions/common/rpcProtocol.ts index 022fd1f4c41..199ea6e15ef 100644 --- a/src/vs/workbench/services/extensions/common/rpcProtocol.ts +++ b/src/vs/workbench/services/extensions/common/rpcProtocol.ts @@ -115,6 +115,13 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { }); } + public drain(): Promise { + if (typeof this._protocol.drain === 'function') { + return this._protocol.drain(); + } + return Promise.resolve(); + } + private _onWillSendRequest(req: number): void { if (this._unacknowledgedCount === 0) { // Since this is the first request we are sending in a while, diff --git a/src/vs/workbench/services/extensions/common/staticExtensions.ts b/src/vs/workbench/services/extensions/common/staticExtensions.ts deleted file mode 100644 index 2bce503265d..00000000000 --- a/src/vs/workbench/services/extensions/common/staticExtensions.ts +++ /dev/null @@ -1,39 +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 { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IExtensionDescription, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; - -export const IStaticExtensionsService = createDecorator('IStaticExtensionsService'); - -export interface IStaticExtensionsService { - _serviceBrand: undefined; - getExtensions(): Promise; -} - -export class StaticExtensionsService implements IStaticExtensionsService { - - _serviceBrand: undefined; - - private readonly _descriptions: IExtensionDescription[] = []; - - constructor(@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService) { - const staticExtensions = environmentService.options && Array.isArray(environmentService.options.staticExtensions) ? environmentService.options.staticExtensions : []; - - this._descriptions = staticExtensions.map(data => { - identifier: new ExtensionIdentifier(`${data.packageJSON.publisher}.${data.packageJSON.name}`), - extensionLocation: data.extensionLocation, - ...data.packageJSON, - }); - } - - async getExtensions(): Promise { - return this._descriptions; - } -} - -registerSingleton(IStaticExtensionsService, StaticExtensionsService, true); diff --git a/src/vs/workbench/services/extensions/common/webWorkerIframe.ts b/src/vs/workbench/services/extensions/common/webWorkerIframe.ts new file mode 100644 index 00000000000..bf4731d3909 --- /dev/null +++ b/src/vs/workbench/services/extensions/common/webWorkerIframe.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. + *--------------------------------------------------------------------------------------------*/ + +export const WEB_WORKER_IFRAME = { + sha: 'sha256-rSINb5Ths99Zj4Ml59jEdHS4WbO+H5Iw+oyRmyi2MLw=', + js: ` +(function() { + const workerSrc = document.getElementById('vscode-worker-src').getAttribute('data-value'); + const worker = new Worker(workerSrc, { name: 'WorkerExtensionHost' }); + const vscodeWebWorkerExtHostId = document.getElementById('vscode-web-worker-ext-host-id').getAttribute('data-value'); + + worker.onmessage = (event) => { + const { data } = event; + if (!(data instanceof ArrayBuffer)) { + console.warn('Unknown data received', data); + window.parent.postMessage({ + vscodeWebWorkerExtHostId, + error: { + name: 'Error', + message: 'Unknown data received', + stack: [] + } + }, '*'); + return; + } + window.parent.postMessage({ + vscodeWebWorkerExtHostId, + data: data + }, '*', [data]); + }; + + worker.onerror = (event) => { + console.error(event.message, event.error); + window.parent.postMessage({ + vscodeWebWorkerExtHostId, + error: { + name: event.error ? event.error.name : '', + message: event.error ? event.error.message : '', + stack: event.error ? event.error.stack : [] + } + }, '*'); + }; + + window.addEventListener('message', function(event) { + if (event.source !== window.parent) { + return; + } + if (event.data.vscodeWebWorkerExtHostId !== vscodeWebWorkerExtHostId) { + return; + } + worker.postMessage(event.data.data, [event.data.data]); + }, false); +})(); +` +}; diff --git a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts index 922362dce3e..c8234ff6bce 100644 --- a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts +++ b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts @@ -18,7 +18,7 @@ import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/enviro import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { BUILTIN_MANIFEST_CACHE_FILE, MANIFEST_CACHE_FOLDER, USER_MANIFEST_CACHE_FILE, ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import product from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { ExtensionScanner, ExtensionScannerInput, IExtensionReference, IExtensionResolver, IRelaxedExtensionDescription } from 'vs/workbench/services/extensions/node/extensionPoints'; @@ -57,6 +57,7 @@ export class CachedExtensionScanner { @IWorkbenchEnvironmentService private readonly _environmentService: INativeWorkbenchEnvironmentService, @IWorkbenchExtensionEnablementService private readonly _extensionEnablementService: IWorkbenchExtensionEnablementService, @IHostService private readonly _hostService: IHostService, + @IProductService private readonly _productService: IProductService ) { this.scannedExtensions = new Promise((resolve, reject) => { this._scannedExtensionsResolve = resolve; @@ -68,8 +69,8 @@ export class CachedExtensionScanner { public async scanSingleExtension(path: string, isBuiltin: boolean, log: ILog): Promise { const translations = await this.translationConfig; - const version = product.version; - const commit = product.commit; + const version = this._productService.version; + const commit = this._productService.commit; const devMode = !!process.env['VSCODE_DEV']; const locale = platform.language; const input = new ExtensionScannerInput(version, commit, locale, devMode, path, isBuiltin, false, translations); @@ -79,7 +80,7 @@ export class CachedExtensionScanner { public async startScanningExtensions(log: ILog): Promise { try { const translations = await this.translationConfig; - const { system, user, development } = await CachedExtensionScanner._scanInstalledExtensions(this._hostService, this._notificationService, this._environmentService, this._extensionEnablementService, log, translations); + const { system, user, development } = await CachedExtensionScanner._scanInstalledExtensions(this._hostService, this._notificationService, this._environmentService, this._extensionEnablementService, this._productService, log, translations); let result = new Map(); system.forEach((systemExtension) => { @@ -239,12 +240,13 @@ export class CachedExtensionScanner { notificationService: INotificationService, environmentService: INativeWorkbenchEnvironmentService, extensionEnablementService: IWorkbenchExtensionEnablementService, + productService: IProductService, log: ILog, translations: Translations ): Promise<{ system: IExtensionDescription[], user: IExtensionDescription[], development: IExtensionDescription[] }> { - const version = product.version; - const commit = product.commit; + const version = productService.version; + const commit = productService.commit; const devMode = !!process.env['VSCODE_DEV']; const locale = platform.language; @@ -260,7 +262,7 @@ export class CachedExtensionScanner { let finalBuiltinExtensions: Promise = builtinExtensions; if (devMode) { - const builtInExtensions = Promise.resolve(product.builtInExtensions || []); + const builtInExtensions = Promise.resolve(productService.builtInExtensions || []); const controlFilePath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions', 'control.json'); const controlFile = pfs.readFile(controlFilePath, 'utf8') diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 88f766b777b..512b1c906ed 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -3,45 +3,44 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ipcRenderer as ipc } from 'electron'; -import { ExtensionHostProcessWorker } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; +import { LocalProcessExtensionHost } from 'vs/workbench/services/extensions/electron-browser/localProcessExtensionHost'; import { CachedExtensionScanner } from 'vs/workbench/services/extensions/electron-browser/cachedExtensionScanner'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { AbstractExtensionService } from 'vs/workbench/services/extensions/common/abstractExtensionService'; +import { AbstractExtensionService, parseScannedExtension } from 'vs/workbench/services/extensions/common/abstractExtensionService'; import * as nls from 'vs/nls'; import { runWhenIdle } from 'vs/base/common/async'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IInitDataProvider, RemoteExtensionHostClient } from 'vs/workbench/services/extensions/common/remoteExtensionHostClient'; +import { IRemoteExtensionHostDataProvider, RemoteExtensionHost, IRemoteExtensionHostInitData } from 'vs/workbench/services/extensions/common/remoteExtensionHost'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil'; -import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions'; -import { ExtensionHostProcessManager } from 'vs/workbench/services/extensions/common/extensionHostProcessManager'; -import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { IExtensionService, toExtension, ExtensionHostKind, IExtensionHost, webWorkerExtHostConfig } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager'; +import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription, ExtensionKind } from 'vs/platform/extensions/common/extensions'; import { Schemas } from 'vs/base/common/network'; import { IFileService } from 'vs/platform/files/common/files'; import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; import { IProductService } from 'vs/platform/product/common/productService'; import { Logger } from 'vs/workbench/services/extensions/common/extensionPoints'; import { flatten } from 'vs/base/common/arrays'; -import { IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; import { IRemoteExplorerService } from 'vs/workbench/services/remote/common/remoteExplorerService'; -import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; +import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; +import { WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHost'; +import { IExtensionActivationHost as IWorkspaceContainsActivationHost, checkGlobFileExists, checkActivateWorkspaceContainsExtension } from 'vs/workbench/api/common/shared/workspaceContains'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { exists } from 'vs/base/node/pfs'; class DeltaExtensionsQueueItem { constructor( @@ -52,8 +51,9 @@ class DeltaExtensionsQueueItem { export class ExtensionService extends AbstractExtensionService implements IExtensionService { - private readonly _remoteExtensionsEnvironmentData: Map; - + private readonly _enableLocalWebWorker: boolean; + private readonly _remoteInitData: Map; + private _runningLocation: Map; private readonly _extensionScanner: CachedExtensionScanner; private _deltaExtensionsQueue: DeltaExtensionsQueueItem[]; @@ -70,11 +70,12 @@ export class ExtensionService extends AbstractExtensionService implements IExten @IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService, @IConfigurationService private readonly _configurationService: IConfigurationService, @ILifecycleService private readonly _lifecycleService: ILifecycleService, - @IStaticExtensionsService private readonly _staticExtensions: IStaticExtensionsService, + @IWebExtensionsScannerService private readonly _webExtensionsScannerService: IWebExtensionsScannerService, @IElectronService private readonly _electronService: IElectronService, @IHostService private readonly _hostService: IHostService, @IRemoteExplorerService private readonly _remoteExplorerService: IRemoteExplorerService, @IExtensionGalleryService private readonly _extensionGalleryService: IExtensionGalleryService, + @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, ) { super( instantiationService, @@ -86,16 +87,10 @@ export class ExtensionService extends AbstractExtensionService implements IExten productService ); - if (this._extensionEnablementService.allUserExtensionsDisabled) { - this._notificationService.prompt(Severity.Info, nls.localize('extensionsDisabled', "All installed extensions are temporarily disabled. Reload the window to return to the previous state."), [{ - label: nls.localize('Reload', "Reload"), - run: () => { - this._hostService.reload(); - } - }]); - } + this._enableLocalWebWorker = this._configurationService.getValue(webWorkerExtHostConfig); - this._remoteExtensionsEnvironmentData = new Map(); + this._remoteInitData = new Map(); + this._runningLocation = new Map(); this._extensionScanner = instantiationService.createInstance(CachedExtensionScanner); this._deltaExtensionsQueue = []; @@ -143,8 +138,28 @@ export class ExtensionService extends AbstractExtensionService implements IExten this._initialize(); }, 50 /*max delay*/); }); + + // delay notification for extensions disabled until workbench restored + if (this._extensionEnablementService.allUserExtensionsDisabled) { + this._lifecycleService.when(LifecyclePhase.Restored).then(() => { + this._notificationService.prompt(Severity.Info, nls.localize('extensionsDisabled', "All installed extensions are temporarily disabled. Reload the window to return to the previous state."), [{ + label: nls.localize('Reload', "Reload"), + run: () => { + this._hostService.reload(); + } + }]); + }); + } } + private _getExtensionHostManager(kind: ExtensionHostKind): ExtensionHostManager | null { + for (const extensionHostManager of this._extensionHostManagers) { + if (extensionHostManager.kind === kind) { + return extensionHostManager; + } + } + return null; + } //#region deltaExtensions @@ -223,11 +238,12 @@ export class ExtensionService extends AbstractExtensionService implements IExten this._checkEnableProposedApi(toAdd); // Update extension points - this._rehandleExtensionPoints(([]).concat(toAdd).concat(toRemove)); + this._doHandleExtensionPoints(([]).concat(toAdd).concat(toRemove)); // Update the extension host - if (this._extensionHostProcessManagers.length > 0) { - await this._extensionHostProcessManagers[0].deltaExtensions(toAdd, toRemove.map(e => e.identifier)); + const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess); + if (localProcessExtensionHost) { + await localProcessExtensionHost.deltaExtensions(toAdd, toRemove.map(e => e.identifier)); } for (let i = 0; i < toAdd.length; i++) { @@ -235,15 +251,11 @@ export class ExtensionService extends AbstractExtensionService implements IExten } } - private _rehandleExtensionPoints(extensionDescriptions: IExtensionDescription[]): void { - this._doHandleExtensionPoints(extensionDescriptions); - } - public canAddExtension(extensionDescription: IExtensionDescription): boolean { return this._canAddExtension(toExtension(extensionDescription)); } - public _canAddExtension(extension: IExtension): boolean { + private _canAddExtension(extension: IExtension): boolean { if (this._environmentService.configuration.remoteAuthority) { return false; } @@ -297,6 +309,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten let shouldActivate = false; let shouldActivateReason: string | null = null; + let hasWorkspaceContains = false; if (Array.isArray(extensionDescription.activationEvents)) { for (let activationEvent of extensionDescription.activationEvents) { // TODO@joao: there's no easy way to contribute this @@ -318,7 +331,10 @@ export class ExtensionService extends AbstractExtensionService implements IExten } if (/^workspaceContains/.test(activationEvent)) { - // do not trigger a search, just activate in this case... + hasWorkspaceContains = true; + } + + if (activationEvent === 'onStartupFinished') { shouldActivate = true; shouldActivateReason = activationEvent; break; @@ -328,56 +344,96 @@ export class ExtensionService extends AbstractExtensionService implements IExten if (shouldActivate) { await Promise.all( - this._extensionHostProcessManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, { startup: false, extensionId: extensionDescription.identifier, activationEvent: shouldActivateReason! })) + this._extensionHostManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, { startup: false, extensionId: extensionDescription.identifier, activationEvent: shouldActivateReason! })) + ).then(() => { }); + } else if (hasWorkspaceContains) { + const workspace = await this._contextService.getCompleteWorkspace(); + const forceUsingSearch = !!this._environmentService.configuration.remoteAuthority; + const host: IWorkspaceContainsActivationHost = { + folders: workspace.folders.map(folder => folder.uri), + forceUsingSearch: forceUsingSearch, + exists: (path) => exists(path), + checkExists: (folders, includes, token) => this._instantiationService.invokeFunction((accessor) => checkGlobFileExists(accessor, folders, includes, token)) + }; + + const result = await checkActivateWorkspaceContainsExtension(host, extensionDescription); + if (!result) { + return; + } + + await Promise.all( + this._extensionHostManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, { startup: false, extensionId: extensionDescription.identifier, activationEvent: result.activationEvent })) ).then(() => { }); } } //#endregion - private _createProvider(remoteAuthority: string): IInitDataProvider { + private async _scanAllLocalExtensions(): Promise { + return flatten(await Promise.all([ + this._extensionScanner.scannedExtensions, + this._webExtensionsScannerService.scanAndTranslateExtensions().then(extensions => extensions.map(parseScannedExtension)) + ])); + } + + private _createLocalExtensionHostDataProvider(isInitialStart: boolean, desiredRunningLocation: ExtensionRunningLocation) { return { - remoteAuthority: remoteAuthority, - getInitData: () => { - return this.whenInstalledExtensionsRegistered().then(() => { - return this._remoteExtensionsEnvironmentData.get(remoteAuthority)!; - }); + getInitData: async () => { + if (isInitialStart) { + const localExtensions = this._checkEnabledAndProposedAPI(await this._scanAllLocalExtensions()); + const runningLocation = _determineRunningLocation(this._productService, this._configurationService, localExtensions, [], false, this._enableLocalWebWorker); + const localProcessExtensions = filterByRunningLocation(localExtensions, runningLocation, desiredRunningLocation); + return { + autoStart: false, + extensions: localProcessExtensions + }; + } else { + // restart case + const allExtensions = await this.getExtensions(); + const localProcessExtensions = filterByRunningLocation(allExtensions, this._runningLocation, desiredRunningLocation); + return { + autoStart: true, + extensions: localProcessExtensions + }; + } } }; } - protected _createExtensionHosts(isInitialStart: boolean, initialActivationEvents: string[]): ExtensionHostProcessManager[] { - let autoStart: boolean; - let extensions: Promise; - if (isInitialStart) { - autoStart = false; - extensions = this._extensionScanner.scannedExtensions.then(extensions => extensions.filter(extension => this._isEnabled(extension))); // remove disabled extensions - } else { - // restart case - autoStart = true; - extensions = this.getExtensions().then((extensions) => extensions.filter(ext => ext.extensionLocation.scheme === Schemas.file)); + private _createRemoteExtensionHostDataProvider(remoteAuthority: string): IRemoteExtensionHostDataProvider { + return { + remoteAuthority: remoteAuthority, + getInitData: async () => { + await this.whenInstalledExtensionsRegistered(); + return this._remoteInitData.get(remoteAuthority)!; + } + }; + } + + protected _createExtensionHosts(isInitialStart: boolean): IExtensionHost[] { + const result: IExtensionHost[] = []; + + const localProcessExtHost = this._instantiationService.createInstance(LocalProcessExtensionHost, this._createLocalExtensionHostDataProvider(isInitialStart, ExtensionRunningLocation.LocalProcess)); + result.push(localProcessExtHost); + + if (this._enableLocalWebWorker) { + const webWorkerExtHost = this._instantiationService.createInstance(WebWorkerExtensionHost, this._createLocalExtensionHostDataProvider(isInitialStart, ExtensionRunningLocation.LocalWebWorker)); + result.push(webWorkerExtHost); } - const result: ExtensionHostProcessManager[] = []; - - const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, autoStart, extensions, this._environmentService.extHostLogsPath); - const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, true, extHostProcessWorker, null, initialActivationEvents); - result.push(extHostProcessManager); - const remoteAgentConnection = this._remoteAgentService.getConnection(); if (remoteAgentConnection) { - const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, this.getExtensions(), this._createProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory); - const remoteExtHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, remoteExtHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); - result.push(remoteExtHostProcessManager); + const remoteExtHost = this._instantiationService.createInstance(RemoteExtensionHost, this._createRemoteExtensionHostDataProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory); + result.push(remoteExtHost); } return result; } - protected _onExtensionHostCrashed(extensionHost: ExtensionHostProcessManager, code: number, signal: string | null): void { + protected _onExtensionHostCrashed(extensionHost: ExtensionHostManager, code: number, signal: string | null): void { super._onExtensionHostCrashed(extensionHost, code, signal); - if (extensionHost.isLocal) { + if (extensionHost.kind === ExtensionHostKind.LocalProcess) { if (code === 55) { this._notificationService.prompt( Severity.Error, @@ -426,13 +482,13 @@ export class ExtensionService extends AbstractExtensionService implements IExten return; } - const extensionHost = this._extensionHostProcessManagers[0]; - this._remoteAuthorityResolverService.clearResolvedAuthority(remoteAuthority); + const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess)!; + this._remoteAuthorityResolverService._clearResolvedAuthority(remoteAuthority); try { - const result = await extensionHost.resolveAuthority(remoteAuthority); - this._remoteAuthorityResolverService.setResolvedAuthority(result.authority, result.options); + const result = await localProcessExtensionHost.resolveAuthority(remoteAuthority); + this._remoteAuthorityResolverService._setResolvedAuthority(result.authority, result.options); } catch (err) { - this._remoteAuthorityResolverService.setResolvedAuthorityError(remoteAuthority, err); + this._remoteAuthorityResolverService._setResolvedAuthorityError(remoteAuthority, err); } } @@ -440,132 +496,110 @@ export class ExtensionService extends AbstractExtensionService implements IExten this._extensionScanner.startScanningExtensions(this.createLogger()); const remoteAuthority = this._environmentService.configuration.remoteAuthority; - const extensionHost = this._extensionHostProcessManagers[0]; + const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess)!; - const allExtensions = flatten(await Promise.all([this._extensionScanner.scannedExtensions, this._staticExtensions.getExtensions()])); - - // enable or disable proposed API per extension - this._checkEnableProposedApi(allExtensions); - - // remove disabled extensions - let localExtensions = remove(allExtensions, extension => this._isDisabled(extension)); + const localExtensions = this._checkEnabledAndProposedAPI(await this._scanAllLocalExtensions()); + let remoteEnv: IRemoteAgentEnvironment | null = null; + let remoteExtensions: IExtensionDescription[] = []; if (remoteAuthority) { - let resolvedAuthority: ResolverResult; + let resolverResult: ResolverResult; try { - resolvedAuthority = await extensionHost.resolveAuthority(remoteAuthority); + resolverResult = await localProcessExtensionHost.resolveAuthority(remoteAuthority); } catch (err) { - const remoteName = getRemoteName(remoteAuthority); if (RemoteAuthorityResolverError.isNoResolverFound(err)) { - err.isHandled = await this._handleNoResolverFound(remoteName, allExtensions); + err.isHandled = await this._handleNoResolverFound(remoteAuthority); } else { console.log(err); if (RemoteAuthorityResolverError.isHandled(err)) { console.log(`Error handled: Not showing a notification for the error`); } } - this._remoteAuthorityResolverService.setResolvedAuthorityError(remoteAuthority, err); + this._remoteAuthorityResolverService._setResolvedAuthorityError(remoteAuthority, err); // Proceed with the local extension host - await this._startLocalExtensionHost(extensionHost, localExtensions, localExtensions.map(extension => extension.identifier)); + await this._startLocalExtensionHost(localExtensions); return; } // set the resolved authority - this._remoteAuthorityResolverService.setResolvedAuthority(resolvedAuthority.authority, resolvedAuthority.options); - this._remoteExplorerService.setTunnelInformation(resolvedAuthority.tunnelInformation); + this._remoteAuthorityResolverService._setResolvedAuthority(resolverResult.authority, resolverResult.options); + this._remoteExplorerService.setTunnelInformation(resolverResult.tunnelInformation); // monitor for breakage const connection = this._remoteAgentService.getConnection(); if (connection) { connection.onDidStateChange(async (e) => { - const remoteAuthority = this._environmentService.configuration.remoteAuthority; - if (!remoteAuthority) { - return; - } if (e.type === PersistentConnectionEventType.ConnectionLost) { - this._remoteAuthorityResolverService.clearResolvedAuthority(remoteAuthority); + this._remoteAuthorityResolverService._clearResolvedAuthority(remoteAuthority); } }); connection.onReconnecting(() => this._resolveAuthorityAgain()); } // fetch the remote environment - const remoteEnv = (await this._remoteAgentService.getEnvironment()); + [remoteEnv, remoteExtensions] = await Promise.all([ + this._remoteAgentService.getEnvironment(), + this._remoteAgentService.scanExtensions() + ]); + remoteExtensions = this._checkEnabledAndProposedAPI(remoteExtensions); if (!remoteEnv) { this._notificationService.notify({ severity: Severity.Error, message: nls.localize('getEnvironmentFailure', "Could not fetch remote environment") }); // Proceed with the local extension host - await this._startLocalExtensionHost(extensionHost, localExtensions, localExtensions.map(extension => extension.identifier)); + await this._startLocalExtensionHost(localExtensions); return; } - - // enable or disable proposed API per extension - this._checkEnableProposedApi(remoteEnv.extensions); - - // remove disabled extensions - remoteEnv.extensions = remove(remoteEnv.extensions, extension => this._isDisabled(extension)); - - // Determine where each extension will execute, based on extensionKind - const isInstalledLocally = new Set(); - localExtensions.forEach(ext => isInstalledLocally.add(ExtensionIdentifier.toKey(ext.identifier))); - - const isInstalledRemotely = new Set(); - remoteEnv.extensions.forEach(ext => isInstalledRemotely.add(ExtensionIdentifier.toKey(ext.identifier))); - - const enum RunningLocation { None, Local, Remote } - const pickRunningLocation = (extension: IExtensionDescription): RunningLocation => { - for (const extensionKind of getExtensionKind(extension, this._productService, this._configurationService)) { - if (extensionKind === 'ui') { - if (isInstalledLocally.has(ExtensionIdentifier.toKey(extension.identifier))) { - return RunningLocation.Local; - } - } else if (extensionKind === 'workspace') { - if (isInstalledRemotely.has(ExtensionIdentifier.toKey(extension.identifier))) { - return RunningLocation.Remote; - } - } - } - return RunningLocation.None; - }; - - const runningLocation = new Map(); - localExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext))); - remoteEnv.extensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext))); - - // remove non-UI extensions from the local extensions - localExtensions = localExtensions.filter(ext => runningLocation.get(ExtensionIdentifier.toKey(ext.identifier)) === RunningLocation.Local); - - // in case of UI extensions overlap, the local extension wins - remoteEnv.extensions = remoteEnv.extensions.filter(ext => runningLocation.get(ExtensionIdentifier.toKey(ext.identifier)) === RunningLocation.Remote); - - // save for remote extension's init data - this._remoteExtensionsEnvironmentData.set(remoteAuthority, remoteEnv); - - await this._startLocalExtensionHost(extensionHost, remoteEnv.extensions.concat(localExtensions), localExtensions.map(extension => extension.identifier)); - } else { - await this._startLocalExtensionHost(extensionHost, localExtensions, localExtensions.map(extension => extension.identifier)); } + + await this._startLocalExtensionHost(localExtensions, remoteAuthority, remoteEnv, remoteExtensions); } - private async _startLocalExtensionHost(extensionHost: ExtensionHostProcessManager, allExtensions: IExtensionDescription[], localExtensions: ExtensionIdentifier[]): Promise { - this._registerAndHandleExtensions(allExtensions); - extensionHost.start(localExtensions.filter(id => this._registry.containsExtension(id))); - } + private async _startLocalExtensionHost(localExtensions: IExtensionDescription[], remoteAuthority: string | undefined = undefined, remoteEnv: IRemoteAgentEnvironment | null = null, remoteExtensions: IExtensionDescription[] = []): Promise { - private _registerAndHandleExtensions(allExtensions: IExtensionDescription[]): void { - const result = this._registry.deltaExtensions(allExtensions, []); + this._runningLocation = _determineRunningLocation(this._productService, this._configurationService, localExtensions, remoteExtensions, Boolean(remoteAuthority), this._enableLocalWebWorker); + + // remove non-UI extensions from the local extensions + const localProcessExtensions = filterByRunningLocation(localExtensions, this._runningLocation, ExtensionRunningLocation.LocalProcess); + const localWebWorkerExtensions = filterByRunningLocation(localExtensions, this._runningLocation, ExtensionRunningLocation.LocalWebWorker); + remoteExtensions = filterByRunningLocation(remoteExtensions, this._runningLocation, ExtensionRunningLocation.Remote); + + const result = this._registry.deltaExtensions(remoteExtensions.concat(localProcessExtensions).concat(localWebWorkerExtensions), []); if (result.removedDueToLooping.length > 0) { this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))); } + if (remoteAuthority && remoteEnv) { + this._remoteInitData.set(remoteAuthority, { + connectionData: this._remoteAuthorityResolverService.getConnectionData(remoteAuthority), + pid: remoteEnv.pid, + appRoot: remoteEnv.appRoot, + extensionHostLogsPath: remoteEnv.extensionHostLogsPath, + globalStorageHome: remoteEnv.globalStorageHome, + workspaceStorageHome: remoteEnv.workspaceStorageHome, + extensions: remoteExtensions, + allExtensions: this._registry.getAllExtensionDescriptions(), + }); + } + this._doHandleExtensionPoints(this._registry.getAllExtensionDescriptions()); + + const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess); + if (localProcessExtensionHost) { + localProcessExtensionHost.start(localProcessExtensions.map(extension => extension.identifier).filter(id => this._registry.containsExtension(id))); + } + + const localWebWorkerExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalWebWorker); + if (localWebWorkerExtensionHost) { + localWebWorkerExtensionHost.start(localWebWorkerExtensions.map(extension => extension.identifier).filter(id => this._registry.containsExtension(id))); + } } public async getInspectPort(tryEnableInspector: boolean): Promise { - if (this._extensionHostProcessManagers.length > 0) { - return this._extensionHostProcessManagers[0].getInspectPort(tryEnableInspector); + const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess); + if (localProcessExtensionHost) { + return localProcessExtensionHost.getInspectPort(tryEnableInspector); } return 0; } @@ -573,14 +607,15 @@ export class ExtensionService extends AbstractExtensionService implements IExten public _onExtensionHostExit(code: number): void { if (this._isExtensionDevTestFromCli) { // When CLI testing make sure to exit with proper exit code - ipc.send('vscode:exit', code); + this._electronService.exit(code); } else { // Expected development extension termination: When the extension host goes down we also shutdown the window this._electronService.closeWindow(); } } - private async _handleNoResolverFound(remoteName: string, allExtensions: IExtensionDescription[]): Promise { + private async _handleNoResolverFound(remoteAuthority: string): Promise { + const remoteName = getRemoteName(remoteAuthority); const recommendation = this._productService.remoteExtensionTips?.[remoteName]; if (!recommendation) { return false; @@ -596,9 +631,10 @@ export class ExtensionService extends AbstractExtensionService implements IExten }; const resolverExtensionId = recommendation.extensionId; + const allExtensions = await this._scanAllLocalExtensions(); const extension = allExtensions.filter(e => e.identifier.value === resolverExtensionId)[0]; if (extension) { - if (this._isDisabled(extension)) { + if (!this._isEnabled(extension)) { const message = nls.localize('enableResolver', "Extension '{0}' is required to open the remote window.\nOK to enable?", recommendation.friendlyName); this._notificationService.prompt(Severity.Info, message, [{ @@ -638,48 +674,81 @@ export class ExtensionService extends AbstractExtensionService implements IExten } return true; - } } -function remove(arr: IExtensionDescription[], predicate: (item: IExtensionDescription) => boolean): IExtensionDescription[]; -function remove(arr: IExtensionDescription[], toRemove: IExtensionDescription[]): IExtensionDescription[]; -function remove(arr: IExtensionDescription[], arg2: ((item: IExtensionDescription) => boolean) | IExtensionDescription[]): IExtensionDescription[] { - if (typeof arg2 === 'function') { - return _removePredicate(arr, arg2); - } - return _removeSet(arr, arg2); +const enum ExtensionRunningLocation { + None, + LocalProcess, + LocalWebWorker, + Remote } -function _removePredicate(arr: IExtensionDescription[], predicate: (item: IExtensionDescription) => boolean): IExtensionDescription[] { - return arr.filter(extension => !predicate(extension)); +export function determineRunningLocation(localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[], allExtensionKinds: Map, hasRemote: boolean, hasLocalWebWorker: boolean): Map { + const localExtensionsSet = new Set(); + localExtensions.forEach(ext => localExtensionsSet.add(ExtensionIdentifier.toKey(ext.identifier))); + + const remoteExtensionsSet = new Set(); + remoteExtensions.forEach(ext => remoteExtensionsSet.add(ExtensionIdentifier.toKey(ext.identifier))); + + const pickRunningLocation = (extension: IExtensionDescription): ExtensionRunningLocation => { + const isInstalledLocally = localExtensionsSet.has(ExtensionIdentifier.toKey(extension.identifier)); + const isInstalledRemotely = remoteExtensionsSet.has(ExtensionIdentifier.toKey(extension.identifier)); + const extensionKinds = allExtensionKinds.get(ExtensionIdentifier.toKey(extension.identifier)) || []; + for (const extensionKind of extensionKinds) { + if (extensionKind === 'ui' && isInstalledLocally) { + // ui extensions run locally if possible + return ExtensionRunningLocation.LocalProcess; + } + if (extensionKind === 'workspace' && isInstalledRemotely) { + // workspace extensions run remotely if possible + return ExtensionRunningLocation.Remote; + } + if (extensionKind === 'workspace' && !hasRemote) { + // workspace extensions also run locally if there is no remote + return ExtensionRunningLocation.LocalProcess; + } + if (extensionKind === 'web' && isInstalledLocally && hasLocalWebWorker) { + // web worker extensions run in the local web worker if possible + return ExtensionRunningLocation.LocalWebWorker; + } + } + return ExtensionRunningLocation.None; + }; + + const runningLocation = new Map(); + localExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext))); + remoteExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext))); + return runningLocation; } -function _removeSet(arr: IExtensionDescription[], toRemove: IExtensionDescription[]): IExtensionDescription[] { - const toRemoveSet = new Set(); - toRemove.forEach(extension => toRemoveSet.add(ExtensionIdentifier.toKey(extension.identifier))); - return arr.filter(extension => !toRemoveSet.has(ExtensionIdentifier.toKey(extension.identifier))); +function _determineRunningLocation(productService: IProductService, configurationService: IConfigurationService, localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[], hasRemote: boolean, hasLocalWebWorker: boolean): Map { + const allExtensionKinds = new Map(); + localExtensions.forEach(ext => allExtensionKinds.set(ExtensionIdentifier.toKey(ext.identifier), getExtensionKind(ext, productService, configurationService))); + remoteExtensions.forEach(ext => allExtensionKinds.set(ExtensionIdentifier.toKey(ext.identifier), getExtensionKind(ext, productService, configurationService))); + return determineRunningLocation(localExtensions, remoteExtensions, allExtensionKinds, hasRemote, hasLocalWebWorker); +} + +function filterByRunningLocation(extensions: IExtensionDescription[], runningLocation: Map, desiredRunningLocation: ExtensionRunningLocation): IExtensionDescription[] { + return extensions.filter(ext => runningLocation.get(ExtensionIdentifier.toKey(ext.identifier)) === desiredRunningLocation); } registerSingleton(IExtensionService, ExtensionService); -class RestartExtensionHostAction extends Action { +class RestartExtensionHostAction extends Action2 { - public static readonly ID = 'workbench.action.restartExtensionHost'; - public static readonly LABEL = nls.localize('restartExtensionHost', "Restart Extension Host"); - - constructor( - id: string, - label: string, - @IExtensionService private readonly _extensionService: IExtensionService - ) { - super(id, label); + constructor() { + super({ + id: 'workbench.action.restartExtensionHost', + title: { value: nls.localize('restartExtensionHost', "Restart Extension Host"), original: 'Restart Extension Host' }, + category: { value: nls.localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer"), original: 'Developer' }, + f1: true + }); } - public async run() { - this._extensionService.restartExtensionHost(); + run(accessor: ServicesAccessor): void { + accessor.get(IExtensionService).restartExtensionHost(); } } -const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(SyncActionDescriptor.from(RestartExtensionHostAction), 'Developer: Restart Extension Host', nls.localize('developer', "Developer")); +registerAction2(RestartExtensionHostAction); diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts similarity index 85% rename from src/vs/workbench/services/extensions/electron-browser/extensionHost.ts rename to src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts index ee57b70741b..fdf086f74c7 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { ChildProcess, fork } from 'child_process'; import { Server, Socket, createServer } from 'net'; -import { CrashReporterStartOptions } from 'electron'; +import { CrashReporterStartOptions } from 'vs/base/parts/sandbox/common/electronTypes'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { timeout } from 'vs/base/common/async'; import { toErrorMessage } from 'vs/base/common/errorMessage'; @@ -25,10 +25,10 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { ILabelService } from 'vs/platform/label/common/label'; import { ILifecycleService, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; -import product from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IInitData, UIKind } from 'vs/workbench/api/common/extHost.protocol'; import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; @@ -37,7 +37,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' import { parseExtensionDevOptions } from '../common/extensionDevOptions'; import { VSBuffer } from 'vs/base/common/buffer'; import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; -import { IExtensionHostStarter, ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionHost, ExtensionHostLogFileName, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions'; import { isUntitledWorkspace } from 'vs/platform/workspaces/common/workspaces'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { joinPath } from 'vs/base/common/resources'; @@ -45,7 +45,19 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output/common/output'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; -export class ExtensionHostProcessWorker implements IExtensionHostStarter { +export interface ILocalProcessExtensionHostInitData { + readonly autoStart: boolean; + readonly extensions: IExtensionDescription[]; +} + +export interface ILocalProcessExtensionHostDataProvider { + getInitData(): Promise; +} + +export class LocalProcessExtensionHost implements IExtensionHost { + + public readonly kind = ExtensionHostKind.LocalProcess; + public readonly remoteAuthority = null; private readonly _onExit: Emitter<[number, string]> = new Emitter<[number, string]>(); public readonly onExit: Event<[number, string]> = this._onExit.event; @@ -73,9 +85,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { private readonly _extensionHostLogFile: URI; constructor( - private readonly _autoStart: boolean, - private readonly _extensions: Promise, - private readonly _extensionHostLogsLocation: URI, + private readonly _initDataProvider: ILocalProcessExtensionHostDataProvider, @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @INotificationService private readonly _notificationService: INotificationService, @IElectronService private readonly _electronService: IElectronService, @@ -85,7 +95,8 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { @ILogService private readonly _logService: ILogService, @ILabelService private readonly _labelService: ILabelService, @IExtensionHostDebugService private readonly _extensionHostDebugService: IExtensionHostDebugService, - @IHostService private readonly _hostService: IHostService + @IHostService private readonly _hostService: IHostService, + @IProductService private readonly _productService: IProductService ) { const devOpts = parseExtensionDevOptions(this._environmentService); this._isExtensionDevHost = devOpts.isExtensionDevHost; @@ -102,7 +113,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { this._extensionHostConnection = null; this._messageProtocol = null; - this._extensionHostLogFile = joinPath(this._extensionHostLogsLocation, `${ExtensionHostLogFileName}.log`); + this._extensionHostLogFile = joinPath(this._environmentService.extHostLogsPath, `${ExtensionHostLogFileName}.log`); this._toDispose.add(this._onExit); this._toDispose.add(this._lifecycleService.onWillShutdown(e => this._onWillShutdown(e))); @@ -150,7 +161,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { VERBOSE_LOGGING: true, VSCODE_IPC_HOOK_EXTHOST: pipeName, VSCODE_HANDLES_UNCAUGHT_ERRORS: true, - VSCODE_LOG_STACK: !this._isExtensionDevTestFromCli && (this._isExtensionDevHost || !this._environmentService.isBuilt || product.quality !== 'stable' || this._environmentService.verbose), + VSCODE_LOG_STACK: !this._isExtensionDevTestFromCli && (this._isExtensionDevHost || !this._environmentService.isBuilt || this._productService.quality !== 'stable' || this._environmentService.verbose), VSCODE_LOG_LEVEL: this._environmentService.verbose ? 'trace' : this._environmentService.log }), // We only detach the extension host on windows. Linux and Mac orphan by default @@ -175,8 +186,8 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { const crashesDirectory = this._environmentService.crashReporterDirectory; if (crashesDirectory) { const crashReporterOptions: CrashReporterStartOptions = { - companyName: product.crashReporter?.companyName || 'Microsoft', - productName: product.crashReporter?.productName || product.nameShort, + companyName: this._productService.crashReporter?.companyName || 'Microsoft', + productName: this._productService.crashReporter?.productName || this._productService.nameShort, submitURL: '', uploadToServer: false, crashesDirectory @@ -409,50 +420,47 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { }); } - private _createExtHostInitData(): Promise { - return Promise.all([this._telemetryService.getTelemetryInfo(), this._extensions]) - .then(([telemetryInfo, extensionDescriptions]) => { - const workspace = this._contextService.getWorkspace(); - const r: IInitData = { - commit: product.commit, - version: product.version, - parentPid: process.pid, - environment: { - isExtensionDevelopmentDebug: this._isExtensionDevDebug, - appRoot: this._environmentService.appRoot ? URI.file(this._environmentService.appRoot) : undefined, - appSettingsHome: this._environmentService.appSettingsHome ? this._environmentService.appSettingsHome : undefined, - appName: product.nameLong, - appUriScheme: product.urlProtocol, - appLanguage: platform.language, - extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, - extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, - globalStorageHome: URI.file(this._environmentService.globalStorageHome), - userHome: this._environmentService.userHome, - webviewResourceRoot: this._environmentService.webviewResourceRoot, - webviewCspSource: this._environmentService.webviewCspSource, - }, - workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : { - configuration: withNullAsUndefined(workspace.configuration), - id: workspace.id, - name: this._labelService.getWorkspaceLabel(workspace), - isUntitled: workspace.configuration ? isUntitledWorkspace(workspace.configuration, this._environmentService) : false - }, - remote: { - authority: this._environmentService.configuration.remoteAuthority, - isRemote: false - }, - resolvedExtensions: [], - hostExtensions: [], - extensions: extensionDescriptions, - telemetryInfo, - logLevel: this._logService.getLevel(), - logsLocation: this._extensionHostLogsLocation, - logFile: this._extensionHostLogFile, - autoStart: this._autoStart, - uiKind: UIKind.Desktop - }; - return r; - }); + private async _createExtHostInitData(): Promise { + const [telemetryInfo, initData] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._initDataProvider.getInitData()]); + const workspace = this._contextService.getWorkspace(); + return { + commit: this._productService.commit, + version: this._productService.version, + parentPid: process.pid, + environment: { + isExtensionDevelopmentDebug: this._isExtensionDevDebug, + appRoot: this._environmentService.appRoot ? URI.file(this._environmentService.appRoot) : undefined, + appName: this._productService.nameLong, + appUriScheme: this._productService.urlProtocol, + appLanguage: platform.language, + extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, + extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, + globalStorageHome: this._environmentService.globalStorageHome, + workspaceStorageHome: this._environmentService.workspaceStorageHome, + webviewResourceRoot: this._environmentService.webviewResourceRoot, + webviewCspSource: this._environmentService.webviewCspSource, + }, + workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : { + configuration: withNullAsUndefined(workspace.configuration), + id: workspace.id, + name: this._labelService.getWorkspaceLabel(workspace), + isUntitled: workspace.configuration ? isUntitledWorkspace(workspace.configuration, this._environmentService) : false + }, + remote: { + authority: this._environmentService.configuration.remoteAuthority, + connectionData: null, + isRemote: false + }, + resolvedExtensions: [], + hostExtensions: [], + extensions: initData.extensions, + telemetryInfo, + logLevel: this._logService.getLevel(), + logsLocation: this._environmentService.extHostLogsPath, + logFile: this._extensionHostLogFile, + autoStart: initData.autoStart, + uiKind: UIKind.Desktop + }; } private _logExtensionHostMessage(entry: IRemoteConsoleLog) { diff --git a/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts b/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts index a4e21809eba..b11f3e7f7fa 100644 --- a/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts +++ b/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts @@ -12,8 +12,7 @@ import { areSameExtensions } from 'vs/platform/extensionManagement/common/extens import { ILogService } from 'vs/platform/log/common/log'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { prefersExecuteOnUI } from 'vs/workbench/services/extensions/common/extensionsUtil'; -import { isNonEmptyArray } from 'vs/base/common/arrays'; -import { values } from 'vs/base/common/map'; +import { isNonEmptyArray, toArray } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { localize } from 'vs/nls'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -24,7 +23,7 @@ import { joinPath } from 'vs/base/common/resources'; export class RemoteExtensionManagementChannelClient extends ExtensionManagementChannelClient { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( channel: IChannel, @@ -102,14 +101,14 @@ export class RemoteExtensionManagementChannelClient extends ExtensionManagementC const result = new Map(); const extensions = [...(manifest.extensionPack || []), ...(manifest.extensionDependencies || [])]; await this.getDependenciesAndPackedExtensionsRecursively(extensions, result, true, token); - return values(result); + return toArray(result.values()); } private async getAllWorkspaceDependenciesAndPackedExtensions(manifest: IExtensionManifest, token: CancellationToken): Promise { const result = new Map(); const extensions = [...(manifest.extensionPack || []), ...(manifest.extensionDependencies || [])]; await this.getDependenciesAndPackedExtensionsRecursively(extensions, result, false, token); - return values(result); + return toArray(result.values()); } private async getDependenciesAndPackedExtensionsRecursively(toGet: string[], result: Map, uiExtension: boolean, token: CancellationToken): Promise { diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts index 79dd77aeb26..49542eda74c 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts @@ -20,9 +20,11 @@ import { IURITransformer, URITransformer, IRawURITransformer } from 'vs/base/com import { exists } from 'vs/base/node/pfs'; import { realpath } from 'vs/base/node/extpath'; import { IHostUtils } from 'vs/workbench/api/common/extHostExtensionService'; -import 'vs/workbench/api/node/extHost.services'; import { RunOnceScheduler } from 'vs/base/common/async'; +import 'vs/workbench/api/common/extHost.common.services'; +import 'vs/workbench/api/node/extHost.node.services'; + interface ParsedExtHostArgs { uriTransformerPath?: string; useHostProxy?: string; @@ -94,10 +96,10 @@ let onTerminate = function () { nativeExit(); }; -function _createExtHostProtocol(): Promise { +function _createExtHostProtocol(): Promise { if (process.env.VSCODE_EXTHOST_WILL_SEND_SOCKET) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { let protocol: PersistentProtocol | null = null; @@ -161,7 +163,7 @@ function _createExtHostProtocol(): Promise { const pipeName = process.env.VSCODE_IPC_HOOK_EXTHOST!; - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const socket = net.createConnection(pipeName, () => { socket.removeListener('error', reject); @@ -201,6 +203,10 @@ async function createExtHostProtocol(): Promise { protocol.send(msg); } } + + drain(): Promise { + return protocol.drain(); + } }; } @@ -299,7 +305,7 @@ export async function startExtensionHostProcess(): Promise { // host abstraction const hostUtils = new class NodeHost implements IHostUtils { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; exit(code: number) { nativeExit(code); } exists(path: string) { return exists(path); } realpath(path: string) { return realpath(path); } diff --git a/src/vs/workbench/services/extensions/node/extensionPoints.ts b/src/vs/workbench/services/extensions/node/extensionPoints.ts index 9961f497f49..23795239346 100644 --- a/src/vs/workbench/services/extensions/node/extensionPoints.ts +++ b/src/vs/workbench/services/extensions/node/extensionPoints.ts @@ -379,7 +379,7 @@ class ExtensionManifestValidator extends ExtensionManifestHandler { notices.push(nls.localize('extensionDescription.activationEvents1', "property `{0}` can be omitted or must be of type `string[]`", 'activationEvents')); return false; } - if (typeof extensionDescription.main === 'undefined') { + if (typeof extensionDescription.main === 'undefined' && typeof extensionDescription.browser === 'undefined') { notices.push(nls.localize('extensionDescription.activationEvents2', "properties `{0}` and `{1}` must both be specified or must both be omitted", 'activationEvents', 'main')); return false; } @@ -389,9 +389,8 @@ class ExtensionManifestValidator extends ExtensionManifestHandler { notices.push(nls.localize('extensionDescription.main1', "property `{0}` can be omitted or must be of type `string`", 'main')); return false; } else { - let normalizedAbsolutePath = path.join(extensionFolderPath, extensionDescription.main); - - if (normalizedAbsolutePath.indexOf(extensionFolderPath)) { + const normalizedAbsolutePath = path.join(extensionFolderPath, extensionDescription.main); + if (!normalizedAbsolutePath.startsWith(extensionFolderPath)) { notices.push(nls.localize('extensionDescription.main2', "Expected `main` ({0}) to be included inside extension's folder ({1}). This might make the extension non-portable.", normalizedAbsolutePath, extensionFolderPath)); // not a failure case } @@ -401,6 +400,22 @@ class ExtensionManifestValidator extends ExtensionManifestHandler { return false; } } + if (typeof extensionDescription.browser !== 'undefined') { + if (typeof extensionDescription.browser !== 'string') { + notices.push(nls.localize('extensionDescription.browser1', "property `{0}` can be omitted or must be of type `string`", 'browser')); + return false; + } else { + const normalizedAbsolutePath = path.join(extensionFolderPath, extensionDescription.browser); + if (!normalizedAbsolutePath.startsWith(extensionFolderPath)) { + notices.push(nls.localize('extensionDescription.browser2', "Expected `browser` ({0}) to be included inside extension's folder ({1}). This might make the extension non-portable.", normalizedAbsolutePath, extensionFolderPath)); + // not a failure case + } + } + if (typeof extensionDescription.activationEvents === 'undefined') { + notices.push(nls.localize('extensionDescription.browser3', "properties `{0}` and `{1}` must both be specified or must both be omitted", 'activationEvents', 'browser')); + return false; + } + } return true; } diff --git a/src/vs/workbench/services/extensions/test/common/extensionsUtil.test.ts b/src/vs/workbench/services/extensions/test/common/extensionsUtil.test.ts new file mode 100644 index 00000000000..b5c5671dd01 --- /dev/null +++ b/src/vs/workbench/services/extensions/test/common/extensionsUtil.test.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 assert from 'assert'; +import { deduceExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { IExtensionManifest, ExtensionKind } from 'vs/platform/extensions/common/extensions'; + +suite('ExtensionKind', () => { + + function check(manifest: Partial, expected: ExtensionKind[]): void { + assert.deepEqual(deduceExtensionKind(manifest), expected); + } + + test('declarative with extension dependencies => workspace', () => { + check({ extensionDependencies: ['ext1'] }, ['workspace']); + }); + + test('declarative extension pack => workspace', () => { + check({ extensionPack: ['ext1', 'ext2'] }, ['workspace']); + }); + + test('declarative with unknown contribution point => workspace', () => { + check({ contributes: { 'unknownPoint': { something: true } } }, ['workspace']); + }); + + test('simple declarative => ui, workspace, web', () => { + check({}, ['ui', 'workspace', 'web']); + }); + + test('only browser => web', () => { + check({ browser: 'main.browser.js' }, ['web']); + }); + + test('only main => workspace', () => { + check({ main: 'main.js' }, ['workspace']); + }); + + test('main and browser => workspace, web', () => { + check({ main: 'main.js', browser: 'main.browser.js' }, ['workspace', 'web']); + }); +}); diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts index 4afc0cdea9e..2041723074a 100644 --- a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts +++ b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts @@ -10,7 +10,10 @@ import { isMessageOfType, MessageType, createMessageOfType } from 'vs/workbench/ import { IInitData } from 'vs/workbench/api/common/extHost.protocol'; import { ExtensionHostMain } from 'vs/workbench/services/extensions/common/extensionHostMain'; import { IHostUtils } from 'vs/workbench/api/common/extHostExtensionService'; -import 'vs/workbench/services/extensions/worker/extHost.services'; +import * as path from 'vs/base/common/path'; + +import 'vs/workbench/api/common/extHost.common.services'; +import 'vs/workbench/api/worker/extHost.worker.services'; //#region --- Define, capture, and override some globals @@ -33,10 +36,21 @@ self.postMessage = () => console.trace(`'postMessage' has been blocked`); const nativeAddEventLister = addEventListener.bind(self); self.addEventLister = () => console.trace(`'addEventListener' has been blocked`); +if (location.protocol === 'data:') { + // make sure new Worker(...) always uses data: + const _Worker = Worker; + Worker = function (stringUrl: string | URL, options?: WorkerOptions) { + const js = `importScripts('${stringUrl}');`; + options = options || {}; + options.name = options.name || path.basename(stringUrl.toString()); + return new _Worker(`data:text/javascript;charset=utf-8,${encodeURIComponent(js)}`, options); + }; +} + //#endregion --- const hostUtil = new class implements IHostUtils { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; exit(_code?: number | undefined): void { nativeClose(); } diff --git a/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts b/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts index b40e932de44..1cc2b478326 100644 --- a/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts +++ b/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts @@ -35,7 +35,7 @@ export const IFilesConfigurationService = createDecorator(EditorExtensions.EditorInputFactories); + constructor( @IEditorService private readonly editorService: EditorServiceImpl, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @@ -123,7 +130,6 @@ export class HistoryService extends Disposable implements IHistoryService { this._register(this.editorService.onDidCloseEditor(event => this.onEditorClosed(event))); this._register(this.storageService.onWillSaveState(() => this.saveState())); this._register(this.fileService.onDidFilesChange(event => this.onDidFilesChange(event))); - this._register(this.resourceExcludeMatcher.onExpressionChange(() => this.removeExcludedFromHistory())); this._register(this.editorService.onDidMostRecentlyActiveEditorsChange(() => this.handleEditorEventInRecentEditorsStack())); // if the service is created late enough that an editor is already opened @@ -259,7 +265,7 @@ export class HistoryService extends Disposable implements IHistoryService { remove(arg1: IEditorInput | IResourceEditorInput | FileChangesEvent): void { this.removeFromHistory(arg1); this.removeFromNavigationStack(arg1); - this.removeFromRecentlyClosedFiles(arg1); + this.removeFromRecentlyClosedEditors(arg1); this.removeFromRecentlyOpened(arg1); } @@ -285,8 +291,8 @@ export class HistoryService extends Disposable implements IHistoryService { this.editorStackListeners.forEach(listeners => dispose(listeners)); this.editorStackListeners.clear(); - // Closed files - this.recentlyClosedFiles = []; + // Recently closed editors + this.recentlyClosedEditors = []; // Context Keys this.updateContextKeys(); @@ -573,7 +579,7 @@ export class HistoryService extends Disposable implements IHistoryService { const resourceEditorInputA = arg1 as IResourceEditorInput; const resourceEditorInputB = inputB as IResourceEditorInput; - return resourceEditorInputA && resourceEditorInputB && resourceEditorInputA.resource.toString() === resourceEditorInputB.resource.toString(); + return resourceEditorInputA && resourceEditorInputB && extUri.isEqual(resourceEditorInputA.resource, resourceEditorInputB.resource); } private matchesFile(resource: URI, arg2: IEditorInput | IResourceEditorInput | FileChangesEvent): boolean { @@ -591,86 +597,130 @@ export class HistoryService extends Disposable implements IHistoryService { return false; // make sure to only check this when workbench has restored (for https://github.com/Microsoft/vscode/issues/48275) } - return inputResource.toString() === resource.toString(); + return extUri.isEqual(inputResource, resource); } const resourceEditorInput = arg2 as IResourceEditorInput; - return resourceEditorInput?.resource.toString() === resource.toString(); + return extUri.isEqual(resourceEditorInput?.resource, resource); } //#endregion - //#region Recently Closed Files + //#region Recently Closed Editors private static readonly MAX_RECENTLY_CLOSED_EDITORS = 20; - private recentlyClosedFiles: IRecentlyClosedFile[] = []; + private recentlyClosedEditors: IRecentlyClosedEditor[] = []; private onEditorClosed(event: IEditorCloseEvent): void { - - // Track closing of editor to support to reopen closed editors (unless editor was replaced) - if (!event.replaced) { - const resource = event.editor ? event.editor.resource : undefined; - const supportsReopen = resource && this.fileService.canHandleResource(resource); // we only support file'ish things to reopen - if (resource && supportsReopen) { - - // Remove all inputs matching and add as last recently closed - this.removeFromRecentlyClosedFiles(event.editor); - this.recentlyClosedFiles.push({ resource, index: event.index, sticky: event.sticky }); - - // Bounding - if (this.recentlyClosedFiles.length > HistoryService.MAX_RECENTLY_CLOSED_EDITORS) { - this.recentlyClosedFiles.shift(); - } - - // Context - this.canReopenClosedEditorContextKey.set(true); - } - } - } - - reopenLastClosedEditor(): void { - let lastClosedFile = this.recentlyClosedFiles.pop(); - while (lastClosedFile && this.containsRecentlyClosedFile(this.editorGroupService.activeGroup, lastClosedFile)) { - lastClosedFile = this.recentlyClosedFiles.pop(); // pop until we find a file that is not opened + const { editor, replaced } = event; + if (replaced) { + return; // ignore if editor was replaced } - if (lastClosedFile) { - (async () => { - const editor = await this.editorService.openEditor({ - resource: lastClosedFile.resource, - options: { pinned: true, sticky: lastClosedFile.sticky, index: lastClosedFile.index } - }); + const factory = this.editorInputFactory.getEditorInputFactory(editor.getTypeId()); + if (!factory || !factory.canSerialize(editor)) { + return; // we need a factory from this point that can serialize this editor + } - // Fix for https://github.com/Microsoft/vscode/issues/67882 - // If opening of the editor fails, make sure to try the next one - // but make sure to remove this one from the list to prevent - // endless loops. - if (!editor) { - this.recentlyClosedFiles.pop(); - this.reopenLastClosedEditor(); - } - })(); + const serialized = factory.serialize(editor); + if (typeof serialized !== 'string') { + return; // we need something to deserialize from + } + + const associatedResources: URI[] = []; + const editorResource = toResource(editor, { supportSideBySide: SideBySideEditor.BOTH }); + if (URI.isUri(editorResource)) { + associatedResources.push(editorResource); + } else if (editorResource) { + associatedResources.push(...coalesce([editorResource.primary, editorResource.secondary])); + } + + // Remove from list of recently closed before... + this.removeFromRecentlyClosedEditors(editor); + + // ...adding it as last recently closed + this.recentlyClosedEditors.push({ + resource: editor.resource, + associatedResources, + serialized: { typeId: editor.getTypeId(), value: serialized }, + index: event.index, + sticky: event.sticky + }); + + // Bounding + if (this.recentlyClosedEditors.length > HistoryService.MAX_RECENTLY_CLOSED_EDITORS) { + this.recentlyClosedEditors.shift(); } // Context - this.canReopenClosedEditorContextKey.set(this.recentlyClosedFiles.length > 0); + this.canReopenClosedEditorContextKey.set(true); } - private containsRecentlyClosedFile(group: IEditorGroup, recentlyClosedEditor: IRecentlyClosedFile): boolean { - for (const editor of group.editors) { - if (isEqual(editor.resource, recentlyClosedEditor.resource)) { - return true; - } + reopenLastClosedEditor(): void { + + // Open editor if we have one + const lastClosedEditor = this.recentlyClosedEditors.pop(); + if (lastClosedEditor) { + this.doReopenLastClosedEditor(lastClosedEditor); } - return false; + // Update context + this.canReopenClosedEditorContextKey.set(this.recentlyClosedEditors.length > 0); } - private removeFromRecentlyClosedFiles(arg1: IEditorInput | IResourceEditorInput | FileChangesEvent): void { - this.recentlyClosedFiles = this.recentlyClosedFiles.filter(e => !this.matchesFile(e.resource, arg1)); - this.canReopenClosedEditorContextKey.set(this.recentlyClosedFiles.length > 0); + private async doReopenLastClosedEditor(lastClosedEditor: IRecentlyClosedEditor): Promise { + + // Determine editor options + let options: IEditorOptions; + if (lastClosedEditor.sticky) { + // Sticky: in case the target index is outside of the range of + // sticky editors, we make sure to not provide the index as + // option. Otherwise the index will cause the sticky flag to + // be ignored. + if (!this.editorGroupService.activeGroup.isSticky(lastClosedEditor.index)) { + options = { pinned: true, sticky: true, ignoreError: true }; + } else { + options = { pinned: true, sticky: true, index: lastClosedEditor.index, ignoreError: true }; + } + } else { + options = { pinned: true, index: lastClosedEditor.index, ignoreError: true }; + } + + // Deserialize and open editor unless already opened + const restoredEditor = this.editorInputFactory.getEditorInputFactory(lastClosedEditor.serialized.typeId)?.deserialize(this.instantiationService, lastClosedEditor.serialized.value); + let editorPane: IEditorPane | undefined = undefined; + if (restoredEditor && !this.editorGroupService.activeGroup.isOpened(restoredEditor)) { + editorPane = await this.editorService.openEditor(restoredEditor, options); + } + + // If no editor was opened, try with the next one + if (!editorPane) { + // Fix for https://github.com/Microsoft/vscode/issues/67882 + // If opening of the editor fails, make sure to try the next one + // but make sure to remove this one from the list to prevent + // endless loops. + remove(this.recentlyClosedEditors, lastClosedEditor); + this.reopenLastClosedEditor(); + } + } + + private removeFromRecentlyClosedEditors(arg1: IEditorInput | IResourceEditorInput | FileChangesEvent): void { + this.recentlyClosedEditors = this.recentlyClosedEditors.filter(recentlyClosedEditor => { + if (recentlyClosedEditor.resource && this.matchesFile(recentlyClosedEditor.resource, arg1)) { + return false; // editor matches directly + } + + if (recentlyClosedEditor.associatedResources.some(associatedResource => this.matchesFile(associatedResource, arg1))) { + return false; // an associated resource matches + } + + return true; + }); + + // Update context + this.canReopenClosedEditorContextKey.set(this.recentlyClosedEditors.length > 0); } //#endregion @@ -705,10 +755,12 @@ export class HistoryService extends Disposable implements IHistoryService { private readonly canReopenClosedEditorContextKey = (new RawContextKey('canReopenClosedEditor', false)).bindTo(this.contextKeyService); private updateContextKeys(): void { - this.canNavigateBackContextKey.set(this.navigationStack.length > 0 && this.navigationStackIndex > 0); - this.canNavigateForwardContextKey.set(this.navigationStack.length > 0 && this.navigationStackIndex < this.navigationStack.length - 1); - this.canNavigateToLastEditLocationContextKey.set(!!this.lastEditLocation); - this.canReopenClosedEditorContextKey.set(this.recentlyClosedFiles.length > 0); + this.contextKeyService.bufferChangeEvents(() => { + this.canNavigateBackContextKey.set(this.navigationStack.length > 0 && this.navigationStackIndex > 0); + this.canNavigateForwardContextKey.set(this.navigationStack.length > 0 && this.navigationStackIndex < this.navigationStack.length - 1); + this.canNavigateToLastEditLocationContextKey.set(!!this.lastEditLocation); + this.canReopenClosedEditorContextKey.set(this.recentlyClosedEditors.length > 0); + }); } //#endregion @@ -720,7 +772,17 @@ export class HistoryService extends Disposable implements IHistoryService { private history: Array | undefined = undefined; - private readonly resourceExcludeMatcher = this._register(createResourceExcludeMatcher(this.instantiationService, this.configurationService)); + private readonly resourceExcludeMatcher = this._register(new IdleValue(() => { + const matcher = this._register(this.instantiationService.createInstance( + ResourceGlobMatcher, + root => getExcludes(root ? this.configurationService.getValue({ resource: root }) : this.configurationService.getValue()) || Object.create(null), + event => event.affectsConfiguration(FILES_EXCLUDE_CONFIG) || event.affectsConfiguration(SEARCH_EXCLUDE_CONFIG) + )); + + this._register(matcher.onExpressionChange(() => this.removeExcludedFromHistory())); + + return matcher; + })); private handleEditorEventInHistory(editor?: IEditorPane): void { @@ -756,7 +818,7 @@ export class HistoryService extends Disposable implements IHistoryService { const resourceEditorInput = input as IResourceEditorInput; - return !this.resourceExcludeMatcher.matches(resourceEditorInput.resource); + return !this.resourceExcludeMatcher.value.matches(resourceEditorInput.resource); } private removeExcludedFromHistory(): void { @@ -813,21 +875,23 @@ export class HistoryService extends Disposable implements IHistoryService { const entriesRaw = this.storageService.get(HistoryService.HISTORY_STORAGE_KEY, StorageScope.WORKSPACE); if (entriesRaw) { - entries = coalesce(JSON.parse(entriesRaw)); + try { + entries = coalesce(JSON.parse(entriesRaw)); + } catch (error) { + onUnexpectedError(error); // https://github.com/microsoft/vscode/issues/99075 + } } - const registry = Registry.as(EditorExtensions.EditorInputFactories); - return coalesce(entries.map(entry => { try { - return this.safeLoadHistoryEntry(registry, entry); + return this.safeLoadHistoryEntry(entry); } catch (error) { return undefined; // https://github.com/Microsoft/vscode/issues/60960 } })); } - private safeLoadHistoryEntry(registry: IEditorInputFactoryRegistry, entry: ISerializedEditorHistoryEntry): IEditorInput | IResourceEditorInput | undefined { + private safeLoadHistoryEntry(entry: ISerializedEditorHistoryEntry): IEditorInput | IResourceEditorInput | undefined { const serializedEditorHistoryEntry = entry; // File resource: via URI.revive() @@ -838,7 +902,7 @@ export class HistoryService extends Disposable implements IHistoryService { // Editor input: via factory const { editorInputJSON } = serializedEditorHistoryEntry; if (editorInputJSON?.deserialized) { - const factory = registry.getEditorInputFactory(editorInputJSON.typeId); + const factory = this.editorInputFactory.getEditorInputFactory(editorInputJSON.typeId); if (factory) { const input = factory.deserialize(this.instantiationService, editorInputJSON.deserialized); if (input) { @@ -857,13 +921,11 @@ export class HistoryService extends Disposable implements IHistoryService { return; // nothing to save because history was not used } - const registry = Registry.as(EditorExtensions.EditorInputFactories); - const entries: ISerializedEditorHistoryEntry[] = coalesce(this.history.map((input): ISerializedEditorHistoryEntry | undefined => { // Editor input: try via factory if (input instanceof EditorInput) { - const factory = registry.getEditorInputFactory(input.getTypeId()); + const factory = this.editorInputFactory.getEditorInputFactory(input.getTypeId()); if (factory) { const deserialized = factory.serialize(input); if (deserialized) { diff --git a/src/vs/workbench/services/history/common/history.ts b/src/vs/workbench/services/history/common/history.ts index 04349f87efe..600f1c19738 100644 --- a/src/vs/workbench/services/history/common/history.ts +++ b/src/vs/workbench/services/history/common/history.ts @@ -12,7 +12,7 @@ export const IHistoryService = createDecorator('historyService' export interface IHistoryService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Re-opens the last closed editor if any. diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index e3366db8981..c4f72f05518 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -7,9 +7,9 @@ import { Event } from 'vs/base/common/event'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; -import { IResourceEditorInputType, IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IWindowSettings, IWindowOpenable, IOpenWindowOptions, isFolderToOpen, isWorkspaceToOpen, isFileToOpen, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; +import { IWindowSettings, IWindowOpenable, IOpenWindowOptions, isFolderToOpen, isWorkspaceToOpen, isFileToOpen, IOpenEmptyWindowOptions, IPathData, IFileToOpen } from 'vs/platform/windows/common/windows'; import { pathsToEditors } from 'vs/workbench/common/editor'; import { IFileService } from 'vs/platform/files/common/files'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -19,6 +19,10 @@ import { URI } from 'vs/base/common/uri'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { domEvent } from 'vs/base/browser/event'; import { memoize } from 'vs/base/common/decorators'; +import { parseLineAndColumnAware } from 'vs/base/common/extpath'; +import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; /** * A workspace to open in the workbench can either be: @@ -55,7 +59,7 @@ export interface IWorkspaceProvider { export class BrowserHostService extends Disposable implements IHostService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private workspaceProvider: IWorkspaceProvider; @@ -65,7 +69,8 @@ export class BrowserHostService extends Disposable implements IHostService { @IConfigurationService private readonly configurationService: IConfigurationService, @IFileService private readonly fileService: IFileService, @ILabelService private readonly labelService: ILabelService, - @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, + @IInstantiationService private readonly instantiationService: IInstantiationService ) { super(); @@ -114,15 +119,19 @@ export class BrowserHostService extends Disposable implements IHostService { private async doOpenWindow(toOpen: IWindowOpenable[], options?: IOpenWindowOptions): Promise { const payload = this.preservePayload(); + const fileOpenables: IFileToOpen[] = []; + const foldersToAdd: IWorkspaceFolderCreationData[] = []; - for (let i = 0; i < toOpen.length; i++) { - const openable = toOpen[i]; + for (const openable of toOpen) { openable.label = openable.label || this.getRecentLabel(openable); - // Folder if (isFolderToOpen(openable)) { - this.workspaceProvider.open({ folderUri: openable.folderUri }, { reuse: this.shouldReuse(options, false /* no file */), payload }); + if (options?.addMode) { + foldersToAdd.push(({ uri: openable.folderUri })); + } else { + this.workspaceProvider.open({ folderUri: openable.folderUri }, { reuse: this.shouldReuse(options, false /* no file */), payload }); + } } // Workspace @@ -130,23 +139,97 @@ export class BrowserHostService extends Disposable implements IHostService { this.workspaceProvider.open({ workspaceUri: openable.workspaceUri }, { reuse: this.shouldReuse(options, false /* no file */), payload }); } - // File + // File (handled later in bulk) else if (isFileToOpen(openable)) { + fileOpenables.push(openable); + } + } + + // Handle Folders to Add + if (foldersToAdd.length > 0) { + this.instantiationService.invokeFunction(accessor => { + const workspaceEditingService: IWorkspaceEditingService = accessor.get(IWorkspaceEditingService); + workspaceEditingService.addFolders(foldersToAdd); + }); + } + + // Handle Files + if (fileOpenables.length > 0) { + + // Support diffMode + if (options?.diffMode && fileOpenables.length === 2) { + const editors = await pathsToEditors(fileOpenables, this.fileService); + if (editors.length !== 2 || !editors[0].resource || !editors[1].resource) { + return; // invalid resources + } // Same Window: open via editor service in current window if (this.shouldReuse(options, true /* file */)) { - const inputs: IResourceEditorInputType[] = await pathsToEditors([openable], this.fileService); - this.editorService.openEditors(inputs); + this.editorService.openEditor({ + leftResource: editors[0].resource, + rightResource: editors[1].resource + }); } // New Window: open into empty window else { const environment = new Map(); - environment.set('openFile', openable.fileUri.toString()); + environment.set('diffFileSecondary', editors[0].resource.toString()); + environment.set('diffFilePrimary', editors[1].resource.toString()); this.workspaceProvider.open(undefined, { payload: Array.from(environment.entries()) }); } } + + // Just open normally + else { + for (const openable of fileOpenables) { + + // Same Window: open via editor service in current window + if (this.shouldReuse(options, true /* file */)) { + let openables: IPathData[] = []; + + // Support: --goto parameter to open on line/col + if (options?.gotoLineMode) { + const pathColumnAware = parseLineAndColumnAware(openable.fileUri.path); + openables = [{ + fileUri: openable.fileUri.with({ path: pathColumnAware.path }), + lineNumber: pathColumnAware.line, + columnNumber: pathColumnAware.column + }]; + } else { + openables = [openable]; + } + + this.editorService.openEditors(await pathsToEditors(openables, this.fileService)); + } + + // New Window: open into empty window + else { + const environment = new Map(); + environment.set('openFile', openable.fileUri.toString()); + + if (options?.gotoLineMode) { + environment.set('gotoLineMode', 'true'); + } + + this.workspaceProvider.open(undefined, { payload: Array.from(environment.entries()) }); + } + } + } + + // Support wait mode + const waitMarkerFileURI = options?.waitMarkerFileURI; + if (waitMarkerFileURI) { + (async () => { + + // Wait for the resources to be closed in the editor... + await this.editorService.whenClosed(fileOpenables.map(openable => ({ resource: openable.fileUri })), { waitForSaved: true }); + + // ...before deleting the wait marker file + await this.fileService.del(waitMarkerFileURI); + })(); + } } } @@ -183,7 +266,11 @@ export class BrowserHostService extends Disposable implements IHostService { return this.labelService.getUriLabel(openable.fileUri); } - private shouldReuse(options: IOpenWindowOptions = {}, isFile: boolean): boolean { + private shouldReuse(options: IOpenWindowOptions = Object.create(null), isFile: boolean): boolean { + if (options.waitMarkerFileURI) { + return true; // always handle --wait in same window + } + const windowConfig = this.configurationService.getValue('window'); const openInNewWindowConfig = isFile ? (windowConfig?.openFilesInNewWindow || 'off' /* default */) : (windowConfig?.openFoldersInNewWindow || 'default' /* default */); @@ -196,7 +283,7 @@ export class BrowserHostService extends Disposable implements IHostService { } private async doOpenEmptyWindow(options?: IOpenEmptyWindowOptions): Promise { - this.workspaceProvider.open(undefined, { reuse: options?.forceReuseWindow }); + return this.workspaceProvider.open(undefined, { reuse: options?.forceReuseWindow }); } async toggleFullScreen(): Promise { diff --git a/src/vs/workbench/services/host/browser/host.ts b/src/vs/workbench/services/host/browser/host.ts index ad10de5ba65..590778fa56e 100644 --- a/src/vs/workbench/services/host/browser/host.ts +++ b/src/vs/workbench/services/host/browser/host.ts @@ -11,7 +11,7 @@ export const IHostService = createDecorator('hostService'); export interface IHostService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; //#region Focus diff --git a/src/vs/workbench/services/host/electron-browser/desktopHostService.ts b/src/vs/workbench/services/host/electron-sandbox/desktopHostService.ts similarity index 87% rename from src/vs/workbench/services/host/electron-browser/desktopHostService.ts rename to src/vs/workbench/services/host/electron-sandbox/desktopHostService.ts index 45d69aa3d91..ac98ee8f1fa 100644 --- a/src/vs/workbench/services/host/electron-browser/desktopHostService.ts +++ b/src/vs/workbench/services/host/electron-sandbox/desktopHostService.ts @@ -5,30 +5,29 @@ import { Event } from 'vs/base/common/event'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILabelService } from 'vs/platform/label/common/label'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWindowOpenable, IOpenWindowOptions, isFolderToOpen, isWorkspaceToOpen, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; import { Disposable } from 'vs/base/common/lifecycle'; -import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; export class DesktopHostService extends Disposable implements IHostService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @IElectronService private readonly electronService: IElectronService, @ILabelService private readonly labelService: ILabelService, - @IWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService ) { super(); } get onDidChangeFocus(): Event { return this._onDidChangeFocus; } private _onDidChangeFocus: Event = Event.latch(Event.any( - Event.map(Event.filter(this.electronService.onWindowFocus, id => id === this.environmentService.configuration.windowId), () => this.hasFocus), - Event.map(Event.filter(this.electronService.onWindowBlur, id => id === this.environmentService.configuration.windowId), () => this.hasFocus) + Event.map(Event.filter(this.electronService.onWindowFocus, id => id === this.electronService.windowId), () => this.hasFocus), + Event.map(Event.filter(this.electronService.onWindowBlur, id => id === this.electronService.windowId), () => this.hasFocus) )); get hasFocus(): boolean { @@ -42,7 +41,7 @@ export class DesktopHostService extends Disposable implements IHostService { return false; } - return activeWindowId === this.environmentService.configuration.windowId; + return activeWindowId === this.electronService.windowId; } openWindow(options?: IOpenEmptyWindowOptions): Promise; diff --git a/src/vs/workbench/services/hover/browser/hover.ts b/src/vs/workbench/services/hover/browser/hover.ts new file mode 100644 index 00000000000..a504b379d65 --- /dev/null +++ b/src/vs/workbench/services/hover/browser/hover.ts @@ -0,0 +1,112 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; + +export const IHoverService = createDecorator('hoverService'); + +/** + * Enables the convenient display of rich markdown-based hovers in the workbench. + */ +export interface IHoverService { + readonly _serviceBrand: undefined; + + /** + * Shows a hover, provided a hover with the same options object is not already visible. + * @param options A set of options defining the characteristics of the hover. + * @param focus Whether to focus the hover (useful for keyboard accessibility). + * + * **Example:** A simple usage with a single element target. + * + * ```typescript + * showHover({ + * text: new MarkdownString('Hello world'), + * target: someElement + * }); + * ``` + */ + showHover(options: IHoverOptions, focus?: boolean): void; + + /** + * Hides the hover if it was visible. + */ + hideHover(): void; +} + +export interface IHoverOptions { + /** + * The text to display in the primary section of the hover. + */ + text: IMarkdownString; + + /** + * The target for the hover. This determines the position of the hover and it will only be + * hidden when the mouse leaves both the hover and the target. A HTMLElement can be used for + * simple cases and a IHoverTarget for more complex cases where multiple elements and/or a + * dispose method is required. + */ + target: IHoverTarget | HTMLElement; + + /** + * A set of actions for the hover's "status bar". + */ + actions?: IHoverAction[]; + + /** + * An optional array of classes to add to the hover element. + */ + additionalClasses?: string[]; + + /** + * An optional link handler for markdown links, if this is not provided the IOpenerService will + * be used to open the links using its default options. + */ + linkHandler?(url: string): void; + + /** + * Whether to hide the hover when the mouse leaves the `target` and enters the actual hover. + * This is false by default and note that it will be ignored if any `actions` are provided such + * that they are accessible. + */ + hideOnHover?: boolean; +} + +export interface IHoverAction { + /** + * The label to use in the hover's status bar. + */ + label: string; + + /** + * The command ID of the action, this is used to resolve the keybinding to display after the + * action label. + */ + commandId: string; + + /** + * An optional class of an icon that will be displayed before the label. + */ + iconClass?: string; + + /** + * The callback to run the action. + * @param target The action element that was activated. + */ + run(target: HTMLElement): void; +} + +/** + * A target for a hover. + */ +export interface IHoverTarget extends IDisposable { + /** + * A set of target elements used to position the hover. If multiple elements are used the hover + * will try to not overlap any target element. An example use case for this is show a hover for + * wrapped text. + */ + readonly targetElements: readonly HTMLElement[]; +} diff --git a/src/vs/workbench/services/hover/browser/hoverService.ts b/src/vs/workbench/services/hover/browser/hoverService.ts new file mode 100644 index 00000000000..a025f1ddb70 --- /dev/null +++ b/src/vs/workbench/services/hover/browser/hoverService.ts @@ -0,0 +1,111 @@ +/*--------------------------------------------------------------------------------------------- + * 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/hover'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { editorHoverBackground, editorHoverBorder, textLinkForeground, editorHoverForeground, editorHoverStatusBarBackground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry'; +import { IHoverService, IHoverOptions } from 'vs/workbench/services/hover/browser/hover'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { HoverWidget } from 'vs/workbench/services/hover/browser/hoverWidget'; +import { IContextViewProvider, IDelegate } from 'vs/base/browser/ui/contextview/contextview'; + +export class HoverService implements IHoverService { + declare readonly _serviceBrand: undefined; + + private _currentHoverOptions: IHoverOptions | undefined; + + constructor( + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IContextViewService private readonly _contextViewService: IContextViewService + ) { + } + + showHover(options: IHoverOptions, focus?: boolean): void { + if (this._currentHoverOptions === options) { + return; + } + this._currentHoverOptions = options; + + const hover = this._instantiationService.createInstance(HoverWidget, options); + hover.onDispose(() => this._currentHoverOptions = undefined); + const provider = this._contextViewService as IContextViewProvider; + provider.showContextView(new HoverContextViewDelegate(hover, focus)); + hover.onRequestLayout(() => provider.layout()); + } + + hideHover(): void { + if (!this._currentHoverOptions) { + return; + } + this._currentHoverOptions = undefined; + this._contextViewService.hideContextView(); + } +} + +class HoverContextViewDelegate implements IDelegate { + + get anchorPosition() { + return this._hover.anchor; + } + + constructor( + private readonly _hover: HoverWidget, + private readonly _focus: boolean = false + ) { + } + + render(container: HTMLElement) { + this._hover.render(container); + if (this._focus) { + this._hover.focus(); + } + return this._hover; + } + + getAnchor() { + return { + x: this._hover.x, + y: this._hover.y + }; + } + + layout() { + this._hover.layout(); + } +} + +registerSingleton(IHoverService, HoverService, true); + +registerThemingParticipant((theme, collector) => { + const hoverBackground = theme.getColor(editorHoverBackground); + if (hoverBackground) { + collector.addRule(`.monaco-workbench .workbench-hover { background-color: ${hoverBackground}; }`); + } + const hoverBorder = theme.getColor(editorHoverBorder); + if (hoverBorder) { + collector.addRule(`.monaco-workbench .workbench-hover { border: 1px solid ${hoverBorder}; }`); + collector.addRule(`.monaco-workbench .workbench-hover .hover-row:not(:first-child):not(:empty) { border-top: 1px solid ${hoverBorder.transparent(0.5)}; }`); + collector.addRule(`.monaco-workbench .workbench-hover hr { border-top: 1px solid ${hoverBorder.transparent(0.5)}; }`); + collector.addRule(`.monaco-workbench .workbench-hover hr { border-bottom: 0px solid ${hoverBorder.transparent(0.5)}; }`); + } + const link = theme.getColor(textLinkForeground); + if (link) { + collector.addRule(`.monaco-workbench .workbench-hover a { color: ${link}; }`); + } + const hoverForeground = theme.getColor(editorHoverForeground); + if (hoverForeground) { + collector.addRule(`.monaco-workbench .workbench-hover { color: ${hoverForeground}; }`); + } + const actionsBackground = theme.getColor(editorHoverStatusBarBackground); + if (actionsBackground) { + collector.addRule(`.monaco-workbench .workbench-hover .hover-row .actions { background-color: ${actionsBackground}; }`); + } + const codeBackground = theme.getColor(textCodeBlockBackground); + if (codeBackground) { + collector.addRule(`.monaco-workbench .workbench-hover code { background-color: ${codeBackground}; }`); + } +}); diff --git a/src/vs/workbench/services/hover/browser/hoverWidget.ts b/src/vs/workbench/services/hover/browser/hoverWidget.ts new file mode 100644 index 00000000000..702e4e5143b --- /dev/null +++ b/src/vs/workbench/services/hover/browser/hoverWidget.ts @@ -0,0 +1,238 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; +import { Event, Emitter } from 'vs/base/common/event'; +import * as dom from 'vs/base/browser/dom'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IHoverTarget, IHoverOptions } from 'vs/workbench/services/hover/browser/hover'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { HoverWidget as BaseHoverWidget, renderHoverAction } from 'vs/base/browser/ui/hover/hoverWidget'; +import { Widget } from 'vs/base/browser/ui/widget'; +import { AnchorPosition } from 'vs/base/browser/ui/contextview/contextview'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; + +const $ = dom.$; + +export class HoverWidget extends Widget { + private readonly _messageListeners = new DisposableStore(); + private readonly _mouseTracker: CompositeMouseTracker; + + private readonly _hover: BaseHoverWidget; + private readonly _target: IHoverTarget; + private readonly _linkHandler: (url: string) => any; + + private _isDisposed: boolean = false; + private _anchor: AnchorPosition = AnchorPosition.ABOVE; + private _x: number = 0; + private _y: number = 0; + + get isDisposed(): boolean { return this._isDisposed; } + get domNode(): HTMLElement { return this._hover.containerDomNode; } + + private readonly _onDispose = this._register(new Emitter()); + get onDispose(): Event { return this._onDispose.event; } + private readonly _onRequestLayout = this._register(new Emitter()); + get onRequestLayout(): Event { return this._onRequestLayout.event; } + + get anchor(): AnchorPosition { return this._anchor; } + get x(): number { return this._x; } + get y(): number { return this._y; } + + constructor( + options: IHoverOptions, + @IKeybindingService private readonly _keybindingService: IKeybindingService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IOpenerService private readonly _openerService: IOpenerService + ) { + super(); + + this._linkHandler = options.linkHandler || this._openerService.open; + + this._target = 'targetElements' in options.target ? options.target : new ElementHoverTarget(options.target); + + this._hover = this._register(new BaseHoverWidget()); + + this._hover.containerDomNode.classList.add('workbench-hover', 'fadeIn'); + if (options.additionalClasses) { + this._hover.containerDomNode.classList.add(...options.additionalClasses); + } + + // Don't allow mousedown out of the widget, otherwise preventDefault will call and text will + // not be selected. + this.onmousedown(this._hover.containerDomNode, e => e.stopPropagation()); + + // Hide hover on escape + this.onkeydown(this._hover.containerDomNode, e => { + if (e.equals(KeyCode.Escape)) { + this.dispose(); + } + }); + + const rowElement = $('div.hover-row.markdown-hover'); + const contentsElement = $('div.hover-contents'); + const markdownElement = renderMarkdown(options.text, { + actionHandler: { + callback: (content) => this._linkHandler(content), + disposeables: this._messageListeners + }, + codeBlockRenderer: async (_, value) => { + const fontFamily = this._configurationService.getValue('editor').fontFamily || EDITOR_FONT_DEFAULTS.fontFamily; + return `${value.replace(/\n/g, '
    ')}
    `; + }, + codeBlockRenderCallback: () => { + contentsElement.classList.add('code-hover-contents'); + // This changes the dimensions of the hover so trigger a layout + this._onRequestLayout.fire(); + } + }); + contentsElement.appendChild(markdownElement); + rowElement.appendChild(contentsElement); + this._hover.contentsDomNode.appendChild(rowElement); + + if (options.actions && options.actions.length > 0) { + const statusBarElement = $('div.hover-row.status-bar'); + const actionsElement = $('div.actions'); + options.actions.forEach(action => { + const keybinding = this._keybindingService.lookupKeybinding(action.commandId); + const keybindingLabel = keybinding ? keybinding.getLabel() : null; + renderHoverAction(actionsElement, { + label: action.label, + commandId: action.commandId, + run: e => { + action.run(e); + this.dispose(); + }, + iconClass: action.iconClass + }, keybindingLabel); + }); + statusBarElement.appendChild(actionsElement); + this._hover.containerDomNode.appendChild(statusBarElement); + } + + const mouseTrackerTargets = [...this._target.targetElements]; + if (!options.hideOnHover || (options.actions && options.actions.length > 0)) { + mouseTrackerTargets.push(this._hover.containerDomNode); + } + this._mouseTracker = new CompositeMouseTracker(mouseTrackerTargets); + this._register(this._mouseTracker.onMouseOut(() => this.dispose())); + this._register(this._mouseTracker); + } + + public render(container?: HTMLElement): void { + if (this._hover.containerDomNode.parentElement !== container) { + container?.appendChild(this._hover.containerDomNode); + } + + this.layout(); + } + + public layout() { + this._hover.containerDomNode.classList.remove('right-aligned'); + this._hover.contentsDomNode.style.maxHeight = ''; + + // Get horizontal alignment and position + const targetBounds = this._target.targetElements.map(e => e.getBoundingClientRect()); + const targetLeft = Math.min(...targetBounds.map(e => e.left)); + if (targetLeft + this._hover.containerDomNode.clientWidth >= document.documentElement.clientWidth) { + this._x = document.documentElement.clientWidth; + this._hover.containerDomNode.classList.add('right-aligned'); + } else { + this._x = targetLeft; + } + + // Get vertical alignment and position + const targetTop = Math.min(...targetBounds.map(e => e.top)); + if (targetTop - this._hover.containerDomNode.clientHeight < 0) { + this._anchor = AnchorPosition.BELOW; + this._y = Math.max(...targetBounds.map(e => e.bottom)) - 2; + } else { + this._y = targetTop; + } + + this._hover.onContentsChanged(); + } + + public focus() { + this._hover.containerDomNode.focus(); + } + + public hide(): void { + this.dispose(); + } + + public dispose(): void { + if (!this._isDisposed) { + this._onDispose.fire(); + this._hover.containerDomNode.parentElement?.removeChild(this.domNode); + this._messageListeners.dispose(); + this._target.dispose(); + super.dispose(); + } + this._isDisposed = true; + } +} + +class CompositeMouseTracker extends Widget { + private _isMouseIn: boolean = false; + private _mouseTimeout: number | undefined; + + private readonly _onMouseOut = new Emitter(); + get onMouseOut(): Event { return this._onMouseOut.event; } + + constructor( + private _elements: HTMLElement[] + ) { + super(); + this._elements.forEach(n => this.onmouseover(n, () => this._onTargetMouseOver())); + this._elements.forEach(n => this.onnonbubblingmouseout(n, () => this._onTargetMouseOut())); + } + + private _onTargetMouseOver(): void { + this._isMouseIn = true; + this._clearEvaluateMouseStateTimeout(); + } + + private _onTargetMouseOut(): void { + this._isMouseIn = false; + this._evaluateMouseState(); + } + + private _evaluateMouseState(): void { + this._clearEvaluateMouseStateTimeout(); + // Evaluate whether the mouse is still outside asynchronously such that other mouse targets + // have the opportunity to first their mouse in event. + this._mouseTimeout = window.setTimeout(() => this._fireIfMouseOutside(), 0); + } + + private _clearEvaluateMouseStateTimeout(): void { + if (this._mouseTimeout) { + clearTimeout(this._mouseTimeout); + this._mouseTimeout = undefined; + } + } + + private _fireIfMouseOutside(): void { + if (!this._isMouseIn) { + this._onMouseOut.fire(); + } + } +} + +class ElementHoverTarget implements IHoverTarget { + readonly targetElements: readonly HTMLElement[]; + + constructor( + private _element: HTMLElement + ) { + this.targetElements = [this._element]; + } + + dispose(): void { + } +} diff --git a/src/vs/workbench/services/hover/browser/media/hover.css b/src/vs/workbench/services/hover/browser/media/hover.css new file mode 100644 index 00000000000..47d8ab484c6 --- /dev/null +++ b/src/vs/workbench/services/hover/browser/media/hover.css @@ -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. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .workbench-hover { + position: relative; + font-size: 14px; + line-height: 19px; + animation: fadein 100ms linear; + /* Must be higher than sash's z-index and terminal canvases */ + z-index: 40; + overflow: hidden; + max-width: 700px; +} + +.monaco-workbench .workbench-hover a { + color: #3794ff; +} + +.monaco-workbench .workbench-hover.right-aligned .hover-row.status-bar .actions { + flex-direction: row-reverse; +} + +.monaco-workbench .workbench-hover.right-aligned .hover-row.status-bar .actions .action-container { + margin-right: 0; + margin-left: 16px; +} diff --git a/src/vs/workbench/services/integrity/browser/integrityService.ts b/src/vs/workbench/services/integrity/browser/integrityService.ts index 0d0e5681cef..a530266ecd6 100644 --- a/src/vs/workbench/services/integrity/browser/integrityService.ts +++ b/src/vs/workbench/services/integrity/browser/integrityService.ts @@ -8,7 +8,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class BrowserIntegrityServiceImpl implements IIntegrityService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; async isPure(): Promise { return { isPure: true, proof: [] }; diff --git a/src/vs/workbench/services/integrity/common/integrity.ts b/src/vs/workbench/services/integrity/common/integrity.ts index 07d34aef076..afbfde91166 100644 --- a/src/vs/workbench/services/integrity/common/integrity.ts +++ b/src/vs/workbench/services/integrity/common/integrity.ts @@ -21,7 +21,7 @@ export interface IntegrityTestResult { } export interface IIntegrityService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; isPure(): Promise; } diff --git a/src/vs/workbench/services/integrity/node/integrityService.ts b/src/vs/workbench/services/integrity/node/integrityService.ts index 8264febf337..cc1b47000c2 100644 --- a/src/vs/workbench/services/integrity/node/integrityService.ts +++ b/src/vs/workbench/services/integrity/node/integrityService.ts @@ -10,7 +10,7 @@ import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; import { ChecksumPair, IIntegrityService, IntegrityTestResult } from 'vs/workbench/services/integrity/common/integrity'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import product from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -56,7 +56,7 @@ class IntegrityStorage { export class IntegrityServiceImpl implements IIntegrityService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _storage: IntegrityStorage; private _isPurePromise: Promise; @@ -65,7 +65,8 @@ export class IntegrityServiceImpl implements IIntegrityService { @INotificationService private readonly notificationService: INotificationService, @IStorageService storageService: IStorageService, @ILifecycleService private readonly lifecycleService: ILifecycleService, - @IOpenerService private readonly openerService: IOpenerService + @IOpenerService private readonly openerService: IOpenerService, + @IProductService private readonly productService: IProductService ) { this._storage = new IntegrityStorage(storageService); @@ -82,12 +83,12 @@ export class IntegrityServiceImpl implements IIntegrityService { private _prompt(): void { const storedData = this._storage.get(); - if (storedData?.dontShowPrompt && storedData.commit === product.commit) { + if (storedData?.dontShowPrompt && storedData.commit === this.productService.commit) { return; // Do not prompt } - const checksumFailMoreInfoUrl = product.checksumFailMoreInfoUrl; - const message = nls.localize('integrity.prompt', "Your {0} installation appears to be corrupt. Please reinstall.", product.nameShort); + const checksumFailMoreInfoUrl = this.productService.checksumFailMoreInfoUrl; + const message = nls.localize('integrity.prompt', "Your {0} installation appears to be corrupt. Please reinstall.", this.productService.nameShort); if (checksumFailMoreInfoUrl) { this.notificationService.prompt( Severity.Warning, @@ -100,7 +101,7 @@ export class IntegrityServiceImpl implements IIntegrityService { { label: nls.localize('integrity.dontShowAgain', "Don't Show Again"), isSecondary: true, - run: () => this._storage.set({ dontShowPrompt: true, commit: product.commit }) + run: () => this._storage.set({ dontShowPrompt: true, commit: this.productService.commit }) } ], { sticky: true } @@ -119,7 +120,7 @@ export class IntegrityServiceImpl implements IIntegrityService { } private async _isPure(): Promise { - const expectedChecksums = product.checksums || {}; + const expectedChecksums = this.productService.checksums || {}; await this.lifecycleService.when(LifecyclePhase.Eventually); diff --git a/src/vs/workbench/services/issue/electron-browser/issueService.ts b/src/vs/workbench/services/issue/electron-sandbox/issueService.ts similarity index 71% rename from src/vs/workbench/services/issue/electron-browser/issueService.ts rename to src/vs/workbench/services/issue/electron-sandbox/issueService.ts index 0143cff6f3e..a2e5a256559 100644 --- a/src/vs/workbench/services/issue/electron-browser/issueService.ts +++ b/src/vs/workbench/services/issue/electron-sandbox/issueService.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IIssueService } from 'vs/platform/issue/node/issue'; -import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; -import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; +import { IIssueService } from 'vs/platform/issue/electron-sandbox/issue'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class IssueService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(@IMainProcessService mainProcessService: IMainProcessService) { return createChannelSender(mainProcessService.getChannel('issue')); diff --git a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts index 98c4d011305..e6621b9d06b 100644 --- a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts +++ b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts @@ -6,6 +6,7 @@ import { localize } from 'vs/nls'; import { Queue } from 'vs/base/common/async'; import * as json from 'vs/base/common/json'; +import * as objects from 'vs/base/common/objects'; import { setProperty } from 'vs/base/common/jsonEdit'; import { Edit } from 'vs/base/common/jsonFormatter'; import { Disposable, IReference } from 'vs/base/common/lifecycle'; @@ -30,7 +31,7 @@ export const IKeybindingEditingService = createDecorator; @@ -79,7 +80,7 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding if (keybindingItem.isDefault && keybindingItem.resolvedKeybinding) { this.removeDefaultKeybinding(keybindingItem, model); } - return this.save().then(() => reference.dispose()); + return this.save().finally(() => reference.dispose()); }); } @@ -92,7 +93,7 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding } else { this.removeUserKeybinding(keybindingItem, model); } - return this.save().then(() => reference.dispose()); + return this.save().finally(() => reference.dispose()); }); } @@ -104,7 +105,7 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding this.removeUserKeybinding(keybindingItem, model); this.removeUnassignedDefaultKeybinding(keybindingItem, model); } - return this.save().then(() => reference.dispose()); + return this.save().finally(() => reference.dispose()); }); } @@ -143,7 +144,11 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding const eol = model.getEOL(); const key = keybindingItem.resolvedKeybinding ? keybindingItem.resolvedKeybinding.getUserSettingsLabel() : null; if (key) { - this.applyEditsToBuffer(setProperty(model.getValue(), [-1], this.asObject(key, keybindingItem.command, keybindingItem.when ? keybindingItem.when.serialize() : undefined, true), { tabSize, insertSpaces, eol })[0], model); + const entry: IUserFriendlyKeybinding = this.asObject(key, keybindingItem.command, keybindingItem.when ? keybindingItem.when.serialize() : undefined, true); + const userKeybindingEntries = json.parse(model.getValue()); + if (userKeybindingEntries.every(e => !this.areSame(e, entry))) { + this.applyEditsToBuffer(setProperty(model.getValue(), [-1], entry, { tabSize, insertSpaces, eol })[0], model); + } } } @@ -196,6 +201,26 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding return object; } + private areSame(a: IUserFriendlyKeybinding, b: IUserFriendlyKeybinding): boolean { + if (a.command !== b.command) { + return false; + } + if (a.key !== b.key) { + return false; + } + const whenA = ContextKeyExpr.deserialize(a.when); + const whenB = ContextKeyExpr.deserialize(b.when); + if ((whenA && !whenB) || (!whenA && whenB)) { + return false; + } + if (whenA && whenB && !whenA.equals(whenB)) { + return false; + } + if (!objects.equals(a.args, b.args)) { + return false; + } + return true; + } private applyEditsToBuffer(edit: Edit, model: ITextModel): void { const startPosition = model.getPositionAt(edit.offset); @@ -206,7 +231,6 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding model.pushEditOperations([new Selection(startPosition.lineNumber, startPosition.column, startPosition.lineNumber, startPosition.column)], [editOperation], () => []); } - private resolveModelReference(): Promise> { return this.fileService.exists(this.resource) .then(exists => { @@ -230,10 +254,12 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding if (model.getValue()) { const parsed = this.parse(model); if (parsed.parseErrors.length) { + reference.dispose(); return Promise.reject(new Error(localize('parseErrors', "Unable to write to the keybindings configuration file. Please open it to correct errors/warnings in the file and try again."))); } if (parsed.result) { if (!isArray(parsed.result)) { + reference.dispose(); return Promise.reject(new Error(localize('errorInvalidConfiguration', "Unable to write to the keybindings configuration file. It has an object which is not of type Array. Please open the file to clean up and try again."))); } } else { diff --git a/src/vs/workbench/services/keybinding/common/keymapInfo.ts b/src/vs/workbench/services/keybinding/common/keymapInfo.ts index b521f80faa6..9eec852a2e9 100644 --- a/src/vs/workbench/services/keybinding/common/keymapInfo.ts +++ b/src/vs/workbench/services/keybinding/common/keymapInfo.ts @@ -94,7 +94,7 @@ export type IKeyboardLayoutInfo = (IWindowsKeyboardLayoutInfo | ILinuxKeyboardLa export const IKeymapService = createDecorator('keymapService'); export interface IKeymapService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; onDidChangeKeyboardMapper: Event; getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper; getCurrentKeyboardLayout(): IKeyboardLayoutInfo | null; diff --git a/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts b/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts index 7416fd9418d..cd493c726b6 100644 --- a/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts +++ b/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts @@ -58,6 +58,8 @@ import { TestTextResourcePropertiesService, TestContextService, TestWorkingCopyS import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; class TestEnvironmentService extends NativeWorkbenchEnvironmentService { @@ -117,6 +119,7 @@ suite('KeybindingsEditing', () => { fileService.registerProvider(Schemas.file, diskFileSystemProvider); fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, diskFileSystemProvider, environmentService, new NullLogService())); instantiationService.stub(IFileService, fileService); + instantiationService.stub(IUriIdentityService, new UriIdentityService(fileService)); instantiationService.stub(IWorkingCopyService, new TestWorkingCopyService()); instantiationService.stub(IWorkingCopyFileService, instantiationService.createInstance(WorkingCopyFileService)); instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); @@ -217,6 +220,16 @@ suite('KeybindingsEditing', () => { .then(() => assert.deepEqual(getUserKeybindings(), expected)); }); + test('remove a default keybinding should not ad duplicate entries', async () => { + const expected: IUserFriendlyKeybinding[] = [{ key: 'alt+c', command: '-a' }]; + await testObject.removeKeybinding(aResolvedKeybindingItem({ command: 'a', firstPart: { keyCode: KeyCode.KEY_C, modifiers: { altKey: true } } })); + await testObject.removeKeybinding(aResolvedKeybindingItem({ command: 'a', firstPart: { keyCode: KeyCode.KEY_C, modifiers: { altKey: true } } })); + await testObject.removeKeybinding(aResolvedKeybindingItem({ command: 'a', firstPart: { keyCode: KeyCode.KEY_C, modifiers: { altKey: true } } })); + await testObject.removeKeybinding(aResolvedKeybindingItem({ command: 'a', firstPart: { keyCode: KeyCode.KEY_C, modifiers: { altKey: true } } })); + await testObject.removeKeybinding(aResolvedKeybindingItem({ command: 'a', firstPart: { keyCode: KeyCode.KEY_C, modifiers: { altKey: true } } })); + assert.deepEqual(getUserKeybindings(), expected); + }); + test('remove a user keybinding', () => { writeToKeybindingsFile({ key: 'alt+c', command: 'b' }); return testObject.removeKeybinding(aResolvedKeybindingItem({ command: 'b', firstPart: { keyCode: KeyCode.KEY_C, modifiers: { altKey: true } }, isDefault: false })) diff --git a/src/vs/workbench/services/keybinding/test/electron-browser/macLinuxFallbackKeyboardMapper.test.ts b/src/vs/workbench/services/keybinding/test/electron-browser/macLinuxFallbackKeyboardMapper.test.ts index c61fee376c9..171dea63f67 100644 --- a/src/vs/workbench/services/keybinding/test/electron-browser/macLinuxFallbackKeyboardMapper.test.ts +++ b/src/vs/workbench/services/keybinding/test/electron-browser/macLinuxFallbackKeyboardMapper.test.ts @@ -107,9 +107,9 @@ suite('keyboardMapper - MAC fallback', () => { }, { label: '⌘', - ariaLabel: 'Command+', + ariaLabel: 'Command', electronAccelerator: null, - userSettingsLabel: 'cmd+', + userSettingsLabel: 'cmd', isWYSIWYG: true, isChord: false, dispatchParts: [null], @@ -228,10 +228,10 @@ suite('keyboardMapper - LINUX fallback', () => { code: null! }, { - label: 'Ctrl+', - ariaLabel: 'Control+', + label: 'Ctrl', + ariaLabel: 'Control', electronAccelerator: null, - userSettingsLabel: 'ctrl+', + userSettingsLabel: 'ctrl', isWYSIWYG: true, isChord: false, dispatchParts: [null], diff --git a/src/vs/workbench/services/keybinding/test/electron-browser/macLinuxKeyboardMapper.test.ts b/src/vs/workbench/services/keybinding/test/electron-browser/macLinuxKeyboardMapper.test.ts index f4088abc61e..4576fec3d30 100644 --- a/src/vs/workbench/services/keybinding/test/electron-browser/macLinuxKeyboardMapper.test.ts +++ b/src/vs/workbench/services/keybinding/test/electron-browser/macLinuxKeyboardMapper.test.ts @@ -344,9 +344,9 @@ suite('keyboardMapper - MAC de_ch', () => { }, { label: '⌘', - ariaLabel: 'Command+', + ariaLabel: 'Command', electronAccelerator: null, - userSettingsLabel: 'cmd+', + userSettingsLabel: 'cmd', isWYSIWYG: true, isChord: false, dispatchParts: [null], @@ -368,9 +368,9 @@ suite('keyboardMapper - MAC de_ch', () => { }, { label: '⌘', - ariaLabel: 'Command+', + ariaLabel: 'Command', electronAccelerator: null, - userSettingsLabel: 'cmd+', + userSettingsLabel: 'cmd', isWYSIWYG: true, isChord: false, dispatchParts: [null], @@ -425,9 +425,9 @@ suite('keyboardMapper - MAC en_us', () => { }, { label: '⌘', - ariaLabel: 'Command+', + ariaLabel: 'Command', electronAccelerator: null, - userSettingsLabel: 'cmd+', + userSettingsLabel: 'cmd', isWYSIWYG: true, isChord: false, dispatchParts: [null], @@ -449,9 +449,9 @@ suite('keyboardMapper - MAC en_us', () => { }, { label: '⌘', - ariaLabel: 'Command+', + ariaLabel: 'Command', electronAccelerator: null, - userSettingsLabel: 'cmd+', + userSettingsLabel: 'cmd', isWYSIWYG: true, isChord: false, dispatchParts: [null], @@ -780,10 +780,10 @@ suite('keyboardMapper - LINUX de_ch', () => { code: 'ControlLeft' }, { - label: 'Ctrl+', - ariaLabel: 'Control+', + label: 'Ctrl', + ariaLabel: 'Control', electronAccelerator: null, - userSettingsLabel: 'ctrl+', + userSettingsLabel: 'ctrl', isWYSIWYG: true, isChord: false, dispatchParts: [null], @@ -804,10 +804,10 @@ suite('keyboardMapper - LINUX de_ch', () => { code: 'ControlRight' }, { - label: 'Ctrl+', - ariaLabel: 'Control+', + label: 'Ctrl', + ariaLabel: 'Control', electronAccelerator: null, - userSettingsLabel: 'ctrl+', + userSettingsLabel: 'ctrl', isWYSIWYG: true, isChord: false, dispatchParts: [null], @@ -1180,10 +1180,10 @@ suite('keyboardMapper - LINUX en_us', () => { code: 'ControlLeft' }, { - label: 'Ctrl+', - ariaLabel: 'Control+', + label: 'Ctrl', + ariaLabel: 'Control', electronAccelerator: null, - userSettingsLabel: 'ctrl+', + userSettingsLabel: 'ctrl', isWYSIWYG: true, isChord: false, dispatchParts: [null], @@ -1204,10 +1204,10 @@ suite('keyboardMapper - LINUX en_us', () => { code: 'ControlRight' }, { - label: 'Ctrl+', - ariaLabel: 'Control+', + label: 'Ctrl', + ariaLabel: 'Control', electronAccelerator: null, - userSettingsLabel: 'ctrl+', + userSettingsLabel: 'ctrl', isWYSIWYG: true, isChord: false, dispatchParts: [null], diff --git a/src/vs/workbench/services/keybinding/test/electron-browser/windowsKeyboardMapper.test.ts b/src/vs/workbench/services/keybinding/test/electron-browser/windowsKeyboardMapper.test.ts index 5babd01552e..8087a1c7992 100644 --- a/src/vs/workbench/services/keybinding/test/electron-browser/windowsKeyboardMapper.test.ts +++ b/src/vs/workbench/services/keybinding/test/electron-browser/windowsKeyboardMapper.test.ts @@ -308,10 +308,10 @@ suite('keyboardMapper - WINDOWS de_ch', () => { code: null! }, { - label: 'Ctrl+', - ariaLabel: 'Control+', + label: 'Ctrl', + ariaLabel: 'Control', electronAccelerator: null, - userSettingsLabel: 'ctrl+', + userSettingsLabel: 'ctrl', isWYSIWYG: true, isChord: false, dispatchParts: [null], @@ -396,10 +396,10 @@ suite('keyboardMapper - WINDOWS en_us', () => { code: null! }, { - label: 'Ctrl+', - ariaLabel: 'Control+', + label: 'Ctrl', + ariaLabel: 'Control', electronAccelerator: null, - userSettingsLabel: 'ctrl+', + userSettingsLabel: 'ctrl', isWYSIWYG: true, isChord: false, dispatchParts: [null], diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index fc519874210..66b354a5a71 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -12,9 +12,8 @@ import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWo import { Registry } from 'vs/platform/registry/common/platform'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceContextService, IWorkspace } from 'vs/platform/workspace/common/workspace'; -import { isEqual, basenameOrAuthority, basename, joinPath, dirname } from 'vs/base/common/resources'; +import { basenameOrAuthority, basename, joinPath, dirname } from 'vs/base/common/resources'; import { tildify, getPathLabel } from 'vs/base/common/labels'; -import { ltrim } from 'vs/base/common/strings'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, WORKSPACE_EXTENSION, toWorkspaceIdentifier, isWorkspaceIdentifier, isUntitledWorkspace } from 'vs/platform/workspaces/common/workspaces'; import { ILabelService, ResourceLabelFormatter, ResourceLabelFormatting, IFormatterChangeEvent } from 'vs/platform/label/common/label'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; @@ -52,6 +51,10 @@ const resourceLabelFormattersExtPoint = ExtensionsRegistry.registerExtensionPoin type: 'string', description: localize('vscode.extension.contributes.resourceLabelFormatters.separator', "Separator to be used in the uri label display. '/' or '\' as an example.") }, + stripPathStartingSeparator: { + type: 'boolean', + description: localize('vscode.extension.contributes.resourceLabelFormatters.stripPathStartingSeparator', "Controls whether `${path}` substitutions should have starting separator characters stripped.") + }, tildify: { type: 'boolean', description: localize('vscode.extension.contributes.resourceLabelFormatters.tildify', "Controls if the start of the uri label should be tildified when possible.") @@ -92,7 +95,7 @@ Registry.as(WorkbenchExtensions.Workbench).regi export class LabelService extends Disposable implements ILabelService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private formatters: ResourceLabelFormatter[] = []; @@ -139,20 +142,21 @@ export class LabelService extends Disposable implements ILabelService { } let label: string | undefined; - const baseResource = this.contextService && this.contextService.getWorkspaceFolder(resource); + const baseResource = this.contextService?.getWorkspaceFolder(resource); if (options.relative && baseResource) { - let relativeLabel: string; - if (isEqual(baseResource.uri, resource)) { - relativeLabel = ''; // no label if resources are identical - } else { - const baseResourceLabel = this.formatUri(baseResource.uri, formatting, options.noPrefix); - relativeLabel = ltrim(this.formatUri(resource, formatting, options.noPrefix).substring(baseResourceLabel.length), formatting.separator); + const baseResourceLabel = this.formatUri(baseResource.uri, formatting, options.noPrefix); + let relativeLabel = this.formatUri(resource, formatting, options.noPrefix); + + let overlap = 0; + while (relativeLabel[overlap] && relativeLabel[overlap] === baseResourceLabel[overlap]) { overlap++; } + if (!relativeLabel[overlap] || relativeLabel[overlap] === formatting.separator) { + relativeLabel = relativeLabel.substring(1 + overlap); } const hasMultipleRoots = this.contextService.getWorkspace().folders.length > 1; if (hasMultipleRoots && !options.noPrefix) { - const rootName = (baseResource && baseResource.name) ? baseResource.name : basenameOrAuthority(baseResource.uri); + const rootName = baseResource?.name ?? basenameOrAuthority(baseResource.uri); relativeLabel = relativeLabel ? (rootName + ' â€ĸ ' + relativeLabel) : rootName; // always show root basename if there are multiple } @@ -244,7 +248,10 @@ export class LabelService extends Disposable implements ILabelService { switch (token) { case 'scheme': return resource.scheme; case 'authority': return resource.authority; - case 'path': return resource.path; + case 'path': + return formatting.stripPathStartingSeparator + ? resource.path.slice(resource.path[0] === formatting.separator ? 1 : 0) + : resource.path; default: { if (qsToken === 'query') { const { query } = resource; diff --git a/src/vs/workbench/services/label/test/browser/label.test.ts b/src/vs/workbench/services/label/test/browser/label.test.ts index 89b38e9cd1d..8828cd48fd5 100644 --- a/src/vs/workbench/services/label/test/browser/label.test.ts +++ b/src/vs/workbench/services/label/test/browser/label.test.ts @@ -3,14 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as resources from 'vs/base/common/resources'; import * as assert from 'assert'; import { TestEnvironmentService, TestPathService } from 'vs/workbench/test/browser/workbenchTestServices'; import { URI } from 'vs/base/common/uri'; import { LabelService } from 'vs/workbench/services/label/common/labelService'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; +import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; suite('URI Label', () => { - let labelService: LabelService; setup(() => { @@ -156,3 +157,95 @@ suite('URI Label', () => { assert.equal(labelService.getUriLabel(uri1, { relative: false }), 'LABEL: /END'); }); }); + + +suite('multi-root worksapce', () => { + let labelService: LabelService; + + setup(() => { + const sources = URI.file('folder1/src'); + const tests = URI.file('folder1/test'); + const other = URI.file('folder2'); + + labelService = new LabelService( + TestEnvironmentService, + new TestContextService( + new Workspace('test-workspaace', [ + new WorkspaceFolder({ uri: sources, index: 0, name: 'Sources' }, { uri: sources.toString() }), + new WorkspaceFolder({ uri: tests, index: 1, name: 'Tests' }, { uri: tests.toString() }), + new WorkspaceFolder({ uri: other, index: 2, name: resources.basename(other) }, { uri: other.toString() }), + ])), + new TestPathService()); + }); + + test('labels of files in multiroot workspaces are the foldername folloed by offset from the folder', () => { + labelService.registerFormatter({ + scheme: 'file', + formatting: { + label: '${authority}${path}', + separator: '/', + tildify: false, + normalizeDriveLetter: false, + authorityPrefix: '//', + workspaceSuffix: '' + } + }); + + const tests = { + 'folder1/src/file': 'Sources â€ĸ file', + 'folder1/src/folder/file': 'Sources â€ĸ folder/file', + 'folder1/src': 'Sources', + 'folder1/other': '/folder1/other', + 'folder2/other': 'folder2 â€ĸ other', + }; + + Object.entries(tests).forEach(([path, label]) => { + const generated = labelService.getUriLabel(URI.file(path), { relative: true }); + assert.equal(generated, label); + }); + }); + + test('labels with context after path', () => { + labelService.registerFormatter({ + scheme: 'file', + formatting: { + label: '${path} (${scheme})', + separator: '/', + } + }); + + const tests = { + 'folder1/src/file': 'Sources â€ĸ file (file)', + 'folder1/src/folder/file': 'Sources â€ĸ folder/file (file)', + 'folder1/src': 'Sources', + 'folder1/other': '/folder1/other (file)', + 'folder2/other': 'folder2 â€ĸ other (file)', + }; + + Object.entries(tests).forEach(([path, label]) => { + const generated = labelService.getUriLabel(URI.file(path), { relative: true }); + assert.equal(generated, label, path); + }); + }); + + test('stripPathStartingSeparator', () => { + labelService.registerFormatter({ + scheme: 'file', + formatting: { + label: '${path}', + separator: '/', + stripPathStartingSeparator: true + } + }); + + const tests = { + 'folder1/src/file': 'Sources â€ĸ file', + 'other/blah': 'other/blah', + }; + + Object.entries(tests).forEach(([path, label]) => { + const generated = labelService.getUriLabel(URI.file(path), { relative: true }); + assert.equal(generated, label, path); + }); + }); +}); diff --git a/src/vs/workbench/services/layout/browser/layoutService.ts b/src/vs/workbench/services/layout/browser/layoutService.ts index 102bbd6225e..b1213ff2ce7 100644 --- a/src/vs/workbench/services/layout/browser/layoutService.ts +++ b/src/vs/workbench/services/layout/browser/layoutService.ts @@ -50,7 +50,7 @@ export function positionFromString(str: string): Position { export interface IWorkbenchLayoutService extends ILayoutService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Emits when the zen mode is enabled or disabled. diff --git a/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts b/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts index 0df88daa425..af642f25aee 100644 --- a/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts +++ b/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts @@ -11,7 +11,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class BrowserLifecycleService extends AbstractLifecycleService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @ILogService readonly logService: ILogService @@ -22,12 +22,10 @@ export class BrowserLifecycleService extends AbstractLifecycleService { } private registerListeners(): void { - // Note: we cannot change this to window.addEventListener('beforeUnload') - // because it seems that mechanism does not allow for preventing the unload - window.onbeforeunload = () => this.onBeforeUnload(); + window.addEventListener('beforeunload', e => this.onBeforeUnload(e)); } - private onBeforeUnload(): string | null { + private onBeforeUnload(event: BeforeUnloadEvent): void { const logService = this.logService; logService.info('[lifecycle] onBeforeUnload triggered'); @@ -48,7 +46,10 @@ export class BrowserLifecycleService extends AbstractLifecycleService { // Veto: signal back to browser by returning a non-falsify return value if (veto) { - return localize('lifecycleVeto', "Changes that you made may not be saved. Please check press 'Cancel' and try again."); + event.preventDefault(); + event.returnValue = localize('lifecycleVeto', "Changes that you made may not be saved. Please check press 'Cancel' and try again."); + + return; } // No Veto: continue with Will Shutdown @@ -61,8 +62,6 @@ export class BrowserLifecycleService extends AbstractLifecycleService { // Finally end with Shutdown event this._onShutdown.fire(); - - return null; } } diff --git a/src/vs/workbench/services/lifecycle/electron-browser/lifecycleService.ts b/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts similarity index 85% rename from src/vs/workbench/services/lifecycle/electron-browser/lifecycleService.ts rename to src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts index 9fe1d298205..7071d3d1de0 100644 --- a/src/vs/workbench/services/lifecycle/electron-browser/lifecycleService.ts +++ b/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts @@ -3,31 +3,30 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { localize } from 'vs/nls'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { ShutdownReason, StartupKind, handleVetos, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage'; -import { ipcRenderer as ipc } from 'electron'; +import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { onUnexpectedError } from 'vs/base/common/errors'; import { AbstractLifecycleService } from 'vs/platform/lifecycle/common/lifecycleService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import Severity from 'vs/base/common/severity'; -import { localize } from 'vs/nls'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; export class NativeLifecycleService extends AbstractLifecycleService { private static readonly LAST_SHUTDOWN_REASON_KEY = 'lifecyle.lastShutdownReason'; - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private shutdownReason: ShutdownReason | undefined; constructor( @INotificationService private readonly notificationService: INotificationService, - @IWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService, + @IElectronService private readonly electronService: IElectronService, @IStorageService readonly storageService: IStorageService, @ILogService readonly logService: ILogService ) { @@ -57,10 +56,10 @@ export class NativeLifecycleService extends AbstractLifecycleService { } private registerListeners(): void { - const windowId = this.environmentService.configuration.windowId; + const windowId = this.electronService.windowId; // Main side indicates that window is about to unload, check for vetos - ipc.on('vscode:onBeforeUnload', (_event: unknown, reply: { okChannel: string, cancelChannel: string, reason: ShutdownReason }) => { + ipcRenderer.on('vscode:onBeforeUnload', (event: unknown, reply: { okChannel: string, cancelChannel: string, reason: ShutdownReason }) => { this.logService.trace(`lifecycle: onBeforeUnload (reason: ${reply.reason})`); // trigger onBeforeShutdown events and veto collecting @@ -68,18 +67,18 @@ export class NativeLifecycleService extends AbstractLifecycleService { if (veto) { this.logService.trace('lifecycle: onBeforeUnload prevented via veto'); - ipc.send(reply.cancelChannel, windowId); + ipcRenderer.send(reply.cancelChannel, windowId); } else { this.logService.trace('lifecycle: onBeforeUnload continues without veto'); this.shutdownReason = reply.reason; - ipc.send(reply.okChannel, windowId); + ipcRenderer.send(reply.okChannel, windowId); } }); }); // Main side indicates that we will indeed shutdown - ipc.on('vscode:onWillUnload', async (_event: unknown, reply: { replyChannel: string, reason: ShutdownReason }) => { + ipcRenderer.on('vscode:onWillUnload', async (event: unknown, reply: { replyChannel: string, reason: ShutdownReason }) => { this.logService.trace(`lifecycle: onWillUnload (reason: ${reply.reason})`); // trigger onWillShutdown events and joining @@ -89,7 +88,7 @@ export class NativeLifecycleService extends AbstractLifecycleService { this._onShutdown.fire(); // acknowledge to main side - ipc.send(reply.replyChannel, windowId); + ipcRenderer.send(reply.replyChannel, windowId); }); // Save shutdown reason to retrieve on next startup diff --git a/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts b/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts index 99394090da8..44999bd842e 100644 --- a/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts +++ b/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; +import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class LocalizationsService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @ISharedProcessService sharedProcessService: ISharedProcessService, diff --git a/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts b/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts deleted file mode 100644 index 16264dd5851..00000000000 --- a/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts +++ /dev/null @@ -1,96 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { KeyValueLogProvider } from 'vs/workbench/services/log/common/keyValueLogProvider'; - -export const INDEXEDDB_VSCODE_DB = 'vscode-web-db'; -export const INDEXEDDB_LOGS_OBJECT_STORE = 'vscode-logs-store'; - -export class IndexedDBLogProvider extends KeyValueLogProvider { - - readonly database: Promise; - - constructor(scheme: string) { - super(scheme); - this.database = this.openDatabase(1); - } - - private openDatabase(version: number): Promise { - return new Promise((c, e) => { - const request = window.indexedDB.open(INDEXEDDB_VSCODE_DB, version); - request.onerror = (err) => e(request.error); - request.onsuccess = () => { - const db = request.result; - if (db.objectStoreNames.contains(INDEXEDDB_LOGS_OBJECT_STORE)) { - c(db); - } - }; - request.onupgradeneeded = () => { - const db = request.result; - if (!db.objectStoreNames.contains(INDEXEDDB_LOGS_OBJECT_STORE)) { - db.createObjectStore(INDEXEDDB_LOGS_OBJECT_STORE); - } - c(db); - }; - }); - } - - protected async getAllKeys(): Promise { - return new Promise(async (c, e) => { - const db = await this.database; - const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE]); - const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE); - const request = objectStore.getAllKeys(); - request.onerror = () => e(request.error); - request.onsuccess = () => c(request.result); - }); - } - - protected hasKey(key: string): Promise { - return new Promise(async (c, e) => { - const db = await this.database; - const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE]); - const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE); - const request = objectStore.getKey(key); - request.onerror = () => e(request.error); - request.onsuccess = () => { - c(!!request.result); - }; - }); - } - - protected getValue(key: string): Promise { - return new Promise(async (c, e) => { - const db = await this.database; - const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE]); - const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE); - const request = objectStore.get(key); - request.onerror = () => e(request.error); - request.onsuccess = () => c(request.result || ''); - }); - } - - protected setValue(key: string, value: string): Promise { - return new Promise(async (c, e) => { - const db = await this.database; - const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE], 'readwrite'); - const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE); - const request = objectStore.put(value, key); - request.onerror = () => e(request.error); - request.onsuccess = () => c(); - }); - } - - protected deleteKey(key: string): Promise { - return new Promise(async (c, e) => { - const db = await this.database; - const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE], 'readwrite'); - const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE); - const request = objectStore.delete(key); - request.onerror = () => e(request.error); - request.onsuccess = () => c(); - }); - } -} diff --git a/src/vs/workbench/services/log/common/inMemoryLogProvider.ts b/src/vs/workbench/services/log/common/inMemoryLogProvider.ts deleted file mode 100644 index f8d87167c6e..00000000000 --- a/src/vs/workbench/services/log/common/inMemoryLogProvider.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 { KeyValueLogProvider } from 'vs/workbench/services/log/common/keyValueLogProvider'; -import { keys } from 'vs/base/common/map'; - -export class InMemoryLogProvider extends KeyValueLogProvider { - - private readonly logs: Map = new Map(); - - protected async getAllKeys(): Promise { - return keys(this.logs); - } - - protected async hasKey(key: string): Promise { - return this.logs.has(key); - } - - protected async getValue(key: string): Promise { - return this.logs.get(key) || ''; - } - - protected async setValue(key: string, value: string): Promise { - this.logs.set(key, value); - } - - protected async deleteKey(key: string): Promise { - this.logs.delete(key); - } - -} diff --git a/src/vs/workbench/services/log/electron-browser/logService.ts b/src/vs/workbench/services/log/electron-browser/logService.ts index 6164cb48775..caa1bd713c0 100644 --- a/src/vs/workbench/services/log/electron-browser/logService.ts +++ b/src/vs/workbench/services/log/electron-browser/logService.ts @@ -6,7 +6,7 @@ import { DelegatedLogService, ILogService, ConsoleLogInMainService, ConsoleLogService, MultiplexLogService } from 'vs/platform/log/common/log'; import { BufferLogService } from 'vs/platform/log/common/bufferLog'; import { NativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; -import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; import { LoggerChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc'; import { SpdLogService } from 'vs/platform/log/node/spdlogService'; import { DisposableStore } from 'vs/base/common/lifecycle'; diff --git a/src/vs/workbench/services/menubar/electron-browser/menubarService.ts b/src/vs/workbench/services/menubar/electron-sandbox/menubarService.ts similarity index 71% rename from src/vs/workbench/services/menubar/electron-browser/menubarService.ts rename to src/vs/workbench/services/menubar/electron-sandbox/menubarService.ts index 9e4efbada0e..0b321336879 100644 --- a/src/vs/workbench/services/menubar/electron-browser/menubarService.ts +++ b/src/vs/workbench/services/menubar/electron-sandbox/menubarService.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IMenubarService } from 'vs/platform/menubar/node/menubar'; -import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; -import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; +import { IMenubarService } from 'vs/platform/menubar/electron-sandbox/menubar'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class MenubarService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(@IMainProcessService mainProcessService: IMainProcessService) { return createChannelSender(mainProcessService.getChannel('menubar')); diff --git a/src/vs/workbench/services/notification/common/notificationService.ts b/src/vs/workbench/services/notification/common/notificationService.ts index 9d86d1af265..b49ea4a665e 100644 --- a/src/vs/workbench/services/notification/common/notificationService.ts +++ b/src/vs/workbench/services/notification/common/notificationService.ts @@ -14,7 +14,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag export class NotificationService extends Disposable implements INotificationService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _model: INotificationsModel = this._register(new NotificationsModel()); get model(): INotificationsModel { return this._model; } diff --git a/src/vs/workbench/services/output/common/outputChannelModel.ts b/src/vs/workbench/services/output/common/outputChannelModel.ts index 2eade61f069..841f2d1e155 100644 --- a/src/vs/workbench/services/output/common/outputChannelModel.ts +++ b/src/vs/workbench/services/output/common/outputChannelModel.ts @@ -31,7 +31,7 @@ export interface IOutputChannelModel extends IDisposable { export const IOutputChannelModelService = createDecorator('outputChannelModelService'); export interface IOutputChannelModelService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; createOutputChannelModel(id: string, modelUri: URI, mimeType: string, file?: URI): IOutputChannelModel; diff --git a/src/vs/workbench/services/output/common/outputChannelModelService.ts b/src/vs/workbench/services/output/common/outputChannelModelService.ts index c7a9060afb3..4d9c36ad0fe 100644 --- a/src/vs/workbench/services/output/common/outputChannelModelService.ts +++ b/src/vs/workbench/services/output/common/outputChannelModelService.ts @@ -7,7 +7,7 @@ import { IOutputChannelModelService, AsbtractOutputChannelModelService } from 'v import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class OutputChannelModelService extends AsbtractOutputChannelModelService implements IOutputChannelModelService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; } registerSingleton(IOutputChannelModelService, OutputChannelModelService); diff --git a/src/vs/workbench/services/output/electron-browser/outputChannelModelService.ts b/src/vs/workbench/services/output/electron-browser/outputChannelModelService.ts index c4322d81a9c..17cb19f487c 100644 --- a/src/vs/workbench/services/output/electron-browser/outputChannelModelService.ts +++ b/src/vs/workbench/services/output/electron-browser/outputChannelModelService.ts @@ -22,6 +22,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Emitter, Event } from 'vs/base/common/event'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; class OutputChannelBackedByFile extends AbstractFileOutputChannelModel implements IOutputChannelModel { @@ -199,12 +200,13 @@ class DelegatedOutputChannelModel extends Disposable implements IOutputChannelMo export class OutputChannelModelService extends AsbtractOutputChannelModelService implements IOutputChannelModelService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @IInstantiationService instantiationService: IInstantiationService, @IWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService, - @IFileService private readonly fileService: IFileService + @IFileService private readonly fileService: IFileService, + @IElectronService private readonly electronService: IElectronService ) { super(instantiationService); } @@ -217,7 +219,7 @@ export class OutputChannelModelService extends AsbtractOutputChannelModelService private _outputDir: Promise | null = null; private get outputDir(): Promise { if (!this._outputDir) { - const outputDir = URI.file(join(this.environmentService.logsPath, `output_${this.environmentService.configuration.windowId}_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`)); + const outputDir = URI.file(join(this.environmentService.logsPath, `output_${this.electronService.windowId}_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`)); this._outputDir = this.fileService.createFolder(outputDir).then(() => outputDir); } return this._outputDir; diff --git a/src/vs/workbench/services/panel/common/panelService.ts b/src/vs/workbench/services/panel/common/panelService.ts index 3045900a7c0..b05bd7ba402 100644 --- a/src/vs/workbench/services/panel/common/panelService.ts +++ b/src/vs/workbench/services/panel/common/panelService.ts @@ -20,7 +20,7 @@ export interface IPanelIdentifier { export interface IPanelService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly onDidPanelOpen: Event<{ readonly panel: IPanel, readonly focus: boolean }>; readonly onDidPanelClose: Event; diff --git a/src/vs/workbench/services/path/browser/pathService.ts b/src/vs/workbench/services/path/browser/pathService.ts index 9ec86fa5de7..13d4610b295 100644 --- a/src/vs/workbench/services/path/browser/pathService.ts +++ b/src/vs/workbench/services/path/browser/pathService.ts @@ -16,7 +16,7 @@ export class BrowserPathService extends AbstractPathService { @IRemoteAgentService remoteAgentService: IRemoteAgentService, @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService ) { - super(() => URI.from({ scheme: Schemas.vscodeRemote, authority: environmentService.configuration.remoteAuthority, path: '/' }), remoteAgentService); + super(URI.from({ scheme: Schemas.vscodeRemote, authority: environmentService.configuration.remoteAuthority, path: '/' }), remoteAgentService); } } diff --git a/src/vs/workbench/services/path/common/pathService.ts b/src/vs/workbench/services/path/common/pathService.ts index d98f86fcc47..c2e1b21bd5a 100644 --- a/src/vs/workbench/services/path/common/pathService.ts +++ b/src/vs/workbench/services/path/common/pathService.ts @@ -18,7 +18,7 @@ export const IPathService = createDecorator('path'); */ export interface IPathService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * The correct path library to use for the target environment. If @@ -40,41 +40,50 @@ export interface IPathService { /** * Resolves the user-home directory for the target environment. * If the envrionment is connected to a remote, this will be the - * remote's user home directory, otherwise the local one. + * remote's user home directory, otherwise the local one unless + * `preferLocal` is set to `true`. */ - readonly userHome: Promise; + userHome(options?: { preferLocal: boolean }): Promise; /** - * Access to `userHome` in a sync fashion. This may be `undefined` - * as long as the remote environment was not resolved. + * @deprecated use `userHome` instead. */ readonly resolvedUserHome: URI | undefined; } export abstract class AbstractPathService implements IPathService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; - private remoteOS: Promise; + private resolveOS: Promise; private resolveUserHome: Promise; private maybeUnresolvedUserHome: URI | undefined; constructor( - fallbackUserHome: () => URI, + private localUserHome: URI, @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService ) { - this.remoteOS = this.remoteAgentService.getEnvironment().then(env => env?.os || OS); - this.resolveUserHome = this.remoteAgentService.getEnvironment().then(env => { - const userHome = this.maybeUnresolvedUserHome = env?.userHome || fallbackUserHome(); + // OS + this.resolveOS = (async () => { + const env = await this.remoteAgentService.getEnvironment(); + + return env?.os || OS; + })(); + + // User Home + this.resolveUserHome = (async () => { + const env = await this.remoteAgentService.getEnvironment(); + const userHome = this.maybeUnresolvedUserHome = env?.userHome || localUserHome; + return userHome; - }); + })(); } - get userHome(): Promise { - return this.resolveUserHome; + async userHome(options?: { preferLocal: boolean }): Promise { + return options?.preferLocal ? this.localUserHome : this.resolveUserHome; } get resolvedUserHome(): URI | undefined { @@ -82,7 +91,7 @@ export abstract class AbstractPathService implements IPathService { } get path(): Promise { - return this.remoteOS.then(os => { + return this.resolveOS.then(os => { return os === OperatingSystem.Windows ? win32 : posix; @@ -95,7 +104,8 @@ export abstract class AbstractPathService implements IPathService { // normalize to fwd-slashes on windows, // on other systems bwd-slashes are valid // filename character, eg /f\oo/ba\r.txt - if ((await this.remoteOS) === OperatingSystem.Windows) { + const os = await this.resolveOS; + if (os === OperatingSystem.Windows) { _path = _path.replace(/\\/g, '/'); } diff --git a/src/vs/workbench/services/path/electron-browser/pathService.ts b/src/vs/workbench/services/path/electron-browser/pathService.ts index 58b8a78a252..8dbe77e0b38 100644 --- a/src/vs/workbench/services/path/electron-browser/pathService.ts +++ b/src/vs/workbench/services/path/electron-browser/pathService.ts @@ -15,7 +15,7 @@ export class NativePathService extends AbstractPathService { @IRemoteAgentService remoteAgentService: IRemoteAgentService, @IWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService ) { - super(() => environmentService.userHome, remoteAgentService); + super(environmentService.userHome, remoteAgentService); } } diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index 6334bcafbf6..e6a180a5c3e 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -46,7 +46,7 @@ const emptyEditableSettingsContent = '{\n}'; export class PreferencesService extends Disposable implements IPreferencesService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private lastOpenedSettingsInput: PreferencesEditorInput | null = null; @@ -210,7 +210,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic } const editorInput = this.getActiveSettingsEditorInput() || this.lastOpenedSettingsInput; - const resource = editorInput ? editorInput.master.resource! : this.userSettingsResource; + const resource = editorInput ? editorInput.primary.resource! : this.userSettingsResource; const target = this.getConfigurationTargetFromSettingsResource(resource); return this.openOrSwitchSettings(target, resource, { query: query }); } @@ -317,7 +317,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic private async openOrSwitchSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Promise { const editorInput = this.getActiveSettingsEditorInput(group); if (editorInput) { - const editorInputResource = editorInput.master.resource; + const editorInputResource = editorInput.primary.resource; if (editorInputResource && editorInputResource.fsPath !== resource.fsPath) { return this.doSwitchSettings(configurationTarget, resource, editorInput, group, options); } @@ -546,7 +546,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.textFileService.read(workspaceConfig) .then(content => { if (Object.keys(parse(content.value)).indexOf('settings') === -1) { - return this.jsonEditingService.write(resource, [{ key: 'settings', value: {} }], true).then(undefined, () => { }); + return this.jsonEditingService.write(resource, [{ path: ['settings'], value: {} }], true).then(undefined, () => { }); } return undefined; }); @@ -614,9 +614,10 @@ export class PreferencesService extends Disposable implements IPreferencesServic const type = schema ? schema.type : 'object' /* Override Identifier */; let setting = settingsModel.getPreference(settingKey); if (!setting) { - const defaultValue = type === 'array' ? this.configurationService.inspect(settingKey).defaultValue : getDefaultValue(type); + const defaultValue = (type === 'object' || type === 'array') ? this.configurationService.inspect(settingKey).defaultValue : getDefaultValue(type); if (defaultValue !== undefined) { - await this.jsonEditingService.write(settingsModel.uri!, [{ key: settingKey, value: defaultValue }], false); + const key = settingsModel instanceof WorkspaceConfigurationEditorModel ? ['settings', settingKey] : [settingKey]; + await this.jsonEditingService.write(settingsModel.uri!, [{ path: key, value: defaultValue }], false); setting = settingsModel.getPreference(settingKey); } } diff --git a/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts b/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts index 362555816ad..fb0b72bd894 100644 --- a/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts +++ b/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts @@ -421,22 +421,6 @@ class KeybindingItemMatches { return this.wordMatchesMetaModifier(word); } - private wordMatchesMetaModifier(word: string): boolean { - if (matchesPrefix(this.modifierLabels.ui.metaKey, word)) { - return true; - } - if (matchesPrefix(this.modifierLabels.aria.metaKey, word)) { - return true; - } - if (matchesPrefix(this.modifierLabels.user.metaKey, word)) { - return true; - } - if (matchesPrefix(localize('meta', "meta"), word)) { - return true; - } - return false; - } - private matchesCtrlModifier(keybinding: ResolvedKeybindingPart | null, word: string): boolean { if (!keybinding) { return false; @@ -447,19 +431,6 @@ class KeybindingItemMatches { return this.wordMatchesCtrlModifier(word); } - private wordMatchesCtrlModifier(word: string): boolean { - if (matchesPrefix(this.modifierLabels.ui.ctrlKey, word)) { - return true; - } - if (matchesPrefix(this.modifierLabels.aria.ctrlKey, word)) { - return true; - } - if (matchesPrefix(this.modifierLabels.user.ctrlKey, word)) { - return true; - } - return false; - } - private matchesShiftModifier(keybinding: ResolvedKeybindingPart | null, word: string): boolean { if (!keybinding) { return false; @@ -470,19 +441,6 @@ class KeybindingItemMatches { return this.wordMatchesShiftModifier(word); } - private wordMatchesShiftModifier(word: string): boolean { - if (matchesPrefix(this.modifierLabels.ui.shiftKey, word)) { - return true; - } - if (matchesPrefix(this.modifierLabels.aria.shiftKey, word)) { - return true; - } - if (matchesPrefix(this.modifierLabels.user.shiftKey, word)) { - return true; - } - return false; - } - private matchesAltModifier(keybinding: ResolvedKeybindingPart | null, word: string): boolean { if (!keybinding) { return false; @@ -493,22 +451,6 @@ class KeybindingItemMatches { return this.wordMatchesAltModifier(word); } - private wordMatchesAltModifier(word: string): boolean { - if (matchesPrefix(this.modifierLabels.ui.altKey, word)) { - return true; - } - if (matchesPrefix(this.modifierLabels.aria.altKey, word)) { - return true; - } - if (matchesPrefix(this.modifierLabels.user.altKey, word)) { - return true; - } - if (matchesPrefix(localize('option', "option"), word)) { - return true; - } - return false; - } - private hasAnyMatch(keybindingMatch: KeybindingMatch): boolean { return !!keybindingMatch.altKey || !!keybindingMatch.ctrlKey || @@ -574,4 +516,62 @@ class KeybindingItemMatches { } return false; } + + private wordMatchesAltModifier(word: string): boolean { + if (strings.equalsIgnoreCase(this.modifierLabels.ui.altKey, word)) { + return true; + } + if (strings.equalsIgnoreCase(this.modifierLabels.aria.altKey, word)) { + return true; + } + if (strings.equalsIgnoreCase(this.modifierLabels.user.altKey, word)) { + return true; + } + if (strings.equalsIgnoreCase(localize('option', "option"), word)) { + return true; + } + return false; + } + + private wordMatchesCtrlModifier(word: string): boolean { + if (strings.equalsIgnoreCase(this.modifierLabels.ui.ctrlKey, word)) { + return true; + } + if (strings.equalsIgnoreCase(this.modifierLabels.aria.ctrlKey, word)) { + return true; + } + if (strings.equalsIgnoreCase(this.modifierLabels.user.ctrlKey, word)) { + return true; + } + return false; + } + + private wordMatchesMetaModifier(word: string): boolean { + if (strings.equalsIgnoreCase(this.modifierLabels.ui.metaKey, word)) { + return true; + } + if (strings.equalsIgnoreCase(this.modifierLabels.aria.metaKey, word)) { + return true; + } + if (strings.equalsIgnoreCase(this.modifierLabels.user.metaKey, word)) { + return true; + } + if (strings.equalsIgnoreCase(localize('meta', "meta"), word)) { + return true; + } + return false; + } + + private wordMatchesShiftModifier(word: string): boolean { + if (strings.equalsIgnoreCase(this.modifierLabels.ui.shiftKey, word)) { + return true; + } + if (strings.equalsIgnoreCase(this.modifierLabels.aria.shiftKey, word)) { + return true; + } + if (strings.equalsIgnoreCase(this.modifierLabels.user.shiftKey, word)) { + return true; + } + return false; + } } diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index f7795c59a86..0d414997afc 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -7,6 +7,7 @@ import { IStringDictionary } from 'vs/base/common/collections'; import { Event } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { IRange } from 'vs/editor/common/core/range'; +import { IJSONSchemaMap, IJSONSchema } from 'vs/base/common/jsonSchema'; import { ITextModel } from 'vs/editor/common/model'; import { localize } from 'vs/nls'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; @@ -29,7 +30,8 @@ export enum SettingValueType { Exclude = 'exclude', Complex = 'complex', NullableInteger = 'nullable-integer', - NullableNumber = 'nullable-number' + NullableNumber = 'nullable-number', + Object = 'object' } export interface ISettingsGroup { @@ -64,6 +66,9 @@ export interface ISetting { scope?: ConfigurationScope; type?: string | string[]; arrayItemType?: string; + objectProperties?: IJSONSchemaMap, + objectPatternProperties?: IJSONSchemaMap, + objectAdditionalProperties?: boolean | IJSONSchema, enum?: string[]; enumDescriptions?: string[]; enumDescriptionsAreMarkdown?: boolean; @@ -188,7 +193,7 @@ export interface IKeybindingsEditorModel extends IPreferencesEditorModel { export const IPreferencesService = createDecorator('preferencesService'); export interface IPreferencesService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; userSettingsResource: URI; workspaceSettingsResource: URI | null; diff --git a/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts b/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts index 6432e6993f5..18fee097595 100644 --- a/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts +++ b/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts @@ -29,7 +29,7 @@ export class PreferencesEditorInput extends SideBySideEditorInput { } getTitle(verbosity: Verbosity): string { - return this.master.getTitle(verbosity); + return this.primary.getTitle(verbosity); } } @@ -45,7 +45,7 @@ export class DefaultPreferencesEditorInput extends ResourceEditorInput { @ILabelService labelService: ILabelService, @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService ) { - super(nls.localize('settingsEditorName', "Default Settings"), '', defaultSettingsResource, undefined, textModelResolverService, textFileService, editorService, editorGroupService, fileService, labelService, filesConfigurationService); + super(defaultSettingsResource, nls.localize('settingsEditorName', "Default Settings"), '', undefined, textModelResolverService, textFileService, editorService, editorGroupService, fileService, labelService, filesConfigurationService); } getTypeId(): string { @@ -80,6 +80,7 @@ export class KeybindingsEditorInput extends EditorInput { constructor(@IInstantiationService instantiationService: IInstantiationService) { super(); + this.keybindingsModel = instantiationService.createInstance(KeybindingsEditorModel, OS); } @@ -91,13 +92,19 @@ export class KeybindingsEditorInput extends EditorInput { return nls.localize('keybindingsInputName', "Keyboard Shortcuts"); } - resolve(): Promise { - return Promise.resolve(this.keybindingsModel); + async resolve(): Promise { + return this.keybindingsModel; } matches(otherInput: unknown): boolean { return otherInput instanceof KeybindingsEditorInput; } + + dispose(): void { + this.keybindingsModel.dispose(); + + super.dispose(); + } } export class SettingsEditor2Input extends EditorInput { @@ -130,7 +137,13 @@ export class SettingsEditor2Input extends EditorInput { return nls.localize('settingsEditor2InputName', "Settings"); } - resolve(): Promise { - return Promise.resolve(this._settingsModel); + async resolve(): Promise { + return this._settingsModel; + } + + dispose(): void { + this._settingsModel.dispose(); + + super.dispose(); } } diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index fb4b28240b1..1172b5b0b32 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -8,7 +8,6 @@ import { IStringDictionary } from 'vs/base/common/collections'; import { Emitter, Event } from 'vs/base/common/event'; import { JSONVisitor, visit } from 'vs/base/common/json'; import { Disposable, IReference } from 'vs/base/common/lifecycle'; -import * as map from 'vs/base/common/map'; import { assign } from 'vs/base/common/objects'; import { URI } from 'vs/base/common/uri'; import { IRange, Range } from 'vs/editor/common/core/range'; @@ -49,7 +48,7 @@ export abstract class AbstractSettingsModel extends EditorModel { */ private removeDuplicateResults(): void { const settingKeys = new Set(); - map.keys(this._currentResultGroups) + [...this._currentResultGroups.keys()] .sort((a, b) => this._currentResultGroups.get(a)!.order - this._currentResultGroups.get(b)!.order) .forEach(groupId => { const group = this._currentResultGroups.get(groupId)!; @@ -171,7 +170,7 @@ export class SettingsEditorModel extends AbstractSettingsModel implements ISetti } protected update(): IFilterResult | undefined { - const resultGroups = map.values(this._currentResultGroups); + const resultGroups = [...this._currentResultGroups.values()]; if (!resultGroups.length) { return undefined; } @@ -620,6 +619,10 @@ export class DefaultSettings extends Disposable { ? prop.items.type : undefined; + const objectProperties = prop.type === 'object' ? prop.properties : undefined; + const objectPatternProperties = prop.type === 'object' ? prop.patternProperties : undefined; + const objectAdditionalProperties = prop.type === 'object' ? prop.additionalProperties : undefined; + result.push({ key, value, @@ -633,6 +636,9 @@ export class DefaultSettings extends Disposable { scope: prop.scope, type: prop.type, arrayItemType: listItemType, + objectProperties, + objectPatternProperties, + objectAdditionalProperties, enum: prop.enum, enumDescriptions: prop.enumDescriptions || prop.markdownEnumDescriptions, enumDescriptionsAreMarkdown: !prop.enumDescriptions, @@ -745,8 +751,7 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements } // Grab current result groups, only render non-empty groups - const resultGroups = map - .values(this._currentResultGroups) + const resultGroups = [...this._currentResultGroups.values()] .sort((a, b) => a.order - b.order); const nonEmptyResultGroups = resultGroups.filter(group => group.result.filterMatches.length); diff --git a/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts b/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts index 8a445d39a20..c6dcb02e4f3 100644 --- a/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts +++ b/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts @@ -34,7 +34,7 @@ class AnAction extends Action { } } -suite('KeybindingsEditorModel test', () => { +suite('KeybindingsEditorModel', () => { let instantiationService: TestInstantiationService; let testObject: KeybindingsEditorModel; @@ -568,6 +568,46 @@ suite('KeybindingsEditorModel test', () => { assert.deepEqual(actual[0].keybindingMatches!.firstPart, { keyCode: true }); }); + test('filter modifiers are not matched when not completely matched (prefix)', async () => { + testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh); + const term = `alt.${uuid.generateUuid()}`; + const command = `command.${term}`; + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape }, isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command: 'some_command', firstPart: { keyCode: KeyCode.Escape, modifiers: { altKey: true } }, isDefault: false })); + + await testObject.resolve(new Map()); + const actual = testObject.fetch(term); + assert.equal(1, actual.length); + assert.equal(command, actual[0].keybindingItem.command); + assert.equal(1, actual[0].commandIdMatches?.length); + }); + + test('filter modifiers are not matched when not completely matched (includes)', async () => { + testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh); + const term = `abcaltdef.${uuid.generateUuid()}`; + const command = `command.${term}`; + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape }, isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command: 'some_command', firstPart: { keyCode: KeyCode.Escape, modifiers: { altKey: true } }, isDefault: false })); + + await testObject.resolve(new Map()); + const actual = testObject.fetch(term); + assert.equal(1, actual.length); + assert.equal(command, actual[0].keybindingItem.command); + assert.equal(1, actual[0].commandIdMatches?.length); + }); + + test('filter modifiers are matched with complete term', async () => { + testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh); + const command = `command.${uuid.generateUuid()}`; + const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { altKey: true } }, isDefault: false }); + prepareKeybindingService(expected, aResolvedKeybindingItem({ command: 'some_command', firstPart: { keyCode: KeyCode.Escape }, isDefault: false })); + + await testObject.resolve(new Map()); + const actual = testObject.fetch('alt').filter(element => element.keybindingItem.command === command); + assert.equal(1, actual.length); + assert.deepEqual(actual[0].keybindingMatches!.firstPart, { altKey: true }); + }); + function prepareKeybindingService(...keybindingItems: ResolvedKeybindingItem[]): ResolvedKeybindingItem[] { instantiationService.stub(IKeybindingService, 'getKeybindings', () => keybindingItems); instantiationService.stub(IKeybindingService, 'getDefaultKeybindings', () => keybindingItems); diff --git a/src/vs/workbench/services/progress/browser/progressIndicator.ts b/src/vs/workbench/services/progress/browser/progressIndicator.ts index ebbfd5bc0e4..79ad961bd5a 100644 --- a/src/vs/workbench/services/progress/browser/progressIndicator.ts +++ b/src/vs/workbench/services/progress/browser/progressIndicator.ts @@ -61,7 +61,7 @@ export class ProgressBarIndicator extends Disposable implements IProgressIndicat export class EditorProgressIndicator extends ProgressBarIndicator { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(progressBar: ProgressBar, private readonly group: IEditorGroupView) { super(progressBar); diff --git a/src/vs/workbench/services/progress/browser/progressService.ts b/src/vs/workbench/services/progress/browser/progressService.ts index d497332e2ff..3e607042705 100644 --- a/src/vs/workbench/services/progress/browser/progressService.ts +++ b/src/vs/workbench/services/progress/browser/progressService.ts @@ -29,7 +29,7 @@ import { IViewsService, IViewDescriptorService, ViewContainerLocation } from 'vs export class ProgressService extends Disposable implements IProgressService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @IActivityService private readonly activityService: IActivityService, @@ -525,7 +525,7 @@ export class ProgressService extends Disposable implements IProgressService { keyEventProcessor: (event: StandardKeyboardEvent) => { const resolved = this.keybindingService.softDispatch(event, this.layoutService.container); if (resolved?.commandId) { - if (allowableCommands.indexOf(resolved.commandId) === -1) { + if (!allowableCommands.includes(resolved.commandId)) { EventHelper.stop(event, true); } } diff --git a/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts b/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts index 4e6ea530e4a..5a9684c38a1 100644 --- a/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts +++ b/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts @@ -4,21 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IRemoteAgentConnection, IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { AbstractRemoteAgentService, RemoteAgentConnection } from 'vs/workbench/services/remote/common/abstractRemoteAgentService'; +import { AbstractRemoteAgentService } from 'vs/workbench/services/remote/common/abstractRemoteAgentService'; import { IProductService } from 'vs/platform/product/common/productService'; import { IWebSocketFactory, BrowserSocketFactory } from 'vs/platform/remote/browser/browserSocketFactory'; import { ISignService } from 'vs/platform/sign/common/sign'; -import { ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; import { ILogService } from 'vs/platform/log/common/log'; export class RemoteAgentService extends AbstractRemoteAgentService implements IRemoteAgentService { - public readonly socketFactory: ISocketFactory; - - private readonly _connection: IRemoteAgentConnection | null = null; - constructor( webSocketFactory: IWebSocketFactory | null | undefined, @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @@ -27,16 +22,6 @@ export class RemoteAgentService extends AbstractRemoteAgentService implements IR @ISignService signService: ISignService, @ILogService logService: ILogService ) { - super(environmentService); - - this.socketFactory = new BrowserSocketFactory(webSocketFactory); - const remoteAuthority = environmentService.configuration.remoteAuthority; - if (remoteAuthority) { - this._connection = this._register(new RemoteAgentConnection(remoteAuthority, productService.commit, this.socketFactory, remoteAuthorityResolverService, signService, logService)); - } - } - - getConnection(): IRemoteAgentConnection | null { - return this._connection; + super(new BrowserSocketFactory(webSocketFactory), environmentService, productService, remoteAuthorityResolverService, signService, logService); } } diff --git a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts index bf8e93ea187..61b727c08fa 100644 --- a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts @@ -5,9 +5,9 @@ import * as nls from 'vs/nls'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IChannel, IServerChannel, getDelayedChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IChannel, IServerChannel, getDelayedChannel, IPCLogger } from 'vs/base/parts/ipc/common/ipc'; import { Client } from 'vs/base/parts/ipc/common/ipc.net'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { connectRemoteAgentManagement, IConnectionOptions, ISocketFactory, PersistenConnectionEvent } from 'vs/platform/remote/common/remoteAgentConnection'; import { IRemoteAgentConnection, IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError } from 'vs/platform/remote/common/remoteAuthorityResolver'; @@ -22,30 +22,62 @@ import { Emitter } from 'vs/base/common/event'; import { ISignService } from 'vs/platform/sign/common/sign'; import { ILogService } from 'vs/platform/log/common/log'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { IProductService } from 'vs/platform/product/common/productService'; -export abstract class AbstractRemoteAgentService extends Disposable { +export abstract class AbstractRemoteAgentService extends Disposable implements IRemoteAgentService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; + public readonly socketFactory: ISocketFactory; + private readonly _connection: IRemoteAgentConnection | null; private _environment: Promise | null; constructor( - @IEnvironmentService protected readonly _environmentService: IEnvironmentService + socketFactory: ISocketFactory, + @IWorkbenchEnvironmentService protected readonly _environmentService: IWorkbenchEnvironmentService, + @IProductService productService: IProductService, + @IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService, + @ISignService signService: ISignService, + @ILogService logService: ILogService ) { super(); + this.socketFactory = socketFactory; + if (this._environmentService.configuration.remoteAuthority) { + this._connection = this._register(new RemoteAgentConnection(this._environmentService.configuration.remoteAuthority, productService.commit, this.socketFactory, this._remoteAuthorityResolverService, signService, logService)); + } else { + this._connection = null; + } this._environment = null; } - abstract getConnection(): IRemoteAgentConnection | null; + getConnection(): IRemoteAgentConnection | null { + return this._connection; + } - getEnvironment(bail?: boolean): Promise { + getEnvironment(): Promise { + return this.getRawEnvironment().then(undefined, () => null); + } + + getRawEnvironment(): Promise { if (!this._environment) { this._environment = this._withChannel( - (channel, connection) => RemoteExtensionEnvironmentChannelClient.getEnvironmentData(channel, connection.remoteAuthority, this._environmentService.extensionDevelopmentLocationURI), + async (channel, connection) => { + const env = await RemoteExtensionEnvironmentChannelClient.getEnvironmentData(channel, connection.remoteAuthority); + this._remoteAuthorityResolverService._setAuthorityConnectionToken(connection.remoteAuthority, env.connectionToken); + return env; + }, null ); } - return bail ? this._environment : this._environment.then(undefined, () => null); + return this._environment; + } + + scanExtensions(skipExtensions: ExtensionIdentifier[] = []): Promise { + return this._withChannel( + (channel, connection) => RemoteExtensionEnvironmentChannelClient.scanExtensions(channel, connection.remoteAuthority, this._environmentService.extensionDevelopmentLocationURI, skipExtensions), + [] + ).then(undefined, () => []); } getDiagnosticInfo(options: IDiagnosticInfoOptions): Promise { @@ -147,7 +179,8 @@ export class RemoteAgentConnection extends Disposable implements IRemoteAgentCon } }, signService: this._signService, - logService: this._logService + logService: this._logService, + ipcLogger: false ? new IPCLogger(`Local \u2192 Remote`, `Remote \u2192 Local`) : null }; const connection = this._register(await connectRemoteAgentManagement(options, this.remoteAuthority, `renderer`)); this._register(connection.onDidStateChange(e => this._onDidStateChange.fire(e))); @@ -162,7 +195,7 @@ class RemoteConnectionFailureNotificationContribution implements IWorkbenchContr @INotificationService notificationService: INotificationService, ) { // Let's cover the case where connecting to fetch the remote extension info fails - remoteAgentService.getEnvironment(true) + remoteAgentService.getRawEnvironment() .then(undefined, err => { if (!RemoteAuthorityResolverError.isHandled(err)) { notificationService.error(nls.localize('connectionError', "Failed to connect to the remote extension host server (Error: {0})", err ? err.message : '')); diff --git a/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts b/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts index 0bdbf66f0f7..052cd072d93 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts @@ -6,62 +6,74 @@ import * as platform from 'vs/base/common/platform'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { IExtensionDescription, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics'; -import { RemoteAuthorities } from 'vs/base/common/network'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; export interface IGetEnvironmentDataArguments { + remoteAuthority: string; +} + +export interface IScanExtensionsArguments { language: string; remoteAuthority: string; extensionDevelopmentPath: UriComponents[] | undefined; + skipExtensions: ExtensionIdentifier[]; } export interface IRemoteAgentEnvironmentDTO { pid: number; connectionToken: string; appRoot: UriComponents; - appSettingsHome: UriComponents; settingsPath: UriComponents; logsPath: UriComponents; extensionsPath: UriComponents; extensionHostLogsPath: UriComponents; globalStorageHome: UriComponents; + workspaceStorageHome: UriComponents; userHome: UriComponents; - extensions: IExtensionDescription[]; os: platform.OperatingSystem; } export class RemoteExtensionEnvironmentChannelClient { - static async getEnvironmentData(channel: IChannel, remoteAuthority: string, extensionDevelopmentPath?: URI[]): Promise { + static async getEnvironmentData(channel: IChannel, remoteAuthority: string): Promise { const args: IGetEnvironmentDataArguments = { - language: platform.language, - remoteAuthority, - extensionDevelopmentPath + remoteAuthority }; const data = await channel.call('getEnvironmentData', args); - RemoteAuthorities.setConnectionToken(remoteAuthority, data.connectionToken); - return { pid: data.pid, connectionToken: data.connectionToken, appRoot: URI.revive(data.appRoot), - appSettingsHome: URI.revive(data.appSettingsHome), settingsPath: URI.revive(data.settingsPath), logsPath: URI.revive(data.logsPath), extensionsPath: URI.revive(data.extensionsPath), extensionHostLogsPath: URI.revive(data.extensionHostLogsPath), globalStorageHome: URI.revive(data.globalStorageHome), + workspaceStorageHome: URI.revive(data.workspaceStorageHome), userHome: URI.revive(data.userHome), - extensions: data.extensions.map(ext => { (ext).extensionLocation = URI.revive(ext.extensionLocation); return ext; }), os: data.os }; } + static async scanExtensions(channel: IChannel, remoteAuthority: string, extensionDevelopmentPath: URI[] | undefined, skipExtensions: ExtensionIdentifier[]): Promise { + const args: IScanExtensionsArguments = { + language: platform.language, + remoteAuthority, + extensionDevelopmentPath, + skipExtensions + }; + + const extensions = await channel.call('scanExtensions', args); + extensions.forEach(ext => { (ext).extensionLocation = URI.revive(ext.extensionLocation); }); + + return extensions; + } + static getDiagnosticInfo(channel: IChannel, options: IDiagnosticInfoOptions): Promise { return channel.call('getDiagnosticInfo', options); } diff --git a/src/vs/workbench/services/remote/common/remoteAgentFileSystemChannel.ts b/src/vs/workbench/services/remote/common/remoteAgentFileSystemChannel.ts index 1ef4d9c4bcf..a4c3881ec9e 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentFileSystemChannel.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentFileSystemChannel.ts @@ -51,8 +51,12 @@ export class RemoteFileSystemProvider extends Disposable implements const connection = remoteAgentService.getConnection()!; this.channel = connection.getChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME); + // Initially assume case sensitivity until remote environment is resolved this.setCaseSensitive(true); - remoteAgentService.getEnvironment().then(remoteAgentEnvironment => this.setCaseSensitive(!!(remoteAgentEnvironment && remoteAgentEnvironment.os === OperatingSystem.Linux))); + (async () => { + const remoteAgentEnvironment = await remoteAgentService.getEnvironment(); + this.setCaseSensitive(remoteAgentEnvironment?.os === OperatingSystem.Linux); + })(); this.registerListeners(); } @@ -117,7 +121,7 @@ export class RemoteFileSystemProvider extends Disposable implements return buff.buffer; } - readFileStream(resource: URI, opts: FileReadStreamOptions, token?: CancellationToken): ReadableStreamEvents { + readFileStream(resource: URI, opts: FileReadStreamOptions, token: CancellationToken): ReadableStreamEvents { const stream = newWriteableStream(data => VSBuffer.concat(data.map(data => VSBuffer.wrap(data))).buffer); // Reading as file stream goes through an event to the remote side @@ -152,19 +156,17 @@ export class RemoteFileSystemProvider extends Disposable implements }); // Support cancellation - if (token) { - token.onCancellationRequested(() => { + token.onCancellationRequested(() => { - // Ensure to end the stream properly with an error - // to indicate the cancellation. - stream.end(canceled()); + // Ensure to end the stream properly with an error + // to indicate the cancellation. + stream.end(canceled()); - // Ensure to dispose the listener upon cancellation. This will - // bubble through the remote side as event and allows to stop - // reading the file. - listener.dispose(); - }); - } + // Ensure to dispose the listener upon cancellation. This will + // bubble through the remote side as event and allows to stop + // reading the file. + listener.dispose(); + }); return stream; } diff --git a/src/vs/workbench/services/remote/common/remoteAgentService.ts b/src/vs/workbench/services/remote/common/remoteAgentService.ts index 00bb6001ad2..2b1a17c2aca 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentService.ts @@ -10,18 +10,30 @@ import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics import { Event } from 'vs/base/common/event'; import { PersistenConnectionEvent as PersistentConnectionEvent, ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; export const RemoteExtensionLogFileName = 'remoteagent'; export const IRemoteAgentService = createDecorator('remoteAgentService'); export interface IRemoteAgentService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly socketFactory: ISocketFactory; getConnection(): IRemoteAgentConnection | null; - getEnvironment(bail?: boolean): Promise; + /** + * Get the remote environment. In case of an error, returns `null`. + */ + getEnvironment(): Promise; + /** + * Get the remote environment. Can return an error. + */ + getRawEnvironment(): Promise; + /** + * Scan remote extensions. + */ + scanExtensions(skipExtensions?: ExtensionIdentifier[]): Promise; getDiagnosticInfo(options: IDiagnosticInfoOptions): Promise; disableTelemetry(): Promise; logTelemetry(eventName: string, data?: ITelemetryData): Promise; diff --git a/src/vs/workbench/services/remote/common/remoteExplorerService.ts b/src/vs/workbench/services/remote/common/remoteExplorerService.ts index 893c7ad1cc6..a8232c97d59 100644 --- a/src/vs/workbench/services/remote/common/remoteExplorerService.ts +++ b/src/vs/workbench/services/remote/common/remoteExplorerService.ts @@ -11,7 +11,9 @@ import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IEditableData } from 'vs/workbench/common/views'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { TunnelInformation, TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { TunnelInformation, TunnelDescription, IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IAddressProvider } from 'vs/platform/remote/common/remoteAgentConnection'; export const IRemoteExplorerService = createDecorator('remoteExplorerService'); export const REMOTE_EXPLORER_TYPE_KEY: string = 'remote.explorerType'; @@ -75,7 +77,9 @@ export class TunnelModel extends Disposable { constructor( @ITunnelService private readonly tunnelService: ITunnelService, @IStorageService private readonly storageService: IStorageService, - @IConfigurationService private readonly configurationService: IConfigurationService + @IConfigurationService private readonly configurationService: IConfigurationService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, + @IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService, ) { super(); this.forwarded = new Map(); @@ -137,7 +141,12 @@ export class TunnelModel extends Disposable { async forward(remote: { host: string, port: number }, local?: number, name?: string): Promise { const key = MakeAddress(remote.host, remote.port); if (!this.forwarded.has(key)) { - const tunnel = await this.tunnelService.openTunnel(remote.host, remote.port, local); + const authority = this.environmentService.configuration.remoteAuthority; + const addressProvider: IAddressProvider | undefined = authority ? { + getAddress: async () => { return (await this.remoteAuthorityResolverService.resolveAuthority(authority)).authority; } + } : undefined; + + const tunnel = await this.tunnelService.openTunnel(addressProvider, remote.host, remote.port, local); if (tunnel && tunnel.localAddress) { const newForward: Tunnel = { remoteHost: tunnel.tunnelRemoteHost, @@ -224,7 +233,7 @@ export class TunnelModel extends Disposable { } export interface IRemoteExplorerService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; onDidChangeTargetType: Event; targetType: string[]; readonly tunnelModel: TunnelModel; @@ -253,9 +262,11 @@ class RemoteExplorerService implements IRemoteExplorerService { constructor( @IStorageService private readonly storageService: IStorageService, @ITunnelService tunnelService: ITunnelService, - @IConfigurationService configurationService: IConfigurationService + @IConfigurationService configurationService: IConfigurationService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService, ) { - this._tunnelModel = new TunnelModel(tunnelService, storageService, configurationService); + this._tunnelModel = new TunnelModel(tunnelService, storageService, configurationService, environmentService, remoteAuthorityResolverService); } set targetType(name: string[]) { diff --git a/src/vs/workbench/services/remote/common/tunnelService.ts b/src/vs/workbench/services/remote/common/tunnelService.ts deleted file mode 100644 index a7f8a98cc41..00000000000 --- a/src/vs/workbench/services/remote/common/tunnelService.ts +++ /dev/null @@ -1,151 +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 { ITunnelService, RemoteTunnel, ITunnelProvider } from 'vs/platform/remote/common/tunnel'; -import { Event, Emitter } from 'vs/base/common/event'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { ILogService } from 'vs/platform/log/common/log'; - -export abstract class AbstractTunnelService implements ITunnelService { - _serviceBrand: undefined; - - private _onTunnelOpened: Emitter = new Emitter(); - public onTunnelOpened: Event = this._onTunnelOpened.event; - private _onTunnelClosed: Emitter<{ host: string, port: number }> = new Emitter(); - public onTunnelClosed: Event<{ host: string, port: number }> = this._onTunnelClosed.event; - protected readonly _tunnels = new Map }>>(); - protected _tunnelProvider: ITunnelProvider | undefined; - - public constructor( - @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, - @ILogService protected readonly logService: ILogService - ) { } - - setTunnelProvider(provider: ITunnelProvider | undefined): IDisposable { - if (!provider) { - return { - dispose: () => { } - }; - } - this._tunnelProvider = provider; - return { - dispose: () => { - this._tunnelProvider = undefined; - } - }; - } - - public get tunnels(): Promise { - const promises: Promise[] = []; - Array.from(this._tunnels.values()).forEach(portMap => Array.from(portMap.values()).forEach(x => promises.push(x.value))); - return Promise.all(promises); - } - - dispose(): void { - for (const portMap of this._tunnels.values()) { - for (const { value } of portMap.values()) { - value.then(tunnel => tunnel.dispose()); - } - portMap.clear(); - } - this._tunnels.clear(); - } - - openTunnel(remoteHost: string | undefined, remotePort: number, localPort: number): Promise | undefined { - const remoteAuthority = this.environmentService.configuration.remoteAuthority; - if (!remoteAuthority) { - return undefined; - } - - if (!remoteHost || (remoteHost === '127.0.0.1')) { - remoteHost = 'localhost'; - } - - const resolvedTunnel = this.retainOrCreateTunnel(remoteAuthority, remoteHost, remotePort, localPort); - if (!resolvedTunnel) { - return resolvedTunnel; - } - - return resolvedTunnel.then(tunnel => { - const newTunnel = this.makeTunnel(tunnel); - if (tunnel.tunnelRemoteHost !== remoteHost || tunnel.tunnelRemotePort !== remotePort) { - this.logService.warn('Created tunnel does not match requirements of requested tunnel. Host or port mismatch.'); - } - this._onTunnelOpened.fire(newTunnel); - return newTunnel; - }); - } - - private makeTunnel(tunnel: RemoteTunnel): RemoteTunnel { - return { - tunnelRemotePort: tunnel.tunnelRemotePort, - tunnelRemoteHost: tunnel.tunnelRemoteHost, - tunnelLocalPort: tunnel.tunnelLocalPort, - localAddress: tunnel.localAddress, - dispose: () => { - const existingHost = this._tunnels.get(tunnel.tunnelRemoteHost); - if (existingHost) { - const existing = existingHost.get(tunnel.tunnelRemotePort); - if (existing) { - existing.refcount--; - this.tryDisposeTunnel(tunnel.tunnelRemoteHost, tunnel.tunnelRemotePort, existing); - } - } - } - }; - } - - private async tryDisposeTunnel(remoteHost: string, remotePort: number, tunnel: { refcount: number, readonly value: Promise }): Promise { - if (tunnel.refcount <= 0) { - const disposePromise: Promise = tunnel.value.then(tunnel => { - tunnel.dispose(true); - this._onTunnelClosed.fire({ host: tunnel.tunnelRemoteHost, port: tunnel.tunnelRemotePort }); - }); - if (this._tunnels.has(remoteHost)) { - this._tunnels.get(remoteHost)!.delete(remotePort); - } - return disposePromise; - } - } - - async closeTunnel(remoteHost: string, remotePort: number): Promise { - const portMap = this._tunnels.get(remoteHost); - if (portMap && portMap.has(remotePort)) { - const value = portMap.get(remotePort)!; - value.refcount = 0; - await this.tryDisposeTunnel(remoteHost, remotePort, value); - } - } - - protected addTunnelToMap(remoteHost: string, remotePort: number, tunnel: Promise) { - if (!this._tunnels.has(remoteHost)) { - this._tunnels.set(remoteHost, new Map()); - } - this._tunnels.get(remoteHost)!.set(remotePort, { refcount: 1, value: tunnel }); - } - - protected abstract retainOrCreateTunnel(remoteAuthority: string, remoteHost: string, remotePort: number, localPort?: number): Promise | undefined; -} - -export class TunnelService extends AbstractTunnelService { - protected retainOrCreateTunnel(remoteAuthority: string, remoteHost: string, remotePort: number, localPort?: number | undefined): Promise | undefined { - const portMap = this._tunnels.get(remoteHost); - const existing = portMap ? portMap.get(remotePort) : undefined; - if (existing) { - ++existing.refcount; - return existing.value; - } - - if (this._tunnelProvider) { - const tunnel = this._tunnelProvider.forwardPort({ remoteAddress: { host: remoteHost, port: remotePort } }); - if (tunnel) { - this.addTunnelToMap(remoteHost, remotePort, tunnel); - } - return tunnel; - } - return undefined; - } -} diff --git a/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts b/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts index 0fecb23ec17..a0cc5572f4e 100644 --- a/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts +++ b/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts @@ -3,36 +3,23 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IRemoteAgentConnection, IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import product from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory'; -import { AbstractRemoteAgentService, RemoteAgentConnection } from 'vs/workbench/services/remote/common/abstractRemoteAgentService'; +import { AbstractRemoteAgentService } from 'vs/workbench/services/remote/common/abstractRemoteAgentService'; import { ISignService } from 'vs/platform/sign/common/sign'; -import { ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; import { ILogService } from 'vs/platform/log/common/log'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; export class RemoteAgentService extends AbstractRemoteAgentService implements IRemoteAgentService { - - public readonly socketFactory: ISocketFactory; - - private readonly _connection: IRemoteAgentConnection | null = null; - constructor( @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IProductService productService: IProductService, @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService, @ISignService signService: ISignService, - @ILogService logService: ILogService + @ILogService logService: ILogService, ) { - super(environmentService); - this.socketFactory = nodeSocketFactory; - if (environmentService.configuration.remoteAuthority) { - this._connection = this._register(new RemoteAgentConnection(environmentService.configuration.remoteAuthority, product.commit, nodeSocketFactory, remoteAuthorityResolverService, signService, logService)); - } - } - - getConnection(): IRemoteAgentConnection | null { - return this._connection; + super(nodeSocketFactory, environmentService, productService, remoteAuthorityResolverService, signService, logService); } } diff --git a/src/vs/workbench/services/request/electron-browser/requestService.ts b/src/vs/workbench/services/request/electron-sandbox/requestService.ts similarity index 93% rename from src/vs/workbench/services/request/electron-browser/requestService.ts rename to src/vs/workbench/services/request/electron-sandbox/requestService.ts index ad433fbcd2a..18ee94b099c 100644 --- a/src/vs/workbench/services/request/electron-browser/requestService.ts +++ b/src/vs/workbench/services/request/electron-sandbox/requestService.ts @@ -8,7 +8,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { RequestService } from 'vs/platform/request/browser/requestService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IRequestService } from 'vs/platform/request/common/request'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; export class NativeRequestService extends RequestService { diff --git a/src/vs/workbench/services/search/common/replace.ts b/src/vs/workbench/services/search/common/replace.ts index 2bbbf1ffa58..9dd7567231c 100644 --- a/src/vs/workbench/services/search/common/replace.ts +++ b/src/vs/workbench/services/search/common/replace.ts @@ -57,13 +57,13 @@ export class ReplacePattern { */ getReplaceString(text: string, preserveCase?: boolean): string | null { this._regExp.lastIndex = 0; - let match = this._regExp.exec(text); + const match = this._regExp.exec(text); if (match) { if (this.hasParameters) { if (match[0] === text) { return text.replace(this._regExp, this.buildReplaceString(match, preserveCase)); } - let replaceString = text.replace(this._regExp, this.buildReplaceString(match, preserveCase)); + const replaceString = text.replace(this._regExp, this.buildReplaceString(match, preserveCase)); return replaceString.substr(match.index, match[0].length - (text.length - replaceString.length)); } return this.buildReplaceString(match, preserveCase); @@ -94,7 +94,7 @@ export class ReplacePattern { let substrFrom = 0, result = ''; for (let i = 0, len = replaceString.length; i < len; i++) { - let chCode = replaceString.charCodeAt(i); + const chCode = replaceString.charCodeAt(i); if (chCode === CharCode.Backslash) { @@ -106,7 +106,7 @@ export class ReplacePattern { break; } - let nextChCode = replaceString.charCodeAt(i); + const nextChCode = replaceString.charCodeAt(i); let replaceWithCharacter: string | null = null; switch (nextChCode) { @@ -140,7 +140,7 @@ export class ReplacePattern { break; } - let nextChCode = replaceString.charCodeAt(i); + const nextChCode = replaceString.charCodeAt(i); let replaceWithCharacter: string | null = null; switch (nextChCode) { diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index 806f1706358..212cc51ea37 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -11,13 +11,12 @@ import * as objects from 'vs/base/common/objects'; import * as extpath from 'vs/base/common/extpath'; import { fuzzyContains, getNLines } from 'vs/base/common/strings'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { IFilesConfiguration, FILES_EXCLUDE_CONFIG } from 'vs/platform/files/common/files'; -import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IFilesConfiguration } from 'vs/platform/files/common/files'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; import { Event } from 'vs/base/common/event'; import { relative } from 'vs/base/common/path'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ResourceGlobMatcher } from 'vs/workbench/common/resources'; +import { isPromiseCanceledError } from 'vs/base/common/errors'; export const VIEWLET_ID = 'workbench.view.search'; export const PANEL_ID = 'workbench.panel.search'; @@ -31,7 +30,7 @@ export const ISearchService = createDecorator('searchService'); * A service that enables to search for files or with in files. */ export interface ISearchService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): Promise; fileSearch(query: IFileQuery, token?: CancellationToken): Promise; clearCache(cacheKey: string): Promise; @@ -350,6 +349,7 @@ export interface ISearchConfigurationProperties { searchEditor: { doubleClickBehaviour: 'selectWord' | 'goToLocation' | 'openLocationToSide', reusePriorSearchConfiguration: boolean, + defaultNumberOfContextLines: number | null, experimental: {} }; sortOrder: SearchSortOrder; @@ -382,14 +382,6 @@ export function getExcludes(configuration: ISearchConfiguration, includeSearchEx return allExcludes; } -export function createResourceExcludeMatcher(instantiationService: IInstantiationService, configurationService: IConfigurationService): ResourceGlobMatcher { - return instantiationService.createInstance( - ResourceGlobMatcher, - root => getExcludes(root ? configurationService.getValue({ resource: root }) : configurationService.getValue()) || Object.create(null), - event => event.affectsConfiguration(FILES_EXCLUDE_CONFIG) || event.affectsConfiguration(SEARCH_EXCLUDE_CONFIG) - ); -} - export function pathIncludedInQuery(queryProps: ICommonQueryProps, fsPath: string): boolean { if (queryProps.excludePattern && glob.match(queryProps.excludePattern, fsPath)) { return false; @@ -421,7 +413,8 @@ export enum SearchErrorCode { globParseError, invalidLiteral, rgProcessError, - other + other, + canceled } export class SearchError extends Error { @@ -430,7 +423,13 @@ export class SearchError extends Error { } } -export function deserializeSearchError(errorMsg: string): SearchError { +export function deserializeSearchError(error: Error): SearchError { + const errorMsg = error.message; + + if (isPromiseCanceledError(error)) { + return new SearchError(errorMsg, SearchErrorCode.canceled); + } + try { const details = JSON.parse(errorMsg); return new SearchError(details.message, details.code); diff --git a/src/vs/workbench/services/search/common/searchService.ts b/src/vs/workbench/services/search/common/searchService.ts index cec7149d6fe..cb047264d05 100644 --- a/src/vs/workbench/services/search/common/searchService.ts +++ b/src/vs/workbench/services/search/common/searchService.ts @@ -7,7 +7,7 @@ import * as arrays from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { keys, ResourceMap, values } from 'vs/base/common/map'; +import { ResourceMap } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; import { StopWatch } from 'vs/base/common/stopwatch'; import { URI as uri } from 'vs/base/common/uri'; @@ -24,7 +24,7 @@ import { DeferredPromise } from 'vs/base/test/common/utils'; export class SearchService extends Disposable implements ISearchService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; protected diskSearch: ISearchResultProvider | null = null; private readonly fileSearchProviders = new Map(); @@ -179,7 +179,7 @@ export class SearchService extends Disposable implements ISearchService { } private async waitForProvider(queryType: QueryType, scheme: string): Promise { - let deferredMap: Map> = queryType === QueryType.File ? + const deferredMap: Map> = queryType === QueryType.File ? this.deferredFileSearchesByScheme : this.deferredTextSearchesByScheme; @@ -199,7 +199,7 @@ export class SearchService extends Disposable implements ISearchService { const searchPs: Promise[] = []; const fqs = this.groupFolderQueriesByScheme(query); - await Promise.all(keys(fqs).map(async scheme => { + await Promise.all([...fqs.keys()].map(async scheme => { const schemeFQs = fqs.get(scheme)!; let provider = query.type === QueryType.File ? this.fileSearchProviders.get(scheme) : @@ -260,7 +260,8 @@ export class SearchService extends Disposable implements ISearchService { }, err => { const endToEndTime = e2eSW.elapsed(); this.logService.trace(`SearchService#search: ${endToEndTime}ms`); - const searchError = deserializeSearchError(err.message); + const searchError = deserializeSearchError(err); + this.logService.trace(`SearchService#searchError: ${searchError.message}`); this.sendTelemetry(query, endToEndTime, undefined, searchError); throw searchError; @@ -387,7 +388,8 @@ export class SearchService extends Disposable implements ISearchService { err.code === SearchErrorCode.globParseError ? 'glob' : err.code === SearchErrorCode.invalidLiteral ? 'literal' : err.code === SearchErrorCode.other ? 'other' : - 'unknown'; + err.code === SearchErrorCode.canceled ? 'canceled' : + 'unknown'; } type TextSearchCompleteClassification = { @@ -491,7 +493,7 @@ export class SearchService extends Disposable implements ISearchService { clearCache(cacheKey: string): Promise { const clearPs = [ this.diskSearch, - ...values(this.fileSearchProviders) + ...Array.from(this.fileSearchProviders.values()) ].map(provider => provider && provider.clearCache(cacheKey)); return Promise.all(clearPs) diff --git a/src/vs/workbench/services/search/node/fileSearch.ts b/src/vs/workbench/services/search/node/fileSearch.ts index 719ad873b50..2042e69d335 100644 --- a/src/vs/workbench/services/search/node/fileSearch.ts +++ b/src/vs/workbench/services/search/node/fileSearch.ts @@ -205,7 +205,7 @@ export class FileWalker { .map(arg => arg.match(/^-/) ? arg : `'${arg}'`) .join(' '); - let rgCmd = `rg ${escapedArgs}\n - cwd: ${ripgrep.cwd}`; + let rgCmd = `${ripgrep.rgDiskPath} ${escapedArgs}\n - cwd: ${ripgrep.cwd}`; if (ripgrep.rgArgs.siblingClauses) { rgCmd += `\n - Sibling clauses: ${JSON.stringify(ripgrep.rgArgs.siblingClauses)}`; } @@ -722,16 +722,16 @@ export function rgErrorMsgForDisplay(msg: string): string | undefined { const lines = msg.trim().split('\n'); const firstLine = lines[0].trim(); - if (strings.startsWith(firstLine, 'Error parsing regex')) { + if (firstLine.startsWith('Error parsing regex')) { return firstLine; } - if (strings.startsWith(firstLine, 'regex parse error')) { + if (firstLine.startsWith('regex parse error')) { return strings.uppercaseFirstLetter(lines[lines.length - 1].trim()); } - if (strings.startsWith(firstLine, 'error parsing glob') || - strings.startsWith(firstLine, 'unsupported encoding')) { + if (firstLine.startsWith('error parsing glob') || + firstLine.startsWith('unsupported encoding')) { // Uppercase first letter return firstLine.charAt(0).toUpperCase() + firstLine.substr(1); } @@ -741,7 +741,7 @@ export function rgErrorMsgForDisplay(msg: string): string | undefined { return `Literal '\\n' currently not supported`; } - if (strings.startsWith(firstLine, 'Literal ')) { + if (firstLine.startsWith('Literal ')) { // Other unsupported chars return firstLine; } diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts index 98e4ca43a95..600f3451357 100644 --- a/src/vs/workbench/services/search/node/rawSearchService.ts +++ b/src/vs/workbench/services/search/node/rawSearchService.ts @@ -5,19 +5,18 @@ import * as fs from 'fs'; import * as gracefulFs from 'graceful-fs'; -import { basename, dirname, join, sep } from 'vs/base/common/path'; import * as arrays from 'vs/base/common/arrays'; import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; +import { compareItemsByFuzzyScore, FuzzyScorerCache, IItemAccessor, prepareQuery } from 'vs/base/common/fuzzyScorer'; import * as objects from 'vs/base/common/objects'; +import { basename, dirname, join, sep } from 'vs/base/common/path'; import { StopWatch } from 'vs/base/common/stopwatch'; -import * as strings from 'vs/base/common/strings'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { compareItemsByFuzzyScore, IItemAccessor, prepareQuery, FuzzyScorerCache } from 'vs/base/common/fuzzyScorer'; import { MAX_FILE_SIZE } from 'vs/base/node/pfs'; -import { ICachedSearchStats, IFileQuery, IFileSearchStats, IFolderQuery, IProgressMessage, IRawFileQuery, IRawQuery, IRawTextQuery, ITextQuery, IFileSearchProgressItem, IRawFileMatch, IRawSearchService, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess, isFilePatternMatch } from 'vs/workbench/services/search/common/search'; +import { ICachedSearchStats, IFileQuery, IFileSearchProgressItem, IFileSearchStats, IFolderQuery, IProgressMessage, IRawFileMatch, IRawFileQuery, IRawQuery, IRawSearchService, IRawTextQuery, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess, isFilePatternMatch, ITextQuery } from 'vs/workbench/services/search/common/search'; import { Engine as FileSearchEngine } from 'vs/workbench/services/search/node/fileSearch'; import { TextSearchEngineAdapter } from 'vs/workbench/services/search/node/textSearchAdapter'; @@ -276,7 +275,7 @@ export class SearchService implements IRawSearchService { let cachedRow: ICacheRow | undefined; for (const previousSearch in cache.resultsToSearchCache) { // If we narrow down, we might be able to reuse the cached results - if (strings.startsWith(searchValue, previousSearch)) { + if (searchValue.startsWith(previousSearch)) { if (hasPathSep && previousSearch.indexOf(sep) < 0 && previousSearch !== '') { continue; // since a path character widens the search for potential more matches, require it in previous search too } diff --git a/src/vs/workbench/services/search/node/ripgrepFileSearch.ts b/src/vs/workbench/services/search/node/ripgrepFileSearch.ts index 44821d950ec..fdf210f00e6 100644 --- a/src/vs/workbench/services/search/node/ripgrepFileSearch.ts +++ b/src/vs/workbench/services/search/node/ripgrepFileSearch.ts @@ -23,6 +23,7 @@ export function spawnRipgrepCmd(config: IFileQuery, folderQuery: IFolderQuery, i const cwd = folderQuery.folder.fsPath; return { cmd: cp.spawn(rgDiskPath, rgArgs.args, { cwd }), + rgDiskPath, siblingClauses: rgArgs.siblingClauses, rgArgs, cwd @@ -131,14 +132,14 @@ function globExprsToRgGlobs(patterns: glob.IExpression, folder?: string, exclude // glob.ts requires forward slashes, but a UNC path still must start with \\ // #38165 and #38151 - if (strings.startsWith(key, '\\\\')) { + if (key.startsWith('\\\\')) { key = '\\\\' + key.substr(2).replace(/\\/g, '/'); } else { key = key.replace(/\\/g, '/'); } if (typeof value === 'boolean' && value) { - if (strings.startsWith(key, '\\\\')) { + if (key.startsWith('\\\\')) { // Absolute globs UNC paths don't work properly, see #58758 key += '**'; } diff --git a/src/vs/workbench/services/search/node/ripgrepSearchUtils.ts b/src/vs/workbench/services/search/node/ripgrepSearchUtils.ts index 550bc8bb462..0b2f1643537 100644 --- a/src/vs/workbench/services/search/node/ripgrepSearchUtils.ts +++ b/src/vs/workbench/services/search/node/ripgrepSearchUtils.ts @@ -3,17 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { startsWith } from 'vs/base/common/strings'; -import { ILogService } from 'vs/platform/log/common/log'; -import { SearchRange, TextSearchMatch } from 'vs/workbench/services/search/common/search'; import { mapArrayOrNot } from 'vs/base/common/arrays'; import { URI } from 'vs/base/common/uri'; +import { ILogService } from 'vs/platform/log/common/log'; +import { SearchRange, TextSearchMatch } from 'vs/workbench/services/search/common/search'; import * as searchExtTypes from 'vs/workbench/services/search/common/searchExtTypes'; export type Maybe = T | null | undefined; export function anchorGlob(glob: string): string { - return startsWith(glob, '**') || startsWith(glob, '/') ? glob : `/${glob}`; + return glob.startsWith('**') || glob.startsWith('/') ? glob : `/${glob}`; } /** diff --git a/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts b/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts index d11789ec923..006b8fec0df 100644 --- a/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts +++ b/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts @@ -5,19 +5,19 @@ import * as cp from 'child_process'; import { EventEmitter } from 'events'; -import * as path from 'vs/base/common/path'; import { StringDecoder } from 'string_decoder'; -import { createRegExp, startsWith, startsWithUTF8BOM, stripUTF8BOM, escapeRegExpCharacters, endsWith } from 'vs/base/common/strings'; +import { coalesce } from 'vs/base/common/arrays'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { groupBy } from 'vs/base/common/collections'; +import { splitGlobAware } from 'vs/base/common/glob'; +import * as path from 'vs/base/common/path'; +import { createRegExp, escapeRegExpCharacters, startsWithUTF8BOM, stripUTF8BOM } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; +import { Progress } from 'vs/platform/progress/common/progress'; import { IExtendedExtensionSearchOptions, SearchError, SearchErrorCode, serializeSearchError } from 'vs/workbench/services/search/common/search'; +import { Range, TextSearchComplete, TextSearchContext, TextSearchMatch, TextSearchOptions, TextSearchPreviewOptions, TextSearchQuery, TextSearchResult } from 'vs/workbench/services/search/common/searchExtTypes'; import { rgPath } from 'vscode-ripgrep'; import { anchorGlob, createTextSearchResult, IOutputChannel, Maybe } from './ripgrepSearchUtils'; -import { coalesce } from 'vs/base/common/arrays'; -import { splitGlobAware } from 'vs/base/common/glob'; -import { groupBy } from 'vs/base/common/collections'; -import { TextSearchQuery, TextSearchOptions, TextSearchResult, TextSearchComplete, TextSearchPreviewOptions, TextSearchContext, TextSearchMatch, Range } from 'vs/workbench/services/search/common/searchExtTypes'; -import { Progress } from 'vs/platform/progress/common/progress'; -import { CancellationToken } from 'vs/base/common/cancellation'; // If vscode-ripgrep is in an .asar file, then the binary is unpacked. const rgDiskPath = rgPath.replace(/\bnode_modules\.asar\b/, 'node_modules.asar.unpacked'); @@ -44,7 +44,7 @@ export class RipgrepTextSearchEngine { const escapedArgs = rgArgs .map(arg => arg.match(/^-/) ? arg : `'${arg}'`) .join(' '); - this.outputChannel.appendLine(`rg ${escapedArgs}\n - cwd: ${cwd}`); + this.outputChannel.appendLine(`${rgDiskPath} ${escapedArgs}\n - cwd: ${cwd}`); let rgProc: Maybe = cp.spawn(rgDiskPath, rgArgs, { cwd }); rgProc.on('error', e => { @@ -57,6 +57,7 @@ export class RipgrepTextSearchEngine { const ripgrepParser = new RipgrepParser(options.maxResults, cwd, options.previewOptions); ripgrepParser.on('result', (match: TextSearchResult) => { gotResult = true; + dataWithoutResult = ''; progress.report(match); }); @@ -79,8 +80,12 @@ export class RipgrepTextSearchEngine { cancel(); }); + let dataWithoutResult = ''; rgProc.stdout!.on('data', data => { ripgrepParser.handleData(data); + if (!gotResult) { + dataWithoutResult += data; + } }); let gotData = false; @@ -96,7 +101,12 @@ export class RipgrepTextSearchEngine { rgProc.on('close', () => { this.outputChannel.appendLine(gotData ? 'Got data from stdout' : 'No data from stdout'); this.outputChannel.appendLine(gotResult ? 'Got result from parser' : 'No result from parser'); + if (dataWithoutResult) { + this.outputChannel.appendLine(`Got data without result: ${dataWithoutResult}`); + } + this.outputChannel.appendLine(''); + if (isDone) { resolve({ limitHit }); } else { @@ -116,7 +126,8 @@ export class RipgrepTextSearchEngine { } /** - * Read the first line of stderr and return an error for display or undefined, based on a whitelist. + * Read the first line of stderr and return an error for display or undefined, based on a list of + * allowed properties. * Ripgrep produces stderr output which is not from a fatal error, and we only want the search to be * "failed" when a fatal error was produced. */ @@ -124,7 +135,7 @@ export function rgErrorMsgForDisplay(msg: string): Maybe { const lines = msg.split('\n'); const firstLine = lines[0].trim(); - if (lines.some(l => startsWith(l, 'regex parse error'))) { + if (lines.some(l => l.startsWith('regex parse error'))) { return new SearchError(buildRegexParseError(lines), SearchErrorCode.regexParseError); } @@ -133,17 +144,17 @@ export function rgErrorMsgForDisplay(msg: string): Maybe { return new SearchError(`Unknown encoding: ${match[1]}`, SearchErrorCode.unknownEncoding); } - if (startsWith(firstLine, 'error parsing glob')) { + if (firstLine.startsWith('error parsing glob')) { // Uppercase first letter return new SearchError(firstLine.charAt(0).toUpperCase() + firstLine.substr(1), SearchErrorCode.globParseError); } - if (startsWith(firstLine, 'the literal')) { + if (firstLine.startsWith('the literal')) { // Uppercase first letter return new SearchError(firstLine.charAt(0).toUpperCase() + firstLine.substr(1), SearchErrorCode.invalidLiteral); } - if (startsWith(firstLine, 'PCRE2: error compiling pattern')) { + if (firstLine.startsWith('PCRE2: error compiling pattern')) { return new SearchError(firstLine, SearchErrorCode.regexParseError); } @@ -151,12 +162,12 @@ export function rgErrorMsgForDisplay(msg: string): Maybe { } export function buildRegexParseError(lines: string[]): string { - let errorMessage: string[] = ['Regex parse error']; - let pcre2ErrorLine = lines.filter(l => (startsWith(l, 'PCRE2:'))); + const errorMessage: string[] = ['Regex parse error']; + const pcre2ErrorLine = lines.filter(l => (l.startsWith('PCRE2:'))); if (pcre2ErrorLine.length >= 1) { - let pcre2ErrorMessage = pcre2ErrorLine[0].replace('PCRE2:', ''); + const pcre2ErrorMessage = pcre2ErrorLine[0].replace('PCRE2:', ''); if (pcre2ErrorMessage.indexOf(':') !== -1 && pcre2ErrorMessage.split(':').length >= 2) { - let pcre2ActualErrorMessage = pcre2ErrorMessage.split(':')[1]; + const pcre2ActualErrorMessage = pcre2ErrorMessage.split(':')[1]; errorMessage.push(':' + pcre2ActualErrorMessage); } } @@ -289,12 +300,12 @@ export class RipgrepParser extends EventEmitter { match.end = match.end <= 3 ? 0 : match.end - 3; } const inBetweenChars = fullTextBytes.slice(prevMatchEnd, match.start).toString().length; - let startCol = prevMatchEndCol + inBetweenChars; + const startCol = prevMatchEndCol + inBetweenChars; const stats = getNumLinesAndLastNewlineLength(matchText); const startLineNumber = prevMatchEndLine; const endLineNumber = stats.numLines + startLineNumber; - let endCol = stats.numLines > 0 ? + const endCol = stats.numLines > 0 ? stats.lastLineLength : stats.lastLineLength + startCol; @@ -357,12 +368,12 @@ function getRgArgs(query: TextSearchQuery, options: TextSearchOptions): string[] const { doubleStarIncludes, otherIncludes } = groupBy( options.includes, - (include: string) => startsWith(include, '**') ? 'doubleStarIncludes' : 'otherIncludes'); + (include: string) => include.startsWith('**') ? 'doubleStarIncludes' : 'otherIncludes'); if (otherIncludes && otherIncludes.length) { const uniqueOthers = new Set(); otherIncludes.forEach(other => { - if (!endsWith(other, '/**')) { + if (!other.endsWith('/**')) { other += '/**'; } diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index a9170133473..8997acabba8 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -53,7 +53,7 @@ export class DiskSearch implements ISearchResultProvider { @IConfigurationService private readonly configService: IConfigurationService, ) { const timeout = this.configService.getValue().search.maintainFileSearchCache ? - Number.MAX_VALUE : + 100 * 60 * 60 * 1000 : 60 * 60 * 1000; const opts: IIPCOptions = { diff --git a/src/vs/workbench/services/search/node/textSearchManager.ts b/src/vs/workbench/services/search/node/textSearchManager.ts index 3433ce35537..84be2f01f98 100644 --- a/src/vs/workbench/services/search/node/textSearchManager.ts +++ b/src/vs/workbench/services/search/node/textSearchManager.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { toCanonicalName } from 'vs/base/node/encoding'; +import { toCanonicalName } from 'vs/workbench/services/textfile/common/encoding'; import * as pfs from 'vs/base/node/pfs'; import { ITextQuery } from 'vs/workbench/services/search/common/search'; import { TextSearchProvider } from 'vs/workbench/services/search/common/searchExtTypes'; diff --git a/src/vs/workbench/services/search/test/common/replace.test.ts b/src/vs/workbench/services/search/test/common/replace.test.ts index c43e0f3930e..7180e0fb43e 100644 --- a/src/vs/workbench/services/search/test/common/replace.test.ts +++ b/src/vs/workbench/services/search/test/common/replace.test.ts @@ -8,7 +8,7 @@ import { ReplacePattern } from 'vs/workbench/services/search/common/replace'; suite('Replace Pattern test', () => { test('parse replace string', () => { - let testParse = (input: string, expected: string, expectedHasParameters: boolean) => { + const testParse = (input: string, expected: string, expectedHasParameters: boolean) => { let actual = new ReplacePattern(input, { pattern: 'somepattern', isRegExp: true }); assert.equal(expected, actual.pattern); assert.equal(expectedHasParameters, actual.hasParameters); @@ -109,7 +109,7 @@ suite('Replace Pattern test', () => { actual = testObject.getReplaceString('bla'); assert.equal('hellobla', actual); - testObject = new ReplacePattern('import * as $1 from \'$2\';', { pattern: 'let\\s+(\\w+)\\s*=\\s*require\\s*\\(\\s*[\'\"]([\\w\.\\-/]+)\\s*[\'\"]\\s*\\)\\s*', isRegExp: true }); + testObject = new ReplacePattern('import * as $1 from \'$2\';', { pattern: 'let\\s+(\\w+)\\s*=\\s*require\\s*\\(\\s*[\'\"]([\\w.\\-/]+)\\s*[\'\"]\\s*\\)\\s*', isRegExp: true }); actual = testObject.getReplaceString('let fs = require(\'fs\')'); assert.equal('import * as fs from \'fs\';', actual); @@ -119,19 +119,19 @@ suite('Replace Pattern test', () => { actual = testObject.getReplaceString('let require(\'fs\')'); assert.equal(null, actual); - testObject = new ReplacePattern('import * as $1 from \'$1\';', { pattern: 'let\\s+(\\w+)\\s*=\\s*require\\s*\\(\\s*[\'\"]([\\w\.\\-/]+)\\s*[\'\"]\\s*\\)\\s*', isRegExp: true }); + testObject = new ReplacePattern('import * as $1 from \'$1\';', { pattern: 'let\\s+(\\w+)\\s*=\\s*require\\s*\\(\\s*[\'\"]([\\w.\\-/]+)\\s*[\'\"]\\s*\\)\\s*', isRegExp: true }); actual = testObject.getReplaceString('let something = require(\'fs\')'); assert.equal('import * as something from \'something\';', actual); - testObject = new ReplacePattern('import * as $2 from \'$1\';', { pattern: 'let\\s+(\\w+)\\s*=\\s*require\\s*\\(\\s*[\'\"]([\\w\.\\-/]+)\\s*[\'\"]\\s*\\)\\s*', isRegExp: true }); + testObject = new ReplacePattern('import * as $2 from \'$1\';', { pattern: 'let\\s+(\\w+)\\s*=\\s*require\\s*\\(\\s*[\'\"]([\\w.\\-/]+)\\s*[\'\"]\\s*\\)\\s*', isRegExp: true }); actual = testObject.getReplaceString('let something = require(\'fs\')'); assert.equal('import * as fs from \'something\';', actual); - testObject = new ReplacePattern('import * as $0 from \'$0\';', { pattern: 'let\\s+(\\w+)\\s*=\\s*require\\s*\\(\\s*[\'\"]([\\w\.\\-/]+)\\s*[\'\"]\\s*\\)\\s*', isRegExp: true }); + testObject = new ReplacePattern('import * as $0 from \'$0\';', { pattern: 'let\\s+(\\w+)\\s*=\\s*require\\s*\\(\\s*[\'\"]([\\w.\\-/]+)\\s*[\'\"]\\s*\\)\\s*', isRegExp: true }); actual = testObject.getReplaceString('let something = require(\'fs\');'); assert.equal('import * as let something = require(\'fs\') from \'let something = require(\'fs\')\';', actual); - testObject = new ReplacePattern('import * as $1 from \'$2\';', { pattern: 'let\\s+(\\w+)\\s*=\\s*require\\s*\\(\\s*[\'\"]([\\w\.\\-/]+)\\s*[\'\"]\\s*\\)\\s*', isRegExp: false }); + testObject = new ReplacePattern('import * as $1 from \'$2\';', { pattern: 'let\\s+(\\w+)\\s*=\\s*require\\s*\\(\\s*[\'\"]([\\w.\\-/]+)\\s*[\'\"]\\s*\\)\\s*', isRegExp: false }); actual = testObject.getReplaceString('let fs = require(\'fs\');'); assert.equal(null, actual); diff --git a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts index 1d2ad24fb75..f2ad6f9a9e2 100644 --- a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts +++ b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts @@ -370,7 +370,7 @@ suite('TextSearch-integration', function () { return doSearchTest(config, 0).then(() => { throw new Error('expected fail'); }, err => { - const searchError = deserializeSearchError(err.message); + const searchError = deserializeSearchError(err); assert.equal(searchError.message, 'Unknown encoding: invalidEncoding'); assert.equal(searchError.code, SearchErrorCode.unknownEncoding); }); @@ -386,8 +386,8 @@ suite('TextSearch-integration', function () { return doSearchTest(config, 0).then(() => { throw new Error('expected fail'); }, err => { - const searchError = deserializeSearchError(err.message); - let regexParseErrorForUnclosedParenthesis = 'Regex parse error: unmatched closing parenthesis'; + const searchError = deserializeSearchError(err); + const regexParseErrorForUnclosedParenthesis = 'Regex parse error: unmatched closing parenthesis'; assert.equal(searchError.message, regexParseErrorForUnclosedParenthesis); assert.equal(searchError.code, SearchErrorCode.regexParseError); }); @@ -403,8 +403,8 @@ suite('TextSearch-integration', function () { return doSearchTest(config, 0).then(() => { throw new Error('expected fail'); }, err => { - const searchError = deserializeSearchError(err.message); - let regexParseErrorForLookAround = 'Regex parse error: lookbehind assertion is not fixed length'; + const searchError = deserializeSearchError(err); + const regexParseErrorForLookAround = 'Regex parse error: lookbehind assertion is not fixed length'; assert.equal(searchError.message, regexParseErrorForLookAround); assert.equal(searchError.code, SearchErrorCode.regexParseError); }); @@ -424,7 +424,7 @@ suite('TextSearch-integration', function () { return doSearchTest(config, 0).then(() => { throw new Error('expected fail'); }, err => { - const searchError = deserializeSearchError(err.message); + const searchError = deserializeSearchError(err); assert.equal(searchError.message, 'Error parsing glob \'/{{}\': nested alternate groups are not allowed'); assert.equal(searchError.code, SearchErrorCode.globParseError); }); diff --git a/src/vs/workbench/services/sharedProcess/electron-browser/sharedProcessService.ts b/src/vs/workbench/services/sharedProcess/electron-browser/sharedProcessService.ts index 8ebf99ffb30..2fb053da89a 100644 --- a/src/vs/workbench/services/sharedProcess/electron-browser/sharedProcessService.ts +++ b/src/vs/workbench/services/sharedProcess/electron-browser/sharedProcessService.ts @@ -6,27 +6,29 @@ import { Client } from 'vs/base/parts/ipc/common/ipc.net'; import { connect } from 'vs/base/parts/ipc/node/ipc.net'; import { IChannel, IServerChannel, getDelayedChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; export class SharedProcessService implements ISharedProcessService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private withSharedProcessConnection: Promise>; private sharedProcessMainChannel: IChannel; constructor( @IMainProcessService mainProcessService: IMainProcessService, + @IElectronService electronService: IElectronService, @IWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService ) { this.sharedProcessMainChannel = mainProcessService.getChannel('sharedProcess'); this.withSharedProcessConnection = this.whenSharedProcessReady() - .then(() => connect(environmentService.sharedIPCHandle, `window:${environmentService.configuration.windowId}`)); + .then(() => connect(environmentService.sharedIPCHandle, `window:${electronService.windowId}`)); } whenSharedProcessReady(): Promise { diff --git a/src/vs/workbench/services/statusbar/common/statusbar.ts b/src/vs/workbench/services/statusbar/common/statusbar.ts index af13535d622..14a52e61d68 100644 --- a/src/vs/workbench/services/statusbar/common/statusbar.ts +++ b/src/vs/workbench/services/statusbar/common/statusbar.ts @@ -33,6 +33,12 @@ export interface IStatusbarEntry { */ readonly ariaLabel: string; + /** + * Role of the status bar entry which defines how a screen reader interacts with it. + * Default is 'button'. + */ + readonly role?: string; + /** * An optional tooltip text to show when you hover over the entry */ @@ -61,7 +67,7 @@ export interface IStatusbarEntry { export interface IStatusbarService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Adds an entry to the statusbar with the given alignment and priority. Use the returned accessor diff --git a/src/vs/workbench/services/telemetry/browser/telemetryService.ts b/src/vs/workbench/services/telemetry/browser/telemetryService.ts index 4fc57a50d39..41b39345f1e 100644 --- a/src/vs/workbench/services/telemetry/browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/browser/telemetryService.ts @@ -33,9 +33,10 @@ export class WebTelemetryAppender implements ITelemetryAppender { export class TelemetryService extends Disposable implements ITelemetryService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private impl: ITelemetryService; + public readonly sendErrorTelemetry = false; constructor( @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @@ -64,6 +65,10 @@ export class TelemetryService extends Disposable implements ITelemetryService { return this.impl.setEnabled(value); } + setExperimentProperty(name: string, value: string): void { + return this.impl.setExperimentProperty(name, value); + } + get isOptedIn(): boolean { return this.impl.isOptedIn; } diff --git a/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts b/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts index ac0a0aeb9e1..99d36e3fa46 100644 --- a/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts @@ -21,9 +21,10 @@ import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/enviro export class TelemetryService extends Disposable implements ITelemetryService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private impl: ITelemetryService; + public readonly sendErrorTelemetry: boolean; constructor( @IWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService, @@ -48,12 +49,18 @@ export class TelemetryService extends Disposable implements ITelemetryService { } else { this.impl = NullTelemetryService; } + + this.sendErrorTelemetry = this.impl.sendErrorTelemetry; } setEnabled(value: boolean): void { return this.impl.setEnabled(value); } + setExperimentProperty(name: string, value: string): void { + return this.impl.setExperimentProperty(name, value); + } + get isOptedIn(): boolean { return this.impl.isOptedIn; } diff --git a/src/vs/workbench/services/textMate/common/textMateService.ts b/src/vs/workbench/services/textMate/common/textMateService.ts index 2cdaac115d5..fdfc21e5b88 100644 --- a/src/vs/workbench/services/textMate/common/textMateService.ts +++ b/src/vs/workbench/services/textMate/common/textMateService.ts @@ -10,7 +10,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' export const ITextMateService = createDecorator('textMateService'); export interface ITextMateService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; onDidEncounterLanguage: Event; diff --git a/src/vs/workbench/services/textfile/browser/browserTextFileService.ts b/src/vs/workbench/services/textfile/browser/browserTextFileService.ts index 83d5fba161a..e53764eb2ef 100644 --- a/src/vs/workbench/services/textfile/browser/browserTextFileService.ts +++ b/src/vs/workbench/services/textfile/browser/browserTextFileService.ts @@ -4,18 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { AbstractTextFileService } from 'vs/workbench/services/textfile/browser/textFileService'; -import { ITextFileService, IResourceEncodings, IResourceEncoding, TextFileEditorModelState } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, TextFileEditorModelState } from 'vs/workbench/services/textfile/common/textfiles'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; export class BrowserTextFileService extends AbstractTextFileService { - readonly encoding: IResourceEncodings = { - getPreferredWriteEncoding(): IResourceEncoding { - return { encoding: 'utf8', hasBOM: false }; - } - }; - protected registerListeners(): void { super.registerListeners(); diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index f9ac0ff168d..9fe141b27b7 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -5,11 +5,10 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; -import { AsyncEmitter } from 'vs/base/common/event'; -import { ITextFileService, ITextFileStreamContent, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, ITextFileSaveOptions, ITextFileEditorModelManager, TextFileCreateEvent } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, ITextFileStreamContent, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, ITextFileSaveOptions, ITextFileEditorModelManager, IResourceEncoding, stringToSnapshot } from 'vs/workbench/services/textfile/common/textfiles'; import { IRevertOptions, IEncodingSupport } from 'vs/workbench/common/editor'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; -import { IFileService, FileOperationError, FileOperationResult, IFileStatWithMetadata, ICreateFileOptions, FileOperation } from 'vs/platform/files/common/files'; +import { IFileService, FileOperationError, FileOperationResult, IFileStatWithMetadata, ICreateFileOptions, IFileStreamContent } from 'vs/platform/files/common/files'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IUntitledTextEditorService, IUntitledTextEditorModelManager } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; @@ -19,42 +18,38 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { Schemas } from 'vs/base/common/network'; import { createTextBufferFactoryFromSnapshot, createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { isEqual, joinPath, dirname, basename, toLocalResource } from 'vs/base/common/resources'; +import { joinPath, dirname, basename, toLocalResource, extUri, extname } from 'vs/base/common/resources'; import { IDialogService, IFileDialogService, IConfirmation } from 'vs/platform/dialogs/common/dialogs'; -import { VSBuffer } from 'vs/base/common/buffer'; +import { VSBuffer, VSBufferReadable, bufferToStream } from 'vs/base/common/buffer'; import { ITextSnapshot, ITextModel } from 'vs/editor/common/model'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; -import { CancellationToken } from 'vs/base/common/cancellation'; import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { suggestFilename } from 'vs/base/common/mime'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { isValidBasename } from 'vs/base/common/extpath'; import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { UTF8, UTF8_with_bom, UTF16be, UTF16le, encodingExists, UTF8_BOM, detectEncodingByBOMFromBuffer, toEncodeReadable, toDecodeStream, IDecodeStreamResult } from 'vs/workbench/services/textfile/common/encoding'; +import { consumeStream } from 'vs/base/common/stream'; +import { IModeService } from 'vs/editor/common/services/modeService'; /** * The workbench file service implementation implements the raw file service spec and adds additional methods on top. */ export abstract class AbstractTextFileService extends Disposable implements ITextFileService { - _serviceBrand: undefined; - - //#region events - - private _onDidCreateTextFile = this._register(new AsyncEmitter()); - readonly onDidCreateTextFile = this._onDidCreateTextFile.event; - - //#endregion + declare readonly _serviceBrand: undefined; readonly files: ITextFileEditorModelManager = this._register(this.instantiationService.createInstance(TextFileEditorModelManager)); readonly untitled: IUntitledTextEditorModelManager = this.untitledTextEditorService; - abstract get encoding(): IResourceEncodings; - constructor( @IFileService protected readonly fileService: IFileService, @IUntitledTextEditorService private untitledTextEditorService: IUntitledTextEditorService, @@ -69,7 +64,9 @@ export abstract class AbstractTextFileService extends Disposable implements ITex @ITextModelService private readonly textModelService: ITextModelService, @ICodeEditorService private readonly codeEditorService: ICodeEditorService, @IPathService private readonly pathService: IPathService, - @IWorkingCopyFileService private readonly workingCopyFileService: IWorkingCopyFileService + @IWorkingCopyFileService private readonly workingCopyFileService: IWorkingCopyFileService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, + @IModeService private readonly modeService: IModeService ) { super(); @@ -84,89 +81,102 @@ export abstract class AbstractTextFileService extends Disposable implements ITex //#region text file read / write / create - async read(resource: URI, options?: IReadTextFileOptions): Promise { - const content = await this.fileService.readFile(resource, options); + private _encoding: EncodingOracle | undefined; - // in case of acceptTextOnly: true, we check the first - // chunk for possibly being binary by looking for 0-bytes - // we limit this check to the first 512 bytes - this.validateBinary(content.value, options); + get encoding(): EncodingOracle { + if (!this._encoding) { + this._encoding = this._register(this.instantiationService.createInstance(EncodingOracle)); + } + + return this._encoding; + } + + async read(resource: URI, options?: IReadTextFileOptions): Promise { + const [bufferStream, decoder] = await this.doRead(resource, { + ...options, + // optimization: since we know that the caller does not + // care about buffering, we indicate this to the reader. + // this reduces all the overhead the buffered reading + // has (open, read, close) if the provider supports + // unbuffered reading. + preferUnbuffered: true + }); return { - ...content, - encoding: 'utf8', - value: content.value.toString() + ...bufferStream, + encoding: decoder.detected.encoding || UTF8, + value: await consumeStream(decoder.stream, strings => strings.join('')) }; } async readStream(resource: URI, options?: IReadTextFileOptions): Promise { - const stream = await this.fileService.readFileStream(resource, options); - - // in case of acceptTextOnly: true, we check the first - // chunk for possibly being binary by looking for 0-bytes - // we limit this check to the first 512 bytes - let checkedForBinary = false; - const throwOnBinary = (data: VSBuffer): Error | undefined => { - if (!checkedForBinary) { - checkedForBinary = true; - - this.validateBinary(data, options); - } - - return undefined; - }; + const [bufferStream, decoder] = await this.doRead(resource, options); return { - ...stream, - encoding: 'utf8', - value: await createTextBufferFactoryFromStream(stream.value, undefined, options?.acceptTextOnly ? throwOnBinary : undefined) + ...bufferStream, + encoding: decoder.detected.encoding || UTF8, + value: await createTextBufferFactoryFromStream(decoder.stream) }; } - private validateBinary(buffer: VSBuffer, options?: IReadTextFileOptions): void { - if (!options || !options.acceptTextOnly) { - return; // no validation needed + private async doRead(resource: URI, options?: IReadTextFileOptions & { preferUnbuffered?: boolean }): Promise<[IFileStreamContent, IDecodeStreamResult]> { + + // read stream raw (either buffered or unbuffered) + let bufferStream: IFileStreamContent; + if (options?.preferUnbuffered) { + const content = await this.fileService.readFile(resource, options); + bufferStream = { + ...content, + value: bufferToStream(content.value) + }; + } else { + bufferStream = await this.fileService.readFileStream(resource, options); } - // in case of acceptTextOnly: true, we check the first - // chunk for possibly being binary by looking for 0-bytes - // we limit this check to the first 512 bytes - for (let i = 0; i < buffer.byteLength && i < 512; i++) { - if (buffer.readUInt8(i) === 0) { - throw new TextFileOperationError(nls.localize('fileBinaryError', "File seems to be binary and cannot be opened as text"), TextFileOperationResult.FILE_IS_BINARY, options); - } + // read through encoding library + const decoder = await toDecodeStream(bufferStream.value, { + guessEncoding: options?.autoGuessEncoding || this.textResourceConfigurationService.getValue(resource, 'files.autoGuessEncoding'), + overwriteEncoding: detectedEncoding => this.encoding.getReadEncoding(resource, options, detectedEncoding) + }); + + // validate binary + if (options?.acceptTextOnly && decoder.detected.seemsBinary) { + throw new TextFileOperationError(nls.localize('fileBinaryError', "File seems to be binary and cannot be opened as text"), TextFileOperationResult.FILE_IS_BINARY, options); } + + return [bufferStream, decoder]; } async create(resource: URI, value?: string | ITextSnapshot, options?: ICreateFileOptions): Promise { + const readable = await this.getEncodedReadable(resource, value); - // file operation participation - await this.workingCopyFileService.runFileOperationParticipants(resource, undefined, FileOperation.CREATE); - - // create file on disk - const stat = await this.doCreate(resource, value, options); - - // If we had an existing model for the given resource, load - // it again to make sure it is up to date with the contents - // we just wrote into the underlying resource by calling - // revert() - const existingModel = this.files.get(resource); - if (existingModel && !existingModel.isDisposed()) { - await existingModel.revert(); - } - - // after event - await this._onDidCreateTextFile.fireAsync({ resource }, CancellationToken.None); - - return stat; - } - - protected doCreate(resource: URI, value?: string | ITextSnapshot, options?: ICreateFileOptions): Promise { - return this.fileService.createFile(resource, toBufferOrReadable(value), options); + return this.workingCopyFileService.create(resource, readable, options); } async write(resource: URI, value: string | ITextSnapshot, options?: IWriteTextFileOptions): Promise { - return this.fileService.writeFile(resource, toBufferOrReadable(value), options); + const readable = await this.getEncodedReadable(resource, value, options); + + return this.fileService.writeFile(resource, readable, options); + } + + private async getEncodedReadable(resource: URI, value?: string | ITextSnapshot): Promise; + private async getEncodedReadable(resource: URI, value: string | ITextSnapshot, options?: IWriteTextFileOptions): Promise; + private async getEncodedReadable(resource: URI, value?: string | ITextSnapshot, options?: IWriteTextFileOptions): Promise { + + // check for encoding + const { encoding, addBOM } = await this.encoding.getWriteEncoding(resource, options); + + // when encoding is standard skip encoding step + if (encoding === UTF8 && !addBOM) { + return typeof value === 'undefined' + ? undefined + : toBufferOrReadable(value); + } + + // otherwise create encoded readable + value = value || ''; + const snapshot = typeof value === 'string' ? stringToSnapshot(value) : value; + return toEncodeReadable(snapshot, encoding, { addBOM }); } //#endregion @@ -222,8 +232,19 @@ export abstract class AbstractTextFileService extends Disposable implements ITex } // Just save if target is same as models own resource - if (source.toString() === target.toString()) { - return this.save(source, options); + if (extUri.isEqual(source, target)) { + return this.save(source, { ...options, force: true /* force to save, even if not dirty (https://github.com/microsoft/vscode/issues/99619) */ }); + } + + // If the target is different but of same identity, we + // move the source to the target, knowing that the + // underlying file system cannot have both and then save. + // However, this will only work if the source exists + // and is not orphaned, so we need to check that too. + if (this.fileService.canHandleResource(source) && this.uriIdentityService.extUri.isEqual(source, target) && (await this.fileService.exists(source))) { + await this.workingCopyFileService.move([{ source, target }]); + + return this.save(target, options); } // Do it @@ -251,11 +272,13 @@ export abstract class AbstractTextFileService extends Disposable implements ITex // Next, if the source does not seem to be a file, we try to // resolve a text model from the resource to get at the // contents and additional meta data (e.g. encoding). - else if (this.textModelService.hasTextModelContentProvider(source.scheme)) { + else if (this.textModelService.canHandleResource(source)) { const modelReference = await this.textModelService.createModelReference(source); - success = await this.doSaveAsTextFile(modelReference.object, source, target, options); - - modelReference.dispose(); // free up our use of the reference + try { + success = await this.doSaveAsTextFile(modelReference.object, source, target, options); + } finally { + modelReference.dispose(); // free up our use of the reference + } } // Finally we simply check if we can find a editor model that @@ -327,7 +350,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex // path. This can happen if the file was created after the untitled file was opened. // See https://github.com/Microsoft/vscode/issues/67946 let write: boolean; - if (sourceModel instanceof UntitledTextEditorModel && sourceModel.hasAssociatedFilePath && targetExists && isEqual(target, toLocalResource(sourceModel.resource, this.environmentService.configuration.remoteAuthority))) { + if (sourceModel instanceof UntitledTextEditorModel && sourceModel.hasAssociatedFilePath && targetExists && this.uriIdentityService.extUri.isEqual(target, toLocalResource(sourceModel.resource, this.environmentService.configuration.remoteAuthority))) { write = await this.confirmOverwrite(target); } else { write = true; @@ -420,8 +443,8 @@ export abstract class AbstractTextFileService extends Disposable implements ITex // Add mode file extension if specified const mode = model.getMode(); - if (mode !== PLAINTEXT_MODE_ID) { - suggestedFilename = suggestFilename(mode, untitledName); + if (mode && mode !== PLAINTEXT_MODE_ID) { + suggestedFilename = this.suggestFilename(mode, untitledName); } else { suggestedFilename = untitledName; } @@ -435,7 +458,18 @@ export abstract class AbstractTextFileService extends Disposable implements ITex // Try to place where last active file was if any // Otherwise fallback to user home - return joinPath(this.fileDialogService.defaultFilePath() || (await this.pathService.userHome), suggestedFilename); + return joinPath(this.fileDialogService.defaultFilePath() || (await this.pathService.userHome()), suggestedFilename); + } + + suggestFilename(mode: string, untitledName: string) { + const extension = this.modeService.getExtensions(mode)[0]; + if (extension) { + if (!untitledName.endsWith(extension)) { + return untitledName + extension; + } + } + const filename = this.modeService.getFilenames(mode)[0]; + return filename || untitledName; } //#endregion @@ -476,3 +510,153 @@ export abstract class AbstractTextFileService extends Disposable implements ITex //#endregion } + +export interface IEncodingOverride { + parent?: URI; + extension?: string; + encoding: string; +} + +export class EncodingOracle extends Disposable implements IResourceEncodings { + + private _encodingOverrides: IEncodingOverride[]; + protected get encodingOverrides(): IEncodingOverride[] { return this._encodingOverrides; } + protected set encodingOverrides(value: IEncodingOverride[]) { this._encodingOverrides = value; } + + constructor( + @ITextResourceConfigurationService private textResourceConfigurationService: ITextResourceConfigurationService, + @IEnvironmentService private environmentService: IEnvironmentService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IFileService private fileService: IFileService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService + ) { + super(); + + this._encodingOverrides = this.getDefaultEncodingOverrides(); + + this.registerListeners(); + } + + private registerListeners(): void { + + // Workspace Folder Change + this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.encodingOverrides = this.getDefaultEncodingOverrides())); + } + + private getDefaultEncodingOverrides(): IEncodingOverride[] { + const defaultEncodingOverrides: IEncodingOverride[] = []; + + // Global settings + defaultEncodingOverrides.push({ parent: this.environmentService.userRoamingDataHome, encoding: UTF8 }); + + // Workspace files (via extension and via untitled workspaces location) + defaultEncodingOverrides.push({ extension: WORKSPACE_EXTENSION, encoding: UTF8 }); + defaultEncodingOverrides.push({ parent: this.environmentService.untitledWorkspacesHome, encoding: UTF8 }); + + // Folder Settings + this.contextService.getWorkspace().folders.forEach(folder => { + defaultEncodingOverrides.push({ parent: joinPath(folder.uri, '.vscode'), encoding: UTF8 }); + }); + + return defaultEncodingOverrides; + } + + async getWriteEncoding(resource: URI, options?: IWriteTextFileOptions): Promise<{ encoding: string, addBOM: boolean }> { + const { encoding, hasBOM } = await this.getPreferredWriteEncoding(resource, options ? options.encoding : undefined); + + // Some encodings come with a BOM automatically + if (hasBOM) { + return { encoding, addBOM: true }; + } + + // Ensure that we preserve an existing BOM if found for UTF8 + // unless we are instructed to overwrite the encoding + const overwriteEncoding = options?.overwriteEncoding; + if (!overwriteEncoding && encoding === UTF8) { + try { + const buffer = (await this.fileService.readFile(resource, { length: UTF8_BOM.length })).value; + if (detectEncodingByBOMFromBuffer(buffer, buffer.byteLength) === UTF8_with_bom) { + return { encoding, addBOM: true }; + } + } catch (error) { + // ignore - file might not exist + } + } + + return { encoding, addBOM: false }; + } + + async getPreferredWriteEncoding(resource: URI, preferredEncoding?: string): Promise { + const resourceEncoding = await this.getEncodingForResource(resource, preferredEncoding); + + return { + encoding: resourceEncoding, + hasBOM: resourceEncoding === UTF16be || resourceEncoding === UTF16le || resourceEncoding === UTF8_with_bom // enforce BOM for certain encodings + }; + } + + getReadEncoding(resource: URI, options: IReadTextFileOptions | undefined, detectedEncoding: string | null): Promise { + let preferredEncoding: string | undefined; + + // Encoding passed in as option + if (options?.encoding) { + if (detectedEncoding === UTF8_with_bom && options.encoding === UTF8) { + preferredEncoding = UTF8_with_bom; // indicate the file has BOM if we are to resolve with UTF 8 + } else { + preferredEncoding = options.encoding; // give passed in encoding highest priority + } + } + + // Encoding detected + else if (detectedEncoding) { + preferredEncoding = detectedEncoding; + } + + // Encoding configured + else if (this.textResourceConfigurationService.getValue(resource, 'files.encoding') === UTF8_with_bom) { + preferredEncoding = UTF8; // if we did not detect UTF 8 BOM before, this can only be UTF 8 then + } + + return this.getEncodingForResource(resource, preferredEncoding); + } + + private async getEncodingForResource(resource: URI, preferredEncoding?: string): Promise { + let fileEncoding: string; + + const override = this.getEncodingOverride(resource); + if (override) { + fileEncoding = override; // encoding override always wins + } else if (preferredEncoding) { + fileEncoding = preferredEncoding; // preferred encoding comes second + } else { + fileEncoding = this.textResourceConfigurationService.getValue(resource, 'files.encoding'); // and last we check for settings + } + + if (fileEncoding !== UTF8) { + if (!fileEncoding || !(await encodingExists(fileEncoding))) { + fileEncoding = UTF8; // the default is UTF-8 + } + } + + return fileEncoding; + } + + private getEncodingOverride(resource: URI): string | undefined { + if (this.encodingOverrides && this.encodingOverrides.length) { + for (const override of this.encodingOverrides) { + + // check if the resource is child of encoding override path + if (override.parent && this.uriIdentityService.extUri.isEqualOrParent(resource, override.parent)) { + return override.encoding; + } + + // check if the resource extension is equal to encoding override + if (override.extension && extname(resource) === `.${override.extension}`) { + return override.encoding; + } + } + } + + return undefined; + } +} diff --git a/src/vs/workbench/services/textfile/common/encoding.ts b/src/vs/workbench/services/textfile/common/encoding.ts new file mode 100644 index 00000000000..c4f80a86739 --- /dev/null +++ b/src/vs/workbench/services/textfile/common/encoding.ts @@ -0,0 +1,689 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Readable, ReadableStream, newWriteableStream } from 'vs/base/common/stream'; +import { VSBuffer, VSBufferReadable, VSBufferReadableStream } from 'vs/base/common/buffer'; + +export const UTF8 = 'utf8'; +export const UTF8_with_bom = 'utf8bom'; +export const UTF16be = 'utf16be'; +export const UTF16le = 'utf16le'; + +export type UTF_ENCODING = typeof UTF8 | typeof UTF8_with_bom | typeof UTF16be | typeof UTF16le; + +export function isUTFEncoding(encoding: string): encoding is UTF_ENCODING { + return [UTF8, UTF8_with_bom, UTF16be, UTF16le].some(utfEncoding => utfEncoding === encoding); +} + +export const UTF16be_BOM = [0xFE, 0xFF]; +export const UTF16le_BOM = [0xFF, 0xFE]; +export const UTF8_BOM = [0xEF, 0xBB, 0xBF]; + +const ZERO_BYTE_DETECTION_BUFFER_MAX_LEN = 512; // number of bytes to look at to decide about a file being binary or not +const NO_ENCODING_GUESS_MIN_BYTES = 512; // when not auto guessing the encoding, small number of bytes are enough +const AUTO_ENCODING_GUESS_MIN_BYTES = 512 * 8; // with auto guessing we want a lot more content to be read for guessing +const AUTO_ENCODING_GUESS_MAX_BYTES = 512 * 128; // set an upper limit for the number of bytes we pass on to jschardet + +export interface IDecodeStreamOptions { + guessEncoding: boolean; + minBytesRequiredForDetection?: number; + + overwriteEncoding(detectedEncoding: string | null): Promise; +} + +export interface IDecodeStreamResult { + stream: ReadableStream; + detected: IDetectedEncodingResult; +} + +export interface IDecoderStream { + write(buffer: Uint8Array): string; + end(): string | undefined; +} + +class DecoderStream implements IDecoderStream { + + /** + * This stream will only load iconv-lite lazily if the encoding + * is not UTF-8. This ensures that for most common cases we do + * not pay the price of loading the module from disk. + * + * We still need to be careful when converting UTF-8 to a string + * though because we read the file in chunks of Buffer and thus + * need to decode it via TextDecoder helper that is available + * in browser and node.js environments. + */ + static async create(encoding: string): Promise { + let decoder: IDecoderStream | undefined = undefined; + if (encoding !== UTF8) { + const iconv = await import('iconv-lite-umd'); + decoder = iconv.getDecoder(toNodeEncoding(encoding)); + } else { + const utf8TextDecoder = new TextDecoder(); + decoder = { + write(buffer: Uint8Array): string { + return utf8TextDecoder.decode(buffer, { + // Signal to TextDecoder that potentially more data is coming + // and that we are calling `decode` in the end to consume any + // remainders + stream: true + }); + }, + + end(): string | undefined { + return utf8TextDecoder.decode(); + } + }; + } + + return new DecoderStream(decoder); + } + + private constructor(private iconvLiteDecoder: IDecoderStream) { } + + write(buffer: Uint8Array): string { + return this.iconvLiteDecoder.write(buffer); + } + + end(): string | undefined { + return this.iconvLiteDecoder.end(); + } +} + +export function toDecodeStream(source: VSBufferReadableStream, options: IDecodeStreamOptions): Promise { + const minBytesRequiredForDetection = options.minBytesRequiredForDetection ?? options.guessEncoding ? AUTO_ENCODING_GUESS_MIN_BYTES : NO_ENCODING_GUESS_MIN_BYTES; + + return new Promise((resolve, reject) => { + const target = newWriteableStream(strings => strings.join('')); + + const bufferedChunks: VSBuffer[] = []; + let bytesBuffered = 0; + + let decoder: IDecoderStream | undefined = undefined; + + const createDecoder = async () => { + try { + + // detect encoding from buffer + const detected = await detectEncodingFromBuffer({ + buffer: VSBuffer.concat(bufferedChunks), + bytesRead: bytesBuffered + }, options.guessEncoding); + + // ensure to respect overwrite of encoding + detected.encoding = await options.overwriteEncoding(detected.encoding); + + // decode and write buffered content + decoder = await DecoderStream.create(detected.encoding); + const decoded = decoder.write(VSBuffer.concat(bufferedChunks).buffer); + target.write(decoded); + + bufferedChunks.length = 0; + bytesBuffered = 0; + + // signal to the outside our detected encoding and final decoder stream + resolve({ + stream: target, + detected + }); + } catch (error) { + reject(error); + } + }; + + // Stream error: forward to target + source.on('error', error => target.error(error)); + + // Stream data + source.on('data', async chunk => { + + // if the decoder is ready, we just write directly + if (decoder) { + target.write(decoder.write(chunk.buffer)); + } + + // otherwise we need to buffer the data until the stream is ready + else { + bufferedChunks.push(chunk); + bytesBuffered += chunk.byteLength; + + // buffered enough data for encoding detection, create stream + if (bytesBuffered >= minBytesRequiredForDetection) { + + // pause stream here until the decoder is ready + source.pause(); + + await createDecoder(); + + // resume stream now that decoder is ready but + // outside of this stack to reduce recursion + setTimeout(() => source.resume()); + } + } + }); + + // Stream end + source.on('end', async () => { + + // 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 + if (!decoder) { + await createDecoder(); + } + + // end the target with the remainders of the decoder + target.end(decoder?.end()); + }); + }); +} + +export async function toEncodeReadable(readable: Readable, encoding: string, options?: { addBOM?: boolean }): Promise { + const iconv = await import('iconv-lite-umd'); + const encoder = iconv.getEncoder(toNodeEncoding(encoding), options); + + let bytesWritten = false; + let done = false; + + return { + read() { + if (done) { + return null; + } + + const chunk = readable.read(); + if (typeof chunk !== 'string') { + done = true; + + // If we are instructed to add a BOM but we detect that no + // bytes have been written, we must ensure to return the BOM + // ourselves so that we comply with the contract. + if (!bytesWritten && options?.addBOM) { + switch (encoding) { + case UTF8: + case UTF8_with_bom: + return VSBuffer.wrap(Uint8Array.from(UTF8_BOM)); + case UTF16be: + return VSBuffer.wrap(Uint8Array.from(UTF16be_BOM)); + case UTF16le: + return VSBuffer.wrap(Uint8Array.from(UTF16le_BOM)); + } + } + + const leftovers = encoder.end(); + if (leftovers && leftovers.length > 0) { + bytesWritten = true; + + return VSBuffer.wrap(leftovers); + } + + return null; + } + + bytesWritten = true; + + return VSBuffer.wrap(encoder.write(chunk)); + } + }; +} + +export async function encodingExists(encoding: string): Promise { + const iconv = await import('iconv-lite-umd'); + + return iconv.encodingExists(toNodeEncoding(encoding)); +} + +export 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: VSBuffer | null, bytesRead: number): typeof UTF8_with_bom | typeof UTF16le | typeof UTF16be | null { + if (!buffer || bytesRead < UTF16be_BOM.length) { + return null; + } + + const b0 = buffer.readUInt8(0); + const b1 = buffer.readUInt8(1); + + // UTF-16 BE + if (b0 === UTF16be_BOM[0] && b1 === UTF16be_BOM[1]) { + return UTF16be; + } + + // UTF-16 LE + if (b0 === UTF16le_BOM[0] && b1 === UTF16le_BOM[1]) { + return UTF16le; + } + + if (bytesRead < UTF8_BOM.length) { + return null; + } + + const b2 = buffer.readUInt8(2); + + // UTF-8 + if (b0 === UTF8_BOM[0] && b1 === UTF8_BOM[1] && b2 === UTF8_BOM[2]) { + return UTF8_with_bom; + } + + return null; +} + +// we explicitly ignore a specific set of encodings from auto guessing +// - ASCII: we never want this encoding (most UTF-8 files would happily detect as +// ASCII files and then you could not type non-ASCII characters anymore) +// - UTF-16: we have our own detection logic for UTF-16 +// - UTF-32: we do not support this encoding in VSCode +const IGNORE_ENCODINGS = ['ascii', 'utf-16', 'utf-32']; + +/** + * Guesses the encoding from buffer. + */ +async function guessEncodingByBuffer(buffer: VSBuffer): Promise { + const jschardet = await import('jschardet'); + + // ensure to limit buffer for guessing due to https://github.com/aadsm/jschardet/issues/53 + const limitedBuffer = buffer.slice(0, AUTO_ENCODING_GUESS_MAX_BYTES); + + // before guessing jschardet calls toString('binary') on input if it is a Buffer, + // since we are using it inside browser environment as well we do conversion ourselves + // https://github.com/aadsm/jschardet/blob/v2.1.1/src/index.js#L36-L40 + const binaryString = encodeLatin1(limitedBuffer.buffer); + + const guessed = jschardet.detect(binaryString); + if (!guessed || !guessed.encoding) { + return null; + } + + const enc = guessed.encoding.toLowerCase(); + if (0 <= IGNORE_ENCODINGS.indexOf(enc)) { + return null; // see comment above why we ignore some encodings + } + + return toIconvLiteEncoding(guessed.encoding); +} + +const JSCHARDET_TO_ICONV_ENCODINGS: { [name: string]: string } = { + 'ibm866': 'cp866', + 'big5': 'cp950' +}; + +function toIconvLiteEncoding(encodingName: string): string { + const normalizedEncodingName = encodingName.replace(/[^a-zA-Z0-9]/g, '').toLowerCase(); + const mapped = JSCHARDET_TO_ICONV_ENCODINGS[normalizedEncodingName]; + + return mapped || normalizedEncodingName; +} + +function encodeLatin1(buffer: Uint8Array): string { + let result = ''; + for (let i = 0; i < buffer.length; i++) { + result += String.fromCharCode(buffer[i]); + } + + return result; +} + +/** + * 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: VSBuffer | 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.readUInt8(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 }; +} + +export const SUPPORTED_ENCODINGS: { [encoding: string]: { labelLong: string; labelShort: string; order: number; encodeOnly?: boolean; alias?: string } } = { + utf8: { + labelLong: 'UTF-8', + labelShort: 'UTF-8', + order: 1, + alias: 'utf8bom' + }, + utf8bom: { + labelLong: 'UTF-8 with BOM', + labelShort: 'UTF-8 with BOM', + encodeOnly: true, + order: 2, + alias: 'utf8' + }, + utf16le: { + labelLong: 'UTF-16 LE', + labelShort: 'UTF-16 LE', + order: 3 + }, + utf16be: { + labelLong: 'UTF-16 BE', + labelShort: 'UTF-16 BE', + order: 4 + }, + windows1252: { + labelLong: 'Western (Windows 1252)', + labelShort: 'Windows 1252', + order: 5 + }, + iso88591: { + labelLong: 'Western (ISO 8859-1)', + labelShort: 'ISO 8859-1', + order: 6 + }, + iso88593: { + labelLong: 'Western (ISO 8859-3)', + labelShort: 'ISO 8859-3', + order: 7 + }, + iso885915: { + labelLong: 'Western (ISO 8859-15)', + labelShort: 'ISO 8859-15', + order: 8 + }, + macroman: { + labelLong: 'Western (Mac Roman)', + labelShort: 'Mac Roman', + order: 9 + }, + cp437: { + labelLong: 'DOS (CP 437)', + labelShort: 'CP437', + order: 10 + }, + windows1256: { + labelLong: 'Arabic (Windows 1256)', + labelShort: 'Windows 1256', + order: 11 + }, + iso88596: { + labelLong: 'Arabic (ISO 8859-6)', + labelShort: 'ISO 8859-6', + order: 12 + }, + windows1257: { + labelLong: 'Baltic (Windows 1257)', + labelShort: 'Windows 1257', + order: 13 + }, + iso88594: { + labelLong: 'Baltic (ISO 8859-4)', + labelShort: 'ISO 8859-4', + order: 14 + }, + iso885914: { + labelLong: 'Celtic (ISO 8859-14)', + labelShort: 'ISO 8859-14', + order: 15 + }, + windows1250: { + labelLong: 'Central European (Windows 1250)', + labelShort: 'Windows 1250', + order: 16 + }, + iso88592: { + labelLong: 'Central European (ISO 8859-2)', + labelShort: 'ISO 8859-2', + order: 17 + }, + cp852: { + labelLong: 'Central European (CP 852)', + labelShort: 'CP 852', + order: 18 + }, + windows1251: { + labelLong: 'Cyrillic (Windows 1251)', + labelShort: 'Windows 1251', + order: 19 + }, + cp866: { + labelLong: 'Cyrillic (CP 866)', + labelShort: 'CP 866', + order: 20 + }, + iso88595: { + labelLong: 'Cyrillic (ISO 8859-5)', + labelShort: 'ISO 8859-5', + order: 21 + }, + koi8r: { + labelLong: 'Cyrillic (KOI8-R)', + labelShort: 'KOI8-R', + order: 22 + }, + koi8u: { + labelLong: 'Cyrillic (KOI8-U)', + labelShort: 'KOI8-U', + order: 23 + }, + iso885913: { + labelLong: 'Estonian (ISO 8859-13)', + labelShort: 'ISO 8859-13', + order: 24 + }, + windows1253: { + labelLong: 'Greek (Windows 1253)', + labelShort: 'Windows 1253', + order: 25 + }, + iso88597: { + labelLong: 'Greek (ISO 8859-7)', + labelShort: 'ISO 8859-7', + order: 26 + }, + windows1255: { + labelLong: 'Hebrew (Windows 1255)', + labelShort: 'Windows 1255', + order: 27 + }, + iso88598: { + labelLong: 'Hebrew (ISO 8859-8)', + labelShort: 'ISO 8859-8', + order: 28 + }, + iso885910: { + labelLong: 'Nordic (ISO 8859-10)', + labelShort: 'ISO 8859-10', + order: 29 + }, + iso885916: { + labelLong: 'Romanian (ISO 8859-16)', + labelShort: 'ISO 8859-16', + order: 30 + }, + windows1254: { + labelLong: 'Turkish (Windows 1254)', + labelShort: 'Windows 1254', + order: 31 + }, + iso88599: { + labelLong: 'Turkish (ISO 8859-9)', + labelShort: 'ISO 8859-9', + order: 32 + }, + windows1258: { + labelLong: 'Vietnamese (Windows 1258)', + labelShort: 'Windows 1258', + order: 33 + }, + gbk: { + labelLong: 'Simplified Chinese (GBK)', + labelShort: 'GBK', + order: 34 + }, + gb18030: { + labelLong: 'Simplified Chinese (GB18030)', + labelShort: 'GB18030', + order: 35 + }, + cp950: { + labelLong: 'Traditional Chinese (Big5)', + labelShort: 'Big5', + order: 36 + }, + big5hkscs: { + labelLong: 'Traditional Chinese (Big5-HKSCS)', + labelShort: 'Big5-HKSCS', + order: 37 + }, + shiftjis: { + labelLong: 'Japanese (Shift JIS)', + labelShort: 'Shift JIS', + order: 38 + }, + eucjp: { + labelLong: 'Japanese (EUC-JP)', + labelShort: 'EUC-JP', + order: 39 + }, + euckr: { + labelLong: 'Korean (EUC-KR)', + labelShort: 'EUC-KR', + order: 40 + }, + windows874: { + labelLong: 'Thai (Windows 874)', + labelShort: 'Windows 874', + order: 41 + }, + iso885911: { + labelLong: 'Latin/Thai (ISO 8859-11)', + labelShort: 'ISO 8859-11', + order: 42 + }, + koi8ru: { + labelLong: 'Cyrillic (KOI8-RU)', + labelShort: 'KOI8-RU', + order: 43 + }, + koi8t: { + labelLong: 'Tajik (KOI8-T)', + labelShort: 'KOI8-T', + order: 44 + }, + gb2312: { + labelLong: 'Simplified Chinese (GB 2312)', + labelShort: 'GB 2312', + order: 45 + }, + cp865: { + labelLong: 'Nordic DOS (CP 865)', + labelShort: 'CP 865', + order: 46 + }, + cp850: { + labelLong: 'Western European DOS (CP 850)', + labelShort: 'CP 850', + order: 47 + } +}; diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 17eadf382ca..1bb7ac6975d 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -19,7 +19,6 @@ import { ITextBufferFactory, ITextModel } from 'vs/editor/common/model'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ILogService } from 'vs/platform/log/common/log'; import { basename } from 'vs/base/common/path'; -import { onUnexpectedError } from 'vs/base/common/errors'; import { IWorkingCopyService, IWorkingCopyBackup } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -76,6 +75,9 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private bufferSavedVersionId: number | undefined; private ignoreDirtyOnModelContentChange = false; + private static readonly UNDO_REDO_SAVE_PARTICIPANTS_AUTO_SAVE_THROTTLE_THRESHOLD = 500; + private lastModelContentChangeFromUndoRedo: number | undefined = undefined; + private lastResolvedFileStat: IFileStatWithMetadata | undefined; private readonly saveSequentializer = new TaskSequentializer(); @@ -287,6 +289,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } private async loadFromBackup(backup: IResolvedBackup, options?: ITextFileLoadOptions): Promise { + const preferredEncoding = await this.textFileService.encoding.getPreferredWriteEncoding(this.resource, this.preferredEncoding); // Load with backup this.loadFromContent({ @@ -297,7 +300,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil size: backup.meta ? backup.meta.size : 0, etag: backup.meta ? backup.meta.etag : ETAG_DISABLED, // etag disabled if unknown! value: backup.value, - encoding: this.textFileService.encoding.getPreferredWriteEncoding(this.resource, this.preferredEncoding).encoding + encoding: preferredEncoding.encoding }, options, true /* from backup */); // Restore orphaned flag based on state @@ -450,16 +453,23 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // where `value` was captured in the content change listener closure scope. // Content Change - this._register(model.onDidChangeContent(() => this.onModelContentChanged(model))); + this._register(model.onDidChangeContent(e => this.onModelContentChanged(model, e.isUndoing || e.isRedoing))); } - private onModelContentChanged(model: ITextModel): void { + private onModelContentChanged(model: ITextModel, isUndoingOrRedoing: boolean): void { this.logService.trace(`[text file model] onModelContentChanged() - enter`, this.resource.toString(true)); // In any case increment the version id because it tracks the textual content state of the model at all times this.versionId++; this.logService.trace(`[text file model] onModelContentChanged() - new versionId ${this.versionId}`, this.resource.toString(true)); + // Remember when the user changed the model through a undo/redo operation. + // We need this information to throttle save participants to fix + // https://github.com/microsoft/vscode/issues/102542 + if (isUndoingOrRedoing) { + this.lastModelContentChangeFromUndoRedo = Date.now(); + } + // We mark check for a dirty-state change upon model content change, unless: // - explicitly instructed to ignore it (e.g. from model.load()) // - the model is readonly (in that case we never assume the change was done by the user) @@ -639,7 +649,31 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Save participants can also be skipped through API. if (this.isResolved() && !options.skipSaveParticipants) { try { - await this.textFileService.files.runSaveParticipants(this, { reason: options.reason ?? SaveReason.EXPLICIT }, saveCancellation.token); + + // Measure the time it took from the last undo/redo operation to this save. If this + // time is below `UNDO_REDO_SAVE_PARTICIPANTS_THROTTLE_THRESHOLD`, we make sure to + // delay the save participant for the remaining time if the reason is auto save. + // + // This fixes the following issue: + // - the user has configured auto save with delay of 100ms or shorter + // - the user has a save participant enabled that modifies the file on each save + // - the user types into the file and the file gets saved + // - the user triggers undo operation + // - this will undo the save participant change but trigger the save participant right after + // - the user has no chance to undo over the save participant + // + // Reported as: https://github.com/microsoft/vscode/issues/102542 + if (options.reason === SaveReason.AUTO && typeof this.lastModelContentChangeFromUndoRedo === 'number') { + const timeFromUndoRedoToSave = Date.now() - this.lastModelContentChangeFromUndoRedo; + if (timeFromUndoRedoToSave < TextFileEditorModel.UNDO_REDO_SAVE_PARTICIPANTS_AUTO_SAVE_THROTTLE_THRESHOLD) { + await timeout(TextFileEditorModel.UNDO_REDO_SAVE_PARTICIPANTS_AUTO_SAVE_THROTTLE_THRESHOLD - timeFromUndoRedoToSave); + } + } + + // Run save participants unless save was cancelled meanwhile + if (!saveCancellation.token.isCancellationRequested) { + await this.textFileService.files.runSaveParticipants(this, { reason: options.reason ?? SaveReason.EXPLICIT }, saveCancellation.token); + } } catch (error) { this.logService.error(`[text file model] runSaveParticipants(${versionId}) - resulted in an error: ${error.toString()}`, this.resource.toString(true)); } @@ -680,7 +714,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // - the model is not in orphan mode (because in that case we know the file does not exist on disk) // - the model version did not change due to save participants running if (options.force && !this.dirty && !this.inOrphanMode && options.reason === SaveReason.EXPLICIT && versionId === this.versionId) { - return this.doTouch(this.versionId, options.reason); + return this.doTouch(this.versionId, options); } // update versionId with its new value (if pre-save changes happened) @@ -694,15 +728,15 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // participant triggering this.logService.trace(`[text file model] doSave(${versionId}) - before write()`, this.resource.toString(true)); const lastResolvedFileStat = assertIsDefined(this.lastResolvedFileStat); - const textFileEdiorModel = this; + const textFileEditorModel = this; return this.saveSequentializer.setPending(versionId, (async () => { try { - const stat = await this.textFileService.write(lastResolvedFileStat.resource, textFileEdiorModel.createSnapshot(), { + const stat = await this.textFileService.write(lastResolvedFileStat.resource, textFileEditorModel.createSnapshot(), { overwriteReadonly: options.overwriteReadonly, overwriteEncoding: options.overwriteEncoding, mtime: lastResolvedFileStat.mtime, encoding: this.getEncoding(), - etag: (options.ignoreModifiedSince || !this.filesConfigurationService.preventSaveConflicts(lastResolvedFileStat.resource, textFileEdiorModel.getMode())) ? ETAG_DISABLED : lastResolvedFileStat.etag, + etag: (options.ignoreModifiedSince || !this.filesConfigurationService.preventSaveConflicts(lastResolvedFileStat.resource, textFileEditorModel.getMode())) ? ETAG_DISABLED : lastResolvedFileStat.etag, writeElevated: options.writeElevated }); @@ -755,11 +789,16 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this._onDidSaveError.fire(); } - private doTouch(this: TextFileEditorModel & IResolvedTextFileEditorModel, versionId: number, reason: SaveReason): Promise { + private doTouch(this: TextFileEditorModel & IResolvedTextFileEditorModel, versionId: number, options: ITextFileSaveOptions): Promise { const lastResolvedFileStat = assertIsDefined(this.lastResolvedFileStat); return this.saveSequentializer.setPending(versionId, (async () => { try { + + // Write contents to touch: we used to simply update the mtime of the file + // but this lead to weird results, either for external watchers or even for + // us where we thought the file has changed on disk. As such, we let the OS + // handle the increment of mtime and not deal with it ourselves. const stat = await this.textFileService.write(lastResolvedFileStat.resource, this.createSnapshot(), { mtime: lastResolvedFileStat.mtime, encoding: this.getEncoding(), @@ -770,9 +809,17 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.updateLastResolvedFileStat(stat); // Emit File Saved Event - this._onDidSave.fire(reason); + this._onDidSave.fire(options.reason ?? SaveReason.EXPLICIT); } catch (error) { - onUnexpectedError(error); // just log any error but do not notify the user since the file was not dirty + + // In any case of an error, we mark the model as dirty to prevent data loss + // It could be possible that the touch corrupted the file on disk (e.g. when + // an error happened after truncating the file) and as such we want to preserve + // the model contents to prevent data loss + this.setDirty(true); + + // Notify user to handle this save error + this.handleSaveError(error, versionId, options); } })()); } diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index be50b353deb..316dbdf5510 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -5,7 +5,7 @@ import { localize } from 'vs/nls'; import { toErrorMessage } from 'vs/base/common/errorMessage'; -import { Emitter } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { dispose, IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; @@ -23,9 +23,10 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IWorkingCopyFileService, WorkingCopyFileEvent } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { ITextSnapshot, ITextBufferFactory } from 'vs/editor/common/model'; -import { joinPath, isEqualOrParent, isEqual } from 'vs/base/common/resources'; +import { joinPath, extUri } from 'vs/base/common/resources'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; export class TextFileEditorModelManager extends Disposable implements ITextFileEditorModelManager { @@ -62,7 +63,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE return { onSaveError(error: Error, model: ITextFileEditorModel): void { - notificationService.error(localize('genericSaveError', "Failed to save '{0}': {1}", model.name, toErrorMessage(error, false))); + notificationService.error(localize({ key: 'genericSaveError', comment: ['{0} is the resource that failed to save and {1} the error message'] }, "Failed to save '{0}': {1}", model.name, toErrorMessage(error, false))); } }; })(); @@ -76,7 +77,8 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE @IInstantiationService private readonly instantiationService: IInstantiationService, @IFileService private readonly fileService: IFileService, @INotificationService private readonly notificationService: INotificationService, - @IWorkingCopyFileService private readonly workingCopyFileService: IWorkingCopyFileService + @IWorkingCopyFileService private readonly workingCopyFileService: IWorkingCopyFileService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService ) { super(); @@ -133,49 +135,58 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE private onWillRunWorkingCopyFileOperation(e: WorkingCopyFileEvent): void { // Move / Copy: remember models to restore after the operation - const source = e.source; - if (source && (e.operation === FileOperation.COPY || e.operation === FileOperation.MOVE)) { - - // find all models that related to either source or target (can be many if resource is a folder) - const sourceModels: TextFileEditorModel[] = []; - const targetModels: TextFileEditorModel[] = []; - for (const model of this.models) { - const resource = model.resource; - - if (isEqualOrParent(resource, e.target, false /* do not ignorecase, see https://github.com/Microsoft/vscode/issues/56384 */)) { - targetModels.push(model); - } - - if (isEqualOrParent(resource, source)) { - sourceModels.push(model); - } - } - - // remember each source model to load again after move is done - // with optional content to restore if it was dirty + if (e.operation === FileOperation.MOVE || e.operation === FileOperation.COPY) { const modelsToRestore: { source: URI, target: URI, snapshot?: ITextSnapshot; mode?: string; encoding?: string; }[] = []; - for (const sourceModel of sourceModels) { - const sourceModelResource = sourceModel.resource; - // If the source is the actual model, just use target as new resource - let targetModelResource: URI; - if (isEqual(sourceModelResource, e.source)) { - targetModelResource = e.target; + for (const { source, target } of e.files) { + if (source) { + if (this.uriIdentityService.extUri.isEqual(source, target)) { + continue; // ignore if resources are considered equal + } + + // find all models that related to either source or target (can be many if resource is a folder) + const sourceModels: TextFileEditorModel[] = []; + const targetModels: TextFileEditorModel[] = []; + for (const model of this.models) { + const resource = model.resource; + + if (extUri.isEqualOrParent(resource, target)) { + // EXPLICITLY do not ignorecase, see https://github.com/Microsoft/vscode/issues/56384 + targetModels.push(model); + } + + if (this.uriIdentityService.extUri.isEqualOrParent(resource, source)) { + sourceModels.push(model); + } + } + + // remember each source model to load again after move is done + // with optional content to restore if it was dirty + for (const sourceModel of sourceModels) { + const sourceModelResource = sourceModel.resource; + + // If the source is the actual model, just use target as new resource + let targetModelResource: URI; + if (this.uriIdentityService.extUri.isEqual(sourceModelResource, source)) { + targetModelResource = target; + } + + // Otherwise a parent folder of the source is being moved, so we need + // to compute the target resource based on that + else { + targetModelResource = joinPath(target, sourceModelResource.path.substr(source.path.length + 1)); + } + + modelsToRestore.push({ + source: sourceModelResource, + target: targetModelResource, + mode: sourceModel.getMode(), + encoding: sourceModel.getEncoding(), + snapshot: sourceModel.isDirty() ? sourceModel.createSnapshot() : undefined + }); + } + } - - // Otherwise a parent folder of the source is being moved, so we need - // to compute the target resource based on that - else { - targetModelResource = joinPath(e.target, sourceModelResource.path.substr(source.path.length + 1)); - } - - modelsToRestore.push({ - source: sourceModelResource, - target: targetModelResource, - mode: sourceModel.getMode(), - encoding: sourceModel.getEncoding(), - snapshot: sourceModel.isDirty() ? sourceModel.createSnapshot() : undefined - }); } this.mapCorrelationIdToModelsToRestore.set(e.correlationId, modelsToRestore); @@ -185,13 +196,15 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE private onDidFailWorkingCopyFileOperation(e: WorkingCopyFileEvent): void { // Move / Copy: restore dirty flag on models to restore that were dirty - if ((e.operation === FileOperation.COPY || e.operation === FileOperation.MOVE)) { + if ((e.operation === FileOperation.MOVE || e.operation === FileOperation.COPY)) { const modelsToRestore = this.mapCorrelationIdToModelsToRestore.get(e.correlationId); if (modelsToRestore) { this.mapCorrelationIdToModelsToRestore.delete(e.correlationId); modelsToRestore.forEach(model => { - // snapshot presence means this model used to be dirty + // snapshot presence means this model used to be dirty and so we restore that + // flag. we do NOT have to restore the content because the model was only soft + // reverted and did not loose its original dirty contents. if (model.snapshot) { this.get(model.source)?.setDirty(true); } @@ -201,40 +214,55 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE } private onDidRunWorkingCopyFileOperation(e: WorkingCopyFileEvent): void { + switch (e.operation) { - // Move / Copy: restore models that were loaded before the operation took place - if ((e.operation === FileOperation.COPY || e.operation === FileOperation.MOVE)) { - e.waitUntil((async () => { - const modelsToRestore = this.mapCorrelationIdToModelsToRestore.get(e.correlationId); - if (modelsToRestore) { - this.mapCorrelationIdToModelsToRestore.delete(e.correlationId); - - await Promise.all(modelsToRestore.map(async modelToRestore => { - - // restore the model, forcing a reload. this is important because - // we know the file has changed on disk after the move and the - // model might have still existed with the previous state. this - // ensures we are not tracking a stale state. - const restoredModel = await this.resolve(modelToRestore.target, { reload: { async: false }, encoding: modelToRestore.encoding }); - - // restore previous dirty content if any and ensure to mark the model as dirty - let textBufferFactory: ITextBufferFactory | undefined = undefined; - if (modelToRestore.snapshot) { - textBufferFactory = createTextBufferFactoryFromSnapshot(modelToRestore.snapshot); + // Create: Revert existing models + case FileOperation.CREATE: + e.waitUntil((async () => { + for (const { target } of e.files) { + const model = this.get(target); + if (model && !model.isDisposed()) { + await model.revert(); } + } + })()); + break; - // restore previous mode only if the mode is now unspecified - let preferredMode: string | undefined = undefined; - if (restoredModel.getMode() === PLAINTEXT_MODE_ID && modelToRestore.mode !== PLAINTEXT_MODE_ID) { - preferredMode = modelToRestore.mode; - } + // Move/Copy: restore models that were loaded before the operation took place + case FileOperation.MOVE: + case FileOperation.COPY: + e.waitUntil((async () => { + const modelsToRestore = this.mapCorrelationIdToModelsToRestore.get(e.correlationId); + if (modelsToRestore) { + this.mapCorrelationIdToModelsToRestore.delete(e.correlationId); - if (textBufferFactory || preferredMode) { - restoredModel.updateTextEditorModel(textBufferFactory, preferredMode); - } - })); - } - })()); + await Promise.all(modelsToRestore.map(async modelToRestore => { + + // restore the model, forcing a reload. this is important because + // we know the file has changed on disk after the move and the + // model might have still existed with the previous state. this + // ensures we are not tracking a stale state. + const restoredModel = await this.resolve(modelToRestore.target, { reload: { async: false }, encoding: modelToRestore.encoding }); + + // restore previous dirty content if any and ensure to mark the model as dirty + let textBufferFactory: ITextBufferFactory | undefined = undefined; + if (modelToRestore.snapshot) { + textBufferFactory = createTextBufferFactoryFromSnapshot(modelToRestore.snapshot); + } + + // restore previous mode only if the mode is now unspecified + let preferredMode: string | undefined = undefined; + if (restoredModel.getMode() === PLAINTEXT_MODE_ID && modelToRestore.mode !== PLAINTEXT_MODE_ID) { + preferredMode = modelToRestore.mode; + } + + if (textBufferFactory || preferredMode) { + restoredModel.updateTextEditorModel(textBufferFactory, preferredMode); + } + })); + } + })()); + break; } } @@ -347,7 +375,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE this.mapResourceToModelListeners.set(model.resource, modelListeners); } - add(resource: URI, model: TextFileEditorModel): void { + protected add(resource: URI, model: TextFileEditorModel): void { const knownModel = this.mapResourceToModel.get(resource); if (knownModel === model) { return; // already cached @@ -364,7 +392,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE this.mapResourceToDisposeListener.set(resource, model.onDispose(() => this.remove(resource))); } - remove(resource: URI): void { + protected remove(resource: URI): void { this.mapResourceToModel.delete(resource); const disposeListener = this.mapResourceToDisposeListener.get(resource); @@ -401,32 +429,52 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE this.mapResourceToPendingModelLoaders.clear(); // dispose the dispose listeners - this.mapResourceToDisposeListener.forEach(l => l.dispose()); + this.mapResourceToDisposeListener.forEach(listener => listener.dispose()); this.mapResourceToDisposeListener.clear(); // dispose the model change listeners - this.mapResourceToModelListeners.forEach(l => l.dispose()); + this.mapResourceToModelListeners.forEach(listener => listener.dispose()); this.mapResourceToModelListeners.clear(); } - disposeModel(model: TextFileEditorModel): void { - if (!model) { - return; // we need data! + canDispose(model: TextFileEditorModel): true | Promise { + + // quick return if model already disposed or not dirty and not loading + if ( + model.isDisposed() || + (!this.mapResourceToPendingModelLoaders.has(model.resource) && !model.isDirty()) + ) { + return true; } - if (model.isDisposed()) { - return; // already disposed - } - - if (this.mapResourceToPendingModelLoaders.has(model.resource)) { - return; // not yet loaded + // promise based return in all other cases + return this.doCanDispose(model); + } + + private async doCanDispose(model: TextFileEditorModel): Promise { + + // pending model load: wait for the load to finish before trying again + const pendingModelLoad = this.mapResourceToPendingModelLoaders.get(model.resource); + if (pendingModelLoad) { + try { + await pendingModelLoad; + } catch (error) { + // ignore any error + } + + return this.canDispose(model); } + // dirty model: we do not allow to dispose dirty models to prevent + // data loss cases. dirty models can only be disposed when they are + // either saved or reverted if (model.isDirty()) { - return; // not saved + await Event.toPromise(model.onDidChangeDirty); + + return this.canDispose(model); } - model.dispose(); + return true; } dispose(): void { diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 99154e4353a..d974bea6d5e 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { Event, IWaitUntil } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IEncodingSupport, IModeSupport, ISaveOptions, IRevertOptions, SaveReason } from 'vs/workbench/common/editor'; import { IBaseStatWithMetadata, IFileStatWithMetadata, IReadFileOptions, IWriteFileOptions, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; @@ -13,7 +13,6 @@ import { ITextEditorModel } from 'vs/editor/common/services/resolverService'; import { ITextBufferFactory, ITextModel, ITextSnapshot } from 'vs/editor/common/model'; import { VSBuffer, VSBufferReadable } from 'vs/base/common/buffer'; import { isUndefinedOrNull } from 'vs/base/common/types'; -import { isNative } from 'vs/base/common/platform'; import { IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IUntitledTextEditorModelManager } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -21,13 +20,9 @@ import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; export const ITextFileService = createDecorator('textFileService'); -export interface TextFileCreateEvent extends IWaitUntil { - readonly resource: URI; -} - export interface ITextFileService extends IDisposable { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * Access to the manager of text file editor models providing further @@ -95,11 +90,6 @@ export interface ITextFileService extends IDisposable { */ write(resource: URI, value: string | ITextSnapshot, options?: IWriteTextFileOptions): Promise; - /** - * An event that is fired after a text file has been created. - */ - readonly onDidCreateTextFile: Event; - /** * Create a file. If the file exists it will be overwritten with the contents if * the options enable to overwrite. @@ -171,7 +161,7 @@ export class TextFileOperationError extends FileOperationError { } export interface IResourceEncodings { - getPreferredWriteEncoding(resource: URI, preferredEncoding?: string): IResourceEncoding; + getPreferredWriteEncoding(resource: URI, preferredEncoding?: string): Promise; } export interface IResourceEncoding { @@ -327,8 +317,14 @@ export interface ITextFileEditorModelManager { readonly onDidSave: Event; readonly onDidRevert: Event; + /** + * Access to all text file editor models in memory. + */ readonly models: ITextFileEditorModel[]; + /** + * Allows to configure the error handler that is called on save errors. + */ saveErrorHandler: ISaveErrorHandler; /** @@ -352,7 +348,12 @@ export interface ITextFileEditorModelManager { */ runSaveParticipants(model: ITextFileEditorModel, context: { reason: SaveReason; }, token: CancellationToken): Promise - disposeModel(model: ITextFileEditorModel): void; + /** + * Waits for the model to be ready to be disposed. There may be conditions + * under which the model cannot be disposed, e.g. when it is dirty. Once the + * promise is settled, it is safe to dispose the model. + */ + canDispose(model: ITextFileEditorModel): true | Promise; } export interface ITextFileSaveOptions extends ISaveOptions { @@ -408,6 +409,7 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport readonly onDidChangeContent: Event; readonly onDidSaveError: Event; readonly onDidChangeOrphaned: Event; + readonly onDidChangeEncoding: Event; hasState(state: TextFileEditorModelState): boolean; @@ -504,258 +506,3 @@ export function toBufferOrReadable(value: string | ITextSnapshot | undefined): V return new TextSnapshotReadable(value); } - -export const SUPPORTED_ENCODINGS: { [encoding: string]: { labelLong: string; labelShort: string; order: number; encodeOnly?: boolean; alias?: string } } = - - // Desktop - isNative ? - { - utf8: { - labelLong: 'UTF-8', - labelShort: 'UTF-8', - order: 1, - alias: 'utf8bom' - }, - utf8bom: { - labelLong: 'UTF-8 with BOM', - labelShort: 'UTF-8 with BOM', - encodeOnly: true, - order: 2, - alias: 'utf8' - }, - utf16le: { - labelLong: 'UTF-16 LE', - labelShort: 'UTF-16 LE', - order: 3 - }, - utf16be: { - labelLong: 'UTF-16 BE', - labelShort: 'UTF-16 BE', - order: 4 - }, - windows1252: { - labelLong: 'Western (Windows 1252)', - labelShort: 'Windows 1252', - order: 5 - }, - iso88591: { - labelLong: 'Western (ISO 8859-1)', - labelShort: 'ISO 8859-1', - order: 6 - }, - iso88593: { - labelLong: 'Western (ISO 8859-3)', - labelShort: 'ISO 8859-3', - order: 7 - }, - iso885915: { - labelLong: 'Western (ISO 8859-15)', - labelShort: 'ISO 8859-15', - order: 8 - }, - macroman: { - labelLong: 'Western (Mac Roman)', - labelShort: 'Mac Roman', - order: 9 - }, - cp437: { - labelLong: 'DOS (CP 437)', - labelShort: 'CP437', - order: 10 - }, - windows1256: { - labelLong: 'Arabic (Windows 1256)', - labelShort: 'Windows 1256', - order: 11 - }, - iso88596: { - labelLong: 'Arabic (ISO 8859-6)', - labelShort: 'ISO 8859-6', - order: 12 - }, - windows1257: { - labelLong: 'Baltic (Windows 1257)', - labelShort: 'Windows 1257', - order: 13 - }, - iso88594: { - labelLong: 'Baltic (ISO 8859-4)', - labelShort: 'ISO 8859-4', - order: 14 - }, - iso885914: { - labelLong: 'Celtic (ISO 8859-14)', - labelShort: 'ISO 8859-14', - order: 15 - }, - windows1250: { - labelLong: 'Central European (Windows 1250)', - labelShort: 'Windows 1250', - order: 16 - }, - iso88592: { - labelLong: 'Central European (ISO 8859-2)', - labelShort: 'ISO 8859-2', - order: 17 - }, - cp852: { - labelLong: 'Central European (CP 852)', - labelShort: 'CP 852', - order: 18 - }, - windows1251: { - labelLong: 'Cyrillic (Windows 1251)', - labelShort: 'Windows 1251', - order: 19 - }, - cp866: { - labelLong: 'Cyrillic (CP 866)', - labelShort: 'CP 866', - order: 20 - }, - iso88595: { - labelLong: 'Cyrillic (ISO 8859-5)', - labelShort: 'ISO 8859-5', - order: 21 - }, - koi8r: { - labelLong: 'Cyrillic (KOI8-R)', - labelShort: 'KOI8-R', - order: 22 - }, - koi8u: { - labelLong: 'Cyrillic (KOI8-U)', - labelShort: 'KOI8-U', - order: 23 - }, - iso885913: { - labelLong: 'Estonian (ISO 8859-13)', - labelShort: 'ISO 8859-13', - order: 24 - }, - windows1253: { - labelLong: 'Greek (Windows 1253)', - labelShort: 'Windows 1253', - order: 25 - }, - iso88597: { - labelLong: 'Greek (ISO 8859-7)', - labelShort: 'ISO 8859-7', - order: 26 - }, - windows1255: { - labelLong: 'Hebrew (Windows 1255)', - labelShort: 'Windows 1255', - order: 27 - }, - iso88598: { - labelLong: 'Hebrew (ISO 8859-8)', - labelShort: 'ISO 8859-8', - order: 28 - }, - iso885910: { - labelLong: 'Nordic (ISO 8859-10)', - labelShort: 'ISO 8859-10', - order: 29 - }, - iso885916: { - labelLong: 'Romanian (ISO 8859-16)', - labelShort: 'ISO 8859-16', - order: 30 - }, - windows1254: { - labelLong: 'Turkish (Windows 1254)', - labelShort: 'Windows 1254', - order: 31 - }, - iso88599: { - labelLong: 'Turkish (ISO 8859-9)', - labelShort: 'ISO 8859-9', - order: 32 - }, - windows1258: { - labelLong: 'Vietnamese (Windows 1258)', - labelShort: 'Windows 1258', - order: 33 - }, - gbk: { - labelLong: 'Simplified Chinese (GBK)', - labelShort: 'GBK', - order: 34 - }, - gb18030: { - labelLong: 'Simplified Chinese (GB18030)', - labelShort: 'GB18030', - order: 35 - }, - cp950: { - labelLong: 'Traditional Chinese (Big5)', - labelShort: 'Big5', - order: 36 - }, - big5hkscs: { - labelLong: 'Traditional Chinese (Big5-HKSCS)', - labelShort: 'Big5-HKSCS', - order: 37 - }, - shiftjis: { - labelLong: 'Japanese (Shift JIS)', - labelShort: 'Shift JIS', - order: 38 - }, - eucjp: { - labelLong: 'Japanese (EUC-JP)', - labelShort: 'EUC-JP', - order: 39 - }, - euckr: { - labelLong: 'Korean (EUC-KR)', - labelShort: 'EUC-KR', - order: 40 - }, - windows874: { - labelLong: 'Thai (Windows 874)', - labelShort: 'Windows 874', - order: 41 - }, - iso885911: { - labelLong: 'Latin/Thai (ISO 8859-11)', - labelShort: 'ISO 8859-11', - order: 42 - }, - koi8ru: { - labelLong: 'Cyrillic (KOI8-RU)', - labelShort: 'KOI8-RU', - order: 43 - }, - koi8t: { - labelLong: 'Tajik (KOI8-T)', - labelShort: 'KOI8-T', - order: 44 - }, - gb2312: { - labelLong: 'Simplified Chinese (GB 2312)', - labelShort: 'GB 2312', - order: 45 - }, - cp865: { - labelLong: 'Nordic DOS (CP 865)', - labelShort: 'CP 865', - order: 46 - }, - cp850: { - labelLong: 'Western European DOS (CP 850)', - labelShort: 'CP 850', - order: 47 - } - } : - - // Web (https://github.com/microsoft/vscode/issues/79275) - { - utf8: { - labelLong: 'UTF-8', - labelShort: 'UTF-8', - order: 1, - alias: 'utf8bom' - } - }; diff --git a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts index 0c2d8de960a..05dfa205077 100644 --- a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts +++ b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts @@ -3,30 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { tmpdir } from 'os'; import { localize } from 'vs/nls'; import { AbstractTextFileService } from 'vs/workbench/services/textfile/browser/textFileService'; -import { ITextFileService, ITextFileStreamContent, ITextFileContent, IResourceEncodings, IResourceEncoding, IReadTextFileOptions, IWriteTextFileOptions, stringToSnapshot, TextFileOperationResult, TextFileOperationError } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, ITextFileStreamContent, ITextFileContent, IReadTextFileOptions, IWriteTextFileOptions } from 'vs/workbench/services/textfile/common/textfiles'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { URI } from 'vs/base/common/uri'; -import { IFileStatWithMetadata, ICreateFileOptions, FileOperationError, FileOperationResult, IFileStreamContent, IFileService } from 'vs/platform/files/common/files'; +import { IFileStatWithMetadata, FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; import { Schemas } from 'vs/base/common/network'; -import { exists, stat, chmod, rimraf, MAX_FILE_SIZE, MAX_HEAP_SIZE } from 'vs/base/node/pfs'; +import { stat, chmod, MAX_FILE_SIZE, MAX_HEAP_SIZE } from 'vs/base/node/pfs'; import { join, dirname } from 'vs/base/common/path'; import { isMacintosh } from 'vs/base/common/platform'; import { IProductService } from 'vs/platform/product/common/productService'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { UTF8, UTF8_with_bom, UTF16be, UTF16le, encodingExists, encodeStream, UTF8_BOM, toDecodeStream, IDecodeStreamResult, detectEncodingByBOMFromBuffer, isUTFEncoding } from 'vs/base/node/encoding'; -import { WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces'; -import { joinPath, extname, isEqualOrParent } from 'vs/base/common/resources'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { VSBufferReadable, bufferToStream } from 'vs/base/common/buffer'; -import { Readable } from 'stream'; -import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; +import { UTF8, UTF8_with_bom } from 'vs/workbench/services/textfile/common/encoding'; import { ITextSnapshot } from 'vs/editor/common/model'; -import { nodeReadableToString, streamToNodeReadable, nodeStreamToVSBufferReadable } from 'vs/base/node/stream'; import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -40,6 +30,8 @@ import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; import { ILogService } from 'vs/platform/log/common/log'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { IModeService } from 'vs/editor/common/services/modeService'; export class NativeTextFileService extends AbstractTextFileService { @@ -59,77 +51,27 @@ export class NativeTextFileService extends AbstractTextFileService { @ICodeEditorService codeEditorService: ICodeEditorService, @IPathService pathService: IPathService, @IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService, - @ILogService private readonly logService: ILogService + @ILogService private readonly logService: ILogService, + @IUriIdentityService uriIdentityService: IUriIdentityService, + @IModeService modeService: IModeService ) { - super(fileService, untitledTextEditorService, lifecycleService, instantiationService, modelService, environmentService, dialogService, fileDialogService, textResourceConfigurationService, filesConfigurationService, textModelService, codeEditorService, pathService, workingCopyFileService); - } - - private _encoding: EncodingOracle | undefined; - get encoding(): EncodingOracle { - if (!this._encoding) { - this._encoding = this._register(this.instantiationService.createInstance(EncodingOracle)); - } - - return this._encoding; + super(fileService, untitledTextEditorService, lifecycleService, instantiationService, modelService, environmentService, dialogService, fileDialogService, textResourceConfigurationService, filesConfigurationService, textModelService, codeEditorService, pathService, workingCopyFileService, uriIdentityService, modeService); } async read(resource: URI, options?: IReadTextFileOptions): Promise { - const [bufferStream, decoder] = await this.doRead(resource, { - ...options, - // optimization: since we know that the caller does not - // care about buffering, we indicate this to the reader. - // this reduces all the overhead the buffered reading - // has (open, read, close) if the provider supports - // unbuffered reading. - preferUnbuffered: true - }); - return { - ...bufferStream, - encoding: decoder.detected.encoding || UTF8, - value: await nodeReadableToString(decoder.stream) - }; + // ensure size & memory limits + options = this.ensureLimits(options); + + return super.read(resource, options); } async readStream(resource: URI, options?: IReadTextFileOptions): Promise { - const [bufferStream, decoder] = await this.doRead(resource, options); - return { - ...bufferStream, - encoding: decoder.detected.encoding || UTF8, - value: await createTextBufferFactoryFromStream(decoder.stream) - }; - } - - private async doRead(resource: URI, options?: IReadTextFileOptions & { preferUnbuffered?: boolean }): Promise<[IFileStreamContent, IDecodeStreamResult]> { - - // ensure limits + // ensure size & memory limits options = this.ensureLimits(options); - // read stream raw (either buffered or unbuffered) - let bufferStream: IFileStreamContent; - if (options.preferUnbuffered) { - const content = await this.fileService.readFile(resource, options); - bufferStream = { - ...content, - value: bufferToStream(content.value) - }; - } else { - bufferStream = await this.fileService.readFileStream(resource, options); - } - - // read through encoding library - const decoder = await toDecodeStream(streamToNodeReadable(bufferStream.value), { - guessEncoding: options?.autoGuessEncoding || this.textResourceConfigurationService.getValue(resource, 'files.autoGuessEncoding'), - overwriteEncoding: detectedEncoding => this.encoding.getReadEncoding(resource, options, detectedEncoding) - }); - - // validate binary - if (options?.acceptTextOnly && decoder.detected.seemsBinary) { - throw new TextFileOperationError(localize('fileBinaryError', "File seems to be binary and cannot be opened as text"), TextFileOperationResult.FILE_IS_BINARY, options); - } - - return [bufferStream, decoder]; + return super.readStream(resource, options); } private ensureLimits(options?: IReadTextFileOptions): IReadTextFileOptions { @@ -153,31 +95,22 @@ export class NativeTextFileService extends AbstractTextFileService { } if (typeof ensuredLimits.memory !== 'number') { - ensuredLimits.memory = Math.max(typeof this.environmentService.args['max-memory'] === 'string' ? parseInt(this.environmentService.args['max-memory']) * 1024 * 1024 || 0 : 0, MAX_HEAP_SIZE); + const maxMemory = this.environmentService.args['max-memory']; + ensuredLimits.memory = Math.max( + typeof maxMemory === 'string' + ? parseInt(maxMemory) * 1024 * 1024 || 0 + : 0, MAX_HEAP_SIZE + ); } return ensuredOptions; } - protected async doCreate(resource: URI, value?: string, options?: ICreateFileOptions): Promise { - - // check for encoding - const { encoding, addBOM } = await this.encoding.getWriteEncoding(resource); - - // return to parent when encoding is standard - if (encoding === UTF8 && !addBOM) { - return super.doCreate(resource, value, options); - } - - // otherwise create with encoding - return this.fileService.createFile(resource, this.getEncodedReadable(value || '', encoding, addBOM), options); - } - async write(resource: URI, value: string | ITextSnapshot, options?: IWriteTextFileOptions): Promise { // check for overwriteReadonly property (only supported for local file://) try { - if (options?.overwriteReadonly && resource.scheme === Schemas.file && await exists(resource.fsPath)) { + if (options?.overwriteReadonly && resource.scheme === Schemas.file && await this.fileService.exists(resource)) { const fileStat = await stat(resource.fsPath); // try to change mode to writeable @@ -193,19 +126,7 @@ export class NativeTextFileService extends AbstractTextFileService { } try { - - // check for encoding - const { encoding, addBOM } = await this.encoding.getWriteEncoding(resource, options); - - // return to parent when encoding is standard - if (encoding === UTF8 && !addBOM) { - return await super.write(resource, value, options); - } - - // otherwise save with encoding - else { - return await this.fileService.writeFile(resource, this.getEncodedReadable(value, encoding, addBOM), options); - } + return super.write(resource, value, options); } catch (error) { // In case of permission denied, we need to check for readonly @@ -229,57 +150,26 @@ export class NativeTextFileService extends AbstractTextFileService { } } - private getEncodedReadable(value: string | ITextSnapshot, encoding: string, addBOM: boolean): VSBufferReadable { - const readable = this.snapshotToNodeReadable(typeof value === 'string' ? stringToSnapshot(value) : value); - const encoder = encodeStream(encoding, { addBOM }); - - const encodedReadable = readable.pipe(encoder); - - return nodeStreamToVSBufferReadable(encodedReadable, addBOM && isUTFEncoding(encoding) ? { encoding } : undefined); - } - - private snapshotToNodeReadable(snapshot: ITextSnapshot): Readable { - return new Readable({ - read: function () { - try { - let chunk: string | null = null; - let canPush = true; - - // Push all chunks as long as we can push and as long as - // the underlying snapshot returns strings to us - while (canPush && typeof (chunk = snapshot.read()) === 'string') { - canPush = this.push(chunk); - } - - // Signal EOS by pushing NULL - if (typeof chunk !== 'string') { - this.push(null); - } - } catch (error) { - this.emit('error', error); - } - }, - encoding: UTF8 // very important, so that strings are passed around and not buffers! - }); - } - private async writeElevated(resource: URI, value: string | ITextSnapshot, options?: IWriteTextFileOptions): Promise { // write into a tmp file first - const tmpPath = join(tmpdir(), `code-elevated-${Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 6)}`); + const source = URI.file(join(this.environmentService.userDataPath, `code-elevated-${Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 6)}`)); const { encoding, addBOM } = await this.encoding.getWriteEncoding(resource, options); - await this.write(URI.file(tmpPath), value, { encoding: encoding === UTF8 && addBOM ? UTF8_with_bom : encoding }); + try { + await this.write(source, value, { encoding: encoding === UTF8 && addBOM ? UTF8_with_bom : encoding }); - // sudo prompt copy - await this.sudoPromptCopy(tmpPath, resource.fsPath, options); + // sudo prompt copy + await this.sudoPromptCopy(source, resource, options); + } finally { - // clean up - await rimraf(tmpPath); + // clean up + await this.fileService.del(source); + } return this.fileService.resolve(resource, { resolveMetadata: true }); } - private async sudoPromptCopy(source: string, target: string, options?: IWriteTextFileOptions): Promise { + private async sudoPromptCopy(source: URI, target: URI, options?: IWriteTextFileOptions): Promise { // load sudo-prompt module lazy const sudoPrompt = await import('sudo-prompt'); @@ -295,7 +185,7 @@ export class NativeTextFileService extends AbstractTextFileService { sudoCommand.push('--file-chmod'); } - sudoCommand.push('--file-write', `"${source}"`, `"${target}"`); + sudoCommand.push('--file-write', `"${source.fsPath}"`, `"${target.fsPath}"`); sudoPrompt.exec(sudoCommand.join(' '), promptOptions, (error: string, stdout: string, stderr: string) => { if (stdout) { @@ -316,148 +206,4 @@ export class NativeTextFileService extends AbstractTextFileService { } } -export interface IEncodingOverride { - parent?: URI; - extension?: string; - encoding: string; -} - -export class EncodingOracle extends Disposable implements IResourceEncodings { - protected encodingOverrides: IEncodingOverride[]; - - constructor( - @ITextResourceConfigurationService private textResourceConfigurationService: ITextResourceConfigurationService, - @IEnvironmentService private environmentService: IEnvironmentService, - @IWorkspaceContextService private contextService: IWorkspaceContextService, - @IFileService private fileService: IFileService - ) { - super(); - - this.encodingOverrides = this.getDefaultEncodingOverrides(); - - this.registerListeners(); - } - - private registerListeners(): void { - - // Workspace Folder Change - this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.encodingOverrides = this.getDefaultEncodingOverrides())); - } - - private getDefaultEncodingOverrides(): IEncodingOverride[] { - const defaultEncodingOverrides: IEncodingOverride[] = []; - - // Global settings - defaultEncodingOverrides.push({ parent: this.environmentService.userRoamingDataHome, encoding: UTF8 }); - - // Workspace files (via extension and via untitled workspaces location) - defaultEncodingOverrides.push({ extension: WORKSPACE_EXTENSION, encoding: UTF8 }); - defaultEncodingOverrides.push({ parent: this.environmentService.untitledWorkspacesHome, encoding: UTF8 }); - - // Folder Settings - this.contextService.getWorkspace().folders.forEach(folder => { - defaultEncodingOverrides.push({ parent: joinPath(folder.uri, '.vscode'), encoding: UTF8 }); - }); - - return defaultEncodingOverrides; - } - - async getWriteEncoding(resource: URI, options?: IWriteTextFileOptions): Promise<{ encoding: string, addBOM: boolean }> { - const { encoding, hasBOM } = this.getPreferredWriteEncoding(resource, options ? options.encoding : undefined); - - // Some encodings come with a BOM automatically - if (hasBOM) { - return { encoding, addBOM: true }; - } - - // Ensure that we preserve an existing BOM if found for UTF8 - // unless we are instructed to overwrite the encoding - const overwriteEncoding = options?.overwriteEncoding; - if (!overwriteEncoding && encoding === UTF8) { - try { - const buffer = (await this.fileService.readFile(resource, { length: UTF8_BOM.length })).value; - if (detectEncodingByBOMFromBuffer(buffer, buffer.byteLength) === UTF8_with_bom) { - return { encoding, addBOM: true }; - } - } catch (error) { - // ignore - file might not exist - } - } - - return { encoding, addBOM: false }; - } - - getPreferredWriteEncoding(resource: URI, preferredEncoding?: string): IResourceEncoding { - const resourceEncoding = this.getEncodingForResource(resource, preferredEncoding); - - return { - encoding: resourceEncoding, - hasBOM: resourceEncoding === UTF16be || resourceEncoding === UTF16le || resourceEncoding === UTF8_with_bom // enforce BOM for certain encodings - }; - } - - getReadEncoding(resource: URI, options: IReadTextFileOptions | undefined, detectedEncoding: string | null): string { - let preferredEncoding: string | undefined; - - // Encoding passed in as option - if (options?.encoding) { - if (detectedEncoding === UTF8_with_bom && options.encoding === UTF8) { - preferredEncoding = UTF8_with_bom; // indicate the file has BOM if we are to resolve with UTF 8 - } else { - preferredEncoding = options.encoding; // give passed in encoding highest priority - } - } - - // Encoding detected - else if (detectedEncoding) { - preferredEncoding = detectedEncoding; - } - - // Encoding configured - else if (this.textResourceConfigurationService.getValue(resource, 'files.encoding') === UTF8_with_bom) { - preferredEncoding = UTF8; // if we did not detect UTF 8 BOM before, this can only be UTF 8 then - } - - return this.getEncodingForResource(resource, preferredEncoding); - } - - private getEncodingForResource(resource: URI, preferredEncoding?: string): string { - let fileEncoding: string; - - const override = this.getEncodingOverride(resource); - if (override) { - fileEncoding = override; // encoding override always wins - } else if (preferredEncoding) { - fileEncoding = preferredEncoding; // preferred encoding comes second - } else { - fileEncoding = this.textResourceConfigurationService.getValue(resource, 'files.encoding'); // and last we check for settings - } - - if (!fileEncoding || !encodingExists(fileEncoding)) { - fileEncoding = UTF8; // the default is UTF 8 - } - - return fileEncoding; - } - - private getEncodingOverride(resource: URI): string | undefined { - if (this.encodingOverrides && this.encodingOverrides.length) { - for (const override of this.encodingOverrides) { - - // check if the resource is child of encoding override path - if (override.parent && isEqualOrParent(resource, override.parent)) { - return override.encoding; - } - - // check if the resource extension is equal to encoding override - if (override.extension && extname(resource) === `.${override.extension}`) { - return override.encoding; - } - } - } - - return undefined; - } -} - registerSingleton(ITextFileService, NativeTextFileService); diff --git a/src/vs/workbench/services/textfile/test/browser/browserTextFileService.io.test.ts b/src/vs/workbench/services/textfile/test/browser/browserTextFileService.io.test.ts new file mode 100644 index 00000000000..031c61de33f --- /dev/null +++ b/src/vs/workbench/services/textfile/test/browser/browserTextFileService.io.test.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 { workbenchInstantiationService, TestInMemoryFileSystemProvider, TestBrowserTextFileServiceWithEncodingOverrides } from 'vs/workbench/test/browser/workbenchTestServices'; +import { NullLogService } from 'vs/platform/log/common/log'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { Schemas } from 'vs/base/common/network'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { IFileService, IStat } from 'vs/platform/files/common/files'; +import { URI } from 'vs/base/common/uri'; +import { join } from 'vs/base/common/path'; +import { UTF16le, detectEncodingByBOMFromBuffer, UTF8_with_bom, UTF16be, toCanonicalName } from 'vs/workbench/services/textfile/common/encoding'; +import { VSBuffer } from 'vs/base/common/buffer'; +import files from 'vs/workbench/services/textfile/test/browser/fixtures/files'; +import createSuite from 'vs/workbench/services/textfile/test/common/textFileService.io.test'; +import { isWeb } from 'vs/base/common/platform'; +import { IWorkingCopyFileService, WorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; +import { TestWorkingCopyService } from 'vs/workbench/test/common/workbenchTestServices'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; + +// optimization: we don't need to run this suite in native environment, +// because we have nativeTextFileService.io.test.ts for it, +// so our tests run faster +if (isWeb) { + suite('Files - BrowserTextFileService i/o', function () { + const disposables = new DisposableStore(); + + let service: ITextFileService; + let fileProvider: TestInMemoryFileSystemProvider; + const testDir = 'test'; + + createSuite({ + setup: async () => { + const instantiationService = workbenchInstantiationService(); + + const logService = new NullLogService(); + const fileService = new FileService(logService); + + fileProvider = new TestInMemoryFileSystemProvider(); + disposables.add(fileService.registerProvider(Schemas.file, fileProvider)); + disposables.add(fileProvider); + + const collection = new ServiceCollection(); + collection.set(IFileService, fileService); + + collection.set(IWorkingCopyFileService, new WorkingCopyFileService(fileService, new TestWorkingCopyService(), instantiationService, new UriIdentityService(fileService))); + + service = instantiationService.createChild(collection).createInstance(TestBrowserTextFileServiceWithEncodingOverrides); + + await fileProvider.mkdir(URI.file(testDir)); + for (let fileName in files) { + await fileProvider.writeFile( + URI.file(join(testDir, fileName)), + files[fileName], + { create: true, overwrite: false } + ); + } + + return { service, testDir }; + }, + + teardown: async () => { + (service.files).dispose(); + + disposables.clear(); + }, + + exists, + stat, + readFile, + detectEncodingByBOM + }); + + async function exists(fsPath: string): Promise { + try { + await fileProvider.readFile(URI.file(fsPath)); + return true; + } + catch (e) { + return false; + } + } + + async function readFile(fsPath: string): Promise; + async function readFile(fsPath: string, encoding: string): Promise; + async function readFile(fsPath: string, encoding?: string): Promise { + const file = await fileProvider.readFile(URI.file(fsPath)); + + if (!encoding) { + return VSBuffer.wrap(file); + } + + return new TextDecoder(toCanonicalName(encoding)).decode(file); + } + + async function stat(fsPath: string): Promise { + return fileProvider.stat(URI.file(fsPath)); + } + + async function detectEncodingByBOM(fsPath: string): Promise { + try { + const buffer = await readFile(fsPath); + + return detectEncodingByBOMFromBuffer(buffer.slice(0, 3), 3); + } catch (error) { + return null; // ignore errors (like file not found) + } + } + }); +} diff --git a/src/vs/workbench/services/textfile/test/browser/fixtures/files.ts b/src/vs/workbench/services/textfile/test/browser/fixtures/files.ts new file mode 100644 index 00000000000..f3d0c5d6cd2 --- /dev/null +++ b/src/vs/workbench/services/textfile/test/browser/fixtures/files.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 { range } from 'vs/base/common/arrays'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { UTF8_BOM } from 'vs/workbench/services/textfile/common/encoding'; + +const fixtures: { [filename: string]: Uint8Array } = {}; + +export default fixtures; + +// encoded from 'АБВГДЕЖЗИЙКЛМНОПРСĐĸĐŖĐ¤ĐĨĐĻЧШЊĐĒĐĢĐŦĐ­ĐŽĐ¯Đ°ĐąĐ˛ĐŗĐ´ĐĩĐļСиКĐēĐģĐŧĐŊĐžĐŋŅ€ŅŅ‚ŅƒŅ„Ņ…Ņ†Ņ‡ŅˆŅ‰ŅŠŅ‹ŅŒŅŅŽŅ' +fixtures['some_cyrillic.txt'] = Uint8Array.from([...range(128, 175 + 1), ...range(224, 239 + 1)]); + +// encoded from '中å›Ŋabc' +fixtures['some_gbk.txt'] = Uint8Array.from([214, 208, 185, 250, 97, 98, 99]); + +// encoded from '中文abc' +fixtures['some_big5.txt'] = Uint8Array.from([164, 164, 164, 229, 97, 98, 99]); + +// encoded from '中文abc' +fixtures['some_shiftjis.txt'] = Uint8Array.from([146, 134, 149, 182, 97, 98, 99]); + +// encoded from 'ObjectCount = LoadObjects("Öffentlicher Ordner");\nPrivate = "PersÃļnliche Information"' +fixtures['some_cp1252.txt'] = Uint8Array.from([ + 79, 98, 106, 101, 99, 116, 67, 111, 117, 110, 116, 32, 61, 32, 76, 111, 97, 100, 79, 98, 106, 101, 99, 116, 115, 40, 34, 214, 102, 102, 101, 110, 116, 108, 105, 99, 104, 101, 114, 32, 79, 114, 100, 110, 101, 114, 34, 41, 59, 10, 10, 80, 114, 105, 118, 97, 116, 101, 32, 61, 32, 34, 80, 101, 114, 115, 246, 110, 108, 105, 99, 104, 101, 32, 73, 110, 102, 111, 114, 109, 97, 116, 105, 111, 110, 34, 10 +]); + +// encoded from 'Private = "PersÃļnliche Information"' +fixtures['some_small_cp1252.txt'] = Uint8Array.from([ + 80, 114, 105, 118, 97, 116, 101, 32, 61, 32, 34, 80, 101, 114, 115, 246, 110, 108, 105, 99, 104, 101, 223, 32, 73, 110, 102, 111, 114, 109, 97, 116, 105, 111, 110, 34 +]); + +// encoded from 'This is some UTF 8 with BOM file.' +fixtures['some_utf8_bom.txt'] = Uint8Array.from([ + 239, 187, 191, 84, 104, 105, 115, 32, 105, 115, 32, 115, 111, 109, 101, 32, 85, 84, 70, 32, 56, 32, 119, 105, 116, 104, 32, 66, 79, 77, 32, 102, 105, 108, 101, 46 +]); + +// encoded from 'This is some UTF 16 with BOM file.' +fixtures['some.utf16le'] = Uint8Array.from([ + 255, 254, 84, 0, 104, 0, 105, 0, 115, 0, 32, 0, 105, 0, 115, 0, 32, 0, 115, 0, 111, 0, 109, 0, 101, 0, 32, 0, 85, 0, 84, 0, 70, 0, 32, 0, 49, 0, 54, 0, 32, 0, 119, 0, 105, 0, 116, 0, 104, 0, 32, 0, 66, 0, 79, 0, 77, 0, 32, 0, 102, 0, 105, 0, 108, 0, 101, 0, 46, 0 +]); + +// encoded from 'this is utf-16 be without bÃļm' +fixtures['utf16_be_nobom.txt'] = Uint8Array.from([ + 0, 116, 0, 104, 0, 105, 0, 115, 0, 32, 0, 105, 0, 115, 0, 32, 0, 117, 0, 116, 0, 102, 0, 45, 0, 49, 0, 54, 0, 32, 0, 98, 0, 101, 0, 32, 0, 119, 0, 105, 0, 116, 0, 104, 0, 111, 0, 117, 0, 116, 0, 32, 0, 98, 0, 246, 0, 109 +]); + +// encoded from 'this is utf-16 le without bÃļm' +fixtures['utf16_le_nobom.txt'] = Uint8Array.from([ + 116, 0, 104, 0, 105, 0, 115, 0, 32, 0, 105, 0, 115, 0, 32, 0, 117, 0, 116, 0, 102, 0, 45, 0, 49, 0, 54, 0, 32, 0, 108, 0, 101, 0, 32, 0, 119, 0, 105, 0, 116, 0, 104, 0, 111, 0, 117, 0, 116, 0, 32, 0, 98, 0, 246, 0, 109, 0 +]); + +// encoded from 'Small file' +fixtures['small.txt'] = Uint8Array.from([83, 109, 97, 108, 108, 32, 102, 105, 108, 101]); + +fixtures['binary.txt'] = Uint8Array.from([ + 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 73, 0, 0, 0, 67, 8, 2, 0, 0, 0, 95, 138, 191, 237, 0, 0, 0, 1, 115, 82, 71, 66, 0, 174, 206, 28, 233, 0, 0, 0, 4, 103, 65, 77, 65, 0, 0, 177, 143, 11, 252, 97, 5, 0, 0, 0, 9, 112, 72, 89, 115, 0, 0, 14, 195, 0, 0, 14, 195, 1, 199, 111, 168, 100, 0, 0, 0, 71, 116, 69, 88, 116, 83, 111, 117, 114, 99, 101, 0, 83, 104, 111, 116, 116, 121, 32, 118, 50, 46, 48, 46, 50, 46, 50, 49, 54, 32, 40, 67, 41, 32, 84, 104, 111, 109, 97, 115, 32, 66, 97, 117, 109, 97, 110, 110, 32, 45, 32, 104, 116, 116, 112, 58, 47, 47, 115, 104, 111, 116, 116, 121, 46, 100, 101, 118, 115, 45, 111, 110, 46, 110, 101, 116, 44, 132, 21, 213, 0, 0, 0, 84, 73, 68, 65, 84, 120, 218, 237, 207, 65, 17, 0, 0, 12, 2, 32, 211, 217, 63, 146, 37, 246, 218, 65, 3, 210, 191, 226, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 118, 100, 169, 4, 173, 8, 44, 248, 184, 40, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130 +]); + +fixtures['some_utf16le.css'] = Uint8Array.from([ + 255, 254, 47, 0, 42, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 13, 0, 10, 0, 84, 0, 104, 0, 101, 0, 32, 0, 98, 0, 97, 0, 115, 0, 101, 0, 32, 0, 99, 0, 111, 0, 108, 0, 111, 0, 114, 0, 32, 0, 102, 0, 111, 0, 114, 0, 32, 0, 116, 0, 104, 0, 105, 0, 115, 0, 32, 0, 116, 0, 101, 0, 109, 0, 112, 0, 108, 0, 97, 0, 116, 0, 101, 0, 32, 0, 105, 0, 115, 0, 32, 0, 35, 0, 53, 0, 99, 0, 56, 0, 55, 0, 98, 0, 50, 0, 46, 0, 32, 0, 73, 0, 102, 0, 32, 0, 121, 0, 111, 0, 117, 0, 39, 0, 100, 0, 32, 0, 108, 0, 105, 0, 107, 0, 101, 0, 13, 0, 10, 0, 116, 0, 111, 0, 32, 0, 117, 0, 115, 0, 101, 0, 32, 0, 97, 0, 32, 0, 100, 0, 105, 0, 102, 0, 102, 0, 101, 0, 114, 0, 101, 0, 110, 0, 116, 0, 32, 0, 99, 0, 111, 0, 108, 0, 111, 0, 114, 0, 32, 0, 115, 0, 116, 0, 97, 0, 114, 0, 116, 0, 32, 0, 98, 0, 121, 0, 32, 0, 114, 0, 101, 0, 112, 0, 108, 0, 97, 0, 99, 0, 105, 0, 110, 0, 103, 0, 32, 0, 97, 0, 108, 0, 108, 0, 32, 0, 105, 0, 110, 0, 115, 0, 116, 0, 97, 0, 110, 0, 99, 0, 101, 0, 115, 0, 32, 0, 111, 0, 102, 0, 13, 0, 10, 0, 35, 0, 53, 0, 99, 0, 56, 0, 55, 0, 98, 0, 50, 0, 32, 0, 119, 0, 105, 0, 116, 0, 104, 0, 32, 0, 121, 0, 111, 0, 117, 0, 114, 0, 32, 0, 110, 0, 101, 0, 119, 0, 32, 0, 99, 0, 111, 0, 108, 0, 111, 0, 114, 0, 46, 0, 13, 0, 10, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 45, 0, 42, 0, 47, 0, 13, 0, 10, 0, 98, 0, 111, 0, 100, 0, 121, 0, 13, 0, 10, 0, 123, 0, 13, 0, 10, 0, 32, 0, 32, 0, 32, 0, 32, 0, 98, 0, 97, 0, 99, 0, 107, 0, 103, 0, 114, 0, 111, 0, 117, 0, 110, 0, 100, 0, 45, 0, 99, 0, 111, 0, 108, 0, 111, 0, 114, 0, 58, 0, 32, 0, 35, 0, 53, 0, 99, 0, 56, 0, 55, 0, 98, 0, 50, 0, 59, 0, 13, 0, 10, 0, 32, 0, 32, 0, 32, 0, 32, 0, 102, 0, 111, 0, 110, 0, 116, 0, 45, 0, 115, 0, 105, 0, 122, 0, 101, 0, 58, 0, 32, 0, 46, 0, 55, 0, 53, 0, 101, 0, 109, 0, 59, 0, 13, 0, 10, 0, 32, 0, 32, 0, 32, 0, 32, 0, 102, 0, 111, 0, 110, 0, 116, 0, 45, 0, 102, 0, 97, 0, 109, 0, 105, 0, 108, 0, 121, 0, 58, 0, 32, 0, 83, 0, 101, 0, 103, 0, 111, 0, 101, 0, 32, 0, 85, 0, 73, 0, 44, 0, 32, 0, 86, 0, 101, 0, 114, 0, 100, 0, 97, 0, 110, 0, 97, 0, 44, 0, 32, 0, 72, 0, 101, 0, 108, 0, 118, 0, 101, 0, 116, 0, 105, 0, 99, 0, 97, 0, 44, 0, 32, 0, 83, 0, 97, 0, 110, 0, 115, 0, 45, 0, 83, 0, 101, 0, 114, 0, 105, 0, 102, 0, 59, 0, 13, 0, 10, 0, 32, 0, 32, 0, 32, 0, 32, 0, 109, 0, 97, 0, 114, 0, 103, 0, 105, 0, 110, 0, 58, 0, 32, 0, 56, 0, 112, 0, 120, 0, 59, 0, 13, 0, 10, 0, 32, 0, 32, 0, 32, 0, 32, 0, 112, 0, 97, 0, 100, 0, 100, 0, 105, 0, 110, 0, 103, 0, 58, 0, 32, 0, 48, 0, 59, 0, 13, 0, 10, 0, 32, 0, 32, 0, 32, 0, 32, 0, 99, 0, 111, 0, 108, 0, 111, 0, 114, 0, 58, 0, 32, 0, 35, 0, 54, 0, 57, 0, 54, 0, 57, 0, 54, 0, 57, 0, 59, 0, 13, 0, 10, 0, 125, 0, 13, 0, 10, 0, 13, 0, 10, 0, 104, 0, 49, 0, 44, 0, 32, 0, 104, 0, 50, 0, 44, 0, 32, 0, 104, 0, 51, 0, 44, 0, 32, 0, 104, 0, 52, 0, 44, 0, 32, 0, 104, 0, 53, 0, 44, 0, 32, 0, 104, 0, 54, 0, 13, 0, 10, 0, 123, 0, 13, 0, 10, 0, 32, 0, 32, 0, 32, 0, 32, 0, 99, 0, 111, 0, 108, 0, 111, 0, 114, 0, 58, 0, 32, 0, 35, 0, 48, 0, 48, 0, 48, 0, 59, 0, 13, 0, 10, 0, 32, 0, 32, 0, 32, 0, 32, 0, 102, 0, 111, 0, 110, 0, 116, 0, 45, 0, 115, 0, 105, 0, 122, 0, 101, 0, 58, 0, 32, 0, 52, 0, 48, 0, 112, 0, 120, 0, 59, 0, 13, 0, 10, 0, 32, 0, 32, 0, 32, 0, 32, 0, 109, 0, 97, 0, 114, 0, 103, 0, 105, 0, 110, 0, 58, 0, 32, 0, 48, 0, 112, 0, 120, 0, 59, 0, 13, 0, 10, 0, 125, 0, 13, 0, 10, 0, 13, 0, 10, 0, 116, 0, 101, 0, 120, 0, 116, 0, 97, 0, 114, 0, 101, 0, 97, 0, 32, 0, 13, 0, 10, 0, 123, 0, 13, 0, 10, 0, 32, 0, 32, 0, 32, 0, 102, 0, 111, 0, 110, 0, 116, 0, 45, 0, 102, 0, 97, 0, 109, 0, 105, 0, 108, 0, 121, 0, 58, 0, 32, 0, 67, 0, 111, 0, 110, 0, 115, 0, 111, 0, 108, 0, 97, 0, 115, 0, 13, 0, 10, 0, 125, 0, 13, 0, 10, 0, 13, 0, 10, 0, 35, 0, 114, 0, 101, 0, 115, 0, 117, 0, 108, 0, 116, 0, 115, 0, 32, 0, 13, 0, 10, 0, 123, 0, 13, 0, 10, 0, 32, 0, 32, 0, 32, 0, 32, 0, 109, 0, 97, 0, 114, 0, 103, 0, 105, 0, 110, 0, 45, 0, 116, 0, 111, 0, 112, 0, 58, 0, 32, 0, 50, 0, 101, 0, 109, 0, 59, 0, 13, 0, 10, 0, 32, 0, 32, 0, 32, 0, 32, 0, 109, 0, 97, 0, 114, 0, 103, 0, 105, 0, 110, 0, 45, 0, 108, 0, 101, 0, 102, 0, 116, 0, 58, 0, 32, 0, 50, 0, 101, 0, 109, 0, 59, 0, 13, 0, 10, 0, 32, 0, 32, 0, 32, 0, 32, 0, 99, 0, 111, 0, 108, 0, 111, 0, 114, 0, 58, 0, 32, 0, 98, 0, 108, 0, 97, 0, 99, 0, 107, 0, 59, 0, 13, 0, 10, 0, 32, 0, 32, 0, 32, 0, 32, 0, 102, 0, 111, 0, 110, 0, 116, 0, 45, 0, 115, 0, 105, 0, 122, 0, 101, 0, 58, 0, 32, 0, 109, 0, 101, 0, 100, 0, 105, 0, 117, 0, 109, 0, 59, 0, 13, 0, 10, 0, 125, 0, 13, 0, 10, 0, 13, 0, 10, 0 +]); + +fixtures['index.html'] = Uint8Array.from([ + 60, 33, 68, 79, 67, 84, 89, 80, 69, 32, 104, 116, 109, 108, 62, 10, 60, 104, 116, 109, 108, 62, 10, 60, 104, 101, 97, 100, 32, 105, 100, 61, 39, 104, 101, 97, 100, 73, 68, 39, 62, 10, 32, 32, 32, 32, 60, 109, 101, 116, 97, 32, 104, 116, 116, 112, 45, 101, 113, 117, 105, 118, 61, 34, 88, 45, 85, 65, 45, 67, 111, 109, 112, 97, 116, 105, 98, 108, 101, 34, 32, 99, 111, 110, 116, 101, 110, 116, 61, 34, 73, 69, 61, 101, 100, 103, 101, 34, 32, 47, 62, 10, 32, 32, 32, 32, 60, 116, 105, 116, 108, 101, 62, 83, 116, 114, 97, 100, 97, 32, 60, 47, 116, 105, 116, 108, 101, 62, 10, 32, 32, 32, 32, 60, 108, 105, 110, 107, 32, 104, 114, 101, 102, 61, 34, 115, 105, 116, 101, 46, 99, 115, 115, 34, 32, 114, 101, 108, 61, 34, 115, 116, 121, 108, 101, 115, 104, 101, 101, 116, 34, 32, 116, 121, 112, 101, 61, 34, 116, 101, 120, 116, 47, 99, 115, 115, 34, 32, 47, 62, 10, 32, 32, 32, 32, 60, 115, 99, 114, 105, 112, 116, 32, 115, 114, 99, 61, 34, 106, 113, 117, 101, 114, 121, 45, 49, 46, 52, 46, 49, 46, 106, 115, 34, 62, 60, 47, 115, 99, 114, 105, 112, 116, 62, 10, 32, 32, 32, 32, 60, 115, 99, 114, 105, 112, 116, 32, 115, 114, 99, 61, 34, 46, 46, 47, 99, 111, 109, 112, 105, 108, 101, 114, 47, 100, 116, 114, 101, 101, 46, 106, 115, 34, 32, 116, 121, 112, 101, 61, 34, 116, 101, 120, 116, 47, 106, 97, 118, 97, 115, 99, 114, 105, 112, 116, 34, 62, 60, 47, 115, 99, 114, 105, 112, 116, 62, 10, 32, 32, 32, 32, 60, 115, 99, 114, 105, 112, 116, 32, 115, 114, 99, 61, 34, 46, 46, 47, 99, 111, 109, 112, 105, 108, 101, 114, 47, 116, 121, 112, 101, 115, 99, 114, 105, 112, 116, 46, 106, 115, 34, 32, 116, 121, 112, 101, 61, 34, 116, 101, 120, 116, 47, 106, 97, 118, 97, 115, 99, 114, 105, 112, 116, 34, 62, 60, 47, 115, 99, 114, 105, 112, 116, 62, 10, 32, 32, 32, 32, 60, 115, 99, 114, 105, 112, 116, 32, 116, 121, 112, 101, 61, 34, 116, 101, 120, 116, 47, 106, 97, 118, 97, 115, 99, 114, 105, 112, 116, 34, 62, 10, 10, 32, 32, 32, 32, 47, 47, 32, 67, 111, 109, 112, 105, 108, 101, 32, 115, 116, 114, 97, 100, 97, 32, 115, 111, 117, 114, 99, 101, 32, 105, 110, 116, 111, 32, 114, 101, 115, 117, 108, 116, 105, 110, 103, 32, 106, 97, 118, 97, 115, 99, 114, 105, 112, 116, 10, 32, 32, 32, 32, 102, 117, 110, 99, 116, 105, 111, 110, 32, 99, 111, 109, 112, 105, 108, 101, 40, 112, 114, 111, 103, 44, 32, 108, 105, 98, 84, 101, 120, 116, 41, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 118, 97, 114, 32, 111, 117, 116, 102, 105, 108, 101, 32, 61, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 115, 111, 117, 114, 99, 101, 58, 32, 34, 34, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 87, 114, 105, 116, 101, 58, 32, 102, 117, 110, 99, 116, 105, 111, 110, 32, 40, 115, 41, 32, 123, 32, 116, 104, 105, 115, 46, 115, 111, 117, 114, 99, 101, 32, 43, 61, 32, 115, 59, 32, 125, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 87, 114, 105, 116, 101, 76, 105, 110, 101, 58, 32, 102, 117, 110, 99, 116, 105, 111, 110, 32, 40, 115, 41, 32, 123, 32, 116, 104, 105, 115, 46, 115, 111, 117, 114, 99, 101, 32, 43, 61, 32, 115, 32, 43, 32, 34, 92, 114, 34, 59, 32, 125, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 125, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 118, 97, 114, 32, 112, 97, 114, 115, 101, 69, 114, 114, 111, 114, 115, 32, 61, 32, 91, 93, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 118, 97, 114, 32, 99, 111, 109, 112, 105, 108, 101, 114, 61, 110, 101, 119, 32, 84, 111, 111, 108, 115, 46, 84, 121, 112, 101, 83, 99, 114, 105, 112, 116, 67, 111, 109, 112, 105, 108, 101, 114, 40, 111, 117, 116, 102, 105, 108, 101, 44, 116, 114, 117, 101, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 99, 111, 109, 112, 105, 108, 101, 114, 46, 115, 101, 116, 69, 114, 114, 111, 114, 67, 97, 108, 108, 98, 97, 99, 107, 40, 102, 117, 110, 99, 116, 105, 111, 110, 40, 115, 116, 97, 114, 116, 44, 108, 101, 110, 44, 32, 109, 101, 115, 115, 97, 103, 101, 41, 32, 123, 32, 112, 97, 114, 115, 101, 69, 114, 114, 111, 114, 115, 46, 112, 117, 115, 104, 40, 123, 115, 116, 97, 114, 116, 58, 115, 116, 97, 114, 116, 44, 32, 108, 101, 110, 58, 108, 101, 110, 44, 32, 109, 101, 115, 115, 97, 103, 101, 58, 109, 101, 115, 115, 97, 103, 101, 125, 41, 59, 32, 125, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 99, 111, 109, 112, 105, 108, 101, 114, 46, 97, 100, 100, 85, 110, 105, 116, 40, 108, 105, 98, 84, 101, 120, 116, 44, 34, 108, 105, 98, 46, 116, 115, 34, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 99, 111, 109, 112, 105, 108, 101, 114, 46, 97, 100, 100, 85, 110, 105, 116, 40, 112, 114, 111, 103, 44, 34, 105, 110, 112, 117, 116, 46, 116, 115, 34, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 99, 111, 109, 112, 105, 108, 101, 114, 46, 116, 121, 112, 101, 67, 104, 101, 99, 107, 40, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 99, 111, 109, 112, 105, 108, 101, 114, 46, 101, 109, 105, 116, 40, 41, 59, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 105, 102, 40, 112, 97, 114, 115, 101, 69, 114, 114, 111, 114, 115, 46, 108, 101, 110, 103, 116, 104, 32, 62, 32, 48, 32, 41, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 116, 104, 114, 111, 119, 32, 110, 101, 119, 32, 69, 114, 114, 111, 114, 40, 112, 97, 114, 115, 101, 69, 114, 114, 111, 114, 115, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 125, 10, 10, 9, 119, 104, 105, 108, 101, 40, 111, 117, 116, 102, 105, 108, 101, 46, 115, 111, 117, 114, 99, 101, 91, 48, 93, 32, 61, 61, 32, 39, 47, 39, 32, 38, 38, 32, 111, 117, 116, 102, 105, 108, 101, 46, 115, 111, 117, 114, 99, 101, 91, 49, 93, 32, 61, 61, 32, 39, 47, 39, 32, 38, 38, 32, 111, 117, 116, 102, 105, 108, 101, 46, 115, 111, 117, 114, 99, 101, 91, 50, 93, 32, 61, 61, 32, 39, 32, 39, 41, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 111, 117, 116, 102, 105, 108, 101, 46, 115, 111, 117, 114, 99, 101, 32, 61, 32, 111, 117, 116, 102, 105, 108, 101, 46, 115, 111, 117, 114, 99, 101, 46, 115, 108, 105, 99, 101, 40, 111, 117, 116, 102, 105, 108, 101, 46, 115, 111, 117, 114, 99, 101, 46, 105, 110, 100, 101, 120, 79, 102, 40, 39, 92, 114, 39, 41, 43, 49, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 118, 97, 114, 32, 101, 114, 114, 111, 114, 80, 114, 101, 102, 105, 120, 32, 61, 32, 34, 34, 59, 10, 9, 102, 111, 114, 40, 118, 97, 114, 32, 105, 32, 61, 32, 48, 59, 105, 60, 112, 97, 114, 115, 101, 69, 114, 114, 111, 114, 115, 46, 108, 101, 110, 103, 116, 104, 59, 105, 43, 43, 41, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 101, 114, 114, 111, 114, 80, 114, 101, 102, 105, 120, 32, 43, 61, 32, 34, 47, 47, 32, 69, 114, 114, 111, 114, 58, 32, 40, 34, 32, 43, 32, 112, 97, 114, 115, 101, 69, 114, 114, 111, 114, 115, 91, 105, 93, 46, 115, 116, 97, 114, 116, 32, 43, 32, 34, 44, 34, 32, 43, 32, 112, 97, 114, 115, 101, 69, 114, 114, 111, 114, 115, 91, 105, 93, 46, 108, 101, 110, 32, 43, 32, 34, 41, 32, 34, 32, 43, 32, 112, 97, 114, 115, 101, 69, 114, 114, 111, 114, 115, 91, 105, 93, 46, 109, 101, 115, 115, 97, 103, 101, 32, 43, 32, 34, 92, 114, 34, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 125, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 114, 101, 116, 117, 114, 110, 32, 101, 114, 114, 111, 114, 80, 114, 101, 102, 105, 120, 32, 43, 32, 111, 117, 116, 102, 105, 108, 101, 46, 115, 111, 117, 114, 99, 101, 59, 10, 32, 32, 32, 32, 125, 10, 32, 32, 32, 32, 60, 47, 115, 99, 114, 105, 112, 116, 62, 10, 32, 32, 32, 32, 60, 115, 99, 114, 105, 112, 116, 32, 116, 121, 112, 101, 61, 34, 116, 101, 120, 116, 47, 106, 97, 118, 97, 115, 99, 114, 105, 112, 116, 34, 62, 10, 9, 10, 32, 32, 32, 32, 32, 32, 32, 32, 118, 97, 114, 32, 108, 105, 98, 84, 101, 120, 116, 32, 61, 32, 34, 34, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 36, 46, 103, 101, 116, 40, 34, 46, 46, 47, 99, 111, 109, 112, 105, 108, 101, 114, 47, 108, 105, 98, 46, 116, 115, 34, 44, 32, 102, 117, 110, 99, 116, 105, 111, 110, 40, 110, 101, 119, 76, 105, 98, 84, 101, 120, 116, 41, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 108, 105, 98, 84, 101, 120, 116, 32, 61, 32, 110, 101, 119, 76, 105, 98, 84, 101, 120, 116, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 125, 41, 59, 9, 10, 32, 32, 32, 32, 32, 32, 32, 32, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 32, 101, 120, 101, 99, 117, 116, 101, 32, 116, 104, 101, 32, 106, 97, 118, 97, 115, 99, 114, 105, 112, 116, 32, 105, 110, 32, 116, 104, 101, 32, 99, 111, 109, 112, 105, 108, 101, 100, 79, 117, 116, 112, 117, 116, 32, 112, 97, 110, 101, 10, 32, 32, 32, 32, 32, 32, 32, 32, 102, 117, 110, 99, 116, 105, 111, 110, 32, 101, 120, 101, 99, 117, 116, 101, 40, 41, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 40, 39, 35, 99, 111, 109, 112, 105, 108, 97, 116, 105, 111, 110, 39, 41, 46, 116, 101, 120, 116, 40, 34, 82, 117, 110, 110, 105, 110, 103, 46, 46, 46, 34, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 118, 97, 114, 32, 116, 120, 116, 32, 61, 32, 36, 40, 39, 35, 99, 111, 109, 112, 105, 108, 101, 100, 79, 117, 116, 112, 117, 116, 39, 41, 46, 118, 97, 108, 40, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 118, 97, 114, 32, 114, 101, 115, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 116, 114, 121, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 118, 97, 114, 32, 114, 101, 116, 32, 61, 32, 101, 118, 97, 108, 40, 116, 120, 116, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 114, 101, 115, 32, 61, 32, 34, 82, 97, 110, 32, 115, 117, 99, 99, 101, 115, 115, 102, 117, 108, 108, 121, 33, 34, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 125, 32, 99, 97, 116, 99, 104, 40, 101, 41, 32, 123, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 114, 101, 115, 32, 61, 32, 34, 69, 120, 99, 101, 112, 116, 105, 111, 110, 32, 116, 104, 114, 111, 119, 110, 58, 32, 34, 32, 43, 32, 101, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 40, 39, 35, 99, 111, 109, 112, 105, 108, 97, 116, 105, 111, 110, 39, 41, 46, 116, 101, 120, 116, 40, 83, 116, 114, 105, 110, 103, 40, 114, 101, 115, 41, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 125, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 32, 114, 101, 99, 111, 109, 112, 105, 108, 101, 32, 116, 104, 101, 32, 115, 116, 114, 97, 100, 97, 83, 114, 99, 32, 97, 110, 100, 32, 112, 111, 112, 117, 108, 97, 116, 101, 32, 116, 104, 101, 32, 99, 111, 109, 112, 105, 108, 101, 100, 79, 117, 116, 112, 117, 116, 32, 112, 97, 110, 101, 10, 32, 32, 32, 32, 32, 32, 32, 32, 102, 117, 110, 99, 116, 105, 111, 110, 32, 115, 114, 99, 85, 112, 100, 97, 116, 101, 100, 40, 41, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 118, 97, 114, 32, 110, 101, 119, 84, 101, 120, 116, 32, 61, 32, 36, 40, 39, 35, 115, 116, 114, 97, 100, 97, 83, 114, 99, 39, 41, 46, 118, 97, 108, 40, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 118, 97, 114, 32, 99, 111, 109, 112, 105, 108, 101, 100, 83, 111, 117, 114, 99, 101, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 116, 114, 121, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 99, 111, 109, 112, 105, 108, 101, 100, 83, 111, 117, 114, 99, 101, 32, 61, 32, 99, 111, 109, 112, 105, 108, 101, 40, 110, 101, 119, 84, 101, 120, 116, 44, 32, 108, 105, 98, 84, 101, 120, 116, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 125, 32, 99, 97, 116, 99, 104, 32, 40, 101, 41, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 99, 111, 109, 112, 105, 108, 101, 100, 83, 111, 117, 114, 99, 101, 32, 61, 32, 34, 47, 47, 80, 97, 114, 115, 101, 32, 101, 114, 114, 111, 114, 34, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 102, 111, 114, 40, 118, 97, 114, 32, 105, 32, 105, 110, 32, 101, 41, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 99, 111, 109, 112, 105, 108, 101, 100, 83, 111, 117, 114, 99, 101, 32, 43, 61, 32, 34, 92, 114, 47, 47, 32, 34, 32, 43, 32, 101, 91, 105, 93, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 40, 39, 35, 99, 111, 109, 112, 105, 108, 101, 100, 79, 117, 116, 112, 117, 116, 39, 41, 46, 118, 97, 108, 40, 99, 111, 109, 112, 105, 108, 101, 100, 83, 111, 117, 114, 99, 101, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 125, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 32, 80, 111, 112, 117, 108, 97, 116, 101, 32, 116, 104, 101, 32, 115, 116, 114, 97, 100, 97, 83, 114, 99, 32, 112, 97, 110, 101, 32, 119, 105, 116, 104, 32, 111, 110, 101, 32, 111, 102, 32, 116, 104, 101, 32, 98, 117, 105, 108, 116, 32, 105, 110, 32, 115, 97, 109, 112, 108, 101, 115, 10, 32, 32, 32, 32, 32, 32, 32, 32, 102, 117, 110, 99, 116, 105, 111, 110, 32, 101, 120, 97, 109, 112, 108, 101, 83, 101, 108, 101, 99, 116, 105, 111, 110, 67, 104, 97, 110, 103, 101, 100, 40, 41, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 118, 97, 114, 32, 101, 120, 97, 109, 112, 108, 101, 115, 32, 61, 32, 100, 111, 99, 117, 109, 101, 110, 116, 46, 103, 101, 116, 69, 108, 101, 109, 101, 110, 116, 66, 121, 73, 100, 40, 39, 101, 120, 97, 109, 112, 108, 101, 115, 39, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 118, 97, 114, 32, 115, 101, 108, 101, 99, 116, 101, 100, 69, 120, 97, 109, 112, 108, 101, 32, 61, 32, 101, 120, 97, 109, 112, 108, 101, 115, 46, 111, 112, 116, 105, 111, 110, 115, 91, 101, 120, 97, 109, 112, 108, 101, 115, 46, 115, 101, 108, 101, 99, 116, 101, 100, 73, 110, 100, 101, 120, 93, 46, 118, 97, 108, 117, 101, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 105, 102, 32, 40, 115, 101, 108, 101, 99, 116, 101, 100, 69, 120, 97, 109, 112, 108, 101, 32, 33, 61, 32, 34, 34, 41, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 46, 103, 101, 116, 40, 39, 101, 120, 97, 109, 112, 108, 101, 115, 47, 39, 32, 43, 32, 115, 101, 108, 101, 99, 116, 101, 100, 69, 120, 97, 109, 112, 108, 101, 44, 32, 102, 117, 110, 99, 116, 105, 111, 110, 32, 40, 115, 114, 99, 84, 101, 120, 116, 41, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 36, 40, 39, 35, 115, 116, 114, 97, 100, 97, 83, 114, 99, 39, 41, 46, 118, 97, 108, 40, 115, 114, 99, 84, 101, 120, 116, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 115, 101, 116, 84, 105, 109, 101, 111, 117, 116, 40, 115, 114, 99, 85, 112, 100, 97, 116, 101, 100, 44, 49, 48, 48, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 125, 44, 32, 102, 117, 110, 99, 116, 105, 111, 110, 32, 40, 101, 114, 114, 41, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 99, 111, 110, 115, 111, 108, 101, 46, 108, 111, 103, 40, 101, 114, 114, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 125, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 125, 10, 10, 32, 32, 32, 32, 60, 47, 115, 99, 114, 105, 112, 116, 62, 10, 60, 47, 104, 101, 97, 100, 62, 10, 60, 98, 111, 100, 121, 62, 10, 32, 32, 32, 32, 60, 104, 49, 62, 84, 121, 112, 101, 83, 99, 114, 105, 112, 116, 60, 47, 104, 49, 62, 10, 32, 32, 32, 32, 60, 98, 114, 32, 47, 62, 10, 32, 32, 32, 32, 60, 115, 101, 108, 101, 99, 116, 32, 105, 100, 61, 34, 101, 120, 97, 109, 112, 108, 101, 115, 34, 32, 111, 110, 99, 104, 97, 110, 103, 101, 61, 39, 101, 120, 97, 109, 112, 108, 101, 83, 101, 108, 101, 99, 116, 105, 111, 110, 67, 104, 97, 110, 103, 101, 100, 40, 41, 39, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 60, 111, 112, 116, 105, 111, 110, 32, 118, 97, 108, 117, 101, 61, 34, 34, 62, 83, 101, 108, 101, 99, 116, 46, 46, 46, 60, 47, 111, 112, 116, 105, 111, 110, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 60, 111, 112, 116, 105, 111, 110, 32, 118, 97, 108, 117, 101, 61, 34, 115, 109, 97, 108, 108, 46, 116, 115, 34, 62, 83, 109, 97, 108, 108, 60, 47, 111, 112, 116, 105, 111, 110, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 60, 111, 112, 116, 105, 111, 110, 32, 118, 97, 108, 117, 101, 61, 34, 101, 109, 112, 108, 111, 121, 101, 101, 46, 116, 115, 34, 62, 69, 109, 112, 108, 111, 121, 101, 101, 115, 60, 47, 111, 112, 116, 105, 111, 110, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 60, 111, 112, 116, 105, 111, 110, 32, 118, 97, 108, 117, 101, 61, 34, 99, 111, 110, 119, 97, 121, 46, 116, 115, 34, 62, 67, 111, 110, 119, 97, 121, 32, 71, 97, 109, 101, 32, 111, 102, 32, 76, 105, 102, 101, 60, 47, 111, 112, 116, 105, 111, 110, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 60, 111, 112, 116, 105, 111, 110, 32, 118, 97, 108, 117, 101, 61, 34, 116, 121, 112, 101, 115, 99, 114, 105, 112, 116, 46, 116, 115, 34, 62, 84, 121, 112, 101, 83, 99, 114, 105, 112, 116, 32, 67, 111, 109, 112, 105, 108, 101, 114, 60, 47, 111, 112, 116, 105, 111, 110, 62, 10, 32, 32, 32, 32, 60, 47, 115, 101, 108, 101, 99, 116, 62, 10, 10, 32, 32, 32, 32, 60, 100, 105, 118, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 60, 116, 101, 120, 116, 97, 114, 101, 97, 32, 105, 100, 61, 39, 115, 116, 114, 97, 100, 97, 83, 114, 99, 39, 32, 114, 111, 119, 115, 61, 39, 52, 48, 39, 32, 99, 111, 108, 115, 61, 39, 56, 48, 39, 32, 111, 110, 99, 104, 97, 110, 103, 101, 61, 39, 115, 114, 99, 85, 112, 100, 97, 116, 101, 100, 40, 41, 39, 32, 111, 110, 107, 101, 121, 117, 112, 61, 39, 115, 114, 99, 85, 112, 100, 97, 116, 101, 100, 40, 41, 39, 32, 115, 112, 101, 108, 108, 99, 104, 101, 99, 107, 61, 34, 102, 97, 108, 115, 101, 34, 62, 10, 47, 47, 84, 121, 112, 101, 32, 121, 111, 117, 114, 32, 84, 121, 112, 101, 83, 99, 114, 105, 112, 116, 32, 104, 101, 114, 101, 46, 46, 46, 10, 32, 32, 32, 32, 32, 32, 60, 47, 116, 101, 120, 116, 97, 114, 101, 97, 62, 10, 32, 32, 32, 32, 32, 32, 60, 116, 101, 120, 116, 97, 114, 101, 97, 32, 105, 100, 61, 39, 99, 111, 109, 112, 105, 108, 101, 100, 79, 117, 116, 112, 117, 116, 39, 32, 114, 111, 119, 115, 61, 39, 52, 48, 39, 32, 99, 111, 108, 115, 61, 39, 56, 48, 39, 32, 115, 112, 101, 108, 108, 99, 104, 101, 99, 107, 61, 34, 102, 97, 108, 115, 101, 34, 62, 10, 47, 47, 67, 111, 109, 112, 105, 108, 101, 100, 32, 99, 111, 100, 101, 32, 119, 105, 108, 108, 32, 115, 104, 111, 119, 32, 117, 112, 32, 104, 101, 114, 101, 46, 46, 46, 10, 32, 32, 32, 32, 32, 32, 60, 47, 116, 101, 120, 116, 97, 114, 101, 97, 62, 10, 32, 32, 32, 32, 32, 32, 60, 98, 114, 32, 47, 62, 10, 32, 32, 32, 32, 32, 32, 60, 98, 117, 116, 116, 111, 110, 32, 111, 110, 99, 108, 105, 99, 107, 61, 39, 101, 120, 101, 99, 117, 116, 101, 40, 41, 39, 47, 62, 82, 117, 110, 60, 47, 98, 117, 116, 116, 111, 110, 62, 32, 10, 32, 32, 32, 32, 32, 32, 60, 100, 105, 118, 32, 105, 100, 61, 39, 99, 111, 109, 112, 105, 108, 97, 116, 105, 111, 110, 39, 62, 80, 114, 101, 115, 115, 32, 39, 114, 117, 110, 39, 32, 116, 111, 32, 101, 120, 101, 99, 117, 116, 101, 32, 99, 111, 100, 101, 46, 46, 46, 60, 47, 100, 105, 118, 62, 10, 32, 32, 32, 32, 32, 32, 60, 100, 105, 118, 32, 105, 100, 61, 39, 114, 101, 115, 117, 108, 116, 115, 39, 62, 46, 46, 46, 119, 114, 105, 116, 101, 32, 121, 111, 117, 114, 32, 114, 101, 115, 117, 108, 116, 115, 32, 105, 110, 116, 111, 32, 35, 114, 101, 115, 117, 108, 116, 115, 46, 46, 46, 60, 47, 100, 105, 118, 62, 10, 32, 32, 32, 32, 60, 47, 100, 105, 118, 62, 10, 32, 32, 32, 32, 60, 100, 105, 118, 32, 105, 100, 61, 39, 98, 111, 100, 39, 32, 115, 116, 121, 108, 101, 61, 39, 100, 105, 115, 112, 108, 97, 121, 58, 110, 111, 110, 101, 39, 62, 60, 47, 100, 105, 118, 62, 10, 60, 47, 98, 111, 100, 121, 62, 10, 60, 47, 104, 116, 109, 108, 62, 10 +]); + +const lorem = getLorem(); + +// needle encoded from 'АБВГДЕЖЗИЙКЛМНОПРСĐĸĐŖĐ¤ĐĨĐĻЧШЊĐĒĐĢĐŦĐ­ĐŽĐ¯Đ°ĐąĐ˛ĐŗĐ´ĐĩĐļСиКĐēĐģĐŧĐŊĐžĐŋŅ€ŅŅ‚ŅƒŅ„Ņ…Ņ†Ņ‡ŅˆŅ‰ŅŠŅ‹ŅŒŅŅŽŅ' +fixtures['lorem_cp866.txt'] = getTestData( + Uint8Array.from([...range(128, 175 + 1), ...range(224, 239 + 1)]) +); + +// needle encoded from ÃļäÃŧß +fixtures['lorem_cp1252.txt'] = getTestData(Uint8Array.from([246, 228, 252, 223])); + +// needle encoded from '中文abc' +fixtures['lorem_big5.txt'] = getTestData(Uint8Array.from([164, 164, 164, 229, 97, 98, 99])); + +// needle encoded from '中文abc' +fixtures['lorem_shiftjis.txt'] = getTestData(Uint8Array.from([146, 134, 149, 182, 97, 98, 99])); + +// needle encoded from '中å›Ŋabc' +fixtures['lorem_gbk.txt'] = getTestData(Uint8Array.from([214, 208, 185, 250, 97, 98, 99])); + +// needle encoded from ÃļäÃŧß +fixtures['lorem.txt'] = getTestData(Uint8Array.from([246, 228, 252, 223])); + +fixtures['lorem_utf8bom.txt'] = VSBuffer.concat([ + VSBuffer.wrap(Uint8Array.from(UTF8_BOM)), + VSBuffer.wrap(getTestData(Uint8Array.from([195, 182, 195, 164, 195, 188, 195, 159]))), +]).buffer; + +fixtures['lorem_utf16be.txt'] = + Uint8Array.from( + fixtures['lorem.txt'].reduce((acc, byte, i) => { + acc[2 * i] = 0; + acc[2 * i + 1] = byte; + return acc; + }, [] as number[]) + ); + +fixtures['lorem_utf16le.txt'] = + Uint8Array.from( + fixtures['lorem.txt'].reduce((acc, byte, i) => { + acc[2 * i] = byte; + acc[2 * i + 1] = 0; + return acc; + }, [] as number[]) + ); + + +function getTestData(needle: Uint8Array): Uint8Array { + const needleBuffer = VSBuffer.wrap(needle); + return VSBuffer + .concat( + [ + needleBuffer, + VSBuffer.fromString(' '), + lorem.head, + needleBuffer, + VSBuffer.fromString(' '), + lorem.tail, + ] + ) + .buffer; +} + +function getLorem() { + return { + head: VSBuffer.fromString(`Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur vulputate, ipsum quis interdum fermentum, lorem sem fermentum eros, vitae auctor neque lacus in nisi. Suspendisse potenti. Maecenas et scelerisque elit, in tincidunt quam. Sed eu tincidunt quam. Nullam justo ex, imperdiet a imperdiet et, fermentum sit amet eros. Aenean quis tempus sem. Pellentesque accumsan magna mi, ut mollis velit sagittis id. Etiam quis ipsum orci. Fusce purus ante, accumsan a lobortis at, venenatis eu nisl. Praesent ornare sed ante placerat accumsan. Suspendisse tempus dignissim fermentum. Nunc a leo ac lacus sodales iaculis eu vitae mi. In feugiat ante at massa finibus cursus. Suspendisse posuere fringilla ornare. Mauris elementum ac quam id convallis. Vestibulum non elit quis urna volutpat aliquam a eu lacus. + +Aliquam vestibulum imperdiet neque, suscipit aliquam elit ultrices bibendum. Suspendisse ultrices pulvinar cursus. Morbi risus nisi, cursus consequat rutrum vitae, molestie sed dui. Fusce posuere, augue quis dignissim aliquam, nisi ipsum porttitor ante, quis fringilla nisl turpis ac nisi. Nulla varius enim eget lorem vehicula gravida. Donec finibus malesuada leo nec semper. Proin ac enim eros. Vivamus non tincidunt nisi, vel tristique lorem. + +Nunc consequat ex id eros dignissim, id rutrum risus laoreet. Sed euismod non erat eu ultricies. Etiam vehicula gravida lacus ut porta. Vestibulum eu eros quis nunc aliquet luctus. Cras quis semper ligula. Nullam gravida vehicula quam sed porta. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In porta cursus vulputate. Quisque porta a nisi eget cursus. Aliquam risus leo, luctus ac magna in, efficitur cursus magna. In condimentum non mi id semper. Donec interdum ante eget commodo maximus. + +Vivamus sit amet vestibulum lectus. Fusce tincidunt mi sapien, dictum sollicitudin diam vulputate in. Integer fringilla consequat mollis. Cras aliquet consequat felis eget feugiat. Nunc tempor cursus arcu, vitae ornare nunc varius et. Vestibulum et tortor vel ante viverra porttitor. Nam at tortor ullamcorper, facilisis augue quis, tristique erat. Aenean ut euismod nibh. Quisque eu tincidunt est, nec euismod eros. + +Proin vehicula nibh non viverra egestas. Phasellus sem dolor, ultricies ac sagittis tristique, lacinia a purus. Vestibulum in ante eros. Pellentesque lacus nulla, tristique vitae interdum vel, malesuada ac diam. Aenean bibendum posuere turpis in accumsan. Ut est nulla, ullamcorper quis turpis at, viverra sagittis mauris. Sed in interdum purus. Praesent scelerisque nibh eget sem euismod, ut imperdiet mi venenatis. Vivamus pulvinar orci sed dapibus auctor. Nulla facilisi. Vestibulum tincidunt erat nec porttitor egestas. Mauris quis risus ante. Nulla facilisi. + +Aliquam ullamcorper ornare lobortis. Phasellus quis sem et ipsum mollis malesuada sed in ex. Ut aliquam ex eget metus finibus maximus. Proin suscipit mauris eu nibh lacinia, quis feugiat dui dapibus. Nam sed libero est. Aenean vulputate orci sit amet diam faucibus, eu sagittis sapien volutpat. Nam imperdiet felis turpis, at pretium odio pulvinar in. Sed vestibulum id eros nec ultricies. Sed quis aliquam tortor, vitae ullamcorper tellus. Donec egestas laoreet eros, id suscipit est rutrum nec. Sed auctor nulla eget metus aliquam, ut condimentum enim elementum. + +Aliquam suscipit non turpis sit amet bibendum. Fusce velit ligula, euismod et maximus at, luctus sed neque. Quisque pretium, nisl at ullamcorper finibus, lectus leo mattis sapien, vel euismod mauris diam ullamcorper ex. Nulla ut risus finibus, lacinia ligula at, auctor erat. Mauris consectetur sagittis ligula vel dapibus. Nullam libero libero, lobortis aliquam libero vel, venenatis ultricies leo. Duis porttitor, nibh congue fermentum posuere, erat libero pulvinar tortor, a pellentesque nunc ipsum vel sem. Nullam volutpat, eros sit amet facilisis consectetur, ipsum est vehicula massa, non vestibulum neque elit in mauris. Nunc hendrerit ipsum non enim bibendum, vitae rhoncus mi egestas. Etiam ullamcorper massa vel nisl sagittis, nec bibendum arcu malesuada. Aenean aliquet turpis justo, a consectetur arcu mollis convallis. Etiam tellus ipsum, ultricies vitae lorem et, ornare facilisis orci. Praesent fringilla justo urna, vel mollis neque pulvinar vestibulum. + +Donec non iaculis erat. Aliquam et mi sed nunc pulvinar ultricies in ut ipsum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Praesent feugiat lacus ac dignissim semper. Phasellus vitae quam nisi. Morbi vel diam ultricies risus lobortis ornare. Fusce maximus et ligula quis iaculis. Sed congue ex eget felis convallis, sit amet hendrerit elit tempor. Donec vehicula blandit ante eget commodo. Vestibulum eleifend diam at feugiat euismod. Etiam magna tellus, dignissim eget fermentum vel, vestibulum vitae mauris. Nam accumsan et erat id sagittis. Donec lacinia, odio ut ornare ultricies, dolor velit accumsan tortor, non finibus erat tellus quis ligula. Nunc quis metus in leo volutpat ornare vulputate eu nisl. + +Donec quis viverra ex. Nullam id feugiat mauris, eu fringilla nulla. Vestibulum id maximus elit. Cras elementum elit sed felis lobortis, eget sagittis nisi hendrerit. Vivamus vitae elit neque. Donec vulputate lacus ut libero ultrices accumsan. Vivamus accumsan nulla orci, in dignissim est laoreet sagittis. Proin at commodo velit. Curabitur in velit felis. Aliquam erat volutpat. Sed consequat, nulla et cursus sodales, nisi lacus mattis risus, quis eleifend erat ex nec turpis. Sed suscipit ultrices lorem in hendrerit. + +Morbi vitae lacus nec libero ornare tempus eu et diam. Suspendisse magna ipsum, fermentum vel odio quis, molestie aliquam urna. Fusce mollis turpis a eros accumsan porttitor. Pellentesque rhoncus dolor sit amet magna rutrum, et dapibus justo tempor. Sed purus nisi, maximus vitae fringilla eu, molestie nec urna. Fusce malesuada finibus pretium. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec sed aliquet eros. Pellentesque luctus diam ante, eget euismod nisl aliquet eu. Sed accumsan elit purus, tempor varius ligula tempus nec. Curabitur ornare leo suscipit suscipit fermentum. Morbi eget nulla est. Maecenas faucibus interdum tristique. + +Etiam ut elit eros. Nulla pharetra suscipit molestie. Nulla facilisis bibendum nisl non molestie. Curabitur turpis lectus, facilisis vel diam non, vulputate ultrices mauris. Aenean placerat aliquam convallis. Suspendisse sed scelerisque tellus. Vivamus lacinia neque eget risus cursus suscipit. Proin consequat dolor vel neque tempor, eu aliquam sem scelerisque. Duis non eros a purus malesuada pharetra non et nulla. Suspendisse potenti. Mauris libero eros, finibus vel nulla id, sagittis dapibus ante. Proin iaculis sed nunc et cursus. + +Quisque accumsan lorem sit amet lorem aliquet euismod. Curabitur fermentum rutrum posuere. Etiam ultricies, sem id pellentesque suscipit, urna magna lacinia eros, quis efficitur risus nisl at lacus. Nulla quis lacus tortor. Mauris placerat ex in dolor tincidunt, vel aliquet nisi pretium. Cras iaculis risus vitae pellentesque aliquet. Quisque a enim imperdiet, ullamcorper arcu vitae, rutrum risus. Nullam consectetur libero at felis fringilla, nec congue nibh dignissim. Nam et lobortis felis, eu pellentesque ligula. Aenean facilisis, ligula non imperdiet maximus, massa orci gravida sapien, at sagittis lacus nisl in lacus. Nulla quis mauris luctus, scelerisque felis consequat, tempus risus. Fusce auctor nisl non nulla luctus molestie. Maecenas sapien nisl, auctor non dolor et, iaculis scelerisque lorem. Suspendisse egestas enim aliquet, accumsan mauris nec, posuere quam. Nulla iaculis dui dui, sit amet vestibulum erat ultricies ac. + +Cras eget dolor erat. Proin at nisl ut leo consectetur ultricies vel ut arcu. Nulla in felis malesuada, ullamcorper tortor et, convallis massa. Nunc urna justo, ornare in nibh vitae, hendrerit condimentum libero. Etiam vitae libero in purus venenatis fringilla. Nullam velit nulla, consequat ut turpis non, egestas hendrerit nibh. Duis tortor turpis, interdum non ante ac, cursus accumsan lectus. Cras pharetra bibendum augue quis dictum. Sed euismod vestibulum justo. Proin porta lobortis purus. Duis venenatis diam tortor, sit amet condimentum eros rhoncus a. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nunc at magna nec diam lobortis efficitur sit amet ut lacus. Nulla quis orci tortor. Pellentesque tempus velit a odio finibus porta. + +Proin feugiat mauris a tellus scelerisque convallis. Maecenas libero magna, blandit nec ultrices id, congue vel mi. Aliquam lacinia, quam vel condimentum convallis, tortor turpis aliquam odio, sed blandit libero lacus et eros. In eleifend iaculis magna ac finibus. Praesent auctor facilisis tellus in congue. Sed molestie lobortis dictum. Nam quis dignissim augue, vel euismod lorem. Curabitur posuere dapibus luctus. Donec ultricies dictum lectus, quis blandit arcu commodo ac. Aenean tincidunt ligula in nunc imperdiet dignissim. Curabitur egestas sollicitudin sapien ut semper. Aenean nec dignissim lacus. + +Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec aliquam dictum vehicula. Donec tortor est, volutpat non nisi nec, varius gravida ex. Nunc vel tristique nunc, vitae mattis nisi. Nunc nec luctus ex, vitae tincidunt lectus. In hac habitasse platea dictumst. Curabitur lobortis ex eget tincidunt tempor. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Ut a vehicula mi. + +Fusce eu libero finibus, interdum nulla a, placerat neque. Cras bibendum tempor libero nec feugiat. Cras ut sodales eros. Proin viverra, massa sit amet viverra egestas, neque nisl porta ex, sit amet hendrerit libero ligula vel urna. Mauris suscipit lacus id justo rhoncus suscipit. Etiam vel libero tellus. Maecenas non diam molestie, condimentum tellus a, bibendum enim. Mauris aliquet imperdiet tellus, eget sagittis dolor. Sed blandit in neque et luctus. Cras elementum sagittis nunc, vel mollis lorem euismod et. Donec posuere at lacus eget suscipit. + +Nulla nunc mi, pretium non massa vel, tempor semper magna. Nunc a leo pulvinar, tincidunt nunc at, dignissim mi. Aliquam erat volutpat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Ut viverra nulla a nisl finibus, at hendrerit ligula ullamcorper. Donec a lorem semper, tempor magna et, lobortis libero. Mauris id sapien leo. Donec dignissim, quam vitae porttitor dignissim, quam justo mattis dui, vel consequat odio elit quis orci. Etiam nec pretium neque, sit amet pretium orci. Duis ac tortor venenatis, feugiat purus non, feugiat nunc. Proin scelerisque nisl in turpis aliquam vulputate. + +Praesent sed est semper, fringilla lorem vitae, tincidunt nibh. Cras eros metus, auctor at mauris sit amet, sodales semper orci. Nunc a ornare ex. Curabitur bibendum arcu congue urna vulputate egestas. Vestibulum finibus id risus et accumsan. Aenean ut volutpat tellus. Aenean tincidunt malesuada urna sit amet vestibulum. Mauris vel tellus dictum, varius lacus quis, dictum arcu. + +Aenean quis metus eu erat feugiat cursus vel at ligula. Proin dapibus sodales urna, id euismod lectus tempus id. Pellentesque ex ligula, convallis et erat vel, vulputate condimentum nisl. Pellentesque pharetra nulla quis massa eleifend hendrerit. Praesent sed massa ipsum. Maecenas vehicula dolor massa, id sodales urna faucibus et. Mauris ac quam non massa tincidunt feugiat et at lacus. Fusce libero massa, vulputate vel scelerisque non, mollis in leo. Ut sit amet ultricies odio. Suspendisse in sapien viverra, facilisis purus ut, pretium libero. + +Vivamus tristique pharetra molestie. Nam a volutpat purus. Praesent consequat gravida nisi, ac blandit nisi suscipit ut. Quisque posuere, ligula a ultrices laoreet, ligula nunc vulputate libero, ut rutrum erat odio tincidunt justo. Sed vitae leo at leo fringilla bibendum. Vestibulum ut augue nec dolor auctor accumsan. Praesent laoreet id eros pulvinar commodo. Suspendisse potenti. Ut pharetra, mauris vitae blandit fringilla, odio ante tincidunt lorem, sit amet tempor metus diam ut turpis. + +Praesent quis egestas arcu. Nullam at porta arcu. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi vulputate ligula malesuada ligula luctus, vulputate tempus erat bibendum. Nunc ullamcorper non lectus at euismod. Etiam nibh felis, tincidunt a metus vel, pellentesque rhoncus neque. Etiam at diam in erat luctus interdum. Nunc vel ipsum pulvinar, sollicitudin lacus ac, tempus urna. Etiam vel lacinia sapien. Pellentesque sagittis velit vel mi efficitur iaculis. Integer euismod sit amet urna in sagittis. Cras eleifend ut nibh in facilisis. Donec et lacus vitae nunc placerat sodales. Nulla sed hendrerit ligula, at dapibus sapien. + +Praesent at iaculis ex. Curabitur est purus, cursus a faucibus quis, dictum id velit. Donec dignissim fringilla viverra. Nunc mauris felis, laoreet sit amet sagittis at, vestibulum in libero. Maecenas quis orci turpis. Quisque ut nibh vitae magna mollis consequat id at mauris. Aliquam eu odio eget nulla bibendum sodales. Quisque vel orci eleifend nisi pretium lacinia. Suspendisse eget risus eget mi volutpat molestie eget quis lacus. Duis nisi libero, tincidunt nec nulla id, faucibus cursus felis. + +Donec tempor eget risus pellentesque molestie. Phasellus porta neque vel arcu egestas, nec blandit velit fringilla. Nullam porta faucibus justo vitae laoreet. Pellentesque viverra id nunc eu varius. Nulla pulvinar lobortis iaculis. Etiam vestibulum odio nec velit tristique, a tristique nisi mattis. In sed fringilla orci, vitae efficitur odio. Quisque dui odio, ornare eget velit at, lacinia consequat libero. Quisque lectus nulla, aliquet eu leo in, porta rutrum diam. Donec nec mattis neque. Nam rutrum, odio ac eleifend bibendum, dolor arcu rutrum neque, eget porta elit tellus a lacus. Sed massa metus, sollicitudin et sapien eu, finibus tempus orci. Proin et sapien sit amet erat molestie interdum. In quis rutrum velit, faucibus ultrices tellus. + +Sed sagittis sed justo eget tincidunt. Maecenas ut leo sagittis, feugiat magna et, viverra velit. Maecenas ex arcu, feugiat at consequat vitae, auctor eu massa. Integer egestas, enim vitae maximus convallis, est lectus pretium mauris, ac posuere lectus nisl quis quam. Aliquam tempus laoreet mi, vitae dapibus dolor varius dapibus. Suspendisse potenti. Donec sit amet purus nec libero dapibus tristique. Pellentesque viverra bibendum ligula. Donec sed felis et ex lobortis laoreet. Phasellus a fringilla libero, vitae malesuada nulla. Pellentesque blandit mattis lacus, et blandit tortor laoreet consequat. Suspendisse libero nunc, viverra sed fermentum in, accumsan egestas arcu. Proin in placerat elit. Sed interdum imperdiet malesuada. Suspendisse aliquet quis mauris eget sollicitudin. + +Vivamus accumsan tellus non erat volutpat, quis dictum dolor feugiat. Praesent rutrum nunc ac est mollis cursus. Fusce semper volutpat dui ut egestas. Curabitur sit amet posuere massa. Cras tincidunt nulla et mi mollis imperdiet. Suspendisse scelerisque ex id sodales vulputate. In nunc augue, pharetra in placerat eu, mattis id tellus. Vivamus cursus efficitur vehicula. Nulla aliquet vehicula aliquet. + +Sed cursus tellus sed porta pulvinar. Sed vitae nisi neque. Nullam aliquet, lorem et efficitur scelerisque, arcu diam aliquam felis, sed pulvinar lorem odio et turpis. Praesent convallis pulvinar turpis eu iaculis. Aliquam nec gravida mi. Curabitur eu nibh tempor, blandit justo in, ultrices felis. Fusce placerat metus non mi sagittis rutrum. Morbi sed dui fringilla, sagittis mauris eget, imperdiet nunc. Phasellus hendrerit sem elit, id hendrerit libero auctor sit amet. Integer sodales elit sit amet consequat cursus. + +Nam semper est eget nunc mollis, in pellentesque lectus fringilla. In finibus vel diam id semper. Nunc mattis quis erat eu consectetur. In hac habitasse platea dictumst. Nullam et ipsum vestibulum ex pulvinar ultricies sit amet id velit. Aenean suscipit mi tortor, a lobortis magna viverra non. Nulla condimentum aliquet ante et ullamcorper. Pellentesque porttitor arcu a posuere tempus. Aenean lacus quam, imperdiet eu justo vitae, pretium efficitur ex. Duis id purus id magna rhoncus ultrices id eu risus. Nunc dignissim et libero id dictum. + +Quisque a tincidunt neque. Phasellus commodo mi sit amet tempor fringilla. Ut rhoncus, neque non porttitor elementum, libero nulla egestas augue, sed fringilla sapien felis ac velit. Phasellus viverra rhoncus mollis. Nam ullamcorper leo vel erat laoreet luctus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus semper a metus a cursus. Nulla sed orci egestas, efficitur purus ac, malesuada tellus. Aenean rutrum velit at tellus fermentum mollis. Aliquam eleifend euismod metus. + +In hac habitasse platea dictumst. Vestibulum volutpat neque vitae porttitor laoreet. Nam at tellus consequat, sodales quam in, pulvinar arcu. Maecenas varius convallis diam, ac lobortis tellus pellentesque quis. Maecenas eget augue massa. Nullam volutpat nibh ac justo rhoncus, ut iaculis tellus rutrum. Fusce efficitur efficitur libero quis condimentum. Curabitur congue neque non tincidunt tristique. Fusce eget tempor ex, at pellentesque odio. Praesent luctus dictum vestibulum. Etiam non orci nunc. Vivamus vitae laoreet purus, a lobortis velit. Curabitur tincidunt purus ac lectus elementum pellentesque. Quisque sed tincidunt est. + +Sed vel ultrices massa, vitae ultricies justo. Cras finibus mauris nec lacus tempus dignissim. Cras faucibus maximus velit, eget faucibus orci luctus vehicula. Nulla massa nunc, porta ac consequat eget, rhoncus non tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce sed maximus metus, vel imperdiet ipsum. Ut scelerisque lectus at blandit porttitor. Ut vulputate nunc pharetra, aliquet sapien ac, sollicitudin sapien. Aenean eget ante lorem. Nam accumsan venenatis tellus id dignissim. + +Curabitur fringilla, magna non maximus dapibus, nulla sapien vestibulum lectus, sit amet semper dolor neque vitae nisl. Nunc ultrices vehicula augue sed iaculis. Maecenas nec diam mollis, suscipit orci et, vestibulum ante. Pellentesque eu nisl tortor. Nunc eleifend, lacus quis volutpat volutpat, nisi mi molestie sem, quis mollis ipsum libero a tellus. Ut viverra dolor mattis convallis interdum. Sed tempus nisl at nunc scelerisque aliquet. Quisque tempor tempor lorem id feugiat. Nullam blandit lectus velit, vitae porta lacus tincidunt a. Vivamus sit amet arcu ultrices, tincidunt mi quis, viverra quam. Aenean fringilla libero elementum lorem semper, quis pulvinar eros gravida. Nullam sodales blandit mauris, sed fermentum velit fermentum sit amet. Donec malesuada mauris in augue sodales vulputate. Vestibulum gravida turpis id elit rhoncus dignissim. Integer non congue lorem, eu viverra orci. + +Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec at dolor magna. Aliquam consectetur erat augue, id iaculis velit pharetra ac. Integer rutrum venenatis dignissim. Integer non sodales elit. Curabitur ut magna ut nibh feugiat aliquam ac ut risus. Morbi nibh quam, aliquam id placerat nec, vestibulum eget velit. Suspendisse at dignissim quam. Vivamus aliquet sem sed nisl volutpat, ut cursus orci ultrices. Aliquam ultrices lacinia enim, vitae aliquet neque. + +Quisque scelerisque finibus diam in mattis. Cras cursus auctor velit. Aliquam sem leo, fermentum et maximus et, molestie a libero. Aenean justo elit, rutrum a ornare id, egestas eget enim. Aenean auctor tristique erat. Curabitur condimentum libero lacus, nec consequat orci vestibulum sed. Fusce elit ligula, blandit vitae sapien vitae, dictum ultrices risus. Nam laoreet suscipit sapien, at interdum velit faucibus sit amet. Duis quis metus egestas lectus elementum posuere non nec libero. Aliquam a dolor bibendum, facilisis nunc a, maximus diam. Vestibulum suscipit tristique magna, non dignissim turpis sodales sed. Nunc ornare, velit ac facilisis fringilla, dolor mi consectetur lorem, vitae finibus erat justo suscipit urna. Maecenas sit amet eros erat. Nunc non arcu ornare, suscipit lorem eget, sodales mauris. Aliquam tincidunt, quam nec mollis lacinia, nisi orci fermentum libero, consequat eleifend lectus quam et sapien. Vestibulum a quam urna. + +Cras arcu leo, euismod ac ullamcorper at, faucibus sed massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vivamus porttitor velit in enim interdum, non commodo metus ornare. Morbi vel lorem quis nisl luctus tristique quis vitae nisl. Suspendisse condimentum tortor enim, nec eleifend ipsum euismod et. Sed gravida quam ut tristique lacinia. Mauris eu interdum ipsum, ac ultrices odio. Nullam auctor tellus a risus porttitor vehicula. Nulla blandit euismod dictum. In pharetra, enim iaculis pulvinar interdum, dui nunc placerat nunc, sit amet pretium lectus nulla vitae quam. Phasellus quis enim sollicitudin, varius nulla id, ornare purus. Donec quam lacus, vestibulum quis nunc ac, mollis dictum nisi. Cras ut mollis elit. Maecenas ultrices ligula at risus faucibus scelerisque. Etiam vitae porttitor purus. Curabitur blandit lectus urna, ut hendrerit tortor feugiat ut. + +Phasellus fringilla, sapien pellentesque commodo pharetra, ante libero aliquam tellus, ut consectetur augue libero a sapien. Maecenas blandit luctus nisl eget aliquet. Maecenas vitae porta dolor, faucibus laoreet sapien. Suspendisse lobortis, ipsum sed vehicula aliquam, elit purus scelerisque dui, rutrum consectetur diam odio et lorem. In nec lacinia metus. Donec viverra libero est, vel bibendum erat condimentum quis. Donec feugiat purus leo. In laoreet vitae felis a porttitor. Mauris ullamcorper, lacus id condimentum suscipit, neque magna pellentesque arcu, eget cursus neque tellus id metus. Curabitur volutpat ac orci vel ultricies. + +Sed ut finibus erat. Sed diam purus, varius non tincidunt quis, ultrices sit amet ipsum. Donec et egestas nulla. Suspendisse placerat nisi at dui laoreet iaculis. Aliquam aliquet leo at augue faucibus molestie. Nullam lacus augue, hendrerit sed nisi eu, faucibus porta est. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nam ut leo aliquet sem fermentum rutrum quis ac justo. Integer placerat aliquam nisl ut sagittis. Proin erat orci, lobortis et sem eget, eleifend fringilla augue. Mauris varius laoreet arcu, sed tincidunt felis. Pellentesque venenatis lorem odio, id pulvinar velit molestie feugiat. Donec mattis lacus sed eleifend pulvinar. + +Sed condimentum ex in tincidunt hendrerit. Etiam eget risus lacinia, euismod nibh eu, pellentesque quam. Proin elit eros, convallis id mauris ac, bibendum ultrices lectus. Morbi venenatis, purus id fermentum consequat, nunc libero tincidunt ligula, non dictum ligula orci nec quam. Nulla nec ultrices lorem. Aenean maximus augue vel dictum pharetra. Etiam turpis urna, pellentesque quis malesuada eu, molestie faucibus felis. + +Vestibulum pharetra augue ut quam blandit congue in nec risus. Proin eu nibh eu dui eleifend porta vitae id lectus. Proin lacus nibh, lobortis sed ligula vitae, interdum lobortis erat. Suspendisse potenti. In sollicitudin quis sapien ut aliquet. Mauris ac nulla arcu. Fusce tristique justo quis lectus mollis, eu volutpat lectus finibus. Vivamus venenatis facilisis ex ut vestibulum. + +Etiam varius lobortis purus, in hendrerit elit tristique at. In tempus, augue vestibulum fermentum gravida, ligula tellus vulputate arcu, eu molestie ex sapien at purus. Vestibulum nec egestas metus. Duis pulvinar quam nec consequat interdum. Aenean non dapibus lacus. Aliquam sit amet aliquet nulla. Sed venenatis volutpat purus nec convallis. Phasellus aliquet semper sodales. Cras risus sapien, condimentum auctor urna a, pulvinar ornare nisl. Sed tincidunt felis elit, ut elementum est bibendum ac. Morbi interdum justo vel dui faucibus condimentum. + +Sed convallis eu sem at tincidunt. Nullam at auctor est, et ullamcorper ipsum. Pellentesque eget ante ante. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer euismod, sapien sed dapibus ornare, nibh enim maximus lacus, lacinia placerat urna quam quis felis. Morbi accumsan id nisl ut condimentum. Donec bibendum nisi est, sed volutpat lorem rhoncus in. Vestibulum ac lacinia nunc, eget volutpat magna. Integer aliquam pharetra ipsum, id placerat nunc volutpat quis. Etiam urna diam, rhoncus sit amet varius vel, euismod vel sem. Nullam vel molestie urna. Vivamus ornare erat at venenatis euismod. Suspendisse potenti. Fusce diam justo, tincidunt vel sem at, commodo faucibus nisl. Duis gravida efficitur diam, vel sagittis erat pulvinar ut. + +Quisque vel pharetra felis. Duis efficitur tortor dolor, vitae porttitor erat fermentum sed. Sed eu mi purus. Etiam dignissim tortor eu tempus molestie. Aenean pretium erat enim, in hendrerit ante hendrerit at. Sed ut risus vel nunc venenatis ultricies quis in lacus. Pellentesque vitae purus euismod, placerat risus non, ullamcorper augue. Quisque varius quam ligula, nec aliquet ex faucibus vitae. Quisque rhoncus sit amet leo tincidunt mattis. Cras id mauris eget purus pretium gravida sit amet eu augue. Aliquam dapibus odio augue, id lacinia velit pulvinar eu. + +Mauris fringilla, tellus nec pharetra iaculis, neque nisi ultrices massa, et tincidunt sem dui sed mi. Curabitur erat lorem, venenatis quis tempus lacinia, tempus sit amet nunc. Aliquam at neque ac metus commodo dictum quis vitae justo. Phasellus eget lacus tempus, blandit lorem vel, rutrum est. Aenean pharetra sem ut augue lobortis dignissim. Sed rhoncus at nulla id ultrices. Cras id condimentum felis. In suscipit luctus vulputate. Donec tincidunt lacus nec enim tincidunt sollicitudin ut quis enim. Nam at libero urna. Praesent sit amet massa vitae massa ullamcorper vehicula. + +Nullam bibendum augue ut turpis condimentum bibendum. Proin sit amet urna hendrerit, sodales tortor a, lobortis lectus. Integer sagittis velit turpis, et tincidunt nisi commodo eget. Duis tincidunt elit finibus accumsan cursus. Aenean dignissim scelerisque felis vel lacinia. Nunc lacinia maximus luctus. In hac habitasse platea dictumst. Vestibulum eget urna et enim tempor tempor. Nam feugiat, felis vel vestibulum tempus, orci justo viverra diam, id dapibus lorem justo in ligula. + +Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In ac pellentesque sem. Vestibulum lacinia magna dui, eu lacinia augue placerat et. Maecenas pulvinar congue est. Pellentesque commodo dui non pulvinar scelerisque. Etiam interdum est posuere sem bibendum, ac commodo magna dictum. Cras ipsum turpis, rhoncus nec posuere vitae, laoreet a arcu. Integer ac massa sit amet enim placerat lacinia sed ultrices arcu. Suspendisse sem nibh, luctus sit amet volutpat in, pellentesque eu metus. Ut gravida neque eget mi accumsan tempus. Nam sit amet aliquet nibh. + +Pellentesque a purus cursus nulla hendrerit congue quis et odio. Aenean hendrerit, leo ullamcorper sagittis hendrerit, erat dui molestie quam, sed condimentum lacus risus sed tellus. Morbi a dapibus lectus, ut feugiat ex. Phasellus pretium quam et sapien mollis, vel iaculis dui dignissim. Sed ullamcorper est turpis, a viverra lorem consectetur in. Aenean aliquet nibh non cursus rutrum. Suspendisse at tristique urna, id lobortis urna. In hac habitasse platea dictumst. Phasellus libero velit, rutrum sed tellus nec, dapibus tincidunt ligula. Quisque vel dui venenatis, consequat nisl ut, lacinia ipsum. Phasellus vitae magna pellentesque, lobortis est id, faucibus quam. Nam eleifend faucibus dui vel pellentesque. + +Etiam ut est non lacus tincidunt interdum. Maecenas sed massa urna. Quisque ut nibh tortor. Pellentesque felis ipsum, tempor finibus ipsum et, euismod pretium metus. Donec sit amet est ipsum. Quisque rhoncus justo non finibus elementum. Nulla nec lectus ac tortor placerat fringilla. Phasellus ac ultrices nunc, eu efficitur nisl. Nulla rhoncus nunc vitae ante dictum tincidunt. Nunc ultrices, massa sit amet malesuada dignissim, lectus lacus consequat sapien, non eleifend metus sem in eros. Phasellus mauris ante, dictum sit amet suscipit ac, rhoncus eget nisi. Phasellus at orci mollis, imperdiet neque eget, faucibus nulla. In at purus massa. Pellentesque quis rutrum lectus. + +Integer eu faucibus turpis, sit amet mollis massa. Vestibulum id nulla commodo, rutrum ipsum sed, semper ante. Phasellus condimentum orci nec nibh convallis, ac maximus orci ullamcorper. Maecenas vitae sollicitudin mi. Integer et finibus lectus, et condimentum ligula. Donec elementum tristique quam vitae dapibus. Morbi euismod ipsum in tristique ullamcorper. + +Duis fermentum non enim eu auctor. Quisque lacinia nibh vehicula nibh posuere, eu volutpat turpis facilisis. Ut ac faucibus nulla. Sed eleifend quis ex et pellentesque. Vestibulum sollicitudin in libero id fringilla. Phasellus dignissim purus consequat, condimentum dui sit amet, condimentum ante. Pellentesque ac consectetur massa, quis sagittis est. Nulla maximus tristique risus accumsan convallis. Curabitur imperdiet ac lacus a ultrices. Nulla facilisi. Sed quis quam quis lectus placerat lobortis vel sed turpis. In mollis dui id neque iaculis, ut aliquet tellus malesuada. Proin at luctus odio, vel blandit sapien. Praesent dignissim tortor vehicula libero fringilla, nec ultrices erat suscipit. Maecenas scelerisque purus in dapibus fermentum. + +Curabitur magna odio, mattis in tortor ut, porttitor congue est. Vestibulum mollis lacinia elementum. Fusce maximus erat vitae nunc rutrum lobortis. Integer ligula eros, auctor vel elit non, posuere luctus lacus. Maecenas quis auctor massa. Ut ipsum lacus, efficitur posuere euismod et, hendrerit efficitur est. Phasellus fringilla, quam id tincidunt pretium, nunc dui sollicitudin orci, eu dignissim nisi metus ut magna. Integer lobortis interdum dolor, non bibendum purus posuere et. Donec non lectus aliquet, pretium dolor eu, cursus massa. Sed ut dui sapien. In sed vestibulum massa. Pellentesque blandit, dui non sodales vehicula, orci metus mollis nunc, non pharetra ex tellus ac est. Mauris sagittis metus et fermentum pretium. Nulla facilisi. Quisque quis ante ut nulla placerat mattis ut quis nisi. + +Sed quis nulla ligula. Quisque dignissim ligula urna, sed aliquam purus semper at. Suspendisse potenti. Nunc massa lectus, pharetra vehicula arcu bibendum, imperdiet sodales ipsum. Nam ac sapien diam. Mauris iaculis fringilla mattis. Pellentesque tempus eros sit amet justo volutpat mollis. Phasellus ac turpis ipsum. Morbi vel ante elit. Aenean posuere quam consequat velit varius suscipit. Donec tempor quam ut nibh cursus efficitur. + +Morbi molestie dolor nec sem egestas suscipit. Etiam placerat pharetra lectus, et ullamcorper risus tristique in. Sed faucibus ullamcorper lectus eget fringilla. Maecenas malesuada hendrerit congue. Sed eget neque a erat placerat tincidunt. Aliquam vitae dignissim turpis. Fusce at placerat magna, a laoreet lectus. Maecenas a purus nec diam gravida fringilla. Nam malesuada euismod ante non vehicula. In faucibus bibendum leo, faucibus posuere nisl pretium quis. Fusce finibus bibendum finibus. Vestibulum eu justo maximus, hendrerit diam nec, dignissim sapien. Aenean dolor lacus, malesuada quis vestibulum ac, venenatis ac ipsum. Cras a est id nunc finibus facilisis. Cras lacinia neque et interdum vehicula. Suspendisse vulputate tellus elit, eget tempor dui finibus vel. + +Cras sed pretium odio. Proin hendrerit elementum felis in tincidunt. Nam sed turpis vel justo molestie accumsan condimentum eu nunc. Praesent lobortis euismod rhoncus. Nulla vitae euismod nibh, quis mattis mi. Fusce ultrices placerat porttitor. Duis sem ipsum, pellentesque sit amet odio a, molestie vulputate mauris. + +Duis blandit mollis ligula, sit amet mattis ligula finibus sit amet. Nunc a leo molestie, placerat diam et, vestibulum leo. Suspendisse facilisis neque purus, nec pellentesque ligula fermentum nec. Aenean malesuada mauris lorem, eu blandit arcu pulvinar quis. Duis laoreet urna lacus, non maximus arcu rutrum ultricies. Nulla augue dolor, suscipit eu mollis eu, aliquam condimentum diam. Ut semper orci luctus, pharetra turpis at, euismod mi. Nulla leo diam, finibus sit amet purus sed, maximus dictum lorem. Integer eu mi id turpis laoreet rhoncus. + +Integer a mauris tincidunt, finibus orci ut, pretium mauris. Nulla molestie nunc mi, id finibus lorem elementum sed. Proin quis laoreet ante. Integer nulla augue, commodo id molestie quis, rutrum ut turpis. Suspendisse et tortor turpis. Sed ut pharetra massa. Pellentesque elementum blandit sem, ut elementum tellus egestas a. Fusce eu purus nibh. + +Cras dignissim ligula scelerisque magna faucibus ullamcorper. Proin at condimentum risus, auctor malesuada quam. Nullam interdum interdum egestas. Nulla aliquam nisi vitae felis mollis dictum. Suspendisse dapibus consectetur tortor. Ut ut nisi non sem bibendum tincidunt. Vivamus suscipit leo quis gravida dignissim. + +Aliquam interdum, leo id vehicula mollis, eros eros rhoncus diam, non mollis ligula mi eu mauris. Sed ultrices vel velit sollicitudin tincidunt. Nunc auctor metus at ligula gravida elementum. Praesent interdum eu elit et mollis. Duis egestas quam sit amet velit dignissim consequat. Aliquam ac turpis nec nunc convallis sagittis. Fusce blandit, erat ac fringilla consectetur, dolor eros sodales leo, vel aliquet risus nisl et diam. Aliquam luctus felis vitae est eleifend euismod facilisis et lacus. Sed leo tellus, auctor eu arcu in, volutpat sagittis nisl. Pellentesque nisl ligula, placerat vel ullamcorper at, vulputate ac odio. Morbi ac faucibus orci, et tempus nulla. Proin rhoncus rutrum dolor, in venenatis mauris. Suspendisse a fermentum augue, non semper mi. Nunc eget pretium neque. Phasellus augue erat, feugiat ac aliquam congue, rutrum non sapien. Pellentesque ac diam gravida, consectetur felis at, ornare neque. + +Nullam interdum mattis sapien quis porttitor. Interdum et malesuada fames ac ante ipsum primis in faucibus. Phasellus aliquet rutrum ipsum id euismod. Maecenas consectetur massa et mi porta viverra. Nunc quam nibh, dignissim vitae maximus et, ullamcorper nec lorem. Nunc vitae justo dapibus, luctus lacus vitae, pretium elit. Maecenas et efficitur leo. Curabitur mauris lectus, placerat quis vehicula vitae, auctor ut urna. Quisque rhoncus pharetra luctus. In hac habitasse platea dictumst. Integer sit amet metus nec eros malesuada aliquam. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi hendrerit mi ac leo aliquam, sit amet ultricies libero commodo. Mauris dapibus purus metus, sit amet viverra nibh imperdiet et. Nullam porta nulla tellus, quis vehicula diam imperdiet non. Vivamus enim massa, bibendum in fermentum in, ultrices at ex. + +Suspendisse fermentum id nibh eget accumsan. Duis dapibus bibendum erat ut sollicitudin. Aliquam nec felis risus. Pellentesque rhoncus ligula id sem maximus mollis sed nec massa. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ipsum ipsum, sodales sed enim id, convallis faucibus eros. Donec ultricies dictum tincidunt. Cras vitae nibh arcu. Pellentesque cursus, sapien nec consequat fermentum, ipsum ante suscipit dui, imperdiet hendrerit est nisl eu massa. Quisque vitae sem ligula. Aenean iaculis metus ut mauris interdum laoreet. Vivamus sed gravida dolor. + +Morbi nulla metus, porttitor sed eros sit amet, efficitur efficitur est. In vel nisl urna. Ut aliquet tellus at congue convallis. Phasellus imperdiet lobortis sollicitudin. Integer sodales, sem eu ultricies pharetra, erat erat porttitor odio, eget dapibus libero ipsum eget velit. Phasellus gravida nulla nisl, eu pharetra mi auctor vel. Sed blandit pharetra velit, ut egestas libero placerat non. Aliquam a interdum quam. Proin at tortor nec dui sollicitudin tempus sed vestibulum elit. Nunc non sollicitudin velit. + +Aenean consequat diam velit, sed rutrum tortor faucibus dictum. Quisque at semper augue. Duis ut est eget mi ornare bibendum id et ligula. Phasellus consequat tortor non leo pulvinar posuere. Proin vestibulum eleifend felis, in hendrerit tortor sollicitudin eu. Phasellus hendrerit, lacus vel laoreet interdum, dui tortor consequat justo, commodo ultricies arcu felis vitae enim. Vivamus eu sapien at leo suscipit rutrum eu at justo. Aenean et dolor a libero ullamcorper posuere. Integer laoreet placerat nisi in vulputate. Mauris laoreet eget risus sed cursus. Donec scelerisque neque a libero eleifend hendrerit. Nulla varius condimentum nunc sit amet fermentum. Aliquam lorem ex, varius nec mollis ut, ultrices in neque. Morbi sit amet porta leo. Integer iaculis fermentum lacus in vestibulum. + +Ut gravida, tellus ut maximus ultrices, erat est venenatis nisl, vitae pretium massa ex ac magna. Sed non purus eget ligula aliquet volutpat non quis arcu. Nam aliquam tincidunt risus, sit amet fringilla sapien vulputate ut. Mauris luctus suscipit pellentesque. Nunc porttitor dapibus ex quis tempus. Ut ullamcorper metus a eros vulputate, vitae viverra lectus convallis. Mauris semper imperdiet augue quis tincidunt. Integer porta pretium magna, sed cursus sem scelerisque sollicitudin. Nam efficitur, nibh pretium eleifend vestibulum, purus diam posuere sem, in egestas mauris augue sit amet urna. + +Vestibulum tincidunt euismod massa in congue. Duis interdum metus non laoreet fringilla. Donec at ligula congue, tincidunt nunc non, scelerisque nunc. Donec bibendum magna non est scelerisque feugiat at nec neque. Ut orci tortor, tempus eget massa non, dignissim faucibus dolor. Nam odio risus, accumsan pretium neque eget, accumsan dignissim dui. In ut neque auctor, scelerisque tellus sed, ullamcorper nisi. Suspendisse varius cursus quam at hendrerit. Vivamus elit libero, sagittis vitae sem ac, vulputate iaculis ligula. + +Sed lobortis laoreet purus sit amet rutrum. Pellentesque feugiat non leo vel lacinia. Quisque feugiat nisl a orci bibendum vestibulum. In et sollicitudin urna. Morbi a arcu ac metus faucibus tempus. Nam eu imperdiet sapien, suscipit mattis tortor. Aenean blandit ipsum nisi, a eleifend ligula euismod at. Integer tincidunt pharetra felis, mollis placerat mauris hendrerit at. Curabitur convallis, est sit amet luctus volutpat, massa lacus cursus augue, sed eleifend magna quam et risus. Aliquam lobortis tincidunt metus vitae porttitor. Suspendisse potenti. Aenean ullamcorper, neque id commodo luctus, nulla nunc lobortis quam, id dapibus neque dui nec mauris. Etiam quis lorem quis elit commodo ornare. Ut pharetra purus ultricies enim ultrices efficitur. Proin vehicula tincidunt molestie. Mauris et placerat sem. + +Aliquam erat volutpat. Suspendisse velit turpis, posuere ac lacus eu, lacinia laoreet velit. Sed interdum felis neque, id blandit sem malesuada sit amet. Ut sagittis justo erat, efficitur semper orci tempor sed. Donec enim massa, posuere varius lectus egestas, pellentesque posuere mi. Cras tincidunt ut libero sed mattis. Suspendisse quis magna et tellus posuere interdum vel at purus. Pellentesque fringilla tristique neque, id aliquet tellus ultricies non. Duis ut tellus vel odio lobortis vulputate. + +Integer at magna ac erat convallis vestibulum. Sed lobortis porttitor mauris. Fusce varius lorem et volutpat pulvinar. Aenean ac vulputate lectus, vitae consequat velit. Suspendisse ex dui, varius ut risus ut, dictum scelerisque sem. Vivamus urna orci, volutpat ut convallis ac, venenatis vitae urna. In hac habitasse platea dictumst. Etiam eu purus arcu. Aenean vulputate leo urna, vel tristique dui sagittis euismod. Suspendisse non tellus efficitur ante rhoncus volutpat at et sapien. + +Sed dapibus accumsan porttitor. Phasellus facilisis lectus finibus ligula dignissim, id pulvinar lectus feugiat. Nullam egestas commodo nisi posuere aliquet. Morbi sit amet tortor sagittis, rutrum dui nec, dapibus sapien. Sed posuere tortor tortor, interdum auctor magna varius vitae. Vestibulum id sagittis augue. Curabitur fermentum arcu sem, eu condimentum quam rutrum non. Phasellus rutrum nibh quis lectus rhoncus pretium. Curabitur dictum interdum elit. Vestibulum maximus sodales imperdiet. Mauris auctor nec purus sed venenatis. In in urna purus. + +Duis placerat molestie suscipit. Morbi a elit id purus efficitur consequat. Nunc ac commodo turpis. Etiam sit amet lacus a ipsum tempus venenatis sed vel nibh. Duis elementum aliquam mi sed tristique. Morbi ligula tortor, semper ac est vel, lobortis maximus erat. Curabitur ipsum felis, laoreet vel condimentum eget, ullamcorper sit amet mauris. Nulla facilisi. Nam at purus sed mi egestas placerat vitae vel magna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Suspendisse at dignissim diam. Phasellus consectetur eget neque vel viverra. Donec sollicitudin mattis dolor vel malesuada. Vivamus vehicula leo neque, vitae fermentum leo posuere et. Praesent dui est, finibus sit amet tristique quis, pharetra vel nibh. + +Duis nulla leo, accumsan eu odio eget, sagittis semper orci. Quisque ullamcorper ligula quam, commodo porttitor mauris ullamcorper eu. Cras varius sagittis felis in aliquam. Duis sodales risus ac justo vehicula, nec mattis diam lacinia. Cras eget lectus ipsum. Ut commodo, enim vitae malesuada hendrerit, ex dolor egestas lectus, sit amet hendrerit metus diam nec est. Vestibulum tortor metus, lobortis sit amet ante eget, tempor molestie lacus. In molestie et urna et semper. Mauris mollis, sem non hendrerit condimentum, sapien nisi cursus est, non suscipit quam justo non metus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam enim est, porta ac feugiat vitae, rutrum in lorem. Duis vehicula tortor ut posuere maximus. + +Nullam vestibulum non tellus sed commodo. Quisque mattis elit sit amet sapien sollicitudin, ut condimentum nisl congue. Aenean sagittis massa vel elit faucibus fermentum. Donec tincidunt nisi nec nisl sodales pellentesque. Mauris congue congue ligula ut suscipit. Vivamus velit tortor, tempor et gravida eget, fermentum sit amet ante. Nullam fringilla, lorem at ultrices cursus, urna neque ornare dolor, eu lacinia orci enim sed nibh. Ut a ullamcorper lectus, id mattis purus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean maximus sollicitudin posuere. Nunc at augue lacus. Aenean efficitur leo sit amet lacinia efficitur. + +Quisque venenatis quam mi, in pharetra odio vulputate eu. In vel nisl pulvinar, pulvinar ligula ut, sodales risus. Sed efficitur lectus at vestibulum tincidunt. Vestibulum eu ullamcorper elit. Fusce vestibulum magna enim, et tempor lacus posuere vitae. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Integer leo elit, luctus nec mattis sit amet, sollicitudin in turpis. + +Proin convallis venenatis leo, vitae tristique erat iaculis nec. Nulla facilisi. Duis porttitor, sapien et bibendum vulputate, sem libero sodales lacus, non malesuada felis erat ut libero. Nam non felis semper, finibus est a, mattis mauris. Praesent nec eros quam. Nulla hendrerit, augue consectetur eleifend ultricies, purus mi condimentum nulla, eget dapibus est nunc sed libero. Nullam elementum dui erat, vitae luctus libero sollicitudin et. Nulla odio magna, placerat in augue eu, dapibus imperdiet odio. Suspendisse imperdiet metus sit amet rhoncus dapibus. Cras at enim et urna vehicula cursus eu a mauris. Integer magna ante, eleifend ac placerat vitae, porta at nisi. Cras eget malesuada orci. Curabitur nunc est, vulputate id viverra et, dignissim sed odio. Curabitur non mattis sem. Sed bibendum, turpis vitae vehicula faucibus, nunc quam ultricies lectus, vitae viverra felis turpis at libero. + +Nullam ut egestas ligula. Proin hendrerit justo a lectus commodo venenatis. Nulla facilisi. Ut cursus lorem quis est bibendum condimentum. Aenean in tristique odio. Fusce tempor hendrerit ipsum. Curabitur mollis felis justo, quis dapibus erat auctor vel. Sed augue lectus, finibus ut urna quis, ullamcorper vestibulum dui. Etiam molestie aliquam tempor. Integer mattis sollicitudin erat, et tristique elit varius vel. Mauris a ex justo. + +Nam eros est, imperdiet non volutpat rutrum, pellentesque accumsan ligula. Duis sit amet turpis metus. Aenean in rhoncus metus, ac fringilla ex. Suspendisse condimentum egestas purus, ut pharetra odio vulputate vel. Duis tincidunt massa a placerat ultrices. Mauris ultricies nibh sit amet condimentum malesuada. Duis tincidunt id ipsum sed congue. + +Praesent eu ex augue. Nullam in porta ligula. In tincidunt accumsan arcu, in pellentesque magna tristique in. Mauris eleifend libero ac nisl viverra faucibus. Nam sollicitudin dolor in commodo hendrerit. Cras at orci metus. Ut quis laoreet orci. Vivamus ultrices leo pellentesque tempor aliquet. Maecenas ut eros vitae purus placerat vestibulum. Etiam vitae gravida dolor, quis rhoncus diam. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. + +Suspendisse fringilla lacinia sagittis. Integer tincidunt consectetur tristique. Morbi non orci convallis, congue sapien quis, vulputate nunc. Donec a libero vel magna elementum facilisis non quis mi. Mauris posuere tellus non ipsum ultrices elementum. Vivamus massa velit, facilisis quis placerat aliquet, aliquet nec leo. Praesent a maximus sem. Sed neque elit, feugiat vel quam non, molestie sagittis nunc. Etiam luctus nunc ac mauris scelerisque, nec rhoncus lacus convallis. Nunc pharetra, nunc ac pulvinar aliquam, ex ipsum euismod augue, nec porttitor lacus turpis vitae neque. Fusce bibendum odio id tortor faucibus pellentesque. Sed ac porta nibh, eu gravida erat. + +Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aliquam quis ullamcorper felis. Nulla mattis sagittis ante ac tincidunt. Integer ac felis efficitur, viverra libero et, facilisis ligula. Suspendisse a metus a massa rhoncus posuere. Phasellus suscipit ligula ut lacus facilisis, ac pellentesque ex tempor. Quisque consectetur massa mi, ac molestie libero dictum quis. Proin porttitor ligula quis erat tincidunt venenatis. Proin congue nunc sed elit gravida, nec consectetur lectus sodales. Etiam tincidunt convallis ipsum at vestibulum. Quisque maximus enim et mauris porttitor, et molestie magna tristique. Morbi vitae metus elit. Maecenas sed volutpat turpis. Aliquam vitae dolor vestibulum, elementum purus eget, dapibus nibh. Nullam egestas dui ac rutrum semper. + +Etiam hendrerit est metus, et condimentum metus aliquam ac. Pellentesque id neque id ipsum rhoncus vulputate. Aliquam erat nisl, posuere sit amet ligula ac, fermentum blandit felis. Vivamus fermentum mi risus, non lacinia purus viverra id. Aenean ac sapien consequat, finibus mauris nec, porta sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed quis consectetur ex, dignissim bibendum nulla. Phasellus ac libero at quam vehicula euismod non eu leo. Phasellus a sapien augue. + +Maecenas ligula dui, bibendum vitae mauris et, auctor laoreet felis. Duis non libero a mi semper mattis. Quisque consequat luctus massa, quis tristique eros auctor feugiat. Maecenas sodales euismod neque vitae facilisis. Nullam laoreet imperdiet velit at pellentesque. Etiam massa odio, facilisis a consequat vitae, placerat vel magna. Nunc sagittis eros nec urna fringilla, pulvinar vestibulum nibh scelerisque. Sed magna metus, cursus eu consequat et, pharetra a est. Suspendisse elementum neque a dui malesuada lacinia. Donec sed ipsum volutpat, cursus urna id, ullamcorper arcu. Maecenas laoreet nisl eget velit egestas sollicitudin. Etiam nisl turpis, mollis id dignissim vitae, tristique vehicula ante. Maecenas eget placerat est, at rutrum augue. Vivamus faucibus lacinia ullamcorper. Sed pulvinar urna sodales ante sodales, at gravida leo dictum. + +Morbi maximus, quam a lobortis bibendum, enim felis varius elit, ac vehicula elit nisl ut lacus. Quisque ut arcu augue. Praesent id turpis quam. Sed sed arcu eros. Maecenas at cursus lorem, ac eleifend nisi. Fusce mattis felis at commodo pharetra. Praesent ac commodo ipsum. Quisque finibus et eros vitae tincidunt. In hac habitasse platea dictumst. Praesent purus ipsum, luctus lobortis ornare quis, auctor eget justo. Nam vel enim sollicitudin, faucibus tortor eu, sagittis eros. Ut nec consectetur erat. Donec ultricies malesuada ligula, a hendrerit sapien volutpat in. Maecenas sed enim vitae sapien pulvinar faucibus. + +Proin semper nunc nibh, non consequat neque ullamcorper vel. Maecenas lobortis sagittis blandit. Aenean et arcu ultricies turpis malesuada malesuada. Ut quam ex, laoreet ut blandit cursus, feugiat vitae dolor. Etiam ex lacus, scelerisque vel erat vel, efficitur tincidunt magna. Morbi tristique lacinia dolor, in egestas magna ultrices vitae. Integer ultrices leo ac tempus venenatis. Praesent ac porta tortor. Vivamus ornare blandit tristique. Nulla rutrum finibus pellentesque. In non dui elementum, fermentum ipsum vel, varius magna. Pellentesque euismod tortor risus, ac pellentesque nisl faucibus eget. + +Vivamus eu enim purus. Cras ultrices rutrum egestas. Sed mollis erat nibh, at posuere nisl luctus nec. Nunc vulputate, sapien id auctor molestie, nisi diam tristique ante, non convallis tellus nibh at orci. Morbi a posuere purus, in ullamcorper ligula. Etiam elementum sit amet dui imperdiet iaculis. Proin vitae tincidunt ipsum, sit amet placerat lectus. Curabitur commodo sapien quam, et accumsan lectus fringilla non. Nullam eget accumsan enim, ac pharetra mauris. Sed quis tristique velit, vitae commodo nisi. Duis turpis dui, maximus ut risus at, finibus consequat nunc. Maecenas sed est accumsan, aliquet diam in, facilisis risus. Curabitur vehicula rutrum auctor. Nam iaculis risus pulvinar maximus viverra. Nulla vel augue et ex sagittis blandit. + +Ut sem nulla, porta ac ante ac, posuere laoreet eros. Donec sodales posuere justo a auctor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Cras mollis at orci hendrerit porta. Nullam sodales tortor tortor, non lacinia diam finibus id. Duis libero orci, suscipit ac odio et, dictum consequat ipsum. Pellentesque eu ligula sagittis, volutpat eros at, lacinia lorem. Cras euismod tellus in iaculis tempor. Quisque accumsan, magna a congue venenatis, ante ipsum aliquam lectus, at egestas enim nunc at justo. Quisque sem purus, viverra ut tristique ut, maximus id enim. Etiam quis placerat sem. In sollicitudin, lacus eu rutrum mollis, nulla eros luctus elit, vel dapibus urna purus nec urna. Phasellus egestas massa quam, ac molestie erat hendrerit a. Praesent ultrices neque ut turpis molestie auctor. Etiam molestie placerat purus, et euismod erat aliquam in. Morbi id suscipit justo. + +Proin est ante, consequat at varius a, mattis quis felis. Sed accumsan nibh sit amet ipsum elementum posuere. Vestibulum bibendum id diam sit amet gravida. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Morbi nec dolor vel ipsum dignissim hendrerit vel non ipsum. Praesent facilisis orci quis elit auctor lobortis. Phasellus cursus risus lectus, vel lobortis libero dapibus in. Quisque tristique tempus leo a pulvinar. Pellentesque a magna tincidunt, pellentesque massa nec, laoreet orci. Morbi congue ornare dolor quis commodo. Phasellus massa nisi, tincidunt at eros dictum, hendrerit lobortis urna. Maecenas porta, magna id mattis molestie, nibh tellus lobortis sem, eget tincidunt ipsum quam eu turpis. + +Ut gravida orci risus, vel rutrum mauris vehicula id. Etiam bibendum, neque a placerat condimentum, ex orci imperdiet lectus, quis dapibus arcu lacus eget lectus. Sed consequat non mi sit amet venenatis. Fusce vestibulum erat libero, eget hendrerit risus vulputate sollicitudin. Integer sed eleifend felis. Donec commodo, sem eu mattis placerat, urna odio aliquam tellus, et laoreet justo tellus eget erat. Fusce sed suscipit tortor. Nam hendrerit nibh ac nunc auctor lacinia. Pellentesque placerat condimentum ipsum, eget semper tortor hendrerit vel. Nullam non urna eu lacus pellentesque congue ut id eros. + +Nunc finibus leo in rhoncus tristique. Sed eu ipsum nec nisl egestas faucibus eget a felis. Pellentesque vitae nisi in nulla accumsan fermentum. Sed venenatis feugiat eleifend. Fusce porttitor varius placerat. Aliquam aliquet lacus sit amet mattis mollis. Sed vel nulla quis dolor suscipit vehicula ac viverra lorem. Duis viverra ipsum eget nulla ullamcorper fermentum. Mauris tincidunt arcu quis quam fringilla ornare. Donec et iaculis tortor. Nam ultricies libero vel ipsum aliquet efficitur. Morbi eget dolor aliquam, tempus sapien eget, viverra ante. Donec varius mollis ex, sed efficitur purus euismod interdum. Quisque vel sapien non neque tincidunt semper. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. + +Suspendisse sit amet purus leo. Fusce lectus lorem, aliquam ac nulla eget, imperdiet ornare eros. Nullam sem augue, varius in nisi non, sollicitudin pellentesque ante. Etiam eu odio condimentum, tempor libero et, egestas arcu. Cras pellentesque eleifend aliquet. Pellentesque non blandit ligula. Ut congue viverra rhoncus. Phasellus mattis mi ac eros placerat, eu feugiat tellus ultrices. Aenean mollis laoreet libero eu imperdiet. Cras sed pulvinar mi, ac vehicula ligula. Vestibulum sit amet ex massa. In a egestas eros. + +Mauris pretium ipsum risus, venenatis cursus ante imperdiet id. Praesent eu turpis nec risus feugiat maximus ullamcorper ac lectus. Integer placerat at mi vel dapibus. Vestibulum fermentum turpis sit amet turpis viverra, id aliquet diam suscipit. Nam nec ex sed ante ullamcorper pharetra quis sit amet risus. Sed ac faucibus velit, id feugiat nibh. Nullam eget ipsum ex. Vivamus tincidunt non nunc non faucibus. Quisque bibendum viverra facilisis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur at nisi hendrerit quam suscipit egestas. Curabitur laoreet maximus ultricies. Duis ut tellus ac augue molestie dictum. + +Suspendisse rhoncus iaculis erat, ut ullamcorper est tristique eget. Donec auctor nec risus at gravida. Vivamus volutpat vulputate tellus, vel ultricies eros suscipit eget. Ut pulvinar id mi eu tempus. Morbi malesuada augue in dui varius, nec blandit neque vehicula. Donec ornare nec nisl in mollis. Morbi enim nisi, rhoncus nec est id, dapibus tempus urna. Ut id elit a felis vestibulum consectetur. Duis lectus quam, pharetra sit amet diam sed, posuere vestibulum erat. Fusce vitae maximus massa. Nullam id metus tempus, iaculis risus eu, lobortis urna. Quisque in congue urna. Pellentesque placerat neque in augue dapibus, non varius ex malesuada. Curabitur ut eleifend libero. Fusce vitae ligula luctus, fermentum enim vitae, ultrices erat. + +Sed viverra augue turpis, scelerisque egestas sapien mattis eu. Duis laoreet magna at ex pharetra dapibus. Praesent eget odio vel quam venenatis dictum. Nulla in sollicitudin dolor. Mauris lobortis nec eros vel rhoncus. Vestibulum porta viverra venenatis. Curabitur vel scelerisque quam, a egestas velit. Praesent volutpat tincidunt magna at laoreet. + +Cras nec lorem odio. Pellentesque quis dui urna. Praesent at tellus ac lectus scelerisque placerat nec eu risus. Vestibulum sit amet mattis ligula. Vivamus sed nisi at leo elementum accumsan at sit amet arcu. Aenean mattis tellus nec leo gravida, eget hendrerit nisl faucibus. Mauris pellentesque luctus condimentum. Maecenas pretium sapien nunc, eget commodo dolor maximus id. Mauris vestibulum accumsan massa a dictum. Phasellus interdum quam ligula, ut maximus diam blandit aliquam. Nunc vitae ex eu erat condimentum consectetur. Maecenas interdum condimentum volutpat. + +Donec et enim a libero rutrum laoreet. Praesent a condimentum sem, at tincidunt quam. In vel molestie risus. Sed urna dui, molestie vitae mollis laoreet, tempor quis lectus. Praesent vitae auctor est, et aliquet nunc. Curabitur vulputate blandit nulla, at gravida metus. Maecenas gravida dui eu iaculis tristique. Pellentesque posuere turpis nec auctor eleifend. Suspendisse bibendum diam eu tellus lobortis, et laoreet quam congue. In hac habitasse platea dictumst. Morbi dictum neque velit, eget rutrum eros ultrices sit amet. + +Phasellus fermentum risus pharetra consectetur bibendum. Donec magna tortor, lacinia vitae nibh quis, aliquet pretium lorem. Donec turpis nisi, pretium eu enim volutpat, mattis malesuada augue. Nullam vel tellus iaculis, sollicitudin elit eget, tincidunt lacus. Fusce elementum elementum felis et iaculis. Suspendisse porta eros nec neque malesuada, in malesuada ante sollicitudin. Vivamus bibendum viverra molestie. + +Integer feugiat, erat nec convallis aliquam, velit felis congue erat, molestie eleifend tellus erat in tellus. Nunc et justo purus. Donec egestas fermentum dui non feugiat. Quisque in sapien sagittis, gravida quam id, iaculis lectus. Cras sagittis rhoncus bibendum. Fusce quis metus in velit scelerisque tincidunt at non ipsum. Vivamus efficitur ante eu odio vulputate, vitae ultricies risus vehicula. Proin eget odio eu sem tincidunt feugiat vel id lorem. + +Vestibulum sit amet nulla dignissim, euismod mi in, fermentum tortor. Donec ut aliquet libero, lacinia accumsan velit. Donec et nulla quam. Nullam laoreet odio nec nunc imperdiet, a congue eros venenatis. Quisque nec tellus sit amet neque interdum posuere. Duis quis mi gravida, tincidunt diam convallis, ultricies augue. Mauris consequat risus non porttitor congue. Ut in ligula consequat, viverra nunc a, eleifend enim. Duis ligula urna, imperdiet nec facilisis et, ornare eu ex. Proin lobortis lectus a lobortis porttitor. Nulla leo metus, egestas eu libero sed, pretium faucibus felis. Vestibulum non sem tortor. Nam cursus est leo. Vivamus luctus enim odio, non interdum sem dapibus a. Aenean accumsan consequat lectus in imperdiet. + +Donec vehicula laoreet ipsum in posuere. Quisque vel quam imperdiet, sollicitudin nisi quis, suscipit velit. Morbi id sodales mauris. Curabitur tellus arcu, feugiat sed dui sit amet, sodales sagittis libero. Aenean vel suscipit metus, non placerat leo. Vestibulum quis nulla elit. Proin scelerisque non ante ut commodo. Interdum et malesuada fames ac ante ipsum primis in faucibus. + +Sed non urna dolor. Suspendisse convallis mi porta pulvinar ultrices. Suspendisse quam ipsum, hendrerit non scelerisque molestie, interdum dictum nunc. Morbi condimentum condimentum turpis eu luctus. Pellentesque sagittis sollicitudin odio, sed ultricies felis ornare sit amet. Sed ultrices ex leo, a tincidunt nisl gravida sed. Nullam ornare accumsan porta. Praesent consectetur id est nec sollicitudin. + +In hac habitasse platea dictumst. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed sed ultrices nibh. Duis accumsan suscipit eros, a dictum odio tempus sit amet. Aenean imperdiet erat ac lacus finibus, scelerisque cursus massa imperdiet. Mauris molestie risus ut lacinia posuere. Nulla et sodales purus. Maecenas orci erat, placerat in tristique quis, placerat in mi. + +Donec sollicitudin pellentesque odio in feugiat. Morbi eu dolor ut mauris congue sollicitudin. Aliquam erat volutpat. Nulla id varius dui. Curabitur finibus urna ante, consectetur interdum nisi volutpat a. Quisque quis mi tristique, consequat tellus eget, rutrum sapien. Vivamus vitae tellus vulputate, rutrum ex eu, vulputate sem. Suspendisse viverra lorem tellus, vel interdum orci gravida quis. Ut laoreet arcu at mi ullamcorper finibus. Duis porta sagittis vestibulum. Sed commodo nisl vitae urna sollicitudin, nec lacinia est sodales. Curabitur imperdiet sodales dui sed iaculis. Sed ac tellus maximus, eleifend quam sit amet, feugiat elit. Aenean viverra, dui at mattis varius, est odio vestibulum sapien, sit amet mollis libero massa nec velit. Etiam quis sodales justo. + +Ut ultricies, sem eget sodales feugiat, nunc arcu congue elit, ac tempor justo massa nec purus. Maecenas enim nunc, pharetra eget dictum sit amet, tempus pellentesque velit. Suspendisse venenatis ligula in nulla mattis, et imperdiet ex tincidunt. Etiam vulputate, tellus et ultrices suscipit, enim velit laoreet massa, vitae congue odio enim ac urna. Morbi quam lorem, iaculis ac varius sagittis, euismod quis dolor. In ut dui eu purus feugiat consectetur. Vestibulum cursus velit quis lacus pellentesque iaculis. Cras in risus sed mauris porta rutrum. Nulla facilisi. Nullam eu bibendum est, non pellentesque lectus. Sed imperdiet feugiat lorem, quis convallis ante auctor in. Maecenas justo magna, scelerisque sit amet tellus eget, varius elementum risus. Duis placerat et quam sed varius. + +Duis nec nibh vitae nibh dignissim mollis quis sed felis. Curabitur vitae quam placerat, venenatis purus ut, euismod nisl. Curabitur porttitor nibh eu pulvinar ullamcorper. Suspendisse posuere nec ipsum ac dapibus. Cras convallis consectetur urna. Phasellus a nibh in dolor lacinia posuere id eget augue. In eu pharetra lorem, vitae cursus lacus. Aliquam tincidunt nibh lectus. Aenean facilisis ultricies posuere. Sed ut placerat orci. Curabitur scelerisque gravida blandit. Maecenas placerat ligula eget suscipit fringilla. Mauris a tortor justo. Aliquam hendrerit semper mollis. Phasellus et tincidunt libero. Etiam vel quam libero. + +Quisque aliquet tempor ex. Ut ante sem, vehicula at enim vel, gravida porta elit. Etiam vitae lacus a neque lobortis consectetur. Mauris sed interdum odio. Mauris elementum ex blandit tempor cursus. Integer in enim in leo viverra elementum. Fusce consectetur metus et sem rutrum, mattis euismod diam semper. Nunc sed ipsum vel urna consequat vehicula. Donec cursus pretium lorem, vestibulum pretium felis commodo sit amet. Nam blandit felis enim, eget gravida ex faucibus a. In nec neque massa. Etiam laoreet posuere ipsum. Praesent volutpat nunc dolor, ac vulputate magna facilisis non. Aenean congue turpis vel lectus sollicitudin tristique. Sed nec consequat purus, non vehicula quam. Etiam ultricies, est ac dictum tincidunt, turpis turpis pretium massa, a vulputate libero justo at nibh. + +Aliquam erat volutpat. Cras ultrices augue ac sollicitudin lobortis. Curabitur et aliquet purus. Duis feugiat semper facilisis. Phasellus lobortis cursus velit, a sollicitudin tortor. Nam feugiat sapien non dapibus condimentum. Morbi at mi bibendum, commodo quam at, laoreet enim. Integer eu ultrices enim. Sed vestibulum eu urna ut dictum. Curabitur at mattis leo, sed cursus massa. Aliquam porttitor, felis quis fermentum porttitor, justo velit feugiat nulla, eget condimentum sem dui ut sapien. + +In fringilla elit eu orci aliquam consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Ut eget fringilla tellus. Curabitur fermentum, mi et condimentum suscipit, elit neque bibendum dui, et hendrerit nunc metus id ipsum. Morbi placerat mi in hendrerit congue. Ut feugiat mauris eget scelerisque viverra. Vivamus sit amet erat dictum, sagittis lectus nec, pulvinar lorem. Sed non enim ac dui sollicitudin aliquet. Quisque ut lacus dolor. Fusce hendrerit malesuada euismod. Nulla faucibus vel mauris eu mollis. Mauris est diam, fringilla ac arcu feugiat, efficitur volutpat turpis. Aliquam venenatis cursus massa sed porttitor. Ut ac finibus enim, in tincidunt sapien. + +Nunc faucibus semper turpis a lacinia. Phasellus gravida, libero vel pulvinar ornare, ex sem tincidunt lectus, sit amet convallis augue risus at tortor. Quisque sit amet ipsum id nulla posuere vestibulum. Pellentesque scelerisque mauris vel leo viverra sodales. Nulla viverra aliquam ex, ut rutrum enim fermentum venenatis. Aenean eget dapibus ex, eget faucibus metus. Vestibulum volutpat leo in diam semper, eget porta magna suscipit. Sed sit amet nulla blandit, aliquam dolor ac, gravida velit. Sed vel velit viverra, maximus est id, convallis justo. + +Curabitur nulla ante, vulputate at libero vel, ullamcorper rutrum nibh. Pellentesque porttitor eu mauris id mattis. Duis vulputate augue elit, eget interdum justo pretium vel. Maecenas eu vulputate arcu, eget posuere purus. Suspendisse viverra a velit dictum eleifend. Suspendisse vitae dapibus diam. Donec vehicula justo in ante interdum, eu luctus diam placerat. Vivamus convallis ipsum eu orci suscipit, sed fermentum enim euismod. Maecenas faucibus elit vitae ex ornare tristique. Donec vestibulum nec elit sit amet porttitor. Aenean tempor lectus eget tortor hendrerit luctus. Nullam interdum vitae lectus vel feugiat. Cras in risus non magna consectetur lobortis. Sed faucibus enim quis gravida convallis. + +Phasellus eget massa sit amet libero ultrices suscipit. Vivamus at risus sapien. Nam mollis nunc eget velit dictum maximus. Sed pellentesque, nunc ac fringilla lacinia, quam enim mattis ex, sed euismod tortor metus eu neque. Ut mattis nisl ut lectus rhoncus, sodales bibendum eros porta. Nulla porttitor enim nec diam sagittis, eget porta velit efficitur. Vestibulum ultricies eros neque. Phasellus rutrum suscipit enim, in interdum ante gravida vitae. Sed in sagittis diam, non commodo velit. + +Morbi hendrerit odio orci, nec tincidunt odio rhoncus nec. Mauris neque velit, vehicula a lorem at, suscipit tristique dui. Sed finibus, nisl in mattis convallis, turpis neque sodales lacus, eu porta enim magna non diam. Nam commodo sodales risus consectetur malesuada. In eget elementum justo. Phasellus sit amet massa imperdiet, dapibus nunc sit amet, suscipit orci. Fusce condimentum laoreet feugiat. Ut ut viverra ante. Praesent bibendum interdum commodo. Nulla mollis nisi a est ornare volutpat. Sed at ligula eu nisi dapibus tempus. Proin cursus vestibulum justo, nec efficitur justo dignissim vel. Nunc quis maximus eros. + +Cras viverra, diam a tristique mattis, libero felis vulputate tellus, a ornare felis leo a dui. Nulla ante nulla, finibus ut tellus ut, blandit pharetra nibh. Proin eleifend fermentum ex, eget auctor libero vulputate in. Nullam ultricies, mauris placerat pretium placerat, leo urna lobortis leo, vel placerat arcu libero sed mauris. Aliquam mauris ligula, ornare at urna at, eleifend gravida ligula. Vestibulum consectetur ut nulla non scelerisque. Donec ornare, sem nec elementum aliquam, urna nulla bibendum metus, eu euismod dui ligula ac est. Fusce laoreet erat eu ex lobortis, quis bibendum ligula interdum. Sed vel mi erat. Vivamus id lacus ac enim mattis tempor. Nunc ultricies pellentesque enim sed euismod. Fusce tincidunt convallis elit quis aliquam. Mauris nulla ipsum, sollicitudin quis diam ac, feugiat volutpat tellus. In nibh nibh, vulputate quis tincidunt quis, pulvinar eget magna. Pellentesque quis finibus dolor. Suspendisse viverra vitae lectus non eleifend. + +Nunc ut orci et sapien maximus semper. Nulla dignissim sem urna, ac varius lectus ultricies id. Quisque aliquet pulvinar pretium. In ultricies molestie tellus vehicula porta. Nam enim lorem, aliquam eget ex et, hendrerit volutpat quam. Maecenas diam lacus, pellentesque eget tempus ac, pharetra eu elit. Donec vel eros a sem facilisis vulputate. Nullam ac nisi vulputate, laoreet nisl ac, eleifend sem. Nullam mi massa, rhoncus sed pharetra interdum, tincidunt eget nunc. Aliquam viverra mattis posuere. Mauris et dui sed nisl sollicitudin fermentum quis ut arcu. Nam placerat eget orci at tincidunt. Curabitur vel turpis metus. Phasellus nibh nulla, fermentum scelerisque sem vel, gravida tincidunt velit. Pellentesque vel quam tempor, finibus massa pellentesque, condimentum dui. + +Donec at mattis neque. Etiam velit diam, consequat auctor mauris id, hendrerit faucibus metus. Maecenas ullamcorper eros a est sodales, ac consectetur odio scelerisque. Donec leo metus, imperdiet at pellentesque vel, feugiat id erat. Suspendisse at magna enim. Vestibulum placerat sodales lorem id sollicitudin. Aenean at euismod ligula, eget mollis diam. Phasellus pulvinar, orci nec pretium condimentum, est erat facilisis purus, quis feugiat augue elit aliquam nulla. Aenean vitae tortor id risus congue tincidunt. Sed dolor enim, mattis a ullamcorper id, volutpat ac leo. + +Proin vehicula feugiat augue, id feugiat quam sodales quis. Donec et ultricies massa, a lacinia nulla. Duis aliquam augue ornare euismod viverra. Ut lectus risus, rutrum sit amet efficitur a, luctus nec nisl. Cras volutpat ullamcorper congue. Sed vitae odio metus. Phasellus aliquet euismod varius. + +Nullam sem ex, malesuada ut magna ut, pretium mollis arcu. Nam porttitor eros cursus mi lacinia faucibus. Suspendisse aliquet eleifend iaculis. Maecenas sit amet viverra tortor. Nunc a mollis risus. Etiam tempus dolor in tortor malesuada mattis. Ut tincidunt venenatis est sit amet dignissim. Vestibulum massa enim, tristique sed scelerisque eu, fringilla ac velit. Donec efficitur quis urna sit amet malesuada. Vestibulum consequat ac ligula in dapibus. Maecenas massa massa, molestie non posuere nec, elementum ut magna. In nisi erat, mollis non venenatis eu, faucibus in justo. Morbi gravida non ex non egestas. Pellentesque finibus laoreet diam, eu commodo augue congue vitae. + +Aenean sem mi, ullamcorper dapibus lobortis vitae, interdum tincidunt tortor. Vivamus eget vulputate libero. Ut bibendum posuere lectus, vel tincidunt tortor aliquet at. Phasellus malesuada orci et bibendum accumsan. Aliquam quis libero vel leo mollis porta. Sed sagittis leo ac lacus dictum, ac malesuada elit finibus. Suspendisse pharetra luctus commodo. Vivamus ultricies a odio non interdum. Vivamus scelerisque tincidunt turpis quis tempor. Pellentesque tortor ligula, varius non nunc eu, blandit sollicitudin neque. Nunc imperdiet, diam et tristique luctus, ipsum ex condimentum nunc, sit amet aliquam justo velit sed libero. Duis vel suscipit ligula. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed tincidunt neque vel massa ultricies, id dictum leo consequat. Curabitur lobortis ultricies tellus, eget mattis nisl aliquam sit amet. + +Proin at suscipit justo. Vivamus ut vestibulum nisl. Pellentesque enim odio, pharetra non magna sed, efficitur auctor magna. Praesent tincidunt ante quis ante hendrerit viverra. Pellentesque vel ipsum id magna vulputate efficitur. Sed nec neque accumsan, pulvinar sapien quis, euismod mauris. Donec condimentum laoreet sapien quis gravida. Quisque sed mattis purus. Vestibulum placerat vel neque maximus scelerisque. + +Vestibulum mattis quam quis efficitur elementum. Duis dictum dolor ac scelerisque commodo. Fusce sollicitudin nisi sit amet dictum placerat. Suspendisse euismod pharetra eleifend. In eros nisl, porttitor sed mauris at, consectetur aliquet mauris. Donec euismod viverra neque sed fermentum. Phasellus libero magna, accumsan ut ultricies vitae, dignissim eget metus. Donec tellus turpis, interdum eget maximus nec, hendrerit eget massa. Curabitur auctor ligula in iaculis auctor. In ultrices quam suscipit cursus finibus. Aenean id mi at dolor interdum iaculis vitae ut lorem. Nullam sed nibh fringilla, lacinia odio nec, placerat erat. In dui libero, viverra ac viverra ac, pellentesque sit amet turpis. + +Nulla in enim ex. Sed feugiat est et consectetur venenatis. Cras varius facilisis dui vel convallis. Vestibulum et elit eget tellus feugiat pellentesque. In ut ante eu purus aliquet posuere. Nulla nec ornare sem, sed luctus lorem. Nam varius iaculis odio, eget faucibus nisl ullamcorper in. Sed eget cursus felis, nec efficitur nisi. + +Vivamus commodo et sem quis pulvinar. Pellentesque libero ante, venenatis vitae ligula sit amet, ornare sollicitudin nulla. Mauris eget tellus hendrerit, pulvinar metus quis, tempor nisi. Proin magna ex, laoreet sed tortor quis, varius fermentum enim. Integer eu dolor dictum, vulputate tortor et, aliquet ligula. Vestibulum vitae justo id mauris luctus sollicitudin. Suspendisse eget auctor neque, sodales egestas lorem. Vestibulum lacinia egestas metus vitae euismod. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vivamus ex tellus, volutpat nec pulvinar sit amet, condimentum vitae dui. Curabitur vel felis sodales, lacinia nunc iaculis, ullamcorper augue. Pellentesque consequat dolor quis eros efficitur malesuada. Nulla ut malesuada lectus. + +Morbi et tristique ante. Aliquam erat volutpat. Vivamus vitae dui nec turpis pellentesque fermentum. Quisque eget velit massa. Pellentesque tristique aliquam nisl, eu sollicitudin justo venenatis sed. Duis eleifend sem eros, ut aliquam libero porttitor id. Sed non nunc consequat, rhoncus diam eu, commodo erat. Praesent fermentum in lectus id blandit. Donec quis ipsum at justo volutpat finibus. Nulla blandit justo nulla, at mollis lacus consequat eget. Aenean sollicitudin quis eros ut ullamcorper. + +Pellentesque venenatis nulla ut mi aliquet feugiat. Cras semper vel magna nec pharetra. Integer mattis felis et sapien commodo imperdiet. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Duis quis luctus felis. Vestibulum justo nibh, aliquam non lectus vitae, molestie placerat justo. Donec lorem nibh, gravida sit amet hendrerit ac, maximus id ipsum. Nunc ac libero sodales risus eleifend sagittis. Phasellus est massa, lobortis elementum ex sed, scelerisque consectetur neque. Nunc faucibus neque id lorem malesuada, eget convallis ex mattis. + +Sed turpis tortor, fermentum non turpis id, posuere varius nibh. Donec iaculis lorem dui. Etiam eros ante, sodales eget venenatis at, consectetur eget risus. Curabitur non aliquam ante, a pretium justo. Maecenas tempor nisl tortor, vitae dictum nisi ultrices eu. Duis eget dui ultrices, porttitor lacus sed, lobortis purus. Quisque mattis elit nec neque sagittis, sed commodo leo blandit. Mauris sodales interdum eleifend. Vestibulum condimentum consectetur augue, id luctus diam convallis et. + +Nunc suscipit risus in justo accumsan, a placerat magna tincidunt. Proin a nisl ipsum. Sed libero dui, tristique in augue quis, auctor tristique risus. Sed porttitor ex augue, eu porta augue molestie a. Duis rhoncus purus libero, eu tempus turpis condimentum at. Sed mollis nisi id lectus placerat tincidunt. Maecenas non scelerisque elit, quis rutrum orci. Donec in tellus pharetra urna ornare lobortis. Phasellus id risus at nisi varius rutrum eu ut turpis. + +Duis dictum justo quis nisl porta, eget tincidunt magna suscipit. Sed velit massa, ullamcorper eu sodales ac, pretium a massa. Duis et rutrum tortor. Nulla accumsan hendrerit sapien, cursus volutpat eros egestas eget. Donec sollicitudin at ante quis sollicitudin. Aenean blandit feugiat diam, id feugiat eros faucibus eget. Donec viverra dolor vel justo scelerisque dignissim. Nulla semper sem nunc, rhoncus semper tellus ultricies sed. Duis in ornare diam. Donec vehicula feugiat varius. Maecenas ut suscipit est. Vivamus sem sem, finibus at dolor sit amet, euismod dapibus ligula. Vestibulum fringilla odio dapibus, congue massa eget, congue sem. Donec feugiat magna eget tortor lacinia scelerisque non et ipsum. + +Suspendisse potenti. Nunc convallis sollicitudin ex eget venenatis. Sed iaculis nibh ex, vel ornare ligula congue dignissim. Quisque sollicitudin dolor ac dui vestibulum, sit amet molestie nisi aliquet. Donec at risus felis. Aenean sollicitudin metus a feugiat porta. Aenean a tortor ut dolor cursus sagittis. Vivamus consectetur porttitor nunc in facilisis. Proin sit amet mi vel lectus consectetur ultrices. + +Sed cursus lectus vitae nunc tristique, nec commodo turpis dapibus. Pellentesque luctus ex id facilisis ornare. Morbi quis placerat dolor. Donec in lectus in arcu mattis porttitor ac sit amet metus. Cras congue mauris non risus sodales, vitae feugiat ipsum bibendum. Nulla venenatis urna sed libero elementum, a cursus lorem commodo. Mauris faucibus lobortis eros nec commodo. + +Nullam suscipit ligula ullamcorper lorem commodo blandit. Nulla porta nibh quis pulvinar placerat. Vivamus eu arcu justo. Vestibulum imperdiet est ut fermentum porttitor. Pellentesque consectetur libero in sapien efficitur scelerisque. Curabitur ac erat sit amet odio aliquet dignissim. Pellentesque mi sem, rhoncus et luctus at, porttitor rutrum lectus. Vestibulum sollicitudin sollicitudin suscipit. Aenean efficitur dolor non ultrices imperdiet. Donec vel sem ex. + +Sed convallis mauris aliquam rutrum cursus. Ut tempor porttitor sodales. Etiam eu risus ac augue gravida egestas et eu dolor. Proin id magna ex. Suspendisse quis lectus quis lorem ultricies tempus. Donec porttitor velit vitae tincidunt faucibus. Aliquam vitae semper nisi. Morbi ultrices, leo non pretium dapibus, dui libero pellentesque ex, vel placerat enim ante vitae dui. Nunc varius, sem sit amet sagittis lobortis, lectus odio scelerisque mauris, ut vestibulum orci magna quis neque. Sed id congue justo. Interdum et malesuada fames ac ante ipsum primis in faucibus. Mauris congue nisi est, malesuada mollis elit tincidunt sed. Curabitur sed ex sit amet felis tristique elementum vitae vel nibh. + +Etiam mollis pretium lobortis. Mauris augue lacus, efficitur at lacus sed, mollis tincidunt lectus. Aliquam erat volutpat. Donec at euismod elit, et mattis felis. Sed id lobortis urna. Morbi imperdiet vestibulum leo, sed maximus leo blandit eu. Aliquam semper lorem neque, nec euismod turpis mattis mollis. Quisque lobortis urna ultrices odio pretium, ac venenatis orci faucibus. Suspendisse bibendum odio ligula, sed lobortis massa pharetra nec. Donec turpis justo, iaculis at dictum ac, finibus eu libero. Maecenas quis porttitor mi, sit amet aliquet neque. + +Vivamus auctor vulputate ante, at egestas lorem. Donec eu risus in nulla mollis ultricies at et urna. Duis accumsan porta egestas. Ut vel euismod augue. Fusce convallis nulla ante, nec fringilla velit aliquet at. Nam malesuada dapibus ligula, a aliquam nibh scelerisque ac. Praesent malesuada neque et pellentesque interdum. Curabitur volutpat at turpis vitae tristique. Vivamus porttitor semper congue. Quisque suscipit lacus mi, rhoncus ultrices tortor auctor quis. Maecenas neque neque, molestie ac facilisis eget, luctus ac lorem. In ut odio ut lacus suscipit pulvinar vitae sed elit. Nulla imperdiet, sem quis euismod sagittis, dui erat luctus dolor, faucibus faucibus erat sem eget nunc. Nam accumsan placerat malesuada. Maecenas convallis finibus pulvinar. + +Cras at placerat tortor. Morbi facilisis auctor felis sit amet molestie. Donec sodales sed lorem vitae suscipit. Etiam fermentum pharetra ipsum, nec luctus orci gravida eu. Pellentesque gravida, est non condimentum tempus, mauris ligula molestie est, in congue dolor nisl vel sapien. Duis congue tempor augue, id rutrum eros porta dapibus. Etiam rutrum eget est eget vestibulum. Aenean mollis arcu vel consequat varius. Praesent at condimentum felis. Duis nec interdum nisl. Donec commodo lorem sed sapien scelerisque malesuada non eu urna. In blandit non ipsum at porta. Nam lobortis leo vitae dui auctor, non feugiat quam bibendum. Donec auctor lectus sagittis laoreet maximus. Maecenas rhoncus laoreet porttitor. Vestibulum porttitor augue ut lectus hendrerit, eget posuere mi gravida. + +Sed mattis ex in erat pulvinar, eu imperdiet magna dapibus. Etiam nisi nibh, tempus non tellus sit amet, mattis tempor odio. Quisque nec lorem feugiat, lobortis odio et, commodo nunc. Maecenas semper purus nisi, nec vehicula nibh eleifend vitae. Nulla fermentum a lectus at maximus. Phasellus finibus metus non euismod ultrices. Etiam a pulvinar ante. Quisque convallis nec metus sit amet facilisis. Praesent laoreet massa et sollicitudin laoreet. Vestibulum in mauris aliquet, convallis mi ut, elementum purus. Nulla purus nulla, sodales at hendrerit quis, tempus sed lectus. + +Nam ut laoreet neque, ut maximus nibh. Maecenas quis justo pellentesque, sollicitudin elit at, venenatis velit. Aenean nunc velit, vehicula scelerisque odio at, consectetur laoreet purus. Duis dui purus, malesuada quis ipsum sit amet, tempor interdum libero. Curabitur porta scelerisque sapien, vitae cursus diam condimentum eu. Phasellus sed orci quam. Nullam vitae dui quis purus tincidunt vestibulum. Curabitur quis nulla porta, cursus arcu non, auctor enim. Etiam sollicitudin ex id sem vehicula mollis. Morbi viverra laoreet tincidunt. Praesent ut semper dui. Nam sit amet pretium neque. Mauris vitae luctus diam, in lacinia purus. Maecenas ut placerat justo, ut porta felis. Integer eu mauris ante. + +Aenean porttitor tellus diam, tempor consequat metus efficitur id. Suspendisse ut felis at erat tempor dictum at nec sapien. Sed vestibulum interdum felis, ac mattis mauris porta in. Nunc et condimentum massa. Sed cursus dictum justo et luctus. Integer convallis enim nisl, a rutrum lectus ultricies in. Donec dapibus lacus at nulla dapibus, id sollicitudin velit hendrerit. Fusce a magna at orci mollis rutrum ac a dolor. Aliquam erat volutpat. Morbi varius porta nunc, sit amet sodales ex hendrerit commodo. Donec tincidunt tortor sapien, vitae egestas sapien vehicula eget. + +Suspendisse potenti. Donec pulvinar felis nec leo malesuada interdum. Integer posuere placerat maximus. Donec nibh ipsum, tincidunt vitae luctus vitae, bibendum at leo. Sed cursus nisl ut ex faucibus aliquet sed nec eros. Curabitur molestie posuere felis. Integer faucibus velit eget consequat iaculis. Mauris sed vulputate odio. Phasellus maximus, elit a pharetra egestas, lorem magna semper tellus, vestibulum semper diam felis at sapien. Suspendisse facilisis, nisl sit amet euismod vehicula, libero nulla vehicula dolor, quis fermentum nibh elit sit amet diam. + +Morbi lorem enim, euismod eu varius ut, scelerisque quis odio. Nam tempus vitae eros id molestie. Nunc pretium in nulla eget accumsan. Quisque mattis est ut semper aliquet. Maecenas eget diam elementum, fermentum ipsum a, euismod sapien. Duis quam ligula, cursus et velit nec, ullamcorper tincidunt magna. Donec vulputate nisl est, et ullamcorper urna tempor sit amet. + +Proin lacinia dui non turpis congue pretium. Morbi posuere metus vel purus imperdiet interdum. Morbi venenatis vel eros non ultricies. Nulla vel semper elit. Ut quis purus tincidunt, auctor justo ut, faucibus turpis. Proin quis mattis erat, at faucibus ligula. Mauris in mauris enim. Donec facilisis enim at est feugiat hendrerit. Nam vel nisi lorem. Fusce ultricies convallis diam, in feugiat tortor luctus quis. Donec tempor, leo vitae volutpat aliquam, magna elit feugiat leo, quis placerat sapien felis eget arcu. Donec ornare fermentum eleifend. Integer a est orci. + +Proin rhoncus egestas leo. Nulla ultricies porta elit quis ornare. Nunc fermentum interdum vehicula. In in ligula lorem. Donec nec arcu sit amet orci lobortis iaculis. Mauris at mollis erat, sit amet mollis tortor. Mauris laoreet justo ullamcorper porttitor auctor. Aenean sit amet aliquam lectus, id fermentum eros. Praesent urna sem, vehicula ac fermentum id, dapibus ut purus. Vestibulum vitae tempus nunc. Donec at nunc ornare metus volutpat porta at eget magna. Donec varius aliquet metus, eu lobortis risus aliquam sed. Ut dapibus fermentum velit, ac tincidunt libero faucibus at. + +In in purus auctor, feugiat massa quis, facilisis nisi. Donec dolor purus, gravida eget dolor ac, porttitor imperdiet urna. Donec faucibus placerat erat, a sagittis ante finibus ac. Sed venenatis dignissim elit, in iaculis felis posuere faucibus. Praesent sed viverra dolor. Mauris sed nulla consectetur nunc laoreet molestie in ut metus. Proin ac ex sit amet magna vulputate hendrerit ac condimentum urna. Proin ligula metus, gravida et sollicitudin facilisis, iaculis ut odio. Cras tincidunt urna et augue varius, ut facilisis urna consequat. Aenean vehicula finibus quam. Ut iaculis eu diam ac mollis. Nam mi lorem, tristique eget varius at, sodales at urna. + +Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin vitae dictum erat, et auctor ipsum. Nullam nunc nunc, sollicitudin quis magna a, vestibulum fermentum mauris. Praesent at erat dolor. Proin laoreet tristique nulla vel efficitur. Nam sed ultrices nibh, id rutrum nunc. Curabitur eleifend a erat sit amet sollicitudin. Nullam metus quam, laoreet vitae dapibus id, placerat sed leo. Aliquam erat volutpat. Donec turpis nisl, cursus eu ex sit amet, lacinia pellentesque nisl. Sed id ipsum massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec interdum scelerisque lorem eu mattis. + +Vivamus ac tristique massa, nec facilisis nisl. Nam ipsum neque, tincidunt vel urna in, cursus imperdiet enim. Nam pellentesque egestas tempus. Morbi facilisis imperdiet libero vitae fringilla. Nam lacinia ligula at sapien facilisis malesuada. Nullam accumsan pulvinar sem, et cursus libero porta sit amet. Curabitur vulputate erat elit, ut pulvinar erat maximus vel. + +Cras aliquet metus ut purus sagittis, vel venenatis ante consectetur. Pellentesque nulla lacus, viverra viverra mattis non, placerat vitae nibh. Donec enim turpis, accumsan sit amet tincidunt eu, imperdiet non metus. Morbi ipsum eros, tincidunt vel est ac, tristique porttitor nibh. Praesent ut ullamcorper mauris. Sed laoreet sit amet diam congue venenatis. Integer porta purus nec orci sagittis posuere. + +Donec vehicula mauris eget lacus mollis venenatis et sed nibh. Nam sodales ligula ipsum, scelerisque lacinia ligula sagittis in. Nam sit amet ipsum at erat malesuada congue. Aenean ut sollicitudin sapien. Etiam at tempor odio. Mauris vitae purus ut magna suscipit consequat. Vivamus quis sapien neque. Nulla vulputate sem sit amet massa pellentesque, eleifend tristique ligula egestas. Suspendisse tincidunt gravida mi, in pulvinar lectus egestas non. Aenean imperdiet ex sit amet nunc sollicitudin porta. Integer justo odio, ultricies at interdum in, rhoncus vitae sem. Sed porttitor arcu quis purus aliquet hendrerit. Praesent tempor tortor at dolor dictum pulvinar. Nulla aliquet nunc non ligula scelerisque accumsan. Donec nulla justo, congue vitae massa in, faucibus hendrerit magna. Donec non egestas purus. + +`), + tail: VSBuffer.fromString(`Vivamus iaculis, lacus efficitur faucibus porta, dui nulla facilisis ligula, ut sodales odio nunc id sapien. Cras viverra auctor ipsum, dapibus mattis neque dictum sed. Sed convallis fermentum molestie. Nulla facilisi turpis duis.`) + }; +} + diff --git a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts index 1382a73a73f..4ebd22cabf1 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts @@ -146,6 +146,40 @@ suite('Files - TextFileEditorModel', () => { assert.ok(!accessor.modelService.getModel(model.resource)); }); + test('save - touching with error turns model dirty', async function () { + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); + + await model.load(); + + let saveErrorEvent = false; + model.onDidSaveError(() => saveErrorEvent = true); + + let savedEvent = false; + model.onDidSave(() => savedEvent = true); + + accessor.fileService.writeShouldThrowError = new Error('failed to write'); + try { + await model.save({ force: true }); + + assert.ok(model.hasState(TextFileEditorModelState.ERROR)); + assert.ok(model.isDirty()); + assert.ok(saveErrorEvent); + + assert.equal(accessor.workingCopyService.dirtyCount, 1); + assert.equal(accessor.workingCopyService.isDirty(model.resource), true); + } finally { + accessor.fileService.writeShouldThrowError = undefined; + } + + await model.save({ force: true }); + + assert.ok(savedEvent); + assert.ok(!model.isDirty()); + + model.dispose(); + assert.ok(!accessor.modelService.getModel(model.resource)); + }); + test('save error (generic)', async function () { const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); diff --git a/src/vs/workbench/services/textfile/test/browser/textFileEditorModelManager.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileEditorModelManager.test.ts index ec0904baeb6..df569b8ccf4 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileEditorModelManager.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileEditorModelManager.test.ts @@ -7,13 +7,15 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; -import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestServiceAccessor, TestTextFileEditorModelManager } from 'vs/workbench/test/browser/workbenchTestServices'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files'; import { toResource } from 'vs/base/test/common/utils'; import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; +import { extUri } from 'vs/base/common/resources'; +import { timeout } from 'vs/base/common/async'; suite('Files - TextFileEditorModelManager', () => { @@ -26,7 +28,7 @@ suite('Files - TextFileEditorModelManager', () => { }); test('add, remove, clear, get, getAll', function () { - const manager: TextFileEditorModelManager = instantiationService.createInstance(TextFileEditorModelManager); + const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); const model1: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random1.txt'), 'utf8', undefined); const model2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random2.txt'), 'utf8', undefined); @@ -81,7 +83,7 @@ suite('Files - TextFileEditorModelManager', () => { }); test('resolve', async () => { - const manager: TextFileEditorModelManager = instantiationService.createInstance(TextFileEditorModelManager); + const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); const resource = URI.file('/test.html'); const encoding = 'utf8'; @@ -115,7 +117,7 @@ suite('Files - TextFileEditorModelManager', () => { }); test('removed from cache when model disposed', function () { - const manager: TextFileEditorModelManager = instantiationService.createInstance(TextFileEditorModelManager); + const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); const model1: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random1.txt'), 'utf8', undefined); const model2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random2.txt'), 'utf8', undefined); @@ -184,8 +186,8 @@ suite('Files - TextFileEditorModelManager', () => { const model1 = await manager.resolve(resource1, { encoding: 'utf8' }); assert.equal(loadedCounter, 1); - accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource: resource1, type: FileChangeType.DELETED }])); - accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource: resource1, type: FileChangeType.ADDED }])); + accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource: resource1, type: FileChangeType.DELETED }], extUri)); + accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource: resource1, type: FileChangeType.ADDED }], extUri)); const model2 = await manager.resolve(resource2, { encoding: 'utf8' }); assert.equal(loadedCounter, 2); @@ -225,18 +227,32 @@ suite('Files - TextFileEditorModelManager', () => { manager.dispose(); }); - test('dispose prevents dirty model from getting disposed', async function () { + test('canDispose with dirty model', async function () { const manager: TextFileEditorModelManager = instantiationService.createInstance(TextFileEditorModelManager); const resource = toResource.call(this, '/path/index_something.txt'); const model = await manager.resolve(resource, { encoding: 'utf8' }); model.updateTextEditorModel(createTextBufferFactory('make dirty')); - manager.disposeModel((model as TextFileEditorModel)); - assert.ok(!model.isDisposed()); + + let canDisposePromise = manager.canDispose(model as TextFileEditorModel); + assert.ok(canDisposePromise instanceof Promise); + + let canDispose = false; + (async () => { + canDispose = await canDisposePromise; + })(); + + assert.equal(canDispose, false); model.revert({ soft: true }); - manager.disposeModel((model as TextFileEditorModel)); - assert.ok(model.isDisposed()); + + await timeout(0); + + assert.equal(canDispose, true); + + let canDispose2 = manager.canDispose(model as TextFileEditorModel); + assert.equal(canDispose2, true); + manager.dispose(); }); @@ -256,7 +272,7 @@ suite('Files - TextFileEditorModelManager', () => { model = await manager.resolve(resource, { mode: 'text' }); assert.equal(model.textEditorModel!.getModeId(), PLAINTEXT_MODE_ID); - manager.disposeModel((model as TextFileEditorModel)); + model.dispose(); manager.dispose(); }); }); diff --git a/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts index ab4d13394e2..8507b7f887d 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestServiceAccessor, TestTextFileEditorModelManager } from 'vs/workbench/test/browser/workbenchTestServices'; import { toResource } from 'vs/base/test/common/utils'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; -import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; +import { FileOperation } from 'vs/platform/files/common/files'; +import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; suite('Files - TextFileService', () => { @@ -23,12 +24,12 @@ suite('Files - TextFileService', () => { teardown(() => { model?.dispose(); - (accessor.textFileService.files).dispose(); + (accessor.textFileService.files).dispose(); }); test('isDirty/getDirty - files and untitled', async function () { model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); - (accessor.textFileService.files).add(model.resource, model); + (accessor.textFileService.files).add(model.resource, model); await model.load(); @@ -50,7 +51,7 @@ suite('Files - TextFileService', () => { test('save - file', async function () { model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); - (accessor.textFileService.files).add(model.resource, model); + (accessor.textFileService.files).add(model.resource, model); await model.load(); model.textEditorModel!.setValue('foo'); @@ -63,7 +64,7 @@ suite('Files - TextFileService', () => { test('saveAll - file', async function () { model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); - (accessor.textFileService.files).add(model.resource, model); + (accessor.textFileService.files).add(model.resource, model); await model.load(); model.textEditorModel!.setValue('foo'); @@ -76,7 +77,7 @@ suite('Files - TextFileService', () => { test('saveAs - file', async function () { model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); - (accessor.textFileService.files).add(model.resource, model); + (accessor.textFileService.files).add(model.resource, model); accessor.fileDialogService.setPickFileToSave(model.resource); await model.load(); @@ -90,7 +91,7 @@ suite('Files - TextFileService', () => { test('revert - file', async function () { model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); - (accessor.textFileService.files).add(model.resource, model); + (accessor.textFileService.files).add(model.resource, model); accessor.fileDialogService.setPickFileToSave(model.resource); await model.load(); @@ -101,9 +102,9 @@ suite('Files - TextFileService', () => { assert.ok(!accessor.textFileService.isDirty(model.resource)); }); - test('create', async function () { + test('create does not overwrite existing model', async function () { model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); - (accessor.textFileService.files).add(model.resource, model); + (accessor.textFileService.files).add(model.resource, model); await model.load(); model!.textEditorModel!.setValue('foo'); @@ -112,14 +113,15 @@ suite('Files - TextFileService', () => { let eventCounter = 0; const disposable1 = accessor.workingCopyFileService.addFileOperationParticipant({ - participate: async target => { - assert.equal(target.toString(), model.resource.toString()); + participate: async files => { + assert.equal(files[0].target, model.resource.toString()); eventCounter++; } }); - const disposable2 = accessor.textFileService.onDidCreateTextFile(e => { - assert.equal(e.resource.toString(), model.resource.toString()); + const disposable2 = accessor.workingCopyFileService.onDidRunWorkingCopyFileOperation(e => { + assert.equal(e.operation, FileOperation.CREATE); + assert.equal(e.files[0].target.toString(), model.resource.toString()); eventCounter++; }); @@ -131,4 +133,36 @@ suite('Files - TextFileService', () => { disposable1.dispose(); disposable2.dispose(); }); + + test('Filename Suggestion - Suggest prefix only when there are no relevant extensions', () => { + ModesRegistry.registerLanguage({ + id: 'plumbus0', + extensions: ['.one', '.two'] + }); + + let suggested = accessor.textFileService.suggestFilename('shleem', 'Untitled-1'); + assert.equal(suggested, 'Untitled-1'); + }); + + test('Filename Suggestion - Suggest prefix with first extension', () => { + ModesRegistry.registerLanguage({ + id: 'plumbus1', + extensions: ['.shleem', '.gazorpazorp'], + filenames: ['plumbus'] + }); + + let suggested = accessor.textFileService.suggestFilename('plumbus1', 'Untitled-1'); + assert.equal(suggested, 'Untitled-1.shleem'); + }); + + test('Filename Suggestion - Suggest filename if there are no extensions', () => { + ModesRegistry.registerLanguage({ + id: 'plumbus2', + filenames: ['plumbus', 'shleem', 'gazorpazorp'] + }); + + let suggested = accessor.textFileService.suggestFilename('plumbus2', 'Untitled-1'); + assert.equal(suggested, 'plumbus'); + }); + }); diff --git a/src/vs/workbench/services/textfile/test/electron-browser/textFileService.io.test.ts b/src/vs/workbench/services/textfile/test/common/textFileService.io.test.ts similarity index 83% rename from src/vs/workbench/services/textfile/test/electron-browser/textFileService.io.test.ts rename to src/vs/workbench/services/textfile/test/common/textFileService.io.test.ts index 67e5bcc9e29..9aeeb40b349 100644 --- a/src/vs/workbench/services/textfile/test/electron-browser/textFileService.io.test.ts +++ b/src/vs/workbench/services/textfile/test/common/textFileService.io.test.ts @@ -4,74 +4,50 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { ITextFileService, snapshotToString, TextFileOperationError, TextFileOperationResult, stringToSnapshot } from 'vs/workbench/services/textfile/common/textfiles'; import { URI } from 'vs/base/common/uri'; -import { ITextFileService, snapshotToString, TextFileOperationResult, TextFileOperationError } from 'vs/workbench/services/textfile/common/textfiles'; -import { IFileService } from 'vs/platform/files/common/files'; -import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; -import { Schemas } from 'vs/base/common/network'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { rimraf, RimRafMode, copy, readFile, exists } from 'vs/base/node/pfs'; -import { DisposableStore } from 'vs/base/common/lifecycle'; -import { FileService } from 'vs/platform/files/common/fileService'; -import { NullLogService } from 'vs/platform/log/common/log'; -import { getRandomTestPath } from 'vs/base/test/node/testUtils'; -import { tmpdir } from 'os'; -import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; -import { generateUuid } from 'vs/base/common/uuid'; import { join, basename } from 'vs/base/common/path'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; -import { UTF16be, UTF16le, UTF8_with_bom, UTF8 } from 'vs/base/node/encoding'; -import { DefaultEndOfLine, ITextSnapshot } from 'vs/editor/common/model'; +import { UTF16le, UTF8_with_bom, UTF16be, UTF8, UTF16le_BOM, UTF16be_BOM, UTF8_BOM } from 'vs/workbench/services/textfile/common/encoding'; +import { VSBuffer } from 'vs/base/common/buffer'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; +import { ITextSnapshot, DefaultEndOfLine } from 'vs/editor/common/model'; import { isWindows } from 'vs/base/common/platform'; -import { readFileSync, statSync } from 'fs'; -import { detectEncodingByBOM } from 'vs/base/test/node/encoding/encoding.test'; -import { workbenchInstantiationService, TestNativeTextFileServiceWithEncodingOverrides } from 'vs/workbench/test/electron-browser/workbenchTestServices'; -suite('Files - TextFileService i/o', function () { - const parentDir = getRandomTestPath(tmpdir(), 'vsctests', 'textfileservice'); +export interface Params { + setup(): Promise<{ + service: ITextFileService, + testDir: string + }> + teardown(): Promise - const disposables = new DisposableStore(); + exists(fsPath: string): Promise; + stat(fsPath: string): Promise<{ size: number }>; + readFile(fsPath: string): Promise; + readFile(fsPath: string, encoding: string): Promise; + readFile(fsPath: string, encoding?: string): Promise; + detectEncodingByBOM(fsPath: string): Promise; +} +/** + * Allows us to reuse test suite across different environments. + * + * It introduces a bit of complexity with setup and teardown, however + * it helps us to ensure that tests are added for all environments at once, + * hence helps us catch bugs better. + */ +export default function createSuite(params: Params) { let service: ITextFileService; - let testDir: string; - - // Given issues such as https://github.com/microsoft/vscode/issues/78602 - // and https://github.com/microsoft/vscode/issues/92334 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 and increase the timeout. - this.retries(3); - this.timeout(1000 * 10); + let testDir = ''; + const { exists, stat, readFile, detectEncodingByBOM } = params; setup(async () => { - const instantiationService = workbenchInstantiationService(); - - const logService = new NullLogService(); - const fileService = new FileService(logService); - - const fileProvider = new DiskFileSystemProvider(logService); - disposables.add(fileService.registerProvider(Schemas.file, fileProvider)); - disposables.add(fileProvider); - - const collection = new ServiceCollection(); - collection.set(IFileService, fileService); - - service = instantiationService.createChild(collection).createInstance(TestNativeTextFileServiceWithEncodingOverrides); - - const id = generateUuid(); - testDir = join(parentDir, id); - const sourceDir = getPathFromAmdModule(require, './fixtures'); - - await copy(sourceDir, testDir); + const result = await params.setup(); + service = result.service; + testDir = result.testDir; }); teardown(async () => { - (service.files).dispose(); - - disposables.clear(); - - await rimraf(parentDir, RimRafMode.MOVE); + await params.teardown(); }); test('create - no encoding - content empty', async () => { @@ -79,16 +55,28 @@ suite('Files - TextFileService i/o', function () { await service.create(resource); - assert.equal(await exists(resource.fsPath), true); + const res = await readFile(resource.fsPath); + assert.equal(res.byteLength, 0 /* no BOM */); }); - test('create - no encoding - content provided', async () => { + test('create - no encoding - content provided (string)', async () => { const resource = URI.file(join(testDir, 'small_new.txt')); await service.create(resource, 'Hello World'); - assert.equal(await exists(resource.fsPath), true); - assert.equal((await readFile(resource.fsPath)).toString(), 'Hello World'); + const res = await readFile(resource.fsPath); + assert.equal(res.toString(), 'Hello World'); + assert.equal(res.byteLength, 'Hello World'.length); + }); + + test('create - no encoding - content provided (snapshot)', async () => { + const resource = URI.file(join(testDir, 'small_new.txt')); + + await service.create(resource, stringToSnapshot('Hello World')); + + const res = await readFile(resource.fsPath); + assert.equal(res.toString(), 'Hello World'); + assert.equal(res.byteLength, 'Hello World'.length); }); test('create - UTF 16 LE - no content', async () => { @@ -100,6 +88,9 @@ suite('Files - TextFileService i/o', function () { const detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.equal(detectedEncoding, UTF16le); + + const res = await readFile(resource.fsPath); + assert.equal(res.byteLength, UTF16le_BOM.length); }); test('create - UTF 16 LE - content provided', async () => { @@ -111,6 +102,9 @@ suite('Files - TextFileService i/o', function () { const detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.equal(detectedEncoding, UTF16le); + + const res = await readFile(resource.fsPath); + assert.equal(res.byteLength, 'Hello World'.length * 2 /* UTF16 2bytes per char */ + UTF16le_BOM.length); }); test('create - UTF 16 BE - no content', async () => { @@ -122,6 +116,9 @@ suite('Files - TextFileService i/o', function () { const detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.equal(detectedEncoding, UTF16be); + + const res = await readFile(resource.fsPath); + assert.equal(res.byteLength, UTF16le_BOM.length); }); test('create - UTF 16 BE - content provided', async () => { @@ -133,6 +130,9 @@ suite('Files - TextFileService i/o', function () { const detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.equal(detectedEncoding, UTF16be); + + const res = await readFile(resource.fsPath); + assert.equal(res.byteLength, 'Hello World'.length * 2 /* UTF16 2bytes per char */ + UTF16be_BOM.length); }); test('create - UTF 8 BOM - no content', async () => { @@ -144,6 +144,9 @@ suite('Files - TextFileService i/o', function () { const detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.equal(detectedEncoding, UTF8_with_bom); + + const res = await readFile(resource.fsPath); + assert.equal(res.byteLength, UTF8_BOM.length); }); test('create - UTF 8 BOM - content provided', async () => { @@ -155,6 +158,9 @@ suite('Files - TextFileService i/o', function () { const detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.equal(detectedEncoding, UTF8_with_bom); + + const res = await readFile(resource.fsPath); + assert.equal(res.byteLength, 'Hello World'.length + UTF8_BOM.length); }); test('create - UTF 8 BOM - empty content - snapshot', async () => { @@ -166,6 +172,9 @@ suite('Files - TextFileService i/o', function () { const detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.equal(detectedEncoding, UTF8_with_bom); + + const res = await readFile(resource.fsPath); + assert.equal(res.byteLength, UTF8_BOM.length); }); test('create - UTF 8 BOM - content provided - snapshot', async () => { @@ -177,6 +186,9 @@ suite('Files - TextFileService i/o', function () { const detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.equal(detectedEncoding, UTF8_with_bom); + + const res = await readFile(resource.fsPath); + assert.equal(res.byteLength, 'Hello World'.length + UTF8_BOM.length); }); test('write - use encoding (UTF 16 BE) - small content as string', async () => { @@ -215,7 +227,7 @@ suite('Files - TextFileService i/o', function () { }); test('write - use encoding (shiftjis)', async () => { - await testEncodingKeepsData(URI.file(join(testDir, 'some_shiftjs.txt')), 'shiftjis', '中文abc'); + await testEncodingKeepsData(URI.file(join(testDir, 'some_shiftjis.txt')), 'shiftjis', '中文abc'); }); test('write - use encoding (gbk)', async () => { @@ -387,8 +399,12 @@ suite('Files - TextFileService i/o', function () { const result = await service.readStream(resource); assert.equal(result.name, basename(resource.fsPath)); - assert.equal(result.size, statSync(resource.fsPath).size); - assert.equal(snapshotToString(result.value.create(DefaultEndOfLine.LF).createSnapshot(false)), snapshotToString(createTextModel(readFileSync(resource.fsPath).toString()).createSnapshot(false))); + assert.equal(result.size, (await stat(resource.fsPath)).size); + + const content = (await readFile(resource.fsPath)).toString(); + assert.equal( + snapshotToString(result.value.create(DefaultEndOfLine.LF).createSnapshot(false)), + snapshotToString(createTextModel(content).createSnapshot(false))); } test('read - small text', async () => { @@ -407,8 +423,8 @@ suite('Files - TextFileService i/o', function () { const result = await service.read(resource); assert.equal(result.name, basename(resource.fsPath)); - assert.equal(result.size, statSync(resource.fsPath).size); - assert.equal(result.value, readFileSync(resource.fsPath).toString()); + assert.equal(result.size, (await stat(resource.fsPath)).size); + assert.equal(result.value, (await readFile(resource.fsPath)).toString()); } test('readStream - encoding picked up (CP1252)', async () => { @@ -492,7 +508,7 @@ suite('Files - TextFileService i/o', function () { await testLargeEncoding('gbk', '中å›Ŋabc'); }); - test('readStream - large ShiftJS', async () => { + test('readStream - large ShiftJIS', async () => { await testLargeEncoding('shiftjis', '中文abc'); }); @@ -574,4 +590,4 @@ suite('Files - TextFileService i/o', function () { const result = await service.read(URI.file(join(testDir, 'small.txt')), { acceptTextOnly: true }); assert.equal(result.name, 'small.txt'); }); -}); +} diff --git a/src/vs/workbench/services/textfile/test/electron-browser/fixtures/some_shiftjs.txt b/src/vs/workbench/services/textfile/test/electron-browser/fixtures/some_shiftjis.txt similarity index 100% rename from src/vs/workbench/services/textfile/test/electron-browser/fixtures/some_shiftjs.txt rename to src/vs/workbench/services/textfile/test/electron-browser/fixtures/some_shiftjis.txt diff --git a/src/vs/workbench/services/textfile/test/electron-browser/nativeTextFileService.io.test.ts b/src/vs/workbench/services/textfile/test/electron-browser/nativeTextFileService.io.test.ts new file mode 100644 index 00000000000..722fb0cc3bf --- /dev/null +++ b/src/vs/workbench/services/textfile/test/electron-browser/nativeTextFileService.io.test.ts @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IFileService } from 'vs/platform/files/common/files'; +import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; +import { Schemas } from 'vs/base/common/network'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { rimraf, RimRafMode, copy, readFile, exists, stat } from 'vs/base/node/pfs'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { NullLogService } from 'vs/platform/log/common/log'; +import { getRandomTestPath } from 'vs/base/test/node/testUtils'; +import { tmpdir } from 'os'; +import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; +import { generateUuid } from 'vs/base/common/uuid'; +import { join } from 'vs/base/common/path'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { detectEncodingByBOM } from 'vs/workbench/services/textfile/test/node/encoding/encoding.test'; +import { workbenchInstantiationService, TestNativeTextFileServiceWithEncodingOverrides } from 'vs/workbench/test/electron-browser/workbenchTestServices'; +import createSuite from 'vs/workbench/services/textfile/test/common/textFileService.io.test'; +import { IWorkingCopyFileService, WorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; +import { TestWorkingCopyService } from 'vs/workbench/test/common/workbenchTestServices'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; + +suite('Files - NativeTextFileService i/o', function () { + const parentDir = getRandomTestPath(tmpdir(), 'vsctests', 'textfileservice'); + + const disposables = new DisposableStore(); + + let service: ITextFileService; + let testDir: string; + + // Given issues such as https://github.com/microsoft/vscode/issues/78602 + // and https://github.com/microsoft/vscode/issues/92334 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 and increase the timeout. + this.retries(3); + this.timeout(1000 * 10); + + createSuite({ + setup: async () => { + const instantiationService = workbenchInstantiationService(); + + const logService = new NullLogService(); + const fileService = new FileService(logService); + + const fileProvider = new DiskFileSystemProvider(logService); + disposables.add(fileService.registerProvider(Schemas.file, fileProvider)); + disposables.add(fileProvider); + + const collection = new ServiceCollection(); + collection.set(IFileService, fileService); + + collection.set(IWorkingCopyFileService, new WorkingCopyFileService(fileService, new TestWorkingCopyService(), instantiationService, new UriIdentityService(fileService))); + + service = instantiationService.createChild(collection).createInstance(TestNativeTextFileServiceWithEncodingOverrides); + + const id = generateUuid(); + testDir = join(parentDir, id); + const sourceDir = getPathFromAmdModule(require, './fixtures'); + + await copy(sourceDir, testDir); + + return { service, testDir }; + }, + + teardown: async () => { + (service.files).dispose(); + + disposables.clear(); + + await rimraf(parentDir, RimRafMode.MOVE); + }, + + exists, + stat, + readFile, + detectEncodingByBOM + }); +}); diff --git a/src/vs/base/test/node/encoding/encoding.test.ts b/src/vs/workbench/services/textfile/test/node/encoding/encoding.test.ts similarity index 57% rename from src/vs/base/test/node/encoding/encoding.test.ts rename to src/vs/workbench/services/textfile/test/node/encoding/encoding.test.ts index 024f7084d72..37930430364 100644 --- a/src/vs/base/test/node/encoding/encoding.test.ts +++ b/src/vs/workbench/services/textfile/test/node/encoding/encoding.test.ts @@ -5,11 +5,13 @@ import * as assert from 'assert'; import * as fs from 'fs'; -import * as encoding from 'vs/base/node/encoding'; +import * as encoding from 'vs/workbench/services/textfile/common/encoding'; import * as terminalEncoding from 'vs/base/node/terminalEncoding'; -import { Readable } from 'stream'; -import * as iconv from 'iconv-lite'; +import * as streams from 'vs/base/common/stream'; +import * as iconv from 'iconv-lite-umd'; import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { newWriteableBufferStream, VSBuffer, VSBufferReadableStream, streamToBufferReadableStream } from 'vs/base/common/buffer'; +import { isWindows } from 'vs/base/common/platform'; export async function detectEncodingByBOM(file: string): Promise { try { @@ -22,7 +24,7 @@ export async function detectEncodingByBOM(file: string): Promise { process.env['VSCODE_CLI_ENCODING'] = 'utf16le'; const enc = await terminalEncoding.resolveTerminalEncoding(); - assert.ok(encoding.encodingExists(enc)); + assert.ok(await encoding.encodingExists(enc)); assert.equal(enc, 'utf16le'); }); @@ -231,32 +233,33 @@ suite('Encoding', () => { }); } - async function readAllAsString(stream: NodeJS.ReadableStream) { - return new Promise((resolve, reject) => { - let all = ''; - stream.on('data', chunk => { - all += chunk; - assert.equal(typeof chunk, 'string'); + function newTestReadableStream(buffers: Buffer[]): VSBufferReadableStream { + const stream = newWriteableBufferStream(); + buffers + .map(VSBuffer.wrap) + .forEach(buffer => { + setTimeout(() => { + stream.write(buffer); + }); }); - stream.on('end', () => { - resolve(all); - }); - stream.on('error', reject); + setTimeout(() => { + stream.end(); }); + return stream; + } + + async function readAllAsString(stream: streams.ReadableStream) { + return streams.consumeStream(stream, strings => strings.join('')); } test('toDecodeStream - some stream', async function () { + const source = newTestReadableStream([ + Buffer.from([65, 66, 67]), + Buffer.from([65, 66, 67]), + Buffer.from([65, 66, 67]), + ]); - 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 }); + const { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 4, guessEncoding: false, overwriteEncoding: async detected => detected || encoding.UTF8 }); assert.ok(detected); assert.ok(stream); @@ -266,17 +269,13 @@ suite('Encoding', () => { }); test('toDecodeStream - some stream, expect too much data', async function () { + const source = newTestReadableStream([ + Buffer.from([65, 66, 67]), + Buffer.from([65, 66, 67]), + Buffer.from([65, 66, 67]), + ]); - 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 }); + const { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 64, guessEncoding: false, overwriteEncoding: async detected => detected || encoding.UTF8 }); assert.ok(detected); assert.ok(stream); @@ -286,14 +285,10 @@ suite('Encoding', () => { }); test('toDecodeStream - some stream, no data', async function () { + const source = newWriteableBufferStream(); + source.end(); - 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 }); + const { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 512, guessEncoding: false, overwriteEncoding: async detected => detected || encoding.UTF8 }); assert.ok(detected); assert.ok(stream); @@ -302,31 +297,141 @@ suite('Encoding', () => { assert.equal(content, ''); }); - test('toDecodeStream - encoding, utf16be', async function () { + const path = getPathFromAmdModule(require, './fixtures/some_utf16be.css'); + const source = streamToBufferReadableStream(fs.createReadStream(path)); - 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 }); + const { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 64, guessEncoding: false, overwriteEncoding: async 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); + const expected = await readAndDecodeFromDisk(path, detected.encoding); + const actual = await readAllAsString(stream); assert.equal(actual, expected); }); - test('toDecodeStream - empty file', async function () { + const path = getPathFromAmdModule(require, './fixtures/empty.txt'); + const source = streamToBufferReadableStream(fs.createReadStream(path)); + const { detected, stream } = await encoding.toDecodeStream(source, { guessEncoding: false, overwriteEncoding: async detected => detected || encoding.UTF8 }); - 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); + const expected = await readAndDecodeFromDisk(path, detected.encoding); + const actual = await readAllAsString(stream); assert.equal(actual, expected); }); + + test('toDecodeStream - decodes buffer entirely', async function () { + const emojis = Buffer.from('đŸ–Ĩī¸đŸ’ģ💾'); + const incompleteEmojis = emojis.slice(0, emojis.length - 1); + + const buffers: Buffer[] = []; + for (let i = 0; i < incompleteEmojis.length; i++) { + buffers.push(incompleteEmojis.slice(i, i + 1)); + } + + const source = newTestReadableStream(buffers); + const { stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 4, guessEncoding: false, overwriteEncoding: async detected => detected || encoding.UTF8 }); + + const expected = new TextDecoder().decode(incompleteEmojis); + const actual = await readAllAsString(stream); + + assert.equal(actual, expected); + }); + + test('toDecodeStream - some stream (GBK issue #101856)', async function () { + const path = getPathFromAmdModule(require, './fixtures/some_gbk.txt'); + const source = streamToBufferReadableStream(fs.createReadStream(path)); + + const { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 4, guessEncoding: false, overwriteEncoding: async () => 'gbk' }); + assert.ok(detected); + assert.ok(stream); + + const content = await readAllAsString(stream); + assert.equal(content.length, 65537); + }); + + (isWindows /* unsupported OS */ ? test.skip : test)('toDecodeStream - some stream (UTF-8 issue #102202)', async function () { + const path = getPathFromAmdModule(require, './fixtures/issue_102202.txt'); + const source = streamToBufferReadableStream(fs.createReadStream(path)); + + const { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 4, guessEncoding: false, overwriteEncoding: async () => 'utf-8' }); + assert.ok(detected); + assert.ok(stream); + + const content = await readAllAsString(stream); + const lines = content.split('\n'); + + assert.equal(lines[981].toString(), '啊啊啊啊啊啊aaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚'); + }); + + test('toEncodeReadable - encoding, utf16be', async function () { + const path = getPathFromAmdModule(require, './fixtures/some_utf16be.css'); + const source = await readAndDecodeFromDisk(path, encoding.UTF16be); + + const expected = VSBuffer.wrap( + iconv.encode(source, encoding.toNodeEncoding(encoding.UTF16be)) + ).toString(); + + const actual = streams.consumeReadable( + await encoding.toEncodeReadable(streams.toReadable(source), encoding.UTF16be), + VSBuffer.concat + ).toString(); + + assert.equal(actual, expected); + }); + + test('toEncodeReadable - empty readable to utf8', async function () { + const source: streams.Readable = { + read() { + return null; + } + }; + + const actual = streams.consumeReadable( + await encoding.toEncodeReadable(source, encoding.UTF8), + VSBuffer.concat + ).toString(); + + assert.equal(actual, ''); + }); + + [{ + utfEncoding: encoding.UTF8, + relatedBom: encoding.UTF8_BOM + }, { + utfEncoding: encoding.UTF8_with_bom, + relatedBom: encoding.UTF8_BOM + }, { + utfEncoding: encoding.UTF16be, + relatedBom: encoding.UTF16be_BOM, + }, { + utfEncoding: encoding.UTF16le, + relatedBom: encoding.UTF16le_BOM + }].forEach(({ utfEncoding, relatedBom }) => { + test(`toEncodeReadable - empty readable to ${utfEncoding} with BOM`, async function () { + const source: streams.Readable = { + read() { + return null; + } + }; + + const encodedReadable = encoding.toEncodeReadable(source, utfEncoding, { addBOM: true }); + + const expected = VSBuffer.wrap(Buffer.from(relatedBom)).toString(); + const actual = streams.consumeReadable(await encodedReadable, VSBuffer.concat).toString(); + + assert.equal(actual, expected); + }); + }); + + test('encodingExists', async function () { + for (const enc in encoding.SUPPORTED_ENCODINGS) { + if (enc === encoding.UTF8_with_bom) { + continue; // skip over encodings from us + } + + assert.equal(iconv.encodingExists(enc), true, enc); + } + }); }); diff --git a/src/vs/workbench/services/textfile/test/node/encoding/fixtures/empty.txt b/src/vs/workbench/services/textfile/test/node/encoding/fixtures/empty.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/vs/workbench/services/textfile/test/node/encoding/fixtures/issue_102202.txt b/src/vs/workbench/services/textfile/test/node/encoding/fixtures/issue_102202.txt new file mode 100644 index 00000000000..8af62720dc2 --- /dev/null +++ b/src/vs/workbench/services/textfile/test/node/encoding/fixtures/issue_102202.txt @@ -0,0 +1,983 @@ +%啊啊 + +% +% 啊啊啊aaaaa +% 啊啊啊aaaīŧˆå•Šå•Šã€å•Šå•Šå•Šå•Šå•Šīŧ‰ +% 啊啊啊啊啊啊啊啊啊 +% +% 啊啊啊啊啊啊啊啊啊aa啊啊īŧˆå•Šå•Ša2aa啊啊啊啊啊aaaaaīŧ‰ +% 啊啊啊啊啊啊 +% +% 啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +% 啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +\aaaaaaa{啊啊啊啊啊啊啊}% +\aaaaa{aa:aa} + +%\aaaaaaa + +% 啊啊 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊、啊啊、啊啊、啊啊、啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€å•Šå•Šå•Šå•ŠīŧŒ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šīŧˆaaaa aaaa aaaaa, aaaīŧ‰å•Šå•Šå•Šå•Šå•Šīŧˆaaaaa-aaaaa aaaaa, aaaīŧ‰å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧ› +啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧˆaaaaaaaa aaaaa aaaaa aaaaīŧŒaaaaīŧ‰å•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧˆaaaaaaaaa aaaaa aaaaaaaīŧŒaaaīŧ‰å•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊īŧŒå•Šå•Šå•Šå•Šã€å•Šå•Šã€å•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +% 啊啊啊啊 +%% īŧˆå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧ‰ +%% 啊啊 +\aaaaaa{啊啊}啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%%% 啊啊 +啊啊啊啊啊啊啊啊啊啊\aaaaaa{啊啊啊啊啊啊啊啊}。 +啊啊啊啊啊啊\aaaaaa{啊啊啊啊}īŧˆaaaa aaaaīŧ‰īŧŒå•Šå•Šå•Šå•Šå•Šã€å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š\aaaaaa{啊啊啊啊啊}īŧˆaaaa aaaaaaaaīŧ‰ã€‚ +%%% 啊啊 +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šīŧˆ\aaaaaaaa{aaaa}īŧ‰ã€å•Šå•Šīŧˆ\aaaaaaaa{aaaaa}īŧ‰ã€å•Šå•Šīŧˆ\aaaaaaaa{aaaa}īŧ‰ã€å•Šå•Šīŧˆ\aaaaaaaa{aaaaa}īŧ‰å•Šå•Šå•Šīŧˆ\aaaaaaaa{aaaaa}īŧ‰å•Šã€‚ +%% 啊啊īŧˆå•Šå•Šå•Šå•Šå•Šå•Šīŧ‰ +\aaaaaa{啊啊啊}啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Š\aaaaaa{啊啊}啊。 +啊啊啊啊啊啊“啊啊”啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šīŧˆå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧ‰å•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧˆå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧ‰ã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊\aaaaaa{啊啊啊}。 +啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧˆå•Šå•Šå•Šå•Šå•Šīŧˆ\aaaaaaaa{aaaaa}īŧ‰å•Šå•Šå•Šå•Šå•Šīŧˆ\aaaaaaaa{aaaaa}īŧ‰å•Šīŧ‰ã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Š\aaaaaa{啊啊啊啊}。 + +%% +\aaaaaa{啊啊啊啊}啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%\aaa{啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧšå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šã€‚} + +%\aaaaa{aaaaaaaaa} +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%啊啊“啊啊啊啊啊啊啊啊啊啊啊aaa4啊啊啊啊”啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaa4啊啊啊啊啊啊啊啊啊啊啊啊。 +%啊啊“啊啊啊啊啊啊aaa4啊啊啊啊啊啊啊啊啊啊啊”啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%%啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚啊啊啊啊啊啊啊啊啊啊啊啊啊啊aaa4啊啊啊啊。啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%\aaa{aaaaaaaaa} + + +% 啊啊啊啊 +啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊\aaaaaa{啊啊啊}īŧˆaaaaa aaaaaaīŧ‰īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊\aaaaaa{啊}īŧˆaaaaaīŧ‰ã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Š512啊啊啊4aa。 +啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Š\aaaaaa{啊啊}。 +啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š +%啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€å•Šå•Šå•Šã€å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧˆå•Šaaaaa啊啊啊啊啊啊īŧ‰īŧŒ +%啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +% aaaa (aa): 啊啊啊啊啊啊啊aaaa +% aaaa (aa): 啊啊啊啊啊啊啊aa啊啊啊啊aaaaaa啊啊啊aaaaaaaa啊啊īŧŸ +%啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒ +啊~\aaa{aaa:aa:aaaaa}啊啊啊aaaaa啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊īŧšå•Šå•Šå•Šå•Šå•Šå•Šīŧˆå•Šå•Šå•Šå•Šå•Šīŧ‰ã€å•Šå•Šå•Šå•Šã€å•Šå•Šå•Šå•Ša/a啊啊啊啊啊啊啊啊啊啊啊啊。 + +啊啊啊啊啊aaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŸ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧˆå•Š\aaaaaaaa{aaaa}啊\aaaaaaaa{aaaaa}啊īŧ‰ã€‚ +啊啊啊啊啊啊啊啊īŧŒaaaaa啊啊啊啊啊啊\aaaaaa{啊啊啊啊啊啊}īŧˆaaaaaaa aaaa aaaaaaīŧŒaaaīŧ‰å•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Š\aaaaaa{啊啊啊}īŧˆaaaa aaaaaīŧ‰ã€\aaaaaa{aaaaa啊啊}īŧˆaaaaaaīŧ‰å•Š\aaaaaa{啊啊啊啊啊}īŧˆaaaaaaīŧ‰å•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧ›aaaaaa啊aaaaaa啊啊啊啊啊啊aaaaa啊aaaaaa啊啊啊啊啊。 +%啊啊啊啊啊啊啊啊啊。 +%啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧšå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧ› +%啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧšå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +aaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊īŧˆå•Šå•Šaaa4啊啊啊啊īŧ‰å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Ša/a啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒa/a啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + + +\aaaaa{aaaaaa}[aa] +\aaaaaaaaa +\aaaaaaaaaaaaaaa[aaaaa=0.5\aaaaaaaaa]{aaaa/aaaa/aa/aaaaa.aaa} \\[1aa] +\aaaaaaa{啊啊啊}% +\aaaaa{aaa:aa:aaaaa} +\aaa{aaaaaa} + + +啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊aaaaa啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊{\aaaaaa}啊啊啊啊啊啊啊啊。 + +\aaaaaaa +啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧš +\aaaaa{aaaaaaaaa} + \aaaa 啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŸīŧˆå•Š\aaa{aaa:aa:aaaaaaa}啊īŧ‰ + \aaaa 啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŸīŧˆå•Š\aaa{aaa:aa:aaaaaaa}啊īŧ‰ + \aaaa 啊啊啊啊啊啊啊啊啊啊啊īŧŸīŧˆå•Š\aaa{aaa:aa:aaaaaaa}啊īŧ‰ +% \aaaa 啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŸ +% \aaaa 啊啊啊啊啊啊啊10啊啊啊啊啊啊啊啊啊100a啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Š100aa啊啊啊īŧŒå•Šå•Šå•Šå•ŠīŧŸ + \aaaa 啊啊啊啊a啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•ŠīŧŸīŧˆå•Š\aaa{aaa:aa:aaa}啊īŧ‰ +% \aaaa 啊啊啊啊aaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŸ +% \aaaa 啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŸ + \aaaa 啊啊啊啊aaaa啊“啊啊啊啊啊”īŧŸå•Šå•Šå•Šã€å•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŸīŧˆå•Š\aaa{aaa:aa:aaa}啊īŧ‰ + \aaaa 啊啊啊啊啊啊啊啊啊啊啊īŧŸ\aaaaaaaa{/aaaa}啊\aaaaaaaa{/aaa}啊啊啊啊īŧŸīŧˆå•Š\aaa{aaa:aa:aaa}啊īŧ‰ +% \aaaa 啊啊啊aaaaaaa啊啊啊啊啊啊啊啊īŧŒå•Šaaaaa啊啊啊īŧŸīŧˆå•Š\aaa{aaa:aa:aaaaaa}啊īŧ‰ +% \aaaa 啊啊啊啊啊īŧŒå•Šå•Šaaaa啊aaaa啊啊啊啊īŧŸ + \aaaa aaaaaaa啊啊啊啊啊啊啊啊啊啊aaaa啊啊啊啊啊啊啊啊啊īŧŸīŧˆå•Š\aaa{aaa:aa:aaaaaa}啊īŧ‰ + \aaaa 啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŸīŧˆå•Š\aaa{aaa:aa:aaaaaa}啊īŧ‰ +\aaa{aaaaaaaaa} +\aa + + + +\aaaaaaa{啊啊aaaaa啊啊啊啊啊}% +\aaaaa{aaa:aa:aaaaaaa} + +\aaaaa{aaaaaaaaa} + \aaaa 啊啊aaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊、啊啊啊啊啊啊啊啊啊啊啊啊īŧŸå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•ŠīŧŸ + \aaaa 啊啊aaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŸ +\aaa{aaaaaaaaa} + + +啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧˆå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧ‰ã€‚ +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 + + + +\aaaaa{aaaaa}[a] +\aaaaaaaaa +\aaaaaaa{aaaaa啊啊啊啊啊啊啊啊啊啊啊}% +\aaaaa{aaa:aa:aaaaaa-aaaaa-aaaaaaaa} +\aaaaa{aaaaaaa}{aa} +\aaaaaaa +\aaaaaa{啊啊啊啊啊} & \aaaaaa{啊啊} \\ +\aaaaaaa +aaaa & 啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š \\ +aaaaa & 啊啊啊啊啊啊啊啊啊啊 \\ +aaa & 啊啊啊啊啊啊啊aa \\ +aaa & 啊啊啊啊啊啊啊啊aa \\ +aaaa & 啊啊啊啊啊 \\ +aaaaa & 啊啊啊啊啊啊啊啊啊啊 \\ +aaaaa & 啊啊啊啊啊啊啊啊啊啊 \\ +aaaaa & 啊啊啊啊啊啊啊啊啊啊 \\ +\aaaaaaaaaa +\aaa{aaaaaaa} +\aaa{aaaaa} + + +\aaaaaaaaaa{aaaaa} + +%aaaaa啊啊啊啊啊啊啊啊啊啊啊啊。 +%啊啊啊aaaaa啊啊啊啊啊啊īŧŒaaaaa啊啊啊啊啊啊啊。 +%啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Š\aaaaaa{aaaaa}īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +aaaaa啊啊啊啊啊aaaaa aaaa啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +aaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Š~\aaa{aaa:aa:aaaaaa-aaaaa-aaaaaaaa}啊啊啊啊啊啊啊啊、啊啊啊啊啊、啊啊啊啊啊啊啊啊啊、啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒaaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊aaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊īŧˆå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧ‰ã€‚ +%啊啊aaaaa啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧˆå•Šå•Š~\aaa{aaa:aa:aaaa-aaaaa}īŧ‰īŧŒå•Šå•Š\aaaaaa{啊啊啊啊}īŧˆaaaaaaa aaaaīŧ‰ã€\aaaaaa{啊啊啊啊}īŧˆaaaaaaaaa aaaaīŧ‰å•Š\aaaaaa{啊啊啊啊啊啊}īŧˆaaaaaaaa aaaa aaaaīŧ‰ã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +\aaaaa{aaaaa}[a] +\aaaaaaaaa +\aaaaaaa{aaaaa啊啊啊啊啊啊啊啊}% +\aaaaa{aaa:aa:aaaa-aaaaa} +\aaaaa{aaaaaaa}{aa} +\aaaaaaa +\aaaaaa{啊啊啊啊} & \aaaaaa{啊啊啊啊} \\ +\aaaaaaa +啊啊啊啊 & 啊啊啊啊 \\ +啊啊啊啊 & 啊啊啊啊啊啊啊啊啊 \\ +啊啊啊啊啊啊 & 啊啊啊啊啊啊īŧˆå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧ‰ \\ +aaaa啊啊 & 啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Š \\ +啊啊啊啊啊 & 啊啊啊啊啊啊īŧŒå•Šaaaa啊啊啊啊啊啊 \\ +啊啊啊啊啊啊 & 啊啊啊啊啊啊啊啊啊 \\ +啊啊啊啊啊 & 啊啊啊啊啊啊啊啊 \\ +\aaaaaaaaaa +\aaa{aaaaaaa} +\aaa{aaaaa} + + +\aaaaaaaaaa{啊啊啊啊} + +% % aaaaaaa(aa): 啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŸ + +%啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šã€å•Šå•Šã€å•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊、啊啊、啊啊、啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊啊啊啊īŧŒ +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊īŧŒ + +啊~\aaa{aaa:aa:aaaaaaaa}啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊aaaaa啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š\aaaaaaaa{aaaaa啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚}。 +啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š4aaīŧŒå•Šå•Šå•Šå•Šå•Šå•Š8啊啊啊īŧŒå•Šå•Šaaaaa啊啊啊啊啊啊12啊īŧŒå•Šå•Šå•Šå•Šå•Š3啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Š1啊。 + +\aaaaa{aaaaaa}[aaa] +\aaaaaaaaa +\aaaaaaaaaaaaaaa[aaaaa=0.9\aaaaaaaaa]{aaaa/aaaa/aa/aaaaa.aaa} \\[1aa] +\aaaaaaa{啊啊啊啊啊啊啊啊}% +\aaaaa{aaa:aa:aaaaaaaa} +\aaa{aaaaaa} + +啊aaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š4aa啊啊啊īŧˆå•Šå•Šå•Šå•Šå•Š0啊4095啊啊īŧŒå•Šå•Š0啊4095īŧ‰ã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊4096啊8191啊啊啊。 +啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š4096\(\aaaaa\)11啊4096\(\aaaaa\)12\(-\)1啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊48aa啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊48aaīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊4aa啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Š512啊啊啊啊啊。 +啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š512啊4aa啊啊啊啊īŧŒå•Šå•Š2aa啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š2aa啊啊啊啊啊4aa啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊4096\(\aaaaa\)12啊4096\(\aaaaa\)13\(-\)1啊4096啊啊啊。 +啊啊啊啊啊啊啊啊啊īŧŒaaaaa啊啊3啊啊啊啊啊啊啊啊啊6aa啊啊啊。 +啊啊啊啊啊啊啊啊啊啊48aa\(+\)6aa啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Š512啊啊啊啊啊啊啊啊。 +啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š512啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Š2aa\(\aaaaa\)512啊1aa啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šaaaaa啊啊啊啊啊啊啊啊啊啊啊啊48aa\(+\)6aa\(+\)1aa。 + +啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š\aaaaaaaa{啊啊啊啊īŧŒå•Šaaaaa啊“啊啊啊啊”啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚}。 +啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒaaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + + +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒaaaaa啊啊啊啊啊啊啊啊啊。 + +%啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊啊啊啊a+啊啊啊啊啊啊啊啊啊啊啊。 + + +\aaaaaaaaaa{啊啊啊啊} + +啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧˆå•Šå•Šå•Šå•Šå•Š~\aaa{aaaaa:aa:aaa-aaa-aaaa}īŧ‰ã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šâ€”—啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊。 +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +\aaaaa{aaaaaaaaa}[aaaaaaa=啊啊\aaaaaaaaaaa{aaaa}啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊, aaaaa=aaaaa:aa:aaa-aaa-aaaa] +[aaaa@aaaaaa ~] # aaaa +. +└── aa-aaaaaaaaa + ├── aaaaa-aaa + │   └── aaaaa-aaaa.aa3 + ├── aaaaaaa.aaa + ├── aaaaaaaa-aaa + │   ├── aaa-aaaa.aaa + │   └── aaaa-aaaa.aaa + ├── aaaaa-aaa + └── aaaaa-aaa + └── aaaaa-aaaa.aaa + +5 aaaaaaaaaaa, 5 aaaaa +\aaa{aaaaaaaaa} + +\aaaaaaa + +啊啊īŧšå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚啊啊啊啊啊啊啊啊īŧŒâ€œå•Šå•Šâ€å•Šâ€œå•Šå•Šå•Šâ€å•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧšå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧ›å•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šaaaaa aaaaaa啊啊啊啊。啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa aaaaaa啊啊啊。 + +啊啊啊aaaaa啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚啊啊啊啊啊啊啊啊啊啊啊啊aaaaa啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚啊啊啊啊啊īŧŒå•Šå•Šaaaaa啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šaaaa aaaa。啊啊aaaaa啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šaaa啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +\aa + +啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Š~\aaa{aaa:aa:aaaaaaaa}啊啊啊啊啊啊啊啊。 +啊~\aaa{aaa:aa:aaaaaa}啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šã€å•Šå•Šå•Šå•Šå•Šå•Šã€å•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊啊啊啊\aaaaaaaa{啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa。}。 + +\aaaaa{aaaaaa}[aaa] +\aaaaaaaaa +\aaaaaaaaaaaaaaa[aaaaa=0.9\aaaaaaaaa]{aaaa/aaaa/aa/aaaaaa.aaa} \\[1aa] +\aaaaaaa{啊啊啊啊啊啊啊啊īŧšå•Šå•Šå•Š}% +\aaaaa{aaa:aa:aaaaaa} +\aaa{aaaaaa} + +啊啊啊啊啊aaaaa啊啊啊啊啊啊啊啊啊啊啊aaaaaīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +啊啊啊啊啊啊啊啊啊啊啊啊、啊啊、啊啊啊啊啊啊啊啊īŧš +\aaaaa{aaaaaaa} + \aaaaaaaaa\aaaaaaa{-0.5aa} + \aaaa 啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + 啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊啊aaaaaīŧŒå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊。 + \aaaa 啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧˆå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧ‰å•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + \aaaa 啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊。 + 啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧˆå•Šå•Šå•Šå•Šå•Šå•Šīŧ‰å•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + 啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + \aaaa 啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + 啊啊啊啊啊啊啊啊啊啊啊啊啊啊aaaaa啊啊啊0啊啊啊啊啊啊啊啊啊啊啊啊。 + 啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + 啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +\aaa{aaaaaaa} + +\aaaaa{aaaaaaa-aaaa}[啊啊啊] +啊aaaaa啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊“.”啊“..”īŧŒå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊。 +啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊 +啊啊啊啊啊啊啊īŧŒâ€œ.”啊“..”啊啊啊aaaaa啊啊啊啊啊啊啊啊。 +\aaa{aaaaaaa-aaaa} + + + + +\aaaaaaaaaa{啊啊啊啊啊啊啊啊} + +\aaaaaaaaaaaaa{啊啊啊啊} + +\aaaaaa{啊啊啊啊}啊啊啊\aaaaaa{啊啊啊}īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š\aaaaaa{啊啊啊啊}。 +啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +啊啊啊啊īŧŒå•Šå•Šå•Šå•Šã€å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊īŧšå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠaaaaaīŧŒå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊啊。 +啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 + +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧšå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +\aaaaaaaaaaaaa{啊啊啊} + +% 啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Š\aaaaaa{啊啊啊}。 +啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠaaaaaīŧŒ +啊啊啊啊啊啊啊啊啊啊aaaaaīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊。 + +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊。 +% 啊啊啊啊啊啊啊啊啊啊啊啊啊啊“啊啊啊”啊啊啊啊aaaaa啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šaaaaa啊啊啊啊啊啊啊啊啊啊。 +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊aaaaa啊啊啊。 + + +\aaaaaaaaaaaaa{啊啊啊啊啊啊啊啊啊} + +啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šaaaaa啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊aaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊啊īŧˆå•Š~\aaa{aaa:aa:aaaaaa-aaaaa-aaaaaaaa}啊啊\aaaaaaaa{aaaaa}īŧ‰īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa。 +啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊1īŧ›å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊1。 +啊aaaaa啊啊啊啊啊0啊īŧŒaaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊啊啊2īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š1。 +%啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧˆå•Šâ€œ.”īŧ‰ã€‚ +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šâ€œ..”īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊啊。 + +%啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠaaaaaīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa。 +%啊啊啊啊啊aaaaa啊啊啊啊啊啊啊。 +%啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊啊。 + +%啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊啊。 +%啊啊啊aaaaa啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠaaaaaīŧŒå•Šå•Šaaaaa啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊aaaaa啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊啊啊啊啊īŧšå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊啊啊啊啊啊、啊啊啊aaaaa啊啊啊“.”啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊“..”啊啊啊啊。 +%啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šâ€œ.”啊“..”啊啊啊啊啊啊。 +%啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šaaaaa啊啊“..”啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊1。 + +\aaaaa{aaaaaaa-aaaa}[啊啊啊] +啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊“.”啊“..”啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š2īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š1。 + +啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šâ€œ.”啊“..”啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧš +啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Š\aaaaaaaa{aa -a aaa}啊啊啊\aaaaaaaa{aaa}啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŸ +啊啊啊īŧŒå•Š\aaaaaaaa{aa}啊啊啊啊\aaaaaaaa{-a}啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šâ€œå•Šå•Šå•Šå•Šâ€å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š\aaaaaaaa{aaa}啊啊啊啊。 +\aaa{aaaaaaa-aaaa} + +\aaaaaaaaaaaaa{啊啊啊啊啊啊啊啊啊啊啊} + +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧˆå•Šå•Šå•Šå•Šīŧ‰īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa。 +啊啊īŧŒ +%啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊aaaaa啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€å•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šâ€œå•Šå•Šå•Šå•Šå•Šå•Šå•Šâ€å•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊aaaaa啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧ› +啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠaaaaaīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š\aaaaaaaa{啊啊啊啊īŧŒâ€œ.”啊“..”啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šã€‚}。 +啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧ› +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%\aaa{啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。} +% 啊啊啊啊啊啊啊 + + +% aaaa (aa): aaaaa aaaaaaaaa aaaaaaa aaa aaaa aaaa + +\aaaaaaaaaa{啊啊啊啊} + +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +% 啊啊īŧŒaaa4啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧˆaaaaa aaaaaīŧ‰īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š +%啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +% aaaa(aa): 啊啊啊啊啊啊aaa2、aaaaa啊啊啊啊啊啊啊啊啊啊啊。 + + +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊~\aaa{aaa:aa:aa-aaaa-aaaaaa}啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€å•Šå•Šå•Šå•Šå•Šã€aaaaa啊啊啊啊、aaaaa啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧˆå•Šå•Šå•Šå•Šīŧ‰å•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊~\aaa{aaa:aa:aa-aaaa-aaaaaa}啊啊啊啊啊啊啊啊啊啊啊啊。 + +\aaaaa{aaaaaa}[aa] + \aaaaaaaaa + \aaaaaaaaaaaaaaa[aaaaa=0.9\aaaaaaaaa]{aaaa/aaaa/aa/aa-aaaa-aaaaaa.aaa} \\[1aa] + \aaaaaaa{啊啊啊啊啊啊啊啊啊}% + \aaaaa{aaa:aa:aa-aaaa-aaaaaa} +\aaa{aaaaaa} + +啊啊啊啊īŧŒ\aaaaaa{啊啊啊}īŧˆaaaaa aaaaaīŧ‰å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊īŧˆaaaaa aaaaaaīŧ‰å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊aaaaa啊啊、啊啊啊啊啊啊啊aaaaa啊啊啊、啊啊啊啊啊啊啊啊啊啊、啊啊啊啊啊啊啊啊啊啊啊啊。 +%\aaa{啊啊啊啊啊啊啊īŧšaaaaa啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚啊啊1īŧšå•Šaaaaa啊啊9.1.1啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧ›å•Šå•Š2īŧšå•Šaaaaa啊啊啊啊啊啊啊9.1.1啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Š} + +\aaaaa{aaaaaaaa} +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊啊。 +啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧˆå•Šå•Šå•Š1啊aaaaa啊啊啊啊aaaaaīŧ‰å•Šå•Šå•Šå•ŠīŧŸ + +啊īŧšå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +\aaa{aaaaaaaa} + + +啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š\aaaaaa{啊啊啊啊啊}啊\aaaaaa{aaaaa啊啊啊啊}。 +啊啊啊啊啊啊啊啊啊īŧˆaaaaaaīŧ‰å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊1īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧ›å•Šå•Š0īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +aaaaa啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊aaaaa啊啊啊啊啊。 + +啊啊啊啊啊啊啊啊啊啊啊啊啊啊\aaaaaa{aaaaa啊}。 +aaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊aaaaa啊啊。 +啊啊啊啊啊啊啊啊aaaaa啊啊啊啊啊啊啊īŧˆå•Šå•Š\aaaaaa{aaaaa啊}īŧ‰å•Šå•Šå•Šaaaaa啊啊啊啊。 +啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊啊aaaaa啊啊啊īŧŒå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊īŧŒå•Šå•Šaaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊aaaaa啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊啊aaaaa啊啊。 +%\aaa{啊啊啊啊啊啊啊啊aaaaa啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šâ€”—啊啊啊啊啊aaaaa啊啊啊啊啊啊aaaaa啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šã€‚} + +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š\aaaaaaaa{啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚}。 +啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%\aaa{啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŸå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŸå•Šå•Šå•Šå•Šã€‚啊啊īŧŒaaaaa啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Š} + + +\aaaaaaa{啊啊啊啊啊啊}\aaaaa{aaa:aa:aaa} + +\aaaaa{aaaaaaaaa} + \aaaa 啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŸ + \aaaa 啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŸ + \aaaa 啊啊啊啊啊啊啊啊īŧŸå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŸ +\aaa{aaaaaaaaa} + +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒ +%啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaa啊啊啊īŧˆå•Ša啊啊啊啊īŧ‰å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +%啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€å•Šå•Šã€å•Šå•Šå•Šå•Šå•Šâ€œå•Šå•Šâ€å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒ +%啊啊啊啊啊啊啊啊啊啊啊啊啊“啊啊啊”īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊aaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%%啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%%啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šâ€œå•Šå•Šå•Šâ€ã€‚ +%%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%%啊啊啊啊啊、啊啊啊啊啊aaa啊啊啊。 + +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊“啊啊”啊啊啊啊啊啊啊啊。 +%啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šaaaa啊aaa4啊啊啊啊啊啊啊啊啊啊。 +%啊啊啊啊啊啊啊啊“啊啊啊”īŧŒå•Šå•Šaaa4啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šaaa32啊啊啊啊啊啊啊aaa啊啊啊啊啊啊啊啊啊啊啊啊啊。 + +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šaaaa啊aaa4啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šaaa4啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šaaa32啊啊啊啊啊啊啊aaa啊啊啊啊啊啊啊啊啊啊啊啊啊。 + +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊īŧŒ\aaaaaaa{啊啊啊啊啊啊}īŧˆaaaaaaa aaaa aaaaaaīŧŒaaaīŧ‰\aaaaaaaa{啊啊aaaaaaa aaaaaaaaaa aaaaaa。} +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +啊啊啊啊~\aaa{aaaaa:aa:aaaaa}啊啊啊啊aaaaa啊啊啊\aaaaaaaa{aaaaa}啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Š\aaaaaaa{啊啊啊啊啊啊}啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€å•Šå•Šå•Šã€å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊\aaaaaaaa{/aaa/aaa1}啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Š\aaaaaaaa{/aaa/aaa}啊啊啊啊啊啊啊啊啊啊aaa4啊啊啊啊啊啊啊啊啊啊啊īŧˆâ€œ/”īŧ‰å•Šã€‚啊啊啊啊啊\aaaaaaaa{aa}啊啊啊啊啊啊、\aaaaaaaa{aaaaaaaa}啊啊啊啊啊啊啊啊啊啊啊啊啊\aaaaaaaa{啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧˆå•Šå•Šå•Š24啊啊īŧ‰å•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚}、\aaaaaaaa{aaaaaa=aaaaaaa-aa}啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 + + +\aaaaa{aaaaaaaaa}[aaaaaaa=啊aaaaa啊啊啊啊啊啊啊\aaaaaaaaaaa{aaaaa}啊啊啊啊啊啊啊啊啊啊啊啊, aaaaa=aaaaa:aa:aaaaa] +# 啊啊啊啊īŧšå•Šå•Š aa 啊啊啊 aaaa 啊啊啊啊啊啊 (啊啊啊啊) +[aaaa@aaaaaa ~] # aaaaa +aaaaa aa /aaa aaaa aaaaa (aa,aaaaaa,aaaaa,aaaaaa,aaaaaaaa) +aaaa aa /aaaa aaaa aaaa (aa,aaaaaa,aaaaa,aaaaaa,aaaaaaaa) +aaaaa aa /aaa aaaa aaaaa (aa,aaaaaa,aaaaaaaa,aaaa=2050804a,aaaa=755) +/aaa/aaa1 aa / aaaa aaa4 (aa,aaaaaaaa,aaaaaa=aaaaaaa-aa) +aaaaa aa /aaa/aaa aaaa aaaaa (aa,aaaaaa,aaaaa) +aaaaaaaaa aa /aaa/aaaaaaaaa aaaa aaaaaaaaa (aa,aaaaaaaa,aaaaaaaa=2a) +aaaaaaa aa /aaa/aaaaaa/aaaaa aaaa aaaaaaa (aa,aaaaaaaa) +aaaaaa aa /aaa/aaa_aaaaaa aaaa aaa_aaaaaa (aa,aaaaaaaa) +/aaa/aaa3 aa /aaaaa/aaa aaaa aaa4 (aa,aaaaaa,aaaaa,aaaaaaaa,aaaa) +\aaa{aaaaaaaaa} +%aaaa aa /aaa aaaa aaaaaaaa (aa,aaaaaa,aaaaaaaa,aaaa=10237212a,aa_aaaaaa=2559303,aaaa=755) +%aaaaaaaaaa aa /aaa/aaaaaa/aaaaaaaa aaaa aaaaaaaaaa (aa,aaaaaa,aaaaa,aaaaaa,aaaaaaaa) +%aaaaaa aa /aaa/aaa aaaa aaaaaa (aa,aaaaaa,aaaaaa,aaaaaaaa,aaa=5,aaaa=620,aaaaaaaa=000) + + +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŸ +%啊啊啊啊啊啊啊啊啊啊啊啊īŧš +%啊啊啊啊啊啊īŧˆaaaīŧ‰\aaaaaaaa{啊 aaaaaaa aaaa aaaaaa 啊 aaaaaaa aaaaaaaaaa aaaaaa。啊啊啊啊啊啊啊啊啊啊啊啊啊。}。 + + +%啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊。啊啊啊啊啊啊啊啊啊啊啊。 +%啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šaaa啊啊啊啊啊啊啊。 + +% 啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +% 啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 + +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊、啊啊啊啊、啊啊啊啊啊啊啊啊啊啊啊。 +啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šâ€œå•Šå•Šâ€ã€‚ +啊啊啊啊“啊啊”īŧŒå•Šå•Šå•Šå•Šå•Šå•Šaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊īŧŒå•Šå•Šå•Šå•Šaaaaa啊啊aaa~\aaaa{aaaaa-aaa-aaa}啊啊īŧŒ +啊啊aaa啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 + +\aaaaaaaaaa{啊啊啊啊啊啊啊啊啊} + +aaa啊啊啊啊啊啊啊啊“啊啊”啊啊啊啊\aaaa{啊啊啊啊}啊\aaaa{啊啊啊啊}啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊aaa啊啊īŧ› +啊啊啊啊啊啊啊啊啊啊啊aaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%\aaa{啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŸ} + +\aaaaaaaaaaaaa{啊啊啊啊} + +啊aaaaa啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%%\aaa{啊啊啊啊啊啊啊īŧŸ} +啊~\aaa{aaa:aa:aaa-aa-aaaaaa}啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaa4啊啊啊啊。 +啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaa4啊啊啊啊啊啊啊\aaaa{啊啊啊啊}īŧŒå•Šå•Šaaa4啊啊啊啊啊“啊啊啊啊”啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +%啊啊啊啊啊啊啊啊啊啊“啊啊”īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧˆå•Šå•Šå•Šå•Šâ€œå•Šå•Šå•Šå•Šâ€īŧ‰īŧŒ +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 + +\aaaaa{aaaaaa}[aaa] +\aaaaaaaaa +\aaaaaaaaaaaaaaa[aaaaa=0.5\aaaaaaaaa]{aaaa/aaaa/aa/aaa-aa-aaaaaa.aaa} \\[1aa] +\aaaaaaa{啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊}% +\aaaaa{aaa:aa:aaa-aa-aaaaaa} +\aaa{aaaaaa} + +% +%\aaaaa{aaaaaaa-aaaa}[啊啊啊啊啊啊“啊啊”] +% +%啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊啊啊“啊啊”。 +%啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šâ€œå•Šå•Šâ€ã€‚ +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊“啊啊”啊īŧŸ +%%啊啊啊啊啊啊啊啊啊啊īŧŸ +%\aaa{aaaaaaa-aaaa} + +\aaaaa{aaaaaaa}[aa] +\aaaaa{aaaaaaaaaaaaa} + + aaaaaa aaaa_aaaaaa_aaaa { + aaaaa aaaa *aaaa; + ... + aaaaaa aaaaaa *(*aaaaa) (aaaaaa aaaa_aaaaaa_aaaa *, + aaa, aaaaa aaaa *, aaaa *); + aaaa (*aaaa_aa) (aaaaaa aaaaa_aaaaa *); + aaaaaa aaaaaa *aaaaa; + aaaaaa aaaa_aaaaaa_aaaa * aaaa; + aaaaaa aaaaa_aaaa aa_aaaaaa; + ... + }; + +\aaa{aaaaaaaaaaaaa} +\aaaaaaa{aaaaa aaa啊啊啊啊啊啊啊啊啊啊}% +\aaaaa{aaaa:aa:aaa-aaaaaa} +\aaa{aaaaaaa} + +%啊aaaaa啊īŧŒå•Šå•Šå•Šå•Šå•Šâ€œå•Šå•Šâ€å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +aaaaa啊啊aaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊aaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊aaa。 +%aaaaa啊啊啊啊啊啊啊啊啊“啊啊”啊啊啊啊啊啊啊。 +%īŧŒå•Šå•Šå•Šå•Šå•Šaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%\aaa{啊啊啊啊啊啊啊啊啊啊啊īŧŸ} +%啊aaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧˆå•Šå•Š~\aaa{aaa:aa:aaa-aa-aaaaaa}啊啊啊啊啊啊īŧ‰īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaa啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊~\aaa{aaaa:aa:aaa-aaaaaa}啊啊啊啊啊啊īŧŒå•Šaaa啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Š\aaaaaaaa{aaaaa}啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +\aaaaa{aaaaaaa}[aa] +\aaaaa{aaaaaaaaaaaaa} + + aaaaaa aaaaa_aaaaaaaaaa { + // aaaaa啊啊啊啊啊啊啊 + aaaaaa aaaaa *(*aaaaa_aaaaa)(aaaaaa aaaaa_aaaaa + *aa); + aaaa (*aaaaaaa_aaaaa)(aaaaaa aaaaa *); + aaa (*aaaaa_aaaaa) (aaaaaa aaaaa *, + aaaaaa aaaaaaaaa_aaaaaaa *aaa); + ... + // 啊啊啊啊啊啊啊啊啊啊啊 + aaa (*aaaa_aa)(aaaaaa aaaaa_aaaaa *aa, aaa aaaa); + aaa (*aaaaaa) (aaaaaa aaaaaa *, aaaaaa aaaaaaa *); + aaa (*aaaaaaa_aa) (aaaaaa aaaaa_aaaaa *, aaa *, + aaaa *); + aaaa (*aaaaaa_aaaaa) (aaaaaa aaaaa_aaaaa *); + ... + }; + +\aaa{aaaaaaaaaaaaa} +\aaaaaaa{aaaaa aaa啊啊啊啊啊啊啊啊啊啊啊啊}% +\aaaaa{aaaa:aa:aaa-aaaaa-aaa} +\aaa{aaaaaaa} + +\aaaaaaaaa{啊啊啊啊啊啊啊啊啊啊。} +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊aaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊~\aaa{aaaa:aa:aaa-aaaaa-aaa}啊啊啊啊aaaaa啊aaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧˆå•Šaaaaaīŧ‰å•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šīŧˆ\aaaaaaaa{aaaaa_aaaaa}īŧ‰ã€å•Šå•Šīŧˆ\aaaaaaaa{aaaaaaa_aaaaa}īŧ‰å•Šå•Šå•Šīŧˆ\aaaaaaaa{aaaaa_aaaaa}īŧ‰ã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧˆ\aaaaaaaa{aaaa_aa}īŧ‰ã€å•Šå•Šīŧˆ\aaaaaaaa{aaaaaa}īŧ‰ã€å•Šå•Šå•Šīŧˆ\aaaaaaaa{aaaaaaa_aa}īŧ‰å•Šå•Šå•Šīŧˆ\aaaaaaaa{aaaaaa_aaaaa}īŧ‰å•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +\aaaaa{aaaaaaaa} +啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šaaa啊啊啊啊啊īŧŸå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŸ + +啊īŧšå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚啊啊啊啊啊啊啊啊啊啊“啊啊”啊aaa啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒaaa啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +\aaa{aaaaaaaa} + +\aaaaa{aaaaaaa}[aa] +\aaaaa{aaaaaaaaaaaaa} + + aaaaaa aaaa_aaaaaaaaaa { + aaaaaa aaaaaa *aaaaa; + aaaa_a (*aaaaaa) (aaaaaa aaaa *, aaaa_a, aaa); + aaaaa_a (*aaaa) (aaaaaa aaaa *, aaaa __aaaa *, + aaaa_a, aaaa_a *); + aaaaa_a (*aaaaa) (aaaaaa aaaa *, + aaaaa aaaa __aaaa *, + aaaa_a, aaaa_a *); + aaaaa_a (*aaaa_aaaa) (aaaaaa aaaaa *, + aaaaaa aaa_aaaa *); + aaaaa_a (*aaaaa_aaaa) (aaaaaa aaaaa *, + aaaaaa aaa_aaaa *); + aaaa (*aaaaaaaa_aaaaa) (aaaaaa aaaa *, aaaaaaaa aaa, + aaaaaaaa aaaa); + aaa (*aaaa) (aaaaaa aaaa *, + aaaaaa aa_aaaa_aaaaaa *); + aaa (*aaaa) (aaaaaa aaaaa *, aaaaaa aaaa *); + aaa (*aaaaa) (aaaaaa aaaa *, aaaa_a, aaaa_a, + aaa aaaaaaaa); + ... + }; + +\aaa{aaaaaaaaaaaaa} +\aaaaaaa{aaaaa aaa啊啊啊啊啊啊啊啊啊啊}% +\aaaaa{aaaa:aa:aaa-aaaa-aaa} +\aaa{aaaaaaa} + + +\aaaaaaaaa{啊啊啊啊啊啊啊。} +啊啊啊啊~\aaa{aaaa:aa:aaa-aaaa-aaa}啊啊啊啊啊啊aaaaa啊aaa啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧˆ\aaaaaaaa{aaaa}īŧ‰ã€å•Šīŧˆ\aaaaaaaa{aaaa}啊\aaaaaaaa{aaaa_aaaa}īŧ‰ã€å•Šīŧˆ\aaaaaaaa{aaaaa}啊\aaaaaaaa{aaaaa_aaaa}īŧ‰ã€å•Šå•Šīŧˆ\aaaaaaaa{aaaaaa}īŧ‰ã€å•Šå•Šå•Šå•Šīŧˆ\aaaaaaaa{aaaa}īŧ‰ã€å•Šå•Šå•Šå•Šīŧˆ\aaaaaaaa{aaaaa}īŧ‰å•Šå•Šå•Šã€‚ + +\aaaaa{aaaaaaa-aaaa}[啊啊啊] +啊啊啊啊~\aaa{aaaa:aa:aaa-aaaa-aaa}啊啊\aaaaaaaa{aaaaaaaa_aaaaa}啊啊\aaaaaaaa{aaaaa}啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€å•Šå•Šå•Šå•Šå•Šaaa。 +啊啊aaaaa啊啊啊\aaaaaaaa{aaaaa}啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š\aaaaaaaa{aaaaa}啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧˆaaaaaaaīŧ‰īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊īŧŒå•Šaaaaa啊啊啊啊啊啊啊啊啊啊aaa啊啊īŧˆå•Šå•Š~\aaa{aaa:aaaaaaaaaaaaaa:aaa}啊啊啊啊啊啊啊īŧ‰īŧŒå•Šå•Šå•Šå•Šå•Šå•Š\aaaaaaaa{aaaaa}啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +\aaa{aaaaaaa-aaaa} + +\aaaaa{aaaaaaa}[aa] +\aaaaa{aaaaaaaaaaaaa} + + aaaaaa aaaaa_aaaaaaaaaa { + aaaaaa aaaaaa * (*aaaaaa) (aaaaaa aaaaa *, + aaaaaa aaaaaa *, + aaaaaaaa aaa); + aaa (*aaaaaaaa) (aaaaaa aaaaaa *, aaaa __aaaa *, + aaa); + aaa (*aaaaaa) (aaaaaa aaaaa *, aaaaaa aaaaaa *, + aaaaa_a, aaaa); + aaa (*aaaa) (aaaaaa aaaaaa *, aaaaaa aaaaa *, + aaaaaa aaaaaa *); + aaa (*aaaaaa) (aaaaaa aaaaa *, aaaaaa aaaaaa *); + aaa (*aaaaaaa) (aaaaaa aaaaa *, aaaaaa aaaaaa *, + aaaaa aaaa *); + aaa (*aaaaa) (aaaaaa aaaaa *, aaaaaa aaaaaa *, + aaaaa_a); + aaa (*aaaaa) (aaaaaa aaaaa *, aaaaaa aaaaaa *); + aaa (*aaaaa) (aaaaaa aaaaa *, aaaaaa aaaaaa *, + aaaaa_a, aaa_a); + aaa (*aaaaaa) (aaaaaa aaaaa *, aaaaaa aaaaaa *, + aaaaaa aaaaa *, aaaaaa aaaaaa *, + aaaaaaaa aaa); + aaa (*aaaaaa_aaaa)(aaaaaa aaaaa *, + aaaaaa aaaaaaaa64 *, aaa); + ... + }; + +\aaa{aaaaaaaaaaaaa} +\aaaaaaa{aaaaa aaa啊啊啊aaaaa啊啊啊啊啊}% +\aaaaa{aaaa:aa:aaa-aaaaa-aaa} +\aaa{aaaaaaa} + +\aaaaaaaaa{aaaaa啊啊啊啊啊。} +啊啊啊啊~\aaa{aaaa:aa:aaa-aaaaa-aaa}啊啊啊啊啊啊aaaaa啊aaa啊啊啊啊啊啊aaaaa啊啊啊啊啊。 +啊啊啊啊啊啊īŧˆ\aaaaaaaa{aaaaaa}īŧ‰ã€å•Šå•Šå•Šīŧˆ\aaaaaaaa{aaaa}īŧ‰ã€å•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧˆ\aaaaaaaa{aaaaaa}īŧ‰ã€å•Šå•Šå•Šå•Šå•Šå•Šīŧˆ\aaaaaaaa{aaaaaaa}īŧ‰ã€å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧˆ\aaaaaaaa{aaaaa}啊\aaaaaaaa{aaaaa}īŧ‰ã€å•Šå•Šå•Šīŧˆ\aaaaaaaa{aaaaaa}īŧ‰ã€å•Šå•Šå•Šå•Šīŧˆ\aaaaaaaa{aaaaaa_aaaa}īŧ‰å•Šå•Šå•Šã€‚ + + +aaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒaaa啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + + + +%啊啊啊啊啊啊啊啊啊īŧŒaaa啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊aaaaa啊aaa啊啊啊啊啊啊啊啊啊啊īŧŒå•Š + +\aaaaa{aaaaaaaa} +aaaaa啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŸå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŸ + +啊īŧšå•Šå•Šaaa啊啊啊啊。啊啊啊aaa啊啊啊啊啊啊“啊啊”。 +\aaa{aaaaaaaa} + +% \aaa{啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šã€‚啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŸå•Šå•Šå•Šaaaaaaa啊啊啊īŧŒå•Šå•Šå•Šå•Šã€‚啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚} + +\aaaaaaaaaaaaa{啊啊啊啊} + +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +aaaaa啊aaa啊啊aaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šã€aaaaa、啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊īŧŒaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 + +aaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊aaa啊啊啊啊\aaaa{啊啊啊啊啊啊}啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +aaa啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +%啊啊啊啊啊aaaaa啊啊aaa啊啊啊啊啊啊啊啊啊啊。 + +\aaaaaaaaa{aaa啊啊啊啊啊。} +aaa啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +aaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€å•Šå•Šã€å•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaa啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šaaa啊啊啊啊啊啊啊啊啊啊啊。 + +\aaaaaaaaa{aaa啊啊aaaaa。} +aaaaa啊aaa啊啊啊啊啊啊啊啊aaaaa啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊aaaaa啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šaaa啊aaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaa啊aaaaa啊啊啊啊。 + +啊啊啊啊啊啊啊啊啊aaaaaīŧŒaaa啊啊啊啊啊aaaaa啊啊īŧˆaaaaaaīŧ‰ã€‚ +aaa啊aaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊aaaaa啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊aaaaaīŧŒå•Šå•Šaaaaa啊啊啊aaaaa啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠaaaaaīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%啊啊aaaaa啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +\aaaaaaaaa{aaa啊啊啊啊啊啊啊啊。} +啊啊aaa啊aaaaa啊啊啊啊啊啊īŧˆaaaaa aaaaīŧ‰å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 + + +\aaaaaaaaa{aaa啊啊啊啊啊。} +%啊aaa啊啊啊啊啊啊啊啊啊aaaaaaīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaaaaa。 + +aaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊aaaaa啊啊啊啊。 +啊啊aaaaa啊啊啊啊īŧŒaaa啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧˆaaaaaaīŧ‰ã€‚ + +\aaaaa{aaaaaaaa} +啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šaaa啊啊啊啊啊啊啊啊啊啊啊啊。 + 啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŸ +\aaa{aaaaaaaa} + + +%\aaaaa{aaaaaaa-aaaa}[啊啊啊啊啊啊] +% +%aaaaa aaa啊啊啊啊啊啊aaaaa啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +%aaaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%\aaa{aaaaaaa-aaaa} + +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 + +啊啊“啊啊—啊啊—啊啊”īŧˆaaaa-aaaaaa-aaaaaīŧ‰å•Šå•Šå•ŠīŧŒå•Šå•Šå•Šaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 + +\aaaaa{aaaaaaaa} +啊啊啊啊啊啊啊啊啊啊啊īŧŒaaa啊啊啊啊啊啊啊啊啊īŧŸ +啊啊啊啊啊啊啊īŧŸ +\aaa{aaaaaaaa} + +\aaaaaaaaaa{啊啊啊啊啊啊啊啊啊} + + + +% % aaaaaaa(aa): 啊啊aa啊啊啊啊啊啊啊啊īŧŸå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +% % 啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŸå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒ +% % 啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•ŠaaīŧŸ + + +啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šaaaaa~\aaaa{aaaaa-1-2017}īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧˆå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧ‰å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +\aaaaaaaaaaaaa{啊啊啊啊啊啊啊啊啊啊啊} + +%啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +%啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šaaaaa啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +\aaaaa{aaaaaaa}[aaa] +\aaaaa{aaaaaaaaaaaaa} + + #aaaaaaa + #aaaaaaa + #aaaaaaa + + #aaaaaa aaaa_aaaa 20 + + aaa aaaa() + { + aaa aa; + aaaa aaaa[aaaa_aaaa + 1]; + + // 啊啊啊啊 + aa = aaaa("/aaaa/aaaaaa/aaaaaaaaaa.aaa", + a_aaaa | a_aaaaa); + + // 啊啊啊啊啊啊啊20啊啊啊 + aaaa(aa, aaaa, aaaa_aaaa); + + // 啊啊啊啊啊啊啊20啊啊啊 + aaaa[aaaa_aaaa] = '\0'; + aaaaaa("aaaa aaaa: %a\a", aaaa); + + // 啊啊啊啊啊啊6啊啊啊啊啊啊 + aaaaa(aa, "aaaaa\a", 6); + + // 啊啊啊啊 + aaaaa(aa); + + aaaaaa 0; + } +\aaa{aaaaaaaaaaaaa} +\aaaaaaa{啊啊啊啊啊啊啊啊啊啊}% +\aaaaa{aaaa:aa:aaaaaa-aaaaaaa} +\aaa{aaaaaaa} + +啊啊啊啊~\aaa{aaaa:aa:aaaaaa-aaaaaaa}啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊~\aaa{aaaa:aa:aaaaaaaaaaaaa}啊啊啊啊啊~\aaa{aaaa:aa:aaaaaaa-aaaaaaaaaa}啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +啊啊啊啊~\aaa{aaaa:aa:aaaaaa-aaaaaaa}啊啊啊啊啊啊啊啊\aaaaaaaa{aaaa}啊啊啊啊啊啊“/aaaa/aaaaaa/aaaaaaaaaa.aaa”啊啊啊。 +啊啊啊啊啊啊\aaaaaaaa{a_aaaa|a_aaaaa}。 +啊啊\aaaaaaaa{a_aaaa}啊啊啊啊啊啊啊啊啊īŧˆå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šīŧ‰īŧ› +\aaaaaaaa{a_aaaaa}啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊īŧŒ\aaaaaaaa{aaaa}啊啊啊啊啊啊啊\aaaaaa{啊啊啊啊啊}。 +啊啊啊啊啊啊啊啊啊\aaaaaaaa{aa}啊啊啊啊。 + +啊啊īŧŒå•Šå•Šå•Š\aaaaaaaa{aa}啊啊啊啊啊啊\aaaaaaaa{aaaa}啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊\aaaaaaaa{aaaa}啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š\aaaaaaaa{20}啊啊啊啊啊啊啊啊啊啊\aaaaaaaa{aaaa}啊啊啊啊啊啊啊。 +啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Š\aaaaaaaa{aaaaa}啊啊啊啊啊啊啊啊啊\aaaaaaaa{6}啊啊啊īŧŒå•Šå•Šå•Šå•Š\aaaaaaaa{aaaaa}啊啊啊啊啊啊īŧˆ\aaaaaaaa{\a}īŧ‰ã€‚ +啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +啊啊啊啊啊啊啊aaaaa啊啊啊啊啊啊啊啊aaa啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +\aaaaaaaaaaaaa{啊啊啊啊} +啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Š\aaaaaaaa{aaaa}啊\aaaa{啊啊啊啊}啊啊啊啊啊啊\aaaa{啊啊啊啊啊}īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +啊啊啊啊啊\aaaaaaaa{aaaa}啊啊啊īŧŒaaaa啊啊啊啊啊啊\aaaaaaaa{aaa_aaaa}啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šaaa啊啊啊啊。 +aaa啊啊啊\aaaaaaaa{aaa_aaaa}啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šã€‚ + +啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊~\aaa{aaaa:aa:aaaaaa-aaaaaaa}啊īŧŒå•Šå•Šâ€œ/aaaa/aaaaaa/aaaaaaaaaa.aaa”啊啊啊啊“aaaa”、“aaaaaa”啊“aaaaaaaaaa.aaa”。 +啊啊啊啊啊啊“/”啊啊啊\aaaaaa{啊啊啊啊}īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +aaa啊啊啊啊啊啊啊啊啊啊啊“aaaa”啊啊啊。 +啊啊啊啊啊īŧŒaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊“aaaa”啊啊啊啊啊啊啊īŧŒaaa啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊、啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊īŧŒaaa啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊īŧŒaaa啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šâ€œaaaa”啊啊啊啊啊啊啊“aaaaaa”啊啊。 +啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊\aaaaaaaa{aaaa}啊啊啊啊\aaaaaaaa{a_aaaaa}啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šâ€œaaaaaaaaaa.aaa”啊啊啊啊啊īŧŒå•Šaaa啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +啊啊啊啊啊啊啊啊īŧŒaaa啊啊啊“aaaaaaaaaa.aaa”啊啊啊啊啊啊aaaaa。 +啊啊aaa啊啊啊啊啊aaaaa啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +\aaaaaaaaaaaaa{啊啊啊啊啊} + +\aaaaa{aaaaaa}[aaa] +\aaaaaaaaa +\aaaaaaaaaaaaaaa[aaaaa=0.6\aaaaaaaaa]{aaaa/aaaa/aa/aaa-aa.aaa} \\[1aa] +\aaaaaaa{啊啊啊啊啊}% +\aaaaa{aaa:aa:aaa-aa} +\aaa{aaaaaa} + +%啊aaaaa啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š\aaaaaaaa{aa}啊啊\aaaaaaaa{aaaaaa}啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šaaa啊啊啊啊。 +啊啊~\aaa{aaa:aa:aaa-aa}īŧŒaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šaaaaaīŧˆå•Šå•Šå•Šå•Šå•Šå•Šaaaaaīŧ‰ã€å•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +啊aaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊aaaaa啊īŧŒaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +aaa啊aaaaa啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Š0īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊īŧŒaaa啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ + +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧˆå•Š\aaaaaaaa{aaaa}啊\aaaaaaaa{aaaaa}īŧ‰å•ŠīŧŒaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒaaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 + +啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。 +啊啊啊啊啊啊啊啊\aaaa{啊啊啊啊}啊啊\aaaa{啊啊啊啊啊}īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊啊啊啊啊aaa啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ +啊啊īŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•ŠīŧŒå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šå•Šã€‚ diff --git a/src/vs/base/test/node/encoding/fixtures/some.cp1252.txt b/src/vs/workbench/services/textfile/test/node/encoding/fixtures/some.cp1252.txt similarity index 100% rename from src/vs/base/test/node/encoding/fixtures/some.cp1252.txt rename to src/vs/workbench/services/textfile/test/node/encoding/fixtures/some.cp1252.txt diff --git a/src/vs/base/test/node/encoding/fixtures/some.css.qwoff b/src/vs/workbench/services/textfile/test/node/encoding/fixtures/some.css.qwoff similarity index 100% rename from src/vs/base/test/node/encoding/fixtures/some.css.qwoff rename to src/vs/workbench/services/textfile/test/node/encoding/fixtures/some.css.qwoff diff --git a/src/vs/base/test/node/encoding/fixtures/some.json.png b/src/vs/workbench/services/textfile/test/node/encoding/fixtures/some.json.png similarity index 100% rename from src/vs/base/test/node/encoding/fixtures/some.json.png rename to src/vs/workbench/services/textfile/test/node/encoding/fixtures/some.json.png diff --git a/src/vs/base/test/node/encoding/fixtures/some.pdf b/src/vs/workbench/services/textfile/test/node/encoding/fixtures/some.pdf similarity index 100% rename from src/vs/base/test/node/encoding/fixtures/some.pdf rename to src/vs/workbench/services/textfile/test/node/encoding/fixtures/some.pdf diff --git a/src/vs/base/test/node/encoding/fixtures/some.png.txt b/src/vs/workbench/services/textfile/test/node/encoding/fixtures/some.png.txt similarity index 100% rename from src/vs/base/test/node/encoding/fixtures/some.png.txt rename to src/vs/workbench/services/textfile/test/node/encoding/fixtures/some.png.txt diff --git a/src/vs/base/test/node/encoding/fixtures/some.qwoff.txt b/src/vs/workbench/services/textfile/test/node/encoding/fixtures/some.qwoff.txt similarity index 100% rename from src/vs/base/test/node/encoding/fixtures/some.qwoff.txt rename to src/vs/workbench/services/textfile/test/node/encoding/fixtures/some.qwoff.txt diff --git a/src/vs/base/test/node/encoding/fixtures/some.shiftjis.txt b/src/vs/workbench/services/textfile/test/node/encoding/fixtures/some.shiftjis.txt similarity index 100% rename from src/vs/base/test/node/encoding/fixtures/some.shiftjis.txt rename to src/vs/workbench/services/textfile/test/node/encoding/fixtures/some.shiftjis.txt diff --git a/src/vs/base/test/node/encoding/fixtures/some.xml.png b/src/vs/workbench/services/textfile/test/node/encoding/fixtures/some.xml.png similarity index 100% rename from src/vs/base/test/node/encoding/fixtures/some.xml.png rename to src/vs/workbench/services/textfile/test/node/encoding/fixtures/some.xml.png diff --git a/src/vs/base/test/node/encoding/fixtures/some_ansi.css b/src/vs/workbench/services/textfile/test/node/encoding/fixtures/some_ansi.css similarity index 100% rename from src/vs/base/test/node/encoding/fixtures/some_ansi.css rename to src/vs/workbench/services/textfile/test/node/encoding/fixtures/some_ansi.css diff --git a/src/vs/base/test/node/encoding/fixtures/some_file.css b/src/vs/workbench/services/textfile/test/node/encoding/fixtures/some_file.css similarity index 100% rename from src/vs/base/test/node/encoding/fixtures/some_file.css rename to src/vs/workbench/services/textfile/test/node/encoding/fixtures/some_file.css diff --git a/src/vs/workbench/services/textfile/test/node/encoding/fixtures/some_gbk.txt b/src/vs/workbench/services/textfile/test/node/encoding/fixtures/some_gbk.txt new file mode 100644 index 00000000000..4f55d514fd1 --- /dev/null +++ b/src/vs/workbench/services/textfile/test/node/encoding/fixtures/some_gbk.txt @@ -0,0 +1 @@ +1ŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°ËŌģļūČũËÄÎåÁųÆß°Ë \ No newline at end of file diff --git a/src/vs/base/test/node/encoding/fixtures/some_utf16be.css b/src/vs/workbench/services/textfile/test/node/encoding/fixtures/some_utf16be.css similarity index 100% rename from src/vs/base/test/node/encoding/fixtures/some_utf16be.css rename to src/vs/workbench/services/textfile/test/node/encoding/fixtures/some_utf16be.css diff --git a/src/vs/base/test/node/encoding/fixtures/some_utf16le.css b/src/vs/workbench/services/textfile/test/node/encoding/fixtures/some_utf16le.css similarity index 100% rename from src/vs/base/test/node/encoding/fixtures/some_utf16le.css rename to src/vs/workbench/services/textfile/test/node/encoding/fixtures/some_utf16le.css diff --git a/src/vs/base/test/node/encoding/fixtures/some_utf8.css b/src/vs/workbench/services/textfile/test/node/encoding/fixtures/some_utf8.css similarity index 100% rename from src/vs/base/test/node/encoding/fixtures/some_utf8.css rename to src/vs/workbench/services/textfile/test/node/encoding/fixtures/some_utf8.css diff --git a/src/vs/base/test/node/encoding/fixtures/utf16_be_nobom.txt b/src/vs/workbench/services/textfile/test/node/encoding/fixtures/utf16_be_nobom.txt similarity index 100% rename from src/vs/base/test/node/encoding/fixtures/utf16_be_nobom.txt rename to src/vs/workbench/services/textfile/test/node/encoding/fixtures/utf16_be_nobom.txt diff --git a/src/vs/base/test/node/encoding/fixtures/utf16_le_nobom.txt b/src/vs/workbench/services/textfile/test/node/encoding/fixtures/utf16_le_nobom.txt similarity index 100% rename from src/vs/base/test/node/encoding/fixtures/utf16_le_nobom.txt rename to src/vs/workbench/services/textfile/test/node/encoding/fixtures/utf16_le_nobom.txt diff --git a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts index e0a8e353551..993ca56f1ed 100644 --- a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts +++ b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts @@ -6,44 +6,65 @@ import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITextModel } from 'vs/editor/common/model'; -import { IDisposable, toDisposable, IReference, ReferenceCollection, ImmortalReference } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable, IReference, ReferenceCollection, Disposable } from 'vs/base/common/lifecycle'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; import { ITextFileService, TextFileLoadReason } from 'vs/workbench/services/textfile/common/textfiles'; import * as network from 'vs/base/common/network'; import { ITextModelService, ITextModelContentProvider, ITextEditorModel, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; -import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { IFileService } from 'vs/platform/files/common/files'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; +import { ModelUndoRedoParticipant } from 'vs/editor/common/services/modelUndoRedoParticipant'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; class ResourceModelCollection extends ReferenceCollection> { - private providers: { [scheme: string]: ITextModelContentProvider[] } = Object.create(null); - private modelsToDispose = new Set(); + private readonly providers = new Map(); + private readonly modelsToDispose = new Set(); constructor( @IInstantiationService private readonly instantiationService: IInstantiationService, @ITextFileService private readonly textFileService: ITextFileService, @IFileService private readonly fileService: IFileService, - @ITelemetryService private readonly telemetryService: ITelemetryService, + @IModelService private readonly modelService: IModelService ) { super(); } - async createReferencedObject(key: string, skipActivateProvider?: boolean): Promise { + createReferencedObject(key: string): Promise { + return this.doCreateReferencedObject(key); + } + + private async doCreateReferencedObject(key: string, skipActivateProvider?: boolean): Promise { + + // Untrack as being disposed this.modelsToDispose.delete(key); + // inMemory Schema: go through model service cache const resource = URI.parse(key); + if (resource.scheme === network.Schemas.inMemory) { + const cachedModel = this.modelService.getModel(resource); + if (!cachedModel) { + throw new Error(`Unable to resolve inMemory resource ${key}`); + } - // File or remote file provider already known + return this.instantiationService.createInstance(ResourceEditorModel, resource); + } + + // Untitled Schema: go through untitled text service + if (resource.scheme === network.Schemas.untitled) { + return this.textFileService.untitled.resolve({ untitledResource: resource }); + } + + // File or remote file: go through text file service if (this.fileService.canHandleResource(resource)) { return this.textFileService.files.resolve(resource, { reason: TextFileLoadReason.REFERENCE }); } // Virtual documents - if (this.providers[resource.scheme]) { + if (this.providers.has(resource.scheme)) { await this.resolveTextModelContent(key); return this.instantiationService.createInstance(ResourceEditorModel, resource); @@ -53,131 +74,135 @@ class ResourceModelCollection extends ReferenceCollection): void { + + // untitled and inMemory are bound to a different lifecycle + const resource = URI.parse(key); + if (resource.scheme === network.Schemas.untitled || resource.scheme === network.Schemas.inMemory) { + return; + } + + // Track as being disposed before waiting for model to load + // to handle the case that the reference is aquired again this.modelsToDispose.add(key); - modelPromise.then(model => { - if (this.modelsToDispose.has(key)) { - if (model instanceof TextFileEditorModel) { - this.textFileService.files.disposeModel(model); - } else { - model.dispose(); + (async () => { + try { + const model = await modelPromise; + + if (!this.modelsToDispose.has(key)) { + // return if model has been aquired again meanwhile + return; } + + if (model instanceof TextFileEditorModel) { + // text file models have conditions that prevent them + // from dispose, so we have to wait until we can dispose + await this.textFileService.files.canDispose(model); + } + + if (!this.modelsToDispose.has(key)) { + // return if model has been aquired again meanwhile + return; + } + + // Finally we can dispose the model + model.dispose(); + } catch (error) { + // ignore + } finally { + this.modelsToDispose.delete(key); // Untrack as being disposed } - }, err => { - // ignore - }); + })(); } registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable { - const registry = this.providers; - const providers = registry[scheme] || (registry[scheme] = []); + let providers = this.providers.get(scheme); + if (!providers) { + providers = []; + this.providers.set(scheme, providers); + } providers.unshift(provider); return toDisposable(() => { - const array = registry[scheme]; - - if (!array) { + const providersForScheme = this.providers.get(scheme); + if (!providersForScheme) { return; } - const index = array.indexOf(provider); - + const index = providersForScheme.indexOf(provider); if (index === -1) { return; } - array.splice(index, 1); + providersForScheme.splice(index, 1); - if (array.length === 0) { - delete registry[scheme]; + if (providersForScheme.length === 0) { + this.providers.delete(scheme); } }); } hasTextModelContentProvider(scheme: string): boolean { - return this.providers[scheme] !== undefined; + return this.providers.get(scheme) !== undefined; } private async resolveTextModelContent(key: string): Promise { const resource = URI.parse(key); - const providers = this.providers[resource.scheme] || []; + const providersForScheme = this.providers.get(resource.scheme) || []; - if (resource.query || resource.fragment) { - type TextModelResolverUri = { - query: boolean; - fragment: boolean; - }; - type TextModelResolverUriMeta = { - query: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - fragment: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - }; - this.telemetryService.publicLog2('textmodelresolveruri', { - query: Boolean(resource.query), - fragment: Boolean(resource.fragment) - }); - } - - for (const provider of providers) { + for (const provider of providersForScheme) { const value = await provider.provideTextContent(resource); if (value) { return value; } } - throw new Error('resource is not available'); + + throw new Error(`Unable to resolve text model content for resource ${key}`); } } -export class TextModelResolverService implements ITextModelService { +export class TextModelResolverService extends Disposable implements ITextModelService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; - private resourceModelCollection: ResourceModelCollection; + private readonly resourceModelCollection = this.instantiationService.createInstance(ResourceModelCollection); constructor( - @IUntitledTextEditorService private readonly untitledTextEditorService: IUntitledTextEditorService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IModelService private readonly modelService: IModelService + @IFileService private readonly fileService: IFileService, + @IUndoRedoService private readonly undoRedoService: IUndoRedoService, + @IModelService private readonly modelService: IModelService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, ) { - this.resourceModelCollection = instantiationService.createInstance(ResourceModelCollection); + super(); + + this._register(new ModelUndoRedoParticipant(this.modelService, this, this.undoRedoService)); } - createModelReference(resource: URI): Promise> { - return this.doCreateModelReference(resource); - } + async createModelReference(resource: URI): Promise> { - private async doCreateModelReference(resource: URI): Promise> { - - // Untitled Schema: go through untitled text service - if (resource.scheme === network.Schemas.untitled) { - const model = await this.untitledTextEditorService.resolve({ untitledResource: resource }); - - return new ImmortalReference(model); - } - - // InMemory Schema: go through model service cache - if (resource.scheme === network.Schemas.inMemory) { - const cachedModel = this.modelService.getModel(resource); - if (!cachedModel) { - throw new Error('Cant resolve inmemory resource'); - } - - return new ImmortalReference(this.instantiationService.createInstance(ResourceEditorModel, resource) as IResolvedTextEditorModel); - } + // From this moment on, only operate on the canonical resource + // to ensure we reduce the chance of resolving the same resource + // with different resource forms (e.g. path casing on Windows) + resource = this.uriIdentityService.asCanonicalUri(resource); const ref = this.resourceModelCollection.acquire(resource.toString()); try { const model = await ref.object; - return { object: model as IResolvedTextEditorModel, dispose: () => ref.dispose() }; + return { + object: model as IResolvedTextEditorModel, + dispose: () => ref.dispose() + }; } catch (error) { ref.dispose(); @@ -189,12 +214,12 @@ export class TextModelResolverService implements ITextModelService { return this.resourceModelCollection.registerTextModelContentProvider(scheme, provider); } - hasTextModelContentProvider(scheme: string): boolean { - if (scheme === network.Schemas.untitled || scheme === network.Schemas.inMemory) { - return true; // we handle untitled:// and inMemory:// within + canHandleResource(resource: URI): boolean { + if (this.fileService.canHandleResource(resource) || resource.scheme === network.Schemas.untitled || resource.scheme === network.Schemas.inMemory) { + return true; // we handle file://, untitled:// and inMemory:// automatically } - return this.resourceModelCollection.hasTextModelContentProvider(scheme); + return this.resourceModelCollection.hasTextModelContentProvider(resource.scheme); } } diff --git a/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts b/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts index d8ca80f38f4..f8b2464c808 100644 --- a/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts +++ b/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts @@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestServiceAccessor, TestTextFileEditorModelManager } from 'vs/workbench/test/browser/workbenchTestServices'; import { toResource } from 'vs/base/test/common/utils'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; @@ -17,6 +17,7 @@ import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/commo import { Event } from 'vs/base/common/event'; import { timeout } from 'vs/base/common/async'; import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; +import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; suite('Workbench - TextModelResolverService', () => { @@ -51,7 +52,7 @@ suite('Workbench - TextModelResolverService', () => { }); let resource = URI.from({ scheme: 'test', authority: null!, path: 'thePath' }); - let input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource, undefined); + let input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, resource, 'The Name', 'The Description', undefined); const model = await input.resolve(); assert.ok(model); @@ -72,7 +73,7 @@ suite('Workbench - TextModelResolverService', () => { test('resolve file', async function () { const textModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_resolver.txt'), 'utf8', undefined); - (accessor.textFileService.files).add(textModel.resource, textModel); + (accessor.textFileService.files).add(textModel.resource, textModel); await textModel.load(); @@ -94,6 +95,63 @@ suite('Workbench - TextModelResolverService', () => { assert.equal(disposed, true); }); + test('resolved dirty file eventually disposes', async function () { + const textModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_resolver.txt'), 'utf8', undefined); + (accessor.textFileService.files).add(textModel.resource, textModel); + + const loadedModel = await textModel.load(); + + loadedModel.updateTextEditorModel(createTextBufferFactory('make dirty')); + + const ref = await accessor.textModelResolverService.createModelReference(textModel.resource); + + let disposed = false; + Event.once(loadedModel.onDispose)(() => { + disposed = true; + }); + + ref.dispose(); + await timeout(0); + assert.equal(disposed, false); // not disposed because model still dirty + + loadedModel.revert(); + + await timeout(0); + assert.equal(disposed, true); // now disposed because model got reverted + }); + + test('resolved dirty file does not dispose when new reference created', async function () { + const textModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_resolver.txt'), 'utf8', undefined); + (accessor.textFileService.files).add(textModel.resource, textModel); + + const loadedModel = await textModel.load(); + + loadedModel.updateTextEditorModel(createTextBufferFactory('make dirty')); + + const ref1 = await accessor.textModelResolverService.createModelReference(textModel.resource); + + let disposed = false; + Event.once(loadedModel.onDispose)(() => { + disposed = true; + }); + + ref1.dispose(); + await timeout(0); + assert.equal(disposed, false); // not disposed because model still dirty + + const ref2 = await accessor.textModelResolverService.createModelReference(textModel.resource); + + loadedModel.revert(); + + await timeout(0); + assert.equal(disposed, false); // not disposed because we got another ref meanwhile + + ref2.dispose(); + + await timeout(0); + assert.equal(disposed, true); // now disposed because last ref got disposed + }); + test('resolve untitled', async () => { const service = accessor.untitledTextEditorService; const untitledModel = service.create(); diff --git a/src/vs/workbench/services/textresourceProperties/common/textResourcePropertiesService.ts b/src/vs/workbench/services/textresourceProperties/common/textResourcePropertiesService.ts index 9177dcc88b7..fca00b8ebb5 100644 --- a/src/vs/workbench/services/textresourceProperties/common/textResourcePropertiesService.ts +++ b/src/vs/workbench/services/textresourceProperties/common/textResourcePropertiesService.ts @@ -16,7 +16,7 @@ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteA export class TextResourcePropertiesService implements ITextResourcePropertiesService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private remoteEnvironment: IRemoteAgentEnvironment | null = null; diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index 935b267508d..e537e332668 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -69,7 +69,7 @@ const fileIconThemesExtPoint = registerFileIconThemeExtensionPoint(); const productIconThemesExtPoint = registerProductIconThemeExtensionPoint(); export class WorkbenchThemeService implements IWorkbenchThemeService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly container: HTMLElement; private settings: ThemeConfiguration; @@ -101,8 +101,9 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { @IWorkbenchLayoutService readonly layoutService: IWorkbenchLayoutService, @ILogService private readonly logService: ILogService ) { - this.container = layoutService.getWorkbenchContainer(); - this.settings = new ThemeConfiguration(configurationService); + this.container = layoutService.container; + const defaultThemeType = environmentService.configuration.defaultThemeType || DARK; + this.settings = new ThemeConfiguration(configurationService, defaultThemeType); this.colorThemeRegistry = new ThemeRegistry(extensionService, colorThemesExtPoint, ColorThemeData.fromExtensionTheme); this.colorThemeWatcher = new ThemeFileWatcher(fileService, environmentService, this.reloadCurrentColorTheme.bind(this)); @@ -123,9 +124,11 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { // themes are loaded asynchronously, we need to initialize // a color theme document with good defaults until the theme is loaded let themeData: ColorThemeData | undefined = ColorThemeData.fromStorageData(this.storageService); - const containerBaseTheme = this.getBaseThemeFromContainer(); - if (!themeData || themeData.baseTheme !== containerBaseTheme) { - themeData = ColorThemeData.createUnloadedTheme(containerBaseTheme); + if (environmentService.configuration.highContrast && themeData?.baseTheme !== HIGH_CONTRAST) { + themeData = ColorThemeData.createUnloadedThemeForThemeType(HIGH_CONTRAST); + } + if (!themeData) { + themeData = ColorThemeData.createUnloadedThemeForThemeType(defaultThemeType); } themeData.setCustomizations(this.settings); this.applyTheme(themeData, undefined, true); @@ -369,13 +372,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return null; } return themeData.ensureLoaded(this.extensionResourceLoaderService).then(_ => { - if (themeId === this.currentColorTheme.id && !this.currentColorTheme.isLoaded && this.currentColorTheme.hasEqualData(themeData)) { - this.currentColorTheme.clearCaches(); - // the loaded theme is identical to the perisisted theme. Don't need to send an event. - this.currentColorTheme = themeData; - themeData.setCustomizations(this.settings); - return Promise.resolve(themeData); - } themeData.setCustomizations(this.settings); return this.applyTheme(themeData, settingsTarget); }, error => { @@ -617,16 +613,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.onProductIconThemeChange.fire(this.currentProductIconTheme); } - - private getBaseThemeFromContainer() { - for (let i = this.container.classList.length - 1; i >= 0; i--) { - const item = this.container.classList.item(i); - if (item === VS_LIGHT_THEME || item === VS_DARK_THEME || item === VS_HC_THEME) { - return item; - } - } - return VS_DARK_THEME; - } } class ThemeFileWatcher { diff --git a/src/vs/workbench/services/themes/common/colorExtensionPoint.ts b/src/vs/workbench/services/themes/common/colorExtensionPoint.ts index 9ed134bb638..3ad28da9268 100644 --- a/src/vs/workbench/services/themes/common/colorExtensionPoint.ts +++ b/src/vs/workbench/services/themes/common/colorExtensionPoint.ts @@ -32,7 +32,7 @@ const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint; getColorTheme(): IWorkbenchColorTheme; getColorThemes(): Promise; diff --git a/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts b/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts index 15422f0975f..07d9b97cbb6 100644 --- a/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts +++ b/src/vs/workbench/services/themes/test/electron-browser/tokenStyleResolving.test.ts @@ -15,7 +15,7 @@ import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemPro import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { getPathFromAmdModule } from 'vs/base/common/amd'; -import { ExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/electron-browser/extensionResourceLoaderService'; +import { ExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/electron-sandbox/extensionResourceLoaderService'; import { ITokenStyle } from 'vs/platform/theme/common/themeService'; const undefinedStyle = { bold: undefined, underline: undefined, italic: undefined }; diff --git a/src/vs/workbench/services/timer/browser/timerService.ts b/src/vs/workbench/services/timer/browser/timerService.ts new file mode 100644 index 00000000000..f7e60ff8f31 --- /dev/null +++ b/src/vs/workbench/services/timer/browser/timerService.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 * as perf from 'vs/base/common/performance'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IUpdateService } from 'vs/platform/update/common/update'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; + +/* __GDPR__FRAGMENT__ + "IMemoryInfo" : { + "workingSetSize" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "privateBytes": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "sharedBytes": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } + } +*/ +export interface IMemoryInfo { + readonly workingSetSize: number; + readonly privateBytes: number; + readonly sharedBytes: number; +} + +/* __GDPR__FRAGMENT__ + "IStartupMetrics" : { + "version" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "ellapsed" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "isLatestVersion": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "didUseCachedData": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "windowKind": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "windowCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "viewletId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "panelId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "editorIds": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "timers.ellapsedAppReady" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedWindowLoad" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedWindowLoadToRequire" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedExtensions" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedExtensionsReady" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedRequire" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedWorkspaceStorageInit" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedWorkspaceServiceInit" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedViewletRestore" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedPanelRestore" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedEditorRestore" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedWorkbench" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedTimersToTimersComputed" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedNlsGeneration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "platform" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "release" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "arch" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "totalmem" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "freemem" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "meminfo" : { "${inline}": [ "${IMemoryInfo}" ] }, + "cpus.count" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "cpus.speed" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "cpus.model" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "initialStartup" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "hasAccessibilitySupport" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "isVMLikelyhood" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "emptyWorkbench" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "loadavg" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } + } +*/ +export interface IStartupMetrics { + + /** + * The version of these metrics. + */ + readonly version: 2; + + /** + * If this started the main process and renderer or just a renderer (new or reloaded). + */ + readonly initialStartup: boolean; + + /** + * No folder, no file, no workspace has been opened + */ + readonly emptyWorkbench: boolean; + + /** + * This is the latest (stable/insider) version. Iff not we should ignore this + * measurement. + */ + readonly isLatestVersion: boolean; + + /** + * Whether we asked for and V8 accepted cached data. + */ + readonly didUseCachedData: boolean; + + /** + * How/why the window was created. See https://github.com/Microsoft/vscode/blob/d1f57d871722f4d6ba63e4ef6f06287121ceb045/src/vs/platform/lifecycle/common/lifecycle.ts#L50 + */ + readonly windowKind: number; + + /** + * The total number of windows that have been restored/created + */ + readonly windowCount: number; + + /** + * The active viewlet id or `undedined` + */ + readonly viewletId?: string; + + /** + * The active panel id or `undefined` + */ + readonly panelId?: string; + + /** + * The editor input types or `[]` + */ + readonly editorIds: string[]; + + /** + * The time it took to create the workbench. + * + * * Happens in the main-process *and* the renderer-process + * * Measured with the *start* and `didStartWorkbench`-performance mark. The *start* is either the start of the + * main process or the start of the renderer. + * * This should be looked at carefully because times vary depending on + * * This being the first window, the only window, or a reloaded window + * * Cached data being present and used or not + * * The numbers and types of editors being restored + * * The numbers of windows being restored (when starting 'fresh') + * * The viewlet being restored (esp. when it's a contributed viewlet) + */ + readonly ellapsed: number; + + /** + * Individual timers... + */ + readonly timers: { + /** + * The time it took to receieve the [`ready`](https://electronjs.org/docs/api/app#event-ready)-event. Measured from the first line + * of JavaScript code till receiving that event. + * + * * Happens in the main-process + * * Measured with the `main:started` and `main:appReady` performance marks. + * * This can be compared between insider and stable builds. + * * This should be looked at per OS version and per electron version. + * * This is often affected by AV software (and can change with AV software updates outside of our release-cycle). + * * It is not our code running here and we can only observe what's happening. + */ + readonly ellapsedAppReady?: number; + + /** + * The time it took to generate NLS data. + * + * * Happens in the main-process + * * Measured with the `nlsGeneration:start` and `nlsGeneration:end` performance marks. + * * This only happens when a non-english locale is being used. + * * It is our code running here and we should monitor this carefully for regressions. + */ + readonly ellapsedNlsGeneration?: number; + + /** + * The time it took to tell electron to open/restore a renderer (browser window). + * + * * Happens in the main-process + * * Measured with the `main:appReady` and `main:loadWindow` performance marks. + * * This can be compared between insider and stable builds. + * * It is our code running here and we should monitor this carefully for regressions. + */ + readonly ellapsedWindowLoad?: number; + + /** + * The time it took to create a new renderer (browser window) and to initialize that to the point + * of load the main-bundle (`workbench.desktop.main.js`). + * + * * Happens in the main-process *and* the renderer-process + * * Measured with the `main:loadWindow` and `willLoadWorkbenchMain` performance marks. + * * This can be compared between insider and stable builds. + * * It is mostly not our code running here and we can only observe what's happening. + * + */ + readonly ellapsedWindowLoadToRequire: number; + + /** + * The time it took to require the workspace storage DB, connect to it + * and load the initial set of values. + * + * * Happens in the renderer-process + * * Measured with the `willInitWorkspaceStorage` and `didInitWorkspaceStorage` performance marks. + */ + readonly ellapsedWorkspaceStorageInit: number; + + /** + * The time it took to initialize the workspace and configuration service. + * + * * Happens in the renderer-process + * * Measured with the `willInitWorkspaceService` and `didInitWorkspaceService` performance marks. + */ + readonly ellapsedWorkspaceServiceInit: number; + + /** + * The time it took to load the main-bundle of the workbench, e.g. `workbench.desktop.main.js`. + * + * * Happens in the renderer-process + * * Measured with the `willLoadWorkbenchMain` and `didLoadWorkbenchMain` performance marks. + * * This varies *a lot* when V8 cached data could be used or not + * * This should be looked at with and without V8 cached data usage and per electron/v8 version + * * This is affected by the size of our code bundle (which grows about 3-5% per release) + */ + readonly ellapsedRequire: number; + + /** + * The time it took to read extensions' package.json-files *and* interpret them (invoking + * the contribution points). + * + * * Happens in the renderer-process + * * Measured with the `willLoadExtensions` and `didLoadExtensions` performance marks. + * * Reading of package.json-files is avoided by caching them all in a single file (after the read, + * until another extension is installed) + * * Happens in parallel to other things, depends on async timing + */ + readonly ellapsedExtensions: number; + + // the time from start till `didLoadExtensions` + // remove? + readonly ellapsedExtensionsReady: number; + + /** + * The time it took to restore the viewlet. + * + * * Happens in the renderer-process + * * Measured with the `willRestoreViewlet` and `didRestoreViewlet` performance marks. + * * This should be looked at per viewlet-type/id. + * * Happens in parallel to other things, depends on async timing + */ + readonly ellapsedViewletRestore: number; + + /** + * The time it took to restore the panel. + * + * * Happens in the renderer-process + * * Measured with the `willRestorePanel` and `didRestorePanel` performance marks. + * * This should be looked at per panel-type/id. + * * Happens in parallel to other things, depends on async timing + */ + readonly ellapsedPanelRestore: number; + + /** + * The time it took to restore editors - that is text editor and complex editor likes the settings UI + * or webviews (markdown preview). + * + * * Happens in the renderer-process + * * Measured with the `willRestoreEditors` and `didRestoreEditors` performance marks. + * * This should be looked at per editor and per editor type. + * * Happens in parallel to other things, depends on async timing + */ + readonly ellapsedEditorRestore: number; + + /** + * The time it took to create the workbench. + * + * * Happens in the renderer-process + * * Measured with the `willStartWorkbench` and `didStartWorkbench` performance marks. + */ + readonly ellapsedWorkbench: number; + + /** + * This time it took inside the renderer to start the workbench. + * + * * Happens in the renderer-process + * * Measured with the `renderer/started` and `didStartWorkbench` performance marks + */ + readonly ellapsedRenderer: number; + + // the time it took to generate this object. + // remove? + readonly ellapsedTimersToTimersComputed: number; + }; + + readonly hasAccessibilitySupport: boolean; + readonly isVMLikelyhood?: number; + readonly platform?: string; + readonly release?: string; + readonly arch?: string; + readonly totalmem?: number; + readonly freemem?: number; + readonly meminfo?: IMemoryInfo; + readonly cpus?: { count: number; speed: number; model: string; }; + readonly loadavg?: number[]; +} + +export interface ITimerService { + readonly _serviceBrand: undefined; + readonly startupMetrics: Promise; +} + +export const ITimerService = createDecorator('timerService'); + +export type Writeable = { -readonly [P in keyof T]: Writeable }; + +export abstract class AbstractTimerService implements ITimerService { + + declare readonly _serviceBrand: undefined; + + private _startupMetrics?: Promise; + + constructor( + @ILifecycleService private readonly _lifecycleService: ILifecycleService, + @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, + @IExtensionService private readonly _extensionService: IExtensionService, + @IUpdateService private readonly _updateService: IUpdateService, + @IViewletService private readonly _viewletService: IViewletService, + @IPanelService private readonly _panelService: IPanelService, + @IEditorService private readonly _editorService: IEditorService, + @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + ) { } + + get startupMetrics(): Promise { + if (!this._startupMetrics) { + this._startupMetrics = this._extensionService.whenInstalledExtensionsRegistered() + .then(() => this._computeStartupMetrics()) + .then(metrics => { + this._reportStartupTimes(metrics); + return metrics; + }); + } + return this._startupMetrics; + } + + private _reportStartupTimes(metrics: IStartupMetrics): void { + + // report IStartupMetrics as telemetry + /* __GDPR__ + "startupTimeVaried" : { + "${include}": [ + "${IStartupMetrics}" + ] + } + */ + this._telemetryService.publicLog('startupTimeVaried', metrics); + + // report raw timers as telemetry + const entries: Record = Object.create(null); + for (const entry of perf.getEntries()) { + entries[entry.name] = entry.startTime; + } + /* __GDPR__ + "startupRawTimers" : { + "entries": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } + } + */ + this._telemetryService.publicLog('startupRawTimers', { entries }); + } + + private async _computeStartupMetrics(): Promise { + + const now = Date.now(); + const initialStartup = this._isInitialStartup(); + const startMark = initialStartup ? 'main:started' : 'main:loadWindow'; + + const activeViewlet = this._viewletService.getActiveViewlet(); + const activePanel = this._panelService.getActivePanel(); + const info: Writeable = { + version: 2, + ellapsed: perf.getDuration(startMark, 'didStartWorkbench'), + + // reflections + isLatestVersion: Boolean(await this._updateService.isLatestVersion()), + didUseCachedData: this._didUseCachedData(), + windowKind: this._lifecycleService.startupKind, + windowCount: await this._getWindowCount(), + viewletId: activeViewlet?.getId(), + editorIds: this._editorService.visibleEditors.map(input => input.getTypeId()), + panelId: activePanel ? activePanel.getId() : undefined, + + // timers + timers: { + ellapsedAppReady: initialStartup ? perf.getDuration('main:started', 'main:appReady') : undefined, + ellapsedNlsGeneration: initialStartup ? perf.getDuration('nlsGeneration:start', 'nlsGeneration:end') : undefined, + ellapsedWindowLoad: initialStartup ? perf.getDuration('main:appReady', 'main:loadWindow') : undefined, + ellapsedWindowLoadToRequire: perf.getDuration('main:loadWindow', 'willLoadWorkbenchMain'), + ellapsedRequire: perf.getDuration('willLoadWorkbenchMain', 'didLoadWorkbenchMain'), + ellapsedWorkspaceStorageInit: perf.getDuration('willInitWorkspaceStorage', 'didInitWorkspaceStorage'), + ellapsedWorkspaceServiceInit: perf.getDuration('willInitWorkspaceService', 'didInitWorkspaceService'), + ellapsedExtensions: perf.getDuration('willLoadExtensions', 'didLoadExtensions'), + ellapsedEditorRestore: perf.getDuration('willRestoreEditors', 'didRestoreEditors'), + ellapsedViewletRestore: perf.getDuration('willRestoreViewlet', 'didRestoreViewlet'), + ellapsedPanelRestore: perf.getDuration('willRestorePanel', 'didRestorePanel'), + ellapsedWorkbench: perf.getDuration('willStartWorkbench', 'didStartWorkbench'), + ellapsedExtensionsReady: perf.getDuration(startMark, 'didLoadExtensions'), + ellapsedRenderer: perf.getDuration('renderer/started', 'didStartWorkbench'), + ellapsedTimersToTimersComputed: Date.now() - now, + }, + + // system info + platform: undefined, + release: undefined, + arch: undefined, + totalmem: undefined, + freemem: undefined, + meminfo: undefined, + cpus: undefined, + loadavg: undefined, + isVMLikelyhood: undefined, + initialStartup, + hasAccessibilitySupport: this._accessibilityService.isScreenReaderOptimized(), + emptyWorkbench: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY + }; + + await this._extendStartupInfo(info); + return info; + } + + protected abstract _isInitialStartup(): boolean; + + protected abstract _didUseCachedData(): boolean; + + protected abstract _getWindowCount(): Promise; + + protected abstract _extendStartupInfo(info: Writeable): Promise; +} + + +export class TimerService extends AbstractTimerService { + + protected _isInitialStartup(): boolean { + return false; + } + protected _didUseCachedData(): boolean { + return false; + } + protected async _getWindowCount(): Promise { + return 1; + } + protected async _extendStartupInfo(info: Writeable): Promise { + info.isVMLikelyhood = 0; + info.platform = navigator.userAgent; + info.release = navigator.appVersion; + } +} diff --git a/src/vs/workbench/services/timer/electron-browser/timerService.ts b/src/vs/workbench/services/timer/electron-browser/timerService.ts index 47fd9783039..c786aeae1c7 100644 --- a/src/vs/workbench/services/timer/electron-browser/timerService.ts +++ b/src/vs/workbench/services/timer/electron-browser/timerService.ts @@ -3,15 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { virtualMachineHint } from 'vs/base/node/id'; -import * as perf from 'vs/base/common/performance'; import * as os from 'os'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IUpdateService } from 'vs/platform/update/common/update'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -19,406 +16,65 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; +import { IStartupMetrics, AbstractTimerService, Writeable } from 'vs/workbench/services/timer/browser/timerService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -/* __GDPR__FRAGMENT__ - "IMemoryInfo" : { - "workingSetSize" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "privateBytes": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "sharedBytes": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } - } -*/ -export interface IMemoryInfo { - readonly workingSetSize: number; - readonly privateBytes: number; - readonly sharedBytes: number; -} - -/* __GDPR__FRAGMENT__ - "IStartupMetrics" : { - "version" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "ellapsed" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "isLatestVersion": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "didUseCachedData": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "windowKind": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "windowCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "viewletId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "panelId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "editorIds": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "timers.ellapsedAppReady" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedWindowLoad" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedWindowLoadToRequire" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedExtensions" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedExtensionsReady" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedRequire" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedWorkspaceStorageInit" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedWorkspaceServiceInit" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedViewletRestore" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedPanelRestore" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedEditorRestore" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedWorkbench" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedTimersToTimersComputed" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedNlsGeneration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "platform" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "release" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "arch" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "totalmem" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "freemem" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "meminfo" : { "${inline}": [ "${IMemoryInfo}" ] }, - "cpus.count" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "cpus.speed" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "cpus.model" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "initialStartup" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "hasAccessibilitySupport" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "isVMLikelyhood" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "emptyWorkbench" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "loadavg" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } - } -*/ -export interface IStartupMetrics { - - /** - * The version of these metrics. - */ - readonly version: 2; - - /** - * If this started the main process and renderer or just a renderer (new or reloaded). - */ - readonly initialStartup: boolean; - - /** - * No folder, no file, no workspace has been opened - */ - readonly emptyWorkbench: boolean; - - /** - * This is the latest (stable/insider) version. Iff not we should ignore this - * measurement. - */ - readonly isLatestVersion: boolean; - - /** - * Whether we asked for and V8 accepted cached data. - */ - readonly didUseCachedData: boolean; - - /** - * How/why the window was created. See https://github.com/Microsoft/vscode/blob/d1f57d871722f4d6ba63e4ef6f06287121ceb045/src/vs/platform/lifecycle/common/lifecycle.ts#L50 - */ - readonly windowKind: number; - - /** - * The total number of windows that have been restored/created - */ - readonly windowCount: number; - - /** - * The active viewlet id or `undedined` - */ - readonly viewletId?: string; - - /** - * The active panel id or `undefined` - */ - readonly panelId?: string; - - /** - * The editor input types or `[]` - */ - readonly editorIds: string[]; - - /** - * The time it took to create the workbench. - * - * * Happens in the main-process *and* the renderer-process - * * Measured with the *start* and `didStartWorkbench`-performance mark. The *start* is either the start of the - * main process or the start of the renderer. - * * This should be looked at carefully because times vary depending on - * * This being the first window, the only window, or a reloaded window - * * Cached data being present and used or not - * * The numbers and types of editors being restored - * * The numbers of windows being restored (when starting 'fresh') - * * The viewlet being restored (esp. when it's a contributed viewlet) - */ - readonly ellapsed: number; - - /** - * Individual timers... - */ - readonly timers: { - /** - * The time it took to receieve the [`ready`](https://electronjs.org/docs/api/app#event-ready)-event. Measured from the first line - * of JavaScript code till receiving that event. - * - * * Happens in the main-process - * * Measured with the `main:started` and `main:appReady` performance marks. - * * This can be compared between insider and stable builds. - * * This should be looked at per OS version and per electron version. - * * This is often affected by AV software (and can change with AV software updates outside of our release-cycle). - * * It is not our code running here and we can only observe what's happening. - */ - readonly ellapsedAppReady?: number; - - /** - * The time it took to generate NLS data. - * - * * Happens in the main-process - * * Measured with the `nlsGeneration:start` and `nlsGeneration:end` performance marks. - * * This only happens when a non-english locale is being used. - * * It is our code running here and we should monitor this carefully for regressions. - */ - readonly ellapsedNlsGeneration?: number; - - /** - * The time it took to tell electron to open/restore a renderer (browser window). - * - * * Happens in the main-process - * * Measured with the `main:appReady` and `main:loadWindow` performance marks. - * * This can be compared between insider and stable builds. - * * It is our code running here and we should monitor this carefully for regressions. - */ - readonly ellapsedWindowLoad?: number; - - /** - * The time it took to create a new renderer (browser window) and to initialize that to the point - * of load the main-bundle (`workbench.desktop.main.js`). - * - * * Happens in the main-process *and* the renderer-process - * * Measured with the `main:loadWindow` and `willLoadWorkbenchMain` performance marks. - * * This can be compared between insider and stable builds. - * * It is mostly not our code running here and we can only observe what's happening. - * - */ - readonly ellapsedWindowLoadToRequire: number; - - /** - * The time it took to require the workspace storage DB, connect to it - * and load the initial set of values. - * - * * Happens in the renderer-process - * * Measured with the `willInitWorkspaceStorage` and `didInitWorkspaceStorage` performance marks. - */ - readonly ellapsedWorkspaceStorageInit: number; - - /** - * The time it took to initialize the workspace and configuration service. - * - * * Happens in the renderer-process - * * Measured with the `willInitWorkspaceService` and `didInitWorkspaceService` performance marks. - */ - readonly ellapsedWorkspaceServiceInit: number; - - /** - * The time it took to load the main-bundle of the workbench, e.g. `workbench.desktop.main.js`. - * - * * Happens in the renderer-process - * * Measured with the `willLoadWorkbenchMain` and `didLoadWorkbenchMain` performance marks. - * * This varies *a lot* when V8 cached data could be used or not - * * This should be looked at with and without V8 cached data usage and per electron/v8 version - * * This is affected by the size of our code bundle (which grows about 3-5% per release) - */ - readonly ellapsedRequire: number; - - /** - * The time it took to read extensions' package.json-files *and* interpret them (invoking - * the contribution points). - * - * * Happens in the renderer-process - * * Measured with the `willLoadExtensions` and `didLoadExtensions` performance marks. - * * Reading of package.json-files is avoided by caching them all in a single file (after the read, - * until another extension is installed) - * * Happens in parallel to other things, depends on async timing - */ - readonly ellapsedExtensions: number; - - // the time from start till `didLoadExtensions` - // remove? - readonly ellapsedExtensionsReady: number; - - /** - * The time it took to restore the viewlet. - * - * * Happens in the renderer-process - * * Measured with the `willRestoreViewlet` and `didRestoreViewlet` performance marks. - * * This should be looked at per viewlet-type/id. - * * Happens in parallel to other things, depends on async timing - */ - readonly ellapsedViewletRestore: number; - - /** - * The time it took to restore the panel. - * - * * Happens in the renderer-process - * * Measured with the `willRestorePanel` and `didRestorePanel` performance marks. - * * This should be looked at per panel-type/id. - * * Happens in parallel to other things, depends on async timing - */ - readonly ellapsedPanelRestore: number; - - /** - * The time it took to restore editors - that is text editor and complex editor likes the settings UI - * or webviews (markdown preview). - * - * * Happens in the renderer-process - * * Measured with the `willRestoreEditors` and `didRestoreEditors` performance marks. - * * This should be looked at per editor and per editor type. - * * Happens in parallel to other things, depends on async timing - */ - readonly ellapsedEditorRestore: number; - - /** - * The time it took to create the workbench. - * - * * Happens in the renderer-process - * * Measured with the `willStartWorkbench` and `didStartWorkbench` performance marks. - */ - readonly ellapsedWorkbench: number; - - // the time it took to generate this object. - // remove? - readonly ellapsedTimersToTimersComputed: number; - }; - - readonly hasAccessibilitySupport: boolean; - readonly isVMLikelyhood?: number; - readonly platform?: string; - readonly release?: string; - readonly arch?: string; - readonly totalmem?: number; - readonly freemem?: number; - readonly meminfo?: IMemoryInfo; - readonly cpus?: { count: number; speed: number; model: string; }; - readonly loadavg?: number[]; -} - -export interface ITimerService { - _serviceBrand: undefined; - readonly startupMetrics: Promise; -} - -class TimerService implements ITimerService { - - _serviceBrand: undefined; - - private _startupMetrics?: Promise; +export class TimerService extends AbstractTimerService { constructor( @IElectronService private readonly _electronService: IElectronService, @IWorkbenchEnvironmentService private readonly _environmentService: INativeWorkbenchEnvironmentService, - @ILifecycleService private readonly _lifecycleService: ILifecycleService, - @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, - @IExtensionService private readonly _extensionService: IExtensionService, - @IUpdateService private readonly _updateService: IUpdateService, - @IViewletService private readonly _viewletService: IViewletService, - @IPanelService private readonly _panelService: IPanelService, - @IEditorService private readonly _editorService: IEditorService, - @IAccessibilityService private readonly _accessibilityService: IAccessibilityService - ) { } - - get startupMetrics(): Promise { - if (!this._startupMetrics) { - this._startupMetrics = Promise - .resolve(this._extensionService.whenInstalledExtensionsRegistered()) - .then(() => this._computeStartupMetrics()); - } - return this._startupMetrics; + @ILifecycleService lifecycleService: ILifecycleService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IExtensionService extensionService: IExtensionService, + @IUpdateService updateService: IUpdateService, + @IViewletService viewletService: IViewletService, + @IPanelService panelService: IPanelService, + @IEditorService editorService: IEditorService, + @IAccessibilityService accessibilityService: IAccessibilityService, + @ITelemetryService telemetryService: ITelemetryService, + ) { + super(lifecycleService, contextService, extensionService, updateService, viewletService, panelService, editorService, accessibilityService, telemetryService); } - private async _computeStartupMetrics(): Promise { - - const now = Date.now(); - const initialStartup = !!this._environmentService.configuration.isInitialStartup; - const startMark = initialStartup ? 'main:started' : 'main:loadWindow'; - - let totalmem: number | undefined; - let freemem: number | undefined; - let cpus: { count: number; speed: number; model: string; } | undefined; - let platform: string | undefined; - let release: string | undefined; - let arch: string | undefined; - let loadavg: number[] | undefined; - let meminfo: IMemoryInfo | undefined; - let isVMLikelyhood: number | undefined; + protected _isInitialStartup(): boolean { + return Boolean(this._environmentService.configuration.isInitialStartup); + } + protected _didUseCachedData(): boolean { + return didUseCachedData(); + } + protected _getWindowCount(): Promise { + return this._electronService.getWindowCount(); + } + protected async _extendStartupInfo(info: Writeable): Promise { try { - totalmem = os.totalmem(); - freemem = os.freemem(); - platform = os.platform(); - release = os.release(); - arch = os.arch(); - loadavg = os.loadavg(); + info.totalmem = os.totalmem(); + info.freemem = os.freemem(); + info.platform = os.platform(); + info.release = os.release(); + info.arch = os.arch(); + info.loadavg = os.loadavg(); const processMemoryInfo = await process.getProcessMemoryInfo(); - meminfo = { + info.meminfo = { workingSetSize: processMemoryInfo.residentSet, privateBytes: processMemoryInfo.private, sharedBytes: processMemoryInfo.shared }; - isVMLikelyhood = Math.round((virtualMachineHint.value() * 100)); + info.isVMLikelyhood = Math.round((virtualMachineHint.value() * 100)); const rawCpus = os.cpus(); if (rawCpus && rawCpus.length > 0) { - cpus = { count: rawCpus.length, speed: rawCpus[0].speed, model: rawCpus[0].model }; + info.cpus = { count: rawCpus.length, speed: rawCpus[0].speed, model: rawCpus[0].model }; } } catch (error) { // ignore, be on the safe side with these hardware method calls } - - const activeViewlet = this._viewletService.getActiveViewlet(); - const activePanel = this._panelService.getActivePanel(); - return { - version: 2, - ellapsed: perf.getDuration(startMark, 'didStartWorkbench'), - - // reflections - isLatestVersion: Boolean(await this._updateService.isLatestVersion()), - didUseCachedData: didUseCachedData(), - windowKind: this._lifecycleService.startupKind, - windowCount: await this._electronService.getWindowCount(), - viewletId: activeViewlet ? activeViewlet.getId() : undefined, - editorIds: this._editorService.visibleEditors.map(input => input.getTypeId()), - panelId: activePanel ? activePanel.getId() : undefined, - - // timers - timers: { - ellapsedAppReady: initialStartup ? perf.getDuration('main:started', 'main:appReady') : undefined, - ellapsedNlsGeneration: initialStartup ? perf.getDuration('nlsGeneration:start', 'nlsGeneration:end') : undefined, - ellapsedWindowLoad: initialStartup ? perf.getDuration('main:appReady', 'main:loadWindow') : undefined, - ellapsedWindowLoadToRequire: perf.getDuration('main:loadWindow', 'willLoadWorkbenchMain'), - ellapsedRequire: perf.getDuration('willLoadWorkbenchMain', 'didLoadWorkbenchMain'), - ellapsedWorkspaceStorageInit: perf.getDuration('willInitWorkspaceStorage', 'didInitWorkspaceStorage'), - ellapsedWorkspaceServiceInit: perf.getDuration('willInitWorkspaceService', 'didInitWorkspaceService'), - ellapsedExtensions: perf.getDuration('willLoadExtensions', 'didLoadExtensions'), - ellapsedEditorRestore: perf.getDuration('willRestoreEditors', 'didRestoreEditors'), - ellapsedViewletRestore: perf.getDuration('willRestoreViewlet', 'didRestoreViewlet'), - ellapsedPanelRestore: perf.getDuration('willRestorePanel', 'didRestorePanel'), - ellapsedWorkbench: perf.getDuration('willStartWorkbench', 'didStartWorkbench'), - ellapsedExtensionsReady: perf.getDuration(startMark, 'didLoadExtensions'), - ellapsedTimersToTimersComputed: Date.now() - now, - }, - - // system info - platform, - release, - arch, - totalmem, - freemem, - meminfo, - cpus, - loadavg, - initialStartup, - isVMLikelyhood, - hasAccessibilitySupport: this._accessibilityService.isScreenReaderOptimized(), - emptyWorkbench: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY - }; } } -export const ITimerService = createDecorator('timerService'); - -registerSingleton(ITimerService, TimerService, true); - //#region cached data logic export function didUseCachedData(): boolean { diff --git a/src/vs/workbench/services/title/common/titleService.ts b/src/vs/workbench/services/title/common/titleService.ts index bb59fe05651..74ce02b4566 100644 --- a/src/vs/workbench/services/title/common/titleService.ts +++ b/src/vs/workbench/services/title/common/titleService.ts @@ -16,7 +16,7 @@ export interface ITitleProperties { export interface ITitleService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; /** * An event when the menubar visibility changes. diff --git a/src/vs/workbench/services/title/electron-sandbox/titleService.ts b/src/vs/workbench/services/title/electron-sandbox/titleService.ts new file mode 100644 index 00000000000..cace9e5cf37 --- /dev/null +++ b/src/vs/workbench/services/title/electron-sandbox/titleService.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. + *--------------------------------------------------------------------------------------------*/ + +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { TitlebarPart } from 'vs/workbench/electron-sandbox/parts/titlebar/titlebarPart'; +import { ITitleService } from 'vs/workbench/services/title/common/titleService'; + +registerSingleton(ITitleService, TitlebarPart); diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts index 5ed6fa76eb8..0e61e602a2a 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IEncodingSupport, EncodingMode, Verbosity, IModeSupport, TextResourceEditorInput } from 'vs/workbench/common/editor'; +import { IEncodingSupport, EncodingMode, Verbosity, IModeSupport } from 'vs/workbench/common/editor'; +import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -12,11 +13,12 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IFileService } from 'vs/platform/files/common/files'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { extUri } from 'vs/base/common/resources'; /** * An editor input to be used for untitled text buffers. */ -export class UntitledTextEditorInput extends TextResourceEditorInput implements IEncodingSupport, IModeSupport { +export class UntitledTextEditorInput extends AbstractTextResourceEditorInput implements IEncodingSupport, IModeSupport { static readonly ID: string = 'workbench.editors.untitledEditorInput'; @@ -31,7 +33,7 @@ export class UntitledTextEditorInput extends TextResourceEditorInput implements @IFileService fileService: IFileService, @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService ) { - super(model.resource, editorService, editorGroupService, textFileService, labelService, fileService, filesConfigurationService); + super(model.resource, undefined, editorService, editorGroupService, textFileService, labelService, fileService, filesConfigurationService); this.registerModelListeners(model); } @@ -121,13 +123,12 @@ export class UntitledTextEditorInput extends TextResourceEditorInput implements } matches(otherInput: unknown): boolean { - if (super.matches(otherInput) === true) { + if (otherInput === this) { return true; } - // Otherwise compare by properties if (otherInput instanceof UntitledTextEditorInput) { - return otherInput.resource.toString() === this.resource.toString(); + return extUri.isEqual(otherInput.resource, this.resource); } return false; diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts index bd4f8afbc99..b5ea40db3c9 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts @@ -40,12 +40,12 @@ export interface IUntitledTextEditorModel extends ITextEditorModel, IModeSupport readonly onDidRevert: Event; /** - * Wether this untitled text model has an associated file path. + * Whether this untitled text model has an associated file path. */ readonly hasAssociatedFilePath: boolean; /** - * Wether this model has an explicit language mode or not. + * Whether this model has an explicit language mode or not. */ readonly hasModeSetExplicitly: boolean; diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts index 5c91d4512b6..8d8fa3ad013 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorService.ts @@ -110,12 +110,12 @@ export interface IUntitledTextEditorModelManager { export interface IUntitledTextEditorService extends IUntitledTextEditorModelManager { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; } export class UntitledTextEditorService extends Disposable implements IUntitledTextEditorService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _onDidChangeDirty = this._register(new Emitter()); readonly onDidChangeDirty = this._onDidChangeDirty.event; diff --git a/src/vs/workbench/services/update/browser/updateService.ts b/src/vs/workbench/services/update/browser/updateService.ts index 155fb7fc720..83064efac07 100644 --- a/src/vs/workbench/services/update/browser/updateService.ts +++ b/src/vs/workbench/services/update/browser/updateService.ts @@ -26,7 +26,7 @@ export interface IUpdateProvider { export class BrowserUpdateService extends Disposable implements IUpdateService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _onStateChange = this._register(new Emitter()); readonly onStateChange: Event = this._onStateChange.event; diff --git a/src/vs/workbench/services/update/electron-browser/updateService.ts b/src/vs/workbench/services/update/electron-sandbox/updateService.ts similarity index 93% rename from src/vs/workbench/services/update/electron-browser/updateService.ts rename to src/vs/workbench/services/update/electron-sandbox/updateService.ts index b8f6558b2ca..b3b4d686ad9 100644 --- a/src/vs/workbench/services/update/electron-browser/updateService.ts +++ b/src/vs/workbench/services/update/electron-sandbox/updateService.ts @@ -6,12 +6,12 @@ import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event, Emitter } from 'vs/base/common/event'; import { IUpdateService, State } from 'vs/platform/update/common/update'; -import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class NativeUpdateService implements IUpdateService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _onStateChange = new Emitter(); readonly onStateChange: Event = this._onStateChange.event; diff --git a/src/vs/workbench/services/uriIdentity/common/uriIdentity.ts b/src/vs/workbench/services/uriIdentity/common/uriIdentity.ts new file mode 100644 index 00000000000..f01d6ccf0f4 --- /dev/null +++ b/src/vs/workbench/services/uriIdentity/common/uriIdentity.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IExtUri } from 'vs/base/common/resources'; + + +export const IUriIdentityService = createDecorator('IUriIdentityService'); + +export interface IUriIdentityService { + + readonly _serviceBrand: undefined; + + /** + * Uri extensions that are aware of casing. + */ + readonly extUri: IExtUri; + + /** + * Returns a canonical uri for the given resource. Different uris can point to the same + * resource. That's because of casing or missing normalization, e.g the following uris + * are different but refer to the same document (because windows paths are not case-sensitive) + * + * ```txt + * file:///c:/foo/bar.txt + * file:///c:/FOO/BAR.txt + * ``` + * + * This function should be invoked when feeding uris into the system that represent the truth, + * e.g document uris or marker-to-document associations etc. This function should NOT be called + * to pretty print a label nor to sanitize a uri. + * + * Samples: + * + * | in | out | | + * |---|---|---| + * | `file:///foo/bar/../bar` | `file:///foo/bar` | n/a | + * | `file:///foo/bar/../bar#frag` | `file:///foo/bar#frag` | keep fragment | + * | `file:///foo/BAR` | `file:///foo/bar` | assume ignore case | + * | `file:///foo/bar/../BAR?q=2` | `file:///foo/BAR?q=2` | query makes it a different document | + */ + asCanonicalUri(uri: URI): URI; +} diff --git a/src/vs/workbench/services/uriIdentity/common/uriIdentityService.ts b/src/vs/workbench/services/uriIdentity/common/uriIdentityService.ts new file mode 100644 index 00000000000..1b2fc624c9c --- /dev/null +++ b/src/vs/workbench/services/uriIdentity/common/uriIdentityService.ts @@ -0,0 +1,117 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { URI } from 'vs/base/common/uri'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IFileService, FileSystemProviderCapabilities, IFileSystemProviderCapabilitiesChangeEvent, IFileSystemProviderRegistrationEvent } from 'vs/platform/files/common/files'; +import { ExtUri, IExtUri, normalizePath } from 'vs/base/common/resources'; +import { SkipList } from 'vs/base/common/skipList'; +import { Event } from 'vs/base/common/event'; +import { DisposableStore } from 'vs/base/common/lifecycle'; + +class Entry { + static _clock = 0; + time: number = Entry._clock++; + constructor(readonly uri: URI) { } + touch() { + this.time = Entry._clock++; + return this; + } +} + +export class UriIdentityService implements IUriIdentityService { + + declare readonly _serviceBrand: undefined; + + readonly extUri: IExtUri; + + private readonly _dispooables = new DisposableStore(); + private readonly _canonicalUris: SkipList; + private readonly _limit = 2 ** 16; + + constructor(@IFileService private readonly _fileService: IFileService) { + + const schemeIgnoresPathCasingCache = new Map(); + + // assume path casing matters unless the file system provider spec'ed the opposite. + // for all other cases path casing matters, e.g for + // * virtual documents + // * in-memory uris + // * all kind of "private" schemes + const ignorePathCasing = (uri: URI): boolean => { + let ignorePathCasing = schemeIgnoresPathCasingCache.get(uri.scheme); + if (ignorePathCasing === undefined) { + // retrieve once and then case per scheme until a change happens + ignorePathCasing = _fileService.canHandleResource(uri) && !this._fileService.hasCapability(uri, FileSystemProviderCapabilities.PathCaseSensitive); + schemeIgnoresPathCasingCache.set(uri.scheme, ignorePathCasing); + } + return ignorePathCasing; + }; + this._dispooables.add(Event.any( + _fileService.onDidChangeFileSystemProviderRegistrations, + _fileService.onDidChangeFileSystemProviderCapabilities + )(e => { + // remove from cache + schemeIgnoresPathCasingCache.delete(e.scheme); + })); + + this.extUri = new ExtUri(ignorePathCasing); + this._canonicalUris = new SkipList((a, b) => this.extUri.compare(a, b, true), this._limit); + } + + dispose(): void { + this._dispooables.dispose(); + this._canonicalUris.clear(); + } + + asCanonicalUri(uri: URI): URI { + + // (1) normalize URI + if (this._fileService.canHandleResource(uri)) { + uri = normalizePath(uri); + } + + // (2) find the uri in its canonical form or use this uri to define it + let item = this._canonicalUris.get(uri); + if (item) { + return item.touch().uri.with({ fragment: uri.fragment }); + } + + // this uri is first and defines the canonical form + this._canonicalUris.set(uri, new Entry(uri)); + this._checkTrim(); + + return uri; + } + + private _checkTrim(): void { + if (this._canonicalUris.size < this._limit) { + return; + } + + // get all entries, sort by touch (MRU) and re-initalize + // the uri cache and the entry clock. this is an expensive + // operation and should happen rarely + const entries = [...this._canonicalUris.entries()].sort((a, b) => { + if (a[1].touch < b[1].touch) { + return 1; + } else if (a[1].touch > b[1].touch) { + return -1; + } else { + return 0; + } + }); + + Entry._clock = 0; + this._canonicalUris.clear(); + const newSize = this._limit * 0.5; + for (let i = 0; i < newSize; i++) { + this._canonicalUris.set(entries[i][0], entries[i][1].touch()); + } + } +} + +registerSingleton(IUriIdentityService, UriIdentityService, true); diff --git a/src/vs/workbench/services/uriIdentity/test/common/uriIdentityService.test.ts b/src/vs/workbench/services/uriIdentity/test/common/uriIdentityService.test.ts new file mode 100644 index 00000000000..683fb6675d7 --- /dev/null +++ b/src/vs/workbench/services/uriIdentity/test/common/uriIdentityService.test.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 assert from 'assert'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; +import { mock } from 'vs/workbench/test/common/workbenchTestServices'; +import { IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; +import { URI } from 'vs/base/common/uri'; +import { Event } from 'vs/base/common/event'; + +suite('URI Identity', function () { + + class FakeFileService extends mock() { + + onDidChangeFileSystemProviderCapabilities = Event.None; + onDidChangeFileSystemProviderRegistrations = Event.None; + + constructor(readonly data: Map) { + super(); + } + canHandleResource(uri: URI) { + return this.data.has(uri.scheme); + } + hasCapability(uri: URI, flag: FileSystemProviderCapabilities): boolean { + const mask = this.data.get(uri.scheme) ?? 0; + return Boolean(mask & flag); + } + } + + let _service: UriIdentityService; + + setup(function () { + _service = new UriIdentityService(new FakeFileService(new Map([ + ['bar', FileSystemProviderCapabilities.PathCaseSensitive], + ['foo', 0] + ]))); + }); + + function assertCanonical(input: URI, expected: URI, service: UriIdentityService = _service) { + const actual = service.asCanonicalUri(input); + assert.equal(actual.toString(), expected.toString()); + assert.ok(service.extUri.isEqual(actual, expected)); + } + + test('extUri (isEqual)', function () { + let a = URI.parse('foo://bar/bang'); + let a1 = URI.parse('foo://bar/BANG'); + let b = URI.parse('bar://bar/bang'); + let b1 = URI.parse('bar://bar/BANG'); + + assert.equal(_service.extUri.isEqual(a, a1), true); + assert.equal(_service.extUri.isEqual(a1, a), true); + + assert.equal(_service.extUri.isEqual(b, b1), false); + assert.equal(_service.extUri.isEqual(b1, b), false); + }); + + test('asCanonicalUri (casing)', function () { + + let a = URI.parse('foo://bar/bang'); + let a1 = URI.parse('foo://bar/BANG'); + let b = URI.parse('bar://bar/bang'); + let b1 = URI.parse('bar://bar/BANG'); + + assertCanonical(a, a); + assertCanonical(a1, a); + + assertCanonical(b, b); + assertCanonical(b1, b1); // case sensitive + }); + + test('asCanonicalUri (normalization)', function () { + let a = URI.parse('foo://bar/bang'); + assertCanonical(a, a); + assertCanonical(URI.parse('foo://bar/./bang'), a); + assertCanonical(URI.parse('foo://bar/./bang'), a); + assertCanonical(URI.parse('foo://bar/./foo/../bang'), a); + }); + + test('asCanonicalUri (keep fragement)', function () { + + let a = URI.parse('foo://bar/bang'); + + assertCanonical(a, a); + assertCanonical(URI.parse('foo://bar/./bang#frag'), a.with({ fragment: 'frag' })); + assertCanonical(URI.parse('foo://bar/./bang#frag'), a.with({ fragment: 'frag' })); + assertCanonical(URI.parse('foo://bar/./bang#frag'), a.with({ fragment: 'frag' })); + assertCanonical(URI.parse('foo://bar/./foo/../bang#frag'), a.with({ fragment: 'frag' })); + + let b = URI.parse('foo://bar/bazz#frag'); + assertCanonical(b, b); + assertCanonical(URI.parse('foo://bar/bazz'), b.with({ fragment: '' })); + assertCanonical(URI.parse('foo://bar/BAZZ#DDD'), b.with({ fragment: 'DDD' })); // lower-case path, but fragment is kept + }); + +}); diff --git a/src/vs/workbench/services/url/browser/urlService.ts b/src/vs/workbench/services/url/browser/urlService.ts index eef5070c450..120ad6973ea 100644 --- a/src/vs/workbench/services/url/browser/urlService.ts +++ b/src/vs/workbench/services/url/browser/urlService.ts @@ -38,7 +38,7 @@ export interface IURLCallbackProvider { export class BrowserURLService extends AbstractURLService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private provider: IURLCallbackProvider | undefined; diff --git a/src/vs/workbench/services/url/electron-browser/urlService.ts b/src/vs/workbench/services/url/electron-sandbox/urlService.ts similarity index 64% rename from src/vs/workbench/services/url/electron-browser/urlService.ts rename to src/vs/workbench/services/url/electron-sandbox/urlService.ts index c5e9d85e467..cdb0cce5e88 100644 --- a/src/vs/workbench/services/url/electron-browser/urlService.ts +++ b/src/vs/workbench/services/url/electron-sandbox/urlService.ts @@ -5,31 +5,29 @@ import { IURLService, IURLHandler, IOpenURLOptions } from 'vs/platform/url/common/url'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; import { URLHandlerChannel } from 'vs/platform/url/common/urlIpc'; -import { URLService } from 'vs/platform/url/node/urlService'; import { IOpenerService, IOpener, matchesScheme } from 'vs/platform/opener/common/opener'; -import product from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; -import { IElectronService } from 'vs/platform/electron/node/electron'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; +import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; +import { NativeURLService } from 'vs/platform/url/common/urlService'; export interface IRelayOpenURLOptions extends IOpenURLOptions { openToSide?: boolean; openExternal?: boolean; } -export class RelayURLService extends URLService implements IURLHandler, IOpener { +export class RelayURLService extends NativeURLService implements IURLHandler, IOpener { private urlService: IURLService; constructor( @IMainProcessService mainProcessService: IMainProcessService, @IOpenerService openerService: IOpenerService, - @IWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService, - @IElectronService private electronService: IElectronService + @IElectronService private readonly electronService: IElectronService, + @IProductService private readonly productService: IProductService ) { super(); @@ -44,9 +42,9 @@ export class RelayURLService extends URLService implements IURLHandler, IOpener let query = uri.query; if (!query) { - query = `windowId=${encodeURIComponent(this.environmentService.configuration.windowId)}`; + query = `windowId=${encodeURIComponent(this.electronService.windowId)}`; } else { - query += `&windowId=${encodeURIComponent(this.environmentService.configuration.windowId)}`; + query += `&windowId=${encodeURIComponent(this.electronService.windowId)}`; } return uri.with({ query }); @@ -54,7 +52,7 @@ export class RelayURLService extends URLService implements IURLHandler, IOpener async open(resource: URI | string, options?: IRelayOpenURLOptions): Promise { - if (!matchesScheme(resource, product.urlProtocol)) { + if (!matchesScheme(resource, this.productService.urlProtocol)) { return false; } diff --git a/src/vs/workbench/services/userData/common/fileUserDataProvider.ts b/src/vs/workbench/services/userData/common/fileUserDataProvider.ts index 9fec4936b0b..0e1a753a60f 100644 --- a/src/vs/workbench/services/userData/common/fileUserDataProvider.ts +++ b/src/vs/workbench/services/userData/common/fileUserDataProvider.ts @@ -7,13 +7,12 @@ import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IFileSystemProviderWithFileReadWriteCapability, IFileChange, IWatchOptions, IStat, FileOverwriteOptions, FileType, FileWriteOptions, FileDeleteOptions, FileSystemProviderCapabilities, IFileSystemProviderWithOpenReadWriteCloseCapability, FileOpenOptions, hasReadWriteCapability, hasOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadStreamCapability, FileReadStreamOptions, hasFileReadStreamCapability } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; -import * as resources from 'vs/base/common/resources'; -import { startsWith } from 'vs/base/common/strings'; import { BACKUPS } from 'vs/platform/environment/common/environment'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ReadableStreamEvents } from 'vs/base/common/stream'; import { ILogService } from 'vs/platform/log/common/log'; +import { ExtUri, extUri, extUriIgnorePathCase } from 'vs/base/common/resources'; export class FileUserDataProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability, @@ -21,12 +20,13 @@ export class FileUserDataProvider extends Disposable implements IFileSystemProviderWithFileReadStreamCapability { readonly capabilities: FileSystemProviderCapabilities = this.fileSystemProvider.capabilities; - readonly onDidChangeCapabilities: Event = Event.None; + readonly onDidChangeCapabilities: Event = this.fileSystemProvider.onDidChangeCapabilities; private readonly _onDidChangeFile = this._register(new Emitter()); readonly onDidChangeFile: Event = this._onDidChangeFile.event; private readonly userDataHome: URI; + private extUri: ExtUri; constructor( private readonly fileSystemUserDataHome: URI, @@ -39,6 +39,10 @@ export class FileUserDataProvider extends Disposable implements this.userDataHome = environmentService.userRoamingDataHome; + this.extUri = !!(this.capabilities & FileSystemProviderCapabilities.PathCaseSensitive) ? extUri : extUriIgnorePathCase; + // update extUri as capabilites might change. + this._register(this.onDidChangeCapabilities(() => this.extUri = !!(this.capabilities & FileSystemProviderCapabilities.PathCaseSensitive) ? extUri : extUriIgnorePathCase)); + // Assumption: This path always exists this._register(this.fileSystemProvider.watch(this.fileSystemUserDataHome, { recursive: false, excludes: [] })); this._register(this.fileSystemProvider.onDidChangeFile(e => this.handleFileChanges(e))); @@ -67,7 +71,7 @@ export class FileUserDataProvider extends Disposable implements throw new Error('not supported'); } - readFileStream(resource: URI, opts: FileReadStreamOptions, token?: CancellationToken): ReadableStreamEvents { + readFileStream(resource: URI, opts: FileReadStreamOptions, token: CancellationToken): ReadableStreamEvents { if (hasFileReadStreamCapability(this.fileSystemProvider)) { return this.fileSystemProvider.readFileStream(this.toFileSystemResource(resource), opts, token); } @@ -135,21 +139,21 @@ export class FileUserDataProvider extends Disposable implements } private toFileSystemResource(userDataResource: URI): URI { - const relativePath = resources.relativePath(this.userDataHome, userDataResource)!; - if (startsWith(relativePath, BACKUPS)) { - return resources.joinPath(resources.dirname(this.fileSystemBackupsHome), relativePath); + const relativePath = this.extUri.relativePath(this.userDataHome, userDataResource)!; + if (relativePath.startsWith(BACKUPS)) { + return this.extUri.joinPath(this.extUri.dirname(this.fileSystemBackupsHome), relativePath); } - return resources.joinPath(this.fileSystemUserDataHome, relativePath); + return this.extUri.joinPath(this.fileSystemUserDataHome, relativePath); } private toUserDataResource(fileSystemResource: URI): URI | null { - if (resources.isEqualOrParent(fileSystemResource, this.fileSystemUserDataHome)) { - const relativePath = resources.relativePath(this.fileSystemUserDataHome, fileSystemResource); - return relativePath ? resources.joinPath(this.userDataHome, relativePath) : this.userDataHome; + if (this.extUri.isEqualOrParent(fileSystemResource, this.fileSystemUserDataHome)) { + const relativePath = this.extUri.relativePath(this.fileSystemUserDataHome, fileSystemResource); + return relativePath ? this.extUri.joinPath(this.userDataHome, relativePath) : this.userDataHome; } - if (resources.isEqualOrParent(fileSystemResource, this.fileSystemBackupsHome)) { - const relativePath = resources.relativePath(this.fileSystemBackupsHome, fileSystemResource); - return relativePath ? resources.joinPath(this.userDataHome, BACKUPS, relativePath) : resources.joinPath(this.userDataHome, BACKUPS); + if (this.extUri.isEqualOrParent(fileSystemResource, this.fileSystemBackupsHome)) { + const relativePath = this.extUri.relativePath(this.fileSystemBackupsHome, fileSystemResource); + return relativePath ? this.extUri.joinPath(this.userDataHome, BACKUPS, relativePath) : this.extUri.joinPath(this.userDataHome, BACKUPS); } return null; } diff --git a/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts b/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts index f511d55fe1f..7f01f78c3eb 100644 --- a/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts +++ b/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts @@ -16,7 +16,7 @@ import { URI } from 'vs/base/common/uri'; import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; import { joinPath, dirname } from 'vs/base/common/resources'; import { VSBuffer } from 'vs/base/common/buffer'; -import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider'; +import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { BACKUPS } from 'vs/platform/environment/common/environment'; import { DisposableStore, IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts new file mode 100644 index 00000000000..b6dabba27d2 --- /dev/null +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -0,0 +1,742 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IUserDataSyncService, IAuthenticationProvider, isAuthenticationProvider, IUserDataAutoSyncService, SyncResource, IResourcePreview, ISyncResourcePreview, Change, IManualSyncTask, IUserDataSyncStoreManagementService, UserDataSyncStoreType } from 'vs/platform/userDataSync/common/userDataSync'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IUserDataSyncWorkbenchService, IUserDataSyncAccount, AccountStatus, CONTEXT_SYNC_ENABLEMENT, CONTEXT_SYNC_STATE, CONTEXT_ACCOUNT_STATE, SHOW_SYNC_LOG_COMMAND_ID, getSyncAreaLabel, IUserDataSyncPreview, IUserDataSyncResource, CONTEXT_ENABLE_SYNC_MERGES_VIEW, SYNC_MERGES_VIEW_ID, CONTEXT_ENABLE_ACTIVITY_VIEWS, SYNC_VIEW_CONTAINER_ID, SYNC_TITLE } from 'vs/workbench/services/userDataSync/common/userDataSync'; +import { AuthenticationSession, AuthenticationSessionsChangeEvent } from 'vs/editor/common/modes'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { Emitter, Event } from 'vs/base/common/event'; +import { flatten, equals } from 'vs/base/common/arrays'; +import { getAuthenticationProviderActivationEvent, IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; +import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; +import { IQuickInputService, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; +import { IStorageService, IWorkspaceStorageChangeEvent, StorageScope } from 'vs/platform/storage/common/storage'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { localize } from 'vs/nls'; +import { canceled } from 'vs/base/common/errors'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { Action } from 'vs/base/common/actions'; +import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; +import { isEqual } from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; +import { IViewsService, ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views'; +import { isNative } from 'vs/base/common/platform'; +import { IHostService } from 'vs/workbench/services/host/browser/host'; + +type UserAccountClassification = { + id: { classification: 'EndUserPseudonymizedInformation', purpose: 'BusinessInsight' }; +}; + +type FirstTimeSyncClassification = { + action: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; +}; + +type UserAccountEvent = { + id: string; +}; + +type FirstTimeSyncAction = 'pull' | 'push' | 'merge' | 'manual'; + +type AccountQuickPickItem = { label: string, authenticationProvider: IAuthenticationProvider, account?: UserDataSyncAccount, description?: string }; + +class UserDataSyncAccount implements IUserDataSyncAccount { + + constructor(readonly authenticationProviderId: string, private readonly session: AuthenticationSession) { } + + get sessionId(): string { return this.session.id; } + get accountName(): string { return this.session.account.label; } + get accountId(): string { return this.session.account.id; } + get token(): string { return this.session.accessToken; } +} + +export class UserDataSyncWorkbenchService extends Disposable implements IUserDataSyncWorkbenchService { + + _serviceBrand: any; + + private static DONOT_USE_WORKBENCH_SESSION_STORAGE_KEY = 'userDataSyncAccount.donotUseWorkbenchSession'; + private static CACHED_SESSION_STORAGE_KEY = 'userDataSyncAccountPreference'; + + private _authenticationProviders: IAuthenticationProvider[] = []; + get enabled() { return this._authenticationProviders.length > 0; } + + private availableAuthenticationProviders: IAuthenticationProvider[] = []; + get authenticationProviders() { return this.availableAuthenticationProviders; } + + private _accountStatus: AccountStatus = AccountStatus.Uninitialized; + get accountStatus(): AccountStatus { return this._accountStatus; } + private readonly _onDidChangeAccountStatus = this._register(new Emitter()); + readonly onDidChangeAccountStatus = this._onDidChangeAccountStatus.event; + + private _all: Map = new Map(); + get all(): UserDataSyncAccount[] { return flatten([...this._all.values()]); } + + get current(): UserDataSyncAccount | undefined { return this.all.filter(account => this.isCurrentAccount(account))[0]; } + + private readonly syncEnablementContext: IContextKey; + private readonly syncStatusContext: IContextKey; + private readonly accountStatusContext: IContextKey; + private readonly mergesViewEnablementContext: IContextKey; + private readonly activityViewsEnablementContext: IContextKey; + + readonly userDataSyncPreview: UserDataSyncPreview = this._register(new UserDataSyncPreview(this.userDataSyncService)); + + constructor( + @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, + @IAuthenticationService private readonly authenticationService: IAuthenticationService, + @IUserDataSyncAccountService private readonly userDataSyncAccountService: IUserDataSyncAccountService, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @IStorageService private readonly storageService: IStorageService, + @IUserDataAutoSyncService private readonly userDataAutoSyncService: IUserDataAutoSyncService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @ILogService private readonly logService: ILogService, + @IProductService private readonly productService: IProductService, + @IConfigurationService configurationService: IConfigurationService, + @IExtensionService private readonly extensionService: IExtensionService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, + @INotificationService private readonly notificationService: INotificationService, + @IProgressService private readonly progressService: IProgressService, + @IDialogService private readonly dialogService: IDialogService, + @IContextKeyService contextKeyService: IContextKeyService, + @IViewsService private readonly viewsService: IViewsService, + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, + @IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, + @IHostService private readonly hostService: IHostService, + ) { + super(); + this._authenticationProviders = this.userDataSyncStoreManagementService.userDataSyncStore?.authenticationProviders || []; + this.syncEnablementContext = CONTEXT_SYNC_ENABLEMENT.bindTo(contextKeyService); + this.syncStatusContext = CONTEXT_SYNC_STATE.bindTo(contextKeyService); + this.accountStatusContext = CONTEXT_ACCOUNT_STATE.bindTo(contextKeyService); + this.activityViewsEnablementContext = CONTEXT_ENABLE_ACTIVITY_VIEWS.bindTo(contextKeyService); + this.mergesViewEnablementContext = CONTEXT_ENABLE_SYNC_MERGES_VIEW.bindTo(contextKeyService); + + if (this._authenticationProviders.length) { + this.syncStatusContext.set(this.userDataSyncService.status); + this._register(userDataSyncService.onDidChangeStatus(status => this.syncStatusContext.set(status))); + this.syncEnablementContext.set(userDataAutoSyncService.isEnabled()); + this._register(userDataAutoSyncService.onDidChangeEnablement(enabled => this.syncEnablementContext.set(enabled))); + + this.waitAndInitialize(); + } + } + + private isSupportedAuthenticationProviderId(authenticationProviderId: string): boolean { + return this._authenticationProviders.some(({ id }) => id === authenticationProviderId); + } + + private async waitAndInitialize(): Promise { + await this.extensionService.whenInstalledExtensionsRegistered(); + + /* activate unregistered providers */ + const unregisteredProviders = this._authenticationProviders.filter(({ id }) => !this.authenticationService.isAuthenticationProviderRegistered(id)); + if (unregisteredProviders.length) { + await Promise.all(unregisteredProviders.map(({ id }) => this.extensionService.activateByEvent(getAuthenticationProviderActivationEvent(id)))); + } + + /* wait until all providers are availabe */ + if (this._authenticationProviders.some(({ id }) => !this.authenticationService.isAuthenticationProviderRegistered(id))) { + await Event.toPromise(Event.filter(this.authenticationService.onDidRegisterAuthenticationProvider, () => this._authenticationProviders.every(({ id }) => this.authenticationService.isAuthenticationProviderRegistered(id)))); + } + + /* initialize */ + await this.initialize(); + } + + private async initialize(): Promise { + if (this.currentSessionId === undefined && this.useWorkbenchSessionId && this.environmentService.options?.authenticationSessionId) { + this.currentSessionId = this.environmentService.options.authenticationSessionId; + this.useWorkbenchSessionId = false; + } + + await this.update(); + + this._register( + Event.any( + Event.filter( + Event.any( + this.authenticationService.onDidRegisterAuthenticationProvider, + this.authenticationService.onDidUnregisterAuthenticationProvider, + ), info => this.isSupportedAuthenticationProviderId(info.id)), + Event.filter(this.userDataSyncAccountService.onTokenFailed, isSuccessive => !isSuccessive)) + (() => this.update())); + + this._register(Event.filter(this.authenticationService.onDidChangeSessions, e => this.isSupportedAuthenticationProviderId(e.providerId))(({ event }) => this.onDidChangeSessions(event))); + this._register(this.storageService.onDidChangeStorage(e => this.onDidChangeStorage(e))); + this._register(Event.filter(this.userDataSyncAccountService.onTokenFailed, isSuccessive => isSuccessive)(() => this.onDidSuccessiveAuthFailures())); + } + + private async update(): Promise { + + this.availableAuthenticationProviders = this._authenticationProviders.filter(({ id }) => this.authenticationService.isAuthenticationProviderRegistered(id)); + + const allAccounts: Map = new Map(); + for (const { id } of this.availableAuthenticationProviders) { + const accounts = await this.getAccounts(id); + allAccounts.set(id, accounts); + } + + this._all = allAccounts; + const current = this.current; + await this.updateToken(current); + this.updateAccountStatus(current ? AccountStatus.Available : AccountStatus.Unavailable); + } + + private async getAccounts(authenticationProviderId: string): Promise { + let accounts: Map = new Map(); + let currentAccount: UserDataSyncAccount | null = null; + + const sessions = await this.authenticationService.getSessions(authenticationProviderId) || []; + for (const session of sessions) { + const account: UserDataSyncAccount = new UserDataSyncAccount(authenticationProviderId, session); + accounts.set(account.accountName, account); + if (this.isCurrentAccount(account)) { + currentAccount = account; + } + } + + if (currentAccount) { + // Always use current account if available + accounts.set(currentAccount.accountName, currentAccount); + } + + return [...accounts.values()]; + } + + private async updateToken(current: UserDataSyncAccount | undefined): Promise { + let value: { token: string, authenticationProviderId: string } | undefined = undefined; + if (current) { + try { + this.logService.trace('Settings Sync: Updating the token for the account', current.accountName); + const token = current.token; + this.logService.trace('Settings Sync: Token updated for the account', current.accountName); + value = { token, authenticationProviderId: current.authenticationProviderId }; + } catch (e) { + this.logService.error(e); + } + } + await this.userDataSyncAccountService.updateAccount(value); + } + + private updateAccountStatus(accountStatus: AccountStatus): void { + if (this._accountStatus !== accountStatus) { + const previous = this._accountStatus; + this.logService.debug('Sync account status changed', previous, accountStatus); + + this._accountStatus = accountStatus; + this.accountStatusContext.set(accountStatus); + this._onDidChangeAccountStatus.fire(accountStatus); + } + } + + async turnOn(): Promise { + const picked = await this.pick(); + if (!picked) { + throw canceled(); + } + + // User did not pick an account or login failed + if (this.accountStatus !== AccountStatus.Available) { + throw new Error(localize('no account', "No account available")); + } + + const syncTitle = SYNC_TITLE; + const title = `${syncTitle} [(${localize('details', "details")})](command:${SHOW_SYNC_LOG_COMMAND_ID})`; + await this.syncBeforeTurningOn(title); + await this.userDataAutoSyncService.turnOn(); + this.notificationService.info(localize('sync turned on', "{0} is turned on", title)); + } + + turnoff(everywhere: boolean): Promise { + return this.userDataAutoSyncService.turnOff(everywhere); + } + + private async syncBeforeTurningOn(title: string): Promise { + + /* Make sure sync started on clean local state */ + await this.userDataSyncService.resetLocal(); + + const manualSyncTask = await this.userDataSyncService.createManualSyncTask(); + try { + let action: FirstTimeSyncAction = 'manual'; + let preview: [SyncResource, ISyncResourcePreview][] = []; + + await this.progressService.withProgress({ + location: ProgressLocation.Notification, + title, + delay: 500, + }, async progress => { + progress.report({ message: localize('turning on', "Turning on...") }); + + preview = await manualSyncTask.preview(); + const hasRemoteData = manualSyncTask.manifest !== null; + const hasLocalData = await this.userDataSyncService.hasLocalData(); + const hasMergesFromAnotherMachine = preview.some(([syncResource, { isLastSyncFromCurrentMachine, resourcePreviews }]) => + syncResource !== SyncResource.GlobalState && !isLastSyncFromCurrentMachine + && resourcePreviews.some(r => r.localChange !== Change.None || r.remoteChange !== Change.None)); + + action = await this.getFirstTimeSyncAction(hasRemoteData, hasLocalData, hasMergesFromAnotherMachine); + const progressDisposable = manualSyncTask.onSynchronizeResources(synchronizingResources => + synchronizingResources.length ? progress.report({ message: localize('syncing resource', "Syncing {0}...", getSyncAreaLabel(synchronizingResources[0][0])) }) : undefined); + try { + switch (action) { + case 'merge': return await manualSyncTask.apply(); + case 'pull': return await manualSyncTask.pull(); + case 'push': return await manualSyncTask.push(); + case 'manual': return; + } + } finally { + progressDisposable.dispose(); + } + }); + if (action === 'manual') { + await this.syncManually(manualSyncTask, preview); + } + } catch (error) { + await manualSyncTask.stop(); + throw error; + } finally { + manualSyncTask.dispose(); + } + } + + private async getFirstTimeSyncAction(hasRemoteData: boolean, hasLocalData: boolean, hasMergesFromAnotherMachine: boolean): Promise { + + if (!hasLocalData /* no data on local */ + || !hasRemoteData /* no data on remote */ + || !hasMergesFromAnotherMachine /* no merges with another machine */ + ) { + return 'merge'; + } + + const result = await this.dialogService.show( + Severity.Info, + localize('merge or replace', "Merge or Replace"), + [ + localize('merge', "Merge"), + localize('replace local', "Replace Local"), + localize('merge Manually', "Merge Manually..."), + localize('cancel', "Cancel"), + ], + { + cancelId: 3, + detail: localize('first time sync detail', "It looks like you last synced from another machine.\nWould you like to merge or replace with your data in the cloud?"), + } + ); + switch (result.choice) { + case 0: + this.telemetryService.publicLog2<{ action: string }, FirstTimeSyncClassification>('sync/firstTimeSync', { action: 'merge' }); + return 'merge'; + case 1: + this.telemetryService.publicLog2<{ action: string }, FirstTimeSyncClassification>('sync/firstTimeSync', { action: 'pull' }); + return 'pull'; + case 2: + this.telemetryService.publicLog2<{ action: string }, FirstTimeSyncClassification>('sync/firstTimeSync', { action: 'manual' }); + return 'manual'; + } + this.telemetryService.publicLog2<{ action: string }, FirstTimeSyncClassification>('sync/firstTimeSync', { action: 'cancelled' }); + throw canceled(); + } + + private async syncManually(task: IManualSyncTask, preview: [SyncResource, ISyncResourcePreview][]): Promise { + const visibleViewContainer = this.viewsService.getVisibleViewContainer(ViewContainerLocation.Sidebar); + this.userDataSyncPreview.setManualSyncPreview(task, preview); + + this.mergesViewEnablementContext.set(true); + await this.waitForActiveSyncViews(); + await this.viewsService.openView(SYNC_MERGES_VIEW_ID); + + const error = await Event.toPromise(this.userDataSyncPreview.onDidCompleteManualSync); + this.userDataSyncPreview.unsetManualSyncPreview(); + + this.mergesViewEnablementContext.set(false); + if (visibleViewContainer) { + this.viewsService.openViewContainer(visibleViewContainer.id); + } else { + const viewContainer = this.viewDescriptorService.getViewContainerByViewId(SYNC_MERGES_VIEW_ID); + this.viewsService.closeViewContainer(viewContainer!.id); + } + + if (error) { + throw error; + } + } + + async resetSyncedData(): Promise { + const result = await this.dialogService.confirm({ + message: localize('reset', "This will clear your data in the cloud and stop sync on all your devices."), + title: localize('reset title', "Clear"), + type: 'info', + primaryButton: localize('reset button', "Reset"), + }); + if (result.confirmed) { + await this.userDataSyncService.resetRemote(); + } + } + + async showSyncActivity(): Promise { + this.activityViewsEnablementContext.set(true); + await this.waitForActiveSyncViews(); + await this.viewsService.openViewContainer(SYNC_VIEW_CONTAINER_ID); + } + + async switchSyncService(type: UserDataSyncStoreType): Promise { + if (!this.userDataSyncStoreManagementService.userDataSyncStore + || !this.userDataSyncStoreManagementService.userDataSyncStore.insidersUrl + || !this.userDataSyncStoreManagementService.userDataSyncStore.stableUrl) { + return; + } + await this.userDataSyncStoreManagementService.switch(type); + const res = await this.dialogService.confirm({ + type: 'info', + message: isNative ? + localize('relaunchMessage', "Switching settings sync service requires a restart to take effect.") : + localize('relaunchMessageWeb', "Switching settings sync service requires a reload to take effect."), + detail: isNative ? + localize('relaunchDetail', "Press the restart button to restart {0} and switch.", this.productService.nameLong) : + localize('relaunchDetailWeb', "Press the reload button to reload {0} and switch.", this.productService.nameLong), + primaryButton: isNative ? + localize('restart', "&&Restart") : + localize('restartWeb', "&&Reload"), + }); + if (res.confirmed) { + this.hostService.restart(); + } + } + + private async waitForActiveSyncViews(): Promise { + const viewContainer = this.viewDescriptorService.getViewContainerById(SYNC_VIEW_CONTAINER_ID); + if (viewContainer) { + const model = this.viewDescriptorService.getViewContainerModel(viewContainer); + if (!model.activeViewDescriptors.length) { + await Event.toPromise(Event.filter(model.onDidChangeActiveViewDescriptors, e => model.activeViewDescriptors.length > 0)); + } + } + } + + private isCurrentAccount(account: UserDataSyncAccount): boolean { + return account.sessionId === this.currentSessionId; + } + + async signIn(): Promise { + await this.pick(); + } + + private async pick(): Promise { + const result = await this.doPick(); + if (!result) { + return false; + } + let sessionId: string, accountName: string, accountId: string; + if (isAuthenticationProvider(result)) { + const session = await this.authenticationService.login(result.id, result.scopes); + sessionId = session.id; + accountName = session.account.label; + accountId = session.account.id; + } else { + sessionId = result.sessionId; + accountName = result.accountName; + accountId = result.accountId; + } + await this.switch(sessionId, accountName, accountId); + return true; + } + + private async doPick(): Promise { + if (this.availableAuthenticationProviders.length === 0) { + return undefined; + } + + await this.update(); + + // Single auth provider and no accounts available + if (this.availableAuthenticationProviders.length === 1 && !this.all.length) { + return this.availableAuthenticationProviders[0]; + } + + return new Promise(async (c, e) => { + let result: UserDataSyncAccount | IAuthenticationProvider | undefined; + const disposables: DisposableStore = new DisposableStore(); + const quickPick = this.quickInputService.createQuickPick(); + disposables.add(quickPick); + + quickPick.title = SYNC_TITLE; + quickPick.ok = false; + quickPick.placeholder = localize('choose account placeholder', "Select an account"); + quickPick.ignoreFocusOut = true; + quickPick.items = this.createQuickpickItems(); + + disposables.add(quickPick.onDidAccept(() => { + result = quickPick.selectedItems[0]?.account ? quickPick.selectedItems[0]?.account : quickPick.selectedItems[0]?.authenticationProvider; + quickPick.hide(); + })); + disposables.add(quickPick.onDidHide(() => { + disposables.dispose(); + c(result); + })); + quickPick.show(); + }); + } + + private createQuickpickItems(): (AccountQuickPickItem | IQuickPickSeparator)[] { + const quickPickItems: (AccountQuickPickItem | IQuickPickSeparator)[] = []; + + // Signed in Accounts + if (this.all.length) { + const authenticationProviders = [...this.availableAuthenticationProviders].sort(({ id }) => id === this.current?.authenticationProviderId ? -1 : 1); + quickPickItems.push({ type: 'separator', label: localize('signed in', "Signed in") }); + for (const authenticationProvider of authenticationProviders) { + const accounts = (this._all.get(authenticationProvider.id) || []).sort(({ sessionId }) => sessionId === this.current?.sessionId ? -1 : 1); + const providerName = this.authenticationService.getLabel(authenticationProvider.id); + for (const account of accounts) { + quickPickItems.push({ + label: `${account.accountName} (${providerName})`, + description: account.sessionId === this.current?.sessionId ? localize('last used', "Last Used with Sync") : undefined, + account, + authenticationProvider, + }); + } + } + quickPickItems.push({ type: 'separator', label: localize('others', "Others") }); + } + + // Account proviers + for (const authenticationProvider of this.availableAuthenticationProviders) { + const signedInForProvider = this.all.some(account => account.authenticationProviderId === authenticationProvider.id); + if (!signedInForProvider || this.authenticationService.supportsMultipleAccounts(authenticationProvider.id)) { + const providerName = this.authenticationService.getLabel(authenticationProvider.id); + quickPickItems.push({ label: localize('sign in using account', "Sign in with {0}", providerName), authenticationProvider }); + } + } + + return quickPickItems; + } + + private async switch(sessionId: string, accountName: string, accountId: string): Promise { + const currentAccount = this.current; + if (this.userDataAutoSyncService.isEnabled() && (currentAccount && currentAccount.accountName !== accountName)) { + // accounts are switched while sync is enabled. + } + this.currentSessionId = sessionId; + this.telemetryService.publicLog2('sync.userAccount', { id: accountId }); + await this.update(); + } + + private async onDidSuccessiveAuthFailures(): Promise { + this.telemetryService.publicLog2('sync/successiveAuthFailures'); + this.currentSessionId = undefined; + await this.update(); + + this.notificationService.notify({ + severity: Severity.Error, + message: localize('successive auth failures', "Settings sync was turned off because of successive authorization failures. Please sign in again to continue synchronizing"), + actions: { + primary: [new Action('sign in', localize('sign in', "Sign in"), undefined, true, () => this.signIn())] + } + }); + } + + private onDidChangeSessions(e: AuthenticationSessionsChangeEvent): void { + if (this.currentSessionId && e.removed.includes(this.currentSessionId)) { + this.currentSessionId = undefined; + } + this.update(); + } + + private onDidChangeStorage(e: IWorkspaceStorageChangeEvent): void { + if (e.key === UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY && e.scope === StorageScope.GLOBAL + && this.currentSessionId !== this.getStoredCachedSessionId() /* This checks if current window changed the value or not */) { + this._cachedCurrentSessionId = null; + this.update(); + } + } + + private _cachedCurrentSessionId: string | undefined | null = null; + private get currentSessionId(): string | undefined { + if (this._cachedCurrentSessionId === null) { + this._cachedCurrentSessionId = this.getStoredCachedSessionId(); + } + return this._cachedCurrentSessionId; + } + + private set currentSessionId(cachedSessionId: string | undefined) { + if (this._cachedCurrentSessionId !== cachedSessionId) { + this._cachedCurrentSessionId = cachedSessionId; + if (cachedSessionId === undefined) { + this.storageService.remove(UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY, StorageScope.GLOBAL); + } else { + this.storageService.store(UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY, cachedSessionId, StorageScope.GLOBAL); + } + } + } + + private getStoredCachedSessionId(): string | undefined { + return this.storageService.get(UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY, StorageScope.GLOBAL); + } + + private get useWorkbenchSessionId(): boolean { + return !this.storageService.getBoolean(UserDataSyncWorkbenchService.DONOT_USE_WORKBENCH_SESSION_STORAGE_KEY, StorageScope.GLOBAL, false); + } + + private set useWorkbenchSessionId(useWorkbenchSession: boolean) { + this.storageService.store(UserDataSyncWorkbenchService.DONOT_USE_WORKBENCH_SESSION_STORAGE_KEY, !useWorkbenchSession, StorageScope.GLOBAL); + } + +} + +class UserDataSyncPreview extends Disposable implements IUserDataSyncPreview { + + private _resources: ReadonlyArray = []; + get resources() { return Object.freeze(this._resources); } + private _onDidChangeResources = this._register(new Emitter>()); + readonly onDidChangeResources = this._onDidChangeResources.event; + + private _conflicts: ReadonlyArray = []; + get conflicts() { return Object.freeze(this._conflicts); } + private _onDidChangeConflicts = this._register(new Emitter>()); + readonly onDidChangeConflicts = this._onDidChangeConflicts.event; + + private _onDidCompleteManualSync = this._register(new Emitter()); + readonly onDidCompleteManualSync = this._onDidCompleteManualSync.event; + private manualSync: { preview: [SyncResource, ISyncResourcePreview][], task: IManualSyncTask, disposables: DisposableStore } | undefined; + + constructor( + private readonly userDataSyncService: IUserDataSyncService + ) { + super(); + this.updateConflicts(userDataSyncService.conflicts); + this._register(userDataSyncService.onDidChangeConflicts(conflicts => this.updateConflicts(conflicts))); + } + + setManualSyncPreview(task: IManualSyncTask, preview: [SyncResource, ISyncResourcePreview][]): void { + const disposables = new DisposableStore(); + this.manualSync = { task, preview, disposables }; + this.updateResources(); + } + + unsetManualSyncPreview(): void { + if (this.manualSync) { + this.manualSync.disposables.dispose(); + this.manualSync = undefined; + } + this.updateResources(); + } + + async accept(syncResource: SyncResource, resource: URI, content?: string | null): Promise { + if (this.manualSync) { + const syncPreview = await this.manualSync.task.accept(resource, content); + this.updatePreview(syncPreview); + } else { + await this.userDataSyncService.accept(syncResource, resource, content, false); + } + } + + async merge(resource: URI): Promise { + if (!this.manualSync) { + throw new Error('Can merge only while syncing manually'); + } + const syncPreview = await this.manualSync.task.merge(resource); + this.updatePreview(syncPreview); + } + + async discard(resource: URI): Promise { + if (!this.manualSync) { + throw new Error('Can discard only while syncing manually'); + } + const syncPreview = await this.manualSync.task.discard(resource); + this.updatePreview(syncPreview); + } + + async apply(): Promise { + if (!this.manualSync) { + throw new Error('Can apply only while syncing manually'); + } + + try { + const syncPreview = await this.manualSync.task.apply(); + this.updatePreview(syncPreview); + if (!this._resources.length) { + this._onDidCompleteManualSync.fire(undefined); + } + } catch (error) { + await this.manualSync.task.stop(); + this.updatePreview([]); + this._onDidCompleteManualSync.fire(error); + } + } + + async cancel(): Promise { + if (!this.manualSync) { + throw new Error('Can cancel only while syncing manually'); + } + await this.manualSync.task.stop(); + this.updatePreview([]); + this._onDidCompleteManualSync.fire(canceled()); + } + + async pull(): Promise { + if (!this.manualSync) { + throw new Error('Can pull only while syncing manually'); + } + await this.manualSync.task.pull(); + this.updatePreview([]); + } + + async push(): Promise { + if (!this.manualSync) { + throw new Error('Can push only while syncing manually'); + } + await this.manualSync.task.push(); + this.updatePreview([]); + } + + private updatePreview(preview: [SyncResource, ISyncResourcePreview][]) { + if (this.manualSync) { + this.manualSync.preview = preview; + this.updateResources(); + } + } + + private updateConflicts(conflicts: [SyncResource, IResourcePreview[]][]): void { + const newConflicts = this.toUserDataSyncResourceGroups(conflicts); + if (!equals(newConflicts, this._conflicts, (a, b) => isEqual(a.local, b.local))) { + this._conflicts = newConflicts; + this._onDidChangeConflicts.fire(this.conflicts); + } + } + + private updateResources(): void { + const newResources = this.toUserDataSyncResourceGroups( + (this.manualSync?.preview || []) + .map(([syncResource, syncResourcePreview]) => + ([ + syncResource, + syncResourcePreview.resourcePreviews + ])) + ); + if (!equals(newResources, this._resources, (a, b) => isEqual(a.local, b.local) && a.mergeState === b.mergeState)) { + this._resources = newResources; + this._onDidChangeResources.fire(this.resources); + } + } + + private toUserDataSyncResourceGroups(syncResourcePreviews: [SyncResource, IResourcePreview[]][]): IUserDataSyncResource[] { + return flatten( + syncResourcePreviews.map(([syncResource, resourcePreviews]) => + resourcePreviews.map(({ localResource, remoteResource, previewResource, acceptedResource, localChange, remoteChange, mergeState }) => + ({ syncResource, local: localResource, remote: remoteResource, merged: previewResource, accepted: acceptedResource, localChange, remoteChange, mergeState }))) + ); + } + +} + +registerSingleton(IUserDataSyncWorkbenchService, UserDataSyncWorkbenchService); diff --git a/src/vs/workbench/services/userDataSync/common/userDataSync.ts b/src/vs/workbench/services/userDataSync/common/userDataSync.ts new file mode 100644 index 00000000000..aaaad9acf40 --- /dev/null +++ b/src/vs/workbench/services/userDataSync/common/userDataSync.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 { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IAuthenticationProvider, SyncStatus, SyncResource, Change, MergeState, UserDataSyncStoreType } from 'vs/platform/userDataSync/common/userDataSync'; +import { Event } from 'vs/base/common/event'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { localize } from 'vs/nls'; +import { URI } from 'vs/base/common/uri'; + +export interface IUserDataSyncAccount { + readonly authenticationProviderId: string; + readonly accountName: string; + readonly accountId: string; +} + +export interface IUserDataSyncPreview { + readonly onDidChangeResources: Event>; + readonly resources: ReadonlyArray; + + accept(syncResource: SyncResource, resource: URI, content?: string | null): Promise; + merge(resource?: URI): Promise; + discard(resource?: URI): Promise; + pull(): Promise; + push(): Promise; + apply(): Promise; + cancel(): Promise; +} + +export interface IUserDataSyncResource { + readonly syncResource: SyncResource; + readonly local: URI; + readonly remote: URI; + readonly merged: URI; + readonly accepted: URI; + readonly localChange: Change; + readonly remoteChange: Change; + readonly mergeState: MergeState; +} + +export const IUserDataSyncWorkbenchService = createDecorator('IUserDataSyncWorkbenchService'); +export interface IUserDataSyncWorkbenchService { + _serviceBrand: any; + + readonly enabled: boolean; + readonly authenticationProviders: IAuthenticationProvider[]; + + readonly all: IUserDataSyncAccount[]; + readonly current: IUserDataSyncAccount | undefined; + + readonly accountStatus: AccountStatus; + readonly onDidChangeAccountStatus: Event; + + readonly userDataSyncPreview: IUserDataSyncPreview; + + turnOn(): Promise; + turnoff(everyWhere: boolean): Promise; + signIn(): Promise; + switchSyncService(type: UserDataSyncStoreType): Promise; + + resetSyncedData(): Promise; + showSyncActivity(): Promise; +} + +export function getSyncAreaLabel(source: SyncResource): string { + switch (source) { + case SyncResource.Settings: return localize('settings', "Settings"); + case SyncResource.Keybindings: return localize('keybindings', "Keyboard Shortcuts"); + case SyncResource.Snippets: return localize('snippets', "User Snippets"); + case SyncResource.Extensions: return localize('extensions', "Extensions"); + case SyncResource.GlobalState: return localize('ui state label', "UI State"); + } +} + +export const enum AccountStatus { + Uninitialized = 'uninitialized', + Unavailable = 'unavailable', + Available = 'available', +} + +export const SYNC_TITLE = localize('sync category', "Settings Sync"); + +// Contexts +export const CONTEXT_SYNC_STATE = new RawContextKey('syncStatus', SyncStatus.Uninitialized); +export const CONTEXT_SYNC_ENABLEMENT = new RawContextKey('syncEnabled', false); +export const CONTEXT_ACCOUNT_STATE = new RawContextKey('userDataSyncAccountStatus', AccountStatus.Uninitialized); +export const CONTEXT_ENABLE_ACTIVITY_VIEWS = new RawContextKey(`enableSyncActivityViews`, false); +export const CONTEXT_ENABLE_SYNC_MERGES_VIEW = new RawContextKey(`enableSyncMergesView`, false); + +// Commands +export const CONFIGURE_SYNC_COMMAND_ID = 'workbench.userDataSync.actions.configure'; +export const SHOW_SYNC_LOG_COMMAND_ID = 'workbench.userDataSync.actions.showLog'; + +// VIEWS +export const SYNC_VIEW_CONTAINER_ID = 'workbench.view.sync'; +export const SYNC_MERGES_VIEW_ID = 'workbench.views.sync.merges'; diff --git a/src/vs/workbench/services/userDataSync/common/userDataSyncUtil.ts b/src/vs/workbench/services/userDataSync/common/userDataSyncUtil.ts index 0fbeb2a56c8..76f7d254f05 100644 --- a/src/vs/workbench/services/userDataSync/common/userDataSyncUtil.ts +++ b/src/vs/workbench/services/userDataSync/common/userDataSyncUtil.ts @@ -14,7 +14,7 @@ import { ITextResourcePropertiesService, ITextResourceConfigurationService } fro class UserDataSyncUtilService implements IUserDataSyncUtilService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @IKeybindingService private readonly keybindingsService: IKeybindingService, diff --git a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncAccountService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncAccountService.ts new file mode 100644 index 00000000000..4be8abf9893 --- /dev/null +++ b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncAccountService.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Event, Emitter } from 'vs/base/common/event'; +import { IUserDataSyncAccountService, IUserDataSyncAccount } from 'vs/platform/userDataSync/common/userDataSyncAccount'; + +export class UserDataSyncAccountService extends Disposable implements IUserDataSyncAccountService { + + declare readonly _serviceBrand: undefined; + + private readonly channel: IChannel; + + private _account: IUserDataSyncAccount | undefined; + get account(): IUserDataSyncAccount | undefined { return this._account; } + + get onTokenFailed(): Event { return this.channel.listen('onTokenFailed'); } + + private _onDidChangeAccount: Emitter = this._register(new Emitter()); + readonly onDidChangeAccount: Event = this._onDidChangeAccount.event; + + constructor( + @ISharedProcessService sharedProcessService: ISharedProcessService, + ) { + super(); + this.channel = sharedProcessService.getChannel('userDataSyncAccount'); + this.channel.call('_getInitialData').then(account => { + this._account = account; + this._register(this.channel.listen('onDidChangeAccount')(account => { + this._account = account; + this._onDidChangeAccount.fire(account); + })); + }); + } + + updateAccount(account: IUserDataSyncAccount | undefined): Promise { + return this.channel.call('updateAccount', account); + } + +} + +registerSingleton(IUserDataSyncAccountService, UserDataSyncAccountService); diff --git a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncMachinesService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncMachinesService.ts new file mode 100644 index 00000000000..d9320052d1d --- /dev/null +++ b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncMachinesService.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IUserDataSyncMachinesService, IUserDataSyncMachine } from 'vs/platform/userDataSync/common/userDataSyncMachines'; + +class UserDataSyncMachinesService extends Disposable implements IUserDataSyncMachinesService { + + declare readonly _serviceBrand: undefined; + + private readonly channel: IChannel; + + constructor( + @ISharedProcessService sharedProcessService: ISharedProcessService + ) { + super(); + this.channel = sharedProcessService.getChannel('userDataSyncMachines'); + } + + getMachines(): Promise { + return this.channel.call('getMachines'); + } + + addCurrentMachine(): Promise { + return this.channel.call('addCurrentMachine'); + } + + removeCurrentMachine(): Promise { + return this.channel.call('removeCurrentMachine'); + } + + renameMachine(machineId: string, name: string): Promise { + return this.channel.call('renameMachine', [machineId, name]); + } + + setEnablement(machineId: string, enabled: boolean): Promise { + return this.channel.call('setEnablement', [machineId, enabled]); + } + +} + +registerSingleton(IUserDataSyncMachinesService, UserDataSyncMachinesService); diff --git a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts index 3cfea3362a7..039a35d98d4 100644 --- a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts +++ b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SyncStatus, SyncResource, IUserDataSyncService, UserDataSyncError, SyncResourceConflicts, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync'; +import { SyncStatus, SyncResource, IUserDataSyncService, UserDataSyncError, ISyncResourceHandle, ISyncTask, IManualSyncTask, IUserDataManifest, ISyncResourcePreview, IResourcePreview } from 'vs/platform/userDataSync/common/userDataSync'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { Disposable } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; @@ -14,7 +14,7 @@ import { URI } from 'vs/base/common/uri'; export class UserDataSyncService extends Disposable implements IUserDataSyncService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly channel: IChannel; @@ -25,10 +25,10 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ get onDidChangeLocal(): Event { return this.channel.listen('onDidChangeLocal'); } - private _conflicts: SyncResourceConflicts[] = []; - get conflicts(): SyncResourceConflicts[] { return this._conflicts; } - private _onDidChangeConflicts: Emitter = this._register(new Emitter()); - readonly onDidChangeConflicts: Event = this._onDidChangeConflicts.event; + private _conflicts: [SyncResource, IResourcePreview[]][] = []; + get conflicts(): [SyncResource, IResourcePreview[]][] { return this._conflicts; } + private _onDidChangeConflicts: Emitter<[SyncResource, IResourcePreview[]][]> = this._register(new Emitter<[SyncResource, IResourcePreview[]][]>()); + readonly onDidChangeConflicts: Event<[SyncResource, IResourcePreview[]][]> = this._onDidChangeConflicts.event; private _lastSyncTime: number | undefined = undefined; get lastSyncTime(): number | undefined { return this._lastSyncTime; } @@ -39,7 +39,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ readonly onSyncErrors: Event<[SyncResource, UserDataSyncError][]> = this._onSyncErrors.event; constructor( - @ISharedProcessService sharedProcessService: ISharedProcessService + @ISharedProcessService private readonly sharedProcessService: ISharedProcessService ) { super(); const userDataSyncChannel = sharedProcessService.getChannel('userDataSync'); @@ -52,7 +52,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ return userDataSyncChannel.listen(event, arg); } }; - this.channel.call<[SyncStatus, SyncResourceConflicts[], number | undefined]>('_getInitialData').then(([status, conflicts, lastSyncTime]) => { + this.channel.call<[SyncStatus, [SyncResource, IResourcePreview[]][], number | undefined]>('_getInitialData').then(([status, conflicts, lastSyncTime]) => { this.updateStatus(status); this.updateConflicts(conflicts); if (lastSyncTime) { @@ -61,36 +61,45 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ this._register(this.channel.listen('onDidChangeStatus')(status => this.updateStatus(status))); this._register(this.channel.listen('onDidChangeLastSyncTime')(lastSyncTime => this.updateLastSyncTime(lastSyncTime))); }); - this._register(this.channel.listen('onDidChangeConflicts')(conflicts => this.updateConflicts(conflicts))); + this._register(this.channel.listen<[SyncResource, IResourcePreview[]][]>('onDidChangeConflicts')(conflicts => this.updateConflicts(conflicts))); this._register(this.channel.listen<[SyncResource, Error][]>('onSyncErrors')(errors => this._onSyncErrors.fire(errors.map(([source, error]) => ([source, UserDataSyncError.toUserDataSyncError(error)]))))); } - pull(): Promise { - return this.channel.call('pull'); + createSyncTask(): Promise { + throw new Error('not supported'); } - sync(): Promise { - return this.channel.call('sync'); + async createManualSyncTask(): Promise { + const { id, manifest } = await this.channel.call<{ id: string, manifest: IUserDataManifest | null }>('createManualSyncTask'); + return new ManualSyncTask(id, manifest, this.sharedProcessService); } - stop(): Promise { - return this.channel.call('stop'); + replace(uri: URI): Promise { + return this.channel.call('replace', [uri]); } reset(): Promise { return this.channel.call('reset'); } + resetRemote(): Promise { + return this.channel.call('resetRemote'); + } + resetLocal(): Promise { return this.channel.call('resetLocal'); } - isFirstTimeSyncWithMerge(): Promise { - return this.channel.call('isFirstTimeSyncWithMerge'); + hasPreviouslySynced(): Promise { + return this.channel.call('hasPreviouslySynced'); } - acceptConflict(conflict: URI, content: string): Promise { - return this.channel.call('acceptConflict', [conflict, content]); + hasLocalData(): Promise { + return this.channel.call('hasLocalData'); + } + + accept(syncResource: SyncResource, resource: URI, content: string | null, apply: boolean): Promise { + return this.channel.call('accept', [syncResource, resource, content, apply]); } resolveContent(resource: URI): Promise { @@ -112,20 +121,29 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ return result.map(({ resource, comparableResource }) => ({ resource: URI.revive(resource), comparableResource: URI.revive(comparableResource) })); } + async getMachineId(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise { + return this.channel.call('getMachineId', [resource, syncResourceHandle]); + } + private async updateStatus(status: SyncStatus): Promise { this._status = status; this._onDidChangeStatus.fire(status); } - private async updateConflicts(conflicts: SyncResourceConflicts[]): Promise { + private async updateConflicts(conflicts: [SyncResource, IResourcePreview[]][]): Promise { // Revive URIs - this._conflicts = conflicts.map(c => - ({ - syncResource: c.syncResource, - conflicts: c.conflicts.map(({ local, remote }) => - ({ local: URI.revive(local), remote: URI.revive(remote) })) - })); - this._onDidChangeConflicts.fire(conflicts); + this._conflicts = conflicts.map(([syncResource, conflicts]) => + ([ + syncResource, + conflicts.map(r => + ({ + ...r, + localResource: URI.revive(r.localResource), + remoteResource: URI.revive(r.remoteResource), + previewResource: URI.revive(r.previewResource), + })) + ])); + this._onDidChangeConflicts.fire(this._conflicts); } private updateLastSyncTime(lastSyncTime: number): void { @@ -136,4 +154,86 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ } } +class ManualSyncTask implements IManualSyncTask { + + private readonly channel: IChannel; + + get onSynchronizeResources(): Event<[SyncResource, URI[]][]> { return this.channel.listen<[SyncResource, URI[]][]>('onSynchronizeResources'); } + + constructor( + readonly id: string, + readonly manifest: IUserDataManifest | null, + sharedProcessService: ISharedProcessService, + ) { + const manualSyncTaskChannel = sharedProcessService.getChannel(`manualSyncTask-${id}`); + this.channel = { + call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise { + return manualSyncTaskChannel.call(command, arg, cancellationToken) + .then(null, error => { throw UserDataSyncError.toUserDataSyncError(error); }); + }, + listen(event: string, arg?: any): Event { + return manualSyncTaskChannel.listen(event, arg); + } + }; + } + + async preview(): Promise<[SyncResource, ISyncResourcePreview][]> { + const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('preview'); + return this.deserializePreviews(previews); + } + + async accept(resource: URI, content?: string | null): Promise<[SyncResource, ISyncResourcePreview][]> { + const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('accept', [resource, content]); + return this.deserializePreviews(previews); + } + + async merge(resource: URI): Promise<[SyncResource, ISyncResourcePreview][]> { + const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('merge', [resource]); + return this.deserializePreviews(previews); + } + + async discard(resource: URI): Promise<[SyncResource, ISyncResourcePreview][]> { + const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('discard', [resource]); + return this.deserializePreviews(previews); + } + + async apply(): Promise<[SyncResource, ISyncResourcePreview][]> { + const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('apply'); + return this.deserializePreviews(previews); + } + + pull(): Promise { + return this.channel.call('pull'); + } + + push(): Promise { + return this.channel.call('push'); + } + + stop(): Promise { + return this.channel.call('stop'); + } + + dispose(): void { + this.channel.call('dispose'); + } + + private deserializePreviews(previews: [SyncResource, ISyncResourcePreview][]): [SyncResource, ISyncResourcePreview][] { + return previews.map(([syncResource, preview]) => + ([ + syncResource, + { + isLastSyncFromCurrentMachine: preview.isLastSyncFromCurrentMachine, + resourcePreviews: preview.resourcePreviews.map(r => ({ + ...r, + localResource: URI.revive(r.localResource), + remoteResource: URI.revive(r.remoteResource), + previewResource: URI.revive(r.previewResource), + acceptedResource: URI.revive(r.acceptedResource), + })) + } + ])); + } +} + registerSingleton(IUserDataSyncService, UserDataSyncService); diff --git a/src/vs/workbench/services/userDataSync/electron-browser/storageKeysSyncRegistryService.ts b/src/vs/workbench/services/userDataSync/electron-sandbox/storageKeysSyncRegistryService.ts similarity index 91% rename from src/vs/workbench/services/userDataSync/electron-browser/storageKeysSyncRegistryService.ts rename to src/vs/workbench/services/userDataSync/electron-sandbox/storageKeysSyncRegistryService.ts index ce4c1262861..12287d95d05 100644 --- a/src/vs/workbench/services/userDataSync/electron-browser/storageKeysSyncRegistryService.ts +++ b/src/vs/workbench/services/userDataSync/electron-sandbox/storageKeysSyncRegistryService.ts @@ -5,7 +5,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; -import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; import { StorageKeysSyncRegistryChannelClient } from 'vs/platform/userDataSync/common/userDataSyncIpc'; class StorageKeysSyncRegistryService extends StorageKeysSyncRegistryChannelClient implements IStorageKeysSyncRegistryService { @@ -15,7 +15,6 @@ class StorageKeysSyncRegistryService extends StorageKeysSyncRegistryChannelClien ) { super(mainProcessService.getChannel('storageKeysSyncRegistryService')); } - } registerSingleton(IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService); diff --git a/src/vs/workbench/services/viewlet/browser/viewlet.ts b/src/vs/workbench/services/viewlet/browser/viewlet.ts index ab12dcde4cc..d227b1ae5a0 100644 --- a/src/vs/workbench/services/viewlet/browser/viewlet.ts +++ b/src/vs/workbench/services/viewlet/browser/viewlet.ts @@ -13,7 +13,7 @@ export const IViewletService = createDecorator('viewletService' export interface IViewletService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; readonly onDidViewletRegister: Event; readonly onDidViewletDeregister: Event; diff --git a/src/vs/workbench/services/views/browser/viewDescriptorService.ts b/src/vs/workbench/services/views/browser/viewDescriptorService.ts index 6875ce28b08..ea89babdc73 100644 --- a/src/vs/workbench/services/views/browser/viewDescriptorService.ts +++ b/src/vs/workbench/services/views/browser/viewDescriptorService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ViewContainerLocation, IViewDescriptorService, ViewContainer, IViewsRegistry, IViewContainersRegistry, IViewDescriptor, Extensions as ViewExtensions } from 'vs/workbench/common/views'; -import { IContextKey, RawContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKey, RawContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -18,15 +18,16 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { generateUuid } from 'vs/base/common/uuid'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ViewContainerModel } from 'vs/workbench/services/views/common/viewContainerModel'; +import { registerAction2, Action2, MenuId } from 'vs/platform/actions/common/actions'; +import { localize } from 'vs/nls'; interface ICachedViewContainerInfo { containerId: string; - location?: ViewContainerLocation; } export class ViewDescriptorService extends Disposable implements IViewDescriptorService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private static readonly CACHED_VIEW_POSITIONS = 'views.cachedViewPositions'; private static readonly CACHED_VIEW_CONTAINER_LOCATIONS = 'views.cachedViewContainerLocations'; @@ -45,6 +46,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor private readonly activeViewContextKeys: Map>; private readonly movableViewContextKeys: Map>; private readonly defaultViewLocationContextKeys: Map>; + private readonly defaultViewContainerLocationContextKeys: Map>; private readonly viewsRegistry: IViewsRegistry; private readonly viewContainersRegistry: IViewContainersRegistry; @@ -84,7 +86,8 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor } } - readonly onDidChangeViewContainers: Event<{ added: ReadonlyArray<{ container: ViewContainer, location: ViewContainerLocation }>, removed: ReadonlyArray<{ container: ViewContainer, location: ViewContainerLocation }> }>; + private readonly _onDidChangeViewContainers = this._register(new Emitter<{ added: ReadonlyArray<{ container: ViewContainer, location: ViewContainerLocation }>, removed: ReadonlyArray<{ container: ViewContainer, location: ViewContainerLocation }> }>()); + readonly onDidChangeViewContainers = this._onDidChangeViewContainers.event; get viewContainers(): ReadonlyArray { return this.viewContainersRegistry.all; } constructor( @@ -98,34 +101,37 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor super(); storageKeysSyncRegistryService.registerStorageKey({ key: ViewDescriptorService.CACHED_VIEW_POSITIONS, version: 1 }); + storageKeysSyncRegistryService.registerStorageKey({ key: ViewDescriptorService.CACHED_VIEW_CONTAINER_LOCATIONS, version: 1 }); this.viewContainerModels = new Map(); this.activeViewContextKeys = new Map>(); this.movableViewContextKeys = new Map>(); this.defaultViewLocationContextKeys = new Map>(); + this.defaultViewContainerLocationContextKeys = new Map>(); this.viewContainersRegistry = Registry.as(ViewExtensions.ViewContainersRegistry); this.viewsRegistry = Registry.as(ViewExtensions.ViewsRegistry); - this.cachedViewInfo = this.getCachedViewPositions(); this.cachedViewContainerInfo = this.getCachedViewContainerLocations(); + this.cachedViewInfo = this.getCachedViewPositions(); // Register all containers that were registered before this ctor this.viewContainers.forEach(viewContainer => this.onDidRegisterViewContainer(viewContainer)); - this.onDidChangeViewContainers = Event.any<{ added: ReadonlyArray<{ container: ViewContainer, location: ViewContainerLocation }>, removed: ReadonlyArray<{ container: ViewContainer, location: ViewContainerLocation }> }>( - Event.map(this.viewContainersRegistry.onDidRegister, e => ({ added: [{ container: e.viewContainer, location: e.viewContainerLocation }], removed: [] })), - Event.map(this.viewContainersRegistry.onDidDeregister, e => ({ added: [], removed: [{ container: e.viewContainer, location: e.viewContainerLocation }] })) - ); - - // Try generating all generated containers that don't need extensions - this.tryGenerateContainers(); this._register(this.viewsRegistry.onViewsRegistered(views => this.onDidRegisterViews(views))); this._register(this.viewsRegistry.onViewsDeregistered(({ views, viewContainer }) => this.onDidDeregisterViews(views, viewContainer))); this._register(this.viewsRegistry.onDidChangeContainer(({ views, from, to }) => this.moveViews(views, from, to))); - this._register(this.viewContainersRegistry.onDidRegister(({ viewContainer }) => this.onDidRegisterViewContainer(viewContainer))); - this._register(this.viewContainersRegistry.onDidDeregister(({ viewContainer }) => this.onDidDeregisterViewContainer(viewContainer))); + this._register(this.viewContainersRegistry.onDidRegister(({ viewContainer }) => { + this.onDidRegisterViewContainer(viewContainer); + this._onDidChangeViewContainers.fire({ added: [{ container: viewContainer, location: this.getViewContainerLocation(viewContainer) }], removed: [] }); + })); + + this._register(this.viewContainersRegistry.onDidDeregister(({ viewContainer }) => { + this.onDidDeregisterViewContainer(viewContainer); + this._onDidChangeViewContainers.fire({ removed: [{ container: viewContainer, location: this.getViewContainerLocation(viewContainer) }], added: [] }); + })); + this._register(toDisposable(() => { this.viewContainerModels.forEach(({ disposable }) => disposable.dispose()); this.viewContainerModels.clear(); @@ -144,18 +150,21 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor // The container has not been registered yet if (!viewContainer || !this.viewContainerModels.has(viewContainer)) { - if (containerData.cachedContainerInfo && this.shouldGenerateContainer(containerData.cachedContainerInfo)) { - const containerInfo = containerData.cachedContainerInfo; - + if (containerData.cachedContainerInfo && this.isGeneratedContainerId(containerData.cachedContainerInfo.containerId)) { if (!this.viewContainersRegistry.get(containerId)) { - this.registerGeneratedViewContainer(containerInfo.location!, containerId); + this.registerGeneratedViewContainer(this.cachedViewContainerInfo.get(containerId)!, containerId); } } + // Registration of a generated container handles registration of its views continue; } - this.addViews(viewContainer, containerData.views); + // Filter out views that have already been added to the view container model + // This is needed when statically-registered views are moved to + // other statically registered containers as they will both try to add on startup + const viewsToAdd = containerData.views.filter(view => this.getViewContainerModel(viewContainer).allViewDescriptors.filter(vd => vd.id === view.id).length === 0); + this.addViews(viewContainer, viewsToAdd); } } @@ -173,7 +182,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor } } - private tryGenerateContainers(fallbackToDefault?: boolean): void { + private fallbackOrphanedViews(): void { for (const [viewId, containerInfo] of this.cachedViewInfo.entries()) { const containerId = containerInfo.containerId; @@ -182,59 +191,47 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor continue; } - // check if we should generate this container - if (this.shouldGenerateContainer(containerInfo)) { - this.registerGeneratedViewContainer(containerInfo.location!, containerId); - continue; + // check if view has been registered to default location + const viewContainer = this.viewsRegistry.getViewContainer(viewId); + const viewDescriptor = this.getViewDescriptorById(viewId); + if (viewContainer && viewDescriptor) { + this.addViews(viewContainer, [viewDescriptor]); } - - if (fallbackToDefault) { - // check if view has been registered to default location - const viewContainer = this.viewsRegistry.getViewContainer(viewId); - const viewDescriptor = this.getViewDescriptorById(viewId); - if (viewContainer && viewDescriptor) { - this.addViews(viewContainer, [viewDescriptor]); - - const newLocation = this.getViewContainerLocation(viewContainer); - if (containerInfo.location && containerInfo.location !== newLocation) { - this._onDidChangeLocation.fire({ views: [viewDescriptor], from: containerInfo.location, to: newLocation }); - } - } - } - } - - if (fallbackToDefault) { - this.saveViewPositionsToCache(); } } private onDidRegisterExtensions(): void { - this.tryGenerateContainers(true); + // If an extension is uninstalled, this method will handle resetting views to default locations + this.fallbackOrphanedViews(); } private onDidRegisterViews(views: { views: IViewDescriptor[], viewContainer: ViewContainer }[]): void { - views.forEach(({ views, viewContainer }) => { - // When views are registered, we need to regroup them based on the cache - const regroupedViews = this.regroupViews(viewContainer.id, views); + this.contextKeyService.bufferChangeEvents(() => { + views.forEach(({ views, viewContainer }) => { + // When views are registered, we need to regroup them based on the cache + const regroupedViews = this.regroupViews(viewContainer.id, views); - // Once they are grouped, try registering them which occurs - // if the container has already been registered within this service - // or we can generate the container from the source view id - this.registerGroupedViews(regroupedViews); + // Once they are grouped, try registering them which occurs + // if the container has already been registered within this service + // or we can generate the container from the source view id + this.registerGroupedViews(regroupedViews); - views.forEach(viewDescriptor => this.getOrCreateMovableViewContextKey(viewDescriptor).set(!!viewDescriptor.canMoveView)); + views.forEach(viewDescriptor => this.getOrCreateMovableViewContextKey(viewDescriptor).set(!!viewDescriptor.canMoveView)); + }); }); } - private shouldGenerateContainer(containerInfo: ICachedViewContainerInfo): boolean { - return containerInfo.containerId.startsWith(ViewDescriptorService.COMMON_CONTAINER_ID_PREFIX) && containerInfo.location !== undefined; + private isGeneratedContainerId(id: string): boolean { + return id.startsWith(ViewDescriptorService.COMMON_CONTAINER_ID_PREFIX); } private onDidDeregisterViews(views: IViewDescriptor[], viewContainer: ViewContainer): void { // When views are registered, we need to regroup them based on the cache const regroupedViews = this.regroupViews(viewContainer.id, views); this.deregisterGroupedViews(regroupedViews); - views.forEach(viewDescriptor => this.getOrCreateMovableViewContextKey(viewDescriptor).set(false)); + this.contextKeyService.bufferChangeEvents(() => { + views.forEach(viewDescriptor => this.getOrCreateMovableViewContextKey(viewDescriptor).set(false)); + }); } private regroupViews(containerId: string, views: IViewDescriptor[]): Map { @@ -257,17 +254,8 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor } getViewLocationById(viewId: string): ViewContainerLocation | null { - const cachedInfo = this.cachedViewInfo.get(viewId); - - if (cachedInfo && cachedInfo.location) { - return cachedInfo.location; - } - - const container = cachedInfo?.containerId ? - this.viewContainersRegistry.get(cachedInfo.containerId) ?? null : - this.viewsRegistry.getViewContainer(viewId); - - if (!container) { + const container = this.getViewContainerByViewId(viewId); + if (container === null) { return null; } @@ -311,16 +299,16 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor return this.viewContainersRegistry.getDefaultViewContainer(location); } - moveViewContainerToLocation(viewContainer: ViewContainer, location: ViewContainerLocation): void { + moveViewContainerToLocation(viewContainer: ViewContainer, location: ViewContainerLocation, requestedIndex?: number): void { const from = this.getViewContainerLocation(viewContainer); const to = location; if (from !== to) { - if (this.getDefaultViewContainerLocation(viewContainer) === to) { - this.cachedViewContainerInfo.delete(viewContainer.id); - } else { - this.cachedViewContainerInfo.set(viewContainer.id, to); - } + this.cachedViewContainerInfo.set(viewContainer.id, to); + const defaultLocation = this.isGeneratedContainerId(viewContainer.id) ? true : this.getViewContainerLocation(viewContainer) === this.getDefaultViewContainerLocation(viewContainer); + this.getOrCreateDefaultViewContainerLocationContextKey(viewContainer).set(defaultLocation); + + viewContainer.requestedIndex = requestedIndex; this._onDidChangeContainerLocation.fire({ viewContainer, from, to }); const views = this.getViewsByContainer(viewContainer); @@ -350,7 +338,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor private moveViews(views: IViewDescriptor[], from: ViewContainer, to: ViewContainer, skipCacheUpdate?: boolean): void { this.removeViews(from, views); - this.addViews(to, views); + this.addViews(to, views, true); const oldLocation = this.getViewContainerLocation(from); const newLocation = this.getViewContainerLocation(to); @@ -406,7 +394,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor private registerGeneratedViewContainer(location: ViewContainerLocation, existingId?: string): ViewContainer { const id = existingId || this.generateContainerId(location); - return this.viewContainersRegistry.registerViewContainer({ + const container = this.viewContainersRegistry.registerViewContainer({ id, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [id, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), name: 'Custom Views', // we don't want to see this, so no need to localize @@ -414,6 +402,16 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor storageId: `${id}.state`, hideIfEmpty: true }, location); + + const cachedInfo = this.cachedViewContainerInfo.get(container.id); + if (cachedInfo !== location) { + this.cachedViewContainerInfo.set(container.id, location); + this.saveViewContainerLocationsToCache(); + } + + this.getOrCreateDefaultViewContainerLocationContextKey(container).set(true); + + return container; } private getCachedViewPositions(): Map { @@ -423,6 +421,14 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor for (const [viewId, containerInfo] of result.entries()) { if (!containerInfo) { result.delete(viewId); + continue; + } + + // Verify a view that is in a generated has cached container info + const generated = this.isGeneratedContainerId(containerInfo.containerId); + const missingCacheData = this.cachedViewContainerInfo.get(containerInfo.containerId) === undefined; + if (generated && missingCacheData) { + result.delete(viewId); } } @@ -450,7 +456,10 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor const newViewContainerInfo = newCachedPositions.get(viewId)!; // Verify if we need to create the destination container if (!this.viewContainersRegistry.get(newViewContainerInfo.containerId)) { - this.registerGeneratedViewContainer(newViewContainerInfo.location!, newViewContainerInfo.containerId); + const location = this.cachedViewContainerInfo.get(newViewContainerInfo.containerId); + if (location !== undefined) { + this.registerGeneratedViewContainer(location, newViewContainerInfo.containerId); + } } // Try moving to the new container @@ -540,10 +549,8 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor this.viewContainers.forEach(viewContainer => { const viewContainerModel = this.getViewContainerModel(viewContainer); viewContainerModel.allViewDescriptors.forEach(viewDescriptor => { - const containerLocation = this.getViewContainerLocation(viewContainer); this.cachedViewInfo.set(viewDescriptor.id, { - containerId: viewContainer.id, - location: containerLocation + containerId: viewContainer.id }); }); }); @@ -564,7 +571,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor private saveViewContainerLocationsToCache(): void { for (const [containerId, location] of this.cachedViewContainerInfo) { const container = this.getViewContainerById(containerId); - if (container && location === this.getDefaultViewContainerLocation(container)) { + if (container && location === this.getDefaultViewContainerLocation(container) && !this.isGeneratedContainerId(containerId)) { this.cachedViewContainerInfo.delete(containerId); } } @@ -597,6 +604,8 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor } private onDidRegisterViewContainer(viewContainer: ViewContainer): void { + const defaultLocation = this.isGeneratedContainerId(viewContainer.id) ? true : this.getViewContainerLocation(viewContainer) === this.getDefaultViewContainerLocation(viewContainer); + this.getOrCreateDefaultViewContainerLocationContextKey(viewContainer).set(defaultLocation); this.getOrRegisterViewContainerModel(viewContainer); } @@ -610,12 +619,22 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor this.onDidChangeActiveViews({ added: viewContainerModel.activeViewDescriptors, removed: [] }); viewContainerModel.onDidChangeActiveViewDescriptors(changed => this.onDidChangeActiveViews(changed), this, disposables); + disposables.add(this.registerResetViewContainerAction(viewContainer)); + this.viewContainerModels.set(viewContainer, { viewContainerModel: viewContainerModel, disposable: disposables }); - const viewsToRegister = this.getViewsByContainer(viewContainer); + // Register all views that were statically registered to this container + // Potentially, this is registering something that was handled by another container + // addViews() handles this by filtering views that are already registered + this.onDidRegisterViews([{ views: this.viewsRegistry.getViews(viewContainer), viewContainer }]); + + // Add views that were registered prior to this view container + const viewsToRegister = this.getViewsByContainer(viewContainer).filter(view => this.getDefaultContainerById(view.id) !== viewContainer); if (viewsToRegister.length) { this.addViews(viewContainer, viewsToRegister); - viewsToRegister.forEach(viewDescriptor => this.getOrCreateMovableViewContextKey(viewDescriptor).set(!!viewDescriptor.canMoveView)); + this.contextKeyService.bufferChangeEvents(() => { + viewsToRegister.forEach(viewDescriptor => this.getOrCreateMovableViewContextKey(viewDescriptor).set(!!viewDescriptor.canMoveView)); + }); } } @@ -631,24 +650,56 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor } private onDidChangeActiveViews({ added, removed }: { added: ReadonlyArray, removed: ReadonlyArray; }): void { - added.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(true)); - removed.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(false)); + this.contextKeyService.bufferChangeEvents(() => { + added.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(true)); + removed.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(false)); + }); } - private addViews(container: ViewContainer, views: IViewDescriptor[]): void { + private registerResetViewContainerAction(viewContainer: ViewContainer): IDisposable { + const that = this; + return registerAction2(class ResetViewLocationAction extends Action2 { + constructor() { + super({ + id: `${viewContainer.id}.resetViewContainerLocation`, + title: { + original: 'Reset Location', + value: localize('resetViewLocation', "Reset Location") + }, + menu: [{ + id: MenuId.ViewContainerTitleContext, + when: ContextKeyExpr.or( + ContextKeyExpr.and( + ContextKeyExpr.equals('container', viewContainer.id), + ContextKeyExpr.equals(`${viewContainer.id}.defaultViewContainerLocation`, false) + ) + ) + }], + }); + } + run(): void { + that.moveViewContainerToLocation(viewContainer, that.getDefaultViewContainerLocation(viewContainer)); + } + }); + } + + private addViews(container: ViewContainer, views: IViewDescriptor[], expandViews?: boolean): void { // Update in memory cache - const location = this.getViewContainerLocation(container); - views.forEach(view => { - this.cachedViewInfo.set(view.id, { containerId: container.id, location }); - this.getOrCreateDefaultViewLocationContextKey(view).set(this.getDefaultContainerById(view.id) === container); + this.contextKeyService.bufferChangeEvents(() => { + views.forEach(view => { + this.cachedViewInfo.set(view.id, { containerId: container.id }); + this.getOrCreateDefaultViewLocationContextKey(view).set(this.getDefaultContainerById(view.id) === container); + }); }); - this.getViewContainerModel(container).add(views); + this.getViewContainerModel(container).add(views.map(view => { return { viewDescriptor: view, collapsed: expandViews ? false : undefined }; })); } private removeViews(container: ViewContainer, views: IViewDescriptor[]): void { // Set view default location keys to false - views.forEach(view => this.getOrCreateDefaultViewLocationContextKey(view).set(false)); + this.contextKeyService.bufferChangeEvents(() => { + views.forEach(view => this.getOrCreateDefaultViewLocationContextKey(view).set(false)); + }); // Remove the views this.getViewContainerModel(container).remove(views); @@ -683,6 +734,16 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor } return contextKey; } + + private getOrCreateDefaultViewContainerLocationContextKey(viewContainer: ViewContainer): IContextKey { + const defaultViewContainerLocationContextKeyId = `${viewContainer.id}.defaultViewContainerLocation`; + let contextKey = this.defaultViewContainerLocationContextKeys.get(defaultViewContainerLocationContextKeyId); + if (!contextKey) { + contextKey = new RawContextKey(defaultViewContainerLocationContextKeyId, false).bindTo(this.contextKeyService); + this.defaultViewContainerLocationContextKeys.set(defaultViewContainerLocationContextKeyId, contextKey); + } + return contextKey; + } } registerSingleton(IViewDescriptorService, ViewDescriptorService); diff --git a/src/vs/workbench/services/views/common/viewContainerModel.ts b/src/vs/workbench/services/views/common/viewContainerModel.ts index 22a0bee904a..60c69da9c41 100644 --- a/src/vs/workbench/services/views/common/viewContainerModel.ts +++ b/src/vs/workbench/services/views/common/viewContainerModel.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ViewContainer, IViewsRegistry, IViewDescriptor, Extensions as ViewExtensions, IViewContainerModel, IAddedViewDescriptorRef, IViewDescriptorRef } from 'vs/workbench/common/views'; +import { ViewContainer, IViewsRegistry, IViewDescriptor, Extensions as ViewExtensions, IViewContainerModel, IAddedViewDescriptorRef, IViewDescriptorRef, IAddedViewDescriptorState } from 'vs/workbench/common/views'; import { IContextKeyService, IReadableSet } from 'vs/platform/contextkey/common/contextkey'; import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -14,7 +14,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { URI } from 'vs/base/common/uri'; import { firstIndex, move } from 'vs/base/common/arrays'; import { isUndefined, isUndefinedOrNull } from 'vs/base/common/types'; -import { values } from 'vs/base/common/map'; import { isEqual } from 'vs/base/common/resources'; class CounterSet implements IReadableSet { @@ -181,7 +180,7 @@ class ViewDescriptorsState extends Disposable { const value = this.storageService.get(this.globalViewsStateStorageId, StorageScope.WORKSPACE, '[]'); const { state: workspaceVisibilityStates } = this.parseStoredGlobalState(value); if (workspaceVisibilityStates.size > 0) { - for (const { id, isHidden } of values(workspaceVisibilityStates)) { + for (const { id, isHidden } of workspaceVisibilityStates.values()) { let viewState = viewStates.get(id); // Not migrated to `viewletStateStorageId` if (viewState) { @@ -204,7 +203,7 @@ class ViewDescriptorsState extends Disposable { if (hasDuplicates) { this.setStoredGlobalState(state); } - for (const { id, isHidden, order } of values(state)) { + for (const { id, isHidden, order } of state.values()) { let viewState = viewStates.get(id); if (viewState) { viewState.visibleGlobal = !isHidden; @@ -229,7 +228,7 @@ class ViewDescriptorsState extends Disposable { } private setStoredGlobalState(storedGlobalState: Map): void { - this.globalViewsStatesValue = JSON.stringify(values(storedGlobalState)); + this.globalViewsStatesValue = JSON.stringify([...storedGlobalState.values()]); } private parseStoredGlobalState(value: string): { state: Map, hasDuplicates: boolean } { @@ -461,12 +460,13 @@ export class ViewContainerModel extends Disposable implements IViewContainerMode }); } - add(viewDescriptors: IViewDescriptor[]): void { + add(addedViewDescriptorStates: IAddedViewDescriptorState[]): void { const addedItems: IViewDescriptorItem[] = []; const addedActiveDescriptors: IViewDescriptor[] = []; const addedVisibleItems: { index: number, viewDescriptor: IViewDescriptor, size?: number, collapsed: boolean; }[] = []; - for (const viewDescriptor of viewDescriptors) { + for (const addedViewDescriptorState of addedViewDescriptorStates) { + const viewDescriptor = addedViewDescriptorState.viewDescriptor; if (viewDescriptor.when) { for (const key of viewDescriptor.when.keys()) { @@ -482,13 +482,13 @@ export class ViewContainerModel extends Disposable implements IViewContainerMode } else { state.visibleGlobal = isUndefinedOrNull(state.visibleGlobal) ? !viewDescriptor.hideByDefault : state.visibleGlobal; } - state.collapsed = isUndefinedOrNull(state.collapsed) ? !!viewDescriptor.collapsed : state.collapsed; + state.collapsed = isUndefinedOrNull(addedViewDescriptorState.collapsed) ? (isUndefinedOrNull(state.collapsed) ? !!viewDescriptor.collapsed : state.collapsed) : addedViewDescriptorState.collapsed; } else { state = { active: false, visibleGlobal: !viewDescriptor.hideByDefault, visibleWorkspace: !viewDescriptor.hideByDefault, - collapsed: !!viewDescriptor.collapsed, + collapsed: isUndefinedOrNull(addedViewDescriptorState.collapsed) ? !!viewDescriptor.collapsed : addedViewDescriptorState.collapsed, }; } this.viewDescriptorsState.set(viewDescriptor.id, state); diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyFileOperationParticipant.ts b/src/vs/workbench/services/workingCopy/common/workingCopyFileOperationParticipant.ts index 4b0928035ee..7d60ea29d7f 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyFileOperationParticipant.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyFileOperationParticipant.ts @@ -33,7 +33,7 @@ export class WorkingCopyFileOperationParticipant extends Disposable { return toDisposable(() => remove()); } - async participate(target: URI, source: URI | undefined, operation: FileOperation): Promise { + async participate(files: { source?: URI, target: URI }[], operation: FileOperation): Promise { const timeout = this.configurationService.getValue('files.participants.timeout'); if (timeout <= 0) { return; // disabled @@ -53,7 +53,7 @@ export class WorkingCopyFileOperationParticipant extends Disposable { } try { - const promise = participant.participate(target, source, operation, progress, timeout, cts.token); + const promise = participant.participate(files, operation, progress, timeout, cts.token); await raceTimeout(promise, timeout, () => cts.dispose(true /* cancel */)); } catch (err) { this.logService.warn(err); diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyFileService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyFileService.ts index 1bfc60d2b8b..6c5dc194083 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyFileService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyFileService.ts @@ -12,12 +12,26 @@ import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle' import { IFileService, FileOperation, IFileStatWithMetadata } from 'vs/platform/files/common/files'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IWorkingCopyService, IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService'; -import { isEqualOrParent, isEqual } from 'vs/base/common/resources'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; import { WorkingCopyFileOperationParticipant } from 'vs/workbench/services/workingCopy/common/workingCopyFileOperationParticipant'; +import { VSBuffer, VSBufferReadable, VSBufferReadableStream } from 'vs/base/common/buffer'; export const IWorkingCopyFileService = createDecorator('workingCopyFileService'); +interface SourceTargetPair { + + /** + * The source resource that is defined for move operations. + */ + readonly source?: URI; + + /** + * The target resource the event is about. + */ + readonly target: URI +} + export interface WorkingCopyFileEvent extends IWaitUntil { /** @@ -32,25 +46,19 @@ export interface WorkingCopyFileEvent extends IWaitUntil { readonly operation: FileOperation; /** - * The resource the event is about. + * The array of source/target pair of files involved in given operation. */ - readonly target: URI; - - /** - * A property that is defined for move operations. - */ - readonly source?: URI; + readonly files: SourceTargetPair[] } export interface IWorkingCopyFileOperationParticipant { /** - * Participate in a file operation of a working copy. Allows to - * change the working copy before it is being saved to disk. + * Participate in a file operation of working copies. Allows to + * change the working copies before they are being saved to disk. */ participate( - target: URI, - source: URI | undefined, + files: SourceTargetPair[], operation: FileOperation, progress: IProgress, timeout: number, @@ -73,7 +81,7 @@ type WorkingCopyProvider = (resourceOrFolder: URI) => IWorkingCopy[]; */ export interface IWorkingCopyFileService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; //#region Events @@ -111,43 +119,49 @@ export interface IWorkingCopyFileService { */ addFileOperationParticipant(participant: IWorkingCopyFileOperationParticipant): IDisposable; - /** - * Execute all known file operation participants. - */ - runFileOperationParticipants(target: URI, source: URI | undefined, operation: FileOperation): Promise + //#endregion //#region File operations /** - * Will move working copies matching the provided resource and children - * to the target resource using the associated file service for that resource. + * Will create a resource with the provided optional contents, optionally overwriting any target. * * Working copy owners can listen to the `onWillRunWorkingCopyFileOperation` and * `onDidRunWorkingCopyFileOperation` events to participate. */ - move(source: URI, target: URI, overwrite?: boolean): Promise; + create(resource: URI, contents?: VSBuffer | VSBufferReadable | VSBufferReadableStream, options?: { overwrite?: boolean }): Promise; /** - * Will copy working copies matching the provided resource and children - * to the target using the associated file service for that resource. + * Will move working copies matching the provided resources and corresponding children + * to the target resources using the associated file service for those resources. * * Working copy owners can listen to the `onWillRunWorkingCopyFileOperation` and * `onDidRunWorkingCopyFileOperation` events to participate. */ - copy(source: URI, target: URI, overwrite?: boolean): Promise; + move(files: Required[], options?: { overwrite?: boolean }): Promise; /** - * Will delete working copies matching the provided resource and children - * using the associated file service for that resource. + * Will copy working copies matching the provided resources and corresponding children + * to the target resources using the associated file service for those resources. * * Working copy owners can listen to the `onWillRunWorkingCopyFileOperation` and * `onDidRunWorkingCopyFileOperation` events to participate. */ - delete(resource: URI, options?: { useTrash?: boolean, recursive?: boolean }): Promise; + copy(files: Required[], options?: { overwrite?: boolean }): Promise; + + /** + * Will delete working copies matching the provided resources and children + * using the associated file service for those resources. + * + * Working copy owners can listen to the `onWillRunWorkingCopyFileOperation` and + * `onDidRunWorkingCopyFileOperation` events to participate. + */ + delete(resources: URI[], options?: { useTrash?: boolean, recursive?: boolean }): Promise; //#endregion + //#region Path related /** @@ -169,7 +183,7 @@ export interface IWorkingCopyFileService { export class WorkingCopyFileService extends Disposable implements IWorkingCopyFileService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; //#region Events @@ -189,7 +203,8 @@ export class WorkingCopyFileService extends Disposable implements IWorkingCopyFi constructor( @IFileService private readonly fileService: IFileService, @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService, - @IInstantiationService private readonly instantiationService: IInstantiationService + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService ) { super(); @@ -200,45 +215,36 @@ export class WorkingCopyFileService extends Disposable implements IWorkingCopyFi // only check for parents if the resource can be handled // by the file system where we then assume a folder like // path structure - return isEqualOrParent(workingCopy.resource, resource); + return this.uriIdentityService.extUri.isEqualOrParent(workingCopy.resource, resource); } - return isEqual(workingCopy.resource, resource); + return this.uriIdentityService.extUri.isEqual(workingCopy.resource, resource); }); }); } - async move(source: URI, target: URI, overwrite?: boolean): Promise { - return this.moveOrCopy(source, target, true, overwrite); - } - async copy(source: URI, target: URI, overwrite?: boolean): Promise { - return this.moveOrCopy(source, target, false, overwrite); - } + //#region File operations - private async moveOrCopy(source: URI, target: URI, move: boolean, overwrite?: boolean): Promise { + async create(resource: URI, contents?: VSBuffer | VSBufferReadable | VSBufferReadableStream, options?: { overwrite?: boolean }): Promise { + + // validate create operation before starting + const validateCreate = await this.fileService.canCreateFile(resource, options); + if (validateCreate instanceof Error) { + throw validateCreate; + } // file operation participant - await this.runFileOperationParticipants(target, source, move ? FileOperation.MOVE : FileOperation.COPY); + await this.runFileOperationParticipants([{ target: resource }], FileOperation.CREATE); - // before event - const event = { correlationId: this.correlationIds++, operation: move ? FileOperation.MOVE : FileOperation.COPY, target, source }; + // before events + const event = { correlationId: this.correlationIds++, operation: FileOperation.CREATE, files: [{ target: resource }] }; await this._onWillRunWorkingCopyFileOperation.fireAsync(event, CancellationToken.None); - // handle dirty working copies depending on the operation: - // - move: revert both source and target (if any) - // - copy: revert target (if any) - const dirtyWorkingCopies = (move ? [...this.getDirty(source), ...this.getDirty(target)] : this.getDirty(target)); - await Promise.all(dirtyWorkingCopies.map(dirtyWorkingCopy => dirtyWorkingCopy.revert({ soft: true }))); - - // now we can rename the source to target via file operation + // now actually create on disk let stat: IFileStatWithMetadata; try { - if (move) { - stat = await this.fileService.move(source, target, overwrite); - } else { - stat = await this.fileService.copy(source, target, overwrite); - } + stat = await this.fileService.createFile(resource, contents, { overwrite: options?.overwrite }); } catch (error) { // error event @@ -253,24 +259,97 @@ export class WorkingCopyFileService extends Disposable implements IWorkingCopyFi return stat; } - async delete(resource: URI, options?: { useTrash?: boolean, recursive?: boolean }): Promise { + async move(files: Required[], options?: { overwrite?: boolean }): Promise { + return this.doMoveOrCopy(files, true, options); + } + + async copy(files: Required[], options?: { overwrite?: boolean }): Promise { + return this.doMoveOrCopy(files, false, options); + } + + private async doMoveOrCopy(files: Required[], move: boolean, options?: { overwrite?: boolean }): Promise { + const overwrite = options?.overwrite; + const stats: IFileStatWithMetadata[] = []; + + // validate move/copy operation before starting + for (const { source, target } of files) { + const validateMoveOrCopy = await (move ? this.fileService.canMove(source, target, overwrite) : this.fileService.canCopy(source, target, overwrite)); + if (validateMoveOrCopy instanceof Error) { + throw validateMoveOrCopy; + } + } // file operation participant - await this.runFileOperationParticipants(resource, undefined, FileOperation.DELETE); + await this.runFileOperationParticipants(files, move ? FileOperation.MOVE : FileOperation.COPY); - // before events - const event = { correlationId: this.correlationIds++, operation: FileOperation.DELETE, target: resource }; + // before event + const event = { correlationId: this.correlationIds++, operation: move ? FileOperation.MOVE : FileOperation.COPY, files }; await this._onWillRunWorkingCopyFileOperation.fireAsync(event, CancellationToken.None); - // Check for any existing dirty working copies for the resource + try { + for (const { source, target } of files) { + + // if source and target are not equal, handle dirty working copies + // depending on the operation: + // - move: revert both source and target (if any) + // - copy: revert target (if any) + if (!this.uriIdentityService.extUri.isEqual(source, target)) { + const dirtyWorkingCopies = (move ? [...this.getDirty(source), ...this.getDirty(target)] : this.getDirty(target)); + await Promise.all(dirtyWorkingCopies.map(dirtyWorkingCopy => dirtyWorkingCopy.revert({ soft: true }))); + } + + // now we can rename the source to target via file operation + if (move) { + stats.push(await this.fileService.move(source, target, overwrite)); + } else { + stats.push(await this.fileService.copy(source, target, overwrite)); + } + } + } catch (error) { + + // error event + await this._onDidFailWorkingCopyFileOperation.fireAsync(event, CancellationToken.None); + + throw error; + } + + // after event + await this._onDidRunWorkingCopyFileOperation.fireAsync(event, CancellationToken.None); + + return stats; + } + + async delete(resources: URI[], options?: { useTrash?: boolean, recursive?: boolean }): Promise { + + // validate delete operation before starting + for (const resource of resources) { + const validateDelete = await this.fileService.canDelete(resource, options); + if (validateDelete instanceof Error) { + throw validateDelete; + } + } + + // file operation participant + const files = resources.map(target => ({ target })); + await this.runFileOperationParticipants(files, FileOperation.DELETE); + + // before events + const event = { correlationId: this.correlationIds++, operation: FileOperation.DELETE, files }; + await this._onWillRunWorkingCopyFileOperation.fireAsync(event, CancellationToken.None); + + // check for any existing dirty working copies for the resource // and do a soft revert before deleting to be able to close // any opened editor with these working copies - const dirtyWorkingCopies = this.getDirty(resource); - await Promise.all(dirtyWorkingCopies.map(dirtyWorkingCopy => dirtyWorkingCopy.revert({ soft: true }))); + for (const resource of resources) { + const dirtyWorkingCopies = this.getDirty(resource); + await Promise.all(dirtyWorkingCopies.map(dirtyWorkingCopy => dirtyWorkingCopy.revert({ soft: true }))); + } - // Now actually delete from disk + // now actually delete from disk try { - await this.fileService.del(resource, options); + for (const resource of resources) { + await this.fileService.del(resource, options); + } } catch (error) { // error event @@ -283,6 +362,8 @@ export class WorkingCopyFileService extends Disposable implements IWorkingCopyFi await this._onDidRunWorkingCopyFileOperation.fireAsync(event, CancellationToken.None); } + //#endregion + //#region File operation participants @@ -292,8 +373,8 @@ export class WorkingCopyFileService extends Disposable implements IWorkingCopyFi return this.fileOperationParticipants.addFileOperationParticipant(participant); } - runFileOperationParticipants(target: URI, source: URI | undefined, operation: FileOperation): Promise { - return this.fileOperationParticipants.participate(target, source, operation); + private runFileOperationParticipants(files: SourceTargetPair[], operation: FileOperation): Promise { + return this.fileOperationParticipants.participate(files, operation); } //#endregion diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts index c63fce817a9..ba56c701fec 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts @@ -117,7 +117,7 @@ export const IWorkingCopyService = createDecorator('working export interface IWorkingCopyService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; //#region Events @@ -163,7 +163,7 @@ export interface IWorkingCopyService { export class WorkingCopyService extends Disposable implements IWorkingCopyService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; //#region Events @@ -191,7 +191,7 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic registerWorkingCopy(workingCopy: IWorkingCopy): IDisposable { if (this.mapResourceToWorkingCopy.has(workingCopy.resource)) { - throw new Error(`Cannot register more than one working copy with the same resource ${workingCopy.resource.toString()}.`); + throw new Error(`Cannot register more than one working copy with the same resource ${workingCopy.resource.toString(true)}.`); } const disposables = new DisposableStore(); diff --git a/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts b/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts index d533349c9ae..a9178e8c36b 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts @@ -8,15 +8,15 @@ import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textF import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { toResource } from 'vs/base/test/common/utils'; -import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestServiceAccessor, TestTextFileEditorModelManager } from 'vs/workbench/test/browser/workbenchTestServices'; import { URI } from 'vs/base/common/uri'; import { FileOperation } from 'vs/platform/files/common/files'; import { TestWorkingCopy } from 'vs/workbench/services/workingCopy/test/common/workingCopyService.test'; +import { VSBuffer } from 'vs/base/common/buffer'; suite('WorkingCopyFileService', () => { let instantiationService: IInstantiationService; - let model: TextFileEditorModel; let accessor: TestServiceAccessor; setup(() => { @@ -25,144 +25,120 @@ suite('WorkingCopyFileService', () => { }); teardown(() => { - model?.dispose(); (accessor.textFileService.files).dispose(); }); + test('create - dirty file', async function () { + await testCreate(toResource.call(this, '/path/file.txt'), VSBuffer.fromString('Hello World')); + }); + test('delete - dirty file', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); - (accessor.textFileService.files).add(model.resource, model); + await testDelete([toResource.call(this, '/path/file.txt')]); + }); - await model.load(); - model!.textEditorModel!.setValue('foo'); - assert.ok(accessor.workingCopyService.isDirty(model.resource)); - - let eventCounter = 0; - let correlationId: number | undefined = undefined; - - const participant = accessor.workingCopyFileService.addFileOperationParticipant({ - participate: async (target, source, operation) => { - assert.equal(target.toString(), model.resource.toString()); - assert.equal(operation, FileOperation.DELETE); - eventCounter++; - } - }); - - const listener1 = accessor.workingCopyFileService.onWillRunWorkingCopyFileOperation(e => { - assert.equal(e.target.toString(), model.resource.toString()); - assert.equal(e.operation, FileOperation.DELETE); - correlationId = e.correlationId; - eventCounter++; - }); - - const listener2 = accessor.workingCopyFileService.onDidRunWorkingCopyFileOperation(e => { - assert.equal(e.target.toString(), model.resource.toString()); - assert.equal(e.operation, FileOperation.DELETE); - assert.equal(e.correlationId, correlationId); - eventCounter++; - }); - - await accessor.workingCopyFileService.delete(model.resource); - assert.ok(!accessor.workingCopyService.isDirty(model.resource)); - - assert.equal(eventCounter, 3); - - participant.dispose(); - listener1.dispose(); - listener2.dispose(); + test('delete multiple - dirty files', async function () { + await testDelete([ + toResource.call(this, '/path/file1.txt'), + toResource.call(this, '/path/file2.txt'), + toResource.call(this, '/path/file3.txt'), + toResource.call(this, '/path/file4.txt')]); }); test('move - dirty file', async function () { - await testMoveOrCopy(toResource.call(this, '/path/file.txt'), toResource.call(this, '/path/file_target.txt'), true); + await testMoveOrCopy([{ source: toResource.call(this, '/path/file.txt'), target: toResource.call(this, '/path/file_target.txt') }], true); + }); + + test('move - source identical to target', async function () { + let sourceModel: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); + (accessor.textFileService.files).add(sourceModel.resource, sourceModel); + + const eventCounter = await testEventsMoveOrCopy([{ source: sourceModel.resource, target: sourceModel.resource }], true); + + sourceModel.dispose(); + assert.equal(eventCounter, 3); + }); + + test('move - one source == target and another source != target', async function () { + let sourceModel1: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file1.txt'), 'utf8', undefined); + let sourceModel2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file2.txt'), 'utf8', undefined); + let targetModel2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_target2.txt'), 'utf8', undefined); + (accessor.textFileService.files).add(sourceModel1.resource, sourceModel1); + (accessor.textFileService.files).add(sourceModel2.resource, sourceModel2); + (accessor.textFileService.files).add(targetModel2.resource, targetModel2); + + const eventCounter = await testEventsMoveOrCopy([ + { source: sourceModel1.resource, target: sourceModel1.resource }, + { source: sourceModel2.resource, target: targetModel2.resource } + ], true); + + sourceModel1.dispose(); + sourceModel2.dispose(); + targetModel2.dispose(); + assert.equal(eventCounter, 3); + }); + + test('move multiple - dirty file', async function () { + await testMoveOrCopy([ + { source: toResource.call(this, '/path/file1.txt'), target: toResource.call(this, '/path/file1_target.txt') }, + { source: toResource.call(this, '/path/file2.txt'), target: toResource.call(this, '/path/file2_target.txt') }], + true); }); test('move - dirty file (target exists and is dirty)', async function () { - await testMoveOrCopy(toResource.call(this, '/path/file.txt'), toResource.call(this, '/path/file_target.txt'), true, true); + await testMoveOrCopy([{ source: toResource.call(this, '/path/file.txt'), target: toResource.call(this, '/path/file_target.txt') }], true, true); }); test('copy - dirty file', async function () { - await testMoveOrCopy(toResource.call(this, '/path/file.txt'), toResource.call(this, '/path/file_target.txt'), false); + await testMoveOrCopy([{ source: toResource.call(this, '/path/file.txt'), target: toResource.call(this, '/path/file_target.txt') }], false); + }); + + test('copy - source identical to target', async function () { + let sourceModel: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); + (accessor.textFileService.files).add(sourceModel.resource, sourceModel); + + const eventCounter = await testEventsMoveOrCopy([{ source: sourceModel.resource, target: sourceModel.resource }]); + + sourceModel.dispose(); + assert.equal(eventCounter, 3); + }); + + test('copy - one source == target and another source != target', async function () { + let sourceModel1: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file1.txt'), 'utf8', undefined); + let sourceModel2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file2.txt'), 'utf8', undefined); + let targetModel2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_target2.txt'), 'utf8', undefined); + (accessor.textFileService.files).add(sourceModel1.resource, sourceModel1); + (accessor.textFileService.files).add(sourceModel2.resource, sourceModel2); + (accessor.textFileService.files).add(targetModel2.resource, targetModel2); + + const eventCounter = await testEventsMoveOrCopy([ + { source: sourceModel1.resource, target: sourceModel1.resource }, + { source: sourceModel2.resource, target: targetModel2.resource } + ]); + + sourceModel1.dispose(); + sourceModel2.dispose(); + targetModel2.dispose(); + assert.equal(eventCounter, 3); + }); + + test('copy multiple - dirty file', async function () { + await testMoveOrCopy([ + { source: toResource.call(this, '/path/file1.txt'), target: toResource.call(this, '/path/file_target1.txt') }, + { source: toResource.call(this, '/path/file2.txt'), target: toResource.call(this, '/path/file_target2.txt') }, + { source: toResource.call(this, '/path/file3.txt'), target: toResource.call(this, '/path/file_target3.txt') }], + false); }); test('copy - dirty file (target exists and is dirty)', async function () { - await testMoveOrCopy(toResource.call(this, '/path/file.txt'), toResource.call(this, '/path/file_target.txt'), false, true); + await testMoveOrCopy([{ source: toResource.call(this, '/path/file.txt'), target: toResource.call(this, '/path/file_target.txt') }], false, true); }); - async function testMoveOrCopy(source: URI, target: URI, move: boolean, targetDirty?: boolean): Promise { - let sourceModel: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, source, 'utf8', undefined); - let targetModel: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, target, 'utf8', undefined); - (accessor.textFileService.files).add(sourceModel.resource, sourceModel); - (accessor.textFileService.files).add(targetModel.resource, targetModel); - - await sourceModel.load(); - sourceModel.textEditorModel!.setValue('foo'); - assert.ok(accessor.textFileService.isDirty(sourceModel.resource)); - - if (targetDirty) { - await targetModel.load(); - targetModel.textEditorModel!.setValue('bar'); - assert.ok(accessor.textFileService.isDirty(targetModel.resource)); - } - - let eventCounter = 0; - let correlationId: number | undefined = undefined; - - const participant = accessor.workingCopyFileService.addFileOperationParticipant({ - participate: async (target, source, operation) => { - assert.equal(target.toString(), targetModel.resource.toString()); - assert.equal(source?.toString(), sourceModel.resource.toString()); - assert.equal(operation, move ? FileOperation.MOVE : FileOperation.COPY); - eventCounter++; - } - }); - - const listener1 = accessor.workingCopyFileService.onWillRunWorkingCopyFileOperation(e => { - assert.equal(e.target.toString(), targetModel.resource.toString()); - assert.equal(e.source?.toString(), sourceModel.resource.toString()); - assert.equal(e.operation, move ? FileOperation.MOVE : FileOperation.COPY); - eventCounter++; - correlationId = e.correlationId; - }); - - const listener2 = accessor.workingCopyFileService.onDidRunWorkingCopyFileOperation(e => { - assert.equal(e.target.toString(), targetModel.resource.toString()); - assert.equal(e.source?.toString(), sourceModel.resource.toString()); - assert.equal(e.operation, move ? FileOperation.MOVE : FileOperation.COPY); - eventCounter++; - assert.equal(e.correlationId, correlationId); - }); - - if (move) { - await accessor.workingCopyFileService.move(sourceModel.resource, targetModel.resource, true); - } else { - await accessor.workingCopyFileService.copy(sourceModel.resource, targetModel.resource, true); - } - - assert.equal(targetModel.textEditorModel!.getValue(), 'foo'); - - if (move) { - assert.ok(!accessor.textFileService.isDirty(sourceModel.resource)); - } else { - assert.ok(accessor.textFileService.isDirty(sourceModel.resource)); - } - assert.ok(accessor.textFileService.isDirty(targetModel.resource)); - - assert.equal(eventCounter, 3); - - sourceModel.dispose(); - targetModel.dispose(); - - participant.dispose(); - listener1.dispose(); - listener2.dispose(); - } - test('getDirty', async function () { const model1 = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file-1.txt'), 'utf8', undefined); - (accessor.textFileService.files).add(model.resource, model); + (accessor.textFileService.files).add(model1.resource, model1); const model2 = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file-2.txt'), 'utf8', undefined); - (accessor.textFileService.files).add(model.resource, model); + (accessor.textFileService.files).add(model2.resource, model2); let dirty = accessor.workingCopyFileService.getDirty(model1.resource); assert.equal(dirty.length, 0); @@ -190,7 +166,7 @@ suite('WorkingCopyFileService', () => { test('registerWorkingCopyProvider', async function () { const model1 = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file-1.txt'), 'utf8', undefined); - (accessor.textFileService.files).add(model.resource, model); + (accessor.textFileService.files).add(model1.resource, model1); await model1.load(); model1.textEditorModel!.setValue('foo'); @@ -212,4 +188,241 @@ suite('WorkingCopyFileService', () => { model1.dispose(); }); + + async function testEventsMoveOrCopy(files: { source: URI, target: URI }[], move?: boolean): Promise { + let eventCounter = 0; + + const participant = accessor.workingCopyFileService.addFileOperationParticipant({ + participate: async files => { + eventCounter++; + } + }); + + const listener1 = accessor.workingCopyFileService.onWillRunWorkingCopyFileOperation(e => { + eventCounter++; + }); + + const listener2 = accessor.workingCopyFileService.onDidRunWorkingCopyFileOperation(e => { + eventCounter++; + }); + + if (move) { + await accessor.workingCopyFileService.move(files, { overwrite: true }); + } else { + await accessor.workingCopyFileService.copy(files, { overwrite: true }); + } + + participant.dispose(); + listener1.dispose(); + listener2.dispose(); + return eventCounter; + } + + async function testMoveOrCopy(files: { source: URI, target: URI }[], move: boolean, targetDirty?: boolean): Promise { + + let eventCounter = 0; + const models = await Promise.all(files.map(async ({ source, target }, i) => { + let sourceModel: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, source, 'utf8', undefined); + let targetModel: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, target, 'utf8', undefined); + (accessor.textFileService.files).add(sourceModel.resource, sourceModel); + (accessor.textFileService.files).add(targetModel.resource, targetModel); + + await sourceModel.load(); + sourceModel.textEditorModel!.setValue('foo' + i); + assert.ok(accessor.textFileService.isDirty(sourceModel.resource)); + if (targetDirty) { + await targetModel.load(); + targetModel.textEditorModel!.setValue('bar' + i); + assert.ok(accessor.textFileService.isDirty(targetModel.resource)); + } + + return { sourceModel, targetModel }; + })); + + const participant = accessor.workingCopyFileService.addFileOperationParticipant({ + participate: async (files, operation) => { + for (let i = 0; i < files.length; i++) { + const { target, source } = files[i]; + const { targetModel, sourceModel } = models[i]; + + assert.equal(target.toString(), targetModel.resource.toString()); + assert.equal(source?.toString(), sourceModel.resource.toString()); + } + + eventCounter++; + + assert.equal(operation, move ? FileOperation.MOVE : FileOperation.COPY); + } + }); + + let correlationId: number; + + const listener1 = accessor.workingCopyFileService.onWillRunWorkingCopyFileOperation(e => { + for (let i = 0; i < e.files.length; i++) { + const { target, source } = files[i]; + const { targetModel, sourceModel } = models[i]; + + assert.equal(target.toString(), targetModel.resource.toString()); + assert.equal(source?.toString(), sourceModel.resource.toString()); + } + + eventCounter++; + + correlationId = e.correlationId; + assert.equal(e.operation, move ? FileOperation.MOVE : FileOperation.COPY); + }); + + const listener2 = accessor.workingCopyFileService.onDidRunWorkingCopyFileOperation(e => { + for (let i = 0; i < e.files.length; i++) { + const { target, source } = files[i]; + const { targetModel, sourceModel } = models[i]; + assert.equal(target.toString(), targetModel.resource.toString()); + assert.equal(source?.toString(), sourceModel.resource.toString()); + } + + eventCounter++; + + assert.equal(e.operation, move ? FileOperation.MOVE : FileOperation.COPY); + assert.equal(e.correlationId, correlationId); + }); + + if (move) { + await accessor.workingCopyFileService.move(models.map(model => ({ source: model.sourceModel.resource, target: model.targetModel.resource })), { overwrite: true }); + } else { + await accessor.workingCopyFileService.copy(models.map(model => ({ source: model.sourceModel.resource, target: model.targetModel.resource })), { overwrite: true }); + } + + for (let i = 0; i < models.length; i++) { + const { sourceModel, targetModel } = models[i]; + + assert.equal(targetModel.textEditorModel!.getValue(), 'foo' + i); + + if (move) { + assert.ok(!accessor.textFileService.isDirty(sourceModel.resource)); + } else { + assert.ok(accessor.textFileService.isDirty(sourceModel.resource)); + } + assert.ok(accessor.textFileService.isDirty(targetModel.resource)); + + sourceModel.dispose(); + targetModel.dispose(); + } + assert.equal(eventCounter, 3); + + participant.dispose(); + listener1.dispose(); + listener2.dispose(); + } + + async function testDelete(resources: URI[]) { + + const models = await Promise.all(resources.map(async resource => { + const model = instantiationService.createInstance(TextFileEditorModel, resource, 'utf8', undefined); + (accessor.textFileService.files).add(model.resource, model); + + await model.load(); + model!.textEditorModel!.setValue('foo'); + assert.ok(accessor.workingCopyService.isDirty(model.resource)); + return model; + })); + + let eventCounter = 0; + let correlationId: number | undefined = undefined; + + const participant = accessor.workingCopyFileService.addFileOperationParticipant({ + participate: async (files, operation) => { + for (let i = 0; i < models.length; i++) { + const model = models[i]; + const file = files[i]; + assert.equal(file.target.toString(), model.resource.toString()); + } + assert.equal(operation, FileOperation.DELETE); + eventCounter++; + } + }); + + const listener1 = accessor.workingCopyFileService.onWillRunWorkingCopyFileOperation(e => { + for (let i = 0; i < models.length; i++) { + const model = models[i]; + const file = e.files[i]; + assert.equal(file.target.toString(), model.resource.toString()); + } + assert.equal(e.operation, FileOperation.DELETE); + correlationId = e.correlationId; + eventCounter++; + }); + + const listener2 = accessor.workingCopyFileService.onDidRunWorkingCopyFileOperation(e => { + for (let i = 0; i < models.length; i++) { + const model = models[i]; + const file = e.files[i]; + assert.equal(file.target.toString(), model.resource.toString()); + } + assert.equal(e.operation, FileOperation.DELETE); + assert.equal(e.correlationId, correlationId); + eventCounter++; + }); + + await accessor.workingCopyFileService.delete(models.map(m => m.resource)); + for (const model of models) { + assert.ok(!accessor.workingCopyService.isDirty(model.resource)); + model.dispose(); + } + + assert.equal(eventCounter, 3); + + participant.dispose(); + listener1.dispose(); + listener2.dispose(); + } + + async function testCreate(resource: URI, contents: VSBuffer) { + const model = instantiationService.createInstance(TextFileEditorModel, resource, 'utf8', undefined); + (accessor.textFileService.files).add(model.resource, model); + + await model.load(); + model!.textEditorModel!.setValue('foo'); + assert.ok(accessor.workingCopyService.isDirty(model.resource)); + + let eventCounter = 0; + let correlationId: number | undefined = undefined; + + const participant = accessor.workingCopyFileService.addFileOperationParticipant({ + participate: async (files, operation) => { + assert.equal(files.length, 1); + const file = files[0]; + assert.equal(file.target.toString(), model.resource.toString()); + assert.equal(operation, FileOperation.CREATE); + eventCounter++; + } + }); + + const listener1 = accessor.workingCopyFileService.onWillRunWorkingCopyFileOperation(e => { + assert.equal(e.files.length, 1); + const file = e.files[0]; + assert.equal(file.target.toString(), model.resource.toString()); + assert.equal(e.operation, FileOperation.CREATE); + correlationId = e.correlationId; + eventCounter++; + }); + + const listener2 = accessor.workingCopyFileService.onDidRunWorkingCopyFileOperation(e => { + assert.equal(e.files.length, 1); + const file = e.files[0]; + assert.equal(file.target.toString(), model.resource.toString()); + assert.equal(e.operation, FileOperation.CREATE); + assert.equal(e.correlationId, correlationId); + eventCounter++; + }); + + await accessor.workingCopyFileService.create(resource, contents); + assert.ok(!accessor.workingCopyService.isDirty(model.resource)); + model.dispose(); + + assert.equal(eventCounter, 3); + + participant.dispose(); + listener1.dispose(); + listener2.dispose(); + } }); diff --git a/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts b/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts index c48f999da64..067eae83070 100644 --- a/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts @@ -8,13 +8,13 @@ import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IJSONEditingService, JSONEditingError, JSONEditingErrorCode } from 'vs/workbench/services/configuration/common/jsonEditing'; -import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, IWorkspacesService, rewriteWorkspaceFileForNewLocation, WORKSPACE_FILTER, IEnterWorkspaceResult, hasWorkspaceFileExtension, WORKSPACE_EXTENSION, isUntitledWorkspace } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, IWorkspacesService, rewriteWorkspaceFileForNewLocation, WORKSPACE_FILTER, IEnterWorkspaceResult, hasWorkspaceFileExtension, WORKSPACE_EXTENSION, isUntitledWorkspace, IStoredWorkspace } from 'vs/platform/workspaces/common/workspaces'; import { WorkspaceService } from 'vs/workbench/services/configuration/browser/configurationService'; import { ConfigurationScope, IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { distinct } from 'vs/base/common/arrays'; -import { isEqual, getComparisonKey } from 'vs/base/common/resources'; +import { isEqualAuthority, extUri } from 'vs/base/common/resources'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IFileService } from 'vs/platform/files/common/files'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -24,14 +24,16 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { Schemas } from 'vs/base/common/network'; +import { SaveReason } from 'vs/workbench/common/editor'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditingService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @IJSONEditingService private readonly jsonEditingService: IJSONEditingService, - @IWorkspaceContextService private readonly contextService: WorkspaceService, + @IWorkspaceContextService protected readonly contextService: WorkspaceService, @IConfigurationService private readonly configurationService: IConfigurationService, @INotificationService private readonly notificationService: INotificationService, @ICommandService private readonly commandService: ICommandService, @@ -41,7 +43,8 @@ export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditi @IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService, @IFileDialogService private readonly fileDialogService: IFileDialogService, @IDialogService protected readonly dialogService: IDialogService, - @IHostService protected readonly hostService: IHostService + @IHostService protected readonly hostService: IHostService, + @IUriIdentityService protected readonly uriIdentityService: IUriIdentityService ) { } async pickNewWorkspacePath(): Promise { @@ -131,7 +134,7 @@ export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditi const remoteAuthority = this.environmentService.configuration.remoteAuthority; if (remoteAuthority) { // https://github.com/microsoft/vscode/issues/94191 - foldersToAdd = foldersToAdd.filter(f => f.uri.scheme !== Schemas.file && (f.uri.scheme !== Schemas.vscodeRemote || f.uri.authority === remoteAuthority)); + foldersToAdd = foldersToAdd.filter(f => f.uri.scheme !== Schemas.file && (f.uri.scheme !== Schemas.vscodeRemote || isEqualAuthority(f.uri.authority, remoteAuthority))); } // If we are in no-workspace or single-folder workspace, adding folders has to @@ -139,7 +142,7 @@ export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditi if (state !== WorkbenchState.WORKSPACE) { let newWorkspaceFolders = this.contextService.getWorkspace().folders.map(folder => ({ uri: folder.uri })); newWorkspaceFolders.splice(typeof index === 'number' ? index : newWorkspaceFolders.length, 0, ...foldersToAdd); - newWorkspaceFolders = distinct(newWorkspaceFolders, folder => getComparisonKey(folder.uri)); + newWorkspaceFolders = distinct(newWorkspaceFolders, folder => this.uriIdentityService.extUri.getComparisonKey(folder.uri)); if (state === WorkbenchState.EMPTY && newWorkspaceFolders.length === 0 || state === WorkbenchState.FOLDER && newWorkspaceFolders.length === 1) { return; // return if the operation is a no-op for the current state @@ -183,7 +186,7 @@ export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditi private includesSingleFolderWorkspace(folders: URI[]): boolean { if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { const workspaceFolder = this.contextService.getWorkspace().folders[0]; - return (folders.some(folder => isEqual(folder, workspaceFolder.uri))); + return (folders.some(folder => this.uriIdentityService.extUri.isEqual(folder, workspaceFolder.uri))); } return false; @@ -197,7 +200,11 @@ export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditi const remoteAuthority = this.environmentService.configuration.remoteAuthority; const untitledWorkspace = await this.workspacesService.createUntitledWorkspace(folders, remoteAuthority); if (path) { - await this.saveWorkspaceAs(untitledWorkspace, path); + try { + await this.saveWorkspaceAs(untitledWorkspace, path); + } finally { + await this.workspacesService.deleteUntitledWorkspace(untitledWorkspace); // https://github.com/microsoft/vscode/issues/100276 + } } else { path = untitledWorkspace.configPath; } @@ -206,12 +213,18 @@ export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditi } async saveAndEnterWorkspace(path: URI): Promise { - if (!await this.isValidTargetWorkspacePath(path)) { + const workspaceIdentifier = this.getCurrentWorkspaceIdentifier(); + if (!workspaceIdentifier) { return; } - const workspaceIdentifier = this.getCurrentWorkspaceIdentifier(); - if (!workspaceIdentifier) { + // Allow to save the workspace of the current window + if (extUri.isEqual(workspaceIdentifier.configPath, path)) { + return this.saveWorkspace(workspaceIdentifier); + } + + // From this moment on we require a valid target that is not opened already + if (!await this.isValidTargetWorkspacePath(path)) { return; } @@ -224,11 +237,11 @@ export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditi return true; // OK } - protected async saveWorkspaceAs(workspace: IWorkspaceIdentifier, targetConfigPathURI: URI): Promise { + protected async saveWorkspaceAs(workspace: IWorkspaceIdentifier, targetConfigPathURI: URI): Promise { const configPathURI = workspace.configPath; // Return early if target is same as source - if (isEqual(configPathURI, targetConfigPathURI)) { + if (this.uriIdentityService.extUri.isEqual(configPathURI, targetConfigPathURI)) { return; } @@ -240,6 +253,28 @@ export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditi await this.textFileService.create(targetConfigPathURI, newRawWorkspaceContents, { overwrite: true }); } + protected async saveWorkspace(workspace: IWorkspaceIdentifier): Promise { + const configPathURI = workspace.configPath; + + // First: try to save any existing model as it could be dirty + const existingModel = this.textFileService.files.get(configPathURI); + if (existingModel) { + await existingModel.save({ force: true, reason: SaveReason.EXPLICIT }); + return; + } + + // Second: if the file exists on disk, simply return + const workspaceFileExists = await this.fileService.exists(configPathURI); + if (workspaceFileExists) { + return; + } + + // Finally, we need to re-create the file as it was deleted + const newWorkspace: IStoredWorkspace = { folders: [] }; + const newRawWorkspaceContents = rewriteWorkspaceFileForNewLocation(JSON.stringify(newWorkspace, null, '\t'), configPathURI, false, configPathURI); + await this.textFileService.create(configPathURI, newRawWorkspaceContents); + } + private handleWorkspaceConfigurationEditingError(error: JSONEditingError): void { switch (error.code) { case JSONEditingErrorCode.ERROR_INVALID_FILE: @@ -313,7 +348,7 @@ export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditi } } - return this.jsonEditingService.write(toWorkspace.configPath, [{ key: 'settings', value: targetWorkspaceConfiguration }], true); + return this.jsonEditingService.write(toWorkspace.configPath, [{ path: ['settings'], value: targetWorkspaceConfiguration }], true); } protected getCurrentWorkspaceIdentifier(): IWorkspaceIdentifier | undefined { diff --git a/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts b/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts index b1a2011c60e..6af823c7957 100644 --- a/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts @@ -19,10 +19,11 @@ import { AbstractWorkspaceEditingService } from 'vs/workbench/services/workspace import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { URI } from 'vs/base/common/uri'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; export class BrowserWorkspaceEditingService extends AbstractWorkspaceEditingService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @IJSONEditingService jsonEditingService: IJSONEditingService, @@ -36,9 +37,10 @@ export class BrowserWorkspaceEditingService extends AbstractWorkspaceEditingServ @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @IFileDialogService fileDialogService: IFileDialogService, @IDialogService dialogService: IDialogService, - @IHostService hostService: IHostService + @IHostService hostService: IHostService, + @IUriIdentityService uriIdentityService: IUriIdentityService ) { - super(jsonEditingService, contextService, configurationService, notificationService, commandService, fileService, textFileService, workspacesService, environmentService, fileDialogService, dialogService, hostService); + super(jsonEditingService, contextService, configurationService, notificationService, commandService, fileService, textFileService, workspacesService, environmentService, fileDialogService, dialogService, hostService, uriIdentityService); } async enterWorkspace(path: URI): Promise { diff --git a/src/vs/workbench/services/workspaces/browser/workspacesService.ts b/src/vs/workbench/services/workspaces/browser/workspacesService.ts index 7bbd6581909..faf2abf6388 100644 --- a/src/vs/workbench/services/workspaces/browser/workspacesService.ts +++ b/src/vs/workbench/services/workspaces/browser/workspacesService.ts @@ -22,7 +22,7 @@ export class BrowserWorkspacesService extends Disposable implements IWorkspacesS static readonly RECENTLY_OPENED_KEY = 'recently.opened'; - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _onRecentlyOpenedChange = this._register(new Emitter()); readonly onRecentlyOpenedChange = this._onRecentlyOpenedChange.event; diff --git a/src/vs/workbench/services/workspaces/common/workspaceEditing.ts b/src/vs/workbench/services/workspaces/common/workspaceEditing.ts index 6bf3b4019e1..400fdac1944 100644 --- a/src/vs/workbench/services/workspaces/common/workspaceEditing.ts +++ b/src/vs/workbench/services/workspaces/common/workspaceEditing.ts @@ -11,7 +11,7 @@ export const IWorkspaceEditingService = createDecorator { - const saveOperation = this.saveUntitedBeforeShutdown(e.reason); + const saveOperation = this.saveUntitledBeforeShutdown(e.reason); if (saveOperation) { e.veto(saveOperation); } }); } - private async saveUntitedBeforeShutdown(reason: ShutdownReason): Promise { + private async saveUntitledBeforeShutdown(reason: ShutdownReason): Promise { if (reason !== ShutdownReason.LOAD && reason !== ShutdownReason.CLOSE) { return false; // only interested when window is closing or loading } @@ -146,7 +148,7 @@ export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingServi const windows = await this.electronService.getWindows(); // Prevent overwriting a workspace that is currently opened in another window - if (windows.some(window => !!window.workspace && isEqual(window.workspace.configPath, path))) { + if (windows.some(window => !!window.workspace && this.uriIdentityService.extUri.isEqual(window.workspace.configPath, path))) { await this.dialogService.show( Severity.Info, nls.localize('workspaceOpenedMessage', "Unable to save workspace '{0}'", basename(path)), diff --git a/src/vs/workbench/services/workspaces/electron-browser/workspacesService.ts b/src/vs/workbench/services/workspaces/electron-sandbox/workspacesService.ts similarity index 56% rename from src/vs/workbench/services/workspaces/electron-browser/workspacesService.ts rename to src/vs/workbench/services/workspaces/electron-sandbox/workspacesService.ts index 22707759bd2..93b3f0d72ed 100644 --- a/src/vs/workbench/services/workspaces/electron-browser/workspacesService.ts +++ b/src/vs/workbench/services/workspaces/electron-sandbox/workspacesService.ts @@ -4,21 +4,20 @@ *--------------------------------------------------------------------------------------------*/ import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; -import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; +import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; export class NativeWorkspacesService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @IMainProcessService mainProcessService: IMainProcessService, - @IWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService + @IElectronService electronService: IElectronService ) { - return createChannelSender(mainProcessService.getChannel('workspaces'), { context: environmentService.configuration.windowId }); + return createChannelSender(mainProcessService.getChannel('workspaces'), { context: electronService.windowId }); } } diff --git a/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts index 28a2f51f260..6a7252b386d 100644 --- a/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts @@ -30,7 +30,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { nullExtensionDescription, IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { dispose } from 'vs/base/common/lifecycle'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; -import { mock } from 'vs/workbench/test/browser/api/mock'; +import { mock } from 'vs/base/test/common/mock'; import { NullApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; @@ -47,6 +47,7 @@ import 'vs/editor/contrib/links/getLinks'; import 'vs/editor/contrib/parameterHints/provideSignatureHelp'; import 'vs/editor/contrib/smartSelect/smartSelect'; import 'vs/editor/contrib/suggest/suggest'; +import 'vs/editor/contrib/rename/rename'; const defaultSelector = { scheme: 'far' }; const model: ITextModel = createTextModel( @@ -232,6 +233,27 @@ suite('ExtHostLanguageFeatureCommands', function () { }); + // --- rename + test('vscode.executeDocumentRenameProvider', async function () { + disposables.push(extHost.registerRenameProvider(nullExtensionDescription, defaultSelector, new class implements vscode.RenameProvider { + provideRenameEdits(document: vscode.TextDocument, position: vscode.Position, newName: string) { + const edit = new types.WorkspaceEdit(); + edit.insert(document.uri, position, newName); + return edit; + } + })); + + await rpcProtocol.sync(); + + const edit = await commands.executeCommand('vscode.executeDocumentRenameProvider', model.uri, new types.Position(0, 12), 'newNameOfThis'); + + assert.ok(edit); + assert.equal(edit.has(model.uri), true); + const textEdits = edit.get(model.uri); + assert.equal(textEdits.length, 1); + assert.equal(textEdits[0].newText, 'newNameOfThis'); + }); + // --- definition test('Definition, invalid arguments', function () { diff --git a/src/vs/workbench/test/browser/api/extHostCommands.test.ts b/src/vs/workbench/test/browser/api/extHostCommands.test.ts index b20a56ba1f1..7a60a85fe87 100644 --- a/src/vs/workbench/test/browser/api/extHostCommands.test.ts +++ b/src/vs/workbench/test/browser/api/extHostCommands.test.ts @@ -8,7 +8,7 @@ import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { MainThreadCommandsShape } from 'vs/workbench/api/common/extHost.protocol'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { SingleProxyRPCProtocol } from './testRPCProtocol'; -import { mock } from 'vs/workbench/test/browser/api/mock'; +import { mock } from 'vs/base/test/common/mock'; import { NullLogService } from 'vs/platform/log/common/log'; suite('ExtHostCommands', function () { diff --git a/src/vs/workbench/test/browser/api/extHostConfiguration.test.ts b/src/vs/workbench/test/browser/api/extHostConfiguration.test.ts index 31e2b101394..a442a594c50 100644 --- a/src/vs/workbench/test/browser/api/extHostConfiguration.test.ts +++ b/src/vs/workbench/test/browser/api/extHostConfiguration.test.ts @@ -10,7 +10,7 @@ import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfigurat import { MainThreadConfigurationShape, IConfigurationInitData } from 'vs/workbench/api/common/extHost.protocol'; import { ConfigurationModel, ConfigurationModelParser } from 'vs/platform/configuration/common/configurationModels'; import { TestRPCProtocol } from './testRPCProtocol'; -import { mock } from 'vs/workbench/test/browser/api/mock'; +import { mock } from 'vs/base/test/common/mock'; import { IWorkspaceFolder, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ConfigurationTarget, IConfigurationModel, IConfigurationChange } from 'vs/platform/configuration/common/configuration'; import { NullLogService } from 'vs/platform/log/common/log'; diff --git a/src/vs/workbench/test/browser/api/extHostDecorations.test.ts b/src/vs/workbench/test/browser/api/extHostDecorations.test.ts new file mode 100644 index 00000000000..073006e636d --- /dev/null +++ b/src/vs/workbench/test/browser/api/extHostDecorations.test.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 assert from 'assert'; +import { timeout } from 'vs/base/common/async'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Event } from 'vs/base/common/event'; +import { URI } from 'vs/base/common/uri'; +import { mock } from 'vs/base/test/common/mock'; +import { NullLogService } from 'vs/platform/log/common/log'; +import { MainThreadDecorationsShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostDecorations } from 'vs/workbench/api/common/extHostDecorations'; +import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; +import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; + +suite('ExtHostDecorations', function () { + + let mainThreadShape: MainThreadDecorationsShape; + let extHostDecorations: ExtHostDecorations; + let providers = new Set(); + + setup(function () { + + providers.clear(); + + mainThreadShape = new class extends mock() { + $registerDecorationProvider(handle: number) { + providers.add(handle); + } + }; + + extHostDecorations = new ExtHostDecorations( + new class extends mock() { + getProxy(): any { + return mainThreadShape; + } + }, + new NullLogService() + ); + }); + + test('SCM Decorations missing #100524', async function () { + + let calledA = false; + let calledB = false; + + // never returns + extHostDecorations.registerDecorationProvider({ + onDidChangeDecorations: Event.None, + provideDecoration() { + calledA = true; + return new Promise(() => { }); + } + }, nullExtensionDescription.identifier); + + // always returns + extHostDecorations.registerDecorationProvider({ + onDidChangeDecorations: Event.None, + provideDecoration() { + calledB = true; + return new Promise(resolve => resolve({ letter: 'H', title: 'Hello' })); + } + }, nullExtensionDescription.identifier); + + + const requests = [...providers.values()].map((handle, idx) => { + return extHostDecorations.$provideDecorations(handle, [{ id: idx, uri: URI.parse('test:///file') }], CancellationToken.None); + }); + + assert.equal(calledA, true); + assert.equal(calledB, true); + + assert.equal(requests.length, 2); + const [first, second] = requests; + + const firstResult = await Promise.race([first, timeout(30).then(() => false)]); + assert.equal(typeof firstResult, 'boolean'); // never finishes... + + const secondResult = await Promise.race([second, timeout(30).then(() => false)]); + assert.equal(typeof secondResult, 'object'); + }); + +}); diff --git a/src/vs/workbench/test/browser/api/extHostDiagnostics.test.ts b/src/vs/workbench/test/browser/api/extHostDiagnostics.test.ts index b4050bd6f27..db7351cfcb7 100644 --- a/src/vs/workbench/test/browser/api/extHostDiagnostics.test.ts +++ b/src/vs/workbench/test/browser/api/extHostDiagnostics.test.ts @@ -9,7 +9,7 @@ import { DiagnosticCollection, ExtHostDiagnostics } from 'vs/workbench/api/commo import { Diagnostic, DiagnosticSeverity, Range, DiagnosticRelatedInformation, Location } from 'vs/workbench/api/common/extHostTypes'; import { MainThreadDiagnosticsShape, IMainContext } from 'vs/workbench/api/common/extHost.protocol'; import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; -import { mock } from 'vs/workbench/test/browser/api/mock'; +import { mock } from 'vs/base/test/common/mock'; import { Emitter, Event } from 'vs/base/common/event'; import { NullLogService } from 'vs/platform/log/common/log'; import type * as vscode from 'vscode'; @@ -389,6 +389,9 @@ suite('ExtHostDiagnostics', () => { assertRegistered(): void { } + drain() { + return undefined!; + } }, new NullLogService()); let collection1 = diags.createDiagnosticCollection(nullExtensionDescription.identifier, 'foo'); @@ -438,6 +441,9 @@ suite('ExtHostDiagnostics', () => { assertRegistered(): void { } + drain() { + return undefined!; + } }, new NullLogService()); diff --git a/src/vs/workbench/test/browser/api/extHostDocumentData.test.perf-data.ts b/src/vs/workbench/test/browser/api/extHostDocumentData.test.perf-data.ts new file mode 100644 index 00000000000..833bca4b2e5 --- /dev/null +++ b/src/vs/workbench/test/browser/api/extHostDocumentData.test.perf-data.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const _$_$_expensive = '{"seq":0,"type":"response","command":"completionInfo","request_seq":956,"success":true,"body":{"isGlobalCompletion":true,"isMemberCompletion":false,"isNewIdentifierLocation":false,"entries":[{"name":"__dirname","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"__filename","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"_getInstrumentationKey","kind":"method","kindModifiers":"private,static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Config"},{"name":"_util","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/instantiation"},{"name":"$","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"abort","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"AbortController","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AbortSignal","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AbstractCaseAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"AbstractCodeEditorService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/services/abstractCodeEditorService"},{"name":"AbstractCommandsQuickAccessProvider","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/browser/commandsQuickAccess"},{"name":"AbstractConfigureRecommendedExtensionsAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"AbstractContextKeyService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/browser/contextKeyService"},{"name":"AbstractDebugAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"AbstractDebugAdapter","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/abstractDebugAdapter"},{"name":"AbstractDeleteAllToBoundaryAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"AbstractEditorCommandsQuickAccessProvider","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/quickAccess/commandsQuickAccess"},{"name":"AbstractEditorNavigationQuickAccessProvider","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/quickAccess/editorNavigationQuickAccess"},{"name":"AbstractExpressionsRenderer","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/baseDebugView"},{"name":"AbstractExtensionService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/abstractExtensionService"},{"name":"AbstractExtHostExtensionService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostExtensionService"},{"name":"AbstractExtHostOutputChannel","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostOutput"},{"name":"AbstractFileDialogService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/browser/abstractFileDialogService"},{"name":"AbstractFileOutputChannelModel","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/output/common/outputChannelModel"},{"name":"AbstractFileSynchroniser","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/abstractSynchronizer"},{"name":"AbstractGotoLineQuickAccessProvider","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/quickAccess/gotoLineQuickAccess"},{"name":"AbstractGotoSymbolQuickAccessProvider","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess"},{"name":"AbstractJsonFileSynchroniser","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/abstractSynchronizer"},{"name":"AbstractKeybindingService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/abstractKeybindingService"},{"name":"AbstractLifecycleService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/common/lifecycleService"},{"name":"AbstractLineHighlightOverlay","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight"},{"name":"AbstractLogService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"AbstractPathService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/path/common/pathService"},{"name":"AbstractProblemCollector","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemCollectors"},{"name":"AbstractProcess","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/processes"},{"name":"AbstractRange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AbstractRemoteAgentService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/abstractRemoteAgentService"},{"name":"AbstractScrollableElement","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/scrollableElement"},{"name":"AbstractScrollbar","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/abstractScrollbar"},{"name":"AbstractSearchAndReplaceAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"AbstractSettingRenderer","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"AbstractSettingsModel","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"AbstractShowReleaseNotesAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/browser/update"},{"name":"AbstractSortLinesAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"AbstractSynchroniser","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/abstractSynchronizer"},{"name":"AbstractTaskService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/abstractTaskService"},{"name":"AbstractTelemetryOptOut","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut"},{"name":"AbstractTextFileService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/browser/textFileService"},{"name":"AbstractTextMateService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/browser/abstractTextMateService"},{"name":"AbstractTextResourceEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/textResourceEditor"},{"name":"AbstractTree","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/abstractTree"},{"name":"AbstractTunnelService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/tunnelService"},{"name":"AbstractUpdateService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/electron-main/abstractUpdateService"},{"name":"AbstractURLService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/url/common/urlService"},{"name":"AbstractVariableResolverService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configurationResolver/common/variableResolver"},{"name":"AbstractWorkspaceEditingService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService"},{"name":"Accelerator","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"acceptLocalChangesCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler"},{"name":"access","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"AccessibilityHelpNLS","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standaloneStrings"},{"name":"AccessibilityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesTree"},{"name":"AccessibilityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree"},{"name":"AccessibilityService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/accessibility/common/accessibilityService"},{"name":"AccessibilitySupport","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/accessibility/common/accessibility"},{"name":"AccessibilitySupport","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"accessSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"AccountsActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/activitybar/activitybarActions"},{"name":"ACL_IDENTITY","kind":"property","kindModifiers":"private,static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"ACLED_DIRECTORIES","kind":"property","kindModifiers":"private,static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"Action","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/actions"},{"name":"Action2","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"ActionBar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/actionbar/actionbar"},{"name":"ActionRunner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/actions"},{"name":"actions","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"vscode-nsfw"},{"name":"ActionsOrientation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/actionbar/actionbar"},{"name":"ActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/actionbar/actionbar"},{"name":"ActivatedExtension","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostExtensionActivator"},{"name":"ActivationTimes","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensions"},{"name":"ACTIVE_GROUP","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorService"},{"name":"activeContrastBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"ActiveEditorContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"ActiveEditorGroupEmptyContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"ActiveEditorGroupIndexContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"ActiveEditorGroupLastContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"ActiveEditorIsReadonlyContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"ActiveGroupEditorsByMostRecentlyUsedQuickAccess","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorQuickAccess"},{"name":"ActivePanelContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/panel"},{"name":"ActiveViewletContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/viewlet"},{"name":"ActiveWindowManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/node/activeWindowTracker"},{"name":"ACTIVITY_BAR_ACTIVE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ACTIVITY_BAR_ACTIVE_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ACTIVITY_BAR_ACTIVE_FOCUS_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ACTIVITY_BAR_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ACTIVITY_BAR_BADGE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ACTIVITY_BAR_BADGE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ACTIVITY_BAR_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ACTIVITY_BAR_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ACTIVITY_BAR_INACTIVE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ActivityAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/compositeBarActions"},{"name":"ActivityActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/compositeBarActions"},{"name":"ActivitybarPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/activitybar/activitybarPart"},{"name":"ActivityService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/activity/browser/activityService"},{"name":"ActivityUpdater","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markers"},{"name":"ADD_CONFIGURATION_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"ADD_ROOT_FOLDER_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceCommands"},{"name":"ADD_ROOT_FOLDER_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceCommands"},{"name":"addArg","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/argvHelper"},{"name":"addClass","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addClasses","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addContextToEditorMatches","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/searchHelpers"},{"name":"AddCursorsAtSearchResults","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"addDisposableGenericMouseDownListner","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addDisposableGenericMouseMoveListner","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addDisposableGenericMouseUpListner","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addDisposableListener","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addDisposableNonBubblingMouseOutListener","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addDisposableNonBubblingPointerOutListener","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addDisposableThrottledListener","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addEventListener","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"AddFunctionBreakpointAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"addListener","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"addListener","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"ADDRCONFIG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"ADDRGETNETWORKPARAMS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"AddRootFolderAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceActions"},{"name":"AddSelectionToNextFindMatchAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"AddSelectionToPreviousFindMatchAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"addSetting","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/settingsMerge"},{"name":"addStandardDisposableGenericMouseDownListner","kind":"let","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addStandardDisposableGenericMouseUpListner","kind":"let","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addStandardDisposableListener","kind":"let","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addTerminalEnvironmentKeys","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment"},{"name":"addToValueTree","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"AddToWorkspaceFolderRecommendationsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"AddToWorkspaceRecommendationsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"addTrailingPathSeparator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"AddWatchExpressionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"adoptToGalleryExtensionId","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementUtil"},{"name":"after","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"afterEach","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"Agent","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"Agent","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"https"},{"name":"Aggregation","kind":"enum member","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/DataPointType"},{"name":"alert","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"alert","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/aria/aria"},{"name":"alertFormattingEdits","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"ALL_SYNC_RESOURCES","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"allCharCodes","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/minimap/minimapCharSheet"},{"name":"AllEditorsByAppearanceQuickAccess","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorQuickAccess"},{"name":"AllEditorsByMostRecentlyUsedQuickAccess","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorQuickAccess"},{"name":"AllKeysConfigurationChangeEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationModels"},{"name":"allowedNodeEnvironmentFlags","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"allowSetForegroundWindow","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/windows-foreground-love/index"},{"name":"allSettings","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"ALPN_ENABLED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"AnalyserNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AnchorAlignment","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/contextview/contextview"},{"name":"anchorGlob","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepSearchUtils"},{"name":"AnchorPosition","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/contextview/contextview"},{"name":"animate","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"Animation","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AnimationEffect","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AnimationEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AnimationPlaybackEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AnimationTimeline","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ansiColorIdentifiers","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry"},{"name":"ansiColorMap","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry"},{"name":"any","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"anyScore","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"AnythingQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/anythingQuickAccess"},{"name":"ApiCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostApiCommands"},{"name":"ApiCommandArgument","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostApiCommands"},{"name":"ApiCommandResult","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostApiCommands"},{"name":"app","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"append","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"appendEditorTitleContextMenuItem","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions.contribution"},{"name":"appendFile","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"appendFileSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"appendKeyBindingLabel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"appendStylizedStringToContainer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform"},{"name":"appendStylizedStringToContainer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugANSIHandling"},{"name":"appendToCommandPalette","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions.contribution"},{"name":"AppInsightsAppender","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/node/appInsightsAppender"},{"name":"applicationCache","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ApplicationCache","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"applicationSettings","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"applyCodeAction","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionCommands"},{"name":"applyConfigurationValues","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"applyDeprecatedVariableMessage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configurationResolver/common/configurationResolverUtils"},{"name":"applyDragImage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dnd"},{"name":"applyEdit","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/jsonEdit"},{"name":"applyEdits","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/jsonEdit"},{"name":"ApplyEditsResult","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"ApplyToKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"appVersion","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Context"},{"name":"arch","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"arch","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"areFunctions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"areKeyboardLayoutsEqual","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/keymapInfo"},{"name":"areSame","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/settingsMerge"},{"name":"areSameExtensions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementUtil"},{"name":"areWebviewInputOptionsEqual","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService"},{"name":"argv","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"argv0","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"AriaLabelProvider","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keybindingLabels"},{"name":"Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ArrayBuffer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"arrayInsert","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"ArrayNavigator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/navigator"},{"name":"ARROW_IMG_SIZE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/scrollbarArrow"},{"name":"as","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"asArray","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"AsbtractOutputChannelModelService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/output/common/outputChannelModel"},{"name":"asCSSUrl","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"asDomUri","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"asJson","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/request/common/request"},{"name":"asPromise","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"assert","kind":"alias","kindModifiers":"","sortText":"0"},{"name":"assertAllDefined","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"AssertDocumentLineMappingDirection","kind":"enum","kindModifiers":"","sortText":"0"},{"name":"assertEqualQueries","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/test/browser/queryBuilder.test"},{"name":"assertEqualSearchPathResults","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/test/browser/queryBuilder.test"},{"name":"AssertionError","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"assertIsDefined","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"assertMapping","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/test/electron-browser/keyboardMapperTestUtils"},{"name":"assertResolveKeybinding","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/test/electron-browser/keyboardMapperTestUtils"},{"name":"assertResolveKeyboardEvent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/test/electron-browser/keyboardMapperTestUtils"},{"name":"assertResolveUserBinding","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/test/electron-browser/keyboardMapperTestUtils"},{"name":"asserts","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"assertSyncedModels","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/editableTextModelTestUtils"},{"name":"assertType","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"assign","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/objects"},{"name":"asText","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/request/common/request"},{"name":"asWebviewUri","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/shared/webview"},{"name":"async","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"AsyncDataSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewer"},{"name":"AsyncDataTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/asyncDataTree"},{"name":"AsyncEmitter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/event"},{"name":"AsyncResource","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"async_hooks"},{"name":"atob","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"Atomics","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"attachBadgeStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachBreadcrumbsStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachButtonStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachCheckboxStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachDialogStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachFindReplaceInputBoxStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachInputBoxStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachLinkStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachListStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachMenuStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachProgressBarStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachQuickInputStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachSelectBoxStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachStylerCallback","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachSuggestEnabledInputBoxStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput"},{"name":"Attr","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Audio","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioBuffer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioBufferSourceNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioContext","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioDestinationNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioListener","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioParam","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioParamMap","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioProcessingEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioScheduledSourceNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioWorklet","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioWorkletNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"authentication","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"AuthenticationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/authentication/browser/authenticationService"},{"name":"AuthenticationTokenService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncAccount"},{"name":"AuthenticationTokenService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/authentication/electron-browser/authenticationTokenService"},{"name":"AuthenticationTokenServiceChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncAccountIpc"},{"name":"AuthenticatorAssertionResponse","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AuthenticatorAttestationResponse","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AuthenticatorResponse","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AuthStatus","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/userDataSync/browser/userDataSyncAuthentication"},{"name":"AutoCheckUpdatesConfigurationKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"AutoFixAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionCommands"},{"name":"AutoIndentOnPaste","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"AutoIndentOnPasteCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"automaticKeyboardNavigationSettingKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"AutoSaveAfterShortDelayContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService"},{"name":"AutoSaveConfiguration","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"AutoSaveMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService"},{"name":"AutoUpdateConfigurationKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"autoUpdater","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"AvailabilityData","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/AvailabilityData"},{"name":"AvailabilityData","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"AverageBufferSize","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase"},{"name":"await","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"BackLayerWebView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView"},{"name":"BackupFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/backup/common/backupFileService"},{"name":"BackupFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/backup/node/backupFileService"},{"name":"BackupFilesModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/backup/common/backupFileService"},{"name":"BackupMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/backup/electron-main/backupMainService"},{"name":"BackupRestorer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/backup/common/backupRestorer"},{"name":"BACKUPS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/common/environment"},{"name":"BackupTracker","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/backup/common/backupTracker"},{"name":"BADFAMILY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"BADFLAGS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"badgeBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"badgeForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"BADHINTS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"BADNAME","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"BADQUERY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"BADRESP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"BADSTR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"BareFontInfo","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/fontInfo"},{"name":"BarProp","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Barrier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"Base","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/Base"},{"name":"Base","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"BaseActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/actionbar/actionbar"},{"name":"BaseAudioContext","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"BaseBinaryResourceEditor","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/binaryEditor"},{"name":"BaseBreakpoint","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"BaseCellViewModel","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel"},{"name":"BaseCloseAllAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"BaseConfigurationResolverService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configurationResolver/browser/configurationResolverService"},{"name":"BaseCreateEditorGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"BaseDropdown","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/dropdown/dropdown"},{"name":"BaseEditor","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/baseEditor"},{"name":"BaseEditorQuickAccessProvider","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorQuickAccess"},{"name":"BaseErrorTelemetry","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/errorTelemetry"},{"name":"BaseExtHostTerminal","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTerminalService"},{"name":"BaseExtHostTerminalService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTerminalService"},{"name":"BaseFocusGroupAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"BaseMoveGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"basename","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"basename","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"basename","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"basenameOrAuthority","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"BaseNavigateEditorAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"BaseQuickAccessEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"BaseQuickAccessNavigateAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/quickAccessActions"},{"name":"BaseResizeViewAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"BaseResolvedKeybinding","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/baseResolvedKeybinding"},{"name":"BaseSaveAllAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"BaseSplitEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"BaseSwitchWindow","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"BaseTextEditor","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/textEditor"},{"name":"BaseTextEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor/textEditorModel"},{"name":"baseTypeToTelemetryType","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/TelemetryTypes/TelemetryType"},{"name":"BaseWebview","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/baseWebviewElement"},{"name":"BaseWindowDriver","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/browser/baseDriver"},{"name":"BaseZoomAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"BasicInplaceReplace","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/inplaceReplaceSupport"},{"name":"BatchedCollector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/textSearchManager"},{"name":"before","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"beforeEach","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"BeforeUnloadEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"BenchmarkSuite","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/benchmark/benchmarkUtils"},{"name":"BetterMergeId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementUtil"},{"name":"BhxBrowser","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"bigint","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"BigInt","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"BigInt64Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"BigIntStats","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"BigUint64Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"BINARY_DIFF_EDITOR_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"BINARY_FILE_EDITOR_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"BinaryEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor/binaryEditorModel"},{"name":"BinaryFileEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor"},{"name":"BinaryResourceDiffEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/binaryDiffEditor"},{"name":"binarySearch","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"BiquadFilterNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Blob","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"BlockCommentCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/comment/blockCommentCommand"},{"name":"blur","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"boolean","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Boolean","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"BooleanEventEmitter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/widget/codeEditorWidget"},{"name":"BOTTOM_CELL_TOOLBAR_HEIGHT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"BoundModelReferenceCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadDocuments"},{"name":"BracesHidingRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesRenderers"},{"name":"BracketElectricCharacterSupport","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/electricCharacter"},{"name":"BracketMatchingController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/bracketMatching/bracketMatching"},{"name":"BracketSelectionRangeProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/smartSelect/bracketSelections"},{"name":"BracketsUtils","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/richEditBrackets"},{"name":"breadcrumbsActiveSelectionForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"breadcrumbsBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"BreadcrumbsConfig","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbs"},{"name":"BreadcrumbsControl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbsControl"},{"name":"BreadcrumbsFilePicker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbsPicker"},{"name":"breadcrumbsFocusForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"breadcrumbsForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"BreadcrumbsItem","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget"},{"name":"BreadcrumbsOutlinePicker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbsPicker"},{"name":"BreadcrumbsPicker","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbsPicker"},{"name":"breadcrumbsPickerBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"BreadcrumbsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbs"},{"name":"BreadcrumbsWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget"},{"name":"break","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"breakBetweenGraphemeBreakType","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"Breakpoint","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Breakpoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"Breakpoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"BREAKPOINT_EDITOR_CONTRIBUTION_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"BreakpointEditorContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution"},{"name":"BREAKPOINTS_VIEW_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"breakpointsExtPoint","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugSchemas"},{"name":"BreakpointsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/breakpointsView"},{"name":"BreakpointWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/breakpointWidget"},{"name":"BreakpointWidgetContext","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"BroadcastChannel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"brotliCompress","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"brotliCompressSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"brotliDecompress","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"brotliDecompressSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"BrowserBackupTracker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/backup/browser/backupTracker"},{"name":"BrowserClipboardService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/clipboard/browser/clipboardService"},{"name":"BrowserCredentialsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/credentials/browser/credentialsService"},{"name":"BrowserEnvironmentConfiguration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/environment/browser/environmentService"},{"name":"BrowserFeatures","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/canIUse"},{"name":"BrowserHostService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/host/browser/browserHostService"},{"name":"BrowserIntegrityServiceImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/integrity/browser/integrityService"},{"name":"BrowserKeyboardMapperFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/browser/keymapService"},{"name":"BrowserKeyboardMapperFactoryBase","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/browser/keymapService"},{"name":"BrowserLifecycleService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/lifecycle/browser/lifecycleService"},{"name":"BrowserPathService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/path/browser/pathService"},{"name":"BrowserRequestService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/request/browser/requestService"},{"name":"BrowserResizeObserver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver"},{"name":"BrowserSocketFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/browser/browserSocketFactory"},{"name":"BrowserStorageService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/browser/storageService"},{"name":"BrowserTelemetryOptOut","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut"},{"name":"BrowserTextFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/browser/browserTextFileService"},{"name":"BrowserUpdateService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/update/browser/updateService"},{"name":"BrowserURLService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/url/browser/urlService"},{"name":"BrowserView","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"BrowserWindow","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"BrowserWindowProxy","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"BrowserWorkbenchEnvironmentService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/environment/browser/environmentService"},{"name":"BrowserWorkspaceEditingService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workspaces/browser/workspaceEditingService"},{"name":"BrowserWorkspacesService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workspaces/browser/workspacesService"},{"name":"btoa","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"buffer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/zip"},{"name":"Buffer","kind":"class","kindModifiers":"declare","sortText":"4"},{"name":"Buffer","kind":"alias","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"buffer"},{"name":"BufferedEmitter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc.net"},{"name":"BufferLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/bufferLog"},{"name":"BufferredOutputChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/output/common/outputChannelModel"},{"name":"bufferToReadable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"bufferToStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"buildHelpMessage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/argv"},{"name":"buildRegexParseError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepTextSearchEngine"},{"name":"buildReplaceStringWithCasePreserved","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/search"},{"name":"buildTelemetryMessage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/node/telemetry"},{"name":"buildVersionMessage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/argv"},{"name":"BUILTIN_MANIFEST_CACHE_FILE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensions"},{"name":"BuiltInBasicsExtensionsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews"},{"name":"BuiltInExtensionsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews"},{"name":"builtinModules","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"module"},{"name":"BuiltInThemesExtensionsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews"},{"name":"BulkCategory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview"},{"name":"BulkEditAccessibilityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"BulkEditDataSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"BulkEditDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"BulkEditIdentityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"BulkEditNaviLabelProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"BulkEditPane","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane"},{"name":"BulkEditPreviewProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview"},{"name":"BulkEditService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/bulkEdit/browser/bulkEditService"},{"name":"BulkEditSorter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"BulkFileOperation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview"},{"name":"BulkFileOperations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview"},{"name":"BulkFileOperationType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview"},{"name":"BulkTextEdit","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview"},{"name":"Button","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/button/button"},{"name":"buttonBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"buttonBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/page/browser/welcomePage"},{"name":"buttonForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"ButtonGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/button/button"},{"name":"buttonHoverBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"buttonHoverBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/page/browser/welcomePage"},{"name":"ByteLengthQueuingStrategy","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Cache","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Cache","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/cache"},{"name":"Cache","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/cache"},{"name":"cached","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-sqlite3/index"},{"name":"cachedDataVersionTag","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"CachedExtensionScanner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner"},{"name":"CachedKeyboardMapper","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/keyboardMapper"},{"name":"CachedListVirtualDelegate","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/list"},{"name":"cachedStringRepeat","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/commands/shiftCommand"},{"name":"caches","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CacheStorage","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"calcANSI8bitColor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform"},{"name":"calcANSI8bitColor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugANSIHandling"},{"name":"calculateLF","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"calculateSize","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"Call","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree"},{"name":"callbackify","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"CallHierarchyDirection","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/common/callHierarchy"},{"name":"CallHierarchyIncomingCall","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CallHierarchyIncomingCall","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CallHierarchyIncomingCall","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"CallHierarchyItem","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CallHierarchyItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CallHierarchyItem","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"CallHierarchyModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/common/callHierarchy"},{"name":"CallHierarchyOutgoingCall","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CallHierarchyOutgoingCall","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CallHierarchyOutgoingCall","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"CallHierarchyProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/common/callHierarchy"},{"name":"CallHierarchyTreePeekWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek"},{"name":"CallRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree"},{"name":"CALLSTACK_VIEW_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CallStackEditorContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/callStackEditorContribution"},{"name":"CallStackView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/callStackView"},{"name":"CancelActionId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"cancelAnimationFrame","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"CancelCellAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/contrib/notebookActions"},{"name":"canceled","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"CancellationToken","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/cancellation"},{"name":"CancellationTokenSource","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CancellationTokenSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/cancellation"},{"name":"CancellationTokenSource","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"CANCELLED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"CancelSearchAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"canExecuteOnUI","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsUtil"},{"name":"canExecuteOnWeb","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsUtil"},{"name":"canExecuteOnWorkspace","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsUtil"},{"name":"canNormalize","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/normalization"},{"name":"CanvasGradient","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CanvasPattern","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CanvasRenderingContext2D","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"captureEvents","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"CaretPosition","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"case","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"CaseSensitiveCheckbox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/findinput/findInputCheckboxes"},{"name":"catch","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"CategoryElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"CategoryElementRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"CDATASection","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CELL_MARGIN","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"CELL_RUN_GUTTER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"CELL_TOOLBAR_SEPERATOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookEditor"},{"name":"CellEditState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookBrowser"},{"name":"CellEditType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"CellFocusMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookBrowser"},{"name":"CellKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CellKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"CellKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"CellKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CellMenus","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/cellMenus"},{"name":"CellOutputKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CellOutputKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"CellOutputKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"CellOutputKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CellRevealPosition","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookBrowser"},{"name":"CellRevealType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookBrowser"},{"name":"CellRunState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookBrowser"},{"name":"CellUri","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"CenteredViewLayout","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/centered/centeredViewLayout"},{"name":"Certificate","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"CHANGE_BUFFER_DELAY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/watcher"},{"name":"ChangeEncodingAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorStatus"},{"name":"ChangeEOLAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorStatus"},{"name":"ChangeIndentationSizeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"ChangeModeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorStatus"},{"name":"ChangeSortAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"Channel","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Channel"},{"name":"ChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc"},{"name":"ChannelMergerNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ChannelServer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc"},{"name":"ChannelSplitterNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CharacterClassifier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/characterClassifier"},{"name":"CharacterData","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CharacterMapping","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"CharacterMappingConstants","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"CharacterPairSupport","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/characterPair"},{"name":"CharacterSet","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/characterClassifier"},{"name":"CharCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/charCode"},{"name":"CharWidthRequest","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/config/charWidthReader"},{"name":"CharWidthRequestType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/config/charWidthReader"},{"name":"chdir","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"Checkbox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/checkbox/checkbox"},{"name":"CheckboxActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/checkbox/checkbox"},{"name":"CheckedStates","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview"},{"name":"CheckForUpdatesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"CheckForVSCodeUpdateAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/browser/update"},{"name":"CheckForVSCodeUpdateActionId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/common/update"},{"name":"checkProposedApiEnabled","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensions"},{"name":"checkServerIdentity","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"checksum","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/crypto"},{"name":"chmod","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"chmod","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"chmodSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"Choice","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"ChoiceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/notifications"},{"name":"ChokidarWatcherService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/unix/chokidarWatcherService"},{"name":"ChordKeybinding","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keyCodes"},{"name":"chown","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"chownSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"chrome","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"ChunkStream","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc.net"},{"name":"Cipher","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"clamp","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/numbers"},{"name":"class","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"CLASSIFIER_MODIFIER_SEPARATOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"ClassName","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"clean","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"cleanMnemonic","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/menu/menu"},{"name":"cleanRemoteAuthority","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetryUtils"},{"name":"CleanSearchEditorStateCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"cleanUndefinedQueryValues","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/test/browser/queryBuilder.test"},{"name":"clear","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"CLEAR_ALL_NOTIFICATIONS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"CLEAR_NOTIFICATION","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"clearAllFontInfos","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/config/configuration"},{"name":"ClearAllNotificationsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsActions"},{"name":"ClearCommandHistoryAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess"},{"name":"ClearEditorHistoryAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"ClearExtensionsInputAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"clearHistoryCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"clearImmediate","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"clearImmediate","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"timers"},{"name":"clearInterval","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"clearInterval","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"timers"},{"name":"clearLine","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"readline"},{"name":"clearNode","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"ClearNotificationAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsActions"},{"name":"ClearRecentFilesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"ClearReplAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/repl"},{"name":"clearScreenDown","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"readline"},{"name":"ClearSearchHistoryCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"ClearSearchResultsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"ClearTerminalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"clearTextMimes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/mime"},{"name":"clearTimeout","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"clearTimeout","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"timers"},{"name":"ClickLinkGesture","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/link/clickLinkGesture"},{"name":"ClickLinkKeyboardEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/link/clickLinkGesture"},{"name":"ClickLinkMouseEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/link/clickLinkGesture"},{"name":"ClickLinkOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/link/clickLinkGesture"},{"name":"Client","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc.net"},{"name":"Client","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser"},{"name":"Client","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc.cp"},{"name":"CLIENT_RENEG_LIMIT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"CLIENT_RENEG_WINDOW","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"ClientCoordinates","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorDom"},{"name":"clientInformation","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ClientRect","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ClientRectList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ClientRequest","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"ClientRequest","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"clipboard","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"Clipboard","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ClipboardBasedVariableResolver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetVariables"},{"name":"ClipboardEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CLIServer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostCLIServer"},{"name":"clock","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"cloneAndChange","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/objects"},{"name":"close","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"close","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"close","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"inspector"},{"name":"CLOSE_EDITOR_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"CLOSE_EDITOR_GROUP_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"CLOSE_EDITORS_AND_GROUP_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"CLOSE_EDITORS_IN_GROUP_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"CLOSE_SAVED_EDITORS_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"CloseAllEditorGroupsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"CloseAllEditorsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"CloseCurrentWindowAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"closed","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CloseDirection","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"CloseEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"CloseEditorInAllGroupsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"CloseEditorsInOtherGroupsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"CloseEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CloseExtensionDetailsOnViewChangeKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"CloseGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"CloseLeftEditorsInGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"CloseOneEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"ClosePanelAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"CloseReplaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"CloseReplaceWidgetActionId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"CloseSidebarAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"closeSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"CloseWorkspaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceActions"},{"name":"cmp","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"coalesce","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"coalesceInPlace","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"CodeAction","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CodeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CodeActionAutoApply","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/types"},{"name":"CodeActionCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionCommands"},{"name":"CodeActionCommandArgs","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/types"},{"name":"codeActionCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeAction"},{"name":"CodeActionDocumentationContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeActions/common/documentationContribution"},{"name":"CodeActionExtensionPointFields","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeActions/common/codeActionsExtensionPoint"},{"name":"CodeActionKeybindingResolver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionMenu"},{"name":"CodeActionKind","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CodeActionKind","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/types"},{"name":"CodeActionKind","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CodeActionMenu","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionMenu"},{"name":"CodeActionModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionModel"},{"name":"CodeActionProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CodeActionsContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeActions/common/codeActionsContribution"},{"name":"codeActionsExtensionPointDescriptor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeActions/common/codeActionsExtensionPoint"},{"name":"CodeActionsState","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionModel"},{"name":"CodeActionTrigger","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CodeActionTriggerType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CodeActionUi","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionUi"},{"name":"CodeApplication","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-main/app"},{"name":"CodeCell","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell"},{"name":"CodeCellRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer"},{"name":"CodeCellViewModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel"},{"name":"CodeDataTransfers","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"CodeEditorService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/browser/codeEditorService"},{"name":"CodeEditorServiceImpl","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/services/codeEditorServiceImpl"},{"name":"CodeEditorStateFlag","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/core/editorState"},{"name":"CodeEditorWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/widget/codeEditorWidget"},{"name":"CodeInset","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CodeLens","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CodeLens","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CodeLensCache","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codelens/codeLensCache"},{"name":"CodeLensContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codelens/codelensController"},{"name":"CodeLensHelper","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codelens/codelensWidget"},{"name":"CodeLensModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codelens/codelens"},{"name":"CodeLensProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CodeLensWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codelens/codelensWidget"},{"name":"CodeWindow","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-main/window"},{"name":"CodiconActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer"},{"name":"CodiconLabel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/codiconLabel/codiconLabel"},{"name":"codiconStartMarker","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/codicon"},{"name":"coerce","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"COLLAPSE_NOTIFICATION","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"CollapseAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/viewlet"},{"name":"CollapseAllAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/treeDefaults"},{"name":"CollapseDeepestExpandedLevelAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"CollapseExplorerView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"CollapseNotificationAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsActions"},{"name":"collectLaunchConfigs","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/diagnostics/node/diagnosticsService"},{"name":"collectWorkspaceStats","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/diagnostics/node/diagnosticsService"},{"name":"Color","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Color","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/color"},{"name":"Color","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"Color","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"ColorDetector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/colorPicker/colorDetector"},{"name":"ColorExtensionPoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/colorExtensionPoint"},{"name":"ColorFormat","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ColorId","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"ColorInformation","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ColorInformation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"colorize","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"colorizeElement","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"colorizeModelLine","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"Colorizer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/colorizer"},{"name":"ColorMap","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/tokenization"},{"name":"ColorPickerBody","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/colorPicker/colorPickerWidget"},{"name":"ColorPickerHeader","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/colorPicker/colorPickerWidget"},{"name":"ColorPickerModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/colorPicker/colorPickerModel"},{"name":"ColorPickerWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/colorPicker/colorPickerWidget"},{"name":"ColorPresentation","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ColorPresentation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ColorPresentation","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"ColorProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"ColorTheme","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ColorThemeData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/colorThemeData"},{"name":"ColorThemeKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ColorThemeKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"colorThemeSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/colorThemeSchema"},{"name":"ColorZone","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/overviewZoneManager"},{"name":"ColumnSelection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorColumnSelection"},{"name":"combinedAppender","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetryUtils"},{"name":"combinedDisposable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/lifecycle"},{"name":"CombinedInstallAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"CombinedSpliceable","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/splice"},{"name":"Command","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"CommandLine","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"CommandOptions","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"commands","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"commands","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/workbench.web.api"},{"name":"CommandsConverter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostCommands"},{"name":"CommandService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/commands/common/commandService"},{"name":"commandsExtensionPoint","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/menusExtensionPoint"},{"name":"CommandsHistory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/browser/commandsQuickAccess"},{"name":"CommandsQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess"},{"name":"CommandsRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/commands/common/commands"},{"name":"CommandString","kind":"type","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"CommandString","kind":"type","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskConfiguration"},{"name":"CommandTrackerAddon","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/addons/commandTrackerAddon"},{"name":"Comment","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CommentBasedVariableResolver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetVariables"},{"name":"CommentContextKeys","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/common/commentContextKeys"},{"name":"CommentController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsEditorContribution"},{"name":"COMMENTEDITOR_DECORATION_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentThreadWidget"},{"name":"CommentFormActions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentFormActions"},{"name":"CommentGlyphWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentGlyphWidget"},{"name":"CommentMenus","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentMenus"},{"name":"CommentMode","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CommentMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CommentMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CommentMode","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/commentMode"},{"name":"CommentNode","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/common/commentModel"},{"name":"CommentNode","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentNode"},{"name":"CommentNodeRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsTreeViewer"},{"name":"comments","kind":"module","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"COMMENTS_VIEW_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsTreeViewer"},{"name":"COMMENTS_VIEW_TITLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsTreeViewer"},{"name":"CommentsAsyncDataSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsTreeViewer"},{"name":"CommentService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentService"},{"name":"CommentsList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsTreeViewer"},{"name":"CommentsModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/common/commentModel"},{"name":"CommentsModelVirualDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsTreeViewer"},{"name":"CommentsPanel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsView"},{"name":"CommentThreadCollapsibleState","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CommentThreadCollapsibleState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CommentThreadCollapsibleState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CommitCharacterController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestCommitCharacters"},{"name":"CommonEditorConfiguration","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/commonEditorConfig"},{"name":"CommonFindController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"commonlyUsedData","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsLayout"},{"name":"commonPrefixLength","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"commonPrefixLength","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"commonSuffixLength","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"CommonTask","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"Comparator","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"compare","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"compare","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"compare","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"COMPARE_RESOURCE_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"COMPARE_SELECTED_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"COMPARE_WITH_SAVED_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"compareAnything","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/comparers"},{"name":"compareBuild","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"compareByPrefix","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/comparers"},{"name":"compareFileExtensions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/comparers"},{"name":"compareFileNames","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/comparers"},{"name":"compareIdentifiers","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"compareIgnoreCase","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"compareItemsByFuzzyScore","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/fuzzyScorer"},{"name":"compareMarkersByUri","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersModel"},{"name":"comparePaths","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/comparers"},{"name":"compareSubstring","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"compareSubstringIgnoreCase","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"CompareWithClipboardAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"CompatChangeAll","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"compile","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCompile"},{"name":"compileFunction","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"vm"},{"name":"CompletionContext","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"CompletionItem","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CompletionItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggest"},{"name":"CompletionItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CompletionItem","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"CompletionItemInsertTextRule","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CompletionItemInsertTextRule","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"CompletionItemKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CompletionItemKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CompletionItemKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CompletionItemKind","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"CompletionItemKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"CompletionItemTag","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CompletionItemTag","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CompletionItemTag","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CompletionItemTag","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"CompletionItemTag","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"completionKindFromString","kind":"let","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"completionKindToCssClass","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CompletionList","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CompletionList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CompletionModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/completionModel"},{"name":"CompletionOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggest"},{"name":"CompletionProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CompletionTriggerKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CompletionTriggerKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CompletionTriggerKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CompletionTriggerKind","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"CompletionTriggerKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"Component","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/component"},{"name":"ComposedTreeDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/abstractTree"},{"name":"Composite","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/composite"},{"name":"CompositeActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/compositeBarActions"},{"name":"CompositeBar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/compositeBar"},{"name":"CompositeDescriptor","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/composite"},{"name":"CompositeDragAndDrop","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/compositeBar"},{"name":"CompositeDragAndDropData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"CompositeDragAndDropObserver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"CompositeOverflowActivityAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/compositeBarActions"},{"name":"CompositeOverflowActivityActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/compositeBarActions"},{"name":"CompositePart","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/compositePart"},{"name":"CompositeProgressIndicator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/progress/browser/progressIndicator"},{"name":"CompositeRegistry","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/composite"},{"name":"CompositeScope","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/progress/browser/progressIndicator"},{"name":"CompositeSnippetVariableResolver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetVariables"},{"name":"CompositionEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"compress","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/compressedObjectTreeModel"},{"name":"compressConsecutiveTextChanges","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textChange"},{"name":"CompressedNavigationController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerViewer"},{"name":"CompressedObjectTreeModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/compressedObjectTreeModel"},{"name":"CompressibleAsyncDataTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/asyncDataTree"},{"name":"CompressibleObjectTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/objectTree"},{"name":"CompressibleObjectTreeModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/compressedObjectTreeModel"},{"name":"computeCodePoint","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"ComputedEditorOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/commonEditorConfig"},{"name":"computeLinks","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/linkComputer"},{"name":"computeRanges","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/indentRangeProvider"},{"name":"computeScreenAwareSize","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"computeStyles","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"config","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"config","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"Config","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"Config","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Config"},{"name":"Configuration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/config/configuration"},{"name":"Configuration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationModels"},{"name":"Configuration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configurationModels"},{"name":"Configuration","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/applicationinsights"},{"name":"CONFIGURATION_SYNC_STORE_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"ConfigurationCache","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/browser/configurationCache"},{"name":"ConfigurationCache","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/node/configurationCache"},{"name":"ConfigurationChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"ConfigurationChangeEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationModels"},{"name":"ConfigurationEditingError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configurationEditingService"},{"name":"ConfigurationEditingErrorCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configurationEditingService"},{"name":"ConfigurationEditingService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configurationEditingService"},{"name":"ConfigurationKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"ConfigurationManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugConfigurationManager"},{"name":"ConfigurationModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationModels"},{"name":"ConfigurationModelParser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationModels"},{"name":"ConfigurationResolverService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configurationResolver/browser/configurationResolverService"},{"name":"ConfigurationResolverService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configurationResolver/electron-browser/configurationResolverService"},{"name":"ConfigurationScope","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"ConfigurationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationService"},{"name":"ConfigurationTarget","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ConfigurationTarget","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"ConfigurationTarget","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ConfigurationTargetToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"configurationTelemetry","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetryUtils"},{"name":"ConfigureAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"ConfigureLanguageBasedSettingsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesActions"},{"name":"ConfigureLocaleAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/localizations/browser/localizationsActions"},{"name":"ConfigureNotificationAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsActions"},{"name":"configureOpenerTrustedDomainsHandler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/url/common/trustedDomains"},{"name":"ConfigureRecommendedExtensionsCommandsContributor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ConfigureRuntimeArgumentsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/developerActions"},{"name":"ConfigureTaskAction","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/abstractTaskService"},{"name":"ConfigureWorkspaceFolderRecommendedExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ConfigureWorkspaceRecommendedExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ConfiguringTask","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"confirm","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"ConfirmResult","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/dialogs/common/dialogs"},{"name":"CONFLICT_RESOLUTION_CONTEXT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler"},{"name":"CONFLICT_RESOLUTION_SCHEME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler"},{"name":"ConflictDetector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/bulkEdit/browser/conflicts"},{"name":"connect","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"http2"},{"name":"connect","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"net"},{"name":"connect","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"connect","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc.net"},{"name":"connect","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/node/driver"},{"name":"connected","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"ConnectionGainEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"ConnectionLostEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"ConnectionType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"connectPrimaryMenuToInlineActionBar","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/util"},{"name":"connectProxyResolver","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/node/proxyResolver"},{"name":"connectRemoteAgentExtensionHost","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"connectRemoteAgentManagement","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"connectRemoteAgentTunnel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"CONNREFUSED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"console","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Console","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"ConsoleLogInAutomationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/browser/log"},{"name":"ConsoleLogInMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"ConsoleLogMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"ConsoleLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"consolidate","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/rangeMap"},{"name":"const","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"constants","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"buffer"},{"name":"constants","kind":"module","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"constants","kind":"module","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"constants","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"http2"},{"name":"constants","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"constants","kind":"module","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"perf_hooks"},{"name":"constants","kind":"module","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"constants","kind":"property","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/constants"},{"name":"Constants","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uint"},{"name":"Constants","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/jschardet/index"},{"name":"Constants","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/minimap/minimapCharSheet"},{"name":"ConstantSourceNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"consumeReadable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/stream"},{"name":"consumeReadableWithLimit","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/stream"},{"name":"consumeStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/stream"},{"name":"consumeStreamWithLimit","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/stream"},{"name":"contains","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"containsDragType","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"containsEmoji","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"containsFullWidthCharacter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"containsRTL","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"containsUppercaseCharacter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"ContentHoverWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/hover/hoverWidgets"},{"name":"contentTracing","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"ContentViewOverlays","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewOverlays"},{"name":"ContentWidgetPositionPreference","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorBrowser"},{"name":"ContentWidgetPositionPreference","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"context","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Context","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/parameterHints/provideSignatureHelp"},{"name":"Context","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggest"},{"name":"Context","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/browser/contextKeyService"},{"name":"Context","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Context"},{"name":"CONTEXT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/toolbar/toolbar"},{"name":"CONTEXT_ACCESSIBILITY_MODE_ENABLED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/accessibility/common/accessibility"},{"name":"CONTEXT_ACTIVE_LOG_OUTPUT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"CONTEXT_AUTH_TOKEN_STATE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/userDataSync/browser/userDataSyncAuthentication"},{"name":"CONTEXT_BREAKPOINT_SELECTED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_BREAKPOINT_WIDGET_VISIBLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_BREAKPOINTS_EXIST","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_BREAKPOINTS_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_CALLSTACK_ITEM_TYPE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_CUSTOM_EDITORS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/customEditor"},{"name":"CONTEXT_DEBUG_CONFIGURATION_TYPE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_DEBUG_STATE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_DEBUG_TYPE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_DEBUG_UX","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_DEBUG_UX_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_EXPRESSION_SELECTED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_EXTENSION_HOST_PROFILE_RECORDED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"CONTEXT_FIND_INPUT_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"CONTEXT_FIND_WIDGET_NOT_VISIBLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"CONTEXT_FIND_WIDGET_VISIBLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/customEditor"},{"name":"CONTEXT_FOCUSED_SESSION_IS_ATTACH","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_IN_BREAKPOINT_WIDGET","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_IN_DEBUG_MODE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_IN_DEBUG_REPL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_IN_OUTPUT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"CONTEXT_JUMP_TO_CURSOR_SUPPORTED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_KEYBINDING_FOCUS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"CONTEXT_KEYBINDINGS_EDITOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"CONTEXT_KEYBINDINGS_SEARCH_FOCUS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"CONTEXT_LOADED_SCRIPTS_ITEM_TYPE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_LOADED_SCRIPTS_SUPPORTED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_MENU_CHANNEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/contextmenu/common/contextmenu"},{"name":"CONTEXT_MENU_CLOSE_CHANNEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/contextmenu/common/contextmenu"},{"name":"CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/rename/onTypeRename"},{"name":"CONTEXT_OUTPUT_SCROLL_LOCK","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"CONTEXT_PROFILE_SESSION_STATE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"CONTEXT_RENAME_INPUT_VISIBLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/rename/renameInputField"},{"name":"CONTEXT_REPLACE_INPUT_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"CONTEXT_RESTART_FRAME_SUPPORTED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_SETTINGS_EDITOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"CONTEXT_SETTINGS_JSON_EDITOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"CONTEXT_SETTINGS_SEARCH_FOCUS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"CONTEXT_STEP_BACK_SUPPORTED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_SYNC_ENABLEMENT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"CONTEXT_SYNC_STATE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"CONTEXT_TOC_ROW_FOCUS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"CONTEXT_UPDATE_STATE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/browser/update"},{"name":"CONTEXT_VARIABLES_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_WATCH_EXPRESSIONS_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"ContextAwareMenuEntryActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/browser/menuEntryActionViewItem"},{"name":"contextBridge","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"ContextKeyAndExpr","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyDefinedExpr","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyEqualsExpr","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyExpr","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyExprType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyFalseExpr","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyNotEqualsExpr","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyNotExpr","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyNotRegexExpr","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyOrExpr","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyRegexExpr","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/browser/contextKeyService"},{"name":"ContextKeyTrueExpr","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextMenuController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/contextmenu/contextmenu"},{"name":"ContextMenuHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextview/browser/contextMenuHandler"},{"name":"ContextMenuService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextview/browser/contextMenuService"},{"name":"ContextMenuService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService"},{"name":"ContextScopedFindInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/browser/contextScopedHistoryWidget"},{"name":"ContextScopedHistoryInputBox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/browser/contextScopedHistoryWidget"},{"name":"ContextScopedReplaceInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/browser/contextScopedHistoryWidget"},{"name":"ContextSubMenu","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/contextmenu"},{"name":"ContextTagKeys","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/ContextTagKeys"},{"name":"ContextTagKeys","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"ContextView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/contextview/contextview"},{"name":"ContextViewService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextview/browser/contextViewService"},{"name":"continue","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"CONTINUE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"CONTINUE_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"Contracts","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/applicationinsights"},{"name":"contrastBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"ContributableViewsModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/views"},{"name":"ContributedCustomEditors","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors"},{"name":"ContributedTask","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"convertBufferRangeToViewport","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers"},{"name":"convertLinkRangeToBuffer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers"},{"name":"convertSettings","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/themeCompatibility"},{"name":"convertSimple2RegExpPattern","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"convertToDAPaths","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugUtils"},{"name":"convertToVSCPaths","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugUtils"},{"name":"ConvolverNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Cookies","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"CoordinatesConverter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/splitLinesCollection"},{"name":"copy","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"COPY_FILE_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"COPY_PATH_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"COPY_RELATIVE_PATH_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"COPY_STACK_TRACE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"copyAllCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"CopyAllCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"copyFile","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"COPYFILE_EXCL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"COPYFILE_FICLONE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"COPYFILE_FICLONE_FORCE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"copyFileHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"copyFileSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"CopyLinesCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/copyLinesCommand"},{"name":"copyMatchCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"CopyMatchCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"CopyNotificationMessageAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsActions"},{"name":"CopyOptions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/textAreaInput"},{"name":"copyPathCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"CopyPathCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"CopyTerminalSelectionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"CopyValueAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"CopyWebviewEditorCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/electron-browser/webviewCommands"},{"name":"CoreEditingCommands","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/coreCommands"},{"name":"CoreEditorCommand","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/coreCommands"},{"name":"CoreNavigationCommands","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/coreCommands"},{"name":"CorrelationContextManager","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/AutoCollection/CorrelationContextManager"},{"name":"count","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"CountBadge","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/countBadge/countBadge"},{"name":"countEOL","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/tokensStore"},{"name":"Counter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/numbers"},{"name":"CountQueuingStrategy","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"countReset","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"cpus","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"cpuUsage","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"crash","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"crashReporter","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"crashReporterIdStorageKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetry"},{"name":"create","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"domain"},{"name":"create","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/worker/simpleWorker"},{"name":"create","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/editorSimpleWorker"},{"name":"create","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/outputLinkComputer"},{"name":"create","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/workbench.web.api"},{"name":"create","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"create","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/electron-browser/textMateWorker"},{"name":"createActionViewItem","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/browser/menuEntryActionViewItem"},{"name":"createAndBindHistoryNavigationWidgetScopedContextKeyService","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/browser/contextScopedHistoryWidget"},{"name":"createAndFillInActionBarActions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/browser/menuEntryActionViewItem"},{"name":"createAndFillInContextMenuActions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/browser/menuEntryActionViewItem"},{"name":"createApiFactoryAndRegisterActors","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.api.impl"},{"name":"createBreadcrumbsPicker","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbsPicker"},{"name":"createBreakpointDecorations","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution"},{"name":"createBrotliCompress","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"createBrotliDecompress","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"createCancelablePromise","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"createCellViewModel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel"},{"name":"createChannelReceiver","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc"},{"name":"createChannelSender","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc"},{"name":"createCipher","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createCipheriv","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createConnection","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"net"},{"name":"createContext","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"vm"},{"name":"createCSSRule","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"createCustomTask","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskConfiguration"},{"name":"createDecipher","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createDecipheriv","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createDecorationsForStackFrame","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/callStackEditorContribution"},{"name":"createDecorator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/decorators"},{"name":"createDecorator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/instantiation"},{"name":"createDeflate","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"createDeflateRaw","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"createDiffEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"createDiffieHellman","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createDiffNavigator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"createECDH","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createEditorFromSearchResult","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"createEditorPagePosition","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorDom"},{"name":"createElement","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/formattedTextRenderer"},{"name":"createError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"createErrorWithActions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errorsWithActions"},{"name":"createExtHostContextProxyIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/proxyIdentifier"},{"name":"createFakeScopedLineTokens","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/modesTestUtils"},{"name":"createFastDomNode","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/fastDomNode"},{"name":"createFileEditorInput","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"createFileIconThemableTreeContainerScope","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/views"},{"name":"createFileSystemProviderError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"createFindMatch","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelSearch"},{"name":"createGunzip","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"createGzip","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"createHash","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createHmac","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createHook","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"async_hooks"},{"name":"createImageBitmap","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"createInflate","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"createInflateRaw","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"createInterface","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"readline"},{"name":"createKeybinding","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keyCodes"},{"name":"createLineMatcher","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"createLineStarts","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase"},{"name":"createLineStartsFast","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase"},{"name":"createMainContextProxyIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/proxyIdentifier"},{"name":"createMatchers","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/textMateScopeMatcher"},{"name":"createMatches","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"createMemoizer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/decorators"},{"name":"createMessageOfType","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionHostProtocol"},{"name":"createMetaElement","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"createMockBuffer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils"},{"name":"createMockSession","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/test/browser/callStack.test"},{"name":"createMockText","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils"},{"name":"createModel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"createMonacoBaseAPI","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneBase"},{"name":"createMonacoEditorAPI","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"createMonacoLanguagesAPI","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"createMouseMoveEventMerger","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/mouseHandler"},{"name":"CreateNewLocalTerminalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/electron-browser/terminalRemote"},{"name":"CreateNewTerminalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"createPrivateKey","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createProxyObject","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"createPublicKey","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createQueuedSender","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/processes"},{"name":"createReadStream","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"createReadStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/io"},{"name":"createRegExp","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"createRequire","kind":"method","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"module"},{"name":"createRequireFromPath","kind":"method","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"module"},{"name":"createResourceExcludeMatcher","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"createRotatingLogger","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/spdlog/index"},{"name":"createRotatingLogger","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/node/spdlogService"},{"name":"createRotatingLoggerAsync","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/spdlog/index"},{"name":"createScanner","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"createScopedLineTokens","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports"},{"name":"createSecretKey","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createSecureContext","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"createSecurePair","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"createSecureServer","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"http2"},{"name":"createSerializedGrid","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/grid"},{"name":"createServer","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"createServer","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"http2"},{"name":"createServer","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"https"},{"name":"createServer","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"net"},{"name":"createServer","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"createSign","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createSimpleKeybinding","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keyCodes"},{"name":"createSingleEditOp","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/browser/commands/shiftCommand.test"},{"name":"createSingleEditOp","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/browser/commands/trimTrailingWhitespaceCommand.test"},{"name":"createSlowExtensionAction","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions"},{"name":"createSocket","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dgram"},{"name":"createStringBuilder","kind":"let","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/stringBuilder"},{"name":"createStubInstance","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"createStyleSheet","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"createSuggestItem","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/test/completionModel.test"},{"name":"createSyncDescriptor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/descriptors"},{"name":"createTerminalEnvironment","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment"},{"name":"createTestCodeEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/browser/testCodeEditor"},{"name":"createTextBuffer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"createTextBufferFactory","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"createTextBufferFactoryFromSnapshot","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"createTextBufferFactoryFromStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"createTextModel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/editorTestUtils"},{"name":"createTextSearchResult","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepSearchUtils"},{"name":"createTOCIterator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/tocTree"},{"name":"createTokenizationSupport","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchLexer"},{"name":"createTracing","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"trace_events"},{"name":"createUintArray","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase"},{"name":"createUnzip","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"createUpdateURL","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/electron-main/abstractUpdateService"},{"name":"createValidator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesValidation"},{"name":"createVerify","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createWaitMarkerFile","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/waitMarkerFile"},{"name":"createWebWorker","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/webWorker"},{"name":"createWebWorker","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"createWriteStream","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"Credential","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CredentialsContainer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Critical","kind":"enum member","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/SeverityLevel"},{"name":"crypto","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Crypto","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CryptoKey","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CryptoKeyPair","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSS","kind":"module","kindModifiers":"declare","sortText":"4"},{"name":"CSSConditionRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"cssEscape","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/getIconClasses"},{"name":"CSSFontFaceRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSGroupingRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSImportRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSKeyframeRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSKeyframesRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSMediaRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSNamespaceRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSPageRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSRuleList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSStyleDeclaration","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSStyleRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSStyleSheet","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSSupportsRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ctxCommentEditorFocused","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/simpleCommentEditor"},{"name":"ctxHasSymbols","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/symbolNavigation"},{"name":"ctxReferenceSearchVisible","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesController"},{"name":"CurrentLineHighlightOverlay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight"},{"name":"CurrentLineMarginHighlightOverlay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight"},{"name":"currentSchemaVersion","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/experiments/common/experimentService"},{"name":"currentSessionDateStorageKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetry"},{"name":"Cursor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursor"},{"name":"CursorAtBoundary","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookBrowser"},{"name":"CursorChangeReason","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorEvents"},{"name":"CursorChangeReason","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"CursorCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCollection"},{"name":"CursorColumns","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"CursorConfiguration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"CursorContext","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"CursorModelState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursor"},{"name":"CursorMove","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorMoveCommands"},{"name":"CursorMoveCommands","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorMoveCommands"},{"name":"CursorPosition","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorMoveOperations"},{"name":"CursorRedo","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/cursorUndo/cursorUndo"},{"name":"CursorState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"CursorStateChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursor"},{"name":"cursorStyleToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"cursorTo","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"readline"},{"name":"CursorUndo","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/cursorUndo/cursorUndo"},{"name":"CursorUndoRedoController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/cursorUndo/cursorUndo"},{"name":"CursorWordAccessibilityLeft","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordAccessibilityLeftSelect","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordAccessibilityRight","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordAccessibilityRightSelect","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordEndLeft","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordEndLeftSelect","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordEndRight","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordEndRightSelect","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordLeft","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordLeftSelect","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordPartLeft","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordPartOperations/wordPartOperations"},{"name":"CursorWordPartLeftSelect","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordPartOperations/wordPartOperations"},{"name":"CursorWordPartRight","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordPartOperations/wordPartOperations"},{"name":"CursorWordPartRightSelect","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordPartOperations/wordPartOperations"},{"name":"CursorWordRight","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordRightSelect","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordStartLeft","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordStartLeftSelect","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordStartRight","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordStartRightSelect","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CustomEditorContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/browser/customEditors"},{"name":"CustomEditorInfo","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/customEditor"},{"name":"CustomEditorInfoCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/customEditor"},{"name":"CustomEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/browser/customEditorInput"},{"name":"CustomEditorInputFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory"},{"name":"CustomEditorModelManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/customEditorModelManager"},{"name":"CustomEditorPriority","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/customEditor"},{"name":"customEditorsAssociationsSettingId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/browser/editorAssociationsSetting"},{"name":"CustomEditorService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/browser/customEditors"},{"name":"customEditorsExtensionPoint","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/extensionPoint"},{"name":"CustomElementRegistry","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"customElements","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CustomEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CustomExecution","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CustomExecution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CustomExecution2","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CustomExecutionDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"CUSTOMIZED_TASK_TYPE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"CustomMenubarControl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/titlebar/menubarControl"},{"name":"CustomTask","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"CustomTextEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/customTextEditorModel"},{"name":"CustomTreeView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/treeView"},{"name":"cutFileHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"CutWebviewEditorCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/electron-browser/webviewCommands"},{"name":"cwd","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/process"},{"name":"cwd","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"DARK","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"darken","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"DarwinUpdateService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/electron-main/updateService.darwin"},{"name":"data","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/common/filters.perf.data"},{"name":"Data","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/Data"},{"name":"Data","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"Database","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-sqlite3/index"},{"name":"DataBreakpoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DataBreakpoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"DataCue","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DataPoint","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/DataPoint"},{"name":"DataPoint","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"DataPointType","kind":"enum","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/DataPointType"},{"name":"DataPointType","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"DataSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesTree"},{"name":"DataSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree"},{"name":"DataTransfer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DataTransferItem","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DataTransferItemList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DataTransfers","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dnd"},{"name":"DataTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/dataTree"},{"name":"DataUri","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"DataView","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Date","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"debounce","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/decorators"},{"name":"debug","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"debug","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"Debug","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/diff/diff"},{"name":"DEBUG_HELPER_SERVICE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"DEBUG_PANEL_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"DEBUG_SCHEME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"DEBUG_SERVICE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"DebugAdapterExecutable","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DebugAdapterExecutable","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DebugAdapterInlineImplementation","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DebugAdapterInlineImplementation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DebugAdapterServer","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DebugAdapterServer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DebugConsoleMode","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DebugConsoleMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DebugContentProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugContentProvider"},{"name":"debugExceptionWidgetBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/exceptionWidget"},{"name":"debugExceptionWidgetBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/exceptionWidget"},{"name":"DebugExtensionHostAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"debugger","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Debugger","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"Debugger","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugger"},{"name":"debuggersExtPoint","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugSchemas"},{"name":"DebugHoverWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugHover"},{"name":"debugIconContinueForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugIconDisconnectForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugIconPauseForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugIconRestartForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugIconStartForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugIconStepBackForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugIconStepIntoForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugIconStepOutForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugIconStepOverForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugIconStopForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debuglog","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"DebugModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"debugPort","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"DebugProgressContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugProgress"},{"name":"DebugService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugService"},{"name":"DebugSession","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugSession"},{"name":"DebugStatusContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugStatus"},{"name":"DebugTaskRunner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugTaskRunner"},{"name":"DebugToolBar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugToolBarBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugToolBarBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"DebugViewPaneContainer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugViewlet"},{"name":"Decipher","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"DeclarationProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"declare","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"decode","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"punycode"},{"name":"decode","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"querystring"},{"name":"decode","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/iconv-lite/lib/index"},{"name":"decodeSemanticTokensDto","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/shared/semanticTokensDto"},{"name":"decodeStream","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/iconv-lite/lib/index"},{"name":"decodeURI","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"decodeURIComponent","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"decodeUTF16LE","kind":"let","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/stringBuilder"},{"name":"decodeUTF8","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"decompress","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/compressedObjectTreeModel"},{"name":"Decoration","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Decoration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DecorationRangeBehavior","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DecorationRangeBehavior","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DecorationRangeBehavior","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"DecorationRenderOptions","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"DecorationSegment","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/lineDecorations"},{"name":"DecorationsOverlay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/decorations/decorations"},{"name":"DecorationsOverviewRuler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler"},{"name":"DecorationsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/decorations/browser/decorationsService"},{"name":"DecorationToRender","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin"},{"name":"DecreaseSearchEditorContextLinesCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"DecreaseViewSizeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"DedupOverlay","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin"},{"name":"deepClone","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/objects"},{"name":"deepEqual","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"deepFreeze","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/objects"},{"name":"deepStrictEqual","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"default","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"DEFAULT_COMMANDS_TO_SKIP_SHELL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"DEFAULT_CUSTOM_EDITOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/browser/editorAssociationsSetting"},{"name":"DEFAULT_ECDH_CURVE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"DEFAULT_EDITOR_MAX_DIMENSIONS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editor"},{"name":"DEFAULT_EDITOR_MIN_DIMENSIONS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editor"},{"name":"DEFAULT_EDITOR_PART_OPTIONS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editor"},{"name":"DEFAULT_ENCODING","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"DEFAULT_LABELS_CONTAINER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/labels"},{"name":"DEFAULT_LETTER_SPACING","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"DEFAULT_LINE_HEIGHT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"DEFAULT_LOG_LEVEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"DEFAULT_PRODUCT_ICON_THEME_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/browser/productIconThemeData"},{"name":"DEFAULT_PRODUCT_ICON_THEME_SETTING_VALUE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/themeConfiguration"},{"name":"DEFAULT_SETTINGS_EDITOR_SETTING","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferences"},{"name":"DEFAULT_SETTINGS_EDITOR_SETTING","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"DEFAULT_TERMINAL_OSX","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService"},{"name":"DEFAULT_VALUE","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"DEFAULT_WORD_REGEXP","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/wordHelper"},{"name":"defaultApp","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"defaultBreadcrumbsStyles","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"defaultCipherList","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"defaultClient","kind":"let","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/applicationinsights"},{"name":"DefaultConfigurationExportHelper","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/configExporter/electron-browser/configurationExportHelper"},{"name":"DefaultConfigurationModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationModels"},{"name":"defaultCoreCipherList","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"defaultCustomEditor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors"},{"name":"DefaultDeserializer","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"defaultDialogStyles","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"DefaultElementMapper","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/compressedObjectTreeModel"},{"name":"DefaultEndOfLine","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"DefaultEndOfLine","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"defaultGenerator","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/idGenerator"},{"name":"defaultInsertColor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"defaultKeybindingsContents","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"DefaultKeybindingsEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"DefaultKeyboardNavigationDelegate","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listWidget"},{"name":"defaultListStyles","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"defaultMaxListeners","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"stream"},{"name":"defaultMenuStyles","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"DefaultPaneDndController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/splitview/paneview"},{"name":"DefaultPreferencesEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesEditor"},{"name":"DefaultPreferencesEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesEditorInput"},{"name":"defaultQuickAccessContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/quickaccess"},{"name":"defaultQuickAccessContextKeyValue","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/quickaccess"},{"name":"DefaultQuickAccessFilterValue","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/common/quickAccess"},{"name":"DefaultRawSettingsEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"DefaultRecommendedExtensionsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews"},{"name":"defaultRemoveColor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"DefaultRoleName","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Context"},{"name":"defaultSearchConfig","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization"},{"name":"DefaultSerializer","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"DefaultSettings","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"DefaultSettingsEditorContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesEditor"},{"name":"DefaultSettingsEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"DefaultSettingsHeaderWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesWidgets"},{"name":"DefaultSettingsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesRenderers"},{"name":"defaultSettingsSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"defaultStatus","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DefaultStyleController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listWidget"},{"name":"defaultStyles","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/selectBox/selectBox"},{"name":"DefaultURITransformer","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uriIpc"},{"name":"defaultWebSocketFactory","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/browser/browserSocketFactory"},{"name":"defaultWindowState","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-main/window"},{"name":"DefaultWorkerFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/worker/defaultWorkerFactory"},{"name":"DeferredPermissionRequest","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DeferredPromise","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/common/utils"},{"name":"define","kind":"const","kindModifiers":"declare","sortText":"4"},{"name":"DefineKeybindingController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution"},{"name":"DefineKeybindingOverlayWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/keybindingWidgets"},{"name":"DefineKeybindingWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/keybindingWidgets"},{"name":"defineTheme","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"DefinitionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/goToCommands"},{"name":"DefinitionLink","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"DefinitionProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"deflate","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"deflateRaw","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"deflateRawSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"deflateSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"DelayedDragHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dnd"},{"name":"DelayedPagedModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/paging"},{"name":"Delayer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"DelayNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Delegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesTree"},{"name":"Delegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsList"},{"name":"DelegatedLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"DelegatingEditorService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/browser/editorService"},{"name":"delete","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"DeleteAllLeftAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"DeleteAllRightAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"DeleteCellEdit","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/cellEdit"},{"name":"deleteFileHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"DeleteLinesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"DeleteOperations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorDeleteOperations"},{"name":"deletePassword","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/keytar/keytar"},{"name":"deletePassword","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/keytar/index"},{"name":"DeleteWordCommand","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"DeleteWordEndLeft","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"DeleteWordEndRight","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"DeleteWordLeft","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"DeleteWordLeftCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"DeleteWordPartLeft","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordPartOperations/wordPartOperations"},{"name":"DeleteWordPartRight","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordPartOperations/wordPartOperations"},{"name":"DeleteWordRight","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"DeleteWordRightCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"DeleteWordStartLeft","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"DeleteWordStartRight","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"delimiter","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"delimiter","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"delta","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"DeltaExtensionsResult","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionDescriptionRegistry"},{"name":"departFocus","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"DependsOrder","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"deprecate","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"Deprecated_RemoteAuthorityContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/contextkeys"},{"name":"describe","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"descriptionForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"deserialize","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"deserializeEnvironmentVariableCollection","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/environmentVariableShared"},{"name":"deserializePipePositions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/test/wordTestUtils"},{"name":"Deserializer","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"deserializeSearchError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"desktopCapturer","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"DesktopDragAndDropData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listView"},{"name":"DesktopHostService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/host/electron-browser/desktopHostService"},{"name":"DESTRUCTION","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"detect","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/jschardet/index"},{"name":"detectAvailableShells","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/node/terminal"},{"name":"detectEncodingByBOM","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/node/encoding/encoding.test"},{"name":"detectEncodingByBOMFromBuffer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"detectEncodingFromBuffer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"DetectIndentation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"detectModeId","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/getIconClasses"},{"name":"DeviceAcceleration","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DeviceLightEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DeviceMotionEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DeviceOrientationEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"devicePixelRatio","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DeviceRotationRate","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DH_CHECK_P_NOT_PRIME","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"DH_CHECK_P_NOT_SAFE_PRIME","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"DH_NOT_SUITABLE_GENERATOR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"DH_UNABLE_TO_CHECK_GENERATOR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"Diagnostic","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Diagnostic","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"Diagnostic","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"DiagnosticCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDiagnostics"},{"name":"DiagnosticRelatedInformation","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DiagnosticRelatedInformation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DiagnosticRelatedInformation","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"DiagnosticsChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/diagnostics/node/diagnosticsIpc"},{"name":"DiagnosticSeverity","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DiagnosticSeverity","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DiagnosticSeverity","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"DiagnosticsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/diagnostics/node/diagnosticsService"},{"name":"DiagnosticsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/diagnostics/node/diagnosticsIpc"},{"name":"DiagnosticTag","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DiagnosticTag","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DiagnosticTag","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"dialog","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"Dialog","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/dialog/dialog"},{"name":"DialogChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/dialogs/electron-browser/dialogIpc"},{"name":"DialogMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/dialogs/electron-main/dialogs"},{"name":"DialogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/browser/dialogService"},{"name":"DialogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/electron-browser/dialogService"},{"name":"didBindWorkbenchListAutomaticKeyboardNavigation","kind":"let","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"DidChangeContentEmitter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"DidChangeDecorationsEmitter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"didUseCachedData","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/timer/electron-browser/timerService"},{"name":"diff","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"diff","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"DiffAPICommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/apiCommands"},{"name":"diffBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"DiffChange","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/diff/diffChange"},{"name":"DiffComputer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/diff/diffComputer"},{"name":"DiffEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor/diffEditorInput"},{"name":"DiffEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor/diffEditorModel"},{"name":"DiffEditorState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorBrowser"},{"name":"DiffEditorWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/widget/diffEditorWidget"},{"name":"DiffieHellman","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"diffInserted","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"diffInsertedOutline","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"DiffNavigator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/widget/diffNavigator"},{"name":"diffRemoved","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"diffRemovedOutline","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"DiffReview","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/widget/diffReview"},{"name":"Dimension","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"dir","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"Dir","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"Direction","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/menu/menu"},{"name":"Direction","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/grid"},{"name":"Direction","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminal"},{"name":"Dirent","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"dirExists","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"dirname","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"dirname","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"dirname","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"DirtyDiffController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"DirtyDiffModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"DirtyDiffWorkbenchController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"DirtyEditorContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"DirtyFilesIndicator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/dirtyFilesIndicator"},{"name":"DirtyWorkingCopiesContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"dirxml","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"DisableAllAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"DisableAllBreakpointsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"DisableAllWorkspaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"DisableAutoUpdateAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"DISABLED_EXTENSIONS_STORAGE_PATH","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"DisabledExtensionsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews"},{"name":"DisableDropDownAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"DisableForWorkspaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"DisableGloballyAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"disconnect","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"disconnect","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"DISCONNECT_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"DISCONNECT_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"DiskFileSystemProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/diskFileSystemProvider"},{"name":"DiskFileSystemProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/electron-browser/diskFileSystemProvider"},{"name":"DiskSearch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/searchService"},{"name":"DispatchConfig","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/dispatchConfig"},{"name":"dispatchEvent","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"Disposable","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Disposable","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/lifecycle"},{"name":"Disposable","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"Disposable","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/workbench.web.api"},{"name":"DisposableStore","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/lifecycle"},{"name":"disposableTimeout","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"dispose","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/lifecycle"},{"name":"dispose","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/applicationinsights"},{"name":"disposed","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"distinct","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"distinct","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/objects"},{"name":"distinctES6","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"distinctParents","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"do","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"doBenchmark","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/benchmark/benchmarkUtils"},{"name":"Dock","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"document","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Document","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"documentationExtensionPointDescriptor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeActions/common/documentationExtensionPoint"},{"name":"DocumentationExtensionPointFields","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeActions/common/documentationExtensionPoint"},{"name":"DocumentFormattingEditProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"DocumentFragment","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DocumentHighlight","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DocumentHighlight","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DocumentHighlight","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"DocumentHighlightKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DocumentHighlightKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"DocumentHighlightKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DocumentHighlightKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"DocumentHighlightProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"DocumentLink","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DocumentLink","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DocumentLink","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"DocumentRangeFormattingEditProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"DocumentRangeSemanticTokensAdapter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostLanguageFeatures"},{"name":"DocumentRangeSemanticTokensProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"DocumentSemanticTokensAdapter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostLanguageFeatures"},{"name":"DocumentSemanticTokensProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"DocumentSymbol","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DocumentSymbol","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DocumentSymbol","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"DocumentSymbolProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"DocumentTimeline","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DocumentType","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"doesNotReject","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"doesNotThrow","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"domain","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"Domain","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"domain"},{"name":"Domain","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/Domain"},{"name":"Domain","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"domainSupportsProperties","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Constants"},{"name":"domainToASCII","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"url"},{"name":"domainToUnicode","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"url"},{"name":"domContentLoaded","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"DOMError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"domEvent","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/event"},{"name":"DOMException","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMImplementation","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMLineBreaksComputerFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/domLineBreaksComputer"},{"name":"DOMMatrix","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMMatrixReadOnly","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMParser","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMPoint","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMPointReadOnly","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMQuad","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DomReadingContext","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/lines/viewLine"},{"name":"DOMRect","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMRectList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMRectReadOnly","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DomScrollableElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/scrollableElement"},{"name":"DOMSettableTokenList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMStringList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMStringMap","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMTokenList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"doNotTrack","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"dosDateTimeToDate","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/yauzl/index"},{"name":"DOWNLOAD_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"DOWNLOAD_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"DownloadItem","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"DownloadService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/download/common/downloadService"},{"name":"DownloadServiceChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/download/common/downloadIpc"},{"name":"DownloadServiceChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/download/common/downloadIpc"},{"name":"DragAndDropCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/dnd/dragAndDropCommand"},{"name":"DragAndDropController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/dnd/dnd"},{"name":"DragAndDropData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dnd"},{"name":"DragAndDropObserver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"DragEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DraggedCompositeIdentifier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"DraggedEditorGroupIdentifier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"DraggedEditorIdentifier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"DraggedViewIdentifier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"DragMouseEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/mouseEvent"},{"name":"Driver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/electron-main/driver"},{"name":"DriverChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/node/driver"},{"name":"DriverChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/node/driver"},{"name":"Dropdown","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/dropdown/dropdown"},{"name":"DropdownMenu","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/dropdown/dropdown"},{"name":"DropdownMenuActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/dropdown/dropdown"},{"name":"DropDownMenuActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"Duplex","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"stream"},{"name":"DuplicateSelectionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"DuplicateWorkspaceInNewWindowAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceActions"},{"name":"DynamicsCompressorNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DynamicStandaloneServices","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneServices"},{"name":"DynamicViewOverlay","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/dynamicViewOverlay"},{"name":"DynamicWebviewEditorOverlay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay"},{"name":"DynamicWorkspaceRecommendations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations"},{"name":"E2BIG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EACCES","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EADDRINUSE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EADDRNOTAVAIL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EAFNOSUPPORT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EAGAIN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EALREADY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EBADF","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EBADMSG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EBUSY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ECANCELED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ECDH","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"ECHILD","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ECONNABORTED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ECONNREFUSED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ECONNRESET","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EDEADLK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EDESTADDRREQ","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"edit","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/content"},{"name":"EditableConfigurationTarget","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configurationEditingService"},{"name":"EditOperation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/editOperation"},{"name":"EditOperationResult","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"EditOperationType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"editor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"EDITOR_BOTTOM_PADDING","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"EDITOR_CONTRIBUTION_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"EDITOR_DRAG_AND_DROP_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EDITOR_FONT_DEFAULTS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"EDITOR_GROUP_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EDITOR_GROUP_EMPTY_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EDITOR_GROUP_FOCUSED_EMPTY_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EDITOR_GROUP_HEADER_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EDITOR_GROUP_HEADER_TABS_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EDITOR_GROUP_HEADER_TABS_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EDITOR_MODEL_DEFAULTS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"EDITOR_PANE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EDITOR_TITLE_HEIGHT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editor"},{"name":"EDITOR_TOOLBAR_HEIGHT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"EDITOR_TOP_MARGIN","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"EDITOR_TOP_PADDING","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"EditorAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"EditorActivation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/editor/common/editor"},{"name":"editorActiveIndentGuides","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorActiveLineNumber","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorActiveLinkForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"EditorAreaVisibleContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"editorAssociationsConfigurationNode","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/browser/editorAssociationsSetting"},{"name":"EditorAutoIndentStrategy","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"EditorAutoIndentStrategy","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"EditorAutoSave","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorAutoSave"},{"name":"editorBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorBracketMatchBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorBracketMatchBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"EditorBreadcrumbsModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbsModel"},{"name":"editorCodeLensForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"EditorCommand","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"EditorCommandsContextActionRunner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"editorConfiguration","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeActions/common/codeActionsContribution"},{"name":"editorConfigurationBaseNode","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/commonEditorConfig"},{"name":"EditorContextKeys","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/editorContextKeys"},{"name":"EditorControl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorControl"},{"name":"editorCursorBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorCursorForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"EditorDescriptor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/editor"},{"name":"EditorDropTarget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorDropTarget"},{"name":"editorErrorBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorErrorForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"EditorExtensionsRegistry","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"editorFindMatch","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorFindMatchBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorFindMatchHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorFindMatchHighlightBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorFindRangeHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorFindRangeHighlightBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"EditorFontLigatures","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"editorForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"EditorGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor/editorGroup"},{"name":"EditorGroupActiveEditorDirtyContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"EditorGroupEditorsCountContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"editorGroupToViewColumn","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/shared/editor"},{"name":"EditorGroupView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorGroupView"},{"name":"editorGutter","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorGutterAddedBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"editorGutterDeletedBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"editorGutterModifiedBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"editorHintBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorHintForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorHoverBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorHoverBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorHoverForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorHoverHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorHoverStatusBarBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorInactiveSelection","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorIndentGuides","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorInfoBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorInfoForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"EditorInput","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"EditorKeybindingCancellationTokenSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/core/keybindingCancellation"},{"name":"EditorLayoutInfoComputer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"EditorLayoutSingleAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"EditorLayoutThreeColumnsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"EditorLayoutThreeRowsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"EditorLayoutTwoByTwoGridAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"EditorLayoutTwoColumnsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"EditorLayoutTwoColumnsBottomAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"EditorLayoutTwoRowsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"EditorLayoutTwoRowsRightAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"editorLightBulbAutoFixForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorLightBulbForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorLineHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorLineHighlightBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorLineNumbers","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorMarkerNavigationBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoError/gotoErrorWidget"},{"name":"editorMarkerNavigationError","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoError/gotoErrorWidget"},{"name":"editorMarkerNavigationInfo","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoError/gotoErrorWidget"},{"name":"editorMarkerNavigationWarning","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoError/gotoErrorWidget"},{"name":"editorMatchesToTextSearchResults","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/searchHelpers"},{"name":"EditorMemento","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/baseEditor"},{"name":"EditorModeContext","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/widget/codeEditorWidget"},{"name":"EditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"EditorModesRegistry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/modesRegistry"},{"name":"EditorMouseEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorDom"},{"name":"EditorMouseEventFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorDom"},{"name":"EditorOpenContext","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/editor/common/editor"},{"name":"EditorOption","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"EditorOption","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"EditorOptions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"EditorOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"editorOptionsRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"editorOverviewRulerBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"EditorPagePosition","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorDom"},{"name":"EditorPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorPart"},{"name":"EditorPinnedContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"EditorPointerEventFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorDom"},{"name":"EditorProgressIndicator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/progress/browser/progressIndicator"},{"name":"editorRangeHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorRangeHighlightBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorRuler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"EditorScopedQuickInputServiceImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputServiceImpl"},{"name":"EditorScroll_","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/coreCommands"},{"name":"EditorScrollbar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar"},{"name":"editorSelectionBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorSelectionForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorSelectionHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorSelectionHighlightBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"EditorService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/browser/editorService"},{"name":"EditorSimpleWorker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/editorSimpleWorker"},{"name":"EditorsObserver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorsObserver"},{"name":"EditorsOrder","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"EditorState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/core/editorState"},{"name":"EditorStateCancellationTokenSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/core/editorState"},{"name":"EditorStatus","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorStatus"},{"name":"editorSuggestWidgetBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestWidget"},{"name":"editorSuggestWidgetBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestWidget"},{"name":"editorSuggestWidgetForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestWidget"},{"name":"editorSuggestWidgetHighlightForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestWidget"},{"name":"editorSuggestWidgetSelectedBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestWidget"},{"name":"EditorsVisibleContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"editorSymbolHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorSymbolHighlightBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"EditorTheme","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewContext"},{"name":"EditorType","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/editorCommon"},{"name":"editorUnnecessaryCodeBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorUnnecessaryCodeOpacity","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"EditorWalkThroughAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough"},{"name":"EditorWalkThroughInputFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough"},{"name":"editorWarningBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorWarningForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"EditorWhitespace","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/linesLayout"},{"name":"editorWhitespaces","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorWidgetBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorWidgetBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorWidgetForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorWidgetResizeBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"EditorWorkerClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/editorWorkerServiceImpl"},{"name":"EditorWorkerHost","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/editorWorkerServiceImpl"},{"name":"EditorWorkerServiceImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/editorWorkerServiceImpl"},{"name":"EditorZoom","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorZoom"},{"name":"EditPreferenceWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesWidgets"},{"name":"EditStack","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/editStack"},{"name":"EDOM","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EEXIST","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EFAULT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EFBIG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EHOSTUNREACH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EIDRM","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EILSEQ","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EINPROGRESS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EINTR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EINVAL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EIO","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EISCONN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EISDIR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"electron","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"Electron","kind":"module","kindModifiers":"declare","sortText":"4"},{"name":"ElectronAcceleratorLabelProvider","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keybindingLabels"},{"name":"ElectronMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/electron/electron-main/electronMainService"},{"name":"ElectronService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/electron/electron-browser/electronService"},{"name":"ElectronURLListener","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/url/electron-main/electronUrlListener"},{"name":"ElectronWebviewBasedWebview","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/electron-browser/webviewElement"},{"name":"ElectronWebviewService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/electron-browser/webviewService"},{"name":"Element","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ElementsDragAndDropData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listView"},{"name":"ElementSizeObserver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/config/elementSizeObserver"},{"name":"ELOOP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"else","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"EmbeddedCodeEditorWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/widget/embeddedCodeEditorWidget"},{"name":"EmbeddedDiffEditorWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/widget/embeddedCodeEditorWidget"},{"name":"embeddedEditorBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart"},{"name":"EMFILE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"emit","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"emit","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"emitKeypressEvents","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"readline"},{"name":"Emitter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/event"},{"name":"Emitter","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/workbench.web.api"},{"name":"Emitter","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"emitWarning","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"EMLINK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EmmetEditorAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/emmet/browser/emmetActions"},{"name":"empty","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"EmptyExtension","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostExtensionActivator"},{"name":"EmptyPane","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/scmViewlet"},{"name":"EmptyPaneDescriptor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/scmViewlet"},{"name":"emptyProgressRunner","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/progress/common/progress"},{"name":"EmptyView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/emptyView"},{"name":"EMSGSIZE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EnableAllAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"EnableAllBreakpointsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"EnableAllWorkspaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"EnableAutoUpdateAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ENABLED_EXTENSIONS_STORAGE_PATH","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"enableDebug","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/jschardet/index"},{"name":"EnabledExtensionsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews"},{"name":"EnableDropDownAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"EnableForWorkspaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"EnableGloballyAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"Enablement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"EnablementState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/common/extensionManagement"},{"name":"enablePromiseAPIs","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"ENAMETOOLONG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"encode","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"punycode"},{"name":"encode","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"querystring"},{"name":"encode","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/iconv-lite/lib/index"},{"name":"EncodedTokenizationSupport2Adapter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"encodeSemanticTokensDto","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/shared/semanticTokensDto"},{"name":"encodeStream","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/iconv-lite/lib/index"},{"name":"encodeStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"encodeURI","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"encodeURIComponent","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"encodeUTF8","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"encodingExists","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/iconv-lite/lib/index"},{"name":"encodingExists","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"EncodingMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"EncodingOracle","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService"},{"name":"endianness","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"EndOfLine","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"EndOfLine","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"EndOfLine","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"EndOfLinePreference","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"EndOfLinePreference","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"EndOfLineSequence","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"EndOfLineSequence","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"endsWith","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"ENETDOWN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENETRESET","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENETUNREACH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENFILE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"Engine","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/fileSearch"},{"name":"ENGINE_METHOD_ALL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_CIPHERS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_DH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_DIGESTS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_DSA","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_ECDH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_ECDSA","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_NONE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_PKEY_ASN1_METHS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_PKEY_METHS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_RAND","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_RSA","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_STORE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOBUFS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENODATA","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENODEV","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOENT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOEXEC","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOLCK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOLINK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOMEM","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOMSG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOPROTOOPT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOSPC","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOSR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOSTR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOSYS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOTCONN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOTDIR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOTEMPTY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOTSOCK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOTSUP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOTTY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ensureFileSystemProviderError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"ensureValidWordDefinition","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/wordHelper"},{"name":"Entry","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/yauzl/index"},{"name":"enum","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"env","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"env","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/process"},{"name":"env","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"ENV_azurePrefix","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Config"},{"name":"ENV_http_proxy","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Config"},{"name":"ENV_https_proxy","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Config"},{"name":"ENV_iKey","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Config"},{"name":"ENV_profileQueryEndpoint","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Config"},{"name":"Envelope","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/Envelope"},{"name":"Envelope","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"EnvironmentService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/environmentService"},{"name":"EnvironmentVariableCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTerminalService"},{"name":"EnvironmentVariableMutatorType","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"EnvironmentVariableMutatorType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/environmentVariable"},{"name":"EnvironmentVariableMutatorType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"EnvironmentVariableService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/environmentVariableService"},{"name":"ENXIO","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EOF","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"EOL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"EOPNOTSUPP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EOVERFLOW","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EPERM","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EPIPE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EPROTO","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EPROTONOSUPPORT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EPROTOTYPE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"eq","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"equal","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"equals","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"equals","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/objects"},{"name":"equalsIgnoreCase","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"ERANGE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EROFS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"error","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"Error","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Error","kind":"enum member","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/SeverityLevel"},{"name":"ErrorEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ErrorEvent","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/errorTelemetry"},{"name":"errorForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"errorHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"ErrorHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"ErrorScope","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"ErrorTelemetry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/browser/errorTelemetry"},{"name":"ErrorTelemetry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/node/errorTelemetry"},{"name":"escape","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"escape","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"querystring"},{"name":"escape","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"escapeCodicons","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/codicons"},{"name":"escapeNonWindowsPath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment"},{"name":"escapeRegExpCharacters","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"ESPIPE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ESRCH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"etag","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"ETAG_DISABLED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"ETIME","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ETIMEDOUT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ETXTBSY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"eval","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"EvalError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"EvaluatableExpression","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"EvaluatableExpression","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"EvaluatableExpression","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"EvaluatableExpressionProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"event","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Event","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Event","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/event"},{"name":"Event","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/workbench.web.api"},{"name":"EventBufferer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/event"},{"name":"EventData","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/EventData"},{"name":"EventData","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"EventEmitter","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"EventEmitter","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"events"},{"name":"EventHelper","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"EventMultiplexer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/event"},{"name":"eventNames","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"eventNames","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"EventSource","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"EventTarget","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"EventType","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"EventType","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/touch"},{"name":"EWOULDBLOCK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"exception","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"ExceptionBreakpoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"ExceptionData","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/ExceptionData"},{"name":"ExceptionData","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"ExceptionDetails","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/ExceptionDetails"},{"name":"ExceptionDetails","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"ExceptionWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/exceptionWidget"},{"name":"ExcludePatternInputWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/patternInputWidget"},{"name":"ExcludeSettingWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"EXDEV","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ExeBasedRecommendations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations"},{"name":"exec","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"child_process"},{"name":"exec","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/sudo-prompt/index"},{"name":"execArgv","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"execFile","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"child_process"},{"name":"execFileSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"child_process"},{"name":"execPath","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"execSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"child_process"},{"name":"ExecutableDebugAdapter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/node/debugAdapter"},{"name":"ExecuteCellAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/contrib/notebookActions"},{"name":"ExecuteCommandAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"ExecuteCommandAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"executionAsyncId","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"async_hooks"},{"name":"ExecutionEngine","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"ExecutionEngine","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskConfiguration"},{"name":"exists","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"exists","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"existsSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"exit","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/native-watchdog/index"},{"name":"exit","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"exitCode","kind":"property","kindModifiers":"declare,optional","sortText":"5","hasAction":true,"source":"process"},{"name":"EXPAND_NOTIFICATION","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"ExpandAllAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"ExpandNotificationAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsActions"},{"name":"expectation","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"ExperimentActionType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/experiments/common/experimentService"},{"name":"experimental","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/colorRegistry.releaseTest"},{"name":"ExperimentalPrompts","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/experiments/browser/experimentalPrompt"},{"name":"ExperimentalRecommendations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/experimentalRecommendations"},{"name":"ExperimentService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/experiments/common/experimentService"},{"name":"ExperimentState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/experiments/common/experimentService"},{"name":"ExplorerCompressedFirstFocusContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerCompressedFocusContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerCompressedLastFocusContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerCompressionDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerViewer"},{"name":"ExplorerDataSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerViewer"},{"name":"ExplorerDecorationsProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerDecorationsProvider"},{"name":"ExplorerDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerViewer"},{"name":"ExplorerFocusCondition","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerFocusedContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerFolderContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/explorerModel"},{"name":"ExplorerModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/explorerModel"},{"name":"ExplorerResourceCut","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerResourceMoveableToTrash","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerResourceNotReadonlyContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerResourceReadonlyContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerRootContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"explorerRootErrorEmitter","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerViewer"},{"name":"ExplorerService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/explorerService"},{"name":"ExplorerView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerView"},{"name":"ExplorerViewletViewsContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/explorerViewlet"},{"name":"ExplorerViewletVisibleContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerViewPaneContainer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/explorerViewlet"},{"name":"export","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"exportEntries","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/performance"},{"name":"exports","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Expression","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"ExpressionContainer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"EXT_HOST_CREATION_DELAY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"extends","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"EXTENSION_BADGE_REMOTE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EXTENSION_BADGE_REMOTE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EXTENSION_IDENTIFIER_PATTERN","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"EXTENSION_IDENTIFIER_REGEX","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"EXTENSION_SETTING_TAG","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"ExtensionAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ExtensionActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ExtensionActivationProgress","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActivationProgress"},{"name":"ExtensionActivationTimes","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostExtensionActivator"},{"name":"ExtensionActivationTimesBuilder","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostExtensionActivator"},{"name":"extensionButtonProminentBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"extensionButtonProminentForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"extensionButtonProminentHoverBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ExtensionContainers","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"ExtensionData","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/workbenchThemeService"},{"name":"ExtensionData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewer"},{"name":"ExtensionDependencyChecker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsDependencyChecker"},{"name":"ExtensionDescriptionRegistry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionDescriptionRegistry"},{"name":"ExtensionDropDownAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ExtensionEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionEditor"},{"name":"ExtensionEditorDropDownAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ExtensionEnablementService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/common/extensionEnablementService"},{"name":"ExtensionGalleryService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionGalleryService"},{"name":"ExtensionHostDebugBroadcastChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/debug/common/extensionHostDebugIpc"},{"name":"ExtensionHostDebugChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/debug/common/extensionHostDebugIpc"},{"name":"ExtensionHostDebugService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/electron-browser/extensionHostDebugService"},{"name":"ExtensionHostLogFileName","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensions"},{"name":"ExtensionHostMain","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionHostMain"},{"name":"ExtensionHostPersistentConnection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"ExtensionHostProcessManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionHostProcessManager"},{"name":"ExtensionHostProcessWorker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/electron-browser/extensionHost"},{"name":"ExtensionHostProfiler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler"},{"name":"ExtensionHostProfileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService"},{"name":"ExtensionIdentifier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensions"},{"name":"ExtensionIdentifierWithVersion","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementUtil"},{"name":"ExtensionKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ExtensionKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ExtensionManagementChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementIpc"},{"name":"ExtensionManagementChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementIpc"},{"name":"ExtensionManagementError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/node/extensionManagementService"},{"name":"ExtensionManagementServerService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService"},{"name":"ExtensionManagementServerService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService"},{"name":"ExtensionManagementService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/common/extensionManagementService"},{"name":"ExtensionManagementService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/node/extensionManagementService"},{"name":"ExtensionManagementService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/node/extensionManagementService"},{"name":"ExtensionMemento","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostMemento"},{"name":"ExtensionMessageCollector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsRegistry"},{"name":"ExtensionPackCountWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWidgets"},{"name":"ExtensionPoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsRegistry"},{"name":"ExtensionPointContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensions"},{"name":"ExtensionPoints","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/extensionHost.contribution"},{"name":"ExtensionPoints","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/configExporter/electron-browser/configurationExportHelper.contribution"},{"name":"ExtensionPointUserDelta","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsRegistry"},{"name":"ExtensionRecommendationReason","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/common/extensionManagement"},{"name":"ExtensionRecommendations","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionRecommendations"},{"name":"ExtensionRecommendationsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService"},{"name":"ExtensionRegistryReporter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"ExtensionRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewer"},{"name":"ExtensionResourceLoaderService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionResourceLoader/electron-browser/extensionResourceLoaderService"},{"name":"extensionResultIsMatch","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/textSearchManager"},{"name":"extensionResultIsMatch","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/api/extHostSearch.test"},{"name":"extensions","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/jsonschemas/common/jsonContributionRegistry"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/keybindingsRegistry"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/common/quickAccess"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/modesRegistry"},{"name":"Extensions","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/contributions"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"Extensions","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/views"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/output/common/output"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/actions"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/viewlet"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/panel"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/editor"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/iconRegistry"},{"name":"EXTENSIONS_CONFIG","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"ExtensionsActivator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostExtensionActivator"},{"name":"ExtensionsAutoProfiler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler"},{"name":"ExtensionScanner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/node/extensionPoints"},{"name":"ExtensionScannerInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/node/extensionPoints"},{"name":"ExtensionsChannelId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"ExtensionsConfigurationInitialContent","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensionsFileTemplate"},{"name":"ExtensionsConfigurationSchema","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensionsFileTemplate"},{"name":"ExtensionsConfigurationSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensionsFileTemplate"},{"name":"ExtensionScriptApis","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ExtensionService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/browser/extensionService"},{"name":"ExtensionService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/electron-browser/extensionService"},{"name":"ExtensionsGridView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewer"},{"name":"ExtensionsInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensionsInput"},{"name":"ExtensionsLabel","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"ExtensionsLifecycle","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/node/extensionLifecycle"},{"name":"ExtensionsListView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews"},{"name":"ExtensionsManifestCache","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/node/extensionsManifestCache"},{"name":"ExtensionsRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsRegistry"},{"name":"ExtensionsRegistryImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsRegistry"},{"name":"ExtensionsSynchroniser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/extensionsSync"},{"name":"ExtensionState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"ExtensionStoragePaths","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostStoragePaths"},{"name":"ExtensionsTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewer"},{"name":"ExtensionsViewletViewsContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewlet"},{"name":"ExtensionsViewPaneContainer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewlet"},{"name":"ExtensionsWorkbenchService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService"},{"name":"ExtensionTipsChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementIpc"},{"name":"ExtensionTipsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/node/extensionTipsService"},{"name":"ExtensionToolTipAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ExtensionType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensions"},{"name":"ExtensionWidget","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWidgets"},{"name":"external","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"External","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ExternalElementsDragAndDropData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listView"},{"name":"ExternalThemeTrieElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/tokenization"},{"name":"ExternalUriResolverContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/url/common/externalUriResolver"},{"name":"ExtHostApiCommands","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostApiCommands"},{"name":"ExtHostApiDeprecationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostApiDeprecationService"},{"name":"ExtHostAuthentication","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostAuthentication"},{"name":"ExtHostCell","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostNotebook"},{"name":"ExtHostClipboard","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostClipboard"},{"name":"ExtHostCommands","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostCommands"},{"name":"ExtHostComments","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostComments"},{"name":"ExtHostCommentThread","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostComments"},{"name":"ExtHostConfigProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostConfiguration"},{"name":"ExtHostConfiguration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostConfiguration"},{"name":"ExtHostContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"extHostCustomer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostCustomers"},{"name":"ExtHostCustomersRegistry","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostCustomers"},{"name":"ExtHostDebugConsole","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDebugService"},{"name":"ExtHostDebugService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostDebugService"},{"name":"ExtHostDebugServiceBase","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDebugService"},{"name":"ExtHostDebugSession","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDebugService"},{"name":"ExtHostDecorations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDecorations"},{"name":"ExtHostDiagnostics","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDiagnostics"},{"name":"ExtHostDialogs","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDialogs"},{"name":"ExtHostDocumentContentProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDocumentContentProviders"},{"name":"ExtHostDocumentData","kind":"alias","kindModifiers":"","sortText":"0"},{"name":"ExtHostDocumentLine","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDocumentData"},{"name":"ExtHostDocuments","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDocuments"},{"name":"ExtHostDocumentsAndEditors","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDocumentsAndEditors"},{"name":"ExtHostDocumentSaveParticipant","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDocumentSaveParticipant"},{"name":"ExtHostDownloadService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostDownloadService"},{"name":"ExtHostEditorInsets","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostCodeInsets"},{"name":"ExtHostEditors","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTextEditors"},{"name":"ExtHostExtensionService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostExtensionService"},{"name":"ExtHostExtensionService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/worker/extHostExtensionService"},{"name":"ExtHostFileSystem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostFileSystem"},{"name":"ExtHostFileSystemEventService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostFileSystemEventService"},{"name":"ExtHostLabelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostLabelService"},{"name":"ExtHostLanguageFeatures","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostLanguageFeatures"},{"name":"ExtHostLanguages","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostLanguages"},{"name":"extHostLogChannelId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/common/logConstants"},{"name":"ExtHostLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostLogService"},{"name":"ExtHostLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/worker/extHostLogService"},{"name":"ExtHostMessageService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostMessageService"},{"name":"extHostNamedCustomer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostCustomers"},{"name":"ExtHostNotebookController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostNotebook"},{"name":"ExtHostNotebookDocument","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostNotebook"},{"name":"ExtHostNotebookEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostNotebook"},{"name":"ExtHostNotebookOutputRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostNotebook"},{"name":"ExtHostOutputChannelBackedByFile","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostOutputService"},{"name":"ExtHostOutputService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostOutput"},{"name":"ExtHostOutputService2","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostOutputService"},{"name":"ExtHostProgress","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostProgress"},{"name":"ExtHostPseudoterminal","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTerminalService"},{"name":"ExtHostPushOutputChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostOutput"},{"name":"ExtHostQuickOpen","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostQuickOpen"},{"name":"ExtHostRpcService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostRpcService"},{"name":"ExtHostSCM","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostSCM"},{"name":"ExtHostSCMInputBox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostSCM"},{"name":"ExtHostSearch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostSearch"},{"name":"ExtHostStatusBar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostStatusBar"},{"name":"ExtHostStatusBarEntry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostStatusBar"},{"name":"ExtHostStorage","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostStorage"},{"name":"ExtHostTask","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostTask"},{"name":"ExtHostTaskBase","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"ExtHostTerminal","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTerminalService"},{"name":"ExtHostTerminalService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostTerminalService"},{"name":"ExtHostTextEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTextEditor"},{"name":"ExtHostTextEditorOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTextEditor"},{"name":"ExtHostTheming","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTheming"},{"name":"ExtHostTimeline","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTimeline"},{"name":"ExtHostTreeViews","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTreeViews"},{"name":"ExtHostTunnelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTunnelService"},{"name":"ExtHostTunnelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostTunnelService"},{"name":"ExtHostUrls","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostUrls"},{"name":"ExtHostVariableResolverService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDebugService"},{"name":"ExtHostWebview","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostWebview"},{"name":"ExtHostWebviewEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostWebview"},{"name":"ExtHostWebviews","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostWebview"},{"name":"ExtHostWindow","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostWindow"},{"name":"ExtHostWorkspace","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostWorkspace"},{"name":"extname","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"extname","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"extname","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"extract","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/zip"},{"name":"ExtractError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/zip"},{"name":"extractLocalHostUriMetaDataForPortMapping","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/tunnel"},{"name":"extractRangeFromFilter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/search"},{"name":"extractResources","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"extractSearchQueryFromLines","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization"},{"name":"extractSearchQueryFromModel","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization"},{"name":"F_OK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"fail","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"FailedExtension","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostExtensionActivator"},{"name":"fakeServer","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"fakeServerWithClock","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"FakeXMLHttpRequest","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"FALLBACK_MAX_MEMORY_SIZE_MB","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/files"},{"name":"false","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"FastDomNode","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/fastDomNode"},{"name":"fchmod","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"fchmodSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"fchown","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"fchownSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"fdatasync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"fdatasyncSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"features","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"FeedbackDropdown","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/feedback/browser/feedback"},{"name":"FeedbackStatusbarConribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem"},{"name":"fetch","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"FetchFileSystemProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/browser/webWorkerFileSystemProvider"},{"name":"File","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"FILE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"FILE_EDITOR_INPUT_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"FileBasedRecommendations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations"},{"name":"FileChangesEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FileChangeType","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"FileChangeType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FileChangeType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"FileChangeType","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/workbench.web.api"},{"name":"FileCopiedContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"FileDialogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/browser/fileDialogService"},{"name":"FileDialogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/electron-browser/fileDialogService"},{"name":"FileDragAndDrop","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerViewer"},{"name":"FileEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/editors/fileEditorInput"},{"name":"FileElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbsModel"},{"name":"FileElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"FileElementRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"fileExists","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"FileFocusKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"FileIconThemeData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/browser/fileIconThemeData"},{"name":"FileKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FileList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"FileLocationKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"FileLoggerService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/fileLogService"},{"name":"FileLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/fileLogService"},{"name":"FileMatch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"FileMatch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchModel"},{"name":"FileMatchOrFolderMatchFocusKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"FileMatchOrFolderMatchWithResourceFocusKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"FileMatchOrMatchFocusKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"FileMatchRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchResultsView"},{"name":"FileOperation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FileOperationError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FileOperationEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FileOperationResult","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FilePreview","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/referencesModel"},{"name":"FileQueryCacheState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/cacheState"},{"name":"FileReader","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"FileReferences","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/referencesModel"},{"name":"FileReferencesRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesTree"},{"name":"FileResourceMarkersRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersTreeViewer"},{"name":"FILES_ASSOCIATIONS_CONFIG","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FILES_EXCLUDE_CONFIG","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FilesConfigurationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService"},{"name":"FileSearchManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/fileSearchManager"},{"name":"FileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/fileService"},{"name":"FilesExplorerFocusCondition","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"FilesExplorerFocusedContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"FilesFilter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerViewer"},{"name":"FileSorter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbsPicker"},{"name":"FileSorter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerViewer"},{"name":"FilesRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerViewer"},{"name":"FileStorage","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/state/node/stateService"},{"name":"FileStorageDatabase","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/browser/storageService"},{"name":"FileSystemError","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"FileSystemError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"FileSystemProviderCapabilities","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FileSystemProviderCapabilities","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/workbench.web.api"},{"name":"FileSystemProviderError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FileSystemProviderErrorCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FileThemeIcon","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"FileType","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"FileType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"fileURLToPath","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"url"},{"name":"FileUserDataProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/userData/common/fileUserDataProvider"},{"name":"FileWalker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/fileSearch"},{"name":"FileWatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/unix/watcherService"},{"name":"FileWatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/win32/watcherService"},{"name":"FileWatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/nsfw/watcherService"},{"name":"FileWatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/nodejs/watcherService"},{"name":"fillResourceDataTransfers","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"Filter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersTreeViewer"},{"name":"FilteredMatchesRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesRenderers"},{"name":"FilterOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersFilterOptions"},{"name":"filtersAction","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/types"},{"name":"filterValidationDecorations","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"FilterViewPaneContainer","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/viewsViewlet"},{"name":"finalHandler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"finally","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"FinalNewLineParticipant","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/saveParticipants"},{"name":"find","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"FIND_IDS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"findBestWindowOrFolderForFile","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/node/window"},{"name":"FindController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"findCredentials","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/keytar/keytar"},{"name":"findCredentials","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/keytar/index"},{"name":"FindDecorations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findDecorations"},{"name":"findExecutable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/node/terminalEnvironment"},{"name":"findExpressionInStackFrame","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugHover"},{"name":"findFirstInSorted","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"findFreePort","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/ports"},{"name":"findFreePortFaster","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/ports"},{"name":"FindInFilesActionId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"FindInFilesCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"FindInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/findinput/findInput"},{"name":"FindMatch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"findMatchingThemeRule","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/common/TMHelper"},{"name":"FindModelBoundToEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"findNodeAtLocation","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"findNodeAtOffset","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"FindOptionOverride","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findState"},{"name":"FindOptionsWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findOptionsWidget"},{"name":"FindOrReplaceInFilesAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"findParentWithClass","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"findPassword","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/keytar/keytar"},{"name":"findPassword","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/keytar/index"},{"name":"FindReplaceState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findState"},{"name":"findRules","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"FindStartFocusAction","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"findValidPasteFileTarget","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"FindWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findWidget"},{"name":"FindWidgetViewZone","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findWidget"},{"name":"findWindowOnExtensionDevelopmentPath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/node/window"},{"name":"findWindowOnWorkspace","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/node/window"},{"name":"findWindowOnWorkspaceOrFolderUri","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/node/window"},{"name":"finished","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"stream"},{"name":"fips","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"first","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"first","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"first","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/collections"},{"name":"FIRST_COMPRESSED_FOLDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"firstIndex","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"FirstMatchFocusKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"firstNonWhitespaceIndex","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"firstOrDefault","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"firstSessionDateStorageKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetry"},{"name":"FixAllAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionCommands"},{"name":"fixAllCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeAction"},{"name":"fixCase","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"fixDriveC","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepFileSearch"},{"name":"fixInsert","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"fixNewline","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepTextSearchEngine"},{"name":"fixPath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/test/browser/queryBuilder.test"},{"name":"fixRegexNewline","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepTextSearchEngine"},{"name":"flatten","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"Float32Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Float64Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"FloatingClickWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorWidgets"},{"name":"focus","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"FOCUS_FIRST_NOTIFICATION_TOAST","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"FOCUS_LAST_NOTIFICATION_TOAST","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"FOCUS_NEXT_NOTIFICATION_TOAST","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"FOCUS_NOTIFICATION_TOAST","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"FOCUS_PREVIOUS_NOTIFICATION_TOAST","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"FOCUS_REPL_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"FocusAboveGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"FocusActiveEditorCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"FocusActiveGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"FocusBelowGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"focusBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"focusedCellIndicator","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookEditor"},{"name":"FocusedViewContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/views"},{"name":"FocusEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"FocusFilesExplorer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"FocusFirstGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"FocusLastGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"FocusLeftGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"FocusNavigationEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"FocusNextGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"FocusNextInputAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"FocusNextSearchResultAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"FocusPreviousGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"FocusPreviousInputAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"FocusPreviousSearchResultAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"FocusQueryEditorWidgetAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"FocusQueryEditorWidgetCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"FocusRightGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"FocusSearchFromResults","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"focusSearchListCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"FocusSearchListCommandID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"FocusSessionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"FocusSessionActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActionViewItems"},{"name":"foldBackgroundBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/folding"},{"name":"FOLDER_CONFIG_FOLDER_NAME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"FOLDER_SCOPES","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"FOLDER_SETTINGS_NAME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"FOLDER_SETTINGS_PATH","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"FOLDER_SETTINGS_PATH","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferences"},{"name":"FolderConfiguration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/browser/configuration"},{"name":"FolderFocusKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"FolderMatch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchModel"},{"name":"FolderMatchRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchResultsView"},{"name":"FolderMatchWithResource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchModel"},{"name":"FolderSettingsActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesWidgets"},{"name":"FolderSettingsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesRenderers"},{"name":"folderSettingsSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"foldersToIncludeGlobs","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepFileSearch"},{"name":"foldersToRgExcludeGlobs","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepFileSearch"},{"name":"FolderThemeIcon","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"FoldingController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/folding"},{"name":"FoldingDecorationProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingDecorations"},{"name":"FoldingModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingModel"},{"name":"FoldingRange","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"FoldingRange","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"FoldingRange","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"FoldingRangeKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"FoldingRangeKind","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"FoldingRangeKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"FoldingRangeKind","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"FoldingRangeProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"FoldingRegion","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingRanges"},{"name":"FoldingRegions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingRanges"},{"name":"FollowerLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/logIpc"},{"name":"FontInfo","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/fontInfo"},{"name":"FontStyle","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"fontStylePattern","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"for","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"forEach","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/collections"},{"name":"foreground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"ForeignElementType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"fork","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"child_process"},{"name":"fork","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"format","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"format","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"url"},{"name":"format","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"format","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"format","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"format","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/jsonFormatter"},{"name":"format","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"formatDocumentRangeWithProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"formatDocumentRangeWithSelectedProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"formatDocumentWithProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"formatDocumentWithSelectedProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"formatOptions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/argv"},{"name":"formatPII","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugUtils"},{"name":"FormatString","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"FormattingConflicts","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"FormattingEdit","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/formattingEdit"},{"name":"FormattingMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"formatWithOptions","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"FormData","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"FORMERR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"forwardedPortsViewEnabled","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/browser/tunnelView"},{"name":"frameElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"frames","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"FrankensteinMode","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/abstractMode"},{"name":"freemem","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"fromBuffer","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/yauzl/index"},{"name":"fromFd","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/yauzl/index"},{"name":"fromMap","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/collections"},{"name":"fromNow","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/date"},{"name":"fromRandomAccessReader","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/yauzl/index"},{"name":"fromRangeOrRangeWithMessage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"fstat","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"fstatSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"FSWatcher","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/chokidar/types/index"},{"name":"fsync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"fsyncSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"ftruncate","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"ftruncateSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"function","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Function","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"FunctionBreakpoint","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"FunctionBreakpoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"FunctionBreakpoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"futimes","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"futimesSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"fuzzyContains","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"fuzzyScore","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"FuzzyScore","kind":"type","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"fuzzyScoreGraceful","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"fuzzyScoreGracefulAggressive","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"GainNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Gamepad","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"GamepadButton","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"GamepadEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"GamepadHapticActuator","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"GamepadPose","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"generateIndent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentUtils"},{"name":"generateKeyPair","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"generateKeyPairSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"generateRandomChunkWithLF","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils"},{"name":"generateRandomEdits","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils"},{"name":"generateRandomPipeName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc.net"},{"name":"generateRandomReplaces","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils"},{"name":"generateSequentialInserts","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils"},{"name":"generateTokensCSSForColorMap","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/tokenization"},{"name":"generateUuid","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uuid"},{"name":"Gesture","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/touch"},{"name":"get","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"get","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"https"},{"name":"getAbsoluteGlob","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepFileSearch"},{"name":"getActiveEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsEditorContribution"},{"name":"getActiveNotebookEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/contrib/notebookActions"},{"name":"getActiveTextEditorOptions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editor"},{"name":"getActiveWebviewEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewCommands"},{"name":"getAllMethodNames","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"getAllPropertyNames","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"getAppDataPath","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/paths"},{"name":"getBaseLabel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"getBasenameTerms","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/glob"},{"name":"getBlinkMemoryInfo","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getBreakpointMessageAndClassName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/breakpointsView"},{"name":"getCharContainingOffset","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"getCharIndex","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/minimap/minimapCharSheet"},{"name":"getCiphers","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"getCiphers","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"getClientArea","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getCodeActions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeAction"},{"name":"getCodeEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorBrowser"},{"name":"getCodeForKeyCode","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/keyboardEvent"},{"name":"getCodeLensData","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codelens/codelens"},{"name":"getColorPresentations","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/colorPicker/color"},{"name":"getColorRegistry","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"getColors","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/colorPicker/color"},{"name":"getComparisonKey","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"getComputedStyle","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"getComputedStyle","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getConfigurationKeys","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"getConfigurationValue","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"getContentHeight","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getContentWidth","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getContext","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerView"},{"name":"getContext","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/callStackView"},{"name":"getContextForContributedActions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/callStackView"},{"name":"getContextMenuActions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"getCorrelationContext","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/applicationinsights"},{"name":"getCPUUsage","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getCreationTime","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getCurrentActivationRecord","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/experiments/common/experimentService"},{"name":"getCurrentKeyboardLayout","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/native-keymap/index"},{"name":"getCurves","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"getCwd","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment"},{"name":"getDeclarationsAtPosition","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/goToSymbol"},{"name":"getDefaultIgnoredSettings","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"getDefaultSettings","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"http2"},{"name":"getDefaultShell","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment"},{"name":"getDefaultShellArgs","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment"},{"name":"getDefaultUserDataPath","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/paths"},{"name":"getDefaultValue","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"getDefaultValues","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"getDefinitionsAtPosition","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/goToSymbol"},{"name":"getDelayedChannel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc"},{"name":"getDiffieHellman","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"getDisallowedIgnoredSettings","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"getDispatchConfig","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/dispatchConfig"},{"name":"getDocumentFormattingEditsUntilResult","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"getDocumentRangeFormattingEditsUntilResult","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"getDocumentSymbols","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/documentSymbols"},{"name":"getDomainsOfRemotes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tags/electron-browser/workspaceTags"},{"name":"getDomNodePagePosition","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getDuration","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/performance"},{"name":"getEditOperation","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/browser/testCommand"},{"name":"getEditorPartOptions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editor"},{"name":"getegid","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getElementsByTagName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getEmptyExpression","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/glob"},{"name":"getEnabledCategories","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"trace_events"},{"name":"getEncodedLanguageId","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"getEntries","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/performance"},{"name":"getEntry","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/performance"},{"name":"getEOL","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/jsonFormatter"},{"name":"getErrorMessage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"geteuid","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getExactExpressionStartAndEnd","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugUtils"},{"name":"getExcludes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"getExpandedBodySize","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/breakpointsView"},{"name":"getExtensionHostDebugSession","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugUtils"},{"name":"getExtensionKind","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsUtil"},{"name":"getExtensions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewer"},{"name":"getExtraColor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughUtils"},{"name":"getFileNamesMessage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/dialogs/common/dialogs"},{"name":"getFirstFrame","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/console"},{"name":"getGalleryExtensionId","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementUtil"},{"name":"getGalleryExtensionTelemetryData","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementUtil"},{"name":"getgid","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getGraphemeBreakType","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"getgroups","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getHashedRemotesFromConfig","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tags/electron-browser/workspaceTags"},{"name":"getHashes","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"getHeapCodeStatistics","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"getHeapSnapshot","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"getHeapSpaceStatistics","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"getHeapStatistics","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"getHeapStatistics","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getHover","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/hover/getHover"},{"name":"getIconClass","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/quickinput/browser/quickInputUtils"},{"name":"getIconClasses","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/getIconClasses"},{"name":"getIconRegistry","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/iconRegistry"},{"name":"getIdAndVersion","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/node/cliProcessMain"},{"name":"getIgnoredSettings","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/settingsMerge"},{"name":"getImplementationsAtPosition","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/goToSymbol"},{"name":"getInstalledExtensions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensionsUtils"},{"name":"getInvalidTypeError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesValidation"},{"name":"getIOCounters","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getIssueReporterStyles","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/issue/electron-browser/issueService"},{"name":"getKeyboardLayoutId","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/keymapInfo"},{"name":"getKeyMap","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/native-keymap/index"},{"name":"getLangEnvVariable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment"},{"name":"getLanguages","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"getLargestChildWidth","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getLastActiveWindow","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/node/window"},{"name":"getLeadingWhitespace","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"getLineEndOffset","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/content"},{"name":"getLineStartOffset","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/content"},{"name":"getLinks","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/links/getLinks"},{"name":"getLocalExtensionTelemetryData","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementUtil"},{"name":"getLocation","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"getLogLevel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"getMac","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/macAddress"},{"name":"getMachineId","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/id"},{"name":"getMachineInfo","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/diagnostics/node/diagnosticsService"},{"name":"getMainProcessParentEnv","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/node/terminalEnvironment"},{"name":"getMaliciousExtensionsSet","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementUtil"},{"name":"getManifest","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/node/extensionManagementUtil"},{"name":"getMapForWordSeparators","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/wordCharacterClassifier"},{"name":"getMatchedCSSRules","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"getMaxListeners","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"getMaxListeners","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getMediaMime","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/mime"},{"name":"getMenuBarVisibility","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/common/windows"},{"name":"getMigratedSettingValue","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"getModel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"getModelMarkers","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"getModels","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"getMultiSelectedEditorContexts","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"getMultiSelectedResources","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/files"},{"name":"getNextCodePoint","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"getNextTickChannel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc"},{"name":"getNLines","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"getNLSConfiguration","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/languagePacks"},{"name":"getNodeColor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"getNodeIsInOverviewRuler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"getNodePath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"getNodeType","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"getNodeValue","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"getNonWhitespacePrefix","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/snippets/browser/snippetsService"},{"name":"getOccurrencesAtPosition","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordHighlighter/wordHighlighter"},{"name":"getOnTypeFormattingEdits","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"getOnTypeRenameRanges","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/rename/onTypeRename"},{"name":"getOpenEditorsViewMultiSelection","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/files"},{"name":"getOrDefault","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/objects"},{"name":"getOrMakeSearchEditorInput","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput"},{"name":"getOrSet","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"getOuterEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"getOutOfWorkspaceEditorResources","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/search"},{"name":"getOutputSimpleEditorOptions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform"},{"name":"getPackedSettings","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"http2"},{"name":"getParseErrorMessage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/jsonErrorMessages"},{"name":"getPassword","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/keytar/keytar"},{"name":"getPassword","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/keytar/index"},{"name":"getPathFromAmdModule","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/amd"},{"name":"getPathLabel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"getPathTerms","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/glob"},{"name":"getPixelRatio","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"getPlatformTextDecoder","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/stringBuilder"},{"name":"getPriority","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"getProcessCpuUsage","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/windows-process-tree/index"},{"name":"getProcessList","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/windows-process-tree/index"},{"name":"getProcessMemoryInfo","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getProcessTree","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/windows-process-tree/index"},{"name":"getProxyAgent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/request/node/proxy"},{"name":"getQuickNavigateHandler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/quickaccess"},{"name":"getRandomElement","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"getRandomEOLSequence","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils"},{"name":"getRandomInt","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils"},{"name":"getRandomString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils"},{"name":"getRandomTestPath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/node/testUtils"},{"name":"getRealAndSyntheticDocumentFormattersOrdered","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"getReferencesAtPosition","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/goToSymbol"},{"name":"getReindentEditOperations","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"getRelativeLocation","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/grid"},{"name":"getRemoteAuthority","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteHosts"},{"name":"getRemoteName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteHosts"},{"name":"getRemotes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tags/electron-browser/workspaceTags"},{"name":"getResizesObserver","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver"},{"name":"getResource","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"getResourceForCommand","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/files"},{"name":"getRoot","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"getSCMResourceContextKey","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/menus"},{"name":"getScopes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"getSearchView","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"getSelection","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"getSelectionKeyboardEvent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"getSelectionSearchString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"getServers","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"getServiceMachineId","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/serviceMachineId/common/serviceMachineId"},{"name":"getSettingsTargetName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferences"},{"name":"getShadowRoot","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getShellEnvironment","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/node/shellEnv"},{"name":"getSimpleCodeEditorWidgetOptions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/simpleEditorOptions"},{"name":"getSimpleEditorOptions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/simpleEditorOptions"},{"name":"getSimpleWorkspaceLabel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/label/common/label"},{"name":"getSingletonServiceDescriptors","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/extensions"},{"name":"getSnippetSuggestSupport","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggest"},{"name":"getSpaceCnt","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentUtils"},{"name":"getStackFrameThreadAndSessionToFocus","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugService"},{"name":"getStateLabel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"getStdinFilePath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/stdin"},{"name":"getStoredWorkspaceFolder","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"getStringIdentifierForProxy","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/proxyIdentifier"},{"name":"GetStringRegKey","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/vscode-windows-registry/index"},{"name":"getSuggestionComparator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggest"},{"name":"getSyncResourceFromLocalPreview","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"getSystemMemoryInfo","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getSystemShell","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/node/terminal"},{"name":"getSystemVersion","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getTemplates","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskTemplates"},{"name":"getTerminalShellConfiguration","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalConfiguration"},{"name":"getThemeTypeSelector","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"getTimeSinceLastZoomLevelChanged","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"getTitleBarStyle","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/common/windows"},{"name":"getTokenClassificationRegistry","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"getTopLeftOffset","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getTotalHeight","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getTotalScrollWidth","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getTotalWidth","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getTypeDefinitionsAtPosition","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/goToSymbol"},{"name":"getuid","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getUnpackedSettings","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"http2"},{"name":"getUri","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/test/browser/queryBuilder.test"},{"name":"getUriFromAmdModule","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/amd"},{"name":"getUriFromSource","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugSource"},{"name":"getUserDataSyncStore","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"getVisbileViewContextKey","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/views"},{"name":"getVisibleAndSorted","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugUtils"},{"name":"getVisibleState","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/indexTreeModel"},{"name":"getWebviewContentMimeType","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/common/mimeTypes"},{"name":"getWellFormedFileName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"getWindowsBuildNumber","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/node/terminal"},{"name":"getWindowsShell","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/processes"},{"name":"getWindowsStateStoreData","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/electron-main/windowsStateStorage"},{"name":"getWordAtText","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/wordHelper"},{"name":"getWordDefinitionFor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDocumentData"},{"name":"getWorkerBootstrapUrl","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/worker/defaultWorkerFactory"},{"name":"getWorkspaceIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workspaces/browser/workspaces"},{"name":"getWorkspaceIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/electron-main/workspacesMainService"},{"name":"getWorkspaceIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test"},{"name":"getWorkspaceSymbols","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/search"},{"name":"getXtermLineContent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers"},{"name":"getZoomFactor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"getZoomLevel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"global","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"GLOBAL_ACTIVITY_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/activity"},{"name":"GlobalActivityActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/activitybar/activitybarActions"},{"name":"globalAgent","kind":"let","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"globalAgent","kind":"let","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"https"},{"name":"GlobalCompareResourcesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"GlobalEditorMouseMoveMonitor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorDom"},{"name":"GlobalExtensionEnablementService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionEnablementService"},{"name":"globalGlob","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/test/browser/queryBuilder.test"},{"name":"GlobalMouseMoveMonitor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/globalMouseMoveMonitor"},{"name":"GlobalNewUntitledFileAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"GlobalRemoveRootFolderAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceActions"},{"name":"globals","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"globalShortcut","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"GlobalStateSynchroniser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/globalStateSync"},{"name":"GlobalStorageDatabaseChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/node/storageIpc"},{"name":"GlobalStorageDatabaseChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/node/storageIpc"},{"name":"GlobalStyleSheet","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/services/codeEditorServiceImpl"},{"name":"globalThis","kind":"module","kindModifiers":"","sortText":"4"},{"name":"GlobPattern","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"GlyphHoverWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/hover/hoverWidgets"},{"name":"GlyphMarginOverlay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin"},{"name":"GOTO_NEXT_CHANGE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"GOTO_PREVIOUS_CHANGE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"GotoDefinitionAtPositionEditorContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosition"},{"name":"GotoLineAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess"},{"name":"GotoLineAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/quickAccess/standaloneGotoLineQuickAccess"},{"name":"GotoLineAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess"},{"name":"GotoLineAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess"},{"name":"GoToLineNLS","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standaloneStrings"},{"name":"GotoLineQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess"},{"name":"GotoSymbolAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess"},{"name":"GotoSymbolQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess"},{"name":"gracefulify","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/graceful-fs/index"},{"name":"grammarsExtPoint","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/common/TMGrammars"},{"name":"Graph","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/graph"},{"name":"GraphemeBreakType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"Grid","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/grid"},{"name":"GridView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/gridview"},{"name":"GridViewSizing","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/grid"},{"name":"group","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"groupBy","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"groupBy","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/collections"},{"name":"groupByExtension","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementUtil"},{"name":"GroupChangeKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorGroupsService"},{"name":"groupCollapsed","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"GroupDirection","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorGroupsService"},{"name":"groupEnd","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"groupIntersect","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/rangeMap"},{"name":"GroupLocation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorGroupsService"},{"name":"GroupOrientation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorGroupsService"},{"name":"GroupsArrangement","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorGroupsService"},{"name":"GroupsOrder","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorGroupsService"},{"name":"GroupType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"gt","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"gte","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"gtr","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"guessIndentation","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/indentationGuesser"},{"name":"guessMimeTypes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/mime"},{"name":"gunzip","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"gunzipSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"gzip","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"gzipSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"handleANSIOutput","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform"},{"name":"handleANSIOutput","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugANSIHandling"},{"name":"Handler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/editorCommon"},{"name":"handleVetos","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/common/lifecycle"},{"name":"hang","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"hasChildProcesses","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/node/terminals"},{"name":"hasClass","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"hasFileFolderCopyCapability","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"hasFileReadStreamCapability","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"hash","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/hash"},{"name":"Hash","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"HashChangeEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Hasher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/hash"},{"name":"hashPath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/backup/node/backupFileService"},{"name":"hasOpenReadWriteCloseCapability","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"hasParentWithClass","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"hasReadWriteCapability","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"HasSearchResults","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"hasSiblingFn","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/glob"},{"name":"hasSiblingPromiseFn","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/glob"},{"name":"hasStdinWithoutTty","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/stdin"},{"name":"hasTextDecoder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/stringBuilder"},{"name":"hasToIgnoreCase","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"hasTrailingPathSeparator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"hasUncaughtExceptionCaptureCallback","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"hasWorkspaceFileExtension","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"hc_black","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/themes"},{"name":"HC_THEME_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/workbenchThemeService"},{"name":"Headers","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HelpQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/browser/helpQuickAccess"},{"name":"HiddenAreasRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesRenderers"},{"name":"HiddenRangeModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/hiddenRangeModel"},{"name":"hide","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"HIDE_NOTIFICATION_TOAST","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"HIDE_NOTIFICATIONS_CENTER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"HideNotificationsCenterAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsActions"},{"name":"HideWebViewEditorFindCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewCommands"},{"name":"HideWelcomeOverlayAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay"},{"name":"HIGH_CONTRAST","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"HighlightedLabel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/highlightedlabel/highlightedLabel"},{"name":"HighlightMatchesRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesRenderers"},{"name":"history","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"History","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HistoryInputBox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/inputbox/inputBox"},{"name":"HistoryNavigationEnablementContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/browser/contextScopedHistoryWidget"},{"name":"HistoryNavigationWidgetContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/browser/contextScopedHistoryWidget"},{"name":"HistoryNavigator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/history"},{"name":"HistoryService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/history/browser/history"},{"name":"HitTestContext","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/mouseTarget"},{"name":"HIVES","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"HKCC","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"HKCR","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"HKCU","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"HKLM","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"HKU","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"Hmac","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"homedir","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"HorizontalPosition","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/renderingContext"},{"name":"HorizontalRange","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/renderingContext"},{"name":"HorizontalScrollbar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/horizontalScrollbar"},{"name":"horizontalScrollingKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"HostExtension","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostExtensionActivator"},{"name":"hostname","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"HotExitConfiguration","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"Hover","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Hover","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"Hover","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"HoverOperation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/hover/hoverOperation"},{"name":"HoverProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"HoverStartMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/hover/hoverOperation"},{"name":"hrtime","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"HSLA","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/color"},{"name":"HSVA","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/color"},{"name":"HTMLAllCollection","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLAnchorElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLAppletElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLAreaElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLAudioElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLBaseElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLBaseFontElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLBodyElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLBRElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLButtonElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLCanvasElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLCollection","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLDataElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLDataListElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLDetailsElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLDialogElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLDirectoryElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLDivElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLDListElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLDocument","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLEmbedElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLFieldSetElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLFontElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLFormControlsCollection","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLFormElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLFrameElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLFrameSetElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLHeadElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLHeadingElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLHRElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLHtmlElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLIFrameElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLImageElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLInputElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLLabelElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLLegendElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLLIElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLLinkElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLMapElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLMarqueeElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLMediaElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLMenuElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLMetaElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLMeterElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLModElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLObjectElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLOListElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLOptGroupElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLOptionElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLOptionsCollection","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLOutputElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLParagraphElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLParamElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLPictureElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLPreElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLProgressElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLQuoteElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLScriptElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLSelectElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLSlotElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLSourceElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLSpanElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLStyleElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTableCaptionElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTableCellElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTableColElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTableDataCellElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTableElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTableHeaderCellElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTableRowElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTableSectionElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTemplateElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTextAreaElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTimeElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTitleElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTrackElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLUListElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLUnknownElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLVideoElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Http2ServerRequest","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"http2"},{"name":"Http2ServerResponse","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"http2"},{"name":"HttpProxyAgent","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/http-proxy-agent/index"},{"name":"IAccessibilityService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/accessibility/common/accessibility"},{"name":"IActivityBarService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/activityBar/browser/activityBarService"},{"name":"IActivityService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/activity/common/activity"},{"name":"IAuthenticationService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/authentication/browser/authenticationService"},{"name":"IUserDataSyncAccountService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncAccount"},{"name":"IBackupFileService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/backup/common/backup"},{"name":"IBackupMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/backup/electron-main/backup"},{"name":"IBreadcrumbsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbs"},{"name":"IBulkEditService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/services/bulkEditService"},{"name":"ICACLS_PATH","kind":"property","kindModifiers":"private,static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"IClipboardService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/clipboard/common/clipboardService"},{"name":"ICodeEditorService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/services/codeEditorService"},{"name":"ICodeLensCache","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codelens/codeLensCache"},{"name":"ICommandService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/commands/common/commands"},{"name":"ICommentService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentService"},{"name":"IconBadge","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/activity/common/activity"},{"name":"IConfigurationResolverService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configurationResolver/common/configurationResolver"},{"name":"IConfigurationService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"iconForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"IconLabel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/iconLabel/iconLabel"},{"name":"iconsSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/iconRegistry"},{"name":"IContextKeyService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"IContextMenuService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextview/browser/contextView"},{"name":"IContextViewService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextview/browser/contextView"},{"name":"ICredentialsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/credentials/common/credentials"},{"name":"ICredentialsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/credentials/common/credentials"},{"name":"ICustomEditorService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/customEditor"},{"name":"ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsEditorContribution"},{"name":"ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/common/driver"},{"name":"ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/diagnostics/node/diagnosticsService"},{"name":"ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/launch/electron-main/launchMainService"},{"name":"ID_EDITOR_WORKER_SERVICE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/editorWorkerService"},{"name":"ID_INDENT_PROVIDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/indentRangeProvider"},{"name":"ID_INIT_PROVIDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/intializingRangeProvider"},{"name":"ID_SYNTAX_PROVIDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/syntaxRangeProvider"},{"name":"IDBCursor","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDBCursorWithValue","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDBDatabase","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDBFactory","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDBIndex","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDBKeyRange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDBObjectStore","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDBOpenDBRequest","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDBRequest","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDBTransaction","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDBVersionChangeEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDebugHelperService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"IDebugService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"IDecorationsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/decorations/browser/decorations"},{"name":"IdentityCoordinatesConverter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/splitLinesCollection"},{"name":"IdentityLinesCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/splitLinesCollection"},{"name":"IdentityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesTree"},{"name":"IdentityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree"},{"name":"IdGenerator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/idGenerator"},{"name":"IDiagnosticsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/diagnostics/node/diagnosticsService"},{"name":"IDialogMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/dialogs/electron-main/dialogs"},{"name":"IDialogService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/dialogs/common/dialogs"},{"name":"IdleValue","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"IdObject","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"IDownloadService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/download/common/download"},{"name":"idPattern","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"IDriver","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/common/driver"},{"name":"IEditorGroupsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorGroupsService"},{"name":"IEditorProgressService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/progress/common/progress"},{"name":"IEditorService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorService"},{"name":"IEditorWorkerService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/editorWorkerService"},{"name":"IElectronMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/electron/electron-main/electronMainService"},{"name":"IElectronService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/electron/node/electron"},{"name":"IEnvironmentService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/common/environment"},{"name":"IEnvironmentVariableService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/environmentVariable"},{"name":"IExperimentService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/experiments/common/experimentService"},{"name":"IExplorerService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"IExtensionGalleryService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"IExtensionHostDebugService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/debug/common/extensionHostDebug"},{"name":"IExtensionHostProfileService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"IExtensionManagementServerService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/common/extensionManagement"},{"name":"IExtensionManagementService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"IExtensionRecommendationsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/common/extensionManagement"},{"name":"IExtensionResourceLoaderService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader"},{"name":"IExtensionService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensions"},{"name":"IExtensionStoragePaths","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostStoragePaths"},{"name":"IExtensionsWorkbenchService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"IExtensionTipsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"IExtensionUrlHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/browser/extensionUrlHandler"},{"name":"IExternalTerminalService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/externalTerminal/common/externalTerminal"},{"name":"IExtHostApiDeprecationService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostApiDeprecationService"},{"name":"IExtHostCommands","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostCommands"},{"name":"IExtHostConfiguration","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostConfiguration"},{"name":"IExtHostDebugService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDebugService"},{"name":"IExtHostDecorations","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDecorations"},{"name":"IExtHostDocumentsAndEditors","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDocumentsAndEditors"},{"name":"IExtHostExtensionService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostExtensionService"},{"name":"IExtHostInitDataService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostInitDataService"},{"name":"IExtHostOutputService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostOutput"},{"name":"IExtHostRpcService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostRpcService"},{"name":"IExtHostSearch","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostSearch"},{"name":"IExtHostStorage","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostStorage"},{"name":"IExtHostTask","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"IExtHostTerminalService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTerminalService"},{"name":"IExtHostTimeline","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTimeline"},{"name":"IExtHostTunnelService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTunnelService"},{"name":"IExtHostWorkspace","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostWorkspace"},{"name":"if","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"ifError","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"IFileDialogService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/dialogs/common/dialogs"},{"name":"IFilesConfigurationService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService"},{"name":"IFileService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"IframeUtils","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/iframe"},{"name":"IFrameWebview","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewElement"},{"name":"IGlobalExtensionEnablementService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"ignoreBracketsInToken","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports"},{"name":"ignoreErrors","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"IgnoreExtensionRecommendationAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"IHistoryService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/history/common/history"},{"name":"IHostService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/host/browser/host"},{"name":"IHostUtils","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostExtensionService"},{"name":"IInstantiationService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/instantiation"},{"name":"IIntegrityService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/integrity/common/integrity"},{"name":"IIRFilterNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IIssueService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/issue/node/issue"},{"name":"IJSONEditingService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/jsonEditing"},{"name":"IKeybindingEditingService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/keybindingEditing"},{"name":"IKeybindingService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/keybinding"},{"name":"IKeymapService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/keymapInfo"},{"name":"ILabelService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/label/common/label"},{"name":"ILaunchMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/launch/electron-main/launchMainService"},{"name":"ILayoutService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/layout/browser/layoutService"},{"name":"ILifecycleMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/electron-main/lifecycleMainService"},{"name":"ILifecycleService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/common/lifecycle"},{"name":"IListService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"illegalArgument","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"illegalState","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"ILocalizationsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/localizations/common/localizations"},{"name":"ILoggerService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"ILogService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"Image","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IMAGE_PREVIEW_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ImageBitmap","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ImageBitmapRenderingContext","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ImageData","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IMainProcessService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/ipc/electron-browser/mainProcessService"},{"name":"IMarkerData","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/markers/common/markers"},{"name":"IMarkerDecorationsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/markersDecorationService"},{"name":"IMarkerService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/markers/common/markers"},{"name":"IMarkersWorkbenchService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markers"},{"name":"IMenubarService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/menubar/node/menubar"},{"name":"IMenuService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"ImmortalReference","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/lifecycle"},{"name":"IMMUTABLE_CODE_TO_KEY_CODE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/scanCode"},{"name":"IMMUTABLE_KEY_CODE_TO_CODE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/scanCode"},{"name":"IModelService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/modelService"},{"name":"IModeService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/modeService"},{"name":"impactsEditorPartOptions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editor"},{"name":"ImplementationProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"implements","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"import","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"importEntries","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/performance"},{"name":"importScripts","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"in","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"inAppPurchase","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"inc","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"IncomingMessage","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"IncomingMessage","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"IncreaseSearchEditorContextLinesCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"IncreaseViewSizeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"incrementFileName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"IndentAction","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"IndentAction","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/languageConfiguration"},{"name":"IndentAction","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"IndentationToSpacesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"IndentationToSpacesCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"IndentationToTabsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"IndentationToTabsCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"IndentConsts","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/indentRules"},{"name":"IndentGuidesOverlay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/indentGuides/indentGuides"},{"name":"IndentLinesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"IndentRangeProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/indentRangeProvider"},{"name":"IndentRulesSupport","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/indentRules"},{"name":"IndentUsingSpaces","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"IndentUsingTabs","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"index","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"indexedDB","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"INDEXEDDB_LOGS_OBJECT_STORE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/log/browser/indexedDBLogProvider"},{"name":"INDEXEDDB_VSCODE_DB","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/log/browser/indexedDBLogProvider"},{"name":"IndexedDBLogProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/log/browser/indexedDBLogProvider"},{"name":"indexOfPath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"IndexTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/indexTree"},{"name":"IndexTreeModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/indexTreeModel"},{"name":"InEditorZenModeContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"Infinity","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"inflate","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"inflateRaw","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"inflateRawSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"inflateSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"info","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"Information","kind":"enum member","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/SeverityLevel"},{"name":"inherits","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"INITIAL","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-textmate/release/main"},{"name":"initialize","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.worker"},{"name":"InitializingRangeProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/intializingRangeProvider"},{"name":"InlineDecoration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/viewModel"},{"name":"InlineDecorationType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/viewModel"},{"name":"InlineDiffMargin","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/widget/inlineDiffMargin"},{"name":"InMemoryBackupFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/backup/common/backupFileService"},{"name":"InMemoryFileSystemProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/inMemoryFilesystemProvider"},{"name":"InMemoryLogProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/log/common/inMemoryLogProvider"},{"name":"InMemoryStorageDatabase","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/storage/common/storage"},{"name":"InMemoryStorageService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/common/storage"},{"name":"InMemoryTask","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"innerHeight","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"innerWidth","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"INotebookService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookService"},{"name":"INotificationService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/notification/common/notification"},{"name":"InPlaceReplaceCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/inPlaceReplace/inPlaceReplaceCommand"},{"name":"inputActiveOptionBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputActiveOptionBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"InputBox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/inputbox/inputBox"},{"name":"InputBoxFocusedKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"InputDeviceInfo","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"InputEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"InputFocusedContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkeys"},{"name":"InputFocusedContextKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkeys"},{"name":"inputForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputPlaceholderForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputsSchema","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configurationResolver/common/configurationResolverSchema"},{"name":"inputValidationErrorBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputValidationErrorBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputValidationErrorForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputValidationInfoBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputValidationInfoBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputValidationInfoForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"InputValidationType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/common/scm"},{"name":"inputValidationWarningBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputValidationWarningBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputValidationWarningForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inQuickPickContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/quickaccess"},{"name":"InQuickPickContextKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/quickaccess"},{"name":"inQuickPickContextKeyValue","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/quickaccess"},{"name":"inRecentFilesPickerContextKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/windowActions"},{"name":"insane","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/insane/insane"},{"name":"InSearchEditor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"insert","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"InsertCellEdit","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/cellEdit"},{"name":"InsertCodeCellAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/contrib/notebookActions"},{"name":"InsertCursorAbove","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"InsertCursorBelow","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"InsertLineAfterAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"InsertLineBeforeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"InsertMarkdownCellAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/contrib/notebookActions"},{"name":"inspect","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"INSPECT_MAX_BYTES","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"buffer"},{"name":"InspectTokensNLS","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standaloneStrings"},{"name":"INSTALL_ERROR_INCOMPATIBLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"INSTALL_ERROR_MALICIOUS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"INSTALL_ERROR_NOT_SUPPORTED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"InstallAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"InstallAnotherVersionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"InstallCountWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWidgets"},{"name":"InstallExtensionQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsQuickAccess"},{"name":"InstallExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"InstallInOtherServerAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"InstallLocalExtensionsInRemoteAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"InstallOperation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"InstallRecommendedExtensionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"InstallSpecificVersionOfExtensionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"InstallVSIXAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"InstallWorkspaceRecommendedExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"instanceof","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"instanceStorageKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetry"},{"name":"InstantiationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/instantiationService"},{"name":"Int16Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Int32Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Int8Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IntegrityServiceImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/integrity/node/integrityService"},{"name":"interface","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Interface","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"readline"},{"name":"INTERNAL_CONSOLE_OPTIONS_SCHEMA","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"InternalEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/editorAction"},{"name":"InternalModelContentChangeEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelEvents"},{"name":"IntersectionObserver","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IntersectionObserverEntry","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"intersects","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"intervalCompare","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"IntervalNode","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"IntervalTimer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"IntervalTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"Intl","kind":"module","kindModifiers":"declare","sortText":"4"},{"name":"IOpenerService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/opener/common/opener"},{"name":"IOutputChannelModelService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/output/common/outputChannelModel"},{"name":"IOutputService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"IPadShowKeyboard","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard"},{"name":"IPanelService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/panel/common/panelService"},{"name":"IPathService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/path/common/pathService"},{"name":"IPCClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc"},{"name":"ipcMain","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"ipcRenderer","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"IPCServer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc"},{"name":"IPeekViewService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"IPreferencesSearchService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"IPreferencesService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferences"},{"name":"IProductService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/product/common/productService"},{"name":"IProgressService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/progress/common/progress"},{"name":"IQuickInputService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/common/quickInput"},{"name":"IRemoteAgentService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/remoteAgentService"},{"name":"IRemoteAuthorityResolverService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAuthorityResolver"},{"name":"IRemoteExplorerService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/remoteExplorerService"},{"name":"IReplaceService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/replace"},{"name":"IRequestService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/request/common/request"},{"name":"IResourceIdentityService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/resource/common/resourceIdentityService"},{"name":"IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"isAbsolute","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"isAbsolute","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"isAbsolutePath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"isActive","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/windows-mutex/index"},{"name":"isAncestor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"isArray","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isArray","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"isatty","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tty"},{"name":"isBasicASCII","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"isBoolean","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isBoolean","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"isBuffer","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"IsCenteredLayoutContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"isChrome","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"ISCMService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/common/scm"},{"name":"isCodeEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorBrowser"},{"name":"isCompositeEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorBrowser"},{"name":"isCompressedFolderName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerViewer"},{"name":"isConfigurationOverrides","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"isContext","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"vm"},{"name":"isDate","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isDebuggerMainContribution","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugUtils"},{"name":"isDecorationOptionsArr","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"isDeepStrictEqual","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"IsDevelopmentContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkeys"},{"name":"isDiffEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorBrowser"},{"name":"isDiffEditorConfigurationKey","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/commonEditorConfig"},{"name":"isDirtyDiffVisible","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"isDisposable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/lifecycle"},{"name":"ISearchHistoryService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchHistoryService"},{"name":"ISearchService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"ISearchWorkbenchService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchModel"},{"name":"isEdge","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"isEdgeWebView","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"isEditorConfigurationKey","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/commonEditorConfig"},{"name":"isEditorInputWithOptions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"isElevated","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/native-is-elevated/index"},{"name":"isEmojiImprecise","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"isEmptyMarkdownString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/htmlContent"},{"name":"isEmptyObject","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"isEngineValid","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensionValidator"},{"name":"isEOL","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/jsonFormatter"},{"name":"isEqual","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"isEqual","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"isEqualAuthority","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"isEqualOrParent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"isEqualOrParent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"isError","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isErrorWithActions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errorsWithActions"},{"name":"isExcludeSetting","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"isFalsyOrEmpty","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"isFalsyOrWhitespace","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"isFileMatch","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"isFilePatternMatch","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"isFileToOpen","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/common/windows"},{"name":"isFilterResult","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/indexTreeModel"},{"name":"isFinite","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"isFirefox","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"isFolderToOpen","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/common/windows"},{"name":"isFullscreen","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"IsFullscreenContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/contextkeys"},{"name":"isFullWidthCharacter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"isFunction","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isFunction","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"isFuzzyAction","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"isFuzzyActionArr","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"isGridBranchNode","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/gridview"},{"name":"isGridBranchNode","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/grid"},{"name":"ISharedProcessMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/ipc/electron-main/sharedProcessMainService"},{"name":"ISharedProcessService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/ipc/electron-browser/sharedProcessService"},{"name":"isHighSurrogate","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"isHTMLElement","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"isIAction","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"isIExtensionIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensions"},{"name":"isIExtensionIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"ISignService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/sign/common/sign"},{"name":"isIMenuItem","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"isInDOM","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"isInShadowDOM","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"isIOS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"isIP","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"net"},{"name":"isIPad","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"isIPv4","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"net"},{"name":"isIPv6","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"net"},{"name":"isISOKeyboard","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/native-keymap/index"},{"name":"isISubmenuItem","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"isKeymapExtension","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensionsUtils"},{"name":"isLanguagePackExtension","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensions"},{"name":"isLinux","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"IsLinuxContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkeys"},{"name":"isLittleEndian","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"isLocationLink","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"isLowerAsciiLetter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"isLowSurrogate","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"IsMacContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkeys"},{"name":"isMacintosh","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"IsMacNativeContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkeys"},{"name":"isMainFrame","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"isMainThread","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"worker_threads"},{"name":"isMarkdownString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/htmlContent"},{"name":"isMaster","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"isMenubarMenuItemAction","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/menubar/node/menubar"},{"name":"isMenubarMenuItemSeparator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/menubar/node/menubar"},{"name":"isMenubarMenuItemSubmenu","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/menubar/node/menubar"},{"name":"isMenubarMenuItemUriAction","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/menubar/node/menubar"},{"name":"isMessageOfType","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionHostProtocol"},{"name":"isMultilineRegexSource","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelSearch"},{"name":"isNamedProblemMatcher","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"isNaN","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"isNative","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"ISnippetsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/snippets/browser/snippets.contribution"},{"name":"isNonEmptyArray","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"isNotificationViewItem","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/notifications"},{"name":"isNull","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isNullOrUndefined","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isNullRange","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"isNumber","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isNumber","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"isObject","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isObject","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"isOpera","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"isParent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"isPathSeparator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"isPatternInWord","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"isPrimitive","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isProgressMessage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"isPromiseCanceledError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"isQuote","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"isRawFileWorkspaceFolder","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"isRawUriWorkspaceFolder","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"isReadableStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/stream"},{"name":"isRecentFile","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"isRecentFolder","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"isRecentWorkspace","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"isRegExp","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isRelativePattern","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/glob"},{"name":"isRemoteConsoleLog","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/console"},{"name":"isRemoteDiagnosticError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/diagnostics/common/diagnostics"},{"name":"isRootOrDriveLetter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"isRootUser","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"isSafari","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"isSCMRepository","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/util"},{"name":"isSCMResource","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/util"},{"name":"isSCMResourceGroup","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/util"},{"name":"isSearchViewFocused","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"isSecureContext","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"isSelectionRangeChangeEvent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listWidget"},{"name":"isSelectionSingleChangeEvent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listWidget"},{"name":"isSemanticColoringEnabled","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/modelServiceImpl"},{"name":"isSerializedEditorGroup","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor/editorGroup"},{"name":"isSerializedFileMatch","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"isSerializedSearchComplete","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"isSerializedSearchSuccess","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"isSessionAttach","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugUtils"},{"name":"isShadowRoot","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"isSingleFolderWorkspaceIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"isSingleFolderWorkspaceInitializationPayload","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"isStandalone","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"isStatusbarInDebugMode","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/statusbarColorProvider"},{"name":"isStoredWorkspaceFolder","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"isString","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"isString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"isStringArray","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"isSuccess","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/request/common/request"},{"name":"IssueMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/issue/electron-main/issueMainService"},{"name":"IssueReporter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-browser/issue/issueReporterMain"},{"name":"IssueReporterModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-browser/issue/issueReporterModel"},{"name":"issueReporterPage","kind":"property","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-browser/issue/issueReporterPage"},{"name":"IssueService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/issue/electron-browser/issueService"},{"name":"IssueType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/issue/node/issue"},{"name":"isSymbol","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"IStandaloneThemeService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/standaloneThemeService"},{"name":"IStateService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/state/node/state"},{"name":"IStaticExtensionsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/staticExtensions"},{"name":"IStatusbarService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/statusbar/common/statusbar"},{"name":"isTextEditorPane","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"isThemeColor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/editorCommon"},{"name":"isThenable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"IStorageKeysSyncRegistryService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/storageKeys"},{"name":"IStorageMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/node/storageMainService"},{"name":"IStorageService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/common/storage"},{"name":"ISuggestDataDtoField","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"ISuggestMemoryService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestMemory"},{"name":"ISuggestResultDtoField","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"isUNC","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"isUndefined","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isUndefined","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"isUndefinedOrNull","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"isUnspecific","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/mime"},{"name":"isUntitledWorkspace","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"isUpper","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"isUpperAsciiLetter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"isUri","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugUtils"},{"name":"isURLDomainTrusted","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/url/common/trustedDomainsValidator"},{"name":"isUTFEncoding","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"isUUID","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uuid"},{"name":"isValidBasename","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"isValidExtensionVersion","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensionValidator"},{"name":"isValidLocalization","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/localizations/common/localizations"},{"name":"isValidMatch","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelSearch"},{"name":"isValidVersion","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensionValidator"},{"name":"isValidVersionStr","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensionValidator"},{"name":"isVersionValid","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensionValidator"},{"name":"isWeb","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"IsWebContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkeys"},{"name":"isWebKit","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"isWebkitWebView","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"isWindows","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"IsWindowsContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkeys"},{"name":"isWindowsDriveLetter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"isWorker","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"isWorkspaceBackupInfo","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/backup/electron-main/backup"},{"name":"isWorkspaceFolder","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/taskQuickPick"},{"name":"isWorkspaceIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"isWorkspaceToOpen","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/common/windows"},{"name":"ISymbolNavigationService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/symbolNavigation"},{"name":"it","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ITaskService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskService"},{"name":"ITelemetryService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetry"},{"name":"ItemActivation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/quickinput/common/quickInput"},{"name":"Iterable","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/iterator"},{"name":"ITerminalInstanceService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminal"},{"name":"ITerminalNativeService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"ITerminalService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminal"},{"name":"ITextFileService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textfiles"},{"name":"ITextMateService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/common/textMateService"},{"name":"ITextModelService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/resolverService"},{"name":"ITextResourceConfigurationService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/textResourceConfigurationService"},{"name":"ITextResourcePropertiesService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/textResourceConfigurationService"},{"name":"IThemeMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/electron-main/themeMainService"},{"name":"IThemeService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"ITimelineService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/common/timeline"},{"name":"ITimerService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/timer/electron-browser/timerService"},{"name":"ITitleService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/title/common/titleService"},{"name":"ITunnelService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/tunnel"},{"name":"IUndoRedoService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/undoRedo/common/undoRedo"},{"name":"IUntitledTextEditorService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/untitled/common/untitledTextEditorService"},{"name":"IUpdateService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/common/update"},{"name":"IURITransformerService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostUriTransformerService"},{"name":"IURLService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/url/common/url"},{"name":"IUserDataAutoSyncService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"IUserDataSyncBackupStoreService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"IUserDataSyncEnablementService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"IUserDataSyncLogService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"IUserDataSyncService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"IUserDataSyncStoreService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"IUserDataSyncUtilService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"IViewDescriptorService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/views"},{"name":"IViewletService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/viewlet/browser/viewlet"},{"name":"IViewsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/views"},{"name":"IWebIssueService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/issue/browser/issueService"},{"name":"IWebviewService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webview"},{"name":"IWebviewWorkbenchService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService"},{"name":"IWindowsMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/electron-main/windows"},{"name":"IWorkbenchEnvironmentService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/environment/common/environmentService"},{"name":"IWorkbenchExtensionEnablementService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/common/extensionManagement"},{"name":"IWorkbenchIssueService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/issue/electron-browser/issue"},{"name":"IWorkbenchLayoutService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/layout/browser/layoutService"},{"name":"IWorkbenchThemeService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/workbenchThemeService"},{"name":"IWorkingCopyFileService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workingCopy/common/workingCopyFileService"},{"name":"IWorkingCopyService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workingCopy/common/workingCopyService"},{"name":"IWorkspace","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspace/common/workspace"},{"name":"IWorkspaceContextService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspace/common/workspace"},{"name":"IWorkspaceEditingService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workspaces/common/workspaceEditing"},{"name":"IWorkspaceFolder","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspace/common/workspace"},{"name":"IWorkspacesHistoryMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService"},{"name":"IWorkspacesMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/electron-main/workspacesMainService"},{"name":"IWorkspacesService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"IWorkspaceTagsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tags/common/workspaceTags"},{"name":"javascriptOnEnterRules","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/modes/supports/javascriptOnEnterRules"},{"name":"join","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"join","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"join","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"JoinAllGroupsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"JoinLinesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"joinPath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"JoinTwoGroupsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"JSON","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"JSONEditingError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/jsonEditing"},{"name":"JSONEditingErrorCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/jsonEditing"},{"name":"JSONEditingService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/jsonEditingService"},{"name":"JsonSchemaVersion","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"JsonSchemaVersion","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskConfiguration"},{"name":"JSONValidationExtensionPoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/jsonValidationExtensionPoint"},{"name":"JUMP_TO_CURSOR_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"KEEP_EDITOR_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookBrowser"},{"name":"KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_NOT_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_FOCUS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_IS_OPEN","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_TEXT_NOT_SELECTED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webview"},{"name":"KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webview"},{"name":"KEYBINDING_ENTRY_TEMPLATE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/keybindingsEditorModel"},{"name":"KeybindingEditorDecorationsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution"},{"name":"KeybindingIO","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/keybindingIO"},{"name":"KeybindingLabel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/keybindingLabel/keybindingLabel"},{"name":"KeybindingParser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keybindingParser"},{"name":"KeybindingResolver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/keybindingResolver"},{"name":"KEYBINDINGS_EDITOR_CLEAR_INPUT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_COPY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_DEFINE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_REMOVE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_RESET","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_SEARCH","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KeybindingsEditingService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/keybindingEditing"},{"name":"KeybindingsEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/keybindingsEditor"},{"name":"KeybindingsEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesEditorInput"},{"name":"KeybindingsEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/keybindingsEditorModel"},{"name":"KeybindingSource","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/keybinding"},{"name":"KeybindingsRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/keybindingsRegistry"},{"name":"KeybindingsSearchWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/keybindingWidgets"},{"name":"KeybindingsSynchroniser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/keybindingsSync"},{"name":"KeybindingWeight","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/keybindingsRegistry"},{"name":"KeybindingWidgetRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution"},{"name":"KEYBOARD_LAYOUT_OPEN_PICKER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KeyboardEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"KeyboardLayoutContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution"},{"name":"KeyboardLayoutPickerAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker"},{"name":"KeyboardLayoutPickerContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker"},{"name":"KeyboardMapperFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/electron-browser/nativeKeymapService"},{"name":"keyboardNavigationSettingKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"KeyboardSupport","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/canIUse"},{"name":"KeyChord","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keyCodes"},{"name":"KeyCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keyCodes"},{"name":"KeyCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"KeyCode","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"KeyCodeUtils","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keyCodes"},{"name":"KeyedTaskIdentifier","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"KeyframeEffect","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"keyFromOverrideIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"KeymapExtensions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensionsUtils"},{"name":"KeymapInfo","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/keymapInfo"},{"name":"KeymapRecommendations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/keymapRecommendations"},{"name":"KeyMod","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keyCodes"},{"name":"KeyMod","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneBase"},{"name":"KeyMod","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"KeyObject","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"keyof","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"keys","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"KeytarCredentialsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/credentials/node/credentialsService"},{"name":"KeytarCredentialsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/credentials/node/credentialsService"},{"name":"KeyValueLogProvider","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/log/common/keyValueLogProvider"},{"name":"kill","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"KillTerminalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"kMaxLength","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"buffer"},{"name":"knownAcronyms","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsLayout"},{"name":"KnownSnippetVariableNames","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetVariables"},{"name":"knownTermMappings","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsLayout"},{"name":"kStringMaxLength","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"buffer"},{"name":"Label","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWidgets"},{"name":"LabelContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/common/remote.contribution"},{"name":"LabelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/label/common/labelService"},{"name":"language","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"Language","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"LanguageConfigurationChangeEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/languageConfigurationRegistry"},{"name":"LanguageConfigurationFileHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint"},{"name":"LanguageConfigurationRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/languageConfigurationRegistry"},{"name":"LanguageConfigurationRegistryImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/languageConfigurationRegistry"},{"name":"LanguageFeatureRegistry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/languageFeatureRegistry"},{"name":"LanguageId","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"LanguageIdentifier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"LanguagePackCachedDataCleaner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner"},{"name":"languages","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"languages","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"LanguageSelector","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"languagesExtPoint","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/mode/common/workbenchModeService"},{"name":"LanguagesRegistry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/languagesRegistry"},{"name":"LanguageType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/localizations/common/localizations"},{"name":"LargeFileOptimizationsWarner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/largeFileOptimizations"},{"name":"LAST_COMPRESSED_FOLDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"lastIndex","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"lastNonWhitespaceIndex","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"lastSessionDateStorageKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetry"},{"name":"LAUNCH_CONFIGURATION_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"LaunchMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/launch/electron-main/launchMainService"},{"name":"launchSchema","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugSchemas"},{"name":"launchSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"layout","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/contextview/contextview"},{"name":"Layout","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/layout"},{"name":"LAYOUT_EDITOR_GROUPS_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"LayoutAnchorPosition","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/contextview/contextview"},{"name":"LayoutController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/gridview"},{"name":"LayoutData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget"},{"name":"LayoutPriority","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/splitview/splitview"},{"name":"LazilyResolvedWebviewEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService"},{"name":"Lazy","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/lazy"},{"name":"lazyEnv","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"LazyOutputChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostOutput"},{"name":"LazyPromise","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/lazyPromise"},{"name":"lchmod","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"lchmodSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"lchown","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"lchownSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"LcsDiff","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/diff/diff"},{"name":"lcut","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"leftest","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"leftRotate","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"LEGACY_CONSOLE_MODE_EXIT_CODE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"legacy_ENV_iKey","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Config"},{"name":"length","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"let","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"lexer","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/marked/marked"},{"name":"Lexer","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/marked/marked"},{"name":"LifecycleMainPhase","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/electron-main/lifecycleMainService"},{"name":"LifecycleMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/electron-main/lifecycleMainService"},{"name":"LifecyclePhase","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/common/lifecycle"},{"name":"LifecyclePhaseToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/common/lifecycle"},{"name":"LIGHT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"LightBulbWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/lightBulbWidget"},{"name":"lighten","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"Limiter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"LineBreakData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/splitLinesCollection"},{"name":"LineCommentCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/comment/lineCommentCommand"},{"name":"LineContext","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/completionModel"},{"name":"LineContext","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestModel"},{"name":"LineDecoder","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/decoder"},{"name":"LineDecoration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/lineDecorations"},{"name":"LineDecorationsNormalizer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/lineDecorations"},{"name":"LineNumbersOverlay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers"},{"name":"LinePartMetadata","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"LineProcess","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/processes"},{"name":"LineRange","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"LinesDecorationsOverlay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations"},{"name":"LinesLayout","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/linesLayout"},{"name":"LineStarts","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase"},{"name":"LineTokens","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/lineTokens"},{"name":"LineTokens2","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/tokensStore"},{"name":"LineVisibleRanges","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/renderingContext"},{"name":"link","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"Link","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/links/getLinks"},{"name":"Link","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/opener/browser/link"},{"name":"LINK_INTERCEPT_THRESHOLD","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminal"},{"name":"LinkComputer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/linkComputer"},{"name":"LinkDetector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/links/links"},{"name":"LinkDetector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/linkDetector"},{"name":"LinkedList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/linkedList"},{"name":"LinkedMap","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"LinkedText","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/linkedText"},{"name":"LinkProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"LinksList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/links/getLinks"},{"name":"linkSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"linuxDistro","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/node/terminal"},{"name":"LinuxDistro","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"LinuxExternalTerminalService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService"},{"name":"LinuxUpdateService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/electron-main/updateService.linux"},{"name":"List","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listWidget"},{"name":"listActiveSelectionBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listActiveSelectionForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listDeemphasizedForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"ListDragOverEffect","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/list"},{"name":"ListDragOverReactions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/list"},{"name":"listDropBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listenerCount","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"listenerCount","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"listenerCount","kind":"method","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"stream"},{"name":"listeners","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"listeners","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"ListeningStateChangedEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ListError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/list"},{"name":"listErrorForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listFilterMatchHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listFilterMatchHighlightBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listFilterWidgetBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listFilterWidgetNoMatchesOutline","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listFilterWidgetOutline","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listFocusBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listFocusForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listHighlightForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listHoverBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listHoverForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listInactiveFocusBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listInactiveSelectionBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listInactiveSelectionForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listInvalidItemForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listProcesses","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/ps"},{"name":"ListResourceNavigator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"ListService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"ListSettingListModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"ListSettingWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"ListView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listView"},{"name":"listWarningForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"loadavg","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"LOADED_SCRIPTS_VIEW_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"LoadedScriptsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/loadedScriptsView"},{"name":"LoaderEvent","kind":"class","kindModifiers":"declare","sortText":"4"},{"name":"LoaderEventType","kind":"enum","kindModifiers":"declare","sortText":"4"},{"name":"LOADIPHLPAPI","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"loadLocalResource","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/common/resourceLoader"},{"name":"loadWASM","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/onigasm-umd/main"},{"name":"LOCAL_MACHINE_SCOPES","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"locale","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"LocalInstallAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"LocalizationsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/localizations/node/localizations"},{"name":"LocalizationsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/localizations/electron-browser/localizationsService"},{"name":"LocalizationWorkbenchContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/localizations/browser/localizations.contribution"},{"name":"localize","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/nls"},{"name":"localize","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/nls.mock"},{"name":"localizeManifest","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionNls"},{"name":"LocalSearchProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesSearch"},{"name":"LocalSearchService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/searchService"},{"name":"LocalSelectionTransfer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"localStorage","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"location","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"location","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"Location","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Location","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Location","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"locationbar","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"log","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"log","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/console"},{"name":"log","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"log","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"log","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"LOG_MIME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"LOG_MODE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"LOG_SCHEME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"LogAppender","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetryUtils"},{"name":"LogContentProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/browser/outputServices"},{"name":"Logger","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionPoints"},{"name":"LoggerChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/logIpc"},{"name":"LoggerChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/logIpc"},{"name":"LoggerService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/node/loggerService"},{"name":"LogLevel","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"LogLevel","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"LogLevel","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"LogLevel","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"LogLevel","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/workbench.web.api"},{"name":"logOnceWebWorkerWarning","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/worker/simpleWorker"},{"name":"logRemoteEntry","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/remoteConsoleUtil"},{"name":"LogsDataCleaner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/common/logsDataCleaner"},{"name":"LogsDataCleaner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner"},{"name":"LogServiceAdapter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"logStorage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/common/storage"},{"name":"LogViewer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/browser/logViewer"},{"name":"LogViewerInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/browser/logViewer"},{"name":"LONG_LINE_BOUNDARY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"LongRunningOperation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/progress/common/progress"},{"name":"lookup","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"lookupService","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"LowerCaseAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"LRUCache","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"LRUMemory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestMemory"},{"name":"lstat","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"lstat","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"lstatSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"lt","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"lte","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"ltr","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"ltrim","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"lutimes","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"lutimesSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"MacExternalTerminalService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService"},{"name":"machineIdKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetry"},{"name":"machineOverridableSettings","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"machineSettings","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"machineSettingsSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"MacLinuxFallbackKeyboardMapper","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper"},{"name":"MacLinuxKeyboardMapper","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper"},{"name":"macLinuxKeyboardMappingEquals","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper"},{"name":"main","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/web.main"},{"name":"main","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/node/cli"},{"name":"main","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/node/cliProcessMain"},{"name":"main","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/desktop.main"},{"name":"Main","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/node/cliProcessMain"},{"name":"MainContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"mainLogChannelId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/common/logConstants"},{"name":"mainModule","kind":"property","kindModifiers":"declare,optional","sortText":"5","hasAction":true,"source":"process"},{"name":"MainPane","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/mainPane"},{"name":"MainPaneDescriptor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/mainPane"},{"name":"MainProcessService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/ipc/electron-browser/mainProcessService"},{"name":"MAINTAIN_UNDO_REDO_STACK","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/modelServiceImpl"},{"name":"MainThreadAuthentication","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadAuthentication"},{"name":"MainThreadAuthenticationProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadAuthentication"},{"name":"MainThreadClipboard","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadClipboard"},{"name":"MainThreadCommands","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadCommands"},{"name":"MainThreadCommentController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadComments"},{"name":"MainThreadComments","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadComments"},{"name":"MainThreadCommentThread","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadComments"},{"name":"MainThreadConfiguration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadConfiguration"},{"name":"MainThreadConsole","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadConsole"},{"name":"MainThreadDebugService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadDebugService"},{"name":"MainThreadDecorations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadDecorations"},{"name":"MainThreadDiagnostics","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadDiagnostics"},{"name":"MainThreadDialogs","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadDialogs"},{"name":"MainThreadDocumentContentProviders","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadDocumentContentProviders"},{"name":"MainThreadDocumentRangeSemanticTokensProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadLanguageFeatures"},{"name":"MainThreadDocuments","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadDocuments"},{"name":"MainThreadDocumentsAndEditors","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors"},{"name":"MainThreadDocumentSemanticTokensProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadLanguageFeatures"},{"name":"MainThreadDownloadService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadDownloadService"},{"name":"MainThreadEditorInsets","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadCodeInsets"},{"name":"MainThreadErrors","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadErrors"},{"name":"MainThreadExtensionService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadExtensionService"},{"name":"MainThreadFileSystem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadFileSystem"},{"name":"MainThreadFileSystemEventService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadFileSystemEventService"},{"name":"MainThreadKeytar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadKeytar"},{"name":"MainThreadLabelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadLabelService"},{"name":"MainThreadLanguageFeatures","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadLanguageFeatures"},{"name":"MainThreadLanguages","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadLanguages"},{"name":"MainThreadLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadLogService"},{"name":"MainThreadMessageService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadMessageService"},{"name":"MainThreadNotebookController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadNotebook"},{"name":"MainThreadNotebookDocument","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadNotebook"},{"name":"MainThreadNotebooks","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadNotebook"},{"name":"MainThreadOutputService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadOutputService"},{"name":"MainThreadProgress","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadProgress"},{"name":"MainThreadQuickOpen","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadQuickOpen"},{"name":"MainThreadSCM","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadSCM"},{"name":"MainThreadSearch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadSearch"},{"name":"MainThreadStatusBar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadStatusBar"},{"name":"MainThreadStorage","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadStorage"},{"name":"MainThreadTask","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadTask"},{"name":"MainThreadTelemetry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadTelemetry"},{"name":"MainThreadTerminalService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadTerminalService"},{"name":"MainThreadTextEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadEditor"},{"name":"MainThreadTextEditorProperties","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadEditor"},{"name":"MainThreadTextEditors","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadEditors"},{"name":"MainThreadTheming","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadTheming"},{"name":"MainThreadTimeline","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadTimeline"},{"name":"MainThreadTreeViews","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadTreeViews"},{"name":"MainThreadTunnelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadTunnelService"},{"name":"MainThreadUrls","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadUrls"},{"name":"MainThreadWebviews","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadWebview"},{"name":"MainThreadWindow","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadWindow"},{"name":"MainThreadWorkspace","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadWorkspace"},{"name":"major","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"MakeAddress","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/remoteExplorerService"},{"name":"MaliciousExtensionChecker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewlet"},{"name":"MaliciousStatusLabelAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ManageAuthorizedExtensionURIsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/browser/extensionUrlHandler"},{"name":"ManageAutomaticTaskRunning","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks"},{"name":"ManageExtensionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ManageExtensionsQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsQuickAccess"},{"name":"ManagementPersistentConnection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"manageTrustedDomainSettingsCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/url/common/trustedDomains"},{"name":"MANIFEST_CACHE_FOLDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensions"},{"name":"Map","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"mapArrayOrNot","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"mapPager","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/paging"},{"name":"mapToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"Margin","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/margin/margin"},{"name":"MarginViewLineDecorationsOverlay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/marginDecorations/marginDecorations"},{"name":"MarginViewOverlays","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewOverlays"},{"name":"mark","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/performance"},{"name":"markAsFileSystemProviderError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"MarkdownCellRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer"},{"name":"MarkdownCellViewModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel"},{"name":"markdownEscapeEscapedCodicons","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/codicons"},{"name":"MarkdownRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/markdown/markdownRenderer"},{"name":"MarkdownRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer"},{"name":"MarkdownString","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"MarkdownString","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/htmlContent"},{"name":"MarkdownString","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"MarkdownString","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"markdownUnescapeCodicons","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/codicons"},{"name":"marked","kind":"alias","kindModifiers":"declare","sortText":"4"},{"name":"markedStringsEquals","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/htmlContent"},{"name":"Marker","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"Marker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersModel"},{"name":"MarkerController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoError/gotoError"},{"name":"MarkerDecorationsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/markerDecorationsServiceImpl"},{"name":"MarkerNavigationWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoError/gotoErrorWidget"},{"name":"MarkerRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersTreeViewer"},{"name":"MarkerService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/markers/common/markerService"},{"name":"MarkerSeverity","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/markers/common/markers"},{"name":"MarkerSeverity","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"MarkerSeverity","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"MarkersFilterActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersViewActions"},{"name":"MarkersFilters","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersViewActions"},{"name":"MarkersModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersModel"},{"name":"MarkersTreeAccessibilityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersTreeViewer"},{"name":"MarkersView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersView"},{"name":"MarkersViewModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersTreeViewer"},{"name":"MarkersWorkbenchService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markers"},{"name":"MarkerTag","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/markers/common/markers"},{"name":"MarkerTag","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"MarkerTag","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"MarkerViewModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersTreeViewer"},{"name":"markTimeline","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"mas","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"match","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/glob"},{"name":"match","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"Match","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchModel"},{"name":"MATCHES_LIMIT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"matchesCamelCase","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"matchesContiguousSubString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"matchesFuzzy","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"matchesFuzzy2","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"matchesFuzzyCodiconAware","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/codicon"},{"name":"matchesPrefix","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"matchesScheme","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/opener/common/opener"},{"name":"matchesStrictPrefix","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"matchesSubString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"matchesWords","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"MatchFindAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"MatchFocusKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"matchMedia","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"MatchRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchResultsView"},{"name":"Math","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MAX_BYTES_ON_DISK","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"MAX_CONNECTION_FAILURES_BEFORE_WARN","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"MAX_FILE_SIZE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"MAX_FOLDING_REGIONS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingRanges"},{"name":"MAX_HEAP_SIZE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"MAX_LINE_NUMBER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingRanges"},{"name":"MAX_OUTPUT_LENGTH","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"MAX_VALUE_RENDER_LENGTH_IN_VIEWLET","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/baseDebugView"},{"name":"maxHeaderSize","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"MaximizeGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"maxSatisfying","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"mayIncludeActionsOfKind","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/types"},{"name":"measure","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"Measurement","kind":"enum member","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/DataPointType"},{"name":"MediaDeviceInfo","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaDevices","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaElementAudioSourceNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaEncryptedEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaKeyMessageEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaKeys","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaKeySession","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaKeyStatusMap","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaKeySystemAccess","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaQueryList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaQueryListEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaSource","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaStream","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaStreamAudioDestinationNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaStreamAudioSourceNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaStreamError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaStreamErrorEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaStreamEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaStreamTrack","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaStreamTrackAudioSourceNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaStreamTrackEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Memento","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/memento"},{"name":"memoize","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/decorators"},{"name":"memory","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"Memory","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestMemory"},{"name":"memoryUsage","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"Menu","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"Menu","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/menu/menu"},{"name":"MENU_ESCAPED_MNEMONIC_REGEX","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/menu/menu"},{"name":"MENU_MNEMONIC_REGEX","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/menu/menu"},{"name":"menuBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"menubar","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Menubar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/menubar/electron-main/menubar"},{"name":"MenuBar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/menu/menubar"},{"name":"MENUBAR_SELECTION_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"MENUBAR_SELECTION_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"MENUBAR_SELECTION_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"MenubarControl","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/titlebar/menubarControl"},{"name":"MenubarMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/menubar/electron-main/menubarMainService"},{"name":"MenubarService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/menubar/electron-browser/menubarService"},{"name":"menuBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"MenuEntryActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/browser/menuEntryActionViewItem"},{"name":"menuForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"MenuId","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"MenuItem","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"MenuItemAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"MenuItemExtensionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"MenuPreventer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/menuPreventer"},{"name":"MenuRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"menuSelectionBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"menuSelectionBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"menuSelectionForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"menuSeparatorBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"MenuService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/menuService"},{"name":"merge","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"merge","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/settingsMerge"},{"name":"merge","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/extensionsMerge"},{"name":"merge","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/keybindingsMerge"},{"name":"merge","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/globalStateMerge"},{"name":"merge","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/snippetsMerge"},{"name":"mergeAllGroups","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"mergeBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"mergeChanges","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationModels"},{"name":"mergeCommonContentBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"mergeCommonHeaderBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"mergeCurrentContentBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"mergeCurrentHeaderBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"MergedEnvironmentVariableCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/environmentVariableCollection"},{"name":"mergeEnvironments","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment"},{"name":"MergeGroupMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorGroupsService"},{"name":"mergeIncomingContentBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"mergeIncomingHeaderBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"mergePagers","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/paging"},{"name":"mergeSort","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"MergeWindowTabsHandlerHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"MessageChannel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MessageChannel","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"worker_threads"},{"name":"MessageController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/message/messageController"},{"name":"MessageData","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/MessageData"},{"name":"MessageData","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"MessageEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MessagePort","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MessagePort","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"worker_threads"},{"name":"Messages","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/messages"},{"name":"MessageType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/inputbox/inputBox"},{"name":"MessageType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionHostProtocol"},{"name":"MetadataConsts","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"MetadataConsts","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/common/textMateService"},{"name":"MetadataConsts","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-textmate/release/main"},{"name":"METHODS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"MetricData","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/MetricData"},{"name":"MetricData","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"MIME_BINARY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/mime"},{"name":"MIME_TEXT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/mime"},{"name":"MIME_UNKNOWN","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/mime"},{"name":"MimeType","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MimeTypeArray","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MimeTypeRendererResolver","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"mimeTypeSupportedByCore","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"MIN_MAX_MEMORY_SIZE_MB","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/files"},{"name":"Minimap","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/minimap/minimap"},{"name":"MINIMAP_GUTTER_WIDTH","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"minimapBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"MinimapCharRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer"},{"name":"MinimapCharRendererFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/minimap/minimapCharRendererFactory"},{"name":"minimapError","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"minimapFindMatch","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"minimapGutterAddedBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"minimapGutterDeletedBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"minimapGutterModifiedBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"MinimapLinesRenderingData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/viewModel"},{"name":"MinimapPosition","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"MinimapPosition","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"minimapSelection","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"minimapSliderActiveBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"minimapSliderBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"minimapSliderHoverBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"MinimapTokensColorTracker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/minimapTokensColorTracker"},{"name":"minimapWarning","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"minimist","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/minimist/index"},{"name":"MinimizeOtherGroupsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MINIMUM_LETTER_SPACING","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"minimumTranslatedStrings","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/localizations/browser/minimalTranslations"},{"name":"minor","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"minSatisfying","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"minVersion","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"MirrorTextModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/mirrorTextModel"},{"name":"MissingDependencyError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensions"},{"name":"mixin","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/objects"},{"name":"mkdir","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"mkdirp","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"mkdirSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"mkdtemp","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"mkdtempSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"mnemonicButtonLabel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"mnemonicMenuLabel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"mocha","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Mocha","kind":"class","kindModifiers":"declare","sortText":"4"},{"name":"mock","kind":"alias","kindModifiers":"","sortText":"0"},{"name":"MockContextKeyService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/test/common/mockKeybindingService"},{"name":"MockDebugAdapter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/test/common/mockDebug"},{"name":"MockDebugService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/test/common/mockDebug"},{"name":"MockKeybindingService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/test/common/mockKeybindingService"},{"name":"MockMode","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/mocks/mockMode"},{"name":"MockObjectTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/test/browser/mockSearchTree"},{"name":"MockRawSession","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/test/common/mockDebug"},{"name":"MockSession","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/test/common/mockDebug"},{"name":"MockTextAreaWrapper","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/browser/controller/textAreaState.test"},{"name":"ModelBasedVariableResolver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetVariables"},{"name":"ModelConstants","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"ModelDecorationMinimapOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"ModelDecorationOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"ModelDecorationOverviewRulerOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"ModelRawContentChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelEvents"},{"name":"ModelRawEOLChanged","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelEvents"},{"name":"ModelRawFlush","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelEvents"},{"name":"ModelRawLineChanged","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelEvents"},{"name":"ModelRawLinesDeleted","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelEvents"},{"name":"ModelRawLinesInserted","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelEvents"},{"name":"ModelServiceImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/modelServiceImpl"},{"name":"ModelTransientSettingWatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/services/abstractCodeEditorService"},{"name":"ModesContentHoverWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/hover/modesContentHover"},{"name":"ModeServiceImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/modeServiceImpl"},{"name":"ModesGlyphHoverWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/hover/modesGlyphHover"},{"name":"ModesHoverController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/hover/hover"},{"name":"ModesRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/modesRegistry"},{"name":"MODIFIED_SETTING_TAG","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"modifiedItemIndicator","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"ModifierLabelProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keybindingLabels"},{"name":"modify","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"modifySearchEditorContextLinesCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"module","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Module","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"module"},{"name":"monaco","kind":"module","kindModifiers":"declare","sortText":"4"},{"name":"MonacoEnvironment","kind":"let","kindModifiers":"declare","sortText":"4"},{"name":"MonarchBracket","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"MonarchTokenizer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchLexer"},{"name":"monitorEventLoopDelay","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"perf_hooks"},{"name":"MonospaceLineBreaksComputerFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/monospaceLineBreaksComputer"},{"name":"MouseController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listWidget"},{"name":"MouseEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MouseHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/mouseHandler"},{"name":"MouseTarget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/mouseTarget"},{"name":"MouseTargetFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/mouseTarget"},{"name":"MouseTargetType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorBrowser"},{"name":"MouseTargetType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"MouseWheelClassifier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/scrollableElement"},{"name":"move","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"move","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"MOVE_ACTIVE_EDITOR_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"MOVE_FILE_TO_TRASH_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"moveBy","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"MoveCaretCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/caretOperations/moveCaretCommand"},{"name":"MoveCellEdit","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/cellEdit"},{"name":"moveCursor","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"readline"},{"name":"MoveEditorLeftInGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveEditorRightInGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveEditorToAboveGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveEditorToBelowGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveEditorToFirstGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveEditorToLastGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveEditorToLeftGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveEditorToNextGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveEditorToPreviousGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveEditorToRightGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"moveFileToTrashHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"MoveFocusedViewAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"MoveGroupDownAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveGroupLeftAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveGroupRightAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveGroupUpAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveLinesCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/moveLinesCommand"},{"name":"MoveOperations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorMoveOperations"},{"name":"MoveSelectionToNextFindMatchAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"MoveSelectionToPreviousFindMatchAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"moveTo","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"MoveToNextChangeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"MoveToPreviousChangeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"MoveWindowTabToNewWindowHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"MoveWordCommand","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"MSAssertion","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSBlobBuilder","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"msContentScript","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSFIDOCredentialAssertion","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSFIDOSignature","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSFIDOSignatureAssertion","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSGesture","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSGestureEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSGraphicsTrust","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSInputMethodContext","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSMediaKeyError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSMediaKeyMessageEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSMediaKeyNeededEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSMediaKeys","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSMediaKeySession","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSPointerEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSStream","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"msWriteProfilerMark","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"MultiCursorSelectionController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"MultiCursorSelectionControllerAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"MultiCursorSession","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"MultiCursorSessionResult","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"MultilineTokens","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/tokensStore"},{"name":"MultilineTokens2","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/tokensStore"},{"name":"MultilineTokensBuilder","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/tokensStore"},{"name":"MultiModelEditStackElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/editStack"},{"name":"MultipleEditorGroupsContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"MultiplexLayoutController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/gridview"},{"name":"MultiplexLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"multiSelectModifierSettingKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"MutableDisposable","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/lifecycle"},{"name":"MutationEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MutationObserver","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MutationRecord","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Mutex","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/windows-mutex/index"},{"name":"MyArray","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/diff/diff"},{"name":"MyEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/parts/editor/baseEditor.test"},{"name":"MyOtherEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/parts/editor/baseEditor.test"},{"name":"name","kind":"const","kindModifiers":"declare","sortText":"4"},{"name":"NamedNodeMap","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"namespace","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Namespace","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"NaN","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"NativeAccessibilityService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/accessibility/electron-browser/accessibilityService"},{"name":"NativeBackupTracker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/backup/electron-browser/backupTracker"},{"name":"NativeClipboardService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/clipboard/electron-browser/clipboardService"},{"name":"NativeExtHostSearch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostSearch"},{"name":"nativeImage","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"NativeImage","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"NativeLifecycleService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/lifecycle/electron-browser/lifecycleService"},{"name":"NativePathService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/path/electron-browser/pathService"},{"name":"NativeRequestService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/request/electron-browser/requestService"},{"name":"NativeResolvedKeybinding","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper"},{"name":"NativeResourceIdentityService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/resource/node/resourceIdentityServiceImpl"},{"name":"NativeSimpleFileDialog","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/electron-browser/simpleFileDialog"},{"name":"NativeStorageService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/node/storageService"},{"name":"NativeTelemetryOptOut","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-browser/telemetryOptOut"},{"name":"NativeTextFileEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/electron-browser/textFileEditor"},{"name":"NativeTextFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService"},{"name":"NativeTextSearchManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/textSearchManager"},{"name":"nativeTheme","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"NativeUpdateService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/update/electron-browser/updateService"},{"name":"NativeWindow","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/window"},{"name":"NativeWorkbenchEnvironmentService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/environment/electron-browser/environmentService"},{"name":"NativeWorkspaceEditingService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workspaces/electron-browser/workspaceEditingService"},{"name":"NativeWorkspacesService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workspaces/electron-browser/workspacesService"},{"name":"NavigateBackwardsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"NavigateBetweenGroupsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"NavigateForwardAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"NavigateLastAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"NavigateToLastEditLocationAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"NavigationModeAddon","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/addons/navigationModeAddon"},{"name":"NavigationPreloadManager","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"navigator","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Navigator","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"neq","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"net","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"netLog","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"networkInterfaces","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"never","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"NEVER_MEASURE_RENDER_TIME_STORAGE_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"NeverShowAgainScope","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/notification/common/notification"},{"name":"new","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"NEW_FILE_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"NEW_FILE_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"NEW_FOLDER_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"NEW_FOLDER_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"NewEditorGroupAboveAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"NewEditorGroupBelowAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"NewEditorGroupLeftAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"NewEditorGroupRightAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"NewExplorerItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/explorerModel"},{"name":"NewFileAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"NewFolderAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"NewWindowAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/windowActions"},{"name":"NewWindowAPICommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/apiCommands"},{"name":"newWindowCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"NewWindowTabHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"newWriteableBufferStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"newWriteableStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/stream"},{"name":"NEXT_COMPRESSED_FOLDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"nextCharLength","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"NextCommentThreadAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsEditorContribution"},{"name":"NextMarkerAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoError/gotoError"},{"name":"NextMatchFindAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"NextMatchFindAction2","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"NextPanelViewAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"NextSelectionMatchFindAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"NextSideBarViewAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/activitybar/activitybarActions"},{"name":"nextTick","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/process"},{"name":"nextTick","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"NO_KEY_MODS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/quickinput/common/quickInput"},{"name":"noAsar","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"NODATA","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"Node","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"nodeAcceptEdit","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"NodeCachedDataCleaner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner"},{"name":"NodeClient","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/NodeClient"},{"name":"NodeColor","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"NodeColor","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"NodeDebugHelperService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/node/debugHelperService"},{"name":"NodeFilter","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"NodeIterator","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"NodeJS","kind":"module","kindModifiers":"declare","sortText":"4"},{"name":"NodeList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"noDeprecation","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"nodeReadableToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/stream"},{"name":"NodeSocket","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc.net"},{"name":"nodeSocketFactory","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/node/nodeSocketFactory"},{"name":"nodesToArrays","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/browser/ui/grid/util"},{"name":"nodeStreamToVSBufferReadable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/stream"},{"name":"NodeTestBackupFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test"},{"name":"NoEditorsVisibleContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"noIntlCompareFileNames","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/comparers"},{"name":"NOMEM","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"NoMemory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestMemory"},{"name":"NONAME","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"NoOpNotification","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/notification/common/notification"},{"name":"NoOpProgress","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/notification/common/notification"},{"name":"NoOpWorkspaceTagsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tags/browser/workspaceTagsService"},{"name":"normalize","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"normalize","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"normalizeDriveLetter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"normalizeExpression","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/test/browser/queryBuilder.test"},{"name":"normalizeFileChanges","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/watcher"},{"name":"normalizeGitHubUrl","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/issue/common/issueReporterUtil"},{"name":"normalizeNFC","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/normalization"},{"name":"normalizeNFD","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/normalization"},{"name":"normalizePath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"normalizeRoots","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/unix/chokidarWatcherService"},{"name":"normalizeVersion","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensionValidator"},{"name":"NoTabsTitleControl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/noTabsTitleControl"},{"name":"notDeepEqual","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"notDeepStrictEqual","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"notebook","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"NOTEBOOK_CELL_EDITABLE_CONTEXT_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"NOTEBOOK_CELL_MARKDOWN_EDIT_MODE_CONTEXT_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"NOTEBOOK_CELL_RUN_STATE_CONTEXT_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"NOTEBOOK_CELL_TYPE_CONTEXT_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"NOTEBOOK_DISPLAY_ORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"NOTEBOOK_EDITABLE_CONTEXT_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"NOTEBOOK_EDITOR_CURSOR_BOUNDARY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"NOTEBOOK_EDITOR_EDITABLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookBrowser"},{"name":"NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookBrowser"},{"name":"NOTEBOOK_EDITOR_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookBrowser"},{"name":"NOTEBOOK_EXECUTING_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"NOTEBOOK_VIEW_TYPE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"NotebookCellList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/notebookCellList"},{"name":"NotebookCellListDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer"},{"name":"NotebookCellMetadataDefaults","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel"},{"name":"NotebookCellTextModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel"},{"name":"NotebookCodeEditors","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookEditor"},{"name":"NotebookContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebook.contribution"},{"name":"notebookDocumentMetadataDefaults","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"NotebookEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookEditor"},{"name":"NotebookEditorCellEdit","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostNotebook"},{"name":"NotebookEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookEditorInput"},{"name":"NotebookEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookEditorInput"},{"name":"NotebookEditorOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookEditor"},{"name":"NotebookEventDispatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher"},{"name":"NotebookFindWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/contrib/notebookFindWidget"},{"name":"NotebookLayoutChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher"},{"name":"NotebookMetadataChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher"},{"name":"notebookOutputContainerColor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookEditor"},{"name":"NotebookOutputRendererInfo","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookOutputRenderer"},{"name":"NotebookOutputRendererInfoStore","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookService"},{"name":"notebookProviderExtensionPoint","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/extensionPoint"},{"name":"NotebookProviderInfo","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookProvider"},{"name":"NotebookProviderInfoStore","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookService"},{"name":"NotebookRegistry","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookRegistry"},{"name":"notebookRendererExtensionPoint","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/extensionPoint"},{"name":"NotebookService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookService"},{"name":"NotebookTextModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/model/notebookTextModel"},{"name":"NotebookViewEventType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher"},{"name":"NotebookViewModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel"},{"name":"notEqual","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"NOTFOUND","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"Notification","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Notification","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"NotificationActionRunner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsActions"},{"name":"NotificationChangeType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/notifications"},{"name":"NotificationFocusedContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"NotificationHandle","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/notifications"},{"name":"NotificationRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsViewer"},{"name":"NOTIFICATIONS_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NOTIFICATIONS_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NOTIFICATIONS_CENTER_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NOTIFICATIONS_CENTER_HEADER_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NOTIFICATIONS_CENTER_HEADER_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NOTIFICATIONS_ERROR_ICON_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NOTIFICATIONS_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NOTIFICATIONS_INFO_ICON_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NOTIFICATIONS_LINKS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NOTIFICATIONS_TOAST_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NOTIFICATIONS_WARNING_ICON_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NotificationsAlerts","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsAlerts"},{"name":"NotificationsCenter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCenter"},{"name":"NotificationsCenterVisibleContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"NotificationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/notification/common/notificationService"},{"name":"NotificationsFilter","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/notification/common/notification"},{"name":"NotificationsList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsList"},{"name":"NotificationsListDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsViewer"},{"name":"NotificationsModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/notifications"},{"name":"NotificationsStatus","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsStatus"},{"name":"NotificationsToasts","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsToasts"},{"name":"NotificationsToastsVisibleContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"NotificationTemplateRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsViewer"},{"name":"NotificationViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/notifications"},{"name":"NotificationViewItemContentChangeKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/notifications"},{"name":"NotificationViewItemProgress","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/notifications"},{"name":"NOTIMP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"NotImplementedError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"NOTINITIALIZED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"notStrictEqual","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"NsfwWatcherService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/nsfw/nsfwWatcherService"},{"name":"null","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"NULL_LANGUAGE_IDENTIFIER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/nullMode"},{"name":"NULL_MODE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/nullMode"},{"name":"NULL_STATE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/nullMode"},{"name":"NullApiDeprecationService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostApiDeprecationService"},{"name":"NullAppender","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetryUtils"},{"name":"NullCommandService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/commands/common/commands"},{"name":"nullExtensionDescription","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensions"},{"name":"NullExtensionService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensions"},{"name":"NullFileSystemProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/test/common/nullFileSystemProvider"},{"name":"NullLifecycleService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/common/lifecycle"},{"name":"NullLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"NullOpenerService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/opener/common/opener"},{"name":"nullRange","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"NullTelemetryService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetryUtils"},{"name":"nullTokenize","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/nullMode"},{"name":"nullTokenize2","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/nullMode"},{"name":"number","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Number","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"NumberBadge","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/activity/common/activity"},{"name":"O_APPEND","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_CREAT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_DIRECT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_DIRECTORY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_DSYNC","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_EXCL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_NOATIME","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_NOCTTY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_NOFOLLOW","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_NONBLOCK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_RDONLY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_RDWR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_SYMLINK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_SYNC","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_TRUNC","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_WRONLY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"object","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Object","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ObjectIdentifier","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"ObjectTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/objectTree"},{"name":"ObjectTreeModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/objectTreeModel"},{"name":"off","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"OfflineAudioCompletionEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OfflineAudioContext","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"offscreenBuffering","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OffscreenCanvas","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OffscreenCanvasRenderingContext2D","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ok","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"ok","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/assert"},{"name":"on","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"on","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"onabort","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onafterprint","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onanimationcancel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onanimationend","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onanimationiteration","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onanimationstart","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onauxclick","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onbeforeprint","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onbeforeunload","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onblur","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"oncancel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"oncanplay","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"oncanplaythrough","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"once","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"once","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"events"},{"name":"once","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/functional"},{"name":"once","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"onchange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onclick","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onclose","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"oncompassneedscalibration","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"oncontextmenu","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"oncuechange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondblclick","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondevicelight","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondevicemotion","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondeviceorientation","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondeviceorientationabsolute","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onDidChangeFullscreen","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"onDidChangeKeyboardLayout","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/native-keymap/index"},{"name":"onDidChangeModelLanguage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"onDidChangeZoomLevel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"onDidCreateEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"onDidCreateModel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"ondrag","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondragend","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondragenter","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondragexit","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondragleave","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondragover","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondragstart","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondrop","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondurationchange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OneCursor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/oneCursor"},{"name":"OneLineRange","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"onemptied","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onended","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OnEnterSupport","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/onEnter"},{"name":"oneOf","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"OneReference","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/referencesModel"},{"name":"OneReferenceRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesTree"},{"name":"onerror","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OneSnippet","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetSession"},{"name":"onExtensionChanged","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensionsUtils"},{"name":"onfocus","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ongotpointercapture","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onhashchange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OnigScanner","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/onigasm-umd/main"},{"name":"OnigString","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/onigasm-umd/main"},{"name":"oninput","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"oninvalid","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onkeydown","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onkeypress","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onkeyup","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onLanguage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"onlanguagechange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ONLINE_SERVICES_SETTING_TAG","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"onload","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onloadeddata","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onloadedmetadata","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onloadstart","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onlostpointercapture","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmessage","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmessageerror","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmousedown","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmouseenter","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmouseleave","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmousemove","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmouseout","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmouseover","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmouseup","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmousewheel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmsgesturechange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmsgesturedoubletap","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmsgestureend","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmsgesturehold","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmsgesturestart","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmsgesturetap","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmsinertiastart","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmspointercancel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmspointerdown","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmspointerenter","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmspointerleave","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmspointermove","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmspointerout","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmspointerover","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmspointerup","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onoffline","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ononline","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onorientationchange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpagehide","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpageshow","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpause","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onplay","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onplaying","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpointercancel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpointerdown","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpointerenter","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpointerleave","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpointermove","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpointerout","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpointerover","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpointerup","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpopstate","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onprogress","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onratechange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onreadystatechange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onrejectionhandled","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onreset","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onresize","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onscroll","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onsecuritypolicyviolation","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onseeked","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onseeking","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onselect","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onselectionchange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onselectstart","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onstalled","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onstorage","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onsubmit","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onsuspend","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ontimeupdate","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ontoggle","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ontouchcancel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ontouchend","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ontouchmove","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ontouchstart","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ontransitioncancel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ontransitionend","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ontransitionrun","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ontransitionstart","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OnTypeFormattingEditProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"OnTypeRenameAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/rename/onTypeRename"},{"name":"OnTypeRenameContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/rename/onTypeRename"},{"name":"OnTypeRenameProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"onUnexpectedError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"onUnexpectedExternalError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"onunhandledrejection","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onunload","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onvolumechange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onvrdisplayactivate","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onvrdisplayblur","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onvrdisplayconnect","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onvrdisplaydeactivate","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onvrdisplaydisconnect","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onvrdisplayfocus","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onvrdisplaypointerrestricted","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onvrdisplaypointerunrestricted","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onvrdisplaypresentchange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onwaiting","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onwheel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onWillDisposeModel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"open","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"open","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"open","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"inspector"},{"name":"open","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/yauzl/index"},{"name":"OPEN_CREATE","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-sqlite3/index"},{"name":"OPEN_EDITOR_AT_INDEX_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"OPEN_PRIVATECACHE","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-sqlite3/index"},{"name":"OPEN_READONLY","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-sqlite3/index"},{"name":"OPEN_READWRITE","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-sqlite3/index"},{"name":"OPEN_SHAREDCACHE","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-sqlite3/index"},{"name":"OPEN_TO_SIDE_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"OPEN_URI","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-sqlite3/index"},{"name":"OpenAPICommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/apiCommands"},{"name":"openBreakpointSource","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/breakpointsView"},{"name":"OpenContext","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/node/window"},{"name":"OpenDebugConsoleAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugViewlet"},{"name":"opendir","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"opendirSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"OpenEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"OpenEditorsFocusedContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"OpenEditorsGroupContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"OpenEditorsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/openEditorsView"},{"name":"OpenEditorsVisibleContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"opener","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OpenerService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/services/openerService"},{"name":"OpenerValidatorContributions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/url/common/trustedDomainsValidator"},{"name":"OpenExplorerViewletAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/files.contribution"},{"name":"OpenExtensionLogsFolderAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/electron-browser/logsActions"},{"name":"OpenExtensionsFolderAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions"},{"name":"OpenExtensionsViewletAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"OpenFileAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceActions"},{"name":"OpenFileFolderAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceActions"},{"name":"openFilePreserveFocusHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"OpenFirstEditorInGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"OpenFolderAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceActions"},{"name":"OpenFolderAPICommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/apiCommands"},{"name":"OpenInEditorCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"OpenInEditorCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"OpenIssueReporter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/apiCommands"},{"name":"OpenIssueReporterActionId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/issue/common/commands"},{"name":"OpenLastEditorInGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"OpenLatestReleaseNotesInBrowserAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/browser/update"},{"name":"OpenLocalFileCommand","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/browser/simpleFileDialog"},{"name":"OpenLocalFileFolderCommand","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/browser/simpleFileDialog"},{"name":"OpenLocalFolderCommand","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/browser/simpleFileDialog"},{"name":"OpenLogsFolderAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/electron-browser/logsActions"},{"name":"OpenMatchToSide","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"openModeSettingKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"OpenNewEditorCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"OpenNewEditorToSideCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"OpenNextEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"OpenNextEditorInGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"OpenNextRecentlyUsedEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"OpenNextRecentlyUsedEditorInGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"OpenPreviousEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"OpenPreviousEditorInGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"OpenPreviousRecentlyUsedEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"OpenPreviousRecentlyUsedEditorInGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"OpenProcessExplorer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/issue/electron-browser/issueActions"},{"name":"OpenRecentAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/windowActions"},{"name":"OpenResultsInEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"OpenSearchEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"OpenSearchEditorToSideAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"openSearchView","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"OpenSearchViewletAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"openStdin","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"openSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"OpenUrlAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/url/common/url.contribution"},{"name":"OpenViewPickerAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess"},{"name":"OpenWebviewDeveloperToolsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/electron-browser/webviewCommands"},{"name":"openWindowCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"OpenWindowSessionLogFileAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/common/logsActions"},{"name":"OpenWithAPICommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/apiCommands"},{"name":"OpenWorkspaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceActions"},{"name":"OpenWorkspaceButtonContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorWidgets"},{"name":"OpenWorkspaceConfigFileAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceActions"},{"name":"OperatingSystem","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"Option","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"optional","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/instantiation"},{"name":"OPTIONS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/argv"},{"name":"or","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"OrganizeImportsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionCommands"},{"name":"organizeImportsCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeAction"},{"name":"orientation","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Orientation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/sash/sash"},{"name":"origin","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"originalFSPath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"orthogonal","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/gridview"},{"name":"OS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"OS_PROVIDES_FILE_PROTECTION","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"OscillatorNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"outerHeight","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"outerWidth","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OutgoingMessage","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"OutlineAccessibilityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlineConfigKeys","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outline"},{"name":"OutlineDataSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlineElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineModel"},{"name":"OutlineElementRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlineElementTemplate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlineFilter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlineGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineModel"},{"name":"OutlineGroupRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlineGroupTemplate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlineIdentityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlineItemComparator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlineModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineModel"},{"name":"OutlineNavigationLabelProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlinePane","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/outline/browser/outlinePane"},{"name":"OutlineSortOrder","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlineViewFiltered","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outline"},{"name":"OutlineViewFocused","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outline"},{"name":"OutlineViewId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outline"},{"name":"OutlineVirtualDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutOfProcessWin32FolderWatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/win32/csharpWatcherService"},{"name":"OUTPUT_MIME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"OUTPUT_MODE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"OUTPUT_SCHEME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"OUTPUT_SERVICE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"OUTPUT_VIEW_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"OutputAppender","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/output/node/outputAppender"},{"name":"OutputBuilder","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/keybindingIO"},{"name":"OutputChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepSearchUtils"},{"name":"OutputChannelModelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/output/common/outputChannelModelService"},{"name":"OutputChannelModelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/output/electron-browser/outputChannelModelService"},{"name":"OutputEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/browser/outputView"},{"name":"OutputLinkComputer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/outputLinkComputer"},{"name":"OutputLinkProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/outputLinkProvider"},{"name":"OutputPosition","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/splitLinesCollection"},{"name":"OutputRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer"},{"name":"OutputService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/browser/outputServices"},{"name":"OutputViewPane","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/browser/outputView"},{"name":"outside","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"OverconstrainedError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OverflowEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OverlayWidgetDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/zoneWidget/zoneWidget"},{"name":"OverlayWidgetPositionPreference","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorBrowser"},{"name":"OverlayWidgetPositionPreference","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"OVERRIDE_PROPERTY_PATTERN","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"overrideIdentifierFromKey","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"OverviewRuler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler"},{"name":"overviewRulerAddedForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"overviewRulerCommentingRangeForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentGlyphWidget"},{"name":"overviewRulerCommonContentForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"overviewRulerCurrentContentForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"overviewRulerDeletedForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"overviewRulerError","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"overviewRulerFindMatchForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"overviewRulerIncomingContentForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"overviewRulerInfo","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"OverviewRulerLane","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"OverviewRulerLane","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"OverviewRulerLane","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"overviewRulerModifiedForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"overviewRulerRangeHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"overviewRulerSelectionHighlightForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"overviewRulerWarning","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"OverviewRulerZone","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/overviewZoneManager"},{"name":"OverviewZoneManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/overviewZoneManager"},{"name":"package","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"pad","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"PageCoordinates","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorDom"},{"name":"PagedList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listPaging"},{"name":"PagedModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/paging"},{"name":"PagedScreenReaderStrategy","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/textAreaState"},{"name":"PageTransitionEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PageViewData","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/PageViewData"},{"name":"PageViewData","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"pageXOffset","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"pageYOffset","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Pane","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/splitview/paneview"},{"name":"PaneComposite","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/panecomposite"},{"name":"PaneCompositePanel","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/panel"},{"name":"Panel","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/panel"},{"name":"PANEL_ACTIVE_TITLE_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"PANEL_ACTIVE_TITLE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"PANEL_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"PANEL_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"PANEL_DRAG_AND_DROP_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"PANEL_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"PANEL_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/outline/browser/outline.contribution"},{"name":"PANEL_INACTIVE_TITLE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"PANEL_INPUT_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"PanelActivityAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"PanelDescriptor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/panel"},{"name":"PanelFocusContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/panel"},{"name":"PanelKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"PanelPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelPart"},{"name":"PanelPositionContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/panel"},{"name":"PanelRegistry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/panel"},{"name":"PaneView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/splitview/paneview"},{"name":"PannerNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ParameterHintsModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/parameterHints/parameterHintsModel"},{"name":"ParameterHintsWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/parameterHints/parameterHintsWidget"},{"name":"ParameterInformation","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ParameterInformation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ParameterInformation","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"parent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"parentPort","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"worker_threads"},{"name":"parse","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"parse","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"querystring"},{"name":"parse","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"url"},{"name":"parse","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"parse","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"parse","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/marked/marked"},{"name":"parse","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/marshalling"},{"name":"parse","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/glob"},{"name":"parse","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/console"},{"name":"parse","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"parse","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/plistParser"},{"name":"parse","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskConfiguration"},{"name":"parse","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/tokenization/typescript"},{"name":"parseArgs","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/argv"},{"name":"parseClassifierString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"parseCLIProcessArgv","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/argvHelper"},{"name":"parseCodicons","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/codicon"},{"name":"ParsedTokenThemeRule","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/tokenization"},{"name":"ParseErrorCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"parseExtensionDevOptions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionDevOptions"},{"name":"parseExtensionHostPort","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/environmentService"},{"name":"parseFloat","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"parseHrefAndDimensions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/htmlContent"},{"name":"parseInt","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"parseKeyboardLayoutDescription","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/keymapInfo"},{"name":"parseLineAndColumnAware","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/node/paths"},{"name":"parseLinkedText","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/linkedText"},{"name":"parseMainProcessArgv","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/argvHelper"},{"name":"ParseOptions","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"parsePathArg","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/environmentService"},{"name":"parseQuery","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"parser","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/marked/marked"},{"name":"Parser","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/parsers"},{"name":"parseRawGrammar","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-textmate/release/main"},{"name":"parseReplaceString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/replacePattern"},{"name":"parseSavedSearchEditor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization"},{"name":"parseSearchPort","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/environmentService"},{"name":"parseTokenTheme","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/tokenization"},{"name":"parseTree","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"parseUserDataDir","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/environmentService"},{"name":"parseVersion","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensionValidator"},{"name":"parseWithLocation","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/plistParser"},{"name":"Part","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/part"},{"name":"PartFingerprint","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewPart"},{"name":"PartFingerprints","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewPart"},{"name":"PartialModelCursorState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"PartialViewCursorState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"Parts","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/layout/browser/layoutService"},{"name":"PassThrough","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"stream"},{"name":"PASTE_FILE_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"pasteFileHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"PasteWebviewEditorCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/electron-browser/webviewCommands"},{"name":"patch","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"Path2D","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"pathIncludedInQuery","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"PathIterator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"pathOrURIToURI","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"pathsToEditors","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"pathToFileURL","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"url"},{"name":"PatternExcludesFocusedKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"PatternIncludesFocusedKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"PatternInputWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/patternInputWidget"},{"name":"patternsToIExpression","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/test/browser/queryBuilder.test"},{"name":"PAUSE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"PAUSE_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"PauseableEmitter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/event"},{"name":"PaymentAddress","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PaymentRequest","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PaymentRequestUpdateEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PaymentResponse","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"pbkdf2","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"pbkdf2Sync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"PeekContext","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewEditorBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewEditorGutterBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewEditorMatchHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewEditorMatchHighlightBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewResultsBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewResultsFileForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewResultsMatchForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewResultsMatchHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewResultsSelectionBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewResultsSelectionForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewTitleBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewTitleForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewTitleInfoForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"PeekViewWidget","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"performance","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"performance","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"perf_hooks"},{"name":"Performance","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PerformanceEntry","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PerformanceMark","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PerformanceMeasure","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PerformanceNavigation","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PerformanceNavigationTiming","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PerformanceObserver","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PerformanceObserver","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"perf_hooks"},{"name":"PerformanceObserverEntryList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PerformanceResourceTiming","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PerformanceTiming","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PerfviewContrib","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor"},{"name":"PerfviewInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor"},{"name":"PerfWidgetExternal","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PeriodicWave","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PermissionRequest","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PermissionRequestedEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Permissions","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PermissionStatus","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PersistentConnectionEventType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"PersistentContributableViewsModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/views"},{"name":"PersistentProtocol","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc.net"},{"name":"personalbar","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PICK_WORKSPACE_FOLDER_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceCommands"},{"name":"pickerGroupBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"pickerGroupForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"PickerQuickAccessProvider","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/browser/pickerQuickAccess"},{"name":"pid","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"Piece","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase"},{"name":"pieceToQuery","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/fuzzyScorer"},{"name":"PieceTreeBase","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase"},{"name":"PieceTreeTextBuffer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer"},{"name":"PieceTreeTextBufferBuilder","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder"},{"name":"PieceTreeTextBufferFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder"},{"name":"pipeline","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"stream"},{"name":"Placeholder","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"PlaceHolderPanelActivityAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"PlaceHolderToggleCompositePinnedAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/activitybar/activitybarActions"},{"name":"PlaceHolderToggleCompositePinnedAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"PlaceHolderViewletActivityAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/activitybar/activitybarActions"},{"name":"PLAINTEXT_LANGUAGE_IDENTIFIER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/modesRegistry"},{"name":"PLAINTEXT_MODE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/modesRegistry"},{"name":"platform","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"platform","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"platform","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/process"},{"name":"platform","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"Platform","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"PlatformToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"Plugin","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PluginArray","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"POINT_CONVERSION_COMPRESSED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"POINT_CONVERSION_HYBRID","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"POINT_CONVERSION_UNCOMPRESSED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"PointerEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PointerEventHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/pointerHandler"},{"name":"PointerHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/pointerHandler"},{"name":"PointerHandlerLastRenderData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/mouseTarget"},{"name":"PopStateEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"popup","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/contextmenu/electron-browser/contextmenu"},{"name":"position","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"Position","kind":"alias","kindModifiers":"","sortText":"0"},{"name":"positionFromString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/layout/browser/layoutService"},{"name":"positionIsInRange","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers"},{"name":"PositionPanelActionConfigs","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"positionToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/layout/browser/layoutService"},{"name":"posix","kind":"module","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"posix","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"postMessage","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"powerMonitor","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"powerSaveBlocker","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"POWERSHELL_PATH","kind":"property","kindModifiers":"private,static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"ppid","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"prebakedMiniMaps","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/minimap/minimapPreBaked"},{"name":"PreferencesContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferencesContribution"},{"name":"PreferencesEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesEditor"},{"name":"PreferencesEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesEditorInput"},{"name":"PreferencesLabel","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"PreferencesSearchService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesSearch"},{"name":"PreferencesService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/browser/preferencesService"},{"name":"preferredSideBySideGroupDirection","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorGroupsService"},{"name":"prefersExecuteOnUI","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsUtil"},{"name":"prefersExecuteOnWorkspace","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsUtil"},{"name":"PrefixMemory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestMemory"},{"name":"PrefixSumComputer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/prefixSumComputer"},{"name":"PrefixSumIndexOfResult","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/prefixSumComputer"},{"name":"prepareActions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/actionbar/actionbar"},{"name":"prepareCommand","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/node/terminals"},{"name":"prepareQuery","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/fuzzyScorer"},{"name":"prepend","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"prependListener","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"prependListener","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"prependOnceListener","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"prependOnceListener","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"prerelease","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"PresentationOptions","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"presentationSchema","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugSchemas"},{"name":"PreserveCaseCheckbox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/findinput/replaceInput"},{"name":"prevCharLength","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"PREVIEW_DIR_NAME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"PREVIOUS_COMPRESSED_FOLDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"PreviousMatchFindAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"PreviousMatchFindAction2","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"PreviousPanelViewAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"PreviousSelectionMatchFindAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"PreviousSideBarViewAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/activitybar/activitybarActions"},{"name":"print","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"privateDecrypt","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"privateEncrypt","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"ProblemCollectorEventKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemCollectors"},{"name":"ProblemHandlingStrategy","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemCollectors"},{"name":"ProblemLocationKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"ProblemMatcherParser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"ProblemMatcherRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"ProblemPatternParser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"ProblemPatternRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"problemsErrorIconForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"problemsInfoIconForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"problemsWarningIconForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"process","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ProcessDataFlag","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/windows-process-tree/index"},{"name":"ProcessExecution","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ProcessExecution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ProcessExecutionDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"ProcessExecutionOptionsDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"ProcessingInstruction","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ProcessRunnerDetector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/node/processRunnerDetector"},{"name":"ProcessState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"ProcessTaskSystem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/node/processTaskSystem"},{"name":"product","kind":"alias","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/product/common/product"},{"name":"ProductContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/browser/update"},{"name":"ProductIconThemeData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/browser/productIconThemeData"},{"name":"productService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"profile","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"profileEnd","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"ProfileSessionState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"Progress","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/progress/common/progress"},{"name":"ProgressBadge","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/activity/common/activity"},{"name":"ProgressBar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/progressbar/progressbar"},{"name":"progressBarBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"ProgressBarIndicator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/progress/browser/progressIndicator"},{"name":"ProgressEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ProgressLocation","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ProgressLocation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/progress/common/progress"},{"name":"ProgressLocation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ProgressLocation","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"ProgressService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/progress/browser/progressService"},{"name":"Promise","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PromiseRejectionEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"promises","kind":"module","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"promises","kind":"module","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"promisify","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"prompt","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"protocol","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"Protocol","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc.net"},{"name":"Protocol","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc.electron"},{"name":"ProtocolConstants","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc.net"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"events"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"module"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"stream"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"https-proxy-agent"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"mocha"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Config"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/Domain"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/AvailabilityData"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/Base"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/ContextTagKeys"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/Data"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/DataPoint"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/Envelope"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/EventData"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/StackFrame"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/ExceptionDetails"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/ExceptionData"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/MessageData"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/MetricData"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/PageViewData"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/RemoteDependencyData"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/RequestData"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Context"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Channel"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/TelemetryClient"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/NodeClient"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/http-proxy-agent/index"},{"name":"provideDecorations","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerDecorationsProvider"},{"name":"provideSelectionRanges","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/smartSelect/smartSelect"},{"name":"provideSignatureHelp","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/parameterHints/provideSignatureHelp"},{"name":"provideSuggestionItems","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggest"},{"name":"Proxy","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ProxyAgent","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-proxy-agent/index"},{"name":"ProxyAuthHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-main/auth"},{"name":"ProxyIdentifier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/proxyIdentifier"},{"name":"pseudoRandomBytes","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"publicDecrypt","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"publicEncrypt","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"PublicKeyCredential","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PushManager","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PushSubscription","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PushSubscriptionOptions","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"pushToEnd","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"pushToStart","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"Query","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensionQuery"},{"name":"QueryBuilder","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/queryBuilder"},{"name":"QueryGlobTester","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"QueryType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"Queue","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"queueMicrotask","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"QuickAccessController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/browser/quickAccess"},{"name":"QuickAccessLeastRecentlyUsedEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"QuickAccessLeastRecentlyUsedEditorInGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"QuickAccessNavigateNextAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/quickAccessActions"},{"name":"QuickAccessPreviousEditorFromHistoryAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"QuickAccessPreviousRecentlyUsedEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"QuickAccessPreviousRecentlyUsedEditorInGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"QuickAccessRegistry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/common/quickAccess"},{"name":"QuickAccessViewPickerAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess"},{"name":"QuickCommandNLS","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standaloneStrings"},{"name":"QuickFixAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionCommands"},{"name":"QuickFixAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersViewActions"},{"name":"QuickFixActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersViewActions"},{"name":"QuickFixController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionCommands"},{"name":"QuickHelpNLS","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standaloneStrings"},{"name":"quickInputBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"QuickInputBox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/quickinput/browser/quickInputBox"},{"name":"QuickInputButtons","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"QuickInputButtons","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"QuickInputController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/quickinput/browser/quickInput"},{"name":"QuickInputEditorContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputServiceImpl"},{"name":"QuickInputEditorWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputServiceImpl"},{"name":"quickInputForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"QuickInputList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/quickinput/browser/quickInputList"},{"name":"QuickInputListFocus","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/quickinput/browser/quickInputList"},{"name":"QuickInputService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/browser/quickInput"},{"name":"QuickInputService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/quickinput/browser/quickInputService"},{"name":"quickInputTitleBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"QUICKOPEN_DETAIL_CONFIG","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/taskQuickPick"},{"name":"QUICKOPEN_SKIP_CONFIG","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/taskQuickPick"},{"name":"QuickOutlineNLS","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standaloneStrings"},{"name":"quickPickItemScorerAccessor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/quickinput/common/quickInput"},{"name":"QuickPickItemScorerAccessor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/quickinput/common/quickInput"},{"name":"QuickSwitchWindow","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"R_OK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"raceCancellation","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"raceTimeout","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"RadioGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/actions"},{"name":"RadioNodeList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RandomAccessReader","kind":"class","kindModifiers":"abstract,export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/yauzl/index"},{"name":"RandomBasedVariableResolver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetVariables"},{"name":"randomBytes","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"randomFill","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"randomFillSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"randomPort","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/ports"},{"name":"RandomSource","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"range","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"Range","kind":"alias","kindModifiers":"","sortText":"0"},{"name":"RangeError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RangeHighlightDecorations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/rangeDecorations"},{"name":"RangeHighlightDecorations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchModel"},{"name":"RangeMap","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/rangeMap"},{"name":"RangesCollector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/indentRangeProvider"},{"name":"RangesCollector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/syntaxRangeProvider"},{"name":"RangeUtil","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/lines/rangeUtil"},{"name":"RatingsWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWidgets"},{"name":"RawContentChangedType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelEvents"},{"name":"RawContextKey","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"RawDebugSession","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/rawDebugSession"},{"name":"rawListeners","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"RawObjectReplElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/replModel"},{"name":"rbDelete","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"rcompare","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"rcompareIdentifiers","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"ReactionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/reactionsAction"},{"name":"ReactionActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/reactionsAction"},{"name":"read","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"Readable","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"stream"},{"name":"ReadableStream","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ReadableStreamReader","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"readableToBuffer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"readCharWidths","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/config/charWidthReader"},{"name":"readdir","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"readdir","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"readDirsInDir","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"readdirSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"readdirSync","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"readdirWithFileTypes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"readFile","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"readFile","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"readFileSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"readFontInfo","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/config/configuration"},{"name":"readFromStdin","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/stdin"},{"name":"readlink","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"readlinkSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"readonly","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"ReadonlyEditorContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"readRawMapping","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/test/electron-browser/keyboardMapperTestUtils"},{"name":"ReadStream","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"ReadStream","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tty"},{"name":"readSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"readTrustedDomains","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/url/common/trustedDomains"},{"name":"readUInt16LE","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"readUInt32BE","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"readUInt32LE","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"readUInt8","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"realcaseSync","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/extpath"},{"name":"realpath","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"realpath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/extpath"},{"name":"realpathSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"realpathSync","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/extpath"},{"name":"ReapplyBreakpointsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"RecommendationWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWidgets"},{"name":"RecommendedExtensionsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews"},{"name":"recomputeMaxEnd","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"recomputeTreeMetadata","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"ReconnectionPermanentFailureEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"ReconnectionRunningEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"ReconnectionWaitEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"Recoverable","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"repl"},{"name":"RedoWebviewEditorCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/electron-browser/webviewCommands"},{"name":"RefactorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionCommands"},{"name":"refactorCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeAction"},{"name":"RefCountedStyleSheet","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/services/codeEditorServiceImpl"},{"name":"ReferenceCollection","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/lifecycle"},{"name":"ReferenceError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ReferenceProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"ReferencesController","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesController"},{"name":"ReferencesModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/referencesModel"},{"name":"ReferenceWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget"},{"name":"Reflect","kind":"module","kindModifiers":"declare","sortText":"4"},{"name":"RefreshAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"RefreshExplorerView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"REFUSED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"REG_BINARY","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"REG_DWORD","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"REG_EXPAND_SZ","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"REG_MULTI_SZ","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"REG_NONE","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"REG_QWORD","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"REG_SZ","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"REG_TYPES","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"RegexCheckbox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/findinput/findInputCheckboxes"},{"name":"RegExp","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"regExpContainsBackreference","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"regExpFlags","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"regExpLeadsToEndlessLoop","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"register","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerAction2","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"registerCodeActionProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerCodeLensProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerColor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"registerColorProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerColors","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry"},{"name":"registerColorThemeExtensionPoint","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/themeExtensionPoints"},{"name":"registerColorThemeSchemas","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/colorThemeSchema"},{"name":"registerCommands","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"registerCompletionItemProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerConfiguration","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"registerContextMenuListener","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/contextmenu/electron-main/contextmenu"},{"name":"registerContributions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/replaceContributions"},{"name":"registerContributions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchWidget"},{"name":"registerDeclarationProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerDefaultLanguageCommand","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"registerDefinitionProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerDiffEditorContribution","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"registerDocumentFormattingEditProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerDocumentHighlightProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerDocumentRangeFormattingEditProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerDocumentRangeSemanticTokensProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerDocumentSemanticTokensProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerDocumentSymbolProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerEditorAction","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"registerEditorCommand","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"registerEditorContribution","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"registerFileIconThemeExtensionPoint","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/themeExtensionPoints"},{"name":"registerFileIconThemeSchemas","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/fileIconThemeSchema"},{"name":"registerFileProtocol","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols"},{"name":"registerFoldingRangeProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerHoverProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerIcon","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/iconRegistry"},{"name":"registerImplementationProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerInstantiatedEditorAction","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"registerLanguageCommand","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"registerLinkProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerModelAndPositionCommand","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"registerModelCommand","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"registerNotificationCommands","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"registerOnTypeFormattingEditProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerOnTypeRenameProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerOutputTransform","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookRegistry"},{"name":"registerProductIconThemeExtensionPoint","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/themeExtensionPoints"},{"name":"registerProductIconThemeSchemas","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/productIconThemeSchema"},{"name":"registerReferenceProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerRemoteContributions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/electron-browser/terminalRemote"},{"name":"registerRenameProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerSelectionRangeProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerSignatureHelpProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerSingleton","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/extensions"},{"name":"registerTerminalActions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"registerTestEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"registerTextMime","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/mime"},{"name":"registerThemingParticipant","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"registerTypeDefinitionProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerWindowDriver","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/browser/driver"},{"name":"registerWindowDriver","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/electron-browser/driver"},{"name":"Registry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/registry/common/platform"},{"name":"Registry","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-textmate/release/main"},{"name":"ReindentLinesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"ReindentSelectedLinesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"ReinstallAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"rejects","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"RelatedInformation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersModel"},{"name":"RelatedInformationRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersTreeViewer"},{"name":"relative","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"relative","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"relativePath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"RelativePattern","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"RelativePattern","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"Relay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/event"},{"name":"RelayURLService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/url/electron-browser/urlService"},{"name":"release","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"release","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"releaseEvents","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"ReleaseNotesManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/browser/releaseNotesEditor"},{"name":"ReloadAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ReloadWebviewAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewCommands"},{"name":"ReloadWindowAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/windowActions"},{"name":"ReloadWindowWithExtensionsDisabledAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"remeasureFonts","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"remote","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"REMOTE_EXPLORER_TYPE_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/remoteExplorerService"},{"name":"REMOTE_FILE_SYSTEM_CHANNEL_NAME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/remoteAgentFileSystemChannel"},{"name":"REMOTE_HOST_SCHEME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteHosts"},{"name":"REMOTE_MACHINE_SCOPES","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"RemoteAgentConnection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/abstractRemoteAgentService"},{"name":"RemoteAgentService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl"},{"name":"RemoteAgentService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl"},{"name":"RemoteAuthorities","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/network"},{"name":"RemoteAuthorityResolverError","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"RemoteAuthorityResolverError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAuthorityResolver"},{"name":"RemoteAuthorityResolverError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"RemoteAuthorityResolverErrorCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAuthorityResolver"},{"name":"RemoteAuthorityResolverService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/browser/remoteAuthorityResolverService"},{"name":"RemoteAuthorityResolverService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService"},{"name":"RemoteBadgeWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWidgets"},{"name":"RemoteConnectionState","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/contextkeys"},{"name":"RemoteDependencyData","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/RemoteDependencyData"},{"name":"RemoteDependencyData","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"RemoteDependencyDataConstants","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Constants"},{"name":"RemoteExtensionEnvironmentChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel"},{"name":"RemoteExtensionHostClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/remoteExtensionHostClient"},{"name":"RemoteExtensionLogFileName","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/remoteAgentService"},{"name":"RemoteExtensionManagementChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc"},{"name":"RemoteExtensionsInstaller","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller"},{"name":"RemoteFileDialogContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/contextkeys"},{"name":"RemoteFileSystemProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/remoteAgentFileSystemChannel"},{"name":"RemoteFileSystemProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"RemoteInstallAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"RemoteNameContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/contextkeys"},{"name":"RemoteSearchService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/searchService"},{"name":"RemoteUserConfiguration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/browser/configuration"},{"name":"RemoteViewPaneContainer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/browser/remote"},{"name":"RemoteWindowActiveIndicator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/browser/remoteIndicator"},{"name":"REMOVE_ROOT_FOLDER_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"REMOVE_ROOT_FOLDER_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"removeAccents","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/normalization"},{"name":"RemoveAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"RemoveActionId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"RemoveAllBreakpointsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"removeAllListeners","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"removeAllListeners","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"RemoveAllWatchExpressionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"removeAnsiEscapeCodes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"RemoveBreakpointAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"removeClass","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"removeClasses","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"removeCSSRulesContainingSelector","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"removeElementsAfterNulls","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/resolvedKeybindingItem"},{"name":"removeEventListener","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"RemoveFromRecentlyOpenedAPICommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/apiCommands"},{"name":"removeFromValueTree","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"removeListener","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"removeListener","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"removeMarkdownEscapes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/htmlContent"},{"name":"removeNode","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"removeProperty","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/jsonEdit"},{"name":"removeTabIndexAndUpdateFocus","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"removeTrailingPathSeparator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"rename","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"rename","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"rename","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/rename/rename"},{"name":"RenameAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/rename/rename"},{"name":"renameHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"renameIgnoreError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"RenameInputField","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/rename/renameInputField"},{"name":"RenameProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"renameSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"renderCodicons","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/codicons"},{"name":"RenderedLinesCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewLayer"},{"name":"Renderer","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/marked/marked"},{"name":"Renderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsList"},{"name":"rendererLogChannelId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/common/logConstants"},{"name":"renderExpressionValue","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/baseDebugView"},{"name":"renderFormattedText","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/formattedTextRenderer"},{"name":"RenderIndentGuides","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/abstractTree"},{"name":"RenderingContext","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/renderingContext"},{"name":"RenderLineInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"RenderLineNumbersType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"RenderLineNumbersType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"RenderLineOutput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"RenderLineOutput2","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"renderMarkdown","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/markdownRenderer"},{"name":"renderMarkdownDocument","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markdown/common/markdownDocumentRenderer"},{"name":"RenderMinimap","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"RenderMinimap","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"renderText","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/formattedTextRenderer"},{"name":"renderVariable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/baseDebugView"},{"name":"renderViewLine","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"renderViewLine2","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"renderViewTree","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/baseDebugView"},{"name":"RenderWhitespace","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"ReopenClosedEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"ReopenResourcesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"repeat","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"Repl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/repl"},{"name":"REPL_MODE_SLOPPY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"repl"},{"name":"REPL_MODE_STRICT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"repl"},{"name":"REPL_VIEW_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"ReplAccessibilityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/replViewer"},{"name":"ReplaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"ReplaceActionId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"ReplaceActiveKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"ReplaceAllAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"ReplaceAllCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/replaceAllCommand"},{"name":"ReplaceAllInFileActionId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"ReplaceAllInFolderAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"ReplaceAllInFolderActionId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"ReplaceCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/commands/replaceCommand"},{"name":"ReplaceCommandThatPreservesSelection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/commands/replaceCommand"},{"name":"ReplaceCommandThatSelectsText","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/commands/replaceCommand"},{"name":"ReplaceCommandWithOffsetCursorState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/commands/replaceCommand"},{"name":"ReplaceCommandWithoutChangingPosition","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/commands/replaceCommand"},{"name":"ReplaceInFilesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"ReplaceInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/findinput/replaceInput"},{"name":"ReplaceInputBoxFocusedKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"ReplacePattern","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/replacePattern"},{"name":"ReplacePattern","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/replace"},{"name":"ReplacePiece","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/replacePattern"},{"name":"ReplacePreviewContentProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/replaceService"},{"name":"ReplaceService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/replaceService"},{"name":"ReplDataSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/replViewer"},{"name":"ReplDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/replViewer"},{"name":"ReplEvaluationInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/replModel"},{"name":"ReplEvaluationInputsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/replViewer"},{"name":"ReplEvaluationResult","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/replModel"},{"name":"ReplEvaluationResultsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/replViewer"},{"name":"ReplGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/replModel"},{"name":"ReplGroupRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/replViewer"},{"name":"ReplModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/replModel"},{"name":"ReplRawObjectsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/replViewer"},{"name":"REPLServer","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"repl"},{"name":"ReplSimpleElementsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/replViewer"},{"name":"ReplVariablesRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/replViewer"},{"name":"report","kind":"property","kindModifiers":"declare,optional","sortText":"5","hasAction":true,"source":"process"},{"name":"reporters","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"mocha"},{"name":"ReportExtensionIssueAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"ReportPerformanceIssueUsingReporterAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/issue/electron-browser/issueActions"},{"name":"RepositoryPane","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/repositoryPane"},{"name":"RepositoryViewDescriptor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/repositoryPane"},{"name":"request","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"request","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"https"},{"name":"request","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/request/browser/request"},{"name":"Request","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"requestAnimationFrame","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"RequestChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/request/common/requestIpc"},{"name":"RequestChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/request/common/requestIpc"},{"name":"RequestData","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/RequestData"},{"name":"RequestData","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"RequestInitiator","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/rpcProtocol"},{"name":"RequestMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/request/electron-main/requestMainService"},{"name":"RequestService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/request/browser/requestService"},{"name":"RequestService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/request/node/requestService"},{"name":"RequestType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc"},{"name":"require","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RequireInterceptor","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostRequireInterceptor"},{"name":"RerunSearchEditorSearchAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"RerunSearchEditorSearchCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"ResetFocusedViewLocationAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"ResetGroupSizesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"resetSentinel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"ResetViewLocationsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"resizeBy","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"resizeTo","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"resolve","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolve","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"resolve","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"url"},{"name":"resolve","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"resolve4","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolve6","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolveAny","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolveCname","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolveColorValue","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"resolveCommonProperties","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/node/commonProperties"},{"name":"ResolvedAuthority","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ResolvedAuthority","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ResolvedKeybinding","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keyCodes"},{"name":"ResolvedKeybindingItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/resolvedKeybindingItem"},{"name":"ResolvedKeybindingPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keyCodes"},{"name":"resolveExtensionsSettings","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"resolveMarketplaceHeaders","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionGalleryService"},{"name":"resolveMx","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolveNaptr","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolveNs","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolvePath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"resolvePatternsForProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"resolvePtr","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"Resolver","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolveSettingsTree","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"resolveSoa","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolveSrv","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolveTerminalEncoding","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/terminalEncoding"},{"name":"resolveTxt","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolveWorkbenchCommonProperties","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/browser/workbenchCommonProperties"},{"name":"resolveWorkbenchCommonProperties","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/node/workbenchCommonProperties"},{"name":"ResourceContextKey","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/resources"},{"name":"ResourceDragAndDrop","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersTreeViewer"},{"name":"ResourceEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor/resourceEditorInput"},{"name":"ResourceEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor/resourceEditorModel"},{"name":"ResourceGlobMatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"ResourceGlobMatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/resources"},{"name":"ResourceLabel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/labels"},{"name":"ResourceLabels","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/labels"},{"name":"resourceLanguageSettingsSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"ResourceMap","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"ResourceMarkers","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersModel"},{"name":"ResourceMarkersRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersTreeViewer"},{"name":"ResourceQueue","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"ResourcesDropHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"ResourceSelectedForCompareContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"resourceSettings","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"resourcesPath","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"ResourceTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resourceTree"},{"name":"resourceUsage","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"ResourceWithCommentsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsTreeViewer"},{"name":"ResourceWithCommentThreads","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/common/commentModel"},{"name":"Response","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ResponseType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc"},{"name":"ResponsiveState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/rpcProtocol"},{"name":"RESTART_FRAME_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"RESTART_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"RESTART_SESSION_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"restore","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"restoreFontInfo","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/config/configuration"},{"name":"restoreParentsScrollTop","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"restoreRecentlyOpened","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"restoreWindowsState","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/electron-main/windowsStateStorage"},{"name":"RestrictedRenderingContext","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/renderingContext"},{"name":"resultIsMatch","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"retry","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"return","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"REVEAL_IN_EXPLORER_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"RevealInSideBarForSearchResults","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"RevealKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"RevealLine_","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/coreCommands"},{"name":"RevealProblemKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"revealResourcesInOS","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/electron-browser/fileCommands"},{"name":"RevealTarget","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"reverse","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"REVERSE_CONTINUE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"REVERT_FILE_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"RevertAndCloseEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"revertLocalChangesCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler"},{"name":"ReviewViewZone","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsEditorContribution"},{"name":"ReviewZoneWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentThreadWidget"},{"name":"revive","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/marshalling"},{"name":"reviveQuery","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostSearch"},{"name":"reviveWebviewExtensionDescription","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory"},{"name":"reviveWorkspaceEditDto","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"reviveWorkspaceIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"rewriteAbsolutePaths","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"v8-inspect-profiler"},{"name":"rewriteWorkspaceFileForNewLocation","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"RGBA","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/color"},{"name":"RGBA8","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/rgba"},{"name":"rgErrorMsgForDisplay","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/fileSearch"},{"name":"rgErrorMsgForDisplay","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepTextSearchEngine"},{"name":"rgPath","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-ripgrep/lib/index"},{"name":"RichEditBracket","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/richEditBrackets"},{"name":"RichEditBrackets","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/richEditBrackets"},{"name":"RichEditSupport","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/languageConfigurationRegistry"},{"name":"rightRotate","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"righttest","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"rimraf","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"RimRafMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"rimrafSync","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"RipgrepParser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepTextSearchEngine"},{"name":"RipgrepSearchProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepSearchProvider"},{"name":"RipgrepTextSearchEngine","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepTextSearchEngine"},{"name":"rmdir","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"rmdirSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"rootCertificates","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"rot","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/numbers"},{"name":"RotatingLogger","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/spdlog/index"},{"name":"RowCache","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/rowCache"},{"name":"RPCProtocol","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/rpcProtocol"},{"name":"RSA_NO_PADDING","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"RSA_PKCS1_OAEP_PADDING","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"RSA_PKCS1_PADDING","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"RSA_PKCS1_PSS_PADDING","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"RSA_SSLV23_PADDING","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"RSA_X931_PADDING","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"rsort","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"RTCCertificate","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCDataChannel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCDataChannelEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCDtlsTransport","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCDtlsTransportStateChangedEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCDtmfSender","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCDTMFSender","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCDTMFToneChangeEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCErrorEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCIceCandidate","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCIceCandidatePairChangedEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCIceGatherer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCIceGathererEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCIceTransport","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCIceTransportStateChangedEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCIdentityAssertion","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCPeerConnection","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCPeerConnectionIceErrorEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCPeerConnectionIceEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCRtpReceiver","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCRtpSender","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCRtpTransceiver","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCSctpTransport","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCSessionDescription","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCSrtpSdesTransport","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCSsrcConflictEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCStatsEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCStatsProvider","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCStatsReport","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCTrackEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"rtrim","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"Rulers","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/rulers/rulers"},{"name":"run","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"RunAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"runAtThisOrScheduleAtNextAnimationFrame","kind":"let","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"RunAutomaticTasks","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks"},{"name":"runInContext","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"vm"},{"name":"runInExternalTerminal","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/node/terminals"},{"name":"runInNewContext","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"vm"},{"name":"runInThisContext","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"vm"},{"name":"runMain","kind":"method","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"module"},{"name":"RunOnceScheduler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"RunOnceWorker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"RunOnOptions","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"RunOnOptions","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskConfiguration"},{"name":"RunOptions","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"RunOptions","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskConfiguration"},{"name":"RuntimeExtensionsEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"RuntimeExtensionsInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput"},{"name":"RuntimeType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"RunToCursorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugEditorActions"},{"name":"runWhenIdle","kind":"let","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"S_IFBLK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IFCHR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IFDIR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IFIFO","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IFLNK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IFMT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IFREG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IFSOCK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IRGRP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IROTH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IRUSR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IRWXG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IRWXO","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IRWXU","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IWGRP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IWOTH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IWUSR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IXGRP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IXOTH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IXUSR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"safeBtoa","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"safeStringify","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/objects"},{"name":"sandbox","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"sandboxed","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"sanitize","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"sanitizeFilePath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"sanitizeGridNodeDescriptor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/grid"},{"name":"sanitizeProcessEnvironment","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/processes"},{"name":"sanitizeRanges","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/syntaxRangeProvider"},{"name":"Sash","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/sash/sash"},{"name":"SashState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/sash/sash"},{"name":"satisfies","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"SAVE_ALL_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SAVE_ALL_IN_GROUP_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SAVE_ALL_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SAVE_FILE_AS_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SAVE_FILE_AS_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SAVE_FILE_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SAVE_FILE_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SAVE_FILE_WITHOUT_FORMATTING_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SAVE_FILE_WITHOUT_FORMATTING_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SAVE_FILES_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SaveAllAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"SaveAllInGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"SaveExtensionHostProfileAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"SaveLocalFileCommand","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/browser/simpleFileDialog"},{"name":"saveParentsScrollTop","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"SaveParticipant","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadSaveParticipant"},{"name":"SaveParticipantsContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/saveParticipants"},{"name":"SaveReason","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"SaveWorkspaceAsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceActions"},{"name":"ScanCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/scanCode"},{"name":"ScanCodeBinding","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/scanCode"},{"name":"ScanCodeUtils","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/scanCode"},{"name":"ScanError","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"Scanner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"scheduleAtNextAnimationFrame","kind":"let","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"schema","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsRegistry"},{"name":"schema","kind":"alias","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/jsonSchemaCommon"},{"name":"schema","kind":"alias","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/jsonSchema_v1"},{"name":"schema","kind":"alias","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/jsonSchema_v2"},{"name":"Schemas","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/network"},{"name":"Schemas","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"scm","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SCMAccessibilityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/repositoryPane"},{"name":"SCMMenus","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/menus"},{"name":"SCMService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/common/scmService"},{"name":"SCMStatusController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/activity"},{"name":"SCMTreeKeyboardNavigationLabelProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/repositoryPane"},{"name":"SCMTreeSorter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/repositoryPane"},{"name":"SCMViewPaneContainer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/scmViewlet"},{"name":"Scope","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"ScopedCredential","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ScopedCredentialInfo","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ScopedLineTokens","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports"},{"name":"score","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/languageSelector"},{"name":"scoreFuzzy","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/fuzzyScorer"},{"name":"scoreFuzzy2","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/fuzzyScorer"},{"name":"scoreItemFuzzy","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/fuzzyScorer"},{"name":"screen","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"screen","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"Screen","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"screenLeft","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ScreenOrientation","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"screenTop","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"screenX","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"screenY","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Script","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"vm"},{"name":"ScriptProcessorNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"scroll","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"Scrollable","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/scrollable"},{"name":"ScrollableElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/scrollableElement"},{"name":"ScrollbarArrow","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/scrollbarArrow"},{"name":"scrollbars","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"scrollbarShadow","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"scrollbarSliderActiveBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"scrollbarSliderBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"scrollbarSliderHoverBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"ScrollbarState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/scrollbarState"},{"name":"ScrollbarVisibility","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/scrollable"},{"name":"ScrollbarVisibility","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"ScrollbarVisibilityController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController"},{"name":"scrollBy","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"ScrollDecorationViewPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration"},{"name":"ScrollPosition","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/addons/commandTrackerAddon"},{"name":"ScrollState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/scrollable"},{"name":"scrollTo","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"ScrollType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/editorCommon"},{"name":"ScrollType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"scrollX","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"scrollY","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"scrypt","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"scryptSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"sdkVersion","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Context"},{"name":"SEARCH_EXCLUDE_CONFIG","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"SearchAccessibilityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchResultsView"},{"name":"SearchAddon","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/xterm-addon-search/typings/xterm-addon-search"},{"name":"SearchChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/searchIpc"},{"name":"SearchChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/searchIpc"},{"name":"SearchCompletionExitCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"SearchData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelSearch"},{"name":"SearchDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchResultsView"},{"name":"SearchDND","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchResultsView"},{"name":"SearchEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditor"},{"name":"SearchEditorBodyScheme","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"searchEditorFindMatch","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"searchEditorFindMatchBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"SearchEditorFindMatchClass","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"SearchEditorID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"SearchEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput"},{"name":"SearchEditorScheme","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"searchEditorTextInputBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditor"},{"name":"Searcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelSearch"},{"name":"SearchError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"SearchErrorCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"SearchHistoryService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchHistoryService"},{"name":"SearchInputBoxFocusedKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"searchMatchComparer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchModel"},{"name":"SearchModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchModel"},{"name":"SearchParams","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelSearch"},{"name":"SearchProviderType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"SearchRange","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"SearchResult","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchModel"},{"name":"SearchResultIdx","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"SearchResultModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"SearchService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/searchService"},{"name":"SearchService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/rawSearchService"},{"name":"SearchSortOrder","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"SearchView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchView"},{"name":"SearchViewFocusedKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"SearchViewPosition","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchView"},{"name":"SearchViewVisibleKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"SearchWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesWidgets"},{"name":"SearchWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchWidget"},{"name":"SearchWorkbenchService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchModel"},{"name":"SecurityPolicyViolationEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SELECT_FOR_COMPARE_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SelectActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/actionbar/actionbar"},{"name":"selectAllSearchEditorMatchesCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"SelectAllSearchEditorMatchesCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"SelectAllTerminalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"SelectAllWebviewEditorCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewCommands"},{"name":"SelectAndStartAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"selectBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"selectBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"SelectBox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/selectBox/selectBox"},{"name":"SelectBoxList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/selectBox/selectBoxCustom"},{"name":"SelectBoxNative","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/selectBox/selectBoxNative"},{"name":"SelectColorThemeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/themes/browser/themes.contribution"},{"name":"SelectDefaultShellWindowsTerminalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"selectForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"SelectHighlightsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"Selection","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Selection","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Selection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/selection"},{"name":"Selection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"Selection","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"Selection","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"selectionBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"SelectionBasedVariableResolver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetVariables"},{"name":"SelectionClipboard","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard"},{"name":"SelectionClipboardContributionID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/selectionClipboard"},{"name":"SelectionDirection","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/selection"},{"name":"SelectionDirection","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"SelectionDirection","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"SelectionHighlighter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"SelectionMatchFindAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"SelectionRange","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SelectionRange","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SelectionRange","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"SelectionRangeRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"SelectionsOverlay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/selections/selections"},{"name":"selectListBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"selectorPattern","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"self","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SEMANTIC_HIGHLIGHTING_SETTING_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/modelServiceImpl"},{"name":"SemanticTokens","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SemanticTokens","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SemanticTokensBuilder","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SemanticTokensBuilder","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SemanticTokensEdit","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SemanticTokensEdit","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SemanticTokensEdits","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SemanticTokensEdits","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SemanticTokensHelp","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/semanticTokensHelp"},{"name":"SemanticTokensLegend","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SemanticTokensLegend","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SemanticTokensProviderStyling","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/semanticTokensProviderStyling"},{"name":"SemanticTokensProviderStylingConstants","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/semanticTokensProviderStyling"},{"name":"SemVer","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"SEMVER_SPEC_VERSION","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"send","kind":"method","kindModifiers":"declare,optional","sortText":"5","hasAction":true,"source":"process"},{"name":"Sender","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"SENTINEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"SENTINEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"sep","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"sep","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"Separator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/actionbar/actionbar"},{"name":"sequence","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"Sequence","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/sequence"},{"name":"Sequencer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"SerializableFileMatch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"SerializableGrid","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/grid"},{"name":"serialize","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"serializeEnvironmentVariableCollection","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/environmentVariableShared"},{"name":"serializeFontInfo","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/config/configuration"},{"name":"serializePipePositions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/test/wordTestUtils"},{"name":"Serializer","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"serializeSearchConfiguration","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization"},{"name":"serializeSearchError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"serializeSearchResultForEditor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization"},{"name":"serve","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc.net"},{"name":"serve","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/electron-main/driver"},{"name":"Server","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"Server","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"https"},{"name":"Server","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"net"},{"name":"Server","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"Server","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/electron-main/ipc.electron-main"},{"name":"Server","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc.cp"},{"name":"Server","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc.net"},{"name":"ServerExtensionsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews"},{"name":"ServerResponse","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"SERVFAIL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"SERVICE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"ServiceCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/serviceCollection"},{"name":"ServiceUIFrameContext","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ServiceWorker","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ServiceWorkerContainer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ServiceWorkerMessageEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ServiceWorkerRegistration","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"session","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"Session","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"inspector"},{"name":"Session","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"sessionStorage","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Set","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SET_CONTEXT_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"setARIAContainer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/aria/aria"},{"name":"setAsyncMode","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/spdlog/index"},{"name":"setCollapseStateAtLevel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingModel"},{"name":"setCollapseStateForMatchingLines","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingModel"},{"name":"setCollapseStateForType","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingModel"},{"name":"setCollapseStateLevelsDown","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingModel"},{"name":"setCollapseStateLevelsUp","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingModel"},{"name":"setCollapseStateUp","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingModel"},{"name":"SetColorThemeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"SetEditorLayoutAPICommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/apiCommands"},{"name":"setegid","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"seteuid","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"setFdLimit","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"SetFileIconThemeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"setFlagsFromString","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"setFullscreen","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"setgid","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"setGlobalLeakWarningThreshold","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/event"},{"name":"setgroups","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"setImmediate","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"setImmediate","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"timers"},{"name":"setImmediate","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"setInterval","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"setInterval","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"timers"},{"name":"setLanguageConfiguration","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"SetLogLevelAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/common/logsActions"},{"name":"SetMap","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/collections"},{"name":"setMaxListeners","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"setMaxListeners","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"setModelLanguage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"setModelMarkers","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"setMonarchTokensProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"setNodeStickiness","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"setOptions","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/marked/marked"},{"name":"SetPanelPositionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"setPassword","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/keytar/keytar"},{"name":"setPassword","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/keytar/index"},{"name":"setPriority","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"SetProductIconThemeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"setProperty","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/jsonEdit"},{"name":"setServers","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"setSnippetSuggestSupport","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggest"},{"name":"setTheme","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"setTimeout","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"setTimeout","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"timers"},{"name":"SettingArrayRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"SettingBoolRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"SettingComplexRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"SettingEnumRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"SettingExcludeRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"SettingGroupRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"settingKeyToDisplayFormat","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"SettingMatches","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesSearch"},{"name":"SettingNewExtensionsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"SettingNumberRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"settings","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"SETTINGS_COMMAND_OPEN_SETTINGS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_FILTER_ONLINE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_FOCUS_FILE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_FOCUS_TOC","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_SEARCH","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"Settings2EditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"SettingsChangeRelauncher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution"},{"name":"settingsCheckboxBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"settingsCheckboxBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"settingsCheckboxForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"SettingsEditor2","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsEditor2"},{"name":"SettingsEditor2Input","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesEditorInput"},{"name":"SettingsEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"SettingsEditorOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferences"},{"name":"SettingsGroupTitleRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesRenderers"},{"name":"SettingsGroupTitleWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesWidgets"},{"name":"settingsHeaderForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"SettingsHeaderWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesWidgets"},{"name":"settingsNumberInputBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"settingsNumberInputBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"settingsNumberInputForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"settingsSelectBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"settingsSelectBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"settingsSelectForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"settingsSelectListBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"SettingsSynchroniser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/settingsSync"},{"name":"SettingsTargetsWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesWidgets"},{"name":"settingsTextInputBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"settingsTextInputBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"settingsTextInputForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"SettingsTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"SettingsTreeElement","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"SettingsTreeFilter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"SettingsTreeGroupElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"SettingsTreeModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"SettingsTreeNewExtensionsElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"SettingsTreeSettingElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"SettingTextRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"SettingTreeRenderers","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"SettingValueType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferences"},{"name":"setTokensProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"setToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"setuid","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"setUncaughtExceptionCaptureCallback","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"setUnexpectedErrorHandler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"setup","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"setup","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"setup","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/applicationinsights"},{"name":"setupMaster","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"setupTerminalCommands","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalCommands"},{"name":"setupTerminalMenu","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalMenu"},{"name":"setWordDefinitionFor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDocumentData"},{"name":"setZoomFactor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"setZoomLevel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"Severity","kind":"alias","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/severity"},{"name":"Severity","kind":"alias","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/notification/common/notification"},{"name":"SeverityIcon","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/severityIcon/common/severityIcon"},{"name":"SeverityLevel","kind":"enum","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/SeverityLevel"},{"name":"SeverityLevel","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"shadowCaretRangeFromPoint","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/mouseTarget"},{"name":"ShadowRoot","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SharedArrayBuffer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"sharedLogChannelId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/common/logConstants"},{"name":"SharedProcess","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-main/sharedProcess"},{"name":"SharedProcessMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/ipc/electron-main/sharedProcessMainService"},{"name":"SharedProcessService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/sharedProcess/electron-browser/sharedProcessService"},{"name":"SharedWorker","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"shell","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"SHELL_CWD_INVALID_EXIT_CODE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"SHELL_PATH_DIRECTORY_EXIT_CODE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"SHELL_PATH_INVALID_EXIT_CODE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"ShellExecution","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ShellExecution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ShellExecutionDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"ShellExecutionOptionsDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"ShellQuoting","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ShellQuoting","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"ShellQuoting","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ShellQuoting","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskConfiguration"},{"name":"shift","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/rangeMap"},{"name":"ShiftCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/commands/shiftCommand"},{"name":"shorten","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"shouldSetLangEnvVariable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment"},{"name":"shouldSynchronizeModel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/modelService"},{"name":"show","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"SHOW_EDITORS_IN_GROUP","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"SHOW_NOTIFICATIONS_CENTER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"ShowActiveFileInExplorer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"ShowAllCommandsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess"},{"name":"ShowAllEditorsByAppearanceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"ShowAllEditorsByMostRecentlyUsedAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"ShowAzureExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ShowBuiltInExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ShowCandidateContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/common/showCandidate"},{"name":"ShowCurrentReleaseNotesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/browser/update"},{"name":"ShowCurrentReleaseNotesActionId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/common/update"},{"name":"ShowDisabledExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ShowEditorsInActiveGroupByMostRecentlyUsedAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"ShowEnabledExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"showExtensionQuery","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/format/browser/showExtensionQuery"},{"name":"ShowInstalledExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ShowLanguageExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorStatus"},{"name":"ShowLanguageExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ShowNextChangeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"ShowNextWindowTabHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"ShowOpenedFileInNewWindow","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"ShowOutdatedExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ShowPopularExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ShowPreviousChangeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"ShowPreviousWindowTabHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"ShowProblemsPanelAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersViewActions"},{"name":"ShowRecommendationsOnlyOnDemandKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"ShowRecommendedExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ShowRecommendedKeymapExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ShowReleaseNotesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/browser/update"},{"name":"ShowRuntimeExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"showSimpleSuggestions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggest"},{"name":"ShowViewletAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/viewlet"},{"name":"ShowWebViewEditorFindWidgetAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewCommands"},{"name":"shuffle","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"ShutdownReason","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/common/lifecycle"},{"name":"SIDE_BAR_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"SIDE_BAR_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"SIDE_BAR_DRAG_AND_DROP_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"SIDE_BAR_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"SIDE_BAR_SECTION_HEADER_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"SIDE_BAR_SECTION_HEADER_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"SIDE_BAR_SECTION_HEADER_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"SIDE_BAR_TITLE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"SIDE_GROUP","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorService"},{"name":"SidebarFocusContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/viewlet"},{"name":"SidebarPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/sidebar/sidebarPart"},{"name":"SideBarVisibleContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/viewlet"},{"name":"SideBySideEditor","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"SideBySideEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/sideBySideEditor"},{"name":"SideBySideEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"SIGABRT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGALRM","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGBREAK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGBUS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGCHLD","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGCONT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGFPE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGHUP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGILL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGINT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGIO","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGIOT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGKILL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"sign","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"SIGN_SERVICE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/sign/common/sign"},{"name":"SignatureHelp","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SignatureHelp","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SignatureHelp","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"SignatureHelpProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"SignatureHelpTriggerKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SignatureHelpTriggerKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"SignatureHelpTriggerKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SignatureHelpTriggerKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"SignatureInformation","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SignatureInformation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SignatureInformation","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"Signer","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"SignService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/sign/browser/signService"},{"name":"SignService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/sign/node/signService"},{"name":"SIGPIPE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGPOLL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGPROF","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGPWR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGQUIT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGSEGV","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGSTKFLT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGSTOP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGSYS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGTERM","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGTRAP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGTSTP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGTTIN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGTTOU","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGUNUSED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGURG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGUSR1","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGUSR2","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGVTALRM","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGWINCH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGXCPU","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGXFSZ","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SimpleBreadcrumbsItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget"},{"name":"SimpleBulkEditService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleButton","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findWidget"},{"name":"SimpleCheckbox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/checkbox/checkbox"},{"name":"simpleCheckboxBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"simpleCheckboxBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"simpleCheckboxForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"SimpleCommentEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/simpleCommentEditor"},{"name":"SimpleConfigurationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleDialogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleEditorModelResolverService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleEditorProgressService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleFileDialog","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/browser/simpleFileDialog"},{"name":"SimpleFindReplaceWidget","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget"},{"name":"SimpleFindWidget","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget"},{"name":"SimpleKeybinding","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keyCodes"},{"name":"SimpleLayoutService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleNotificationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleReplElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/replModel"},{"name":"SimpleResourceConfigurationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleResourcePropertiesService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleServicesNLS","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standaloneStrings"},{"name":"SimpleUriLabelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleWorkerClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/worker/simpleWorker"},{"name":"SimpleWorkerServer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/worker/simpleWorker"},{"name":"SimpleWorkspaceContextService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SingleCursorState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"SingleEditorGroupsContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"singleLetterHash","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"SingleLineInputHeight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchWidget"},{"name":"SingleModelEditStackElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/editStack"},{"name":"singlePagePager","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/paging"},{"name":"SingleProxyRPCProtocol","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/api/testRPCProtocol"},{"name":"sinon","kind":"alias","kindModifiers":"declare","sortText":"4"},{"name":"Sinon","kind":"var","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"size","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"size","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/collections"},{"name":"Sizing","kind":"type","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/splitview/splitview"},{"name":"Sizing","kind":"type","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/grid"},{"name":"SlicedLineTokens","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/lineTokens"},{"name":"SlowBuffer","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"buffer"},{"name":"SlowExtensionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions"},{"name":"SmartSnippetInserter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/smartSnippetInserter"},{"name":"SmoothScrollableElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/scrollableElement"},{"name":"SmoothScrollingOperation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/scrollable"},{"name":"SmoothScrollingUpdate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/scrollable"},{"name":"snapshotToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textfiles"},{"name":"SnapUpdateService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/electron-main/updateService.snap"},{"name":"Snippet","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/snippets/browser/snippetsFile"},{"name":"SnippetCompletion","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider"},{"name":"SnippetCompletionProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider"},{"name":"SnippetController2","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetController2"},{"name":"SnippetFile","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/snippets/browser/snippetsFile"},{"name":"snippetFinalTabstopHighlightBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"snippetFinalTabstopHighlightBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"SnippetParser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"SnippetSession","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetSession"},{"name":"SnippetSortOrder","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggest"},{"name":"SnippetSource","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/snippets/browser/snippetsFile"},{"name":"SnippetsSynchroniser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/snippetsSync"},{"name":"SnippetString","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SnippetString","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"snippetTabstopHighlightBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"snippetTabstopHighlightBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"Socket","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dgram"},{"name":"Socket","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"net"},{"name":"SocketDebugAdapter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/node/debugAdapter"},{"name":"sort","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"SortBy","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"sortedDiff","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"Sorter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree"},{"name":"SortLinesAscendingAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"SortLinesCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/sortLinesCommand"},{"name":"SortLinesDescendingAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"sortMimeTypes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"SortOrder","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"SortOrder","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"Source","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/processes"},{"name":"Source","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/processes"},{"name":"Source","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugSource"},{"name":"SourceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionCommands"},{"name":"sourceActionCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeAction"},{"name":"SourceBreakpoint","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SourceBreakpoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SourceBuffer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SourceBufferList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SourceControlInputBoxValidationType","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SourceControlInputBoxValidationType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SparseEncodedTokens","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/tokensStore"},{"name":"spawn","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"child_process"},{"name":"spawn","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"node-pty"},{"name":"spawnRipgrepCmd","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepFileSearch"},{"name":"spawnSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"child_process"},{"name":"SpdLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/node/spdlogService"},{"name":"specify","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechGrammar","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechGrammarList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechRecognition","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechRecognitionAlternative","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechRecognitionEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechRecognitionResult","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechRecognitionResultList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"speechSynthesis","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechSynthesis","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechSynthesisErrorEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechSynthesisEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechSynthesisUtterance","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechSynthesisVoice","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SPLIT_EDITOR_DOWN","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"SPLIT_EDITOR_LEFT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"SPLIT_EDITOR_RIGHT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"SPLIT_EDITOR_UP","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"splitEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"SplitEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"SplitEditorDownAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"SplitEditorLeftAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"SplitEditorOrthogonalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"SplitEditorRightAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"SplitEditorsVertically","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"SplitEditorUpAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"splitGlobAware","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/glob"},{"name":"SplitInActiveWorkspaceTerminalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"SplitLine","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/splitLinesCollection"},{"name":"SplitLinesCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/splitLinesCollection"},{"name":"splitName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"SplitTerminalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"SplitView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/splitview/splitview"},{"name":"spreadGlobComponents","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepTextSearchEngine"},{"name":"spy","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"SQLiteStorageDatabase","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/storage/node/storage"},{"name":"SSL_OP_ALL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_CIPHER_SERVER_PREFERENCE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_CISCO_ANYCONNECT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_COOKIE_EXCHANGE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_CRYPTOPRO_TLSEXT_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_EPHEMERAL_RSA","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_LEGACY_SERVER_CONNECT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_MICROSOFT_SESS_ID_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_MSIE_SSLV2_RSA_PADDING","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NETSCAPE_CA_DN_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NETSCAPE_CHALLENGE_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NO_COMPRESSION","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NO_QUERY_MTU","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NO_SSLv2","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NO_SSLv3","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NO_TICKET","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NO_TLSv1","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NO_TLSv1_1","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NO_TLSv1_2","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_PKCS1_CHECK_1","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_PKCS1_CHECK_2","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_SINGLE_DH_USE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_SINGLE_ECDH_USE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_SSLEAY_080_CLIENT_DH_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_TLS_BLOCK_PADDING_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_TLS_D5_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_TLS_ROLLBACK_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"StableEditorScrollState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/core/editorState"},{"name":"StackFrame","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"StackFrame","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/StackFrame"},{"name":"StackFrame","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"StandaloneCodeEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneCodeEditor"},{"name":"StandaloneCodeEditorNLS","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standaloneStrings"},{"name":"StandaloneCodeEditorServiceImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneCodeServiceImpl"},{"name":"StandaloneCommandService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"StandaloneCommandsQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess"},{"name":"StandaloneConfigurationModelParser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configurationModels"},{"name":"StandaloneDiffEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneCodeEditor"},{"name":"StandaloneEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneCodeEditor"},{"name":"StandaloneGotoLineQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/quickAccess/standaloneGotoLineQuickAccess"},{"name":"StandaloneGotoSymbolQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess"},{"name":"StandaloneKeybindingService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"StandaloneQuickInputServiceImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputServiceImpl"},{"name":"StandaloneReferencesController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/referenceSearch/standaloneReferenceSearch"},{"name":"StandaloneTelemetryService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"StandaloneThemeServiceImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneThemeServiceImpl"},{"name":"StandardAutoClosingPairConditional","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/languageConfiguration"},{"name":"StandardKeyboardEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/keyboardEvent"},{"name":"StandardMouseEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/mouseEvent"},{"name":"standardMouseMoveMerger","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/globalMouseMoveMonitor"},{"name":"StandardTokenType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"StandardTokenType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/common/textMateService"},{"name":"StandardTokenType","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-textmate/release/main"},{"name":"StandardWheelEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/mouseEvent"},{"name":"StandardWindow","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"start","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"repl"},{"name":"start","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/applicationinsights"},{"name":"start","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/native-watchdog/index"},{"name":"StartAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"StartDebugActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActionViewItems"},{"name":"StartDebugQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugQuickAccess"},{"name":"startExtensionHostProcess","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/node/extensionHostProcessSetup"},{"name":"StartExtensionHostProfileAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"StartFindAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"StartFindReplaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"StartFindWithSelectionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"startProfiling","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"v8-inspect-profiler"},{"name":"StartStopProblemCollector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemCollectors"},{"name":"startsWith","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"startsWithIgnoreCase","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"startsWithUTF8BOM","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"startup","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-browser/issue/issueReporterMain"},{"name":"startup","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-browser/processExplorer/processExplorerMain"},{"name":"startup","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-browser/sharedProcess/sharedProcessMain"},{"name":"StartupKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/common/lifecycle"},{"name":"StartupKindToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/common/lifecycle"},{"name":"StartupProfiler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/performance/electron-browser/startupProfiler"},{"name":"StartupTimings","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/performance/electron-browser/startupTimings"},{"name":"stat","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"stat","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"State","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestModel"},{"name":"State","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"State","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/common/update"},{"name":"State","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/linkComputer"},{"name":"stateExists","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"StatefullMarkdownCell","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell"},{"name":"StateMachine","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/linkComputer"},{"name":"Statement","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-sqlite3/index"},{"name":"StateService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/state/node/stateService"},{"name":"StateType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/common/update"},{"name":"StaticDND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dnd"},{"name":"StaticExtensionsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/staticExtensions"},{"name":"StaticLanguageSelector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/mocks/mockMode"},{"name":"StaticRange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"StaticRouter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc"},{"name":"StaticServices","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneServices"},{"name":"StatisticType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"statLink","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"Stats","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"statSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"status","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"status","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/aria/aria"},{"name":"STATUS_BAR_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_DEBUGGING_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/statusbarColorProvider"},{"name":"STATUS_BAR_DEBUGGING_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/statusbarColorProvider"},{"name":"STATUS_BAR_DEBUGGING_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/statusbarColorProvider"},{"name":"STATUS_BAR_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_HOST_NAME_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_HOST_NAME_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_ITEM_ACTIVE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_ITEM_HOVER_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_NO_FOLDER_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_NO_FOLDER_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_NO_FOLDER_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_PROMINENT_ITEM_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_PROMINENT_ITEM_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_CODES","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"statusbar","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"StatusbarAlignment","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/statusbar/common/statusbar"},{"name":"StatusBarAlignment","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"StatusBarAlignment","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"StatusBarColorProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/statusbarColorProvider"},{"name":"StatusbarPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/statusbar/statusbarPart"},{"name":"StatusLabelAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"StatusMessageChangeType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/notifications"},{"name":"StatusUpdater","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewlet"},{"name":"stderr","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"stdin","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"stdinDataListener","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/stdin"},{"name":"stdout","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"STEP_BACK_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"STEP_INTO_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"STEP_INTO_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"STEP_OUT_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"STEP_OUT_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"STEP_OVER_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"STEP_OVER_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"StereoPannerNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"stop","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"stop","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/event"},{"name":"STOP_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"STOP_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"StopExtensionHostProfileAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"StopWatch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/stopwatch"},{"name":"Storage","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Storage","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/storage/common/storage"},{"name":"StorageDataCleaner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner"},{"name":"StorageEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"StorageHint","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/storage/common/storage"},{"name":"StorageKeysSyncRegistryChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncIpc"},{"name":"StorageKeysSyncRegistryChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncIpc"},{"name":"StorageKeysSyncRegistryService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/storageKeys"},{"name":"StorageMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/node/storageMainService"},{"name":"StorageManager","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"StorageManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionEnablementService"},{"name":"StorageScope","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/common/storage"},{"name":"strcmp","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/tokenization"},{"name":"Stream","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"stream"},{"name":"StreamDebugAdapter","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/node/debugAdapter"},{"name":"streamToBuffer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"streamToBufferReadableStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"streamToNodeReadable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/stream"},{"name":"strict","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"strictEqual","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"string","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"String","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"StringBuffer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase"},{"name":"StringDecoder","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"string_decoder"},{"name":"stringDiff","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/diff/diff"},{"name":"StringDiffSequence","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/diff/diff"},{"name":"StringEOL","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/tokensStore"},{"name":"stringHash","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/hash"},{"name":"stringify","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"querystring"},{"name":"stringify","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/marshalling"},{"name":"StringIterator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"StringRepresentationProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesTree"},{"name":"StringSHA1","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/hash"},{"name":"stringToSnapshot","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textfiles"},{"name":"stripCodicons","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/codicons"},{"name":"stripComments","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"stripUTF8BOM","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"stripWildcards","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"stub","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"styleMedia","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"StyleMedia","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"StyleSheet","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"StyleSheetList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SubmenuAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/menu/menu"},{"name":"SubmenuItemAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"substituteMatches","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"SubtleCrypto","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SuggestAlternatives","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestAlternatives"},{"name":"SuggestController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestController"},{"name":"SuggestEnabledInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput"},{"name":"suggestFilename","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/mime"},{"name":"SuggestMemoryService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestMemory"},{"name":"SuggestModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestModel"},{"name":"SuggestWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestWidget"},{"name":"suggestWidgetStatusbarMenu","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggest"},{"name":"suite","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"suiteRepeat","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/common/utils"},{"name":"suiteSetup","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"suiteTeardown","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"super","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"SUPPORTED_CODE_ACTIONS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionModel"},{"name":"SUPPORTED_ENCODINGS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textfiles"},{"name":"SurroundSelectionCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/commands/surroundSelectionCommand"},{"name":"SVGAElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAngle","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedAngle","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedBoolean","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedEnumeration","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedInteger","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedLength","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedLengthList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedNumber","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedNumberList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedPreserveAspectRatio","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedRect","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedString","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedTransformList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimateElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimateMotionElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimateTransformElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimationElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGCircleElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGClipPathElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGComponentTransferFunctionElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGCursorElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGDefsElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGDescElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGElementInstance","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGElementInstanceList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGEllipseElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEBlendElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEColorMatrixElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEComponentTransferElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFECompositeElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEConvolveMatrixElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEDiffuseLightingElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEDisplacementMapElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEDistantLightElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEDropShadowElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEFloodElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEFuncAElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEFuncBElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEFuncGElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEFuncRElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEGaussianBlurElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEImageElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEMergeElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEMergeNodeElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEMorphologyElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEOffsetElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEPointLightElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFESpecularLightingElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFESpotLightElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFETileElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFETurbulenceElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFilterElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGForeignObjectElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGGElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGGeometryElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGGradientElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGGraphicsElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGImageElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGLength","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGLengthList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGLinearGradientElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGLineElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGMarkerElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGMaskElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGMatrix","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGMetadataElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGNumber","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGNumberList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSeg","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegArcAbs","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegArcRel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegClosePath","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegCurvetoCubicAbs","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegCurvetoCubicRel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegCurvetoCubicSmoothAbs","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegCurvetoCubicSmoothRel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegCurvetoQuadraticAbs","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegCurvetoQuadraticRel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegCurvetoQuadraticSmoothAbs","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegCurvetoQuadraticSmoothRel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegLinetoAbs","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegLinetoHorizontalAbs","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegLinetoHorizontalRel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegLinetoRel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegLinetoVerticalAbs","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegLinetoVerticalRel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegMovetoAbs","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegMovetoRel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPatternElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPoint","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPointList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPolygonElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPolylineElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPreserveAspectRatio","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGRadialGradientElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGRect","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGRectElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGScriptElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGStopElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGStringList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGStyleElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGSVGElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGSwitchElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGSymbolElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGTextContentElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGTextElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGTextPathElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGTextPositioningElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGTitleElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGTransform","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGTransformList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGTSpanElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGUnitTypes","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGUseElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGViewElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGZoomAndPan","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGZoomEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"switch","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"SwitchPanelViewAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"SwitchRemoteAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/browser/explorerViewItems"},{"name":"SwitchRemoteViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/browser/explorerViewItems"},{"name":"SwitchTerminalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"SwitchTerminalActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"SwitchWindow","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"symbol","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Symbol","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SYMBOL_ICON_ARRAY_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_BOOLEAN_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_CLASS_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_COLOR_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_CONSTANT_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_CONSTRUCTOR_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_ENUMERATOR_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_ENUMERATOR_MEMBER_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_EVENT_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_FIELD_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_FILE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_FOLDER_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_FUNCTION_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_INTERFACE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_KEY_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_KEYWORD_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_METHOD_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_MODULE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_NAMESPACE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_NULL_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_NUMBER_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_OBJECT_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_OPERATOR_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_PACKAGE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_PROPERTY_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_REFERENCE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_SNIPPET_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_STRING_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_STRUCT_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_TEXT_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_TYPEPARAMETER_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_UNIT_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_VARIABLE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SymbolInformation","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SymbolInformation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SymbolKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SymbolKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"SymbolKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SymbolKind","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"SymbolKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"SymbolKinds","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"SymbolsQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/symbolsQuickAccess"},{"name":"SymbolTag","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SymbolTag","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"SymbolTag","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SymbolTag","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"SymbolTag","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"symlink","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"symlink","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"symlinkSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"SyncActionDescriptor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"SyncDescriptor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/descriptors"},{"name":"SyncIgnoredIconAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"SyncManager","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SyncResource","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"SyncStatus","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"SyntaxError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SyntaxKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"SyntaxRangeProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/syntaxRangeProvider"},{"name":"SystemDisabledWarningAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"systemPreferences","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TAB_ACTIVE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_ACTIVE_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_ACTIVE_BORDER_TOP","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_ACTIVE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_ACTIVE_MODIFIED_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_HOVER_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_HOVER_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_HOVER_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_INACTIVE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_INACTIVE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_INACTIVE_MODIFIED_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_ACTIVE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_ACTIVE_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_ACTIVE_BORDER_TOP","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_ACTIVE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_ACTIVE_MODIFIED_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_HOVER_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_HOVER_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_HOVER_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_INACTIVE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_INACTIVE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_INACTIVE_MODIFIED_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TabCompletionController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/snippets/browser/tabCompletion"},{"name":"TabFocus","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/commonEditorConfig"},{"name":"table","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"TabsTitleControl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/tabsTitleControl"},{"name":"TAG","kind":"property","kindModifiers":"private,static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"tail","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"tail2","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"takeHeapSnapshot","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"Task","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Task","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TASK_RUNNING_STATE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"Task2","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TaskConfigSource","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskConfiguration"},{"name":"TaskDefinition","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"TaskDefinitionDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"TaskDefinitionRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry"},{"name":"TaskDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"TaskError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskSystem"},{"name":"TaskErrors","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskSystem"},{"name":"TaskEvent","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"TaskEventKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"TaskExecuteKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskSystem"},{"name":"TaskExecutionDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"TaskFilterDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"TaskGroup","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TaskGroup","kind":"type","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"TaskGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TaskHandleDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"TaskIdentifier","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskConfiguration"},{"name":"TaskPanelKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TaskPanelKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TaskPresentationOptionsDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"TaskQuickPick","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/taskQuickPick"},{"name":"TaskRevealKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TaskRevealKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TaskRunResult","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugTaskRunner"},{"name":"TaskRunSource","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"TaskRunType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"tasks","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TASKS_CONFIGURATION_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"TASKS_DEFAULT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"TaskScope","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TaskScope","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"TaskScope","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TaskSequentializer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"TaskService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/taskService"},{"name":"TaskService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/electron-browser/taskService"},{"name":"TaskSorter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"TaskSourceKind","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"TasksQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/tasksQuickAccess"},{"name":"tasksSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"TaskStatusBarContributions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/task.contribution"},{"name":"teardown","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"TelemetryAppenderChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/node/telemetryIpc"},{"name":"TelemetryAppenderClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/node/telemetryIpc"},{"name":"TelemetryClient","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/TelemetryClient"},{"name":"TelemetryClient","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/applicationinsights"},{"name":"TelemetryContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution"},{"name":"telemetryLogChannelId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/common/logConstants"},{"name":"TelemetryService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetryService"},{"name":"TelemetryService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/telemetry/browser/telemetryService"},{"name":"TelemetryService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/telemetry/electron-browser/telemetryService"},{"name":"TelemetryType","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/TelemetryTypes/TelemetryType"},{"name":"telemetryTypeToBaseType","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/TelemetryTypes/TelemetryType"},{"name":"TEMPDIR_PREFIX","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"template","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"Terminal","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"xterm"},{"name":"TERMINAL_ACTION_CATEGORY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"TERMINAL_BACKGROUND_COLOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry"},{"name":"TERMINAL_BORDER_COLOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry"},{"name":"TERMINAL_COMMAND_ID","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"TERMINAL_CONFIG_SECTION","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"TERMINAL_CURSOR_BACKGROUND_COLOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry"},{"name":"TERMINAL_CURSOR_FOREGROUND_COLOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry"},{"name":"TERMINAL_FOREGROUND_COLOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry"},{"name":"TERMINAL_SELECTION_BACKGROUND_COLOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry"},{"name":"TERMINAL_VIEW_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"TerminalConfigHelper","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper"},{"name":"terminalConfiguration","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalConfiguration"},{"name":"TerminalCursorStyle","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"TerminalDataBufferer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalDataBuffering"},{"name":"TerminalFindWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalFindWidget"},{"name":"TerminalInstance","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalInstance"},{"name":"TerminalInstanceService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalInstanceService"},{"name":"TerminalInstanceService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService"},{"name":"TerminalLinkManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager"},{"name":"TerminalNativeService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService"},{"name":"TerminalPasteAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"TerminalProcess","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/node/terminalProcess"},{"name":"TerminalProcessExtHostProxy","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy"},{"name":"TerminalProcessManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalProcessManager"},{"name":"TerminalQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalsQuickAccess"},{"name":"terminalSendSequenceCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"TerminalService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalService"},{"name":"TerminalTab","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalTab"},{"name":"TerminalTaskSystem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem"},{"name":"TerminalValidatedLocalLinkProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider"},{"name":"TerminalViewPane","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalView"},{"name":"TerminalWebLinkProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/links/terminalWebLinkProvider"},{"name":"TerminalWidgetManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalWidgetManager"},{"name":"TerminalWordLinkProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider"},{"name":"TERMINATE_THREAD_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"TerminateResponseCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/processes"},{"name":"TerminateResponseCode","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/processes"},{"name":"TernarySearchTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"test","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"test","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"TEST_VIEW_CONTAINER_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/views"},{"name":"TestAccessibilityService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"testApplyEditsWithSyncedModels","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/editableTextModelTestUtils"},{"name":"TestBackupFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestBackupMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test"},{"name":"testCase","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"TestCell","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/test/testNotebookEditor"},{"name":"TestChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/test/node/testService"},{"name":"TestCodeEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/browser/testCodeEditor"},{"name":"TestCodeEditorService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/browser/editorTestServices"},{"name":"TestColorTheme","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/test/common/testThemeService"},{"name":"testCommand","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/browser/testCommand"},{"name":"TestCommandService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/browser/editorTestServices"},{"name":"TestConfiguration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/mocks/testConfiguration"},{"name":"TestConfigurationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/test/common/testConfigurationService"},{"name":"TestContextService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/common/workbenchTestServices"},{"name":"TestDecorationProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/test/foldingModel.test"},{"name":"TestDecorationsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestDialogMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test"},{"name":"TestDialogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/dialogs/test/common/testDialogService"},{"name":"TestDiskFileSystemProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/test/electron-browser/diskFileService.test"},{"name":"TestEditorGroupAccessor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestEditorGroupsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestEditorGroupView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestEditorPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestEditorService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestElectronService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/workbenchTestServices"},{"name":"TestEnvironmentService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestEnvironmentService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/workbenchTestServices"},{"name":"TestExperimentService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test"},{"name":"TestExtensionEnablementService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test"},{"name":"TestExtensionService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/common/workbenchTestServices"},{"name":"testFile","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/node/utils"},{"name":"TestFileDialogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestFileEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestFileIconTheme","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/test/common/testThemeService"},{"name":"TestFilesConfigurationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestFindController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/test/findController.test"},{"name":"TestHistoryService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestHostService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestInstantiationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/test/common/instantiationServiceMock"},{"name":"TestLayoutService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestLifecycleService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestListService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestMenuService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestNativePathService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/workbenchTestServices"},{"name":"TestNativeTextFileServiceWithEncodingOverrides","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/workbenchTestServices"},{"name":"TestNotebookEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/test/testNotebookEditor"},{"name":"TestNotificationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/notification/test/common/testNotificationService"},{"name":"TestPanelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestPathService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestProgressService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestReadonlyTextFileEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"testRepeat","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/common/utils"},{"name":"testRepeatedActionAndExtractPositions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/test/wordTestUtils"},{"name":"testRepeatOnly","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/common/utils"},{"name":"TestRPCProtocol","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/api/testRPCProtocol"},{"name":"TestService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/test/node/testService"},{"name":"TestServiceAccessor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestServiceAccessor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/workbenchTestServices"},{"name":"TestServiceClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/test/node/testService"},{"name":"TestSharedProcessService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/workbenchTestServices"},{"name":"TestStorageService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/common/workbenchTestServices"},{"name":"testTextBufferFactory","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/linesTextBuffer/linesTextBufferBuilder.test"},{"name":"TestTextFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestTextFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/workbenchTestServices"},{"name":"TestTextResourceConfigurationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestTextResourcePropertiesService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/services/modelService.test"},{"name":"TestTextResourcePropertiesService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/common/workbenchTestServices"},{"name":"TestThemeService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/test/common/testThemeService"},{"name":"TestUserDataSyncUtilService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/test/common/userDataSyncClient"},{"name":"TestView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/browser/ui/grid/util"},{"name":"TestViewletService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"testViewModel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/viewModel/testViewModel"},{"name":"TestViewsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestWindowConfiguration","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/workbenchTestServices"},{"name":"TestWorkingCopy","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test"},{"name":"TestWorkingCopyService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/common/workbenchTestServices"},{"name":"testWorkspace","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspace/test/common/testWorkspace"},{"name":"TestWorkspace","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspace/test/common/testWorkspace"},{"name":"Text","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Text","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"TEXT_DIFF_EDITOR_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"TEXT_FILE_EDITOR_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"TextAreaHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/textAreaHandler"},{"name":"TextAreaInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/textAreaInput"},{"name":"TextAreaState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/textAreaState"},{"name":"TextBadge","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/activity/common/activity"},{"name":"textBlockQuoteBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"textBlockQuoteBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"TextChange","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textChange"},{"name":"textCodeBlockBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"TextCompareEditorActiveContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"TextCompareEditorVisibleContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"TextDecoder","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TextDecoder","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"TextDecoderStream","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TextDiffEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/textDiffEditor"},{"name":"TextDiffEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor/textDiffEditorModel"},{"name":"TextDocumentSaveReason","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TextDocumentSaveReason","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TextDocumentSaveReason","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"TextEdit","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TextEdit","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TextEdit","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"TextEditElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"TextEditElementRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"TextEditorCursorBlinkingStyle","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"TextEditorCursorBlinkingStyle","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"TextEditorCursorStyle","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TextEditorCursorStyle","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"TextEditorCursorStyle","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"TextEditorDecorationType","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTextEditor"},{"name":"TextEditorEdit","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTextEditor"},{"name":"TextEditorLineNumbersStyle","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TextEditorLineNumbersStyle","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TextEditorLineNumbersStyle","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"TextEditorOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"TextEditorOptions","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"TextEditorRevealType","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TextEditorRevealType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"TextEditorRevealType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TextEditorSelectionChangeKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TextEditorSelectionChangeKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TextEditorSelectionRevealType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/editor/common/editor"},{"name":"TextEditorState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/history/browser/history"},{"name":"TextEncoder","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TextEncoder","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"TextEncoderStream","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TextEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TextFileContentProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"TextFileEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/editors/textFileEditor"},{"name":"TextFileEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textFileEditorModel"},{"name":"TextFileEditorModelManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textFileEditorModelManager"},{"name":"TextFileEditorModelState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textfiles"},{"name":"TextFileEditorTracker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/editors/textFileEditorTracker"},{"name":"TextFileLoadReason","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textfiles"},{"name":"TextFileOperationError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textfiles"},{"name":"TextFileOperationResult","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textfiles"},{"name":"TextFileSaveErrorHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler"},{"name":"TextFileSaveParticipant","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textFileSaveParticipant"},{"name":"TextInputActionsProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/textInputActions"},{"name":"textLinkActiveForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"textLinkForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"textmateColorGroupSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/colorThemeSchema"},{"name":"textmateColorSettingsSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/colorThemeSchema"},{"name":"textmateColorsSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/colorThemeSchema"},{"name":"TextMateService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/browser/textMateService"},{"name":"TextMateService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/electron-browser/textMateService"},{"name":"TextmateSnippet","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"TextMateWorker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/electron-browser/textMateWorker"},{"name":"TextMateWorkerHost","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/electron-browser/textMateService"},{"name":"TextMetrics","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TextModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"TextModelCancellationTokenSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/core/editorState"},{"name":"TextModelResolvedOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"TextModelResolverService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textmodelResolver/common/textModelResolverService"},{"name":"TextModelSearch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelSearch"},{"name":"TextModelTokenization","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelTokens"},{"name":"textPreformatForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"TextResourceConfigurationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/textResourceConfigurationServiceImpl"},{"name":"TextResourceEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/textResourceEditor"},{"name":"TextResourceEditorInput","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"TextResourcePropertiesService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textresourceProperties/common/textResourcePropertiesService"},{"name":"TextSearchEngineAdapter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/textSearchAdapter"},{"name":"TextSearchManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/textSearchManager"},{"name":"TextSearchMatch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"TextSearchResultsCollector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/textSearchManager"},{"name":"textSeparatorForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"TextSnapshotReadable","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textfiles"},{"name":"TextTrack","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TextTrackCue","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TextTrackCueList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TextTrackList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Themable","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"ThemableDecorationAttachmentRenderOptions","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"ThemableDecorationRenderOptions","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"ThemeColor","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ThemeColor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"themeColorFromId","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"ThemeConfiguration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/themeConfiguration"},{"name":"ThemeIcon","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ThemeIcon","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"ThemeIcon","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ThemeMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/electron-main/themeMainService"},{"name":"ThemeRegistry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/themeExtensionPoints"},{"name":"ThemeRule","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/common/TMHelper"},{"name":"ThemeSettings","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/workbenchThemeService"},{"name":"ThemeTrieElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/tokenization"},{"name":"ThemeTrieElementRule","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/tokenization"},{"name":"this","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Thread","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"ThreadAndSessionIds","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"threadId","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"worker_threads"},{"name":"throttle","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/decorators"},{"name":"ThrottledDelayer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"Throttler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"throw","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"throwDeprecation","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"throwProposedApiError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensions"},{"name":"throws","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"tildify","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"time","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"TimeBasedVariableResolver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetVariables"},{"name":"timeEnd","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"timeline","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"TimelineElementTemplate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/browser/timelinePane"},{"name":"timelineEnd","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"TimelineFollowActiveEditorContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/browser/timelinePane"},{"name":"TimelineIdentityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/browser/timelinePane"},{"name":"TimelineItem","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TimelineItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TimelineKeyboardNavigationLabelProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/browser/timelinePane"},{"name":"TimelineListVirtualDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/browser/timelinePane"},{"name":"TimelinePane","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/browser/timelinePane"},{"name":"TimelinePaneDescriptor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/browser/timeline.contribution"},{"name":"TimelinePaneId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/common/timeline"},{"name":"TimelineService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/common/timelineService"},{"name":"timeLog","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"timeout","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"TIMEOUT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"TimeoutTimer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"TimeRanges","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"timeStamp","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"timingSafeEqual","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"title","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"TITLE_BAR_ACTIVE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TITLE_BAR_ACTIVE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TITLE_BAR_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TITLE_BAR_INACTIVE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TITLE_BAR_INACTIVE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TitlebarPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/titlebar/titlebarPart"},{"name":"TitlebarPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/parts/titlebar/titlebarPart"},{"name":"TitleCaseAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"TitleControl","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/titleControl"},{"name":"TitleEventSource","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"TLSSocket","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"TMGrammarFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/common/TMGrammarFactory"},{"name":"tmpdir","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"TMScopeRegistry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/common/TMScopeRegistry"},{"name":"toASCII","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"punycode"},{"name":"toBackupWorkspaceResource","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/backup/electron-browser/backup"},{"name":"toBufferOrReadable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textfiles"},{"name":"toCanonicalName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"tocData","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsLayout"},{"name":"TOCRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/tocTree"},{"name":"TOCTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/tocTree"},{"name":"TOCTreeModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/tocTree"},{"name":"toDecodeStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"toDisposable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/lifecycle"},{"name":"toErrorMessage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errorMessage"},{"name":"toExtension","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensions"},{"name":"toExtensionDescription","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"toFileChanges","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/watcher"},{"name":"toFileOperationResult","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"toFileSystemProviderErrorCode","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"TOGGLE_BREAKPOINT_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugEditorActions"},{"name":"TOGGLE_CONDITIONAL_BREAKPOINT_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugEditorActions"},{"name":"TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"TOGGLE_DIFF_SIDE_BY_SIDE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"TOGGLE_IGNORE_EXTENSION_ACTION_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"TOGGLE_INLINE_BREAKPOINT_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"TOGGLE_LOG_POINT_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugEditorActions"},{"name":"TOGGLE_NOTIFICATION","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"TOGGLE_NOTIFICATIONS_CENTER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"ToggleActivityBarVisibilityAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"ToggleAutoSaveAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"ToggleAutoUpdateAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ToggleBreakpointsActivatedAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"toggleCaseSensitiveCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"ToggleCaseSensitiveCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"ToggleCaseSensitiveKeybinding","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"toggleClass","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"ToggleCollapseAndExpandAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"toggleCollapseState","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingModel"},{"name":"ToggleColumnSelectionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection"},{"name":"ToggleCompositePinnedAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/compositeBarActions"},{"name":"ToggleDevToolsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/developerActions"},{"name":"ToggleEditorLayoutAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"ToggleEditorVisibilityAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"ToggleGroupSizesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"ToggleHighContrastNLS","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standaloneStrings"},{"name":"ToggleMaximizedPanelAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"ToggleMenuBarAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"ToggleMinimapAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/toggleMinimap"},{"name":"ToggleMultiCursorModifierAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier"},{"name":"TogglePanelAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"ToggleReactionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/reactionsAction"},{"name":"toggleRegexCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"ToggleRegexCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"ToggleRegexKeybinding","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"ToggleRenderControlCharacterAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter"},{"name":"ToggleRenderWhitespaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace"},{"name":"toggleSearchEditorCaseSensitiveCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"ToggleSearchEditorCaseSensitiveCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"toggleSearchEditorContextLinesCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"ToggleSearchEditorContextLinesCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"toggleSearchEditorRegexCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"ToggleSearchEditorRegexCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"toggleSearchEditorWholeWordCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"ToggleSearchEditorWholeWordCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"ToggleSearchOnTypeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"ToggleSearchScopeKeybinding","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"ToggleSharedProcessAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/developerActions"},{"name":"ToggleSidebarPositionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"ToggleSidebarVisibilityAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"ToggleStatusbarVisibilityAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"ToggleTabFocusModeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode"},{"name":"ToggleTerminalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"ToggleViewAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"ToggleViewletAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/activitybar/activitybarActions"},{"name":"ToggleViewModeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/repositoryPane"},{"name":"toggleWholeWordCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"ToggleWholeWordCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"ToggleWholeWordKeybinding","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"ToggleWindowTabsBarHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"Token","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/token"},{"name":"Token","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"TOKEN_CLASSIFIER_LANGUAGE_SEPARATOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"TOKEN_TYPE_WILDCARD","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"TokenClassificationExtensionPoints","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/tokenClassificationExtensionPoint"},{"name":"TokenizationRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"TokenizationRegistryImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/tokenizationRegistry"},{"name":"TokenizationResult","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/token"},{"name":"TokenizationResult2","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/token"},{"name":"TokenizationStateStore","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelTokens"},{"name":"TokenizationSupport2Adapter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"tokenize","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"tokenizeLineToHTML","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/textToHtmlTokenizer"},{"name":"tokenizeToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/textToHtmlTokenizer"},{"name":"TokenMetadata","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"TokensStore","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/tokensStore"},{"name":"TokensStore2","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/tokensStore"},{"name":"TokenStyle","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"TokenStylingRule","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"tokenStylingSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"TokenTheme","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/tokenization"},{"name":"TokenType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"toKey","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/common/timeline"},{"name":"toLocalISOString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/date"},{"name":"toLocalResource","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"toMultilineTokens2","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/semanticTokensProviderStyling"},{"name":"toNamespacedPath","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"toNodeEncoding","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"toolbar","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ToolBar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/toolbar/toolbar"},{"name":"TOOLTIP_HOVER_THRESHOLD","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers"},{"name":"TooltipWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWidgets"},{"name":"toOverrides","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"top","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"top","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"topAsync","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"toReadable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/stream"},{"name":"toResource","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/common/utils"},{"name":"toResource","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"toSlashes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"toStandardTokenType","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/tokenization"},{"name":"toStoreData","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"toStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/stream"},{"name":"toString","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"totalmem","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"Touch","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Touch","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"TouchBar","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TouchBarButton","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TouchBarColorPicker","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TouchBarGroup","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TouchBarLabel","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TouchBarPopover","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TouchBarScrubber","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TouchBarSegmentedControl","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TouchBarSlider","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TouchBarSpacer","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TouchEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TouchList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"toUint32","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uint"},{"name":"toUint8","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uint"},{"name":"toUnicode","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"punycode"},{"name":"toValuesTree","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"toWorkspaceFolder","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspace/common/workspace"},{"name":"toWorkspaceFolders","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspace/common/workspace"},{"name":"toWorkspaceIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"trace","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"traceDeprecation","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"traceProcessWarnings","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"TrackedRangeStickiness","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"TrackedRangeStickiness","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"TrackEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"trackFocus","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"transcode","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"buffer"},{"name":"transform","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/stream"},{"name":"Transform","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"stream"},{"name":"Transform","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"TransformableMarker","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"transformAndReviveIncomingURIs","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uriIpc"},{"name":"transformErrorForSerialization","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"transformIncomingURIs","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uriIpc"},{"name":"transformOutgoingURIs","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uriIpc"},{"name":"TransformStream","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TransitionEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Translations","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionPoints"},{"name":"translationsConfigFile","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"transparent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"TransposeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"Tray","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TreeDragOverBubble","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/tree"},{"name":"TreeDragOverReactions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/tree"},{"name":"TreeElement","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineModel"},{"name":"TreeError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/tree"},{"name":"treeIndentGuidesStroke","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"TreeItem","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TreeItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TreeItem2","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TreeItemCollapsibleState","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TreeItemCollapsibleState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/views"},{"name":"TreeItemCollapsibleState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TreeMouseEventTarget","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/tree"},{"name":"TreeNode","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"TreeResourceNavigator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"TreeView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/treeView"},{"name":"TreeViewPane","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/treeView"},{"name":"TreeVisibility","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/tree"},{"name":"TreeWalker","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TRIGGER_RENAME_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"TriggerAction","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/browser/pickerQuickAccess"},{"name":"triggerAsyncId","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"async_hooks"},{"name":"triggerDownload","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"TriggerParameterHintsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/parameterHints/parameterHints"},{"name":"Triggers","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskSystem"},{"name":"TriggerSuggestAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestController"},{"name":"trim","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"TrimFinalNewLinesParticipant","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/saveParticipants"},{"name":"trimTrailingWhitespace","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/commands/trimTrailingWhitespaceCommand"},{"name":"TrimTrailingWhitespaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"TrimTrailingWhitespaceCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/commands/trimTrailingWhitespaceCommand"},{"name":"TrimWhitespaceParticipant","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/saveParticipants"},{"name":"true","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"trueMachineIdKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetry"},{"name":"truncate","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"truncate","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"truncateSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"TRUSTED_DOMAINS_CONTENT_STORAGE_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/url/common/trustedDomains"},{"name":"TRUSTED_DOMAINS_STORAGE_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/url/common/trustedDomains"},{"name":"TrustedDomainsFileSystemProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/url/common/trustedDomainsFileSystemProvider"},{"name":"try","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"TunnelCloseableContextKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/browser/tunnelView"},{"name":"TunnelDto","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTunnelService"},{"name":"TunnelFactoryContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/common/tunnelFactory"},{"name":"TunnelModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/remoteExplorerService"},{"name":"TunnelPanel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/browser/tunnelView"},{"name":"TunnelPanelDescriptor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/browser/tunnelView"},{"name":"TunnelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/tunnelService"},{"name":"TunnelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/node/tunnelService"},{"name":"TunnelType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/remoteExplorerService"},{"name":"TunnelTypeContextKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/browser/tunnelView"},{"name":"TunnelViewModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/browser/tunnelView"},{"name":"twistiePixels","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/baseDebugView"},{"name":"type","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"type","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"Type","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/comment/lineCommentCommand"},{"name":"typeAndModifierIdPattern","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"TypeDefinitionProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"TypeError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"typeof","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"TypeOperations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorTypeOperations"},{"name":"types","kind":"module","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"TypeWithAutoClosingCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorTypeOperations"},{"name":"ucs2","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"punycode"},{"name":"UIEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"UIKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"UIKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"UILabelProvider","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keybindingLabels"},{"name":"Uint16Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Uint32Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Uint8Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Uint8ClampedArray","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Uint8Matrix","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/linkComputer"},{"name":"umask","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"undefined","kind":"var","kindModifiers":"","sortText":"4"},{"name":"UndoIgnoreExtensionRecommendationAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"UndoRedoElementType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/undoRedo/common/undoRedo"},{"name":"UndoRedoService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/undoRedo/common/undoRedoService"},{"name":"UndoWebviewEditorCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/electron-browser/webviewCommands"},{"name":"unescape","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"unescape","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"querystring"},{"name":"Unicode11Addon","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/xterm-addon-unicode11/typings/xterm-addon-unicode11"},{"name":"unicodeEscapesToPCRE2","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepTextSearchEngine"},{"name":"UninstallAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"unique","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"uniqueFilter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"unknown","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"UNKNOWN_SOURCE_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugSource"},{"name":"UnknownExtensionRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewer"},{"name":"unlink","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"unlink","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"unlinkSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"UnloadReason","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/electron-main/lifecycleMainService"},{"name":"unmnemonicLabel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"untildify","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"UNTITLED_WORKSPACE_NAME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"UntitledTextEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/untitled/common/untitledTextEditorInput"},{"name":"UntitledTextEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/untitled/common/untitledTextEditorModel"},{"name":"UntitledTextEditorService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/untitled/common/untitledTextEditorService"},{"name":"unwatchFile","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"unzip","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"unzipSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"UpdateAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"UpdateAllAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"UpdateChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/electron-main/updateIpc"},{"name":"updateColorThemeConfigurationSchemas","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/themeConfiguration"},{"name":"UpdateContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/browser/update"},{"name":"updateFileIconThemeConfigurationSchemas","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/themeConfiguration"},{"name":"updateIgnoredSettings","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/settingsMerge"},{"name":"updateProblemMatchers","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/jsonSchema_v2"},{"name":"updateProductIconThemeConfigurationSchemas","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/themeConfiguration"},{"name":"updateTreeMetadata","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"UpdateType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/common/update"},{"name":"updateViewTypeSchema","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/browser/editorAssociationsSetting"},{"name":"UpperCaseAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"uppercaseFirstLetter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"uptime","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"uptime","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"Uri","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Uri","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"URI","kind":"alias","kindModifiers":"","sortText":"0"},{"name":"URIError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"UriIterator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"uriToFsPath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uri"},{"name":"URITransformer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uriIpc"},{"name":"URITransformerService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostUriTransformerService"},{"name":"url","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"inspector"},{"name":"URL","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"URL","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"url"},{"name":"URLHandlerChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/url/common/urlIpc"},{"name":"URLHandlerChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/url/common/urlIpc"},{"name":"URLHandlerRouter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/url/common/urlIpc"},{"name":"URLSearchParams","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"URLSearchParams","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"url"},{"name":"URLService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/url/node/urlService"},{"name":"USE_ICACLS","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"USE_SPLIT_JSON_SETTING","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferences"},{"name":"useFakeTimers","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"useFakeXMLHttpRequest","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"USER_DATA_SYNC_SCHEME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"USER_MANIFEST_CACHE_FILE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensions"},{"name":"USER_STANDALONE_CONFIGURATIONS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"USER_TASKS_GROUP_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskService"},{"name":"userAgent","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"UserConfiguration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/browser/configuration"},{"name":"UserDataAutoSyncChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncIpc"},{"name":"UserDataAutoSyncService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataAutoSyncService"},{"name":"UserDataAutoSyncService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService"},{"name":"UserDataAutoSyncService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/electron-browser/userDataAutoSyncService"},{"name":"UserDataAutoSyncService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService"},{"name":"UserDataSycnUtilServiceChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncIpc"},{"name":"UserDataSyncAuthentication","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/userDataSync/browser/userDataSyncAuthentication"},{"name":"UserDataSyncBackupStoreService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncBackupStoreService"},{"name":"UserDataSyncChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncIpc"},{"name":"UserDataSyncClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/test/common/userDataSyncClient"},{"name":"UserDataSyncEnablementService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncEnablementService"},{"name":"UserDataSyncError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"UserDataSyncErrorCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"userDataSyncLogChannelId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/common/logConstants"},{"name":"UserDataSyncLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncLog"},{"name":"UserDataSyncService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncService"},{"name":"UserDataSyncService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService"},{"name":"UserDataSyncStoreError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"UserDataSyncStoreService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncStoreService"},{"name":"UserDataSyncTestServer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/test/common/userDataSyncClient"},{"name":"UserDataSyncTrigger","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger"},{"name":"UserDataSyncUtilServiceClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncIpc"},{"name":"UserDataSyncViewContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/userDataSync/browser/userDataSyncView"},{"name":"UserDataSyncWorkbenchContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/userDataSync/browser/userDataSync"},{"name":"userInfo","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"UserSettings","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationModels"},{"name":"UserSettingsLabelProvider","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keybindingLabels"},{"name":"UserSettingsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesRenderers"},{"name":"userSettingsSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"useSlashForPath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"USLayoutResolvedKeybinding","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/usLayoutResolvedKeybinding"},{"name":"USUAL_WORD_SEPARATORS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/wordHelper"},{"name":"UTF16be","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"UTF16be_BOM","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"UTF16le","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"UTF16le_BOM","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"UTF8","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"UTF8_BOM","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"UTF8_BOM_CHARACTER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"UTF8_with_bom","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"utimes","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"utimesSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"UV_UDP_REUSEADDR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"V4MAPPED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"valid","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"ValidAnnotatedEditOperation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"validateConstraint","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"validateConstraints","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"ValidatedEditorOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"validateFileName","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/yauzl/index"},{"name":"validateFileName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"validatePaths","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/node/paths"},{"name":"validateProperty","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"validateTelemetryData","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetryUtils"},{"name":"ValidationState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/parsers"},{"name":"ValidationStatus","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/parsers"},{"name":"ValidityState","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"validRange","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"values","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"values","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/collections"},{"name":"var","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Variable","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"Variable","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"VARIABLES_VIEW_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"VariablesDataSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/variablesView"},{"name":"variableSetEmitter","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/variablesView"},{"name":"VariablesRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/variablesView"},{"name":"VariablesView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/variablesView"},{"name":"verbose","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-sqlite3/index"},{"name":"Verbose","kind":"enum member","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/SeverityLevel"},{"name":"Verbosity","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"VerifiedTask","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem"},{"name":"verify","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"Verify","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"version","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"version","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"punycode"},{"name":"version","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/spdlog/index"},{"name":"version","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"versions","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"VerticalRevealType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"VerticalScrollbar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/verticalScrollbar"},{"name":"VideoPlaybackQuality","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"View","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewImpl"},{"name":"VIEW_CONTAINER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/explorerViewlet"},{"name":"VIEW_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"ViewColumn","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ViewColumn","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ViewColumn","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"viewColumnToEditorGroup","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/shared/editor"},{"name":"ViewConfigurationChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewContainerLocation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/views"},{"name":"ViewContentPriority","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/views"},{"name":"ViewContentSizeChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewContentWidgets","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets"},{"name":"ViewContext","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewContext"},{"name":"ViewController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewController"},{"name":"ViewCursor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/viewCursors/viewCursor"},{"name":"ViewCursors","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/viewCursors/viewCursors"},{"name":"ViewCursorStateChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewDecorationsChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewDescriptorService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/views/browser/viewDescriptorService"},{"name":"ViewEventDispatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEventDispatcher"},{"name":"ViewEventEmitter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewEventHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/viewEventHandler"},{"name":"ViewEventsCollector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewEventType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewFlushedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewFocusChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewIdentifierMap","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint"},{"name":"ViewLanguageConfigurationEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewLayout","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLayout"},{"name":"Viewlet","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/viewlet"},{"name":"VIEWLET_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"VIEWLET_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"VIEWLET_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"VIEWLET_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/common/scm"},{"name":"VIEWLET_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"VIEWLET_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/common/remote.contribution"},{"name":"ViewletActivityAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/activitybar/activitybarActions"},{"name":"ViewletDescriptor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/viewlet"},{"name":"ViewletRegistry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/viewlet"},{"name":"ViewLine","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/lines/viewLine"},{"name":"ViewLineData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/viewModel"},{"name":"ViewLineMappingChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewLineOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/lines/viewLine"},{"name":"ViewLineRenderingData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/viewModel"},{"name":"ViewLines","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/lines/viewLines"},{"name":"ViewLinesChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewLinesDeletedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewLinesInsertedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewLineToken","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/core/viewLineToken"},{"name":"ViewLineTokenFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/core/viewLineToken"},{"name":"ViewLineTokens","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/core/viewLineToken"},{"name":"ViewMenuActions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/viewMenuActions"},{"name":"ViewModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/viewModelImpl"},{"name":"ViewModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugViewModel"},{"name":"ViewModelDecoration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/viewModel"},{"name":"ViewModelDecorations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/viewModelDecorations"},{"name":"ViewOutgoingEvents","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewOutgoingEvents"},{"name":"ViewOverlayLine","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewOverlays"},{"name":"ViewOverlays","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewOverlays"},{"name":"ViewOverlayWidgets","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets"},{"name":"ViewPane","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/viewPaneContainer"},{"name":"ViewPaneContainer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/viewPaneContainer"},{"name":"ViewPart","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewPart"},{"name":"Viewport","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/viewModel"},{"name":"ViewportData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLinesViewportData"},{"name":"ViewQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess"},{"name":"ViewRevealRangeRequestEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"viewsContainersContribution","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/viewsExtensionPoint"},{"name":"ViewScrollChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/views"},{"name":"ViewsWelcomeContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution"},{"name":"viewsWelcomeExtensionPointDescriptor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint"},{"name":"ViewsWelcomeExtensionPointFields","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint"},{"name":"ViewThemeChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewTokensChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewTokensColorsChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"viewTypeSchamaAddition","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/browser/editorAssociationsSetting"},{"name":"ViewZoneDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/zoneWidget/zoneWidget"},{"name":"ViewZones","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/viewZones/viewZones"},{"name":"ViewZonesChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"VirtualDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersTreeViewer"},{"name":"VirtualDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree"},{"name":"virtualMachineHint","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/id"},{"name":"VirualDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewer"},{"name":"VisibleLinesCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewLayer"},{"name":"VisibleRanges","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/renderingContext"},{"name":"visit","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"void","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"VRDisplay","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"VRDisplayCapabilities","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"VRDisplayEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"VREyeParameters","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"VRFieldOfView","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"VRFrameData","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"VRPose","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"vs","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/themes"},{"name":"vs_code_editor_walkthrough","kind":"property","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough"},{"name":"vs_code_welcome_page","kind":"property","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page"},{"name":"vs_dark","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/themes"},{"name":"VS_DARK_THEME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/workbenchThemeService"},{"name":"VS_HC_THEME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/workbenchThemeService"},{"name":"VS_LIGHT_THEME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/workbenchThemeService"},{"name":"VSBuffer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"VTTCue","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"VTTRegion","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"W_OK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WAIT_BETWEEN_RESEND","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"WALK_THROUGH_FOCUS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart"},{"name":"WalkThroughArrowDown","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughActions"},{"name":"WalkThroughArrowUp","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughActions"},{"name":"WalkThroughContentProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider"},{"name":"WalkThroughInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput"},{"name":"WalkThroughModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput"},{"name":"WalkThroughPageDown","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughActions"},{"name":"WalkThroughPageUp","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughActions"},{"name":"WalkThroughPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart"},{"name":"WalkThroughSnippetContentProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider"},{"name":"warn","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"Warning","kind":"enum member","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/SeverityLevel"},{"name":"watch","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"watch","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/chokidar/types/index"},{"name":"WATCH_VIEW_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"WatcherChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/unix/watcherIpc"},{"name":"WatcherChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/nsfw/watcherIpc"},{"name":"WatcherChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/unix/watcherIpc"},{"name":"WatcherChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/nsfw/watcherIpc"},{"name":"WatchExpressionsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/watchExpressionsView"},{"name":"WatchExpressionsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/watchExpressionsView"},{"name":"watchFile","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"watchFile","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/watcher"},{"name":"watchFolder","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/watcher"},{"name":"WatchingProblemCollector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemCollectors"},{"name":"WatermarkContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/watermark/browser/watermark"},{"name":"WaveShaperNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WeakMap","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WeakMapper","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/tree"},{"name":"WeakSet","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebAssembly","kind":"module","kindModifiers":"declare","sortText":"4"},{"name":"WebAuthentication","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebAuthnAssertion","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"webContents","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"WebContents","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"webFrame","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"WebGL2RenderingContext","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLActiveInfo","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebglAddon","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/xterm-addon-webgl/typings/xterm-addon-webgl"},{"name":"WebGLBuffer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLContextEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLFramebuffer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLObject","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLProgram","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLQuery","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLRenderbuffer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLRenderingContext","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLSampler","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLShader","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLShaderPrecisionFormat","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLSync","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLTexture","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLTransformFeedback","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLUniformLocation","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLVertexArrayObject","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebIssueService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/issue/browser/issueService"},{"name":"webkitCancelAnimationFrame","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"webkitConvertPointFromNodeToPage","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"webkitConvertPointFromPageToNode","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"WebKitCSSMatrix","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebKitPoint","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"webkitRequestAnimationFrame","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"webkitRTCPeerConnection","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"webkitURL","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebLinksAddon","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/xterm-addon-web-links/typings/xterm-addon-web-links"},{"name":"WebRequest","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"WebResourceIdentityService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/resource/common/resourceIdentityService"},{"name":"WebSocket","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebSocketNodeSocket","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc.net"},{"name":"WebTelemetryAppender","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/telemetry/browser/telemetryService"},{"name":"webviewDeveloperCategory","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webview"},{"name":"WebviewEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewEditor"},{"name":"WebviewEditorCapabilities","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"WebViewEditorFindNextCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewCommands"},{"name":"WebViewEditorFindPreviousCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewCommands"},{"name":"WebviewEditorInputFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory"},{"name":"WebviewEditorService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService"},{"name":"WebviewFindWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewFindWidget"},{"name":"webviewHasOwnEditFunctionsContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webview"},{"name":"webviewHasOwnEditFunctionsContextKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webview"},{"name":"WebviewIconManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewIconManager"},{"name":"WebviewInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewEditorInput"},{"name":"WebviewMessageChannels","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/baseWebviewElement"},{"name":"WebviewPortMappingManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/common/portMapping"},{"name":"WebviewResourceResponse","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/common/resourceLoader"},{"name":"WebviewResourceScheme","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/common/resourceLoader"},{"name":"WebviewService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewService"},{"name":"webviewTag","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"WebviewThemeDataProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/common/themeing"},{"name":"WebWorkerExtensionHostStarter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter"},{"name":"WelcomeInputFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/page/browser/welcomePage"},{"name":"WelcomeOverlayAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay"},{"name":"WelcomePageAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/page/browser/welcomePage"},{"name":"welcomePageBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/page/browser/welcomePage"},{"name":"WelcomePageContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/page/browser/welcomePage"},{"name":"WelcomeView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/welcomeView"},{"name":"WheelEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"whenDeleted","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"whenProviderRegistered","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"while","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"WholeWordsCheckbox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/findinput/findInputCheckboxes"},{"name":"Widget","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/widget"},{"name":"widgetShadow","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"WidgetVerticalAlignment","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalWidgetManager"},{"name":"WillSaveStateReason","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/common/storage"},{"name":"win32","kind":"module","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"win32","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"win32","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/processes"},{"name":"Win32UpdateService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/electron-main/updateService.win32"},{"name":"window","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"window","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Window","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WINDOW_ACTIVE_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"WINDOW_INACTIVE_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"WindowDriverChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/node/driver"},{"name":"WindowDriverChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/node/driver"},{"name":"WindowDriverRegistryChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/node/driver"},{"name":"WindowDriverRegistryChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/node/driver"},{"name":"WindowMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/electron-main/windows"},{"name":"windowOpenNoOpener","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"windowSettings","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"WindowsExternalTerminalService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService"},{"name":"WindowsKeyboardMapper","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/windowsKeyboardMapper"},{"name":"windowsKeyboardMappingEquals","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/windowsKeyboardMapper"},{"name":"WindowsMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/electron-main/windowsMainService"},{"name":"WindowsNativeResolvedKeybinding","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/windowsKeyboardMapper"},{"name":"WindowsShellHelper","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/electron-browser/windowsShellHelper"},{"name":"WindowsShellType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminal"},{"name":"windowsStore","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"Winreg","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"with","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"withEditorModel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/editorTestUtils"},{"name":"withFormatting","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/jsonEdit"},{"name":"withNullAsUndefined","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"withTestCodeEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/browser/testCodeEditor"},{"name":"withTestNotebook","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/test/testNotebookEditor"},{"name":"withUndefinedAsNull","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"WordCharacterClass","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/wordCharacterClassifier"},{"name":"WordCharacterClassifier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/wordCharacterClassifier"},{"name":"WordContextKey","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/wordContextKey"},{"name":"WordDistance","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/wordDistance"},{"name":"WordLeftCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"WordNavigationType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorWordOperations"},{"name":"WordOperations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorWordOperations"},{"name":"WordPartLeftCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordPartOperations/wordPartOperations"},{"name":"WordPartOperations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorWordOperations"},{"name":"WordPartRightCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordPartOperations/wordPartOperations"},{"name":"WordRightCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"WordSelectionRangeProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/smartSelect/wordSelections"},{"name":"Workbench","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/workbench"},{"name":"WORKBENCH_BACKGROUND","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"WorkbenchAsyncDataTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"workbenchColorsSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"WorkbenchCompressibleAsyncDataTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchCompressibleObjectTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"workbenchConfigurationNodeBase","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/configuration"},{"name":"WorkbenchContextKeysHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/contextkeys"},{"name":"WorkbenchDataTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"workbenchInstantiationService","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"workbenchInstantiationService","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/workbenchTestServices"},{"name":"WorkbenchIssueService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/issue/electron-browser/issueService"},{"name":"WorkbenchKeybindingService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/browser/keybindingService"},{"name":"WorkbenchList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchListAutomaticKeyboardNavigation","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchListAutomaticKeyboardNavigationKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchListDoubleSelection","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchListFocusContextKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchListHasSelectionOrFocus","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchListMultiSelection","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchListSupportsKeyboardNavigation","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchListSupportsMultiSelectContextKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchModeServiceImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/mode/common/workbenchModeService"},{"name":"WorkbenchObjectTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchPagedList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchReferencesController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/workbenchReferenceSearch"},{"name":"WorkbenchState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspace/common/workspace"},{"name":"WorkbenchStateContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/contextkeys"},{"name":"WorkbenchThemeService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/browser/workbenchThemeService"},{"name":"worker","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"Worker","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Worker","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"Worker","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"worker_threads"},{"name":"workerData","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"worker_threads"},{"name":"WorkerExtHostDebugService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDebugService"},{"name":"WorkerExtHostTask","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"WorkerExtHostTerminalService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTerminalService"},{"name":"workers","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"WorkingCopyCapabilities","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workingCopy/common/workingCopyService"},{"name":"WorkingCopyFileOperationParticipant","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workingCopy/common/workingCopyFileOperationParticipant"},{"name":"WorkingCopyFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workingCopy/common/workingCopyFileService"},{"name":"WorkingCopyService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workingCopy/common/workingCopyService"},{"name":"Worklet","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"workspace","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Workspace","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspace/common/workspace"},{"name":"WORKSPACE_EXTENSION","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"WORKSPACE_FILTER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"WORKSPACE_SCOPES","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"WORKSPACE_STANDALONE_CONFIGURATIONS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"WorkspaceBasedVariableResolver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetVariables"},{"name":"WorkspaceChangeExtHostRelauncher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution"},{"name":"WorkspaceConfiguration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/browser/configuration"},{"name":"WorkspaceConfigurationEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"WorkspaceConfigurationModelParser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configurationModels"},{"name":"WorkspaceEdit","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"WorkspaceEdit","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"WorkspaceEdit","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"WorkspaceFileEdit","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"WorkspaceFolder","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspace/common/workspace"},{"name":"WorkspaceFolderCountContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/contextkeys"},{"name":"WorkspaceRecommendations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations"},{"name":"WorkspaceRecommendedExtensionsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews"},{"name":"WorkspaceService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/browser/configurationService"},{"name":"WorkspaceSettingsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesRenderers"},{"name":"workspaceSettingsSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"WorkspacesHistoryMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService"},{"name":"WorkspacesMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/electron-main/workspacesMainService"},{"name":"WorkspacesService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/electron-main/workspacesService"},{"name":"WorkspaceSymbol","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"WorkspaceSymbolProviderRegistry","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/search"},{"name":"WorkspaceTags","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tags/electron-browser/workspaceTags"},{"name":"WorkspaceTagsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService"},{"name":"WorkspaceTextEdit","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"WorkspaceWatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/workspaceWatcher"},{"name":"wrap","kind":"method","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"module"},{"name":"WrappingIndent","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"WrappingIndent","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"wrapWithCorrelationContext","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/applicationinsights"},{"name":"Writable","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"stream"},{"name":"WritableStream","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"write","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"writeFile","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"writeFile","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"writeFileSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"writeFileSync","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"writeHeapSnapshot","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"writeProfile","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"v8-inspect-profiler"},{"name":"writer","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"repl"},{"name":"WriteStream","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"WriteStream","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tty"},{"name":"writeSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"writeTransientState","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap"},{"name":"writeUInt16LE","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"writeUInt32BE","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"writeUInt32LE","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"writeUInt8","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"writev","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"writevSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"WSA_E_CANCELLED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSA_E_NO_MORE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEACCES","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEADDRINUSE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEADDRNOTAVAIL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEAFNOSUPPORT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEALREADY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEBADF","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAECANCELLED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAECONNABORTED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAECONNREFUSED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAECONNRESET","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEDESTADDRREQ","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEDISCON","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEDQUOT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEFAULT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEHOSTDOWN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEHOSTUNREACH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEINPROGRESS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEINTR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEINVAL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEINVALIDPROCTABLE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEINVALIDPROVIDER","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEISCONN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAELOOP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEMFILE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEMSGSIZE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAENAMETOOLONG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAENETDOWN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAENETRESET","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAENETUNREACH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAENOBUFS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAENOMORE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAENOPROTOOPT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAENOTCONN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAENOTEMPTY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAENOTSOCK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEOPNOTSUPP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEPFNOSUPPORT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEPROCLIM","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEPROTONOSUPPORT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEPROTOTYPE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEPROVIDERFAILEDINIT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEREFUSED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEREMOTE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAESHUTDOWN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAESOCKTNOSUPPORT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAESTALE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAETIMEDOUT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAETOOMANYREFS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEUSERS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEWOULDBLOCK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSANOTINITIALISED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSASERVICE_NOT_FOUND","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSASYSCALLFAILURE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSASYSNOTREADY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSATYPE_NOT_FOUND","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAVERNOTSUPPORTED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"X_OK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"x01","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x02","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x03","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x04","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x05","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x06","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x07","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x08","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x09","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x10","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x11","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x12","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x13","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x14","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x15","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x16","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x17","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x18","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x19","kind":"const","kindModifiers":"","sortText":"4"},{"name":"xdescribe","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"xdgRuntimeDir","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/environmentService"},{"name":"xit","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"XMLDocument","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"XMLHttpRequest","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"XMLHttpRequestEventTarget","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"XMLHttpRequestUpload","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"XMLSerializer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"XPathEvaluator","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"XPathExpression","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"XPathResult","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"XSLTProcessor","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"yield","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Z_ASCII","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_BEST_COMPRESSION","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_BEST_SPEED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_BINARY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_BLOCK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_BUF_ERROR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_DATA_ERROR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_DEFAULT_COMPRESSION","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_DEFAULT_STRATEGY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_DEFLATED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_ERRNO","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_FILTERED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_FINISH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_FIXED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_FULL_FLUSH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_HUFFMAN_ONLY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_MEM_ERROR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_NEED_DICT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_NO_COMPRESSION","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_NO_FLUSH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_OK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_PARTIAL_FLUSH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_RLE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_STREAM_END","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_STREAM_ERROR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_SYNC_FLUSH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_TEXT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_TREES","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_UNKNOWN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_VERSION_ERROR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"zip","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/zip"},{"name":"ZipFile","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/yauzl/index"},{"name":"ZipFile","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/yazl/index"},{"name":"ZoneWidget","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/zoneWidget/zoneWidget"},{"name":"ZoomInAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"ZoomOutAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"ZoomResetAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"}]}}'; diff --git a/src/vs/workbench/test/browser/api/extHostDocumentData.test.ts b/src/vs/workbench/test/browser/api/extHostDocumentData.test.ts index a016d6560b8..93dead3f3df 100644 --- a/src/vs/workbench/test/browser/api/extHostDocumentData.test.ts +++ b/src/vs/workbench/test/browser/api/extHostDocumentData.test.ts @@ -10,8 +10,8 @@ import { Position } from 'vs/workbench/api/common/extHostTypes'; import { Range } from 'vs/editor/common/core/range'; import { MainThreadDocumentsShape } from 'vs/workbench/api/common/extHost.protocol'; import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel'; -import { mock } from 'vs/workbench/test/browser/api/mock'; - +import { mock } from 'vs/base/test/common/mock'; +import * as perfData from './extHostDocumentData.test.perf-data'; suite('ExtHostDocumentData', () => { @@ -301,9 +301,9 @@ suite('ExtHostDocumentData', () => { test('getWordRangeAtPosition can freeze the extension host #95319', function () { const regex = /(https?:\/\/github\.com\/(([^\s]+)\/([^\s]+))\/([^\s]+\/)?(issues|pull)\/([0-9]+))|(([^\s]+)\/([^\s]+))?#([1-9][0-9]*)($|[\s\:\;\-\(\=])/; - const line = '{"seq":0,"type":"response","command":"completionInfo","request_seq":956,"success":true,"body":{"isGlobalCompletion":true,"isMemberCompletion":false,"isNewIdentifierLocation":false,"entries":[{"name":"__dirname","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"__filename","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"_getInstrumentationKey","kind":"method","kindModifiers":"private,static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Config"},{"name":"_util","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/instantiation"},{"name":"$","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"abort","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"AbortController","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AbortSignal","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AbstractCaseAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"AbstractCodeEditorService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/services/abstractCodeEditorService"},{"name":"AbstractCommandsQuickAccessProvider","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/browser/commandsQuickAccess"},{"name":"AbstractConfigureRecommendedExtensionsAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"AbstractContextKeyService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/browser/contextKeyService"},{"name":"AbstractDebugAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"AbstractDebugAdapter","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/abstractDebugAdapter"},{"name":"AbstractDeleteAllToBoundaryAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"AbstractEditorCommandsQuickAccessProvider","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/quickAccess/commandsQuickAccess"},{"name":"AbstractEditorNavigationQuickAccessProvider","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/quickAccess/editorNavigationQuickAccess"},{"name":"AbstractExpressionsRenderer","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/baseDebugView"},{"name":"AbstractExtensionService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/abstractExtensionService"},{"name":"AbstractExtHostExtensionService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostExtensionService"},{"name":"AbstractExtHostOutputChannel","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostOutput"},{"name":"AbstractFileDialogService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/browser/abstractFileDialogService"},{"name":"AbstractFileOutputChannelModel","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/output/common/outputChannelModel"},{"name":"AbstractFileSynchroniser","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/abstractSynchronizer"},{"name":"AbstractGotoLineQuickAccessProvider","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/quickAccess/gotoLineQuickAccess"},{"name":"AbstractGotoSymbolQuickAccessProvider","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess"},{"name":"AbstractJsonFileSynchroniser","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/abstractSynchronizer"},{"name":"AbstractKeybindingService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/abstractKeybindingService"},{"name":"AbstractLifecycleService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/common/lifecycleService"},{"name":"AbstractLineHighlightOverlay","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight"},{"name":"AbstractLogService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"AbstractPathService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/path/common/pathService"},{"name":"AbstractProblemCollector","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemCollectors"},{"name":"AbstractProcess","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/processes"},{"name":"AbstractRange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AbstractRemoteAgentService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/abstractRemoteAgentService"},{"name":"AbstractScrollableElement","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/scrollableElement"},{"name":"AbstractScrollbar","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/abstractScrollbar"},{"name":"AbstractSearchAndReplaceAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"AbstractSettingRenderer","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"AbstractSettingsModel","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"AbstractShowReleaseNotesAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/browser/update"},{"name":"AbstractSortLinesAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"AbstractSynchroniser","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/abstractSynchronizer"},{"name":"AbstractTaskService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/abstractTaskService"},{"name":"AbstractTelemetryOptOut","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut"},{"name":"AbstractTextFileService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/browser/textFileService"},{"name":"AbstractTextMateService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/browser/abstractTextMateService"},{"name":"AbstractTextResourceEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/textResourceEditor"},{"name":"AbstractTree","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/abstractTree"},{"name":"AbstractTunnelService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/tunnelService"},{"name":"AbstractUpdateService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/electron-main/abstractUpdateService"},{"name":"AbstractURLService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/url/common/urlService"},{"name":"AbstractVariableResolverService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configurationResolver/common/variableResolver"},{"name":"AbstractWorkspaceEditingService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService"},{"name":"Accelerator","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"acceptLocalChangesCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler"},{"name":"access","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"AccessibilityHelpNLS","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standaloneStrings"},{"name":"AccessibilityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesTree"},{"name":"AccessibilityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree"},{"name":"AccessibilityService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/accessibility/common/accessibilityService"},{"name":"AccessibilitySupport","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/accessibility/common/accessibility"},{"name":"AccessibilitySupport","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"accessSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"AccountsActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/activitybar/activitybarActions"},{"name":"ACL_IDENTITY","kind":"property","kindModifiers":"private,static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"ACLED_DIRECTORIES","kind":"property","kindModifiers":"private,static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"Action","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/actions"},{"name":"Action2","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"ActionBar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/actionbar/actionbar"},{"name":"ActionRunner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/actions"},{"name":"actions","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"vscode-nsfw"},{"name":"ActionsOrientation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/actionbar/actionbar"},{"name":"ActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/actionbar/actionbar"},{"name":"ActivatedExtension","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostExtensionActivator"},{"name":"ActivationTimes","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensions"},{"name":"ACTIVE_GROUP","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorService"},{"name":"activeContrastBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"ActiveEditorContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"ActiveEditorGroupEmptyContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"ActiveEditorGroupIndexContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"ActiveEditorGroupLastContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"ActiveEditorIsReadonlyContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"ActiveGroupEditorsByMostRecentlyUsedQuickAccess","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorQuickAccess"},{"name":"ActivePanelContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/panel"},{"name":"ActiveViewletContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/viewlet"},{"name":"ActiveWindowManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/node/activeWindowTracker"},{"name":"ACTIVITY_BAR_ACTIVE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ACTIVITY_BAR_ACTIVE_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ACTIVITY_BAR_ACTIVE_FOCUS_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ACTIVITY_BAR_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ACTIVITY_BAR_BADGE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ACTIVITY_BAR_BADGE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ACTIVITY_BAR_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ACTIVITY_BAR_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ACTIVITY_BAR_INACTIVE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ActivityAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/compositeBarActions"},{"name":"ActivityActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/compositeBarActions"},{"name":"ActivitybarPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/activitybar/activitybarPart"},{"name":"ActivityService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/activity/browser/activityService"},{"name":"ActivityUpdater","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markers"},{"name":"ADD_CONFIGURATION_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"ADD_ROOT_FOLDER_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceCommands"},{"name":"ADD_ROOT_FOLDER_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceCommands"},{"name":"addArg","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/argvHelper"},{"name":"addClass","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addClasses","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addContextToEditorMatches","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/searchHelpers"},{"name":"AddCursorsAtSearchResults","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"addDisposableGenericMouseDownListner","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addDisposableGenericMouseMoveListner","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addDisposableGenericMouseUpListner","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addDisposableListener","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addDisposableNonBubblingMouseOutListener","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addDisposableNonBubblingPointerOutListener","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addDisposableThrottledListener","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addEventListener","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"AddFunctionBreakpointAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"addListener","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"addListener","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"ADDRCONFIG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"ADDRGETNETWORKPARAMS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"AddRootFolderAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceActions"},{"name":"AddSelectionToNextFindMatchAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"AddSelectionToPreviousFindMatchAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"addSetting","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/settingsMerge"},{"name":"addStandardDisposableGenericMouseDownListner","kind":"let","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addStandardDisposableGenericMouseUpListner","kind":"let","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addStandardDisposableListener","kind":"let","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"addTerminalEnvironmentKeys","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment"},{"name":"addToValueTree","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"AddToWorkspaceFolderRecommendationsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"AddToWorkspaceRecommendationsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"addTrailingPathSeparator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"AddWatchExpressionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"adoptToGalleryExtensionId","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementUtil"},{"name":"after","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"afterEach","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"Agent","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"Agent","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"https"},{"name":"Aggregation","kind":"enum member","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/DataPointType"},{"name":"alert","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"alert","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/aria/aria"},{"name":"alertFormattingEdits","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"ALL_SYNC_RESOURCES","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"allCharCodes","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/minimap/minimapCharSheet"},{"name":"AllEditorsByAppearanceQuickAccess","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorQuickAccess"},{"name":"AllEditorsByMostRecentlyUsedQuickAccess","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorQuickAccess"},{"name":"AllKeysConfigurationChangeEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationModels"},{"name":"allowedNodeEnvironmentFlags","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"allowSetForegroundWindow","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/windows-foreground-love/index"},{"name":"allSettings","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"ALPN_ENABLED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"AnalyserNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AnchorAlignment","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/contextview/contextview"},{"name":"anchorGlob","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepSearchUtils"},{"name":"AnchorPosition","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/contextview/contextview"},{"name":"animate","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"Animation","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AnimationEffect","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AnimationEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AnimationPlaybackEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AnimationTimeline","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ansiColorIdentifiers","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry"},{"name":"ansiColorMap","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry"},{"name":"any","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"anyScore","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"AnythingQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/anythingQuickAccess"},{"name":"ApiCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostApiCommands"},{"name":"ApiCommandArgument","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostApiCommands"},{"name":"ApiCommandResult","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostApiCommands"},{"name":"app","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"append","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"appendEditorTitleContextMenuItem","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions.contribution"},{"name":"appendFile","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"appendFileSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"appendKeyBindingLabel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"appendStylizedStringToContainer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform"},{"name":"appendStylizedStringToContainer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugANSIHandling"},{"name":"appendToCommandPalette","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions.contribution"},{"name":"AppInsightsAppender","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/node/appInsightsAppender"},{"name":"applicationCache","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ApplicationCache","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"applicationSettings","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"applyCodeAction","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionCommands"},{"name":"applyConfigurationValues","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"applyDeprecatedVariableMessage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configurationResolver/common/configurationResolverUtils"},{"name":"applyDragImage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dnd"},{"name":"applyEdit","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/jsonEdit"},{"name":"applyEdits","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/jsonEdit"},{"name":"ApplyEditsResult","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"ApplyToKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"appVersion","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Context"},{"name":"arch","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"arch","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"areFunctions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"areKeyboardLayoutsEqual","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/keymapInfo"},{"name":"areSame","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/settingsMerge"},{"name":"areSameExtensions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementUtil"},{"name":"areWebviewInputOptionsEqual","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService"},{"name":"argv","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"argv0","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"AriaLabelProvider","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keybindingLabels"},{"name":"Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ArrayBuffer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"arrayInsert","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"ArrayNavigator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/navigator"},{"name":"ARROW_IMG_SIZE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/scrollbarArrow"},{"name":"as","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"asArray","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"AsbtractOutputChannelModelService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/output/common/outputChannelModel"},{"name":"asCSSUrl","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"asDomUri","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"asJson","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/request/common/request"},{"name":"asPromise","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"assert","kind":"alias","kindModifiers":"","sortText":"0"},{"name":"assertAllDefined","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"AssertDocumentLineMappingDirection","kind":"enum","kindModifiers":"","sortText":"0"},{"name":"assertEqualQueries","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/test/browser/queryBuilder.test"},{"name":"assertEqualSearchPathResults","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/test/browser/queryBuilder.test"},{"name":"AssertionError","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"assertIsDefined","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"assertMapping","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/test/electron-browser/keyboardMapperTestUtils"},{"name":"assertResolveKeybinding","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/test/electron-browser/keyboardMapperTestUtils"},{"name":"assertResolveKeyboardEvent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/test/electron-browser/keyboardMapperTestUtils"},{"name":"assertResolveUserBinding","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/test/electron-browser/keyboardMapperTestUtils"},{"name":"asserts","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"assertSyncedModels","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/editableTextModelTestUtils"},{"name":"assertType","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"assign","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/objects"},{"name":"asText","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/request/common/request"},{"name":"asWebviewUri","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/shared/webview"},{"name":"async","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"AsyncDataSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewer"},{"name":"AsyncDataTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/asyncDataTree"},{"name":"AsyncEmitter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/event"},{"name":"AsyncResource","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"async_hooks"},{"name":"atob","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"Atomics","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"attachBadgeStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachBreadcrumbsStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachButtonStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachCheckboxStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachDialogStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachFindReplaceInputBoxStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachInputBoxStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachLinkStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachListStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachMenuStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachProgressBarStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachQuickInputStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachSelectBoxStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachStylerCallback","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"attachSuggestEnabledInputBoxStyler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput"},{"name":"Attr","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Audio","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioBuffer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioBufferSourceNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioContext","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioDestinationNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioListener","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioParam","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioParamMap","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioProcessingEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioScheduledSourceNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioWorklet","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AudioWorkletNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"authentication","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"AuthenticationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/authentication/browser/authenticationService"},{"name":"AuthenticationTokenService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/authentication/common/authentication"},{"name":"AuthenticationTokenService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/authentication/electron-browser/authenticationTokenService"},{"name":"AuthenticationTokenServiceChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/authentication/common/authenticationIpc"},{"name":"AuthenticatorAssertionResponse","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AuthenticatorAttestationResponse","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AuthenticatorResponse","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"AuthStatus","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/userDataSync/browser/userDataSyncAuthentication"},{"name":"AutoCheckUpdatesConfigurationKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"AutoFixAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionCommands"},{"name":"AutoIndentOnPaste","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"AutoIndentOnPasteCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"automaticKeyboardNavigationSettingKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"AutoSaveAfterShortDelayContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService"},{"name":"AutoSaveConfiguration","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"AutoSaveMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService"},{"name":"AutoUpdateConfigurationKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"autoUpdater","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"AvailabilityData","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/AvailabilityData"},{"name":"AvailabilityData","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"AverageBufferSize","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase"},{"name":"await","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"BackLayerWebView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView"},{"name":"BackupFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/backup/common/backupFileService"},{"name":"BackupFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/backup/node/backupFileService"},{"name":"BackupFilesModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/backup/common/backupFileService"},{"name":"BackupMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/backup/electron-main/backupMainService"},{"name":"BackupRestorer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/backup/common/backupRestorer"},{"name":"BACKUPS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/common/environment"},{"name":"BackupTracker","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/backup/common/backupTracker"},{"name":"BADFAMILY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"BADFLAGS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"badgeBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"badgeForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"BADHINTS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"BADNAME","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"BADQUERY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"BADRESP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"BADSTR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"BareFontInfo","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/fontInfo"},{"name":"BarProp","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Barrier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"Base","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/Base"},{"name":"Base","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"BaseActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/actionbar/actionbar"},{"name":"BaseAudioContext","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"BaseBinaryResourceEditor","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/binaryEditor"},{"name":"BaseBreakpoint","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"BaseCellViewModel","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel"},{"name":"BaseCloseAllAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"BaseConfigurationResolverService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configurationResolver/browser/configurationResolverService"},{"name":"BaseCreateEditorGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"BaseDropdown","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/dropdown/dropdown"},{"name":"BaseEditor","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/baseEditor"},{"name":"BaseEditorQuickAccessProvider","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorQuickAccess"},{"name":"BaseErrorTelemetry","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/errorTelemetry"},{"name":"BaseExtHostTerminal","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTerminalService"},{"name":"BaseExtHostTerminalService","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTerminalService"},{"name":"BaseFocusGroupAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"BaseMoveGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"basename","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"basename","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"basename","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"basenameOrAuthority","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"BaseNavigateEditorAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"BaseQuickAccessEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"BaseQuickAccessNavigateAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/quickAccessActions"},{"name":"BaseResizeViewAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"BaseResolvedKeybinding","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/baseResolvedKeybinding"},{"name":"BaseSaveAllAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"BaseSplitEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"BaseSwitchWindow","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"BaseTextEditor","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/textEditor"},{"name":"BaseTextEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor/textEditorModel"},{"name":"baseTypeToTelemetryType","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/TelemetryTypes/TelemetryType"},{"name":"BaseWebview","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/baseWebviewElement"},{"name":"BaseWindowDriver","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/browser/baseDriver"},{"name":"BaseZoomAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"BasicInplaceReplace","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/inplaceReplaceSupport"},{"name":"BatchedCollector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/textSearchManager"},{"name":"before","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"beforeEach","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"BeforeUnloadEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"BenchmarkSuite","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/benchmark/benchmarkUtils"},{"name":"BetterMergeId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementUtil"},{"name":"BhxBrowser","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"bigint","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"BigInt","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"BigInt64Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"BigIntStats","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"BigUint64Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"BINARY_DIFF_EDITOR_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"BINARY_FILE_EDITOR_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"BinaryEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor/binaryEditorModel"},{"name":"BinaryFileEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor"},{"name":"BinaryResourceDiffEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/binaryDiffEditor"},{"name":"binarySearch","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"BiquadFilterNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Blob","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"BlockCommentCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/comment/blockCommentCommand"},{"name":"blur","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"boolean","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Boolean","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"BooleanEventEmitter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/widget/codeEditorWidget"},{"name":"BOTTOM_CELL_TOOLBAR_HEIGHT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"BoundModelReferenceCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadDocuments"},{"name":"BracesHidingRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesRenderers"},{"name":"BracketElectricCharacterSupport","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/electricCharacter"},{"name":"BracketMatchingController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/bracketMatching/bracketMatching"},{"name":"BracketSelectionRangeProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/smartSelect/bracketSelections"},{"name":"BracketsUtils","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/richEditBrackets"},{"name":"breadcrumbsActiveSelectionForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"breadcrumbsBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"BreadcrumbsConfig","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbs"},{"name":"BreadcrumbsControl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbsControl"},{"name":"BreadcrumbsFilePicker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbsPicker"},{"name":"breadcrumbsFocusForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"breadcrumbsForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"BreadcrumbsItem","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget"},{"name":"BreadcrumbsOutlinePicker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbsPicker"},{"name":"BreadcrumbsPicker","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbsPicker"},{"name":"breadcrumbsPickerBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"BreadcrumbsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbs"},{"name":"BreadcrumbsWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget"},{"name":"break","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"breakBetweenGraphemeBreakType","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"Breakpoint","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Breakpoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"Breakpoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"BREAKPOINT_EDITOR_CONTRIBUTION_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"BreakpointEditorContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution"},{"name":"BREAKPOINTS_VIEW_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"breakpointsExtPoint","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugSchemas"},{"name":"BreakpointsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/breakpointsView"},{"name":"BreakpointWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/breakpointWidget"},{"name":"BreakpointWidgetContext","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"BroadcastChannel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"brotliCompress","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"brotliCompressSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"brotliDecompress","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"brotliDecompressSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"BrowserBackupTracker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/backup/browser/backupTracker"},{"name":"BrowserClipboardService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/clipboard/browser/clipboardService"},{"name":"BrowserCredentialsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/credentials/browser/credentialsService"},{"name":"BrowserEnvironmentConfiguration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/environment/browser/environmentService"},{"name":"BrowserFeatures","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/canIUse"},{"name":"BrowserHostService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/host/browser/browserHostService"},{"name":"BrowserIntegrityServiceImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/integrity/browser/integrityService"},{"name":"BrowserKeyboardMapperFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/browser/keymapService"},{"name":"BrowserKeyboardMapperFactoryBase","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/browser/keymapService"},{"name":"BrowserLifecycleService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/lifecycle/browser/lifecycleService"},{"name":"BrowserPathService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/path/browser/pathService"},{"name":"BrowserRequestService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/request/browser/requestService"},{"name":"BrowserResizeObserver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver"},{"name":"BrowserSocketFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/browser/browserSocketFactory"},{"name":"BrowserStorageService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/browser/storageService"},{"name":"BrowserTelemetryOptOut","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut"},{"name":"BrowserTextFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/browser/browserTextFileService"},{"name":"BrowserUpdateService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/update/browser/updateService"},{"name":"BrowserURLService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/url/browser/urlService"},{"name":"BrowserView","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"BrowserWindow","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"BrowserWindowProxy","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"BrowserWorkbenchEnvironmentService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/environment/browser/environmentService"},{"name":"BrowserWorkspaceEditingService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workspaces/browser/workspaceEditingService"},{"name":"BrowserWorkspacesService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workspaces/browser/workspacesService"},{"name":"btoa","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"buffer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/zip"},{"name":"Buffer","kind":"class","kindModifiers":"declare","sortText":"4"},{"name":"Buffer","kind":"alias","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"buffer"},{"name":"BufferedEmitter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc.net"},{"name":"BufferLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/bufferLog"},{"name":"BufferredOutputChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/output/common/outputChannelModel"},{"name":"bufferToReadable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"bufferToStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"buildHelpMessage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/argv"},{"name":"buildRegexParseError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepTextSearchEngine"},{"name":"buildReplaceStringWithCasePreserved","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/search"},{"name":"buildTelemetryMessage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/node/telemetry"},{"name":"buildVersionMessage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/argv"},{"name":"BUILTIN_MANIFEST_CACHE_FILE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensions"},{"name":"BuiltInBasicsExtensionsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews"},{"name":"BuiltInExtensionsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews"},{"name":"builtinModules","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"module"},{"name":"BuiltInThemesExtensionsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews"},{"name":"BulkCategory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview"},{"name":"BulkEditAccessibilityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"BulkEditDataSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"BulkEditDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"BulkEditIdentityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"BulkEditNaviLabelProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"BulkEditPane","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane"},{"name":"BulkEditPreviewProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview"},{"name":"BulkEditService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/bulkEdit/browser/bulkEditService"},{"name":"BulkEditSorter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"BulkFileOperation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview"},{"name":"BulkFileOperations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview"},{"name":"BulkFileOperationType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview"},{"name":"BulkTextEdit","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview"},{"name":"Button","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/button/button"},{"name":"buttonBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"buttonBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/page/browser/welcomePage"},{"name":"buttonForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"ButtonGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/button/button"},{"name":"buttonHoverBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"buttonHoverBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/page/browser/welcomePage"},{"name":"ByteLengthQueuingStrategy","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Cache","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Cache","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/cache"},{"name":"Cache","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/cache"},{"name":"cached","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-sqlite3/index"},{"name":"cachedDataVersionTag","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"CachedExtensionScanner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner"},{"name":"CachedKeyboardMapper","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/keyboardMapper"},{"name":"CachedListVirtualDelegate","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/list"},{"name":"cachedStringRepeat","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/commands/shiftCommand"},{"name":"caches","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CacheStorage","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"calcANSI8bitColor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform"},{"name":"calcANSI8bitColor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugANSIHandling"},{"name":"calculateLF","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"calculateSize","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"Call","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree"},{"name":"callbackify","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"CallHierarchyDirection","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/common/callHierarchy"},{"name":"CallHierarchyIncomingCall","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CallHierarchyIncomingCall","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CallHierarchyIncomingCall","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"CallHierarchyItem","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CallHierarchyItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CallHierarchyItem","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"CallHierarchyModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/common/callHierarchy"},{"name":"CallHierarchyOutgoingCall","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CallHierarchyOutgoingCall","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CallHierarchyOutgoingCall","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"CallHierarchyProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/common/callHierarchy"},{"name":"CallHierarchyTreePeekWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek"},{"name":"CallRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree"},{"name":"CALLSTACK_VIEW_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CallStackEditorContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/callStackEditorContribution"},{"name":"CallStackView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/callStackView"},{"name":"CancelActionId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"cancelAnimationFrame","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"CancelCellAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/contrib/notebookActions"},{"name":"canceled","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"CancellationToken","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/cancellation"},{"name":"CancellationTokenSource","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CancellationTokenSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/cancellation"},{"name":"CancellationTokenSource","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"CANCELLED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"CancelSearchAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"canExecuteOnUI","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsUtil"},{"name":"canExecuteOnWeb","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsUtil"},{"name":"canExecuteOnWorkspace","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsUtil"},{"name":"canNormalize","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/normalization"},{"name":"CanvasGradient","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CanvasPattern","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CanvasRenderingContext2D","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"captureEvents","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"CaretPosition","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"case","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"CaseSensitiveCheckbox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/findinput/findInputCheckboxes"},{"name":"catch","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"CategoryElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"CategoryElementRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"CDATASection","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CELL_MARGIN","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"CELL_RUN_GUTTER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"CELL_TOOLBAR_SEPERATOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookEditor"},{"name":"CellEditState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookBrowser"},{"name":"CellEditType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"CellFocusMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookBrowser"},{"name":"CellKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CellKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"CellKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"CellKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CellMenus","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/cellMenus"},{"name":"CellOutputKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CellOutputKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"CellOutputKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"CellOutputKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CellRevealPosition","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookBrowser"},{"name":"CellRevealType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookBrowser"},{"name":"CellRunState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookBrowser"},{"name":"CellUri","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"CenteredViewLayout","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/centered/centeredViewLayout"},{"name":"Certificate","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"CHANGE_BUFFER_DELAY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/watcher"},{"name":"ChangeEncodingAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorStatus"},{"name":"ChangeEOLAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorStatus"},{"name":"ChangeIndentationSizeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"ChangeModeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorStatus"},{"name":"ChangeSortAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"Channel","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Channel"},{"name":"ChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc"},{"name":"ChannelMergerNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ChannelServer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc"},{"name":"ChannelSplitterNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CharacterClassifier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/characterClassifier"},{"name":"CharacterData","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CharacterMapping","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"CharacterMappingConstants","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"CharacterPairSupport","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/characterPair"},{"name":"CharacterSet","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/characterClassifier"},{"name":"CharCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/charCode"},{"name":"CharWidthRequest","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/config/charWidthReader"},{"name":"CharWidthRequestType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/config/charWidthReader"},{"name":"chdir","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"Checkbox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/checkbox/checkbox"},{"name":"CheckboxActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/checkbox/checkbox"},{"name":"CheckedStates","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview"},{"name":"CheckForUpdatesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"CheckForVSCodeUpdateAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/browser/update"},{"name":"CheckForVSCodeUpdateActionId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/common/update"},{"name":"checkProposedApiEnabled","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensions"},{"name":"checkServerIdentity","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"checksum","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/crypto"},{"name":"chmod","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"chmod","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"chmodSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"Choice","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"ChoiceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/notifications"},{"name":"ChokidarWatcherService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/unix/chokidarWatcherService"},{"name":"ChordKeybinding","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keyCodes"},{"name":"chown","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"chownSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"chrome","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"ChunkStream","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc.net"},{"name":"Cipher","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"clamp","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/numbers"},{"name":"class","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"CLASSIFIER_MODIFIER_SEPARATOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"ClassName","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"clean","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"cleanMnemonic","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/menu/menu"},{"name":"cleanRemoteAuthority","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetryUtils"},{"name":"CleanSearchEditorStateCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"cleanUndefinedQueryValues","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/test/browser/queryBuilder.test"},{"name":"clear","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"CLEAR_ALL_NOTIFICATIONS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"CLEAR_NOTIFICATION","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"clearAllFontInfos","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/config/configuration"},{"name":"ClearAllNotificationsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsActions"},{"name":"ClearCommandHistoryAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess"},{"name":"ClearEditorHistoryAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"ClearExtensionsInputAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"clearHistoryCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"clearImmediate","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"clearImmediate","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"timers"},{"name":"clearInterval","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"clearInterval","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"timers"},{"name":"clearLine","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"readline"},{"name":"clearNode","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"ClearNotificationAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsActions"},{"name":"ClearRecentFilesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"ClearReplAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/repl"},{"name":"clearScreenDown","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"readline"},{"name":"ClearSearchHistoryCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"ClearSearchResultsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"ClearTerminalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"clearTextMimes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/mime"},{"name":"clearTimeout","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"clearTimeout","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"timers"},{"name":"ClickLinkGesture","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/link/clickLinkGesture"},{"name":"ClickLinkKeyboardEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/link/clickLinkGesture"},{"name":"ClickLinkMouseEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/link/clickLinkGesture"},{"name":"ClickLinkOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/link/clickLinkGesture"},{"name":"Client","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc.net"},{"name":"Client","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser"},{"name":"Client","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc.cp"},{"name":"CLIENT_RENEG_LIMIT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"CLIENT_RENEG_WINDOW","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"ClientCoordinates","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorDom"},{"name":"clientInformation","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ClientRect","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ClientRectList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ClientRequest","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"ClientRequest","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"clipboard","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"Clipboard","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ClipboardBasedVariableResolver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetVariables"},{"name":"ClipboardEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CLIServer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostCLIServer"},{"name":"clock","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"cloneAndChange","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/objects"},{"name":"close","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"close","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"close","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"inspector"},{"name":"CLOSE_EDITOR_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"CLOSE_EDITOR_GROUP_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"CLOSE_EDITORS_AND_GROUP_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"CLOSE_EDITORS_IN_GROUP_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"CLOSE_SAVED_EDITORS_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"CloseAllEditorGroupsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"CloseAllEditorsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"CloseCurrentWindowAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"closed","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CloseDirection","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"CloseEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"CloseEditorInAllGroupsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"CloseEditorsInOtherGroupsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"CloseEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CloseExtensionDetailsOnViewChangeKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"CloseGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"CloseLeftEditorsInGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"CloseOneEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"ClosePanelAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"CloseReplaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"CloseReplaceWidgetActionId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"CloseSidebarAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"closeSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"CloseWorkspaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceActions"},{"name":"cmp","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"coalesce","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"coalesceInPlace","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"CodeAction","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CodeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CodeActionAutoApply","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/types"},{"name":"CodeActionCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionCommands"},{"name":"CodeActionCommandArgs","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/types"},{"name":"codeActionCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeAction"},{"name":"CodeActionDocumentationContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeActions/common/documentationContribution"},{"name":"CodeActionExtensionPointFields","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeActions/common/codeActionsExtensionPoint"},{"name":"CodeActionKeybindingResolver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionMenu"},{"name":"CodeActionKind","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CodeActionKind","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/types"},{"name":"CodeActionKind","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CodeActionMenu","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionMenu"},{"name":"CodeActionModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionModel"},{"name":"CodeActionProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CodeActionsContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeActions/common/codeActionsContribution"},{"name":"codeActionsExtensionPointDescriptor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeActions/common/codeActionsExtensionPoint"},{"name":"CodeActionsState","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionModel"},{"name":"CodeActionTrigger","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CodeActionTriggerType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CodeActionUi","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionUi"},{"name":"CodeApplication","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-main/app"},{"name":"CodeCell","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell"},{"name":"CodeCellRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer"},{"name":"CodeCellViewModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel"},{"name":"CodeDataTransfers","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"CodeEditorService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/browser/codeEditorService"},{"name":"CodeEditorServiceImpl","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/services/codeEditorServiceImpl"},{"name":"CodeEditorStateFlag","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/core/editorState"},{"name":"CodeEditorWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/widget/codeEditorWidget"},{"name":"CodeInset","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CodeLens","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CodeLens","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CodeLensCache","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codelens/codeLensCache"},{"name":"CodeLensContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codelens/codelensController"},{"name":"CodeLensHelper","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codelens/codelensWidget"},{"name":"CodeLensModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codelens/codelens"},{"name":"CodeLensProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CodeLensWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codelens/codelensWidget"},{"name":"CodeWindow","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-main/window"},{"name":"CodiconActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer"},{"name":"CodiconLabel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/codiconLabel/codiconLabel"},{"name":"codiconStartMarker","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/codicon"},{"name":"coerce","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"COLLAPSE_NOTIFICATION","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"CollapseAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/viewlet"},{"name":"CollapseAllAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/treeDefaults"},{"name":"CollapseDeepestExpandedLevelAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"CollapseExplorerView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"CollapseNotificationAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsActions"},{"name":"collectLaunchConfigs","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/diagnostics/node/diagnosticsService"},{"name":"collectWorkspaceStats","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/diagnostics/node/diagnosticsService"},{"name":"Color","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Color","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/color"},{"name":"Color","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"Color","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"ColorDetector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/colorPicker/colorDetector"},{"name":"ColorExtensionPoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/colorExtensionPoint"},{"name":"ColorFormat","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ColorId","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"ColorInformation","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ColorInformation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"colorize","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"colorizeElement","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"colorizeModelLine","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"Colorizer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/colorizer"},{"name":"ColorMap","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/tokenization"},{"name":"ColorPickerBody","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/colorPicker/colorPickerWidget"},{"name":"ColorPickerHeader","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/colorPicker/colorPickerWidget"},{"name":"ColorPickerModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/colorPicker/colorPickerModel"},{"name":"ColorPickerWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/colorPicker/colorPickerWidget"},{"name":"ColorPresentation","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ColorPresentation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ColorPresentation","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"ColorProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"ColorTheme","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ColorThemeData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/colorThemeData"},{"name":"ColorThemeKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ColorThemeKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"colorThemeSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/colorThemeSchema"},{"name":"ColorZone","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/overviewZoneManager"},{"name":"ColumnSelection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorColumnSelection"},{"name":"combinedAppender","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetryUtils"},{"name":"combinedDisposable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/lifecycle"},{"name":"CombinedInstallAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"CombinedSpliceable","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/splice"},{"name":"Command","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"CommandLine","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"CommandOptions","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"commands","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"commands","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/workbench.web.api"},{"name":"CommandsConverter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostCommands"},{"name":"CommandService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/commands/common/commandService"},{"name":"commandsExtensionPoint","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/menusExtensionPoint"},{"name":"CommandsHistory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/browser/commandsQuickAccess"},{"name":"CommandsQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess"},{"name":"CommandsRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/commands/common/commands"},{"name":"CommandString","kind":"type","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"CommandString","kind":"type","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskConfiguration"},{"name":"CommandTrackerAddon","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/addons/commandTrackerAddon"},{"name":"Comment","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CommentBasedVariableResolver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetVariables"},{"name":"CommentContextKeys","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/common/commentContextKeys"},{"name":"CommentController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsEditorContribution"},{"name":"COMMENTEDITOR_DECORATION_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentThreadWidget"},{"name":"CommentFormActions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentFormActions"},{"name":"CommentGlyphWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentGlyphWidget"},{"name":"CommentMenus","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentMenus"},{"name":"CommentMode","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CommentMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CommentMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CommentMode","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/commentMode"},{"name":"CommentNode","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/common/commentModel"},{"name":"CommentNode","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentNode"},{"name":"CommentNodeRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsTreeViewer"},{"name":"comments","kind":"module","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"COMMENTS_VIEW_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsTreeViewer"},{"name":"COMMENTS_VIEW_TITLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsTreeViewer"},{"name":"CommentsAsyncDataSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsTreeViewer"},{"name":"CommentService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentService"},{"name":"CommentsList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsTreeViewer"},{"name":"CommentsModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/common/commentModel"},{"name":"CommentsModelVirualDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsTreeViewer"},{"name":"CommentsPanel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsView"},{"name":"CommentThreadCollapsibleState","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CommentThreadCollapsibleState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CommentThreadCollapsibleState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CommitCharacterController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestCommitCharacters"},{"name":"CommonEditorConfiguration","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/commonEditorConfig"},{"name":"CommonFindController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"commonlyUsedData","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsLayout"},{"name":"commonPrefixLength","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"commonPrefixLength","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"commonSuffixLength","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"CommonTask","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"Comparator","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"compare","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"compare","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"compare","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"COMPARE_RESOURCE_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"COMPARE_SELECTED_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"COMPARE_WITH_SAVED_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"compareAnything","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/comparers"},{"name":"compareBuild","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"compareByPrefix","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/comparers"},{"name":"compareFileExtensions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/comparers"},{"name":"compareFileNames","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/comparers"},{"name":"compareIdentifiers","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"compareIgnoreCase","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"compareItemsByFuzzyScore","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/fuzzyScorer"},{"name":"compareMarkersByUri","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersModel"},{"name":"comparePaths","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/comparers"},{"name":"compareSubstring","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"compareSubstringIgnoreCase","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"CompareWithClipboardAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"CompatChangeAll","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"compile","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCompile"},{"name":"compileFunction","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"vm"},{"name":"CompletionContext","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"CompletionItem","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CompletionItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggest"},{"name":"CompletionItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CompletionItem","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"CompletionItemInsertTextRule","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CompletionItemInsertTextRule","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"CompletionItemKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CompletionItemKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CompletionItemKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CompletionItemKind","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"CompletionItemKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"CompletionItemTag","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CompletionItemTag","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CompletionItemTag","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CompletionItemTag","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"CompletionItemTag","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"completionKindFromString","kind":"let","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"completionKindToCssClass","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CompletionList","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CompletionList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CompletionModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/completionModel"},{"name":"CompletionOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggest"},{"name":"CompletionProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CompletionTriggerKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CompletionTriggerKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"CompletionTriggerKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CompletionTriggerKind","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"CompletionTriggerKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"Component","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/component"},{"name":"ComposedTreeDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/abstractTree"},{"name":"Composite","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/composite"},{"name":"CompositeActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/compositeBarActions"},{"name":"CompositeBar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/compositeBar"},{"name":"CompositeDescriptor","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/composite"},{"name":"CompositeDragAndDrop","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/compositeBar"},{"name":"CompositeDragAndDropData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"CompositeDragAndDropObserver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"CompositeOverflowActivityAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/compositeBarActions"},{"name":"CompositeOverflowActivityActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/compositeBarActions"},{"name":"CompositePart","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/compositePart"},{"name":"CompositeProgressIndicator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/progress/browser/progressIndicator"},{"name":"CompositeRegistry","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/composite"},{"name":"CompositeScope","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/progress/browser/progressIndicator"},{"name":"CompositeSnippetVariableResolver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetVariables"},{"name":"CompositionEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"compress","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/compressedObjectTreeModel"},{"name":"compressConsecutiveTextChanges","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textChange"},{"name":"CompressedNavigationController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerViewer"},{"name":"CompressedObjectTreeModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/compressedObjectTreeModel"},{"name":"CompressibleAsyncDataTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/asyncDataTree"},{"name":"CompressibleObjectTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/objectTree"},{"name":"CompressibleObjectTreeModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/compressedObjectTreeModel"},{"name":"computeCodePoint","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"ComputedEditorOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/commonEditorConfig"},{"name":"computeLinks","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/linkComputer"},{"name":"computeRanges","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/indentRangeProvider"},{"name":"computeScreenAwareSize","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"computeStyles","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"config","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"config","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"Config","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"Config","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Config"},{"name":"Configuration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/config/configuration"},{"name":"Configuration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationModels"},{"name":"Configuration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configurationModels"},{"name":"Configuration","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/applicationinsights"},{"name":"CONFIGURATION_SYNC_STORE_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"ConfigurationCache","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/browser/configurationCache"},{"name":"ConfigurationCache","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/node/configurationCache"},{"name":"ConfigurationChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"ConfigurationChangeEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationModels"},{"name":"ConfigurationEditingError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configurationEditingService"},{"name":"ConfigurationEditingErrorCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configurationEditingService"},{"name":"ConfigurationEditingService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configurationEditingService"},{"name":"ConfigurationKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"ConfigurationManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugConfigurationManager"},{"name":"ConfigurationModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationModels"},{"name":"ConfigurationModelParser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationModels"},{"name":"ConfigurationResolverService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configurationResolver/browser/configurationResolverService"},{"name":"ConfigurationResolverService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configurationResolver/electron-browser/configurationResolverService"},{"name":"ConfigurationScope","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"ConfigurationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationService"},{"name":"ConfigurationTarget","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ConfigurationTarget","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"ConfigurationTarget","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ConfigurationTargetToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"configurationTelemetry","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetryUtils"},{"name":"ConfigureAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"ConfigureLanguageBasedSettingsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesActions"},{"name":"ConfigureLocaleAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/localizations/browser/localizationsActions"},{"name":"ConfigureNotificationAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsActions"},{"name":"configureOpenerTrustedDomainsHandler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/url/common/trustedDomains"},{"name":"ConfigureRecommendedExtensionsCommandsContributor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ConfigureRuntimeArgumentsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/developerActions"},{"name":"ConfigureTaskAction","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/abstractTaskService"},{"name":"ConfigureWorkspaceFolderRecommendedExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ConfigureWorkspaceRecommendedExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ConfiguringTask","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"confirm","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"ConfirmResult","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/dialogs/common/dialogs"},{"name":"CONFLICT_RESOLUTION_CONTEXT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler"},{"name":"CONFLICT_RESOLUTION_SCHEME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler"},{"name":"ConflictDetector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/bulkEdit/browser/conflicts"},{"name":"connect","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"http2"},{"name":"connect","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"net"},{"name":"connect","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"connect","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc.net"},{"name":"connect","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/node/driver"},{"name":"connected","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"ConnectionGainEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"ConnectionLostEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"ConnectionType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"connectPrimaryMenuToInlineActionBar","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/util"},{"name":"connectProxyResolver","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/node/proxyResolver"},{"name":"connectRemoteAgentExtensionHost","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"connectRemoteAgentManagement","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"connectRemoteAgentTunnel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"CONNREFUSED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"console","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Console","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"ConsoleLogInAutomationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/browser/log"},{"name":"ConsoleLogInMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"ConsoleLogMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"ConsoleLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"consolidate","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/rangeMap"},{"name":"const","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"constants","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"buffer"},{"name":"constants","kind":"module","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"constants","kind":"module","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"constants","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"http2"},{"name":"constants","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"constants","kind":"module","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"perf_hooks"},{"name":"constants","kind":"module","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"constants","kind":"property","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/constants"},{"name":"Constants","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uint"},{"name":"Constants","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/jschardet/index"},{"name":"Constants","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/minimap/minimapCharSheet"},{"name":"ConstantSourceNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"consumeReadable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/stream"},{"name":"consumeReadableWithLimit","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/stream"},{"name":"consumeStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/stream"},{"name":"consumeStreamWithLimit","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/stream"},{"name":"contains","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"containsDragType","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"containsEmoji","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"containsFullWidthCharacter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"containsRTL","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"containsUppercaseCharacter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"ContentHoverWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/hover/hoverWidgets"},{"name":"contentTracing","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"ContentViewOverlays","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewOverlays"},{"name":"ContentWidgetPositionPreference","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorBrowser"},{"name":"ContentWidgetPositionPreference","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"context","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Context","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/parameterHints/provideSignatureHelp"},{"name":"Context","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggest"},{"name":"Context","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/browser/contextKeyService"},{"name":"Context","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Context"},{"name":"CONTEXT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/toolbar/toolbar"},{"name":"CONTEXT_ACCESSIBILITY_MODE_ENABLED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/accessibility/common/accessibility"},{"name":"CONTEXT_ACTIVE_LOG_OUTPUT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"CONTEXT_AUTH_TOKEN_STATE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/userDataSync/browser/userDataSyncAuthentication"},{"name":"CONTEXT_BREAKPOINT_SELECTED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_BREAKPOINT_WIDGET_VISIBLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_BREAKPOINTS_EXIST","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_BREAKPOINTS_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_CALLSTACK_ITEM_TYPE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_CUSTOM_EDITORS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/customEditor"},{"name":"CONTEXT_DEBUG_CONFIGURATION_TYPE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_DEBUG_STATE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_DEBUG_TYPE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_DEBUG_UX","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_DEBUG_UX_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_EXPRESSION_SELECTED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_EXTENSION_HOST_PROFILE_RECORDED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"CONTEXT_FIND_INPUT_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"CONTEXT_FIND_WIDGET_NOT_VISIBLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"CONTEXT_FIND_WIDGET_VISIBLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/customEditor"},{"name":"CONTEXT_FOCUSED_SESSION_IS_ATTACH","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_IN_BREAKPOINT_WIDGET","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_IN_DEBUG_MODE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_IN_DEBUG_REPL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_IN_OUTPUT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"CONTEXT_JUMP_TO_CURSOR_SUPPORTED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_KEYBINDING_FOCUS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"CONTEXT_KEYBINDINGS_EDITOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"CONTEXT_KEYBINDINGS_SEARCH_FOCUS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"CONTEXT_LOADED_SCRIPTS_ITEM_TYPE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_LOADED_SCRIPTS_SUPPORTED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_MENU_CHANNEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/contextmenu/common/contextmenu"},{"name":"CONTEXT_MENU_CLOSE_CHANNEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/contextmenu/common/contextmenu"},{"name":"CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/rename/onTypeRename"},{"name":"CONTEXT_OUTPUT_SCROLL_LOCK","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"CONTEXT_PROFILE_SESSION_STATE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"CONTEXT_RENAME_INPUT_VISIBLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/rename/renameInputField"},{"name":"CONTEXT_REPLACE_INPUT_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"CONTEXT_RESTART_FRAME_SUPPORTED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_SETTINGS_EDITOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"CONTEXT_SETTINGS_JSON_EDITOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"CONTEXT_SETTINGS_SEARCH_FOCUS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"CONTEXT_STEP_BACK_SUPPORTED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_SYNC_ENABLEMENT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"CONTEXT_SYNC_STATE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"CONTEXT_TOC_ROW_FOCUS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"CONTEXT_UPDATE_STATE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/browser/update"},{"name":"CONTEXT_VARIABLES_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"CONTEXT_WATCH_EXPRESSIONS_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"ContextAwareMenuEntryActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/browser/menuEntryActionViewItem"},{"name":"contextBridge","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"ContextKeyAndExpr","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyDefinedExpr","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyEqualsExpr","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyExpr","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyExprType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyFalseExpr","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyNotEqualsExpr","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyNotExpr","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyNotRegexExpr","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyOrExpr","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyRegexExpr","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextKeyService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/browser/contextKeyService"},{"name":"ContextKeyTrueExpr","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"ContextMenuController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/contextmenu/contextmenu"},{"name":"ContextMenuHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextview/browser/contextMenuHandler"},{"name":"ContextMenuService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextview/browser/contextMenuService"},{"name":"ContextMenuService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService"},{"name":"ContextScopedFindInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/browser/contextScopedHistoryWidget"},{"name":"ContextScopedHistoryInputBox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/browser/contextScopedHistoryWidget"},{"name":"ContextScopedReplaceInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/browser/contextScopedHistoryWidget"},{"name":"ContextSubMenu","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/contextmenu"},{"name":"ContextTagKeys","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/ContextTagKeys"},{"name":"ContextTagKeys","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"ContextView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/contextview/contextview"},{"name":"ContextViewService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextview/browser/contextViewService"},{"name":"continue","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"CONTINUE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"CONTINUE_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"Contracts","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/applicationinsights"},{"name":"contrastBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"ContributableViewsModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/views"},{"name":"ContributedCustomEditors","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors"},{"name":"ContributedTask","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"convertBufferRangeToViewport","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers"},{"name":"convertLinkRangeToBuffer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers"},{"name":"convertSettings","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/themeCompatibility"},{"name":"convertSimple2RegExpPattern","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"convertToDAPaths","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugUtils"},{"name":"convertToVSCPaths","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugUtils"},{"name":"ConvolverNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Cookies","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"CoordinatesConverter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/splitLinesCollection"},{"name":"copy","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"COPY_FILE_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"COPY_PATH_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"COPY_RELATIVE_PATH_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"COPY_STACK_TRACE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"copyAllCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"CopyAllCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"copyFile","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"COPYFILE_EXCL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"COPYFILE_FICLONE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"COPYFILE_FICLONE_FORCE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"copyFileHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"copyFileSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"CopyLinesCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/copyLinesCommand"},{"name":"copyMatchCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"CopyMatchCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"CopyNotificationMessageAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsActions"},{"name":"CopyOptions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/textAreaInput"},{"name":"copyPathCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"CopyPathCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"CopyTerminalSelectionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"CopyValueAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"CopyWebviewEditorCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/electron-browser/webviewCommands"},{"name":"CoreEditingCommands","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/coreCommands"},{"name":"CoreEditorCommand","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/coreCommands"},{"name":"CoreNavigationCommands","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/coreCommands"},{"name":"CorrelationContextManager","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/AutoCollection/CorrelationContextManager"},{"name":"count","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"CountBadge","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/countBadge/countBadge"},{"name":"countEOL","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/tokensStore"},{"name":"Counter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/numbers"},{"name":"CountQueuingStrategy","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"countReset","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"cpus","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"cpuUsage","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"crash","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"crashReporter","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"crashReporterIdStorageKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetry"},{"name":"create","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"domain"},{"name":"create","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/worker/simpleWorker"},{"name":"create","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/editorSimpleWorker"},{"name":"create","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/outputLinkComputer"},{"name":"create","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/workbench.web.api"},{"name":"create","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"create","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/electron-browser/textMateWorker"},{"name":"createActionViewItem","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/browser/menuEntryActionViewItem"},{"name":"createAndBindHistoryNavigationWidgetScopedContextKeyService","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/browser/contextScopedHistoryWidget"},{"name":"createAndFillInActionBarActions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/browser/menuEntryActionViewItem"},{"name":"createAndFillInContextMenuActions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/browser/menuEntryActionViewItem"},{"name":"createApiFactoryAndRegisterActors","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.api.impl"},{"name":"createBreadcrumbsPicker","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbsPicker"},{"name":"createBreakpointDecorations","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution"},{"name":"createBrotliCompress","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"createBrotliDecompress","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"createCancelablePromise","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"createCellViewModel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel"},{"name":"createChannelReceiver","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc"},{"name":"createChannelSender","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc"},{"name":"createCipher","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createCipheriv","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createConnection","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"net"},{"name":"createContext","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"vm"},{"name":"createCSSRule","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"createCustomTask","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskConfiguration"},{"name":"createDecipher","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createDecipheriv","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createDecorationsForStackFrame","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/callStackEditorContribution"},{"name":"createDecorator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/decorators"},{"name":"createDecorator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/instantiation"},{"name":"createDeflate","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"createDeflateRaw","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"createDiffEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"createDiffieHellman","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createDiffNavigator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"createECDH","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createEditorFromSearchResult","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"createEditorPagePosition","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorDom"},{"name":"createElement","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/formattedTextRenderer"},{"name":"createError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"createErrorWithActions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errorsWithActions"},{"name":"createExtHostContextProxyIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/proxyIdentifier"},{"name":"createFakeScopedLineTokens","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/modesTestUtils"},{"name":"createFastDomNode","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/fastDomNode"},{"name":"createFileEditorInput","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"createFileIconThemableTreeContainerScope","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/views"},{"name":"createFileSystemProviderError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"createFindMatch","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelSearch"},{"name":"createGunzip","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"createGzip","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"createHash","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createHmac","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createHook","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"async_hooks"},{"name":"createImageBitmap","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"createInflate","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"createInflateRaw","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"createInterface","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"readline"},{"name":"createKeybinding","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keyCodes"},{"name":"createLineMatcher","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"createLineStarts","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase"},{"name":"createLineStartsFast","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase"},{"name":"createMainContextProxyIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/proxyIdentifier"},{"name":"createMatchers","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/textMateScopeMatcher"},{"name":"createMatches","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"createMemoizer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/decorators"},{"name":"createMessageOfType","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionHostProtocol"},{"name":"createMetaElement","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"createMockBuffer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils"},{"name":"createMockSession","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/test/browser/callStack.test"},{"name":"createMockText","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils"},{"name":"createModel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"createMonacoBaseAPI","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneBase"},{"name":"createMonacoEditorAPI","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"createMonacoLanguagesAPI","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"createMouseMoveEventMerger","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/mouseHandler"},{"name":"CreateNewLocalTerminalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/electron-browser/terminalRemote"},{"name":"CreateNewTerminalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"createPrivateKey","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createProxyObject","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"createPublicKey","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createQueuedSender","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/processes"},{"name":"createReadStream","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"createReadStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/io"},{"name":"createRegExp","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"createRequire","kind":"method","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"module"},{"name":"createRequireFromPath","kind":"method","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"module"},{"name":"createResourceExcludeMatcher","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"createRotatingLogger","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/spdlog/index"},{"name":"createRotatingLogger","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/node/spdlogService"},{"name":"createRotatingLoggerAsync","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/spdlog/index"},{"name":"createScanner","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"createScopedLineTokens","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports"},{"name":"createSecretKey","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createSecureContext","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"createSecurePair","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"createSecureServer","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"http2"},{"name":"createSerializedGrid","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/grid"},{"name":"createServer","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"createServer","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"http2"},{"name":"createServer","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"https"},{"name":"createServer","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"net"},{"name":"createServer","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"createSign","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createSimpleKeybinding","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keyCodes"},{"name":"createSingleEditOp","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/browser/commands/shiftCommand.test"},{"name":"createSingleEditOp","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/browser/commands/trimTrailingWhitespaceCommand.test"},{"name":"createSlowExtensionAction","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions"},{"name":"createSocket","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dgram"},{"name":"createStringBuilder","kind":"let","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/stringBuilder"},{"name":"createStubInstance","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"createStyleSheet","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"createSuggestItem","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/test/completionModel.test"},{"name":"createSyncDescriptor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/descriptors"},{"name":"createTerminalEnvironment","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment"},{"name":"createTestCodeEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/browser/testCodeEditor"},{"name":"createTextBuffer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"createTextBufferFactory","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"createTextBufferFactoryFromSnapshot","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"createTextBufferFactoryFromStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"createTextModel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/editorTestUtils"},{"name":"createTextSearchResult","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepSearchUtils"},{"name":"createTOCIterator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/tocTree"},{"name":"createTokenizationSupport","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchLexer"},{"name":"createTracing","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"trace_events"},{"name":"createUintArray","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase"},{"name":"createUnzip","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"createUpdateURL","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/electron-main/abstractUpdateService"},{"name":"createValidator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesValidation"},{"name":"createVerify","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"createWaitMarkerFile","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/waitMarkerFile"},{"name":"createWebWorker","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/webWorker"},{"name":"createWebWorker","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"createWriteStream","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"Credential","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CredentialsContainer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Critical","kind":"enum member","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/SeverityLevel"},{"name":"crypto","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Crypto","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CryptoKey","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CryptoKeyPair","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSS","kind":"module","kindModifiers":"declare","sortText":"4"},{"name":"CSSConditionRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"cssEscape","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/getIconClasses"},{"name":"CSSFontFaceRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSGroupingRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSImportRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSKeyframeRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSKeyframesRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSMediaRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSNamespaceRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSPageRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSRuleList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSStyleDeclaration","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSStyleRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSStyleSheet","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CSSSupportsRule","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ctxCommentEditorFocused","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/simpleCommentEditor"},{"name":"ctxHasSymbols","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/symbolNavigation"},{"name":"ctxReferenceSearchVisible","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesController"},{"name":"CurrentLineHighlightOverlay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight"},{"name":"CurrentLineMarginHighlightOverlay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight"},{"name":"currentSchemaVersion","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/experiments/common/experimentService"},{"name":"currentSessionDateStorageKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetry"},{"name":"Cursor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursor"},{"name":"CursorAtBoundary","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookBrowser"},{"name":"CursorChangeReason","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorEvents"},{"name":"CursorChangeReason","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"CursorCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCollection"},{"name":"CursorColumns","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"CursorConfiguration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"CursorContext","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"CursorModelState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursor"},{"name":"CursorMove","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorMoveCommands"},{"name":"CursorMoveCommands","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorMoveCommands"},{"name":"CursorPosition","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorMoveOperations"},{"name":"CursorRedo","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/cursorUndo/cursorUndo"},{"name":"CursorState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"CursorStateChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursor"},{"name":"cursorStyleToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"cursorTo","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"readline"},{"name":"CursorUndo","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/cursorUndo/cursorUndo"},{"name":"CursorUndoRedoController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/cursorUndo/cursorUndo"},{"name":"CursorWordAccessibilityLeft","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordAccessibilityLeftSelect","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordAccessibilityRight","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordAccessibilityRightSelect","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordEndLeft","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordEndLeftSelect","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordEndRight","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordEndRightSelect","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordLeft","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordLeftSelect","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordPartLeft","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordPartOperations/wordPartOperations"},{"name":"CursorWordPartLeftSelect","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordPartOperations/wordPartOperations"},{"name":"CursorWordPartRight","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordPartOperations/wordPartOperations"},{"name":"CursorWordPartRightSelect","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordPartOperations/wordPartOperations"},{"name":"CursorWordRight","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordRightSelect","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordStartLeft","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordStartLeftSelect","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordStartRight","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CursorWordStartRightSelect","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"CustomEditorContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/browser/customEditors"},{"name":"CustomEditorInfo","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/customEditor"},{"name":"CustomEditorInfoCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/customEditor"},{"name":"CustomEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/browser/customEditorInput"},{"name":"CustomEditorInputFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory"},{"name":"CustomEditorModelManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/customEditorModelManager"},{"name":"CustomEditorPriority","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/customEditor"},{"name":"customEditorsAssociationsSettingId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/browser/editorAssociationsSetting"},{"name":"CustomEditorService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/browser/customEditors"},{"name":"customEditorsExtensionPoint","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/extensionPoint"},{"name":"CustomElementRegistry","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"customElements","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CustomEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"CustomExecution","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CustomExecution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"CustomExecution2","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"CustomExecutionDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"CUSTOMIZED_TASK_TYPE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"CustomMenubarControl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/titlebar/menubarControl"},{"name":"CustomTask","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"CustomTextEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/customTextEditorModel"},{"name":"CustomTreeView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/treeView"},{"name":"cutFileHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"CutWebviewEditorCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/electron-browser/webviewCommands"},{"name":"cwd","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/process"},{"name":"cwd","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"DARK","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"darken","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"DarwinUpdateService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/electron-main/updateService.darwin"},{"name":"data","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/common/filters.perf.data"},{"name":"Data","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/Data"},{"name":"Data","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"Database","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-sqlite3/index"},{"name":"DataBreakpoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DataBreakpoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"DataCue","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DataPoint","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/DataPoint"},{"name":"DataPoint","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"DataPointType","kind":"enum","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/DataPointType"},{"name":"DataPointType","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"DataSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesTree"},{"name":"DataSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree"},{"name":"DataTransfer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DataTransferItem","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DataTransferItemList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DataTransfers","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dnd"},{"name":"DataTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/dataTree"},{"name":"DataUri","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"DataView","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Date","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"debounce","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/decorators"},{"name":"debug","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"debug","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"Debug","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/diff/diff"},{"name":"DEBUG_HELPER_SERVICE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"DEBUG_PANEL_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"DEBUG_SCHEME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"DEBUG_SERVICE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"DebugAdapterExecutable","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DebugAdapterExecutable","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DebugAdapterInlineImplementation","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DebugAdapterInlineImplementation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DebugAdapterServer","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DebugAdapterServer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DebugConsoleMode","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DebugConsoleMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DebugContentProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugContentProvider"},{"name":"debugExceptionWidgetBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/exceptionWidget"},{"name":"debugExceptionWidgetBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/exceptionWidget"},{"name":"DebugExtensionHostAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"debugger","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Debugger","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"Debugger","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugger"},{"name":"debuggersExtPoint","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugSchemas"},{"name":"DebugHoverWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugHover"},{"name":"debugIconContinueForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugIconDisconnectForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugIconPauseForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugIconRestartForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugIconStartForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugIconStepBackForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugIconStepIntoForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugIconStepOutForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugIconStepOverForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugIconStopForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debuglog","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"DebugModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"debugPort","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"DebugProgressContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugProgress"},{"name":"DebugService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugService"},{"name":"DebugSession","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugSession"},{"name":"DebugStatusContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugStatus"},{"name":"DebugTaskRunner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugTaskRunner"},{"name":"DebugToolBar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugToolBarBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"debugToolBarBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugToolBar"},{"name":"DebugViewPaneContainer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugViewlet"},{"name":"Decipher","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"DeclarationProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"declare","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"decode","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"punycode"},{"name":"decode","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"querystring"},{"name":"decode","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/iconv-lite/lib/index"},{"name":"decodeSemanticTokensDto","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/shared/semanticTokensDto"},{"name":"decodeStream","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/iconv-lite/lib/index"},{"name":"decodeURI","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"decodeURIComponent","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"decodeUTF16LE","kind":"let","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/stringBuilder"},{"name":"decodeUTF8","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"decompress","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/compressedObjectTreeModel"},{"name":"Decoration","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Decoration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DecorationRangeBehavior","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DecorationRangeBehavior","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DecorationRangeBehavior","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"DecorationRenderOptions","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"DecorationSegment","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/lineDecorations"},{"name":"DecorationsOverlay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/decorations/decorations"},{"name":"DecorationsOverviewRuler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler"},{"name":"DecorationsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/decorations/browser/decorationsService"},{"name":"DecorationToRender","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin"},{"name":"DecreaseSearchEditorContextLinesCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"DecreaseViewSizeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"DedupOverlay","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin"},{"name":"deepClone","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/objects"},{"name":"deepEqual","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"deepFreeze","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/objects"},{"name":"deepStrictEqual","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"default","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"DEFAULT_COMMANDS_TO_SKIP_SHELL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"DEFAULT_CUSTOM_EDITOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/browser/editorAssociationsSetting"},{"name":"DEFAULT_ECDH_CURVE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"DEFAULT_EDITOR_MAX_DIMENSIONS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editor"},{"name":"DEFAULT_EDITOR_MIN_DIMENSIONS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editor"},{"name":"DEFAULT_EDITOR_PART_OPTIONS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editor"},{"name":"DEFAULT_ENCODING","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"DEFAULT_LABELS_CONTAINER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/labels"},{"name":"DEFAULT_LETTER_SPACING","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"DEFAULT_LINE_HEIGHT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"DEFAULT_LOG_LEVEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"DEFAULT_PRODUCT_ICON_THEME_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/browser/productIconThemeData"},{"name":"DEFAULT_PRODUCT_ICON_THEME_SETTING_VALUE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/themeConfiguration"},{"name":"DEFAULT_SETTINGS_EDITOR_SETTING","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferences"},{"name":"DEFAULT_SETTINGS_EDITOR_SETTING","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"DEFAULT_TERMINAL_OSX","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService"},{"name":"DEFAULT_VALUE","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"DEFAULT_WORD_REGEXP","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/wordHelper"},{"name":"defaultApp","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"defaultBreadcrumbsStyles","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"defaultCipherList","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"defaultClient","kind":"let","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/applicationinsights"},{"name":"DefaultConfigurationExportHelper","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/configExporter/electron-browser/configurationExportHelper"},{"name":"DefaultConfigurationModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationModels"},{"name":"defaultCoreCipherList","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"defaultCustomEditor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors"},{"name":"DefaultDeserializer","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"defaultDialogStyles","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"DefaultElementMapper","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/compressedObjectTreeModel"},{"name":"DefaultEndOfLine","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"DefaultEndOfLine","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"defaultGenerator","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/idGenerator"},{"name":"defaultInsertColor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"defaultKeybindingsContents","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"DefaultKeybindingsEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"DefaultKeyboardNavigationDelegate","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listWidget"},{"name":"defaultListStyles","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"defaultMaxListeners","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"stream"},{"name":"defaultMenuStyles","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/styler"},{"name":"DefaultPaneDndController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/splitview/paneview"},{"name":"DefaultPreferencesEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesEditor"},{"name":"DefaultPreferencesEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesEditorInput"},{"name":"defaultQuickAccessContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/quickaccess"},{"name":"defaultQuickAccessContextKeyValue","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/quickaccess"},{"name":"DefaultQuickAccessFilterValue","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/common/quickAccess"},{"name":"DefaultRawSettingsEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"DefaultRecommendedExtensionsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews"},{"name":"defaultRemoveColor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"DefaultRoleName","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Context"},{"name":"defaultSearchConfig","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization"},{"name":"DefaultSerializer","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"DefaultSettings","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"DefaultSettingsEditorContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesEditor"},{"name":"DefaultSettingsEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"DefaultSettingsHeaderWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesWidgets"},{"name":"DefaultSettingsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesRenderers"},{"name":"defaultSettingsSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"defaultStatus","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DefaultStyleController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listWidget"},{"name":"defaultStyles","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/selectBox/selectBox"},{"name":"DefaultURITransformer","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uriIpc"},{"name":"defaultWebSocketFactory","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/browser/browserSocketFactory"},{"name":"defaultWindowState","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-main/window"},{"name":"DefaultWorkerFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/worker/defaultWorkerFactory"},{"name":"DeferredPermissionRequest","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DeferredPromise","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/common/utils"},{"name":"define","kind":"const","kindModifiers":"declare","sortText":"4"},{"name":"DefineKeybindingController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution"},{"name":"DefineKeybindingOverlayWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/keybindingWidgets"},{"name":"DefineKeybindingWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/keybindingWidgets"},{"name":"defineTheme","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"DefinitionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/goToCommands"},{"name":"DefinitionLink","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"DefinitionProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"deflate","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"deflateRaw","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"deflateRawSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"deflateSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"DelayedDragHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dnd"},{"name":"DelayedPagedModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/paging"},{"name":"Delayer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"DelayNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Delegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesTree"},{"name":"Delegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsList"},{"name":"DelegatedLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"DelegatingEditorService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/browser/editorService"},{"name":"delete","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"DeleteAllLeftAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"DeleteAllRightAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"DeleteCellEdit","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/cellEdit"},{"name":"deleteFileHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"DeleteLinesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"DeleteOperations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorDeleteOperations"},{"name":"deletePassword","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/keytar/keytar"},{"name":"deletePassword","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/keytar/index"},{"name":"DeleteWordCommand","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"DeleteWordEndLeft","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"DeleteWordEndRight","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"DeleteWordLeft","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"DeleteWordLeftCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"DeleteWordPartLeft","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordPartOperations/wordPartOperations"},{"name":"DeleteWordPartRight","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordPartOperations/wordPartOperations"},{"name":"DeleteWordRight","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"DeleteWordRightCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"DeleteWordStartLeft","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"DeleteWordStartRight","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"delimiter","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"delimiter","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"delta","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"DeltaExtensionsResult","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionDescriptionRegistry"},{"name":"departFocus","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"DependsOrder","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"deprecate","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"Deprecated_RemoteAuthorityContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/contextkeys"},{"name":"describe","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"descriptionForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"deserialize","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"deserializeEnvironmentVariableCollection","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/environmentVariableShared"},{"name":"deserializePipePositions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/test/wordTestUtils"},{"name":"Deserializer","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"deserializeSearchError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"desktopCapturer","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"DesktopDragAndDropData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listView"},{"name":"DesktopHostService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/host/electron-browser/desktopHostService"},{"name":"DESTRUCTION","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"detect","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/jschardet/index"},{"name":"detectAvailableShells","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/node/terminal"},{"name":"detectEncodingByBOM","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/node/encoding/encoding.test"},{"name":"detectEncodingByBOMFromBuffer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"detectEncodingFromBuffer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"DetectIndentation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"detectModeId","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/getIconClasses"},{"name":"DeviceAcceleration","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DeviceLightEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DeviceMotionEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DeviceOrientationEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"devicePixelRatio","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DeviceRotationRate","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DH_CHECK_P_NOT_PRIME","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"DH_CHECK_P_NOT_SAFE_PRIME","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"DH_NOT_SUITABLE_GENERATOR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"DH_UNABLE_TO_CHECK_GENERATOR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"Diagnostic","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Diagnostic","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"Diagnostic","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"DiagnosticCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDiagnostics"},{"name":"DiagnosticRelatedInformation","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DiagnosticRelatedInformation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DiagnosticRelatedInformation","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"DiagnosticsChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/diagnostics/node/diagnosticsIpc"},{"name":"DiagnosticSeverity","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DiagnosticSeverity","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DiagnosticSeverity","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"DiagnosticsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/diagnostics/node/diagnosticsService"},{"name":"DiagnosticsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/diagnostics/node/diagnosticsIpc"},{"name":"DiagnosticTag","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DiagnosticTag","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DiagnosticTag","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"dialog","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"Dialog","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/dialog/dialog"},{"name":"DialogChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/dialogs/electron-browser/dialogIpc"},{"name":"DialogMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/dialogs/electron-main/dialogs"},{"name":"DialogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/browser/dialogService"},{"name":"DialogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/electron-browser/dialogService"},{"name":"didBindWorkbenchListAutomaticKeyboardNavigation","kind":"let","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"DidChangeContentEmitter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"DidChangeDecorationsEmitter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"didUseCachedData","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/timer/electron-browser/timerService"},{"name":"diff","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"diff","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"DiffAPICommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/apiCommands"},{"name":"diffBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"DiffChange","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/diff/diffChange"},{"name":"DiffComputer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/diff/diffComputer"},{"name":"DiffEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor/diffEditorInput"},{"name":"DiffEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor/diffEditorModel"},{"name":"DiffEditorState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorBrowser"},{"name":"DiffEditorWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/widget/diffEditorWidget"},{"name":"DiffieHellman","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"diffInserted","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"diffInsertedOutline","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"DiffNavigator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/widget/diffNavigator"},{"name":"diffRemoved","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"diffRemovedOutline","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"DiffReview","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/widget/diffReview"},{"name":"Dimension","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"dir","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"Dir","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"Direction","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/menu/menu"},{"name":"Direction","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/grid"},{"name":"Direction","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminal"},{"name":"Dirent","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"dirExists","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"dirname","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"dirname","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"dirname","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"DirtyDiffController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"DirtyDiffModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"DirtyDiffWorkbenchController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"DirtyEditorContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"DirtyFilesIndicator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/dirtyFilesIndicator"},{"name":"DirtyWorkingCopiesContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"dirxml","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"DisableAllAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"DisableAllBreakpointsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"DisableAllWorkspaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"DisableAutoUpdateAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"DISABLED_EXTENSIONS_STORAGE_PATH","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"DisabledExtensionsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews"},{"name":"DisableDropDownAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"DisableForWorkspaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"DisableGloballyAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"disconnect","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"disconnect","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"DISCONNECT_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"DISCONNECT_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"DiskFileSystemProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/diskFileSystemProvider"},{"name":"DiskFileSystemProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/electron-browser/diskFileSystemProvider"},{"name":"DiskSearch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/searchService"},{"name":"DispatchConfig","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/dispatchConfig"},{"name":"dispatchEvent","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"Disposable","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Disposable","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/lifecycle"},{"name":"Disposable","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"Disposable","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/workbench.web.api"},{"name":"DisposableStore","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/lifecycle"},{"name":"disposableTimeout","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"dispose","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/lifecycle"},{"name":"dispose","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/applicationinsights"},{"name":"disposed","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"distinct","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"distinct","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/objects"},{"name":"distinctES6","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"distinctParents","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"do","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"doBenchmark","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/benchmark/benchmarkUtils"},{"name":"Dock","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"document","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Document","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"documentationExtensionPointDescriptor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeActions/common/documentationExtensionPoint"},{"name":"DocumentationExtensionPointFields","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeActions/common/documentationExtensionPoint"},{"name":"DocumentFormattingEditProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"DocumentFragment","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DocumentHighlight","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DocumentHighlight","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DocumentHighlight","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"DocumentHighlightKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DocumentHighlightKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"DocumentHighlightKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DocumentHighlightKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"DocumentHighlightProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"DocumentLink","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DocumentLink","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DocumentLink","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"DocumentRangeFormattingEditProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"DocumentRangeSemanticTokensAdapter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostLanguageFeatures"},{"name":"DocumentRangeSemanticTokensProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"DocumentSemanticTokensAdapter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostLanguageFeatures"},{"name":"DocumentSemanticTokensProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"DocumentSymbol","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"DocumentSymbol","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"DocumentSymbol","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"DocumentSymbolProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"DocumentTimeline","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DocumentType","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"doesNotReject","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"doesNotThrow","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"domain","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"Domain","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"domain"},{"name":"Domain","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/Domain"},{"name":"Domain","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"domainSupportsProperties","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Constants"},{"name":"domainToASCII","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"url"},{"name":"domainToUnicode","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"url"},{"name":"domContentLoaded","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"DOMError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"domEvent","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/event"},{"name":"DOMException","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMImplementation","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMLineBreaksComputerFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/domLineBreaksComputer"},{"name":"DOMMatrix","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMMatrixReadOnly","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMParser","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMPoint","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMPointReadOnly","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMQuad","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DomReadingContext","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/lines/viewLine"},{"name":"DOMRect","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMRectList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMRectReadOnly","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DomScrollableElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/scrollableElement"},{"name":"DOMSettableTokenList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMStringList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMStringMap","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DOMTokenList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"doNotTrack","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"dosDateTimeToDate","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/yauzl/index"},{"name":"DOWNLOAD_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"DOWNLOAD_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"DownloadItem","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"DownloadService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/download/common/downloadService"},{"name":"DownloadServiceChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/download/common/downloadIpc"},{"name":"DownloadServiceChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/download/common/downloadIpc"},{"name":"DragAndDropCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/dnd/dragAndDropCommand"},{"name":"DragAndDropController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/dnd/dnd"},{"name":"DragAndDropData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dnd"},{"name":"DragAndDropObserver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"DragEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DraggedCompositeIdentifier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"DraggedEditorGroupIdentifier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"DraggedEditorIdentifier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"DraggedViewIdentifier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"DragMouseEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/mouseEvent"},{"name":"Driver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/electron-main/driver"},{"name":"DriverChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/node/driver"},{"name":"DriverChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/node/driver"},{"name":"Dropdown","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/dropdown/dropdown"},{"name":"DropdownMenu","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/dropdown/dropdown"},{"name":"DropdownMenuActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/dropdown/dropdown"},{"name":"DropDownMenuActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"Duplex","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"stream"},{"name":"DuplicateSelectionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"DuplicateWorkspaceInNewWindowAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceActions"},{"name":"DynamicsCompressorNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"DynamicStandaloneServices","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneServices"},{"name":"DynamicViewOverlay","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/dynamicViewOverlay"},{"name":"DynamicWebviewEditorOverlay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay"},{"name":"DynamicWorkspaceRecommendations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations"},{"name":"E2BIG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EACCES","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EADDRINUSE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EADDRNOTAVAIL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EAFNOSUPPORT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EAGAIN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EALREADY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EBADF","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EBADMSG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EBUSY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ECANCELED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ECDH","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"ECHILD","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ECONNABORTED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ECONNREFUSED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ECONNRESET","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EDEADLK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EDESTADDRREQ","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"edit","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/content"},{"name":"EditableConfigurationTarget","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configurationEditingService"},{"name":"EditOperation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/editOperation"},{"name":"EditOperationResult","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"EditOperationType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"editor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"EDITOR_BOTTOM_PADDING","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"EDITOR_CONTRIBUTION_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"EDITOR_DRAG_AND_DROP_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EDITOR_FONT_DEFAULTS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"EDITOR_GROUP_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EDITOR_GROUP_EMPTY_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EDITOR_GROUP_FOCUSED_EMPTY_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EDITOR_GROUP_HEADER_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EDITOR_GROUP_HEADER_TABS_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EDITOR_GROUP_HEADER_TABS_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EDITOR_MODEL_DEFAULTS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"EDITOR_PANE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EDITOR_TITLE_HEIGHT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editor"},{"name":"EDITOR_TOOLBAR_HEIGHT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"EDITOR_TOP_MARGIN","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"EDITOR_TOP_PADDING","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"EditorAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"EditorActivation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/editor/common/editor"},{"name":"editorActiveIndentGuides","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorActiveLineNumber","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorActiveLinkForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"EditorAreaVisibleContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"editorAssociationsConfigurationNode","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/browser/editorAssociationsSetting"},{"name":"EditorAutoIndentStrategy","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"EditorAutoIndentStrategy","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"EditorAutoSave","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorAutoSave"},{"name":"editorBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorBracketMatchBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorBracketMatchBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"EditorBreadcrumbsModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbsModel"},{"name":"editorCodeLensForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"EditorCommand","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"EditorCommandsContextActionRunner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"editorConfiguration","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeActions/common/codeActionsContribution"},{"name":"editorConfigurationBaseNode","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/commonEditorConfig"},{"name":"EditorContextKeys","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/editorContextKeys"},{"name":"EditorControl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorControl"},{"name":"editorCursorBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorCursorForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"EditorDescriptor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/editor"},{"name":"EditorDropTarget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorDropTarget"},{"name":"editorErrorBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorErrorForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"EditorExtensionsRegistry","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"editorFindMatch","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorFindMatchBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorFindMatchHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorFindMatchHighlightBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorFindRangeHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorFindRangeHighlightBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"EditorFontLigatures","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"editorForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"EditorGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor/editorGroup"},{"name":"EditorGroupActiveEditorDirtyContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"EditorGroupEditorsCountContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"editorGroupToViewColumn","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/shared/editor"},{"name":"EditorGroupView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorGroupView"},{"name":"editorGutter","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorGutterAddedBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"editorGutterDeletedBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"editorGutterModifiedBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"editorHintBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorHintForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorHoverBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorHoverBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorHoverForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorHoverHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorHoverStatusBarBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorInactiveSelection","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorIndentGuides","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorInfoBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorInfoForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"EditorInput","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"EditorKeybindingCancellationTokenSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/core/keybindingCancellation"},{"name":"EditorLayoutInfoComputer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"EditorLayoutSingleAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"EditorLayoutThreeColumnsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"EditorLayoutThreeRowsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"EditorLayoutTwoByTwoGridAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"EditorLayoutTwoColumnsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"EditorLayoutTwoColumnsBottomAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"EditorLayoutTwoRowsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"EditorLayoutTwoRowsRightAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"editorLightBulbAutoFixForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorLightBulbForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorLineHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorLineHighlightBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorLineNumbers","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorMarkerNavigationBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoError/gotoErrorWidget"},{"name":"editorMarkerNavigationError","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoError/gotoErrorWidget"},{"name":"editorMarkerNavigationInfo","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoError/gotoErrorWidget"},{"name":"editorMarkerNavigationWarning","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoError/gotoErrorWidget"},{"name":"editorMatchesToTextSearchResults","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/searchHelpers"},{"name":"EditorMemento","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/baseEditor"},{"name":"EditorModeContext","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/widget/codeEditorWidget"},{"name":"EditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"EditorModesRegistry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/modesRegistry"},{"name":"EditorMouseEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorDom"},{"name":"EditorMouseEventFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorDom"},{"name":"EditorOpenContext","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/editor/common/editor"},{"name":"EditorOption","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"EditorOption","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"EditorOptions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"EditorOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"editorOptionsRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"editorOverviewRulerBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"EditorPagePosition","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorDom"},{"name":"EditorPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorPart"},{"name":"EditorPinnedContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"EditorPointerEventFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorDom"},{"name":"EditorProgressIndicator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/progress/browser/progressIndicator"},{"name":"editorRangeHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorRangeHighlightBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorRuler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"EditorScopedQuickInputServiceImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputServiceImpl"},{"name":"EditorScroll_","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/coreCommands"},{"name":"EditorScrollbar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar"},{"name":"editorSelectionBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorSelectionForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorSelectionHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorSelectionHighlightBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"EditorService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/browser/editorService"},{"name":"EditorSimpleWorker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/editorSimpleWorker"},{"name":"EditorsObserver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorsObserver"},{"name":"EditorsOrder","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"EditorState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/core/editorState"},{"name":"EditorStateCancellationTokenSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/core/editorState"},{"name":"EditorStatus","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorStatus"},{"name":"editorSuggestWidgetBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestWidget"},{"name":"editorSuggestWidgetBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestWidget"},{"name":"editorSuggestWidgetForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestWidget"},{"name":"editorSuggestWidgetHighlightForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestWidget"},{"name":"editorSuggestWidgetSelectedBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestWidget"},{"name":"EditorsVisibleContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"editorSymbolHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorSymbolHighlightBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"EditorTheme","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewContext"},{"name":"EditorType","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/editorCommon"},{"name":"editorUnnecessaryCodeBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorUnnecessaryCodeOpacity","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"EditorWalkThroughAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough"},{"name":"EditorWalkThroughInputFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough"},{"name":"editorWarningBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorWarningForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"EditorWhitespace","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/linesLayout"},{"name":"editorWhitespaces","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"editorWidgetBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorWidgetBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorWidgetForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"editorWidgetResizeBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"EditorWorkerClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/editorWorkerServiceImpl"},{"name":"EditorWorkerHost","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/editorWorkerServiceImpl"},{"name":"EditorWorkerServiceImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/editorWorkerServiceImpl"},{"name":"EditorZoom","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorZoom"},{"name":"EditPreferenceWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesWidgets"},{"name":"EditStack","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/editStack"},{"name":"EDOM","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EEXIST","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EFAULT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EFBIG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EHOSTUNREACH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EIDRM","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EILSEQ","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EINPROGRESS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EINTR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EINVAL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EIO","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EISCONN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EISDIR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"electron","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"Electron","kind":"module","kindModifiers":"declare","sortText":"4"},{"name":"ElectronAcceleratorLabelProvider","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keybindingLabels"},{"name":"ElectronMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/electron/electron-main/electronMainService"},{"name":"ElectronService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/electron/electron-browser/electronService"},{"name":"ElectronURLListener","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/url/electron-main/electronUrlListener"},{"name":"ElectronWebviewBasedWebview","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/electron-browser/webviewElement"},{"name":"ElectronWebviewService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/electron-browser/webviewService"},{"name":"Element","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ElementsDragAndDropData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listView"},{"name":"ElementSizeObserver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/config/elementSizeObserver"},{"name":"ELOOP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"else","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"EmbeddedCodeEditorWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/widget/embeddedCodeEditorWidget"},{"name":"EmbeddedDiffEditorWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/widget/embeddedCodeEditorWidget"},{"name":"embeddedEditorBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart"},{"name":"EMFILE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"emit","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"emit","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"emitKeypressEvents","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"readline"},{"name":"Emitter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/event"},{"name":"Emitter","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/workbench.web.api"},{"name":"Emitter","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"emitWarning","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"EMLINK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EmmetEditorAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/emmet/browser/emmetActions"},{"name":"empty","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"EmptyExtension","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostExtensionActivator"},{"name":"EmptyPane","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/scmViewlet"},{"name":"EmptyPaneDescriptor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/scmViewlet"},{"name":"emptyProgressRunner","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/progress/common/progress"},{"name":"EmptyView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/emptyView"},{"name":"EMSGSIZE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EnableAllAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"EnableAllBreakpointsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"EnableAllWorkspaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"EnableAutoUpdateAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ENABLED_EXTENSIONS_STORAGE_PATH","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"enableDebug","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/jschardet/index"},{"name":"EnabledExtensionsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews"},{"name":"EnableDropDownAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"EnableForWorkspaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"EnableGloballyAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"Enablement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"EnablementState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/common/extensionManagement"},{"name":"enablePromiseAPIs","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"ENAMETOOLONG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"encode","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"punycode"},{"name":"encode","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"querystring"},{"name":"encode","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/iconv-lite/lib/index"},{"name":"EncodedTokenizationSupport2Adapter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"encodeSemanticTokensDto","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/shared/semanticTokensDto"},{"name":"encodeStream","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/iconv-lite/lib/index"},{"name":"encodeStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"encodeURI","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"encodeURIComponent","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"encodeUTF8","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"encodingExists","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/iconv-lite/lib/index"},{"name":"encodingExists","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"EncodingMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"EncodingOracle","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService"},{"name":"endianness","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"EndOfLine","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"EndOfLine","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"EndOfLine","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"EndOfLinePreference","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"EndOfLinePreference","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"EndOfLineSequence","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"EndOfLineSequence","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"endsWith","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"ENETDOWN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENETRESET","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENETUNREACH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENFILE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"Engine","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/fileSearch"},{"name":"ENGINE_METHOD_ALL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_CIPHERS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_DH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_DIGESTS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_DSA","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_ECDH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_ECDSA","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_NONE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_PKEY_ASN1_METHS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_PKEY_METHS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_RAND","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_RSA","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENGINE_METHOD_STORE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOBUFS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENODATA","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENODEV","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOENT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOEXEC","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOLCK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOLINK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOMEM","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOMSG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOPROTOOPT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOSPC","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOSR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOSTR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOSYS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOTCONN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOTDIR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOTEMPTY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOTSOCK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOTSUP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ENOTTY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ensureFileSystemProviderError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"ensureValidWordDefinition","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/wordHelper"},{"name":"Entry","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/yauzl/index"},{"name":"enum","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"env","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"env","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/process"},{"name":"env","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"ENV_azurePrefix","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Config"},{"name":"ENV_http_proxy","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Config"},{"name":"ENV_https_proxy","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Config"},{"name":"ENV_iKey","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Config"},{"name":"ENV_profileQueryEndpoint","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Config"},{"name":"Envelope","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/Envelope"},{"name":"Envelope","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"EnvironmentService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/environmentService"},{"name":"EnvironmentVariableCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTerminalService"},{"name":"EnvironmentVariableMutatorType","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"EnvironmentVariableMutatorType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/environmentVariable"},{"name":"EnvironmentVariableMutatorType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"EnvironmentVariableService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/environmentVariableService"},{"name":"ENXIO","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EOF","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"EOL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"EOPNOTSUPP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EOVERFLOW","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EPERM","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EPIPE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EPROTO","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EPROTONOSUPPORT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EPROTOTYPE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"eq","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"equal","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"equals","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"equals","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/objects"},{"name":"equalsIgnoreCase","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"ERANGE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"EROFS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"error","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"Error","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Error","kind":"enum member","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/SeverityLevel"},{"name":"ErrorEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ErrorEvent","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/errorTelemetry"},{"name":"errorForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"errorHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"ErrorHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"ErrorScope","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"ErrorTelemetry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/browser/errorTelemetry"},{"name":"ErrorTelemetry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/node/errorTelemetry"},{"name":"escape","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"escape","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"querystring"},{"name":"escape","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"escapeCodicons","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/codicons"},{"name":"escapeNonWindowsPath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment"},{"name":"escapeRegExpCharacters","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"ESPIPE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ESRCH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"etag","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"ETAG_DISABLED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"ETIME","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ETIMEDOUT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ETXTBSY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"eval","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"EvalError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"EvaluatableExpression","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"EvaluatableExpression","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"EvaluatableExpression","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"EvaluatableExpressionProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"event","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Event","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Event","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/event"},{"name":"Event","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/workbench.web.api"},{"name":"EventBufferer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/event"},{"name":"EventData","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/EventData"},{"name":"EventData","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"EventEmitter","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"EventEmitter","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"events"},{"name":"EventHelper","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"EventMultiplexer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/event"},{"name":"eventNames","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"eventNames","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"EventSource","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"EventTarget","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"EventType","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"EventType","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/touch"},{"name":"EWOULDBLOCK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"exception","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"ExceptionBreakpoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"ExceptionData","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/ExceptionData"},{"name":"ExceptionData","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"ExceptionDetails","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/ExceptionDetails"},{"name":"ExceptionDetails","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"ExceptionWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/exceptionWidget"},{"name":"ExcludePatternInputWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/patternInputWidget"},{"name":"ExcludeSettingWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"EXDEV","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"ExeBasedRecommendations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations"},{"name":"exec","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"child_process"},{"name":"exec","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/sudo-prompt/index"},{"name":"execArgv","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"execFile","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"child_process"},{"name":"execFileSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"child_process"},{"name":"execPath","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"execSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"child_process"},{"name":"ExecutableDebugAdapter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/node/debugAdapter"},{"name":"ExecuteCellAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/contrib/notebookActions"},{"name":"ExecuteCommandAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"ExecuteCommandAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"executionAsyncId","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"async_hooks"},{"name":"ExecutionEngine","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"ExecutionEngine","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskConfiguration"},{"name":"exists","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"exists","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"existsSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"exit","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/native-watchdog/index"},{"name":"exit","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"exitCode","kind":"property","kindModifiers":"declare,optional","sortText":"5","hasAction":true,"source":"process"},{"name":"EXPAND_NOTIFICATION","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"ExpandAllAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"ExpandNotificationAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsActions"},{"name":"expectation","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"ExperimentActionType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/experiments/common/experimentService"},{"name":"experimental","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/colorRegistry.releaseTest"},{"name":"ExperimentalPrompts","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/experiments/browser/experimentalPrompt"},{"name":"ExperimentalRecommendations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/experimentalRecommendations"},{"name":"ExperimentService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/experiments/common/experimentService"},{"name":"ExperimentState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/experiments/common/experimentService"},{"name":"ExplorerCompressedFirstFocusContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerCompressedFocusContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerCompressedLastFocusContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerCompressionDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerViewer"},{"name":"ExplorerDataSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerViewer"},{"name":"ExplorerDecorationsProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerDecorationsProvider"},{"name":"ExplorerDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerViewer"},{"name":"ExplorerFocusCondition","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerFocusedContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerFolderContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/explorerModel"},{"name":"ExplorerModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/explorerModel"},{"name":"ExplorerResourceCut","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerResourceMoveableToTrash","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerResourceNotReadonlyContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerResourceReadonlyContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerRootContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"explorerRootErrorEmitter","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerViewer"},{"name":"ExplorerService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/explorerService"},{"name":"ExplorerView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerView"},{"name":"ExplorerViewletViewsContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/explorerViewlet"},{"name":"ExplorerViewletVisibleContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"ExplorerViewPaneContainer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/explorerViewlet"},{"name":"export","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"exportEntries","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/performance"},{"name":"exports","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Expression","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"ExpressionContainer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"EXT_HOST_CREATION_DELAY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"extends","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"EXTENSION_BADGE_REMOTE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EXTENSION_BADGE_REMOTE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"EXTENSION_IDENTIFIER_PATTERN","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"EXTENSION_IDENTIFIER_REGEX","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"EXTENSION_SETTING_TAG","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"ExtensionAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ExtensionActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ExtensionActivationProgress","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActivationProgress"},{"name":"ExtensionActivationTimes","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostExtensionActivator"},{"name":"ExtensionActivationTimesBuilder","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostExtensionActivator"},{"name":"extensionButtonProminentBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"extensionButtonProminentForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"extensionButtonProminentHoverBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ExtensionContainers","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"ExtensionData","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/workbenchThemeService"},{"name":"ExtensionData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewer"},{"name":"ExtensionDependencyChecker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsDependencyChecker"},{"name":"ExtensionDescriptionRegistry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionDescriptionRegistry"},{"name":"ExtensionDropDownAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ExtensionEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionEditor"},{"name":"ExtensionEditorDropDownAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ExtensionEnablementService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/common/extensionEnablementService"},{"name":"ExtensionGalleryService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionGalleryService"},{"name":"ExtensionHostDebugBroadcastChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/debug/common/extensionHostDebugIpc"},{"name":"ExtensionHostDebugChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/debug/common/extensionHostDebugIpc"},{"name":"ExtensionHostDebugService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/electron-browser/extensionHostDebugService"},{"name":"ExtensionHostLogFileName","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensions"},{"name":"ExtensionHostMain","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionHostMain"},{"name":"ExtensionHostPersistentConnection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"ExtensionHostProcessManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionHostProcessManager"},{"name":"ExtensionHostProcessWorker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/electron-browser/extensionHost"},{"name":"ExtensionHostProfiler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler"},{"name":"ExtensionHostProfileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService"},{"name":"ExtensionIdentifier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensions"},{"name":"ExtensionIdentifierWithVersion","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementUtil"},{"name":"ExtensionKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ExtensionKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ExtensionManagementChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementIpc"},{"name":"ExtensionManagementChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementIpc"},{"name":"ExtensionManagementError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/node/extensionManagementService"},{"name":"ExtensionManagementServerService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService"},{"name":"ExtensionManagementServerService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService"},{"name":"ExtensionManagementService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/common/extensionManagementService"},{"name":"ExtensionManagementService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/node/extensionManagementService"},{"name":"ExtensionManagementService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/node/extensionManagementService"},{"name":"ExtensionMemento","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostMemento"},{"name":"ExtensionMessageCollector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsRegistry"},{"name":"ExtensionPackCountWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWidgets"},{"name":"ExtensionPoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsRegistry"},{"name":"ExtensionPointContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensions"},{"name":"ExtensionPoints","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/extensionHost.contribution"},{"name":"ExtensionPoints","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/configExporter/electron-browser/configurationExportHelper.contribution"},{"name":"ExtensionPointUserDelta","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsRegistry"},{"name":"ExtensionRecommendationReason","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/common/extensionManagement"},{"name":"ExtensionRecommendations","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionRecommendations"},{"name":"ExtensionRecommendationsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService"},{"name":"ExtensionRegistryReporter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"ExtensionRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewer"},{"name":"ExtensionResourceLoaderService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionResourceLoader/electron-browser/extensionResourceLoaderService"},{"name":"extensionResultIsMatch","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/textSearchManager"},{"name":"extensionResultIsMatch","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/api/extHostSearch.test"},{"name":"extensions","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/jsonschemas/common/jsonContributionRegistry"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/keybindingsRegistry"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/common/quickAccess"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/modesRegistry"},{"name":"Extensions","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/contributions"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"Extensions","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/views"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/output/common/output"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/actions"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/viewlet"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/panel"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/editor"},{"name":"Extensions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/iconRegistry"},{"name":"EXTENSIONS_CONFIG","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"ExtensionsActivator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostExtensionActivator"},{"name":"ExtensionsAutoProfiler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler"},{"name":"ExtensionScanner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/node/extensionPoints"},{"name":"ExtensionScannerInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/node/extensionPoints"},{"name":"ExtensionsChannelId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"ExtensionsConfigurationInitialContent","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensionsFileTemplate"},{"name":"ExtensionsConfigurationSchema","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensionsFileTemplate"},{"name":"ExtensionsConfigurationSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensionsFileTemplate"},{"name":"ExtensionScriptApis","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ExtensionService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/browser/extensionService"},{"name":"ExtensionService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/electron-browser/extensionService"},{"name":"ExtensionsGridView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewer"},{"name":"ExtensionsInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensionsInput"},{"name":"ExtensionsLabel","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"ExtensionsLifecycle","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/node/extensionLifecycle"},{"name":"ExtensionsListView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews"},{"name":"ExtensionsManifestCache","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/node/extensionsManifestCache"},{"name":"ExtensionsRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsRegistry"},{"name":"ExtensionsRegistryImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsRegistry"},{"name":"ExtensionsSynchroniser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/extensionsSync"},{"name":"ExtensionState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"ExtensionStoragePaths","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostStoragePaths"},{"name":"ExtensionsTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewer"},{"name":"ExtensionsViewletViewsContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewlet"},{"name":"ExtensionsViewPaneContainer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewlet"},{"name":"ExtensionsWorkbenchService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService"},{"name":"ExtensionTipsChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementIpc"},{"name":"ExtensionTipsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/node/extensionTipsService"},{"name":"ExtensionToolTipAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ExtensionType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensions"},{"name":"ExtensionWidget","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWidgets"},{"name":"external","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"External","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ExternalElementsDragAndDropData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listView"},{"name":"ExternalThemeTrieElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/tokenization"},{"name":"ExternalUriResolverContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/url/common/externalUriResolver"},{"name":"ExtHostApiCommands","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostApiCommands"},{"name":"ExtHostApiDeprecationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostApiDeprecationService"},{"name":"ExtHostAuthentication","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostAuthentication"},{"name":"ExtHostCell","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostNotebook"},{"name":"ExtHostClipboard","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostClipboard"},{"name":"ExtHostCommands","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostCommands"},{"name":"ExtHostComments","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostComments"},{"name":"ExtHostCommentThread","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostComments"},{"name":"ExtHostConfigProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostConfiguration"},{"name":"ExtHostConfiguration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostConfiguration"},{"name":"ExtHostContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"extHostCustomer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostCustomers"},{"name":"ExtHostCustomersRegistry","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostCustomers"},{"name":"ExtHostDebugConsole","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDebugService"},{"name":"ExtHostDebugService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostDebugService"},{"name":"ExtHostDebugServiceBase","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDebugService"},{"name":"ExtHostDebugSession","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDebugService"},{"name":"ExtHostDecorations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDecorations"},{"name":"ExtHostDiagnostics","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDiagnostics"},{"name":"ExtHostDialogs","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDialogs"},{"name":"ExtHostDocumentContentProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDocumentContentProviders"},{"name":"ExtHostDocumentData","kind":"alias","kindModifiers":"","sortText":"0"},{"name":"ExtHostDocumentLine","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDocumentData"},{"name":"ExtHostDocuments","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDocuments"},{"name":"ExtHostDocumentsAndEditors","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDocumentsAndEditors"},{"name":"ExtHostDocumentSaveParticipant","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDocumentSaveParticipant"},{"name":"ExtHostDownloadService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostDownloadService"},{"name":"ExtHostEditorInsets","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostCodeInsets"},{"name":"ExtHostEditors","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTextEditors"},{"name":"ExtHostExtensionService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostExtensionService"},{"name":"ExtHostExtensionService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/worker/extHostExtensionService"},{"name":"ExtHostFileSystem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostFileSystem"},{"name":"ExtHostFileSystemEventService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostFileSystemEventService"},{"name":"ExtHostLabelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostLabelService"},{"name":"ExtHostLanguageFeatures","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostLanguageFeatures"},{"name":"ExtHostLanguages","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostLanguages"},{"name":"extHostLogChannelId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/common/logConstants"},{"name":"ExtHostLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostLogService"},{"name":"ExtHostLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/worker/extHostLogService"},{"name":"ExtHostMessageService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostMessageService"},{"name":"extHostNamedCustomer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostCustomers"},{"name":"ExtHostNotebookController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostNotebook"},{"name":"ExtHostNotebookDocument","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostNotebook"},{"name":"ExtHostNotebookEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostNotebook"},{"name":"ExtHostNotebookOutputRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostNotebook"},{"name":"ExtHostOutputChannelBackedByFile","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostOutputService"},{"name":"ExtHostOutputService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostOutput"},{"name":"ExtHostOutputService2","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostOutputService"},{"name":"ExtHostProgress","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostProgress"},{"name":"ExtHostPseudoterminal","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTerminalService"},{"name":"ExtHostPushOutputChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostOutput"},{"name":"ExtHostQuickOpen","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostQuickOpen"},{"name":"ExtHostRpcService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostRpcService"},{"name":"ExtHostSCM","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostSCM"},{"name":"ExtHostSCMInputBox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostSCM"},{"name":"ExtHostSearch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostSearch"},{"name":"ExtHostStatusBar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostStatusBar"},{"name":"ExtHostStatusBarEntry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostStatusBar"},{"name":"ExtHostStorage","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostStorage"},{"name":"ExtHostTask","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostTask"},{"name":"ExtHostTaskBase","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"ExtHostTerminal","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTerminalService"},{"name":"ExtHostTerminalService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostTerminalService"},{"name":"ExtHostTextEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTextEditor"},{"name":"ExtHostTextEditorOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTextEditor"},{"name":"ExtHostTheming","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTheming"},{"name":"ExtHostTimeline","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTimeline"},{"name":"ExtHostTreeViews","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTreeViews"},{"name":"ExtHostTunnelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTunnelService"},{"name":"ExtHostTunnelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostTunnelService"},{"name":"ExtHostUrls","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostUrls"},{"name":"ExtHostVariableResolverService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDebugService"},{"name":"ExtHostWebview","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostWebview"},{"name":"ExtHostWebviewEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostWebview"},{"name":"ExtHostWebviews","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostWebview"},{"name":"ExtHostWindow","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostWindow"},{"name":"ExtHostWorkspace","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostWorkspace"},{"name":"extname","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"extname","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"extname","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"extract","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/zip"},{"name":"ExtractError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/zip"},{"name":"extractLocalHostUriMetaDataForPortMapping","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/tunnel"},{"name":"extractRangeFromFilter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/search"},{"name":"extractResources","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"extractSearchQueryFromLines","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization"},{"name":"extractSearchQueryFromModel","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization"},{"name":"F_OK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"fail","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"FailedExtension","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostExtensionActivator"},{"name":"fakeServer","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"fakeServerWithClock","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"FakeXMLHttpRequest","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"FALLBACK_MAX_MEMORY_SIZE_MB","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/files"},{"name":"false","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"FastDomNode","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/fastDomNode"},{"name":"fchmod","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"fchmodSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"fchown","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"fchownSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"fdatasync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"fdatasyncSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"features","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"FeedbackDropdown","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/feedback/browser/feedback"},{"name":"FeedbackStatusbarConribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem"},{"name":"fetch","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"FetchFileSystemProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/browser/webWorkerFileSystemProvider"},{"name":"File","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"FILE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"FILE_EDITOR_INPUT_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"FileBasedRecommendations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations"},{"name":"FileChangesEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FileChangeType","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"FileChangeType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FileChangeType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"FileChangeType","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/workbench.web.api"},{"name":"FileCopiedContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"FileDialogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/browser/fileDialogService"},{"name":"FileDialogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/electron-browser/fileDialogService"},{"name":"FileDragAndDrop","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerViewer"},{"name":"FileEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/editors/fileEditorInput"},{"name":"FileElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbsModel"},{"name":"FileElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"FileElementRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"fileExists","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"FileFocusKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"FileIconThemeData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/browser/fileIconThemeData"},{"name":"FileKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FileList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"FileLocationKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"FileLoggerService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/fileLogService"},{"name":"FileLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/fileLogService"},{"name":"FileMatch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"FileMatch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchModel"},{"name":"FileMatchOrFolderMatchFocusKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"FileMatchOrFolderMatchWithResourceFocusKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"FileMatchOrMatchFocusKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"FileMatchRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchResultsView"},{"name":"FileOperation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FileOperationError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FileOperationEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FileOperationResult","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FilePreview","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/referencesModel"},{"name":"FileQueryCacheState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/cacheState"},{"name":"FileReader","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"FileReferences","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/referencesModel"},{"name":"FileReferencesRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesTree"},{"name":"FileResourceMarkersRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersTreeViewer"},{"name":"FILES_ASSOCIATIONS_CONFIG","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FILES_EXCLUDE_CONFIG","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FilesConfigurationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService"},{"name":"FileSearchManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/fileSearchManager"},{"name":"FileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/fileService"},{"name":"FilesExplorerFocusCondition","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"FilesExplorerFocusedContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"FilesFilter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerViewer"},{"name":"FileSorter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbsPicker"},{"name":"FileSorter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerViewer"},{"name":"FilesRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerViewer"},{"name":"FileStorage","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/state/node/stateService"},{"name":"FileStorageDatabase","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/browser/storageService"},{"name":"FileSystemError","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"FileSystemError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"FileSystemProviderCapabilities","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FileSystemProviderCapabilities","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/workbench.web.api"},{"name":"FileSystemProviderError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FileSystemProviderErrorCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"FileThemeIcon","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"FileType","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"FileType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"fileURLToPath","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"url"},{"name":"FileUserDataProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/userData/common/fileUserDataProvider"},{"name":"FileWalker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/fileSearch"},{"name":"FileWatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/unix/watcherService"},{"name":"FileWatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/win32/watcherService"},{"name":"FileWatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/nsfw/watcherService"},{"name":"FileWatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/nodejs/watcherService"},{"name":"fillResourceDataTransfers","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"Filter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersTreeViewer"},{"name":"FilteredMatchesRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesRenderers"},{"name":"FilterOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersFilterOptions"},{"name":"filtersAction","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/types"},{"name":"filterValidationDecorations","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"FilterViewPaneContainer","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/viewsViewlet"},{"name":"finalHandler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"finally","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"FinalNewLineParticipant","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/saveParticipants"},{"name":"find","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"FIND_IDS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"findBestWindowOrFolderForFile","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/node/window"},{"name":"FindController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"findCredentials","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/keytar/keytar"},{"name":"findCredentials","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/keytar/index"},{"name":"FindDecorations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findDecorations"},{"name":"findExecutable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/node/terminalEnvironment"},{"name":"findExpressionInStackFrame","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugHover"},{"name":"findFirstInSorted","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"findFreePort","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/ports"},{"name":"findFreePortFaster","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/ports"},{"name":"FindInFilesActionId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"FindInFilesCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"FindInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/findinput/findInput"},{"name":"FindMatch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"findMatchingThemeRule","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/common/TMHelper"},{"name":"FindModelBoundToEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"findNodeAtLocation","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"findNodeAtOffset","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"FindOptionOverride","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findState"},{"name":"FindOptionsWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findOptionsWidget"},{"name":"FindOrReplaceInFilesAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"findParentWithClass","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"findPassword","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/keytar/keytar"},{"name":"findPassword","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/keytar/index"},{"name":"FindReplaceState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findState"},{"name":"findRules","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"FindStartFocusAction","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"findValidPasteFileTarget","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"FindWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findWidget"},{"name":"FindWidgetViewZone","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findWidget"},{"name":"findWindowOnExtensionDevelopmentPath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/node/window"},{"name":"findWindowOnWorkspace","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/node/window"},{"name":"findWindowOnWorkspaceOrFolderUri","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/node/window"},{"name":"finished","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"stream"},{"name":"fips","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"first","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"first","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"first","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/collections"},{"name":"FIRST_COMPRESSED_FOLDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"firstIndex","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"FirstMatchFocusKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"firstNonWhitespaceIndex","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"firstOrDefault","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"firstSessionDateStorageKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetry"},{"name":"FixAllAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionCommands"},{"name":"fixAllCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeAction"},{"name":"fixCase","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"fixDriveC","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepFileSearch"},{"name":"fixInsert","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"fixNewline","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepTextSearchEngine"},{"name":"fixPath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/test/browser/queryBuilder.test"},{"name":"fixRegexNewline","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepTextSearchEngine"},{"name":"flatten","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"Float32Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Float64Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"FloatingClickWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorWidgets"},{"name":"focus","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"FOCUS_FIRST_NOTIFICATION_TOAST","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"FOCUS_LAST_NOTIFICATION_TOAST","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"FOCUS_NEXT_NOTIFICATION_TOAST","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"FOCUS_NOTIFICATION_TOAST","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"FOCUS_PREVIOUS_NOTIFICATION_TOAST","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"FOCUS_REPL_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"FocusAboveGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"FocusActiveEditorCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"FocusActiveGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"FocusBelowGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"focusBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"focusedCellIndicator","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookEditor"},{"name":"FocusedViewContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/views"},{"name":"FocusEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"FocusFilesExplorer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"FocusFirstGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"FocusLastGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"FocusLeftGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"FocusNavigationEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"FocusNextGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"FocusNextInputAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"FocusNextSearchResultAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"FocusPreviousGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"FocusPreviousInputAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"FocusPreviousSearchResultAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"FocusQueryEditorWidgetAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"FocusQueryEditorWidgetCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"FocusRightGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"FocusSearchFromResults","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"focusSearchListCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"FocusSearchListCommandID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"FocusSessionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"FocusSessionActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActionViewItems"},{"name":"foldBackgroundBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/folding"},{"name":"FOLDER_CONFIG_FOLDER_NAME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"FOLDER_SCOPES","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"FOLDER_SETTINGS_NAME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"FOLDER_SETTINGS_PATH","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"FOLDER_SETTINGS_PATH","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferences"},{"name":"FolderConfiguration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/browser/configuration"},{"name":"FolderFocusKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"FolderMatch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchModel"},{"name":"FolderMatchRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchResultsView"},{"name":"FolderMatchWithResource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchModel"},{"name":"FolderSettingsActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesWidgets"},{"name":"FolderSettingsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesRenderers"},{"name":"folderSettingsSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"foldersToIncludeGlobs","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepFileSearch"},{"name":"foldersToRgExcludeGlobs","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepFileSearch"},{"name":"FolderThemeIcon","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"FoldingController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/folding"},{"name":"FoldingDecorationProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingDecorations"},{"name":"FoldingModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingModel"},{"name":"FoldingRange","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"FoldingRange","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"FoldingRange","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"FoldingRangeKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"FoldingRangeKind","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"FoldingRangeKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"FoldingRangeKind","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"FoldingRangeProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"FoldingRegion","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingRanges"},{"name":"FoldingRegions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingRanges"},{"name":"FollowerLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/logIpc"},{"name":"FontInfo","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/fontInfo"},{"name":"FontStyle","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"fontStylePattern","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"for","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"forEach","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/collections"},{"name":"foreground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"ForeignElementType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"fork","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"child_process"},{"name":"fork","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"format","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"format","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"url"},{"name":"format","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"format","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"format","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"format","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/jsonFormatter"},{"name":"format","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"formatDocumentRangeWithProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"formatDocumentRangeWithSelectedProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"formatDocumentWithProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"formatDocumentWithSelectedProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"formatOptions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/argv"},{"name":"formatPII","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugUtils"},{"name":"FormatString","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"FormattingConflicts","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"FormattingEdit","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/formattingEdit"},{"name":"FormattingMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"formatWithOptions","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"FormData","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"FORMERR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"forwardedPortsViewEnabled","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/browser/tunnelView"},{"name":"frameElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"frames","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"FrankensteinMode","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/abstractMode"},{"name":"freemem","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"fromBuffer","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/yauzl/index"},{"name":"fromFd","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/yauzl/index"},{"name":"fromMap","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/collections"},{"name":"fromNow","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/date"},{"name":"fromRandomAccessReader","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/yauzl/index"},{"name":"fromRangeOrRangeWithMessage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"fstat","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"fstatSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"FSWatcher","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/chokidar/types/index"},{"name":"fsync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"fsyncSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"ftruncate","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"ftruncateSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"function","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Function","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"FunctionBreakpoint","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"FunctionBreakpoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"FunctionBreakpoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"futimes","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"futimesSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"fuzzyContains","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"fuzzyScore","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"FuzzyScore","kind":"type","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"fuzzyScoreGraceful","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"fuzzyScoreGracefulAggressive","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"GainNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Gamepad","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"GamepadButton","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"GamepadEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"GamepadHapticActuator","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"GamepadPose","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"generateIndent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentUtils"},{"name":"generateKeyPair","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"generateKeyPairSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"generateRandomChunkWithLF","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils"},{"name":"generateRandomEdits","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils"},{"name":"generateRandomPipeName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc.net"},{"name":"generateRandomReplaces","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils"},{"name":"generateSequentialInserts","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils"},{"name":"generateTokensCSSForColorMap","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/tokenization"},{"name":"generateUuid","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uuid"},{"name":"Gesture","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/touch"},{"name":"get","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"get","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"https"},{"name":"getAbsoluteGlob","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepFileSearch"},{"name":"getActiveEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsEditorContribution"},{"name":"getActiveNotebookEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/contrib/notebookActions"},{"name":"getActiveTextEditorOptions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editor"},{"name":"getActiveWebviewEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewCommands"},{"name":"getAllMethodNames","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"getAllPropertyNames","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"getAppDataPath","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/paths"},{"name":"getBaseLabel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"getBasenameTerms","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/glob"},{"name":"getBlinkMemoryInfo","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getBreakpointMessageAndClassName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/breakpointsView"},{"name":"getCharContainingOffset","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"getCharIndex","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/minimap/minimapCharSheet"},{"name":"getCiphers","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"getCiphers","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"getClientArea","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getCodeActions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeAction"},{"name":"getCodeEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorBrowser"},{"name":"getCodeForKeyCode","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/keyboardEvent"},{"name":"getCodeLensData","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codelens/codelens"},{"name":"getColorPresentations","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/colorPicker/color"},{"name":"getColorRegistry","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"getColors","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/colorPicker/color"},{"name":"getComparisonKey","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"getComputedStyle","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"getComputedStyle","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getConfigurationKeys","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"getConfigurationValue","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"getContentHeight","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getContentWidth","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getContext","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerView"},{"name":"getContext","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/callStackView"},{"name":"getContextForContributedActions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/callStackView"},{"name":"getContextMenuActions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"getCorrelationContext","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/applicationinsights"},{"name":"getCPUUsage","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getCreationTime","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getCurrentActivationRecord","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/experiments/common/experimentService"},{"name":"getCurrentKeyboardLayout","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/native-keymap/index"},{"name":"getCurves","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"getCwd","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment"},{"name":"getDeclarationsAtPosition","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/goToSymbol"},{"name":"getDefaultIgnoredSettings","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"getDefaultSettings","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"http2"},{"name":"getDefaultShell","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment"},{"name":"getDefaultShellArgs","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment"},{"name":"getDefaultUserDataPath","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/paths"},{"name":"getDefaultValue","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"getDefaultValues","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"getDefinitionsAtPosition","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/goToSymbol"},{"name":"getDelayedChannel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc"},{"name":"getDiffieHellman","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"getDisallowedIgnoredSettings","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"getDispatchConfig","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/dispatchConfig"},{"name":"getDocumentFormattingEditsUntilResult","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"getDocumentRangeFormattingEditsUntilResult","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"getDocumentSymbols","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/documentSymbols"},{"name":"getDomainsOfRemotes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tags/electron-browser/workspaceTags"},{"name":"getDomNodePagePosition","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getDuration","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/performance"},{"name":"getEditOperation","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/browser/testCommand"},{"name":"getEditorPartOptions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editor"},{"name":"getegid","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getElementsByTagName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getEmptyExpression","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/glob"},{"name":"getEnabledCategories","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"trace_events"},{"name":"getEncodedLanguageId","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"getEntries","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/performance"},{"name":"getEntry","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/performance"},{"name":"getEOL","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/jsonFormatter"},{"name":"getErrorMessage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"geteuid","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getExactExpressionStartAndEnd","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugUtils"},{"name":"getExcludes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"getExpandedBodySize","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/breakpointsView"},{"name":"getExtensionHostDebugSession","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugUtils"},{"name":"getExtensionKind","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsUtil"},{"name":"getExtensions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewer"},{"name":"getExtraColor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughUtils"},{"name":"getFileNamesMessage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/dialogs/common/dialogs"},{"name":"getFirstFrame","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/console"},{"name":"getGalleryExtensionId","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementUtil"},{"name":"getGalleryExtensionTelemetryData","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementUtil"},{"name":"getgid","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getGraphemeBreakType","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"getgroups","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getHashedRemotesFromConfig","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tags/electron-browser/workspaceTags"},{"name":"getHashes","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"getHeapCodeStatistics","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"getHeapSnapshot","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"getHeapSpaceStatistics","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"getHeapStatistics","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"getHeapStatistics","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getHover","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/hover/getHover"},{"name":"getIconClass","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/quickinput/browser/quickInputUtils"},{"name":"getIconClasses","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/getIconClasses"},{"name":"getIconRegistry","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/iconRegistry"},{"name":"getIdAndVersion","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/node/cliProcessMain"},{"name":"getIgnoredSettings","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/settingsMerge"},{"name":"getImplementationsAtPosition","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/goToSymbol"},{"name":"getInstalledExtensions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensionsUtils"},{"name":"getInvalidTypeError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesValidation"},{"name":"getIOCounters","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getIssueReporterStyles","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/issue/electron-browser/issueService"},{"name":"getKeyboardLayoutId","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/keymapInfo"},{"name":"getKeyMap","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/native-keymap/index"},{"name":"getLangEnvVariable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment"},{"name":"getLanguages","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"getLargestChildWidth","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getLastActiveWindow","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/node/window"},{"name":"getLeadingWhitespace","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"getLineEndOffset","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/content"},{"name":"getLineStartOffset","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/content"},{"name":"getLinks","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/links/getLinks"},{"name":"getLocalExtensionTelemetryData","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementUtil"},{"name":"getLocation","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"getLogLevel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"getMac","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/macAddress"},{"name":"getMachineId","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/id"},{"name":"getMachineInfo","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/diagnostics/node/diagnosticsService"},{"name":"getMainProcessParentEnv","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/node/terminalEnvironment"},{"name":"getMaliciousExtensionsSet","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementUtil"},{"name":"getManifest","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/node/extensionManagementUtil"},{"name":"getMapForWordSeparators","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/wordCharacterClassifier"},{"name":"getMatchedCSSRules","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"getMaxListeners","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"getMaxListeners","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getMediaMime","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/mime"},{"name":"getMenuBarVisibility","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/common/windows"},{"name":"getMigratedSettingValue","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"getModel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"getModelMarkers","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"getModels","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"getMultiSelectedEditorContexts","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"getMultiSelectedResources","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/files"},{"name":"getNextCodePoint","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"getNextTickChannel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc"},{"name":"getNLines","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"getNLSConfiguration","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/languagePacks"},{"name":"getNodeColor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"getNodeIsInOverviewRuler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"getNodePath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"getNodeType","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"getNodeValue","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"getNonWhitespacePrefix","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/snippets/browser/snippetsService"},{"name":"getOccurrencesAtPosition","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordHighlighter/wordHighlighter"},{"name":"getOnTypeFormattingEdits","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"getOnTypeRenameRanges","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/rename/onTypeRename"},{"name":"getOpenEditorsViewMultiSelection","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/files"},{"name":"getOrDefault","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/objects"},{"name":"getOrMakeSearchEditorInput","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput"},{"name":"getOrSet","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"getOuterEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"getOutOfWorkspaceEditorResources","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/search"},{"name":"getOutputSimpleEditorOptions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform"},{"name":"getPackedSettings","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"http2"},{"name":"getParseErrorMessage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/jsonErrorMessages"},{"name":"getPassword","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/keytar/keytar"},{"name":"getPassword","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/keytar/index"},{"name":"getPathFromAmdModule","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/amd"},{"name":"getPathLabel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"getPathTerms","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/glob"},{"name":"getPixelRatio","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"getPlatformTextDecoder","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/stringBuilder"},{"name":"getPriority","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"getProcessCpuUsage","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/windows-process-tree/index"},{"name":"getProcessList","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/windows-process-tree/index"},{"name":"getProcessMemoryInfo","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getProcessTree","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/windows-process-tree/index"},{"name":"getProxyAgent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/request/node/proxy"},{"name":"getQuickNavigateHandler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/quickaccess"},{"name":"getRandomElement","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"getRandomEOLSequence","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils"},{"name":"getRandomInt","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils"},{"name":"getRandomString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils"},{"name":"getRandomTestPath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/node/testUtils"},{"name":"getRealAndSyntheticDocumentFormattersOrdered","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/format/format"},{"name":"getReferencesAtPosition","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/goToSymbol"},{"name":"getReindentEditOperations","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"getRelativeLocation","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/grid"},{"name":"getRemoteAuthority","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteHosts"},{"name":"getRemoteName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteHosts"},{"name":"getRemotes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tags/electron-browser/workspaceTags"},{"name":"getResizesObserver","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver"},{"name":"getResource","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"getResourceForCommand","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/files"},{"name":"getRoot","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"getSCMResourceContextKey","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/menus"},{"name":"getScopes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"getSearchView","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"getSelection","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"getSelectionKeyboardEvent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"getSelectionSearchString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"getServers","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"getServiceMachineId","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/serviceMachineId/common/serviceMachineId"},{"name":"getSettingsTargetName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferences"},{"name":"getShadowRoot","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getShellEnvironment","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/node/shellEnv"},{"name":"getSimpleCodeEditorWidgetOptions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/simpleEditorOptions"},{"name":"getSimpleEditorOptions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/simpleEditorOptions"},{"name":"getSimpleWorkspaceLabel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/label/common/label"},{"name":"getSingletonServiceDescriptors","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/extensions"},{"name":"getSnippetSuggestSupport","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggest"},{"name":"getSpaceCnt","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentUtils"},{"name":"getStackFrameThreadAndSessionToFocus","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugService"},{"name":"getStateLabel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"getStdinFilePath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/stdin"},{"name":"getStoredWorkspaceFolder","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"getStringIdentifierForProxy","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/proxyIdentifier"},{"name":"GetStringRegKey","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/vscode-windows-registry/index"},{"name":"getSuggestionComparator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggest"},{"name":"getSyncResourceFromLocalPreview","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"getSystemMemoryInfo","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getSystemShell","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/node/terminal"},{"name":"getSystemVersion","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getTemplates","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskTemplates"},{"name":"getTerminalShellConfiguration","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalConfiguration"},{"name":"getThemeTypeSelector","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"getTimeSinceLastZoomLevelChanged","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"getTitleBarStyle","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/common/windows"},{"name":"getTokenClassificationRegistry","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"getTopLeftOffset","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getTotalHeight","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getTotalScrollWidth","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getTotalWidth","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"getTypeDefinitionsAtPosition","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/goToSymbol"},{"name":"getuid","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"getUnpackedSettings","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"http2"},{"name":"getUri","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/test/browser/queryBuilder.test"},{"name":"getUriFromAmdModule","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/amd"},{"name":"getUriFromSource","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugSource"},{"name":"getUserDataSyncStore","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"getVisbileViewContextKey","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/views"},{"name":"getVisibleAndSorted","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugUtils"},{"name":"getVisibleState","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/indexTreeModel"},{"name":"getWebviewContentMimeType","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/common/mimeTypes"},{"name":"getWellFormedFileName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"getWindowsBuildNumber","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/node/terminal"},{"name":"getWindowsShell","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/processes"},{"name":"getWindowsStateStoreData","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/electron-main/windowsStateStorage"},{"name":"getWordAtText","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/wordHelper"},{"name":"getWordDefinitionFor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDocumentData"},{"name":"getWorkerBootstrapUrl","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/worker/defaultWorkerFactory"},{"name":"getWorkspaceIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workspaces/browser/workspaces"},{"name":"getWorkspaceIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/electron-main/workspacesMainService"},{"name":"getWorkspaceIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test"},{"name":"getWorkspaceSymbols","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/search"},{"name":"getXtermLineContent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers"},{"name":"getZoomFactor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"getZoomLevel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"global","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"GLOBAL_ACTIVITY_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/activity"},{"name":"GlobalActivityActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/activitybar/activitybarActions"},{"name":"globalAgent","kind":"let","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"globalAgent","kind":"let","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"https"},{"name":"GlobalCompareResourcesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"GlobalEditorMouseMoveMonitor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorDom"},{"name":"GlobalExtensionEnablementService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionEnablementService"},{"name":"globalGlob","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/test/browser/queryBuilder.test"},{"name":"GlobalMouseMoveMonitor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/globalMouseMoveMonitor"},{"name":"GlobalNewUntitledFileAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"GlobalRemoveRootFolderAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceActions"},{"name":"globals","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"globalShortcut","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"GlobalStateSynchroniser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/globalStateSync"},{"name":"GlobalStorageDatabaseChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/node/storageIpc"},{"name":"GlobalStorageDatabaseChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/node/storageIpc"},{"name":"GlobalStyleSheet","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/services/codeEditorServiceImpl"},{"name":"globalThis","kind":"module","kindModifiers":"","sortText":"4"},{"name":"GlobPattern","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"GlyphHoverWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/hover/hoverWidgets"},{"name":"GlyphMarginOverlay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin"},{"name":"GOTO_NEXT_CHANGE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"GOTO_PREVIOUS_CHANGE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"GotoDefinitionAtPositionEditorContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosition"},{"name":"GotoLineAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess"},{"name":"GotoLineAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/quickAccess/standaloneGotoLineQuickAccess"},{"name":"GotoLineAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess"},{"name":"GotoLineAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess"},{"name":"GoToLineNLS","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standaloneStrings"},{"name":"GotoLineQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess"},{"name":"GotoSymbolAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess"},{"name":"GotoSymbolQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess"},{"name":"gracefulify","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/graceful-fs/index"},{"name":"grammarsExtPoint","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/common/TMGrammars"},{"name":"Graph","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/graph"},{"name":"GraphemeBreakType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"Grid","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/grid"},{"name":"GridView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/gridview"},{"name":"GridViewSizing","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/grid"},{"name":"group","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"groupBy","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"groupBy","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/collections"},{"name":"groupByExtension","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagementUtil"},{"name":"GroupChangeKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorGroupsService"},{"name":"groupCollapsed","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"GroupDirection","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorGroupsService"},{"name":"groupEnd","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"groupIntersect","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/rangeMap"},{"name":"GroupLocation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorGroupsService"},{"name":"GroupOrientation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorGroupsService"},{"name":"GroupsArrangement","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorGroupsService"},{"name":"GroupsOrder","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorGroupsService"},{"name":"GroupType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"gt","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"gte","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"gtr","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"guessIndentation","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/indentationGuesser"},{"name":"guessMimeTypes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/mime"},{"name":"gunzip","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"gunzipSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"gzip","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"gzipSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"handleANSIOutput","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform"},{"name":"handleANSIOutput","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugANSIHandling"},{"name":"Handler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/editorCommon"},{"name":"handleVetos","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/common/lifecycle"},{"name":"hang","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"hasChildProcesses","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/node/terminals"},{"name":"hasClass","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"hasFileFolderCopyCapability","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"hasFileReadStreamCapability","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"hash","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/hash"},{"name":"Hash","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"HashChangeEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Hasher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/hash"},{"name":"hashPath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/backup/node/backupFileService"},{"name":"hasOpenReadWriteCloseCapability","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"hasParentWithClass","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"hasReadWriteCapability","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"HasSearchResults","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"hasSiblingFn","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/glob"},{"name":"hasSiblingPromiseFn","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/glob"},{"name":"hasStdinWithoutTty","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/stdin"},{"name":"hasTextDecoder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/stringBuilder"},{"name":"hasToIgnoreCase","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"hasTrailingPathSeparator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"hasUncaughtExceptionCaptureCallback","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"hasWorkspaceFileExtension","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"hc_black","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/themes"},{"name":"HC_THEME_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/workbenchThemeService"},{"name":"Headers","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HelpQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/browser/helpQuickAccess"},{"name":"HiddenAreasRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesRenderers"},{"name":"HiddenRangeModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/hiddenRangeModel"},{"name":"hide","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"HIDE_NOTIFICATION_TOAST","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"HIDE_NOTIFICATIONS_CENTER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"HideNotificationsCenterAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsActions"},{"name":"HideWebViewEditorFindCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewCommands"},{"name":"HideWelcomeOverlayAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay"},{"name":"HIGH_CONTRAST","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"HighlightedLabel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/highlightedlabel/highlightedLabel"},{"name":"HighlightMatchesRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesRenderers"},{"name":"history","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"History","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HistoryInputBox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/inputbox/inputBox"},{"name":"HistoryNavigationEnablementContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/browser/contextScopedHistoryWidget"},{"name":"HistoryNavigationWidgetContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/browser/contextScopedHistoryWidget"},{"name":"HistoryNavigator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/history"},{"name":"HistoryService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/history/browser/history"},{"name":"HitTestContext","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/mouseTarget"},{"name":"HIVES","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"HKCC","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"HKCR","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"HKCU","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"HKLM","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"HKU","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"Hmac","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"homedir","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"HorizontalPosition","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/renderingContext"},{"name":"HorizontalRange","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/renderingContext"},{"name":"HorizontalScrollbar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/horizontalScrollbar"},{"name":"horizontalScrollingKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"HostExtension","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostExtensionActivator"},{"name":"hostname","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"HotExitConfiguration","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"Hover","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Hover","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"Hover","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"HoverOperation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/hover/hoverOperation"},{"name":"HoverProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"HoverStartMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/hover/hoverOperation"},{"name":"hrtime","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"HSLA","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/color"},{"name":"HSVA","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/color"},{"name":"HTMLAllCollection","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLAnchorElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLAppletElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLAreaElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLAudioElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLBaseElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLBaseFontElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLBodyElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLBRElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLButtonElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLCanvasElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLCollection","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLDataElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLDataListElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLDetailsElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLDialogElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLDirectoryElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLDivElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLDListElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLDocument","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLEmbedElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLFieldSetElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLFontElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLFormControlsCollection","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLFormElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLFrameElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLFrameSetElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLHeadElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLHeadingElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLHRElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLHtmlElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLIFrameElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLImageElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLInputElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLLabelElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLLegendElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLLIElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLLinkElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLMapElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLMarqueeElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLMediaElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLMenuElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLMetaElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLMeterElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLModElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLObjectElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLOListElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLOptGroupElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLOptionElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLOptionsCollection","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLOutputElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLParagraphElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLParamElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLPictureElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLPreElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLProgressElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLQuoteElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLScriptElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLSelectElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLSlotElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLSourceElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLSpanElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLStyleElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTableCaptionElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTableCellElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTableColElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTableDataCellElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTableElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTableHeaderCellElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTableRowElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTableSectionElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTemplateElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTextAreaElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTimeElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTitleElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLTrackElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLUListElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLUnknownElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"HTMLVideoElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Http2ServerRequest","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"http2"},{"name":"Http2ServerResponse","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"http2"},{"name":"HttpProxyAgent","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/http-proxy-agent/index"},{"name":"IAccessibilityService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/accessibility/common/accessibility"},{"name":"IActivityBarService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/activityBar/browser/activityBarService"},{"name":"IActivityService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/activity/common/activity"},{"name":"IAuthenticationService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/authentication/browser/authenticationService"},{"name":"IAuthenticationTokenService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/authentication/common/authentication"},{"name":"IBackupFileService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/backup/common/backup"},{"name":"IBackupMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/backup/electron-main/backup"},{"name":"IBreadcrumbsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/breadcrumbs"},{"name":"IBulkEditService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/services/bulkEditService"},{"name":"ICACLS_PATH","kind":"property","kindModifiers":"private,static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"IClipboardService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/clipboard/common/clipboardService"},{"name":"ICodeEditorService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/services/codeEditorService"},{"name":"ICodeLensCache","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codelens/codeLensCache"},{"name":"ICommandService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/commands/common/commands"},{"name":"ICommentService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentService"},{"name":"IconBadge","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/activity/common/activity"},{"name":"IConfigurationResolverService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configurationResolver/common/configurationResolver"},{"name":"IConfigurationService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"iconForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"IconLabel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/iconLabel/iconLabel"},{"name":"iconsSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/iconRegistry"},{"name":"IContextKeyService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"IContextMenuService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextview/browser/contextView"},{"name":"IContextViewService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextview/browser/contextView"},{"name":"ICredentialsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/credentials/common/credentials"},{"name":"ICredentialsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/credentials/common/credentials"},{"name":"ICustomEditorService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/customEditor/common/customEditor"},{"name":"ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsEditorContribution"},{"name":"ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/common/driver"},{"name":"ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/diagnostics/node/diagnosticsService"},{"name":"ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/launch/electron-main/launchMainService"},{"name":"ID_EDITOR_WORKER_SERVICE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/editorWorkerService"},{"name":"ID_INDENT_PROVIDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/indentRangeProvider"},{"name":"ID_INIT_PROVIDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/intializingRangeProvider"},{"name":"ID_SYNTAX_PROVIDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/syntaxRangeProvider"},{"name":"IDBCursor","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDBCursorWithValue","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDBDatabase","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDBFactory","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDBIndex","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDBKeyRange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDBObjectStore","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDBOpenDBRequest","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDBRequest","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDBTransaction","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDBVersionChangeEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IDebugHelperService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"IDebugService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"IDecorationsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/decorations/browser/decorations"},{"name":"IdentityCoordinatesConverter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/splitLinesCollection"},{"name":"IdentityLinesCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/splitLinesCollection"},{"name":"IdentityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesTree"},{"name":"IdentityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree"},{"name":"IdGenerator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/idGenerator"},{"name":"IDiagnosticsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/diagnostics/node/diagnosticsService"},{"name":"IDialogMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/dialogs/electron-main/dialogs"},{"name":"IDialogService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/dialogs/common/dialogs"},{"name":"IdleValue","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"IdObject","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"IDownloadService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/download/common/download"},{"name":"idPattern","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"IDriver","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/common/driver"},{"name":"IEditorGroupsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorGroupsService"},{"name":"IEditorProgressService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/progress/common/progress"},{"name":"IEditorService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorService"},{"name":"IEditorWorkerService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/editorWorkerService"},{"name":"IElectronMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/electron/electron-main/electronMainService"},{"name":"IElectronService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/electron/node/electron"},{"name":"IEnvironmentService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/common/environment"},{"name":"IEnvironmentVariableService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/environmentVariable"},{"name":"IExperimentService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/experiments/common/experimentService"},{"name":"IExplorerService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"IExtensionGalleryService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"IExtensionHostDebugService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/debug/common/extensionHostDebug"},{"name":"IExtensionHostProfileService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"IExtensionManagementServerService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/common/extensionManagement"},{"name":"IExtensionManagementService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"IExtensionRecommendationsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/common/extensionManagement"},{"name":"IExtensionResourceLoaderService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader"},{"name":"IExtensionService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensions"},{"name":"IExtensionStoragePaths","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostStoragePaths"},{"name":"IExtensionsWorkbenchService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"IExtensionTipsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"IExtensionUrlHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/browser/extensionUrlHandler"},{"name":"IExternalTerminalService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/externalTerminal/common/externalTerminal"},{"name":"IExtHostApiDeprecationService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostApiDeprecationService"},{"name":"IExtHostCommands","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostCommands"},{"name":"IExtHostConfiguration","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostConfiguration"},{"name":"IExtHostDebugService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDebugService"},{"name":"IExtHostDecorations","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDecorations"},{"name":"IExtHostDocumentsAndEditors","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDocumentsAndEditors"},{"name":"IExtHostExtensionService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostExtensionService"},{"name":"IExtHostInitDataService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostInitDataService"},{"name":"IExtHostOutputService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostOutput"},{"name":"IExtHostRpcService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostRpcService"},{"name":"IExtHostSearch","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostSearch"},{"name":"IExtHostStorage","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostStorage"},{"name":"IExtHostTask","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"IExtHostTerminalService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTerminalService"},{"name":"IExtHostTimeline","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTimeline"},{"name":"IExtHostTunnelService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTunnelService"},{"name":"IExtHostWorkspace","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostWorkspace"},{"name":"if","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"ifError","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"IFileDialogService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/dialogs/common/dialogs"},{"name":"IFilesConfigurationService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService"},{"name":"IFileService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"IframeUtils","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/iframe"},{"name":"IFrameWebview","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewElement"},{"name":"IGlobalExtensionEnablementService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"ignoreBracketsInToken","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports"},{"name":"ignoreErrors","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"IgnoreExtensionRecommendationAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"IHistoryService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/history/common/history"},{"name":"IHostService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/host/browser/host"},{"name":"IHostUtils","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostExtensionService"},{"name":"IInstantiationService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/instantiation"},{"name":"IIntegrityService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/integrity/common/integrity"},{"name":"IIRFilterNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IIssueService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/issue/node/issue"},{"name":"IJSONEditingService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/jsonEditing"},{"name":"IKeybindingEditingService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/keybindingEditing"},{"name":"IKeybindingService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/keybinding"},{"name":"IKeymapService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/keymapInfo"},{"name":"ILabelService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/label/common/label"},{"name":"ILaunchMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/launch/electron-main/launchMainService"},{"name":"ILayoutService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/layout/browser/layoutService"},{"name":"ILifecycleMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/electron-main/lifecycleMainService"},{"name":"ILifecycleService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/common/lifecycle"},{"name":"IListService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"illegalArgument","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"illegalState","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"ILocalizationsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/localizations/common/localizations"},{"name":"ILoggerService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"ILogService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"Image","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IMAGE_PREVIEW_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"ImageBitmap","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ImageBitmapRenderingContext","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ImageData","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IMainProcessService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/ipc/electron-browser/mainProcessService"},{"name":"IMarkerData","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/markers/common/markers"},{"name":"IMarkerDecorationsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/markersDecorationService"},{"name":"IMarkerService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/markers/common/markers"},{"name":"IMarkersWorkbenchService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markers"},{"name":"IMenubarService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/menubar/node/menubar"},{"name":"IMenuService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"ImmortalReference","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/lifecycle"},{"name":"IMMUTABLE_CODE_TO_KEY_CODE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/scanCode"},{"name":"IMMUTABLE_KEY_CODE_TO_CODE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/scanCode"},{"name":"IModelService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/modelService"},{"name":"IModeService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/modeService"},{"name":"impactsEditorPartOptions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editor"},{"name":"ImplementationProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"implements","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"import","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"importEntries","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/performance"},{"name":"importScripts","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"in","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"inAppPurchase","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"inc","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"IncomingMessage","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"IncomingMessage","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"IncreaseSearchEditorContextLinesCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"IncreaseViewSizeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"incrementFileName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"IndentAction","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"IndentAction","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/languageConfiguration"},{"name":"IndentAction","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"IndentationToSpacesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"IndentationToSpacesCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"IndentationToTabsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"IndentationToTabsCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"IndentConsts","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/indentRules"},{"name":"IndentGuidesOverlay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/indentGuides/indentGuides"},{"name":"IndentLinesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"IndentRangeProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/indentRangeProvider"},{"name":"IndentRulesSupport","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/indentRules"},{"name":"IndentUsingSpaces","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"IndentUsingTabs","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"index","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"indexedDB","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"INDEXEDDB_LOGS_OBJECT_STORE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/log/browser/indexedDBLogProvider"},{"name":"INDEXEDDB_VSCODE_DB","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/log/browser/indexedDBLogProvider"},{"name":"IndexedDBLogProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/log/browser/indexedDBLogProvider"},{"name":"indexOfPath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"IndexTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/indexTree"},{"name":"IndexTreeModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/indexTreeModel"},{"name":"InEditorZenModeContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"Infinity","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"inflate","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"inflateRaw","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"inflateRawSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"inflateSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"info","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"Information","kind":"enum member","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/SeverityLevel"},{"name":"inherits","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"INITIAL","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-textmate/release/main"},{"name":"initialize","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.worker"},{"name":"InitializingRangeProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/intializingRangeProvider"},{"name":"InlineDecoration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/viewModel"},{"name":"InlineDecorationType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/viewModel"},{"name":"InlineDiffMargin","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/widget/inlineDiffMargin"},{"name":"InMemoryBackupFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/backup/common/backupFileService"},{"name":"InMemoryFileSystemProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/inMemoryFilesystemProvider"},{"name":"InMemoryLogProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/log/common/inMemoryLogProvider"},{"name":"InMemoryStorageDatabase","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/storage/common/storage"},{"name":"InMemoryStorageService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/common/storage"},{"name":"InMemoryTask","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"innerHeight","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"innerWidth","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"INotebookService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookService"},{"name":"INotificationService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/notification/common/notification"},{"name":"InPlaceReplaceCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/inPlaceReplace/inPlaceReplaceCommand"},{"name":"inputActiveOptionBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputActiveOptionBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"InputBox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/inputbox/inputBox"},{"name":"InputBoxFocusedKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"InputDeviceInfo","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"InputEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"InputFocusedContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkeys"},{"name":"InputFocusedContextKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkeys"},{"name":"inputForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputPlaceholderForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputsSchema","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configurationResolver/common/configurationResolverSchema"},{"name":"inputValidationErrorBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputValidationErrorBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputValidationErrorForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputValidationInfoBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputValidationInfoBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputValidationInfoForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"InputValidationType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/common/scm"},{"name":"inputValidationWarningBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputValidationWarningBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inputValidationWarningForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"inQuickPickContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/quickaccess"},{"name":"InQuickPickContextKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/quickaccess"},{"name":"inQuickPickContextKeyValue","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/quickaccess"},{"name":"inRecentFilesPickerContextKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/windowActions"},{"name":"insane","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/insane/insane"},{"name":"InSearchEditor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"insert","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"InsertCellEdit","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/cellEdit"},{"name":"InsertCodeCellAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/contrib/notebookActions"},{"name":"InsertCursorAbove","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"InsertCursorBelow","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"InsertLineAfterAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"InsertLineBeforeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"InsertMarkdownCellAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/contrib/notebookActions"},{"name":"inspect","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"INSPECT_MAX_BYTES","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"buffer"},{"name":"InspectTokensNLS","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standaloneStrings"},{"name":"INSTALL_ERROR_INCOMPATIBLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"INSTALL_ERROR_MALICIOUS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"INSTALL_ERROR_NOT_SUPPORTED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"InstallAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"InstallAnotherVersionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"InstallCountWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWidgets"},{"name":"InstallExtensionQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsQuickAccess"},{"name":"InstallExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"InstallInOtherServerAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"InstallLocalExtensionsInRemoteAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"InstallOperation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"InstallRecommendedExtensionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"InstallSpecificVersionOfExtensionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"InstallVSIXAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"InstallWorkspaceRecommendedExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"instanceof","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"instanceStorageKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetry"},{"name":"InstantiationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/instantiationService"},{"name":"Int16Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Int32Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Int8Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IntegrityServiceImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/integrity/node/integrityService"},{"name":"interface","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Interface","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"readline"},{"name":"INTERNAL_CONSOLE_OPTIONS_SCHEMA","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"InternalEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/editorAction"},{"name":"InternalModelContentChangeEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelEvents"},{"name":"IntersectionObserver","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"IntersectionObserverEntry","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"intersects","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"intervalCompare","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"IntervalNode","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"IntervalTimer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"IntervalTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"Intl","kind":"module","kindModifiers":"declare","sortText":"4"},{"name":"IOpenerService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/opener/common/opener"},{"name":"IOutputChannelModelService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/output/common/outputChannelModel"},{"name":"IOutputService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"IPadShowKeyboard","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard"},{"name":"IPanelService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/panel/common/panelService"},{"name":"IPathService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/path/common/pathService"},{"name":"IPCClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc"},{"name":"ipcMain","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"ipcRenderer","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"IPCServer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc"},{"name":"IPeekViewService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"IPreferencesSearchService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"IPreferencesService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferences"},{"name":"IProductService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/product/common/productService"},{"name":"IProgressService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/progress/common/progress"},{"name":"IQuickInputService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/common/quickInput"},{"name":"IRemoteAgentService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/remoteAgentService"},{"name":"IRemoteAuthorityResolverService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAuthorityResolver"},{"name":"IRemoteExplorerService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/remoteExplorerService"},{"name":"IReplaceService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/replace"},{"name":"IRequestService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/request/common/request"},{"name":"IResourceIdentityService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/resource/common/resourceIdentityService"},{"name":"IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"isAbsolute","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"isAbsolute","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"isAbsolutePath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"isActive","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/windows-mutex/index"},{"name":"isAncestor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"isArray","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isArray","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"isatty","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tty"},{"name":"isBasicASCII","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"isBoolean","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isBoolean","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"isBuffer","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"IsCenteredLayoutContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"isChrome","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"ISCMService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/common/scm"},{"name":"isCodeEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorBrowser"},{"name":"isCompositeEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorBrowser"},{"name":"isCompressedFolderName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerViewer"},{"name":"isConfigurationOverrides","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"isContext","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"vm"},{"name":"isDate","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isDebuggerMainContribution","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugUtils"},{"name":"isDecorationOptionsArr","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"isDeepStrictEqual","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"IsDevelopmentContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkeys"},{"name":"isDiffEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorBrowser"},{"name":"isDiffEditorConfigurationKey","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/commonEditorConfig"},{"name":"isDirtyDiffVisible","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"isDisposable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/lifecycle"},{"name":"ISearchHistoryService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchHistoryService"},{"name":"ISearchService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"ISearchWorkbenchService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchModel"},{"name":"isEdge","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"isEdgeWebView","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"isEditorConfigurationKey","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/commonEditorConfig"},{"name":"isEditorInputWithOptions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"isElevated","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/native-is-elevated/index"},{"name":"isEmojiImprecise","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"isEmptyMarkdownString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/htmlContent"},{"name":"isEmptyObject","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"isEngineValid","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensionValidator"},{"name":"isEOL","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/jsonFormatter"},{"name":"isEqual","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"isEqual","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"isEqualAuthority","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"isEqualOrParent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"isEqualOrParent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"isError","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isErrorWithActions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errorsWithActions"},{"name":"isExcludeSetting","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"isFalsyOrEmpty","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"isFalsyOrWhitespace","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"isFileMatch","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"isFilePatternMatch","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"isFileToOpen","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/common/windows"},{"name":"isFilterResult","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/indexTreeModel"},{"name":"isFinite","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"isFirefox","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"isFolderToOpen","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/common/windows"},{"name":"isFullscreen","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"IsFullscreenContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/contextkeys"},{"name":"isFullWidthCharacter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"isFunction","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isFunction","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"isFuzzyAction","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"isFuzzyActionArr","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"isGridBranchNode","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/gridview"},{"name":"isGridBranchNode","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/grid"},{"name":"ISharedProcessMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/ipc/electron-main/sharedProcessMainService"},{"name":"ISharedProcessService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/ipc/electron-browser/sharedProcessService"},{"name":"isHighSurrogate","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"isHTMLElement","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"isIAction","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"isIExtensionIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensions"},{"name":"isIExtensionIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"ISignService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/sign/common/sign"},{"name":"isIMenuItem","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"isInDOM","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"isInShadowDOM","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"isIOS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"isIP","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"net"},{"name":"isIPad","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"isIPv4","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"net"},{"name":"isIPv6","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"net"},{"name":"isISOKeyboard","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/native-keymap/index"},{"name":"isISubmenuItem","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"isKeymapExtension","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensionsUtils"},{"name":"isLanguagePackExtension","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensions"},{"name":"isLinux","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"IsLinuxContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkeys"},{"name":"isLittleEndian","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"isLocationLink","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"isLowerAsciiLetter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"isLowSurrogate","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"IsMacContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkeys"},{"name":"isMacintosh","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"IsMacNativeContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkeys"},{"name":"isMainFrame","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"isMainThread","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"worker_threads"},{"name":"isMarkdownString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/htmlContent"},{"name":"isMaster","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"isMenubarMenuItemAction","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/menubar/node/menubar"},{"name":"isMenubarMenuItemSeparator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/menubar/node/menubar"},{"name":"isMenubarMenuItemSubmenu","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/menubar/node/menubar"},{"name":"isMenubarMenuItemUriAction","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/menubar/node/menubar"},{"name":"isMessageOfType","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionHostProtocol"},{"name":"isMultilineRegexSource","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelSearch"},{"name":"isNamedProblemMatcher","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"isNaN","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"isNative","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"ISnippetsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/snippets/browser/snippets.contribution"},{"name":"isNonEmptyArray","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"isNotificationViewItem","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/notifications"},{"name":"isNull","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isNullOrUndefined","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isNullRange","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"isNumber","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isNumber","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"isObject","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isObject","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"isOpera","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"isParent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"isPathSeparator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"isPatternInWord","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"isPrimitive","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isProgressMessage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"isPromiseCanceledError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"isQuote","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"isRawFileWorkspaceFolder","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"isRawUriWorkspaceFolder","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"isReadableStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/stream"},{"name":"isRecentFile","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"isRecentFolder","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"isRecentWorkspace","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"isRegExp","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isRelativePattern","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/glob"},{"name":"isRemoteConsoleLog","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/console"},{"name":"isRemoteDiagnosticError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/diagnostics/common/diagnostics"},{"name":"isRootOrDriveLetter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"isRootUser","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"isSafari","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"isSCMRepository","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/util"},{"name":"isSCMResource","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/util"},{"name":"isSCMResourceGroup","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/util"},{"name":"isSearchViewFocused","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"isSecureContext","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"isSelectionRangeChangeEvent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listWidget"},{"name":"isSelectionSingleChangeEvent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listWidget"},{"name":"isSemanticColoringEnabled","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/modelServiceImpl"},{"name":"isSerializedEditorGroup","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor/editorGroup"},{"name":"isSerializedFileMatch","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"isSerializedSearchComplete","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"isSerializedSearchSuccess","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"isSessionAttach","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugUtils"},{"name":"isShadowRoot","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"isSingleFolderWorkspaceIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"isSingleFolderWorkspaceInitializationPayload","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"isStandalone","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"isStatusbarInDebugMode","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/statusbarColorProvider"},{"name":"isStoredWorkspaceFolder","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"isString","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"isString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"isStringArray","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"isSuccess","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/request/common/request"},{"name":"IssueMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/issue/electron-main/issueMainService"},{"name":"IssueReporter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-browser/issue/issueReporterMain"},{"name":"IssueReporterModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-browser/issue/issueReporterModel"},{"name":"issueReporterPage","kind":"property","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-browser/issue/issueReporterPage"},{"name":"IssueService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/issue/electron-browser/issueService"},{"name":"IssueType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/issue/node/issue"},{"name":"isSymbol","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"IStandaloneThemeService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/standaloneThemeService"},{"name":"IStateService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/state/node/state"},{"name":"IStaticExtensionsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/staticExtensions"},{"name":"IStatusbarService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/statusbar/common/statusbar"},{"name":"isTextEditorPane","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"isThemeColor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/editorCommon"},{"name":"isThenable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"IStorageKeysSyncRegistryService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/storageKeys"},{"name":"IStorageMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/node/storageMainService"},{"name":"IStorageService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/common/storage"},{"name":"ISuggestDataDtoField","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"ISuggestMemoryService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestMemory"},{"name":"ISuggestResultDtoField","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"isUNC","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"isUndefined","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"isUndefined","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"isUndefinedOrNull","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"isUnspecific","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/mime"},{"name":"isUntitledWorkspace","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"isUpper","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"isUpperAsciiLetter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"isUri","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugUtils"},{"name":"isURLDomainTrusted","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/url/common/trustedDomainsValidator"},{"name":"isUTFEncoding","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"isUUID","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uuid"},{"name":"isValidBasename","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"isValidExtensionVersion","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensionValidator"},{"name":"isValidLocalization","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/localizations/common/localizations"},{"name":"isValidMatch","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelSearch"},{"name":"isValidVersion","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensionValidator"},{"name":"isValidVersionStr","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensionValidator"},{"name":"isVersionValid","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensionValidator"},{"name":"isWeb","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"IsWebContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkeys"},{"name":"isWebKit","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"isWebkitWebView","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"isWindows","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"IsWindowsContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkeys"},{"name":"isWindowsDriveLetter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"isWorker","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"isWorkspaceBackupInfo","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/backup/electron-main/backup"},{"name":"isWorkspaceFolder","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/taskQuickPick"},{"name":"isWorkspaceIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"isWorkspaceToOpen","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/common/windows"},{"name":"ISymbolNavigationService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/symbolNavigation"},{"name":"it","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ITaskService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskService"},{"name":"ITelemetryService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetry"},{"name":"ItemActivation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/quickinput/common/quickInput"},{"name":"Iterable","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/iterator"},{"name":"ITerminalInstanceService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminal"},{"name":"ITerminalNativeService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"ITerminalService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminal"},{"name":"ITextFileService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textfiles"},{"name":"ITextMateService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/common/textMateService"},{"name":"ITextModelService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/resolverService"},{"name":"ITextResourceConfigurationService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/textResourceConfigurationService"},{"name":"ITextResourcePropertiesService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/textResourceConfigurationService"},{"name":"IThemeMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/electron-main/themeMainService"},{"name":"IThemeService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"ITimelineService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/common/timeline"},{"name":"ITimerService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/timer/electron-browser/timerService"},{"name":"ITitleService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/title/common/titleService"},{"name":"ITunnelService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/tunnel"},{"name":"IUndoRedoService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/undoRedo/common/undoRedo"},{"name":"IUntitledTextEditorService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/untitled/common/untitledTextEditorService"},{"name":"IUpdateService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/common/update"},{"name":"IURITransformerService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostUriTransformerService"},{"name":"IURLService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/url/common/url"},{"name":"IUserDataAutoSyncService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"IUserDataSyncBackupStoreService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"IUserDataSyncEnablementService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"IUserDataSyncLogService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"IUserDataSyncService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"IUserDataSyncStoreService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"IUserDataSyncUtilService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"IViewDescriptorService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/views"},{"name":"IViewletService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/viewlet/browser/viewlet"},{"name":"IViewsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/views"},{"name":"IWebIssueService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/issue/browser/issueService"},{"name":"IWebviewService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webview"},{"name":"IWebviewWorkbenchService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService"},{"name":"IWindowsMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/electron-main/windows"},{"name":"IWorkbenchEnvironmentService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/environment/common/environmentService"},{"name":"IWorkbenchExtensionEnablementService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/common/extensionManagement"},{"name":"IWorkbenchIssueService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/issue/electron-browser/issue"},{"name":"IWorkbenchLayoutService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/layout/browser/layoutService"},{"name":"IWorkbenchThemeService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/workbenchThemeService"},{"name":"IWorkingCopyFileService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workingCopy/common/workingCopyFileService"},{"name":"IWorkingCopyService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workingCopy/common/workingCopyService"},{"name":"IWorkspace","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspace/common/workspace"},{"name":"IWorkspaceContextService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspace/common/workspace"},{"name":"IWorkspaceEditingService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workspaces/common/workspaceEditing"},{"name":"IWorkspaceFolder","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspace/common/workspace"},{"name":"IWorkspacesHistoryMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService"},{"name":"IWorkspacesMainService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/electron-main/workspacesMainService"},{"name":"IWorkspacesService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"IWorkspaceTagsService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tags/common/workspaceTags"},{"name":"javascriptOnEnterRules","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/modes/supports/javascriptOnEnterRules"},{"name":"join","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"join","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"join","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"JoinAllGroupsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"JoinLinesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"joinPath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"JoinTwoGroupsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"JSON","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"JSONEditingError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/jsonEditing"},{"name":"JSONEditingErrorCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/jsonEditing"},{"name":"JSONEditingService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/jsonEditingService"},{"name":"JsonSchemaVersion","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"JsonSchemaVersion","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskConfiguration"},{"name":"JSONValidationExtensionPoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/jsonValidationExtensionPoint"},{"name":"JUMP_TO_CURSOR_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"KEEP_EDITOR_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookBrowser"},{"name":"KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_NOT_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_FOCUS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_IS_OPEN","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_TEXT_NOT_SELECTED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webview"},{"name":"KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webview"},{"name":"KEYBINDING_ENTRY_TEMPLATE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/keybindingsEditorModel"},{"name":"KeybindingEditorDecorationsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution"},{"name":"KeybindingIO","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/keybindingIO"},{"name":"KeybindingLabel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/keybindingLabel/keybindingLabel"},{"name":"KeybindingParser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keybindingParser"},{"name":"KeybindingResolver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/keybindingResolver"},{"name":"KEYBINDINGS_EDITOR_CLEAR_INPUT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_COPY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_DEFINE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_REMOVE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_RESET","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_SEARCH","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KeybindingsEditingService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/keybindingEditing"},{"name":"KeybindingsEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/keybindingsEditor"},{"name":"KeybindingsEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesEditorInput"},{"name":"KeybindingsEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/keybindingsEditorModel"},{"name":"KeybindingSource","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/keybinding"},{"name":"KeybindingsRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/keybindingsRegistry"},{"name":"KeybindingsSearchWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/keybindingWidgets"},{"name":"KeybindingsSynchroniser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/keybindingsSync"},{"name":"KeybindingWeight","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/keybindingsRegistry"},{"name":"KeybindingWidgetRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution"},{"name":"KEYBOARD_LAYOUT_OPEN_PICKER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"KeyboardEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"KeyboardLayoutContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution"},{"name":"KeyboardLayoutPickerAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker"},{"name":"KeyboardLayoutPickerContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker"},{"name":"KeyboardMapperFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/electron-browser/nativeKeymapService"},{"name":"keyboardNavigationSettingKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"KeyboardSupport","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/canIUse"},{"name":"KeyChord","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keyCodes"},{"name":"KeyCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keyCodes"},{"name":"KeyCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"KeyCode","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"KeyCodeUtils","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keyCodes"},{"name":"KeyedTaskIdentifier","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"KeyframeEffect","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"keyFromOverrideIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"KeymapExtensions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensionsUtils"},{"name":"KeymapInfo","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/keymapInfo"},{"name":"KeymapRecommendations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/keymapRecommendations"},{"name":"KeyMod","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keyCodes"},{"name":"KeyMod","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneBase"},{"name":"KeyMod","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"KeyObject","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"keyof","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"keys","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"KeytarCredentialsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/credentials/node/credentialsService"},{"name":"KeytarCredentialsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/credentials/node/credentialsService"},{"name":"KeyValueLogProvider","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/log/common/keyValueLogProvider"},{"name":"kill","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"KillTerminalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"kMaxLength","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"buffer"},{"name":"knownAcronyms","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsLayout"},{"name":"KnownSnippetVariableNames","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetVariables"},{"name":"knownTermMappings","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsLayout"},{"name":"kStringMaxLength","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"buffer"},{"name":"Label","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWidgets"},{"name":"LabelContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/common/remote.contribution"},{"name":"LabelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/label/common/labelService"},{"name":"language","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"Language","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"LanguageConfigurationChangeEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/languageConfigurationRegistry"},{"name":"LanguageConfigurationFileHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint"},{"name":"LanguageConfigurationRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/languageConfigurationRegistry"},{"name":"LanguageConfigurationRegistryImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/languageConfigurationRegistry"},{"name":"LanguageFeatureRegistry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/languageFeatureRegistry"},{"name":"LanguageId","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"LanguageIdentifier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"LanguagePackCachedDataCleaner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner"},{"name":"languages","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"languages","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"LanguageSelector","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"languagesExtPoint","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/mode/common/workbenchModeService"},{"name":"LanguagesRegistry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/languagesRegistry"},{"name":"LanguageType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/localizations/common/localizations"},{"name":"LargeFileOptimizationsWarner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/largeFileOptimizations"},{"name":"LAST_COMPRESSED_FOLDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"lastIndex","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"lastNonWhitespaceIndex","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"lastSessionDateStorageKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetry"},{"name":"LAUNCH_CONFIGURATION_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"LaunchMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/launch/electron-main/launchMainService"},{"name":"launchSchema","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugSchemas"},{"name":"launchSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"layout","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/contextview/contextview"},{"name":"Layout","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/layout"},{"name":"LAYOUT_EDITOR_GROUPS_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"LayoutAnchorPosition","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/contextview/contextview"},{"name":"LayoutController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/gridview"},{"name":"LayoutData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget"},{"name":"LayoutPriority","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/splitview/splitview"},{"name":"LazilyResolvedWebviewEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService"},{"name":"Lazy","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/lazy"},{"name":"lazyEnv","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"LazyOutputChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostOutput"},{"name":"LazyPromise","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/lazyPromise"},{"name":"lchmod","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"lchmodSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"lchown","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"lchownSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"LcsDiff","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/diff/diff"},{"name":"lcut","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"leftest","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"leftRotate","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"LEGACY_CONSOLE_MODE_EXIT_CODE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"legacy_ENV_iKey","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Config"},{"name":"length","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"let","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"lexer","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/marked/marked"},{"name":"Lexer","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/marked/marked"},{"name":"LifecycleMainPhase","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/electron-main/lifecycleMainService"},{"name":"LifecycleMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/electron-main/lifecycleMainService"},{"name":"LifecyclePhase","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/common/lifecycle"},{"name":"LifecyclePhaseToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/common/lifecycle"},{"name":"LIGHT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"LightBulbWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/lightBulbWidget"},{"name":"lighten","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"Limiter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"LineBreakData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/splitLinesCollection"},{"name":"LineCommentCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/comment/lineCommentCommand"},{"name":"LineContext","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/completionModel"},{"name":"LineContext","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestModel"},{"name":"LineDecoder","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/decoder"},{"name":"LineDecoration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/lineDecorations"},{"name":"LineDecorationsNormalizer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/lineDecorations"},{"name":"LineNumbersOverlay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers"},{"name":"LinePartMetadata","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"LineProcess","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/processes"},{"name":"LineRange","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"LinesDecorationsOverlay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations"},{"name":"LinesLayout","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/linesLayout"},{"name":"LineStarts","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase"},{"name":"LineTokens","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/lineTokens"},{"name":"LineTokens2","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/tokensStore"},{"name":"LineVisibleRanges","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/renderingContext"},{"name":"link","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"Link","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/links/getLinks"},{"name":"Link","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/opener/browser/link"},{"name":"LINK_INTERCEPT_THRESHOLD","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminal"},{"name":"LinkComputer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/linkComputer"},{"name":"LinkDetector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/links/links"},{"name":"LinkDetector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/linkDetector"},{"name":"LinkedList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/linkedList"},{"name":"LinkedMap","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"LinkedText","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/linkedText"},{"name":"LinkProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"LinksList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/links/getLinks"},{"name":"linkSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"linuxDistro","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/node/terminal"},{"name":"LinuxDistro","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"LinuxExternalTerminalService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService"},{"name":"LinuxUpdateService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/electron-main/updateService.linux"},{"name":"List","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listWidget"},{"name":"listActiveSelectionBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listActiveSelectionForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listDeemphasizedForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"ListDragOverEffect","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/list"},{"name":"ListDragOverReactions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/list"},{"name":"listDropBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listenerCount","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"listenerCount","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"listenerCount","kind":"method","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"stream"},{"name":"listeners","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"listeners","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"ListeningStateChangedEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ListError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/list"},{"name":"listErrorForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listFilterMatchHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listFilterMatchHighlightBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listFilterWidgetBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listFilterWidgetNoMatchesOutline","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listFilterWidgetOutline","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listFocusBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listFocusForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listHighlightForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listHoverBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listHoverForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listInactiveFocusBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listInactiveSelectionBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listInactiveSelectionForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listInvalidItemForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"listProcesses","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/ps"},{"name":"ListResourceNavigator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"ListService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"ListSettingListModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"ListSettingWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"ListView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listView"},{"name":"listWarningForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"loadavg","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"LOADED_SCRIPTS_VIEW_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"LoadedScriptsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/loadedScriptsView"},{"name":"LoaderEvent","kind":"class","kindModifiers":"declare","sortText":"4"},{"name":"LoaderEventType","kind":"enum","kindModifiers":"declare","sortText":"4"},{"name":"LOADIPHLPAPI","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"loadLocalResource","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/common/resourceLoader"},{"name":"loadWASM","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/onigasm-umd/main"},{"name":"LOCAL_MACHINE_SCOPES","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"locale","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"LocalInstallAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"LocalizationsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/localizations/node/localizations"},{"name":"LocalizationsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/localizations/electron-browser/localizationsService"},{"name":"LocalizationWorkbenchContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/localizations/browser/localizations.contribution"},{"name":"localize","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/nls"},{"name":"localize","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/nls.mock"},{"name":"localizeManifest","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionNls"},{"name":"LocalSearchProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesSearch"},{"name":"LocalSearchService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/searchService"},{"name":"LocalSelectionTransfer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"localStorage","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"location","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"location","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"Location","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Location","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Location","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"locationbar","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"log","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"log","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/console"},{"name":"log","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"log","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"log","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"LOG_MIME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"LOG_MODE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"LOG_SCHEME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"LogAppender","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetryUtils"},{"name":"LogContentProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/browser/outputServices"},{"name":"Logger","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionPoints"},{"name":"LoggerChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/logIpc"},{"name":"LoggerChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/logIpc"},{"name":"LoggerService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/node/loggerService"},{"name":"LogLevel","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"LogLevel","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"LogLevel","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"LogLevel","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"LogLevel","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/workbench.web.api"},{"name":"logOnceWebWorkerWarning","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/worker/simpleWorker"},{"name":"logRemoteEntry","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/remoteConsoleUtil"},{"name":"LogsDataCleaner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/common/logsDataCleaner"},{"name":"LogsDataCleaner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner"},{"name":"LogServiceAdapter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"logStorage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/common/storage"},{"name":"LogViewer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/browser/logViewer"},{"name":"LogViewerInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/browser/logViewer"},{"name":"LONG_LINE_BOUNDARY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"LongRunningOperation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/progress/common/progress"},{"name":"lookup","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"lookupService","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"LowerCaseAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"LRUCache","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"LRUMemory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestMemory"},{"name":"lstat","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"lstat","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"lstatSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"lt","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"lte","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"ltr","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"ltrim","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"lutimes","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"lutimesSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"MacExternalTerminalService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService"},{"name":"machineIdKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetry"},{"name":"machineOverridableSettings","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"machineSettings","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"machineSettingsSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"MacLinuxFallbackKeyboardMapper","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper"},{"name":"MacLinuxKeyboardMapper","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper"},{"name":"macLinuxKeyboardMappingEquals","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper"},{"name":"main","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/web.main"},{"name":"main","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/node/cli"},{"name":"main","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/node/cliProcessMain"},{"name":"main","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/desktop.main"},{"name":"Main","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/node/cliProcessMain"},{"name":"MainContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"mainLogChannelId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/common/logConstants"},{"name":"mainModule","kind":"property","kindModifiers":"declare,optional","sortText":"5","hasAction":true,"source":"process"},{"name":"MainPane","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/mainPane"},{"name":"MainPaneDescriptor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/mainPane"},{"name":"MainProcessService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/ipc/electron-browser/mainProcessService"},{"name":"MAINTAIN_UNDO_REDO_STACK","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/modelServiceImpl"},{"name":"MainThreadAuthentication","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadAuthentication"},{"name":"MainThreadAuthenticationProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadAuthentication"},{"name":"MainThreadClipboard","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadClipboard"},{"name":"MainThreadCommands","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadCommands"},{"name":"MainThreadCommentController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadComments"},{"name":"MainThreadComments","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadComments"},{"name":"MainThreadCommentThread","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadComments"},{"name":"MainThreadConfiguration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadConfiguration"},{"name":"MainThreadConsole","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadConsole"},{"name":"MainThreadDebugService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadDebugService"},{"name":"MainThreadDecorations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadDecorations"},{"name":"MainThreadDiagnostics","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadDiagnostics"},{"name":"MainThreadDialogs","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadDialogs"},{"name":"MainThreadDocumentContentProviders","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadDocumentContentProviders"},{"name":"MainThreadDocumentRangeSemanticTokensProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadLanguageFeatures"},{"name":"MainThreadDocuments","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadDocuments"},{"name":"MainThreadDocumentsAndEditors","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors"},{"name":"MainThreadDocumentSemanticTokensProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadLanguageFeatures"},{"name":"MainThreadDownloadService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadDownloadService"},{"name":"MainThreadEditorInsets","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadCodeInsets"},{"name":"MainThreadErrors","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadErrors"},{"name":"MainThreadExtensionService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadExtensionService"},{"name":"MainThreadFileSystem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadFileSystem"},{"name":"MainThreadFileSystemEventService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadFileSystemEventService"},{"name":"MainThreadKeytar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadKeytar"},{"name":"MainThreadLabelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadLabelService"},{"name":"MainThreadLanguageFeatures","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadLanguageFeatures"},{"name":"MainThreadLanguages","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadLanguages"},{"name":"MainThreadLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadLogService"},{"name":"MainThreadMessageService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadMessageService"},{"name":"MainThreadNotebookController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadNotebook"},{"name":"MainThreadNotebookDocument","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadNotebook"},{"name":"MainThreadNotebooks","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadNotebook"},{"name":"MainThreadOutputService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadOutputService"},{"name":"MainThreadProgress","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadProgress"},{"name":"MainThreadQuickOpen","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadQuickOpen"},{"name":"MainThreadSCM","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadSCM"},{"name":"MainThreadSearch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadSearch"},{"name":"MainThreadStatusBar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadStatusBar"},{"name":"MainThreadStorage","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadStorage"},{"name":"MainThreadTask","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadTask"},{"name":"MainThreadTelemetry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadTelemetry"},{"name":"MainThreadTerminalService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadTerminalService"},{"name":"MainThreadTextEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadEditor"},{"name":"MainThreadTextEditorProperties","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadEditor"},{"name":"MainThreadTextEditors","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadEditors"},{"name":"MainThreadTheming","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadTheming"},{"name":"MainThreadTimeline","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadTimeline"},{"name":"MainThreadTreeViews","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadTreeViews"},{"name":"MainThreadTunnelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadTunnelService"},{"name":"MainThreadUrls","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadUrls"},{"name":"MainThreadWebviews","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadWebview"},{"name":"MainThreadWindow","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadWindow"},{"name":"MainThreadWorkspace","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadWorkspace"},{"name":"major","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"MakeAddress","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/remoteExplorerService"},{"name":"MaliciousExtensionChecker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewlet"},{"name":"MaliciousStatusLabelAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ManageAuthorizedExtensionURIsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/browser/extensionUrlHandler"},{"name":"ManageAutomaticTaskRunning","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks"},{"name":"ManageExtensionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ManageExtensionsQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsQuickAccess"},{"name":"ManagementPersistentConnection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"manageTrustedDomainSettingsCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/url/common/trustedDomains"},{"name":"MANIFEST_CACHE_FOLDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensions"},{"name":"Map","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"mapArrayOrNot","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"mapPager","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/paging"},{"name":"mapToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"Margin","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/margin/margin"},{"name":"MarginViewLineDecorationsOverlay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/marginDecorations/marginDecorations"},{"name":"MarginViewOverlays","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewOverlays"},{"name":"mark","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/performance"},{"name":"markAsFileSystemProviderError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"MarkdownCellRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer"},{"name":"MarkdownCellViewModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel"},{"name":"markdownEscapeEscapedCodicons","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/codicons"},{"name":"MarkdownRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/markdown/markdownRenderer"},{"name":"MarkdownRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer"},{"name":"MarkdownString","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"MarkdownString","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/htmlContent"},{"name":"MarkdownString","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"MarkdownString","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"markdownUnescapeCodicons","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/codicons"},{"name":"marked","kind":"alias","kindModifiers":"declare","sortText":"4"},{"name":"markedStringsEquals","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/htmlContent"},{"name":"Marker","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"Marker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersModel"},{"name":"MarkerController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoError/gotoError"},{"name":"MarkerDecorationsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/markerDecorationsServiceImpl"},{"name":"MarkerNavigationWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoError/gotoErrorWidget"},{"name":"MarkerRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersTreeViewer"},{"name":"MarkerService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/markers/common/markerService"},{"name":"MarkerSeverity","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/markers/common/markers"},{"name":"MarkerSeverity","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"MarkerSeverity","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"MarkersFilterActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersViewActions"},{"name":"MarkersFilters","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersViewActions"},{"name":"MarkersModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersModel"},{"name":"MarkersTreeAccessibilityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersTreeViewer"},{"name":"MarkersView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersView"},{"name":"MarkersViewModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersTreeViewer"},{"name":"MarkersWorkbenchService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markers"},{"name":"MarkerTag","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/markers/common/markers"},{"name":"MarkerTag","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"MarkerTag","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"MarkerViewModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersTreeViewer"},{"name":"markTimeline","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"mas","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"match","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/glob"},{"name":"match","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"Match","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchModel"},{"name":"MATCHES_LIMIT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"matchesCamelCase","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"matchesContiguousSubString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"matchesFuzzy","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"matchesFuzzy2","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"matchesFuzzyCodiconAware","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/codicon"},{"name":"matchesPrefix","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"matchesScheme","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/opener/common/opener"},{"name":"matchesStrictPrefix","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"matchesSubString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"matchesWords","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"MatchFindAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"MatchFocusKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"matchMedia","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"MatchRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchResultsView"},{"name":"Math","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MAX_BYTES_ON_DISK","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"MAX_CONNECTION_FAILURES_BEFORE_WARN","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"MAX_FILE_SIZE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"MAX_FOLDING_REGIONS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingRanges"},{"name":"MAX_HEAP_SIZE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"MAX_LINE_NUMBER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingRanges"},{"name":"MAX_OUTPUT_LENGTH","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"MAX_VALUE_RENDER_LENGTH_IN_VIEWLET","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/baseDebugView"},{"name":"maxHeaderSize","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"MaximizeGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"maxSatisfying","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"mayIncludeActionsOfKind","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/types"},{"name":"measure","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"Measurement","kind":"enum member","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/DataPointType"},{"name":"MediaDeviceInfo","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaDevices","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaElementAudioSourceNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaEncryptedEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaKeyMessageEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaKeys","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaKeySession","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaKeyStatusMap","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaKeySystemAccess","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaQueryList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaQueryListEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaSource","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaStream","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaStreamAudioDestinationNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaStreamAudioSourceNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaStreamError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaStreamErrorEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaStreamEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaStreamTrack","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaStreamTrackAudioSourceNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MediaStreamTrackEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Memento","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/memento"},{"name":"memoize","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/decorators"},{"name":"memory","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"Memory","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestMemory"},{"name":"memoryUsage","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"Menu","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"Menu","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/menu/menu"},{"name":"MENU_ESCAPED_MNEMONIC_REGEX","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/menu/menu"},{"name":"MENU_MNEMONIC_REGEX","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/menu/menu"},{"name":"menuBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"menubar","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Menubar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/menubar/electron-main/menubar"},{"name":"MenuBar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/menu/menubar"},{"name":"MENUBAR_SELECTION_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"MENUBAR_SELECTION_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"MENUBAR_SELECTION_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"MenubarControl","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/titlebar/menubarControl"},{"name":"MenubarMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/menubar/electron-main/menubarMainService"},{"name":"MenubarService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/menubar/electron-browser/menubarService"},{"name":"menuBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"MenuEntryActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/browser/menuEntryActionViewItem"},{"name":"menuForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"MenuId","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"MenuItem","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"MenuItemAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"MenuItemExtensionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"MenuPreventer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/menuPreventer"},{"name":"MenuRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"menuSelectionBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"menuSelectionBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"menuSelectionForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"menuSeparatorBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"MenuService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/menuService"},{"name":"merge","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"merge","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/settingsMerge"},{"name":"merge","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/extensionsMerge"},{"name":"merge","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/keybindingsMerge"},{"name":"merge","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/globalStateMerge"},{"name":"merge","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/snippetsMerge"},{"name":"mergeAllGroups","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"mergeBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"mergeChanges","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationModels"},{"name":"mergeCommonContentBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"mergeCommonHeaderBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"mergeCurrentContentBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"mergeCurrentHeaderBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"MergedEnvironmentVariableCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/environmentVariableCollection"},{"name":"mergeEnvironments","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment"},{"name":"MergeGroupMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorGroupsService"},{"name":"mergeIncomingContentBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"mergeIncomingHeaderBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"mergePagers","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/paging"},{"name":"mergeSort","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"MergeWindowTabsHandlerHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"MessageChannel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MessageChannel","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"worker_threads"},{"name":"MessageController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/message/messageController"},{"name":"MessageData","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/MessageData"},{"name":"MessageData","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"MessageEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MessagePort","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MessagePort","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"worker_threads"},{"name":"Messages","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/messages"},{"name":"MessageType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/inputbox/inputBox"},{"name":"MessageType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionHostProtocol"},{"name":"MetadataConsts","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"MetadataConsts","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/common/textMateService"},{"name":"MetadataConsts","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-textmate/release/main"},{"name":"METHODS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"MetricData","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/MetricData"},{"name":"MetricData","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"MIME_BINARY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/mime"},{"name":"MIME_TEXT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/mime"},{"name":"MIME_UNKNOWN","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/mime"},{"name":"MimeType","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MimeTypeArray","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MimeTypeRendererResolver","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"mimeTypeSupportedByCore","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"MIN_MAX_MEMORY_SIZE_MB","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/files"},{"name":"Minimap","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/minimap/minimap"},{"name":"MINIMAP_GUTTER_WIDTH","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"minimapBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"MinimapCharRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer"},{"name":"MinimapCharRendererFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/minimap/minimapCharRendererFactory"},{"name":"minimapError","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"minimapFindMatch","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"minimapGutterAddedBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"minimapGutterDeletedBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"minimapGutterModifiedBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"MinimapLinesRenderingData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/viewModel"},{"name":"MinimapPosition","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"MinimapPosition","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"minimapSelection","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"minimapSliderActiveBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"minimapSliderBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"minimapSliderHoverBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"MinimapTokensColorTracker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/minimapTokensColorTracker"},{"name":"minimapWarning","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"minimist","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/minimist/index"},{"name":"MinimizeOtherGroupsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MINIMUM_LETTER_SPACING","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"minimumTranslatedStrings","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/localizations/browser/minimalTranslations"},{"name":"minor","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"minSatisfying","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"minVersion","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"MirrorTextModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/mirrorTextModel"},{"name":"MissingDependencyError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensions"},{"name":"mixin","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/objects"},{"name":"mkdir","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"mkdirp","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"mkdirSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"mkdtemp","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"mkdtempSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"mnemonicButtonLabel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"mnemonicMenuLabel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"mocha","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Mocha","kind":"class","kindModifiers":"declare","sortText":"4"},{"name":"mock","kind":"alias","kindModifiers":"","sortText":"0"},{"name":"MockContextKeyService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/test/common/mockKeybindingService"},{"name":"MockDebugAdapter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/test/common/mockDebug"},{"name":"MockDebugService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/test/common/mockDebug"},{"name":"MockKeybindingService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/test/common/mockKeybindingService"},{"name":"MockMode","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/mocks/mockMode"},{"name":"MockObjectTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/test/browser/mockSearchTree"},{"name":"MockRawSession","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/test/common/mockDebug"},{"name":"MockSession","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/test/common/mockDebug"},{"name":"MockTextAreaWrapper","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/browser/controller/textAreaState.test"},{"name":"ModelBasedVariableResolver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetVariables"},{"name":"ModelConstants","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"ModelDecorationMinimapOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"ModelDecorationOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"ModelDecorationOverviewRulerOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"ModelRawContentChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelEvents"},{"name":"ModelRawEOLChanged","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelEvents"},{"name":"ModelRawFlush","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelEvents"},{"name":"ModelRawLineChanged","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelEvents"},{"name":"ModelRawLinesDeleted","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelEvents"},{"name":"ModelRawLinesInserted","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelEvents"},{"name":"ModelServiceImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/modelServiceImpl"},{"name":"ModelTransientSettingWatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/services/abstractCodeEditorService"},{"name":"ModesContentHoverWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/hover/modesContentHover"},{"name":"ModeServiceImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/modeServiceImpl"},{"name":"ModesGlyphHoverWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/hover/modesGlyphHover"},{"name":"ModesHoverController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/hover/hover"},{"name":"ModesRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/modesRegistry"},{"name":"MODIFIED_SETTING_TAG","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"modifiedItemIndicator","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"ModifierLabelProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keybindingLabels"},{"name":"modify","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"modifySearchEditorContextLinesCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"module","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Module","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"module"},{"name":"monaco","kind":"module","kindModifiers":"declare","sortText":"4"},{"name":"MonacoEnvironment","kind":"let","kindModifiers":"declare","sortText":"4"},{"name":"MonarchBracket","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"MonarchTokenizer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchLexer"},{"name":"monitorEventLoopDelay","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"perf_hooks"},{"name":"MonospaceLineBreaksComputerFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/monospaceLineBreaksComputer"},{"name":"MouseController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listWidget"},{"name":"MouseEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MouseHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/mouseHandler"},{"name":"MouseTarget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/mouseTarget"},{"name":"MouseTargetFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/mouseTarget"},{"name":"MouseTargetType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorBrowser"},{"name":"MouseTargetType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"MouseWheelClassifier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/scrollableElement"},{"name":"move","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"move","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"MOVE_ACTIVE_EDITOR_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"MOVE_FILE_TO_TRASH_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"moveBy","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"MoveCaretCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/caretOperations/moveCaretCommand"},{"name":"MoveCellEdit","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/cellEdit"},{"name":"moveCursor","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"readline"},{"name":"MoveEditorLeftInGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveEditorRightInGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveEditorToAboveGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveEditorToBelowGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveEditorToFirstGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveEditorToLastGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveEditorToLeftGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveEditorToNextGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveEditorToPreviousGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveEditorToRightGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"moveFileToTrashHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"MoveFocusedViewAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"MoveGroupDownAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveGroupLeftAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveGroupRightAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveGroupUpAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"MoveLinesCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/moveLinesCommand"},{"name":"MoveOperations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorMoveOperations"},{"name":"MoveSelectionToNextFindMatchAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"MoveSelectionToPreviousFindMatchAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"moveTo","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"MoveToNextChangeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"MoveToPreviousChangeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"MoveWindowTabToNewWindowHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"MoveWordCommand","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"MSAssertion","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSBlobBuilder","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"msContentScript","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSFIDOCredentialAssertion","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSFIDOSignature","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSFIDOSignatureAssertion","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSGesture","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSGestureEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSGraphicsTrust","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSInputMethodContext","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSMediaKeyError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSMediaKeyMessageEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSMediaKeyNeededEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSMediaKeys","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSMediaKeySession","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSPointerEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MSStream","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"msWriteProfilerMark","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"MultiCursorSelectionController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"MultiCursorSelectionControllerAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"MultiCursorSession","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"MultiCursorSessionResult","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"MultilineTokens","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/tokensStore"},{"name":"MultilineTokens2","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/tokensStore"},{"name":"MultilineTokensBuilder","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/tokensStore"},{"name":"MultiModelEditStackElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/editStack"},{"name":"MultipleEditorGroupsContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"MultiplexLayoutController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/gridview"},{"name":"MultiplexLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"multiSelectModifierSettingKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"MutableDisposable","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/lifecycle"},{"name":"MutationEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MutationObserver","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"MutationRecord","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Mutex","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/windows-mutex/index"},{"name":"MyArray","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/diff/diff"},{"name":"MyEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/parts/editor/baseEditor.test"},{"name":"MyOtherEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/parts/editor/baseEditor.test"},{"name":"name","kind":"const","kindModifiers":"declare","sortText":"4"},{"name":"NamedNodeMap","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"namespace","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Namespace","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"NaN","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"NativeAccessibilityService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/accessibility/electron-browser/accessibilityService"},{"name":"NativeBackupTracker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/backup/electron-browser/backupTracker"},{"name":"NativeClipboardService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/clipboard/electron-browser/clipboardService"},{"name":"NativeExtHostSearch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/node/extHostSearch"},{"name":"nativeImage","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"NativeImage","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"NativeLifecycleService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/lifecycle/electron-browser/lifecycleService"},{"name":"NativePathService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/path/electron-browser/pathService"},{"name":"NativeRequestService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/request/electron-browser/requestService"},{"name":"NativeResolvedKeybinding","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper"},{"name":"NativeResourceIdentityService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/resource/node/resourceIdentityServiceImpl"},{"name":"NativeSimpleFileDialog","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/electron-browser/simpleFileDialog"},{"name":"NativeStorageService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/node/storageService"},{"name":"NativeTelemetryOptOut","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-browser/telemetryOptOut"},{"name":"NativeTextFileEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/electron-browser/textFileEditor"},{"name":"NativeTextFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService"},{"name":"NativeTextSearchManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/textSearchManager"},{"name":"nativeTheme","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"NativeUpdateService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/update/electron-browser/updateService"},{"name":"NativeWindow","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/window"},{"name":"NativeWorkbenchEnvironmentService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/environment/electron-browser/environmentService"},{"name":"NativeWorkspaceEditingService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workspaces/electron-browser/workspaceEditingService"},{"name":"NativeWorkspacesService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workspaces/electron-browser/workspacesService"},{"name":"NavigateBackwardsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"NavigateBetweenGroupsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"NavigateForwardAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"NavigateLastAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"NavigateToLastEditLocationAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"NavigationModeAddon","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/addons/navigationModeAddon"},{"name":"NavigationPreloadManager","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"navigator","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Navigator","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"neq","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"net","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"netLog","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"networkInterfaces","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"never","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"NEVER_MEASURE_RENDER_TIME_STORAGE_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"NeverShowAgainScope","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/notification/common/notification"},{"name":"new","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"NEW_FILE_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"NEW_FILE_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"NEW_FOLDER_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"NEW_FOLDER_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"NewEditorGroupAboveAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"NewEditorGroupBelowAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"NewEditorGroupLeftAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"NewEditorGroupRightAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"NewExplorerItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/explorerModel"},{"name":"NewFileAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"NewFolderAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"NewWindowAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/windowActions"},{"name":"NewWindowAPICommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/apiCommands"},{"name":"newWindowCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"NewWindowTabHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"newWriteableBufferStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"newWriteableStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/stream"},{"name":"NEXT_COMPRESSED_FOLDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"nextCharLength","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"NextCommentThreadAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsEditorContribution"},{"name":"NextMarkerAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoError/gotoError"},{"name":"NextMatchFindAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"NextMatchFindAction2","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"NextPanelViewAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"NextSelectionMatchFindAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"NextSideBarViewAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/activitybar/activitybarActions"},{"name":"nextTick","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/process"},{"name":"nextTick","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"NO_KEY_MODS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/quickinput/common/quickInput"},{"name":"noAsar","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"NODATA","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"Node","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"nodeAcceptEdit","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"NodeCachedDataCleaner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner"},{"name":"NodeClient","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/NodeClient"},{"name":"NodeColor","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"NodeColor","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"NodeDebugHelperService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/node/debugHelperService"},{"name":"NodeFilter","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"NodeIterator","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"NodeJS","kind":"module","kindModifiers":"declare","sortText":"4"},{"name":"NodeList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"noDeprecation","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"nodeReadableToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/stream"},{"name":"NodeSocket","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc.net"},{"name":"nodeSocketFactory","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/node/nodeSocketFactory"},{"name":"nodesToArrays","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/browser/ui/grid/util"},{"name":"nodeStreamToVSBufferReadable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/stream"},{"name":"NodeTestBackupFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test"},{"name":"NoEditorsVisibleContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"noIntlCompareFileNames","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/comparers"},{"name":"NOMEM","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"NoMemory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestMemory"},{"name":"NONAME","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"NoOpNotification","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/notification/common/notification"},{"name":"NoOpProgress","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/notification/common/notification"},{"name":"NoOpWorkspaceTagsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tags/browser/workspaceTagsService"},{"name":"normalize","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"normalize","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"normalizeDriveLetter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"normalizeExpression","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/test/browser/queryBuilder.test"},{"name":"normalizeFileChanges","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/watcher"},{"name":"normalizeGitHubUrl","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/issue/common/issueReporterUtil"},{"name":"normalizeNFC","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/normalization"},{"name":"normalizeNFD","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/normalization"},{"name":"normalizePath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"normalizeRoots","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/unix/chokidarWatcherService"},{"name":"normalizeVersion","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensionValidator"},{"name":"NoTabsTitleControl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/noTabsTitleControl"},{"name":"notDeepEqual","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"notDeepStrictEqual","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"notebook","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"NOTEBOOK_CELL_EDITABLE_CONTEXT_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"NOTEBOOK_CELL_MARKDOWN_EDIT_MODE_CONTEXT_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"NOTEBOOK_CELL_RUN_STATE_CONTEXT_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"NOTEBOOK_CELL_TYPE_CONTEXT_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"NOTEBOOK_DISPLAY_ORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"NOTEBOOK_EDITABLE_CONTEXT_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"NOTEBOOK_EDITOR_CURSOR_BOUNDARY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"NOTEBOOK_EDITOR_EDITABLE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookBrowser"},{"name":"NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookBrowser"},{"name":"NOTEBOOK_EDITOR_FOCUSED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookBrowser"},{"name":"NOTEBOOK_EXECUTING_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"NOTEBOOK_VIEW_TYPE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/constants"},{"name":"NotebookCellList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/notebookCellList"},{"name":"NotebookCellListDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer"},{"name":"NotebookCellMetadataDefaults","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel"},{"name":"NotebookCellTextModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel"},{"name":"NotebookCodeEditors","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookEditor"},{"name":"NotebookContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebook.contribution"},{"name":"notebookDocumentMetadataDefaults","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"NotebookEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookEditor"},{"name":"NotebookEditorCellEdit","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostNotebook"},{"name":"NotebookEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookEditorInput"},{"name":"NotebookEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookEditorInput"},{"name":"NotebookEditorOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookEditor"},{"name":"NotebookEventDispatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher"},{"name":"NotebookFindWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/contrib/notebookFindWidget"},{"name":"NotebookLayoutChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher"},{"name":"NotebookMetadataChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher"},{"name":"notebookOutputContainerColor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookEditor"},{"name":"NotebookOutputRendererInfo","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookOutputRenderer"},{"name":"NotebookOutputRendererInfoStore","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookService"},{"name":"notebookProviderExtensionPoint","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/extensionPoint"},{"name":"NotebookProviderInfo","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookProvider"},{"name":"NotebookProviderInfoStore","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookService"},{"name":"NotebookRegistry","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookRegistry"},{"name":"notebookRendererExtensionPoint","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/extensionPoint"},{"name":"NotebookService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookService"},{"name":"NotebookTextModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/model/notebookTextModel"},{"name":"NotebookViewEventType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher"},{"name":"NotebookViewModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel"},{"name":"notEqual","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"NOTFOUND","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"Notification","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Notification","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"NotificationActionRunner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsActions"},{"name":"NotificationChangeType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/notifications"},{"name":"NotificationFocusedContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"NotificationHandle","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/notifications"},{"name":"NotificationRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsViewer"},{"name":"NOTIFICATIONS_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NOTIFICATIONS_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NOTIFICATIONS_CENTER_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NOTIFICATIONS_CENTER_HEADER_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NOTIFICATIONS_CENTER_HEADER_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NOTIFICATIONS_ERROR_ICON_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NOTIFICATIONS_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NOTIFICATIONS_INFO_ICON_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NOTIFICATIONS_LINKS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NOTIFICATIONS_TOAST_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NOTIFICATIONS_WARNING_ICON_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"NotificationsAlerts","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsAlerts"},{"name":"NotificationsCenter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCenter"},{"name":"NotificationsCenterVisibleContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"NotificationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/notification/common/notificationService"},{"name":"NotificationsFilter","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/notification/common/notification"},{"name":"NotificationsList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsList"},{"name":"NotificationsListDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsViewer"},{"name":"NotificationsModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/notifications"},{"name":"NotificationsStatus","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsStatus"},{"name":"NotificationsToasts","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsToasts"},{"name":"NotificationsToastsVisibleContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"NotificationTemplateRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsViewer"},{"name":"NotificationViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/notifications"},{"name":"NotificationViewItemContentChangeKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/notifications"},{"name":"NotificationViewItemProgress","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/notifications"},{"name":"NOTIMP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"NotImplementedError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"NOTINITIALIZED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"notStrictEqual","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"NsfwWatcherService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/nsfw/nsfwWatcherService"},{"name":"null","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"NULL_LANGUAGE_IDENTIFIER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/nullMode"},{"name":"NULL_MODE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/nullMode"},{"name":"NULL_STATE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/nullMode"},{"name":"NullApiDeprecationService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostApiDeprecationService"},{"name":"NullAppender","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetryUtils"},{"name":"NullCommandService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/commands/common/commands"},{"name":"nullExtensionDescription","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensions"},{"name":"NullExtensionService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensions"},{"name":"NullFileSystemProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/test/common/nullFileSystemProvider"},{"name":"NullLifecycleService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/common/lifecycle"},{"name":"NullLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/common/log"},{"name":"NullOpenerService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/opener/common/opener"},{"name":"nullRange","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"NullTelemetryService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetryUtils"},{"name":"nullTokenize","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/nullMode"},{"name":"nullTokenize2","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/nullMode"},{"name":"number","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Number","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"NumberBadge","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/activity/common/activity"},{"name":"O_APPEND","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_CREAT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_DIRECT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_DIRECTORY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_DSYNC","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_EXCL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_NOATIME","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_NOCTTY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_NOFOLLOW","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_NONBLOCK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_RDONLY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_RDWR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_SYMLINK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_SYNC","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_TRUNC","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"O_WRONLY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"object","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Object","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ObjectIdentifier","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"ObjectTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/objectTree"},{"name":"ObjectTreeModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/objectTreeModel"},{"name":"off","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"OfflineAudioCompletionEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OfflineAudioContext","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"offscreenBuffering","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OffscreenCanvas","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OffscreenCanvasRenderingContext2D","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ok","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"ok","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/assert"},{"name":"on","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"on","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"onabort","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onafterprint","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onanimationcancel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onanimationend","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onanimationiteration","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onanimationstart","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onauxclick","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onbeforeprint","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onbeforeunload","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onblur","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"oncancel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"oncanplay","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"oncanplaythrough","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"once","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"once","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"events"},{"name":"once","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/functional"},{"name":"once","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"onchange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onclick","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onclose","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"oncompassneedscalibration","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"oncontextmenu","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"oncuechange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondblclick","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondevicelight","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondevicemotion","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondeviceorientation","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondeviceorientationabsolute","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onDidChangeFullscreen","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"onDidChangeKeyboardLayout","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/native-keymap/index"},{"name":"onDidChangeModelLanguage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"onDidChangeZoomLevel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"onDidCreateEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"onDidCreateModel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"ondrag","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondragend","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondragenter","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondragexit","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondragleave","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondragover","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondragstart","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondrop","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ondurationchange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OneCursor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/oneCursor"},{"name":"OneLineRange","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"onemptied","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onended","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OnEnterSupport","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/onEnter"},{"name":"oneOf","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"OneReference","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/referencesModel"},{"name":"OneReferenceRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesTree"},{"name":"onerror","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OneSnippet","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetSession"},{"name":"onExtensionChanged","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensionsUtils"},{"name":"onfocus","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ongotpointercapture","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onhashchange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OnigScanner","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/onigasm-umd/main"},{"name":"OnigString","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/onigasm-umd/main"},{"name":"oninput","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"oninvalid","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onkeydown","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onkeypress","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onkeyup","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onLanguage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"onlanguagechange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ONLINE_SERVICES_SETTING_TAG","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"onload","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onloadeddata","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onloadedmetadata","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onloadstart","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onlostpointercapture","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmessage","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmessageerror","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmousedown","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmouseenter","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmouseleave","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmousemove","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmouseout","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmouseover","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmouseup","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmousewheel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmsgesturechange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmsgesturedoubletap","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmsgestureend","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmsgesturehold","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmsgesturestart","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmsgesturetap","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmsinertiastart","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmspointercancel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmspointerdown","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmspointerenter","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmspointerleave","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmspointermove","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmspointerout","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmspointerover","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onmspointerup","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onoffline","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ononline","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onorientationchange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpagehide","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpageshow","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpause","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onplay","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onplaying","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpointercancel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpointerdown","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpointerenter","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpointerleave","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpointermove","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpointerout","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpointerover","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpointerup","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onpopstate","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onprogress","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onratechange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onreadystatechange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onrejectionhandled","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onreset","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onresize","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onscroll","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onsecuritypolicyviolation","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onseeked","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onseeking","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onselect","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onselectionchange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onselectstart","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onstalled","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onstorage","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onsubmit","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onsuspend","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ontimeupdate","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ontoggle","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ontouchcancel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ontouchend","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ontouchmove","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ontouchstart","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ontransitioncancel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ontransitionend","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ontransitionrun","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ontransitionstart","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OnTypeFormattingEditProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"OnTypeRenameAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/rename/onTypeRename"},{"name":"OnTypeRenameContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/rename/onTypeRename"},{"name":"OnTypeRenameProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"onUnexpectedError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"onUnexpectedExternalError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"onunhandledrejection","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onunload","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onvolumechange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onvrdisplayactivate","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onvrdisplayblur","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onvrdisplayconnect","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onvrdisplaydeactivate","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onvrdisplaydisconnect","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onvrdisplayfocus","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onvrdisplaypointerrestricted","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onvrdisplaypointerunrestricted","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onvrdisplaypresentchange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onwaiting","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onwheel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"onWillDisposeModel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"open","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"open","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"open","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"inspector"},{"name":"open","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/yauzl/index"},{"name":"OPEN_CREATE","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-sqlite3/index"},{"name":"OPEN_EDITOR_AT_INDEX_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"OPEN_PRIVATECACHE","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-sqlite3/index"},{"name":"OPEN_READONLY","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-sqlite3/index"},{"name":"OPEN_READWRITE","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-sqlite3/index"},{"name":"OPEN_SHAREDCACHE","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-sqlite3/index"},{"name":"OPEN_TO_SIDE_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"OPEN_URI","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-sqlite3/index"},{"name":"OpenAPICommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/apiCommands"},{"name":"openBreakpointSource","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/breakpointsView"},{"name":"OpenContext","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/node/window"},{"name":"OpenDebugConsoleAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugViewlet"},{"name":"opendir","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"opendirSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"OpenEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"OpenEditorsFocusedContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"OpenEditorsGroupContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"OpenEditorsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/openEditorsView"},{"name":"OpenEditorsVisibleContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"opener","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OpenerService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/services/openerService"},{"name":"OpenerValidatorContributions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/url/common/trustedDomainsValidator"},{"name":"OpenExplorerViewletAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/files.contribution"},{"name":"OpenExtensionLogsFolderAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/electron-browser/logsActions"},{"name":"OpenExtensionsFolderAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions"},{"name":"OpenExtensionsViewletAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"OpenFileAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceActions"},{"name":"OpenFileFolderAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceActions"},{"name":"openFilePreserveFocusHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"OpenFirstEditorInGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"OpenFolderAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceActions"},{"name":"OpenFolderAPICommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/apiCommands"},{"name":"OpenInEditorCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"OpenInEditorCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"OpenIssueReporter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/apiCommands"},{"name":"OpenIssueReporterActionId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/issue/common/commands"},{"name":"OpenLastEditorInGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"OpenLatestReleaseNotesInBrowserAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/browser/update"},{"name":"OpenLocalFileCommand","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/browser/simpleFileDialog"},{"name":"OpenLocalFileFolderCommand","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/browser/simpleFileDialog"},{"name":"OpenLocalFolderCommand","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/browser/simpleFileDialog"},{"name":"OpenLogsFolderAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/electron-browser/logsActions"},{"name":"OpenMatchToSide","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"openModeSettingKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"OpenNewEditorCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"OpenNewEditorToSideCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"OpenNextEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"OpenNextEditorInGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"OpenNextRecentlyUsedEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"OpenNextRecentlyUsedEditorInGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"OpenPreviousEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"OpenPreviousEditorInGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"OpenPreviousRecentlyUsedEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"OpenPreviousRecentlyUsedEditorInGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"OpenProcessExplorer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/issue/electron-browser/issueActions"},{"name":"OpenRecentAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/windowActions"},{"name":"OpenResultsInEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"OpenSearchEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"OpenSearchEditorToSideAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"openSearchView","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"OpenSearchViewletAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"openStdin","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"openSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"OpenUrlAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/url/common/url.contribution"},{"name":"OpenViewPickerAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess"},{"name":"OpenWebviewDeveloperToolsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/electron-browser/webviewCommands"},{"name":"openWindowCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"OpenWindowSessionLogFileAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/common/logsActions"},{"name":"OpenWithAPICommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/apiCommands"},{"name":"OpenWorkspaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceActions"},{"name":"OpenWorkspaceButtonContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorWidgets"},{"name":"OpenWorkspaceConfigFileAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceActions"},{"name":"OperatingSystem","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"Option","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"optional","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/instantiation"},{"name":"OPTIONS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/argv"},{"name":"or","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/filters"},{"name":"OrganizeImportsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionCommands"},{"name":"organizeImportsCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeAction"},{"name":"orientation","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Orientation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/sash/sash"},{"name":"origin","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"originalFSPath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"orthogonal","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/gridview"},{"name":"OS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"OS_PROVIDES_FILE_PROTECTION","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"OscillatorNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"outerHeight","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"outerWidth","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OutgoingMessage","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"OutlineAccessibilityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlineConfigKeys","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outline"},{"name":"OutlineDataSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlineElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineModel"},{"name":"OutlineElementRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlineElementTemplate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlineFilter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlineGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineModel"},{"name":"OutlineGroupRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlineGroupTemplate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlineIdentityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlineItemComparator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlineModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineModel"},{"name":"OutlineNavigationLabelProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlinePane","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/outline/browser/outlinePane"},{"name":"OutlineSortOrder","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutlineViewFiltered","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outline"},{"name":"OutlineViewFocused","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outline"},{"name":"OutlineViewId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outline"},{"name":"OutlineVirtualDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"OutOfProcessWin32FolderWatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/win32/csharpWatcherService"},{"name":"OUTPUT_MIME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"OUTPUT_MODE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"OUTPUT_SCHEME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"OUTPUT_SERVICE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"OUTPUT_VIEW_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/output"},{"name":"OutputAppender","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/output/node/outputAppender"},{"name":"OutputBuilder","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/keybindingIO"},{"name":"OutputChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepSearchUtils"},{"name":"OutputChannelModelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/output/common/outputChannelModelService"},{"name":"OutputChannelModelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/output/electron-browser/outputChannelModelService"},{"name":"OutputEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/browser/outputView"},{"name":"OutputLinkComputer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/outputLinkComputer"},{"name":"OutputLinkProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/common/outputLinkProvider"},{"name":"OutputPosition","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/splitLinesCollection"},{"name":"OutputRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer"},{"name":"OutputService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/browser/outputServices"},{"name":"OutputViewPane","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/output/browser/outputView"},{"name":"outside","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"OverconstrainedError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OverflowEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"OverlayWidgetDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/zoneWidget/zoneWidget"},{"name":"OverlayWidgetPositionPreference","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorBrowser"},{"name":"OverlayWidgetPositionPreference","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"OVERRIDE_PROPERTY_PATTERN","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"overrideIdentifierFromKey","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"OverviewRuler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler"},{"name":"overviewRulerAddedForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"overviewRulerCommentingRangeForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentGlyphWidget"},{"name":"overviewRulerCommonContentForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"overviewRulerCurrentContentForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"overviewRulerDeletedForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"overviewRulerError","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"overviewRulerFindMatchForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"overviewRulerIncomingContentForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"overviewRulerInfo","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"OverviewRulerLane","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"OverviewRulerLane","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"OverviewRulerLane","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"overviewRulerModifiedForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"overviewRulerRangeHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"overviewRulerSelectionHighlightForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"overviewRulerWarning","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/editorColorRegistry"},{"name":"OverviewRulerZone","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/overviewZoneManager"},{"name":"OverviewZoneManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/overviewZoneManager"},{"name":"package","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"pad","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"PageCoordinates","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorDom"},{"name":"PagedList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/listPaging"},{"name":"PagedModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/paging"},{"name":"PagedScreenReaderStrategy","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/textAreaState"},{"name":"PageTransitionEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PageViewData","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/PageViewData"},{"name":"PageViewData","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"pageXOffset","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"pageYOffset","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Pane","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/splitview/paneview"},{"name":"PaneComposite","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/panecomposite"},{"name":"PaneCompositePanel","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/panel"},{"name":"Panel","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/panel"},{"name":"PANEL_ACTIVE_TITLE_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"PANEL_ACTIVE_TITLE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"PANEL_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"PANEL_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"PANEL_DRAG_AND_DROP_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"PANEL_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"PANEL_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/outline/browser/outline.contribution"},{"name":"PANEL_INACTIVE_TITLE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"PANEL_INPUT_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"PanelActivityAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"PanelDescriptor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/panel"},{"name":"PanelFocusContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/panel"},{"name":"PanelKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"PanelPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelPart"},{"name":"PanelPositionContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/panel"},{"name":"PanelRegistry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/panel"},{"name":"PaneView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/splitview/paneview"},{"name":"PannerNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ParameterHintsModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/parameterHints/parameterHintsModel"},{"name":"ParameterHintsWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/parameterHints/parameterHintsWidget"},{"name":"ParameterInformation","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ParameterInformation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ParameterInformation","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"parent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"parentPort","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"worker_threads"},{"name":"parse","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"parse","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"querystring"},{"name":"parse","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"url"},{"name":"parse","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"parse","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"parse","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/marked/marked"},{"name":"parse","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/marshalling"},{"name":"parse","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/glob"},{"name":"parse","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/console"},{"name":"parse","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"parse","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/plistParser"},{"name":"parse","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskConfiguration"},{"name":"parse","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/tokenization/typescript"},{"name":"parseArgs","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/argv"},{"name":"parseClassifierString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"parseCLIProcessArgv","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/argvHelper"},{"name":"parseCodicons","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/codicon"},{"name":"ParsedTokenThemeRule","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/tokenization"},{"name":"ParseErrorCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"parseExtensionDevOptions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionDevOptions"},{"name":"parseExtensionHostPort","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/environmentService"},{"name":"parseFloat","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"parseHrefAndDimensions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/htmlContent"},{"name":"parseInt","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"parseKeyboardLayoutDescription","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/keymapInfo"},{"name":"parseLineAndColumnAware","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/node/paths"},{"name":"parseLinkedText","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/linkedText"},{"name":"parseMainProcessArgv","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/argvHelper"},{"name":"ParseOptions","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"parsePathArg","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/environmentService"},{"name":"parseQuery","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"parser","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/marked/marked"},{"name":"Parser","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/parsers"},{"name":"parseRawGrammar","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-textmate/release/main"},{"name":"parseReplaceString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/replacePattern"},{"name":"parseSavedSearchEditor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization"},{"name":"parseSearchPort","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/environmentService"},{"name":"parseTokenTheme","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/tokenization"},{"name":"parseTree","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"parseUserDataDir","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/environmentService"},{"name":"parseVersion","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensionValidator"},{"name":"parseWithLocation","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/plistParser"},{"name":"Part","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/part"},{"name":"PartFingerprint","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewPart"},{"name":"PartFingerprints","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewPart"},{"name":"PartialModelCursorState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"PartialViewCursorState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"Parts","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/layout/browser/layoutService"},{"name":"PassThrough","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"stream"},{"name":"PASTE_FILE_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"pasteFileHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"PasteWebviewEditorCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/electron-browser/webviewCommands"},{"name":"patch","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"Path2D","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"pathIncludedInQuery","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"PathIterator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"pathOrURIToURI","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"pathsToEditors","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"pathToFileURL","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"url"},{"name":"PatternExcludesFocusedKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"PatternIncludesFocusedKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"PatternInputWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/patternInputWidget"},{"name":"patternsToIExpression","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/test/browser/queryBuilder.test"},{"name":"PAUSE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"PAUSE_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"PauseableEmitter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/event"},{"name":"PaymentAddress","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PaymentRequest","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PaymentRequestUpdateEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PaymentResponse","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"pbkdf2","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"pbkdf2Sync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"PeekContext","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewEditorBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewEditorGutterBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewEditorMatchHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewEditorMatchHighlightBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewResultsBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewResultsFileForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewResultsMatchForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewResultsMatchHighlight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewResultsSelectionBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewResultsSelectionForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewTitleBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewTitleForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"peekViewTitleInfoForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"PeekViewWidget","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/peekView/peekView"},{"name":"performance","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"performance","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"perf_hooks"},{"name":"Performance","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PerformanceEntry","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PerformanceMark","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PerformanceMeasure","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PerformanceNavigation","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PerformanceNavigationTiming","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PerformanceObserver","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PerformanceObserver","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"perf_hooks"},{"name":"PerformanceObserverEntryList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PerformanceResourceTiming","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PerformanceTiming","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PerfviewContrib","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor"},{"name":"PerfviewInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor"},{"name":"PerfWidgetExternal","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PeriodicWave","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PermissionRequest","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PermissionRequestedEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Permissions","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PermissionStatus","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PersistentConnectionEventType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"PersistentContributableViewsModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/views"},{"name":"PersistentProtocol","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc.net"},{"name":"personalbar","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PICK_WORKSPACE_FOLDER_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceCommands"},{"name":"pickerGroupBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"pickerGroupForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"PickerQuickAccessProvider","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/browser/pickerQuickAccess"},{"name":"pid","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"Piece","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase"},{"name":"pieceToQuery","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/fuzzyScorer"},{"name":"PieceTreeBase","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase"},{"name":"PieceTreeTextBuffer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer"},{"name":"PieceTreeTextBufferBuilder","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder"},{"name":"PieceTreeTextBufferFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder"},{"name":"pipeline","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"stream"},{"name":"Placeholder","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"PlaceHolderPanelActivityAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"PlaceHolderToggleCompositePinnedAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/activitybar/activitybarActions"},{"name":"PlaceHolderToggleCompositePinnedAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"PlaceHolderViewletActivityAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/activitybar/activitybarActions"},{"name":"PLAINTEXT_LANGUAGE_IDENTIFIER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/modesRegistry"},{"name":"PLAINTEXT_MODE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/modesRegistry"},{"name":"platform","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"platform","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"platform","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/process"},{"name":"platform","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"Platform","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"PlatformToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"Plugin","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PluginArray","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"POINT_CONVERSION_COMPRESSED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"POINT_CONVERSION_HYBRID","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"POINT_CONVERSION_UNCOMPRESSED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"PointerEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PointerEventHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/pointerHandler"},{"name":"PointerHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/pointerHandler"},{"name":"PointerHandlerLastRenderData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/mouseTarget"},{"name":"PopStateEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"popup","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/contextmenu/electron-browser/contextmenu"},{"name":"position","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"Position","kind":"alias","kindModifiers":"","sortText":"0"},{"name":"positionFromString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/layout/browser/layoutService"},{"name":"positionIsInRange","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers"},{"name":"PositionPanelActionConfigs","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"positionToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/layout/browser/layoutService"},{"name":"posix","kind":"module","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"posix","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"postMessage","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"powerMonitor","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"powerSaveBlocker","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"POWERSHELL_PATH","kind":"property","kindModifiers":"private,static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"ppid","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"prebakedMiniMaps","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/minimap/minimapPreBaked"},{"name":"PreferencesContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferencesContribution"},{"name":"PreferencesEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesEditor"},{"name":"PreferencesEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesEditorInput"},{"name":"PreferencesLabel","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"PreferencesSearchService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesSearch"},{"name":"PreferencesService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/browser/preferencesService"},{"name":"preferredSideBySideGroupDirection","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorGroupsService"},{"name":"prefersExecuteOnUI","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsUtil"},{"name":"prefersExecuteOnWorkspace","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsUtil"},{"name":"PrefixMemory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestMemory"},{"name":"PrefixSumComputer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/prefixSumComputer"},{"name":"PrefixSumIndexOfResult","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/prefixSumComputer"},{"name":"prepareActions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/actionbar/actionbar"},{"name":"prepareCommand","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/node/terminals"},{"name":"prepareQuery","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/fuzzyScorer"},{"name":"prepend","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"prependListener","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"prependListener","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"prependOnceListener","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"prependOnceListener","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"prerelease","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"PresentationOptions","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"presentationSchema","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugSchemas"},{"name":"PreserveCaseCheckbox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/findinput/replaceInput"},{"name":"prevCharLength","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"PREVIEW_DIR_NAME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"PREVIOUS_COMPRESSED_FOLDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"PreviousMatchFindAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"PreviousMatchFindAction2","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"PreviousPanelViewAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"PreviousSelectionMatchFindAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"PreviousSideBarViewAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/activitybar/activitybarActions"},{"name":"print","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"privateDecrypt","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"privateEncrypt","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"ProblemCollectorEventKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemCollectors"},{"name":"ProblemHandlingStrategy","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemCollectors"},{"name":"ProblemLocationKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"ProblemMatcherParser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"ProblemMatcherRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"ProblemPatternParser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"ProblemPatternRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"problemsErrorIconForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"problemsInfoIconForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"problemsWarningIconForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"process","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ProcessDataFlag","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/windows-process-tree/index"},{"name":"ProcessExecution","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ProcessExecution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ProcessExecutionDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"ProcessExecutionOptionsDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"ProcessingInstruction","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ProcessRunnerDetector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/node/processRunnerDetector"},{"name":"ProcessState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"ProcessTaskSystem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/node/processTaskSystem"},{"name":"product","kind":"alias","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/product/common/product"},{"name":"ProductContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/browser/update"},{"name":"ProductIconThemeData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/browser/productIconThemeData"},{"name":"productService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"profile","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"profileEnd","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"ProfileSessionState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"Progress","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/progress/common/progress"},{"name":"ProgressBadge","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/activity/common/activity"},{"name":"ProgressBar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/progressbar/progressbar"},{"name":"progressBarBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"ProgressBarIndicator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/progress/browser/progressIndicator"},{"name":"ProgressEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ProgressLocation","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ProgressLocation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/progress/common/progress"},{"name":"ProgressLocation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ProgressLocation","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"ProgressService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/progress/browser/progressService"},{"name":"Promise","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PromiseRejectionEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"promises","kind":"module","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"promises","kind":"module","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"promisify","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"prompt","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"protocol","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"Protocol","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc.net"},{"name":"Protocol","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc.electron"},{"name":"ProtocolConstants","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc.net"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"events"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"module"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"stream"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"https-proxy-agent"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"mocha"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Config"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/Domain"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/AvailabilityData"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/Base"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/ContextTagKeys"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/Data"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/DataPoint"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/Envelope"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/EventData"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/StackFrame"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/ExceptionDetails"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/ExceptionData"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/MessageData"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/MetricData"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/PageViewData"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/RemoteDependencyData"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/RequestData"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Context"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Channel"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/TelemetryClient"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/NodeClient"},{"name":"prototype","kind":"property","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/http-proxy-agent/index"},{"name":"provideDecorations","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/views/explorerDecorationsProvider"},{"name":"provideSelectionRanges","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/smartSelect/smartSelect"},{"name":"provideSignatureHelp","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/parameterHints/provideSignatureHelp"},{"name":"provideSuggestionItems","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggest"},{"name":"Proxy","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ProxyAgent","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-proxy-agent/index"},{"name":"ProxyAuthHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-main/auth"},{"name":"ProxyIdentifier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/proxyIdentifier"},{"name":"pseudoRandomBytes","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"publicDecrypt","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"publicEncrypt","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"PublicKeyCredential","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PushManager","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PushSubscription","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"PushSubscriptionOptions","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"pushToEnd","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"pushToStart","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"Query","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensionQuery"},{"name":"QueryBuilder","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/queryBuilder"},{"name":"QueryGlobTester","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"QueryType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"Queue","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"queueMicrotask","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"QuickAccessController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/browser/quickAccess"},{"name":"QuickAccessLeastRecentlyUsedEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"QuickAccessLeastRecentlyUsedEditorInGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"QuickAccessNavigateNextAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/quickAccessActions"},{"name":"QuickAccessPreviousEditorFromHistoryAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"QuickAccessPreviousRecentlyUsedEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"QuickAccessPreviousRecentlyUsedEditorInGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"QuickAccessRegistry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/common/quickAccess"},{"name":"QuickAccessViewPickerAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess"},{"name":"QuickCommandNLS","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standaloneStrings"},{"name":"QuickFixAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionCommands"},{"name":"QuickFixAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersViewActions"},{"name":"QuickFixActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersViewActions"},{"name":"QuickFixController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionCommands"},{"name":"QuickHelpNLS","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standaloneStrings"},{"name":"quickInputBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"QuickInputBox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/quickinput/browser/quickInputBox"},{"name":"QuickInputButtons","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"QuickInputButtons","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"QuickInputController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/quickinput/browser/quickInput"},{"name":"QuickInputEditorContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputServiceImpl"},{"name":"QuickInputEditorWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputServiceImpl"},{"name":"quickInputForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"QuickInputList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/quickinput/browser/quickInputList"},{"name":"QuickInputListFocus","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/quickinput/browser/quickInputList"},{"name":"QuickInputService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/browser/quickInput"},{"name":"QuickInputService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/quickinput/browser/quickInputService"},{"name":"quickInputTitleBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"QUICKOPEN_DETAIL_CONFIG","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/taskQuickPick"},{"name":"QUICKOPEN_SKIP_CONFIG","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/taskQuickPick"},{"name":"QuickOutlineNLS","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standaloneStrings"},{"name":"quickPickItemScorerAccessor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/quickinput/common/quickInput"},{"name":"QuickPickItemScorerAccessor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/quickinput/common/quickInput"},{"name":"QuickSwitchWindow","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"R_OK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"raceCancellation","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"raceTimeout","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"RadioGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/actions"},{"name":"RadioNodeList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RandomAccessReader","kind":"class","kindModifiers":"abstract,export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/yauzl/index"},{"name":"RandomBasedVariableResolver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetVariables"},{"name":"randomBytes","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"randomFill","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"randomFillSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"randomPort","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/ports"},{"name":"RandomSource","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"range","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"Range","kind":"alias","kindModifiers":"","sortText":"0"},{"name":"RangeError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RangeHighlightDecorations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/rangeDecorations"},{"name":"RangeHighlightDecorations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchModel"},{"name":"RangeMap","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/rangeMap"},{"name":"RangesCollector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/indentRangeProvider"},{"name":"RangesCollector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/syntaxRangeProvider"},{"name":"RangeUtil","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/lines/rangeUtil"},{"name":"RatingsWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWidgets"},{"name":"RawContentChangedType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelEvents"},{"name":"RawContextKey","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"RawDebugSession","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/rawDebugSession"},{"name":"rawListeners","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"RawObjectReplElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/replModel"},{"name":"rbDelete","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"rcompare","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"rcompareIdentifiers","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"ReactionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/reactionsAction"},{"name":"ReactionActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/reactionsAction"},{"name":"read","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"Readable","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"stream"},{"name":"ReadableStream","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ReadableStreamReader","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"readableToBuffer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"readCharWidths","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/config/charWidthReader"},{"name":"readdir","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"readdir","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"readDirsInDir","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"readdirSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"readdirSync","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"readdirWithFileTypes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"readFile","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"readFile","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"readFileSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"readFontInfo","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/config/configuration"},{"name":"readFromStdin","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/stdin"},{"name":"readlink","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"readlinkSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"readonly","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"ReadonlyEditorContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"readRawMapping","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/test/electron-browser/keyboardMapperTestUtils"},{"name":"ReadStream","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"ReadStream","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tty"},{"name":"readSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"readTrustedDomains","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/url/common/trustedDomains"},{"name":"readUInt16LE","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"readUInt32BE","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"readUInt32LE","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"readUInt8","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"realcaseSync","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/extpath"},{"name":"realpath","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"realpath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/extpath"},{"name":"realpathSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"realpathSync","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/extpath"},{"name":"ReapplyBreakpointsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"RecommendationWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWidgets"},{"name":"RecommendedExtensionsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews"},{"name":"recomputeMaxEnd","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"recomputeTreeMetadata","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"ReconnectionPermanentFailureEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"ReconnectionRunningEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"ReconnectionWaitEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAgentConnection"},{"name":"Recoverable","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"repl"},{"name":"RedoWebviewEditorCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/electron-browser/webviewCommands"},{"name":"RefactorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionCommands"},{"name":"refactorCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeAction"},{"name":"RefCountedStyleSheet","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/services/codeEditorServiceImpl"},{"name":"ReferenceCollection","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/lifecycle"},{"name":"ReferenceError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ReferenceProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"ReferencesController","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesController"},{"name":"ReferencesModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/referencesModel"},{"name":"ReferenceWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget"},{"name":"Reflect","kind":"module","kindModifiers":"declare","sortText":"4"},{"name":"RefreshAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"RefreshExplorerView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"REFUSED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"REG_BINARY","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"REG_DWORD","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"REG_EXPAND_SZ","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"REG_MULTI_SZ","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"REG_NONE","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"REG_QWORD","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"REG_SZ","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"REG_TYPES","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"winreg"},{"name":"RegexCheckbox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/findinput/findInputCheckboxes"},{"name":"RegExp","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"regExpContainsBackreference","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"regExpFlags","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"regExpLeadsToEndlessLoop","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"register","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerAction2","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"registerCodeActionProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerCodeLensProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerColor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"registerColorProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerColors","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry"},{"name":"registerColorThemeExtensionPoint","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/themeExtensionPoints"},{"name":"registerColorThemeSchemas","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/colorThemeSchema"},{"name":"registerCommands","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"registerCompletionItemProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerConfiguration","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"registerContextMenuListener","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/contextmenu/electron-main/contextmenu"},{"name":"registerContributions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/replaceContributions"},{"name":"registerContributions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchWidget"},{"name":"registerDeclarationProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerDefaultLanguageCommand","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"registerDefinitionProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerDiffEditorContribution","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"registerDocumentFormattingEditProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerDocumentHighlightProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerDocumentRangeFormattingEditProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerDocumentRangeSemanticTokensProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerDocumentSemanticTokensProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerDocumentSymbolProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerEditorAction","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"registerEditorCommand","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"registerEditorContribution","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"registerFileIconThemeExtensionPoint","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/themeExtensionPoints"},{"name":"registerFileIconThemeSchemas","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/fileIconThemeSchema"},{"name":"registerFileProtocol","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols"},{"name":"registerFoldingRangeProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerHoverProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerIcon","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/iconRegistry"},{"name":"registerImplementationProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerInstantiatedEditorAction","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"registerLanguageCommand","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"registerLinkProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerModelAndPositionCommand","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"registerModelCommand","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/editorExtensions"},{"name":"registerNotificationCommands","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"registerOnTypeFormattingEditProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerOnTypeRenameProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerOutputTransform","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/notebookRegistry"},{"name":"registerProductIconThemeExtensionPoint","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/themeExtensionPoints"},{"name":"registerProductIconThemeSchemas","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/productIconThemeSchema"},{"name":"registerReferenceProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerRemoteContributions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/electron-browser/terminalRemote"},{"name":"registerRenameProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerSelectionRangeProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerSignatureHelpProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerSingleton","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/extensions"},{"name":"registerTerminalActions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"registerTestEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"registerTextMime","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/mime"},{"name":"registerThemingParticipant","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"registerTypeDefinitionProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"registerWindowDriver","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/browser/driver"},{"name":"registerWindowDriver","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/electron-browser/driver"},{"name":"Registry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/registry/common/platform"},{"name":"Registry","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-textmate/release/main"},{"name":"ReindentLinesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"ReindentSelectedLinesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/indentation/indentation"},{"name":"ReinstallAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"rejects","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"RelatedInformation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersModel"},{"name":"RelatedInformationRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersTreeViewer"},{"name":"relative","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"relative","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"relativePath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"RelativePattern","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"RelativePattern","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"Relay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/event"},{"name":"RelayURLService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/url/electron-browser/urlService"},{"name":"release","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"release","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"releaseEvents","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"ReleaseNotesManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/browser/releaseNotesEditor"},{"name":"ReloadAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ReloadWebviewAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewCommands"},{"name":"ReloadWindowAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/windowActions"},{"name":"ReloadWindowWithExtensionsDisabledAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"remeasureFonts","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"remote","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"REMOTE_EXPLORER_TYPE_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/remoteExplorerService"},{"name":"REMOTE_FILE_SYSTEM_CHANNEL_NAME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/remoteAgentFileSystemChannel"},{"name":"REMOTE_HOST_SCHEME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteHosts"},{"name":"REMOTE_MACHINE_SCOPES","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"RemoteAgentConnection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/abstractRemoteAgentService"},{"name":"RemoteAgentService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl"},{"name":"RemoteAgentService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl"},{"name":"RemoteAuthorities","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/network"},{"name":"RemoteAuthorityResolverError","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"RemoteAuthorityResolverError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAuthorityResolver"},{"name":"RemoteAuthorityResolverError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"RemoteAuthorityResolverErrorCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/common/remoteAuthorityResolver"},{"name":"RemoteAuthorityResolverService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/browser/remoteAuthorityResolverService"},{"name":"RemoteAuthorityResolverService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService"},{"name":"RemoteBadgeWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWidgets"},{"name":"RemoteConnectionState","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/contextkeys"},{"name":"RemoteDependencyData","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/RemoteDependencyData"},{"name":"RemoteDependencyData","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"RemoteDependencyDataConstants","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Constants"},{"name":"RemoteExtensionEnvironmentChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel"},{"name":"RemoteExtensionHostClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/remoteExtensionHostClient"},{"name":"RemoteExtensionLogFileName","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/remoteAgentService"},{"name":"RemoteExtensionManagementChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc"},{"name":"RemoteExtensionsInstaller","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller"},{"name":"RemoteFileDialogContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/contextkeys"},{"name":"RemoteFileSystemProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/remoteAgentFileSystemChannel"},{"name":"RemoteFileSystemProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"RemoteInstallAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"RemoteNameContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/contextkeys"},{"name":"RemoteSearchService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/searchService"},{"name":"RemoteUserConfiguration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/browser/configuration"},{"name":"RemoteViewPaneContainer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/browser/remote"},{"name":"RemoteWindowActiveIndicator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/browser/remoteIndicator"},{"name":"REMOVE_ROOT_FOLDER_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"REMOVE_ROOT_FOLDER_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"removeAccents","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/normalization"},{"name":"RemoveAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"RemoveActionId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"RemoveAllBreakpointsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"removeAllListeners","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"removeAllListeners","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"RemoveAllWatchExpressionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"removeAnsiEscapeCodes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"RemoveBreakpointAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"removeClass","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"removeClasses","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"removeCSSRulesContainingSelector","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"removeElementsAfterNulls","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/resolvedKeybindingItem"},{"name":"removeEventListener","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"RemoveFromRecentlyOpenedAPICommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/apiCommands"},{"name":"removeFromValueTree","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"removeListener","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"removeListener","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"removeMarkdownEscapes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/htmlContent"},{"name":"removeNode","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"removeProperty","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/jsonEdit"},{"name":"removeTabIndexAndUpdateFocus","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"removeTrailingPathSeparator","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"rename","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"rename","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"rename","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/rename/rename"},{"name":"RenameAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/rename/rename"},{"name":"renameHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"renameIgnoreError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"RenameInputField","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/rename/renameInputField"},{"name":"RenameProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"renameSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"renderCodicons","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/codicons"},{"name":"RenderedLinesCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewLayer"},{"name":"Renderer","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/marked/marked"},{"name":"Renderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsList"},{"name":"rendererLogChannelId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/common/logConstants"},{"name":"renderExpressionValue","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/baseDebugView"},{"name":"renderFormattedText","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/formattedTextRenderer"},{"name":"RenderIndentGuides","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/abstractTree"},{"name":"RenderingContext","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/renderingContext"},{"name":"RenderLineInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"RenderLineNumbersType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"RenderLineNumbersType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"RenderLineOutput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"RenderLineOutput2","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"renderMarkdown","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/markdownRenderer"},{"name":"renderMarkdownDocument","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markdown/common/markdownDocumentRenderer"},{"name":"RenderMinimap","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"RenderMinimap","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"renderText","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/formattedTextRenderer"},{"name":"renderVariable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/baseDebugView"},{"name":"renderViewLine","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"renderViewLine2","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"renderViewTree","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/baseDebugView"},{"name":"RenderWhitespace","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLineRenderer"},{"name":"ReopenClosedEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"ReopenResourcesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"repeat","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"Repl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/repl"},{"name":"REPL_MODE_SLOPPY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"repl"},{"name":"REPL_MODE_STRICT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"repl"},{"name":"REPL_VIEW_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"ReplAccessibilityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/replViewer"},{"name":"ReplaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"ReplaceActionId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"ReplaceActiveKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"ReplaceAllAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"ReplaceAllCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/replaceAllCommand"},{"name":"ReplaceAllInFileActionId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"ReplaceAllInFolderAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"ReplaceAllInFolderActionId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"ReplaceCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/commands/replaceCommand"},{"name":"ReplaceCommandThatPreservesSelection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/commands/replaceCommand"},{"name":"ReplaceCommandThatSelectsText","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/commands/replaceCommand"},{"name":"ReplaceCommandWithOffsetCursorState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/commands/replaceCommand"},{"name":"ReplaceCommandWithoutChangingPosition","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/commands/replaceCommand"},{"name":"ReplaceInFilesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"ReplaceInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/findinput/replaceInput"},{"name":"ReplaceInputBoxFocusedKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"ReplacePattern","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/replacePattern"},{"name":"ReplacePattern","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/replace"},{"name":"ReplacePiece","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/replacePattern"},{"name":"ReplacePreviewContentProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/replaceService"},{"name":"ReplaceService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/replaceService"},{"name":"ReplDataSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/replViewer"},{"name":"ReplDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/replViewer"},{"name":"ReplEvaluationInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/replModel"},{"name":"ReplEvaluationInputsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/replViewer"},{"name":"ReplEvaluationResult","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/replModel"},{"name":"ReplEvaluationResultsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/replViewer"},{"name":"ReplGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/replModel"},{"name":"ReplGroupRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/replViewer"},{"name":"ReplModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/replModel"},{"name":"ReplRawObjectsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/replViewer"},{"name":"REPLServer","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"repl"},{"name":"ReplSimpleElementsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/replViewer"},{"name":"ReplVariablesRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/replViewer"},{"name":"report","kind":"property","kindModifiers":"declare,optional","sortText":"5","hasAction":true,"source":"process"},{"name":"reporters","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"mocha"},{"name":"ReportExtensionIssueAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"ReportPerformanceIssueUsingReporterAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/issue/electron-browser/issueActions"},{"name":"RepositoryPane","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/repositoryPane"},{"name":"RepositoryViewDescriptor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/repositoryPane"},{"name":"request","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"request","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"https"},{"name":"request","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/request/browser/request"},{"name":"Request","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"requestAnimationFrame","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"RequestChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/request/common/requestIpc"},{"name":"RequestChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/request/common/requestIpc"},{"name":"RequestData","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/RequestData"},{"name":"RequestData","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"RequestInitiator","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/rpcProtocol"},{"name":"RequestMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/request/electron-main/requestMainService"},{"name":"RequestService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/request/browser/requestService"},{"name":"RequestService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/request/node/requestService"},{"name":"RequestType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc"},{"name":"require","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RequireInterceptor","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostRequireInterceptor"},{"name":"RerunSearchEditorSearchAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"RerunSearchEditorSearchCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"ResetFocusedViewLocationAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"ResetGroupSizesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"resetSentinel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"ResetViewLocationsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"resizeBy","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"resizeTo","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"resolve","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolve","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"resolve","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"url"},{"name":"resolve","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"resolve4","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolve6","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolveAny","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolveCname","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolveColorValue","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"resolveCommonProperties","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/node/commonProperties"},{"name":"ResolvedAuthority","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ResolvedAuthority","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ResolvedKeybinding","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keyCodes"},{"name":"ResolvedKeybindingItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/resolvedKeybindingItem"},{"name":"ResolvedKeybindingPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keyCodes"},{"name":"resolveExtensionsSettings","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"resolveMarketplaceHeaders","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionGalleryService"},{"name":"resolveMx","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolveNaptr","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolveNs","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolvePath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"resolvePatternsForProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"resolvePtr","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"Resolver","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolveSettingsTree","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"resolveSoa","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolveSrv","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolveTerminalEncoding","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/terminalEncoding"},{"name":"resolveTxt","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"resolveWorkbenchCommonProperties","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/browser/workbenchCommonProperties"},{"name":"resolveWorkbenchCommonProperties","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/node/workbenchCommonProperties"},{"name":"ResourceContextKey","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/resources"},{"name":"ResourceDragAndDrop","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersTreeViewer"},{"name":"ResourceEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor/resourceEditorInput"},{"name":"ResourceEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor/resourceEditorModel"},{"name":"ResourceGlobMatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"ResourceGlobMatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/resources"},{"name":"ResourceLabel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/labels"},{"name":"ResourceLabels","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/labels"},{"name":"resourceLanguageSettingsSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"ResourceMap","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"ResourceMarkers","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersModel"},{"name":"ResourceMarkersRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersTreeViewer"},{"name":"ResourceQueue","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"ResourcesDropHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/dnd"},{"name":"ResourceSelectedForCompareContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"resourceSettings","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"resourcesPath","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"ResourceTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resourceTree"},{"name":"resourceUsage","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"ResourceWithCommentsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsTreeViewer"},{"name":"ResourceWithCommentThreads","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/common/commentModel"},{"name":"Response","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ResponseType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc"},{"name":"ResponsiveState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/rpcProtocol"},{"name":"RESTART_FRAME_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"RESTART_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"RESTART_SESSION_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"restore","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"restoreFontInfo","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/config/configuration"},{"name":"restoreParentsScrollTop","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"restoreRecentlyOpened","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"restoreWindowsState","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/electron-main/windowsStateStorage"},{"name":"RestrictedRenderingContext","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/renderingContext"},{"name":"resultIsMatch","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"retry","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"return","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"REVEAL_IN_EXPLORER_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"RevealInSideBarForSearchResults","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"RevealKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"RevealLine_","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/coreCommands"},{"name":"RevealProblemKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"revealResourcesInOS","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/electron-browser/fileCommands"},{"name":"RevealTarget","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"reverse","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"REVERSE_CONTINUE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"REVERT_FILE_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"RevertAndCloseEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"revertLocalChangesCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler"},{"name":"ReviewViewZone","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentsEditorContribution"},{"name":"ReviewZoneWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/commentThreadWidget"},{"name":"revive","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/marshalling"},{"name":"reviveQuery","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostSearch"},{"name":"reviveWebviewExtensionDescription","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory"},{"name":"reviveWorkspaceEditDto","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"reviveWorkspaceIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"rewriteAbsolutePaths","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"v8-inspect-profiler"},{"name":"rewriteWorkspaceFileForNewLocation","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"RGBA","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/color"},{"name":"RGBA8","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/rgba"},{"name":"rgErrorMsgForDisplay","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/fileSearch"},{"name":"rgErrorMsgForDisplay","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepTextSearchEngine"},{"name":"rgPath","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-ripgrep/lib/index"},{"name":"RichEditBracket","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/richEditBrackets"},{"name":"RichEditBrackets","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/richEditBrackets"},{"name":"RichEditSupport","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/languageConfigurationRegistry"},{"name":"rightRotate","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"righttest","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"rimraf","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"RimRafMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"rimrafSync","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"RipgrepParser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepTextSearchEngine"},{"name":"RipgrepSearchProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepSearchProvider"},{"name":"RipgrepTextSearchEngine","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepTextSearchEngine"},{"name":"rmdir","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"rmdirSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"rootCertificates","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"rot","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/numbers"},{"name":"RotatingLogger","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/spdlog/index"},{"name":"RowCache","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/rowCache"},{"name":"RPCProtocol","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/rpcProtocol"},{"name":"RSA_NO_PADDING","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"RSA_PKCS1_OAEP_PADDING","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"RSA_PKCS1_PADDING","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"RSA_PKCS1_PSS_PADDING","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"RSA_SSLV23_PADDING","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"RSA_X931_PADDING","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"rsort","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"RTCCertificate","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCDataChannel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCDataChannelEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCDtlsTransport","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCDtlsTransportStateChangedEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCDtmfSender","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCDTMFSender","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCDTMFToneChangeEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCErrorEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCIceCandidate","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCIceCandidatePairChangedEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCIceGatherer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCIceGathererEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCIceTransport","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCIceTransportStateChangedEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCIdentityAssertion","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCPeerConnection","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCPeerConnectionIceErrorEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCPeerConnectionIceEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCRtpReceiver","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCRtpSender","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCRtpTransceiver","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCSctpTransport","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCSessionDescription","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCSrtpSdesTransport","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCSsrcConflictEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCStatsEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCStatsProvider","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCStatsReport","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"RTCTrackEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"rtrim","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"Rulers","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/rulers/rulers"},{"name":"run","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"RunAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"runAtThisOrScheduleAtNextAnimationFrame","kind":"let","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"RunAutomaticTasks","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks"},{"name":"runInContext","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"vm"},{"name":"runInExternalTerminal","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/node/terminals"},{"name":"runInNewContext","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"vm"},{"name":"runInThisContext","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"vm"},{"name":"runMain","kind":"method","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"module"},{"name":"RunOnceScheduler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"RunOnceWorker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"RunOnOptions","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"RunOnOptions","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskConfiguration"},{"name":"RunOptions","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"RunOptions","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskConfiguration"},{"name":"RuntimeExtensionsEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"RuntimeExtensionsInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput"},{"name":"RuntimeType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"RunToCursorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugEditorActions"},{"name":"runWhenIdle","kind":"let","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"S_IFBLK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IFCHR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IFDIR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IFIFO","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IFLNK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IFMT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IFREG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IFSOCK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IRGRP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IROTH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IRUSR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IRWXG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IRWXO","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IRWXU","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IWGRP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IWOTH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IWUSR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IXGRP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IXOTH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"S_IXUSR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"safeBtoa","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"safeStringify","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/objects"},{"name":"sandbox","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"sandboxed","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"sanitize","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"sanitizeFilePath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"sanitizeGridNodeDescriptor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/grid"},{"name":"sanitizeProcessEnvironment","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/processes"},{"name":"sanitizeRanges","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/syntaxRangeProvider"},{"name":"Sash","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/sash/sash"},{"name":"SashState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/sash/sash"},{"name":"satisfies","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"SAVE_ALL_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SAVE_ALL_IN_GROUP_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SAVE_ALL_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SAVE_FILE_AS_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SAVE_FILE_AS_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SAVE_FILE_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SAVE_FILE_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SAVE_FILE_WITHOUT_FORMATTING_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SAVE_FILE_WITHOUT_FORMATTING_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SAVE_FILES_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SaveAllAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"SaveAllInGroupAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"SaveExtensionHostProfileAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"SaveLocalFileCommand","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/browser/simpleFileDialog"},{"name":"saveParentsScrollTop","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"SaveParticipant","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/mainThreadSaveParticipant"},{"name":"SaveParticipantsContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/saveParticipants"},{"name":"SaveReason","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"SaveWorkspaceAsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/workspaceActions"},{"name":"ScanCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/scanCode"},{"name":"ScanCodeBinding","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/scanCode"},{"name":"ScanCodeUtils","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/scanCode"},{"name":"ScanError","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"Scanner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"scheduleAtNextAnimationFrame","kind":"let","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"schema","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionsRegistry"},{"name":"schema","kind":"alias","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/jsonSchemaCommon"},{"name":"schema","kind":"alias","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/jsonSchema_v1"},{"name":"schema","kind":"alias","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/jsonSchema_v2"},{"name":"Schemas","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/network"},{"name":"Schemas","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemMatcher"},{"name":"scm","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SCMAccessibilityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/repositoryPane"},{"name":"SCMMenus","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/menus"},{"name":"SCMService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/common/scmService"},{"name":"SCMStatusController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/activity"},{"name":"SCMTreeKeyboardNavigationLabelProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/repositoryPane"},{"name":"SCMTreeSorter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/repositoryPane"},{"name":"SCMViewPaneContainer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/scmViewlet"},{"name":"Scope","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"ScopedCredential","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ScopedCredentialInfo","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ScopedLineTokens","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports"},{"name":"score","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/languageSelector"},{"name":"scoreFuzzy","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/fuzzyScorer"},{"name":"scoreFuzzy2","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/fuzzyScorer"},{"name":"scoreItemFuzzy","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/fuzzyScorer"},{"name":"screen","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"screen","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"Screen","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"screenLeft","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ScreenOrientation","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"screenTop","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"screenX","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"screenY","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Script","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"vm"},{"name":"ScriptProcessorNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"scroll","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"Scrollable","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/scrollable"},{"name":"ScrollableElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/scrollableElement"},{"name":"ScrollbarArrow","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/scrollbarArrow"},{"name":"scrollbars","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"scrollbarShadow","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"scrollbarSliderActiveBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"scrollbarSliderBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"scrollbarSliderHoverBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"ScrollbarState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/scrollbarState"},{"name":"ScrollbarVisibility","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/scrollable"},{"name":"ScrollbarVisibility","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"ScrollbarVisibilityController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController"},{"name":"scrollBy","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"ScrollDecorationViewPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration"},{"name":"ScrollPosition","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/addons/commandTrackerAddon"},{"name":"ScrollState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/scrollable"},{"name":"scrollTo","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"ScrollType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/editorCommon"},{"name":"ScrollType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"scrollX","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"scrollY","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"scrypt","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"scryptSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"sdkVersion","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Context"},{"name":"SEARCH_EXCLUDE_CONFIG","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"SearchAccessibilityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchResultsView"},{"name":"SearchAddon","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/xterm-addon-search/typings/xterm-addon-search"},{"name":"SearchChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/searchIpc"},{"name":"SearchChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/searchIpc"},{"name":"SearchCompletionExitCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"SearchData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelSearch"},{"name":"SearchDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchResultsView"},{"name":"SearchDND","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchResultsView"},{"name":"SearchEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditor"},{"name":"SearchEditorBodyScheme","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"searchEditorFindMatch","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"searchEditorFindMatchBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"SearchEditorFindMatchClass","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"SearchEditorID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"SearchEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput"},{"name":"SearchEditorScheme","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"searchEditorTextInputBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditor"},{"name":"Searcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelSearch"},{"name":"SearchError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"SearchErrorCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"SearchHistoryService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchHistoryService"},{"name":"SearchInputBoxFocusedKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"searchMatchComparer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchModel"},{"name":"SearchModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchModel"},{"name":"SearchParams","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelSearch"},{"name":"SearchProviderType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"SearchRange","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"SearchResult","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchModel"},{"name":"SearchResultIdx","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"SearchResultModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"SearchService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/searchService"},{"name":"SearchService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/rawSearchService"},{"name":"SearchSortOrder","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"SearchView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchView"},{"name":"SearchViewFocusedKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"SearchViewPosition","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchView"},{"name":"SearchViewVisibleKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"SearchWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesWidgets"},{"name":"SearchWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchWidget"},{"name":"SearchWorkbenchService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/searchModel"},{"name":"SecurityPolicyViolationEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SELECT_FOR_COMPARE_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileCommands"},{"name":"SelectActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/actionbar/actionbar"},{"name":"selectAllSearchEditorMatchesCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"SelectAllSearchEditorMatchesCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"SelectAllTerminalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"SelectAllWebviewEditorCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewCommands"},{"name":"SelectAndStartAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"selectBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"selectBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"SelectBox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/selectBox/selectBox"},{"name":"SelectBoxList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/selectBox/selectBoxCustom"},{"name":"SelectBoxNative","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/selectBox/selectBoxNative"},{"name":"SelectColorThemeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/themes/browser/themes.contribution"},{"name":"SelectDefaultShellWindowsTerminalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"selectForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"SelectHighlightsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"Selection","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Selection","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Selection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/selection"},{"name":"Selection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"Selection","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"Selection","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"selectionBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"SelectionBasedVariableResolver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetVariables"},{"name":"SelectionClipboard","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard"},{"name":"SelectionClipboardContributionID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/selectionClipboard"},{"name":"SelectionDirection","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/selection"},{"name":"SelectionDirection","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"SelectionDirection","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"SelectionHighlighter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/multicursor/multicursor"},{"name":"SelectionMatchFindAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"SelectionRange","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SelectionRange","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SelectionRange","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"SelectionRangeRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"SelectionsOverlay","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/selections/selections"},{"name":"selectListBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"selectorPattern","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"self","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SEMANTIC_HIGHLIGHTING_SETTING_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/modelServiceImpl"},{"name":"SemanticTokens","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SemanticTokens","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SemanticTokensBuilder","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SemanticTokensBuilder","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SemanticTokensEdit","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SemanticTokensEdit","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SemanticTokensEdits","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SemanticTokensEdits","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SemanticTokensHelp","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/semanticTokensHelp"},{"name":"SemanticTokensLegend","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SemanticTokensLegend","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SemanticTokensProviderStyling","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/semanticTokensProviderStyling"},{"name":"SemanticTokensProviderStylingConstants","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/semanticTokensProviderStyling"},{"name":"SemVer","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"SEMVER_SPEC_VERSION","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"send","kind":"method","kindModifiers":"declare,optional","sortText":"5","hasAction":true,"source":"process"},{"name":"Sender","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"SENTINEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"SENTINEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"sep","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"sep","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"Separator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/actionbar/actionbar"},{"name":"sequence","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"Sequence","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/sequence"},{"name":"Sequencer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"SerializableFileMatch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"SerializableGrid","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/grid"},{"name":"serialize","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"serializeEnvironmentVariableCollection","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/environmentVariableShared"},{"name":"serializeFontInfo","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/config/configuration"},{"name":"serializePipePositions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/test/wordTestUtils"},{"name":"Serializer","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"serializeSearchConfiguration","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization"},{"name":"serializeSearchError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"serializeSearchResultForEditor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization"},{"name":"serve","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc.net"},{"name":"serve","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/electron-main/driver"},{"name":"Server","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"Server","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"https"},{"name":"Server","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"net"},{"name":"Server","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"Server","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/electron-main/ipc.electron-main"},{"name":"Server","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc.cp"},{"name":"Server","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc.net"},{"name":"ServerExtensionsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews"},{"name":"ServerResponse","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"SERVFAIL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"SERVICE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"ServiceCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/serviceCollection"},{"name":"ServiceUIFrameContext","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ServiceWorker","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ServiceWorkerContainer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ServiceWorkerMessageEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ServiceWorkerRegistration","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"session","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"Session","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"inspector"},{"name":"Session","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"sessionStorage","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Set","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SET_CONTEXT_COMMAND_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/contextkey/common/contextkey"},{"name":"setARIAContainer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/aria/aria"},{"name":"setAsyncMode","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/spdlog/index"},{"name":"setCollapseStateAtLevel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingModel"},{"name":"setCollapseStateForMatchingLines","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingModel"},{"name":"setCollapseStateForType","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingModel"},{"name":"setCollapseStateLevelsDown","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingModel"},{"name":"setCollapseStateLevelsUp","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingModel"},{"name":"setCollapseStateUp","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingModel"},{"name":"SetColorThemeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"SetEditorLayoutAPICommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/apiCommands"},{"name":"setegid","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"seteuid","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"setFdLimit","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"SetFileIconThemeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"setFlagsFromString","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"setFullscreen","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"setgid","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"setGlobalLeakWarningThreshold","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/event"},{"name":"setgroups","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"setImmediate","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"setImmediate","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"timers"},{"name":"setImmediate","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"setInterval","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"setInterval","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"timers"},{"name":"setLanguageConfiguration","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"SetLogLevelAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/common/logsActions"},{"name":"SetMap","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/collections"},{"name":"setMaxListeners","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"setMaxListeners","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"setModelLanguage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"setModelMarkers","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"setMonarchTokensProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"setNodeStickiness","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/intervalTree"},{"name":"setOptions","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/marked/marked"},{"name":"SetPanelPositionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"setPassword","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/keytar/keytar"},{"name":"setPassword","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/keytar/index"},{"name":"setPriority","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"SetProductIconThemeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"setProperty","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/jsonEdit"},{"name":"setServers","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"setSnippetSuggestSupport","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggest"},{"name":"setTheme","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"setTimeout","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"setTimeout","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"timers"},{"name":"SettingArrayRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"SettingBoolRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"SettingComplexRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"SettingEnumRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"SettingExcludeRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"SettingGroupRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"settingKeyToDisplayFormat","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"SettingMatches","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesSearch"},{"name":"SettingNewExtensionsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"SettingNumberRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"settings","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"SETTINGS_COMMAND_OPEN_SETTINGS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_FILTER_ONLINE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_FOCUS_FILE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_FOCUS_TOC","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_SEARCH","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/preferences"},{"name":"Settings2EditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"SettingsChangeRelauncher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution"},{"name":"settingsCheckboxBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"settingsCheckboxBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"settingsCheckboxForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"SettingsEditor2","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsEditor2"},{"name":"SettingsEditor2Input","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesEditorInput"},{"name":"SettingsEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"SettingsEditorOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferences"},{"name":"SettingsGroupTitleRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesRenderers"},{"name":"SettingsGroupTitleWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesWidgets"},{"name":"settingsHeaderForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"SettingsHeaderWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesWidgets"},{"name":"settingsNumberInputBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"settingsNumberInputBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"settingsNumberInputForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"settingsSelectBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"settingsSelectBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"settingsSelectForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"settingsSelectListBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"SettingsSynchroniser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/settingsSync"},{"name":"SettingsTargetsWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesWidgets"},{"name":"settingsTextInputBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"settingsTextInputBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"settingsTextInputForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsWidgets"},{"name":"SettingsTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"SettingsTreeElement","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"SettingsTreeFilter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"SettingsTreeGroupElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"SettingsTreeModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"SettingsTreeNewExtensionsElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"SettingsTreeSettingElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTreeModels"},{"name":"SettingTextRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"SettingTreeRenderers","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsTree"},{"name":"SettingValueType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferences"},{"name":"setTokensProvider","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"setToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"setuid","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"setUncaughtExceptionCaptureCallback","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"setUnexpectedErrorHandler","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"setup","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"setup","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"setup","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/applicationinsights"},{"name":"setupMaster","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"setupTerminalCommands","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalCommands"},{"name":"setupTerminalMenu","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalMenu"},{"name":"setWordDefinitionFor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDocumentData"},{"name":"setZoomFactor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"setZoomLevel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/browser"},{"name":"Severity","kind":"alias","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/severity"},{"name":"Severity","kind":"alias","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/notification/common/notification"},{"name":"SeverityIcon","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/severityIcon/common/severityIcon"},{"name":"SeverityLevel","kind":"enum","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/SeverityLevel"},{"name":"SeverityLevel","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"shadowCaretRangeFromPoint","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/mouseTarget"},{"name":"ShadowRoot","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SharedArrayBuffer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"sharedLogChannelId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/common/logConstants"},{"name":"SharedProcess","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-main/sharedProcess"},{"name":"SharedProcessMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/ipc/electron-main/sharedProcessMainService"},{"name":"SharedProcessService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/sharedProcess/electron-browser/sharedProcessService"},{"name":"SharedWorker","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"shell","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"SHELL_CWD_INVALID_EXIT_CODE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"SHELL_PATH_DIRECTORY_EXIT_CODE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"SHELL_PATH_INVALID_EXIT_CODE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"ShellExecution","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ShellExecution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ShellExecutionDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"ShellExecutionOptionsDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"ShellQuoting","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ShellQuoting","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"ShellQuoting","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ShellQuoting","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskConfiguration"},{"name":"shift","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/list/rangeMap"},{"name":"ShiftCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/commands/shiftCommand"},{"name":"shorten","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"shouldSetLangEnvVariable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment"},{"name":"shouldSynchronizeModel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/modelService"},{"name":"show","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"SHOW_EDITORS_IN_GROUP","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"SHOW_NOTIFICATIONS_CENTER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"ShowActiveFileInExplorer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"ShowAllCommandsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess"},{"name":"ShowAllEditorsByAppearanceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"ShowAllEditorsByMostRecentlyUsedAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"ShowAzureExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ShowBuiltInExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ShowCandidateContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/common/showCandidate"},{"name":"ShowCurrentReleaseNotesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/browser/update"},{"name":"ShowCurrentReleaseNotesActionId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/common/update"},{"name":"ShowDisabledExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ShowEditorsInActiveGroupByMostRecentlyUsedAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"ShowEnabledExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"showExtensionQuery","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/format/browser/showExtensionQuery"},{"name":"ShowInstalledExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ShowLanguageExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorStatus"},{"name":"ShowLanguageExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ShowNextChangeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"ShowNextWindowTabHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"ShowOpenedFileInNewWindow","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"ShowOutdatedExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ShowPopularExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ShowPreviousChangeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator"},{"name":"ShowPreviousWindowTabHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"ShowProblemsPanelAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersViewActions"},{"name":"ShowRecommendationsOnlyOnDemandKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"ShowRecommendedExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ShowRecommendedKeymapExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ShowReleaseNotesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/browser/update"},{"name":"ShowRuntimeExtensionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"showSimpleSuggestions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggest"},{"name":"ShowViewletAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/viewlet"},{"name":"ShowWebViewEditorFindWidgetAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewCommands"},{"name":"shuffle","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"ShutdownReason","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/common/lifecycle"},{"name":"SIDE_BAR_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"SIDE_BAR_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"SIDE_BAR_DRAG_AND_DROP_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"SIDE_BAR_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"SIDE_BAR_SECTION_HEADER_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"SIDE_BAR_SECTION_HEADER_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"SIDE_BAR_SECTION_HEADER_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"SIDE_BAR_TITLE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"SIDE_GROUP","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/common/editorService"},{"name":"SidebarFocusContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/viewlet"},{"name":"SidebarPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/sidebar/sidebarPart"},{"name":"SideBarVisibleContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/viewlet"},{"name":"SideBySideEditor","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"SideBySideEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/sideBySideEditor"},{"name":"SideBySideEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"SIGABRT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGALRM","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGBREAK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGBUS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGCHLD","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGCONT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGFPE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGHUP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGILL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGINT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGIO","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGIOT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGKILL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"sign","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"SIGN_SERVICE_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/sign/common/sign"},{"name":"SignatureHelp","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SignatureHelp","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SignatureHelp","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"SignatureHelpProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"SignatureHelpTriggerKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SignatureHelpTriggerKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"SignatureHelpTriggerKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SignatureHelpTriggerKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"SignatureInformation","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SignatureInformation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SignatureInformation","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"Signer","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"SignService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/sign/browser/signService"},{"name":"SignService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/sign/node/signService"},{"name":"SIGPIPE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGPOLL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGPROF","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGPWR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGQUIT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGSEGV","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGSTKFLT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGSTOP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGSYS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGTERM","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGTRAP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGTSTP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGTTIN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGTTOU","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGUNUSED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGURG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGUSR1","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGUSR2","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGVTALRM","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGWINCH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGXCPU","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SIGXFSZ","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SimpleBreadcrumbsItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget"},{"name":"SimpleBulkEditService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleButton","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findWidget"},{"name":"SimpleCheckbox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/checkbox/checkbox"},{"name":"simpleCheckboxBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"simpleCheckboxBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"simpleCheckboxForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"SimpleCommentEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/simpleCommentEditor"},{"name":"SimpleConfigurationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleDialogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleEditorModelResolverService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleEditorProgressService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleFileDialog","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/dialogs/browser/simpleFileDialog"},{"name":"SimpleFindReplaceWidget","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget"},{"name":"SimpleFindWidget","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget"},{"name":"SimpleKeybinding","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keyCodes"},{"name":"SimpleLayoutService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleNotificationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleReplElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/replModel"},{"name":"SimpleResourceConfigurationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleResourcePropertiesService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleServicesNLS","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standaloneStrings"},{"name":"SimpleUriLabelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SimpleWorkerClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/worker/simpleWorker"},{"name":"SimpleWorkerServer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/worker/simpleWorker"},{"name":"SimpleWorkspaceContextService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"SingleCursorState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorCommon"},{"name":"SingleEditorGroupsContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"singleLetterHash","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"SingleLineInputHeight","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchWidget"},{"name":"SingleModelEditStackElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/editStack"},{"name":"singlePagePager","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/paging"},{"name":"SingleProxyRPCProtocol","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/api/testRPCProtocol"},{"name":"sinon","kind":"alias","kindModifiers":"declare","sortText":"4"},{"name":"Sinon","kind":"var","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"size","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"size","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/collections"},{"name":"Sizing","kind":"type","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/splitview/splitview"},{"name":"Sizing","kind":"type","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/grid/grid"},{"name":"SlicedLineTokens","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/lineTokens"},{"name":"SlowBuffer","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"buffer"},{"name":"SlowExtensionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions"},{"name":"SmartSnippetInserter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/common/smartSnippetInserter"},{"name":"SmoothScrollableElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/scrollableElement"},{"name":"SmoothScrollingOperation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/scrollable"},{"name":"SmoothScrollingUpdate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/scrollable"},{"name":"snapshotToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textfiles"},{"name":"SnapUpdateService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/electron-main/updateService.snap"},{"name":"Snippet","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/snippets/browser/snippetsFile"},{"name":"SnippetCompletion","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider"},{"name":"SnippetCompletionProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider"},{"name":"SnippetController2","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetController2"},{"name":"SnippetFile","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/snippets/browser/snippetsFile"},{"name":"snippetFinalTabstopHighlightBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"snippetFinalTabstopHighlightBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"SnippetParser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"SnippetSession","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetSession"},{"name":"SnippetSortOrder","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggest"},{"name":"SnippetSource","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/snippets/browser/snippetsFile"},{"name":"SnippetsSynchroniser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/snippetsSync"},{"name":"SnippetString","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SnippetString","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"snippetTabstopHighlightBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"snippetTabstopHighlightBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"Socket","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dgram"},{"name":"Socket","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"net"},{"name":"SocketDebugAdapter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/node/debugAdapter"},{"name":"sort","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"SortBy","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"sortedDiff","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"Sorter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree"},{"name":"SortLinesAscendingAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"SortLinesCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/sortLinesCommand"},{"name":"SortLinesDescendingAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"sortMimeTypes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/common/notebookCommon"},{"name":"SortOrder","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"SortOrder","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"Source","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/processes"},{"name":"Source","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/processes"},{"name":"Source","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugSource"},{"name":"SourceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionCommands"},{"name":"sourceActionCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeAction"},{"name":"SourceBreakpoint","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SourceBreakpoint","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SourceBuffer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SourceBufferList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SourceControlInputBoxValidationType","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SourceControlInputBoxValidationType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SparseEncodedTokens","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/tokensStore"},{"name":"spawn","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"child_process"},{"name":"spawn","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"node-pty"},{"name":"spawnRipgrepCmd","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepFileSearch"},{"name":"spawnSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"child_process"},{"name":"SpdLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/log/node/spdlogService"},{"name":"specify","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechGrammar","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechGrammarList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechRecognition","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechRecognitionAlternative","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechRecognitionEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechRecognitionResult","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechRecognitionResultList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"speechSynthesis","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechSynthesis","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechSynthesisErrorEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechSynthesisEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechSynthesisUtterance","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SpeechSynthesisVoice","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SPLIT_EDITOR_DOWN","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"SPLIT_EDITOR_LEFT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"SPLIT_EDITOR_RIGHT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"SPLIT_EDITOR_UP","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"splitEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"SplitEditorAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"SplitEditorDownAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"SplitEditorLeftAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"SplitEditorOrthogonalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"SplitEditorRightAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"SplitEditorsVertically","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"SplitEditorUpAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"splitGlobAware","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/glob"},{"name":"SplitInActiveWorkspaceTerminalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"SplitLine","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/splitLinesCollection"},{"name":"SplitLinesCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/splitLinesCollection"},{"name":"splitName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"SplitTerminalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"SplitView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/splitview/splitview"},{"name":"spreadGlobComponents","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepTextSearchEngine"},{"name":"spy","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"SQLiteStorageDatabase","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/storage/node/storage"},{"name":"SSL_OP_ALL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_CIPHER_SERVER_PREFERENCE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_CISCO_ANYCONNECT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_COOKIE_EXCHANGE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_CRYPTOPRO_TLSEXT_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_EPHEMERAL_RSA","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_LEGACY_SERVER_CONNECT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_MICROSOFT_SESS_ID_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_MSIE_SSLV2_RSA_PADDING","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NETSCAPE_CA_DN_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NETSCAPE_CHALLENGE_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NO_COMPRESSION","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NO_QUERY_MTU","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NO_SSLv2","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NO_SSLv3","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NO_TICKET","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NO_TLSv1","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NO_TLSv1_1","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_NO_TLSv1_2","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_PKCS1_CHECK_1","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_PKCS1_CHECK_2","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_SINGLE_DH_USE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_SINGLE_ECDH_USE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_SSLEAY_080_CLIENT_DH_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_TLS_BLOCK_PADDING_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_TLS_D5_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"SSL_OP_TLS_ROLLBACK_BUG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"StableEditorScrollState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/core/editorState"},{"name":"StackFrame","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"StackFrame","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/StackFrame"},{"name":"StackFrame","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/index"},{"name":"StandaloneCodeEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneCodeEditor"},{"name":"StandaloneCodeEditorNLS","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standaloneStrings"},{"name":"StandaloneCodeEditorServiceImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneCodeServiceImpl"},{"name":"StandaloneCommandService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"StandaloneCommandsQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess"},{"name":"StandaloneConfigurationModelParser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configurationModels"},{"name":"StandaloneDiffEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneCodeEditor"},{"name":"StandaloneEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneCodeEditor"},{"name":"StandaloneGotoLineQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/quickAccess/standaloneGotoLineQuickAccess"},{"name":"StandaloneGotoSymbolQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess"},{"name":"StandaloneKeybindingService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"StandaloneQuickInputServiceImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputServiceImpl"},{"name":"StandaloneReferencesController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/referenceSearch/standaloneReferenceSearch"},{"name":"StandaloneTelemetryService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/simpleServices"},{"name":"StandaloneThemeServiceImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneThemeServiceImpl"},{"name":"StandardAutoClosingPairConditional","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/languageConfiguration"},{"name":"StandardKeyboardEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/keyboardEvent"},{"name":"StandardMouseEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/mouseEvent"},{"name":"standardMouseMoveMerger","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/globalMouseMoveMonitor"},{"name":"StandardTokenType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"StandardTokenType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/common/textMateService"},{"name":"StandardTokenType","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-textmate/release/main"},{"name":"StandardWheelEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/mouseEvent"},{"name":"StandardWindow","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"start","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"repl"},{"name":"start","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/applicationinsights"},{"name":"start","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/native-watchdog/index"},{"name":"StartAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"StartDebugActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActionViewItems"},{"name":"StartDebugQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugQuickAccess"},{"name":"startExtensionHostProcess","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/node/extensionHostProcessSetup"},{"name":"StartExtensionHostProfileAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"StartFindAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"StartFindReplaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"StartFindWithSelectionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findController"},{"name":"startProfiling","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"v8-inspect-profiler"},{"name":"StartStopProblemCollector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemCollectors"},{"name":"startsWith","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"startsWithIgnoreCase","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"startsWithUTF8BOM","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"startup","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-browser/issue/issueReporterMain"},{"name":"startup","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-browser/processExplorer/processExplorerMain"},{"name":"startup","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-browser/sharedProcess/sharedProcessMain"},{"name":"StartupKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/common/lifecycle"},{"name":"StartupKindToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/common/lifecycle"},{"name":"StartupProfiler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/performance/electron-browser/startupProfiler"},{"name":"StartupTimings","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/performance/electron-browser/startupTimings"},{"name":"stat","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"stat","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"State","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestModel"},{"name":"State","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"State","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/common/update"},{"name":"State","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/linkComputer"},{"name":"stateExists","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"StatefullMarkdownCell","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell"},{"name":"StateMachine","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/linkComputer"},{"name":"Statement","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-sqlite3/index"},{"name":"StateService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/state/node/stateService"},{"name":"StateType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/common/update"},{"name":"StaticDND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dnd"},{"name":"StaticExtensionsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/staticExtensions"},{"name":"StaticLanguageSelector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/mocks/mockMode"},{"name":"StaticRange","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"StaticRouter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/common/ipc"},{"name":"StaticServices","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneServices"},{"name":"StatisticType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionManagement"},{"name":"statLink","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"Stats","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"statSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"status","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"status","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/aria/aria"},{"name":"STATUS_BAR_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_DEBUGGING_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/statusbarColorProvider"},{"name":"STATUS_BAR_DEBUGGING_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/statusbarColorProvider"},{"name":"STATUS_BAR_DEBUGGING_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/statusbarColorProvider"},{"name":"STATUS_BAR_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_HOST_NAME_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_HOST_NAME_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_ITEM_ACTIVE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_ITEM_HOVER_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_NO_FOLDER_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_NO_FOLDER_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_NO_FOLDER_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_PROMINENT_ITEM_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_PROMINENT_ITEM_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"STATUS_CODES","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"http"},{"name":"statusbar","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"StatusbarAlignment","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/statusbar/common/statusbar"},{"name":"StatusBarAlignment","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"StatusBarAlignment","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"StatusBarColorProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/statusbarColorProvider"},{"name":"StatusbarPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/statusbar/statusbarPart"},{"name":"StatusLabelAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"StatusMessageChangeType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/notifications"},{"name":"StatusUpdater","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewlet"},{"name":"stderr","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"stdin","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"stdinDataListener","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/stdin"},{"name":"stdout","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"STEP_BACK_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"STEP_INTO_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"STEP_INTO_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"STEP_OUT_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"STEP_OUT_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"STEP_OVER_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"STEP_OVER_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"StereoPannerNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"stop","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"stop","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/event"},{"name":"STOP_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"STOP_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"StopExtensionHostProfileAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor"},{"name":"StopWatch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/stopwatch"},{"name":"Storage","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Storage","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/storage/common/storage"},{"name":"StorageDataCleaner","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner"},{"name":"StorageEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"StorageHint","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/storage/common/storage"},{"name":"StorageKeysSyncRegistryChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncIpc"},{"name":"StorageKeysSyncRegistryChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncIpc"},{"name":"StorageKeysSyncRegistryService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/storageKeys"},{"name":"StorageMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/node/storageMainService"},{"name":"StorageManager","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"StorageManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensionManagement/common/extensionEnablementService"},{"name":"StorageScope","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/common/storage"},{"name":"strcmp","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/tokenization"},{"name":"Stream","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"stream"},{"name":"StreamDebugAdapter","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/node/debugAdapter"},{"name":"streamToBuffer","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"streamToBufferReadableStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"streamToNodeReadable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/stream"},{"name":"strict","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"strictEqual","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"string","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"String","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"StringBuffer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase"},{"name":"StringDecoder","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"string_decoder"},{"name":"stringDiff","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/diff/diff"},{"name":"StringDiffSequence","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/diff/diff"},{"name":"StringEOL","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/tokensStore"},{"name":"stringHash","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/hash"},{"name":"stringify","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"querystring"},{"name":"stringify","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/marshalling"},{"name":"StringIterator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"StringRepresentationProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/gotoSymbol/peek/referencesTree"},{"name":"StringSHA1","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/hash"},{"name":"stringToSnapshot","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textfiles"},{"name":"stripCodicons","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/codicons"},{"name":"stripComments","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"stripUTF8BOM","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"stripWildcards","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"stub","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"styleMedia","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"StyleMedia","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"StyleSheet","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"StyleSheetList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SubmenuAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/menu/menu"},{"name":"SubmenuItemAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"substituteMatches","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/monarch/monarchCommon"},{"name":"SubtleCrypto","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SuggestAlternatives","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestAlternatives"},{"name":"SuggestController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestController"},{"name":"SuggestEnabledInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput"},{"name":"suggestFilename","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/mime"},{"name":"SuggestMemoryService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestMemory"},{"name":"SuggestModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestModel"},{"name":"SuggestWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestWidget"},{"name":"suggestWidgetStatusbarMenu","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggest"},{"name":"suite","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"suiteRepeat","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/common/utils"},{"name":"suiteSetup","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"suiteTeardown","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"super","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"SUPPORTED_CODE_ACTIONS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/codeAction/codeActionModel"},{"name":"SUPPORTED_ENCODINGS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textfiles"},{"name":"SurroundSelectionCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/commands/surroundSelectionCommand"},{"name":"SVGAElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAngle","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedAngle","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedBoolean","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedEnumeration","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedInteger","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedLength","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedLengthList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedNumber","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedNumberList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedPreserveAspectRatio","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedRect","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedString","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimatedTransformList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimateElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimateMotionElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimateTransformElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGAnimationElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGCircleElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGClipPathElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGComponentTransferFunctionElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGCursorElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGDefsElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGDescElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGElementInstance","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGElementInstanceList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGEllipseElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEBlendElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEColorMatrixElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEComponentTransferElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFECompositeElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEConvolveMatrixElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEDiffuseLightingElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEDisplacementMapElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEDistantLightElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEDropShadowElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEFloodElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEFuncAElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEFuncBElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEFuncGElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEFuncRElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEGaussianBlurElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEImageElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEMergeElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEMergeNodeElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEMorphologyElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEOffsetElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFEPointLightElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFESpecularLightingElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFESpotLightElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFETileElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFETurbulenceElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGFilterElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGForeignObjectElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGGElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGGeometryElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGGradientElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGGraphicsElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGImageElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGLength","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGLengthList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGLinearGradientElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGLineElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGMarkerElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGMaskElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGMatrix","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGMetadataElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGNumber","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGNumberList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSeg","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegArcAbs","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegArcRel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegClosePath","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegCurvetoCubicAbs","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegCurvetoCubicRel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegCurvetoCubicSmoothAbs","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegCurvetoCubicSmoothRel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegCurvetoQuadraticAbs","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegCurvetoQuadraticRel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegCurvetoQuadraticSmoothAbs","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegCurvetoQuadraticSmoothRel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegLinetoAbs","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegLinetoHorizontalAbs","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegLinetoHorizontalRel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegLinetoRel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegLinetoVerticalAbs","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegLinetoVerticalRel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegMovetoAbs","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPathSegMovetoRel","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPatternElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPoint","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPointList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPolygonElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPolylineElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGPreserveAspectRatio","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGRadialGradientElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGRect","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGRectElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGScriptElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGStopElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGStringList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGStyleElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGSVGElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGSwitchElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGSymbolElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGTextContentElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGTextElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGTextPathElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGTextPositioningElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGTitleElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGTransform","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGTransformList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGTSpanElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGUnitTypes","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGUseElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGViewElement","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGZoomAndPan","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SVGZoomEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"switch","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"SwitchPanelViewAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"SwitchRemoteAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/browser/explorerViewItems"},{"name":"SwitchRemoteViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/browser/explorerViewItems"},{"name":"SwitchTerminalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"SwitchTerminalActionViewItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"SwitchWindow","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"symbol","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Symbol","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SYMBOL_ICON_ARRAY_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_BOOLEAN_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_CLASS_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_COLOR_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_CONSTANT_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_CONSTRUCTOR_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_ENUMERATOR_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_ENUMERATOR_MEMBER_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_EVENT_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_FIELD_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_FILE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_FOLDER_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_FUNCTION_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_INTERFACE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_KEY_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_KEYWORD_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_METHOD_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_MODULE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_NAMESPACE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_NULL_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_NUMBER_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_OBJECT_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_OPERATOR_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_PACKAGE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_PROPERTY_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_REFERENCE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_SNIPPET_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_STRING_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_STRUCT_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_TEXT_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_TYPEPARAMETER_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_UNIT_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SYMBOL_ICON_VARIABLE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineTree"},{"name":"SymbolInformation","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SymbolInformation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SymbolKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SymbolKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"SymbolKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SymbolKind","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"SymbolKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"SymbolKinds","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"SymbolsQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/symbolsQuickAccess"},{"name":"SymbolTag","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"SymbolTag","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"SymbolTag","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"SymbolTag","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"SymbolTag","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"symlink","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"symlink","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"symlinkSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"SyncActionDescriptor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/actions/common/actions"},{"name":"SyncDescriptor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/common/descriptors"},{"name":"SyncIgnoredIconAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"SyncManager","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SyncResource","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"SyncStatus","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"SyntaxError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"SyntaxKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"SyntaxRangeProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/syntaxRangeProvider"},{"name":"SystemDisabledWarningAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"systemPreferences","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TAB_ACTIVE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_ACTIVE_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_ACTIVE_BORDER_TOP","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_ACTIVE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_ACTIVE_MODIFIED_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_HOVER_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_HOVER_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_HOVER_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_INACTIVE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_INACTIVE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_INACTIVE_MODIFIED_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_ACTIVE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_ACTIVE_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_ACTIVE_BORDER_TOP","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_ACTIVE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_ACTIVE_MODIFIED_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_HOVER_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_HOVER_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_HOVER_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_INACTIVE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_INACTIVE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TAB_UNFOCUSED_INACTIVE_MODIFIED_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TabCompletionController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/snippets/browser/tabCompletion"},{"name":"TabFocus","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/commonEditorConfig"},{"name":"table","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"TabsTitleControl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/tabsTitleControl"},{"name":"TAG","kind":"property","kindModifiers":"private,static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"tail","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"tail2","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"takeHeapSnapshot","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"Task","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Task","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TASK_RUNNING_STATE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"Task2","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TaskConfigSource","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskConfiguration"},{"name":"TaskDefinition","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"TaskDefinitionDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"TaskDefinitionRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry"},{"name":"TaskDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"TaskError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskSystem"},{"name":"TaskErrors","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskSystem"},{"name":"TaskEvent","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"TaskEventKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"TaskExecuteKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskSystem"},{"name":"TaskExecutionDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"TaskFilterDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"TaskGroup","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TaskGroup","kind":"type","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"TaskGroup","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TaskHandleDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"TaskIdentifier","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskConfiguration"},{"name":"TaskPanelKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TaskPanelKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TaskPresentationOptionsDTO","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"TaskQuickPick","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/taskQuickPick"},{"name":"TaskRevealKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TaskRevealKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TaskRunResult","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugTaskRunner"},{"name":"TaskRunSource","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"TaskRunType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"tasks","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TASKS_CONFIGURATION_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"TASKS_DEFAULT","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"TaskScope","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TaskScope","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"TaskScope","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TaskSequentializer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"TaskService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/taskService"},{"name":"TaskService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/electron-browser/taskService"},{"name":"TaskSorter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"TaskSourceKind","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/tasks"},{"name":"TasksQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/tasksQuickAccess"},{"name":"tasksSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"TaskStatusBarContributions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/task.contribution"},{"name":"teardown","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"TelemetryAppenderChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/node/telemetryIpc"},{"name":"TelemetryAppenderClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/node/telemetryIpc"},{"name":"TelemetryClient","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/TelemetryClient"},{"name":"TelemetryClient","kind":"alias","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/applicationinsights"},{"name":"TelemetryContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution"},{"name":"telemetryLogChannelId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/common/logConstants"},{"name":"TelemetryService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetryService"},{"name":"TelemetryService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/telemetry/browser/telemetryService"},{"name":"TelemetryService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/telemetry/electron-browser/telemetryService"},{"name":"TelemetryType","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/TelemetryTypes/TelemetryType"},{"name":"telemetryTypeToBaseType","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/TelemetryTypes/TelemetryType"},{"name":"TEMPDIR_PREFIX","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"template","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"Terminal","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"xterm"},{"name":"TERMINAL_ACTION_CATEGORY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"TERMINAL_BACKGROUND_COLOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry"},{"name":"TERMINAL_BORDER_COLOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry"},{"name":"TERMINAL_COMMAND_ID","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"TERMINAL_CONFIG_SECTION","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"TERMINAL_CURSOR_BACKGROUND_COLOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry"},{"name":"TERMINAL_CURSOR_FOREGROUND_COLOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry"},{"name":"TERMINAL_FOREGROUND_COLOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry"},{"name":"TERMINAL_SELECTION_BACKGROUND_COLOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry"},{"name":"TERMINAL_VIEW_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"TerminalConfigHelper","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper"},{"name":"terminalConfiguration","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalConfiguration"},{"name":"TerminalCursorStyle","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"TerminalDataBufferer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminalDataBuffering"},{"name":"TerminalFindWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalFindWidget"},{"name":"TerminalInstance","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalInstance"},{"name":"TerminalInstanceService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalInstanceService"},{"name":"TerminalInstanceService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService"},{"name":"TerminalLinkManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager"},{"name":"TerminalNativeService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService"},{"name":"TerminalPasteAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"TerminalProcess","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/node/terminalProcess"},{"name":"TerminalProcessExtHostProxy","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy"},{"name":"TerminalProcessManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalProcessManager"},{"name":"TerminalQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalsQuickAccess"},{"name":"terminalSendSequenceCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"TerminalService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalService"},{"name":"TerminalTab","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalTab"},{"name":"TerminalTaskSystem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem"},{"name":"TerminalValidatedLocalLinkProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider"},{"name":"TerminalViewPane","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalView"},{"name":"TerminalWebLinkProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/links/terminalWebLinkProvider"},{"name":"TerminalWidgetManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalWidgetManager"},{"name":"TerminalWordLinkProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider"},{"name":"TERMINATE_THREAD_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"TerminateResponseCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/processes"},{"name":"TerminateResponseCode","kind":"alias","kindModifiers":"","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/processes"},{"name":"TernarySearchTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"test","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"test","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"TEST_VIEW_CONTAINER_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/views"},{"name":"TestAccessibilityService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"testApplyEditsWithSyncedModels","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/editableTextModelTestUtils"},{"name":"TestBackupFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestBackupMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test"},{"name":"testCase","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"TestCell","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/test/testNotebookEditor"},{"name":"TestChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/test/node/testService"},{"name":"TestCodeEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/browser/testCodeEditor"},{"name":"TestCodeEditorService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/browser/editorTestServices"},{"name":"TestColorTheme","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/test/common/testThemeService"},{"name":"testCommand","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/browser/testCommand"},{"name":"TestCommandService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/browser/editorTestServices"},{"name":"TestConfiguration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/mocks/testConfiguration"},{"name":"TestConfigurationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/test/common/testConfigurationService"},{"name":"TestContextService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/common/workbenchTestServices"},{"name":"TestDecorationProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/test/foldingModel.test"},{"name":"TestDecorationsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestDialogMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test"},{"name":"TestDialogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/dialogs/test/common/testDialogService"},{"name":"TestDiskFileSystemProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/test/electron-browser/diskFileService.test"},{"name":"TestEditorGroupAccessor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestEditorGroupsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestEditorGroupView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestEditorPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestEditorService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestElectronService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/workbenchTestServices"},{"name":"TestEnvironmentService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestEnvironmentService","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/workbenchTestServices"},{"name":"TestExperimentService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test"},{"name":"TestExtensionEnablementService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test"},{"name":"TestExtensionService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/common/workbenchTestServices"},{"name":"testFile","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/node/utils"},{"name":"TestFileDialogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestFileEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestFileIconTheme","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/test/common/testThemeService"},{"name":"TestFilesConfigurationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestFindController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/test/findController.test"},{"name":"TestHistoryService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestHostService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestInstantiationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/instantiation/test/common/instantiationServiceMock"},{"name":"TestLayoutService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestLifecycleService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestListService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestMenuService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestNativePathService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/workbenchTestServices"},{"name":"TestNativeTextFileServiceWithEncodingOverrides","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/workbenchTestServices"},{"name":"TestNotebookEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/test/testNotebookEditor"},{"name":"TestNotificationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/notification/test/common/testNotificationService"},{"name":"TestPanelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestPathService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestProgressService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestReadonlyTextFileEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"testRepeat","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/common/utils"},{"name":"testRepeatedActionAndExtractPositions","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/test/wordTestUtils"},{"name":"testRepeatOnly","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/common/utils"},{"name":"TestRPCProtocol","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/api/testRPCProtocol"},{"name":"TestService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/test/node/testService"},{"name":"TestServiceAccessor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestServiceAccessor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/workbenchTestServices"},{"name":"TestServiceClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/test/node/testService"},{"name":"TestSharedProcessService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/workbenchTestServices"},{"name":"TestStorageService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/common/workbenchTestServices"},{"name":"testTextBufferFactory","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/model/linesTextBuffer/linesTextBufferBuilder.test"},{"name":"TestTextFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestTextFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/workbenchTestServices"},{"name":"TestTextResourceConfigurationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestTextResourcePropertiesService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/services/modelService.test"},{"name":"TestTextResourcePropertiesService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/common/workbenchTestServices"},{"name":"TestThemeService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/test/common/testThemeService"},{"name":"TestUserDataSyncUtilService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/test/common/userDataSyncClient"},{"name":"TestView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/browser/ui/grid/util"},{"name":"TestViewletService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"testViewModel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/viewModel/testViewModel"},{"name":"TestViewsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"TestWindowConfiguration","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/workbenchTestServices"},{"name":"TestWorkingCopy","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test"},{"name":"TestWorkingCopyService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/common/workbenchTestServices"},{"name":"testWorkspace","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspace/test/common/testWorkspace"},{"name":"TestWorkspace","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspace/test/common/testWorkspace"},{"name":"Text","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Text","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"TEXT_DIFF_EDITOR_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"TEXT_FILE_EDITOR_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"TextAreaHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/textAreaHandler"},{"name":"TextAreaInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/textAreaInput"},{"name":"TextAreaState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/controller/textAreaState"},{"name":"TextBadge","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/activity/common/activity"},{"name":"textBlockQuoteBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"textBlockQuoteBorder","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"TextChange","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textChange"},{"name":"textCodeBlockBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"TextCompareEditorActiveContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"TextCompareEditorVisibleContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"TextDecoder","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TextDecoder","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"TextDecoderStream","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TextDiffEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/textDiffEditor"},{"name":"TextDiffEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor/textDiffEditorModel"},{"name":"TextDocumentSaveReason","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TextDocumentSaveReason","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TextDocumentSaveReason","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"TextEdit","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TextEdit","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TextEdit","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"TextEditElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"TextEditElementRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree"},{"name":"TextEditorCursorBlinkingStyle","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"TextEditorCursorBlinkingStyle","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"TextEditorCursorStyle","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TextEditorCursorStyle","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"TextEditorCursorStyle","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"TextEditorDecorationType","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTextEditor"},{"name":"TextEditorEdit","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTextEditor"},{"name":"TextEditorLineNumbersStyle","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TextEditorLineNumbersStyle","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TextEditorLineNumbersStyle","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"TextEditorOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"TextEditorOptions","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"TextEditorRevealType","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TextEditorRevealType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"TextEditorRevealType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TextEditorSelectionChangeKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TextEditorSelectionChangeKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TextEditorSelectionRevealType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/editor/common/editor"},{"name":"TextEditorState","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/history/browser/history"},{"name":"TextEncoder","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TextEncoder","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"TextEncoderStream","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TextEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TextFileContentProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"TextFileEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/editors/textFileEditor"},{"name":"TextFileEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textFileEditorModel"},{"name":"TextFileEditorModelManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textFileEditorModelManager"},{"name":"TextFileEditorModelState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textfiles"},{"name":"TextFileEditorTracker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/editors/textFileEditorTracker"},{"name":"TextFileLoadReason","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textfiles"},{"name":"TextFileOperationError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textfiles"},{"name":"TextFileOperationResult","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textfiles"},{"name":"TextFileSaveErrorHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler"},{"name":"TextFileSaveParticipant","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textFileSaveParticipant"},{"name":"TextInputActionsProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/textInputActions"},{"name":"textLinkActiveForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"textLinkForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"textmateColorGroupSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/colorThemeSchema"},{"name":"textmateColorSettingsSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/colorThemeSchema"},{"name":"textmateColorsSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/colorThemeSchema"},{"name":"TextMateService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/browser/textMateService"},{"name":"TextMateService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/electron-browser/textMateService"},{"name":"TextmateSnippet","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"TextMateWorker","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/electron-browser/textMateWorker"},{"name":"TextMateWorkerHost","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/electron-browser/textMateService"},{"name":"TextMetrics","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TextModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModel"},{"name":"TextModelCancellationTokenSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/core/editorState"},{"name":"TextModelResolvedOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"TextModelResolverService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textmodelResolver/common/textModelResolverService"},{"name":"TextModelSearch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelSearch"},{"name":"TextModelTokenization","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelTokens"},{"name":"textPreformatForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"TextResourceConfigurationService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/textResourceConfigurationServiceImpl"},{"name":"TextResourceEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/textResourceEditor"},{"name":"TextResourceEditorInput","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"TextResourcePropertiesService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textresourceProperties/common/textResourcePropertiesService"},{"name":"TextSearchEngineAdapter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/textSearchAdapter"},{"name":"TextSearchManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/textSearchManager"},{"name":"TextSearchMatch","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"TextSearchResultsCollector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/textSearchManager"},{"name":"textSeparatorForeground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"TextSnapshotReadable","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textfiles"},{"name":"TextTrack","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TextTrackCue","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TextTrackCueList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TextTrackList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Themable","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"ThemableDecorationAttachmentRenderOptions","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"ThemableDecorationRenderOptions","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"ThemeColor","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ThemeColor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"themeColorFromId","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"ThemeConfiguration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/themeConfiguration"},{"name":"ThemeIcon","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ThemeIcon","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/themeService"},{"name":"ThemeIcon","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ThemeMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/electron-main/themeMainService"},{"name":"ThemeRegistry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/themeExtensionPoints"},{"name":"ThemeRule","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/common/TMHelper"},{"name":"ThemeSettings","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/workbenchThemeService"},{"name":"ThemeTrieElement","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/tokenization"},{"name":"ThemeTrieElementRule","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/tokenization"},{"name":"this","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Thread","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"ThreadAndSessionIds","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"threadId","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"worker_threads"},{"name":"throttle","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/decorators"},{"name":"ThrottledDelayer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"Throttler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"throw","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"throwDeprecation","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"throwProposedApiError","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensions"},{"name":"throws","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"assert"},{"name":"tildify","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"time","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"TimeBasedVariableResolver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetVariables"},{"name":"timeEnd","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"timeline","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"TimelineElementTemplate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/browser/timelinePane"},{"name":"timelineEnd","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"TimelineFollowActiveEditorContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/browser/timelinePane"},{"name":"TimelineIdentityProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/browser/timelinePane"},{"name":"TimelineItem","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TimelineItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TimelineKeyboardNavigationLabelProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/browser/timelinePane"},{"name":"TimelineListVirtualDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/browser/timelinePane"},{"name":"TimelinePane","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/browser/timelinePane"},{"name":"TimelinePaneDescriptor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/browser/timeline.contribution"},{"name":"TimelinePaneId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/common/timeline"},{"name":"TimelineService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/common/timelineService"},{"name":"timeLog","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"timeout","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"TIMEOUT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"TimeoutTimer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/async"},{"name":"TimeRanges","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"timeStamp","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"timingSafeEqual","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"title","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"TITLE_BAR_ACTIVE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TITLE_BAR_ACTIVE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TITLE_BAR_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TITLE_BAR_INACTIVE_BACKGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TITLE_BAR_INACTIVE_FOREGROUND","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"TitlebarPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/titlebar/titlebarPart"},{"name":"TitlebarPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/parts/titlebar/titlebarPart"},{"name":"TitleCaseAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"TitleControl","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/titleControl"},{"name":"TitleEventSource","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/common/terminal"},{"name":"TLSSocket","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tls"},{"name":"TMGrammarFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/common/TMGrammarFactory"},{"name":"tmpdir","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"TMScopeRegistry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textMate/common/TMScopeRegistry"},{"name":"toASCII","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"punycode"},{"name":"toBackupWorkspaceResource","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/backup/electron-browser/backup"},{"name":"toBufferOrReadable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/textfile/common/textfiles"},{"name":"toCanonicalName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"tocData","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/settingsLayout"},{"name":"TOCRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/tocTree"},{"name":"TOCTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/tocTree"},{"name":"TOCTreeModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/tocTree"},{"name":"toDecodeStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"toDisposable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/lifecycle"},{"name":"toErrorMessage","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errorMessage"},{"name":"toExtension","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensions"},{"name":"toExtensionDescription","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"toFileChanges","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/watcher"},{"name":"toFileOperationResult","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"toFileSystemProviderErrorCode","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"TOGGLE_BREAKPOINT_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugEditorActions"},{"name":"TOGGLE_CONDITIONAL_BREAKPOINT_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugEditorActions"},{"name":"TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"TOGGLE_DIFF_SIDE_BY_SIDE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorCommands"},{"name":"TOGGLE_IGNORE_EXTENSION_ACTION_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"TOGGLE_INLINE_BREAKPOINT_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugCommands"},{"name":"TOGGLE_LOG_POINT_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugEditorActions"},{"name":"TOGGLE_NOTIFICATION","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"TOGGLE_NOTIFICATIONS_CENTER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/notifications/notificationsCommands"},{"name":"ToggleActivityBarVisibilityAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"ToggleAutoSaveAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"ToggleAutoUpdateAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"ToggleBreakpointsActivatedAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/debugActions"},{"name":"toggleCaseSensitiveCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"ToggleCaseSensitiveCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"ToggleCaseSensitiveKeybinding","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"toggleClass","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"ToggleCollapseAndExpandAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"toggleCollapseState","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/folding/foldingModel"},{"name":"ToggleColumnSelectionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection"},{"name":"ToggleCompositePinnedAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/compositeBarActions"},{"name":"ToggleDevToolsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/developerActions"},{"name":"ToggleEditorLayoutAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"ToggleEditorVisibilityAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"ToggleGroupSizesAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/editor/editorActions"},{"name":"ToggleHighContrastNLS","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standaloneStrings"},{"name":"ToggleMaximizedPanelAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"ToggleMenuBarAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"ToggleMinimapAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/toggleMinimap"},{"name":"ToggleMultiCursorModifierAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier"},{"name":"TogglePanelAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/panel/panelActions"},{"name":"ToggleReactionsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/comments/browser/reactionsAction"},{"name":"toggleRegexCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"ToggleRegexCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"ToggleRegexKeybinding","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"ToggleRenderControlCharacterAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter"},{"name":"ToggleRenderWhitespaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace"},{"name":"toggleSearchEditorCaseSensitiveCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"ToggleSearchEditorCaseSensitiveCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"toggleSearchEditorContextLinesCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"ToggleSearchEditorContextLinesCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"toggleSearchEditorRegexCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"ToggleSearchEditorRegexCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"toggleSearchEditorWholeWordCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions"},{"name":"ToggleSearchEditorWholeWordCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/searchEditor/browser/constants"},{"name":"ToggleSearchOnTypeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"ToggleSearchScopeKeybinding","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"ToggleSharedProcessAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/developerActions"},{"name":"ToggleSidebarPositionAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"ToggleSidebarVisibilityAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"ToggleStatusbarVisibilityAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"ToggleTabFocusModeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode"},{"name":"ToggleTerminalAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalActions"},{"name":"ToggleViewAction","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/actions/layoutActions"},{"name":"ToggleViewletAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/activitybar/activitybarActions"},{"name":"ToggleViewModeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/browser/repositoryPane"},{"name":"toggleWholeWordCommand","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/browser/searchActions"},{"name":"ToggleWholeWordCommandId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/constants"},{"name":"ToggleWholeWordKeybinding","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/find/findModel"},{"name":"ToggleWindowTabsBarHandler","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"Token","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/token"},{"name":"Token","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"TOKEN_CLASSIFIER_LANGUAGE_SEPARATOR","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"TOKEN_TYPE_WILDCARD","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"TokenClassificationExtensionPoints","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/tokenClassificationExtensionPoint"},{"name":"TokenizationRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"TokenizationRegistryImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/tokenizationRegistry"},{"name":"TokenizationResult","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/token"},{"name":"TokenizationResult2","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/core/token"},{"name":"TokenizationStateStore","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/textModelTokens"},{"name":"TokenizationSupport2Adapter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneLanguages"},{"name":"tokenize","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/browser/standaloneEditor"},{"name":"tokenizeLineToHTML","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/textToHtmlTokenizer"},{"name":"tokenizeToString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/textToHtmlTokenizer"},{"name":"TokenMetadata","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"TokensStore","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/tokensStore"},{"name":"TokensStore2","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/tokensStore"},{"name":"TokenStyle","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"TokenStylingRule","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"tokenStylingSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"TokenTheme","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/tokenization"},{"name":"TokenType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"toKey","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/timeline/common/timeline"},{"name":"toLocalISOString","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/date"},{"name":"toLocalResource","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/resources"},{"name":"toMultilineTokens2","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/services/semanticTokensProviderStyling"},{"name":"toNamespacedPath","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"toNodeEncoding","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"toolbar","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"ToolBar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/toolbar/toolbar"},{"name":"TOOLTIP_HOVER_THRESHOLD","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers"},{"name":"TooltipWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsWidgets"},{"name":"toOverrides","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"top","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"top","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"topAsync","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"toReadable","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/stream"},{"name":"toResource","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/test/common/utils"},{"name":"toResource","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"toSlashes","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/extpath"},{"name":"toStandardTokenType","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/supports/tokenization"},{"name":"toStoreData","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"toStream","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/stream"},{"name":"toString","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"totalmem","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"Touch","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Touch","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"TouchBar","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TouchBarButton","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TouchBarColorPicker","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TouchBarGroup","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TouchBarLabel","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TouchBarPopover","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TouchBarScrubber","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TouchBarSegmentedControl","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TouchBarSlider","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TouchBarSpacer","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TouchEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TouchList","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"toUint32","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uint"},{"name":"toUint8","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uint"},{"name":"toUnicode","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"punycode"},{"name":"toValuesTree","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configuration"},{"name":"toWorkspaceFolder","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspace/common/workspace"},{"name":"toWorkspaceFolders","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspace/common/workspace"},{"name":"toWorkspaceIdentifier","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"trace","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"traceDeprecation","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"traceProcessWarnings","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"TrackedRangeStickiness","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"TrackedRangeStickiness","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"TrackEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"trackFocus","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"transcode","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"buffer"},{"name":"transform","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/stream"},{"name":"Transform","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"stream"},{"name":"Transform","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"TransformableMarker","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"transformAndReviveIncomingURIs","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uriIpc"},{"name":"transformErrorForSerialization","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/errors"},{"name":"transformIncomingURIs","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uriIpc"},{"name":"transformOutgoingURIs","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uriIpc"},{"name":"TransformStream","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TransitionEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Translations","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/common/extensionPoints"},{"name":"translationsConfigFile","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"transparent","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"TransposeAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"Tray","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"TreeDragOverBubble","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/tree"},{"name":"TreeDragOverReactions","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/tree"},{"name":"TreeElement","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/documentSymbols/outlineModel"},{"name":"TreeError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/tree"},{"name":"treeIndentGuidesStroke","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"TreeItem","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TreeItem","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TreeItem2","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TreeItemCollapsibleState","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"TreeItemCollapsibleState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/views"},{"name":"TreeItemCollapsibleState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"TreeMouseEventTarget","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/tree"},{"name":"TreeNode","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"TreeResourceNavigator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"TreeView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/treeView"},{"name":"TreeViewPane","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/treeView"},{"name":"TreeVisibility","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/tree"},{"name":"TreeWalker","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"TRIGGER_RENAME_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"TriggerAction","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/quickinput/browser/pickerQuickAccess"},{"name":"triggerAsyncId","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"async_hooks"},{"name":"triggerDownload","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"TriggerParameterHintsAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/parameterHints/parameterHints"},{"name":"Triggers","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskSystem"},{"name":"TriggerSuggestAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/suggestController"},{"name":"trim","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"TrimFinalNewLinesParticipant","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/saveParticipants"},{"name":"trimTrailingWhitespace","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/commands/trimTrailingWhitespaceCommand"},{"name":"TrimTrailingWhitespaceAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"TrimTrailingWhitespaceCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/commands/trimTrailingWhitespaceCommand"},{"name":"TrimWhitespaceParticipant","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/saveParticipants"},{"name":"true","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"trueMachineIdKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetry"},{"name":"truncate","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"truncate","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"truncateSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"TRUSTED_DOMAINS_CONTENT_STORAGE_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/url/common/trustedDomains"},{"name":"TRUSTED_DOMAINS_STORAGE_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/url/common/trustedDomains"},{"name":"TrustedDomainsFileSystemProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/url/common/trustedDomainsFileSystemProvider"},{"name":"try","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"TunnelCloseableContextKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/browser/tunnelView"},{"name":"TunnelDto","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTunnelService"},{"name":"TunnelFactoryContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/common/tunnelFactory"},{"name":"TunnelModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/remoteExplorerService"},{"name":"TunnelPanel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/browser/tunnelView"},{"name":"TunnelPanelDescriptor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/browser/tunnelView"},{"name":"TunnelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/tunnelService"},{"name":"TunnelService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/node/tunnelService"},{"name":"TunnelType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/remote/common/remoteExplorerService"},{"name":"TunnelTypeContextKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/browser/tunnelView"},{"name":"TunnelViewModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/browser/tunnelView"},{"name":"twistiePixels","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/baseDebugView"},{"name":"type","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"type","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"Type","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/comment/lineCommentCommand"},{"name":"typeAndModifierIdPattern","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/tokenClassificationRegistry"},{"name":"TypeDefinitionProviderRegistry","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"TypeError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"typeof","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"TypeOperations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorTypeOperations"},{"name":"types","kind":"module","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"util"},{"name":"TypeWithAutoClosingCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorTypeOperations"},{"name":"ucs2","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"punycode"},{"name":"UIEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"UIKind","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"UIKind","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"UILabelProvider","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keybindingLabels"},{"name":"Uint16Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Uint32Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Uint8Array","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Uint8ClampedArray","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Uint8Matrix","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes/linkComputer"},{"name":"umask","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"undefined","kind":"var","kindModifiers":"","sortText":"4"},{"name":"UndoIgnoreExtensionRecommendationAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"UndoRedoElementType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/undoRedo/common/undoRedo"},{"name":"UndoRedoService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/undoRedo/common/undoRedoService"},{"name":"UndoWebviewEditorCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/electron-browser/webviewCommands"},{"name":"unescape","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"unescape","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"querystring"},{"name":"Unicode11Addon","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/xterm-addon-unicode11/typings/xterm-addon-unicode11"},{"name":"unicodeEscapesToPCRE2","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/node/ripgrepTextSearchEngine"},{"name":"UninstallAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"unique","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"uniqueFilter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/arrays"},{"name":"unknown","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"UNKNOWN_SOURCE_LABEL","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugSource"},{"name":"UnknownExtensionRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewer"},{"name":"unlink","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"unlink","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"unlinkSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"UnloadReason","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/lifecycle/electron-main/lifecycleMainService"},{"name":"unmnemonicLabel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"untildify","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/labels"},{"name":"UNTITLED_WORKSPACE_NAME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"UntitledTextEditorInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/untitled/common/untitledTextEditorInput"},{"name":"UntitledTextEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/untitled/common/untitledTextEditorModel"},{"name":"UntitledTextEditorService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/untitled/common/untitledTextEditorService"},{"name":"unwatchFile","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"unzip","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"unzipSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"UpdateAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"UpdateAllAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions"},{"name":"UpdateChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/electron-main/updateIpc"},{"name":"updateColorThemeConfigurationSchemas","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/themeConfiguration"},{"name":"UpdateContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/update/browser/update"},{"name":"updateFileIconThemeConfigurationSchemas","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/themeConfiguration"},{"name":"updateIgnoredSettings","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/settingsMerge"},{"name":"updateProblemMatchers","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/jsonSchema_v2"},{"name":"updateProductIconThemeConfigurationSchemas","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/themeConfiguration"},{"name":"updateTreeMetadata","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase"},{"name":"UpdateType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/common/update"},{"name":"updateViewTypeSchema","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/browser/editorAssociationsSetting"},{"name":"UpperCaseAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/linesOperations/linesOperations"},{"name":"uppercaseFirstLetter","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"uptime","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"uptime","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"Uri","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Uri","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/editor.api"},{"name":"URI","kind":"alias","kindModifiers":"","sortText":"0"},{"name":"URIError","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"UriIterator","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"uriToFsPath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uri"},{"name":"URITransformer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/uriIpc"},{"name":"URITransformerService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostUriTransformerService"},{"name":"url","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"inspector"},{"name":"URL","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"URL","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"url"},{"name":"URLHandlerChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/url/common/urlIpc"},{"name":"URLHandlerChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/url/common/urlIpc"},{"name":"URLHandlerRouter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/url/common/urlIpc"},{"name":"URLSearchParams","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"URLSearchParams","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"url"},{"name":"URLService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/url/node/urlService"},{"name":"USE_ICACLS","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"USE_SPLIT_JSON_SETTING","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferences"},{"name":"useFakeTimers","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"useFakeXMLHttpRequest","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/sinon/index"},{"name":"USER_DATA_SYNC_SCHEME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"USER_MANIFEST_CACHE_FILE","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/extensions/common/extensions"},{"name":"USER_STANDALONE_CONFIGURATIONS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"USER_TASKS_GROUP_KEY","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/taskService"},{"name":"userAgent","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/platform"},{"name":"UserConfiguration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/browser/configuration"},{"name":"UserDataAutoSyncChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncIpc"},{"name":"UserDataAutoSyncService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataAutoSyncService"},{"name":"UserDataAutoSyncService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService"},{"name":"UserDataAutoSyncService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/electron-browser/userDataAutoSyncService"},{"name":"UserDataAutoSyncService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService"},{"name":"UserDataSycnUtilServiceChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncIpc"},{"name":"UserDataSyncAuthentication","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/userDataSync/browser/userDataSyncAuthentication"},{"name":"UserDataSyncBackupStoreService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncBackupStoreService"},{"name":"UserDataSyncChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncIpc"},{"name":"UserDataSyncClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/test/common/userDataSyncClient"},{"name":"UserDataSyncEnablementService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncEnablementService"},{"name":"UserDataSyncError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"UserDataSyncErrorCode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"userDataSyncLogChannelId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/logs/common/logConstants"},{"name":"UserDataSyncLogService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncLog"},{"name":"UserDataSyncService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncService"},{"name":"UserDataSyncService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService"},{"name":"UserDataSyncStoreError","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSync"},{"name":"UserDataSyncStoreService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncStoreService"},{"name":"UserDataSyncTestServer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/test/common/userDataSyncClient"},{"name":"UserDataSyncTrigger","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger"},{"name":"UserDataSyncUtilServiceClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/userDataSync/common/userDataSyncIpc"},{"name":"UserDataSyncViewContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/userDataSync/browser/userDataSyncView"},{"name":"UserDataSyncWorkbenchContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/userDataSync/browser/userDataSync"},{"name":"userInfo","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"os"},{"name":"UserSettings","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationModels"},{"name":"UserSettingsLabelProvider","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/keybindingLabels"},{"name":"UserSettingsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesRenderers"},{"name":"userSettingsSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"useSlashForPath","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"USLayoutResolvedKeybinding","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/keybinding/common/usLayoutResolvedKeybinding"},{"name":"USUAL_WORD_SEPARATORS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model/wordHelper"},{"name":"UTF16be","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"UTF16be_BOM","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"UTF16le","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"UTF16le_BOM","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"UTF8","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"UTF8_BOM","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"UTF8_BOM_CHARACTER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/strings"},{"name":"UTF8_with_bom","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/encoding"},{"name":"utimes","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"utimesSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"UV_UDP_REUSEADDR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"V4MAPPED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"dns"},{"name":"valid","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"ValidAnnotatedEditOperation","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/model"},{"name":"validateConstraint","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"validateConstraints","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"ValidatedEditorOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"validateFileName","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/yauzl/index"},{"name":"validateFileName","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/fileActions"},{"name":"validatePaths","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/code/node/paths"},{"name":"validateProperty","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"validateTelemetryData","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/telemetry/common/telemetryUtils"},{"name":"ValidationState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/parsers"},{"name":"ValidationStatus","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/parsers"},{"name":"ValidityState","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"validRange","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"semver-umd"},{"name":"values","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/map"},{"name":"values","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/collections"},{"name":"var","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Variable","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetParser"},{"name":"Variable","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugModel"},{"name":"VARIABLES_VIEW_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"VariablesDataSource","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/variablesView"},{"name":"variableSetEmitter","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/variablesView"},{"name":"VariablesRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/variablesView"},{"name":"VariablesView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/variablesView"},{"name":"verbose","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/vscode-sqlite3/index"},{"name":"Verbose","kind":"enum member","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/SeverityLevel"},{"name":"Verbosity","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/editor"},{"name":"VerifiedTask","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem"},{"name":"verify","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"Verify","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"crypto"},{"name":"version","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"version","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"punycode"},{"name":"version","kind":"const","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/spdlog/index"},{"name":"version","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"versions","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"VerticalRevealType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"VerticalScrollbar","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/scrollbar/verticalScrollbar"},{"name":"VideoPlaybackQuality","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"View","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewImpl"},{"name":"VIEW_CONTAINER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/browser/explorerViewlet"},{"name":"VIEW_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"ViewColumn","kind":"enum","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"ViewColumn","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"ViewColumn","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"viewColumnToEditorGroup","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/shared/editor"},{"name":"ViewConfigurationChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewContainerLocation","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/views"},{"name":"ViewContentPriority","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/views"},{"name":"ViewContentSizeChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewContentWidgets","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets"},{"name":"ViewContext","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewContext"},{"name":"ViewController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewController"},{"name":"ViewCursor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/viewCursors/viewCursor"},{"name":"ViewCursors","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/viewCursors/viewCursors"},{"name":"ViewCursorStateChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewDecorationsChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewDescriptorService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/views/browser/viewDescriptorService"},{"name":"ViewEventDispatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEventDispatcher"},{"name":"ViewEventEmitter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewEventHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/viewEventHandler"},{"name":"ViewEventsCollector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewEventType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewFlushedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewFocusChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewIdentifierMap","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint"},{"name":"ViewLanguageConfigurationEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewLayout","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLayout"},{"name":"Viewlet","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/viewlet"},{"name":"VIEWLET_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"VIEWLET_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/search/common/search"},{"name":"VIEWLET_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/common/extensions"},{"name":"VIEWLET_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/scm/common/scm"},{"name":"VIEWLET_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/files"},{"name":"VIEWLET_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/remote/common/remote.contribution"},{"name":"ViewletActivityAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/activitybar/activitybarActions"},{"name":"ViewletDescriptor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/viewlet"},{"name":"ViewletRegistry","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/viewlet"},{"name":"ViewLine","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/lines/viewLine"},{"name":"ViewLineData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/viewModel"},{"name":"ViewLineMappingChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewLineOptions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/lines/viewLine"},{"name":"ViewLineRenderingData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/viewModel"},{"name":"ViewLines","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/lines/viewLines"},{"name":"ViewLinesChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewLinesDeletedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewLinesInsertedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewLineToken","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/core/viewLineToken"},{"name":"ViewLineTokenFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/core/viewLineToken"},{"name":"ViewLineTokens","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/core/viewLineToken"},{"name":"ViewMenuActions","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/viewMenuActions"},{"name":"ViewModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/viewModelImpl"},{"name":"ViewModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debugViewModel"},{"name":"ViewModelDecoration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/viewModel"},{"name":"ViewModelDecorations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/viewModelDecorations"},{"name":"ViewOutgoingEvents","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewOutgoingEvents"},{"name":"ViewOverlayLine","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewOverlays"},{"name":"ViewOverlays","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewOverlays"},{"name":"ViewOverlayWidgets","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets"},{"name":"ViewPane","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/viewPaneContainer"},{"name":"ViewPaneContainer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/viewPaneContainer"},{"name":"ViewPart","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewPart"},{"name":"Viewport","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewModel/viewModel"},{"name":"ViewportData","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/viewLayout/viewLinesViewportData"},{"name":"ViewQuickAccessProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess"},{"name":"ViewRevealRangeRequestEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"viewsContainersContribution","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/browser/viewsExtensionPoint"},{"name":"ViewScrollChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/parts/views/views"},{"name":"ViewsWelcomeContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution"},{"name":"viewsWelcomeExtensionPointDescriptor","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint"},{"name":"ViewsWelcomeExtensionPointFields","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint"},{"name":"ViewThemeChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewTokensChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"ViewTokensColorsChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"viewTypeSchamaAddition","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/editor/browser/editorAssociationsSetting"},{"name":"ViewZoneDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/zoneWidget/zoneWidget"},{"name":"ViewZones","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/viewParts/viewZones/viewZones"},{"name":"ViewZonesChangedEvent","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/viewEvents"},{"name":"VirtualDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/markers/browser/markersTreeViewer"},{"name":"VirtualDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree"},{"name":"virtualMachineHint","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/id"},{"name":"VirualDelegate","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewer"},{"name":"VisibleLinesCollection","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/browser/view/viewLayer"},{"name":"VisibleRanges","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/view/renderingContext"},{"name":"visit","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/json"},{"name":"void","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"VRDisplay","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"VRDisplayCapabilities","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"VRDisplayEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"VREyeParameters","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"VRFieldOfView","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"VRFrameData","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"VRPose","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"vs","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/themes"},{"name":"vs_code_editor_walkthrough","kind":"property","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough"},{"name":"vs_code_welcome_page","kind":"property","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page"},{"name":"vs_dark","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/standalone/common/themes"},{"name":"VS_DARK_THEME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/workbenchThemeService"},{"name":"VS_HC_THEME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/workbenchThemeService"},{"name":"VS_LIGHT_THEME","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/common/workbenchThemeService"},{"name":"VSBuffer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"VTTCue","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"VTTRegion","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"W_OK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WAIT_BETWEEN_RESEND","kind":"property","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Library/Sender"},{"name":"WALK_THROUGH_FOCUS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart"},{"name":"WalkThroughArrowDown","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughActions"},{"name":"WalkThroughArrowUp","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughActions"},{"name":"WalkThroughContentProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider"},{"name":"WalkThroughInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput"},{"name":"WalkThroughModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput"},{"name":"WalkThroughPageDown","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughActions"},{"name":"WalkThroughPageUp","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughActions"},{"name":"WalkThroughPart","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart"},{"name":"WalkThroughSnippetContentProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider"},{"name":"warn","kind":"method","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"console"},{"name":"Warning","kind":"enum member","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/Declarations/Contracts/Generated/SeverityLevel"},{"name":"watch","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"watch","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/chokidar/types/index"},{"name":"WATCH_VIEW_ID","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/common/debug"},{"name":"WatcherChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/unix/watcherIpc"},{"name":"WatcherChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/nsfw/watcherIpc"},{"name":"WatcherChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/unix/watcherIpc"},{"name":"WatcherChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/node/watcher/nsfw/watcherIpc"},{"name":"WatchExpressionsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/watchExpressionsView"},{"name":"WatchExpressionsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/watchExpressionsView"},{"name":"watchFile","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"watchFile","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/watcher"},{"name":"watchFolder","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/watcher"},{"name":"WatchingProblemCollector","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tasks/common/problemCollectors"},{"name":"WatermarkContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/watermark/browser/watermark"},{"name":"WaveShaperNode","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WeakMap","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WeakMapper","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/tree/tree"},{"name":"WeakSet","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebAssembly","kind":"module","kindModifiers":"declare","sortText":"4"},{"name":"WebAuthentication","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebAuthnAssertion","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"webContents","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"WebContents","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"webFrame","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"WebGL2RenderingContext","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLActiveInfo","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebglAddon","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/xterm-addon-webgl/typings/xterm-addon-webgl"},{"name":"WebGLBuffer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLContextEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLFramebuffer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLObject","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLProgram","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLQuery","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLRenderbuffer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLRenderingContext","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLSampler","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLShader","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLShaderPrecisionFormat","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLSync","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLTexture","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLTransformFeedback","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLUniformLocation","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebGLVertexArrayObject","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebIssueService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/issue/browser/issueService"},{"name":"webkitCancelAnimationFrame","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"webkitConvertPointFromNodeToPage","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"webkitConvertPointFromPageToNode","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"WebKitCSSMatrix","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebKitPoint","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"webkitRequestAnimationFrame","kind":"function","kindModifiers":"declare","sortText":"4"},{"name":"webkitRTCPeerConnection","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"webkitURL","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebLinksAddon","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/xterm-addon-web-links/typings/xterm-addon-web-links"},{"name":"WebRequest","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"WebResourceIdentityService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/resource/common/resourceIdentityService"},{"name":"WebSocket","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WebSocketNodeSocket","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/parts/ipc/node/ipc.net"},{"name":"WebTelemetryAppender","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/telemetry/browser/telemetryService"},{"name":"webviewDeveloperCategory","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webview"},{"name":"WebviewEditor","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewEditor"},{"name":"WebviewEditorCapabilities","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHost.protocol"},{"name":"WebViewEditorFindNextCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewCommands"},{"name":"WebViewEditorFindPreviousCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewCommands"},{"name":"WebviewEditorInputFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory"},{"name":"WebviewEditorService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService"},{"name":"WebviewFindWidget","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewFindWidget"},{"name":"webviewHasOwnEditFunctionsContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webview"},{"name":"webviewHasOwnEditFunctionsContextKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webview"},{"name":"WebviewIconManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewIconManager"},{"name":"WebviewInput","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewEditorInput"},{"name":"WebviewMessageChannels","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/baseWebviewElement"},{"name":"WebviewPortMappingManager","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/common/portMapping"},{"name":"WebviewResourceResponse","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/common/resourceLoader"},{"name":"WebviewResourceScheme","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/common/resourceLoader"},{"name":"WebviewService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/browser/webviewService"},{"name":"webviewTag","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"electron"},{"name":"WebviewThemeDataProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/webview/common/themeing"},{"name":"WebWorkerExtensionHostStarter","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter"},{"name":"WelcomeInputFactory","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/page/browser/welcomePage"},{"name":"WelcomeOverlayAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay"},{"name":"WelcomePageAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/page/browser/welcomePage"},{"name":"welcomePageBackground","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/page/browser/welcomePage"},{"name":"WelcomePageContribution","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/welcome/page/browser/welcomePage"},{"name":"WelcomeView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/debug/browser/welcomeView"},{"name":"WheelEvent","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"whenDeleted","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"whenProviderRegistered","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/files/common/files"},{"name":"while","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"WholeWordsCheckbox","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/findinput/findInputCheckboxes"},{"name":"Widget","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/ui/widget"},{"name":"widgetShadow","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"WidgetVerticalAlignment","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminalWidgetManager"},{"name":"WillSaveStateReason","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/storage/common/storage"},{"name":"win32","kind":"module","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"path"},{"name":"win32","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/path"},{"name":"win32","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/processes"},{"name":"Win32UpdateService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/update/electron-main/updateService.win32"},{"name":"window","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"window","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Window","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"WINDOW_ACTIVE_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"WINDOW_INACTIVE_BORDER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"WindowDriverChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/node/driver"},{"name":"WindowDriverChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/node/driver"},{"name":"WindowDriverRegistryChannel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/node/driver"},{"name":"WindowDriverRegistryChannelClient","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/driver/node/driver"},{"name":"WindowMode","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/electron-main/windows"},{"name":"windowOpenNoOpener","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/browser/dom"},{"name":"windowSettings","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/configuration/common/configurationRegistry"},{"name":"WindowsExternalTerminalService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService"},{"name":"WindowsKeyboardMapper","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/windowsKeyboardMapper"},{"name":"windowsKeyboardMappingEquals","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/windowsKeyboardMapper"},{"name":"WindowsMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/windows/electron-main/windowsMainService"},{"name":"WindowsNativeResolvedKeybinding","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/common/windowsKeyboardMapper"},{"name":"WindowsShellHelper","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/electron-browser/windowsShellHelper"},{"name":"WindowsShellType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/terminal/browser/terminal"},{"name":"windowsStore","kind":"property","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"process"},{"name":"Winreg","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"with","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"withEditorModel","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/common/editorTestUtils"},{"name":"withFormatting","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/jsonEdit"},{"name":"withNullAsUndefined","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"withTestCodeEditor","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/test/browser/testCodeEditor"},{"name":"withTestNotebook","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/notebook/test/testNotebookEditor"},{"name":"withUndefinedAsNull","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/types"},{"name":"WordCharacterClass","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/wordCharacterClassifier"},{"name":"WordCharacterClassifier","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/wordCharacterClassifier"},{"name":"WordContextKey","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/wordContextKey"},{"name":"WordDistance","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/suggest/wordDistance"},{"name":"WordLeftCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"WordNavigationType","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorWordOperations"},{"name":"WordOperations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorWordOperations"},{"name":"WordPartLeftCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordPartOperations/wordPartOperations"},{"name":"WordPartOperations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/controller/cursorWordOperations"},{"name":"WordPartRightCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordPartOperations/wordPartOperations"},{"name":"WordRightCommand","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/wordOperations/wordOperations"},{"name":"WordSelectionRangeProvider","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/smartSelect/wordSelections"},{"name":"Workbench","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/workbench"},{"name":"WORKBENCH_BACKGROUND","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/theme"},{"name":"WorkbenchAsyncDataTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"workbenchColorsSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/theme/common/colorRegistry"},{"name":"WorkbenchCompressibleAsyncDataTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchCompressibleObjectTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"workbenchConfigurationNodeBase","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/common/configuration"},{"name":"WorkbenchContextKeysHandler","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/contextkeys"},{"name":"WorkbenchDataTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"workbenchInstantiationService","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/browser/workbenchTestServices"},{"name":"workbenchInstantiationService","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/test/electron-browser/workbenchTestServices"},{"name":"WorkbenchIssueService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/issue/electron-browser/issueService"},{"name":"WorkbenchKeybindingService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/keybinding/browser/keybindingService"},{"name":"WorkbenchList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchListAutomaticKeyboardNavigation","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchListAutomaticKeyboardNavigationKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchListDoubleSelection","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchListFocusContextKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchListHasSelectionOrFocus","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchListMultiSelection","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchListSupportsKeyboardNavigation","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchListSupportsMultiSelectContextKey","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchModeServiceImpl","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/mode/common/workbenchModeService"},{"name":"WorkbenchObjectTree","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchPagedList","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/list/browser/listService"},{"name":"WorkbenchReferencesController","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/workbenchReferenceSearch"},{"name":"WorkbenchState","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspace/common/workspace"},{"name":"WorkbenchStateContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/contextkeys"},{"name":"WorkbenchThemeService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/themes/browser/workbenchThemeService"},{"name":"worker","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"Worker","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"Worker","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"Worker","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"worker_threads"},{"name":"workerData","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"worker_threads"},{"name":"WorkerExtHostDebugService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostDebugService"},{"name":"WorkerExtHostTask","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTask"},{"name":"WorkerExtHostTerminalService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTerminalService"},{"name":"workers","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"cluster"},{"name":"WorkingCopyCapabilities","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workingCopy/common/workingCopyService"},{"name":"WorkingCopyFileOperationParticipant","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workingCopy/common/workingCopyFileOperationParticipant"},{"name":"WorkingCopyFileService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workingCopy/common/workingCopyFileService"},{"name":"WorkingCopyService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/workingCopy/common/workingCopyService"},{"name":"Worklet","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"workspace","kind":"module","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"Workspace","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspace/common/workspace"},{"name":"WORKSPACE_EXTENSION","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"WORKSPACE_FILTER","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/common/workspaces"},{"name":"WORKSPACE_SCOPES","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"WORKSPACE_STANDALONE_CONFIGURATIONS","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"WorkspaceBasedVariableResolver","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/snippet/snippetVariables"},{"name":"WorkspaceChangeExtHostRelauncher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution"},{"name":"WorkspaceConfiguration","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/browser/configuration"},{"name":"WorkspaceConfigurationEditorModel","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/preferences/common/preferencesModels"},{"name":"WorkspaceConfigurationModelParser","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configurationModels"},{"name":"WorkspaceEdit","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"vscode"},{"name":"WorkspaceEdit","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypes"},{"name":"WorkspaceEdit","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"WorkspaceFileEdit","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"WorkspaceFolder","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspace/common/workspace"},{"name":"WorkspaceFolderCountContext","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/browser/contextkeys"},{"name":"WorkspaceRecommendations","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations"},{"name":"WorkspaceRecommendedExtensionsView","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViews"},{"name":"WorkspaceService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/browser/configurationService"},{"name":"WorkspaceSettingsRenderer","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/preferences/browser/preferencesRenderers"},{"name":"workspaceSettingsSchemaId","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/services/configuration/common/configuration"},{"name":"WorkspacesHistoryMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService"},{"name":"WorkspacesMainService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/electron-main/workspacesMainService"},{"name":"WorkspacesService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/workspaces/electron-main/workspacesService"},{"name":"WorkspaceSymbol","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/api/common/extHostTypeConverters"},{"name":"WorkspaceSymbolProviderRegistry","kind":"module","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/search/common/search"},{"name":"WorkspaceTags","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tags/electron-browser/workspaceTags"},{"name":"WorkspaceTagsService","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService"},{"name":"WorkspaceTextEdit","kind":"interface","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/modes"},{"name":"WorkspaceWatcher","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/files/common/workspaceWatcher"},{"name":"wrap","kind":"method","kindModifiers":"static,declare","sortText":"5","hasAction":true,"source":"module"},{"name":"WrappingIndent","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/config/editorOptions"},{"name":"WrappingIndent","kind":"enum","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/common/standalone/standaloneEnums"},{"name":"wrapWithCorrelationContext","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/applicationinsights/out/applicationinsights"},{"name":"Writable","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"stream"},{"name":"WritableStream","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"write","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"writeFile","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"writeFile","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"writeFileSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"writeFileSync","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/pfs"},{"name":"writeHeapSnapshot","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"v8"},{"name":"writeProfile","kind":"function","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"v8-inspect-profiler"},{"name":"writer","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"repl"},{"name":"WriteStream","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"WriteStream","kind":"class","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"tty"},{"name":"writeSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"writeTransientState","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap"},{"name":"writeUInt16LE","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"writeUInt32BE","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"writeUInt32LE","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"writeUInt8","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/common/buffer"},{"name":"writev","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"writevSync","kind":"function","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"fs"},{"name":"WSA_E_CANCELLED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSA_E_NO_MORE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEACCES","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEADDRINUSE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEADDRNOTAVAIL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEAFNOSUPPORT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEALREADY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEBADF","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAECANCELLED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAECONNABORTED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAECONNREFUSED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAECONNRESET","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEDESTADDRREQ","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEDISCON","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEDQUOT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEFAULT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEHOSTDOWN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEHOSTUNREACH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEINPROGRESS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEINTR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEINVAL","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEINVALIDPROCTABLE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEINVALIDPROVIDER","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEISCONN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAELOOP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEMFILE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEMSGSIZE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAENAMETOOLONG","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAENETDOWN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAENETRESET","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAENETUNREACH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAENOBUFS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAENOMORE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAENOPROTOOPT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAENOTCONN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAENOTEMPTY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAENOTSOCK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEOPNOTSUPP","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEPFNOSUPPORT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEPROCLIM","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEPROTONOSUPPORT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEPROTOTYPE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEPROVIDERFAILEDINIT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEREFUSED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEREMOTE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAESHUTDOWN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAESOCKTNOSUPPORT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAESTALE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAETIMEDOUT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAETOOMANYREFS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEUSERS","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAEWOULDBLOCK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSANOTINITIALISED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSASERVICE_NOT_FOUND","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSASYSCALLFAILURE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSASYSNOTREADY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSATYPE_NOT_FOUND","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"WSAVERNOTSUPPORTED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"X_OK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"constants"},{"name":"x01","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x02","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x03","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x04","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x05","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x06","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x07","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x08","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x09","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x10","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x11","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x12","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x13","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x14","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x15","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x16","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x17","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x18","kind":"const","kindModifiers":"","sortText":"4"},{"name":"x19","kind":"const","kindModifiers":"","sortText":"4"},{"name":"xdescribe","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"xdgRuntimeDir","kind":"const","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/platform/environment/node/environmentService"},{"name":"xit","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"XMLDocument","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"XMLHttpRequest","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"XMLHttpRequestEventTarget","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"XMLHttpRequestUpload","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"XMLSerializer","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"XPathEvaluator","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"XPathExpression","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"XPathResult","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"XSLTProcessor","kind":"var","kindModifiers":"declare","sortText":"4"},{"name":"yield","kind":"keyword","kindModifiers":"","sortText":"4"},{"name":"Z_ASCII","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_BEST_COMPRESSION","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_BEST_SPEED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_BINARY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_BLOCK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_BUF_ERROR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_DATA_ERROR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_DEFAULT_COMPRESSION","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_DEFAULT_STRATEGY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_DEFLATED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_ERRNO","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_FILTERED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_FINISH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_FIXED","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_FULL_FLUSH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_HUFFMAN_ONLY","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_MEM_ERROR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_NEED_DICT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_NO_COMPRESSION","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_NO_FLUSH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_OK","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_PARTIAL_FLUSH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_RLE","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_STREAM_END","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_STREAM_ERROR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_SYNC_FLUSH","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_TEXT","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_TREES","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_UNKNOWN","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"Z_VERSION_ERROR","kind":"const","kindModifiers":"declare","sortText":"5","hasAction":true,"source":"zlib"},{"name":"zip","kind":"function","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/base/node/zip"},{"name":"ZipFile","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/yauzl/index"},{"name":"ZipFile","kind":"class","kindModifiers":"export,declare","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/node_modules/@types/yazl/index"},{"name":"ZoneWidget","kind":"class","kindModifiers":"abstract,export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/editor/contrib/zoneWidget/zoneWidget"},{"name":"ZoomInAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"ZoomOutAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"},{"name":"ZoomResetAction","kind":"class","kindModifiers":"export","sortText":"5","hasAction":true,"source":"/Users/jrieken/Code/vscode/src/vs/workbench/electron-browser/actions/windowActions"}]}}'; + data = new ExtHostDocumentData(undefined!, URI.file(''), [ - line + perfData._$_$_expensive ], '\n', 'text', 1, false); let range = data.document.getWordRangeAtPosition(new Position(0, 1_177_170), regex)!; diff --git a/src/vs/workbench/test/browser/api/extHostDocumentSaveParticipant.test.ts b/src/vs/workbench/test/browser/api/extHostDocumentSaveParticipant.test.ts index 89743a073e5..855f92b34de 100644 --- a/src/vs/workbench/test/browser/api/extHostDocumentSaveParticipant.test.ts +++ b/src/vs/workbench/test/browser/api/extHostDocumentSaveParticipant.test.ts @@ -12,7 +12,7 @@ import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/common/extHostD import { SingleProxyRPCProtocol } from './testRPCProtocol'; import { SaveReason } from 'vs/workbench/common/editor'; import type * as vscode from 'vscode'; -import { mock } from 'vs/workbench/test/browser/api/mock'; +import { mock } from 'vs/base/test/common/mock'; import { NullLogService } from 'vs/platform/log/common/log'; import { timeout } from 'vs/base/common/async'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; diff --git a/src/vs/workbench/test/browser/api/extHostFileSystemEventService.test.ts b/src/vs/workbench/test/browser/api/extHostFileSystemEventService.test.ts index c20cee41ce1..0d26033abf7 100644 --- a/src/vs/workbench/test/browser/api/extHostFileSystemEventService.test.ts +++ b/src/vs/workbench/test/browser/api/extHostFileSystemEventService.test.ts @@ -15,7 +15,8 @@ suite('ExtHostFileSystemEventService', () => { const protocol: IMainContext = { getProxy: () => { return undefined!; }, set: undefined!, - assertRegistered: undefined! + assertRegistered: undefined!, + drain: undefined! }; const watcher1 = new ExtHostFileSystemEventService(protocol, new NullLogService(), undefined!).createFileSystemWatcher('**/somethingInteresting', false, false, false); diff --git a/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts index 02c2209624b..f6860e3f9c6 100644 --- a/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts @@ -43,7 +43,7 @@ import { getColors } from 'vs/editor/contrib/colorPicker/color'; import { CancellationToken } from 'vs/base/common/cancellation'; import { nullExtensionDescription as defaultExtension } from 'vs/workbench/services/extensions/common/extensions'; import { provideSelectionRanges } from 'vs/editor/contrib/smartSelect/smartSelect'; -import { mock } from 'vs/workbench/test/browser/api/mock'; +import { mock } from 'vs/base/test/common/mock'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { dispose } from 'vs/base/common/lifecycle'; import { withNullAsUndefined } from 'vs/base/common/types'; @@ -762,6 +762,72 @@ suite('ExtHostLanguageFeatures', function () { assert.equal(value.edits.length, 2); }); + test('Multiple RenameProviders don\'t respect all possible PrepareRename handlers, #98352', async function () { + + let called = [false, false, false, false]; + + disposables.push(extHost.registerRenameProvider(defaultExtension, defaultSelector, new class implements vscode.RenameProvider { + prepareRename(document: vscode.TextDocument, position: vscode.Position,): vscode.ProviderResult { + called[0] = true; + let range = document.getWordRangeAtPosition(position); + return range; + } + + provideRenameEdits(): vscode.ProviderResult { + called[1] = true; + return undefined; + } + })); + + disposables.push(extHost.registerRenameProvider(defaultExtension, defaultSelector, new class implements vscode.RenameProvider { + prepareRename(document: vscode.TextDocument, position: vscode.Position,): vscode.ProviderResult { + called[2] = true; + return Promise.reject('Cannot rename this symbol2.'); + } + provideRenameEdits(): vscode.ProviderResult { + called[3] = true; + return undefined; + } + })); + + await rpcProtocol.sync(); + await rename(model, new EditorPosition(1, 1), 'newName'); + + assert.deepEqual(called, [true, true, true, false]); + }); + + test('Multiple RenameProviders don\'t respect all possible PrepareRename handlers, #98352', async function () { + + let called = [false, false, false]; + + disposables.push(extHost.registerRenameProvider(defaultExtension, defaultSelector, new class implements vscode.RenameProvider { + prepareRename(document: vscode.TextDocument, position: vscode.Position,): vscode.ProviderResult { + called[0] = true; + let range = document.getWordRangeAtPosition(position); + return range; + } + + provideRenameEdits(): vscode.ProviderResult { + called[1] = true; + return undefined; + } + })); + + disposables.push(extHost.registerRenameProvider(defaultExtension, defaultSelector, new class implements vscode.RenameProvider { + + provideRenameEdits(document: vscode.TextDocument, position: vscode.Position, newName: string,): vscode.ProviderResult { + called[2] = true; + return new types.WorkspaceEdit(); + } + })); + + await rpcProtocol.sync(); + await rename(model, new EditorPosition(1, 1), 'newName'); + + // first provider has NO prepare which means it is taken by default + assert.deepEqual(called, [false, false, true]); + }); + // --- parameter hints test('Parameter Hints, order', async () => { @@ -817,9 +883,9 @@ suite('ExtHostLanguageFeatures', function () { }, [])); await rpcProtocol.sync(); - const value = await provideSuggestionItems(model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(modes.CompletionItemKind.Snippet))); - assert.equal(value.length, 1); - assert.equal(value[0].completion.insertText, 'testing2'); + const { items } = await provideSuggestionItems(model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(modes.CompletionItemKind.Snippet))); + assert.equal(items.length, 1); + assert.equal(items[0].completion.insertText, 'testing2'); }); test('Suggest, order 2/3', async () => { @@ -837,9 +903,9 @@ suite('ExtHostLanguageFeatures', function () { }, [])); await rpcProtocol.sync(); - const value = await provideSuggestionItems(model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(modes.CompletionItemKind.Snippet))); - assert.equal(value.length, 1); - assert.equal(value[0].completion.insertText, 'weak-selector'); + const { items } = await provideSuggestionItems(model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(modes.CompletionItemKind.Snippet))); + assert.equal(items.length, 1); + assert.equal(items[0].completion.insertText, 'weak-selector'); }); test('Suggest, order 2/3', async () => { @@ -857,10 +923,10 @@ suite('ExtHostLanguageFeatures', function () { }, [])); await rpcProtocol.sync(); - const value = await provideSuggestionItems(model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(modes.CompletionItemKind.Snippet))); - assert.equal(value.length, 2); - assert.equal(value[0].completion.insertText, 'strong-1'); // sort by label - assert.equal(value[1].completion.insertText, 'strong-2'); + const { items } = await provideSuggestionItems(model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(modes.CompletionItemKind.Snippet))); + assert.equal(items.length, 2); + assert.equal(items[0].completion.insertText, 'strong-1'); // sort by label + assert.equal(items[1].completion.insertText, 'strong-2'); }); test('Suggest, evil provider', async () => { @@ -879,8 +945,8 @@ suite('ExtHostLanguageFeatures', function () { await rpcProtocol.sync(); - const value = await provideSuggestionItems(model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(modes.CompletionItemKind.Snippet))); - assert.equal(value[0].container.incomplete, false); + const { items } = await provideSuggestionItems(model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(modes.CompletionItemKind.Snippet))); + assert.equal(items[0].container.incomplete, false); }); test('Suggest, CompletionList', async () => { @@ -892,8 +958,8 @@ suite('ExtHostLanguageFeatures', function () { }, [])); await rpcProtocol.sync(); - provideSuggestionItems(model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(modes.CompletionItemKind.Snippet))).then(value => { - assert.equal(value[0].container.incomplete, true); + provideSuggestionItems(model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(modes.CompletionItemKind.Snippet))).then(model => { + assert.equal(model.items[0].container.incomplete, true); }); }); diff --git a/src/vs/workbench/test/browser/api/extHostMessagerService.test.ts b/src/vs/workbench/test/browser/api/extHostMessagerService.test.ts index 004b4258742..cd6db5fb7e4 100644 --- a/src/vs/workbench/test/browser/api/extHostMessagerService.test.ts +++ b/src/vs/workbench/test/browser/api/extHostMessagerService.test.ts @@ -8,12 +8,12 @@ import { MainThreadMessageService } from 'vs/workbench/api/browser/mainThreadMes import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService, INotification, NoOpNotification, INotificationHandle, Severity, IPromptChoice, IPromptOptions, IStatusMessageOptions, NotificationsFilter } from 'vs/platform/notification/common/notification'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { mock } from 'vs/workbench/test/browser/api/mock'; +import { mock } from 'vs/base/test/common/mock'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; const emptyDialogService = new class implements IDialogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; show(): never { throw new Error('not implemented'); } @@ -37,7 +37,7 @@ const emptyCommandService: ICommandService = { }; const emptyNotificationService = new class implements INotificationService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; notify(...args: any[]): never { throw new Error('not implemented'); } @@ -62,7 +62,7 @@ const emptyNotificationService = new class implements INotificationService { }; class EmptyNotificationService implements INotificationService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(private withNotify: (notification: INotification) => void) { } diff --git a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts new file mode 100644 index 00000000000..4f4d4967242 --- /dev/null +++ b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts @@ -0,0 +1,151 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; +import { TestRPCProtocol } from 'vs/workbench/test/browser/api/testRPCProtocol'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { NullLogService } from 'vs/platform/log/common/log'; +import { mock } from 'vs/base/test/common/mock'; +import { MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostNotebookDocument, ExtHostCell } from 'vs/workbench/api/common/extHostNotebook'; +import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { URI } from 'vs/base/common/uri'; +import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; + +suite('NotebookCell', function () { + + let rpcProtocol: TestRPCProtocol; + let extHostDocumentsAndEditors: ExtHostDocumentsAndEditors; + + const disposables = new DisposableStore(); + const fakeNotebookProxy = new class extends mock() { }; + const fakeNotebook = new class extends mock() { }; + + setup(async function () { + disposables.clear(); + rpcProtocol = new TestRPCProtocol(); + extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol, new NullLogService()); + }); + + test('Document is real', function () { + + const dto = { + cellKind: CellKind.Code, + eol: '\n', + source: ['aaaa', 'bbbb', 'cccc'], + handle: 0, + language: 'fooLang', + outputs: [], + uri: URI.parse('test:/path') + }; + const cell = new ExtHostCell(fakeNotebookProxy, fakeNotebook, extHostDocumentsAndEditors, dto); + + assert.ok(cell.document); + assert.strictEqual(cell.document.version, 0); + assert.strictEqual(cell.document.languageId, dto.language); + assert.strictEqual(cell.document.uri.toString(), dto.uri.toString()); + assert.strictEqual(cell.uri.toString(), dto.uri.toString()); + }); + + + test('Document is uses actual document when possible', function () { + + const dto = { + cellKind: CellKind.Code, + eol: '\n', + source: ['aaaa', 'bbbb', 'cccc'], + handle: 0, + language: 'fooLang', + outputs: [], + uri: URI.parse('test:/path') + }; + const cell = new ExtHostCell(fakeNotebookProxy, fakeNotebook, extHostDocumentsAndEditors, dto); + + // this is the "default document" which is used when the real + // document isn't open + const documentNow = cell.document; + + extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ + addedDocuments: [{ + isDirty: false, + versionId: 12, + modeId: dto.language, + uri: dto.uri, + lines: dto.source, + EOL: dto.eol + }] + }); + + // the real document + assert.ok(documentNow !== cell.document); + assert.strictEqual(cell.document.languageId, dto.language); + assert.strictEqual(cell.document.uri.toString(), dto.uri.toString()); + assert.strictEqual(cell.uri.toString(), dto.uri.toString()); + + // back to "default document" + extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ removedDocuments: [dto.uri] }); + assert.ok(documentNow === cell.document); + }); + + test('Document can change language (1/2)', function () { + + const dto = { + cellKind: CellKind.Code, + eol: '\n', + source: ['aaaa', 'bbbb', 'cccc'], + handle: 0, + language: 'fooLang', + outputs: [], + uri: URI.parse('test:/path') + }; + const cell = new ExtHostCell(fakeNotebookProxy, fakeNotebook, extHostDocumentsAndEditors, dto); + + assert.strictEqual(cell.document.languageId, dto.language); + cell.defaultDocument._acceptLanguageId('barLang'); + assert.strictEqual(cell.document.languageId, 'barLang'); + }); + + + test('Document can change language (1/2)', function () { + + + const dto = { + cellKind: CellKind.Code, + eol: '\n', + source: ['aaaa', 'bbbb', 'cccc'], + handle: 0, + language: 'fooLang', + outputs: [], + uri: URI.parse('test:/path') + }; + + extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ + addedDocuments: [{ + isDirty: false, + versionId: 12, + modeId: dto.language, + uri: dto.uri, + lines: dto.source, + EOL: dto.eol + }] + }); + + const extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors); + + const cell = new ExtHostCell(fakeNotebookProxy, fakeNotebook, extHostDocumentsAndEditors, dto); + + // a real document already exists and therefore + // the "default document" doesn't count + + assert.strictEqual(cell.document.languageId, dto.language); + cell.defaultDocument._acceptLanguageId('barLang'); + assert.strictEqual(cell.document.languageId, dto.language); + + extHostDocuments.$acceptModelModeChanged(dto.uri, dto.language, 'barLang'); + assert.strictEqual(cell.document.languageId, 'barLang'); + }); + +}); diff --git a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts index 7695ee2df5b..3fcaa6da684 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts @@ -44,7 +44,7 @@ suite('NotebookConcatDocument', function () { }); extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol, new NullLogService()); extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors); - extHostNotebooks = new ExtHostNotebookController(rpcProtocol, new ExtHostCommands(rpcProtocol, new NullLogService()), extHostDocumentsAndEditors); + extHostNotebooks = new ExtHostNotebookController(rpcProtocol, new ExtHostCommands(rpcProtocol, new NullLogService()), extHostDocumentsAndEditors, { isExtensionDevelopmentDebug: false, webviewCspSource: '', webviewResourceRoot: '' }); let reg = extHostNotebooks.registerNotebookContentProvider(nullExtensionDescription, 'test', new class extends mock() { // async openNotebook() { } }); @@ -52,24 +52,29 @@ suite('NotebookConcatDocument', function () { addedDocuments: [{ handle: 0, uri: notebookUri, - viewType: 'test' - }] + viewType: 'test', + cells: [{ + handle: 0, + uri: CellUri.generate(notebookUri, 0), + source: ['### Heading'], + eol: '\n', + language: 'markdown', + cellKind: CellKind.Markdown, + outputs: [], + }], + versionId: 0 + }], + addedEditors: [ + { + documentUri: notebookUri, + id: '_notebook_editor_0', + selections: [0] + } + ] }); - extHostNotebooks.$acceptModelChanged(notebookUri, { - kind: NotebookCellsChangeType.ModelChange, - versionId: 0, - changes: [[0, 0, [{ - handle: 0, - uri: CellUri.generate(notebookUri, 0), - source: ['### Heading'], - language: 'markdown', - cellKind: CellKind.Markdown, - outputs: [], - }]]] - }); - await extHostNotebooks.$acceptDocumentAndEditorsDelta({ newActiveEditor: notebookUri }); + await extHostNotebooks.$acceptDocumentAndEditorsDelta({ newActiveEditor: '_notebook_editor_0' }); - notebook = extHostNotebooks.activeNotebookDocument!; + notebook = extHostNotebooks.notebookDocuments[0]!; disposables.add(reg); disposables.add(notebook); @@ -107,6 +112,43 @@ suite('NotebookConcatDocument', function () { assert.deepStrictEqual(actual, lines); } + test('contains', function () { + + const cellUri1 = CellUri.generate(notebook.uri, 1); + const cellUri2 = CellUri.generate(notebook.uri, 2); + + extHostNotebooks.$acceptModelChanged(notebookUri, { + kind: NotebookCellsChangeType.ModelChange, + versionId: notebook.versionId + 1, + changes: [[0, 0, [{ + handle: 1, + uri: cellUri1, + source: ['Hello', 'World', 'Hello World!'], + eol: '\n', + language: 'test', + cellKind: CellKind.Code, + outputs: [], + }, { + handle: 2, + uri: cellUri2, + source: ['Hallo', 'Welt', 'Hallo Welt!'], + eol: '\n', + language: 'test', + cellKind: CellKind.Code, + outputs: [], + }]]] + }); + + + assert.equal(notebook.cells.length, 1 + 2); // markdown and code + + let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined); + + assert.equal(doc.contains(cellUri1), true); + assert.equal(doc.contains(cellUri2), true); + assert.equal(doc.contains(URI.parse('some://miss/path')), false); + }); + test('location, position mapping', function () { extHostNotebooks.$acceptModelChanged(notebookUri, { @@ -116,6 +158,7 @@ suite('NotebookConcatDocument', function () { handle: 1, uri: CellUri.generate(notebook.uri, 1), source: ['Hello', 'World', 'Hello World!'], + eol: '\n', language: 'test', cellKind: CellKind.Code, outputs: [], @@ -123,6 +166,7 @@ suite('NotebookConcatDocument', function () { handle: 2, uri: CellUri.generate(notebook.uri, 2), source: ['Hallo', 'Welt', 'Hallo Welt!'], + eol: '\n', language: 'test', cellKind: CellKind.Code, outputs: [], @@ -155,6 +199,7 @@ suite('NotebookConcatDocument', function () { handle: 1, uri: CellUri.generate(notebook.uri, 1), source: ['Hello', 'World', 'Hello World!'], + eol: '\n', language: 'test', cellKind: CellKind.Code, outputs: [], @@ -177,6 +222,7 @@ suite('NotebookConcatDocument', function () { handle: 2, uri: CellUri.generate(notebook.uri, 2), source: ['Hallo', 'Welt', 'Hallo Welt!'], + eol: '\n', language: 'test', cellKind: CellKind.Code, outputs: [], @@ -218,6 +264,7 @@ suite('NotebookConcatDocument', function () { handle: 1, uri: CellUri.generate(notebook.uri, 1), source: ['Hello', 'World', 'Hello World!'], + eol: '\n', language: 'test', cellKind: CellKind.Code, outputs: [], @@ -225,6 +272,7 @@ suite('NotebookConcatDocument', function () { handle: 2, uri: CellUri.generate(notebook.uri, 2), source: ['Hallo', 'Welt', 'Hallo Welt!'], + eol: '\n', language: 'test', cellKind: CellKind.Code, outputs: [], @@ -281,6 +329,7 @@ suite('NotebookConcatDocument', function () { handle: 1, uri: CellUri.generate(notebook.uri, 1), source: ['fooLang-document'], + eol: '\n', language: 'fooLang', cellKind: CellKind.Code, outputs: [], @@ -288,6 +337,7 @@ suite('NotebookConcatDocument', function () { handle: 2, uri: CellUri.generate(notebook.uri, 2), source: ['barLang-document'], + eol: '\n', language: 'barLang', cellKind: CellKind.Code, outputs: [], @@ -309,6 +359,7 @@ suite('NotebookConcatDocument', function () { handle: 3, uri: CellUri.generate(notebook.uri, 3), source: ['barLang-document2'], + eol: '\n', language: 'barLang', cellKind: CellKind.Code, outputs: [], @@ -342,6 +393,7 @@ suite('NotebookConcatDocument', function () { handle: 1, uri: CellUri.generate(notebook.uri, 1), source: ['Hello', 'World', 'Hello World!'], + eol: '\n', language: 'test', cellKind: CellKind.Code, outputs: [], @@ -349,6 +401,7 @@ suite('NotebookConcatDocument', function () { handle: 2, uri: CellUri.generate(notebook.uri, 2), source: ['Hallo', 'Welt', 'Hallo Welt!'], + eol: '\n', language: 'test', cellKind: CellKind.Code, outputs: [], @@ -393,6 +446,7 @@ suite('NotebookConcatDocument', function () { handle: 1, uri: CellUri.generate(notebook.uri, 1), source: ['Hello', 'World', 'Hello World!'], + eol: '\n', language: 'test', cellKind: CellKind.Code, outputs: [], @@ -400,6 +454,7 @@ suite('NotebookConcatDocument', function () { handle: 2, uri: CellUri.generate(notebook.uri, 2), source: ['Hallo', 'Welt', 'Hallo Welt!'], + eol: '\n', language: 'test', cellKind: CellKind.Code, outputs: [], @@ -428,6 +483,7 @@ suite('NotebookConcatDocument', function () { handle: 1, uri: CellUri.generate(notebook.uri, 1), source: ['Hello', 'World', 'Hello World!'], + eol: '\n', language: 'test', cellKind: CellKind.Code, outputs: [], @@ -435,6 +491,7 @@ suite('NotebookConcatDocument', function () { handle: 2, uri: CellUri.generate(notebook.uri, 2), source: ['Hallo', 'Welt', 'Hallo Welt!'], + eol: '\n', language: 'test', cellKind: CellKind.Code, outputs: [], @@ -450,4 +507,55 @@ suite('NotebookConcatDocument', function () { assert.equal(doc.getText(new Range(0, 0, 1, 0)), 'Hello\n'); assert.equal(doc.getText(new Range(2, 0, 4, 0)), 'Hello World!\nHallo\n'); }); + + test('validateRange/Position', function () { + + extHostNotebooks.$acceptModelChanged(notebookUri, { + kind: NotebookCellsChangeType.ModelChange, + versionId: notebook.versionId + 1, + changes: [[0, 0, [{ + handle: 1, + uri: CellUri.generate(notebook.uri, 1), + source: ['Hello', 'World', 'Hello World!'], + eol: '\n', + language: 'test', + cellKind: CellKind.Code, + outputs: [], + }, { + handle: 2, + uri: CellUri.generate(notebook.uri, 2), + source: ['Hallo', 'Welt', 'Hallo Welt!'], + eol: '\n', + language: 'test', + cellKind: CellKind.Code, + outputs: [], + }]]] + }); + + assert.equal(notebook.cells.length, 1 + 2); // markdown and code + + let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined); + assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); + + + function assertPosition(actual: vscode.Position, expectedLine: number, expectedCh: number) { + assert.equal(actual.line, expectedLine); + assert.equal(actual.character, expectedCh); + } + + + // "fixed" + assertPosition(doc.validatePosition(new Position(0, 1000)), 0, 5); + assertPosition(doc.validatePosition(new Position(2, 1000)), 2, 12); + assertPosition(doc.validatePosition(new Position(5, 1000)), 5, 11); + assertPosition(doc.validatePosition(new Position(5000, 1000)), 5, 11); + + // "good" + assertPosition(doc.validatePosition(new Position(0, 1)), 0, 1); + assertPosition(doc.validatePosition(new Position(0, 5)), 0, 5); + assertPosition(doc.validatePosition(new Position(2, 8)), 2, 8); + assertPosition(doc.validatePosition(new Position(2, 12)), 2, 12); + assertPosition(doc.validatePosition(new Position(5, 11)), 5, 11); + + }); }); diff --git a/src/vs/workbench/test/browser/api/extHostTextEditor.test.ts b/src/vs/workbench/test/browser/api/extHostTextEditor.test.ts index aee71d8b8dd..ddfaa9b86f6 100644 --- a/src/vs/workbench/test/browser/api/extHostTextEditor.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTextEditor.test.ts @@ -9,7 +9,7 @@ import { MainThreadTextEditorsShape, IResolvedTextEditorConfiguration, ITextEdit import { ExtHostTextEditorOptions, ExtHostTextEditor } from 'vs/workbench/api/common/extHostTextEditor'; import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData'; import { URI } from 'vs/base/common/uri'; -import { mock } from 'vs/workbench/test/browser/api/mock'; +import { mock } from 'vs/base/test/common/mock'; import { NullLogService } from 'vs/platform/log/common/log'; suite('ExtHostTextEditor', () => { diff --git a/src/vs/workbench/test/browser/api/extHostTextEditors.test.ts b/src/vs/workbench/test/browser/api/extHostTextEditors.test.ts index 035cdeb9957..4d4a90b1ec1 100644 --- a/src/vs/workbench/test/browser/api/extHostTextEditors.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTextEditors.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { MainContext, MainThreadTextEditorsShape, IWorkspaceEditDto } from 'vs/workbench/api/common/extHost.protocol'; import { URI } from 'vs/base/common/uri'; -import { mock } from 'vs/workbench/test/browser/api/mock'; +import { mock } from 'vs/base/test/common/mock'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { SingleProxyRPCProtocol, TestRPCProtocol } from 'vs/workbench/test/browser/api/testRPCProtocol'; import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors'; diff --git a/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts b/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts index 887d20ea7ca..69494b5d834 100644 --- a/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts @@ -14,7 +14,7 @@ import { TestRPCProtocol } from './testRPCProtocol'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { MainThreadCommands } from 'vs/workbench/api/browser/mainThreadCommands'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { mock } from 'vs/workbench/test/browser/api/mock'; +import { mock } from 'vs/base/test/common/mock'; import { TreeItemCollapsibleState, ITreeItem } from 'vs/workbench/common/views'; import { NullLogService } from 'vs/platform/log/common/log'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; diff --git a/src/vs/workbench/test/browser/api/extHostTypeConverter.test.ts b/src/vs/workbench/test/browser/api/extHostTypeConverter.test.ts index aac33827faa..a14517bfff2 100644 --- a/src/vs/workbench/test/browser/api/extHostTypeConverter.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTypeConverter.test.ts @@ -7,12 +7,21 @@ import * as assert from 'assert'; import { MarkdownString, LogLevel } from 'vs/workbench/api/common/extHostTypeConverters'; import { isEmptyObject } from 'vs/base/common/types'; -import { size, forEach } from 'vs/base/common/collections'; +import { forEach } from 'vs/base/common/collections'; import * as types from 'vs/workbench/api/common/extHostTypes'; import { LogLevel as _MainLogLevel } from 'vs/platform/log/common/log'; import { URI } from 'vs/base/common/uri'; suite('ExtHostTypeConverter', function () { + function size(from: Record): number { + let count = 0; + for (let key in from) { + if (Object.prototype.hasOwnProperty.call(from, key)) { + count += 1; + } + } + return count; + } test('MarkdownConvert - uris', function () { diff --git a/src/vs/workbench/test/browser/api/extHostWebview.test.ts b/src/vs/workbench/test/browser/api/extHostWebview.test.ts index c3e830dd4b9..f74b2998d27 100644 --- a/src/vs/workbench/test/browser/api/extHostWebview.test.ts +++ b/src/vs/workbench/test/browser/api/extHostWebview.test.ts @@ -11,7 +11,7 @@ import { NullLogService } from 'vs/platform/log/common/log'; import { MainThreadWebviews } from 'vs/workbench/api/browser/mainThreadWebview'; import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview'; import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; -import { mock } from 'vs/workbench/test/browser/api/mock'; +import { mock } from 'vs/base/test/common/mock'; import { SingleProxyRPCProtocol } from './testRPCProtocol'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; diff --git a/src/vs/workbench/test/browser/api/extHostWorkspace.test.ts b/src/vs/workbench/test/browser/api/extHostWorkspace.test.ts index bd1c55ebce7..7e9732b500a 100644 --- a/src/vs/workbench/test/browser/api/extHostWorkspace.test.ts +++ b/src/vs/workbench/test/browser/api/extHostWorkspace.test.ts @@ -14,7 +14,7 @@ import { MainThreadWorkspace } from 'vs/workbench/api/browser/mainThreadWorkspac import { IMainContext, IWorkspaceData, MainContext, ITextSearchComplete } from 'vs/workbench/api/common/extHost.protocol'; import { RelativePattern } from 'vs/workbench/api/common/extHostTypes'; import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; -import { mock } from 'vs/workbench/test/browser/api/mock'; +import { mock } from 'vs/base/test/common/mock'; import { TestRPCProtocol } from './testRPCProtocol'; import { ExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; @@ -298,7 +298,8 @@ suite('ExtHostWorkspace', function () { const protocol: IMainContext = { getProxy: () => { return undefined!; }, set: () => { return undefined!; }, - assertRegistered: () => { } + assertRegistered: () => { }, + drain: () => { return undefined!; }, }; const ws = createExtHostWorkspace(protocol, { id: 'foo', name: 'Test', folders: [] }, new NullLogService()); diff --git a/src/vs/workbench/test/browser/api/mainThreadCommands.test.ts b/src/vs/workbench/test/browser/api/mainThreadCommands.test.ts index cf5f3583914..f761c11ce08 100644 --- a/src/vs/workbench/test/browser/api/mainThreadCommands.test.ts +++ b/src/vs/workbench/test/browser/api/mainThreadCommands.test.ts @@ -8,7 +8,7 @@ import { MainThreadCommands } from 'vs/workbench/api/browser/mainThreadCommands' import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { SingleProxyRPCProtocol } from './testRPCProtocol'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { mock } from 'vs/workbench/test/browser/api/mock'; +import { mock } from 'vs/base/test/common/mock'; suite('MainThreadCommands', function () { diff --git a/src/vs/workbench/test/browser/api/mainThreadDiagnostics.test.ts b/src/vs/workbench/test/browser/api/mainThreadDiagnostics.test.ts index 72f53943e04..3f10f0cffd6 100644 --- a/src/vs/workbench/test/browser/api/mainThreadDiagnostics.test.ts +++ b/src/vs/workbench/test/browser/api/mainThreadDiagnostics.test.ts @@ -8,6 +8,8 @@ import { MarkerService } from 'vs/platform/markers/common/markerService'; import { MainThreadDiagnostics } from 'vs/workbench/api/browser/mainThreadDiagnostics'; import { URI } from 'vs/base/common/uri'; import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; +import { mock } from 'vs/workbench/test/common/workbenchTestServices'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; suite('MainThreadDiagnostics', function () { @@ -20,16 +22,23 @@ suite('MainThreadDiagnostics', function () { test('clear markers on dispose', function () { - let diag = new MainThreadDiagnostics(new class implements IExtHostContext { - remoteAuthority = ''; - assertRegistered() { } - set(v: any): any { return null; } - getProxy(): any { - return { - $acceptMarkersChange() { } - }; + let diag = new MainThreadDiagnostics( + new class implements IExtHostContext { + remoteAuthority = ''; + assertRegistered() { } + set(v: any): any { return null; } + getProxy(): any { + return { + $acceptMarkersChange() { } + }; + } + drain(): any { return null; } + }, + markerService, + new class extends mock() { + asCanonicalUri(uri: URI) { return uri; } } - }, markerService); + ); diag.$changeMany('foo', [[URI.file('a'), [{ code: '666', diff --git a/src/vs/workbench/test/browser/api/mainThreadDocumentContentProviders.test.ts b/src/vs/workbench/test/browser/api/mainThreadDocumentContentProviders.test.ts index 234f1163ae4..22fb7d3502f 100644 --- a/src/vs/workbench/test/browser/api/mainThreadDocumentContentProviders.test.ts +++ b/src/vs/workbench/test/browser/api/mainThreadDocumentContentProviders.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { MainThreadDocumentContentProviders } from 'vs/workbench/api/browser/mainThreadDocumentContentProviders'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; -import { mock } from 'vs/workbench/test/browser/api/mock'; +import { mock } from 'vs/base/test/common/mock'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { TestRPCProtocol } from 'vs/workbench/test/browser/api/testRPCProtocol'; diff --git a/src/vs/workbench/test/browser/api/mainThreadDocuments.test.ts b/src/vs/workbench/test/browser/api/mainThreadDocuments.test.ts index a14eca8b02a..7740d677a42 100644 --- a/src/vs/workbench/test/browser/api/mainThreadDocuments.test.ts +++ b/src/vs/workbench/test/browser/api/mainThreadDocuments.test.ts @@ -7,10 +7,12 @@ import * as assert from 'assert'; import { BoundModelReferenceCollection } from 'vs/workbench/api/browser/mainThreadDocuments'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { timeout } from 'vs/base/common/async'; +import { URI } from 'vs/base/common/uri'; +import { extUri } from 'vs/base/common/resources'; suite('BoundModelReferenceCollection', () => { - let col = new BoundModelReferenceCollection(15, 75); + let col = new BoundModelReferenceCollection(extUri, 15, 75); teardown(() => { col.dispose(); @@ -20,12 +22,14 @@ suite('BoundModelReferenceCollection', () => { let didDispose = false; - col.add({ - object: { textEditorModel: createTextModel('farboo') }, - dispose() { - didDispose = true; - } - }); + col.add( + URI.parse('test://farboo'), + { + object: { textEditorModel: createTextModel('farboo') }, + dispose() { + didDispose = true; + } + }); await timeout(30); assert.equal(didDispose, true); @@ -35,27 +39,95 @@ suite('BoundModelReferenceCollection', () => { let disposed: number[] = []; - col.add({ - object: { textEditorModel: createTextModel('farboo') }, - dispose() { - disposed.push(0); - } - }); - col.add({ - object: { textEditorModel: createTextModel('boofar') }, - dispose() { - disposed.push(1); - } - }); + col.add( + URI.parse('test://farboo'), + { + object: { textEditorModel: createTextModel('farboo') }, + dispose() { + disposed.push(0); + } + }); - col.add({ - object: { textEditorModel: createTextModel(new Array(71).join('x')) }, - dispose() { - disposed.push(2); - } - }); + col.add( + URI.parse('test://boofar'), + { + object: { textEditorModel: createTextModel('boofar') }, + dispose() { + disposed.push(1); + } + }); + + col.add( + URI.parse('test://xxxxxxx'), + { + object: { textEditorModel: createTextModel(new Array(71).join('x')) }, + dispose() { + disposed.push(2); + } + }); assert.deepEqual(disposed, [0, 1]); }); + test('dispose uri', () => { + + let disposed: number[] = []; + + col.add( + URI.parse('test:///farboo'), + { + object: { textEditorModel: createTextModel('farboo') }, + dispose() { + disposed.push(0); + } + }); + + col.add( + URI.parse('test:///boofar'), + { + object: { textEditorModel: createTextModel('boofar') }, + dispose() { + disposed.push(1); + } + }); + + col.add( + URI.parse('test:///boo/far1'), + { + object: { textEditorModel: createTextModel('boo/far1') }, + dispose() { + disposed.push(2); + } + }); + + col.add( + URI.parse('test:///boo/far2'), + { + object: { textEditorModel: createTextModel('boo/far2') }, + dispose() { + disposed.push(3); + } + }); + + col.add( + URI.parse('test:///boo1/far'), + { + object: { textEditorModel: createTextModel('boo1/far') }, + dispose() { + disposed.push(4); + } + }); + + col.remove(URI.parse('test:///unknown')); + assert.equal(disposed.length, 0); + + col.remove(URI.parse('test:///farboo')); + assert.deepEqual(disposed, [0]); + + disposed = []; + + col.remove(URI.parse('test:///boo')); + assert.deepEqual(disposed, [2, 3]); + }); + }); diff --git a/src/vs/workbench/test/browser/api/mainThreadDocumentsAndEditors.test.ts b/src/vs/workbench/test/browser/api/mainThreadDocumentsAndEditors.test.ts index 09a1e6a56a9..a67651403b7 100644 --- a/src/vs/workbench/test/browser/api/mainThreadDocumentsAndEditors.test.ts +++ b/src/vs/workbench/test/browser/api/mainThreadDocumentsAndEditors.test.ts @@ -11,8 +11,8 @@ import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta } from 'vs/workbench/api/common/extHost.protocol'; -import { createTestCodeEditor, TestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; -import { mock } from 'vs/workbench/test/browser/api/mock'; +import { createTestCodeEditor, ITestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { mock } from 'vs/base/test/common/mock'; import { TestEditorService, TestEditorGroupsService, TestEnvironmentService } from 'vs/workbench/test/browser/workbenchTestServices'; import { Event } from 'vs/base/common/event'; import { ITextModel } from 'vs/editor/common/model'; @@ -25,7 +25,9 @@ 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'; -import { TestTextResourcePropertiesService } from 'vs/workbench/test/common/workbenchTestServices'; +import { TestTextResourcePropertiesService, TestWorkingCopyFileService } from 'vs/workbench/test/common/workbenchTestServices'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; suite('MainThreadDocumentsAndEditors', () => { @@ -35,7 +37,7 @@ suite('MainThreadDocumentsAndEditors', () => { let deltas: IDocumentsAndEditorsDelta[] = []; const hugeModelString = new Array(2 + (50 * 1024 * 1024)).join('-'); - function myCreateTestCodeEditor(model: ITextModel | undefined): TestCodeEditor { + function myCreateTestCodeEditor(model: ITextModel | undefined): ITestCodeEditor { return createTestCodeEditor({ model: model, serviceCollection: new ServiceCollection( @@ -66,6 +68,8 @@ suite('MainThreadDocumentsAndEditors', () => { const fileService = new class extends mock() { onDidRunOperation = Event.None; + onDidChangeFileSystemProviderCapabilities = Event.None; + onDidChangeFileSystemProviderRegistrations = Event.None; }; new MainThreadDocumentsAndEditors( @@ -81,14 +85,21 @@ suite('MainThreadDocumentsAndEditors', () => { editorGroupService, null!, new class extends mock() implements IPanelService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; onDidPanelOpen = Event.None; onDidPanelClose = Event.None; getActivePanel() { return undefined; } }, - TestEnvironmentService + TestEnvironmentService, + new TestWorkingCopyFileService(), + new UriIdentityService(fileService), + new class extends mock() { + readText() { + return Promise.resolve('clipboard_contents'); + } + } ); }); diff --git a/src/vs/workbench/test/browser/api/mainThreadEditors.test.ts b/src/vs/workbench/test/browser/api/mainThreadEditors.test.ts index 2dc9e4ca630..d0f65701a26 100644 --- a/src/vs/workbench/test/browser/api/mainThreadEditors.test.ts +++ b/src/vs/workbench/test/browser/api/mainThreadEditors.test.ts @@ -11,7 +11,7 @@ import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ExtHostDocumentsAndEditorsShape, ExtHostContext, ExtHostDocumentsShape, IWorkspaceTextEditDto } from 'vs/workbench/api/common/extHost.protocol'; -import { mock } from 'vs/workbench/test/browser/api/mock'; +import { mock } from 'vs/base/test/common/mock'; import { Event } from 'vs/base/common/event'; import { MainThreadTextEditors } from 'vs/workbench/api/browser/mainThreadEditors'; import { URI } from 'vs/base/common/uri'; @@ -48,6 +48,8 @@ import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { TestTextResourcePropertiesService, TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { extUri } from 'vs/base/common/resources'; suite('MainThreadEditors', () => { @@ -93,10 +95,6 @@ suite('MainThreadEditors', () => { services.set(IEditorGroupsService, new TestEditorGroupsService()); services.set(ITextFileService, new class extends mock() { isDirty() { return false; } - create(resource: URI) { - createdResources.add(resource); - return Promise.resolve(Object.create(null)); - } files = { onDidSave: Event.None, onDidRevert: Event.None, @@ -104,16 +102,25 @@ suite('MainThreadEditors', () => { }; }); services.set(IWorkingCopyFileService, new class extends mock() { - move(source: URI, target: URI) { + onDidRunWorkingCopyFileOperation = Event.None; + create(resource: URI) { + createdResources.add(resource); + return Promise.resolve(Object.create(null)); + } + move(files: { source: URI, target: URI }[]) { + const { source, target } = files[0]; movedResources.set(source, target); return Promise.resolve(Object.create(null)); } - copy(source: URI, target: URI) { + copy(files: { source: URI, target: URI }[]) { + const { source, target } = files[0]; copiedResources.set(source, target); return Promise.resolve(Object.create(null)); } - delete(resource: URI) { - deletedResources.add(resource); + delete(resources: URI[]) { + for (const resource of resources) { + deletedResources.add(resource); + } return Promise.resolve(undefined); } }); @@ -130,13 +137,16 @@ suite('MainThreadEditors', () => { }); services.set(IPanelService, new class extends mock() implements IPanelService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; onDidPanelOpen = Event.None; onDidPanelClose = Event.None; getActivePanel() { return undefined; } }); + services.set(IUriIdentityService, new class extends mock() { + get extUri() { return extUri; } + }); const instaService = new InstantiationService(services); diff --git a/src/vs/workbench/test/browser/api/mainThreadTreeViews.test.ts b/src/vs/workbench/test/browser/api/mainThreadTreeViews.test.ts new file mode 100644 index 00000000000..e88fcd90b6d --- /dev/null +++ b/src/vs/workbench/test/browser/api/mainThreadTreeViews.test.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 * as assert from 'assert'; +import { ExtHostTreeViewsShape, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; +import { mock } from 'vs/base/test/common/mock'; +import { ITreeItem, IViewsRegistry, Extensions, ViewContainerLocation, IViewContainersRegistry, ITreeViewDescriptor, ITreeView, ViewContainer, IViewDescriptorService, TreeItemCollapsibleState } from 'vs/workbench/common/views'; +import { NullLogService } from 'vs/platform/log/common/log'; +import { MainThreadTreeViews } from 'vs/workbench/api/browser/mainThreadTreeViews'; +import { TestViewsService, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { TestExtensionService } from 'vs/workbench/test/common/workbenchTestServices'; +import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { CustomTreeView } from 'vs/workbench/contrib/views/browser/treeView'; +import { ViewDescriptorService } from 'vs/workbench/services/views/browser/viewDescriptorService'; + +suite('MainThreadHostTreeView', function () { + const testTreeViewId = 'testTreeView'; + const customValue = 'customValue'; + const ViewsRegistry = Registry.as(Extensions.ViewsRegistry); + + interface CustomTreeItem extends ITreeItem { + customProp: string; + } + + class MockExtHostTreeViewsShape extends mock() { + async $getChildren(treeViewId: string, treeItemHandle?: string): Promise { + return [{ handle: 'testItem1', collapsibleState: TreeItemCollapsibleState.Expanded, customProp: customValue }]; + } + + async $hasResolve(): Promise { + return false; + } + + $setVisible(): void { } + } + + let container: ViewContainer; + let mainThreadTreeViews: MainThreadTreeViews; + let extHostTreeViewsShape: MockExtHostTreeViewsShape; + + setup(async () => { + const instantiationService: TestInstantiationService = workbenchInstantiationService(); + const viewDescriptorService = instantiationService.createInstance(ViewDescriptorService); + instantiationService.stub(IViewDescriptorService, viewDescriptorService); + container = Registry.as(Extensions.ViewContainersRegistry).registerViewContainer({ id: 'testContainer', name: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + const viewDescriptor: ITreeViewDescriptor = { + id: testTreeViewId, + ctorDescriptor: null!, + name: 'Test View 1', + treeView: instantiationService.createInstance(CustomTreeView, 'testTree', 'Test Title'), + }; + ViewsRegistry.registerViews([viewDescriptor], container); + + const testExtensionService = new TestExtensionService(); + extHostTreeViewsShape = new MockExtHostTreeViewsShape(); + mainThreadTreeViews = new MainThreadTreeViews( + new class implements IExtHostContext { + remoteAuthority = ''; + assertRegistered() { } + set(v: any): any { return null; } + getProxy(): any { + return extHostTreeViewsShape; + } + drain(): any { return null; } + }, new TestViewsService(), new TestNotificationService(), testExtensionService, new NullLogService()); + mainThreadTreeViews.$registerTreeViewDataProvider(testTreeViewId, { showCollapseAll: false, canSelectMany: false }); + await testExtensionService.whenInstalledExtensionsRegistered(); + }); + + teardown(() => { + ViewsRegistry.deregisterViews(ViewsRegistry.getViews(container), container); + }); + + test('getChildren keeps custom properties', async () => { + const treeView: ITreeView = (ViewsRegistry.getView(testTreeViewId)).treeView; + const children = await treeView.dataProvider?.getChildren({ handle: 'root', collapsibleState: TreeItemCollapsibleState.Expanded }); + assert(children!.length === 1, 'Exactly one child should be returned'); + assert((children![0]).customProp === customValue, 'Tree Items should keep custom properties'); + }); + + +}); diff --git a/src/vs/workbench/test/browser/api/testRPCProtocol.ts b/src/vs/workbench/test/browser/api/testRPCProtocol.ts index 16673942bd6..d2d2b1c504f 100644 --- a/src/vs/workbench/test/browser/api/testRPCProtocol.ts +++ b/src/vs/workbench/test/browser/api/testRPCProtocol.ts @@ -19,7 +19,8 @@ export function SingleProxyRPCProtocol(thing: any): IExtHostContext & IExtHostRp set(identifier: ProxyIdentifier, value: R): R { return value; }, - assertRegistered: undefined! + assertRegistered: undefined!, + drain: undefined! }; } @@ -40,6 +41,10 @@ export class TestRPCProtocol implements IExtHostContext, IExtHostRpcService { this._proxies = Object.create(null); } + drain(): Promise { + return Promise.resolve(); + } + private get _callCount(): number { return this._callCountValue; } diff --git a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts index 635c4c865fe..82a0abe7d2a 100644 --- a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts @@ -20,6 +20,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IEditorModel } from 'vs/platform/editor/common/editor'; import { dispose } from 'vs/base/common/lifecycle'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; +import { extUri } from 'vs/base/common/resources'; const NullThemeService = new TestThemeService(); @@ -158,10 +159,10 @@ suite('Workbench base editor', () => { let inst = workbenchInstantiationService(); - const editor = EditorRegistry.getEditor(inst.createInstance(MyResourceEditorInput, 'fake', '', URI.file('/fake'), undefined))!.instantiate(inst); + const editor = EditorRegistry.getEditor(inst.createInstance(MyResourceEditorInput, URI.file('/fake'), 'fake', '', undefined))!.instantiate(inst); assert.strictEqual(editor.getId(), 'myEditor'); - const otherEditor = EditorRegistry.getEditor(inst.createInstance(ResourceEditorInput, 'fake', '', URI.file('/fake'), undefined))!.instantiate(inst); + const otherEditor = EditorRegistry.getEditor(inst.createInstance(ResourceEditorInput, URI.file('/fake'), 'fake', '', undefined))!.instantiate(inst); assert.strictEqual(otherEditor.getId(), 'workbench.editors.textResourceEditor'); disposable.dispose(); @@ -170,7 +171,7 @@ suite('Workbench base editor', () => { test('Editor Lookup favors specific class over superclass (match on super class)', function () { let inst = workbenchInstantiationService(); - const editor = EditorRegistry.getEditor(inst.createInstance(MyResourceEditorInput, 'fake', '', URI.file('/fake'), undefined))!.instantiate(inst); + const editor = EditorRegistry.getEditor(inst.createInstance(MyResourceEditorInput, URI.file('/fake'), 'fake', '', undefined))!.instantiate(inst); assert.strictEqual('workbench.editors.textResourceEditor', editor.getId()); }); @@ -250,6 +251,9 @@ suite('Workbench base editor', () => { assert.ok(!memento.loadEditorState(testGroup4, URI.file('/C'))); assert.ok(memento.loadEditorState(testGroup0, URI.file('/D'))); assert.ok(!memento.loadEditorState(testGroup0, URI.file('/E'))); + + // Use fallbackToOtherGroupState + assert.ok(memento.loadEditorState(testGroup4, URI.file('/C'), true)); }); test('EditorMemento - move', function () { @@ -266,7 +270,7 @@ suite('Workbench base editor', () => { memento.saveEditorState(testGroup0, URI.file('/some/folder/file-2.txt'), { line: 2 }); memento.saveEditorState(testGroup0, URI.file('/some/other/file.txt'), { line: 3 }); - memento.moveEditorState(URI.file('/some/folder/file-1.txt'), URI.file('/some/folder/file-moved.txt')); + memento.moveEditorState(URI.file('/some/folder/file-1.txt'), URI.file('/some/folder/file-moved.txt'), extUri); let res = memento.loadEditorState(testGroup0, URI.file('/some/folder/file-1.txt')); assert.ok(!res); @@ -274,7 +278,7 @@ suite('Workbench base editor', () => { res = memento.loadEditorState(testGroup0, URI.file('/some/folder/file-moved.txt')); assert.equal(res?.line, 1); - memento.moveEditorState(URI.file('/some/folder'), URI.file('/some/folder-moved')); + memento.moveEditorState(URI.file('/some/folder'), URI.file('/some/folder-moved'), extUri); res = memento.loadEditorState(testGroup0, URI.file('/some/folder-moved/file-moved.txt')); assert.equal(res?.line, 1); diff --git a/src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts b/src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts index 067919112c6..1a39ca03966 100644 --- a/src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts @@ -32,7 +32,7 @@ suite('Breadcrumb Model', function () { test('only uri, inside workspace', function () { - let model = new EditorBreadcrumbsModel(URI.parse('foo:/bar/baz/ws/some/path/file.ts'), undefined, configService, configService, workspaceService); + let model = new EditorBreadcrumbsModel(URI.parse('foo:/bar/baz/ws/some/path/file.ts'), URI.parse('foo:/bar/baz/ws/some/path/file.ts'), undefined, configService, configService, workspaceService); let elements = model.getElements(); assert.equal(elements.length, 3); @@ -45,9 +45,24 @@ suite('Breadcrumb Model', function () { assert.equal(three.uri.toString(), 'foo:/bar/baz/ws/some/path/file.ts'); }); + test('display uri matters for FileElement', function () { + + let model = new EditorBreadcrumbsModel(URI.parse('foo:/bar/baz/ws/some/PATH/file.ts'), URI.parse('foo:/bar/baz/ws/some/path/file.ts'), undefined, configService, configService, workspaceService); + let elements = model.getElements(); + + assert.equal(elements.length, 3); + let [one, two, three] = elements as FileElement[]; + assert.equal(one.kind, FileKind.FOLDER); + assert.equal(two.kind, FileKind.FOLDER); + assert.equal(three.kind, FileKind.FILE); + assert.equal(one.uri.toString(), 'foo:/bar/baz/ws/some'); + assert.equal(two.uri.toString(), 'foo:/bar/baz/ws/some/PATH'); + assert.equal(three.uri.toString(), 'foo:/bar/baz/ws/some/PATH/file.ts'); + }); + test('only uri, outside workspace', function () { - let model = new EditorBreadcrumbsModel(URI.parse('foo:/outside/file.ts'), undefined, configService, configService, workspaceService); + let model = new EditorBreadcrumbsModel(URI.parse('foo:/outside/file.ts'), URI.parse('foo:/outside/file.ts'), undefined, configService, configService, workspaceService); let elements = model.getElements(); assert.equal(elements.length, 2); diff --git a/src/vs/workbench/test/browser/parts/editor/editor.test.ts b/src/vs/workbench/test/browser/parts/editor/editor.test.ts index e974da14e70..74821dc414f 100644 --- a/src/vs/workbench/test/browser/parts/editor/editor.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editor.test.ts @@ -34,8 +34,8 @@ suite('Workbench editor', () => { const untitled = instantiationService.createInstance(UntitledTextEditorInput, service.create()); assert.equal(toResource(untitled)!.toString(), untitled.resource.toString()); - assert.equal(toResource(untitled, { supportSideBySide: SideBySideEditor.MASTER })!.toString(), untitled.resource.toString()); - assert.equal(toResource(untitled, { supportSideBySide: SideBySideEditor.DETAILS })!.toString(), untitled.resource.toString()); + assert.equal(toResource(untitled, { supportSideBySide: SideBySideEditor.PRIMARY })!.toString(), untitled.resource.toString()); + assert.equal(toResource(untitled, { supportSideBySide: SideBySideEditor.SECONDARY })!.toString(), untitled.resource.toString()); assert.equal(toResource(untitled, { supportSideBySide: SideBySideEditor.BOTH })!.toString(), untitled.resource.toString()); assert.equal(toResource(untitled, { filterByScheme: Schemas.untitled })!.toString(), untitled.resource.toString()); assert.equal(toResource(untitled, { filterByScheme: [Schemas.file, Schemas.untitled] })!.toString(), untitled.resource.toString()); @@ -44,8 +44,8 @@ suite('Workbench editor', () => { const file = new TestEditorInput(URI.file('/some/path.txt'), 'editorResourceFileTest'); assert.equal(toResource(file)!.toString(), file.resource.toString()); - assert.equal(toResource(file, { supportSideBySide: SideBySideEditor.MASTER })!.toString(), file.resource.toString()); - assert.equal(toResource(file, { supportSideBySide: SideBySideEditor.DETAILS })!.toString(), file.resource.toString()); + assert.equal(toResource(file, { supportSideBySide: SideBySideEditor.PRIMARY })!.toString(), file.resource.toString()); + assert.equal(toResource(file, { supportSideBySide: SideBySideEditor.SECONDARY })!.toString(), file.resource.toString()); assert.equal(toResource(file, { supportSideBySide: SideBySideEditor.BOTH })!.toString(), file.resource.toString()); assert.equal(toResource(file, { filterByScheme: Schemas.file })!.toString(), file.resource.toString()); assert.equal(toResource(file, { filterByScheme: [Schemas.file, Schemas.untitled] })!.toString(), file.resource.toString()); @@ -56,20 +56,20 @@ suite('Workbench editor', () => { assert.ok(!toResource(diffEditorInput)); assert.ok(!toResource(diffEditorInput, { filterByScheme: Schemas.file })); - assert.equal(toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.MASTER })!.toString(), file.resource.toString()); - assert.equal(toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.MASTER, filterByScheme: Schemas.file })!.toString(), file.resource.toString()); - assert.equal(toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.MASTER, filterByScheme: [Schemas.file, Schemas.untitled] })!.toString(), file.resource.toString()); + assert.equal(toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.PRIMARY })!.toString(), file.resource.toString()); + assert.equal(toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.PRIMARY, filterByScheme: Schemas.file })!.toString(), file.resource.toString()); + assert.equal(toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.PRIMARY, filterByScheme: [Schemas.file, Schemas.untitled] })!.toString(), file.resource.toString()); - assert.equal(toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.DETAILS })!.toString(), untitled.resource.toString()); - assert.equal(toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.DETAILS, filterByScheme: Schemas.untitled })!.toString(), untitled.resource.toString()); - assert.equal(toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.DETAILS, filterByScheme: [Schemas.file, Schemas.untitled] })!.toString(), untitled.resource.toString()); + assert.equal(toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.SECONDARY })!.toString(), untitled.resource.toString()); + assert.equal(toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.SECONDARY, filterByScheme: Schemas.untitled })!.toString(), untitled.resource.toString()); + assert.equal(toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.SECONDARY, filterByScheme: [Schemas.file, Schemas.untitled] })!.toString(), untitled.resource.toString()); - assert.equal((toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.BOTH }) as { master: URI, detail: URI }).master.toString(), file.resource.toString()); - assert.equal((toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.BOTH, filterByScheme: Schemas.file }) as { master: URI, detail: URI }).master.toString(), file.resource.toString()); - assert.equal((toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.BOTH, filterByScheme: [Schemas.file, Schemas.untitled] }) as { master: URI, detail: URI }).master.toString(), file.resource.toString()); + assert.equal((toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.BOTH }) as { primary: URI, secondary: URI }).primary.toString(), file.resource.toString()); + assert.equal((toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.BOTH, filterByScheme: Schemas.file }) as { primary: URI, secondary: URI }).primary.toString(), file.resource.toString()); + assert.equal((toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.BOTH, filterByScheme: [Schemas.file, Schemas.untitled] }) as { primary: URI, secondary: URI }).primary.toString(), file.resource.toString()); - assert.equal((toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.BOTH }) as { master: URI, detail: URI }).detail.toString(), untitled.resource.toString()); - assert.equal((toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.BOTH, filterByScheme: Schemas.untitled }) as { master: URI, detail: URI }).detail.toString(), untitled.resource.toString()); - assert.equal((toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.BOTH, filterByScheme: [Schemas.file, Schemas.untitled] }) as { master: URI, detail: URI }).detail.toString(), untitled.resource.toString()); + assert.equal((toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.BOTH }) as { primary: URI, secondary: URI }).secondary.toString(), untitled.resource.toString()); + assert.equal((toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.BOTH, filterByScheme: Schemas.untitled }) as { primary: URI, secondary: URI }).secondary.toString(), untitled.resource.toString()); + assert.equal((toResource(diffEditorInput, { supportSideBySide: SideBySideEditor.BOTH, filterByScheme: [Schemas.file, Schemas.untitled] }) as { primary: URI, secondary: URI }).secondary.toString(), untitled.resource.toString()); }); }); diff --git a/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts b/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts index 37bfc6dc478..6333219069c 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts @@ -35,8 +35,8 @@ suite('Workbench editor model', () => { } }); - let input = instantiationService.createInstance(ResourceEditorInput, 'name', 'description', URI.from({ scheme: 'test', authority: null!, path: 'thePath' }), undefined); - let otherInput = instantiationService.createInstance(ResourceEditorInput, 'name2', 'description', URI.from({ scheme: 'test', authority: null!, path: 'thePath' }), undefined); + let input = instantiationService.createInstance(ResourceEditorInput, URI.from({ scheme: 'test', authority: null!, path: 'thePath' }), 'name', 'description', undefined); + let otherInput = instantiationService.createInstance(ResourceEditorInput, URI.from({ scheme: 'test', authority: null!, path: 'thePath' }), 'name2', 'description', undefined); let diffInput = new DiffEditorInput('name', 'description', input, otherInput); let model = await diffInput.resolve() as TextDiffEditorModel; diff --git a/src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts b/src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts index 2f18fe411d6..1e618eb1943 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts @@ -79,6 +79,8 @@ interface GroupEvents { closed: EditorCloseEvent[]; pinned: EditorInput[]; unpinned: EditorInput[]; + sticky: EditorInput[]; + unsticky: EditorInput[]; moved: EditorInput[]; disposed: EditorInput[]; } @@ -90,6 +92,8 @@ function groupListener(group: EditorGroup): GroupEvents { activated: [], pinned: [], unpinned: [], + sticky: [], + unsticky: [], moved: [], disposed: [] }; @@ -98,6 +102,7 @@ function groupListener(group: EditorGroup): GroupEvents { group.onDidCloseEditor(e => groupEvents.closed.push(e)); group.onDidActivateEditor(e => groupEvents.activated.push(e)); group.onDidChangeEditorPinned(e => group.isPinned(e) ? groupEvents.pinned.push(e) : groupEvents.unpinned.push(e)); + group.onDidChangeEditorSticky(e => group.isSticky(e) ? groupEvents.sticky.push(e) : groupEvents.unsticky.push(e)); group.onDidMoveEditor(e => groupEvents.moved.push(e)); group.onDidDisposeEditor(e => groupEvents.disposed.push(e)); @@ -145,11 +150,14 @@ class NonSerializableTestEditorInput extends EditorInput { class TestFileEditorInput extends EditorInput implements IFileEditorInput { + readonly preferredResource = this.resource; + constructor(public id: string, public resource: URI) { super(); } getTypeId() { return 'testFileEditorInputForGroups'; } resolve(): Promise { return Promise.resolve(null!); } + setPreferredResource(resource: URI): void { } setEncoding(encoding: string) { } getEncoding() { return undefined; } setPreferredEncoding(encoding: string) { } @@ -271,9 +279,11 @@ suite('Workbench editor groups', () => { group.openEditor(input1, { pinned: true, active: true }); assert.equal(group.contains(input1), true); - assert.equal(group.contains(input1, true), true); + assert.equal(group.contains(input1, { strictEquals: true }), true); + assert.equal(group.contains(input1, { supportSideBySide: true }), true); assert.equal(group.contains(input2), false); - assert.equal(group.contains(input2, true), false); + assert.equal(group.contains(input2, { strictEquals: true }), false); + assert.equal(group.contains(input2, { supportSideBySide: true }), false); assert.equal(group.contains(diffInput1), false); assert.equal(group.contains(diffInput2), false); @@ -301,7 +311,7 @@ suite('Workbench editor groups', () => { group.closeEditor(input1); assert.equal(group.contains(input1), false); - assert.equal(group.contains(input1, true), true); + assert.equal(group.contains(input1, { supportSideBySide: true }), true); assert.equal(group.contains(input2), true); assert.equal(group.contains(diffInput1), true); assert.equal(group.contains(diffInput2), true); @@ -309,27 +319,27 @@ suite('Workbench editor groups', () => { group.closeEditor(input2); assert.equal(group.contains(input1), false); - assert.equal(group.contains(input1, true), true); + assert.equal(group.contains(input1, { supportSideBySide: true }), true); assert.equal(group.contains(input2), false); - assert.equal(group.contains(input2, true), true); + assert.equal(group.contains(input2, { supportSideBySide: true }), true); assert.equal(group.contains(diffInput1), true); assert.equal(group.contains(diffInput2), true); group.closeEditor(diffInput1); assert.equal(group.contains(input1), false); - assert.equal(group.contains(input1, true), true); + assert.equal(group.contains(input1, { supportSideBySide: true }), true); assert.equal(group.contains(input2), false); - assert.equal(group.contains(input2, true), true); + assert.equal(group.contains(input2, { supportSideBySide: true }), true); assert.equal(group.contains(diffInput1), false); assert.equal(group.contains(diffInput2), true); group.closeEditor(diffInput2); assert.equal(group.contains(input1), false); - assert.equal(group.contains(input1, true), false); + assert.equal(group.contains(input1, { supportSideBySide: true }), false); assert.equal(group.contains(input2), false); - assert.equal(group.contains(input2, true), false); + assert.equal(group.contains(input2, { supportSideBySide: true }), false); assert.equal(group.contains(diffInput1), false); assert.equal(group.contains(diffInput2), false); @@ -609,6 +619,12 @@ suite('Workbench editor groups', () => { group.pin(sameInput1); assert.equal(events.pinned[0], input1); + group.stick(sameInput1); + assert.equal(events.sticky[0], input1); + + group.unstick(sameInput1); + assert.equal(events.unsticky[0], input1); + group.moveEditor(sameInput1, 1); assert.equal(events.moved[0], input1); diff --git a/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts b/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts index 7634e777a07..a0fa7d56f5d 100644 --- a/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts @@ -26,7 +26,7 @@ suite('Resource text editors', () => { const resource = URI.from({ scheme: 'inmemory', authority: null!, path: 'thePath' }); accessor.modelService.createModel('function test() {}', accessor.modeService.create('text'), resource); - const input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource, undefined); + const input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, resource, 'The Name', 'The Description', undefined); const model = await input.resolve(); @@ -42,7 +42,7 @@ suite('Resource text editors', () => { const resource = URI.from({ scheme: 'inmemory', authority: null!, path: 'thePath' }); accessor.modelService.createModel('function test() {}', accessor.modeService.create('text'), resource); - const input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource, 'resource-input-test'); + const input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, resource, 'The Name', 'The Description', 'resource-input-test'); const model = await input.resolve(); assert.ok(model); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index c1561a0ba6e..8840867e739 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -23,7 +23,7 @@ import { IUntitledTextEditorService, UntitledTextEditorService } from 'vs/workbe import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { ILifecycleService, BeforeShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { FileOperationEvent, IFileService, IFileStat, IResolveFileResult, FileChangesEvent, IResolveFileOptions, ICreateFileOptions, IFileSystemProvider, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileType, FileDeleteOptions, FileOverwriteOptions, FileWriteOptions, FileOpenOptions, IFileStatWithMetadata, IResolveMetadataFileOptions, IWriteFileOptions, IReadFileOptions, IFileContent, IFileStreamContent, FileOperationError } from 'vs/platform/files/common/files'; +import { FileOperationEvent, IFileService, IFileStat, IResolveFileResult, FileChangesEvent, IResolveFileOptions, ICreateFileOptions, IFileSystemProvider, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileType, FileDeleteOptions, FileOverwriteOptions, FileWriteOptions, FileOpenOptions, IFileStatWithMetadata, IResolveMetadataFileOptions, IWriteFileOptions, IReadFileOptions, IFileContent, IFileStreamContent, FileOperationError, IFileSystemProviderWithFileReadStreamCapability } from 'vs/platform/files/common/files'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; @@ -107,9 +107,16 @@ import { TestWorkingCopyService, TestContextService, TestStorageService, TestTex import { IViewsService, IView, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views'; import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IPaneComposite } from 'vs/workbench/common/panecomposite'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; +import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; +import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; +import { newWriteableStream, ReadableStreamEvents } from 'vs/base/common/stream'; +import { EncodingOracle, IEncodingOverride } from 'vs/workbench/services/textfile/browser/textFileService'; +import { UTF16le, UTF16be, UTF8_with_bom } from 'vs/workbench/services/textfile/common/encoding'; export function createFileEditorInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { - return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined); + return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined); } export interface ITestInstantiationService extends IInstantiationService { @@ -118,7 +125,8 @@ export interface ITestInstantiationService extends IInstantiationService { export function workbenchInstantiationService(overrides?: { textFileService?: (instantiationService: IInstantiationService) => ITextFileService - pathService?: (instantiationService: IInstantiationService) => IPathService + pathService?: (instantiationService: IInstantiationService) => IPathService, + editorService?: (instantiationService: IInstantiationService) => IEditorService }): ITestInstantiationService { const instantiationService = new TestInstantiationService(new ServiceCollection([ILifecycleService, new TestLifecycleService()])); @@ -149,7 +157,9 @@ export function workbenchInstantiationService(overrides?: { const themeService = new TestThemeService(); instantiationService.stub(IThemeService, themeService); instantiationService.stub(IModelService, instantiationService.createInstance(ModelServiceImpl)); - instantiationService.stub(IFileService, new TestFileService()); + const fileService = new TestFileService(); + instantiationService.stub(IFileService, fileService); + instantiationService.stub(IUriIdentityService, new UriIdentityService(fileService)); instantiationService.stub(IBackupFileService, new TestBackupFileService()); instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(INotificationService, new TestNotificationService()); @@ -167,7 +177,7 @@ export function workbenchInstantiationService(overrides?: { const editorGroupService = new TestEditorGroupsService([new TestEditorGroupView(0)]); instantiationService.stub(IEditorGroupsService, editorGroupService); instantiationService.stub(ILabelService, instantiationService.createInstance(LabelService)); - const editorService = new TestEditorService(editorGroupService); + const editorService = overrides?.editorService ? overrides.editorService(instantiationService) : new TestEditorService(editorGroupService); instantiationService.stub(IEditorService, editorService); instantiationService.stub(ICodeEditorService, new CodeEditorService(editorService, themeService)); instantiationService.stub(IViewletService, new TestViewletService()); @@ -219,7 +229,9 @@ export class TestTextFileService extends BrowserTextFileService { @ITextModelService textModelService: ITextModelService, @ICodeEditorService codeEditorService: ICodeEditorService, @IPathService pathService: IPathService, - @IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService + @IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService, + @IUriIdentityService uriIdentityService: IUriIdentityService, + @IModeService modeService: IModeService ) { super( fileService, @@ -235,7 +247,9 @@ export class TestTextFileService extends BrowserTextFileService { textModelService, codeEditorService, pathService, - workingCopyFileService + workingCopyFileService, + uriIdentityService, + modeService ); } @@ -265,6 +279,31 @@ export class TestTextFileService extends BrowserTextFileService { } } +export class TestBrowserTextFileServiceWithEncodingOverrides extends BrowserTextFileService { + + private _testEncoding: TestEncodingOracle | undefined; + get encoding(): TestEncodingOracle { + if (!this._testEncoding) { + this._testEncoding = this._register(this.instantiationService.createInstance(TestEncodingOracle)); + } + + return this._testEncoding; + } +} + +export class TestEncodingOracle extends EncodingOracle { + + protected get encodingOverrides(): IEncodingOverride[] { + return [ + { extension: 'utf16le', encoding: UTF16le }, + { extension: 'utf16be', encoding: UTF16be }, + { extension: 'utf8bom', encoding: UTF8_with_bom } + ]; + } + + protected set encodingOverrides(overrides: IEncodingOverride[]) { } +} + class TestEnvironmentServiceWithArgs extends BrowserWorkbenchEnvironmentService { args = []; } @@ -273,7 +312,7 @@ export const TestEnvironmentService = new TestEnvironmentServiceWithArgs(Object. export class TestProgressService implements IProgressService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; withProgress( options: IProgressOptions | IProgressWindowOptions | IProgressNotificationOptions | IProgressCompositeOptions, @@ -286,7 +325,7 @@ export class TestProgressService implements IProgressService { export class TestAccessibilityService implements IAccessibilityService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; onDidChangeScreenReaderOptimized = Event.None; @@ -298,7 +337,7 @@ export class TestAccessibilityService implements IAccessibilityService { export class TestDecorationsService implements IDecorationsService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; onDidChangeDecorations: Event = Event.None; @@ -308,7 +347,7 @@ export class TestDecorationsService implements IDecorationsService { export class TestMenuService implements IMenuService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; createMenu(_id: MenuId, _scopedKeybindingService: IContextKeyService): IMenu { return { @@ -321,7 +360,7 @@ export class TestMenuService implements IMenuService { export class TestHistoryService implements IHistoryService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(private root?: URI) { } @@ -342,7 +381,7 @@ export class TestHistoryService implements IHistoryService { export class TestFileDialogService implements IFileDialogService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private confirmResult!: ConfirmResult; @@ -367,7 +406,7 @@ export class TestFileDialogService implements IFileDialogService { export class TestLayoutService implements IWorkbenchLayoutService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; openedDefaultEditors = false; @@ -427,7 +466,7 @@ export class TestLayoutService implements IWorkbenchLayoutService { let activeViewlet: Viewlet = {} as any; export class TestViewletService implements IViewletService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; onDidViewletRegisterEmitter = new Emitter(); onDidViewletDeregisterEmitter = new Emitter(); @@ -452,7 +491,7 @@ export class TestViewletService implements IViewletService { } export class TestPanelService implements IPanelService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; onDidPanelOpen = new Emitter<{ panel: IPanel, focus: boolean }>().event; onDidPanelClose = new Emitter().event; @@ -471,7 +510,7 @@ export class TestPanelService implements IPanelService { } export class TestViewsService implements IViewsService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; onDidChangeViewContainerVisibility = new Emitter<{ id: string; visible: boolean; location: ViewContainerLocation }>().event; @@ -487,11 +526,12 @@ export class TestViewsService implements IViewsService { openView(id: string, focus?: boolean | undefined): Promise { return Promise.resolve(null); } closeView(id: string): void { } getViewProgressIndicator(id: string) { return null!; } + getActiveViewPaneContainerWithId(id: string) { return null; } } export class TestEditorGroupsService implements IEditorGroupsService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(public groups: TestEditorGroupView[] = []) { } @@ -622,7 +662,7 @@ export class TestEditorGroupAccessor implements IEditorGroupsAccessor { export class TestEditorService implements EditorServiceImpl { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; onDidActiveEditorChange: Event = Event.None; onDidVisibleEditorsChange: Event = Event.None; @@ -630,10 +670,17 @@ export class TestEditorService implements EditorServiceImpl { onDidOpenEditorFail: Event = Event.None; onDidMostRecentlyActiveEditorsChange: Event = Event.None; + private _activeTextEditorControl: ICodeEditor | IDiffEditor | undefined; + public get activeTextEditorControl(): ICodeEditor | IDiffEditor | undefined { return this._activeTextEditorControl; } + public set activeTextEditorControl(value: ICodeEditor | IDiffEditor | undefined) { this._activeTextEditorControl = value; } + activeEditorPane: IVisibleEditorPane | undefined; - activeTextEditorControl: ICodeEditor | IDiffEditor | undefined; activeTextEditorMode: string | undefined; - activeEditor: IEditorInput | undefined; + + private _activeEditor: IEditorInput | undefined; + public get activeEditor(): IEditorInput | undefined { return this._activeEditor; } + public set activeEditor(value: IEditorInput | undefined) { this._activeEditor = value; } + editors: ReadonlyArray = []; mostRecentlyActiveEditors: ReadonlyArray = []; visibleEditorPanes: ReadonlyArray = []; @@ -670,11 +717,12 @@ export class TestEditorService implements EditorServiceImpl { saveAll(options?: ISaveEditorsOptions): Promise { throw new Error('Method not implemented.'); } revert(editors: IEditorIdentifier[], options?: IRevertOptions): Promise { throw new Error('Method not implemented.'); } revertAll(options?: IRevertAllEditorsOptions): Promise { throw new Error('Method not implemented.'); } + whenClosed(editors: IResourceEditorInput[], options?: { waitForSaved: boolean }): Promise { throw new Error('Method not implemented.'); } } export class TestFileService implements IFileService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _onDidFilesChange = new Emitter(); private readonly _onDidRunOperation = new Emitter(); @@ -736,7 +784,7 @@ export class TestFileService implements IFileService { this.lastReadFileUri = resource; return Promise.resolve({ - resource: resource, + resource, value: { on: (event: string, callback: Function): void => { if (event === 'data') { @@ -746,6 +794,7 @@ export class TestFileService implements IFileService { callback(); } }, + removeListener: () => { }, resume: () => { }, pause: () => { }, destroy: () => { } @@ -817,10 +866,15 @@ export class TestFileService implements IFileService { getWriteEncoding(_resource: URI): IResourceEncoding { return { encoding: 'utf8', hasBOM: false }; } dispose(): void { } + + async canCreateFile(source: URI, options?: ICreateFileOptions): Promise { return true; } + async canMove(source: URI, target: URI, overwrite?: boolean | undefined): Promise { return true; } + async canCopy(source: URI, target: URI, overwrite?: boolean | undefined): Promise { return true; } + async canDelete(resource: URI, options?: { useTrash?: boolean | undefined; recursive?: boolean | undefined; } | undefined): Promise { return true; } } export class TestBackupFileService implements IBackupFileService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; hasBackups(): Promise { return Promise.resolve(false); } hasBackup(_resource: URI): Promise { return Promise.resolve(false); } @@ -842,7 +896,7 @@ export class TestBackupFileService implements IBackupFileService { export class TestLifecycleService implements ILifecycleService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; phase!: LifecyclePhase; startupKind!: StartupKind; @@ -870,7 +924,7 @@ export class TestLifecycleService implements ILifecycleService { export class TestTextResourceConfigurationService implements ITextResourceConfigurationService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(private configurationService = new TestConfigurationService()) { } @@ -923,11 +977,44 @@ export class RemoteFileSystemProvider implements IFileSystemProvider { private toFileResource(resource: URI): URI { return resource.with({ scheme: Schemas.file, authority: '' }); } } +export class TestInMemoryFileSystemProvider extends InMemoryFileSystemProvider implements IFileSystemProviderWithFileReadStreamCapability { + readonly capabilities: FileSystemProviderCapabilities = + FileSystemProviderCapabilities.FileReadWrite + | FileSystemProviderCapabilities.PathCaseSensitive + | FileSystemProviderCapabilities.FileReadStream; + + + readFileStream(resource: URI): ReadableStreamEvents { + const BUFFER_SIZE = 64 * 1024; + const stream = newWriteableStream(data => VSBuffer.concat(data.map(data => VSBuffer.wrap(data))).buffer); + + (async () => { + try { + const data = await this.readFile(resource); + + let offset = 0; + while (offset < data.length) { + await timeout(0); + await stream.write(data.subarray(offset, offset + BUFFER_SIZE)); + offset += BUFFER_SIZE; + } + + await timeout(0); + stream.end(); + } catch (error) { + stream.end(error); + } + })(); + + return stream; + } +} + export const productService: IProductService = { _serviceBrand: undefined, ...product }; export class TestHostService implements IHostService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private _hasFocus = true; get hasFocus() { return this._hasFocus; } @@ -1035,6 +1122,9 @@ export function registerTestEditor(id: string, inputs: SyncDescriptor { return !this.fails ? Promise.resolve(null) : Promise.reject(new Error('fails')); } matches(other: EditorInput): boolean { return !!(other?.resource && this.resource.toString() === other.resource.toString() && other instanceof TestFileEditorInput && other.getTypeId() === this.typeId); } + setPreferredResource(resource: URI): void { } setEncoding(encoding: string) { } getEncoding() { return undefined; } setPreferredEncoding(encoding: string) { } @@ -1089,7 +1180,7 @@ export class TestFileEditorInput extends EditorInput implements IFileEditorInput this.gotDisposed = true; } movedEditor: IMoveResult | undefined = undefined; - move(): IMoveResult | undefined { return this.movedEditor; } + rename(): IMoveResult | undefined { return this.movedEditor; } } export class TestEditorPart extends EditorPart { @@ -1112,7 +1203,7 @@ export class TestEditorPart extends EditorPart { } export class TestListService implements IListService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; lastFocusedList: any | undefined = undefined; @@ -1123,16 +1214,27 @@ export class TestListService implements IListService { export class TestPathService implements IPathService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(private readonly fallbackUserHome: URI = URI.from({ scheme: Schemas.vscodeRemote, path: '/' })) { } get path() { return Promise.resolve(isWindows ? win32 : posix); } - get userHome() { return Promise.resolve(this.fallbackUserHome); } + async userHome() { return this.fallbackUserHome; } get resolvedUserHome() { return this.fallbackUserHome; } async fileURI(path: string): Promise { return URI.file(path); } } + +export class TestTextFileEditorModelManager extends TextFileEditorModelManager { + + add(resource: URI, model: TextFileEditorModel): void { + return super.add(resource, model); + } + + remove(resource: URI): void { + return super.remove(resource); + } +} diff --git a/src/vs/workbench/test/common/workbenchTestServices.ts b/src/vs/workbench/test/common/workbenchTestServices.ts index 3f4f21f7cec..3dda9efc00d 100644 --- a/src/vs/workbench/test/common/workbenchTestServices.ts +++ b/src/vs/workbench/test/common/workbenchTestServices.ts @@ -14,12 +14,16 @@ import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderW import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService'; import { isLinux, isMacintosh } from 'vs/base/common/platform'; import { InMemoryStorageService, IWillSaveStateEvent } from 'vs/platform/storage/common/storage'; -import { WorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { WorkingCopyService, IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { NullExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IWorkingCopyFileService, IWorkingCopyFileOperationParticipant, WorkingCopyFileEvent } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { IFileStatWithMetadata } from 'vs/platform/files/common/files'; +import { VSBuffer, VSBufferReadable, VSBufferReadableStream } from 'vs/base/common/buffer'; export class TestTextResourcePropertiesService implements ITextResourcePropertiesService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor( @IConfigurationService private readonly configurationService: IConfigurationService, @@ -37,7 +41,7 @@ export class TestTextResourcePropertiesService implements ITextResourcePropertie export class TestContextService implements IWorkspaceContextService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private workspace: Workspace; private options: object; @@ -121,6 +125,29 @@ export class TestStorageService extends InMemoryStorageService { export class TestWorkingCopyService extends WorkingCopyService { } +export class TestWorkingCopyFileService implements IWorkingCopyFileService { + + declare readonly _serviceBrand: undefined; + + onWillRunWorkingCopyFileOperation: Event = Event.None; + onDidFailWorkingCopyFileOperation: Event = Event.None; + onDidRunWorkingCopyFileOperation: Event = Event.None; + + addFileOperationParticipant(participant: IWorkingCopyFileOperationParticipant): IDisposable { return Disposable.None; } + + async delete(resources: URI[], options?: { useTrash?: boolean | undefined; recursive?: boolean | undefined; } | undefined): Promise { } + + registerWorkingCopyProvider(provider: (resourceOrFolder: URI) => IWorkingCopy[]): IDisposable { return Disposable.None; } + + getDirty(resource: URI): IWorkingCopy[] { return []; } + + create(resource: URI, contents?: VSBuffer | VSBufferReadable | VSBufferReadableStream, options?: { overwrite?: boolean | undefined; } | undefined): Promise { throw new Error('Method not implemented.'); } + + move(files: { source: URI; target: URI; }[], options?: { overwrite?: boolean }): Promise { throw new Error('Method not implemented.'); } + + copy(files: { source: URI; target: URI; }[], options?: { overwrite?: boolean }): Promise { throw new Error('Method not implemented.'); } +} + export function mock(): Ctor { return function () { } as any; } diff --git a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts index 342a63637b7..ff6d95047f9 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts @@ -19,7 +19,7 @@ import { TestRPCProtocol } from 'vs/workbench/test/browser/api/testRPCProtocol'; import type * as vscode from 'vscode'; import { NullLogService } from 'vs/platform/log/common/log'; import { URITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService'; -import { mock } from 'vs/workbench/test/browser/api/mock'; +import { mock } from 'vs/base/test/common/mock'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { TextSearchManager } from 'vs/workbench/services/search/common/textSearchManager'; import { NativeTextSearchManager } from 'vs/workbench/services/search/node/textSearchManager'; @@ -144,7 +144,7 @@ suite('ExtHostSearch', () => { constructor() { super( rpcProtocol, - new class extends mock() { remote = { isRemote: false, authority: undefined }; }, + new class extends mock() { remote = { isRemote: false, authority: undefined, connectionData: null }; }, new URITransformerService(null), logService ); diff --git a/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts b/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts index 8ae5761afb7..ed378fb9496 100644 --- a/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts +++ b/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts @@ -163,6 +163,7 @@ suite.skip('TextSearch performance (integration)', () => { class TestTelemetryService implements ITelemetryService { public _serviceBrand: undefined; public isOptedIn = true; + public sendErrorTelemetry = true; public events: any[] = []; @@ -175,6 +176,9 @@ class TestTelemetryService implements ITelemetryService { public setEnabled(value: boolean): void { } + public setExperimentProperty(name: string, value: string): void { + } + public publicLog(eventName: string, data?: any): Promise { const event = { name: eventName, data: data }; this.events.push(event); diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index 0387e29d43b..9e555a89207 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -3,20 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { workbenchInstantiationService as browserWorkbenchInstantiationService, ITestInstantiationService, TestLifecycleService, TestFilesConfigurationService, TestFileService, TestFileDialogService, TestPathService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService as browserWorkbenchInstantiationService, ITestInstantiationService, TestLifecycleService, TestFilesConfigurationService, TestFileService, TestFileDialogService, TestPathService, TestEncodingOracle } from 'vs/workbench/test/browser/workbenchTestServices'; import { Event } from 'vs/base/common/event'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { NativeWorkbenchEnvironmentService, INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; -import { NativeTextFileService, EncodingOracle, IEncodingOverride } from 'vs/workbench/services/textfile/electron-browser/nativeTextFileService'; -import { IElectronService } from 'vs/platform/electron/node/electron'; -import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs'; +import { NativeTextFileService, } from 'vs/workbench/services/textfile/electron-browser/nativeTextFileService'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { FileOperationError, IFileService } from 'vs/platform/files/common/files'; import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IDialogService, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IDialogService, IFileDialogService, INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; import { IProductService } from 'vs/platform/product/common/productService'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; @@ -25,20 +24,22 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { URI } from 'vs/base/common/uri'; import { IReadTextFileOptions, ITextFileStreamContent, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; -import { IOpenEmptyWindowOptions, IWindowOpenable, IOpenWindowOptions } from 'vs/platform/windows/common/windows'; +import { IOpenEmptyWindowOptions, IWindowOpenable, IOpenWindowOptions, IOpenedWindow } from 'vs/platform/windows/common/windows'; import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; import { LogLevel, ILogService } from 'vs/platform/log/common/log'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; -import { UTF16le, UTF16be, UTF8_with_bom } from 'vs/base/node/encoding'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { NodeTestBackupFileService } from 'vs/workbench/services/backup/test/electron-browser/backupFileService.test'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { INativeWindowConfiguration, IOpenedWindow } from 'vs/platform/windows/node/window'; +import { INativeWindowConfiguration } from 'vs/platform/windows/node/window'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { MouseInputEvent } from 'vs/base/parts/sandbox/common/electronTypes'; +import { IModeService } from 'vs/editor/common/services/modeService'; export const TestWindowConfiguration: INativeWindowConfiguration = { windowId: 0, @@ -75,7 +76,9 @@ export class TestTextFileService extends NativeTextFileService { @ICodeEditorService codeEditorService: ICodeEditorService, @IPathService athService: IPathService, @IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService, - @ILogService logService: ILogService + @ILogService logService: ILogService, + @IUriIdentityService uriIdentityService: IUriIdentityService, + @IModeService modeService: IModeService ) { super( fileService, @@ -93,7 +96,9 @@ export class TestTextFileService extends NativeTextFileService { codeEditorService, athService, workingCopyFileService, - logService + logService, + uriIdentityService, + modeService ); } @@ -135,22 +140,9 @@ export class TestNativeTextFileServiceWithEncodingOverrides extends NativeTextFi } } -class TestEncodingOracle extends EncodingOracle { - - protected get encodingOverrides(): IEncodingOverride[] { - return [ - { extension: 'utf16le', encoding: UTF16le }, - { extension: 'utf16be', encoding: UTF16be }, - { extension: 'utf8bom', encoding: UTF8_with_bom } - ]; - } - - protected set encodingOverrides(overrides: IEncodingOverride[]) { } -} - export class TestSharedProcessService implements ISharedProcessService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; getChannel(channelName: string): any { return undefined; } @@ -161,13 +153,17 @@ export class TestSharedProcessService implements ISharedProcessService { } export class TestElectronService implements IElectronService { - _serviceBrand: undefined; + + declare readonly _serviceBrand: undefined; + + readonly windowId = -1; onWindowOpen: Event = Event.None; onWindowMaximize: Event = Event.None; onWindowUnmaximize: Event = Event.None; onWindowFocus: Event = Event.None; onWindowBlur: Event = Event.None; + onOSResume: Event = Event.None; windowCount = Promise.resolve(1); getWindowCount(): Promise { return this.windowCount; } @@ -197,24 +193,38 @@ export class TestElectronService implements IElectronService { async pickWorkspaceAndOpen(options: INativeOpenDialogOptions): Promise { } async showItemInFolder(path: string): Promise { } async setRepresentedFilename(path: string): Promise { } + async isAdmin(): Promise { return false; } + async getTotalMem(): Promise { return 0; } + async killProcess(): Promise { } async setDocumentEdited(edited: boolean): Promise { } async openExternal(url: string): Promise { return false; } async updateTouchBar(): Promise { } + async moveItemToTrash(): Promise { return false; } async newWindowTab(): Promise { } async showPreviousWindowTab(): Promise { } async showNextWindowTab(): Promise { } async moveWindowTabToNewWindow(): Promise { } async mergeAllWindowTabs(): Promise { } async toggleWindowTabsBar(): Promise { } + async notifyReady(): Promise { } async relaunch(options?: { addArgs?: string[] | undefined; removeArgs?: string[] | undefined; } | undefined): Promise { } async reload(): Promise { } async closeWindow(): Promise { } async closeWindowById(): Promise { } async quit(): Promise { } + async exit(code: number): Promise { } async openDevTools(options?: Electron.OpenDevToolsOptions | undefined): Promise { } async toggleDevTools(): Promise { } async startCrashReporter(options: Electron.CrashReporterStartOptions): Promise { } async resolveProxy(url: string): Promise { return undefined; } + async readClipboardText(type?: 'selection' | 'clipboard' | undefined): Promise { return ''; } + async writeClipboardText(text: string, type?: 'selection' | 'clipboard' | undefined): Promise { } + async readClipboardFindText(): Promise { return ''; } + async writeClipboardFindText(text: string): Promise { } + async writeClipboardBuffer(format: string, buffer: Uint8Array, type?: 'selection' | 'clipboard' | undefined): Promise { } + async readClipboardBuffer(format: string): Promise { return Uint8Array.from([]); } + async hasClipboard(format: string, type?: 'selection' | 'clipboard' | undefined): Promise { return false; } + async sendInputEvent(event: MouseInputEvent): Promise { } } export function workbenchInstantiationService(): ITestInstantiationService { @@ -247,7 +257,7 @@ export class TestServiceAccessor { export class TestNativePathService extends TestPathService { - _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; constructor(@IWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService) { super(environmentService.userHome); diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index b1efb5d681f..6d6d66e1381 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -53,6 +53,7 @@ import 'vs/workbench/browser/parts/views/viewsService'; //#region --- workbench services import 'vs/platform/undoRedo/common/undoRedoService'; +import 'vs/workbench/services/uriIdentity/common/uriIdentityService'; import 'vs/workbench/services/extensions/browser/extensionUrlHandler'; import 'vs/workbench/services/bulkEdit/browser/bulkEditService'; import 'vs/workbench/services/keybinding/common/keybindingEditing'; @@ -72,9 +73,10 @@ import 'vs/workbench/services/mode/common/workbenchModeService'; import 'vs/workbench/services/commands/common/commandService'; import 'vs/workbench/services/themes/browser/workbenchThemeService'; import 'vs/workbench/services/label/common/labelService'; +import 'vs/workbench/services/extensionManagement/common/webExtensionsScannerService'; import 'vs/workbench/services/extensionManagement/common/extensionEnablementService'; +import 'vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService'; import 'vs/workbench/services/notification/common/notificationService'; -import 'vs/workbench/services/extensions/common/staticExtensions'; import 'vs/workbench/services/userDataSync/common/userDataSyncUtil'; import 'vs/workbench/services/remote/common/remoteExplorerService'; import 'vs/workbench/services/workingCopy/common/workingCopyService'; @@ -82,6 +84,9 @@ import 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import 'vs/workbench/services/views/browser/viewDescriptorService'; import 'vs/workbench/services/quickinput/browser/quickInputService'; +import 'vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService'; +import 'vs/workbench/services/authentication/browser/authenticationService'; +import 'vs/workbench/services/hover/browser/hoverService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; @@ -108,10 +113,10 @@ import { IDownloadService } from 'vs/platform/download/common/download'; import { DownloadService } from 'vs/platform/download/common/downloadService'; import { OpenerService } from 'vs/editor/browser/services/openerService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; -import { UserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSyncEnablementService'; +import { IUserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { UserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSyncResourceEnablementService'; -registerSingleton(IUserDataSyncEnablementService, UserDataSyncEnablementService); +registerSingleton(IUserDataSyncResourceEnablementService, UserDataSyncResourceEnablementService); registerSingleton(IGlobalExtensionEnablementService, GlobalExtensionEnablementService); registerSingleton(IExtensionGalleryService, ExtensionGalleryService, true); registerSingleton(IContextViewService, ContextViewService, true); @@ -139,6 +144,9 @@ import 'vs/workbench/contrib/preferences/browser/preferences.contribution'; import 'vs/workbench/contrib/preferences/browser/keybindingsEditorContribution'; import 'vs/workbench/contrib/preferences/browser/preferencesSearch'; +// Performance +import 'vs/workbench/contrib/performance/browser/performance.contribution'; + // Notebook import 'vs/workbench/contrib/notebook/browser/notebook.contribution'; @@ -166,9 +174,11 @@ import 'vs/workbench/contrib/search/browser/searchView'; // Search Editor import 'vs/workbench/contrib/searchEditor/browser/searchEditor.contribution'; +// Sash +import 'vs/workbench/contrib/sash/browser/sash.contribution'; + // SCM import 'vs/workbench/contrib/scm/browser/scm.contribution'; -import 'vs/workbench/contrib/scm/browser/scmViewlet'; // Debug import 'vs/workbench/contrib/debug/browser/debug.contribution'; @@ -185,7 +195,7 @@ import 'vs/workbench/contrib/markers/browser/markers.contribution'; import 'vs/workbench/contrib/comments/browser/comments.contribution'; // URL Support -import 'vs/workbench/contrib/url/common/url.contribution'; +import 'vs/workbench/contrib/url/browser/url.contribution'; // Webview import 'vs/workbench/contrib/webview/browser/webview.contribution'; @@ -201,6 +211,7 @@ import 'vs/workbench/contrib/output/browser/outputView'; // Terminal import 'vs/workbench/contrib/terminal/common/environmentVariable.contribution'; +import 'vs/workbench/contrib/terminal/common/terminalExtensionPoints.contribution'; import 'vs/workbench/contrib/terminal/browser/terminal.contribution'; import 'vs/workbench/contrib/terminal/browser/terminalView'; diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index b68c4dce757..d52fd0e9c7a 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -11,23 +11,29 @@ // ####################################################################### -//#region --- workbench common +//#region --- workbench common & sandbox -import 'vs/workbench/workbench.common.main'; +import 'vs/workbench/workbench.sandbox.main'; + +//#endregion + + +//#region --- workbench actions + +import 'vs/workbench/electron-browser/actions/developerActions'; //#endregion //#region --- workbench (desktop main) -import 'vs/workbench/electron-browser/desktop.contribution'; import 'vs/workbench/electron-browser/desktop.main'; //#endregion //#region --- workbench services -import 'vs/workbench/services/dialogs/electron-browser/fileDialogService'; + import 'vs/workbench/services/integrity/node/integrityService'; import 'vs/workbench/services/textMate/electron-browser/textMateService'; import 'vs/workbench/services/search/node/searchService'; @@ -37,7 +43,6 @@ import 'vs/workbench/services/dialogs/electron-browser/dialogService'; import 'vs/workbench/services/keybinding/electron-browser/nativeKeymapService'; import 'vs/workbench/services/keybinding/electron-browser/keybinding.contribution'; import 'vs/workbench/services/extensions/electron-browser/extensionService'; -import 'vs/workbench/services/contextmenu/electron-browser/contextmenuService'; import 'vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService'; import 'vs/workbench/services/extensionManagement/electron-browser/extensionTipsService'; import 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; @@ -45,48 +50,37 @@ import 'vs/workbench/services/telemetry/electron-browser/telemetryService'; import 'vs/workbench/services/configurationResolver/electron-browser/configurationResolverService'; import 'vs/workbench/services/extensionManagement/node/extensionManagementService'; import 'vs/workbench/services/accessibility/electron-browser/accessibilityService'; -import 'vs/workbench/services/remote/node/tunnelService'; import 'vs/workbench/services/backup/node/backupFileService'; -import 'vs/workbench/services/url/electron-browser/urlService'; -import 'vs/workbench/services/workspaces/electron-browser/workspacesService'; import 'vs/workbench/services/workspaces/electron-browser/workspaceEditingService'; -import 'vs/workbench/services/userDataSync/electron-browser/storageKeysSyncRegistryService'; +import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncMachinesService'; import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncService'; -import 'vs/workbench/services/authentication/electron-browser/authenticationTokenService'; -import 'vs/workbench/services/authentication/browser/authenticationService'; -import 'vs/workbench/services/host/electron-browser/desktopHostService'; -import 'vs/workbench/services/request/electron-browser/requestService'; -import 'vs/workbench/services/lifecycle/electron-browser/lifecycleService'; +import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncAccountService'; import 'vs/workbench/services/sharedProcess/electron-browser/sharedProcessService'; -import 'vs/workbench/services/electron/electron-browser/electronService'; import 'vs/workbench/services/localizations/electron-browser/localizationsService'; -import 'vs/workbench/services/clipboard/electron-browser/clipboardService'; -import 'vs/workbench/services/update/electron-browser/updateService'; -import 'vs/workbench/services/issue/electron-browser/issueService'; -import 'vs/workbench/services/menubar/electron-browser/menubarService'; -import 'vs/workbench/services/extensionResourceLoader/electron-browser/extensionResourceLoaderService'; import 'vs/workbench/services/path/electron-browser/pathService'; +import 'vs/workbench/services/experiment/electron-browser/experimentService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService'; -import { TitlebarPart } from 'vs/workbench/electron-browser/parts/titlebar/titlebarPart'; -import { ITitleService } from 'vs/workbench/services/title/common/titleService'; -import { IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataAutoSyncService, IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataAutoSyncService } from 'vs/workbench/contrib/userDataSync/electron-browser/userDataAutoSyncService'; +import { ITunnelService } from 'vs/platform/remote/common/tunnel'; +import { TunnelService } from 'vs/platform/remote/node/tunnelService'; +import { ITimerService } from 'vs/workbench/services/timer/browser/timerService'; +import { TimerService } from 'vs/workbench/services/timer/electron-browser/timerService'; registerSingleton(ICredentialsService, KeytarCredentialsService, true); -registerSingleton(ITitleService, TitlebarPart); +registerSingleton(IUserDataSyncStoreManagementService, UserDataSyncStoreManagementService); registerSingleton(IUserDataAutoSyncService, UserDataAutoSyncService); +registerSingleton(ITunnelService, TunnelService); +registerSingleton(ITimerService, TimerService); //#endregion //#region --- workbench contributions -// Localizations -import 'vs/workbench/contrib/localizations/browser/localizations.contribution'; - // Logs import 'vs/workbench/contrib/logs/electron-browser/logs.contribution'; @@ -97,16 +91,8 @@ import 'vs/workbench/contrib/tags/electron-browser/tags.contribution'; // Rapid Render Splash import 'vs/workbench/contrib/splash/electron-browser/partsSplash.contribution'; -// Explorer -import 'vs/workbench/contrib/files/electron-browser/files.contribution'; -import 'vs/workbench/contrib/files/electron-browser/fileActions.contribution'; - -// Backup -import 'vs/workbench/contrib/backup/electron-browser/backup.contribution'; - // Debug import 'vs/workbench/contrib/debug/node/debugHelperService'; -import 'vs/workbench/contrib/debug/electron-browser/extensionHostDebugService'; // Webview import 'vs/workbench/contrib/webview/electron-browser/webview.contribution'; @@ -126,8 +112,8 @@ import 'vs/workbench/contrib/remote/electron-browser/remote.contribution'; // CodeEditor Contributions import 'vs/workbench/contrib/codeEditor/electron-browser/codeEditor.contribution'; -// Execution -import 'vs/workbench/contrib/externalTerminal/node/externalTerminalService'; +// External Terminal +import 'vs/workbench/contrib/externalTerminal/node/externalTerminal.contribution'; // Performance import 'vs/workbench/contrib/performance/electron-browser/performance.contribution'; @@ -147,10 +133,8 @@ import 'vs/workbench/contrib/tasks/electron-browser/taskService'; // User Data Sync import 'vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution'; -// Telemetry Opt Out -import 'vs/workbench/contrib/welcome/telemetryOptOut/electron-browser/telemetryOptOut.contribution'; - // Configuration Exporter import 'vs/workbench/contrib/configExporter/electron-browser/configurationExportHelper.contribution'; +import { UserDataSyncStoreManagementService } from 'vs/workbench/contrib/userDataSync/electron-browser/userDataSyncStoreManagementService'; //#endregion diff --git a/src/vs/workbench/workbench.sandbox.main.ts b/src/vs/workbench/workbench.sandbox.main.ts new file mode 100644 index 00000000000..4ab428b3565 --- /dev/null +++ b/src/vs/workbench/workbench.sandbox.main.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +// ####################################################################### +// ### ### +// ### !!! PLEASE ADD COMMON IMPORTS INTO WORKBENCH.COMMON.MAIN.TS !!! ### +// ### ### +// ####################################################################### + +//#region --- workbench common + +import 'vs/workbench/workbench.common.main'; + +//#endregion + + +//#region --- workbench services + +import 'vs/workbench/services/dialogs/electron-sandbox/fileDialogService'; +import 'vs/workbench/services/workspaces/electron-sandbox/workspacesService'; +import 'vs/workbench/services/userDataSync/electron-sandbox/storageKeysSyncRegistryService'; +import 'vs/workbench/services/menubar/electron-sandbox/menubarService'; +import 'vs/workbench/services/issue/electron-sandbox/issueService'; +import 'vs/workbench/services/update/electron-sandbox/updateService'; +import 'vs/workbench/services/url/electron-sandbox/urlService'; +import 'vs/workbench/services/lifecycle/electron-sandbox/lifecycleService'; +import 'vs/workbench/services/title/electron-sandbox/titleService'; +import 'vs/workbench/services/host/electron-sandbox/desktopHostService'; +import 'vs/workbench/services/request/electron-sandbox/requestService'; +import 'vs/workbench/services/extensionResourceLoader/electron-sandbox/extensionResourceLoaderService'; +import 'vs/workbench/services/clipboard/electron-sandbox/clipboardService'; +import 'vs/workbench/services/contextmenu/electron-sandbox/contextmenuService'; + +//#endregion + + +//#region --- workbench contributions + +// Localizations +import 'vs/workbench/contrib/localizations/browser/localizations.contribution'; + +// Desktop +import 'vs/workbench/electron-sandbox/desktop.contribution'; + +// Explorer +import 'vs/workbench/contrib/files/electron-sandbox/files.contribution'; +import 'vs/workbench/contrib/files/electron-sandbox/fileActions.contribution'; + +// Backup +import 'vs/workbench/contrib/backup/electron-sandbox/backup.contribution'; + +// CodeEditor Contributions +import 'vs/workbench/contrib/codeEditor/electron-sandbox/codeEditor.contribution'; + +// Debug +import 'vs/workbench/contrib/debug/electron-sandbox/extensionHostDebugService'; + +// Telemetry Opt Out +import 'vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.contribution'; + +//#endregion diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 4172005de90..7932149e035 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -17,6 +17,8 @@ import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IWorkspaceProvider, IWorkspace } from 'vs/workbench/services/host/browser/browserHostService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { IProductConfiguration } from 'vs/platform/product/common/productService'; +import { mark } from 'vs/base/common/performance'; interface IResourceUriProvider { (uri: URI): URI; @@ -25,9 +27,10 @@ interface IResourceUriProvider { interface IStaticExtension { packageJSON: IExtensionManifest; extensionLocation: URI; + isBuiltin?: boolean; } -interface ICommontTelemetryPropertiesResolver { +interface ICommonTelemetryPropertiesResolver { (): { [key: string]: any }; } @@ -35,6 +38,19 @@ interface IExternalUriResolver { (uri: URI): Promise; } +interface ITunnelProvider { + + /** + * Support for creating tunnels. + */ + tunnelFactory?: ITunnelFactory; + + /** + * Support for filtering candidate ports + */ + showPortCandidate?: IShowPortCandidate; +} + interface ITunnelFactory { (tunnelOptions: ITunnelOptions): Promise | undefined; } @@ -89,9 +105,9 @@ interface ICommand { interface IHomeIndicator { /** - * The identifier of the command to run when clicking the home indicator. + * The link to open when clicking the home indicator. */ - command: string; + href: string; /** * The icon name for the home indicator. This needs to be one of the existing @@ -145,17 +161,34 @@ interface IDefaultPanelLayout { })[]; } +interface IDefaultView { + readonly id: string; +} + interface IDefaultEditor { readonly uri: UriComponents; readonly openOnlyIfExists?: boolean; + readonly openWith?: string; } interface IDefaultLayout { + /** @deprecated Use views instead (TODO@eamodio remove eventually) */ readonly sidebar?: IDefaultSideBarLayout; + /** @deprecated Use views instead (TODO@eamodio remove eventually) */ readonly panel?: IDefaultPanelLayout; + readonly views?: IDefaultView[]; readonly editors?: IDefaultEditor[]; } +interface IProductQualityChangeHandler { + + /** + * Handler is being called when the user wants to switch between + * `insider` or `stable` product qualities. + */ + (newQuality: 'insider' | 'stable'): void; +} + interface IWorkbenchConstructionOptions { //#region Connection related configuration @@ -193,14 +226,10 @@ interface IWorkbenchConstructionOptions { readonly resolveExternalUri?: IExternalUriResolver; /** - * Support for creating tunnels. + * A provider for supplying tunneling functionality, + * such as creating tunnels and showing candidate ports to forward. */ - readonly tunnelFactory?: ITunnelFactory; - - /** - * Support for filtering candidate ports - */ - readonly showCandidate?: IShowPortCandidate; + readonly tunnelProvider?: ITunnelProvider; //#endregion @@ -238,20 +267,20 @@ interface IWorkbenchConstructionOptions { */ readonly staticExtensions?: ReadonlyArray; + /** + * Service end-point hosting builtin extensions + */ + readonly builtinExtensionsServiceUrl?: string; + /** * Support for URL callbacks. */ readonly urlCallbackProvider?: IURLCallbackProvider; - /** - * Support for update reporting. - */ - readonly updateProvider?: IUpdateProvider; - /** * Support adding additional properties to telemetry. */ - readonly resolveCommonTelemetryProperties?: ICommontTelemetryPropertiesResolver; + readonly resolveCommonTelemetryProperties?: ICommonTelemetryPropertiesResolver; /** * A set of optional commands that should be registered with the commands @@ -261,15 +290,45 @@ interface IWorkbenchConstructionOptions { */ readonly commands?: readonly ICommand[]; + /** + * Optional default layout to apply on first time the workspace is opened. + */ + readonly defaultLayout?: IDefaultLayout; + + /** + * Optional configuration default overrides contributed to the workbench. + */ + readonly configurationDefaults?: Record; + + //#endregion + + + //#region Update/Quality related + + /** + * Support for update reporting + */ + readonly updateProvider?: IUpdateProvider; + + /** + * Support for product quality switching + */ + readonly productQualityChangeHandler?: IProductQualityChangeHandler; + + //#endregion + + + //#region Branding + /** * Optional home indicator to appear above the hamburger menu in the activity bar. */ readonly homeIndicator?: IHomeIndicator; /** - * Optional default layout to apply on first time the workspace is opened. + * Optional override for the product configuration properties. */ - readonly defaultLayout?: IDefaultLayout; + readonly productConfiguration?: Partial; //#endregion @@ -286,6 +345,11 @@ interface IWorkbenchConstructionOptions { */ readonly driver?: boolean; + /** + * Endpoints to be used for proxying authentication code exchange calls in the browser. + */ + readonly codeExchangeProxyEndpoints?: { [providerId: string]: string } + //#endregion } @@ -306,6 +370,10 @@ let workbenchPromiseResolve: Function; const workbenchPromise = new Promise(resolve => workbenchPromiseResolve = resolve); async function create(domElement: HTMLElement, options: IWorkbenchConstructionOptions): Promise { + // Mark start of workbench + mark('didLoadWorkbenchMain'); + performance.mark('workbench-start'); + // Assert that the workbench is not created more than once. We currently // do not support this and require a full context switch to clean-up. if (created) { @@ -394,17 +462,19 @@ export { // LogLevel LogLevel, - // Updates + // Updates/Quality IUpdateProvider, IUpdate, + IProductQualityChangeHandler, // Telemetry - ICommontTelemetryPropertiesResolver, + ICommonTelemetryPropertiesResolver, // External Uris IExternalUriResolver, // Tunnel + ITunnelProvider, ITunnelFactory, ITunnel, ITunnelOptions, @@ -416,14 +486,16 @@ export { ICommand, commands, - // Home Indicator + // Branding IHomeIndicator, + IProductConfiguration, // Default layout + IDefaultView, IDefaultEditor, IDefaultLayout, IDefaultPanelLayout, - IDefaultSideBarLayout, + IDefaultSideBarLayout }; //#endregion diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index fb31b7ca292..0669178db4c 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -26,6 +26,7 @@ import 'vs/workbench/browser/web.main'; //#region --- workbench services + import 'vs/workbench/services/integrity/browser/integrityService'; import 'vs/workbench/services/textMate/browser/textMateService'; import 'vs/workbench/services/search/common/searchService'; @@ -60,22 +61,22 @@ import { BackupFileService } from 'vs/workbench/services/backup/common/backupFil import { IExtensionManagementService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionTipsService'; import { ExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagementService'; -import { ITunnelService } from 'vs/platform/remote/common/tunnel'; -import { TunnelService } from 'vs/workbench/services/remote/common/tunnelService'; +import { ITunnelService, TunnelService } from 'vs/platform/remote/common/tunnel'; import { ILoggerService } from 'vs/platform/log/common/log'; import { FileLoggerService } from 'vs/platform/log/common/fileLogService'; -import { IUserDataSyncStoreService, IUserDataSyncService, IUserDataSyncLogService, IUserDataAutoSyncService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync'; +import { UserDataSyncMachinesService, IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; +import { IUserDataSyncStoreService, IUserDataSyncService, IUserDataSyncLogService, IUserDataAutoSyncService, IUserDataSyncBackupStoreService, IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync'; import { StorageKeysSyncRegistryService, IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; -import { AuthenticationService, IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; -import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; +import { UserDataSyncStoreService, UserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSyncBackupStoreService'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; -import { IAuthenticationTokenService, AuthenticationTokenService } from 'vs/platform/authentication/common/authentication'; +import { IUserDataSyncAccountService, UserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; import { UserDataAutoSyncService } from 'vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService'; import { AccessibilityService } from 'vs/platform/accessibility/common/accessibilityService'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { TitlebarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart'; +import { ITimerService, TimerService } from 'vs/workbench/services/timer/browser/timerService'; registerSingleton(IExtensionManagementService, ExtensionManagementService); registerSingleton(IBackupFileService, BackupFileService); @@ -83,16 +84,18 @@ registerSingleton(IAccessibilityService, AccessibilityService, true); registerSingleton(IContextMenuService, ContextMenuService); registerSingleton(ITunnelService, TunnelService, true); registerSingleton(ILoggerService, FileLoggerService); -registerSingleton(IAuthenticationService, AuthenticationService); registerSingleton(IUserDataSyncLogService, UserDataSyncLogService); +registerSingleton(IUserDataSyncStoreManagementService, UserDataSyncStoreManagementService); registerSingleton(IUserDataSyncStoreService, UserDataSyncStoreService); +registerSingleton(IUserDataSyncMachinesService, UserDataSyncMachinesService); registerSingleton(IUserDataSyncBackupStoreService, UserDataSyncBackupStoreService); registerSingleton(IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService); -registerSingleton(IAuthenticationTokenService, AuthenticationTokenService); +registerSingleton(IUserDataSyncAccountService, UserDataSyncAccountService); registerSingleton(IUserDataAutoSyncService, UserDataAutoSyncService); registerSingleton(IUserDataSyncService, UserDataSyncService); registerSingleton(ITitleService, TitlebarPart); registerSingleton(IExtensionTipsService, ExtensionTipsService); +registerSingleton(ITimerService, TimerService); //#endregion @@ -126,6 +129,6 @@ import 'vs/workbench/contrib/tasks/browser/taskService'; import 'vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.contribution'; // Issues -import 'vs/workbench/contrib/issue/browser/issue.contribution'; +import 'vs/workbench/contrib/issue/browser/issue.web.contribution'; //#endregion diff --git a/test/automation/src/code.ts b/test/automation/src/code.ts index c9382b9fb23..ba7249f6b67 100644 --- a/test/automation/src/code.ts +++ b/test/automation/src/code.ts @@ -120,8 +120,10 @@ export async function spawn(options: SpawnOptions): Promise { let child: cp.ChildProcess | undefined; let connectDriver: typeof connectElectronDriver; + copyExtension(options, 'vscode-notebook-tests'); + if (options.web) { - await launch(options.userDataDir, options.workspacePath, options.codePath); + await launch(options.userDataDir, options.workspacePath, options.codePath, options.extensionsPath); connectDriver = connectPlaywrightDriver.bind(connectPlaywrightDriver, options.browser); return connect(connectDriver, child, '', handle, options.logger); } @@ -134,6 +136,7 @@ export async function spawn(options: SpawnOptions): Promise { options.workspacePath, '--skip-release-notes', '--disable-telemetry', + '--no-cached-data', '--disable-updates', '--disable-crash-reporter', `--extensions-dir=${options.extensionsPath}`, @@ -148,11 +151,7 @@ export async function spawn(options: SpawnOptions): Promise { if (codePath) { // running against a build: copy the test resolver extension - const testResolverExtPath = path.join(options.extensionsPath, 'vscode-test-resolver'); - if (!fs.existsSync(testResolverExtPath)) { - const orig = path.join(repoPath, 'extensions', 'vscode-test-resolver'); - await new Promise((c, e) => ncp(orig, testResolverExtPath, err => err ? e(err) : c())); - } + copyExtension(options, 'vscode-test-resolver'); } args.push('--enable-proposed-api=vscode.vscode-test-resolver'); const remoteDataDir = `${options.userDataDir}-server`; @@ -160,6 +159,9 @@ export async function spawn(options: SpawnOptions): Promise { env['TESTRESOLVER_DATA_FOLDER'] = remoteDataDir; } + + args.push('--enable-proposed-api=vscode.vscode-notebook-tests'); + if (!codePath) { args.unshift(repoPath); } @@ -185,6 +187,14 @@ export async function spawn(options: SpawnOptions): Promise { return connect(connectDriver, child, outPath, handle, options.logger); } +async function copyExtension(options: SpawnOptions, extId: string): Promise { + const testResolverExtPath = path.join(options.extensionsPath, extId); + if (!fs.existsSync(testResolverExtPath)) { + const orig = path.join(repoPath, 'extensions', extId); + await new Promise((c, e) => ncp(orig, testResolverExtPath, err => err ? e(err) : c())); + } +} + async function poll( fn: () => Thenable, acceptFn: (result: T) => boolean, diff --git a/test/automation/src/extensions.ts b/test/automation/src/extensions.ts index 01d8efd61ef..f7bf11891b9 100644 --- a/test/automation/src/extensions.ts +++ b/test/automation/src/extensions.ts @@ -34,9 +34,9 @@ export class Extensions extends Viewlet { await this.code.waitForTypeInEditor(SEARCH_BOX, `@id:${id}`); } - async installExtension(id: string, name: string): Promise { + async installExtension(id: string, name: string, version: string, publisherDisplayName: string): Promise { await this.searchForExtension(id); - const ariaLabel = `${name}. Press enter for extension details.`; + const ariaLabel = `${name}, ${version}, ${publisherDisplayName}, press enter for extension details.`; await this.code.waitAndClick(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[aria-label="${ariaLabel}"] .extension-list-item li[class='action-item'] .extension-action.install`); await this.code.waitForElement(`.extension-editor .monaco-action-bar .action-item:not(.disabled) .extension-action.uninstall`); } diff --git a/test/automation/src/notebook.ts b/test/automation/src/notebook.ts new file mode 100644 index 00000000000..0110e09385c --- /dev/null +++ b/test/automation/src/notebook.ts @@ -0,0 +1,96 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Code } from './code'; +import { QuickAccess } from './quickaccess'; + +const activeRowSelector = `.notebook-editor .monaco-list-row.focused`; + +export class Notebook { + + constructor( + private readonly quickAccess: QuickAccess, + private readonly code: Code) { + } + + async openNotebook() { + await this.quickAccess.runCommand('vscode-notebook-tests.createNewNotebook'); + await this.code.waitForElement(activeRowSelector); + await this.focusFirstCell(); + await this.waitForActiveCellEditorContents('code()'); + } + + async focusNextCell() { + await this.code.dispatchKeybinding('down'); + } + + async focusFirstCell() { + await this.quickAccess.runCommand('notebook.focusTop'); + } + + async editCell() { + await this.code.dispatchKeybinding('enter'); + } + + async stopEditingCell() { + await this.quickAccess.runCommand('notebook.cell.quitEdit'); + } + + async waitForTypeInEditor(text: string): Promise { + const editor = `${activeRowSelector} .monaco-editor`; + + await this.code.waitForElement(editor); + + const textarea = `${editor} textarea`; + await this.code.waitForActiveElement(textarea); + + await this.code.waitForTypeInEditor(textarea, text); + + await this._waitForActiveCellEditorContents(c => c.indexOf(text) > -1); + } + + async waitForActiveCellEditorContents(contents: string): Promise { + return this._waitForActiveCellEditorContents(str => str === contents); + } + + private async _waitForActiveCellEditorContents(accept: (contents: string) => boolean): Promise { + const selector = `${activeRowSelector} .monaco-editor .view-lines`; + return this.code.waitForTextContent(selector, undefined, c => accept(c.replace(/\u00a0/g, ' '))); + } + + async waitForMarkdownContents(markdownSelector: string, text: string): Promise { + const selector = `${activeRowSelector} .markdown ${markdownSelector}`; + await this.code.waitForTextContent(selector, text); + } + + async insertNotebookCell(kind: 'markdown' | 'code'): Promise { + if (kind === 'markdown') { + await this.quickAccess.runCommand('notebook.cell.insertMarkdownCellBelow'); + } else { + await this.quickAccess.runCommand('notebook.cell.insertCodeCellBelow'); + } + } + + async deleteActiveCell(): Promise { + await this.quickAccess.runCommand('notebook.cell.delete'); + } + + async focusInCellOutput(): Promise { + await this.quickAccess.runCommand('notebook.cell.focusInOutput'); + await this.code.waitForActiveElement('webview, .webview'); + } + + async focusOutCellOutput(): Promise { + await this.quickAccess.runCommand('notebook.cell.focusOutOutput'); + } + + async executeActiveCell(): Promise { + await this.quickAccess.runCommand('notebook.cell.execute'); + } + + async executeCellAction(selector: string): Promise { + await this.code.waitAndClick(selector); + } +} diff --git a/test/automation/src/playwrightDriver.ts b/test/automation/src/playwrightDriver.ts index ca40282175f..7d7c34deb25 100644 --- a/test/automation/src/playwrightDriver.ts +++ b/test/automation/src/playwrightDriver.ts @@ -25,7 +25,8 @@ const vscodeToPlaywrightKey: { [key: string]: string } = { up: 'ArrowUp', down: 'ArrowDown', left: 'ArrowLeft', - home: 'Home' + home: 'Home', + esc: 'Escape' }; function buildDriver(browser: playwright.Browser, page: playwright.Page): IDriver { @@ -86,13 +87,11 @@ function timeout(ms: number): Promise { return new Promise(r => setTimeout(r, ms)); } -// function runInDriver(call: string, args: (string | boolean)[]): Promise {} - let server: ChildProcess | undefined; let endpoint: string | undefined; let workspacePath: string | undefined; -export async function launch(userDataDir: string, _workspacePath: string, codeServerPath = process.env.VSCODE_REMOTE_SERVER_PATH): Promise { +export async function launch(userDataDir: string, _workspacePath: string, codeServerPath = process.env.VSCODE_REMOTE_SERVER_PATH, extPath: string): Promise { workspacePath = _workspacePath; const agentFolder = userDataDir; @@ -105,12 +104,14 @@ export async function launch(userDataDir: string, _workspacePath: string, codeSe let serverLocation: string | undefined; if (codeServerPath) { serverLocation = join(codeServerPath, `server.${process.platform === 'win32' ? 'cmd' : 'sh'}`); + console.log(`Starting built server from '${serverLocation}'`); } else { serverLocation = join(__dirname, '..', '..', '..', `resources/server/web.${process.platform === 'win32' ? 'bat' : 'sh'}`); + console.log(`Starting server out of sources from '${serverLocation}'`); } server = spawn( serverLocation, - ['--browser', 'none', '--driver', 'web'], + ['--browser', 'none', '--driver', 'web', '--extensions-dir', extPath], { env } ); server.stderr?.on('data', error => console.log(`Server stderr: ${error}`)); @@ -145,7 +146,8 @@ export function connect(browserType: 'chromium' | 'webkit' | 'firefox' = 'chromi const context = await browser.newContext(); const page = await context.newPage(); await page.setViewportSize({ width, height }); - await page.goto(`${endpoint}&folder=vscode-remote://localhost:9888${URI.file(workspacePath!).path}`); + const payloadParam = `[["enableProposedApi",""]]`; + await page.goto(`${endpoint}&folder=vscode-remote://localhost:9888${URI.file(workspacePath!).path}&payload=${payloadParam}`); const result = { client: { dispose: () => browser.close() && teardown() }, driver: buildDriver(browser, page) diff --git a/test/automation/src/statusbar.ts b/test/automation/src/statusbar.ts index 362562c38bc..2c331f70ba3 100644 --- a/test/automation/src/statusbar.ts +++ b/test/automation/src/statusbar.ts @@ -19,7 +19,7 @@ export const enum StatusBarElement { export class StatusBar { - private readonly mainSelector = 'div[id="workbench.parts.statusbar"]'; + private readonly mainSelector = 'footer[id="workbench.parts.statusbar"]'; constructor(private code: Code) { } diff --git a/test/automation/src/workbench.ts b/test/automation/src/workbench.ts index e2373948924..9b46d78f8fe 100644 --- a/test/automation/src/workbench.ts +++ b/test/automation/src/workbench.ts @@ -19,6 +19,7 @@ import { KeybindingsEditor } from './keybindings'; import { Editors } from './editors'; import { Code } from './code'; import { Terminal } from './terminal'; +import { Notebook } from './notebook'; export interface Commands { runCommand(command: string): Promise; @@ -41,6 +42,7 @@ export class Workbench { readonly settingsEditor: SettingsEditor; readonly keybindingsEditor: KeybindingsEditor; readonly terminal: Terminal; + readonly notebook: Notebook; constructor(code: Code, userDataPath: string) { this.editors = new Editors(code); @@ -58,5 +60,6 @@ export class Workbench { this.settingsEditor = new SettingsEditor(code, userDataPath, this.editors, this.editor, this.quickaccess); this.keybindingsEditor = new KeybindingsEditor(code); this.terminal = new Terminal(code, this.quickaccess); + this.notebook = new Notebook(this.quickaccess, code); } } diff --git a/test/integration/browser/src/index.ts b/test/integration/browser/src/index.ts index 7058f92ecf2..91bdd3a8322 100644 --- a/test/integration/browser/src/index.ts +++ b/test/integration/browser/src/index.ts @@ -95,10 +95,13 @@ async function launchServer(): Promise<{ endpoint: url.UrlWithStringQuery, serve let serverLocation: string; if (process.env.VSCODE_REMOTE_SERVER_PATH) { serverLocation = path.join(process.env.VSCODE_REMOTE_SERVER_PATH, `server.${process.platform === 'win32' ? 'cmd' : 'sh'}`); + + console.log(`Starting built server from '${serverLocation}'`); } else { serverLocation = path.join(__dirname, '..', '..', '..', '..', `resources/server/web.${process.platform === 'win32' ? 'bat' : 'sh'}`); - process.env.VSCODE_DEV = '1'; + + console.log(`Starting server out of sources from '${serverLocation}'`); } let serverProcess = cp.spawn( diff --git a/test/smoke/src/areas/extensions/extensions.test.ts b/test/smoke/src/areas/extensions/extensions.test.ts index 5d729a2c2cf..d0820a78a7a 100644 --- a/test/smoke/src/areas/extensions/extensions.test.ts +++ b/test/smoke/src/areas/extensions/extensions.test.ts @@ -17,7 +17,7 @@ export function setup() { await app.workbench.extensions.openExtensionsViewlet(); - await app.workbench.extensions.installExtension('michelkaporin.vscode-smoketest-check', 'vscode-smoketest-check'); + await app.workbench.extensions.installExtension('michelkaporin.vscode-smoketest-check', 'vscode-smoketest-check', '0.0.1', 'Michel Kaporin'); await app.workbench.extensions.waitForExtensionsViewlet(); diff --git a/test/smoke/src/areas/notebook/notebook.test.ts b/test/smoke/src/areas/notebook/notebook.test.ts new file mode 100644 index 00000000000..4ceb37250f3 --- /dev/null +++ b/test/smoke/src/areas/notebook/notebook.test.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +import * as cp from 'child_process'; +import { Application } from '../../../../automation'; + +// function wait(ms: number): Promise { +// return new Promise(r => setTimeout(r, ms)); +// } + + +export function setup() { + describe('Notebooks', () => { + after(async function () { + const app = this.app as Application; + cp.execSync('git checkout . --quiet', { cwd: app.workspacePathOrFolder }); + cp.execSync('git reset --hard origin/master --quiet', { cwd: app.workspacePathOrFolder }); + }); + + afterEach(async function () { + const app = this.app as Application; + await app.workbench.quickaccess.runCommand('workbench.action.files.save'); + await app.workbench.quickaccess.runCommand('workbench.action.closeActiveEditor'); + }); + + it('inserts/edits code cell', async function () { + const app = this.app as Application; + await app.workbench.notebook.openNotebook(); + await app.workbench.notebook.focusNextCell(); + await app.workbench.notebook.insertNotebookCell('code'); + await app.workbench.notebook.waitForTypeInEditor('// some code'); + await app.workbench.notebook.stopEditingCell(); + }); + + it('inserts/edits markdown cell', async function () { + const app = this.app as Application; + await app.workbench.notebook.openNotebook(); + await app.workbench.notebook.focusNextCell(); + await app.workbench.notebook.insertNotebookCell('markdown'); + await app.workbench.notebook.waitForTypeInEditor('## hello2! '); + await app.workbench.notebook.stopEditingCell(); + await app.workbench.notebook.waitForMarkdownContents('h2', 'hello2!'); + }); + + it('moves focus as it inserts/deletes a cell', async function () { + const app = this.app as Application; + await app.workbench.notebook.openNotebook(); + await app.workbench.notebook.insertNotebookCell('code'); + await app.workbench.notebook.waitForActiveCellEditorContents(' '); + await app.workbench.notebook.stopEditingCell(); + await app.workbench.notebook.deleteActiveCell(); + await app.workbench.notebook.waitForMarkdownContents('p', 'Markdown Cell'); + }); + + it.skip('moves focus in and out of output', async function () { + const app = this.app as Application; + await app.workbench.notebook.openNotebook(); + await app.workbench.notebook.executeActiveCell(); + await app.workbench.notebook.focusInCellOutput(); + await app.workbench.notebook.focusOutCellOutput(); + await app.workbench.notebook.waitForActiveCellEditorContents('code()'); + }); + + it('cell action execution', async function () { + const app = this.app as Application; + await app.workbench.notebook.openNotebook(); + await app.workbench.notebook.insertNotebookCell('code'); + await app.workbench.notebook.executeCellAction('.notebook-editor .monaco-list-row.focused div.monaco-toolbar .codicon-debug'); + await app.workbench.notebook.waitForActiveCellEditorContents('test'); + }); + }); +} diff --git a/test/smoke/src/areas/workbench/localization.test.ts b/test/smoke/src/areas/workbench/localization.test.ts index 4e15358eeb0..ad6dbcdd7c2 100644 --- a/test/smoke/src/areas/workbench/localization.test.ts +++ b/test/smoke/src/areas/workbench/localization.test.ts @@ -6,7 +6,7 @@ import { Application, Quality } from '../../../../automation'; export function setup() { - describe('Localization', () => { + describe.skip('Localization', () => { before(async function () { const app = this.app as Application; @@ -15,7 +15,7 @@ export function setup() { } await app.workbench.extensions.openExtensionsViewlet(); - await app.workbench.extensions.installExtension('ms-ceintl.vscode-language-pack-de', 'German Language Pack for Visual Studio Code'); + await app.workbench.extensions.installExtension('ms-ceintl.vscode-language-pack-de', 'German Language Pack for Visual Studio Code', '1.47.3', 'Microsoft'); await app.restart({ extraArgs: ['--locale=DE'] }); }); @@ -33,8 +33,8 @@ export function setup() { await app.workbench.search.openSearchViewlet(); await app.workbench.search.waitForTitle(title => /suchen/i.test(title)); - await app.workbench.scm.openSCMViewlet(); - await app.workbench.scm.waitForTitle(title => /quellcodeverwaltung/i.test(title)); + // await app.workbench.scm.openSCMViewlet(); + // await app.workbench.scm.waitForTitle(title => /quellcodeverwaltung/i.test(title)); // See https://github.com/microsoft/vscode/issues/93462 // await app.workbench.debug.openDebugViewlet(); diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 7f6eb22d6e5..51c085de716 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -25,6 +25,7 @@ import { setup as setupDataMigrationTests } from './areas/workbench/data-migrati import { setup as setupDataLossTests } from './areas/workbench/data-loss.test'; import { setup as setupDataPreferencesTests } from './areas/preferences/preferences.test'; import { setup as setupDataSearchTests } from './areas/search/search.test'; +import { setup as setupDataNotebookTests } from './areas/notebook/notebook.test'; import { setup as setupDataLanguagesTests } from './areas/languages/languages.test'; import { setup as setupDataEditorTests } from './areas/editor/editor.test'; import { setup as setupDataStatusbarTests } from './areas/statusbar/statusbar.test'; @@ -154,6 +155,8 @@ if (!opts.web) { } else { quality = Quality.Stable; } + + console.log(`Running desktop smoke tests against ${electronPath}`); } // @@ -162,14 +165,20 @@ if (!opts.web) { else { const testCodeServerPath = opts.build || process.env.VSCODE_REMOTE_SERVER_PATH; - if (typeof testCodeServerPath === 'string' && !fs.existsSync(testCodeServerPath)) { - fail(`Can't find Code server at ${testCodeServerPath}.`); + if (typeof testCodeServerPath === 'string') { + if (!fs.existsSync(testCodeServerPath)) { + fail(`Can't find Code server at ${testCodeServerPath}.`); + } else { + console.log(`Running web smoke tests against ${testCodeServerPath}`); + } } if (!testCodeServerPath) { process.env.VSCODE_REPOSITORY = repoPath; process.env.VSCODE_DEV = '1'; process.env.VSCODE_CLI = '1'; + + console.log(`Running web smoke out of sources`); } if (process.env.VSCODE_DEV === '1') { @@ -307,6 +316,7 @@ describe(`VSCode Smoke Tests (${opts.web ? 'Web' : 'Electron'})`, () => { if (!opts.web) { setupDataLossTests(); } if (!opts.web) { setupDataPreferencesTests(); } setupDataSearchTests(); + setupDataNotebookTests(); setupDataLanguagesTests(); setupDataEditorTests(); setupDataStatusbarTests(!!opts.web); diff --git a/test/smoke/yarn.lock b/test/smoke/yarn.lock index 319fb686589..9ce4feaa1e8 100644 --- a/test/smoke/yarn.lock +++ b/test/smoke/yarn.lock @@ -1111,9 +1111,9 @@ locate-path@^3.0.0: path-exists "^3.0.0" lodash@^4.16.4, lodash@^4.17.15, lodash@^4.5.1: - version "4.17.15" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" - integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== + version "4.17.19" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" + integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== log-symbols@2.2.0: version "2.2.0" diff --git a/test/unit/browser/index.js b/test/unit/browser/index.js index 71514111a10..926d96f6352 100644 --- a/test/unit/browser/index.js +++ b/test/unit/browser/index.js @@ -72,7 +72,7 @@ function ensureIsArray(a) { const testModules = (async function () { - const excludeGlob = '**/{node,electron-browser,electron-main}/**/*.test.js'; + const excludeGlob = '**/{node,electron-sandbox,electron-browser,electron-main}/**/*.test.js'; let isDefaultModules = true; let promise; diff --git a/test/unit/browser/renderer.html b/test/unit/browser/renderer.html index 61b2de146ab..27f7ecd3c42 100644 --- a/test/unit/browser/renderer.html +++ b/test/unit/browser/renderer.html @@ -51,10 +51,12 @@ catchError: true, baseUrl: new URL('../../../src', baseUrl).href, paths: { - 'vs': new URL(`../../../${!!isBuild ? 'out-build' : 'out'}/vs`, baseUrl).href, + vs: new URL(`../../../${!!isBuild ? 'out-build' : 'out'}/vs`, baseUrl).href, assert: new URL('../assert.js', baseUrl).href, sinon: new URL('../../../node_modules/sinon/pkg/sinon-1.17.7.js', baseUrl).href, - xterm: new URL('../../../node_modules/xterm/lib/xterm.js', baseUrl).href + xterm: new URL('../../../node_modules/xterm/lib/xterm.js', baseUrl).href, + 'iconv-lite-umd': new URL('../../../node_modules/iconv-lite-umd/lib/iconv-lite-umd.js', baseUrl).href, + jschardet: new URL('../../../node_modules/jschardet/dist/jschardet.min.js', baseUrl).href } }); diff --git a/test/unit/electron/index.js b/test/unit/electron/index.js index 2230b37df9f..dcd00923c9b 100644 --- a/test/unit/electron/index.js +++ b/test/unit/electron/index.js @@ -59,7 +59,8 @@ function deserializeRunnable(runnable) { async: runnable.async, slow: () => runnable.slow, speed: runnable.speed, - duration: runnable.duration + duration: runnable.duration, + currentRetry: () => runnable.currentRetry }; } @@ -112,9 +113,11 @@ app.on('ready', () => { width: 800, show: false, webPreferences: { - backgroundThrottling: false, + preload: path.join(__dirname, '..', '..', '..', 'src', 'vs', 'base', 'parts', 'sandbox', 'electron-browser', 'preload.js'), // ensure similar environment as VSCode as tests may depend on this nodeIntegration: true, - webSecurity: false, + enableWebSQL: false, + enableRemoteModule: false, + nativeWindowOpen: true, webviewTag: true } }); diff --git a/test/unit/electron/renderer.js b/test/unit/electron/renderer.js index 734c0837841..e2a4b435690 100644 --- a/test/unit/electron/renderer.js +++ b/test/unit/electron/renderer.js @@ -32,7 +32,7 @@ function initLoader(opts) { nodeRequire: require, nodeMain: __filename, catchError: true, - baseUrl: bootstrap.uriFromPath(path.join(__dirname, '../../../src')), + baseUrl: bootstrap.fileUriFromPath(path.join(__dirname, '../../../src')), paths: { 'vs': `../${outdir}/vs`, 'lib': `../${outdir}/lib`, diff --git a/yarn.lock b/yarn.lock index 9aac06278c6..c039a3e99ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -222,13 +222,6 @@ dependencies: "@types/node" "*" -"@types/iconv-lite@0.0.1": - version "0.0.1" - resolved "https://registry.yarnpkg.com/@types/iconv-lite/-/iconv-lite-0.0.1.tgz#aa3b8bda2be512b1ae0a057b942e869c370a5569" - integrity sha1-qjuL2ivlErGuCgV7lC6GnDcKVWk= - dependencies: - "@types/node" "*" - "@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" @@ -340,234 +333,233 @@ dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin@2.3.2": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.3.2.tgz#7e112ca0bb29044d915baf10163a8199a20f7c69" - integrity sha512-tcnpksq1bXzcIRbYLeXkgp6l+ggEMXXUcl1wsSvL807fRtmvVQKygElwEUf4hBA76dNag3VAK1q2m3vd7qJaZA== +"@typescript-eslint/eslint-plugin@3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.2.0.tgz#7fb997f391af32ae6ca1dbe56bcefe4dd30bda14" + integrity sha512-t9RTk/GyYilIXt6BmZurhBzuMT9kLKw3fQoJtK9ayv0tXTlznXEAnx07sCLXdkN3/tZDep1s1CEV95CWuARYWA== dependencies: - "@typescript-eslint/experimental-utils" "2.3.2" - eslint-utils "^1.4.2" + "@typescript-eslint/experimental-utils" "3.2.0" functional-red-black-tree "^1.0.1" - regexpp "^2.0.1" + regexpp "^3.0.0" + semver "^7.3.2" tsutils "^3.17.1" -"@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== +"@typescript-eslint/experimental-utils@3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.2.0.tgz#4dab8fc9f44f059ec073470a81bb4d7d7d51e6c5" + integrity sha512-UbJBsk+xO9dIFKtj16+m42EvUvsjZbbgQ2O5xSTSfVT1Z3yGkL90DVu0Hd3029FZ5/uBgl+F3Vo8FAcEcqc6aQ== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.13.0" + "@typescript-eslint/typescript-estree" "3.2.0" eslint-scope "^5.0.0" + eslint-utils "^2.0.0" -"@typescript-eslint/experimental-utils@2.3.2": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.3.2.tgz#e50f31264507e6fec7b33840bb6af260c24f4ea8" - integrity sha512-t+JGdTT6dRbmvKDlhlVkEueoZa0fhJNfG6z2cpnRPLwm3VwYr2BjR//acJGC1Yza0I9ZNcDfRY7ubQEvvfG6Jg== +"@typescript-eslint/experimental-utils@3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.3.0.tgz#d72a946e056a83d4edf97f3411cceb639b0b8c87" + integrity sha512-d4pGIAbu/tYsrPrdHCQ5xfadJGvlkUxbeBB56nO/VGmEDi/sKmfa5fGty5t5veL1OyJBrUmSiRn1R1qfVDydrg== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.3.2" + "@typescript-eslint/typescript-estree" "3.3.0" eslint-scope "^5.0.0" + eslint-utils "^2.0.0" -"@typescript-eslint/parser@^2.12.0": - version "2.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.13.0.tgz#ea1ab394cf9ca17467e3da7f96eca9309f57c326" - integrity sha512-vbDeLr5QRJ1K7x5iRK8J9wuGwR9OVyd1zDAY9XFAQvAosHVjSVbDgkm328ayE6hx2QWVGhwvGaEhedcqAbfQcA== +"@typescript-eslint/parser@^3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.3.0.tgz#fcae40012ded822aa8b2739a1a03a4e3c5bbb7bb" + integrity sha512-a7S0Sqn/+RpOOWTcaLw6RD4obsharzxmgMfdK24l364VxuBODXjuJM7ImCkSXEN7oz52aiZbXSbc76+2EsE91w== dependencies: "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "2.13.0" - "@typescript-eslint/typescript-estree" "2.13.0" + "@typescript-eslint/experimental-utils" "3.3.0" + "@typescript-eslint/typescript-estree" "3.3.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== +"@typescript-eslint/typescript-estree@3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.2.0.tgz#c735f1ca6b4d3cd671f30de8c9bde30843e7ead8" + integrity sha512-uh+Y2QO7dxNrdLw7mVnjUqkwO/InxEqwN0wF+Za6eo3coxls9aH9kQ/5rSvW2GcNanebRTmsT5w1/92lAOb1bA== 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" + lodash "^4.17.15" + semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/typescript-estree@2.3.2": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.3.2.tgz#107414aa04e689fe6f7251eb63fb500217f2b7f4" - integrity sha512-eZNEAai16nwyhIVIEaWQlaUgAU3S9CkQ58qvK0+3IuSdLJD3W1PNuehQFMIhW/mTP1oFR9GNoTcLg7gtXz6lzA== +"@typescript-eslint/typescript-estree@3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.3.0.tgz#841ffed25c29b0049ebffb4c2071268a34558a2a" + integrity sha512-3SqxylENltEvJsjjMSDCUx/edZNSC7wAqifUU1Ywp//0OWEZwMZJfecJud9XxJ/40rAKEbJMKBOQzeOjrLJFzQ== dependencies: - glob "^7.1.4" + 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" + lodash "^4.17.15" + semver "^7.3.2" + tsutils "^3.17.1" -"@webassemblyjs/ast@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.5.13.tgz#81155a570bd5803a30ec31436bc2c9c0ede38f25" - integrity sha512-49nwvW/Hx9i+OYHg+mRhKZfAlqThr11Dqz8TsrvqGKMhdI2ijy3KBJOun2Z4770TPjrIJhR6KxChQIDaz8clDA== +"@webassemblyjs/ast@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" + integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== dependencies: - "@webassemblyjs/helper-module-context" "1.5.13" - "@webassemblyjs/helper-wasm-bytecode" "1.5.13" - "@webassemblyjs/wast-parser" "1.5.13" - debug "^3.1.0" - mamacro "^0.0.3" + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/wast-parser" "1.9.0" -"@webassemblyjs/floating-point-hex-parser@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.13.tgz#29ce0baa97411f70e8cce68ce9c0f9d819a4e298" - integrity sha512-vrvvB18Kh4uyghSKb0NTv+2WZx871WL2NzwMj61jcq2bXkyhRC+8Q0oD7JGVf0+5i/fKQYQSBCNMMsDMRVAMqA== +"@webassemblyjs/floating-point-hex-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" + integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== -"@webassemblyjs/helper-api-error@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.13.tgz#e49b051d67ee19a56e29b9aa8bd949b5b4442a59" - integrity sha512-dBh2CWYqjaDlvMmRP/kudxpdh30uXjIbpkLj9HQe+qtYlwvYjPRjdQXrq1cTAAOUSMTtzqbXIxEdEZmyKfcwsg== +"@webassemblyjs/helper-api-error@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" + integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== -"@webassemblyjs/helper-buffer@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.13.tgz#873bb0a1b46449231137c1262ddfd05695195a1e" - integrity sha512-v7igWf1mHcpJNbn4m7e77XOAWXCDT76Xe7Is1VQFXc4K5jRcFrl9D0NrqM4XifQ0bXiuTSkTKMYqDxu5MhNljA== +"@webassemblyjs/helper-buffer@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" + integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== + +"@webassemblyjs/helper-code-frame@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27" + integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== dependencies: - debug "^3.1.0" + "@webassemblyjs/wast-printer" "1.9.0" -"@webassemblyjs/helper-code-frame@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.13.tgz#1bd2181b6a0be14e004f0fe9f5a660d265362b58" - integrity sha512-yN6ScQQDFCiAXnVctdVO/J5NQRbwyTbQzsGzEgXsAnrxhjp0xihh+nNHQTMrq5UhOqTb5LykpJAvEv9AT0jnAQ== +"@webassemblyjs/helper-fsm@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8" + integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== + +"@webassemblyjs/helper-module-context@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07" + integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== dependencies: - "@webassemblyjs/wast-printer" "1.5.13" + "@webassemblyjs/ast" "1.9.0" -"@webassemblyjs/helper-fsm@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.13.tgz#cdf3d9d33005d543a5c5e5adaabf679ffa8db924" - integrity sha512-hSIKzbXjVMRvy3Jzhgu+vDd/aswJ+UMEnLRCkZDdknZO3Z9e6rp1DAs0tdLItjCFqkz9+0BeOPK/mk3eYvVzZg== +"@webassemblyjs/helper-wasm-bytecode@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" + integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== -"@webassemblyjs/helper-module-context@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.13.tgz#dc29ddfb51ed657655286f94a5d72d8a489147c5" - integrity sha512-zxJXULGPLB7r+k+wIlvGlXpT4CYppRz8fLUM/xobGHc9Z3T6qlmJD9ySJ2jknuktuuiR9AjnNpKYDECyaiX+QQ== +"@webassemblyjs/helper-wasm-section@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" + integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== dependencies: - debug "^3.1.0" - mamacro "^0.0.3" + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" -"@webassemblyjs/helper-wasm-bytecode@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.13.tgz#03245817f0a762382e61733146f5773def15a747" - integrity sha512-0n3SoNGLvbJIZPhtMFq0XmmnA/YmQBXaZKQZcW8maGKwLpVcgjNrxpFZHEOLKjXJYVN5Il8vSfG7nRX50Zn+aw== - -"@webassemblyjs/helper-wasm-section@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.13.tgz#efc76f44a10d3073b584b43c38a179df173d5c7d" - integrity sha512-IJ/goicOZ5TT1axZFSnlAtz4m8KEjYr12BNOANAwGFPKXM4byEDaMNXYowHMG0yKV9a397eU/NlibFaLwr1fbw== +"@webassemblyjs/ieee754@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" + integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== dependencies: - "@webassemblyjs/ast" "1.5.13" - "@webassemblyjs/helper-buffer" "1.5.13" - "@webassemblyjs/helper-wasm-bytecode" "1.5.13" - "@webassemblyjs/wasm-gen" "1.5.13" - debug "^3.1.0" + "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/ieee754@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.5.13.tgz#573e97c8c12e4eebb316ca5fde0203ddd90b0364" - integrity sha512-TseswvXEPpG5TCBKoLx9tT7+/GMACjC1ruo09j46ULRZWYm8XHpDWaosOjTnI7kr4SRJFzA6MWoUkAB+YCGKKg== +"@webassemblyjs/leb128@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" + integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== dependencies: - ieee754 "^1.1.11" + "@xtuc/long" "4.2.2" -"@webassemblyjs/leb128@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.5.13.tgz#ab52ebab9cec283c1c1897ac1da833a04a3f4cee" - integrity sha512-0NRMxrL+GG3eISGZBmLBLAVjphbN8Si15s7jzThaw1UE9e5BY1oH49/+MA1xBzxpf1OW5sf9OrPDOclk9wj2yg== +"@webassemblyjs/utf8@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" + integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== + +"@webassemblyjs/wasm-edit@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf" + integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== dependencies: - long "4.0.0" + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/helper-wasm-section" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/wasm-opt" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + "@webassemblyjs/wast-printer" "1.9.0" -"@webassemblyjs/utf8@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.5.13.tgz#6b53d2cd861cf94fa99c1f12779dde692fbc2469" - integrity sha512-Ve1ilU2N48Ew0lVGB8FqY7V7hXjaC4+PeZM+vDYxEd+R2iQ0q+Wb3Rw8v0Ri0+rxhoz6gVGsnQNb4FjRiEH/Ng== - -"@webassemblyjs/wasm-edit@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.13.tgz#c9cef5664c245cf11b3b3a73110c9155831724a8" - integrity sha512-X7ZNW4+Hga4f2NmqENnHke2V/mGYK/xnybJSIXImt1ulxbCOEs/A+ZK/Km2jgihjyVxp/0z0hwIcxC6PrkWtgw== +"@webassemblyjs/wasm-gen@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" + integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== dependencies: - "@webassemblyjs/ast" "1.5.13" - "@webassemblyjs/helper-buffer" "1.5.13" - "@webassemblyjs/helper-wasm-bytecode" "1.5.13" - "@webassemblyjs/helper-wasm-section" "1.5.13" - "@webassemblyjs/wasm-gen" "1.5.13" - "@webassemblyjs/wasm-opt" "1.5.13" - "@webassemblyjs/wasm-parser" "1.5.13" - "@webassemblyjs/wast-printer" "1.5.13" - debug "^3.1.0" + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/ieee754" "1.9.0" + "@webassemblyjs/leb128" "1.9.0" + "@webassemblyjs/utf8" "1.9.0" -"@webassemblyjs/wasm-gen@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.13.tgz#8e6ea113c4b432fa66540189e79b16d7a140700e" - integrity sha512-yfv94Se8R73zmr8GAYzezFHc3lDwE/lBXQddSiIZEKZFuqy7yWtm3KMwA1uGbv5G1WphimJxboXHR80IgX1hQA== +"@webassemblyjs/wasm-opt@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" + integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== dependencies: - "@webassemblyjs/ast" "1.5.13" - "@webassemblyjs/helper-wasm-bytecode" "1.5.13" - "@webassemblyjs/ieee754" "1.5.13" - "@webassemblyjs/leb128" "1.5.13" - "@webassemblyjs/utf8" "1.5.13" + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" -"@webassemblyjs/wasm-opt@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.13.tgz#147aad7717a7ee4211c36b21a5f4c30dddf33138" - integrity sha512-IkXSkgzVhQ0QYAdIayuCWMmXSYx0dHGU8Ah/AxJf1gBvstMWVnzJnBwLsXLyD87VSBIcsqkmZ28dVb0mOC3oBg== +"@webassemblyjs/wasm-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" + integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== dependencies: - "@webassemblyjs/ast" "1.5.13" - "@webassemblyjs/helper-buffer" "1.5.13" - "@webassemblyjs/wasm-gen" "1.5.13" - "@webassemblyjs/wasm-parser" "1.5.13" - debug "^3.1.0" + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-api-error" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/ieee754" "1.9.0" + "@webassemblyjs/leb128" "1.9.0" + "@webassemblyjs/utf8" "1.9.0" -"@webassemblyjs/wasm-parser@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.13.tgz#6f46516c5bb23904fbdf58009233c2dd8a54c72f" - integrity sha512-XnYoIcu2iqq8/LrtmdnN3T+bRjqYFjRHqWbqK3osD/0r/Fcv4d9ecRzjVtC29ENEuNTK4mQ9yyxCBCbK8S/cpg== +"@webassemblyjs/wast-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914" + integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== dependencies: - "@webassemblyjs/ast" "1.5.13" - "@webassemblyjs/helper-api-error" "1.5.13" - "@webassemblyjs/helper-wasm-bytecode" "1.5.13" - "@webassemblyjs/ieee754" "1.5.13" - "@webassemblyjs/leb128" "1.5.13" - "@webassemblyjs/utf8" "1.5.13" + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/floating-point-hex-parser" "1.9.0" + "@webassemblyjs/helper-api-error" "1.9.0" + "@webassemblyjs/helper-code-frame" "1.9.0" + "@webassemblyjs/helper-fsm" "1.9.0" + "@xtuc/long" "4.2.2" -"@webassemblyjs/wast-parser@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.5.13.tgz#5727a705d397ae6a3ae99d7f5460acf2ec646eea" - integrity sha512-Lbz65T0LQ1LgzKiUytl34CwuhMNhaCLgrh0JW4rJBN6INnBB8NMwUfQM+FxTnLY9qJ+lHJL/gCM5xYhB9oWi4A== +"@webassemblyjs/wast-printer@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" + integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== dependencies: - "@webassemblyjs/ast" "1.5.13" - "@webassemblyjs/floating-point-hex-parser" "1.5.13" - "@webassemblyjs/helper-api-error" "1.5.13" - "@webassemblyjs/helper-code-frame" "1.5.13" - "@webassemblyjs/helper-fsm" "1.5.13" - long "^3.2.0" - mamacro "^0.0.3" + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/wast-parser" "1.9.0" + "@xtuc/long" "4.2.2" -"@webassemblyjs/wast-printer@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.5.13.tgz#bb34d528c14b4f579e7ec11e793ec50ad7cd7c95" - integrity sha512-QcwogrdqcBh8Z+eUF8SG+ag5iwQSXxQJELBEHmLkk790wgQgnIMmntT2sMAMw53GiFNckArf5X0bsCA44j3lWQ== - dependencies: - "@webassemblyjs/ast" "1.5.13" - "@webassemblyjs/wast-parser" "1.5.13" - long "^3.2.0" +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -acorn-dynamic-import@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz#901ceee4c7faaef7e07ad2a47e890675da50a278" - integrity sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg== - dependencies: - acorn "^5.0.0" - acorn-jsx@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e" @@ -578,16 +570,16 @@ acorn-jsx@^5.1.0: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384" integrity sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw== -acorn@^5.0.0, acorn@^5.6.2: - version "5.7.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" - integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== - acorn@^6.0.2: version "6.0.7" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.7.tgz#490180ce18337270232d9488a44be83d9afb7fd3" integrity sha512-HNJNgE60C9eOTgn974Tlp3dpLZdUr+SoxxDwPaY9J/kDNOLQTkaDgwBUXAF4SSsrAwD9RpdxuHK/EbuF+W9Ahw== +acorn@^6.4.1: + version "6.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" + integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== + acorn@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" @@ -600,6 +592,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" @@ -614,6 +611,11 @@ agent-base@~4.2.1: dependencies: es6-promisify "^5.0.0" +ajv-errors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" + integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== + ajv-keywords@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" @@ -1003,11 +1005,6 @@ assert-plus@1.0.0, assert-plus@^1.0.0: resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= -assert-plus@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" - integrity sha1-104bh+ev/A24qttwIfP+SBAasjQ= - assert@^1.1.1: version "1.4.1" resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" @@ -1035,7 +1032,7 @@ async-done@^1.2.0, async-done@^1.2.2: process-nextick-args "^1.0.7" stream-exhaust "^1.0.1" -async-each@^1.0.0, async-each@^1.0.1: +async-each@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" integrity sha1-GdOGodntxufByF04iu28xW0zYC0= @@ -1096,17 +1093,12 @@ autoprefixer@^6.3.1: postcss "^5.2.16" postcss-value-parser "^3.2.3" -aws-sign2@~0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" - integrity sha1-FDQt0428yU0OW4fXY81jYSwOeU8= - 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.2.1, aws4@^1.6.0: +aws4@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" integrity sha1-g+9cqGCysy5KDe7e6MdxudtXRx4= @@ -1116,6 +1108,13 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== +axios@^0.19.0: + version "0.19.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" + integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== + dependencies: + follow-redirects "1.5.10" + azure-storage@^2.10.2: version "2.10.2" resolved "https://registry.yarnpkg.com/azure-storage/-/azure-storage-2.10.2.tgz#3bcabdbf10e72fd0990db81116e49023c4a675b6" @@ -1249,6 +1248,11 @@ bluebird@^3.5.1: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" integrity sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA== +bluebird@^3.5.5: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + 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" @@ -1264,13 +1268,6 @@ boolean@^3.0.0: resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.0.0.tgz#fab78d5907dbae6216ab46d32733bb7b76b99e76" integrity sha512-OElxJ1lUSinuoUnkpOgLmxp0DC4ytEhODEL6QJU0NpxE/mI4rUSh8h1P1Wkvfi3xQEBcxXR2gBIPNYNuaFcAbQ== -boom@2.x.x: - version "2.10.1" - resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" - integrity sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8= - dependencies: - hoek "2.x.x" - boom@4.x.x: version "4.3.1" resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31" @@ -1302,7 +1299,7 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" -braces@^2.3.0, braces@^2.3.1, braces@^2.3.2: +braces@^2.3.1, braces@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== @@ -1478,6 +1475,27 @@ cacache@^10.0.4: unique-filename "^1.1.0" y18n "^4.0.0" +cacache@^12.0.2: + version "12.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" + integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== + dependencies: + bluebird "^3.5.5" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.4" + graceful-fs "^4.1.15" + infer-owner "^1.0.3" + lru-cache "^5.1.1" + mississippi "^3.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.3" + ssri "^6.0.1" + unique-filename "^1.1.1" + y18n "^4.0.0" + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -1536,11 +1554,6 @@ caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000760.tgz#3ea29473eb78a6ccb09f2eb73ac9e1debfec528d" integrity sha1-PqKUc+t4psywny63Osnh3r/sUo0= -caseless@~0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" - integrity sha1-cVuW6phBWTzDMGeSP17GDr2k99c= - caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -1553,16 +1566,7 @@ chainsaw@~0.1.0: dependencies: traverse ">=0.3.0 <0.4" -chalk@2.4.2, chalk@^2.1.0, 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== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^1.1.1, chalk@^1.1.3: +chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= @@ -1582,6 +1586,15 @@ chalk@^2.0.0: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@^2.1.0, 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== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chalk@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba" @@ -1647,25 +1660,39 @@ chokidar@^2.0.0: optionalDependencies: fsevents "^1.2.7" -chokidar@^2.0.2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26" - integrity sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ== +chokidar@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== dependencies: anymatch "^2.0.0" - async-each "^1.0.0" - braces "^2.3.0" + async-each "^1.0.1" + braces "^2.3.2" glob-parent "^3.1.0" - inherits "^2.0.1" + inherits "^2.0.3" is-binary-path "^1.0.0" is-glob "^4.0.0" - lodash.debounce "^4.0.8" - normalize-path "^2.1.1" + normalize-path "^3.0.0" path-is-absolute "^1.0.0" - readdirp "^2.0.0" - upath "^1.0.5" + readdirp "^2.2.1" + upath "^1.1.1" optionalDependencies: - fsevents "^1.2.2" + fsevents "^1.2.7" + +chokidar@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.0.tgz#b30611423ce376357c765b9b8f904b9fba3c0be8" + integrity sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.4.0" + optionalDependencies: + fsevents "~2.1.2" chownr@^1.0.1: version "1.0.1" @@ -1685,10 +1712,10 @@ chrome-remote-interface@0.26.1: commander "2.11.x" ws "^3.3.3" -chrome-trace-event@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz#45a91bd2c20c9411f0963b5aaeb9a1b95e09cc48" - integrity sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A== +chrome-trace-event@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" + integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== dependencies: tslib "^1.9.0" @@ -1956,10 +1983,10 @@ commander@^2.19.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== -commander@~2.13.0: - version "2.13.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" - integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA== +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== commandpost@^1.0.0: version "1.2.1" @@ -2003,6 +2030,21 @@ concat-with-sourcemaps@^1.0.0: dependencies: source-map "^0.5.1" +concurrently@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-5.2.0.tgz#ead55121d08a0fc817085584c123cedec2e08975" + integrity sha512-XxcDbQ4/43d6CxR7+iV8IZXhur4KbmEJk1CetVMUqCy34z9l0DkszbY+/9wvmSnToTej0SYomc2WSRH+L0zVJw== + dependencies: + chalk "^2.4.2" + date-fns "^2.0.1" + lodash "^4.17.15" + read-pkg "^4.0.1" + rxjs "^6.5.2" + spawn-command "^0.0.2-1" + supports-color "^6.1.0" + tree-kill "^1.2.2" + yargs "^13.3.0" + config-chain@^1.1.11, config-chain@^1.1.12: version "1.1.12" resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" @@ -2104,17 +2146,6 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -coveralls@^2.11.11: - version "2.13.3" - resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-2.13.3.tgz#9ad7c2ae527417f361e8b626483f48ee92dd2bc7" - integrity sha512-iiAmn+l1XqRwNLXhW8Rs5qHZRFMYp9ZIPjEOVRpC/c4so6Y/f4/lFi0FfR5B9cCqgyhkJ5cZmbvcVRfP8MHchw== - dependencies: - js-yaml "3.6.1" - lcov-parse "0.0.10" - log-driver "1.2.5" - minimist "1.2.0" - request "2.79.0" - create-ecdh@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" @@ -2146,7 +2177,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: +cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -2162,13 +2193,6 @@ crypt@~0.0.1: resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= -cryptiles@2.x.x: - version "2.0.5" - resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" - integrity sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g= - dependencies: - boom "2.x.x" - cryptiles@3.x.x: version "3.1.2" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe" @@ -2325,6 +2349,11 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +date-fns@^2.0.1: + version "2.14.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.14.0.tgz#359a87a265bb34ef2e38f93ecf63ac453f9bc7ba" + integrity sha512-1zD+68jhFgDIM0rF05rcwYO8cExdNqxjq4xP1QKM60Q45mnO6zaMWB4tOzrIr4M4GSLntsKeE4c9Bdl2jhL/yw== + date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" @@ -2349,13 +2378,20 @@ debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@3.1.0: +debug@3.1.0, debug@=3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== dependencies: ms "2.0.0" +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + debug@^3.1.0: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" @@ -2363,13 +2399,6 @@ debug@^3.1.0: dependencies: ms "^2.1.1" -debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== - dependencies: - ms "^2.1.1" - decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -2715,10 +2744,10 @@ electron-to-chromium@^1.2.7: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.27.tgz#78ecb8a399066187bb374eede35d9c70565a803d" integrity sha1-eOy4o5kGYYe7N07t412ccFZagD0= -electron@7.2.4: - version "7.2.4" - resolved "https://registry.yarnpkg.com/electron/-/electron-7.2.4.tgz#9fc0446dae23ead897af8742470cb18da55c6ce9" - integrity sha512-Z+R692uTzXgP8AHrabE+kkrMlQJ6pnAYoINenwj9QSqaD2YbO8IuXU9DMCcUY0+VpA91ee09wFZJNUKYPMnCKg== +electron@7.3.2: + version "7.3.2" + resolved "https://registry.yarnpkg.com/electron/-/electron-7.3.2.tgz#184b69fe9089693e179b3b34effa975dfc8e505d" + integrity sha512-5uSWVfCJogiPiU0G+RKi4ECnNs0gPNjAwYVE9KR7RXaOJYcpNIC5RFejaaUnuRoBssJ5B1n/5WU6wDUxvPajWQ== dependencies: "@electron/get" "^1.0.1" "@types/node" "^12.0.12" @@ -2759,6 +2788,11 @@ emojis-list@^2.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= +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== + encodeurl@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -2778,7 +2812,7 @@ end-of-stream@^1.4.1: dependencies: once "^1.4.0" -enhanced-resolve@4.1.0, enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: +enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" integrity sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng== @@ -2787,6 +2821,15 @@ enhanced-resolve@4.1.0, enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: memory-fs "^0.4.0" tapable "^1.0.0" +enhanced-resolve@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.2.0.tgz#5d43bda4a0fd447cb0ebbe71bef8deff8805ad0d" + integrity sha512-S7eiFb/erugyd1rLb6mQ3Vuq+EXHv5cpCkNqqIkYkBgN2QdFnyCZzFBleqwGEx4lgNGYij81BWnCrFNK7vxvjQ== + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.5.0" + tapable "^1.0.0" + entities@^1.1.1, entities@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" @@ -2811,6 +2854,13 @@ error-ex@^1.2.0: dependencies: is-arrayish "^0.2.1" +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: version "0.10.35" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.35.tgz#18ee858ce6a3c45c7d79e91c15fcca9ec568494f" @@ -2900,6 +2950,14 @@ eslint-scope@^4.0.0: esrecurse "^4.1.0" estraverse "^4.1.1" +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + eslint-scope@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" @@ -2913,13 +2971,20 @@ eslint-utils@^1.3.1: resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.3.1.tgz#9a851ba89ee7c460346f97cf8939c7298827e512" integrity sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q== -eslint-utils@^1.4.2, eslint-utils@^1.4.3: +eslint-utils@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== dependencies: eslint-visitor-keys "^1.1.0" +eslint-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.0.0.tgz#7be1cc70f27a72a76cd14aa698bcabed6890e1cd" + integrity sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA== + dependencies: + eslint-visitor-keys "^1.1.0" + eslint-visitor-keys@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" @@ -3094,10 +3159,10 @@ event-stream@~3.3.4: stream-combiner "^0.2.2" through "^2.3.8" -events@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" - integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= +events@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59" + integrity sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg== evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" @@ -3181,7 +3246,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@^3.0.0, extend@~3.0.0, extend@~3.0.1: +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= @@ -3305,6 +3370,11 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" +figgy-pudding@^3.5.1: + version "3.5.2" + resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" + integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== + figures@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" @@ -3389,6 +3459,15 @@ find-cache-dir@^1.0.0: make-dir "^1.0.0" pkg-dir "^2.0.0" +find-cache-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== + dependencies: + commondir "^1.0.1" + make-dir "^2.0.0" + pkg-dir "^3.0.0" + find-parent-dir@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/find-parent-dir/-/find-parent-dir-0.3.0.tgz#33c44b429ab2b2f0646299c5f9f718f376ff8d54" @@ -3416,16 +3495,6 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -findup-sync@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" - integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== - dependencies: - detect-file "^1.0.0" - is-glob "^4.0.0" - micromatch "^3.0.4" - resolve-dir "^1.0.1" - findup-sync@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" @@ -3436,6 +3505,16 @@ findup-sync@^2.0.0: micromatch "^3.0.4" resolve-dir "^1.0.1" +findup-sync@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" + integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== + dependencies: + detect-file "^1.0.0" + is-glob "^4.0.0" + micromatch "^3.0.4" + resolve-dir "^1.0.1" + fined@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/fined/-/fined-1.1.0.tgz#b37dc844b76a2f5e7081e884f7c0ae344f153476" @@ -3494,6 +3573,13 @@ flush-write-stream@^1.0.0, flush-write-stream@^1.0.2: inherits "^2.0.1" readable-stream "^2.0.4" +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== + dependencies: + debug "=3.1.0" + for-in@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.5.tgz#007374e2b6d5c67420a1479bdb75a04872b738c4" @@ -3523,15 +3609,6 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= -form-data@~2.1.1: - version "2.1.4" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" - integrity sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE= - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.5" - mime-types "^2.1.12" - form-data@~2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf" @@ -3641,14 +3718,6 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^1.2.2: - version "1.2.4" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426" - integrity sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg== - dependencies: - nan "^2.9.2" - node-pre-gyp "^0.10.0" - fsevents@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.7.tgz#4851b664a3783e52003b3c66eb0eee1074933aa4" @@ -3662,6 +3731,11 @@ fsevents@~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== + fstream@^1.0.2: version "1.0.11" resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" @@ -3696,18 +3770,6 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -generate-function@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" - integrity sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ= - -generate-object-property@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" - integrity sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA= - dependencies: - is-property "^1.0.0" - gensync@^1.0.0-beta.1: version "1.0.0-beta.1" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" @@ -3915,13 +3977,6 @@ global-agent@^2.0.2: semver "^6.3.0" serialize-error "^5.0.0" -global-modules@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" - integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== - dependencies: - global-prefix "^3.0.0" - global-modules@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" @@ -3931,6 +3986,13 @@ global-modules@^1.0.0: is-windows "^1.0.1" resolve-dir "^1.0.0" +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + global-prefix@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" @@ -4043,6 +4105,11 @@ graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg= +graceful-fs@^4.1.15: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + growl@1.9.2: version "1.9.2" resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" @@ -4308,16 +4375,6 @@ har-schema@^2.0.0: resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= -har-validator@~2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" - integrity sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0= - dependencies: - chalk "^1.1.1" - commander "^2.9.0" - is-my-json-valid "^2.12.4" - pinkie-promise "^2.0.0" - har-validator@~5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" @@ -4432,16 +4489,6 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" -hawk@~3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" - integrity sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ= - dependencies: - boom "2.x.x" - cryptiles "2.x.x" - hoek "2.x.x" - sntp "1.x.x" - hawk@~6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038" @@ -4461,11 +4508,6 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoek@2.x.x: - version "2.16.3" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" - integrity sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0= - hoek@4.x.x: version "4.2.0" resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d" @@ -4518,15 +4560,6 @@ http-proxy-agent@^2.1.0: agent-base "4" debug "3.1.0" -http-signature@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" - integrity sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8= - dependencies: - assert-plus "^0.2.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -4557,6 +4590,14 @@ https-proxy-agent@^3.0.0: agent-base "^4.3.0" debug "^3.1.0" +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: + agent-base "5" + debug "4" + husky@^0.13.1: version "0.13.4" resolved "https://registry.yarnpkg.com/husky/-/husky-0.13.4.tgz#48785c5028de3452a51c48c12c4f94b2124a1407" @@ -4567,12 +4608,10 @@ husky@^0.13.1: is-ci "^1.0.9" normalize-path "^1.0.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== - dependencies: - safer-buffer ">= 2.1.2 < 3" +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== iconv-lite@^0.4.19: version "0.4.19" @@ -4600,7 +4639,7 @@ icss-utils@^4.0.0, icss-utils@^4.1.1: dependencies: postcss "^7.0.14" -ieee754@^1.1.11, ieee754@^1.1.4: +ieee754@^1.1.4: version "1.1.12" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" integrity sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA== @@ -4635,7 +4674,7 @@ import-fresh@^3.0.0: parent-module "^1.0.0" resolve-from "^4.0.0" -import-local@2.0.0: +import-local@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== @@ -4653,10 +4692,10 @@ indexes-of@^1.0.1: resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= -indexof@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" - integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= +infer-owner@^1.0.3: + 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" @@ -4691,10 +4730,10 @@ ini@^1.3.5: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== -innosetup@5.6.1: - version "5.6.1" - resolved "https://registry.yarnpkg.com/innosetup/-/innosetup-5.6.1.tgz#6e7031ba35b23e716e4f29686bc994052e0c278c" - integrity sha512-Eit24N3JR8O0Wpuq/dMWCl2r550eiNP2124SbdbwOob43x89WPGL/SGpZG5EPHu20kV2N+4TwvHwFIM8pFUJ0g== +innosetup@6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/innosetup/-/innosetup-6.0.5.tgz#3001e54c638e4e19edd9ebdc6b1855b9ba8b0bbf" + integrity sha512-XRvidEN0dcxe7NrGXBjl/clfNRfmyakSDtgKaJgvZ3ciKE5ArQOVGqUJcLyPhllqHhMzYGpbUF3ZTZvtKVDP2g== inquirer@^6.1.0: version "6.2.2" @@ -4734,16 +4773,16 @@ inquirer@^7.0.0: strip-ansi "^5.1.0" through "^2.3.6" -interpret@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" - integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== - interpret@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" integrity sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ= +interpret@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== + invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" @@ -4948,16 +4987,6 @@ is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-my-json-valid@^2.12.4: - version "2.16.1" - resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz#5a846777e2c2620d1e69104e5d3a03b1f6088f11" - integrity sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ== - dependencies: - generate-function "^2.0.0" - generate-object-property "^1.1.0" - jsonpointer "^4.0.0" - xtend "^4.0.0" - is-negated-glob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2" @@ -5033,11 +5062,6 @@ is-promise@^2.1.0: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= -is-property@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" - integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ= - is-relative@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-0.2.1.tgz#d27f4c7d516d175fb610db84bbeef23c3bc97aa5" @@ -5236,14 +5260,6 @@ js-tokens@^4.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@3.6.1: - version "3.6.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.6.1.tgz#6e5fe67d8b205ce4d22fad05b7781e8dadcc4b30" - integrity sha1-bl/mfYsgXOTSL60Ft3geja3MSzA= - dependencies: - argparse "^1.0.7" - esprima "^2.6.0" - js-yaml@^3.12.0: version "3.12.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.1.tgz#295c8632a18a23e054cf5c9d3cecafe678167600" @@ -5273,10 +5289,10 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jschardet@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.1.1.tgz#af6f8fd0b3b0f5d46a8fd9614a4fce490575c184" - integrity sha512-pA5qG9Zwm8CBpGlK/lo2GE9jPxwqRgMV7Lzc/1iaPccw6v4Rhj8Zg2BTyrdmHmxlJojnbLupLeRnaPLsq03x6Q== +jschardet@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.2.1.tgz#03b0264669a90c7a5c436a68c5a7d4e4cb0c9823" + integrity sha512-Ks2JNuUJoc7PGaZ7bVFtSEvOcr0rBq6Q1J5/7+zKWLT+g+4zziL63O0jg7y2jxhzIa1LVsHUbPXrbaWmz9iwDw== jsdoctypeparser@^6.1.0: version "6.1.0" @@ -5315,7 +5331,7 @@ json-edm-parser@0.1.2: dependencies: jsonparse "~1.2.0" -json-parse-better-errors@^1.0.2: +json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== @@ -5395,11 +5411,6 @@ jsonparse@~1.2.0: resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.2.0.tgz#5c0c5685107160e72fe7489bddea0b44c2bc67bd" integrity sha1-XAxWhRBxYOcv50ib3eoLRMK8Z70= -jsonpointer@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" - integrity sha1-T9kss04OnbPInIYi7PUfm5eMbLk= - jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -5507,11 +5518,6 @@ lcid@^2.0.0: dependencies: invert-kv "^2.0.0" -lcov-parse@0.0.10: - version "0.0.10" - resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-0.0.10.tgz#1b0b8ff9ac9c7889250582b70b71315d9da6d9a3" - integrity sha1-GwuP+ayceIklBYK3C3ExXZ2m2aM= - lead@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lead/-/lead-1.0.0.tgz#6f14f99a37be3a9dd784f5495690e5903466ee42" @@ -5559,19 +5565,10 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" -loader-runner@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" - integrity sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI= - -loader-utils@1.2.3, loader-utils@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" - integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== - dependencies: - big.js "^5.2.2" - emojis-list "^2.0.0" - json5 "^1.0.1" +loader-runner@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" + integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== loader-utils@^1.0.2, loader-utils@^1.1.0: version "1.1.0" @@ -5582,6 +5579,24 @@ loader-utils@^1.0.2, loader-utils@^1.1.0: emojis-list "^2.0.0" json5 "^0.5.0" +loader-utils@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" + integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== + dependencies: + big.js "^5.2.2" + emojis-list "^2.0.0" + json5 "^1.0.1" + +loader-utils@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" + integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -5608,11 +5623,6 @@ lodash.clone@^4.3.2: resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6" integrity sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y= -lodash.debounce@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= - lodash.isequal@^4.0.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" @@ -5653,56 +5663,21 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "~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.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.15.0: - version "4.17.4" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" - integrity sha1-eCA6TRwyiuHYbcpkYONptX9AVa4= - -lodash@^4.17.10: - version "4.17.10" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" - integrity sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg== - -lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5: - version "4.17.11" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" - integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== - -lodash@^4.17.13, lodash@^4.17.14, 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-driver@1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.5.tgz#7ae4ec257302fd790d557cb10c97100d857b0056" - integrity sha1-euTsJXMC/XkNVXyxDJcQDYV7AFY= +lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5: + version "4.17.19" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" + integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== lolex@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.3.2.tgz#7c3da62ffcb30f0f5a80a2566ca24e45d8a01f31" integrity sha1-fD2mL/yzDw9agKJWbKJORdigHzE= -long@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" - integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== - -long@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" - integrity sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s= - lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" @@ -5734,6 +5709,13 @@ lru-cache@^4.1.3: pseudomap "^1.0.2" yallist "^2.1.2" +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + macaddress@^0.2.8: version "0.2.8" resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12" @@ -5746,6 +5728,14 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" +make-dir@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + make-dir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.0.0.tgz#1b5f39f6b9270ed33f9f054c5c0f84304989f801" @@ -5760,11 +5750,6 @@ make-iterator@^1.0.0: dependencies: kind-of "^6.0.2" -mamacro@^0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" - integrity sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA== - map-age-cleaner@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" @@ -5858,7 +5843,7 @@ mem@^4.0.0: mimic-fn "^2.0.0" p-is-promise "^2.0.0" -memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1: +memory-fs@^0.4.0, memory-fs@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= @@ -5866,6 +5851,14 @@ memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1: errno "^0.1.3" readable-stream "^2.0.1" +memory-fs@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" + integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + merge-options@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-options/-/merge-options-1.0.1.tgz#2a64b24457becd4e4dc608283247e94ce589aa32" @@ -5899,7 +5892,7 @@ micromatch@^2.3.7: parse-glob "^3.0.4" regex-cache "^0.4.2" -micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8: +micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -5936,7 +5929,7 @@ mime-db@~1.36.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.36.0.tgz#5020478db3c7fe93aad7bbcc4dcf869c43363397" integrity sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw== -mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.7: +mime-types@^2.1.12, mime-types@~2.1.17: version "2.1.17" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" integrity sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo= @@ -6008,7 +6001,7 @@ minimatch@0.3: lru-cache "2" sigmund "~1.0.0" -"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: +"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.3, 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== @@ -6020,7 +6013,7 @@ minimist@0.0.8: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= -minimist@1.2.0, minimist@^1.2.0: +minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= @@ -6066,6 +6059,22 @@ mississippi@^2.0.0: stream-each "^1.1.0" through2 "^2.0.0" +mississippi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" + integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^3.0.0" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + mixin-deep@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" @@ -6091,6 +6100,13 @@ mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkd dependencies: minimist "0.0.8" +mkdirp@^0.5.3: + 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" + mkpath@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/mkpath/-/mkpath-0.1.0.tgz#7554a6f8d871834cc97b5462b122c4c124d6de91" @@ -6250,6 +6266,11 @@ neo-async@^2.5.0: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.1.tgz#acb909e327b1e87ec9ef15f41b8a269512ad41ee" integrity sha512-3KL3fvuRkZ7s4IFOMfztb7zJp3QaVWnBeGoJlgB38XnCRPj/0tLzzLG5IB8NYOHbJ8g8UGrgZv44GLDk6CxTxA== +neo-async@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" + integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== + nice-try@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4" @@ -6267,10 +6288,10 @@ node-addon-api@1.6.2: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.6.2.tgz#d8aad9781a5cfc4132cc2fecdbdd982534265217" integrity sha512-479Bjw9nTE5DdBSZZWprFryHGjUaQC31y1wHo19We/k0BZlrmhqQitWoUL0cD8+scljCbIUL+E58oRDEakdGGA== -node-libs-browser@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.1.0.tgz#5f94263d404f6e44767d726901fff05478d600df" - integrity sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg== +node-libs-browser@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" + integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== dependencies: assert "^1.1.1" browserify-zlib "^0.2.0" @@ -6279,10 +6300,10 @@ node-libs-browser@^2.0.0: constants-browserify "^1.0.0" crypto-browserify "^3.11.0" domain-browser "^1.1.1" - events "^1.0.0" + events "^3.0.0" https-browserify "^1.0.0" os-browserify "^0.3.0" - path-browserify "0.0.0" + path-browserify "0.0.1" process "^0.11.10" punycode "^1.2.4" querystring-es3 "^0.2.0" @@ -6293,8 +6314,8 @@ node-libs-browser@^2.0.0: timers-browserify "^2.0.4" tty-browserify "0.0.0" url "^0.11.0" - util "^0.10.3" - vm-browserify "0.0.4" + util "^0.11.0" + vm-browserify "^1.0.1" node-pre-gyp@^0.10.0: version "0.10.3" @@ -6473,7 +6494,7 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.3.tgz#64348e3b3d80f035b40ac11563d278f8b72db89c" integrity sha512-IKdSTiDWCarf2JTS5e9e2+5tPZGdkRJ79XjYV0pzK8Q9BpsFyBq1RGKxzs7Q8UBushGw7m6TzVKz6fcY99iSWw== -oauth-sign@~0.8.1, oauth-sign@~0.8.2: +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= @@ -6834,6 +6855,14 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + parse-node-version@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" @@ -6868,10 +6897,10 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= -path-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" - integrity sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo= +path-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" + integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== path-dirname@^1.0.0: version "1.0.2" @@ -6976,6 +7005,11 @@ picomatch@^2.0.4: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6" integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA== +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== + pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -6986,6 +7020,11 @@ pify@^3.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" @@ -7544,11 +7583,6 @@ q@^1.0.1, q@^1.1.2: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= -qs@~6.3.0: - version "6.3.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c" - integrity sha1-51vV9uJoEioqDgvaYwslUMFmUCw= - qs@~6.5.1: version "6.5.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" @@ -7613,6 +7647,13 @@ randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: dependencies: safe-buffer "^5.1.0" +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + randomfill@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" @@ -7658,6 +7699,15 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" +read-pkg@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-4.0.1.tgz#963625378f3e1c4d48c85872b5a6ec7d5d093237" + integrity sha1-ljYlN48+HE1IyFhytabsfV0JMjc= + dependencies: + normalize-package-data "^2.3.2" + parse-json "^4.0.0" + pify "^3.0.0" + read@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" @@ -7741,16 +7791,6 @@ readable-stream@~2.0.0: string_decoder "~0.10.x" util-deprecate "~1.0.1" -readdirp@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" - integrity sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg= - dependencies: - graceful-fs "^4.1.2" - minimatch "^3.0.2" - readable-stream "^2.0.2" - set-immediate-shim "^1.0.1" - readdirp@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" @@ -7767,6 +7807,13 @@ readdirp@~3.2.0: dependencies: picomatch "^2.0.4" +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" + rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" @@ -7811,6 +7858,11 @@ regexpp@^2.0.1: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== +regexpp@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" + integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== + regextras@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/regextras/-/regextras-0.7.0.tgz#2298bef8cfb92b1b7e3b9b12aa8f69547b7d71e4" @@ -7881,32 +7933,6 @@ replacestream@^4.0.0: object-assign "^4.0.1" readable-stream "^2.0.2" -request@2.79.0: - version "2.79.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" - integrity sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4= - dependencies: - aws-sign2 "~0.6.0" - aws4 "^1.2.1" - caseless "~0.11.0" - combined-stream "~1.0.5" - extend "~3.0.0" - forever-agent "~0.6.1" - form-data "~2.1.1" - har-validator "~2.0.6" - hawk "~3.1.3" - http-signature "~1.1.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.7" - oauth-sign "~0.8.1" - qs "~6.3.0" - stringstream "~0.0.4" - tough-cookie "~2.3.0" - tunnel-agent "~0.4.1" - uuid "^3.0.0" - "request@>= 2.44.0 < 3.0.0", request@^2.79.0: version "2.83.0" resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356" @@ -8092,6 +8118,13 @@ rimraf@^2.4.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2: dependencies: glob "^7.0.5" +rimraf@^2.6.3: + 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" @@ -8145,6 +8178,13 @@ rxjs@^6.4.0: dependencies: tslib "^1.9.0" +rxjs@^6.5.2: + version "6.6.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.0.tgz#af2901eedf02e3a83ffa7f886240ff9018bbec84" + integrity sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg== + dependencies: + tslib "^1.9.0" + rxjs@^6.5.3: version "6.5.3" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a" @@ -8201,12 +8241,13 @@ sax@>=0.6.0, sax@^1.2.4, sax@~1.2.1: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -schema-utils@^0.4.4, schema-utils@^0.4.5: - version "0.4.7" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187" - integrity sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ== +schema-utils@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" + integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== dependencies: ajv "^6.1.0" + ajv-errors "^1.0.0" ajv-keywords "^3.1.0" schema-utils@^2.0.0, schema-utils@^2.0.1: @@ -8229,10 +8270,10 @@ semver-greatest-satisfied-range@^1.1.0: dependencies: sver-compat "^1.5.0" -semver-umd@^5.5.6: - version "5.5.6" - resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.6.tgz#1d185bbd2caec825c564b54907cd09e14083f228" - integrity sha512-6ARYXVi4Y4VO5HfyCjT/6xyykBtJwEXSGQ8ON4UPQSFOjZUDsbAE0J614QcBBsLTTyQMEqvsXN804vAqpydjzw== +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@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.3.0: version "5.4.1" @@ -8264,6 +8305,11 @@ semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.3.2: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== + serialize-error@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-5.0.0.tgz#a7ebbcdb03a5d71a6ed8461ffe0fc1a1afed62ac" @@ -8276,16 +8322,18 @@ serialize-javascript@^1.4.0: resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.5.0.tgz#1aa336162c88a890ddad5384baebc93a655161fe" integrity sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ== +serialize-javascript@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-3.1.0.tgz#8bf3a9170712664ef2561b44b691eafe399214ea" + integrity sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg== + dependencies: + randombytes "^2.1.0" + set-blocking@^2.0.0, 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= -set-immediate-shim@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" - integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= - set-value@^0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" @@ -8419,13 +8467,6 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -sntp@1.x.x: - version "1.0.9" - resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" - integrity sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg= - dependencies: - hoek "2.x.x" - sntp@2.x.x: version "2.1.0" resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8" @@ -8472,6 +8513,14 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.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-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" @@ -8499,6 +8548,11 @@ sparkles@^1.0.0: resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3" integrity sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM= +spawn-command@^0.0.2-1: + version "0.0.2-1" + resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" + integrity sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A= + spdlog@^0.11.1: version "0.11.1" resolved "https://registry.yarnpkg.com/spdlog/-/spdlog-0.11.1.tgz#29721b31018a5fe6a3ce2531f9d8d43e0bd6b825" @@ -8596,6 +8650,13 @@ ssri@^5.2.4: dependencies: safe-buffer "^5.1.1" +ssri@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" + integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== + dependencies: + figgy-pudding "^3.5.1" + stack-chain@^1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" @@ -8744,7 +8805,7 @@ string_decoder@~1.0.3: dependencies: safe-buffer "~5.1.0" -stringstream@~0.0.4, stringstream@~0.0.5: +stringstream@~0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" integrity sha1-TkhM1N5aC7vuGORjB3EKioFiGHg= @@ -8839,13 +8900,6 @@ supports-color@1.2.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e" integrity sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4= -supports-color@6.1.0, supports-color@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" - integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== - dependencies: - has-flag "^3.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" @@ -8872,6 +8926,20 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" +supports-color@^5.5.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" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + supports-color@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" @@ -8925,6 +8993,11 @@ tapable@^1.0.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.0.0.tgz#cbb639d9002eed9c6b5975eb20598d7936f1f9f2" integrity sha512-dQRhbNQkRnaqauC7WqSJ21EEksgT0fYZX2lqXzGkpo8JNig9zGZTYoMGvyI2nWmXlE2VSVXVDu7wLVGu/mQEsg== +tapable@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== + tar-fs@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.1.tgz#e44086c1c60d31a4f0cf893b1c4e155dabfae9e2" @@ -8968,6 +9041,13 @@ tar@^4: safe-buffer "^5.1.2" yallist "^3.0.2" +tas-client@^0.0.950: + version "0.0.950" + resolved "https://registry.yarnpkg.com/tas-client/-/tas-client-0.0.950.tgz#0fadc684721d5bc6d6af03b09e1ff5a83a5186fc" + integrity sha512-AvCNjvfouxJyKln+TsobOBO5KmXklL9+FlxrEPlIgaixy1TxCC2v2Vs/MflCiyHlGl+BeIStP4oAVPqo5c0pIA== + dependencies: + axios "^0.19.0" + temp@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" @@ -8976,6 +9056,30 @@ temp@^0.8.3: os-tmpdir "^1.0.0" rimraf "~2.2.6" +terser-webpack-plugin@^1.4.3: + version "1.4.4" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.4.tgz#2c63544347324baafa9a56baaddf1634c8abfc2f" + integrity sha512-U4mACBHIegmfoEe5fdongHESNJWqsGU+W0S/9+BmYGVQDw1+c2Ow05TpMhxjPK1sRb7cuYq1BPl1e5YHJMTCqA== + dependencies: + cacache "^12.0.2" + find-cache-dir "^2.1.0" + is-wsl "^1.1.0" + schema-utils "^1.0.0" + serialize-javascript "^3.1.0" + source-map "^0.6.1" + terser "^4.1.2" + webpack-sources "^1.4.0" + worker-farm "^1.7.0" + +terser@^4.1.2: + 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" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -9145,7 +9249,7 @@ touch@0.0.3: dependencies: nopt "~1.0.10" -tough-cookie@~2.3.0, tough-cookie@~2.3.3: +tough-cookie@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" integrity sha1-C2GKVWW23qkL80JdBNVe3EdadWE= @@ -9212,11 +9316,6 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -tunnel-agent@~0.4.1: - version "0.4.3" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" - integrity sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us= - tunnel@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.4.tgz#2d3785a158c174c9a16dc2c046ec5fc5f1742213" @@ -9270,38 +9369,16 @@ typescript@^2.6.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" integrity sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q= -typescript@^3.9.1-rc: - version "3.9.1-rc" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.1-rc.tgz#81d5a5a0a597e224b6e2af8dffb46524b2eaf5f3" - integrity sha512-+cPv8L2Vd4KidCotqi2wjegBZ5n47CDRUu/QiLVu2YbeXAz78hIfcai9ziBiNI6JTGTVwUqXRug2UZxDcxhvFw== +typescript@^4.0.0-dev.20200729: + version "4.0.0-dev.20200729" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.0-dev.20200729.tgz#3e335af3ed54513bbfd9485799837b95bdfd15a0" + integrity sha512-jzPalday93NlFVuRkY7Vixd7I9dLKoefoB2dvIhoqWaAGc5WPbOQmCHOilEGPSXNd9gb+uy97RH6+EwM/cf1gQ== uc.micro@^1.0.1, uc.micro@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.3.tgz#7ed50d5e0f9a9fb0a573379259f2a77458d50192" integrity sha1-ftUNXg+an7ClczeSWfKndFjVAZI= -uglify-es@^3.3.4: - version "3.3.9" - resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" - integrity sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ== - dependencies: - commander "~2.13.0" - source-map "~0.6.1" - -uglifyjs-webpack-plugin@^1.2.4: - version "1.2.7" - resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.7.tgz#57638dd99c853a1ebfe9d97b42160a8a507f9d00" - integrity sha512-1VicfKhCYHLS8m1DCApqBhoulnASsEoJ/BvpUpP4zoNAPpKzdH+ghk0olGJMmwX2/jprK2j3hAHdUbczBSy2FA== - dependencies: - cacache "^10.0.4" - find-cache-dir "^1.0.0" - schema-utils "^0.4.5" - serialize-javascript "^1.4.0" - source-map "^0.6.1" - uglify-es "^3.3.4" - webpack-sources "^1.1.0" - worker-farm "^1.5.2" - ultron@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" @@ -9371,6 +9448,13 @@ unique-filename@^1.1.0: dependencies: unique-slug "^2.0.0" +unique-filename@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== + dependencies: + unique-slug "^2.0.0" + unique-slug@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.0.tgz#db6676e7c7cc0629878ff196097c78855ae9f4ab" @@ -9399,11 +9483,16 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" -upath@^1.0.5, upath@^1.1.0: +upath@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" integrity sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw== +upath@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + uri-js@^4.2.1, uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" @@ -9458,10 +9547,10 @@ util@0.10.3, "util@>=0.10.3 <1": dependencies: inherits "2.0.1" -util@^0.10.3: - version "0.10.4" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" - integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== +util@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" + integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== dependencies: inherits "2.0.3" @@ -9475,16 +9564,16 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== -v8-compile-cache@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz#00f7494d2ae2b688cfe2899df6ed2c54bef91dbe" - integrity sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w== - v8-compile-cache@^2.0.3: version "2.1.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== +v8-compile-cache@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745" + integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ== + v8-inspect-profiler@^0.0.20: version "0.0.20" resolved "https://registry.yarnpkg.com/v8-inspect-profiler/-/v8-inspect-profiler-0.0.20.tgz#f7ad0f8178dcea2f1504334e8844ef38181792ab" @@ -9648,12 +9737,10 @@ vinyl@~2.0.1: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" -vm-browserify@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" - integrity sha1-XX6kW7755Kb/ZflUOOCofDV9WnM= - dependencies: - indexof "0.0.1" +vm-browserify@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" + integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== vsce@1.48.0: version "1.48.0" @@ -9678,10 +9765,10 @@ vsce@1.48.0: yauzl "^2.3.1" yazl "^2.2.2" -vscode-debugprotocol@^1.40.0: - version "1.40.0" - resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.40.0.tgz#63e1f670a6f5c4928f3f91b27b259a21c4db7861" - integrity sha512-Fwze+9qbLDPuQUhtITJSu/Vk6zIuakNM1iR2ZiZRgRaMEgBpMs2JSKaT0chrhJHCOy6/UbpsUbUBIseF6msV+g== +vscode-debugprotocol@1.41.0: + version "1.41.0" + resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.41.0.tgz#fc99b01dee26e9f25cbb5708318fc0081002808c" + integrity sha512-Sxp7kDDuhpEZiDaIfhM0jLF3RtMqvc6CpoESANE77t351uezsd/oDoqALLcOnmmsDzTgQ3W0sCvM4gErnjDFpA== vscode-nls-dev@^3.3.1: version "3.3.1" @@ -9726,10 +9813,13 @@ vscode-proxy-agent@^0.5.2: https-proxy-agent "^2.2.3" socks-proxy-agent "^4.0.1" -vscode-ripgrep@^1.5.8: - version "1.5.8" - resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.8.tgz#32cb33da6d1a9ca8f5de8c2813ed5114fd55fc11" - integrity sha512-l6Pv/t1Jk63RU+kEkMO04XxnNRYdyzuesizj9AzFpcfrUxxpAjEJBK1qO9Mov30UUGZl7uDUBn+uCv9koaHPPA== +vscode-ripgrep@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.8.0.tgz#dfe7c2ae2a2032df8a8108765c2feef73474888a" + integrity sha512-/Q5XtePkTLLi8yplr5ai24pVEymRF62xH9xXrtj35GTaDCJg3zq1s1/L1UqhVbfNDv4OcMBYjyIAt/quEi3d5w== + dependencies: + https-proxy-agent "^4.0.0" + proxy-from-env "^1.1.0" vscode-sqlite3@4.0.10: version "4.0.10" @@ -9738,10 +9828,10 @@ vscode-sqlite3@4.0.10: dependencies: nan "^2.14.0" -vscode-textmate@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.1.1.tgz#d88dbf271bee7cede455a21bd4894ba5724a4a7e" - integrity sha512-5VHjF+Fglf9d2JI5OyQ7FHutK6/29G0qYyD920K0SWO7uY8JTWbqyKAHEtfB/ZDk2fOe/E23n3wz9fHXKi63yg== +vscode-textmate@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.2.0.tgz#01f01760a391e8222fe4f33fbccbd1ad71aed74e" + integrity sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ== vscode-windows-ca-certs@0.2.0: version "0.2.0" @@ -9765,85 +9855,92 @@ vso-node-api@6.1.2-preview: typed-rest-client "^0.9.0" underscore "^1.8.3" -watchpack@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" - integrity sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA== +watchpack-chokidar2@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz#9948a1866cbbd6cb824dea13a7ed691f6c8ddff0" + integrity sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA== + dependencies: + chokidar "^2.1.8" + +watchpack@^1.6.1: + version "1.7.2" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.2.tgz#c02e4d4d49913c3e7e122c3325365af9d331e9aa" + integrity sha512-ymVbbQP40MFTp+cNMvpyBpBtygHnPzPkHqoIwRRj/0B8KhqQwV8LaKjtbaxF2lK4vl8zN9wCxS46IFCU5K4W0g== dependencies: - chokidar "^2.0.2" graceful-fs "^4.1.2" neo-async "^2.5.0" + optionalDependencies: + chokidar "^3.4.0" + watchpack-chokidar2 "^2.0.0" -webpack-cli@^3.3.8: - version "3.3.8" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.8.tgz#caeaebcc26f685db1736e5decd3f01aac30123ec" - integrity sha512-RANYSXwikSWINjHMd/mtesblNSpjpDLoYTBtP99n1RhXqVI/wxN40Auqy42I7y4xrbmRBoA5Zy5E0JSBD5XRhw== +webpack-cli@^3.3.12: + version "3.3.12" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.12.tgz#94e9ada081453cd0aa609c99e500012fd3ad2d4a" + integrity sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag== dependencies: - chalk "2.4.2" - cross-spawn "6.0.5" - enhanced-resolve "4.1.0" - findup-sync "3.0.0" - global-modules "2.0.0" - import-local "2.0.0" - interpret "1.2.0" - loader-utils "1.2.3" - supports-color "6.1.0" - v8-compile-cache "2.0.3" - yargs "13.2.4" + chalk "^2.4.2" + cross-spawn "^6.0.5" + enhanced-resolve "^4.1.1" + findup-sync "^3.0.0" + global-modules "^2.0.0" + import-local "^2.0.0" + interpret "^1.4.0" + loader-utils "^1.4.0" + supports-color "^6.1.0" + v8-compile-cache "^2.1.1" + yargs "^13.3.2" -webpack-sources@^1.0.1, webpack-sources@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54" - integrity sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw== +webpack-sources@^1.4.0, webpack-sources@^1.4.1: + 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" -webpack-stream@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/webpack-stream/-/webpack-stream-5.1.1.tgz#15b1d91da6887a37f6832128383ae0282bd7d0e7" - integrity sha512-Q6Wn8brjTGGWIobttGPt3JQzBkGgipqXSnkP8/+dJWHMG9DDSu3PGfctUo3Fp6asBGu+Vrb/g1qnvTGQECobOQ== +webpack-stream@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/webpack-stream/-/webpack-stream-5.2.1.tgz#35c992161399fe8cad9c10d4a5c258f022629b39" + integrity sha512-WvyVU0K1/VB1NZ7JfsaemVdG0PXAQUqbjUNW4A58th4pULvKMQxG+y33HXTL02JvD56ko2Cub+E2NyPwrLBT/A== dependencies: - fancy-log "^1.3.2" + fancy-log "^1.3.3" lodash.clone "^4.3.2" lodash.some "^4.2.2" memory-fs "^0.4.1" plugin-error "^1.0.1" - supports-color "^5.3.0" + supports-color "^5.5.0" through "^2.3.8" vinyl "^2.1.0" - webpack "^4.7.0" + webpack "^4.26.1" -webpack@^4.16.5, webpack@^4.7.0: - version "4.16.5" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.16.5.tgz#29fb39462823d7eb8aefcab8b45f7f241db0d092" - integrity sha512-i5cHYHonzSc1zBuwB5MSzW4v9cScZFbprkHK8ZgzPDCRkQXGGpYzPmJhbus5bOrZ0tXTcQp+xyImRSvKb0b+Kw== +webpack@^4.26.1, webpack@^4.43.0: + version "4.43.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.43.0.tgz#c48547b11d563224c561dad1172c8aa0b8a678e6" + integrity sha512-GW1LjnPipFW2Y78OOab8NJlCflB7EFskMih2AHdvjbpKMeDJqEgSx24cXXXiPS65+WSwVyxtDsJH6jGX2czy+g== dependencies: - "@webassemblyjs/ast" "1.5.13" - "@webassemblyjs/helper-module-context" "1.5.13" - "@webassemblyjs/wasm-edit" "1.5.13" - "@webassemblyjs/wasm-opt" "1.5.13" - "@webassemblyjs/wasm-parser" "1.5.13" - acorn "^5.6.2" - acorn-dynamic-import "^3.0.0" - ajv "^6.1.0" - ajv-keywords "^3.1.0" - chrome-trace-event "^1.0.0" + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/wasm-edit" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + acorn "^6.4.1" + ajv "^6.10.2" + ajv-keywords "^3.4.1" + chrome-trace-event "^1.0.2" enhanced-resolve "^4.1.0" - eslint-scope "^4.0.0" + eslint-scope "^4.0.3" json-parse-better-errors "^1.0.2" - loader-runner "^2.3.0" - loader-utils "^1.1.0" - memory-fs "~0.4.1" - micromatch "^3.1.8" - mkdirp "~0.5.0" - neo-async "^2.5.0" - node-libs-browser "^2.0.0" - schema-utils "^0.4.4" - tapable "^1.0.0" - uglifyjs-webpack-plugin "^1.2.4" - watchpack "^1.5.0" - webpack-sources "^1.0.1" + loader-runner "^2.4.0" + loader-utils "^1.2.3" + memory-fs "^0.4.1" + micromatch "^3.1.10" + mkdirp "^0.5.3" + neo-async "^2.6.1" + node-libs-browser "^2.2.1" + schema-utils "^1.0.0" + tapable "^1.1.3" + terser-webpack-plugin "^1.4.3" + watchpack "^1.6.1" + webpack-sources "^1.4.1" when@^3.7.7: version "3.7.8" @@ -9926,10 +10023,10 @@ wordwrap@~1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= -worker-farm@^1.5.2: - version "1.6.0" - resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.6.0.tgz#aecc405976fab5a95526180846f0dba288f3a4a0" - integrity sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ== +worker-farm@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" + integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== dependencies: errno "~0.1.7" @@ -10042,30 +10139,25 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" -xterm-addon-search@0.7.0-beta.2: - version "0.7.0-beta.2" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.7.0-beta.2.tgz#384bda136c707f97a77eefc76cc7d9e572ce0719" - integrity sha512-A9fyiBBvG6ZNIwSJ03+sRCv9y20/uzd1wjCoaYUqp9fu3YGiHaGwyo9rAfm2M/fQM5vBmyJk4Qw/lwVq7TtlAw== +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-beta.5: - version "0.2.0-beta.5" - resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.2.0-beta.5.tgz#5961850162df20b5e966166423cd6957ac2db298" - integrity sha512-IjnbBcyfS5JgJDXPO0W2nk/VBtGwx6GWE2snMC676z4DmAABUqPXfTzJKfUoWqoT6UcbxB0oIjDzykCfoRJp6Q== +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-web-links@0.4.0-beta.6: - version "0.4.0-beta.6" - resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.4.0-beta.6.tgz#d159d4542eb9a02d57977fe7eb5f42f8ef2f27fa" - integrity sha512-dsQVD/EyVq8PtAYGh2PGQTCt009UipIfX6Q2SBDlz+W9x7IkXjhRxRaryMmLsBCca20qeVKwmbQ+ANhLi+nTaQ== +xterm-addon-webgl@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.8.0.tgz#4bc6bb4dbfea5b0d2d7978d6c5cef922d584fb4f" + integrity sha512-dlpYPsv0C9S6v6+T/h/d/otSbdUTizMJdxvSoS34tUpMOHev6iW7Zqt5KRFqYxl4vCqpDk9Wmhb3fKL3kwX5fQ== -xterm-addon-webgl@0.7.0-beta.10: - version "0.7.0-beta.10" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.7.0-beta.10.tgz#39fdb96351e97a1bf15f4c4c8944ba3d05cacee4" - integrity sha512-nQl/ASk+ck11aSrBZXb2a0tu+SNDnm89owBk/sAZeZzi5MHNo6bB8y2VTKNNC6D3i3aFouTz4VorYB25LUgNFg== - -xterm@4.6.0-beta.44: - version "4.6.0-beta.44" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.6.0-beta.44.tgz#76b2a6b8e147595ab44aa752c0e721d935464615" - integrity sha512-vYtfz4spFcSKLEUpC6anH7TwDams71+k2wAtUzCJ47dNL2IrwYafcFsvGPm46QLTtq4M2Bp9rQo3R3V746yxNg== +xterm@4.9.0-beta.8: + version "4.9.0-beta.8" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.9.0-beta.8.tgz#ca121934d63f88668d2d5b11d9b2fc3bde7bd805" + integrity sha512-EEonYBLANDUBfEeEnHG632bZdgBaAUWst8LFr6oC6f2uLFfJGHQvVJuLaEkPtRvS+jOeoorEXZRPmso1/ANHXA== y18n@^3.2.1: version "3.2.1" @@ -10095,6 +10187,14 @@ yargs-parser@^13.1.0: camelcase "^5.0.0" decamelize "^1.2.0" +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@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" @@ -10102,7 +10202,7 @@ yargs-parser@^5.0.0: dependencies: camelcase "^3.0.0" -yargs@13.2.4, yargs@^13.2.4: +yargs@^13.2.4: version "13.2.4" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83" integrity sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg== @@ -10119,6 +10219,22 @@ yargs@13.2.4, yargs@^13.2.4: y18n "^4.0.0" yargs-parser "^13.1.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@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8"